Compare commits
2 Commits
e94036f3bc
...
e3deb06e5f
Author | SHA1 | Date |
---|---|---|
Xavier Del Campo Romero | e3deb06e5f | |
Xavier Del Campo Romero | 46e0a3884f |
|
@ -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)
|
||||
|
|
249
credentials.cpp
249
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)
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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
169
jiddb.cpp
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
8
jiddb.h
8
jiddb.h
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
101
trust_db.cpp
101
trust_db.cpp
|
@ -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
101
xxcc.cpp
|
@ -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
4
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);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue