diff --git a/CMakeLists.txt b/CMakeLists.txt index 378aa51..1151cc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ option(WITH_OMEMO "Build OMEMO support module" ON) # Dependencies ## Qt if (NOT DEFINED QT_VERSION_MAJOR) - find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) + find_package(QT NAMES Qt5 REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) else () find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) endif() diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 2632848..f939b9e 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -71,26 +71,12 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) { case QXmppMessage::GroupChat: handled = handleGroupMessage(msg); break; - case QXmppMessage::Error: { - std::tuple ids = getOriginalPendingMessageId(msg.id()); - if (std::get<0>(ids)) { - QString id = std::get<1>(ids); - QString jid = std::get<2>(ids); - RosterItem* cnt = acc->rh->getRosterItem(jid); - QMap cData = { - {"state", static_cast(Shared::Message::State::error)}, - {"errorText", msg.error().text()} - }; - if (cnt != nullptr) - cnt->changeMessage(id, cData); - - emit acc->changeMessage(jid, id, cData); - handled = true; - } else { + case QXmppMessage::Error: + handled = handlePendingMessageError(msg.id(), msg.error().text()); + if (!handled) qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping"; - } - } - break; + + break; case QXmppMessage::Headline: qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping"; break; @@ -99,6 +85,27 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) { logMessage(msg); } +bool Core::MessageHandler::handlePendingMessageError(const QString& id, const QString& errorText) { + std::tuple ids = getOriginalPendingMessageId(id); + if (std::get<0>(ids)) { + QString id = std::get<1>(ids); + QString jid = std::get<2>(ids); + RosterItem* ri = acc->rh->getRosterItem(jid); + QMap cData = { + {"state", static_cast(Shared::Message::State::error)}, + {"errorText", errorText} + }; + if (ri != nullptr) + ri->changeMessage(id, cData); + + emit acc->changeMessage(jid, id, cData); + return true; + } + + return false; +} + + bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) { Shared::Message sMsg(Shared::Message::chat); @@ -182,9 +189,9 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp QString id; #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) id = source.originId(); - if (id.size() == 0) { + if (id.size() == 0) id = source.id(); - } + target.setStanzaId(source.stanzaId()); qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId(); #else @@ -202,24 +209,19 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp target.setBody(source.body()); target.setForwarded(forwarded); - if (guessing) { - if (target.getFromJid() == acc->getBareJid()) { - outgoing = true; - } else { - outgoing = false; - } - } + if (guessing) + outgoing = target.getFromJid() == acc->getBareJid(); + target.setOutgoing(outgoing); - if (time.isValid()) { + if (time.isValid()) target.setTime(time); - } else { + else target.setCurrentTime(); - } - + QString oob = source.outOfBandUrl(); - if (oob.size() > 0) { + if (oob.size() > 0) target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId)); - } + target.setOutOfBandUrl(oob); } @@ -249,7 +251,7 @@ void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) { } #endif -std::tuple Core::MessageHandler::getOriginalPendingMessageId(const QString& id) { +std::tuple Core::MessageHandler::getOriginalPendingMessageId(const QString& id, bool clear) { std::tuple result({false, "", ""}); std::map::const_iterator itr = pendingStateMessages.find(id); if (itr != pendingStateMessages.end()) { @@ -263,12 +265,14 @@ std::tuple Core::MessageHandler::getOriginalPendingMessa else std::get<1>(result) = itr->first; - pendingCorrectionMessages.erase(itrC); + if (clear) + pendingCorrectionMessages.erase(itrC); } else { std::get<1>(result) = itr->first; } - pendingStateMessages.erase(itr); + if (clear) + pendingStateMessages.erase(itr); } return result; @@ -281,9 +285,9 @@ void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; RosterItem* ri = acc->rh->getRosterItem(std::get<2>(ids)); - if (ri != nullptr) { + if (ri != nullptr) ri->changeMessage(std::get<1>(ids), cData); - } + emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData); } } @@ -302,45 +306,25 @@ void Core::MessageHandler::performSending(Shared::Message data, const QString& o QString id = data.getId(); qDebug() << "Sending message with id:" << id; if (originalId.size() > 0) - qDebug() << "To replace one with id:" << originalId; + qDebug() << "To replace the one with id:" << originalId; RosterItem* ri = acc->rh->getRosterItem(jid); - bool sent = false; if (newMessage && originalId.size() > 0) newMessage = false; QDateTime sendTime = QDateTime::currentDateTimeUtc(); - if (acc->state == Shared::ConnectionState::connected) { - QXmppMessage msg(createPacket(data, sendTime, originalId)); - - 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: internal QXMPP library error, probably need to check out the logs"); - } - - } else { - data.setState(Shared::Message::State::error); - data.setErrorText("You are is offline or reconnecting"); - } + std::pair result = scheduleSending(data, sendTime, originalId); + data.setState(result.first); + data.setErrorText(result.second); QMap changes(getChanges(data, sendTime, newMessage, originalId)); - - QString realId; - if (originalId.size() > 0) - realId = originalId; - else - realId = id; - if (ri != nullptr) { if (newMessage) ri->appendMessageToArchive(data); else - ri->changeMessage(realId, changes); + ri->changeMessage(originalId.isEmpty() ? id : originalId, changes); - if (sent) { + if (data.getState() != Shared::Message::State::error) { pendingStateMessages.insert(std::make_pair(id, jid)); if (originalId.size() > 0) pendingCorrectionMessages.insert(std::make_pair(id, originalId)); @@ -350,9 +334,81 @@ void Core::MessageHandler::performSending(Shared::Message data, const QString& o } } - emit acc->changeMessage(jid, realId, changes); + emit acc->changeMessage(jid, originalId.isEmpty() ? id : originalId, changes); } +std::pair Core::MessageHandler::scheduleSending( + const Shared::Message& message, + const QDateTime& sendTime, + const QString& originalId +) { + if (acc->state != Shared::ConnectionState::connected) + return {Shared::Message::State::error, "You are is offline or reconnecting"}; + + QXmppMessage msg = createPacket(message, sendTime, originalId); + QString id = msg.id(); +#ifdef WITH_OMEMO + if (message.getEncryption() == Shared::EncryptionProtocol::omemo2) { + QXmppTask task = acc->om->encryptMessage(std::move(msg), std::nullopt); + if (task.isFinished()) { + const QXmppE2eeExtension::MessageEncryptResult& res = task.result(); + if (std::holds_alternative>(res)) { + const std::unique_ptr& encrypted = std::get>(res); + bool success = acc->client.sendPacket(*encrypted.get()); + if (success) + return {Shared::Message::State::sent, ""}; + else + return {Shared::Message::State::error, "Error sending successfully encrypted message"}; + } else if (std::holds_alternative(res)) { + const QXmppError& err = std::get(res); + return {Shared::Message::State::error, err.description}; + } else { + return {Shared::Message::State::error, "Unexpected error ecryptng the message"}; + } + } else { + task.then(this, [this, id] (QXmppE2eeExtension::MessageEncryptResult&& result) { + if (std::holds_alternative>(result)) { + const std::unique_ptr& encrypted = std::get>(result); + encrypted->setBody("This message is encrypted with OMEMO 2 but could not be decrypted"); + bool success = acc->client.sendPacket(*encrypted.get()); + if (success) { + std::tuple ids = getOriginalPendingMessageId(id, false); + if (std::get<0>(ids)) { + QString id = std::get<1>(ids); + QString jid = std::get<2>(ids); + RosterItem* ri = acc->rh->getRosterItem(jid); + QMap cData = {{"state", static_cast(Shared::Message::State::sent)}}; + if (ri != nullptr) + ri->changeMessage(id, cData); + + emit acc->changeMessage(jid, id, cData); + } else { + qDebug() << "Encrypted message has been successfully sent, but it couldn't be found to update the sate"; + } + } else { + handlePendingMessageError(id, "Error sending successfully encrypted message"); + } + } else if (std::holds_alternative(result)) { + const QXmppError& err = std::get(result); + handlePendingMessageError(id, err.description); + } else { + handlePendingMessageError(id, "Unexpected error ecryptng the message"); + } + }); + return {Shared::Message::State::pending, ""}; + } + } else +#endif + { + bool success = acc->client.sendPacket(msg); + if (success) + return {Shared::Message::State::sent, ""}; + else + return {Shared::Message::State::error, "Error sending message, internal QXMPP error"}; + } +} + + QMap Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const { QMap changes; diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index a08fee8..99a77d3 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -23,9 +23,13 @@ #include #include +#include #include #include +#ifdef WITH_OMEMO + #include +#endif #include #include @@ -74,7 +78,9 @@ private: void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText); QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const; QMap getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const; - std::tuple getOriginalPendingMessageId(const QString& id); + std::tuple getOriginalPendingMessageId(const QString& id, bool clear = true); + bool handlePendingMessageError(const QString& id, const QString& errorText); + std::pair scheduleSending(const Shared::Message& message, const QDateTime& sendTime, const QString& originalId); private: Account* acc; diff --git a/shared/message.cpp b/shared/message.cpp index dab03a3..5b535c1 100644 --- a/shared/message.cpp +++ b/shared/message.cpp @@ -247,6 +247,19 @@ void Shared::Message::setEdited(bool p_edited) { edited = p_edited; } +Shared::EncryptionProtocol Shared::Message::getEncryption() const { + return encryption; +} + +void Shared::Message::setEncryption(EncryptionProtocol encryption) { + Shared::Message::encryption = encryption; +} + +void Shared::Message::setError(const QString& text) { + state = State::error; + errorText = text; +} + QDataStream& operator<<(QDataStream& out, const Shared::Message& info) { out << info.jFrom; out << info.rFrom; @@ -271,6 +284,7 @@ QDataStream& operator<<(QDataStream& out, const Shared::Message& info) { } out << info.stanzaId; out << info.attachPath; + out << (quint8)info.encryption; return out; } @@ -303,6 +317,9 @@ QDataStream & operator>>(QDataStream& in, Shared::Message& info) { } in >> info.stanzaId; in >> info.attachPath; + quint8 e; + in >> e; + info.encryption = static_cast(e); return in; } diff --git a/shared/message.h b/shared/message.h index 557b1b3..d7df9f3 100644 --- a/shared/message.h +++ b/shared/message.h @@ -25,6 +25,8 @@ #include #include +#include "enums.h" + namespace Shared { class Message; } @@ -94,6 +96,9 @@ public: bool change(const QMap& data); void setStanzaId(const QString& sid); void setAttachPath(const QString& path); + void setEncryption(EncryptionProtocol encryption); + + void setError(const QString& text); QString getFrom() const; QString getFromJid() const; @@ -123,6 +128,7 @@ public: QString getOriginalBody() const; QString getStanzaId() const; QString getAttachPath() const; + EncryptionProtocol getEncryption() const; private: QString jFrom; @@ -144,6 +150,7 @@ private: QDateTime lastModified; QString stanzaId; QString attachPath; + EncryptionProtocol encryption; }; } diff --git a/ui/widgets/chat.cpp b/ui/widgets/chat.cpp index 3b9f090..628f30f 100644 --- a/ui/widgets/chat.cpp +++ b/ui/widgets/chat.cpp @@ -31,10 +31,12 @@ Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent): setAvatar(p_contact->getAvatarPath()); connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged); +#ifdef WITH_OMEMO if (p_contact->hasKeys(Shared::EncryptionProtocol::omemo2)) { m_ui->encryptionButton->setVisible(true); - //if () + updateEncryptionState(); } +#endif } Chat::~Chat() @@ -56,9 +58,14 @@ void Chat::onContactChanged(Models::Item* item, int row, int col) { case 7: setAvatar(contact->getAvatarPath()); break; +#ifdef WITH_OMEMO case 8: m_ui->encryptionButton->setVisible(contact->hasKeys(Shared::EncryptionProtocol::omemo2)); break; + case 9: + updateEncryptionState(); + break; +#endif } } } @@ -69,12 +76,25 @@ void Chat::updateState() { statusIcon->setToolTip(Shared::Global::getName(av)); } +void Chat::updateEncryptionState() { + m_ui->encryptionButton->setEnabled(true); + if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) + m_ui->encryptionButton->setIcon(Shared::icon("lock")); + else + m_ui->encryptionButton->setIcon(Shared::icon("unlock")); +} + + Shared::Message Chat::createMessage() const { Shared::Message msg = Conversation::createMessage(); msg.setType(Shared::Message::chat); msg.setFrom(account->getFullJid()); msg.setToJid(palJid); msg.setToResource(activePalResource); +#ifdef WITH_OMEMO + if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) + msg.setEncryption(Shared::EncryptionProtocol::omemo2); +#endif return msg; } @@ -87,3 +107,11 @@ void Chat::onMessage(const Shared::Message& data){ setPalResource(res); } } + +void Chat::onEncryptionButtonClicked() { + m_ui->encryptionButton->setEnabled(false); + if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) + emit setEncryption(Shared::EncryptionProtocol::none); + else + emit setEncryption(Shared::EncryptionProtocol::omemo2); +} diff --git a/ui/widgets/chat.h b/ui/widgets/chat.h index 78e6bec..cf7ecd3 100644 --- a/ui/widgets/chat.h +++ b/ui/widgets/chat.h @@ -37,6 +37,7 @@ public: protected slots: void onContactChanged(Models::Item* item, int row, int col); + void onEncryptionButtonClicked() override; protected: Shared::Message createMessage() const override; @@ -44,6 +45,7 @@ protected: private: void updateState(); + void updateEncryptionState(); private: Models::Contact* contact; diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index e6316c7..8336d5e 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -91,6 +91,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::clear); connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, this, &Conversation::onTextEditDocSizeChanged); + connect(m_ui->encryptionButton, &QPushButton::clicked, this, &Conversation::onEncryptionButtonClicked); m_ui->messageEditor->installEventFilter(&ker); @@ -345,6 +346,8 @@ void Conversation::clear() { m_ui->messageEditor->clear(); } +void Conversation::onEncryptionButtonClicked() {} + void Conversation::setAvatar(const QString& path) { QPixmap pixmap; if (path.size() == 0) { diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 8af1745..4db5029 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -119,6 +119,7 @@ protected slots: void onFeedContext(const QPoint &pos); void onMessageEditorContext(const QPoint &pos); void onMessageEditRequested(const QString& id); + virtual void onEncryptionButtonClicked(); public: const bool isMuc; diff --git a/ui/widgets/messageline/messagefeed.cpp b/ui/widgets/messageline/messagefeed.cpp index 1eba1fa..29f51d2 100644 --- a/ui/widgets/messageline/messagefeed.cpp +++ b/ui/widgets/messageline/messagefeed.cpp @@ -230,6 +230,7 @@ std::set Models::MessageFeed::detectChanges(c } void Models::MessageFeed::removeMessage(const QString& id) { + SHARED_UNUSED(id); //todo; }