diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 9369cb7..8b6fa69 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -6,7 +6,8 @@ endif(WIN32) target_sources(squawk PRIVATE account.cpp account.h - adapterFuctions.cpp + adapterfunctions.cpp + adapterfunctions.h archive.cpp archive.h conference.cpp diff --git a/core/account.cpp b/core/account.cpp index 91d0f2b..3d782cd 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -41,13 +41,12 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& rcpm(new QXmppMessageReceiptManager()), reconnectScheduled(false), reconnectTimer(new QTimer), - avatarHash(), - avatarType(), - ownVCardRequestInProgress(false), network(p_net), passwordType(Shared::AccountPassword::plain), + pepSupport(false), mh(new MessageHandler(this)), - rh(new RosterHandler(this)) + rh(new RosterHandler(this)), + vh(new VCardHandler(this)) { config.setUser(p_login); config.setDomain(p_server); @@ -73,10 +72,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(mm); client.addExtension(bm); - - QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); - //QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler - client.addExtension(um); QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived); QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed); @@ -91,52 +86,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(rcpm); QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived); - - QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); - path += "/" + name; - QDir dir(path); - - if (!dir.exists()) { - bool res = dir.mkpath(path); - if (!res) { - qDebug() << "Couldn't create a cache directory for account" << name; - throw 22; - } - } - - QFile* avatar = new QFile(path + "/avatar.png"); - QString type = "png"; - if (!avatar->exists()) { - delete avatar; - avatar = new QFile(path + "/avatar.jpg"); - type = "jpg"; - if (!avatar->exists()) { - delete avatar; - avatar = new QFile(path + "/avatar.jpeg"); - type = "jpeg"; - if (!avatar->exists()) { - delete avatar; - avatar = new QFile(path + "/avatar.gif"); - type = "gif"; - } - } - } - - if (avatar->exists()) { - if (avatar->open(QFile::ReadOnly)) { - QCryptographicHash sha1(QCryptographicHash::Sha1); - sha1.addData(avatar); - avatarHash = sha1.result(); - avatarType = type; - } - } - if (avatarType.size() != 0) { - presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); - presence.setPhotoHash(avatarHash.toUtf8()); - } else { - presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady); - } - reconnectTimer->setSingleShot(true); QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer); @@ -160,6 +109,7 @@ Account::~Account() QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete); QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError); + delete vh; delete mh; delete rh; @@ -264,36 +214,6 @@ void Core::Account::reconnect() } } -QString Core::Account::getName() const { - return name;} - -QString Core::Account::getLogin() const { - return config.user();} - -QString Core::Account::getPassword() const { - return config.password();} - -QString Core::Account::getServer() const { - return config.domain();} - -Shared::AccountPassword Core::Account::getPasswordType() const { - return passwordType;} - -void Core::Account::setPasswordType(Shared::AccountPassword pt) { - passwordType = pt; } - -void Core::Account::setLogin(const QString& p_login) { - config.setUser(p_login);} - -void Core::Account::setName(const QString& p_name) { - name = p_name;} - -void Core::Account::setPassword(const QString& p_password) { - config.setPassword(p_password);} - -void Core::Account::setServer(const QString& p_server) { - config.setDomain(p_server);} - Shared::Availability Core::Account::getAvailability() const { if (state == Shared::ConnectionState::connected) { @@ -325,32 +245,11 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) QString jid = comps.front().toLower(); QString resource = comps.back(); - QString myJid = getLogin() + "@" + getServer(); - - if (jid == myJid) { + if (jid == getBareJid()) { if (resource == getResource()) { emit availabilityChanged(static_cast(p_presence.availableStatusType())); } else { - if (!ownVCardRequestInProgress) { - switch (p_presence.vCardUpdateType()) { - case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo - break; - case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here - break; - case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any - if (avatarType.size() > 0) { - vm->requestClientVCard(); - ownVCardRequestInProgress = true; - } - break; - case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load - if (avatarHash != p_presence.photoHash()) { - vm->requestClientVCard(); - ownVCardRequestInProgress = true; - } - break; - } - } + vh->handleOtherPresenceOfMyAccountChange(p_presence); } } else { RosterItem* item = rh->getRosterItem(jid); @@ -392,18 +291,6 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) } } -QString Core::Account::getResource() const { - return config.resource();} - -void Core::Account::setResource(const QString& p_resource) { - config.setResource(p_resource);} - -QString Core::Account::getFullJid() const { - return getLogin() + "@" + getServer() + "/" + getResource();} - -void Core::Account::sendMessage(const Shared::Message& data) { - mh->sendMessage(data);} - void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg) { if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) { @@ -667,6 +554,151 @@ void Core::Account::setRoomJoined(const QString& jid, bool joined) conf->setJoined(joined); } +void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) +{ + if (items.from() == getServer()) { + std::set needToRequest; + qDebug() << "Server items list received for account " << name << ":"; + for (QXmppDiscoveryIq::Item item : items.items()) { + QString jid = item.jid(); + if (jid != getServer()) { + qDebug() << " Node" << jid; + needToRequest.insert(jid); + } else { + qDebug() << " " << item.node().toStdString().c_str(); + } + } + + for (const QString& jid : needToRequest) { + dm->requestInfo(jid); + } + } +} + +void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) +{ + if (info.from() == getServer()) { + bool enableCC = false; + qDebug() << "Server info received for account" << name; + QStringList features = info.features(); + qDebug() << "List of supported features of the server " << getServer() << ":"; + for (const QString& feature : features) { + qDebug() << " " << feature.toStdString().c_str(); + if (feature == "urn:xmpp:carbons:2") { + enableCC = true; + } + } + + if (enableCC) { + qDebug() << "Enabling carbon copies for account" << name; + cm->setCarbonsEnabled(true); + } + + qDebug() << "Requesting account" << name << "capabilities"; + dm->requestInfo(getBareJid()); + } else if (info.from() == getBareJid()) { + qDebug() << "Received capabilities for account" << name << ":"; + QList identities = info.identities(); + bool pepSupported = false; + for (const QXmppDiscoveryIq::Identity& identity : identities) { + QString type = identity.type(); + qDebug() << " " << identity.category() << type; + if (type == "pep") { + pepSupported = true; + } + } + 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(); + } + } +} + +void Core::Account::handleDisconnection() +{ + cm->setCarbonsEnabled(false); + rh->handleOffline(); + vh->handleOffline(); + archiveQueries.clear(); +} + +void Core::Account::onContactHistoryResponse(const std::list& list, bool last) +{ + RosterItem* contact = static_cast(sender()); + + qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements"; + if (last) { + qDebug() << "The response contains the first accounted message"; + } + emit responseArchive(contact->jid, list, last); +} + +QString Core::Account::getResource() const { + return config.resource();} + +void Core::Account::setResource(const QString& p_resource) { + config.setResource(p_resource);} + +QString Core::Account::getBareJid() const { + return getLogin() + "@" + getServer();} + +QString Core::Account::getFullJid() const { + return getBareJid() + "/" + getResource();} + +QString Core::Account::getName() const { + return name;} + +QString Core::Account::getLogin() const { + return config.user();} + +QString Core::Account::getPassword() const { + return config.password();} + +QString Core::Account::getServer() const { + return config.domain();} + +Shared::AccountPassword Core::Account::getPasswordType() const { + return passwordType;} + +void Core::Account::setPasswordType(Shared::AccountPassword pt) { + passwordType = pt; } + +void Core::Account::setLogin(const QString& p_login) { + config.setUser(p_login);} + +void Core::Account::setName(const QString& p_name) { + name = p_name;} + +void Core::Account::setPassword(const QString& p_password) { + config.setPassword(p_password);} + +void Core::Account::setServer(const QString& p_server) { + config.setDomain(p_server);} + +void Core::Account::sendMessage(const Shared::Message& data) { + mh->sendMessage(data);} + +void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data){ + mh->requestChangeMessage(jid, messageId, data);} + +void Core::Account::resendMessage(const QString& jid, const QString& id) { + mh->resendMessage(jid, id);} + +void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) { + mh->sendMessage(data, false, originalId);} + +void Core::Account::requestVCard(const QString& jid) { + vh->requestVCard(jid);} + +void Core::Account::uploadVCard(const Shared::VCard& card) { + vh->uploadVCard(card);} + +QString Core::Account::getAvatarPath() const { + return vh->getAvatarPath();} + void Core::Account::removeRoomRequest(const QString& jid){ rh->removeRoomRequest(jid);} @@ -688,254 +720,3 @@ void Core::Account::renameContactRequest(const QString& jid, const QString& newN rm->renameItem(jid, newName); } } - -void Core::Account::onVCardReceived(const QXmppVCardIq& card) -{ - QString id = card.from(); - QStringList comps = id.split("/"); - QString jid = comps.front().toLower(); - QString resource(""); - if (comps.size() > 1) { - resource = comps.back(); - } - pendingVCardRequests.erase(id); - RosterItem* item = rh->getRosterItem(jid); - - if (item == 0) { - if (jid == getLogin() + "@" + getServer()) { - onOwnVCardReceived(card); - } else { - qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; - } - return; - } - - Shared::VCard vCard = item->handleResponseVCard(card, resource); - - emit receivedVCard(jid, vCard); -} - -void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) -{ - QByteArray ava = card.photo(); - bool avaChanged = false; - QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/"; - if (ava.size() > 0) { - QCryptographicHash sha1(QCryptographicHash::Sha1); - sha1.addData(ava); - QString newHash(sha1.result()); - QMimeDatabase db; - QMimeType newType = db.mimeTypeForData(ava); - if (avatarType.size() > 0) { - if (avatarHash != newHash) { - QString oldPath = path + "avatar." + avatarType; - QFile oldAvatar(oldPath); - bool oldToRemove = false; - if (oldAvatar.exists()) { - if (oldAvatar.rename(oldPath + ".bak")) { - oldToRemove = true; - } else { - qDebug() << "Received new avatar for account" << name << "but can't get rid of the old one, doing nothing"; - } - } - QFile newAvatar(path + "avatar." + newType.preferredSuffix()); - if (newAvatar.open(QFile::WriteOnly)) { - newAvatar.write(ava); - newAvatar.close(); - avatarHash = newHash; - avatarType = newType.preferredSuffix(); - avaChanged = true; - } else { - qDebug() << "Received new avatar for account" << name << "but can't save it"; - if (oldToRemove) { - qDebug() << "rolling back to the old avatar"; - if (!oldAvatar.rename(oldPath)) { - qDebug() << "Couldn't roll back to the old avatar in account" << name; - } - } - } - } - } else { - QFile newAvatar(path + "avatar." + newType.preferredSuffix()); - if (newAvatar.open(QFile::WriteOnly)) { - newAvatar.write(ava); - newAvatar.close(); - avatarHash = newHash; - avatarType = newType.preferredSuffix(); - avaChanged = true; - } else { - qDebug() << "Received new avatar for account" << name << "but can't save it"; - } - } - } else { - if (avatarType.size() > 0) { - QFile oldAvatar(path + "avatar." + avatarType); - if (!oldAvatar.remove()) { - qDebug() << "Received vCard for account" << name << "without avatar, but can't get rid of the file, doing nothing"; - } else { - avatarType = ""; - avatarHash = ""; - avaChanged = true; - } - } - } - - if (avaChanged) { - QMap change; - if (avatarType.size() > 0) { - presence.setPhotoHash(avatarHash.toUtf8()); - presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); - change.insert("avatarPath", path + "avatar." + avatarType); - } else { - presence.setPhotoHash(""); - presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto); - change.insert("avatarPath", ""); - } - client.setClientPresence(presence); - emit changed(change); - } - - ownVCardRequestInProgress = false; - - Shared::VCard vCard; - initializeVCard(vCard, card); - - if (avatarType.size() > 0) { - vCard.setAvatarType(Shared::Avatar::valid); - vCard.setAvatarPath(path + "avatar." + avatarType); - } else { - vCard.setAvatarType(Shared::Avatar::empty); - } - - emit receivedVCard(getLogin() + "@" + getServer(), vCard); -} - -QString Core::Account::getAvatarPath() const -{ - if (avatarType.size() == 0) { - return ""; - } else { - return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/" + "avatar." + avatarType; - } -} - -void Core::Account::requestVCard(const QString& jid) -{ - if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { - qDebug() << "requesting vCard" << jid; - if (jid == getLogin() + "@" + getServer()) { - if (!ownVCardRequestInProgress) { - vm->requestClientVCard(); - ownVCardRequestInProgress = true; - } - } else { - vm->requestVCard(jid); - pendingVCardRequests.insert(jid); - } - } -} - -void Core::Account::uploadVCard(const Shared::VCard& card) -{ - QXmppVCardIq iq; - initializeQXmppVCard(iq, card); - - if (card.getAvatarType() != Shared::Avatar::empty) { - QString newPath = card.getAvatarPath(); - QString oldPath = getAvatarPath(); - QByteArray data; - QString type; - if (newPath != oldPath) { - QFile avatar(newPath); - if (!avatar.open(QFile::ReadOnly)) { - qDebug() << "An attempt to upload new vCard to account" << name - << "but it wasn't possible to read file" << newPath - << "which was supposed to be new avatar, uploading old avatar"; - if (avatarType.size() > 0) { - QFile oA(oldPath); - if (!oA.open(QFile::ReadOnly)) { - qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar"; - } else { - data = oA.readAll(); - } - } - } else { - data = avatar.readAll(); - } - } else { - if (avatarType.size() > 0) { - QFile oA(oldPath); - if (!oA.open(QFile::ReadOnly)) { - qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar"; - } else { - data = oA.readAll(); - } - } - } - - if (data.size() > 0) { - QMimeDatabase db; - type = db.mimeTypeForData(data).name(); - iq.setPhoto(data); - iq.setPhotoType(type); - } - } - - vm->setClientVCard(iq); - onOwnVCardReceived(iq); -} - -void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) -{ - for (QXmppDiscoveryIq::Item item : items.items()) { - if (item.jid() != getServer()) { - dm->requestInfo(item.jid()); - } - } -} - -void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) -{ - qDebug() << "Discovery info received for account" << name; - if (info.from() == getServer()) { - if (info.features().contains("urn:xmpp:carbons:2")) { - qDebug() << "Enabling carbon copies for account" << name; - cm->setCarbonsEnabled(true); - } - } -} - -void Core::Account::handleDisconnection() -{ - cm->setCarbonsEnabled(false); - rh->handleOffline(); - archiveQueries.clear(); - pendingVCardRequests.clear(); - Shared::VCard vCard; //just to show, that there is now more pending request - for (const QString& jid : pendingVCardRequests) { - emit receivedVCard(jid, vCard); //need to show it better in the future, like with an error - } - pendingVCardRequests.clear(); - ownVCardRequestInProgress = false; -} - -void Core::Account::onContactHistoryResponse(const std::list& list, bool last) -{ - RosterItem* contact = static_cast(sender()); - - qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements"; - if (last) { - qDebug() << "The response contains the first accounted message"; - } - emit responseArchive(contact->jid, list, last); -} - -void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data){ - mh->requestChangeMessage(jid, messageId, data);} - -void Core::Account::resendMessage(const QString& jid, const QString& id) { - mh->resendMessage(jid, id);} - -void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) { - mh->sendMessage(data, false, originalId);} - diff --git a/core/account.h b/core/account.h index 664b547..c8e6e41 100644 --- a/core/account.h +++ b/core/account.h @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -50,6 +49,7 @@ #include "handlers/messagehandler.h" #include "handlers/rosterhandler.h" +#include "handlers/vcardhandler.h" namespace Core { @@ -59,6 +59,7 @@ class Account : public QObject Q_OBJECT friend class MessageHandler; friend class RosterHandler; + friend class VCardHandler; public: Account( const QString& p_login, @@ -76,6 +77,8 @@ public: QString getPassword() const; QString getResource() const; QString getAvatarPath() const; + QString getBareJid() const; + QString getFullJid() const; Shared::Availability getAvailability() const; Shared::AccountPassword getPasswordType() const; @@ -86,7 +89,6 @@ public: void setResource(const QString& p_resource); void setAvailability(Shared::Availability avail); void setPasswordType(Shared::AccountPassword pt); - QString getFullJid() const; void sendMessage(const Shared::Message& data); void requestArchive(const QString& jid, int count, const QString& before); void subscribeToContact(const QString& jid, const QString& reason); @@ -157,16 +159,13 @@ private: bool reconnectScheduled; QTimer* reconnectTimer; - std::set pendingVCardRequests; - - QString avatarHash; - QString avatarType; - bool ownVCardRequestInProgress; NetworkAccess* network; Shared::AccountPassword passwordType; + bool pepSupport; MessageHandler* mh; RosterHandler* rh; + VCardHandler* vh; private slots: void onClientStateChange(QXmppClient::State state); @@ -179,9 +178,6 @@ private slots: void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete); void onMamLog(QXmppLogger::MessageType type, const QString &msg); - - void onVCardReceived(const QXmppVCardIq& card); - void onOwnVCardReceived(const QXmppVCardIq& card); void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items); void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info); @@ -191,9 +187,6 @@ private: void handleDisconnection(); void onReconnectTimer(); }; - -void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); -void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard); } diff --git a/core/adapterFuctions.cpp b/core/adapterfunctions.cpp similarity index 98% rename from core/adapterFuctions.cpp rename to core/adapterfunctions.cpp index 3d84dfb..eec5a9f 100644 --- a/core/adapterFuctions.cpp +++ b/core/adapterfunctions.cpp @@ -1,5 +1,5 @@ /* - * Squawk messenger. + * Squawk messenger. * Copyright (C) 2019 Yury Gubich * * This program is free software: you can redistribute it and/or modify @@ -15,10 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef CORE_ADAPTER_FUNCTIONS_H -#define CORE_ADAPTER_FUNCTIONS_H -#include "account.h" +#include "adapterfunctions.h" void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card) { @@ -271,5 +269,3 @@ void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) { iq.setEmails(emails); iq.setPhones(phs); } - -#endif // CORE_ADAPTER_FUNCTIONS_H diff --git a/core/adapterfunctions.h b/core/adapterfunctions.h new file mode 100644 index 0000000..6e50a75 --- /dev/null +++ b/core/adapterfunctions.h @@ -0,0 +1,32 @@ +/* + * 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_ADAPTER_FUNCTIONS_H +#define CORE_ADAPTER_FUNCTIONS_H + +#include +#include + +namespace Core { + +void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); +void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard); + +} + + +#endif // CORE_ADAPTER_FUNCTIONS_H diff --git a/core/handlers/CMakeLists.txt b/core/handlers/CMakeLists.txt index 6da2ef3..fb67953 100644 --- a/core/handlers/CMakeLists.txt +++ b/core/handlers/CMakeLists.txt @@ -3,4 +3,6 @@ target_sources(squawk PRIVATE messagehandler.h rosterhandler.cpp rosterhandler.h + vcardhandler.cpp + vcardhandler.h ) diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 0555873..b6d32b9 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -176,7 +176,7 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp target.setForwarded(forwarded); if (guessing) { - if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) { + if (target.getFromJid() == acc->getBareJid()) { outgoing = true; } else { outgoing = false; diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index ce5f1b7..6a233d6 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -26,7 +26,8 @@ Core::RosterHandler::RosterHandler(Core::Account* account): conferences(), groups(), queuedContacts(), - outOfRosterContacts() + outOfRosterContacts(), + pepSupport(false) { connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived); connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded); @@ -51,8 +52,7 @@ Core::RosterHandler::~RosterHandler() void Core::RosterHandler::onRosterReceived() { - acc->vm->requestClientVCard(); //TODO need to make sure server actually supports vCards - acc->ownVCardRequestInProgress = true; + acc->requestVCard(acc->getBareJid()); //TODO need to make sure server actually supports vCards QStringList bj = acc->rm->getRosterBareJids(); for (int i = 0; i < bj.size(); ++i) { @@ -588,4 +588,13 @@ void Core::RosterHandler::handleOffline() pair.second->clearArchiveRequests(); pair.second->downgradeDatabaseState(); } + setPepSupport(false); +} + + +void Core::RosterHandler::setPepSupport(bool support) +{ + if (pepSupport != support) { + pepSupport = support; + } } diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index b1dfc45..02bbc98 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -64,6 +64,7 @@ public: void storeConferences(); void clearConferences(); + void setPepSupport(bool support); private slots: void onRosterReceived(); @@ -107,6 +108,7 @@ private: std::map> groups; std::map queuedContacts; std::set outOfRosterContacts; + bool pepSupport; }; } diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp new file mode 100644 index 0000000..2a8d65c --- /dev/null +++ b/core/handlers/vcardhandler.cpp @@ -0,0 +1,312 @@ +// 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 "vcardhandler.h" +#include "core/account.h" + +Core::VCardHandler::VCardHandler(Account* account): + QObject(), + acc(account), + ownVCardRequestInProgress(false), + pendingVCardRequests(), + avatarHash(), + avatarType() +{ + connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived); + //for some reason it doesn't work, launching from common handler + //connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived); + + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + path += "/" + acc->name; + QDir dir(path); + + if (!dir.exists()) { + bool res = dir.mkpath(path); + if (!res) { + qDebug() << "Couldn't create a cache directory for account" << acc->name; + throw 22; + } + } + + QFile* avatar = new QFile(path + "/avatar.png"); + QString type = "png"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.jpg"); + type = "jpg"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.jpeg"); + type = "jpeg"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.gif"); + type = "gif"; + } + } + } + + if (avatar->exists()) { + if (avatar->open(QFile::ReadOnly)) { + QCryptographicHash sha1(QCryptographicHash::Sha1); + sha1.addData(avatar); + avatarHash = sha1.result(); + avatarType = type; + } + } + if (avatarType.size() != 0) { + acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); + acc->presence.setPhotoHash(avatarHash.toUtf8()); + } else { + acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady); + } +} + +Core::VCardHandler::~VCardHandler() +{ + +} + +void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) +{ + QString id = card.from(); + QStringList comps = id.split("/"); + QString jid = comps.front().toLower(); + QString resource(""); + if (comps.size() > 1) { + resource = comps.back(); + } + pendingVCardRequests.erase(id); + RosterItem* item = acc->rh->getRosterItem(jid); + + if (item == 0) { + if (jid == acc->getBareJid()) { + onOwnVCardReceived(card); + } else { + qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; + } + return; + } + + Shared::VCard vCard = item->handleResponseVCard(card, resource); + + emit acc->receivedVCard(jid, vCard); +} + +void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) +{ + QByteArray ava = card.photo(); + bool avaChanged = false; + QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/"; + if (ava.size() > 0) { + QCryptographicHash sha1(QCryptographicHash::Sha1); + sha1.addData(ava); + QString newHash(sha1.result()); + QMimeDatabase db; + QMimeType newType = db.mimeTypeForData(ava); + if (avatarType.size() > 0) { + if (avatarHash != newHash) { + QString oldPath = path + "avatar." + avatarType; + QFile oldAvatar(oldPath); + bool oldToRemove = false; + if (oldAvatar.exists()) { + if (oldAvatar.rename(oldPath + ".bak")) { + oldToRemove = true; + } else { + qDebug() << "Received new avatar for account" << acc->name << "but can't get rid of the old one, doing nothing"; + } + } + QFile newAvatar(path + "avatar." + newType.preferredSuffix()); + if (newAvatar.open(QFile::WriteOnly)) { + newAvatar.write(ava); + newAvatar.close(); + avatarHash = newHash; + avatarType = newType.preferredSuffix(); + avaChanged = true; + } else { + qDebug() << "Received new avatar for account" << acc->name << "but can't save it"; + if (oldToRemove) { + qDebug() << "rolling back to the old avatar"; + if (!oldAvatar.rename(oldPath)) { + qDebug() << "Couldn't roll back to the old avatar in account" << acc->name; + } + } + } + } + } else { + QFile newAvatar(path + "avatar." + newType.preferredSuffix()); + if (newAvatar.open(QFile::WriteOnly)) { + newAvatar.write(ava); + newAvatar.close(); + avatarHash = newHash; + avatarType = newType.preferredSuffix(); + avaChanged = true; + } else { + qDebug() << "Received new avatar for account" << acc->name << "but can't save it"; + } + } + } else { + if (avatarType.size() > 0) { + QFile oldAvatar(path + "avatar." + avatarType); + if (!oldAvatar.remove()) { + qDebug() << "Received vCard for account" << acc->name << "without avatar, but can't get rid of the file, doing nothing"; + } else { + avatarType = ""; + avatarHash = ""; + avaChanged = true; + } + } + } + + if (avaChanged) { + QMap change; + if (avatarType.size() > 0) { + acc->presence.setPhotoHash(avatarHash.toUtf8()); + acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); + change.insert("avatarPath", path + "avatar." + avatarType); + } else { + acc->presence.setPhotoHash(""); + acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto); + change.insert("avatarPath", ""); + } + acc->client.setClientPresence(acc->presence); + emit acc->changed(change); + } + + ownVCardRequestInProgress = false; + + Shared::VCard vCard; + initializeVCard(vCard, card); + + if (avatarType.size() > 0) { + vCard.setAvatarType(Shared::Avatar::valid); + vCard.setAvatarPath(path + "avatar." + avatarType); + } else { + vCard.setAvatarType(Shared::Avatar::empty); + } + + emit acc->receivedVCard(acc->getBareJid(), vCard); +} + +void Core::VCardHandler::handleOffline() +{ + pendingVCardRequests.clear(); + Shared::VCard vCard; //just to show, that there is now more pending request + for (const QString& jid : pendingVCardRequests) { + emit acc->receivedVCard(jid, vCard); //need to show it better in the future, like with an error + } + pendingVCardRequests.clear(); + ownVCardRequestInProgress = false; +} + +void Core::VCardHandler::requestVCard(const QString& jid) +{ + if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { + qDebug() << "requesting vCard" << jid; + if (jid == acc->getBareJid()) { + if (!ownVCardRequestInProgress) { + acc->vm->requestClientVCard(); + ownVCardRequestInProgress = true; + } + } else { + acc->vm->requestVCard(jid); + pendingVCardRequests.insert(jid); + } + } +} + +void Core::VCardHandler::handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence) +{ + if (!ownVCardRequestInProgress) { + switch (p_presence.vCardUpdateType()) { + case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo + break; + case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here + break; + case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any + if (avatarType.size() > 0) { + acc->vm->requestClientVCard(); + ownVCardRequestInProgress = true; + } + break; + case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load + if (avatarHash != p_presence.photoHash()) { + acc->vm->requestClientVCard(); + ownVCardRequestInProgress = true; + } + break; + } + } +} + +void Core::VCardHandler::uploadVCard(const Shared::VCard& card) +{ + QXmppVCardIq iq; + initializeQXmppVCard(iq, card); + + if (card.getAvatarType() != Shared::Avatar::empty) { + QString newPath = card.getAvatarPath(); + QString oldPath = getAvatarPath(); + QByteArray data; + QString type; + if (newPath != oldPath) { + QFile avatar(newPath); + if (!avatar.open(QFile::ReadOnly)) { + qDebug() << "An attempt to upload new vCard to account" << acc->name + << "but it wasn't possible to read file" << newPath + << "which was supposed to be new avatar, uploading old avatar"; + if (avatarType.size() > 0) { + QFile oA(oldPath); + if (!oA.open(QFile::ReadOnly)) { + qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar"; + } else { + data = oA.readAll(); + } + } + } else { + data = avatar.readAll(); + } + } else { + if (avatarType.size() > 0) { + QFile oA(oldPath); + if (!oA.open(QFile::ReadOnly)) { + qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar"; + } else { + data = oA.readAll(); + } + } + } + + if (data.size() > 0) { + QMimeDatabase db; + type = db.mimeTypeForData(data).name(); + iq.setPhoto(data); + iq.setPhotoType(type); + } + } + + acc->vm->setClientVCard(iq); + onOwnVCardReceived(iq); +} + +QString Core::VCardHandler::getAvatarPath() const +{ + if (avatarType.size() == 0) { + return ""; + } else { + return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType; + } +} diff --git a/core/handlers/vcardhandler.h b/core/handlers/vcardhandler.h new file mode 100644 index 0000000..4febb69 --- /dev/null +++ b/core/handlers/vcardhandler.h @@ -0,0 +1,65 @@ +// 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_VCARDHANDLER_H +#define CORE_VCARDHANDLER_H + +#include + +#include +#include + +#include + +#include +#include + +/** + * @todo write docs + */ + +namespace Core { + +class Account; + +class VCardHandler : public QObject +{ + Q_OBJECT +public: + VCardHandler(Account* account); + ~VCardHandler(); + + void handleOffline(); + void requestVCard(const QString& jid); + void handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence); + void uploadVCard(const Shared::VCard& card); + QString getAvatarPath() const; + +private slots: + void onVCardReceived(const QXmppVCardIq& card); + void onOwnVCardReceived(const QXmppVCardIq& card); + +private: + Account* acc; + + bool ownVCardRequestInProgress; + std::set pendingVCardRequests; + QString avatarHash; + QString avatarType; +}; +} + +#endif // CORE_VCARDHANDLER_H diff --git a/core/rosteritem.h b/core/rosteritem.h index 237a46a..d422e3f 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -35,6 +35,7 @@ #include "shared/message.h" #include "shared/vcard.h" #include "archive.h" +#include "adapterfunctions.h" namespace Core {