WIP OMEMO

This commit is contained in:
Xavier Del Campo Romero 2023-08-28 02:03:00 +02:00
parent 5edd3047ab
commit b9163a3146
Signed by: xavi
GPG Key ID: 84FF3612A9BF43F2
10 changed files with 367 additions and 271 deletions

View File

@ -23,6 +23,7 @@ set(PROJECT_SOURCES
account.ui
accounts.cpp
accounts.ui
atm_db.cpp
client.cpp
contact.cpp
contact.ui

42
atm_db.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "atm_db.h"
#include <QXmppFutureUtils_p.h>
AtmDb::AtmDb(const QString &jid, const JidDb &db) :
TrustDb(jid, db)
{}
QXmppTask<void> AtmDb::addKeysForPostponedTrustDecisions(
const QString &encryption, const QByteArray &senderKeyId,
const QList<QXmppTrustMessageKeyOwner> &keyOwners)
{
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> AtmDb::removeKeysForPostponedTrustDecisions(
const QString &encryption,
const QList<QByteArray> &keyIdsForAuthentication,
const QList<QByteArray> &keyIdsForDistrusting)
{
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> AtmDb::removeKeysForPostponedTrustDecisions(
const QString &encryption,
const QList<QByteArray> &senderKeyIds)
{
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> AtmDb::removeKeysForPostponedTrustDecisions(
const QString &encryption)
{
return QXmpp::Private::makeReadyTask();
}
QXmppTask<QHash<bool, QMultiHash<QString, QByteArray>>>
AtmDb::keysForPostponedTrustDecisions(const QString &encryption,
const QList<QByteArray> &senderKeyIds)
{
return QXmpp::Private::makeReadyTask(
QHash<bool, QMultiHash<QString, QByteArray>>());
}

37
atm_db.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef ATM_DB_H
#define ATM_DB_H
#include "jiddb.h"
#include "trust_db.h"
#include <QXmppAtmTrustStorage.h>
#include <QXmppTask.h>
#include <QXmppTrustMessageKeyOwner.h>
#include <QByteArray>
#include <QHash>
#include <QMultiHash>
#include <QList>
#include <QString>
class AtmDb : public TrustDb, virtual public QXmppAtmTrustStorage
{
public:
AtmDb(const QString &jid, const JidDb &db);
QXmppTask<void> addKeysForPostponedTrustDecisions(
const QString &encryption, const QByteArray &senderKeyId,
const QList<QXmppTrustMessageKeyOwner> &keyOwners) override;
QXmppTask<void> removeKeysForPostponedTrustDecisions(
const QString &encryption,
const QList<QByteArray> &keyIdsForAuthentication,
const QList<QByteArray> &keyIdsForDistrusting) override;
QXmppTask<void> removeKeysForPostponedTrustDecisions(
const QString &encryption,
const QList<QByteArray> &senderKeyIds) override;
QXmppTask<void> removeKeysForPostponedTrustDecisions(
const QString &encryption) override;
QXmppTask<QHash<bool, QMultiHash<QString, QByteArray>>>
keysForPostponedTrustDecisions(const QString &encryption,
const QList<QByteArray> &senderKeyIds = {}) override;
};
#endif

View File

@ -1,18 +1,38 @@
#include "client.h"
#include <QXmppTask.h>
#include <QDebug>
Client::Client(const QString &jid, QObject *const parent) :
QXmppClient(parent),
jid(jid),
db(jid),
trust_db(jid, db),
trust(&trust_db),
omemo_db(jid),
db(this->jid),
atm_db(this->jid, db),
atm(&atm_db),
omemo_db(this->jid),
omemo(&omemo_db)
{
addExtension(&trust);
addExtension(&atm);
addExtension(&carbon);
addExtension(&mam);
addExtension(&pubsub);
addExtension(&omemo);
connect(this, &QXmppClient::connected, this,
[this]()
{
omemo.setUp().then(this,
[](const bool &&result)
{
qDebug() << "setUp result: " << result;
});
});
omemo.setSecurityPolicy(QXmpp::TrustSecurityPolicy::Toakafa);
omemo.load().then(this,
[=](const bool &&result)
{
qDebug() << "load result: " << result;
});
}
QString Client::jidBare()

View File

@ -1,12 +1,16 @@
#ifndef CLIENT_H
#define CLIENT_H
#include "omemo_db.h"
#include "atm_db.h"
#include "jiddb.h"
#include "omemo_db.h"
#include "trust_db.h"
#include <QObject>
#include <QString>
#include <QXmppAtmManager.h>
#include <QXmppCarbonManagerV2.h>
#include <QXmppClient.h>
#include <QXmppMamManager.h>
#include <QXmppOmemoManager.h>
#include <QXmppOmemoStorage.h>
#include <QXmppPubSubManager.h>
@ -22,10 +26,12 @@ public:
JidDb &database();
private:
QXmppCarbonManagerV2 carbon;
QXmppMamManager mam;
const QString jid;
JidDb db;
TrustDb trust_db;
QXmppTrustManager trust;
AtmDb atm_db;
QXmppAtmManager atm;
QXmppPubSubManager pubsub;
OmemoDb omemo_db;
QXmppOmemoManager omemo;

View File

@ -86,7 +86,7 @@ int JidDb::ensureContactTable(const QString &jid) const
static QString securityPolicyTableName(const QString &encryption)
{
return "securityPolicy/" + encryption;;
return "securityPolicy/" + encryption;
}
int JidDb::ensureSecurityPolicyTable(const QString &encryption,
@ -109,7 +109,8 @@ int JidDb::ensureSecurityPolicyTable(const QString &encryption,
static QString keysTableName(const QString &encryption)
{
return "keys/" + encryption;
// return "keys/" + encryption;
return "keys";
}
int JidDb::ensureKeysTable(const QString &encryption, QString &table) const

View File

@ -8,141 +8,38 @@
#include <QString>
#include <iostream>
#include <optional>
#include <variant>
static const QString service_ns = "xxcc/omemo/";
static const QString service = "xxcc", key_namespace = "omemo";
OmemoDb::OmemoDb(const QString &jid) :
jid(jid)
{
}
QString OmemoDb::service() const
{
return service_ns + jid;
}
QXmppTask<QXmppOmemoStorage::OmemoData> OmemoDb::allData()
{
return QXmpp::Private::makeReadyTask(QXmppOmemoStorage::OmemoData());
}
int OmemoDb::storeOwnKeyId(const QString &service, const uint32_t id)
QXmppTask<OmemoDb::Result> OmemoDb::storeOwnKeyId(const QString &key,
const uint32_t id) const
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
const auto str = QString::number(id, 16);
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/keyid");
job.setTextData(QString::number(id, 16));
job.start();
loop.exec();
if (job.error())
{
std::cerr << "Failed to store own key ID: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
return 0;
}
int OmemoDb::storePrivateIdentityKey(const QString &service,
const QByteArray &privateIdentityKey)
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/privateIdentityKey");
job.setBinaryData(privateIdentityKey);
job.start();
loop.exec();
if (job.error())
{
std::cerr << "Failed to store private identity key: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
return 0;
}
int OmemoDb::storePublicIdentityKey(const QString &service,
const QByteArray &publicIdentityKey)
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/publicIdentityKey");
job.setBinaryData(publicIdentityKey);
job.start();
loop.exec();
if (job.error())
{
std::cerr << "Failed to store public identity key: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
return 0;
}
int OmemoDb::storeLatestSignedPreKeyId(const QString &service,
const uint32_t latestSignedPreKeyId)
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/latestSignedPreKeyId");
job.setTextData(QString::number(latestSignedPreKeyId, 16));
job.start();
loop.exec();
if (job.error())
{
std::cerr << "Failed to store latest signed prekey ID: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
return 0;
}
int OmemoDb::storeLatestPreKeyId(const QString &service,
const uint32_t latestPreKeyId)
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/latestPreKeyId");
job.setTextData(QString::number(latestPreKeyId, 16));
job.start();
loop.exec();
if (job.error())
{
std::cerr << "Failed to store latest prekey ID: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
return 0;
return store(key + "/device/ownkeyid", str);
}
QXmppTask<void> OmemoDb::setOwnDevice(const std::optional<OwnDevice> &device)
{
#if 0
if (!device.has_value())
return QXmpp::Private::makeReadyTask();
QXmppPromise<void> promise;
auto task = promise.task();
auto future = QtConcurrent::run(
task.then(this,
[=] () mutable
{
const auto &d = device.value();
@ -158,7 +55,10 @@ QXmppTask<void> OmemoDb::setOwnDevice(const std::optional<OwnDevice> &device)
promise.finish();
});
return promise.task();
return task;
#else
return QXmpp::Private::makeReadyTask();
#endif
}
QXmppTask<void> OmemoDb::addSignedPreKeyPair(const uint32_t keyId,
@ -183,158 +83,175 @@ QXmppTask<void> OmemoDb::removePreKeyPair(const uint32_t keyId)
return QXmpp::Private::makeReadyTask();
}
int OmemoDb::storeDeviceLabel(const QString &service, const QString &label)
OmemoDb::Store OmemoDb::storeCommon() const
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
const auto job = new QKeychain::WritePasswordJob(service);
QXmppPromise<Result> promise;
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/label");
job.setTextData(label);
job.start();
loop.exec();
if (job.error())
connect(job, &QKeychain::Job::finished, this,
[=](QKeychain::Job *job) mutable
{
std::cerr << "Failed to store device label: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
if (job->error())
{
const auto str = "Failed to store device label: "
+ job->errorString();
std::cerr << qPrintable(str) << std::endl;
promise.finish(Error{str});
}
else
promise.finish(QXmpp::Success());
});
return 0;
return Store(promise.task(), job);
}
int OmemoDb::storeKeyId(const QString &service, const QByteArray &keyId)
QXmppTask<OmemoDb::Result> OmemoDb::store(const QString &key,
const QByteArray &data) const
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
const auto result = storeCommon();
const auto job = result.job;
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/key_id");
job.setBinaryData(keyId);
job.start();
loop.exec();
if (job.error())
{
std::cerr << "Failed to store device keyID: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
return 0;
job->setKey(key);
job->setBinaryData(data);
job->start();
return result.task;
}
int OmemoDb::storeSession(const QString &service, const QByteArray &session)
QXmppTask<OmemoDb::Result> OmemoDb::store(const QString &key,
const QString &data) const
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
const auto result = storeCommon();
const auto job = result.job;
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/session");
job.setBinaryData(session);
job.start();
loop.exec();
if (job.error())
{
std::cerr << "Failed to store device session: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
return 0;
job->setKey(key);
job->setTextData(data);
job->start();
return result.task;
}
int OmemoDb::storeUnrespondedSentStanzasCount(const QString &service,
const int count)
/* TODO: move to SQLite database. */
QXmppTask<OmemoDb::Result> OmemoDb::storeDeviceLabel(const QString &key,
const QString &label) const
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/unresponded_sent_stanzas");
job.setTextData(QString::number(count));
job.start();
loop.exec();
if (job.error())
{
std::cerr << "Failed to store device unresponded sent stanzas: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
return 0;
return store(key, label);
}
int OmemoDb::storeUnrespondedReceivedStanzasCount(const QString &service,
const int count)
QXmppTask<OmemoDb::Result> OmemoDb::storeKeyId(const QString &key,
const QByteArray &keyId) const
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/unresponded_received_stanzas");
job.setTextData(QString::number(count));
job.start();
loop.exec();
if (job.error())
{
std::cerr << "Failed to store device unresponded received stanzas: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
return 0;
return store(key, keyId);
}
int OmemoDb::storeRemovalFromDeviceListDate(const QString &service,
const QDateTime &dt)
/* TODO: move to SQLite database. */
QXmppTask<OmemoDb::Result> OmemoDb::storeSession(const QString &key,
const QByteArray &session) const
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
return store(key, session);
}
loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.setKey("device/unresponded_received_stanzas");
job.setTextData(QString::number(dt.currentMSecsSinceEpoch()));
job.start();
loop.exec();
QXmppTask<OmemoDb::Result> OmemoDb::storeRemovalFromDeviceListDate(
const QString &key, const QDateTime &dt) const
{
const auto str = QString::number(dt.currentMSecsSinceEpoch());
if (job.error())
return store(key, str);
}
QXmppTask<OmemoDb::Result> OmemoDb::storeUnrespondedReceivedStanzasCount(
const QString &key, int count) const
{
const auto str = QString::number(count, 10);
return store(key, str);
}
QXmppTask<OmemoDb::Result> OmemoDb::storeUnrespondedSentStanzasCount(
const QString &key, int count) const
{
const auto str = QString::number(count, 10);
return store(key, str);
}
template<typename T, typename Q> bool OmemoDb::runTask(
QXmppPromise<void> &promise, QList<StoreArgs>::const_iterator it,
QList<StoreArgs>::const_iterator end)
{
const auto task = it->fn;
if (std::holds_alternative<T>(task))
{
std::cerr << "Failed to store device unresponded received stanzas: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
const auto &fn = std::get<T>(task);
auto t = (*this.*fn)(it->key, std::get<Q>(it->data));
t.then(this,
[=](const Result &&result) mutable
{
if (std::holds_alternative<QXmpp::Success>(result))
{
std::cerr << "runTask " << qPrintable(it->key)
<< " successful" << std::endl;
runTask(promise, ++it, end);
}
else if (std::holds_alternative<Error>(result))
{
const auto &error = std::get<Error>(result);
std::cerr << "runTask " << qPrintable(it->key) << " failed: "
<< qPrintable(error.description) << std::endl;
promise.finish();
}
});
return true;
}
return 0;
return false;
}
QXmppTask<void> OmemoDb::runTask(QXmppPromise<void> &promise,
QList<OmemoDb::StoreArgs>::const_iterator it,
const QList<StoreArgs>::const_iterator end)
{
if (it != end)
{
runTask<TaskBA, QByteArray>(promise, it, end)
|| runTask<TaskStr, QString>(promise, it, end)
|| runTask<TaskInt, int>(promise, it, end)
|| runTask<TaskUint32, uint32_t>(promise, it, end);
}
else
promise.finish();
return promise.task();
}
QXmppTask<void> OmemoDb::addDevice(const QString &jid,
const uint32_t deviceId, const Device &device)
{
const QString fullservice = service_ns + jid;
const auto ns = key_namespace + "/" + jid + "/"
+ QString::number(deviceId, 16);
auto functions = new QList<StoreArgs>;
*functions << StoreArgs{ns + "/device/label", &OmemoDb::storeDeviceLabel,
device.label};
*functions << StoreArgs{ns + "/device/keyid", &OmemoDb::storeKeyId,
device.keyId};
*functions << StoreArgs{ns + "/device/session", &OmemoDb::storeSession,
device.session};
*functions << StoreArgs{ns + "/device/unrespondedSentStanzas",
&OmemoDb::storeUnrespondedSentStanzasCount,
device.unrespondedSentStanzasCount};
*functions << StoreArgs{ns + "/device/unrespondedReceivedStanzas",
&OmemoDb::storeUnrespondedReceivedStanzasCount,
device.unrespondedReceivedStanzasCount};
*functions << StoreArgs{ns + "removalFromDeviceListDate",
&OmemoDb::storeRemovalFromDeviceListDate,
device.removalFromDeviceListDate};
QXmppPromise<void> promise;
auto future = QtConcurrent::run(
[=] () mutable
{
storeDeviceLabel(fullservice, device.label)
|| storeKeyId(fullservice, device.keyId)
|| storeSession(fullservice, device.session)
|| storeUnrespondedSentStanzasCount(fullservice,
device.unrespondedSentStanzasCount)
|| storeUnrespondedReceivedStanzasCount(fullservice,
device.unrespondedSentStanzasCount)
|| storeRemovalFromDeviceListDate(fullservice,
device.removalFromDeviceListDate);
promise.finish();
});
return promise.task();
return runTask(promise, functions->cbegin(), functions->cend());
}
QXmppTask<void> OmemoDb::removeDevice(const QString &jid,

View File

@ -1,15 +1,20 @@
#ifndef OMEMO_DB_H
#define OMEMO_DB_H
#include <QXmppGlobal.h>
#include <QXmppOmemoStorage.h>
#include <QXmppPromise.h>
#include <QXmppTask.h>
#include <qt5keychain/keychain.h>
#include <QDateTime>
#include <QByteArray>
#include <QString>
#include <cstdint>
class OmemoDb : public QXmppOmemoStorage
class OmemoDb : public QObject, public QXmppOmemoStorage
{
Q_OBJECT
public:
OmemoDb(const QString &jid);
QXmppTask<OmemoData> allData() override;
@ -29,26 +34,74 @@ public:
QXmppTask<void> resetAll() override;
private:
struct Error
{
QString description;
};
using Result = std::variant<QXmpp::Success, Error>;
struct Store
{
Store(const QXmppTask<Result> &task, QKeychain::WritePasswordJob *job) :
task(task), job(job) {}
QXmppTask<Result> task;
QKeychain::WritePasswordJob *job;
};
const QString &jid;
QString service() const;
static int storeOwnKeyId(const QString &service, uint32_t id);
static int storePrivateIdentityKey(const QString &service,
const QByteArray &privateIdentityKey);
static int storePublicIdentityKey(const QString &service,
const QByteArray &publicIdentityKey);
static int storeLatestSignedPreKeyId(const QString &service,
uint32_t latestSignedPreKeyId);
static int storeLatestPreKeyId(const QString &service,
uint32_t latestPreKeyId);
static int storeDeviceLabel(const QString &service, const QString &label);
static int storeKeyId(const QString &service, const QByteArray &keyId);
static int storeSession(const QString &service, const QByteArray &session);
static int storeUnrespondedSentStanzasCount(const QString &service,
int count);
static int storeUnrespondedReceivedStanzasCount(const QString &service,
int count);
static int storeRemovalFromDeviceListDate(const QString &service,
const QDateTime &dt);
QXmppTask<Result> store(const QString &key, const QByteArray &data) const;
QXmppTask<Result> store(const QString &key, const QString &data) const;
Store storeCommon() const;
QXmppTask<Result> storeSession(const QString &ns,
const QByteArray &session) const;
QXmppTask<Result> storeDeviceLabel(const QString &key,
const QString &label) const;
QXmppTask<Result> storeKeyId(const QString &key,
const QByteArray &keyId) const;
QXmppTask<Result> storeOwnKeyId(const QString &key, uint32_t id) const;
QXmppTask<Result> storePrivateIdentityKey(const QString &key,
const QByteArray &privateIdentityKey) const;
QXmppTask<Result> storePublicIdentityKey(const QString &key,
const QByteArray &publicIdentityKey) const;
QXmppTask<Result> storeLatestSignedPreKeyId(const QString &key,
uint32_t latestSignedPreKeyId) const;
QXmppTask<Result> storeLatestPreKeyId(const QString &key,
uint32_t latestPreKeyId) const;
QXmppTask<Result> storeUnrespondedSentStanzasCount(const QString &key,
int count) const;
QXmppTask<Result> storeUnrespondedReceivedStanzasCount(const QString &key,
int count) const;
QXmppTask<Result> storeRemovalFromDeviceListDate(const QString &key,
const QDateTime &dt) const;
using TaskBA = QXmppTask<Result> (OmemoDb::*)(const QString &,
const QByteArray &) const;
using TaskStr = QXmppTask<Result> (OmemoDb::*)(const QString &,
const QString &) const;
using TaskInt = QXmppTask<Result> (OmemoDb::*)(const QString &, int) const;
using TaskUint32 = QXmppTask<Result> (OmemoDb::*)(const QString &,
uint32_t) const;
using TaskDt = QXmppTask<Result> (OmemoDb::*)(const QString &,
const QDateTime &) const;
using StoreFn = std::variant<TaskBA, TaskStr, TaskInt, TaskUint32, TaskDt>;
using StoreData = std::variant<QByteArray , QString, int, uint32_t, QDateTime>;
struct StoreArgs
{
QString key;
StoreFn fn;
StoreData data;
};
QXmppTask<void> runTask(QXmppPromise<void> &promise,
QList<StoreArgs>::const_iterator it,
QList<StoreArgs>::const_iterator end);
template<typename T, typename Q> bool runTask(QXmppPromise<void> &promise,
QList<StoreArgs>::const_iterator it,
QList<StoreArgs>::const_iterator end);
};
#endif

View File

@ -6,6 +6,7 @@
#include "conversation.h"
#include "message.h"
#include <QXmppMessage.h>
#include <QXmppOmemoElement_p.h>
#include <QXmppRosterManager.h>
#include <QXmppUtils.h>
#include <QKeyEvent>
@ -13,6 +14,7 @@
#include <QScroller>
#include <stdexcept>
#include <utility>
#include <variant>
xxcc::xxcc(QWidget *const parent) :
QWidget(parent),
@ -194,6 +196,8 @@ void xxcc::addAccount(Client *const c)
throw std::runtime_error("Expected non-null QXmppRosterManager");
}
#include <QDebug>
void xxcc::send(void)
{
if (!selected)
@ -201,14 +205,32 @@ void xxcc::send(void)
const auto from = selected->jidBare(),
to = ui.jid->text(), msg = ui.chatinput->toPlainText();
const bool enc = ui.omemo->isChecked();
static const auto encmsg = "This is an OMEMO-encrypted message.";
QXmppMessage out(from, to, msg);
QXmppMessage out(from, to, enc ? encmsg : msg);
out.setStamp(QDateTime::currentDateTimeUtc());
selected->sendPacket(out);
addOutMessage(out);
storeMessage(out, Direction::Out);
ui.chatinput->clear();
selected->sendSensitive(std::move(out)).then(this,
[=](const QXmpp::SendResult &&result) mutable
{
qDebug() << "result.index(): " << result.index();
if (std::holds_alternative<QXmpp::SendSuccess>(result))
{
const auto &success = std::get<QXmpp::SendSuccess>(result);
qDebug() << "acknowledged: " << success.acknowledged;
addOutMessage(out);
storeMessage(out, Direction::Out);
ui.chatinput->clear();
}
else if (std::holds_alternative<QXmppError>(result))
{
const auto &error = std::get<QXmppError>(result);
qDebug() << error.description;
}
});
// selected->sendPacket(out);
}
void xxcc::storeMessage(const QXmppMessage &msg, const Direction dir) const

View File

@ -226,10 +226,7 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox">
<property name="enabled">
<bool>false</bool>
</property>
<widget class="QCheckBox" name="omemo">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>