aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-08-28 00:20:54 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-09-18 10:35:30 +0200
commit5bbe04c9ca091a0626f34afe5e3ba2141e2963de (patch)
tree35e76183618fe557b4ded5735fb846e3c6cf3995
parentd32b9e93572c5e6999a7323139de38cc1a7197cf (diff)
WIP OMEMO TrustDb/JidDb
-rw-r--r--CMakeLists.txt1
-rw-r--r--atm_db.cpp42
-rw-r--r--atm_db.h37
-rw-r--r--client.cpp40
-rw-r--r--client.h12
-rw-r--r--credentials.cpp251
-rw-r--r--credentials.h31
-rw-r--r--jiddb.cpp711
-rw-r--r--jiddb.h64
-rw-r--r--omemo_db.cpp339
-rw-r--r--omemo_db.h32
-rw-r--r--trust_db.cpp376
-rw-r--r--xxcc.cpp95
-rw-r--r--xxcc.h4
-rw-r--r--xxcc.ui5
15 files changed, 1488 insertions, 552 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 497175b..d4fe840 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,6 +23,7 @@ set(PROJECT_SOURCES
account.ui
accounts.cpp
accounts.ui
+ atm_db.cpp
client.cpp
contact.cpp
contact.ui
diff --git a/atm_db.cpp b/atm_db.cpp
new file mode 100644
index 0000000..f038959
--- /dev/null
+++ b/atm_db.cpp
@@ -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>>());
+}
diff --git a/atm_db.h b/atm_db.h
new file mode 100644
index 0000000..0cbc247
--- /dev/null
+++ b/atm_db.h
@@ -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
diff --git a/client.cpp b/client.cpp
index d4d3f6f..3cb4e86 100644
--- a/client.cpp
+++ b/client.cpp
@@ -1,18 +1,48 @@
#include "client.h"
+#include <QXmppTask.h>
+#include <QDebug>
+#include <iostream>
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(db),
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;
+ });
+
+ logger()->setLoggingType(QXmppLogger::SignalLogging);
+ logger()->setMessageTypes(QXmppLogger::DebugMessage);
+
+ connect(logger(), &QXmppLogger::message, this,
+ [=] (QXmppLogger::MessageType type, const QString &text)
+ {
+ std::cerr << qPrintable(text) << std::endl;
+ });
}
QString Client::jidBare()
diff --git a/client.h b/client.h
index 0a54d68..3da53e8 100644
--- a/client.h
+++ b/client.h
@@ -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;
diff --git a/credentials.cpp b/credentials.cpp
index 8d6a01b..5cd6e6f 100644
--- a/credentials.cpp
+++ b/credentials.cpp
@@ -1,103 +1,218 @@
#include "credentials.h"
-#include <QEventLoop>
#include <qt5keychain/keychain.h>
+#include <QXmppPromise.h>
#include <QXmppConfiguration.h>
#include <iostream>
static const QString service = "xxcc", sep = ";";
-QList<Credentials::Pair> Credentials::load()
+QXmppTask<Credentials::PairListResult> Credentials::load(
+ Credentials::PairList &pairs,
+ QStringList::const_iterator &it,
+ QStringList::const_iterator end)
{
- const QStringList users = storedUsers();
- QList<Pair> ret;
+ if (it == end)
+ return QXmpp::Private::makeReadyTask<PairListResult>(
+ PairListResult(pairs));
- for (const auto &user : users)
- {
- QKeychain::ReadPasswordJob job(service);
- QEventLoop loop;
-
- job.setKey(user);
- job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
- job.start();
- loop.exec();
+ QXmppPromise<PairListResult> promise;
- const QString pwd = job.textData();
+ load(*it).then(this,
+ [=] (LoadResult &&result) mutable
+ {
+ if (std::holds_alternative<QString>(result))
+ {
+ pairs << Pair(*it, std::get<QString>(result));
+
+ load(pairs, ++it, end).then(this,
+ [=] (PairListResult &&result) mutable
+ {
+ promise.finish(result);
+ });
+ }
+ else if (std::holds_alternative<Error>(result))
+ promise.finish(Error{std::get<Error>(result)});
+ });
+
+ return promise.task();
+}
- if (job.error())
- std::cerr << "Failed to retrieve password for " << qPrintable(user)
- << ": " << qPrintable(job.errorString()) << std::endl;
- else
- ret.append(Pair(user, pwd));
- }
+QXmppTask<Credentials::PairListResult> Credentials::load()
+{
+ QXmppPromise<PairListResult> promise;
- return ret;
+ storedUsers().then(this,
+ [=] (Users &&result) mutable
+ {
+ if (std::holds_alternative<QStringList>(result))
+ {
+ PairList pairs;
+ auto list = new QStringList(std::get<QStringList>(result));
+ auto begin = list->cbegin();
+ const auto end = list->cend();
+
+ load(pairs, begin, end).then(this,
+ [=] (PairListResult &&result) mutable
+ {
+ delete list;
+ promise.finish(result);
+ });
+ }
+ else if (std::holds_alternative<Error>(result))
+ promise.finish(Error{std::get<Error>(result)});
+ });
+
+ return promise.task();
}
-void Credentials::store(Client *c)
+QXmppTask<Credentials::StoreResult> Credentials::store(Client *c)
{
- QKeychain::WritePasswordJob job(service);
+ QXmppPromise<StoreResult> promise;
const QXmppConfiguration &cfg = c->configuration();
const QString user = cfg.jidBare();
- QEventLoop loop;
-
- job.setKey(user);
- job.setTextData(cfg.password());
- job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
- job.start();
- loop.exec();
-
- if (job.error())
- std::cerr << "Failed to store password: "
- << qPrintable(job.errorString()) << std::endl;
- else
- storeUser(user);
+
+ store(user, cfg.password()).then(this,
+ [=] (StoreResult &&result) mutable
+ {
+ store(user).then(this,
+ [=] (StoreResult &&result) mutable
+ {
+ promise.finish(result);
+ });
+ });
+
+ return promise.task();
}
-void Credentials::storeUser(const QString &user)
+QXmppTask<Credentials::StoreResult> Credentials::store(const QString &user)
{
- QString list = storedUsersList();
- QKeychain::WritePasswordJob job(service);
- QEventLoop loop;
+ QXmppPromise<StoreResult> promise;
+
+ storedUsersList().then(this,
+ [=] (LoadResult &&result) mutable
+ {
+ if (std::holds_alternative<QString>(result))
+ {
+ auto &list = std::get<QString>(result);
+
+ if (!list.isEmpty())
+ list += sep;
+
+ list += user;
+ store("users", list).then(this,
+ [=] (StoreResult &&result) mutable
+ {
+ promise.finish(result);
+ });
+ }
+ else if (std::holds_alternative<Error>(result))
+ {
+ store("users", user).then(this,
+ [=] (StoreResult &&result) mutable
+ {
+ promise.finish(result);
+ });
+ }
+ });
+
+ return promise.task();
+}
- if (!list.isEmpty())
- list += sep;
+QXmppTask<Credentials::LoadResult> Credentials::storedUsersList()
+{
+ QXmppPromise<LoadResult> promise;
- list += user;
+ load("users").then(this,
+ [=] (LoadResult &&result) mutable
+ {
+ promise.finish(result);
+ });
- job.setKey("users");
- job.setTextData(list);
- job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
- job.start();
- loop.exec();
+ return promise.task();
- if (job.error())
- std::cerr << "Failed to store user: "
- << qPrintable(job.errorString()) << std::endl;
}
-QString Credentials::storedUsersList()
+QXmppTask<Credentials::LoadResult> Credentials::load(const QString &key)
{
- QKeychain::ReadPasswordJob job(service);
- QString ret;
- QEventLoop loop;
+ QXmppPromise<LoadResult> promise;
+ auto job = new QKeychain::ReadPasswordJob(service);
- job.setKey("users");
- job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
- job.start();
- loop.exec();
+ job->setKey(key);
+ job->connect(job, &QKeychain::Job::finished, this,
+ [=] (QKeychain::Job *const job) mutable
+ {
+ auto readjob = dynamic_cast<QKeychain::ReadPasswordJob *>(job);
- const QString users = job.textData();
+ if (readjob->error())
+ {
+ const auto error = "Failed to load " + key + ": "
+ + readjob->errorString();
- if (job.error())
- std::cerr << "Failed to retrieve users: "
- << qPrintable(job.errorString()) << std::endl;
- else
- ret = users;
+ std::cerr << qPrintable(error) << std::endl;
+ promise.finish(Error{error});
+ }
+ else
+ {
+ auto users = readjob->textData();
- return ret;
+ promise.finish(QString(users));
+ }
+ });
+
+ job->start();
+ return promise.task();
}
-QStringList Credentials::storedUsers()
+QXmppTask<Credentials::StoreResult> Credentials::store(const QString &key,
+ const QString &value)
{
- return storedUsersList().split(sep);
+ QXmppPromise<StoreResult> promise;
+ auto job = new QKeychain::WritePasswordJob(service);
+
+ job->setKey(key);
+ job->setTextData(value);
+ job->connect(job, &QKeychain::Job::finished, this,
+ [=] (QKeychain::Job *const job) mutable
+ {
+ if (job->error())
+ {
+ StoreResult sr;
+
+ const auto error = "Failed to store " + key + ": "
+ + job->errorString();
+
+ std::cerr << qPrintable(error) << std::endl;
+ sr = Error{error};
+ promise.finish(StoreResult(sr));
+ }
+ else
+ promise.finish(Success{});
+ });
+
+ job->start();
+ return promise.task();
+}
+
+QXmppTask<Credentials::Users> Credentials::storedUsers()
+{
+ QXmppPromise<Users> promise;
+
+ storedUsersList().then(this,
+ [=] (LoadResult &&result) mutable
+ {
+ if (std::holds_alternative<QString>(result))
+ {
+ const auto &value = std::get<QString>(result);
+
+ promise.finish(QStringList(value.split(sep)));
+ }
+ else if (std::holds_alternative<Error>(result))
+ {
+ const auto &error = std::get<Error>(result);
+
+ promise.finish(Error{error.description});
+ }
+ });
+
+ return promise.task();
}
diff --git a/credentials.h b/credentials.h
index 7de90e0..100729b 100644
--- a/credentials.h
+++ b/credentials.h
@@ -2,25 +2,44 @@
#define CREDENTIALS_H
#include "client.h"
+#include <QXmppTask.h>
+#include <QXmppPromise.h>
#include <QObject>
#include <QList>
#include <QPair>
#include <QStringList>
+#include <variant>
class Credentials : public QObject
{
Q_OBJECT
public:
+ struct Success {};
+ struct Error
+ {
+ QString description;
+ };
+
using Pair = QPair<QString, QString>;
- QList<Pair> load();
+ using PairList = QList<Pair>;
+ using StoreResult = std::variant<Success, Error>;
+ using LoadResult = std::variant<QString, Error>;
+ using PairListResult = std::variant<PairList, Error>;
-public Q_SLOTS:
- void store(Client *c);
+ QXmppTask<PairListResult> load();
+ QXmppTask<StoreResult> store(Client *c);
private:
- static void storeUser(const QString &user);
- static QString storedUsersList();
- static QStringList storedUsers();
+ using Users = std::variant<QStringList, Error>;
+
+ QXmppTask<StoreResult> store(const QString &key, const QString &value);
+ QXmppTask<StoreResult> store(const QString &user);
+ QXmppTask<LoadResult> load(const QString &key);
+ QXmppTask<LoadResult> storedUsersList();
+ QXmppTask<Users> storedUsers();
+ QXmppTask<PairListResult> load(PairList &pairs,
+ QStringList::const_iterator &it,
+ QStringList::const_iterator end);
};
#endif
diff --git a/jiddb.cpp b/jiddb.cpp
index b555f87..f66d242 100644
--- a/jiddb.cpp
+++ b/jiddb.cpp
@@ -1,6 +1,7 @@
#include "jiddb.h"
#include <QDir>
#include <QSqlQuery>
+#include <QSqlError>
#include <QStandardPaths>
#include <QTimeZone>
#include <QVariant>
@@ -70,7 +71,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 +85,72 @@ int JidDb::ensureContactDb(const QString &jid) const
return 0;
}
+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;
+}
+
+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: "
+ << qPrintable(q.lastError().text()) << 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::messages: query exec failed";
+ std::cerr << "JidDb::messages: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
return QList<Message>();
}
}
else if (!q.exec("select * from '" + jid + "' order by time desc limit "
+ QString::number(tail) + ";"))
{
- std::cerr << "JidDb::messages: query exec failed" << std::endl;
+ std::cerr << "JidDb::messages: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
return QList<Message>();
}
@@ -143,7 +191,7 @@ QList<JidDb::Message> JidDb::messages(const QString &jid,
return ret;
}
-int JidDb::storeMessage(const JidDb::Message &msg) const
+int JidDb::store(const JidDb::Message &msg) const
{
QSqlQuery q(db);
QString dir;
@@ -159,7 +207,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 "
@@ -213,3 +261,654 @@ QList<JidDb::Conversation> JidDb::conversations() 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();
+}
+
+std::optional<QXmppOmemoStorage::OwnDevice> JidDb::omemoOwnDevice() const
+{
+ return std::optional<QXmppOmemoStorage::OwnDevice>();
+}
+
+int JidDb::store(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::store: 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::store(const QString &encryption, const JidDb::Keys &keys) const
+{
+ QString table;
+
+ if (ensureKeysTable(encryption, table))
+ {
+ std::cerr << "JidDb::store: ensureKeysTable failed" << std::endl;
+ return -1;
+ }
+
+ for (const auto &k : keys.keys)
+ {
+ QSqlQuery q(db);
+
+ const auto kstr = k.isEmpty() ? "\"\""
+ : "'" + QString::fromLatin1(k.toBase64()) + "'";
+
+ if (!q.exec("insert or replace into '" + table
+ + "' (owner, trustlevel, key) values ("
+ "'" + keys.owner + "', "
+ "'" + keys.trust_level + "',"
+ + kstr
+ + ");"))
+ {
+ std::cerr << "JidDb::store: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int JidDb::ensureOmemoDeviceTable() const
+{
+ QSqlQuery q(db);
+
+ if (!q.exec("create table if not exists 'omemo/devices' "
+ "(jid TEXT, deviceID INTEGER, keyID TEXT, session TEXT, "
+ "unresp_sent INTEGER, unresp_recv INTEGER, removal INTEGER) "
+ "strict;"))
+ {
+ std::cerr << "JidDb::ensureOmemoTable: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int JidDb::store(const QString &jid, const uint32_t id,
+ const QXmppOmemoStorage::Device &d) const
+{
+ if (ensureOmemoDeviceTable())
+ {
+ std::cerr << "JidDb::store: ensureOmemoTable failed" << std::endl;
+ return -1;
+ }
+
+ QSqlQuery q(db);
+ const auto keyId = d.keyId.isEmpty() ? "\"\""
+ : "'" + QString::fromLatin1(d.keyId.toBase64()) + "'",
+ session = d.session.isEmpty() ? "\"\""
+ : "'" + QString::fromLatin1(d.session.toBase64()) + "'",
+ query = "insert or replace into 'omemo/devices' "
+ "(jid, deviceID, keyID, session, "
+ "unresp_sent, unresp_recv , removal) values ("
+ "'" + jid + "', "
+ + QString::number(id) + ", "
+ + keyId + ", "
+ + session + ", "
+ + QString::number(d.unrespondedSentStanzasCount) + ", "
+ + QString::number(d.unrespondedReceivedStanzasCount) + ", "
+ + QString::number(d.removalFromDeviceListDate.toSecsSinceEpoch())
+ + ");";
+
+ if (!q.exec(query))
+ {
+ std::cerr << "JidDb::store(id, jid, d): query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int JidDb::ensureOmemoSignedPreKeyTable() const
+{
+ QSqlQuery q(db);
+
+ if (!q.exec("create table if not exists 'omemo/signedprekeys' "
+ "(id INTEGER, creation INTEGER, data TEXT) strict;"))
+ {
+ std::cerr << "JidDb::ensureOmemoSignedPreKeyTable: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int JidDb::store(uint32_t id, const QXmppOmemoStorage::SignedPreKeyPair &spk)
+ const
+{
+ if (ensureOmemoSignedPreKeyTable())
+ {
+ std::cerr << "JidDb::store: ensureOmemoSignedPreKeyTable failed"
+ << std::endl;
+ return -1;
+ }
+
+ QSqlQuery q(db);
+ const auto data = spk.data.isEmpty() ? "\"\""
+ : "'" + QString::fromLatin1(spk.data.toBase64()) + "'",
+ query = "insert or replace into 'omemo/signedprekeys' "
+ "(id, creation, data) values ("
+ + QString::number(id) + ", "
+ + QString::number(spk.creationDate.toSecsSinceEpoch()) + ", "
+ + data
+ + ");";
+
+ if (!q.exec(query))
+ {
+ std::cerr << "JidDb::store(spk): query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int JidDb::ensureOmemoPreKeyTable() const
+{
+ QSqlQuery q(db);
+
+ if (!q.exec("create table if not exists 'omemo/prekeys' "
+ "(id INTEGER, data TEXT) strict;"))
+ {
+ std::cerr << "JidDb::ensureOmemoPreKeyTable: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int JidDb::store(const QHash<uint32_t, QByteArray> &pairs) const
+{
+ if (ensureOmemoPreKeyTable())
+ {
+ std::cerr << "JidDb::store: ensureOmemoPreKeyTable failed"
+ << std::endl;
+ return -1;
+ }
+
+ for (const auto id : pairs.keys())
+ {
+ const auto data = pairs.value(id);
+ const auto datastr = data.isEmpty() ? "\"\""
+ : "'" + QString::fromLatin1(data.toBase64()) + "'",
+ query = "insert or replace into 'omemo/prekeys' "
+ "(id, data) values ("
+ + QString::number(id) + ", "
+ + datastr
+ + ");";
+
+ QSqlQuery q(db);
+
+ if (!q.exec(query))
+ {
+ std::cerr << "JidDb::store(pairs): query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int JidDb::ensureOmemoOwnDeviceTable() const
+{
+ QSqlQuery q(db);
+
+ if (!q.exec("create table if not exists 'omemo/owndevice' "
+ "(id INTEGER, label TEXT, privkey TEXT, pubkey TEXT, "
+ "latestSignedPreKeyId INTEGER, latestPreKeyId INTEGER) strict;"))
+ {
+ std::cerr << "JidDb::ensureOmemoOwnDeviceTable: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int JidDb::store(const QXmppOmemoStorage::OwnDevice &own) const
+{
+ if (ensureOmemoOwnDeviceTable())
+ {
+ std::cerr << "JidDb::store: ensureOmemoOwnDeviceTable failed"
+ << std::endl;
+ return -1;
+ }
+
+ QSqlQuery q(db);
+ const auto privkey = own.privateIdentityKey.isEmpty() ? "\"\""
+ : "'" + QString::fromLatin1(own.privateIdentityKey.toBase64()) + "'",
+ pubkey = own.publicIdentityKey.isEmpty() ? "\"\""
+ : "'" + QString::fromLatin1(own.publicIdentityKey.toBase64()) + "'",
+ query = "insert or replace into 'omemo/owndevice' "
+ "(id, label, privkey, pubkey, latestSignedPreKeyId, "
+ "latestPreKeyId) values ("
+ + QString::number(own.id) + ", "
+ "'" + own.label + "', "
+ + privkey + ", "
+ + pubkey + ", "
+ + QString::number(own.latestSignedPreKeyId) + ", "
+ + QString::number(own.latestPreKeyId)
+ + ");";
+
+ if (!q.exec(query))
+ {
+ std::cerr << "JidDb::store(own): query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ std::cerr << "Query was: " << qPrintable(query) << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+QList<JidDb::OmemoDevice> JidDb::omemoDevices() const
+{
+ QList<OmemoDevice> ret;
+ QSqlQuery q(db);
+
+ if (!q.exec("select * from 'omemo/devices';"))
+ {
+ std::cerr << "JidDb::omemoDevices: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return QList<OmemoDevice>();
+ }
+
+ while (q.next())
+ {
+ QXmppOmemoStorage::Device device;
+ bool ok;
+ const uint32_t deviceId = q.value("deviceID").toULongLong(&ok);
+
+ if (!ok)
+ {
+ std::cerr << "JidDb::messages: invalid deviceID" << std::endl;
+ // Attempt to read other messages.
+ continue;
+ }
+
+ device.unrespondedReceivedStanzasCount =
+ q.value("unresp_recv").toInt(&ok);
+
+ if (!ok)
+ {
+ std::cerr << "JidDb::messages: invalid unresp_recv" << std::endl;
+ // Attempt to read other messages.
+ continue;
+ }
+
+ device.unrespondedSentStanzasCount = q.value("unresp_sent").toInt(&ok);
+
+ if (!ok)
+ {
+ std::cerr << "JidDb::messages: invalid unresp_sent" << std::endl;
+ // Attempt to read other messages.
+ continue;
+ }
+
+ const QByteArray keyId = q.value("keyID").toByteArray(),
+ session = q.value("session").toByteArray();
+ const auto jid = q.value("jid").toString();
+ bool found = false;
+
+ for (auto &r : ret)
+ if (jid == r.jid)
+ {
+ r.devices.insert(deviceId, device);
+ break;
+ }
+
+ if (!found)
+ {
+ OmemoDevice d;
+
+ d.jid = jid;
+ d.devices.insert(deviceId, device);
+ ret << d;
+ }
+ }
+
+ return ret;
+}
+
+QHash<uint32_t, QXmppOmemoStorage::SignedPreKeyPair> JidDb::signedPreKeyPairs()
+ const
+{
+ QHash<uint32_t, QXmppOmemoStorage::SignedPreKeyPair> ret;
+
+ QSqlQuery q(db);
+
+ if (!q.exec("select * from 'omemo/signedprekeys';"))
+ {
+ std::cerr << "JidDb::signedPreKeyPairs: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return QHash<uint32_t, QXmppOmemoStorage::SignedPreKeyPair>();
+ }
+
+ while (q.next())
+ {
+ QXmppOmemoStorage::SignedPreKeyPair spk;
+ bool ok;
+ const uint32_t id = q.value("id").toULongLong(&ok);
+
+ if (!ok)
+ {
+ std::cerr << "JidDb::messages: invalid id" << std::endl;
+ // Attempt to read other entries.
+ continue;
+ }
+
+ const auto creation = q.value("creation").toInt(&ok);
+
+ if (!ok)
+ {
+ std::cerr << "JidDb::messages: invalid creation date" << std::endl;
+ // Attempt to read other entries.
+ continue;
+ }
+
+ spk.creationDate = QDateTime::fromSecsSinceEpoch(creation);
+ spk.data = q.value("data").toString().toLatin1().toBase64();
+ ret.insert(id, spk);
+ }
+
+ return ret;
+}
+
+QHash<uint32_t, QByteArray> JidDb::preKeyPairs() const
+{
+ QHash<uint32_t, QByteArray> ret;
+
+ QSqlQuery q(db);
+
+ if (!q.exec("select * from 'omemo/signedprekeys';"))
+ {
+ std::cerr << "JidDb::signedPreKeyPairs: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return QHash<uint32_t, QByteArray>();
+ }
+
+ while (q.next())
+ {
+ bool ok;
+ const uint32_t id = q.value("id").toULongLong(&ok);
+
+ if (!ok)
+ {
+ std::cerr << "JidDb::messages: invalid id" << std::endl;
+ // Attempt to read other entries.
+ continue;
+ }
+
+ const auto data = q.value("data").toString().toLatin1().toBase64();
+ ret.insert(id, data);
+ }
+
+ return ret;
+}
+
+void JidDb::removeKeys(const QString &encryption) const
+{
+ QString table;
+
+ if (ensureKeysTable(encryption, table))
+ std::cerr << "JidDb::removeKeys: ensureKeysTable failed" << std::endl;
+ else
+ {
+ QSqlQuery q(db);
+
+ if (!q.exec("delete from '" + table + "'"))
+ std::cerr << "JidDb::removeKeys: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ }
+}
+
+void JidDb::removeOmemoDevice(const QString &jid, const uint32_t id) const
+{
+ if (ensureOmemoDeviceTable())
+ std::cerr << "JidDb::removeOmemoDevice: ensureOmemoDeviceTable failed"
+ << std::endl;
+ else
+ {
+ QSqlQuery q(db);
+ const auto idstr = QString::number(id);
+
+ if (!q.exec("delete from 'omemo/devices' "
+ "where (jid = '" + jid + "' "
+ "and id = " + idstr + ")"))
+ std::cerr << "JidDb::removeOmemoDevice: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ }
+}
+
+void JidDb::removeOmemoDevices(const QString &jid) const
+{
+ if (ensureOmemoDeviceTable())
+ std::cerr << "JidDb::removeOmemoDevices: ensureOmemoDeviceTable failed"
+ << std::endl;
+ else
+ {
+ QSqlQuery q(db);
+
+ if (!q.exec("delete from 'omemo/devices' "
+ "where jid = '" + jid + "'"))
+ std::cerr << "JidDb::removeOmemoDevices: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ }
+}
+
+int JidDb::ensureOwnIdTable(const QString &encryption, QString &table)
+ const
+{
+ QSqlQuery q(db);
+
+ table = encryption + "/ownID";
+
+ if (!q.exec("create table if not exists '" + table + "' "
+ "(ID TEXT) strict;"))
+ {
+ std::cerr << "JidDb::ensureOwnIdTable: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+QByteArray JidDb::ownKeyId(const QString &encryption) const
+{
+ QString table;
+
+ if (ensureOwnIdTable(encryption, table))
+ {
+ std::cerr << "JidDb::store: ensureOwnIdTable failed" << std::endl;
+ return QByteArray();
+ }
+
+ QSqlQuery q(db);
+
+ if (!q.exec("select * from '" + table + "';"))
+ {
+ std::cerr << "JidDb::ownKeyId: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ return QByteArray();
+ }
+
+ while (q.next())
+ {
+ const auto id = q.value("id").toString();
+
+ return QByteArray::fromBase64(id.toLatin1());
+ }
+
+ return QByteArray();
+}
+
+int JidDb::store(const QString &encryption, const QByteArray &ownId) const
+{
+ QString table;
+
+ if (ensureOwnIdTable(encryption, table))
+ {
+ std::cerr << "JidDb::store: ensureOmemoOwnDeviceTable failed"
+ << std::endl;
+ return -1;
+ }
+
+ QSqlQuery q(db);
+ const auto id = ownId.isEmpty() ? "\"\""
+ : "'" + QString::fromLatin1(ownId.toBase64()) + "'",
+ query = "insert or replace into '" + table + "' "
+ "(id) values ("
+ + id
+ + ");";
+
+ return 0;
+}
+
+void JidDb::removeSignedPreKeyPair(const uint32_t id) const
+{
+ if (ensureOmemoSignedPreKeyTable())
+ std::cerr << "JidDb::removeSignedPreKeyPair: "
+ "ensureOmemoSignedPreKeyTable failed" << std::endl;
+ else
+ {
+ QSqlQuery q(db);
+ const auto idstr = QString::number(id);
+
+ if (!q.exec("delete from 'omemo/signedprekeys'"
+ "where id = " + id))
+ std::cerr << "JidDb::removeSignedPreKeyPair: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ }
+}
+
+void JidDb::removePreKeyPair(const uint32_t id) const
+{
+ if (ensureOmemoPreKeyTable())
+ std::cerr << "JidDb::removePreKeyPair: "
+ "ensureOmemoPreKeyTable failed" << std::endl;
+ else
+ {
+ QSqlQuery q(db);
+ const auto idstr = QString::number(id);
+
+ if (!q.exec("delete from 'omemo/prekeys'"
+ "where id = " + id))
+ std::cerr << "JidDb::removePreKeyPair: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ }
+}
+
+void JidDb::removeOmemo() const
+{
+ if (ensureOmemoSignedPreKeyTable())
+ std::cerr << "JidDb::removeOmemo: "
+ "ensureOmemoSignedPreKeyTable failed" << std::endl;
+ else if (ensureOmemoPreKeyTable())
+ std::cerr << "JidDb::removeOmemo: "
+ "ensureOmemoPreKeyTable failed" << std::endl;
+ else
+ {
+ QSqlQuery q(db);
+
+ if (!q.exec("delete from 'omemo/signedprekeys'"))
+ std::cerr << "JidDb::removeOmemo: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ else
+ {
+ QSqlQuery q(db);
+
+ if (!q.exec("delete from 'omemo/prekeys'"))
+ std::cerr << "JidDb::removeOmemo: query exec failed: "
+ << qPrintable(q.lastError().text()) << std::endl;
+ }
+ }
+}
diff --git a/jiddb.h b/jiddb.h
index a9f9c8b..6c9dcb8 100644
--- a/jiddb.h
+++ b/jiddb.h
@@ -2,12 +2,17 @@
#define JID_DB_H
#include "direction.h"
+#include <QByteArray>
#include <QDateTime>
#include <QList>
#include <QObject>
+#include <QPair>
#include <QString>
#include <QStringList>
#include <QSqlDatabase>
+#include <QXmppOmemoStorage.h>
+#include <QVariant>
+#include <optional>
class JidDb : public QObject
{
@@ -30,6 +35,30 @@ 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;
+ };
+
+ struct OmemoDevice
+ {
+ QString jid;
+ QHash<uint32_t, QXmppOmemoStorage::Device> devices;
+ };
+
JidDb(const QString &jid);
QStringList roster() const;
const QString &jid;
@@ -38,16 +67,47 @@ public Q_SLOTS:
QList<Conversation> conversations() const;
QList<Message> messages(const QString &jid,
int tail = -1) const;
- int storeMessage(const Message &msg) const;
+ QList<Keys> keys(const QString &encryption) const;
+ QString securityPolicy(const QString &encryption) const;
+ QList<OmemoDevice> omemoDevices() const;
+ std::optional<QXmppOmemoStorage::OwnDevice> omemoOwnDevice() const;
+ QHash<uint32_t, QXmppOmemoStorage::SignedPreKeyPair> signedPreKeyPairs()
+ const;
+ QHash<uint32_t, QByteArray> preKeyPairs() const;
+ QByteArray ownKeyId(const QString &encryption) const;
int addToRoster(const QString &jid);
int addToRoster(const QStringList &roster);
+ int store(const Message &msg) const;
+ int store(const QString &encryption,
+ const QString &securityPolicy) const;
+ int store(const QString &encryption, const Keys &keys) const;
+ int store(const QString &jid, uint32_t id,
+ const QXmppOmemoStorage::Device &d) const;
+ int store(uint32_t id, const QXmppOmemoStorage::SignedPreKeyPair &spk) const;
+ int store(const QHash<uint32_t, QByteArray> &pairs) const;
+ int store(const QXmppOmemoStorage::OwnDevice &own) const;
+ int store(const QString &encryption, const QByteArray &ownId) const;
+ void removeKeys(const QString &encryption) const;
+ void removeOmemoDevice(const QString &jid, uint32_t id) const;
+ void removeOmemoDevices(const QString &jid) const;
+ void removeOmemo() const;
+ void removeSignedPreKeyPair(uint32_t id) const;
+ void removePreKeyPair(uint32_t id) 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;
+ int ensureOmemoDeviceTable() const;
+ int ensureOmemoSignedPreKeyTable() const;
+ int ensureOmemoPreKeyTable() const;
+ int ensureOmemoOwnDeviceTable() const;
+ int ensureOwnIdTable(const QString &encryption, QString &table) const;
QStringList tables() const;
};
diff --git a/omemo_db.cpp b/omemo_db.cpp
index bc8751b..c1949c0 100644
--- a/omemo_db.cpp
+++ b/omemo_db.cpp
@@ -1,374 +1,93 @@
#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_ns = "xxcc/omemo/";
+static const QString service = "xxcc", key_namespace = "omemo";
-OmemoDb::OmemoDb(const QString &jid) :
- jid(jid)
+OmemoDb::OmemoDb(const JidDb &db) :
+ db(db)
{
}
-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)
-{
- 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;
+ QXmppOmemoStorage::OmemoData ret;
+ QHash<QString, QHash<uint32_t, Device>> devices;
- loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
- job.setKey("device/latestPreKeyId");
- job.setTextData(QString::number(latestPreKeyId, 16));
- job.start();
- loop.exec();
+ for (const auto &d : db.omemoDevices())
+ ret.devices.insert(d.jid, d.devices);
- if (job.error())
- {
- std::cerr << "Failed to store latest prekey ID: "
- << qPrintable(job.errorString()) << std::endl;
- return -1;
- }
-
- return 0;
+ ret.ownDevice = db.omemoOwnDevice();
+ ret.preKeyPairs = db.preKeyPairs();
+ ret.signedPreKeyPairs = db.signedPreKeyPairs();
+ return QXmpp::Private::makeReadyTask(QXmppOmemoStorage::OmemoData(ret));
}
QXmppTask<void> OmemoDb::setOwnDevice(const std::optional<OwnDevice> &device)
{
- if (!device.has_value())
- return QXmpp::Private::makeReadyTask();
-
- QXmppPromise<void> promise;
-
- auto future = QtConcurrent::run(
- [=] () 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();
- });
+ if (device.has_value() && db.store(device.value()))
+ std::cerr << "db.store failed" << std::endl;
- return promise.task();
+ return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::addSignedPreKeyPair(const uint32_t keyId,
const SignedPreKeyPair &keyPair)
{
+ if (db.store(keyId, keyPair))
+ std::cerr << "db.store failed" << std::endl;
+
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::removeSignedPreKeyPair(uint32_t keyId)
{
+ db.removeSignedPreKeyPair(keyId);
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::addPreKeyPairs(
const QHash<uint32_t, QByteArray> &keyPairs)
{
+ if (db.store(keyPairs))
+ std::cerr << "db.store failed" << std::endl;
+
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::removePreKeyPair(const uint32_t keyId)
{
+ db.removePreKeyPair(keyId);
return QXmpp::Private::makeReadyTask();
}
-int OmemoDb::storeDeviceLabel(const QString &service, const QString &label)
-{
- QKeychain::WritePasswordJob job(service);
- QEventLoop loop;
-
- loop.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
- job.setKey("device/label");
- job.setTextData(label);
- job.start();
- loop.exec();
-
- if (job.error())
- {
- std::cerr << "Failed to store device label: "
- << qPrintable(job.errorString()) << std::endl;
- return -1;
- }
-
- return 0;
-}
-
-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;
-}
-
-int OmemoDb::storeSession(const QString &service, const QByteArray &session)
-{
- QKeychain::WritePasswordJob job(service);
- QEventLoop loop;
-
- 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;
-}
-
-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 uint32_t deviceId, const Device &d)
{
- const QString fullservice = service_ns + jid;
- 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);
+ if (db.store(jid, deviceId, d))
+ std::cerr << "db.store failed" << std::endl;
- promise.finish();
- });
-
- return promise.task();
+ return QXmpp::Private::makeReadyTask();
}
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
+ db.removeOmemoDevice(jid, deviceId);
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::removeDevices(const QString &jid)
{
+ db.removeOmemoDevices(jid);
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::resetAll()
{
+ db.removeOmemo();
return QXmpp::Private::makeReadyTask();
}
diff --git a/omemo_db.h b/omemo_db.h
index d331d8f..da3fd93 100644
--- a/omemo_db.h
+++ b/omemo_db.h
@@ -1,17 +1,24 @@
#ifndef OMEMO_DB_H
#define OMEMO_DB_H
+#include "jiddb.h"
+#include <QXmppGlobal.h>
#include <QXmppOmemoStorage.h>
+#include <QXmppPromise.h>
#include <QXmppTask.h>
+#include <qt5keychain/keychain.h>
#include <QDateTime>
#include <QByteArray>
#include <QString>
+#include <QSqlDatabase>
#include <cstdint>
-class OmemoDb : public QXmppOmemoStorage
+class OmemoDb : public QObject, public QXmppOmemoStorage
{
+ Q_OBJECT
+
public:
- OmemoDb(const QString &jid);
+ OmemoDb(const JidDb &db);
QXmppTask<OmemoData> allData() override;
QXmppTask<void> setOwnDevice(
const std::optional<OwnDevice> &device) override;
@@ -29,26 +36,7 @@ public:
QXmppTask<void> resetAll() override;
private:
- 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);
+ const JidDb &db;
};
#endif
diff --git a/trust_db.cpp b/trust_db.cpp
index 138c028..0d8073b 100644
--- a/trust_db.cpp
+++ b/trust_db.cpp
@@ -1,8 +1,9 @@
#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>
@@ -13,16 +14,44 @@ TrustDb::TrustDb(const QString &jid, const JidDb &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)
{
- switch (securityPolicy)
- {
- case QXmpp::NoSecurityPolicy:
- return "NoSecurityPolicy";
+ for (const auto &t : tsp_levels)
+ if (t.policy == securityPolicy)
+ return t.str;
- case QXmpp::Toakafa:
- return "Toakafa";
- }
+ return "unknown";
+}
+
+static QString toString(const QXmpp::TrustLevel &trustLevel)
+{
+ for (const auto &t : trustlevels)
+ if (t.level == trustLevel)
+ return t.str;
return "unknown";
}
@@ -30,16 +59,24 @@ static QString toString(const QXmpp::TrustSecurityPolicy securityPolicy)
static int toSecurityPolicy(const QString &s,
QXmpp::TrustSecurityPolicy &securityPolicy)
{
- if (s == "NoSecurityPolicy")
- {
- securityPolicy = QXmpp::NoSecurityPolicy;
- return 0;
- }
- else if (s == "Toakafa")
- {
- securityPolicy = QXmpp::Toakafa;
- return 0;
- }
+ 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;
}
@@ -52,35 +89,18 @@ QString TrustDb::service() const
QXmppTask<void> TrustDb::setSecurityPolicy(const QString &encryption,
const QXmpp::TrustSecurityPolicy securityPolicy)
{
- QKeychain::WritePasswordJob job(service());
- QEventLoop loop;
-
- job.setKey("securityPolicy/" + encryption);
- job.setTextData(toString(securityPolicy));
- job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
- job.start();
- loop.exec();
-
- if (job.error())
- std::cerr << "Failed to store security policy: "
- << qPrintable(job.errorString()) << std::endl;
+ if (db.store(encryption, toString(securityPolicy)))
+ std::cerr << "TrustDb::setSecurityPolicy: store failed"
+ << std::endl;
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> TrustDb::resetSecurityPolicy(const QString &encryption)
{
- QKeychain::DeletePasswordJob job(service());
- QEventLoop loop;
-
- job.setKey("securityPolicy/" + encryption);
- job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
- job.start();
- loop.exec();
-
- if (job.error())
- std::cerr << "Failed to reset security policy: "
- << qPrintable(job.errorString()) << std::endl;
+ if (db.store(encryption, QString()))
+ std::cerr << "TrustDb::resetSecurityPolicy: store failed"
+ << std::endl;
return QXmpp::Private::makeReadyTask();
}
@@ -88,23 +108,11 @@ QXmppTask<void> TrustDb::resetSecurityPolicy(const QString &encryption)
QXmppTask<QXmpp::TrustSecurityPolicy> TrustDb::securityPolicy(
const QString &encryption)
{
- QKeychain::ReadPasswordJob job(service());
- QEventLoop loop;
-
- job.setKey("securityPolicy/" + encryption);
- job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
- job.start();
- loop.exec();
+ const auto s = db.securityPolicy(encryption);
+ QXmpp::TrustSecurityPolicy policy;
- if (job.error())
- std::cerr << "Failed to read security policy: "
- << qPrintable(job.errorString()) << std::endl;
-
- QXmpp::TrustSecurityPolicy policy = QXmpp::Toakafa;
- const QString data = job.textData();
-
- if (toSecurityPolicy(data, policy))
- std::cerr << "Invalid security policy " << qPrintable(data)
+ if (toSecurityPolicy(s, policy))
+ std::cerr << "TrustDb::securityPolicy: toSecurityPolicy failed"
<< std::endl;
return QXmpp::Private::makeReadyTask(QXmpp::TrustSecurityPolicy(policy));
@@ -113,81 +121,80 @@ QXmppTask<QXmpp::TrustSecurityPolicy> TrustDb::securityPolicy(
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;
+ if (db.store(encryption, keyId))
+ std::cerr << "TrustDb::setOwnKey: db.store failed" << 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));
+ return QXmpp::Private::makeReadyTask(db.ownKeyId(encryption));
}
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.store(encryption, keys))
+ std::cerr << "TrustDb::addKeys: store 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.store(encryption, k))
+ std::cerr << "TrustDb::removeKeys: store 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.store(encryption, k))
+ std::cerr << "TrustDb::removeKeys: store failed" << std::endl;
+
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> TrustDb::removeKeys(const QString &encryption)
{
+ db.removeKeys(encryption);
return QXmpp::Private::makeReadyTask();
}
@@ -195,8 +202,32 @@ 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>>());
+ QMultiHash<QString, QByteArray>>(ret));
}
QXmppTask<QHash<QString,
@@ -204,14 +235,66 @@ QXmppTask<QHash<QString,
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>>());
+ QHash<QByteArray, QXmpp::TrustLevel>>(ret));
}
QXmppTask<bool> TrustDb::hasKey(const QString &encryption,
const QString &keyOwnerJid, QXmpp::TrustLevels trustLevels)
{
- return QXmpp::Private::makeReadyTask(bool());
+ 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,
@@ -220,8 +303,33 @@ QXmppTask<QHash<QString,
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.store(encryption, key))
+ std::cerr << "TrustDb::setTrustLevel: store failed"
+ << std::endl;
+
return QXmpp::Private::makeReadyTask(QHash<QString,
- QMultiHash<QString, QByteArray>>());
+ QMultiHash<QString, QByteArray>>(ret));
}
QXmppTask<QHash<QString,
@@ -231,17 +339,77 @@ QXmppTask<QHash<QString,
const QXmpp::TrustLevel oldTrustLevel,
const QXmpp::TrustLevel newTrustLevel)
{
+ QHash<QString, QMultiHash<QString, QByteArray>> ret;
+ auto keys = db.keys(encryption);
+ const auto str = toString(newTrustLevel);
+
+ for (const auto &owner : keyOwnerJids)
+ for (auto &k : keys)
+ {
+ QXmpp::TrustLevel trust_level;
+
+ if (k.owner != owner
+ || toTrustLevel(k.trust_level, trust_level)
+ || trust_level != oldTrustLevel)
+ 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.store(encryption, key))
+ std::cerr << "TrustDb::setTrustLevel: store failed" << std::endl;
+
return QXmpp::Private::makeReadyTask(QHash<QString,
- QMultiHash<QString, QByteArray>>());
+ QMultiHash<QString, QByteArray>>(ret));
}
QXmppTask<QXmpp::TrustLevel> TrustDb::trustLevel(const QString &encryption,
const QString &keyOwnerJid, const QByteArray &keyId)
{
- return QXmpp::Private::makeReadyTask(QXmpp::TrustLevel());
+ const auto keys = db.keys(encryption);
+
+ for (const auto &k : keys)
+ {
+ if (k.owner != keyOwnerJid)
+ continue;
+
+ for (const auto &id : k.keys)
+ if (id == keyId)
+ {
+ QXmpp::TrustLevel ret(QXmpp::TrustLevel::Undecided);
+
+ if (toTrustLevel(k.trust_level, ret))
+ std::cerr << "toTrustLevel failed" << std::endl;
+
+ return QXmpp::Private::makeReadyTask( QXmpp::TrustLevel(ret));
+ }
+ }
+
+ return QXmpp::Private::makeReadyTask(QXmpp::TrustLevel(
+ QXmpp::TrustLevel::Undecided));
}
QXmppTask<void> TrustDb::resetAll(const QString &encryption)
{
+ db.removeKeys(encryption);
+
return QXmpp::Private::makeReadyTask();
}
+
+namespace QXmpp
+{
+
+uint qHash(const TrustLevel &key, uint seed)
+{
+ return qHash(key, seed);
+}
+
+}
diff --git a/xxcc.cpp b/xxcc.cpp
index b55d58a..760fd12 100644
--- a/xxcc.cpp
+++ b/xxcc.cpp
@@ -6,32 +6,56 @@
#include "conversation.h"
#include "message.h"
#include <QXmppMessage.h>
+#include <QXmppOmemoElement_p.h>
#include <QXmppRosterManager.h>
#include <QXmppUtils.h>
#include <QKeyEvent>
#include <QPushButton>
#include <QScroller>
+#include <iostream>
#include <stdexcept>
#include <utility>
+#include <variant>
xxcc::xxcc(QWidget *const parent) :
QWidget(parent),
selected(nullptr)
{
- const auto pairs = creds.load();
-
ui.setupUi(this);
QScroller::grabGesture(ui.conversations_list, QScroller::TouchGesture);
QScroller::grabGesture(ui.messages, QScroller::TouchGesture);
- connectAccounts(pairs);
- retrieveConversations();
+
+ creds.load().then(this,
+ [=] (Credentials::PairListResult &&result)
+ {
+ if (std::holds_alternative<Credentials::PairList>(result))
+ {
+ const auto &pairs = std::get<Credentials::PairList>(result);
+
+ connectAccounts(pairs);
+ retrieveConversations();
+ }
+ else if (std::holds_alternative<Credentials::Error>(result))
+ {
+ const auto &error = std::get<Credentials::Error>(result);
+
+ std::cerr << qPrintable(error.description) << std::endl;
+ }
+ });
+
+
connect(ui.accounts, &QPushButton::released, this,
[this]
{
Accounts a(clients, this);
a.connect(&a, &Accounts::new_account, this, &xxcc::addAccount);
- a.connect(&a, &Accounts::new_account, &creds, &Credentials::store);
+ a.connect(&a, &Accounts::new_account, this,
+ [&] (Client *c)
+ {
+ creds.store(c);
+ });
+
a.exec();
});
@@ -157,9 +181,9 @@ void xxcc::addInMessage(const QXmppMessage &msg)
ui.messages);
}
-void xxcc::addOutMessage(const QXmppMessage &msg)
+void xxcc::addOutMessage(const QString &msg, const QDateTime &dt)
{
- new Message(msg.body(), msg.stamp().toLocalTime(), Direction::Out,
+ new Message(msg, dt.toLocalTime(), Direction::Out,
ui.messages);
}
@@ -194,6 +218,8 @@ void xxcc::addAccount(Client *const c)
throw std::runtime_error("Expected non-null QXmppRosterManager");
}
+#include <QDebug>
+
void xxcc::send(void)
{
if (!selected)
@@ -201,30 +227,52 @@ void xxcc::send(void)
const auto from = selected->jidBare(),
to = ui.jid->text(), msg = ui.chatinput->toPlainText();
-
+ const bool enc = ui.omemo->isChecked();
QXmppMessage out(from, to, msg);
+ const auto dt = QDateTime::currentDateTimeUtc();
+
+ out.setE2eeFallbackBody("[xxcc: This is an OMEMO-encrypted message]");
+ out.setStamp(dt);
+ // TODO: QXmpp forces OMEMO 2 (XEP-0384 version >= 0.8.0).
+ // This breaks compatibility with Dino and Gajim, who still use 0.1.0.
+ // out.setEncryptionMethod(QXmpp::Omemo0);
+
+ selected->sendSensitive(std::move(out)).then(this,
+ [=](const QXmpp::SendResult &&result) mutable
+ {
+ qDebug() << "result.index(): " << result.index();
- out.setStamp(QDateTime::currentDateTimeUtc());
- selected->sendPacket(out);
- addOutMessage(out);
- storeMessage(out, Direction::Out);
- ui.chatinput->clear();
+ if (std::holds_alternative<QXmpp::SendSuccess>(result))
+ {
+ const auto &success = std::get<QXmpp::SendSuccess>(result);
+ qDebug() << "acknowledged: " << success.acknowledged;
+ addOutMessage(msg, dt);
+ storeMessage(from, to, msg, dt, Direction::Out);
+ ui.chatinput->clear();
+ }
+ else if (std::holds_alternative<QXmppError>(result))
+ {
+ const auto &error = std::get<QXmppError>(result);
+ qDebug() << error.description;
+ }
+ });
}
-void xxcc::storeMessage(const QXmppMessage &msg, const Direction dir) const
+void xxcc::storeMessage(const QString &from, const QString &to,
+ const QString &msg, const QDateTime &dt, const Direction dir) const
{
QString jid, contact;
switch (dir)
{
case Direction::In:
- jid = QXmppUtils::jidToBareJid(msg.to());
- contact = QXmppUtils::jidToBareJid(msg.from());
+ jid = QXmppUtils::jidToBareJid(to);
+ contact = QXmppUtils::jidToBareJid(from);
break;
case Direction::Out:
- jid = msg.from();
- contact = msg.to();
+ jid = from;
+ contact = to;
break;
}
@@ -235,15 +283,20 @@ void xxcc::storeMessage(const QXmppMessage &msg, const Direction dir) const
const auto &db = c->database();
JidDb::Message m;
- m.body = msg.body();
- m.dt = msg.stamp();
+ m.body = msg;
+ m.dt = dt;
m.direction = dir;
m.contact = contact;
- db.storeMessage(m);
+ db.store(m);
}
}
}
+void xxcc::storeMessage(const QXmppMessage &msg, const Direction dir) const
+{
+ storeMessage(msg.from(), msg.to(), msg.body(), msg.stamp(), dir);
+}
+
void xxcc::retrieveConversations()
{
for (const auto c : clients)
diff --git a/xxcc.h b/xxcc.h
index d2f9496..f4825e1 100644
--- a/xxcc.h
+++ b/xxcc.h
@@ -28,12 +28,14 @@ private:
Client *selected;
void connectAccounts(const QList<Credentials::Pair> &pairs);
void storeMessage(const QXmppMessage &msg, Direction dir) const;
+ void storeMessage(const QString &from, const QString &to,
+ const QString &msg, const QDateTime &dt, Direction dir) const;
void retrieveConversations();
private Q_SLOTS:
void startChat(QString from, QString to);
void addInMessage(const QXmppMessage &msg);
- void addOutMessage(const QXmppMessage &msg);
+ void addOutMessage(const QString &msg, const QDateTime &dt);
void addAccount(Client *c);
void send(void);
};
diff --git a/xxcc.ui b/xxcc.ui
index 12cf74a..e338d47 100644
--- a/xxcc.ui
+++ b/xxcc.ui
@@ -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>