Compare commits

...

9 Commits

19 changed files with 1389 additions and 84 deletions

View File

@ -15,14 +15,15 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Sql REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Sql REQUIRED)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Concurrent Widgets Sql REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Concurrent Widgets Sql REQUIRED)
set(PROJECT_SOURCES
account.cpp
account.ui
accounts.cpp
accounts.ui
atm_db.cpp
client.cpp
contact.cpp
contact.ui
@ -37,6 +38,8 @@ set(PROJECT_SOURCES
main.cpp
message.cpp
message.ui
omemo_db.cpp
trust_db.cpp
xxcc.cpp
xxcc.ui
)
@ -73,6 +76,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}Keychain::Qt${QT_VERSION_MAJOR}Keychain)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Concurrent
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Sql)

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,13 +1,50 @@
#include "client.h"
#include <QXmppMamManager.h>
#include <QXmppTask.h>
#include <QDebug>
Client::Client(QObject *const parent) :
QXmppClient(parent)
Client::Client(const QString &jid, QObject *const parent) :
QXmppClient(parent),
jid(jid),
db(this->jid),
atm_db(this->jid, db),
atm(&atm_db),
omemo_db(this->jid),
omemo(&omemo_db)
{
// addExtension(new QXmppMamManager);
addExtension(&atm);
addExtension(&carbon);
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()
{
return configuration().jidBare();
}
const JidDb &Client::database() const
{
return db;
}
JidDb &Client::database()
{
return db;
}

View File

@ -1,15 +1,38 @@
#ifndef CLIENT_H
#define CLIENT_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 <QXmppOmemoManager.h>
#include <QXmppOmemoStorage.h>
#include <QXmppPubSubManager.h>
#include <QXmppTrustManager.h>
#include <QXmppTrustStorage.h>
class Client : public QXmppClient
{
public:
Client(QObject *parent = nullptr);
Client(const QString &jid, QObject *parent = nullptr);
QString jidBare();
const JidDb &database() const;
JidDb &database();
private:
QXmppCarbonManagerV2 carbon;
const QString jid;
JidDb db;
AtmDb atm_db;
QXmppAtmManager atm;
QXmppPubSubManager pubsub;
OmemoDb omemo_db;
QXmppOmemoManager omemo;
};
#endif

View File

@ -3,16 +3,16 @@
#include <QListWidgetItem>
#include <QScroller>
Contacts::Contacts(QList<JidDb *> &databases, QWidget *const parent) :
QDialog(parent),
databases(databases)
Contacts::Contacts(const QList<Client *> &clients,
QWidget *const parent) :
QDialog(parent)
{
ui.setupUi(this);
QScroller::grabGesture(ui.contacts_list, QScroller::TouchGesture);
for (const auto db : databases)
for (const auto &contact : db->roster())
add(db->jid, contact);
for (const auto c : clients)
for (const auto &contact : c->database().roster())
add(c->jidBare(), contact);
connect(ui.contacts_list, &QListWidget::itemActivated, this,
[this]

View File

@ -2,9 +2,9 @@
#define CONTACTS_H
#include "ui_contacts.h"
#include "jiddb.h"
#include "client.h"
#include <QDialog>
#include <QList>
#include <QHash>
#include <QString>
#include <QWidget>
@ -13,12 +13,12 @@ class Contacts : public QDialog
Q_OBJECT
public:
Contacts(QList<JidDb *> &databases, QWidget *parent = nullptr);
Contacts(const QList<Client *> &clients,
QWidget *parent = nullptr);
void add(const QString &own, const QString &other);
private:
Ui_contacts ui;
QList<JidDb *> &databases;
enum Role {From = Qt::UserRole, To};
Q_SIGNALS:

196
jiddb.cpp
View File

@ -70,7 +70,7 @@ QStringList JidDb::roster() const
return ret;
}
int JidDb::ensureContactDb(const QString &jid) const
int JidDb::ensureContactTable(const QString &jid) const
{
QSqlQuery q(db);
@ -84,25 +84,71 @@ int JidDb::ensureContactDb(const QString &jid) const
return 0;
}
QList<JidDb::Message> JidDb::getMessages(const QString &jid,
static QString securityPolicyTableName(const QString &encryption)
{
return "securityPolicy/" + encryption;
}
int JidDb::ensureSecurityPolicyTable(const QString &encryption,
QString &table) const
{
QSqlQuery q(db);
table = securityPolicyTableName(encryption);
if (!q.exec("create table if not exists '" + table
+ "' (policy TEXT unique) strict;"))
{
std::cerr << "JidDb::ensureSecurityPolicyTable: query exec failed"
<< std::endl;
return -1;
}
return 0;
}
static QString keysTableName(const QString &encryption)
{
// return "keys/" + encryption;
return "keys";
}
int JidDb::ensureKeysTable(const QString &encryption, QString &table) const
{
QSqlQuery q(db);
table = keysTableName(encryption);
if (!q.exec("create table if not exists '" + table
+ "' (owner TEXT, trustlevel TEXT, key TEXT) strict;"))
{
std::cerr << "JidDb::ensureKeysTable: query exec failed"
<< std::endl;
return -1;
}
return 0;
}
QList<JidDb::Message> JidDb::messages(const QString &jid,
const int tail) const
{
QSqlQuery q(db);
if (ensureContactDb(jid))
if (ensureContactTable(jid))
return QList<Message>();
else if (tail < 0)
{
if (!q.exec("select * from '" + jid + "' order by time;"))
{
std::cerr << "JidDb::getMessages: query exec failed";
std::cerr << "JidDb::messages: query exec failed";
return QList<Message>();
}
}
else if (!q.exec("select * from '" + jid + "' order by time desc limit "
+ QString::number(tail) + ";"))
{
std::cerr << "JidDb::getMessages: query exec failed" << std::endl;
std::cerr << "JidDb::messages: query exec failed" << std::endl;
return QList<Message>();
}
@ -116,7 +162,7 @@ QList<JidDb::Message> JidDb::getMessages(const QString &jid,
if (!ok)
{
std::cerr << "JidDb::getMessages: invalid time" << std::endl;
std::cerr << "JidDb::messages: invalid time" << std::endl;
// Attempt to read other messages.
continue;
}
@ -132,7 +178,7 @@ QList<JidDb::Message> JidDb::getMessages(const QString &jid,
m.direction = Direction::Out;
else
{
std::cerr << "JidDb::getMessages: invalid direction" << std::endl;
std::cerr << "JidDb::messages: invalid direction" << std::endl;
// Attempt to read other messages.
continue;
}
@ -159,7 +205,7 @@ int JidDb::storeMessage(const JidDb::Message &msg) const
break;
}
ensureContactDb(msg.contact);
ensureContactTable(msg.contact);
if (!q.exec("insert into '" + msg.contact
+ "' (direction, time, body) values "
@ -181,7 +227,7 @@ QStringList JidDb::tables() const
if (!q.exec("select name from sqlite_schema where "
"type = 'table' and name not like 'sqlite_%'"))
{
std::cerr << "JidDb::getConversations: query failed" << std::endl;
std::cerr << "JidDb::conversations: query failed" << std::endl;
return QStringList();
}
@ -193,7 +239,7 @@ QStringList JidDb::tables() const
return ret;
}
QList<JidDb::Conversation> JidDb::getConversations() const
QList<JidDb::Conversation> JidDb::conversations() const
{
const auto conversations = tables();
QList<Conversation> ret;
@ -201,11 +247,11 @@ QList<JidDb::Conversation> JidDb::getConversations() const
for (const auto &jid : conversations)
if (jid.contains('@'))
{
const auto messages = getMessages(jid, 1);
const auto msgs = messages(jid, 1);
if (!messages.isEmpty())
if (!msgs.isEmpty())
{
const auto &m = messages.first();
const auto &m = msgs.first();
ret << Conversation(jid, m.body, m.dt);
}
@ -213,3 +259,127 @@ QList<JidDb::Conversation> JidDb::getConversations() const
return ret;
}
QString JidDb::securityPolicy(const QString &encryption) const
{
const auto tb = tables();
const auto name = securityPolicyTableName(encryption);
for (const auto &t : tb)
{
if (t == name)
{
QStringList ret;
QSqlQuery q(db);
if (!q.exec("select policy from '" + name + "';"))
std::cerr << "query exec failed" << std::endl;
else
while (q.next())
return q.value("policy").toString();
break;
}
}
return QString();
}
int JidDb::storeSecurityPolicy(const QString &encryption,
const QString &policy) const
{
QString table;
if (ensureSecurityPolicyTable(encryption, table))
return -1;
QSqlQuery q(db);
if (!q.exec("insert or ignore into '" + table
+ "' (policy) values ('" + policy + "');"))
{
std::cerr << "JidDb::storeSecurityPolicy: query exec failed"
<< std::endl;
return -1;
}
return 0;
}
QList<JidDb::Keys> JidDb::keys(const QString &encryption) const
{
QList<Keys> ret;
QString table;
if (ensureKeysTable(encryption, table))
{
std::cerr << "JidDb::keys: ensureKeysTable failed" << std::endl;
return QList<Keys>();
}
QSqlQuery q(db);
if (!q.exec("select owner, trustlevel, key from '" + table + "';"))
std::cerr << "JidDb::keys: query exec failed" << std::endl;
else
while (q.next())
{
const auto owner = q.value("owner").toString();
const auto trust_level = q.value("trustlevel").toString();
const auto key_b64 = q.value("key").toString();
const auto key = QByteArray::fromBase64(key_b64.toLatin1());
bool found = false;
for (auto &k : ret)
if (k.owner == owner)
{
k.keys.append(key);
found = true;
break;
}
if (!found)
{
Keys keys;
keys.owner = owner;
keys.trust_level = trust_level;
keys.keys.append(key);
ret.append(keys);
}
}
return ret;
}
int JidDb::storeKeys(const QString &encryption, const JidDb::Keys &keys) const
{
QString table;
if (ensureKeysTable(encryption, table))
{
std::cerr << "JidDb::storeKeys: ensureKeysTable failed" << std::endl;
return -1;
}
for (const auto &k : keys.keys)
{
QSqlQuery q(db);
if (!q.exec("insert or replace into '" + table
+ "' (owner, trustlevel, key) values ('"
+ keys.owner + ", " + keys.trust_level + ","
+ k.toBase64() + "');"))
{
std::cerr << "JidDb::storeKeys: query exec failed"
<< std::endl;
return -1;
}
}
return 0;
}
void JidDb::removeKeys() const
{
}

38
jiddb.h
View File

@ -2,12 +2,15 @@
#define JID_DB_H
#include "direction.h"
#include <QByteArray>
#include <QDateTime>
#include <QList>
#include <QObject>
#include <QPair>
#include <QString>
#include <QStringList>
#include <QSqlDatabase>
#include <QVariant>
class JidDb : public QObject
{
@ -30,24 +33,51 @@ public:
QDateTime dt;
};
struct Trust
{
QString security_policy;
};
struct Keys
{
Keys() {}
Keys(const QString &owner, const QString &trust_level,
const QList<QByteArray> &keys) :
owner(owner),
trust_level(trust_level),
keys(keys)
{}
QString owner, trust_level;
QList<QByteArray> keys;
};
JidDb(const QString &jid);
QStringList roster() const;
const QString jid;
const QString &jid;
public Q_SLOTS:
QList<Conversation> getConversations() const;
QList<Message> getMessages(const QString &jid,
QList<Conversation> conversations() const;
QList<Message> messages(const QString &jid,
int tail = -1) const;
QString securityPolicy(const QString &encryption) const;
QList<Keys> keys(const QString &encryption) const;
int storeMessage(const Message &msg) const;
int addToRoster(const QString &jid);
int addToRoster(const QStringList &roster);
int storeSecurityPolicy(const QString &encryption,
const QString &policy) const;
int storeKeys(const QString &encryption, const Keys &keys) const;
void removeKeys() const;
Q_SIGNALS:
void addedToRoster(QString jid);
private:
QSqlDatabase db;
int ensureContactDb(const QString &jid) const;
int ensureSecurityPolicyTable(const QString &encryption,
QString &table) const;
int ensureContactTable(const QString &jid) const;
int ensureKeysTable(const QString &encryption, QString &table) const;
QStringList tables() const;
};

View File

@ -1,7 +1,10 @@
find_package(PkgConfig REQUIRED)
pkg_check_modules(protobuf-c REQUIRED IMPORTED_TARGET libprotobuf-c)
add_subdirectory(libomemo-c)
target_include_directories(omemo-c PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/libomemo-c/src>
$<INSTALL_INTERFACE:libomemo-c/src>)
target_link_libraries(omemo-c PRIVATE PkgConfig::protobuf-c)
export(TARGETS omemo-c FILE Findomemo-c.cmake)
install(

View File

@ -105,7 +105,7 @@ void Login::setup(const QString &jid, const QString pwd, const QString &domain)
cfg.setPassword(pwd);
cfg.setAutoReconnectionEnabled(false);
const auto client = new Client;
const auto client = new Client(jid);
connect(client, &Client::stateChanged,
[d = domain, e = ui.error] (const Client::State state)

410
omemo_db.cpp Normal file
View File

@ -0,0 +1,410 @@
#include "omemo_db.h"
#include <QXmppConfiguration.h>
#include <QXmppFutureUtils_p.h>
#include <QXmppPromise.h>
#include <qt5keychain/keychain.h>
#include <QEventLoop>
#include <QtConcurrent>
#include <QString>
#include <iostream>
#include <optional>
static const QString service = "xxcc", key_namespace = "omemo";
OmemoDb::OmemoDb(const QString &jid) :
jid(jid)
{
}
QXmppTask<QXmppOmemoStorage::OmemoData> OmemoDb::allData()
{
return QXmpp::Private::makeReadyTask(QXmppOmemoStorage::OmemoData());
}
int OmemoDb::storeOwnKeyId(const QString &service, const uint32_t id)
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
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;
}
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();
task.then(this,
[=] () mutable
{
const auto &d = device.value();
const auto srv = service();
storeDeviceLabel(srv, d.label)
|| storeOwnKeyId(srv, d.id)
|| storePrivateIdentityKey(srv, d.privateIdentityKey)
|| storePublicIdentityKey(srv, d.publicIdentityKey)
|| storeLatestSignedPreKeyId(srv, d.latestSignedPreKeyId)
|| storeLatestPreKeyId(srv, d.latestPreKeyId);
promise.finish();
});
return task;
#else
return QXmpp::Private::makeReadyTask();
#endif
}
QXmppTask<void> OmemoDb::addSignedPreKeyPair(const uint32_t keyId,
const SignedPreKeyPair &keyPair)
{
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::removeSignedPreKeyPair(uint32_t keyId)
{
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::addPreKeyPairs(
const QHash<uint32_t, QByteArray> &keyPairs)
{
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::removePreKeyPair(const uint32_t keyId)
{
return QXmpp::Private::makeReadyTask();
}
/* TODO: move to SQLite database. */
QXmppTask<OmemoDb::Result> OmemoDb::storeDeviceLabel(const QString &ns,
const QString &label)
{
const auto job = new QKeychain::WritePasswordJob(service);
QXmppPromise<Result> promise;
auto task = promise.task();
connect(job, &QKeychain::Job::finished, this,
[=](QKeychain::Job *job) mutable
{
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());
});
job->setKey(ns + "/device/label");
job->setTextData(label);
job->start();
return task;
}
int OmemoDb::storeKeyId(const QString &service, const QByteArray &keyId)
{
QKeychain::WritePasswordJob job(service);
QEventLoop loop;
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;
}
/* TODO: move to SQLite database. */
QXmppTask<OmemoDb::Result> OmemoDb::storeSession(const QString &ns,
const QByteArray &session)
{
const auto job = new QKeychain::WritePasswordJob(service);
QXmppPromise<Result> promise;
auto task = promise.task();
connect(job, &QKeychain::Job::finished, this,
[=](QKeychain::Job *job) mutable
{
if (job->error())
{
const auto str = "Failed to store device session: "
+ job->errorString();
std::cerr << qPrintable(str) << std::endl;
promise.finish(Error{str});
}
else
promise.finish(QXmpp::Success());
});
job->setKey(ns + "/device/session");
qDebug() << "session data: " << session.count();
job->setBinaryData(session);
job->start();
return task;
}
int OmemoDb::storeUnrespondedSentStanzasCount(const QString &service,
const int count)
{
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;
}
int OmemoDb::storeUnrespondedReceivedStanzasCount(const QString &service,
const int count)
{
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;
}
int OmemoDb::storeRemovalFromDeviceListDate(const QString &service,
const QDateTime &dt)
{
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(dt.currentMSecsSinceEpoch()));
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;
}
QXmppTask<void> OmemoDb::addDevice(const QString &jid,
const uint32_t deviceId, const Device &device)
{
const auto ns = key_namespace + "/" + jid;
QXmppPromise<void> promise;
auto task = promise.task();
auto future = storeSession(ns, device.session);
future.then(this,
[=](OmemoDb::Result &&result) mutable
{
if (std::holds_alternative<QXmpp::Success>(result))
{
std::cerr << "storeSession was successful" << std::endl;
storeDeviceLabel(ns, device.label).then(this,
[=](OmemoDb::Result &&) mutable
{
if (std::holds_alternative<QXmpp::Success>(result))
{
std::cerr << "storeDeviceLabel was successful" << std::endl;
promise.finish();
}
else
promise.finish();
});
}
else
promise.finish();
#if 0
storeDeviceLabel(fullservice, device.label)
|| storeKeyId(fullservice, device.keyId)
|| storeSession(fullservice, device.session)
|| storeUnrespondedSentStanzasCount(fullservice,
device.unrespondedSentStanzasCount)
|| storeUnrespondedReceivedStanzasCount(fullservice,
device.unrespondedSentStanzasCount)
|| storeRemovalFromDeviceListDate(fullservice,
device.removalFromDeviceListDate);
#endif
});
return task;
}
QXmppTask<void> OmemoDb::removeDevice(const QString &jid,
const uint32_t deviceId)
{
#if 0
const QString fullservice = service_ns + jid;
QKeychain::DeletePasswordJob job(fullservice);
QEventLoop loop;
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();
if (job.error())
{
std::cerr << "Failed to store device unresponded received stanzas: "
<< qPrintable(job.errorString()) << std::endl;
return -1;
}
return 0;
#endif
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::removeDevices(const QString &jid)
{
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::resetAll()
{
return QXmpp::Private::makeReadyTask();
}

63
omemo_db.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef OMEMO_DB_H
#define OMEMO_DB_H
#include <QXmppGlobal.h>
#include <QXmppOmemoStorage.h>
#include <QXmppTask.h>
#include <QDateTime>
#include <QByteArray>
#include <QString>
#include <cstdint>
class OmemoDb : public QObject, public QXmppOmemoStorage
{
Q_OBJECT
public:
OmemoDb(const QString &jid);
QXmppTask<OmemoData> allData() override;
QXmppTask<void> setOwnDevice(
const std::optional<OwnDevice> &device) override;
QXmppTask<void> addSignedPreKeyPair(uint32_t keyId,
const SignedPreKeyPair &keyPair) override;
QXmppTask<void> removeSignedPreKeyPair(uint32_t keyId) override;
QXmppTask<void> addPreKeyPairs(
const QHash<uint32_t, QByteArray> &keyPairs) override;
QXmppTask<void> removePreKeyPair(uint32_t keyId) override;
QXmppTask<void> addDevice(const QString &jid,
uint32_t deviceId, const Device &device) override;
QXmppTask<void> removeDevice(const QString &jid,
uint32_t deviceId) override;
QXmppTask<void> removeDevices(const QString &jid) override;
QXmppTask<void> resetAll() override;
private:
struct Error
{
QString description;
};
using Result = std::variant<QXmpp::Success, Error>;
const QString &jid;
QXmppTask<Result> storeSession(const QString &ns,
const QByteArray &session);
QXmppTask<Result> storeDeviceLabel(const QString &service, const QString &label);
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 storeKeyId(const QString &service, const QByteArray &keyId);
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);
};
#endif

@ -1 +1 @@
Subproject commit 2ec7614ca23fb36a6de7077d8e34e2004c070606
Subproject commit d7dae544d07016cb27b07fe1a83dbd90729a1312

404
trust_db.cpp Normal file
View File

@ -0,0 +1,404 @@
#include "trust_db.h"
#include <QXmppTrustLevel.h>
#include <QXmppTrustSecurityPolicy.h>
#include <QXmppConfiguration.h>
#include <QXmppFutureUtils_p.h>
#include <QXmppPromise.h>
#include <qt5keychain/keychain.h>
#include <QEventLoop>
#include <QtConcurrent>
#include <iostream>
TrustDb::TrustDb(const QString &jid, const JidDb &db) :
jid(jid),
db(db)
{
}
static const struct
{
QXmpp::TrustLevel level;
const char *str;
} trustlevels[] =
{
{QXmpp::TrustLevel::Undecided, "Undecided"},
{QXmpp::TrustLevel::AutomaticallyDistrusted, "AutomaticallyDistrusted"},
{QXmpp::TrustLevel::ManuallyDistrusted, "ManuallyDistrusted"},
{QXmpp::TrustLevel::AutomaticallyTrusted, "AutomaticallyTrusted"},
{QXmpp::TrustLevel::ManuallyTrusted, "ManuallyTrusted"},
{QXmpp::TrustLevel::Authenticated, "Authenticated"}
};
static const struct
{
QXmpp::TrustSecurityPolicy policy;
const char *str;
} tsp_levels[] =
{
{QXmpp::NoSecurityPolicy, "NoSecurityPolicy"},
{QXmpp::Toakafa, "Toakafa"}
};
static QString toString(const QXmpp::TrustSecurityPolicy securityPolicy)
{
for (const auto &t : tsp_levels)
if (t.policy == securityPolicy)
return t.str;
return "unknown";
}
static QString toString(const QXmpp::TrustLevel &trustLevel)
{
for (const auto &t : trustlevels)
if (t.level == trustLevel)
return t.str;
return "unknown";
}
static int toSecurityPolicy(const QString &s,
QXmpp::TrustSecurityPolicy &securityPolicy)
{
for (const auto &t : tsp_levels)
if (t.str == s)
{
securityPolicy = t.policy;
return 0;
}
return -1;
}
static int toTrustLevel(const QString &tstr, QXmpp::TrustLevel &trustLevel)
{
for (const auto &t : trustlevels)
if (t.str == tstr)
{
trustLevel = t.level;
return 0;
}
return -1;
}
QString TrustDb::service() const
{
return "xxcc/trust/" + jid;
}
QXmppTask<void> TrustDb::setSecurityPolicy(const QString &encryption,
const QXmpp::TrustSecurityPolicy securityPolicy)
{
if (db.storeSecurityPolicy(encryption, toString(securityPolicy)))
std::cerr << "TrustDb::setSecurityPolicy: storeSecurityPolicy failed"
<< std::endl;
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> TrustDb::resetSecurityPolicy(const QString &encryption)
{
if (db.storeSecurityPolicy(encryption, QString()))
std::cerr << "TrustDb::resetSecurityPolicy: storeSecurityPolicy failed"
<< std::endl;
return QXmpp::Private::makeReadyTask();
}
QXmppTask<QXmpp::TrustSecurityPolicy> TrustDb::securityPolicy(
const QString &encryption)
{
const auto s = db.securityPolicy(encryption);
QXmpp::TrustSecurityPolicy policy;
if (toSecurityPolicy(s, policy))
std::cerr << "TrustDb::securityPolicy: toSecurityPolicy failed"
<< std::endl;
return QXmpp::Private::makeReadyTask(QXmpp::TrustSecurityPolicy(policy));
}
QXmppTask<void> TrustDb::setOwnKey(const QString &encryption,
const QByteArray &keyId)
{
QKeychain::WritePasswordJob job(service());
QEventLoop loop;
job.setKey("key/" + encryption);
job.setBinaryData(keyId);
job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.start();
loop.exec();
if (job.error())
std::cerr << "Failed to store own key: "
<< qPrintable(job.errorString()) << std::endl;
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> TrustDb::resetOwnKey(const QString &encryption)
{
QKeychain::DeletePasswordJob job(service());
QEventLoop loop;
job.setKey("key/" + encryption);
job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.start();
loop.exec();
if (job.error())
std::cerr << "Failed to reset own key: "
<< qPrintable(job.errorString()) << std::endl;
return QXmpp::Private::makeReadyTask();
}
QXmppTask<QByteArray> TrustDb::ownKey(const QString &encryption)
{
QKeychain::ReadPasswordJob job(service());
QEventLoop loop;
job.setKey("key/" + encryption);
job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.start();
loop.exec();
QByteArray ret;
if (job.error())
std::cerr << "Failed to read own key: "
<< qPrintable(job.errorString()) << std::endl;
else
ret = job.binaryData();
return QXmpp::Private::makeReadyTask(QByteArray(ret));
}
QXmppTask<void> TrustDb::addKeys(const QString &encryption,
const QString &keyOwnerJid, const QList<QByteArray> &keyIds,
const QXmpp::TrustLevel trustLevel)
{
const struct JidDb::Keys keys(keyOwnerJid, toString(trustLevel), keyIds);
if (db.storeKeys(encryption, keys))
std::cerr << "TrustDb::addKeys: storeKeys failed" << std::endl;
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> TrustDb::removeKeys(const QString &encryption,
const QList<QByteArray> &keyIds)
{
auto keys = db.keys(encryption);
for (const auto &id : keyIds)
for (auto &k : keys)
{
const auto i = k.keys.indexOf(id);
if (i != -1)
{
k.keys.removeAt(i);
break;
}
}
for (const auto &k : keys)
if (db.storeKeys(encryption, k))
std::cerr << "TrustDb::removeKeys: storeKeys failed" << std::endl;
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> TrustDb::removeKeys(const QString &encryption,
const QString &keyOwnerJid)
{
auto keys = db.keys(encryption);
for (int i = 0; i < keys.count(); i++)
if (keys[i].owner == keyOwnerJid)
{
keys.removeAt(i);
break;
}
for (const auto &k : keys)
if (db.storeKeys(encryption, k))
std::cerr << "TrustDb::removeKeys: storeKeys failed" << std::endl;
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> TrustDb::removeKeys(const QString &encryption)
{
db.removeKeys();
return QXmpp::Private::makeReadyTask();
}
QXmppTask<QHash<QXmpp::TrustLevel,
QMultiHash<QString, QByteArray>>> TrustDb::keys(const QString &encryption,
const QXmpp::TrustLevels trustLevels)
{
QHash<QXmpp::TrustLevel, QMultiHash<QString, QByteArray>> ret;
const auto keys = db.keys(encryption);
for (const auto &k : keys)
{
QXmpp::TrustLevel level;
if (toTrustLevel(k.trust_level, level))
{
std::cerr << "TrustDb::keys: invalid trust level: "
<< qPrintable(k.trust_level) << std::endl;
continue;
}
else if (!trustLevels.testFlag(level))
continue;
QMultiHash<QString, QByteArray> mh;
for (const auto &key : k.keys)
mh.insert(k.owner, key);
ret.insert(level, mh);
}
return QXmpp::Private::makeReadyTask(QHash<QXmpp::TrustLevel,
QMultiHash<QString, QByteArray>>(ret));
}
QXmppTask<QHash<QString,
QHash<QByteArray, QXmpp::TrustLevel>>> TrustDb::keys(const QString &encryption,
const QList<QString> &keyOwnerJids,
const QXmpp::TrustLevels trustLevels)
{
QHash<QString, QHash<QByteArray, QXmpp::TrustLevel>> ret;
const auto keys = db.keys(encryption);
for (const auto &jid : keyOwnerJids)
for (const auto &k : keys)
{
if (k.owner != jid)
continue;
QXmpp::TrustLevel level;
if (toTrustLevel(k.trust_level, level))
{
std::cerr << "TrustDb::keys: invalid trust level: "
<< qPrintable(k.trust_level) << std::endl;
continue;
}
else if (!trustLevels.testFlag(level))
continue;
QHash<QByteArray, QXmpp::TrustLevel> h;
for (const auto &key : k.keys)
h.insert(key, level);
ret.insert(jid, h);
break;
}
return QXmpp::Private::makeReadyTask(QHash<QString,
QHash<QByteArray, QXmpp::TrustLevel>>(ret));
}
QXmppTask<bool> TrustDb::hasKey(const QString &encryption,
const QString &keyOwnerJid, QXmpp::TrustLevels trustLevels)
{
bool ret = false;
const auto keys = db.keys(encryption);
for (const auto &k : keys)
{
if (k.owner != keyOwnerJid)
continue;
QXmpp::TrustLevel level;
if (toTrustLevel(k.trust_level, level))
{
std::cerr << "TrustDb::keys: invalid trust level: "
<< qPrintable(k.trust_level) << std::endl;
break;
}
else if (!trustLevels.testFlag(level))
break;
ret = true;
break;
}
return QXmpp::Private::makeReadyTask(bool(ret));
}
QXmppTask<QHash<QString,
QMultiHash<QString, QByteArray>>> TrustDb::setTrustLevel(
const QString &encryption,
const QMultiHash<QString, QByteArray> &keyIds,
const QXmpp::TrustLevel trustLevel)
{
QHash<QString, QMultiHash<QString, QByteArray>> ret;
auto keys = db.keys(encryption);
const auto str = toString(trustLevel);
for (const auto &owner : keyIds.keys())
for (auto &k : keys)
{
if (k.owner != owner)
continue;
k.trust_level = str;
QMultiHash<QString, QByteArray> mh;
for (const auto &id : k.keys)
mh.insert(owner, id);
ret.insert(owner, mh);
break;
}
for (const auto &key : keys)
if (db.storeKeys(encryption, key))
std::cerr << "TrustDb::setTrustLevel: storeKeys failed"
<< std::endl;
return QXmpp::Private::makeReadyTask(QHash<QString,
QMultiHash<QString, QByteArray>>(ret));
}
QXmppTask<QHash<QString,
QMultiHash<QString, QByteArray>>> TrustDb::setTrustLevel(
const QString &encryption,
const QList<QString> &keyOwnerJids,
const QXmpp::TrustLevel oldTrustLevel,
const QXmpp::TrustLevel newTrustLevel)
{
return QXmpp::Private::makeReadyTask(QHash<QString,
QMultiHash<QString, QByteArray>>());
}
QXmppTask<QXmpp::TrustLevel> TrustDb::trustLevel(const QString &encryption,
const QString &keyOwnerJid, const QByteArray &keyId)
{
return QXmpp::Private::makeReadyTask(QXmpp::TrustLevel());
}
QXmppTask<void> TrustDb::resetAll(const QString &encryption)
{
return QXmpp::Private::makeReadyTask();
}
namespace QXmpp
{
uint qHash(const TrustLevel &key, uint seed)
{
return qHash(key, seed);
}
}

72
trust_db.h Normal file
View File

@ -0,0 +1,72 @@
#ifndef TRUST_DB_H
#define TRUST_DB_H
#include "jiddb.h"
#include <QXmppClient.h>
#include <QXmppTask.h>
#include <QXmppTrustLevel.h>
#include <QXmppTrustStorage.h>
#include <QByteArray>
#include <QHash>
#include <QList>
#include <QMultiHash>
#include <QString>
class TrustDb : virtual public QXmppTrustStorage
{
public:
TrustDb(const QString &jid, const JidDb &db);
QXmppTask<void> setSecurityPolicy(const QString &encryption,
QXmpp::TrustSecurityPolicy securityPolicy) override;
QXmppTask<void> resetSecurityPolicy(const QString &encryption) override;
QXmppTask<QXmpp::TrustSecurityPolicy> securityPolicy(
const QString &encryption) override;
QXmppTask<void> setOwnKey(const QString &encryption,
const QByteArray &keyId) override;
QXmppTask<void> resetOwnKey(const QString &encryption) override;
QXmppTask<QByteArray> ownKey(const QString &encryption) override;
QXmppTask<void> addKeys(const QString &encryption,
const QString &keyOwnerJid, const QList<QByteArray> &keyIds,
QXmpp::TrustLevel trustLevel
= QXmpp::TrustLevel::AutomaticallyDistrusted) override;
QXmppTask<void> removeKeys(const QString &encryption,
const QList<QByteArray> &keyIds) override;
QXmppTask<void> removeKeys(const QString &encryption,
const QString &keyOwnerJid) override;
QXmppTask<void> removeKeys(const QString &encryption) override;
QXmppTask<QHash<QXmpp::TrustLevel,
QMultiHash<QString, QByteArray>>> keys(const QString &encryption,
QXmpp::TrustLevels trustLevels = {}) override;
QXmppTask<QHash<QString,
QHash<QByteArray, QXmpp::TrustLevel>>> keys(const QString &encryption,
const QList<QString> &keyOwnerJids,
QXmpp::TrustLevels trustLevels = {}) override;
QXmppTask<bool> hasKey(const QString &encryption,
const QString &keyOwnerJid, QXmpp::TrustLevels trustLevels) override;
QXmppTask<QHash<QString,
QMultiHash<QString, QByteArray>>> setTrustLevel(
const QString &encryption,
const QMultiHash<QString, QByteArray> &keyIds,
QXmpp::TrustLevel trustLevel) override;
QXmppTask<QHash<QString,
QMultiHash<QString, QByteArray>>> setTrustLevel(
const QString &encryption,
const QList<QString> &keyOwnerJids,
QXmpp::TrustLevel oldTrustLevel,
QXmpp::TrustLevel newTrustLevel) override;
QXmppTask<QXmpp::TrustLevel> trustLevel(const QString &encryption,
const QString &keyOwnerJid, const QByteArray &keyId) override;
QXmppTask<void> resetAll(const QString &encryption) override;
private:
const QString &jid;
const JidDb &db;
QString service() const;
};
#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),
@ -23,7 +25,6 @@ xxcc::xxcc(QWidget *const parent) :
ui.setupUi(this);
QScroller::grabGesture(ui.conversations_list, QScroller::TouchGesture);
QScroller::grabGesture(ui.messages, QScroller::TouchGesture);
setupDatabases(pairs);
connectAccounts(pairs);
retrieveConversations();
connect(ui.accounts, &QPushButton::released, this,
@ -39,16 +40,20 @@ xxcc::xxcc(QWidget *const parent) :
connect(ui.contacts, &QPushButton::released, this,
[this]
{
Contacts c(databases, this);
Contacts c(clients, this);
connect(&c, &Contacts::startChat, this, &xxcc::startChat);
for (const auto db : databases)
for (const auto cl : clients)
{
const auto db = &cl->database();
c.connect(db, &JidDb::addedToRoster, &c,
[&c, db] (const QString jid)
{
c.add(db->jid, jid);
});
}
c.exec();
});
@ -74,17 +79,15 @@ xxcc::xxcc(QWidget *const parent) :
break;
}
for (const auto *const db : databases)
if (db->jid == selected->jidBare())
{
static const auto n_messages = 20;
const auto messages = db->getMessages(conv->to, n_messages);
if (selected)
{
const auto &db = selected->database();
static const auto n_messages = 20;
const auto messages = db.messages(conv->to, n_messages);
for (auto it = messages.rbegin(); it != messages.rend(); it++)
new Message(it->body, it->dt, it->direction, ui.messages);
break;
}
for (auto it = messages.rbegin(); it != messages.rend(); it++)
new Message(it->body, it->dt, it->direction, ui.messages);
}
ui.sw->setCurrentIndex(Tab::Chat);
ui.jid->setText(conv->to);
@ -99,9 +102,6 @@ xxcc::~xxcc()
{
for (const auto c : clients)
delete c;
for (const auto db : databases)
delete db;
}
void xxcc::connectAccounts(const QList<Credentials::Pair> &pairs)
@ -115,19 +115,13 @@ void xxcc::connectAccounts(const QList<Credentials::Pair> &pairs)
cfg.setPassword(p.second);
cfg.setAutoReconnectionEnabled(true);
const auto client = new Client;
const auto client = new Client(p.first);
addAccount(client);
client->connectToServer(cfg);
}
}
void xxcc::setupDatabases(const QList<Credentials::Pair> &pairs)
{
for (const auto &p : pairs)
databases.append(new JidDb(p.first));
}
void xxcc::startChat(const QString from, const QString to)
{
bool found = false;
@ -196,17 +190,14 @@ void xxcc::addAccount(Client *const c)
roster->connect(roster, &QXmppRosterManager::rosterReceived, c,
[this, c, roster]
{
for (const auto db : databases)
if (db->jid == c->jidBare())
{
db->addToRoster(roster->getRosterBareJids());
break;
}
c->database().addToRoster(roster->getRosterBareJids());
});
else
throw std::runtime_error("Expected non-null QXmppRosterManager");
}
#include <QDebug>
void xxcc::send(void)
{
if (!selected)
@ -214,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
@ -241,24 +250,30 @@ void xxcc::storeMessage(const QXmppMessage &msg, const Direction dir) const
break;
}
for (const auto db : databases)
if (db->jid == jid)
for (const auto c : clients)
{
if (c->jidBare() == jid)
{
const auto &db = c->database();
JidDb::Message m;
m.body = msg.body();
m.dt = msg.stamp();
m.direction = dir;
m.contact = contact;
db->storeMessage(m);
break;
db.storeMessage(m);
}
}
}
void xxcc::retrieveConversations()
{
for (const auto *const db : databases)
for (const auto &conv : db->getConversations())
new Conversation(db->jid, conv.to,
ui.conversations_list, conv.last_msg, conv.dt);
for (const auto c : clients)
{
const auto &db = c->database();
for (const auto &conv : db.conversations())
new Conversation(db.jid, conv.to,
ui.conversations_list, conv.last_msg, conv.dt);
}
}

2
xxcc.h
View File

@ -24,10 +24,8 @@ private:
enum Tab {Conversations, Chat};
Ui_main ui;
QList<Client *> clients;
QList<JidDb *> databases;
Credentials creds;
Client *selected;
void setupDatabases(const QList<Credentials::Pair> &pairs);
void connectAccounts(const QList<Credentials::Pair> &pairs);
void storeMessage(const QXmppMessage &msg, Direction dir) const;
void retrieveConversations();

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>