Compare commits
22 Commits
master
...
maybe-omem
Author | SHA1 | Date | |
---|---|---|---|
6c9f1ab964 | |||
442ad37300 | |||
08fe37bfb2 | |||
bb2ce750c8 | |||
bbeeee4c8a | |||
574210f5d9 | |||
2654e38665 | |||
12ffe8e8e6 | |||
006752b31c | |||
b1a8f162ce | |||
6721b62629 | |||
b22a4c8ca3 | |||
a6254d88b3 | |||
2fbbe1ec22 | |||
140b0fa6b4 | |||
cb7e2ede75 | |||
bc66ab7e52 | |||
f94c3dac14 | |||
7d648ab081 | |||
7c1ae4737e | |||
04e745fad4 | |||
9fbbe0c120 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
|||||||
[submodule "external/qxmpp"]
|
[submodule "external/qxmpp"]
|
||||||
path = external/qxmpp
|
path = external/qxmpp
|
||||||
url = https://github.com/qxmpp-project/qxmpp.git
|
url = https://github.com/qxmpp-project/qxmpp.git
|
||||||
|
[submodule "external/signal-protocol-c"]
|
||||||
|
path = external/signal-protocol-c
|
||||||
|
url = https://github.com/signalapp/libsignal-protocol-c.git
|
||||||
|
@ -16,12 +16,14 @@ add_executable(squawk)
|
|||||||
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR})
|
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR})
|
||||||
|
|
||||||
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
||||||
|
option(SYSTEM_SIGNAL "Use system signal-protocol-c lib" ON)
|
||||||
option(WITH_KWALLET "Build KWallet support module" ON)
|
option(WITH_KWALLET "Build KWallet support module" ON)
|
||||||
option(WITH_KIO "Build KIO support module" ON)
|
option(WITH_KIO "Build KIO support module" ON)
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
## Qt
|
## Qt
|
||||||
find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED)
|
find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED)
|
||||||
|
target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml)
|
||||||
|
|
||||||
## QXmpp
|
## QXmpp
|
||||||
if (SYSTEM_QXMPP)
|
if (SYSTEM_QXMPP)
|
||||||
@ -42,6 +44,16 @@ else ()
|
|||||||
target_link_libraries(squawk PRIVATE QXmpp::QXmpp)
|
target_link_libraries(squawk PRIVATE QXmpp::QXmpp)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
# Signal
|
||||||
|
if (NOT SYSTEM_SIGNAL)
|
||||||
|
add_subdirectory(external/signal-protocol-c)
|
||||||
|
add_dependencies(squawk signal-protocol-c)
|
||||||
|
target_link_libraries(squawk PRIVATE signal-protocol-c)
|
||||||
|
else ()
|
||||||
|
find_package(Signal REQUIRED)
|
||||||
|
target_link_libraries(squawk PRIVATE Signal::Signal)
|
||||||
|
endif ()
|
||||||
|
|
||||||
## KIO
|
## KIO
|
||||||
if (WITH_KIO)
|
if (WITH_KIO)
|
||||||
find_package(KF5KIO CONFIG)
|
find_package(KF5KIO CONFIG)
|
||||||
@ -68,15 +80,15 @@ if (WITH_KWALLET)
|
|||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
## Signal (TODO)
|
|
||||||
# find_package(Signal REQUIRED)
|
|
||||||
|
|
||||||
## LMDB
|
## LMDB
|
||||||
find_package(LMDB REQUIRED)
|
find_package(LMDB REQUIRED)
|
||||||
|
|
||||||
# Linking
|
|
||||||
target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml)
|
|
||||||
target_link_libraries(squawk PRIVATE lmdb)
|
target_link_libraries(squawk PRIVATE lmdb)
|
||||||
|
|
||||||
|
# OpenSSL
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
target_link_libraries(squawk PRIVATE OpenSSL::Crypto)
|
||||||
|
|
||||||
|
# Misc
|
||||||
target_link_libraries(squawk PRIVATE simpleCrypt)
|
target_link_libraries(squawk PRIVATE simpleCrypt)
|
||||||
target_link_libraries(squawk PRIVATE uuid)
|
target_link_libraries(squawk PRIVATE uuid)
|
||||||
|
|
||||||
@ -101,6 +113,7 @@ add_subdirectory(resources)
|
|||||||
add_subdirectory(shared)
|
add_subdirectory(shared)
|
||||||
add_subdirectory(translations)
|
add_subdirectory(translations)
|
||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
||||||
|
add_subdirectory(qomemo)
|
||||||
|
|
||||||
# Install the executable
|
# Install the executable
|
||||||
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
@ -47,7 +47,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
network(p_net),
|
network(p_net),
|
||||||
passwordType(Shared::AccountPassword::plain),
|
passwordType(Shared::AccountPassword::plain),
|
||||||
mh(new MessageHandler(this)),
|
mh(new MessageHandler(this)),
|
||||||
rh(new RosterHandler(this))
|
rh(new RosterHandler(this)),
|
||||||
|
omemo(new QXmpp::Omemo::Manager())
|
||||||
{
|
{
|
||||||
config.setUser(p_login);
|
config.setUser(p_login);
|
||||||
config.setDomain(p_server);
|
config.setDomain(p_server);
|
||||||
@ -91,6 +92,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
client.addExtension(rcpm);
|
client.addExtension(rcpm);
|
||||||
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
|
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
|
||||||
|
|
||||||
|
client.addExtension(omemo.get());
|
||||||
|
|
||||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
path += "/" + name;
|
path += "/" + name;
|
||||||
|
@ -30,23 +30,24 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <QXmppRosterManager.h>
|
|
||||||
#include <QXmppCarbonManager.h>
|
|
||||||
#include <QXmppDiscoveryManager.h>
|
|
||||||
#include <QXmppMamManager.h>
|
|
||||||
#include <QXmppMucManager.h>
|
|
||||||
#include <QXmppClient.h>
|
|
||||||
#include <QXmppBookmarkManager.h>
|
#include <QXmppBookmarkManager.h>
|
||||||
#include <QXmppBookmarkSet.h>
|
#include <QXmppBookmarkSet.h>
|
||||||
|
#include <QXmppCarbonManager.h>
|
||||||
|
#include <QXmppClient.h>
|
||||||
|
#include <QXmppDiscoveryManager.h>
|
||||||
|
#include <QXmppMamManager.h>
|
||||||
|
#include <QXmppMessageReceiptManager.h>
|
||||||
|
#include <QXmppMucManager.h>
|
||||||
|
#include <QXmppRosterManager.h>
|
||||||
#include <QXmppUploadRequestManager.h>
|
#include <QXmppUploadRequestManager.h>
|
||||||
#include <QXmppVCardIq.h>
|
#include <QXmppVCardIq.h>
|
||||||
#include <QXmppVCardManager.h>
|
#include <QXmppVCardManager.h>
|
||||||
#include <QXmppMessageReceiptManager.h>
|
#include <qomemo/qxmpp_omemo_manager.h>
|
||||||
|
|
||||||
#include "shared/shared.h"
|
|
||||||
#include "contact.h"
|
|
||||||
#include "conference.h"
|
#include "conference.h"
|
||||||
|
#include "contact.h"
|
||||||
#include "networkaccess.h"
|
#include "networkaccess.h"
|
||||||
|
#include "shared/shared.h"
|
||||||
|
|
||||||
#include "handlers/messagehandler.h"
|
#include "handlers/messagehandler.h"
|
||||||
#include "handlers/rosterhandler.h"
|
#include "handlers/rosterhandler.h"
|
||||||
@ -166,6 +167,8 @@ private:
|
|||||||
MessageHandler* mh;
|
MessageHandler* mh;
|
||||||
RosterHandler* rh;
|
RosterHandler* rh;
|
||||||
|
|
||||||
|
QScopedPointer<QXmpp::Omemo::Manager> omemo;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onClientStateChange(QXmppClient::State state);
|
void onClientStateChange(QXmppClient::State state);
|
||||||
void onClientError(QXmppClient::Error err);
|
void onClientError(QXmppClient::Error err);
|
||||||
|
1
external/signal-protocol-c
vendored
Submodule
1
external/signal-protocol-c
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 3a83a4f4ed2302ff6e68ab569c88793b50c22d28
|
25
qomemo/CMakeLists.txt
Normal file
25
qomemo/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
bundle.cpp
|
||||||
|
bundle.h
|
||||||
|
database.cpp
|
||||||
|
database.h
|
||||||
|
device.cpp
|
||||||
|
device.h
|
||||||
|
device_key_storage.cpp
|
||||||
|
device_key_storage.h
|
||||||
|
device_service.cpp
|
||||||
|
device_service.h
|
||||||
|
key.cpp
|
||||||
|
key.h
|
||||||
|
qomemo.cpp
|
||||||
|
qomemo.h
|
||||||
|
sce.cpp
|
||||||
|
sce.h
|
||||||
|
user_device_list.cpp
|
||||||
|
user_device_list.h
|
||||||
|
qxmpp_omemo_manager.cpp
|
||||||
|
qxmpp_omemo_manager.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(signal)
|
||||||
|
add_subdirectory(variant)
|
5
qomemo/bundle.cpp
Normal file
5
qomemo/bundle.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bundle.h"
|
34
qomemo/bundle.h
Normal file
34
qomemo/bundle.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
class QXmppPubSubIq;
|
||||||
|
|
||||||
|
class QXmppElement;
|
||||||
|
|
||||||
|
class QXmppIq;
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class PreKey {
|
||||||
|
public:
|
||||||
|
int id;
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Bundle {
|
||||||
|
public:
|
||||||
|
QByteArray spk;
|
||||||
|
int spkId;
|
||||||
|
QByteArray spks;
|
||||||
|
QByteArray ik;
|
||||||
|
QList<PreKey> prekeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
180
qomemo/database.cpp
Normal file
180
qomemo/database.cpp
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "database.h"
|
||||||
|
|
||||||
|
#include "bundle.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QException>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
using namespace QXmpp::Omemo;
|
||||||
|
|
||||||
|
Database::Database(QString jid) : jid(std::move(jid)) {
|
||||||
|
auto cacheLocation =
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||||
|
auto path = QString("%1/.omemo/%2").arg(cacheLocation, jid);
|
||||||
|
QDir cache(path);
|
||||||
|
|
||||||
|
if (!cache.exists() && !cache.mkpath(path)) {
|
||||||
|
qWarning() << "Could not create:" << path;
|
||||||
|
throw QException();
|
||||||
|
}
|
||||||
|
|
||||||
|
mdb_env_create(&env);
|
||||||
|
|
||||||
|
mdb_env_set_maxdbs(env, 5);
|
||||||
|
mdb_env_set_mapsize(env, 512UL * 1024UL * 1024UL);
|
||||||
|
mdb_env_open(env, path.toStdString().c_str(), 0, 0664);
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(env, nullptr, 0, &txn);
|
||||||
|
mdb_dbi_open(txn, "keys", MDB_CREATE, &dbiKeys);
|
||||||
|
mdb_dbi_open(txn, "devices", MDB_CREATE, &dbiDevices);
|
||||||
|
mdb_dbi_open(txn, "identity_keys", MDB_CREATE, &dbiIdentityKeys);
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Database::~Database() {
|
||||||
|
mdb_dbi_close(env, dbiKeys);
|
||||||
|
mdb_dbi_close(env, dbiDevices);
|
||||||
|
mdb_dbi_close(env, dbiIdentityKeys);
|
||||||
|
mdb_env_close(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<QByteArray> Database::loadIdentityKeySecret(int deviceId) { return std::nullopt; }
|
||||||
|
|
||||||
|
bool Database::saveIdentityKeySecret(int deviceId,
|
||||||
|
const QByteArray &identityKeySecret) {
|
||||||
|
MDB_val mdbKey, mdbValue;
|
||||||
|
auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString();
|
||||||
|
|
||||||
|
mdbKey.mv_data = key.data();
|
||||||
|
mdbKey.mv_size = key.size();
|
||||||
|
|
||||||
|
mdbValue.mv_data = const_cast<char *>(identityKeySecret.data());
|
||||||
|
mdbValue.mv_size = identityKeySecret.size();
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(env, nullptr, 0, &txn);
|
||||||
|
auto err = mdb_put(txn, dbiIdentityKeys, &mdbKey, &mdbValue, MDB_NOOVERWRITE);
|
||||||
|
if (!err) {
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "could not save identity key secret:" << mdb_strerror(err);
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> Database::loadActiveDeviceId() {
|
||||||
|
MDB_val key, value;
|
||||||
|
|
||||||
|
key.mv_data = (void *) "active";
|
||||||
|
key.mv_size = sizeof("active");
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(env, nullptr, 0, &txn);
|
||||||
|
|
||||||
|
auto err = mdb_get(txn, dbiIdentityKeys, &key, &value);
|
||||||
|
if (err) {
|
||||||
|
qWarning() << "could not load active device id:" << mdb_strerror(err);
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.mv_size != sizeof(int)) {
|
||||||
|
qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int);
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto id = *reinterpret_cast<int *>(value.mv_data);
|
||||||
|
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::saveActiveDeviceId(int deviceId) {
|
||||||
|
MDB_val key, value;
|
||||||
|
|
||||||
|
key.mv_data = (void *) "active";
|
||||||
|
key.mv_size = sizeof("active");
|
||||||
|
|
||||||
|
value.mv_data = &deviceId;
|
||||||
|
value.mv_size = sizeof(deviceId);
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(env, nullptr, 0, &txn);
|
||||||
|
|
||||||
|
auto err = mdb_put(txn, dbiIdentityKeys, &key, &value, 0);
|
||||||
|
if (err) {
|
||||||
|
qWarning() << "could not save active device id" << mdb_strerror(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mdb_txn_commit(txn);
|
||||||
|
if (err) {
|
||||||
|
qWarning() << "could not save active device id" << mdb_strerror(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
56
qomemo/database.h
Normal file
56
qomemo/database.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QString>
|
||||||
|
#include <lmdb.h>
|
||||||
|
|
||||||
|
#include "key.h"
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class Bundle;
|
||||||
|
|
||||||
|
class Database {
|
||||||
|
public:
|
||||||
|
explicit Database(QString jid);
|
||||||
|
~Database();
|
||||||
|
Database(const Database &) = delete;
|
||||||
|
Database(Database &&) = delete;
|
||||||
|
Database &operator=(const Database &) = delete;
|
||||||
|
|
||||||
|
// For local user
|
||||||
|
std::optional<int> loadActiveDeviceId();
|
||||||
|
bool saveActiveDeviceId(int deviceId);
|
||||||
|
|
||||||
|
std::optional<QByteArray> loadIdentityKeySecret(int deviceId);
|
||||||
|
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;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MDB_env *env{};
|
||||||
|
MDB_dbi dbiDevices{};
|
||||||
|
MDB_dbi dbiKeys{};
|
||||||
|
MDB_dbi dbiPreKeys{};
|
||||||
|
MDB_dbi dbiIdentityKeys{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
5
qomemo/device.cpp
Normal file
5
qomemo/device.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "device.h"
|
25
qomemo/device.h
Normal file
25
qomemo/device.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
class QXmppElement;
|
||||||
|
|
||||||
|
class QXmppIq;
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class Device {
|
||||||
|
public:
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeviceList {
|
||||||
|
public:
|
||||||
|
QList<Device> devices;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
14
qomemo/device_key_storage.cpp
Normal file
14
qomemo/device_key_storage.cpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "device_key_storage.h"
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
|
int QXmpp::Omemo::DeviceKeyStorage::generateDeviceId() {
|
||||||
|
QRandomGenerator random{};
|
||||||
|
|
||||||
|
return 1 + random.bounded(INT32_MAX - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmpp::Omemo::DeviceKeyStorage::DeviceKeyStorage(int deviceId) : deviceId(deviceId) {}
|
18
qomemo/device_key_storage.h
Normal file
18
qomemo/device_key_storage.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class DeviceKeyStorage {
|
||||||
|
public:
|
||||||
|
static int generateDeviceId();
|
||||||
|
|
||||||
|
explicit DeviceKeyStorage(int deviceId);
|
||||||
|
|
||||||
|
const int deviceId;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
35
qomemo/device_service.cpp
Normal file
35
qomemo/device_service.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "device_service.h"
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
using namespace QXmpp::Omemo;
|
||||||
|
|
||||||
|
DeviceService::DeviceService(QObject *parent) : QObject(parent) {}
|
||||||
|
|
||||||
|
void DeviceService::onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list) {
|
||||||
|
|
||||||
|
for (const auto &device : list.devices) {
|
||||||
|
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;
|
||||||
|
}
|
38
qomemo/device_service.h
Normal file
38
qomemo/device_service.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
#include "qomemo.h"
|
||||||
|
#include "user_device_list.h"
|
||||||
|
#include "database.h"
|
||||||
|
|
||||||
|
#include <QXmppClient.h>
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class DeviceList;
|
||||||
|
|
||||||
|
class DeviceService : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DeviceService(QObject *parent);
|
||||||
|
|
||||||
|
QSharedPointer<Database> getDatabase(const QString& jid);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void addIdentity(const QString& jid, int deviceId, const QByteArray& publicKey);
|
||||||
|
|
||||||
|
void onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
|
||||||
|
|
||||||
|
void onDeviceListNotFound(const QString &jid);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QString, QSharedPointer<Database>> databases{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // 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{};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
94
qomemo/qomemo.cpp
Normal file
94
qomemo/qomemo.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qomemo.h"
|
||||||
|
#include "sce.h"
|
||||||
|
#include <shared/qxmppfactories.h>
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDomDocument>
|
||||||
|
|
||||||
|
using namespace QXmpp::Factories;
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Omemo::EncryptedMessage::header() const {
|
||||||
|
auto result = createElement("header");
|
||||||
|
result.setAttribute("sid", QString::number(fromDeviceId));
|
||||||
|
|
||||||
|
auto ivNode = createElement("iv");
|
||||||
|
ivNode.setValue(iv);
|
||||||
|
|
||||||
|
for (const auto &key : keys) {
|
||||||
|
result.appendChild(key.toXml());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const {
|
||||||
|
auto result = createElement("encrypted", "eu.siacs.conversations.axolotl");
|
||||||
|
|
||||||
|
result.appendChild(header());
|
||||||
|
// TODO: Payload is optional
|
||||||
|
result.appendChild(payload());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Omemo::EncryptedMessage::payload() const {
|
||||||
|
QBuffer buffer;
|
||||||
|
buffer.open(QIODevice::ReadWrite);
|
||||||
|
QXmlStreamWriter writer(&buffer);
|
||||||
|
message.toXml(&writer);
|
||||||
|
|
||||||
|
QDomDocument doc;
|
||||||
|
doc.setContent(buffer.data(), true);
|
||||||
|
|
||||||
|
QXmppElement root(doc.documentElement());
|
||||||
|
root.setTagName("payload");
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Omemo::EncryptedMessage::content() const {
|
||||||
|
auto envelope = createElement("content", "urn:xmpp:sce:0");
|
||||||
|
|
||||||
|
envelope.appendChild(payload());
|
||||||
|
|
||||||
|
if (!from.isEmpty()) {
|
||||||
|
auto fromNode = createElement("from");
|
||||||
|
fromNode.setAttribute("jid", from);
|
||||||
|
envelope.appendChild(fromNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!to.isEmpty()) {
|
||||||
|
auto toNode = createElement("to");
|
||||||
|
toNode.setAttribute("jid", to);
|
||||||
|
envelope.appendChild(toNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!timestamp.isNull()) {
|
||||||
|
auto timeNode = createElement("time");
|
||||||
|
timeNode.setAttribute("stamp", timestamp.toString(Qt::DateFormat::ISODate));
|
||||||
|
envelope.appendChild(timeNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rpad = createElement("rpad");
|
||||||
|
rpad.setValue(QXmpp::Sce::generatePadding());
|
||||||
|
envelope.appendChild(rpad);
|
||||||
|
|
||||||
|
return envelope;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Omemo::MessageKey::toXml() const {
|
||||||
|
auto result = createElement("key");
|
||||||
|
|
||||||
|
result.setAttribute("rid", QString::number(receivingDeviceId));
|
||||||
|
if (prekey)
|
||||||
|
result.setAttribute("prekey", "true");
|
||||||
|
|
||||||
|
result.setValue(key);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
42
qomemo/qomemo.h
Normal file
42
qomemo/qomemo.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QXmppPubSubIq.h>
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QXmppMessage.h>
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class MessageKey {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] QXmppElement toXml() const;
|
||||||
|
|
||||||
|
int receivingDeviceId{};
|
||||||
|
bool prekey{};
|
||||||
|
QString key{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class EncryptedMessage {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] QXmppElement header() const;
|
||||||
|
[[nodiscard]] QXmppElement content() const;
|
||||||
|
[[nodiscard]] QXmppElement toXml() const;
|
||||||
|
[[nodiscard]] QXmppElement payload() const;
|
||||||
|
|
||||||
|
int fromDeviceId{};
|
||||||
|
|
||||||
|
QList<MessageKey> keys{};
|
||||||
|
QString from{};
|
||||||
|
QString to{};
|
||||||
|
QDateTime timestamp{};
|
||||||
|
|
||||||
|
QString iv{};
|
||||||
|
|
||||||
|
QXmppMessage message{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
292
qomemo/qxmpp_omemo_manager.cpp
Normal file
292
qomemo/qxmpp_omemo_manager.cpp
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qxmpp_omemo_manager.h"
|
||||||
|
|
||||||
|
#include "qomemo/signal/context.h"
|
||||||
|
|
||||||
|
#include "bundle.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "variant/conversations.h"
|
||||||
|
|
||||||
|
#include <QDomElement>
|
||||||
|
|
||||||
|
#include <QXmppClient.h>
|
||||||
|
#include <QXmppPubSubIq.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <QRandomGenerator64>
|
||||||
|
#include <external/signal-protocol-c/src/signal_protocol_internal.h>
|
||||||
|
|
||||||
|
using namespace QXmpp::Omemo;
|
||||||
|
|
||||||
|
Manager::Manager()
|
||||||
|
: deviceService{new DeviceService(this)},
|
||||||
|
omemoVariant(new Variant::Conversations),
|
||||||
|
signalContext(new Signal::Context) {
|
||||||
|
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) {
|
||||||
|
QString str{};
|
||||||
|
QTextStream info(&str);
|
||||||
|
stanza.save(info, 4);
|
||||||
|
|
||||||
|
std::cout << str.toStdString();
|
||||||
|
|
||||||
|
if (handleDeviceList(stanza) || handleMissingDeviceList(stanza) || handleEncryptedMessage(stanza))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Manager::handleEncryptedMessage(const QDomElement &stanza) {
|
||||||
|
if (stanza.tagName() != "message")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto encrypted = stanza.firstChildElement("encrypted");
|
||||||
|
if (encrypted.isNull())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
qDebug() << "!!!! Got encrypted message!!";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QXmpp::Omemo::Manager::setClient(QXmppClient *client) {
|
||||||
|
QXmppClientExtension::setClient(client);
|
||||||
|
|
||||||
|
if (!client)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QObject::connect(client, &QXmppClient::connected, this,
|
||||||
|
&Manager::fetchOwnDevices);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QXmpp::Omemo::Manager::fetchOwnDevices() {
|
||||||
|
QXmppPubSubIq iq{};
|
||||||
|
iq.setFrom(client()->configuration().jid());
|
||||||
|
iq.setTo(client()->configuration().jidBare());
|
||||||
|
iq.setType(QXmppIq::Get);
|
||||||
|
iq.setQueryNode(omemoVariant->getDeviceListNode());
|
||||||
|
|
||||||
|
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::system()->bounded(INT32_MAX);
|
||||||
|
db->saveActiveDeviceId(deviceId);
|
||||||
|
|
||||||
|
Device device{};
|
||||||
|
device.id = deviceId;
|
||||||
|
|
||||||
|
auto updatedList = currentList;
|
||||||
|
|
||||||
|
|
||||||
|
updatedList.devices.push_back(device);
|
||||||
|
publishDeviceList(updatedList);
|
||||||
|
|
||||||
|
auto bundle = generateAndSaveBundle(deviceId);
|
||||||
|
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);
|
||||||
|
|
||||||
|
result.ik = identityKey;
|
||||||
|
|
||||||
|
// 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 = 1;
|
||||||
|
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));
|
||||||
|
|
||||||
|
result.spk = spk.key.publicKey;
|
||||||
|
result.spks = spk.signature;
|
||||||
|
result.spkId = 1;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
PreKey pk{};
|
||||||
|
pk.data = preKey.publicKey;
|
||||||
|
pk.id = i;
|
||||||
|
|
||||||
|
result.prekeys.append(pk);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
64
qomemo/qxmpp_omemo_manager.h
Normal file
64
qomemo/qxmpp_omemo_manager.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "device_service.h"
|
||||||
|
#include "qomemo.h"
|
||||||
|
#include "variant/omemo_base.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QXmppClientExtension.h>
|
||||||
|
|
||||||
|
namespace Signal {
|
||||||
|
class Context;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class Manager : public QXmppClientExtension {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Manager();
|
||||||
|
~Manager() override = default;
|
||||||
|
|
||||||
|
bool handleStanza(const QDomElement &stanza) override;
|
||||||
|
|
||||||
|
bool handleDeviceList(const QDomElement& stanza);
|
||||||
|
|
||||||
|
bool handleMissingDeviceList(const QDomElement& stanza);
|
||||||
|
|
||||||
|
bool handleEncryptedMessage(const QDomElement& stanza);
|
||||||
|
|
||||||
|
QSharedPointer<DeviceService> getDeviceService();
|
||||||
|
|
||||||
|
Bundle generateAndSaveBundle(int deviceId);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
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:
|
||||||
|
void deviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
|
||||||
|
|
||||||
|
void deviceListNotFound(const QString &jid);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setClient(QXmppClient *client) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<DeviceService> deviceService;
|
||||||
|
QScopedPointer<Variant::Base> omemoVariant;
|
||||||
|
std::shared_ptr<Signal::Context> signalContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
26
qomemo/sce.cpp
Normal file
26
qomemo/sce.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sce.h"
|
||||||
|
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
|
#define RPAD_ALPHABET "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
||||||
|
|
||||||
|
constexpr int RPAD_MAX_LENGTH = 200;
|
||||||
|
|
||||||
|
QString QXmpp::Sce::generatePadding() {
|
||||||
|
QRandomGenerator random{};
|
||||||
|
QString result{};
|
||||||
|
QString alphabet{QStringLiteral(RPAD_ALPHABET)};
|
||||||
|
|
||||||
|
auto length = random.bounded(RPAD_MAX_LENGTH);
|
||||||
|
result.resize(length);
|
||||||
|
|
||||||
|
for (auto i = 0; i < length; ++i) {
|
||||||
|
result[i] = alphabet[random.bounded(alphabet.length())];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
14
qomemo/sce.h
Normal file
14
qomemo/sce.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QXmppElement.h>
|
||||||
|
|
||||||
|
namespace QXmpp::Sce {
|
||||||
|
|
||||||
|
QString generatePadding();
|
||||||
|
|
||||||
|
}
|
9
qomemo/signal/CMakeLists.txt
Normal file
9
qomemo/signal/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
context.cpp
|
||||||
|
context.h
|
||||||
|
util.cpp
|
||||||
|
util.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(crypto)
|
||||||
|
add_subdirectory(stores)
|
28
qomemo/signal/context.cpp
Normal file
28
qomemo/signal/context.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
|
||||||
|
using namespace Signal;
|
||||||
|
|
||||||
|
Context::Context() : cryptoProvider{ Signal::Crypto::createProvider() } {
|
||||||
|
signal_context_create(&ctx, nullptr);
|
||||||
|
signal_context_set_crypto_provider(ctx, &cryptoProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
Context::~Context() {
|
||||||
|
signal_context_destroy(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Crypto::ECKeyPair> Context::generateCurveKeyPair() {
|
||||||
|
auto result = std::unique_ptr<Crypto::ECKeyPair>();
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_context *Context::temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped() {
|
||||||
|
return ctx;
|
||||||
|
}
|
32
qomemo/signal/context.h
Normal file
32
qomemo/signal/context.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "crypto/ec.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal {
|
||||||
|
|
||||||
|
class Context {
|
||||||
|
public:
|
||||||
|
Context();
|
||||||
|
~Context();
|
||||||
|
Context(const Context &) = delete;
|
||||||
|
Context(Context &&) = delete;
|
||||||
|
Context &operator=(const Context &) = delete;
|
||||||
|
|
||||||
|
std::unique_ptr<Crypto::ECKeyPair> generateCurveKeyPair();
|
||||||
|
|
||||||
|
signal_context *temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped();
|
||||||
|
|
||||||
|
private:
|
||||||
|
signal_crypto_provider cryptoProvider{};
|
||||||
|
signal_context *ctx{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal
|
12
qomemo/signal/crypto/CMakeLists.txt
Normal file
12
qomemo/signal/crypto/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
aes_openssl.cpp
|
||||||
|
aes_openssl.h
|
||||||
|
crypto.cpp
|
||||||
|
crypto.h
|
||||||
|
hmac_sha256_openssl.cpp
|
||||||
|
hmac_sha256_openssl.h
|
||||||
|
sha512_digest_openssl.cpp
|
||||||
|
sha512_digest_openssl.h
|
||||||
|
ec.cpp
|
||||||
|
ec.h
|
||||||
|
)
|
187
qomemo/signal/crypto/aes_openssl.cpp
Normal file
187
qomemo/signal/crypto/aes_openssl.cpp
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "aes_openssl.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/opensslv.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace Signal::Crypto;
|
||||||
|
|
||||||
|
class EVPCipherCtxWrapper {
|
||||||
|
public:
|
||||||
|
EVPCipherCtxWrapper() {
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
ctx = EVP_CIPHER_CTX_new();
|
||||||
|
#else
|
||||||
|
ctx = new EVP_CIPHER_CTX;
|
||||||
|
EVP_CIPHER_CTX_init(ctx);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
~EVPCipherCtxWrapper() {
|
||||||
|
if (good()) {
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
#else
|
||||||
|
EVP_CIPHER_CTX_cleanup(ctx);
|
||||||
|
delete ctx;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPCipherCtxWrapper(const EVPCipherCtxWrapper &) = delete;
|
||||||
|
EVPCipherCtxWrapper(EVPCipherCtxWrapper &&) = delete;
|
||||||
|
EVPCipherCtxWrapper &operator=(const EVPCipherCtxWrapper &) = delete;
|
||||||
|
|
||||||
|
[[nodiscard]] bool good() const { return ctx != nullptr; }
|
||||||
|
|
||||||
|
[[nodiscard]] EVP_CIPHER_CTX *operator*() const { return ctx; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
EVP_CIPHER_CTX *ctx{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
static const EVP_CIPHER *aes_cipher(int cipher, size_t key_len) {
|
||||||
|
if (cipher == SG_CIPHER_AES_CBC_PKCS5) {
|
||||||
|
if (key_len == 16) {
|
||||||
|
return EVP_aes_128_cbc();
|
||||||
|
} else if (key_len == 24) {
|
||||||
|
return EVP_aes_192_cbc();
|
||||||
|
} else if (key_len == 32) {
|
||||||
|
return EVP_aes_256_cbc();
|
||||||
|
}
|
||||||
|
} else if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
|
||||||
|
if (key_len == 16) {
|
||||||
|
return EVP_aes_128_ctr();
|
||||||
|
} else if (key_len == 24) {
|
||||||
|
return EVP_aes_192_ctr();
|
||||||
|
} else if (key_len == 32) {
|
||||||
|
return EVP_aes_256_ctr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Aes::encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv,
|
||||||
|
size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *) {
|
||||||
|
const EVP_CIPHER *evp_cipher = aes_cipher(cipher, key_len);
|
||||||
|
if (!evp_cipher) {
|
||||||
|
fprintf(stderr, "invalid AES mode or key size: %zu\n", key_len);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iv_len != 16) {
|
||||||
|
fprintf(stderr, "invalid AES IV size: %zu\n", iv_len);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plaintext_len > INT_MAX - EVP_CIPHER_block_size(evp_cipher)) {
|
||||||
|
fprintf(stderr, "invalid plaintext length: %zu\n", plaintext_len);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPCipherCtxWrapper ctx{};
|
||||||
|
if (!ctx.good()) {
|
||||||
|
fprintf(stderr, "could not create context\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = EVP_EncryptInit_ex(*ctx, evp_cipher, nullptr, key, iv);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot initialize cipher\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
|
||||||
|
result = EVP_CIPHER_CTX_set_padding(*ctx, 0);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot set padding\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto out_buf = std::make_unique<uint8_t>(plaintext_len + EVP_CIPHER_block_size(evp_cipher));
|
||||||
|
|
||||||
|
int out_len = 0;
|
||||||
|
result = EVP_EncryptUpdate(*ctx, out_buf.get(), &out_len, plaintext,
|
||||||
|
plaintext_len);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot encrypt plaintext\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int final_len = 0;
|
||||||
|
result = EVP_EncryptFinal_ex(*ctx, out_buf.get() + out_len, &final_len);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot finish encrypting plaintext\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = signal_buffer_create(out_buf.get(), out_len + final_len);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Aes::decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv,
|
||||||
|
size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *) {
|
||||||
|
const EVP_CIPHER *evp_cipher = aes_cipher(cipher, key_len);
|
||||||
|
if (!evp_cipher) {
|
||||||
|
fprintf(stderr, "invalid AES mode or key size: %zu\n", key_len);
|
||||||
|
return SG_ERR_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iv_len != 16) {
|
||||||
|
fprintf(stderr, "invalid AES IV size: %zu\n", iv_len);
|
||||||
|
return SG_ERR_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ciphertext_len > INT_MAX - EVP_CIPHER_block_size(evp_cipher)) {
|
||||||
|
fprintf(stderr, "invalid ciphertext length: %zu\n", ciphertext_len);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPCipherCtxWrapper ctx{};
|
||||||
|
if (!ctx.good()) {
|
||||||
|
fprintf(stderr, "could not create context\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = EVP_DecryptInit_ex(*ctx, evp_cipher, nullptr, key, iv);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot initialize cipher\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
|
||||||
|
result = EVP_CIPHER_CTX_set_padding(*ctx, 0);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot set padding\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto out_buf = std::make_unique<uint8_t>(ciphertext_len + EVP_CIPHER_block_size(evp_cipher));
|
||||||
|
|
||||||
|
int out_len = 0;
|
||||||
|
result = EVP_DecryptUpdate(*ctx, out_buf.get(), &out_len, ciphertext, ciphertext_len);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot decrypt ciphertext\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int final_len = 0;
|
||||||
|
result = EVP_DecryptFinal_ex(*ctx, out_buf.get() + out_len, &final_len);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot finish decrypting ciphertext\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = signal_buffer_create(out_buf.get(), out_len + final_len);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
17
qomemo/signal/crypto/aes_openssl.h
Normal file
17
qomemo/signal/crypto/aes_openssl.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Crypto::Aes {
|
||||||
|
|
||||||
|
int encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv,
|
||||||
|
size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *user_data);
|
||||||
|
|
||||||
|
int decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv,
|
||||||
|
size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *user_data);
|
||||||
|
|
||||||
|
} // namespace Signal::Crypto::Aes
|
40
qomemo/signal/crypto/crypto.cpp
Normal file
40
qomemo/signal/crypto/crypto.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "crypto.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "aes_openssl.h"
|
||||||
|
#include "hmac_sha256_openssl.h"
|
||||||
|
#include "sha512_digest_openssl.h"
|
||||||
|
|
||||||
|
int random_func(uint8_t *data, size_t len, void *) {
|
||||||
|
if (RAND_bytes(data, len)) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_crypto_provider Signal::Crypto::createProvider() {
|
||||||
|
signal_crypto_provider result{};
|
||||||
|
|
||||||
|
result.random_func = random_func;
|
||||||
|
result.hmac_sha256_init_func = HmacSha256::init;
|
||||||
|
result.hmac_sha256_update_func = HmacSha256::update;
|
||||||
|
result.hmac_sha256_final_func = HmacSha256::final;
|
||||||
|
result.hmac_sha256_cleanup_func = HmacSha256::cleanup;
|
||||||
|
result.sha512_digest_init_func = Sha512::init;
|
||||||
|
result.sha512_digest_update_func = Sha512::update;
|
||||||
|
result.sha512_digest_final_func = Sha512::final;
|
||||||
|
result.sha512_digest_cleanup_func = Sha512::cleanup;
|
||||||
|
result.encrypt_func = Aes::encrypt;
|
||||||
|
result.decrypt_func = Aes::decrypt;
|
||||||
|
result.user_data = nullptr;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
13
qomemo/signal/crypto/crypto.h
Normal file
13
qomemo/signal/crypto/crypto.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Crypto {
|
||||||
|
|
||||||
|
signal_crypto_provider createProvider();
|
||||||
|
|
||||||
|
}
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
71
qomemo/signal/crypto/hmac_sha256_openssl.cpp
Normal file
71
qomemo/signal/crypto/hmac_sha256_openssl.cpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hmac_sha256_openssl.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace Signal::Crypto;
|
||||||
|
|
||||||
|
int HmacSha256::init(void **hmac_context, const uint8_t *key, size_t key_len, void *) {
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
HMAC_CTX *ctx = HMAC_CTX_new();
|
||||||
|
if (!ctx) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
auto ctx = new HMAC_CTX;
|
||||||
|
HMAC_CTX_init(ctx);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*hmac_context = ctx;
|
||||||
|
|
||||||
|
if (HMAC_Init_ex(ctx, key, key_len, EVP_sha256(), nullptr) != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SG_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HmacSha256::update(void *hmac_context, const uint8_t *data, size_t data_len, void *) {
|
||||||
|
auto ctx = static_cast<HMAC_CTX *>(hmac_context);
|
||||||
|
int result = HMAC_Update(ctx, data, data_len);
|
||||||
|
|
||||||
|
return (result == 1) ? SG_SUCCESS : SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HmacSha256::final(void *hmac_context, signal_buffer **output, void *) {
|
||||||
|
auto ctx = static_cast<HMAC_CTX *>(hmac_context);
|
||||||
|
unsigned char md[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
if (HMAC_Final(ctx, md, &len) != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_buffer *output_buffer = signal_buffer_create(md, len);
|
||||||
|
if (!output_buffer) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = output_buffer;
|
||||||
|
|
||||||
|
return SG_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HmacSha256::cleanup(void *hmac_context, void *) {
|
||||||
|
if (hmac_context) {
|
||||||
|
auto ctx = static_cast<HMAC_CTX *>(hmac_context);
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
HMAC_CTX_free(ctx);
|
||||||
|
#else
|
||||||
|
HMAC_CTX_cleanup(ctx);
|
||||||
|
delete ctx;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
16
qomemo/signal/crypto/hmac_sha256_openssl.h
Normal file
16
qomemo/signal/crypto/hmac_sha256_openssl.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Crypto::HmacSha256 {
|
||||||
|
|
||||||
|
int init(void **hmac_context, const uint8_t *key, size_t key_len, void *);
|
||||||
|
int update(void *hmac_context, const uint8_t *data, size_t data_len, void *);
|
||||||
|
int final(void *hmac_context, signal_buffer **output, void *);
|
||||||
|
void cleanup(void *hmac_context, void *);
|
||||||
|
|
||||||
|
} // namespace Signal::Crypto::HmacSha256
|
67
qomemo/signal/crypto/sha512_digest_openssl.cpp
Normal file
67
qomemo/signal/crypto/sha512_digest_openssl.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sha512_digest_openssl.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace Signal::Crypto;
|
||||||
|
|
||||||
|
int Sha512::init(void **digest_context, void *) {
|
||||||
|
auto ctx = EVP_MD_CTX_create();
|
||||||
|
if (!ctx) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = EVP_DigestInit_ex(ctx, EVP_sha512(), nullptr);
|
||||||
|
|
||||||
|
if (result == 1) {
|
||||||
|
*digest_context = ctx;
|
||||||
|
return SG_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_MD_CTX_destroy(ctx);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sha512::update(void *digest_context, const uint8_t *data, size_t data_len, void *) {
|
||||||
|
auto ctx = static_cast<EVP_MD_CTX *>(digest_context);
|
||||||
|
auto result = EVP_DigestUpdate(ctx, data, data_len);
|
||||||
|
|
||||||
|
return (result == 1) ? SG_SUCCESS : SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sha512::final(void *digest_context, signal_buffer **output, void *) {
|
||||||
|
auto ctx = static_cast<EVP_MD_CTX *>(digest_context);
|
||||||
|
unsigned char md[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
auto result = EVP_DigestFinal_ex(ctx, md, &len);
|
||||||
|
if (result != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = EVP_DigestInit_ex(ctx, EVP_sha512(), nullptr);
|
||||||
|
if (result != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_buffer *output_buffer = signal_buffer_create(md, len);
|
||||||
|
if (!output_buffer) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = output_buffer;
|
||||||
|
|
||||||
|
return SG_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sha512::cleanup(void *digest_context, void *) {
|
||||||
|
auto ctx = static_cast<EVP_MD_CTX *>(digest_context);
|
||||||
|
EVP_MD_CTX_destroy(ctx);
|
||||||
|
}
|
16
qomemo/signal/crypto/sha512_digest_openssl.h
Normal file
16
qomemo/signal/crypto/sha512_digest_openssl.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Crypto::Sha512 {
|
||||||
|
|
||||||
|
int init(void **digest_context, void *);
|
||||||
|
int update(void *digest_context, const uint8_t *data, size_t data_len, void *);
|
||||||
|
int final(void *digest_context, signal_buffer **output, void *);
|
||||||
|
void cleanup(void *digest_context, void *);
|
||||||
|
|
||||||
|
} // namespace Signal::Crypto::Sha512
|
14
qomemo/signal/stores/CMakeLists.txt
Normal file
14
qomemo/signal/stores/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
identity_key_store.cpp
|
||||||
|
identity_key_store.h
|
||||||
|
pre_key_store.cpp
|
||||||
|
pre_key_store.h
|
||||||
|
sender_key_store.cpp
|
||||||
|
sender_key_store.h
|
||||||
|
session_store.cpp
|
||||||
|
session_store.h
|
||||||
|
signed_pre_key_store.cpp
|
||||||
|
signed_pre_key_store.h
|
||||||
|
store_context.cpp
|
||||||
|
store_context.h
|
||||||
|
)
|
70
qomemo/signal/stores/identity_key_store.cpp
Normal file
70
qomemo/signal/stores/identity_key_store.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "identity_key_store.h"
|
||||||
|
|
||||||
|
#include "qomemo/signal/util.h"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
Signal::Store::IdentityKeyStore::IdentityKeyStore(QXmpp::Omemo::DeviceService &deviceService, QString jid, int deviceId)
|
||||||
|
: 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) {
|
||||||
|
return static_cast<IdentityKeyStore *>(ptr)->getIdentityKeyPair(public_data, private_data);
|
||||||
|
};
|
||||||
|
store.get_local_registration_id = [](void *ptr, uint32_t *registrationId) {
|
||||||
|
return static_cast<IdentityKeyStore *>(ptr)->getLocalRegistrationId(registrationId);
|
||||||
|
};
|
||||||
|
store.is_trusted_identity = [](const signal_protocol_address *address, uint8_t *key_data, size_t key_len,
|
||||||
|
void *ptr) {
|
||||||
|
return static_cast<IdentityKeyStore *>(ptr)->isTrustedIdentity(address, key_data, key_len);
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
}
|
32
qomemo/signal/stores/identity_key_store.h
Normal file
32
qomemo/signal/stores/identity_key_store.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
#include "qomemo/device_service.h"
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class IdentityKeyStore {
|
||||||
|
public:
|
||||||
|
IdentityKeyStore(QXmpp::Omemo::DeviceService &deviceService, QString jid, int deviceId);
|
||||||
|
|
||||||
|
int getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data);
|
||||||
|
int getLocalRegistrationId(uint32_t *registration_id);
|
||||||
|
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);
|
||||||
|
|
||||||
|
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
|
38
qomemo/signal/stores/pre_key_store.cpp
Normal file
38
qomemo/signal/stores/pre_key_store.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pre_key_store.h"
|
||||||
|
|
||||||
|
Signal::Store::PreKeyStore::PreKeyStore(QSharedPointer<QXmpp::Omemo::Database> database) : database(
|
||||||
|
std::move(database)) {}
|
||||||
|
|
||||||
|
int Signal::Store::PreKeyStore::containsPreKey(uint32_t pre_key_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::PreKeyStore::loadPreKey(signal_buffer **record, uint32_t pre_key_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::PreKeyStore::storePreKey(uint32_t pre_key_id, uint8_t *record, size_t record_len) {
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
}
|
28
qomemo/signal/stores/pre_key_store.h
Normal file
28
qomemo/signal/stores/pre_key_store.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
#include "qomemo/device_service.h"
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class PreKeyStore {
|
||||||
|
public:
|
||||||
|
explicit PreKeyStore(QSharedPointer<QXmpp::Omemo::Database> database);
|
||||||
|
|
||||||
|
int containsPreKey(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 removePreKey(uint32_t pre_key_id);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_pre_key_store &store);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<QXmpp::Omemo::Database> database;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal::Store
|
30
qomemo/signal/stores/sender_key_store.cpp
Normal file
30
qomemo/signal/stores/sender_key_store.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sender_key_store.h"
|
||||||
|
|
||||||
|
int Signal::Store::SenderKeyStore::loadSenderKey(signal_buffer **record, signal_buffer **user_record,
|
||||||
|
const signal_protocol_sender_key_name *sender_key_name) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SenderKeyStore::storeSenderKey(const signal_protocol_sender_key_name *sender_key_name,
|
||||||
|
uint8_t *record, size_t record_len, uint8_t *user_record,
|
||||||
|
size_t user_record_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::Store::SenderKeyStore::fillCallbacks(signal_protocol_sender_key_store &store) {
|
||||||
|
store.user_data = nullptr;
|
||||||
|
store.destroy_func = nullptr;
|
||||||
|
store.load_sender_key = [](signal_buffer **record, signal_buffer **user_record,
|
||||||
|
const signal_protocol_sender_key_name *sender_key_name, void *ptr) {
|
||||||
|
return static_cast<SenderKeyStore *>(ptr)->loadSenderKey(record, user_record, sender_key_name);
|
||||||
|
};
|
||||||
|
store.store_sender_key = [](const signal_protocol_sender_key_name *sender_key_name, uint8_t *record,
|
||||||
|
size_t record_len, uint8_t *user_record, size_t user_record_len, void *ptr) {
|
||||||
|
return static_cast<SenderKeyStore *>(ptr)->storeSenderKey(sender_key_name, record, record_len, user_record,
|
||||||
|
user_record_len);
|
||||||
|
};
|
||||||
|
}
|
21
qomemo/signal/stores/sender_key_store.h
Normal file
21
qomemo/signal/stores/sender_key_store.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class SenderKeyStore {
|
||||||
|
public:
|
||||||
|
int storeSenderKey(const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, size_t record_len,
|
||||||
|
uint8_t *user_record, size_t user_record_len);
|
||||||
|
int loadSenderKey(signal_buffer **record, signal_buffer **user_record,
|
||||||
|
const signal_protocol_sender_key_name *sender_key_name);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_sender_key_store &store);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal::Store
|
57
qomemo/signal/stores/session_store.cpp
Normal file
57
qomemo/signal/stores/session_store.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "session_store.h"
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::loadSession(signal_buffer **record, signal_buffer **user_record,
|
||||||
|
const signal_protocol_address *address) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::getSubDeviceSessions(signal_int_list **sessions, const char *name, size_t name_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::storeSession(const signal_protocol_address *address, uint8_t *record,
|
||||||
|
size_t record_len, uint8_t *user_record, size_t user_record_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::containsSession(const signal_protocol_address *address) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::deleteSession(const signal_protocol_address *address) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::deleteAllSessions(const char *name, size_t name_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::Store::SessionStore::fillCallbacks(signal_protocol_session_store &store) {
|
||||||
|
store.user_data = nullptr;
|
||||||
|
store.destroy_func = nullptr;
|
||||||
|
store.load_session_func = [](signal_buffer **record, signal_buffer **user_record,
|
||||||
|
const signal_protocol_address *address, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->loadSession(record, user_record, address);
|
||||||
|
};
|
||||||
|
store.get_sub_device_sessions_func = [](signal_int_list **sessions, const char *name, size_t name_len, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->getSubDeviceSessions(sessions, name, name_len);
|
||||||
|
};
|
||||||
|
store.store_session_func = [](const signal_protocol_address *address, uint8_t *record, size_t record_len,
|
||||||
|
uint8_t *user_record, size_t user_record_len, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->storeSession(address, record, record_len, user_record,
|
||||||
|
user_record_len);
|
||||||
|
};
|
||||||
|
store.contains_session_func = [](const signal_protocol_address *address, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->containsSession(address);
|
||||||
|
};
|
||||||
|
store.delete_session_func = [](const signal_protocol_address *address, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->deleteSession(address);
|
||||||
|
};
|
||||||
|
store.delete_all_sessions_func = [](const char *name, size_t name_len, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->deleteAllSessions(name, name_len);
|
||||||
|
};
|
||||||
|
}
|
24
qomemo/signal/stores/session_store.h
Normal file
24
qomemo/signal/stores/session_store.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class SessionStore {
|
||||||
|
public:
|
||||||
|
int loadSession(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address);
|
||||||
|
int getSubDeviceSessions(signal_int_list **sessions, const char *name, size_t name_len);
|
||||||
|
int storeSession(const signal_protocol_address *address, uint8_t *record, size_t record_len,
|
||||||
|
uint8_t *user_record, size_t user_record_len);
|
||||||
|
int containsSession(const signal_protocol_address *address);
|
||||||
|
int deleteSession(const signal_protocol_address *address);
|
||||||
|
int deleteAllSessions(const char *name, size_t name_len);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_session_store &store);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal::Store
|
43
qomemo/signal/stores/signed_pre_key_store.cpp
Normal file
43
qomemo/signal/stores/signed_pre_key_store.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "signed_pre_key_store.h"
|
||||||
|
|
||||||
|
int Signal::Store::SignedPreKeyStore::loadSignedPreKey(signal_buffer **record, uint32_t signed_pre_key_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Signal::Store::SignedPreKeyStore::storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SignedPreKeyStore::containsSignedPreKey(uint32_t signed_pre_key_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SignedPreKeyStore::removeSignedPreKey(uint32_t signed_pre_key_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::Store::SignedPreKeyStore::fillCallbacks(signal_protocol_signed_pre_key_store &store) {
|
||||||
|
store.user_data = nullptr;
|
||||||
|
store.destroy_func = nullptr;
|
||||||
|
store.load_signed_pre_key = [](signal_buffer **record, uint32_t signed_pre_key_id, void *ptr) {
|
||||||
|
return static_cast<SignedPreKeyStore *>(ptr)->loadSignedPreKey(
|
||||||
|
record, signed_pre_key_id);
|
||||||
|
};
|
||||||
|
store.store_signed_pre_key = [](uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *ptr) {
|
||||||
|
return static_cast<SignedPreKeyStore *>(ptr)->storeSignedPreKey(
|
||||||
|
signed_pre_key_id, record, record_len);
|
||||||
|
};
|
||||||
|
store.contains_signed_pre_key = [](uint32_t signed_pre_key_id, void *ptr) {
|
||||||
|
return static_cast<SignedPreKeyStore *>(ptr)->containsSignedPreKey(
|
||||||
|
signed_pre_key_id);
|
||||||
|
};
|
||||||
|
store.remove_signed_pre_key = [](uint32_t signed_pre_key_id, void *ptr) {
|
||||||
|
return static_cast<SignedPreKeyStore *>(ptr)->removeSignedPreKey(
|
||||||
|
signed_pre_key_id);
|
||||||
|
};
|
||||||
|
}
|
21
qomemo/signal/stores/signed_pre_key_store.h
Normal file
21
qomemo/signal/stores/signed_pre_key_store.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class SignedPreKeyStore {
|
||||||
|
public:
|
||||||
|
int loadSignedPreKey(signal_buffer **record, uint32_t signed_pre_key_id);
|
||||||
|
int storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len);
|
||||||
|
int containsSignedPreKey(uint32_t signed_pre_key_id);
|
||||||
|
int removeSignedPreKey(uint32_t signed_pre_key_id);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_signed_pre_key_store &store);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal::Store
|
25
qomemo/signal/stores/store_context.cpp
Normal file
25
qomemo/signal/stores/store_context.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "store_context.h"
|
||||||
|
|
||||||
|
Signal::Store::Context::Context(signal_context *global) : identityKeyStore(), preKeyStore(), senderKeyStore(), sessionStore(), signedPreKeyStore() {
|
||||||
|
signal_protocol_store_context_create(&ctx, global);
|
||||||
|
|
||||||
|
identityKeyStore->fillCallbacks(iks);
|
||||||
|
preKeyStore->fillCallbacks(pks);
|
||||||
|
senderKeyStore->fillCallbacks(sks);
|
||||||
|
sessionStore->fillCallbacks(ss);
|
||||||
|
signedPreKeyStore->fillCallbacks(spks);
|
||||||
|
|
||||||
|
signal_protocol_store_context_set_identity_key_store(ctx, &iks);
|
||||||
|
signal_protocol_store_context_set_pre_key_store(ctx, &pks);
|
||||||
|
signal_protocol_store_context_set_sender_key_store(ctx, &sks);
|
||||||
|
signal_protocol_store_context_set_session_store(ctx, &ss);
|
||||||
|
signal_protocol_store_context_set_signed_pre_key_store(ctx, &spks);
|
||||||
|
}
|
||||||
|
|
||||||
|
Signal::Store::Context::~Context() {
|
||||||
|
signal_protocol_store_context_destroy(ctx);
|
||||||
|
}
|
42
qomemo/signal/stores/store_context.h
Normal file
42
qomemo/signal/stores/store_context.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
#include "identity_key_store.h"
|
||||||
|
#include "pre_key_store.h"
|
||||||
|
#include "sender_key_store.h"
|
||||||
|
#include "session_store.h"
|
||||||
|
#include "signed_pre_key_store.h"
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class Context {
|
||||||
|
public:
|
||||||
|
explicit Context(signal_context *global);
|
||||||
|
~Context();
|
||||||
|
|
||||||
|
Context(const Context &) = delete;
|
||||||
|
Context(Context &&) = delete;
|
||||||
|
Context &operator=(const Context &) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
signal_protocol_store_context *ctx{nullptr};
|
||||||
|
|
||||||
|
signal_protocol_identity_key_store iks{};
|
||||||
|
signal_protocol_pre_key_store pks{};
|
||||||
|
signal_protocol_sender_key_store sks{};
|
||||||
|
signal_protocol_session_store ss{};
|
||||||
|
signal_protocol_signed_pre_key_store spks{};
|
||||||
|
|
||||||
|
std::unique_ptr<IdentityKeyStore> identityKeyStore;
|
||||||
|
std::unique_ptr<PreKeyStore> preKeyStore;
|
||||||
|
std::unique_ptr<SenderKeyStore> senderKeyStore;
|
||||||
|
std::unique_ptr<SessionStore> sessionStore;
|
||||||
|
std::unique_ptr<SignedPreKeyStore> signedPreKeyStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // 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);
|
||||||
|
|
||||||
|
}
|
9
qomemo/user_device_list.cpp
Normal file
9
qomemo/user_device_list.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "user_device_list.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
QXmpp::Omemo::UserDeviceList::UserDeviceList(QString jid) : jid(std::move(jid)) {}
|
18
qomemo/user_device_list.h
Normal file
18
qomemo/user_device_list.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class UserDeviceList {
|
||||||
|
public:
|
||||||
|
explicit UserDeviceList(QString jid);
|
||||||
|
|
||||||
|
const QString jid;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
1
qomemo/variant/CMakeLists.txt
Normal file
1
qomemo/variant/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
target_sources(squawk PRIVATE xep0384.cpp xep0384.h conversations.cpp conversations.h omemo_base.cpp omemo_base.h)
|
205
qomemo/variant/conversations.cpp
Normal file
205
qomemo/variant/conversations.cpp
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "conversations.h"
|
||||||
|
|
||||||
|
#include "qomemo/bundle.h"
|
||||||
|
#include "qomemo/device.h"
|
||||||
|
#include "shared/qxmppfactories.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QXmppIq.h>
|
||||||
|
#include <QXmppPubSubIq.h>
|
||||||
|
|
||||||
|
using namespace QXmpp::Omemo;
|
||||||
|
using namespace QXmpp::Factories;
|
||||||
|
|
||||||
|
QXmppElement Variant::Conversations::deviceToXml(const Device &device) {
|
||||||
|
auto result = createElement("device");
|
||||||
|
result.setAttribute("id", QString::number(device.id));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device Variant::Conversations::deviceFromXml(const QXmppElement &xml) {
|
||||||
|
Device result{};
|
||||||
|
|
||||||
|
if (!elementMatches(xml, "device"))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result.id = xml.attribute("id").toInt();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement
|
||||||
|
Variant::Conversations::deviceListToXml(const DeviceList &deviceList) {
|
||||||
|
auto element = createElement("list", "eu.siacs.conversations.axolotl");
|
||||||
|
|
||||||
|
for (const auto &device : deviceList.devices) {
|
||||||
|
element.appendChild(deviceToXml(device));
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
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{};
|
||||||
|
|
||||||
|
auto deviceElement = list.firstChildElement("device");
|
||||||
|
while (!deviceElement.isNull()) {
|
||||||
|
result.devices.push_back(deviceFromXml(deviceElement));
|
||||||
|
deviceElement = deviceElement.nextSiblingElement("device");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) {
|
||||||
|
QXmppIq iq{};
|
||||||
|
|
||||||
|
iq.setType(QXmppIq::Set);
|
||||||
|
|
||||||
|
auto item = createElement("item");
|
||||||
|
item.appendChild(deviceListToXml(deviceList));
|
||||||
|
|
||||||
|
auto publish = createElement("publish");
|
||||||
|
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:%1").arg(deviceId));
|
||||||
|
publish.appendChild(item);
|
||||||
|
|
||||||
|
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
|
||||||
|
pubSub.appendChild(publish);
|
||||||
|
pubSub.appendChild(createOpenPublishOptions());
|
||||||
|
|
||||||
|
iq.setExtensions({ pubSub });
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
32
qomemo/variant/conversations.h
Normal file
32
qomemo/variant/conversations.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "omemo_base.h"
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo::Variant {
|
||||||
|
|
||||||
|
class Conversations : public Base {
|
||||||
|
public:
|
||||||
|
~Conversations() override = default;
|
||||||
|
|
||||||
|
QXmppElement deviceToXml(const Device &device) override;
|
||||||
|
Device deviceFromXml(const QXmppElement &xml) override;
|
||||||
|
|
||||||
|
[[nodiscard]] QString getDeviceListNode() const override;
|
||||||
|
QXmppElement deviceListToXml(const DeviceList &deviceList) override;
|
||||||
|
std::optional<DeviceList> latestDeviceListFromPubSubNode(const QXmppElement &items) 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
|
5
qomemo/variant/omemo_base.cpp
Normal file
5
qomemo/variant/omemo_base.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "omemo_base.h"
|
45
qomemo/variant/omemo_base.h
Normal file
45
qomemo/variant/omemo_base.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
class QXmppElement;
|
||||||
|
class QXmppIq;
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class PreKey;
|
||||||
|
class Bundle;
|
||||||
|
class Device;
|
||||||
|
class DeviceList;
|
||||||
|
|
||||||
|
namespace Variant {
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
public:
|
||||||
|
virtual ~Base() = default;
|
||||||
|
|
||||||
|
virtual QXmppElement deviceToXml(const Device &device) = 0;
|
||||||
|
virtual Device deviceFromXml(const QXmppElement &xml) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual QString getDeviceListNode() const = 0;
|
||||||
|
virtual QXmppElement deviceListToXml(const DeviceList &deviceList) = 0;
|
||||||
|
virtual std::optional<DeviceList> latestDeviceListFromPubSubNode(const QXmppElement &xml) = 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 QXmpp::Omemo
|
5
qomemo/variant/xep0384.cpp
Normal file
5
qomemo/variant/xep0384.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xep0384.h"
|
7
qomemo/variant/xep0384.h
Normal file
7
qomemo/variant/xep0384.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {}
|
@ -16,4 +16,6 @@ target_sources(squawk PRIVATE
|
|||||||
utils.h
|
utils.h
|
||||||
vcard.cpp
|
vcard.cpp
|
||||||
vcard.h
|
vcard.h
|
||||||
|
qxmppfactories.cpp
|
||||||
|
qxmppfactories.h
|
||||||
)
|
)
|
||||||
|
75
shared/qxmppfactories.cpp
Normal file
75
shared/qxmppfactories.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qxmppfactories.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
bool QXmpp::Factories::elementMatches(const QXmppElement &element,
|
||||||
|
const QString &tagName,
|
||||||
|
const QString &xmlns) {
|
||||||
|
if (element.tagName() != tagName) {
|
||||||
|
qWarning() << "tag name: expected = " << tagName
|
||||||
|
<< ", got = " << element.tagName();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!xmlns.isEmpty() && element.attribute("xmlns") != xmlns) {
|
||||||
|
qWarning() << "xmlns: expected = " << xmlns
|
||||||
|
<< ", got = " << element.attribute("xmlns");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Factories::createElement(const QString &tagName,
|
||||||
|
const QString &xmlns) {
|
||||||
|
QXmppElement el{};
|
||||||
|
el.setTagName(tagName);
|
||||||
|
|
||||||
|
if (!xmlns.isEmpty())
|
||||||
|
el.setAttribute("xmlns", xmlns);
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Factories::createValue(const QString &value) {
|
||||||
|
auto el = createElement("value");
|
||||||
|
el.setValue(value);
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Factories::createField(const QString &key,
|
||||||
|
const QString &value, bool hidden) {
|
||||||
|
auto field = createElement("field");
|
||||||
|
field.setAttribute("var", key);
|
||||||
|
if (hidden)
|
||||||
|
field.setAttribute("type", "hidden");
|
||||||
|
field.appendChild(createValue(value));
|
||||||
|
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement
|
||||||
|
QXmpp::Factories::createOpenPublishOptions(const QString &maxItems) {
|
||||||
|
auto formType = createField(
|
||||||
|
"FORM_TYPE", "http://jabber.org/protocol/pubsub#publish-options", true);
|
||||||
|
auto accessModel = createField("pubsub#access_model", "open");
|
||||||
|
|
||||||
|
auto x = createElement("x", "jabber:x:data");
|
||||||
|
x.setAttribute("type", "submit");
|
||||||
|
|
||||||
|
x.appendChild(formType);
|
||||||
|
x.appendChild(accessModel);
|
||||||
|
|
||||||
|
if (!maxItems.isEmpty())
|
||||||
|
x.appendChild(createField("pubsub#max_items", maxItems));
|
||||||
|
|
||||||
|
auto publishOptions = createElement("publish-options");
|
||||||
|
publishOptions.appendChild(x);
|
||||||
|
|
||||||
|
return publishOptions;
|
||||||
|
}
|
25
shared/qxmppfactories.h
Normal file
25
shared/qxmppfactories.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QXmppElement.h>
|
||||||
|
|
||||||
|
namespace QXmpp::Factories {
|
||||||
|
|
||||||
|
bool elementMatches(const QXmppElement &element, const QString &tagName,
|
||||||
|
const QString &xmlns = QStringLiteral(""));
|
||||||
|
|
||||||
|
QXmppElement createElement(const QString &tagName,
|
||||||
|
const QString &xmlns = QStringLiteral(""));
|
||||||
|
|
||||||
|
QXmppElement createValue(const QString &value);
|
||||||
|
|
||||||
|
QXmppElement createField(const QString &key, const QString &value,
|
||||||
|
bool hidden = false);
|
||||||
|
|
||||||
|
QXmppElement
|
||||||
|
createOpenPublishOptions(const QString &maxItems = QStringLiteral(""));
|
||||||
|
|
||||||
|
} // namespace QXmpp::Factories
|
@ -3,3 +3,4 @@ target_sources(squawk PRIVATE squawk.cpp squawk.h squawk.ui)
|
|||||||
add_subdirectory(models)
|
add_subdirectory(models)
|
||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
add_subdirectory(widgets)
|
add_subdirectory(widgets)
|
||||||
|
add_subdirectory(omemo)
|
8
ui/omemo/CMakeLists.txt
Normal file
8
ui/omemo/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
contactsettings.cpp
|
||||||
|
contactsettings.h
|
||||||
|
contactsettings.ui
|
||||||
|
omemodevices.cpp
|
||||||
|
omemodevices.h
|
||||||
|
omemodevices.ui
|
||||||
|
)
|
25
ui/omemo/contactsettings.cpp
Normal file
25
ui/omemo/contactsettings.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "contactsettings.h"
|
||||||
|
#include "ui_contactsettings.h"
|
||||||
|
#include "omemodevices.h"
|
||||||
|
|
||||||
|
ContactSettings::ContactSettings(QString jid, QWidget *parent)
|
||||||
|
: QDialog(parent), jid(std::move(jid)), m_ui(new Ui::ContactSettings()) {
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(m_ui->devicesButton, &QPushButton::clicked, this, &ContactSettings::openDeviceList);
|
||||||
|
|
||||||
|
setWindowTitle(QString("Encryption settings for %1").arg(this->jid));
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactSettings::~ContactSettings() {}
|
||||||
|
|
||||||
|
void ContactSettings::openDeviceList() {
|
||||||
|
auto devices = new OMEMODevices(jid, this);
|
||||||
|
|
||||||
|
devices->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
devices->show();
|
||||||
|
}
|
26
ui/omemo/contactsettings.h
Normal file
26
ui/omemo/contactsettings.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ContactSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ContactSettings : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ContactSettings(QString jid, QWidget *parent = nullptr);
|
||||||
|
~ContactSettings() override;
|
||||||
|
|
||||||
|
const QString jid;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void openDeviceList();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<Ui::ContactSettings> m_ui;
|
||||||
|
};
|
144
ui/omemo/contactsettings.ui
Normal file
144
ui/omemo/contactsettings.ui
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ContactSettings</class>
|
||||||
|
<widget class="QDialog" name="ContactSettings">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Contact Settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Settings for foo@example.com</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="widget" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="useOmemoCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use OMEMO</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="devicesButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Devices...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>ContactSettings</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>ContactSettings</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
15
ui/omemo/omemodevices.cpp
Normal file
15
ui/omemo/omemodevices.cpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "omemodevices.h"
|
||||||
|
#include "ui_omemodevices.h"
|
||||||
|
|
||||||
|
OMEMODevices::OMEMODevices(QString jid, QWidget *parent)
|
||||||
|
: QDialog(parent), jid(std::move(jid)), m_ui(new Ui::OMEMODevices()) {
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
setWindowTitle(QString("%1's OMEMO devices").arg(this->jid));
|
||||||
|
}
|
||||||
|
|
||||||
|
OMEMODevices::~OMEMODevices() {}
|
23
ui/omemo/omemodevices.h
Normal file
23
ui/omemo/omemodevices.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class OMEMODevices;
|
||||||
|
}
|
||||||
|
|
||||||
|
class OMEMODevices : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit OMEMODevices(QString jid, QWidget *parent = nullptr);
|
||||||
|
~OMEMODevices() override;
|
||||||
|
|
||||||
|
const QString jid;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<Ui::OMEMODevices> m_ui;
|
||||||
|
};
|
508
ui/omemo/omemodevices.ui
Normal file
508
ui/omemo/omemodevices.ui
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>OMEMODevices</class>
|
||||||
|
<widget class="QDialog" name="OMEMODevices">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>560</width>
|
||||||
|
<height>357</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>OMEMO Devices</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>List of OMEMO fingerprints for fooooo@bar.com</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QScrollArea" name="scrollArea">
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>540</width>
|
||||||
|
<height>281</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_3">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">background-color: rgb(6, 88, 6)</string>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::Panel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Monospace</family>
|
||||||
|
<pointsize>8</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd</string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Squawk</string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_6">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="view-barcode-qr">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_9">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="approved">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_10">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="edit-delete-remove">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::Panel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Monospace</family>
|
||||||
|
<pointsize>8</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd</string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Dino</string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_5">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="view-barcode-qr">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_7">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="approved">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_8">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="edit-delete-remove">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">background: rgb(111, 47, 47)</string>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::Panel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<property name="lineWidth">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>240</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Monospace</family>
|
||||||
|
<pointsize>8</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd</string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Conversations</string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_4">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="view-barcode-qr">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="approved">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="edit-delete-remove">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close</set>
|
||||||
|
</property>
|
||||||
|
<property name="centerButtons">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>OMEMODevices</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>OMEMODevices</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -20,6 +20,8 @@
|
|||||||
#include "ui_squawk.h"
|
#include "ui_squawk.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <utility>
|
||||||
|
#include <ui/omemo/omemodevices.h>
|
||||||
|
|
||||||
Squawk::Squawk(QWidget *parent) :
|
Squawk::Squawk(QWidget *parent) :
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
@ -134,6 +136,13 @@ void Squawk::onNewContact()
|
|||||||
nc->exec();
|
nc->exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Squawk::openDeviceList(QString bareJid) {
|
||||||
|
auto od = new OMEMODevices(std::move(bareJid), this);
|
||||||
|
|
||||||
|
od->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
od->show();
|
||||||
|
}
|
||||||
|
|
||||||
void Squawk::onNewConference()
|
void Squawk::onNewConference()
|
||||||
{
|
{
|
||||||
JoinConference* jc = new JoinConference(rosterModel.accountsModel, this);
|
JoinConference* jc = new JoinConference(rosterModel.accountsModel, this);
|
||||||
@ -531,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,6 +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 openDeviceList(QString bareJid);
|
||||||
|
|
||||||
void onUnnoticedMessage(const QString& account, const Shared::Message& msg);
|
void onUnnoticedMessage(const QString& account, const Shared::Message& msg);
|
||||||
|
|
||||||
|
11
ui/squawk.ui
11
ui/squawk.ui
@ -162,7 +162,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>718</width>
|
<width>718</width>
|
||||||
<height>27</height>
|
<height>23</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuSettings">
|
<widget class="QMenu" name="menuSettings">
|
||||||
@ -224,6 +224,15 @@
|
|||||||
<string>Add conference</string>
|
<string>Add conference</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionDeviceList">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="computer-laptop">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Device list</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <QAbstractTextDocumentLayout>
|
#include <QAbstractTextDocumentLayout>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <ui/omemo/contactsettings.h>
|
||||||
|
|
||||||
Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent):
|
Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent):
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
@ -80,6 +81,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el,
|
|||||||
connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton);
|
connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton);
|
||||||
connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged,
|
connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged,
|
||||||
this, &Conversation::onTextEditDocSizeChanged);
|
this, &Conversation::onTextEditDocSizeChanged);
|
||||||
|
connect(m_ui->encryptionButton, &QToolButton::clicked, this, &Conversation::openEncryptionSettings);
|
||||||
|
|
||||||
m_ui->messageEditor->installEventFilter(&ker);
|
m_ui->messageEditor->installEventFilter(&ker);
|
||||||
|
|
||||||
@ -433,3 +435,10 @@ void Conversation::onFeedContext(const QPoint& pos)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Conversation::openEncryptionSettings() {
|
||||||
|
auto cs = new ContactSettings(palJid, this);
|
||||||
|
|
||||||
|
cs->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
cs->show();
|
||||||
|
}
|
||||||
|
@ -107,6 +107,7 @@ protected slots:
|
|||||||
void onFeedMessage(const Shared::Message& msg);
|
void onFeedMessage(const Shared::Message& msg);
|
||||||
void positionShadow();
|
void positionShadow();
|
||||||
void onFeedContext(const QPoint &pos);
|
void onFeedContext(const QPoint &pos);
|
||||||
|
void openEncryptionSettings();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const bool isMuc;
|
const bool isMuc;
|
||||||
|
@ -292,6 +292,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="encryptionButton">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="security-low">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="autoRaise">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="attachButton">
|
<widget class="QPushButton" name="attachButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -400,8 +414,8 @@ background-color: transparent
|
|||||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
p, li { white-space: pre-wrap; }
|
p, li { white-space: pre-wrap; }
|
||||||
</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;">
|
</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;">
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Liberation Sans';"><br /></p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="acceptRichText">
|
<property name="acceptRichText">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
|
Loading…
Reference in New Issue
Block a user