diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 6c7a3b5..d3327c9 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -12,8 +12,6 @@ target_sources(squawk PRIVATE conference.h contact.cpp contact.h - networkaccess.cpp - networkaccess.h rosteritem.cpp rosteritem.h ${SIGNALCATCHER_SOURCE} @@ -27,3 +25,4 @@ target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) add_subdirectory(handlers) add_subdirectory(storage) add_subdirectory(passwordStorageEngines) +add_subdirectory(components) diff --git a/core/account.cpp b/core/account.cpp index 0cb159d..08321d3 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -285,7 +285,10 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) emit addPresence(jid, resource, { {"lastActivity", lastInteraction}, {"availability", p_presence.availableStatusType()}, //TODO check and handle invisible - {"status", p_presence.statusText()} + {"status", p_presence.statusText()}, + {"capabilityNode", p_presence.capabilityNode()}, + {"capabilityVer", p_presence.capabilityVer().toBase64()}, + {"capabilityHash", p_presence.capabilityHash()} }); } break; @@ -594,7 +597,8 @@ void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) { - if (info.from() == getServer()) { + QString from = info.from(); + if (from == getServer()) { bool enableCC = false; qDebug() << "Server info received for account" << name; QStringList features = info.features(); @@ -613,7 +617,7 @@ void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) qDebug() << "Requesting account" << name << "capabilities"; dm->requestInfo(getBareJid()); - } else if (info.from() == getBareJid()) { + } else if (from == getBareJid()) { qDebug() << "Received capabilities for account" << name << ":"; QList identities = info.identities(); bool pepSupported = false; @@ -626,10 +630,27 @@ void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) } rh->setPepSupport(pepSupported); } else { - qDebug() << "Received info for account" << name << "about" << info.from(); - QList identities = info.identities(); - for (const QXmppDiscoveryIq::Identity& identity : identities) { - qDebug() << " " << identity.name() << identity.category() << identity.type(); + qDebug() << "Received info for account" << name << "about" << from; + QString node = info.queryNode(); + if (!node.isEmpty()) { + QStringList feats = info.features(); + std::list identities; + std::set features(feats.begin(), feats.end()); + QList idents = info.identities(); + for (const QXmppDiscoveryIq::Identity& ident : idents) { + identities.emplace_back(); + Shared::Identity& identity = identities.back(); + identity.category = ident.category(); + identity.language = ident.language(); + identity.name = ident.name(); + identity.type = ident.type(); + + qDebug() << " " << identity.name << identity.category << identity.type; + } + for (const QString& feat : features) { + qDebug() << " " << feat; + } + emit infoDiscovered(from, node, identities, features); } } } diff --git a/core/account.h b/core/account.h index 2c9ec70..5651ad9 100644 --- a/core/account.h +++ b/core/account.h @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -42,10 +43,11 @@ #include #include -#include "shared/shared.h" +#include +#include #include "contact.h" #include "conference.h" -#include "networkaccess.h" +#include #include "handlers/messagehandler.h" #include "handlers/rosterhandler.h" @@ -118,6 +120,8 @@ public: void resendMessage(const QString& jid, const QString& id); void replaceMessage(const QString& originalId, const Shared::Message& data); void invalidatePassword(); + + void discoverInfo(const QString& address, const QString& node); public slots: void connect(); @@ -151,6 +155,7 @@ signals: void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap headers); void uploadFileError(const QString& jid, const QString& messageId, const QString& error); void needPassword(); + void infoDiscovered(const QString& address, const QString& node, const std::list& identities, const std::set& features); private: QString name; diff --git a/core/components/CMakeLists.txt b/core/components/CMakeLists.txt new file mode 100644 index 0000000..5faf837 --- /dev/null +++ b/core/components/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(squawk PRIVATE + networkaccess.cpp + networkaccess.h + clientcache.cpp + clientcache.h +) diff --git a/core/components/clientcache.cpp b/core/components/clientcache.cpp new file mode 100644 index 0000000..0259f9c --- /dev/null +++ b/core/components/clientcache.cpp @@ -0,0 +1,55 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#include "clientcache.h" + +Core::ClientCache::ClientCache(): + requested(), + cache("clients") +{ + cache.open(); +} + +Core::ClientCache::~ClientCache() { + cache.close(); +} + +void Core::ClientCache::open() { + cache.open();} + +void Core::ClientCache::close() { + cache.close();} + + +bool Core::ClientCache::checkClient(const QString& node, const QString& ver, const QString& hash) { + QString id = node + "/" + ver; + if (requested.count(id) == 0 && !cache.checkRecord(id)) { + Shared::ClientInfo& info = requested.insert(std::make_pair(id, Shared::ClientInfo())).first->second; + info.capabilitiesNode = node; + info.capabilitiesVerification = ver; + info.capabilitiesHash = hash; + emit requestClientInfo(id); + return false; + } + + return true; +} + + +bool Core::ClientCache::registerClientInfo(const QString& sourceFullJid, const QString& id, const Shared::Identity& identity, const std::set& features) { + +} diff --git a/core/components/clientcache.h b/core/components/clientcache.h new file mode 100644 index 0000000..78c7e84 --- /dev/null +++ b/core/components/clientcache.h @@ -0,0 +1,56 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef CORE_CLIENTCACHE_H +#define CORE_CLIENTCACHE_H + +#include +#include + +#include +#include + +#include +#include +#include + +namespace Core { + +class ClientCache : public QObject { + Q_OBJECT +public: + ClientCache(); + ~ClientCache(); + + void open(); + void close(); + +signals: + void requestClientInfo(const QString& id); + +public slots: + bool checkClient(const QString& node, const QString& ver, const QString& hash); + bool registerClientInfo(const QString& sourceFullJid, const QString& id, const Shared::Identity& identity, const std::set& features); + +private: + std::map requested; + Cache cache; +}; + +} + + +#endif // CORE_CLIENTCACHE_H diff --git a/core/networkaccess.cpp b/core/components/networkaccess.cpp similarity index 100% rename from core/networkaccess.cpp rename to core/components/networkaccess.cpp diff --git a/core/networkaccess.h b/core/components/networkaccess.h similarity index 98% rename from core/networkaccess.h rename to core/components/networkaccess.h index 6ddfa99..c94c22a 100644 --- a/core/networkaccess.h +++ b/core/components/networkaccess.h @@ -30,8 +30,8 @@ #include -#include "storage/urlstorage.h" -#include "shared/pathcheck.h" +#include +#include namespace Core { diff --git a/core/squawk.cpp b/core/squawk.cpp index 0f8fe9f..5edcf58 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -28,9 +28,10 @@ Core::Squawk::Squawk(QObject* parent): amap(), state(Shared::Availability::offline), network(), - isInitialized(false) + isInitialized(false), + clientCache(), #ifdef WITH_KWALLET - ,kwallet() + kwallet() #endif { connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress); @@ -67,6 +68,7 @@ void Core::Squawk::stop() { qDebug("Stopping squawk core.."); network.stop(); + clientCache.close(); if (isInitialized) { QSettings settings; @@ -115,6 +117,7 @@ void Core::Squawk::start() readSettings(); isInitialized = true; network.start(); + clientCache.open(); } void Core::Squawk::newAccountRequest(const QMap& map) @@ -180,6 +183,8 @@ void Core::Squawk::addAccount( connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard); connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError); + + connect(acc, &Account::infoDiscovered, this, &Squawk::onAccountInfoDiscovered); QMap map = { {"login", login}, @@ -314,6 +319,27 @@ void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, { Account* acc = static_cast(sender()); emit addPresence(acc->getName(), jid, name, data); + + QString node = data["capabilityNode"].toString(); + QString ver = data["capabilityVer"].toString(); + QString hash = data["capabilityHash"].toString(); + if (!clientCache.checkClient(node, ver, hash)) { + acc->discoverInfo(jid + "/" + name, node + "/" + ver); + } +} + +void Core::Squawk::onAccountInfoDiscovered( + const QString& address, + const QString& node, + const std::list& identities, + const std::set& features) +{ + Account* acc = static_cast(sender()); + + if (identities.size() != 1 || clientCache.registerClientInfo(address, node, identities.back(), features)) { + qDebug() << "Account" << acc->getName() << "received an ill-formed client discovery response from" << address << "about" << node; + return; + } } void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name) diff --git a/core/squawk.h b/core/squawk.h index 3b8073b..ea17cdf 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -32,9 +32,10 @@ #include "shared/message.h" #include "shared/global.h" #include "shared/clientinfo.h" -#include "networkaccess.h" #include "external/simpleCrypt/simplecrypt.h" -#include + +#include +#include #ifdef WITH_KWALLET #include "passwordStorageEngines/kwallet.h" @@ -137,7 +138,7 @@ private: Shared::Availability state; NetworkAccess network; bool isInitialized; - //Cache clientCache; + ClientCache clientCache; #ifdef WITH_KWALLET PSE::KWallet kwallet; @@ -181,6 +182,8 @@ private slots: void onWalletOpened(bool success); void onWalletRejectPassword(const QString& login); + + void onAccountInfoDiscovered(const QString& address, const QString& node, const std::list& identities, const std::set& features); private: void readSettings(); diff --git a/core/storage/CMakeLists.txt b/core/storage/CMakeLists.txt index 5d34b7c..2da3c67 100644 --- a/core/storage/CMakeLists.txt +++ b/core/storage/CMakeLists.txt @@ -1,10 +1,10 @@ target_sources(squawk PRIVATE archive.cpp archive.h - storage.cpp + storage.hpp storage.h urlstorage.cpp urlstorage.h - cache.cpp + cache.hpp cache.h ) diff --git a/core/storage/cache.h b/core/storage/cache.h index e5b1b88..5091561 100644 --- a/core/storage/cache.h +++ b/core/storage/cache.h @@ -18,6 +18,7 @@ #define CORE_CACHE_H #include +#include #include @@ -39,12 +40,16 @@ public: void changeRecord(const QString& key, const T& value); void removeRecord(const QString& key); T getRecord(const QString& key) const; + bool checkRecord(const QString& key) const; private: Core::Storage storage; std::map* cache; + std::set* abscent; }; } +#include "cache.hpp" + #endif // CORE_CACHE_H diff --git a/core/storage/cache.cpp b/core/storage/cache.hpp similarity index 60% rename from core/storage/cache.cpp rename to core/storage/cache.hpp index 71fe369..1604d66 100644 --- a/core/storage/cache.cpp +++ b/core/storage/cache.hpp @@ -14,21 +14,21 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#ifndef CORE_CACHE_HPP +#define CORE_CACHE_HPP #include "cache.h" -#include - -template class Core::Cache; - template Core::Cache::Cache(const QString& name): storage(name), - cache(new std::map ()) {} + cache(new std::map ()), + abscent(new std::set ()) {} template Core::Cache::~Cache() { close(); delete cache; + delete abscent; } template @@ -43,27 +43,60 @@ template void Core::Cache::addRecord(const QString& key, const T& value) { storage.addRecord(key, value); cache->insert(std::make_pair(key, value)); + abscent->erase(key); } template T Core::Cache::getRecord(const QString& key) const { typename std::map::const_iterator itr = cache->find(key); if (itr == cache->end()) { - T value = storage.getRecord(key); - itr = cache->insert(std::make_pair(key, value)).first; + if (abscent->count(key) > 0) { + throw Archive::NotFound(key, storage.getName().toStdString()); + } + + try { + T value = storage.getRecord(key); + itr = cache->insert(std::make_pair(key, value)).first; + } catch (const Archive::NotFound& error) { + abscent->insert(key); + throw error; + } } return itr->second; } +template +bool Core::Cache::checkRecord(const QString& key) const { + typename std::map::const_iterator itr = cache->find(key); + if (itr != cache->end()) + return true; + + if (abscent->count(key) > 0) + return false; + + try { + T value = storage.getRecord(key); + itr = cache->insert(std::make_pair(key, value)).first; + } catch (const Archive::NotFound& error) { + return false; + } + + return true; +} + template void Core::Cache::changeRecord(const QString& key, const T& value) { - storage.changeRecord(key, value); + storage.changeRecord(key, value); //there is a non straightforward behaviour: if there was no element at the sorage it will be added cache->at(key) = value; + abscent->erase(key); //so... this line here is to make it coherent with the storage } template void Core::Cache::removeRecord(const QString& key) { storage.removeRecord(key); cache->erase(key); + abscent->insert(key); } + +#endif //CORE_CACHE_HPP diff --git a/core/storage/storage.h b/core/storage/storage.h index 5942c0f..5d6dabc 100644 --- a/core/storage/storage.h +++ b/core/storage/storage.h @@ -43,6 +43,7 @@ public: void changeRecord(const QString& key, const T& value); void removeRecord(const QString& key); T getRecord(const QString& key) const; + QString getName() const; private: @@ -54,4 +55,6 @@ private: } +#include "storage.hpp" + #endif // CORE_STORAGE_H diff --git a/core/storage/storage.cpp b/core/storage/storage.hpp similarity index 97% rename from core/storage/storage.cpp rename to core/storage/storage.hpp index 99db8af..48e334d 100644 --- a/core/storage/storage.cpp +++ b/core/storage/storage.hpp @@ -15,16 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef CORE_STORAGE_HPP +#define CORE_STORAGE_HPP #include #include #include "storage.h" -#include - -template class Core::Storage; - template Core::Storage::Storage(const QString& p_name): name(p_name), @@ -202,3 +200,9 @@ void Core::Storage::removeRecord(const QString& key) mdb_txn_commit(txn); } } + +template +QString Core::Storage::getName() const { + return name;} + +#endif //CORE_STORAGE_HPP diff --git a/main/main.cpp b/main/main.cpp index 3368e0a..086dbc0 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -19,6 +19,7 @@ #include "shared/global.h" #include "shared/messageinfo.h" #include "shared/pathcheck.h" +#include "shared/identity.h" #include "main/application.h" #include "core/signalcatcher.h" #include "core/squawk.h" @@ -39,6 +40,9 @@ int main(int argc, char *argv[]) qRegisterMetaType("Shared::VCard"); qRegisterMetaType>("std::list"); qRegisterMetaType>("std::list"); + qRegisterMetaType>("std::list"); + qRegisterMetaType>("std::set"); + qRegisterMetaType>("std::list"); qRegisterMetaType>("QSet"); qRegisterMetaType("Shared::ConnectionState"); qRegisterMetaType("Shared::Availability"); diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 1e47618..5828bd4 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -20,4 +20,5 @@ target_sources(squawk PRIVATE pathcheck.h clientinfo.h clientinfo.cpp + identity.h ) diff --git a/shared/clientinfo.cpp b/shared/clientinfo.cpp index 745bd54..3a2f441 100644 --- a/shared/clientinfo.cpp +++ b/shared/clientinfo.cpp @@ -23,8 +23,14 @@ Shared::ClientInfo::ClientInfo(): capabilitiesNode(), capabilitiesVerification(), capabilitiesHash(), + specificPresence(), capabilitiesExtensions() {} +QString Shared::ClientInfo::getId() const { + return capabilitiesNode + "/" + capabilitiesVerification; +} + + QDataStream & Shared::ClientInfo::operator >> (QDataStream& stream) const { stream << name; stream << category; diff --git a/shared/clientinfo.h b/shared/clientinfo.h index 742e49a..55dd29f 100644 --- a/shared/clientinfo.h +++ b/shared/clientinfo.h @@ -29,6 +29,8 @@ class ClientInfo public: ClientInfo(); + QString getId() const; + QDataStream& operator << (QDataStream& stream); QDataStream& operator >> (QDataStream& stream) const; @@ -39,6 +41,7 @@ public: QString capabilitiesNode; QString capabilitiesVerification; QString capabilitiesHash; + QString specificPresence; std::set capabilitiesExtensions; }; diff --git a/shared/identity.h b/shared/identity.h new file mode 100644 index 0000000..6041e1a --- /dev/null +++ b/shared/identity.h @@ -0,0 +1,35 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SHARED_IDENTITY_H +#define SHARED_IDENTITY_H + +#include + +namespace Shared { + +struct Identity { + QString category; + QString language; + QString name; + QString type; +}; + +} + +#endif //SHARED_IDENTITY_H