From 9ca4aa29d466905cf453ad137398518c968f1c4c Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 28 Apr 2020 23:35:52 +0300 Subject: [PATCH] started account refactoring --- core/CMakeLists.txt | 1 + core/account.cpp | 383 +++--------------------------- core/account.h | 24 +- core/handlers/messagehandler.cpp | 390 +++++++++++++++++++++++++++++++ core/handlers/messagehandler.h | 76 ++++++ 5 files changed, 501 insertions(+), 373 deletions(-) create mode 100644 core/handlers/messagehandler.cpp create mode 100644 core/handlers/messagehandler.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index c9c573b..64319c2 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -18,6 +18,7 @@ set(squawkCORE_SRC storage.cpp networkaccess.cpp adapterFuctions.cpp + handlers/messagehandler.cpp ) add_subdirectory(passwordStorageEngines) diff --git a/core/account.cpp b/core/account.cpp index 90c5b42..c9e08eb 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -53,19 +53,19 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& avatarType(), ownVCardRequestInProgress(false), network(p_net), - pendingStateMessages(), - passwordType(Shared::AccountPassword::plain) + passwordType(Shared::AccountPassword::plain), + mh(new MessageHandler(this)) { config.setUser(p_login); config.setDomain(p_server); config.setPassword(p_password); config.setAutoAcceptSubscriptions(true); - config.setAutoReconnectionEnabled(false); + //config.setAutoReconnectionEnabled(false); QObject::connect(&client, &QXmppClient::connected, this, &Account::onClientConnected); QObject::connect(&client, &QXmppClient::disconnected, this, &Account::onClientDisconnected); QObject::connect(&client, &QXmppClient::presenceReceived, this, &Account::onPresenceReceived); - QObject::connect(&client, &QXmppClient::messageReceived, this, &Account::onMessageReceived); + QObject::connect(&client, &QXmppClient::messageReceived, mh, &MessageHandler::onMessageReceived); QObject::connect(&client, &QXmppClient::error, this, &Account::onClientError); QObject::connect(rm, &QXmppRosterManager::rosterReceived, this, &Account::onRosterReceived); @@ -76,8 +76,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(cm); - QObject::connect(cm, &QXmppCarbonManager::messageReceived, this, &Account::onCarbonMessageReceived); - QObject::connect(cm, &QXmppCarbonManager::messageSent, this, &Account::onCarbonMessageSent); + QObject::connect(cm, &QXmppCarbonManager::messageReceived, mh, &MessageHandler::onCarbonMessageReceived); + QObject::connect(cm, &QXmppCarbonManager::messageSent, mh, &MessageHandler::onCarbonMessageSent); client.addExtension(am); @@ -95,17 +95,17 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& //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, this, &Account::onUploadSlotReceived); - QObject::connect(um, &QXmppUploadRequestManager::requestFailed, this, &Account::onUploadSlotRequestFailed); + QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived); + QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed); QObject::connect(dm, &QXmppDiscoveryManager::itemsReceived, this, &Account::onDiscoveryItemsReceived); QObject::connect(dm, &QXmppDiscoveryManager::infoReceived, this, &Account::onDiscoveryInfoReceived); - QObject::connect(network, &NetworkAccess::uploadFileComplete, this, &Account::onFileUploaded); - QObject::connect(network, &NetworkAccess::uploadFileError, this, &Account::onFileUploadError); + QObject::connect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onFileUploaded); + QObject::connect(network, &NetworkAccess::uploadFileError, mh, &MessageHandler::onFileUploadError); client.addExtension(rcpm); - QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, this, &Account::onReceiptReceived); + QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived); QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); @@ -156,8 +156,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& Account::~Account() { - QObject::disconnect(network, &NetworkAccess::uploadFileComplete, this, &Account::onFileUploaded); - QObject::disconnect(network, &NetworkAccess::uploadFileError, this, &Account::onFileUploadError); + QObject::disconnect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onFileUploaded); + QObject::disconnect(network, &NetworkAccess::uploadFileError, mh, &MessageHandler::onFileUploadError); for (std::map::const_iterator itr = contacts.begin(), end = contacts.end(); itr != end; ++itr) { delete itr->second; @@ -566,290 +566,19 @@ void Core::Account::setResource(const QString& p_resource) config.setResource(p_resource); } -void Core::Account::onMessageReceived(const QXmppMessage& msg) -{ - bool handled = false; - switch (msg.type()) { - case QXmppMessage::Normal: - qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping"; - break; - case QXmppMessage::Chat: - handled = handleChatMessage(msg); - break; - case QXmppMessage::GroupChat: - handled = handleGroupMessage(msg); - break; - case QXmppMessage::Error: { - QString id = msg.id(); - std::map::const_iterator itr = pendingStateMessages.find(id); - if (itr != pendingStateMessages.end()) { - QString jid = itr->second; - RosterItem* cnt = getRosterItem(jid); - QMap cData = { - {"state", static_cast(Shared::Message::State::error)}, - {"errorText", msg.error().text()} - }; - if (cnt != 0) { - cnt->changeMessage(id, cData); - } - ; - emit changeMessage(jid, id, cData); - pendingStateMessages.erase(itr); - handled = true; - } else { - qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping"; - } - } - break; - case QXmppMessage::Headline: - qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping"; - break; - } - if (!handled) { - logMessage(msg); - } -} - -void Core::Account::logMessage(const QXmppMessage& msg, const QString& reason) -{ - qDebug() << reason; - qDebug() << "- from: " << msg.from(); - qDebug() << "- to: " << msg.to(); - qDebug() << "- body: " << msg.body(); - qDebug() << "- type: " << msg.type(); - qDebug() << "- state: " << msg.state(); - qDebug() << "- stamp: " << msg.stamp(); - qDebug() << "- id: " << msg.id(); - qDebug() << "- outOfBandUrl: " << msg.outOfBandUrl(); - qDebug() << "- isAttentionRequested: " << msg.isAttentionRequested(); - qDebug() << "- isReceiptRequested: " << msg.isReceiptRequested(); - qDebug() << "- receiptId: " << msg.receiptId(); - qDebug() << "- subject: " << msg.subject(); - qDebug() << "- thread: " << msg.thread(); - qDebug() << "- isMarkable: " << msg.isMarkable(); - qDebug() << "=============================="; -} - - QString Core::Account::getFullJid() const { return getLogin() + "@" + getServer() + "/" + getResource(); } -void Core::Account::sendMessage(Shared::Message data) +void Core::Account::sendMessage(const Shared::Message& data) { - QString jid = data.getPenPalJid(); - QString id = data.getId(); - RosterItem* ri = getRosterItem(jid); - if (state == Shared::ConnectionState::connected) { - QXmppMessage msg(getFullJid(), data.getTo(), data.getBody(), data.getThread()); - msg.setId(id); - msg.setType(static_cast(data.getType())); //it is safe here, my type is compatible - msg.setOutOfBandUrl(data.getOutOfBandUrl()); - msg.setReceiptRequested(true); - - bool sent = client.sendPacket(msg); - - if (sent) { - data.setState(Shared::Message::State::sent); - } else { - data.setState(Shared::Message::State::error); - data.setErrorText("Couldn't send message via QXMPP library check out logs"); - } - - if (ri != 0) { - ri->appendMessageToArchive(data); - if (sent) { - pendingStateMessages.insert(std::make_pair(id, jid)); - } - } - - } else { - data.setState(Shared::Message::State::error); - data.setErrorText("You are is offline or reconnecting"); - } - - emit changeMessage(jid, id, { - {"state", static_cast(data.getState())}, - {"errorText", data.getErrorText()} - }); + mh->sendMessage(data); } void Core::Account::sendMessage(const Shared::Message& data, const QString& path) { - if (state == Shared::ConnectionState::connected) { - QString url = network->getFileRemoteUrl(path); - if (url.size() != 0) { - sendMessageWithLocalUploadedFile(data, url); - } else { - if (network->isUploading(path, data.getId())) { - pendingMessages.emplace(data.getId(), data); - } else { - if (um->serviceFound()) { - QFileInfo file(path); - if (file.exists() && file.isReadable()) { - uploadingSlotsQueue.emplace_back(path, data); - if (uploadingSlotsQueue.size() == 1) { - um->requestUploadSlot(file); - } - } else { - emit onFileUploadError(data.getId(), "Uploading file no longer exists or your system user has no permission to read it"); - qDebug() << "Requested upload slot in account" << name << "for file" << path << "but the file doesn't exist or is not readable"; - } - } else { - emit onFileUploadError(data.getId(), "Your server doesn't support file upload service, or it's prohibited for your account"); - qDebug() << "Requested upload slot in account" << name << "for file" << path << "but upload manager didn't discover any upload services"; - } - } - } - } else { - emit onFileUploadError(data.getId(), "Account is offline or reconnecting"); - qDebug() << "An attempt to send message with not connected account " << name << ", skipping"; - } -} - -void Core::Account::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url) -{ - msg.setOutOfBandUrl(url); - if (msg.getBody().size() == 0) { - msg.setBody(url); - } - sendMessage(msg); - //TODO removal/progress update -} - - -void Core::Account::onCarbonMessageReceived(const QXmppMessage& msg) -{ - handleChatMessage(msg, false, true); -} - -void Core::Account::onCarbonMessageSent(const QXmppMessage& msg) -{ - handleChatMessage(msg, true, true); -} - -bool Core::Account::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) -{ - const QString& body(msg.body()); - if (body.size() != 0) { - Shared::Message sMsg(Shared::Message::chat); - initializeMessage(sMsg, msg, outgoing, forwarded, guessing); - QString jid = sMsg.getPenPalJid(); - std::map::const_iterator itr = contacts.find(jid); - Contact* cnt; - if (itr != contacts.end()) { - cnt = itr->second; - } else { - cnt = new Contact(jid, name); - contacts.insert(std::make_pair(jid, cnt)); - outOfRosterContacts.insert(jid); - cnt->setSubscriptionState(Shared::SubscriptionState::unknown); - emit addContact(jid, "", QMap({ - {"state", QVariant::fromValue(Shared::SubscriptionState::unknown)} - })); - handleNewContact(cnt); - } - if (outgoing) { - if (forwarded) { - sMsg.setState(Shared::Message::State::sent); - } - } else { - sMsg.setState(Shared::Message::State::delivered); - } - QString oId = msg.replaceId(); - if (oId.size() > 0) { - QMap cData = { - {"body", sMsg.getBody()}, - {"stamp", sMsg.getTime()} - }; - cnt->correctMessageInArchive(oId, sMsg); - emit changeMessage(jid, oId, cData); - } else { - cnt->appendMessageToArchive(sMsg); - emit message(sMsg); - } - - return true; - } - return false; -} - -bool Core::Account::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) -{ - const QString& body(msg.body()); - if (body.size() != 0) { - const QString& id(msg.id()); - Shared::Message sMsg(Shared::Message::groupChat); - initializeMessage(sMsg, msg, outgoing, forwarded, guessing); - QString jid = sMsg.getPenPalJid(); - std::map::const_iterator itr = conferences.find(jid); - Conference* cnt; - if (itr != conferences.end()) { - cnt = itr->second; - } else { - return false; - } - - std::map::const_iterator pItr = pendingStateMessages.find(id); - if (pItr != pendingStateMessages.end()) { - QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; - cnt->changeMessage(id, cData); - pendingStateMessages.erase(pItr); - emit changeMessage(jid, id, cData); - } else { - QString oId = msg.replaceId(); - if (oId.size() > 0) { - QMap cData = { - {"body", sMsg.getBody()}, - {"stamp", sMsg.getTime()} - }; - cnt->correctMessageInArchive(oId, sMsg); - emit changeMessage(jid, oId, cData); - } else { - cnt->appendMessageToArchive(sMsg); - QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60); - if (sMsg.getTime() > minAgo) { //otherwise it's considered a delayed delivery, most probably MUC history receipt - emit message(sMsg); - } else { - //qDebug() << "Delayed delivery: "; - } - } - } - - return true; - } - return false; -} - - -void Core::Account::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const -{ - const QDateTime& time(source.stamp()); - QString id = source.id(); - if (id.size() == 0) { - target.generateRandomId(); - } else { - target.setId(id); - } - target.setFrom(source.from()); - target.setTo(source.to()); - target.setBody(source.body()); - target.setForwarded(forwarded); - target.setOutOfBandUrl(source.outOfBandUrl()); - if (guessing) { - if (target.getFromJid() == getLogin() + "@" + getServer()) { - outgoing = true; - } else { - outgoing = false; - } - } - target.setOutgoing(outgoing); - if (time.isValid()) { - target.setTime(time); - } else { - target.setCurrentTime(); - } + mh->sendMessage(data, path); } void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg) @@ -860,8 +589,16 @@ void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMess QString jid = itr->second; RosterItem* item = getRosterItem(jid); + qDebug() << "archive for" << jid; + qDebug() << "id:" << msg.id(); + qDebug() << "oid:" << msg.originId(); + qDebug() << "sid:" << msg.stanzaId(); + qDebug() << "rid:" << msg.replaceId(); + qDebug() << "============================"; + + Shared::Message sMsg(static_cast(msg.type())); - initializeMessage(sMsg, msg, false, true, true); + mh->initializeMessage(sMsg, msg, false, true, true); sMsg.setState(Shared::Message::State::sent); QString oId = msg.replaceId(); @@ -892,7 +629,6 @@ Core::RosterItem * Core::Account::getRosterItem(const QString& jid) return item; } - void Core::Account::requestArchive(const QString& jid, int count, const QString& before) { qDebug() << "An archive request for " << jid << ", before " << before; @@ -1671,57 +1407,6 @@ void Core::Account::uploadVCard(const Shared::VCard& card) onOwnVCardReceived(iq); } -void Core::Account::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot) -{ - if (uploadingSlotsQueue.size() == 0) { - qDebug() << "HTTP Upload manager of account" << name << "reports about success requesting upload slot, but none was requested"; - } else { - const std::pair& pair = uploadingSlotsQueue.front(); - const QString& mId = pair.second.getId(); - network->uploadFile(mId, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders()); - pendingMessages.emplace(mId, pair.second); - uploadingSlotsQueue.pop_front(); - - if (uploadingSlotsQueue.size() > 0) { - um->requestUploadSlot(uploadingSlotsQueue.front().first); - } - } -} - -void Core::Account::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request) -{ - if (uploadingSlotsQueue.size() == 0) { - qDebug() << "HTTP Upload manager of account" << name << "reports about an error requesting upload slot, but none was requested"; - qDebug() << request.error().text(); - } else { - const std::pair& pair = uploadingSlotsQueue.front(); - qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << name << ":" << request.error().text(); - emit uploadFileError(pair.second.getId(), "Error requesting slot to upload file: " + request.error().text()); - - if (uploadingSlotsQueue.size() > 0) { - um->requestUploadSlot(uploadingSlotsQueue.front().first); - } - uploadingSlotsQueue.pop_front(); - } -} - -void Core::Account::onFileUploaded(const QString& messageId, const QString& url) -{ - std::map::const_iterator itr = pendingMessages.find(messageId); - if (itr != pendingMessages.end()) { - sendMessageWithLocalUploadedFile(itr->second, url); - pendingMessages.erase(itr); - } -} - -void Core::Account::onFileUploadError(const QString& messageId, const QString& errMsg) -{ - std::map::const_iterator itr = pendingMessages.find(messageId); - if (itr != pendingMessages.end()) { - pendingMessages.erase(itr); - } -} - void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) { for (QXmppDiscoveryIq::Item item : items.items()) { @@ -1740,26 +1425,12 @@ void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) } } -void Core::Account::onReceiptReceived(const QString& jid, const QString& id) -{ - std::map::const_iterator itr = pendingStateMessages.find(id); - if (itr != pendingStateMessages.end()) { - QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; - RosterItem* ri = getRosterItem(itr->second); - if (ri != 0) { - ri->changeMessage(id, cData); - } - pendingStateMessages.erase(itr); - emit changeMessage(itr->second, id, cData); - } -} - void Core::Account::cancelHistoryRequests() { - for (const std::pair& pair : conferences) { + for (const std::pair& pair : conferences) { pair.second->clearArchiveRequests(); } - for (const std::pair& pair : contacts) { + for (const std::pair& pair : contacts) { pair.second->clearArchiveRequests(); } achiveQueries.clear(); diff --git a/core/account.h b/core/account.h index b706916..b0d5ee0 100644 --- a/core/account.h +++ b/core/account.h @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -48,12 +47,15 @@ #include "conference.h" #include "networkaccess.h" +#include "handlers/messagehandler.h" + namespace Core { class Account : public QObject { Q_OBJECT + friend class MessageHandler; public: Account( const QString& p_login, @@ -86,7 +88,7 @@ public: void setAvailability(Shared::Availability avail); void setPasswordType(Shared::AccountPassword pt); QString getFullJid() const; - void sendMessage(Shared::Message data); + void sendMessage(const Shared::Message& data); void sendMessage(const Shared::Message& data, const QString& path); void requestArchive(const QString& jid, int count, const QString& before); void setReconnectTimes(unsigned int times); @@ -165,9 +167,10 @@ private: QString avatarType; bool ownVCardRequestInProgress; NetworkAccess* network; - std::map pendingStateMessages; Shared::AccountPassword passwordType; + MessageHandler* mh; + private slots: void onClientConnected(); void onClientDisconnected(); @@ -180,10 +183,6 @@ private slots: void onRosterPresenceChanged(const QString& bareJid, const QString& resource); void onPresenceReceived(const QXmppPresence& presence); - void onMessageReceived(const QXmppMessage& message); - - void onCarbonMessageReceived(const QXmppMessage& message); - void onCarbonMessageSent(const QXmppMessage& message); void onMamMessageReceived(const QString& bareJid, const QXmppMessage& message); void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete); @@ -211,15 +210,9 @@ private slots: void onVCardReceived(const QXmppVCardIq& card); void onOwnVCardReceived(const QXmppVCardIq& card); - - void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot); - void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request); - void onFileUploaded(const QString& messageId, const QString& url); - void onFileUploadError(const QString& messageId, const QString& errMsg); + void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items); void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info); - - void onReceiptReceived(const QString& jid, const QString &id); private: void addedAccount(const QString &bareJid); @@ -231,13 +224,10 @@ private: void addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin); void addToGroup(const QString& jid, const QString& group); void removeFromGroup(const QString& jid, const QString& group); - void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const; Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs) const; - void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: "); void storeConferences(); void clearConferences(); void cancelHistoryRequests(); - void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url); RosterItem* getRosterItem(const QString& jid); }; diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp new file mode 100644 index 0000000..aa02e78 --- /dev/null +++ b/core/handlers/messagehandler.cpp @@ -0,0 +1,390 @@ +/* + * 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 "messagehandler.h" +#include "core/account.h" + +Core::MessageHandler::MessageHandler(Core::Account* account): + QObject(), + acc(account), + pendingStateMessages(), + pendingMessages(), + uploadingSlotsQueue() +{ +} + +void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) +{ + bool handled = false; + switch (msg.type()) { + case QXmppMessage::Normal: + qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping"; + break; + case QXmppMessage::Chat: + handled = handleChatMessage(msg); + break; + case QXmppMessage::GroupChat: + handled = handleGroupMessage(msg); + break; + case QXmppMessage::Error: { + QString id = msg.id(); + std::map::const_iterator itr = pendingStateMessages.find(id); + if (itr != pendingStateMessages.end()) { + QString jid = itr->second; + RosterItem* cnt = acc->getRosterItem(jid); + QMap cData = { + {"state", static_cast(Shared::Message::State::error)}, + {"errorText", msg.error().text()} + }; + if (cnt != 0) { + cnt->changeMessage(id, cData); + } + ; + emit acc->changeMessage(jid, id, cData); + pendingStateMessages.erase(itr); + handled = true; + } else { + qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping"; + } + } + break; + case QXmppMessage::Headline: + qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping"; + break; + } + if (!handled) { + logMessage(msg); + } +} + +bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) +{ + const QString& body(msg.body()); + if (body.size() != 0) { + Shared::Message sMsg(Shared::Message::chat); + initializeMessage(sMsg, msg, outgoing, forwarded, guessing); + QString jid = sMsg.getPenPalJid(); + std::map::const_iterator itr = acc->contacts.find(jid); + Contact* cnt; + if (itr != acc->contacts.end()) { + cnt = itr->second; + } else { + cnt = new Contact(jid, acc->name); + acc->contacts.insert(std::make_pair(jid, cnt)); + acc->outOfRosterContacts.insert(jid); + cnt->setSubscriptionState(Shared::SubscriptionState::unknown); + emit acc->addContact(jid, "", QMap({ + {"state", QVariant::fromValue(Shared::SubscriptionState::unknown)} + })); + acc->handleNewContact(cnt); + } + if (outgoing) { + if (forwarded) { + sMsg.setState(Shared::Message::State::sent); + } + } else { + sMsg.setState(Shared::Message::State::delivered); + } + QString oId = msg.replaceId(); + if (oId.size() > 0) { + QMap cData = { + {"body", sMsg.getBody()}, + {"stamp", sMsg.getTime()} + }; + cnt->correctMessageInArchive(oId, sMsg); + emit acc->changeMessage(jid, oId, cData); + } else { + cnt->appendMessageToArchive(sMsg); + emit acc->message(sMsg); + } + + return true; + } + return false; +} + +bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) +{ + const QString& body(msg.body()); + if (body.size() != 0) { + QString id = msg.id(); + + Shared::Message sMsg(Shared::Message::groupChat); + initializeMessage(sMsg, msg, outgoing, forwarded, guessing); + QString jid = sMsg.getPenPalJid(); + std::map::const_iterator itr = acc->conferences.find(jid); + Conference* cnt; + if (itr != acc->conferences.end()) { + cnt = itr->second; + } else { + return false; + } + + std::map::const_iterator pItr = pendingStateMessages.find(id); + if (pItr != pendingStateMessages.end()) { + QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; + cnt->changeMessage(id, cData); + pendingStateMessages.erase(pItr); + emit acc->changeMessage(jid, id, cData); + } else { + QString oId = msg.replaceId(); + if (oId.size() > 0) { + QMap cData = { + {"body", sMsg.getBody()}, + {"stamp", sMsg.getTime()} + }; + cnt->correctMessageInArchive(oId, sMsg); + emit acc->changeMessage(jid, oId, cData); + } else { + cnt->appendMessageToArchive(sMsg); + QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60); + if (sMsg.getTime() > minAgo) { //otherwise it's considered a delayed delivery, most probably MUC history receipt + emit acc->message(sMsg); + } else { + //qDebug() << "Delayed delivery: "; + } + } + } + + return true; + } + return false; +} + + +void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const +{ + const QDateTime& time(source.stamp()); + QString id; +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) + id = source.originId(); + if (id.size() == 0) { + id = source.id(); + } + if (id.size() == 0) { + id = source.stanzaId(); + } +#else + id = source.id(); +#endif + if (id.size() == 0) { + target.generateRandomId(); + } else { + target.setId(id); + } + target.setFrom(source.from()); + target.setTo(source.to()); + target.setBody(source.body()); + target.setForwarded(forwarded); + target.setOutOfBandUrl(source.outOfBandUrl()); + if (guessing) { + if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) { + outgoing = true; + } else { + outgoing = false; + } + } + target.setOutgoing(outgoing); + if (time.isValid()) { + target.setTime(time); + } else { + target.setCurrentTime(); + } +} + +void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason) +{ + qDebug() << reason; + qDebug() << "- from: " << msg.from(); + qDebug() << "- to: " << msg.to(); + qDebug() << "- body: " << msg.body(); + qDebug() << "- type: " << msg.type(); + qDebug() << "- state: " << msg.state(); + qDebug() << "- stamp: " << msg.stamp(); + qDebug() << "- id: " << msg.id(); + qDebug() << "- outOfBandUrl: " << msg.outOfBandUrl(); + qDebug() << "- isAttentionRequested: " << msg.isAttentionRequested(); + qDebug() << "- isReceiptRequested: " << msg.isReceiptRequested(); + qDebug() << "- receiptId: " << msg.receiptId(); + qDebug() << "- subject: " << msg.subject(); + qDebug() << "- thread: " << msg.thread(); + qDebug() << "- isMarkable: " << msg.isMarkable(); + qDebug() << "=============================="; +} + +void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg) +{ + handleChatMessage(msg, false, true); +} + +void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) +{ + handleChatMessage(msg, true, true); +} + +void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id) +{ + std::map::const_iterator itr = pendingStateMessages.find(id); + if (itr != pendingStateMessages.end()) { + QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; + RosterItem* ri = acc->getRosterItem(itr->second); + if (ri != 0) { + ri->changeMessage(id, cData); + } + pendingStateMessages.erase(itr); + emit acc->changeMessage(itr->second, id, cData); + } +} + +void Core::MessageHandler::sendMessage(Shared::Message data) +{ + QString jid = data.getPenPalJid(); + QString id = data.getId(); + RosterItem* ri = acc->getRosterItem(jid); + if (acc->state == Shared::ConnectionState::connected) { + QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread()); + +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) + msg.setOriginId(id); +#endif + msg.setId(id); + msg.setType(static_cast(data.getType())); //it is safe here, my type is compatible + msg.setOutOfBandUrl(data.getOutOfBandUrl()); + msg.setReceiptRequested(true); + + bool sent = acc->client.sendPacket(msg); + + if (sent) { + data.setState(Shared::Message::State::sent); + } else { + data.setState(Shared::Message::State::error); + data.setErrorText("Couldn't send message via QXMPP library check out logs"); + } + + if (ri != 0) { + ri->appendMessageToArchive(data); + if (sent) { + pendingStateMessages.insert(std::make_pair(id, jid)); + } + } + + } else { + data.setState(Shared::Message::State::error); + data.setErrorText("You are is offline or reconnecting"); + } + + emit acc->changeMessage(jid, id, { + {"state", static_cast(data.getState())}, + {"errorText", data.getErrorText()} + }); +} + +void Core::MessageHandler::sendMessage(const Shared::Message& data, const QString& path) +{ + if (acc->state == Shared::ConnectionState::connected) { + QString url = acc->network->getFileRemoteUrl(path); + if (url.size() != 0) { + sendMessageWithLocalUploadedFile(data, url); + } else { + if (acc->network->isUploading(path, data.getId())) { + pendingMessages.emplace(data.getId(), data); + } else { + if (acc->um->serviceFound()) { + QFileInfo file(path); + if (file.exists() && file.isReadable()) { + uploadingSlotsQueue.emplace_back(path, data); + if (uploadingSlotsQueue.size() == 1) { + acc->um->requestUploadSlot(file); + } + } else { + onFileUploadError(data.getId(), "Uploading file no longer exists or your system user has no permission to read it"); + qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable"; + } + } else { + onFileUploadError(data.getId(), "Your server doesn't support file upload service, or it's prohibited for your account"); + qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services"; + } + } + } + } else { + onFileUploadError(data.getId(), "Account is offline or reconnecting"); + qDebug() << "An attempt to send message with not connected account " << acc->name << ", skipping"; + } +} + + +void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot) +{ + if (uploadingSlotsQueue.size() == 0) { + qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested"; + } else { + const std::pair& pair = uploadingSlotsQueue.front(); + const QString& mId = pair.second.getId(); + acc->network->uploadFile(mId, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders()); + pendingMessages.emplace(mId, pair.second); + uploadingSlotsQueue.pop_front(); + + if (uploadingSlotsQueue.size() > 0) { + acc->um->requestUploadSlot(uploadingSlotsQueue.front().first); + } + } +} + +void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request) +{ + if (uploadingSlotsQueue.size() == 0) { + qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested"; + qDebug() << request.error().text(); + } else { + const std::pair& pair = uploadingSlotsQueue.front(); + qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << request.error().text(); + emit acc->uploadFileError(pair.second.getId(), "Error requesting slot to upload file: " + request.error().text()); + + if (uploadingSlotsQueue.size() > 0) { + acc->um->requestUploadSlot(uploadingSlotsQueue.front().first); + } + uploadingSlotsQueue.pop_front(); + } +} + +void Core::MessageHandler::onFileUploaded(const QString& messageId, const QString& url) +{ + std::map::const_iterator itr = pendingMessages.find(messageId); + if (itr != pendingMessages.end()) { + sendMessageWithLocalUploadedFile(itr->second, url); + pendingMessages.erase(itr); + } +} + +void Core::MessageHandler::onFileUploadError(const QString& messageId, const QString& errMsg) +{ + std::map::const_iterator itr = pendingMessages.find(messageId); + if (itr != pendingMessages.end()) { + pendingMessages.erase(itr); + } +} + +void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url) +{ + msg.setOutOfBandUrl(url); + if (msg.getBody().size() == 0) { + msg.setBody(url); + } + sendMessage(msg); + //TODO removal/progress update +} diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h new file mode 100644 index 0000000..54b8331 --- /dev/null +++ b/core/handlers/messagehandler.h @@ -0,0 +1,76 @@ +/* + * 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_MESSAGEHANDLER_H +#define CORE_MESSAGEHANDLER_H + +#include + +#include +#include + +#include +#include + +#include "shared/message.h" + +namespace Core { + +/** + * @todo write docs + */ + +class Account; + +class MessageHandler : public QObject +{ + Q_OBJECT +public: + MessageHandler(Account* account); + +public: + void sendMessage(Shared::Message data); + void sendMessage(const Shared::Message& data, const QString& path); + void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const; + +public slots: + void onMessageReceived(const QXmppMessage& message); + void onCarbonMessageReceived(const QXmppMessage& message); + void onCarbonMessageSent(const QXmppMessage& message); + void onReceiptReceived(const QString& jid, const QString& id); + void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot); + void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request); + void onFileUploaded(const QString& messageId, const QString& url); + void onFileUploadError(const QString& messageId, const QString& errMsg); + +private: + bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false); + bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false); + void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: "); + void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url); + +private: + Account* acc; + std::map pendingStateMessages; + std::map pendingMessages; + std::deque> uploadingSlotsQueue; +}; + +} + +#endif // CORE_MESSAGEHANDLER_H