forked from blue/squawk
feat: omemo signal lib wip
This commit is contained in:
parent
08fe37bfb2
commit
442ad37300
@ -9,6 +9,8 @@ target_sources(squawk PRIVATE
|
|||||||
device_key_storage.h
|
device_key_storage.h
|
||||||
device_service.cpp
|
device_service.cpp
|
||||||
device_service.h
|
device_service.h
|
||||||
|
key.cpp
|
||||||
|
key.h
|
||||||
qomemo.cpp
|
qomemo.cpp
|
||||||
qomemo.h
|
qomemo.h
|
||||||
sce.cpp
|
sce.cpp
|
||||||
|
@ -3,127 +3,3 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "bundle.h"
|
#include "bundle.h"
|
||||||
|
|
||||||
#include <QXmppPubSubIq.h>
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "shared/qxmppfactories.h"
|
|
||||||
|
|
||||||
using namespace QXmpp::Factories;
|
|
||||||
|
|
||||||
QXmppElement QXmpp::Omemo::PreKey::toXml() const {
|
|
||||||
auto pk = createElement("preKeyPublic");
|
|
||||||
pk.setAttribute("preKeyId", QString::number(id));
|
|
||||||
// TODO: Base64
|
|
||||||
pk.setValue(data);
|
|
||||||
|
|
||||||
return pk;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) {
|
|
||||||
if (!elementMatches(element, "preKeyPublic"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
id = element.attribute("preKeyId").toInt();
|
|
||||||
// TODO: Base64
|
|
||||||
data = element.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
QXmppElement QXmpp::Omemo::Bundle::toXml() const {
|
|
||||||
auto spkNode = createElement("signedPreKeyPublic");
|
|
||||||
spkNode.setAttribute("signedPreKeyId", QString::number(spkId));
|
|
||||||
spkNode.setValue(spk);
|
|
||||||
|
|
||||||
auto spksNode = createElement("signedPreKeySignature");
|
|
||||||
spksNode.setValue(spks);
|
|
||||||
|
|
||||||
auto ikNode = createElement("identityKey");
|
|
||||||
ikNode.setValue(ik);
|
|
||||||
|
|
||||||
auto prekeysNode = createElement("prekeys");
|
|
||||||
for (const auto &pk : prekeys) {
|
|
||||||
prekeysNode.appendChild(pk.toXml());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = createElement("bundle", "eu.siacs.conversations.axolotl");
|
|
||||||
result.appendChild(spkNode);
|
|
||||||
result.appendChild(spksNode);
|
|
||||||
result.appendChild(ikNode);
|
|
||||||
result.appendChild(prekeysNode);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const {
|
|
||||||
QXmppIq iq{};
|
|
||||||
|
|
||||||
iq.setType(QXmppIq::Set);
|
|
||||||
|
|
||||||
auto item = createElement("item");
|
|
||||||
item.appendChild(toXml());
|
|
||||||
|
|
||||||
auto publish = createElement("publish");
|
|
||||||
publish.setAttribute(
|
|
||||||
"node",
|
|
||||||
QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId));
|
|
||||||
publish.appendChild(item);
|
|
||||||
|
|
||||||
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
|
|
||||||
pubSub.appendChild(publish);
|
|
||||||
pubSub.appendChild(createOpenPublishOptions());
|
|
||||||
|
|
||||||
iq.extensions().push_back(pubSub);
|
|
||||||
|
|
||||||
return iq;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) {
|
|
||||||
if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto spkNode = element.firstChildElement("spk");
|
|
||||||
if (spkNode.isNull()) {
|
|
||||||
qWarning() << "'bundle': missing 'spk'";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spk = spkNode.value();
|
|
||||||
spkId = spkNode.attribute("id").toInt();
|
|
||||||
|
|
||||||
auto spksNode = element.firstChildElement("spks");
|
|
||||||
if (spksNode.isNull()) {
|
|
||||||
qWarning() << "'bundle': missing 'spks'";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spks = spksNode.value();
|
|
||||||
|
|
||||||
auto ikNode = element.firstChildElement("ik");
|
|
||||||
if (ikNode.isNull()) {
|
|
||||||
qWarning() << "'bundle': missing 'ik'";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ik = ikNode.value();
|
|
||||||
|
|
||||||
auto prekeysNode = element.firstChildElement("prekeys");
|
|
||||||
auto pkNode = prekeysNode.firstChildElement("pk");
|
|
||||||
|
|
||||||
prekeys.clear();
|
|
||||||
while (!pkNode.isNull()) {
|
|
||||||
PreKey pk{};
|
|
||||||
pk.fromXml(pkNode);
|
|
||||||
prekeys.push_back(pk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) {
|
|
||||||
QXmppPubSubIq iq{};
|
|
||||||
iq.setType(QXmppIq::Get);
|
|
||||||
iq.setQueryNode(
|
|
||||||
QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId));
|
|
||||||
|
|
||||||
QXmppPubSubItem item{};
|
|
||||||
item.setId(QString::number(deviceId));
|
|
||||||
iq.setItems({item});
|
|
||||||
|
|
||||||
return iq;
|
|
||||||
}
|
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
class QXmppPubSubIq;
|
class QXmppPubSubIq;
|
||||||
|
|
||||||
@ -17,26 +18,16 @@ namespace QXmpp::Omemo {
|
|||||||
|
|
||||||
class PreKey {
|
class PreKey {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] QXmppElement toXml() const;
|
|
||||||
/// Expects a <pk>
|
|
||||||
void fromXml(const QXmppElement &element);
|
|
||||||
|
|
||||||
int id;
|
int id;
|
||||||
QString data;
|
QByteArray data;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Bundle {
|
class Bundle {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId);
|
QByteArray spk;
|
||||||
|
|
||||||
[[nodiscard]] QXmppElement toXml() const;
|
|
||||||
[[nodiscard]] QXmppIq toIq(int deviceId) const;
|
|
||||||
void fromXml(const QXmppElement &element);
|
|
||||||
|
|
||||||
QString spk;
|
|
||||||
int spkId;
|
int spkId;
|
||||||
QString spks;
|
QByteArray spks;
|
||||||
QString ik;
|
QByteArray ik;
|
||||||
QList<PreKey> prekeys;
|
QList<PreKey> prekeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
|
||||||
|
#include "bundle.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QException>
|
#include <QException>
|
||||||
@ -44,17 +46,17 @@ Database::~Database() {
|
|||||||
mdb_env_close(env);
|
mdb_env_close(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
QBuffer Database::loadIdentityKeySecret(int deviceId) { return QBuffer(); }
|
std::optional<QByteArray> Database::loadIdentityKeySecret(int deviceId) { return std::nullopt; }
|
||||||
|
|
||||||
bool Database::saveIdentityKeySecret(int deviceId,
|
bool Database::saveIdentityKeySecret(int deviceId,
|
||||||
const QBuffer &identityKeySecret) {
|
const QByteArray &identityKeySecret) {
|
||||||
MDB_val mdbKey, mdbValue;
|
MDB_val mdbKey, mdbValue;
|
||||||
auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString();
|
auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString();
|
||||||
|
|
||||||
mdbKey.mv_data = key.data();
|
mdbKey.mv_data = key.data();
|
||||||
mdbKey.mv_size = key.size();
|
mdbKey.mv_size = key.size();
|
||||||
|
|
||||||
mdbValue.mv_data = const_cast<char *>(identityKeySecret.data().data());
|
mdbValue.mv_data = const_cast<char *>(identityKeySecret.data());
|
||||||
mdbValue.mv_size = identityKeySecret.size();
|
mdbValue.mv_size = identityKeySecret.size();
|
||||||
|
|
||||||
MDB_txn *txn;
|
MDB_txn *txn;
|
||||||
@ -71,7 +73,7 @@ bool Database::saveIdentityKeySecret(int deviceId,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Database::loadActiveDeviceId() {
|
std::optional<int> Database::loadActiveDeviceId() {
|
||||||
MDB_val key, value;
|
MDB_val key, value;
|
||||||
|
|
||||||
key.mv_data = (void *) "active";
|
key.mv_data = (void *) "active";
|
||||||
@ -83,12 +85,14 @@ int Database::loadActiveDeviceId() {
|
|||||||
auto err = mdb_get(txn, dbiIdentityKeys, &key, &value);
|
auto err = mdb_get(txn, dbiIdentityKeys, &key, &value);
|
||||||
if (err) {
|
if (err) {
|
||||||
qWarning() << "could not load active device id:" << mdb_strerror(err);
|
qWarning() << "could not load active device id:" << mdb_strerror(err);
|
||||||
return 0;
|
mdb_txn_abort(txn);
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.mv_size != sizeof(int)) {
|
if (value.mv_size != sizeof(int)) {
|
||||||
qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int);
|
qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int);
|
||||||
return 0;
|
mdb_txn_abort(txn);
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto id = *reinterpret_cast<int *>(value.mv_data);
|
auto id = *reinterpret_cast<int *>(value.mv_data);
|
||||||
@ -124,3 +128,53 @@ bool Database::saveActiveDeviceId(int deviceId) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Database::saveIdentityKey(int deviceId, const QByteArray &identityKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<QByteArray> Database::loadIdentityKey(int deviceId) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::containsPreKey() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<KeyPair> Database::loadPreKey(int deviceId, int id) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::savePreKey(int deviceId, int id, const KeyPair &preKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Bundle> Database::loadBundle(int deviceId) {
|
||||||
|
Bundle result{};
|
||||||
|
|
||||||
|
auto ik = loadIdentityKey(deviceId);
|
||||||
|
|
||||||
|
result.ik = ik.value();
|
||||||
|
|
||||||
|
auto spk = loadSignedPreKey(deviceId);
|
||||||
|
|
||||||
|
result.spk = spk->key.publicKey;
|
||||||
|
result.spks = spk->signature;
|
||||||
|
result.spkId = spk->id;
|
||||||
|
|
||||||
|
// PreKeys
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::saveBundle(int deviceId, const Bundle &bundle) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<SignedPreKey> Database::loadSignedPreKey(int deviceId) {
|
||||||
|
return std::optional<SignedPreKey>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::saveSignedPreKey(int deviceId, const SignedPreKey &signedPreKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -8,8 +8,12 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <lmdb.h>
|
#include <lmdb.h>
|
||||||
|
|
||||||
|
#include "key.h"
|
||||||
|
|
||||||
namespace QXmpp::Omemo {
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class Bundle;
|
||||||
|
|
||||||
class Database {
|
class Database {
|
||||||
public:
|
public:
|
||||||
explicit Database(QString jid);
|
explicit Database(QString jid);
|
||||||
@ -18,14 +22,26 @@ namespace QXmpp::Omemo {
|
|||||||
Database(Database &&) = delete;
|
Database(Database &&) = delete;
|
||||||
Database &operator=(const Database &) = delete;
|
Database &operator=(const Database &) = delete;
|
||||||
|
|
||||||
QBuffer loadIdentityKey();
|
// For local user
|
||||||
bool saveIdentityKey(const QBuffer &identityKey);
|
std::optional<int> loadActiveDeviceId();
|
||||||
|
|
||||||
int loadActiveDeviceId();
|
|
||||||
bool saveActiveDeviceId(int deviceId);
|
bool saveActiveDeviceId(int deviceId);
|
||||||
|
|
||||||
QBuffer loadIdentityKeySecret(int deviceId);
|
std::optional<QByteArray> loadIdentityKeySecret(int deviceId);
|
||||||
bool saveIdentityKeySecret(int deviceId, const QBuffer &identityKeySecret);
|
bool saveIdentityKeySecret(int deviceId, const QByteArray &identityKeySecret);
|
||||||
|
|
||||||
|
std::optional<Bundle> loadBundle(int deviceId);
|
||||||
|
bool saveBundle(int deviceId, const Bundle& bundle);
|
||||||
|
|
||||||
|
// For any user
|
||||||
|
std::optional<QByteArray> loadIdentityKey(int deviceId);
|
||||||
|
bool saveIdentityKey(int deviceId, const QByteArray &identityKey);
|
||||||
|
|
||||||
|
bool containsPreKey();
|
||||||
|
std::optional<KeyPair> loadPreKey(int deviceId, int id);
|
||||||
|
bool savePreKey(int deviceId, int id, const KeyPair &preKey);
|
||||||
|
|
||||||
|
std::optional<SignedPreKey> loadSignedPreKey(int deviceId);
|
||||||
|
bool saveSignedPreKey(int deviceId, const SignedPreKey& signedPreKey);
|
||||||
|
|
||||||
const QString jid;
|
const QString jid;
|
||||||
|
|
||||||
|
@ -5,11 +5,31 @@
|
|||||||
#include "device_service.h"
|
#include "device_service.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
|
||||||
QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {}
|
using namespace QXmpp::Omemo;
|
||||||
|
|
||||||
void QXmpp::Omemo::DeviceService::onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list) {
|
DeviceService::DeviceService(QObject *parent) : QObject(parent) {}
|
||||||
|
|
||||||
|
void DeviceService::onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list) {
|
||||||
|
|
||||||
for (const auto &device : list.devices) {
|
for (const auto &device : list.devices) {
|
||||||
qInfo() << "Got device for" << jid << ":" << device.id;
|
qInfo() << "Got device for" << jid << ":" << device.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Database> DeviceService::getDatabase(const QString &jid) {
|
||||||
|
if (!databases.contains(jid)) {
|
||||||
|
databases.insert(jid, QSharedPointer<Database>::create(jid));
|
||||||
|
}
|
||||||
|
|
||||||
|
return databases[jid];
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceService::addIdentity(const QString &jid, int deviceId, const QByteArray& publicKey) {
|
||||||
|
auto db = getDatabase(jid);
|
||||||
|
|
||||||
|
db->saveIdentityKey(deviceId, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceService::onDeviceListNotFound(const QString &jid) {
|
||||||
|
qInfo() << "Device list not found:" << jid;
|
||||||
|
}
|
||||||
|
@ -4,8 +4,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
#include "qomemo.h"
|
#include "qomemo.h"
|
||||||
#include "user_device_list.h"
|
#include "user_device_list.h"
|
||||||
|
#include "database.h"
|
||||||
|
|
||||||
#include <QXmppClient.h>
|
#include <QXmppClient.h>
|
||||||
|
|
||||||
@ -19,11 +22,17 @@ namespace QXmpp::Omemo {
|
|||||||
public:
|
public:
|
||||||
explicit DeviceService(QObject *parent);
|
explicit DeviceService(QObject *parent);
|
||||||
|
|
||||||
|
QSharedPointer<Database> getDatabase(const QString& jid);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
void addIdentity(const QString& jid, int deviceId, const QByteArray& publicKey);
|
||||||
|
|
||||||
void onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
|
void onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
|
||||||
|
|
||||||
|
void onDeviceListNotFound(const QString &jid);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<QString, UserDeviceList> device_lists{};
|
QMap<QString, QSharedPointer<Database>> databases{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QXmpp::Omemo
|
} // namespace QXmpp::Omemo
|
||||||
|
6
qomemo/key.cpp
Normal file
6
qomemo/key.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "key.h"
|
||||||
|
|
27
qomemo/key.h
Normal file
27
qomemo/key.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class KeyPair {
|
||||||
|
public:
|
||||||
|
QByteArray publicKey{};
|
||||||
|
std::optional<QByteArray> secretKey{ std::nullopt };
|
||||||
|
};
|
||||||
|
|
||||||
|
class SignedPreKey {
|
||||||
|
public:
|
||||||
|
int id{ 0 };
|
||||||
|
|
||||||
|
KeyPair key{};
|
||||||
|
QByteArray signature{};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
#include "qxmpp_omemo_manager.h"
|
#include "qxmpp_omemo_manager.h"
|
||||||
|
|
||||||
|
#include "qomemo/signal/context.h"
|
||||||
|
|
||||||
|
#include "bundle.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "variant/conversations.h"
|
#include "variant/conversations.h"
|
||||||
|
|
||||||
@ -12,13 +15,24 @@
|
|||||||
#include <QXmppClient.h>
|
#include <QXmppClient.h>
|
||||||
#include <QXmppPubSubIq.h>
|
#include <QXmppPubSubIq.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <QRandomGenerator64>
|
||||||
|
#include <external/signal-protocol-c/src/signal_protocol_internal.h>
|
||||||
|
|
||||||
using namespace QXmpp::Omemo;
|
using namespace QXmpp::Omemo;
|
||||||
|
|
||||||
Manager::Manager()
|
Manager::Manager()
|
||||||
: deviceService(new DeviceService(this)),
|
: deviceService{new DeviceService(this)},
|
||||||
omemoVariant(new Variant::Conversations) {
|
omemoVariant(new Variant::Conversations) {
|
||||||
connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived);
|
connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived);
|
||||||
|
connect(this, &Manager::deviceListNotFound, deviceService.get(), &DeviceService::onDeviceListNotFound);
|
||||||
|
connect(this, &Manager::deviceListNotFound, this, [this](const QString &jid) {
|
||||||
|
if (jid == client()->configuration().jidBare())
|
||||||
|
generateDeviceListForSelf();
|
||||||
|
});
|
||||||
|
connect(this, &Manager::deviceListReceived, this, [this](const QString &jid, const DeviceList ¤tList) {
|
||||||
|
if (jid == client()->configuration().jidBare())
|
||||||
|
generateDeviceForSelfIfNeeded(currentList);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) {
|
bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) {
|
||||||
@ -28,31 +42,62 @@ bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) {
|
|||||||
|
|
||||||
std::cout << str.toStdString();
|
std::cout << str.toStdString();
|
||||||
|
|
||||||
if (stanza.tagName() == "iq") {
|
if (handleDeviceList(stanza) || handleMissingDeviceList(stanza))
|
||||||
if (stanza.attribute("type") == "result") {
|
return true;
|
||||||
auto pubsub = stanza.firstChildElement("pubsub");
|
|
||||||
if (!pubsub.isNull()) {
|
|
||||||
auto items = pubsub.firstChildElement("items");
|
|
||||||
if (items.attribute("node") ==
|
|
||||||
"eu.siacs.conversations.axolotl.devicelist") {
|
|
||||||
auto item = items.firstChildElement("item");
|
|
||||||
if (!item.isNull()) {
|
|
||||||
auto list = item.firstChildElement("list");
|
|
||||||
if (!list.isNull()) {
|
|
||||||
DeviceList deviceList = omemoVariant->deviceListFromXml(list);
|
|
||||||
emit deviceListReceived(stanza.attribute("from"), deviceList);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Manager::handleDeviceList(const QDomElement &stanza) {
|
||||||
|
if (!(stanza.tagName() == "iq"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(stanza.attribute("type") == "result"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto pubsub = stanza.firstChildElement("pubsub");
|
||||||
|
if (pubsub.isNull())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto items = pubsub.firstChildElement("items");
|
||||||
|
if (!(items.attribute("node") == omemoVariant->getDeviceListNode()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto deviceList = omemoVariant->latestDeviceListFromPubSubNode(items);
|
||||||
|
if (!deviceList.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
emit deviceListReceived(stanza.attribute("from"), deviceList.value());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Manager::handleMissingDeviceList(const QDomElement &stanza) {
|
||||||
|
if (stanza.tagName() != "iq")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stanza.attribute("type") != "error")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto pubsub = stanza.firstChildElement("pubsub");
|
||||||
|
if (pubsub.isNull())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto items = pubsub.firstChildElement("items");
|
||||||
|
if (items.attribute("node") != omemoVariant->getDeviceListNode())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto error = stanza.firstChildElement("error");
|
||||||
|
|
||||||
|
if (error.namespaceURI() != "jabber:client" || error.attribute("code") != "404")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
qDebug() << "Got 404 deviceList for" << stanza.attribute("from");
|
||||||
|
emit deviceListNotFound(stanza.attribute("from"));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void QXmpp::Omemo::Manager::setClient(QXmppClient *client) {
|
void QXmpp::Omemo::Manager::setClient(QXmppClient *client) {
|
||||||
QXmppClientExtension::setClient(client);
|
QXmppClientExtension::setClient(client);
|
||||||
|
|
||||||
@ -68,7 +113,152 @@ void QXmpp::Omemo::Manager::fetchOwnDevices() {
|
|||||||
iq.setFrom(client()->configuration().jid());
|
iq.setFrom(client()->configuration().jid());
|
||||||
iq.setTo(client()->configuration().jidBare());
|
iq.setTo(client()->configuration().jidBare());
|
||||||
iq.setType(QXmppIq::Get);
|
iq.setType(QXmppIq::Get);
|
||||||
iq.setQueryNode("eu.siacs.conversations.axolotl.devicelist");
|
iq.setQueryNode(omemoVariant->getDeviceListNode());
|
||||||
|
|
||||||
client()->sendPacket(iq);
|
client()->sendPacket(iq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSharedPointer<DeviceService> Manager::getDeviceService() {
|
||||||
|
return deviceService;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::publishDeviceList(const DeviceList &deviceList) {
|
||||||
|
// QXmppPubSubIq iq{};
|
||||||
|
// iq.setFrom(client()->configuration().jid());
|
||||||
|
// iq.setType(QXmppIq::Set);
|
||||||
|
// iq.setQueryNode(omemoVariant->getDeviceListNode());
|
||||||
|
// iq.setItems()
|
||||||
|
|
||||||
|
auto iq = omemoVariant->deviceListSetIq(deviceList);
|
||||||
|
iq.setFrom(client()->configuration().jid());
|
||||||
|
|
||||||
|
QString str{};
|
||||||
|
QXmlStreamWriter info(&str);
|
||||||
|
iq.toXml(&info);
|
||||||
|
qInfo() << str;
|
||||||
|
|
||||||
|
client()->sendPacket(iq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::generateDeviceListForSelf() {
|
||||||
|
qInfo() << "Generate device for self...";
|
||||||
|
|
||||||
|
generateDeviceForSelfIfNeeded(DeviceList());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::publishBundle(int deviceId, const Bundle &bundle) {
|
||||||
|
auto iq = omemoVariant->bundleSetIq(deviceId, bundle);
|
||||||
|
iq.setFrom(client()->configuration().jid());
|
||||||
|
|
||||||
|
QString str{};
|
||||||
|
QXmlStreamWriter info(&str);
|
||||||
|
iq.toXml(&info);
|
||||||
|
qInfo() << str;
|
||||||
|
|
||||||
|
client()->sendPacket(iq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::generateDeviceForSelfIfNeeded(const DeviceList ¤tList) {
|
||||||
|
auto db = deviceService->getDatabase(client()->configuration().jidBare());
|
||||||
|
auto activeId = db->loadActiveDeviceId();
|
||||||
|
|
||||||
|
if (activeId.has_value()) {
|
||||||
|
qInfo() << "Current device:" << *activeId;
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (const auto &d : currentList.devices)
|
||||||
|
if (d.id == *activeId)
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qInfo() << "Could not find device" << *activeId << ", generating new one";
|
||||||
|
}
|
||||||
|
|
||||||
|
qInfo() << "Generating device";
|
||||||
|
|
||||||
|
auto deviceId = QRandomGenerator64{}.bounded(INT32_MAX);
|
||||||
|
db->saveActiveDeviceId(deviceId);
|
||||||
|
|
||||||
|
Device device{};
|
||||||
|
device.id = deviceId;
|
||||||
|
|
||||||
|
auto updatedList = currentList;
|
||||||
|
|
||||||
|
auto bundle = Bundle();
|
||||||
|
|
||||||
|
updatedList.devices.push_back(device);
|
||||||
|
|
||||||
|
publishDeviceList(updatedList);
|
||||||
|
publishBundle(deviceId, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle Manager::generateAndSaveBundle(int deviceId) {
|
||||||
|
auto database = deviceService->getDatabase(client()->configuration().jidBare());
|
||||||
|
Bundle result{};
|
||||||
|
|
||||||
|
ec_key_pair *ecKeyPair;
|
||||||
|
curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &ecKeyPair);
|
||||||
|
|
||||||
|
signal_buffer *ecPublic;
|
||||||
|
ec_public_key_serialize(&ecPublic, ec_key_pair_get_public(ecKeyPair));
|
||||||
|
|
||||||
|
signal_buffer *ecSecret;
|
||||||
|
ec_private_key_serialize(&ecSecret, ec_key_pair_get_private(ecKeyPair));
|
||||||
|
|
||||||
|
QByteArray identityKey((const char *) signal_buffer_const_data(ecPublic), (int) signal_buffer_len(ecPublic));
|
||||||
|
QByteArray identityKeySecret((const char *) signal_buffer_const_data(ecSecret), (int) signal_buffer_len(ecSecret));
|
||||||
|
|
||||||
|
database->saveIdentityKey(deviceId, identityKey);
|
||||||
|
database->saveIdentityKeySecret(deviceId, identityKeySecret);
|
||||||
|
|
||||||
|
// Generate SPK
|
||||||
|
ec_key_pair *ecSpk;
|
||||||
|
curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &ecSpk);
|
||||||
|
signal_buffer *spkPublic, *spkSecret;
|
||||||
|
ec_public_key_serialize(&spkPublic, ec_key_pair_get_public(ecSpk));
|
||||||
|
ec_private_key_serialize(&spkSecret, ec_key_pair_get_private(ecSpk));
|
||||||
|
|
||||||
|
// Generate SPKs
|
||||||
|
signal_buffer *signature;
|
||||||
|
curve_calculate_signature(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &signature,
|
||||||
|
ec_key_pair_get_private(ecKeyPair), signal_buffer_const_data(spkPublic),
|
||||||
|
signal_buffer_len(spkPublic));
|
||||||
|
|
||||||
|
SignedPreKey spk{};
|
||||||
|
spk.id = 0;
|
||||||
|
spk.key.publicKey = QByteArray((const char *) signal_buffer_const_data(spkPublic), (int) signal_buffer_len(spkPublic));
|
||||||
|
spk.key.secretKey = QByteArray((const char *) signal_buffer_const_data(spkSecret), (int) signal_buffer_len(spkSecret));
|
||||||
|
spk.signature = QByteArray((const char *) signal_buffer_const_data(signature), (int) signal_buffer_len(signature));
|
||||||
|
|
||||||
|
// Generate 100 PK
|
||||||
|
for (auto i = 1; i <= 100; ++i) {
|
||||||
|
ec_key_pair *currentPreKey;
|
||||||
|
curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), ¤tPreKey);
|
||||||
|
|
||||||
|
signal_buffer *pkPublic, *pkSecret;
|
||||||
|
ec_public_key_serialize(&pkPublic, ec_key_pair_get_public(currentPreKey));
|
||||||
|
ec_private_key_serialize(&pkSecret, ec_key_pair_get_private(currentPreKey));
|
||||||
|
|
||||||
|
KeyPair preKey{};
|
||||||
|
preKey.publicKey = QByteArray((const char *) signal_buffer_const_data(pkPublic), (int) signal_buffer_len(pkPublic));
|
||||||
|
preKey.secretKey = QByteArray((const char *) signal_buffer_const_data(pkSecret), (int) signal_buffer_len(pkSecret));
|
||||||
|
|
||||||
|
database->savePreKey(deviceId, i, preKey);
|
||||||
|
|
||||||
|
signal_buffer_free(pkPublic);
|
||||||
|
signal_buffer_free(pkSecret);
|
||||||
|
SIGNAL_UNREF(currentPreKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_buffer_free(signature);
|
||||||
|
|
||||||
|
signal_buffer_free(ecPublic);
|
||||||
|
signal_buffer_free(ecSecret);
|
||||||
|
SIGNAL_UNREF(ecKeyPair);
|
||||||
|
|
||||||
|
signal_buffer_free(spkPublic);
|
||||||
|
signal_buffer_free(spkSecret);
|
||||||
|
SIGNAL_UNREF(ecSpk);
|
||||||
|
}
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
|
|
||||||
#include <QXmppClientExtension.h>
|
#include <QXmppClientExtension.h>
|
||||||
|
|
||||||
|
namespace Signal {
|
||||||
|
class Context;
|
||||||
|
}
|
||||||
|
|
||||||
namespace QXmpp::Omemo {
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
class Manager : public QXmppClientExtension {
|
class Manager : public QXmppClientExtension {
|
||||||
@ -21,18 +25,37 @@ namespace QXmpp::Omemo {
|
|||||||
|
|
||||||
bool handleStanza(const QDomElement &stanza) override;
|
bool handleStanza(const QDomElement &stanza) override;
|
||||||
|
|
||||||
|
bool handleDeviceList(const QDomElement& stanza);
|
||||||
|
|
||||||
|
bool handleMissingDeviceList(const QDomElement& stanza);
|
||||||
|
|
||||||
|
QSharedPointer<DeviceService> getDeviceService();
|
||||||
|
|
||||||
|
Bundle generateAndSaveBundle(int deviceId);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void fetchOwnDevices();
|
void fetchOwnDevices();
|
||||||
|
|
||||||
|
void publishDeviceList(const QXmpp::Omemo::DeviceList& deviceList);
|
||||||
|
|
||||||
|
void generateDeviceListForSelf();
|
||||||
|
|
||||||
|
void generateDeviceForSelfIfNeeded(const QXmpp::Omemo::DeviceList ¤tList);
|
||||||
|
|
||||||
|
void publishBundle(int deviceId, const QXmpp::Omemo::Bundle& bundle);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void deviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
|
void deviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
|
||||||
|
|
||||||
|
void deviceListNotFound(const QString &jid);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setClient(QXmppClient *client) override;
|
void setClient(QXmppClient *client) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QScopedPointer<DeviceService> deviceService;
|
QSharedPointer<DeviceService> deviceService;
|
||||||
QScopedPointer<Variant::Base> omemoVariant;
|
QScopedPointer<Variant::Base> omemoVariant;
|
||||||
|
QScopedPointer<Signal::Context> signalContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QXmpp::Omemo
|
} // namespace QXmpp::Omemo
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
target_sources(squawk PRIVATE
|
target_sources(squawk PRIVATE
|
||||||
context.cpp
|
context.cpp
|
||||||
context.h
|
context.h
|
||||||
|
util.cpp
|
||||||
|
util.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(crypto)
|
add_subdirectory(crypto)
|
||||||
|
@ -4,6 +4,19 @@
|
|||||||
|
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
|
|
||||||
Signal::Context::Context() {}
|
using namespace Signal;
|
||||||
|
|
||||||
Signal::Context::~Context() {}
|
Context::Context() {}
|
||||||
|
|
||||||
|
Context::~Context() {}
|
||||||
|
|
||||||
|
std::unique_ptr<Crypto::ECKeyPair> Context::generateCurveKeyPair() {
|
||||||
|
auto result = std::unique_ptr<Crypto::ECKeyPair>();
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_context *Context::temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped() {
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "crypto/ec.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <signal/signal_protocol.h>
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
namespace Signal {
|
namespace Signal {
|
||||||
@ -16,6 +20,10 @@ namespace Signal {
|
|||||||
Context(Context &&) = delete;
|
Context(Context &&) = delete;
|
||||||
Context &operator=(const Context &) = delete;
|
Context &operator=(const Context &) = delete;
|
||||||
|
|
||||||
|
std::unique_ptr<Crypto::ECKeyPair> generateCurveKeyPair();
|
||||||
|
|
||||||
|
signal_context *temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
signal_context *ctx{nullptr};
|
signal_context *ctx{nullptr};
|
||||||
};
|
};
|
||||||
|
@ -7,4 +7,6 @@ target_sources(squawk PRIVATE
|
|||||||
hmac_sha256_openssl.h
|
hmac_sha256_openssl.h
|
||||||
sha512_digest_openssl.cpp
|
sha512_digest_openssl.cpp
|
||||||
sha512_digest_openssl.h
|
sha512_digest_openssl.h
|
||||||
|
ec.cpp
|
||||||
|
ec.h
|
||||||
)
|
)
|
||||||
|
5
qomemo/signal/crypto/ec.cpp
Normal file
5
qomemo/signal/crypto/ec.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-06-17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ec.h"
|
20
qomemo/signal/crypto/ec.h
Normal file
20
qomemo/signal/crypto/ec.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-06-17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Crypto {
|
||||||
|
|
||||||
|
class ECKeyPair {
|
||||||
|
public:
|
||||||
|
ECKeyPair();
|
||||||
|
~ECKeyPair();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ec_key_pair *ec;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -4,13 +4,56 @@
|
|||||||
|
|
||||||
#include "identity_key_store.h"
|
#include "identity_key_store.h"
|
||||||
|
|
||||||
void Signal::Store::IdentityKeyStore::boundToContext(
|
#include "qomemo/signal/util.h"
|
||||||
signal_protocol_store_context *ctx) {
|
#include <utility>
|
||||||
signal_protocol_identity_key_store store{};
|
|
||||||
|
|
||||||
store.user_data = nullptr;
|
Signal::Store::IdentityKeyStore::IdentityKeyStore(QXmpp::Omemo::DeviceService &deviceService, QString jid, int deviceId)
|
||||||
store.destroy_func = nullptr;
|
: jid(std::move(jid)), deviceId(deviceId), deviceService(deviceService) {
|
||||||
|
database = deviceService.getDatabase(jid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::IdentityKeyStore::getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data) {
|
||||||
|
auto pk = database->loadIdentityKey(deviceId);
|
||||||
|
auto sk = database->loadIdentityKeySecret(deviceId);
|
||||||
|
|
||||||
|
*public_data = signal_buffer_create(reinterpret_cast<const uint8_t *>(pk->data()), pk->size());
|
||||||
|
*private_data = signal_buffer_create(reinterpret_cast<const uint8_t *>(sk->data()), sk->size());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::IdentityKeyStore::getLocalRegistrationId(uint32_t *registration_id) {
|
||||||
|
// TODO: Figure out what registration id is used for
|
||||||
|
*registration_id = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::IdentityKeyStore::saveIdentity(const signal_protocol_address *address, uint8_t *key_data,
|
||||||
|
size_t key_len) {
|
||||||
|
auto identityJid = Signal::Util::jidFromAddress(address);
|
||||||
|
auto identityKey = Signal::Util::byteArray(key_data, key_len);
|
||||||
|
|
||||||
|
deviceService.getDatabase(identityJid)->saveIdentityKey(address->device_id, identityKey);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::IdentityKeyStore::isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data,
|
||||||
|
size_t key_len) {
|
||||||
|
auto identityJid = Signal::Util::jidFromAddress(address);
|
||||||
|
auto actualIdentityKey = deviceService.getDatabase(identityJid)->loadIdentityKey(address->device_id);
|
||||||
|
|
||||||
|
if (!actualIdentityKey.has_value()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto givenIdentityKey = Signal::Util::byteArray(key_data, key_len);
|
||||||
|
|
||||||
|
return givenIdentityKey == actualIdentityKey ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::Store::IdentityKeyStore::fillCallbacks(signal_protocol_identity_key_store &store) {
|
||||||
store.get_identity_key_pair = [](signal_buffer **public_data, signal_buffer **private_data, void *ptr) {
|
store.get_identity_key_pair = [](signal_buffer **public_data, signal_buffer **private_data, void *ptr) {
|
||||||
return static_cast<IdentityKeyStore *>(ptr)->getIdentityKeyPair(public_data, private_data);
|
return static_cast<IdentityKeyStore *>(ptr)->getIdentityKeyPair(public_data, private_data);
|
||||||
};
|
};
|
||||||
@ -24,24 +67,4 @@ void Signal::Store::IdentityKeyStore::boundToContext(
|
|||||||
store.save_identity = [](const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *ptr) {
|
store.save_identity = [](const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *ptr) {
|
||||||
return static_cast<IdentityKeyStore *>(ptr)->saveIdentity(address, key_data, key_len);
|
return static_cast<IdentityKeyStore *>(ptr)->saveIdentity(address, key_data, key_len);
|
||||||
};
|
};
|
||||||
|
|
||||||
signal_protocol_store_context_set_identity_key_store(ctx, &store);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Signal::Store::IdentityKeyStore::getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Signal::Store::IdentityKeyStore::getLocalRegistrationId(uint32_t *registration_id) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Signal::Store::IdentityKeyStore::saveIdentity(const signal_protocol_address *address, uint8_t *key_data,
|
|
||||||
size_t key_len) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Signal::Store::IdentityKeyStore::isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data,
|
|
||||||
size_t key_len) {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,27 @@
|
|||||||
|
|
||||||
#include <signal/signal_protocol.h>
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
#include "qomemo/device_service.h"
|
||||||
|
|
||||||
namespace Signal::Store {
|
namespace Signal::Store {
|
||||||
|
|
||||||
class IdentityKeyStore {
|
class IdentityKeyStore {
|
||||||
public:
|
public:
|
||||||
static void boundToContext(signal_protocol_store_context *ctx);
|
IdentityKeyStore(QXmpp::Omemo::DeviceService &deviceService, QString jid, int deviceId);
|
||||||
|
|
||||||
int getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data);
|
int getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data);
|
||||||
int getLocalRegistrationId(uint32_t *registration_id);
|
int getLocalRegistrationId(uint32_t *registration_id);
|
||||||
int saveIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len);
|
int saveIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len);
|
||||||
int isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len);
|
int isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_identity_key_store &store);
|
||||||
|
|
||||||
|
const QString jid;
|
||||||
|
const int deviceId;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QXmpp::Omemo::DeviceService &deviceService;
|
||||||
|
QSharedPointer<QXmpp::Omemo::Database> database;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Signal::Store
|
} // namespace Signal::Store
|
||||||
|
@ -4,29 +4,8 @@
|
|||||||
|
|
||||||
#include "pre_key_store.h"
|
#include "pre_key_store.h"
|
||||||
|
|
||||||
void Signal::Store::PreKeyStore::boundToContext(
|
Signal::Store::PreKeyStore::PreKeyStore(QSharedPointer<QXmpp::Omemo::Database> database) : database(
|
||||||
signal_protocol_store_context *ctx) {
|
std::move(database)) {}
|
||||||
signal_protocol_pre_key_store store{};
|
|
||||||
|
|
||||||
store.destroy_func = nullptr;
|
|
||||||
store.user_data = nullptr;
|
|
||||||
|
|
||||||
store.contains_pre_key = [](uint32_t id, void *ptr) {
|
|
||||||
return static_cast<PreKeyStore *>(ptr)->containsPreKey(id);
|
|
||||||
};
|
|
||||||
store.load_pre_key = [](signal_buffer **record, uint32_t id, void *ptr) {
|
|
||||||
return static_cast<PreKeyStore *>(ptr)->loadPreKey(record, id);
|
|
||||||
};
|
|
||||||
store.remove_pre_key = [](uint32_t id, void *ptr) {
|
|
||||||
return static_cast<PreKeyStore *>(ptr)->removePreKey(id);
|
|
||||||
};
|
|
||||||
store.store_pre_key = [](uint32_t id, uint8_t *record, size_t size,
|
|
||||||
void *ptr) {
|
|
||||||
return static_cast<PreKeyStore *>(ptr)->storePreKey(id, record, size);
|
|
||||||
};
|
|
||||||
|
|
||||||
signal_protocol_store_context_set_pre_key_store(ctx, &store);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Signal::Store::PreKeyStore::containsPreKey(uint32_t pre_key_id) {
|
int Signal::Store::PreKeyStore::containsPreKey(uint32_t pre_key_id) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -41,3 +20,19 @@ int Signal::Store::PreKeyStore::storePreKey(uint32_t pre_key_id, uint8_t *record
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Signal::Store::PreKeyStore::removePreKey(uint32_t pre_key_id) { return 0; }
|
int Signal::Store::PreKeyStore::removePreKey(uint32_t pre_key_id) { return 0; }
|
||||||
|
|
||||||
|
void Signal::Store::PreKeyStore::fillCallbacks(signal_protocol_pre_key_store &store) {
|
||||||
|
store.contains_pre_key = [](uint32_t id, void *ptr) {
|
||||||
|
return static_cast<PreKeyStore *>(ptr)->containsPreKey(id);
|
||||||
|
};
|
||||||
|
store.load_pre_key = [](signal_buffer **record, uint32_t id, void *ptr) {
|
||||||
|
return static_cast<PreKeyStore *>(ptr)->loadPreKey(record, id);
|
||||||
|
};
|
||||||
|
store.remove_pre_key = [](uint32_t id, void *ptr) {
|
||||||
|
return static_cast<PreKeyStore *>(ptr)->removePreKey(id);
|
||||||
|
};
|
||||||
|
store.store_pre_key = [](uint32_t id, uint8_t *record, size_t size,
|
||||||
|
void *ptr) {
|
||||||
|
return static_cast<PreKeyStore *>(ptr)->storePreKey(id, record, size);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -6,16 +6,23 @@
|
|||||||
|
|
||||||
#include <signal/signal_protocol.h>
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
#include "qomemo/device_service.h"
|
||||||
|
|
||||||
namespace Signal::Store {
|
namespace Signal::Store {
|
||||||
|
|
||||||
class PreKeyStore {
|
class PreKeyStore {
|
||||||
public:
|
public:
|
||||||
static void boundToContext(signal_protocol_store_context *ctx);
|
explicit PreKeyStore(QSharedPointer<QXmpp::Omemo::Database> database);
|
||||||
|
|
||||||
int containsPreKey(uint32_t pre_key_id);
|
int containsPreKey(uint32_t pre_key_id);
|
||||||
int loadPreKey(signal_buffer **record, uint32_t pre_key_id);
|
int loadPreKey(signal_buffer **record, uint32_t pre_key_id);
|
||||||
int storePreKey(uint32_t pre_key_id, uint8_t *record, size_t record_len);
|
int storePreKey(uint32_t pre_key_id, uint8_t *record, size_t record_len);
|
||||||
int removePreKey(uint32_t pre_key_id);
|
int removePreKey(uint32_t pre_key_id);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_pre_key_store &store);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<QXmpp::Omemo::Database> database;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Signal::Store
|
} // namespace Signal::Store
|
||||||
|
14
qomemo/signal/util.cpp
Normal file
14
qomemo/signal/util.cpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
QString Signal::Util::jidFromAddress(const signal_protocol_address *address) {
|
||||||
|
// TODO: Validate this
|
||||||
|
return QString::fromRawData(reinterpret_cast<const QChar *>(address->name), static_cast<int>(address->name_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Signal::Util::byteArray(const uint8_t *data, size_t len) {
|
||||||
|
return QByteArray::fromRawData(reinterpret_cast<const char *>(data), static_cast<int>(len));
|
||||||
|
}
|
18
qomemo/signal/util.h
Normal file
18
qomemo/signal/util.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
namespace Signal::Util {
|
||||||
|
|
||||||
|
QString jidFromAddress(const signal_protocol_address *address);
|
||||||
|
|
||||||
|
QByteArray byteArray(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
}
|
@ -4,10 +4,13 @@
|
|||||||
|
|
||||||
#include "conversations.h"
|
#include "conversations.h"
|
||||||
|
|
||||||
|
#include "qomemo/bundle.h"
|
||||||
#include "qomemo/device.h"
|
#include "qomemo/device.h"
|
||||||
#include "shared/qxmppfactories.h"
|
#include "shared/qxmppfactories.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
#include <QXmppIq.h>
|
#include <QXmppIq.h>
|
||||||
|
#include <QXmppPubSubIq.h>
|
||||||
|
|
||||||
using namespace QXmpp::Omemo;
|
using namespace QXmpp::Omemo;
|
||||||
using namespace QXmpp::Factories;
|
using namespace QXmpp::Factories;
|
||||||
@ -41,13 +44,21 @@ Variant::Conversations::deviceListToXml(const DeviceList &deviceList) {
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceList Variant::Conversations::deviceListFromXml(const QXmppElement &xml) {
|
std::optional<DeviceList> Variant::Conversations::latestDeviceListFromPubSubNode(const QXmppElement &items) {
|
||||||
|
auto item = items.firstChildElement("item");
|
||||||
|
if (item.isNull())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto list = item.firstChildElement("list");
|
||||||
|
if (list.isNull())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (!elementMatches(list, "list", "eu.siacs.conversations.axolotl"))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
DeviceList result{};
|
DeviceList result{};
|
||||||
|
|
||||||
if (!elementMatches(xml, "list", "eu.siacs.conversations.axolotl"))
|
auto deviceElement = list.firstChildElement("device");
|
||||||
return result;
|
|
||||||
|
|
||||||
auto deviceElement = xml.firstChildElement("device");
|
|
||||||
while (!deviceElement.isNull()) {
|
while (!deviceElement.isNull()) {
|
||||||
result.devices.push_back(deviceFromXml(deviceElement));
|
result.devices.push_back(deviceFromXml(deviceElement));
|
||||||
deviceElement = deviceElement.nextSiblingElement("device");
|
deviceElement = deviceElement.nextSiblingElement("device");
|
||||||
@ -65,7 +76,34 @@ QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) {
|
|||||||
item.appendChild(deviceListToXml(deviceList));
|
item.appendChild(deviceListToXml(deviceList));
|
||||||
|
|
||||||
auto publish = createElement("publish");
|
auto publish = createElement("publish");
|
||||||
publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist");
|
publish.setAttribute("node", getDeviceListNode());
|
||||||
|
publish.appendChild(item);
|
||||||
|
|
||||||
|
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
|
||||||
|
pubSub.appendChild(publish);
|
||||||
|
pubSub.appendChild(createOpenPublishOptions());
|
||||||
|
|
||||||
|
iq.setExtensions({ pubSub });
|
||||||
|
|
||||||
|
return iq;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Variant::Conversations::getDeviceListNode() const {
|
||||||
|
return "eu.siacs.conversations.axolotl.devicelist";
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppIq Variant::Conversations::bundleSetIq(int deviceId, const Bundle &bundle) {
|
||||||
|
QXmppIq iq{};
|
||||||
|
|
||||||
|
iq.setType(QXmppIq::Set);
|
||||||
|
|
||||||
|
auto item = createElement("item");
|
||||||
|
item.appendChild(bundleToXml(bundle));
|
||||||
|
|
||||||
|
auto publish = createElement("publish");
|
||||||
|
publish.setAttribute(
|
||||||
|
"node",
|
||||||
|
QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId));
|
||||||
publish.appendChild(item);
|
publish.appendChild(item);
|
||||||
|
|
||||||
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
|
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
|
||||||
@ -76,3 +114,92 @@ QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) {
|
|||||||
|
|
||||||
return iq;
|
return iq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QXmppElement Variant::Conversations::bundleToXml(const Bundle &bundle) {
|
||||||
|
auto spkNode = createElement("signedPreKeyPublic");
|
||||||
|
spkNode.setAttribute("signedPreKeyId", QString::number(bundle.spkId));
|
||||||
|
spkNode.setValue(bundle.spk.toBase64());
|
||||||
|
|
||||||
|
auto spksNode = createElement("signedPreKeySignature");
|
||||||
|
spksNode.setValue(bundle.spks.toBase64());
|
||||||
|
|
||||||
|
auto ikNode = createElement("identityKey");
|
||||||
|
ikNode.setValue(bundle.ik.toBase64());
|
||||||
|
|
||||||
|
auto prekeysNode = createElement("prekeys");
|
||||||
|
for (const auto &pk : bundle.prekeys) {
|
||||||
|
prekeysNode.appendChild(preKeyToXml(pk));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = createElement("bundle", "eu.siacs.conversations.axolotl");
|
||||||
|
result.appendChild(spkNode);
|
||||||
|
result.appendChild(spksNode);
|
||||||
|
result.appendChild(ikNode);
|
||||||
|
result.appendChild(prekeysNode);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement Variant::Conversations::preKeyToXml(const PreKey &pk) {
|
||||||
|
auto x = createElement("preKeyPublic");
|
||||||
|
x.setAttribute("preKeyId", QString::number(pk.id));
|
||||||
|
x.setValue(pk.data.toBase64());
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<PreKey> Variant::Conversations::preKeyFromXml(const QXmppElement &xml) {
|
||||||
|
if (!elementMatches(xml, "preKeyPublic"))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto pk = PreKey();
|
||||||
|
pk.id = xml.attribute("preKeyId").toInt();
|
||||||
|
pk.data = QByteArray::fromBase64Encoding(xml.value().toUtf8()).decoded;
|
||||||
|
|
||||||
|
return pk;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Bundle> Variant::Conversations::bundleFromXml(const QXmppElement &xml) {
|
||||||
|
if (!elementMatches(xml, "bundle", "eu.siacs.conversations.axolotl"))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto spkNode = xml.firstChildElement("signedPreKeyPublic");
|
||||||
|
if (spkNode.isNull()) {
|
||||||
|
qWarning() << "'bundle': missing 'signedPreKeyPublic'";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle result{};
|
||||||
|
|
||||||
|
result.spk = QByteArray::fromBase64Encoding(spkNode.value().toUtf8()).decoded;
|
||||||
|
result.spkId = spkNode.attribute("signedPreKeyId").toInt();
|
||||||
|
|
||||||
|
auto spksNode = xml.firstChildElement("signedPreKeySignature");
|
||||||
|
if (spksNode.isNull()) {
|
||||||
|
qWarning() << "'bundle': missing 'signedPreKeySignature'";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.spks = QByteArray::fromBase64Encoding(spksNode.value().toUtf8()).decoded;
|
||||||
|
|
||||||
|
auto ikNode = xml.firstChildElement("identityKey");
|
||||||
|
if (ikNode.isNull()) {
|
||||||
|
qWarning() << "'bundle': missing 'identityKey'";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.ik = QByteArray::fromBase64Encoding(ikNode.value().toUtf8()).decoded;
|
||||||
|
|
||||||
|
auto prekeysNode = xml.firstChildElement("prekeys");
|
||||||
|
auto pkNode = prekeysNode.firstChildElement("preKeyPublic");
|
||||||
|
|
||||||
|
result.prekeys.clear();
|
||||||
|
while (!pkNode.isNull()) {
|
||||||
|
auto maybePk = preKeyFromXml(pkNode);
|
||||||
|
if (maybePk)
|
||||||
|
result.prekeys.push_back(*maybePk);
|
||||||
|
pkNode = pkNode.nextSiblingElement("preKeyPublic");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -15,9 +15,18 @@ namespace QXmpp::Omemo::Variant {
|
|||||||
QXmppElement deviceToXml(const Device &device) override;
|
QXmppElement deviceToXml(const Device &device) override;
|
||||||
Device deviceFromXml(const QXmppElement &xml) override;
|
Device deviceFromXml(const QXmppElement &xml) override;
|
||||||
|
|
||||||
|
[[nodiscard]] QString getDeviceListNode() const override;
|
||||||
QXmppElement deviceListToXml(const DeviceList &deviceList) override;
|
QXmppElement deviceListToXml(const DeviceList &deviceList) override;
|
||||||
DeviceList deviceListFromXml(const QXmppElement &xml) override;
|
std::optional<DeviceList> latestDeviceListFromPubSubNode(const QXmppElement &items) override;
|
||||||
QXmppIq deviceListSetIq(const DeviceList &deviceList) override;
|
QXmppIq deviceListSetIq(const DeviceList &deviceList) override;
|
||||||
|
|
||||||
|
QXmppElement preKeyToXml(const PreKey &pk) override;
|
||||||
|
std::optional<PreKey> preKeyFromXml(const QXmppElement &xml) override;
|
||||||
|
|
||||||
|
QXmppElement bundleToXml(const Bundle &bundle) override;
|
||||||
|
std::optional<Bundle> bundleFromXml(const QXmppElement &xml) override;
|
||||||
|
|
||||||
|
QXmppIq bundleSetIq(int deviceId, const Bundle &bundle) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QXmpp::Omemo::Variant
|
} // namespace QXmpp::Omemo::Variant
|
||||||
|
@ -4,14 +4,17 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
class QXmppElement;
|
#include <optional>
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
class QXmppElement;
|
||||||
class QXmppIq;
|
class QXmppIq;
|
||||||
|
|
||||||
namespace QXmpp::Omemo {
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class PreKey;
|
||||||
|
class Bundle;
|
||||||
class Device;
|
class Device;
|
||||||
|
|
||||||
class DeviceList;
|
class DeviceList;
|
||||||
|
|
||||||
namespace Variant {
|
namespace Variant {
|
||||||
@ -23,9 +26,18 @@ namespace QXmpp::Omemo {
|
|||||||
virtual QXmppElement deviceToXml(const Device &device) = 0;
|
virtual QXmppElement deviceToXml(const Device &device) = 0;
|
||||||
virtual Device deviceFromXml(const QXmppElement &xml) = 0;
|
virtual Device deviceFromXml(const QXmppElement &xml) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual QString getDeviceListNode() const = 0;
|
||||||
virtual QXmppElement deviceListToXml(const DeviceList &deviceList) = 0;
|
virtual QXmppElement deviceListToXml(const DeviceList &deviceList) = 0;
|
||||||
virtual DeviceList deviceListFromXml(const QXmppElement &xml) = 0;
|
virtual std::optional<DeviceList> latestDeviceListFromPubSubNode(const QXmppElement &xml) = 0;
|
||||||
virtual QXmppIq deviceListSetIq(const DeviceList &deviceList) = 0;
|
virtual QXmppIq deviceListSetIq(const DeviceList &deviceList) = 0;
|
||||||
|
|
||||||
|
virtual QXmppElement preKeyToXml(const PreKey &pk) = 0;
|
||||||
|
virtual std::optional<PreKey> preKeyFromXml(const QXmppElement &xml) = 0;
|
||||||
|
|
||||||
|
virtual QXmppElement bundleToXml(const Bundle& bundle) = 0;
|
||||||
|
virtual std::optional<Bundle> bundleFromXml(const QXmppElement& xml) = 0;
|
||||||
|
|
||||||
|
virtual QXmppIq bundleSetIq(int deviceId, const Bundle& bundle) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Variant
|
} // namespace Variant
|
||||||
|
@ -6,17 +6,19 @@
|
|||||||
#include "ui_contactsettings.h"
|
#include "ui_contactsettings.h"
|
||||||
#include "omemodevices.h"
|
#include "omemodevices.h"
|
||||||
|
|
||||||
ContactSettings::ContactSettings(QWidget *parent)
|
ContactSettings::ContactSettings(QString jid, QWidget *parent)
|
||||||
: QDialog(parent), m_ui(new Ui::ContactSettings()) {
|
: QDialog(parent), jid(std::move(jid)), m_ui(new Ui::ContactSettings()) {
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
connect(m_ui->devicesButton, &QPushButton::clicked, this, &ContactSettings::openDeviceList);
|
connect(m_ui->devicesButton, &QPushButton::clicked, this, &ContactSettings::openDeviceList);
|
||||||
|
|
||||||
|
setWindowTitle(QString("Encryption settings for %1").arg(this->jid));
|
||||||
}
|
}
|
||||||
|
|
||||||
ContactSettings::~ContactSettings() {}
|
ContactSettings::~ContactSettings() {}
|
||||||
|
|
||||||
void ContactSettings::openDeviceList() {
|
void ContactSettings::openDeviceList() {
|
||||||
auto devices = new OMEMODevices(this);
|
auto devices = new OMEMODevices(jid, this);
|
||||||
|
|
||||||
devices->setAttribute(Qt::WA_DeleteOnClose);
|
devices->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
devices->show();
|
devices->show();
|
||||||
|
@ -13,9 +13,11 @@ namespace Ui {
|
|||||||
class ContactSettings : public QDialog {
|
class ContactSettings : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ContactSettings(QWidget *parent = nullptr);
|
explicit ContactSettings(QString jid, QWidget *parent = nullptr);
|
||||||
~ContactSettings() override;
|
~ContactSettings() override;
|
||||||
|
|
||||||
|
const QString jid;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void openDeviceList();
|
void openDeviceList();
|
||||||
|
|
||||||
|
@ -5,9 +5,11 @@
|
|||||||
#include "omemodevices.h"
|
#include "omemodevices.h"
|
||||||
#include "ui_omemodevices.h"
|
#include "ui_omemodevices.h"
|
||||||
|
|
||||||
OMEMODevices::OMEMODevices(QWidget *parent)
|
OMEMODevices::OMEMODevices(QString jid, QWidget *parent)
|
||||||
: QDialog(parent), m_ui(new Ui::OMEMODevices()) {
|
: QDialog(parent), jid(std::move(jid)), m_ui(new Ui::OMEMODevices()) {
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
setWindowTitle(QString("%1's OMEMO devices").arg(this->jid));
|
||||||
}
|
}
|
||||||
|
|
||||||
OMEMODevices::~OMEMODevices() {}
|
OMEMODevices::~OMEMODevices() {}
|
||||||
|
@ -13,9 +13,11 @@ namespace Ui {
|
|||||||
class OMEMODevices : public QDialog {
|
class OMEMODevices : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit OMEMODevices(QWidget *parent = nullptr);
|
explicit OMEMODevices(QString jid, QWidget *parent = nullptr);
|
||||||
~OMEMODevices() override;
|
~OMEMODevices() override;
|
||||||
|
|
||||||
|
const QString jid;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QScopedPointer<Ui::OMEMODevices> m_ui;
|
QScopedPointer<Ui::OMEMODevices> m_ui;
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "ui_squawk.h"
|
#include "ui_squawk.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <utility>
|
||||||
#include <ui/omemo/omemodevices.h>
|
#include <ui/omemo/omemodevices.h>
|
||||||
|
|
||||||
Squawk::Squawk(QWidget *parent) :
|
Squawk::Squawk(QWidget *parent) :
|
||||||
@ -61,7 +62,6 @@ Squawk::Squawk(QWidget *parent) :
|
|||||||
connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed);
|
connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed);
|
||||||
connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged);
|
connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged);
|
||||||
connect(&rosterModel, &Models::Roster::unnoticedMessage, this, &Squawk::onUnnoticedMessage);
|
connect(&rosterModel, &Models::Roster::unnoticedMessage, this, &Squawk::onUnnoticedMessage);
|
||||||
connect(m_ui->actionDeviceList, &QAction::triggered, this, &Squawk::onOMEMODevices);
|
|
||||||
|
|
||||||
connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged);
|
connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged);
|
||||||
connect(&rosterModel, &Models::Roster::requestArchive, this, &Squawk::onRequestArchive);
|
connect(&rosterModel, &Models::Roster::requestArchive, this, &Squawk::onRequestArchive);
|
||||||
@ -136,8 +136,8 @@ void Squawk::onNewContact()
|
|||||||
nc->exec();
|
nc->exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::onOMEMODevices() {
|
void Squawk::openDeviceList(QString bareJid) {
|
||||||
auto od = new OMEMODevices(this);
|
auto od = new OMEMODevices(std::move(bareJid), this);
|
||||||
|
|
||||||
od->setAttribute(Qt::WA_DeleteOnClose);
|
od->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
od->show();
|
od->show();
|
||||||
@ -540,6 +540,10 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QAction* devices = contextMenu->addAction(Shared::icon("security-high"), tr("Devices"));
|
||||||
|
devices->setEnabled(active);
|
||||||
|
connect(devices, &QAction::triggered, [this, acc]() { openDeviceList(acc->getBareJid()); });
|
||||||
|
|
||||||
QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard"));
|
QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard"));
|
||||||
card->setEnabled(active);
|
card->setEnabled(active);
|
||||||
connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true));
|
connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true));
|
||||||
|
@ -155,7 +155,7 @@ private slots:
|
|||||||
void onPasswordPromptRejected();
|
void onPasswordPromptRejected();
|
||||||
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
|
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
|
||||||
void onContextAboutToHide();
|
void onContextAboutToHide();
|
||||||
void onOMEMODevices();
|
void openDeviceList(QString bareJid);
|
||||||
|
|
||||||
void onUnnoticedMessage(const QString& account, const Shared::Message& msg);
|
void onUnnoticedMessage(const QString& account, const Shared::Message& msg);
|
||||||
|
|
||||||
|
13
ui/squawk.ui
13
ui/squawk.ui
@ -169,17 +169,7 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Settings</string>
|
<string>Settings</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuOMEMO">
|
|
||||||
<property name="title">
|
|
||||||
<string>OMEMO</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="security-high"/>
|
|
||||||
</property>
|
|
||||||
<addaction name="actionDeviceList"/>
|
|
||||||
</widget>
|
|
||||||
<addaction name="actionAccounts"/>
|
<addaction name="actionAccounts"/>
|
||||||
<addaction name="menuOMEMO"/>
|
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@ -236,7 +226,8 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionDeviceList">
|
<action name="actionDeviceList">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="computer-laptop"/>
|
<iconset theme="computer-laptop">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Device list</string>
|
<string>Device list</string>
|
||||||
|
@ -437,7 +437,7 @@ void Conversation::onFeedContext(const QPoint& pos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::openEncryptionSettings() {
|
void Conversation::openEncryptionSettings() {
|
||||||
auto cs = new ContactSettings(this);
|
auto cs = new ContactSettings(palJid, this);
|
||||||
|
|
||||||
cs->setAttribute(Qt::WA_DeleteOnClose);
|
cs->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
cs->show();
|
cs->show();
|
||||||
|
Loading…
Reference in New Issue
Block a user