cant believe it, first ever encrypted messages!

This commit is contained in:
Blue 2023-11-05 16:29:44 -03:00
parent a7d1a28f29
commit 637eb702a8
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
10 changed files with 190 additions and 69 deletions

View File

@ -38,7 +38,7 @@ option(WITH_OMEMO "Build OMEMO support module" ON)
# Dependencies # Dependencies
## Qt ## Qt
if (NOT DEFINED QT_VERSION_MAJOR) 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 () else ()
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core)
endif() endif()

View File

@ -71,26 +71,12 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) {
case QXmppMessage::GroupChat: case QXmppMessage::GroupChat:
handled = handleGroupMessage(msg); handled = handleGroupMessage(msg);
break; break;
case QXmppMessage::Error: { case QXmppMessage::Error:
std::tuple<bool, QString, QString> ids = getOriginalPendingMessageId(msg.id()); handled = handlePendingMessageError(msg.id(), msg.error().text());
if (std::get<0>(ids)) { if (!handled)
QString id = std::get<1>(ids);
QString jid = std::get<2>(ids);
RosterItem* cnt = acc->rh->getRosterItem(jid);
QMap<QString, QVariant> cData = {
{"state", static_cast<uint>(Shared::Message::State::error)},
{"errorText", msg.error().text()}
};
if (cnt != nullptr)
cnt->changeMessage(id, cData);
emit acc->changeMessage(jid, id, cData);
handled = true;
} else {
qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping"; qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping";
}
} break;
break;
case QXmppMessage::Headline: case QXmppMessage::Headline:
qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping"; qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping";
break; break;
@ -99,6 +85,27 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) {
logMessage(msg); logMessage(msg);
} }
bool Core::MessageHandler::handlePendingMessageError(const QString& id, const QString& errorText) {
std::tuple<bool, QString, QString> 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<QString, QVariant> cData = {
{"state", static_cast<uint>(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) { bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) {
if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) { if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) {
Shared::Message sMsg(Shared::Message::chat); Shared::Message sMsg(Shared::Message::chat);
@ -182,9 +189,9 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
QString id; QString id;
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
id = source.originId(); id = source.originId();
if (id.size() == 0) { if (id.size() == 0)
id = source.id(); id = source.id();
}
target.setStanzaId(source.stanzaId()); target.setStanzaId(source.stanzaId());
qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId(); qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId();
#else #else
@ -202,24 +209,19 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
target.setBody(source.body()); target.setBody(source.body());
target.setForwarded(forwarded); target.setForwarded(forwarded);
if (guessing) { if (guessing)
if (target.getFromJid() == acc->getBareJid()) { outgoing = target.getFromJid() == acc->getBareJid();
outgoing = true;
} else {
outgoing = false;
}
}
target.setOutgoing(outgoing); target.setOutgoing(outgoing);
if (time.isValid()) { if (time.isValid())
target.setTime(time); target.setTime(time);
} else { else
target.setCurrentTime(); target.setCurrentTime();
}
QString oob = source.outOfBandUrl(); QString oob = source.outOfBandUrl();
if (oob.size() > 0) { if (oob.size() > 0)
target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId)); target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId));
}
target.setOutOfBandUrl(oob); target.setOutOfBandUrl(oob);
} }
@ -249,7 +251,7 @@ void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) {
} }
#endif #endif
std::tuple<bool, QString, QString> Core::MessageHandler::getOriginalPendingMessageId(const QString& id) { std::tuple<bool, QString, QString> Core::MessageHandler::getOriginalPendingMessageId(const QString& id, bool clear) {
std::tuple<bool, QString, QString> result({false, "", ""}); std::tuple<bool, QString, QString> result({false, "", ""});
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id); std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
if (itr != pendingStateMessages.end()) { if (itr != pendingStateMessages.end()) {
@ -263,12 +265,14 @@ std::tuple<bool, QString, QString> Core::MessageHandler::getOriginalPendingMessa
else else
std::get<1>(result) = itr->first; std::get<1>(result) = itr->first;
pendingCorrectionMessages.erase(itrC); if (clear)
pendingCorrectionMessages.erase(itrC);
} else { } else {
std::get<1>(result) = itr->first; std::get<1>(result) = itr->first;
} }
pendingStateMessages.erase(itr); if (clear)
pendingStateMessages.erase(itr);
} }
return result; return result;
@ -281,9 +285,9 @@ void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString&
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}}; QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
RosterItem* ri = acc->rh->getRosterItem(std::get<2>(ids)); RosterItem* ri = acc->rh->getRosterItem(std::get<2>(ids));
if (ri != nullptr) { if (ri != nullptr)
ri->changeMessage(std::get<1>(ids), cData); ri->changeMessage(std::get<1>(ids), cData);
}
emit acc->changeMessage(std::get<2>(ids), 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(); QString id = data.getId();
qDebug() << "Sending message with id:" << id; qDebug() << "Sending message with id:" << id;
if (originalId.size() > 0) 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); RosterItem* ri = acc->rh->getRosterItem(jid);
bool sent = false;
if (newMessage && originalId.size() > 0) if (newMessage && originalId.size() > 0)
newMessage = false; newMessage = false;
QDateTime sendTime = QDateTime::currentDateTimeUtc(); QDateTime sendTime = QDateTime::currentDateTimeUtc();
if (acc->state == Shared::ConnectionState::connected) { std::pair<Shared::Message::State, QString> result = scheduleSending(data, sendTime, originalId);
QXmppMessage msg(createPacket(data, sendTime, originalId)); data.setState(result.first);
data.setErrorText(result.second);
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");
}
QMap<QString, QVariant> changes(getChanges(data, sendTime, newMessage, originalId)); QMap<QString, QVariant> changes(getChanges(data, sendTime, newMessage, originalId));
QString realId;
if (originalId.size() > 0)
realId = originalId;
else
realId = id;
if (ri != nullptr) { if (ri != nullptr) {
if (newMessage) if (newMessage)
ri->appendMessageToArchive(data); ri->appendMessageToArchive(data);
else 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)); pendingStateMessages.insert(std::make_pair(id, jid));
if (originalId.size() > 0) if (originalId.size() > 0)
pendingCorrectionMessages.insert(std::make_pair(id, originalId)); 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<Shared::Message::State, QString> 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<QXmppE2eeExtension::MessageEncryptResult> task = acc->om->encryptMessage(std::move(msg), std::nullopt);
if (task.isFinished()) {
const QXmppE2eeExtension::MessageEncryptResult& res = task.result();
if (std::holds_alternative<std::unique_ptr<QXmppMessage>>(res)) {
const std::unique_ptr<QXmppMessage>& encrypted = std::get<std::unique_ptr<QXmppMessage>>(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<QXmppError>(res)) {
const QXmppError& err = std::get<QXmppError>(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<std::unique_ptr<QXmppMessage>>(result)) {
const std::unique_ptr<QXmppMessage>& encrypted = std::get<std::unique_ptr<QXmppMessage>>(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<bool, QString, QString> 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<QString, QVariant> cData = {{"state", static_cast<uint>(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<QXmppError>(result)) {
const QXmppError& err = std::get<QXmppError>(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<QString, QVariant> Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const { QMap<QString, QVariant> Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const {
QMap<QString, QVariant> changes; QMap<QString, QVariant> changes;

View File

@ -23,9 +23,13 @@
#include <deque> #include <deque>
#include <map> #include <map>
#include <functional>
#include <QXmppMessage.h> #include <QXmppMessage.h>
#include <QXmppHttpUploadIq.h> #include <QXmppHttpUploadIq.h>
#ifdef WITH_OMEMO
#include <QXmppE2eeExtension.h>
#endif
#include <shared/message.h> #include <shared/message.h>
#include <shared/messageinfo.h> #include <shared/messageinfo.h>
@ -74,7 +78,9 @@ private:
void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText); void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText);
QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const; QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const;
QMap<QString, QVariant> getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const; QMap<QString, QVariant> getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const;
std::tuple<bool, QString, QString> getOriginalPendingMessageId(const QString& id); std::tuple<bool, QString, QString> getOriginalPendingMessageId(const QString& id, bool clear = true);
bool handlePendingMessageError(const QString& id, const QString& errorText);
std::pair<Shared::Message::State, QString> scheduleSending(const Shared::Message& message, const QDateTime& sendTime, const QString& originalId);
private: private:
Account* acc; Account* acc;

View File

@ -247,6 +247,19 @@ void Shared::Message::setEdited(bool p_edited) {
edited = 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) { QDataStream& operator<<(QDataStream& out, const Shared::Message& info) {
out << info.jFrom; out << info.jFrom;
out << info.rFrom; out << info.rFrom;
@ -271,6 +284,7 @@ QDataStream& operator<<(QDataStream& out, const Shared::Message& info) {
} }
out << info.stanzaId; out << info.stanzaId;
out << info.attachPath; out << info.attachPath;
out << (quint8)info.encryption;
return out; return out;
} }
@ -303,6 +317,9 @@ QDataStream & operator>>(QDataStream& in, Shared::Message& info) {
} }
in >> info.stanzaId; in >> info.stanzaId;
in >> info.attachPath; in >> info.attachPath;
quint8 e;
in >> e;
info.encryption = static_cast<Shared::EncryptionProtocol>(e);
return in; return in;
} }

View File

@ -25,6 +25,8 @@
#include <QMap> #include <QMap>
#include <QDataStream> #include <QDataStream>
#include "enums.h"
namespace Shared { namespace Shared {
class Message; class Message;
} }
@ -94,6 +96,9 @@ public:
bool change(const QMap<QString, QVariant>& data); bool change(const QMap<QString, QVariant>& data);
void setStanzaId(const QString& sid); void setStanzaId(const QString& sid);
void setAttachPath(const QString& path); void setAttachPath(const QString& path);
void setEncryption(EncryptionProtocol encryption);
void setError(const QString& text);
QString getFrom() const; QString getFrom() const;
QString getFromJid() const; QString getFromJid() const;
@ -123,6 +128,7 @@ public:
QString getOriginalBody() const; QString getOriginalBody() const;
QString getStanzaId() const; QString getStanzaId() const;
QString getAttachPath() const; QString getAttachPath() const;
EncryptionProtocol getEncryption() const;
private: private:
QString jFrom; QString jFrom;
@ -144,6 +150,7 @@ private:
QDateTime lastModified; QDateTime lastModified;
QString stanzaId; QString stanzaId;
QString attachPath; QString attachPath;
EncryptionProtocol encryption;
}; };
} }

View File

@ -31,10 +31,12 @@ Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent):
setAvatar(p_contact->getAvatarPath()); setAvatar(p_contact->getAvatarPath());
connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged); connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged);
#ifdef WITH_OMEMO
if (p_contact->hasKeys(Shared::EncryptionProtocol::omemo2)) { if (p_contact->hasKeys(Shared::EncryptionProtocol::omemo2)) {
m_ui->encryptionButton->setVisible(true); m_ui->encryptionButton->setVisible(true);
//if () updateEncryptionState();
} }
#endif
} }
Chat::~Chat() Chat::~Chat()
@ -56,9 +58,14 @@ void Chat::onContactChanged(Models::Item* item, int row, int col) {
case 7: case 7:
setAvatar(contact->getAvatarPath()); setAvatar(contact->getAvatarPath());
break; break;
#ifdef WITH_OMEMO
case 8: case 8:
m_ui->encryptionButton->setVisible(contact->hasKeys(Shared::EncryptionProtocol::omemo2)); m_ui->encryptionButton->setVisible(contact->hasKeys(Shared::EncryptionProtocol::omemo2));
break; break;
case 9:
updateEncryptionState();
break;
#endif
} }
} }
} }
@ -69,12 +76,25 @@ void Chat::updateState() {
statusIcon->setToolTip(Shared::Global::getName(av)); 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 Chat::createMessage() const {
Shared::Message msg = Conversation::createMessage(); Shared::Message msg = Conversation::createMessage();
msg.setType(Shared::Message::chat); msg.setType(Shared::Message::chat);
msg.setFrom(account->getFullJid()); msg.setFrom(account->getFullJid());
msg.setToJid(palJid); msg.setToJid(palJid);
msg.setToResource(activePalResource); msg.setToResource(activePalResource);
#ifdef WITH_OMEMO
if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2)
msg.setEncryption(Shared::EncryptionProtocol::omemo2);
#endif
return msg; return msg;
} }
@ -87,3 +107,11 @@ void Chat::onMessage(const Shared::Message& data){
setPalResource(res); 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);
}

View File

@ -37,6 +37,7 @@ public:
protected slots: protected slots:
void onContactChanged(Models::Item* item, int row, int col); void onContactChanged(Models::Item* item, int row, int col);
void onEncryptionButtonClicked() override;
protected: protected:
Shared::Message createMessage() const override; Shared::Message createMessage() const override;
@ -44,6 +45,7 @@ protected:
private: private:
void updateState(); void updateState();
void updateEncryptionState();
private: private:
Models::Contact* contact; Models::Contact* contact;

View File

@ -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->clearButton, &QPushButton::clicked, this, &Conversation::clear);
connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged,
this, &Conversation::onTextEditDocSizeChanged); this, &Conversation::onTextEditDocSizeChanged);
connect(m_ui->encryptionButton, &QPushButton::clicked, this, &Conversation::onEncryptionButtonClicked);
m_ui->messageEditor->installEventFilter(&ker); m_ui->messageEditor->installEventFilter(&ker);
@ -345,6 +346,8 @@ void Conversation::clear() {
m_ui->messageEditor->clear(); m_ui->messageEditor->clear();
} }
void Conversation::onEncryptionButtonClicked() {}
void Conversation::setAvatar(const QString& path) { void Conversation::setAvatar(const QString& path) {
QPixmap pixmap; QPixmap pixmap;
if (path.size() == 0) { if (path.size() == 0) {

View File

@ -119,6 +119,7 @@ protected slots:
void onFeedContext(const QPoint &pos); void onFeedContext(const QPoint &pos);
void onMessageEditorContext(const QPoint &pos); void onMessageEditorContext(const QPoint &pos);
void onMessageEditRequested(const QString& id); void onMessageEditRequested(const QString& id);
virtual void onEncryptionButtonClicked();
public: public:
const bool isMuc; const bool isMuc;

View File

@ -230,6 +230,7 @@ std::set<Models::MessageFeed::MessageRoles> Models::MessageFeed::detectChanges(c
} }
void Models::MessageFeed::removeMessage(const QString& id) { void Models::MessageFeed::removeMessage(const QString& id) {
SHARED_UNUSED(id);
//todo; //todo;
} }