Compare commits

...

2 Commits

Author SHA1 Message Date
Xavier Del Campo Romero e3deb06e5f
OMEMO 2023-09-04 01:30:18 +02:00
Xavier Del Campo Romero 46e0a3884f
WIP async credentials 2023-09-03 22:38:33 +02:00
9 changed files with 512 additions and 161 deletions

View File

@ -35,8 +35,8 @@ Client::Client(const QString &jid, QObject *const parent) :
qDebug() << "load result: " << result;
});
logger()->setLoggingType(QXmppLogger::StdoutLogging);
logger()->setMessageTypes(QXmppLogger::AnyMessage);
logger()->setLoggingType(QXmppLogger::SignalLogging);
logger()->setMessageTypes(QXmppLogger::DebugMessage);
connect(logger(), &QXmppLogger::message, this,
[=] (QXmppLogger::MessageType type, const QString &text)

View File

@ -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)
QXmppPromise<PairListResult> promise;
load(*it).then(this,
[=] (LoadResult &&result) mutable
{
QKeychain::ReadPasswordJob job(service);
QEventLoop loop;
if (std::holds_alternative<QString>(result))
{
pairs << Pair(*it, std::get<QString>(result));
job.setKey(user);
job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.start();
loop.exec();
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)});
});
const QString pwd = job.textData();
if (job.error())
std::cerr << "Failed to retrieve password for " << qPrintable(user)
<< ": " << qPrintable(job.errorString()) << std::endl;
else
ret.append(Pair(user, pwd));
}
return ret;
return promise.task();
}
void Credentials::store(Client *c)
QXmppTask<Credentials::PairListResult> Credentials::load()
{
QKeychain::WritePasswordJob job(service);
QXmppPromise<PairListResult> promise;
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();
}
QXmppTask<Credentials::StoreResult> Credentials::store(Client *c)
{
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();
store(user, cfg.password()).then(this,
[=] (StoreResult &&result) mutable
{
store(user).then(this,
[=] (StoreResult &&result) mutable
{
promise.finish(result);
});
});
if (job.error())
std::cerr << "Failed to store password: "
<< qPrintable(job.errorString()) << std::endl;
else
storeUser(user);
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;
if (!list.isEmpty())
list += sep;
storedUsersList().then(this,
[=] (LoadResult &&result) mutable
{
if (std::holds_alternative<QString>(result))
{
auto &list = std::get<QString>(result);
list += user;
if (!list.isEmpty())
list += sep;
job.setKey("users");
job.setTextData(list);
job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.start();
loop.exec();
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);
});
}
});
if (job.error())
std::cerr << "Failed to store user: "
<< qPrintable(job.errorString()) << std::endl;
return promise.task();
}
QString Credentials::storedUsersList()
QXmppTask<Credentials::LoadResult> Credentials::storedUsersList()
{
QKeychain::ReadPasswordJob job(service);
QString ret;
QEventLoop loop;
QXmppPromise<LoadResult> promise;
job.setKey("users");
job.connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.start();
loop.exec();
load("users").then(this,
[=] (LoadResult &&result) mutable
{
promise.finish(result);
});
const QString users = job.textData();
return promise.task();
if (job.error())
std::cerr << "Failed to retrieve users: "
<< qPrintable(job.errorString()) << std::endl;
else
ret = users;
return ret;
}
QStringList Credentials::storedUsers()
QXmppTask<Credentials::LoadResult> Credentials::load(const QString &key)
{
return storedUsersList().split(sep);
QXmppPromise<LoadResult> promise;
auto job = new QKeychain::ReadPasswordJob(service);
job->setKey(key);
job->connect(job, &QKeychain::Job::finished, this,
[=] (QKeychain::Job *const job) mutable
{
auto readjob = dynamic_cast<QKeychain::ReadPasswordJob *>(job);
if (readjob->error())
{
const auto error = "Failed to load " + key + ": "
+ readjob->errorString();
std::cerr << qPrintable(error) << std::endl;
promise.finish(Error{error});
}
else
{
auto users = readjob->textData();
promise.finish(QString(users));
}
});
job->start();
return promise.task();
}
QXmppTask<Credentials::StoreResult> Credentials::store(const QString &key,
const QString &value)
{
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();
}

View File

@ -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:
using Pair = QPair<QString, QString>;
QList<Pair> load();
struct Success {};
struct Error
{
QString description;
};
public Q_SLOTS:
void store(Client *c);
using Pair = QPair<QString, QString>;
using PairList = QList<Pair>;
using StoreResult = std::variant<Success, Error>;
using LoadResult = std::variant<QString, Error>;
using PairListResult = std::variant<PairList, Error>;
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

169
jiddb.cpp
View File

@ -734,14 +734,181 @@ QHash<uint32_t, QByteArray> JidDb::preKeyPairs() const
return ret;
}
void JidDb::removeKeys() const
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;
}
}
}

View File

@ -74,6 +74,7 @@ public Q_SLOTS:
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;
@ -85,9 +86,13 @@ public Q_SLOTS:
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;
void removeKeys() 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);
@ -102,6 +107,7 @@ private:
int ensureOmemoSignedPreKeyTable() const;
int ensureOmemoPreKeyTable() const;
int ensureOmemoOwnDeviceTable() const;
int ensureOwnIdTable(const QString &encryption, QString &table) const;
QStringList tables() const;
};

View File

@ -45,6 +45,7 @@ QXmppTask<void> OmemoDb::addSignedPreKeyPair(const uint32_t keyId,
QXmppTask<void> OmemoDb::removeSignedPreKeyPair(uint32_t keyId)
{
db.removeSignedPreKeyPair(keyId);
return QXmpp::Private::makeReadyTask();
}
@ -59,6 +60,7 @@ QXmppTask<void> OmemoDb::addPreKeyPairs(
QXmppTask<void> OmemoDb::removePreKeyPair(const uint32_t keyId)
{
db.removePreKeyPair(keyId);
return QXmpp::Private::makeReadyTask();
}
@ -80,10 +82,12 @@ QXmppTask<void> OmemoDb::removeDevice(const QString &jid,
QXmppTask<void> OmemoDb::removeDevices(const QString &jid)
{
db.removeOmemoDevices(jid);
return QXmpp::Private::makeReadyTask();
}
QXmppTask<void> OmemoDb::resetAll()
{
db.removeOmemo();
return QXmpp::Private::makeReadyTask();
}

View File

@ -4,7 +4,6 @@
#include <QXmppConfiguration.h>
#include <QXmppFutureUtils_p.h>
#include <QXmppPromise.h>
#include <qt5keychain/keychain.h>
#include <QEventLoop>
#include <QtConcurrent>
#include <iostream>
@ -122,58 +121,20 @@ 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,
@ -233,7 +194,7 @@ QXmppTask<void> TrustDb::removeKeys(const QString &encryption,
QXmppTask<void> TrustDb::removeKeys(const QString &encryption)
{
db.removeKeys();
db.removeKeys(encryption);
return QXmpp::Private::makeReadyTask();
}
@ -378,18 +339,68 @@ 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();
}

101
xxcc.cpp
View File

@ -12,6 +12,7 @@
#include <QKeyEvent>
#include <QPushButton>
#include <QScroller>
#include <iostream>
#include <stdexcept>
#include <utility>
#include <variant>
@ -20,20 +21,41 @@ 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();
});
@ -159,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);
}
@ -207,50 +229,50 @@ void xxcc::send(void)
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(QDateTime::currentDateTimeUtc());
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);
if (enc)
selected->sendSensitive(std::move(out)).then(this,
[=](const QXmpp::SendResult &&result) mutable
{
selected->sendSensitive(std::move(out)).then(this,
[=](const QXmpp::SendResult &&result) mutable
{
qDebug() << "result.index(): " << result.index();
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;
}
});
}
else
selected->sendPacket(out);
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;
}
@ -261,8 +283,8 @@ 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.store(m);
@ -270,6 +292,11 @@ void xxcc::storeMessage(const QXmppMessage &msg, const Direction dir) const
}
}
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)

4
xxcc.h
View File

@ -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);
};