Archived
1
Fork 0

Reformat code

This commit is contained in:
Joshua Goins 2022-08-15 21:53:56 -04:00
parent 6df6ea1523
commit d83471009f
26 changed files with 810 additions and 698 deletions

33
.clang-format Normal file
View file

@ -0,0 +1,33 @@
---
AllowShortIfStatementsOnASingleLine: Never
CompactNamespaces: 'false'
DisableFormat: 'false'
IndentCaseLabels: 'true'
IndentPPDirectives: BeforeHash
IndentWidth: '4'
Language: Cpp
NamespaceIndentation: All
PointerAlignment: Left
ReflowComments: 'true'
SortIncludes: 'true'
SortUsingDeclarations: 'true'
SpacesInCStyleCastParentheses: 'false'
Standard: Cpp11
TabWidth: '0'
UseTab: Never
AllowShortEnumsOnASingleLine: false
BraceWrapping:
AfterEnum: true
AccessModifierOffset: -4
SpaceAfterTemplateKeyword: 'false'
AllowAllParametersOfDeclarationOnNextLine: false
AlignAfterOpenBracket: AlwaysBreak
BinPackArguments: false
BinPackParameters: false
ColumnLimit: 120
AllowShortBlocksOnASingleLine: 'false'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: 'Empty'
AllowShortLambdasOnASingleLine: 'Empty'
AllowShortLoopsOnASingleLine: 'false'
SeparateDefinitionBlocks : 'Always'

8
.editorconfig Normal file
View file

@ -0,0 +1,8 @@
root = true
[*]
insert_final_newline = true
indent_style = space
indent_size = 4
tab_width = 4
max_line_length = 120

View file

@ -1,6 +1,8 @@
# Trinity # Trinity
A Qt5 QML Matrix client, which supports end-to-end encryption (WIP) and has a Discord-like interface. A Qt5 QML Matrix client, which supports end-to-end encryption (WIP) and has a Discord-like interface.
![Screenshot](misc/screenshot.png)
## Features ## Features
* Basic messaging capabilities * Basic messaging capabilities
* Sending and recieving markdown messages, formatting can be disabled * Sending and recieving markdown messages, formatting can be disabled
@ -13,28 +15,18 @@ A Qt5 QML Matrix client, which supports end-to-end encryption (WIP) and has a Di
* Start direct chats with other members * Start direct chats with other members
* Custom emote support * Custom emote support
## Screenshots ## Installation
![Screenshot](misc/screenshot.png)
*Note: This is an outdated screenshot :-)* I'm not providing any pre-built binaries yet, so you must compile from source.
## Dependencies If you use Arch Linux, I maintain the [AUR package](https://aur.archlinux.org/packages/trinity-matrix-git).
## Building
Building Trinity is very easy, simply call CMake. Please make sure all the dependencies below are met.
### Dependencies
* Qt5 * Qt5
* WebEngine is also required * WebEngine is also required
* CMark * CMark
* libolm * libolm
## Installation
There's no pre-compiled binaries yet, but compiling from source is easily provided you have Qt5 installed. If you use
Arch Linux, there's a [PKGBUILD available from the AUR](https://aur.archlinux.org/packages/trinity-matrix-git). Simply call it from `makepkg` or through your favorite AUR helper:
```
$ aur sync trinity-matrix-git
```
Since there's not a tagged release yet, there's only the git version available as a PKGBUILD at the moment.
## Licensing
Trinity's source code is distributed under the GPLv3 license. See the `LICENSE` file for more details.
`resources/background.jpg` shown on the login page is from Death to Stock.

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <QObject> #include <QObject>
#include <vector>
#include <QQmlContext> #include <QQmlContext>
#include <vector>
class MatrixCore; class MatrixCore;

View file

@ -1,10 +1,9 @@
#pragma once #pragma once
#include <QSystemTrayIcon>
#include <QApplication> #include <QApplication>
#include <QSystemTrayIcon>
class Desktop : public QObject class Desktop : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
Q_INVOKABLE void showTrayIcon(const bool shouldHide) { Q_INVOKABLE void showTrayIcon(const bool shouldHide) {
@ -21,7 +20,7 @@ public:
} }
Q_INVOKABLE bool isTrayIconEnabled() { Q_INVOKABLE bool isTrayIconEnabled() {
return icon->isVisible();; return icon->isVisible();
} }
QSystemTrayIcon* icon; QSystemTrayIcon* icon;

View file

@ -1,9 +1,7 @@
#ifndef DIRECTORYMODEL_H #ifndef DIRECTORYMODEL_H
#define DIRECTORYMODEL_H #define DIRECTORYMODEL_H
class DirectoryModel {
class DirectoryModel
{
public: public:
DirectoryModel(); DirectoryModel();
}; };

View file

@ -5,8 +5,7 @@
#include "emote.h" #include "emote.h"
class EmoteListModel : public QAbstractListModel class EmoteListModel : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
int rowCount(const QModelIndex& parent) const override; int rowCount(const QModelIndex& parent) const override;

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <QString>
#include <QJsonObject> #include <QJsonObject>
#include <QString>
#include <olm/olm.h> #include <olm/olm.h>

View file

@ -6,8 +6,7 @@ class MatrixCore;
struct Room; struct Room;
class Event; class Event;
class EventModel : public QAbstractListModel class EventModel : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
enum EventRoles { enum EventRoles {

View file

@ -1,23 +1,22 @@
#pragma once #pragma once
#include <QObject> #include <QJsonObject>
#include <QList> #include <QList>
#include <QMap> #include <QMap>
#include <QJsonObject> #include <QObject>
#include "eventmodel.h"
#include "room.h"
#include "roomlistmodel.h"
#include "membermodel.h"
#include "roomlistsortmodel.h"
#include "emote.h" #include "emote.h"
#include "emotelistmodel.h" #include "emotelistmodel.h"
#include "encryption.h" #include "encryption.h"
#include "eventmodel.h"
#include "membermodel.h"
#include "room.h"
#include "roomlistmodel.h"
#include "roomlistsortmodel.h"
class Network; class Network;
class MatrixCore : public QObject class MatrixCore : public QObject {
{
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString profileName MEMBER profileName CONSTANT) Q_PROPERTY(QString profileName MEMBER profileName CONSTANT)
Q_PROPERTY(bool initialSyncComplete READ isInitialSyncComplete NOTIFY initialSyncFinished) Q_PROPERTY(bool initialSyncComplete READ isInitialSyncComplete NOTIFY initialSyncFinished)
@ -39,7 +38,11 @@ public:
Encryption* encryption = nullptr; Encryption* encryption = nullptr;
// account // account
Q_INVOKABLE void registerAccount(const QString& username, const QString& password, const QString& session = "", const QString& type = ""); Q_INVOKABLE void registerAccount(
const QString& username,
const QString& password,
const QString& session = "",
const QString& type = "");
Q_INVOKABLE void login(const QString& username, const QString& password); Q_INVOKABLE void login(const QString& username, const QString& password);
Q_INVOKABLE void logout(); Q_INVOKABLE void logout();
@ -94,7 +97,14 @@ public:
void setMarkdownEnabled(const bool enabled); void setMarkdownEnabled(const bool enabled);
void sendKeyToDevice(QString roomId, QString senderCurveIdentity, QString senderEdIdentity, QString session_id, QString session_key, QString user_id, QString device_id); void sendKeyToDevice(
QString roomId,
QString senderCurveIdentity,
QString senderEdIdentity,
QString session_id,
QString session_key,
QString user_id,
QString device_id);
OlmOutboundGroupSession* currentSession = nullptr; OlmOutboundGroupSession* currentSession = nullptr;
QString currentSessionId, currentSessionKey; QString currentSessionId, currentSessionKey;

View file

@ -4,8 +4,7 @@
class Room; class Room;
class MemberModel : public QAbstractListModel class MemberModel : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
enum EventRoles { enum EventRoles {

View file

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QJsonObject>
#include <QJsonDocument>
#include <QUrl> #include <QUrl>
#include "requestsender.h" #include "requestsender.h"
@ -17,8 +17,7 @@ public:
QNetworkAccessManager* manager; QNetworkAccessManager* manager;
QString homeserverURL, accessToken; QString homeserverURL, accessToken;
template<typename Fn> template<typename Fn> inline void postJSON(const QString& path, const QJsonObject object, Fn&& fn) {
inline void postJSON(const QString& path, const QJsonObject object, Fn&& fn) {
QNetworkRequest request(homeserverURL + path); QNetworkRequest request(homeserverURL + path);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", accessToken.toLocal8Bit()); request.setRawHeader("Authorization", accessToken.toLocal8Bit());
@ -38,7 +37,8 @@ public:
} }
template<typename Fn, typename ProgressFn> template<typename Fn, typename ProgressFn>
inline void postBinary(const QString& path, const QByteArray data, const QString mimeType, Fn&& fn, ProgressFn&& progressFn) { inline void
postBinary(const QString& path, const QByteArray data, const QString mimeType, Fn&& fn, ProgressFn&& progressFn) {
QNetworkRequest request(homeserverURL + path); QNetworkRequest request(homeserverURL + path);
request.setHeader(QNetworkRequest::ContentTypeHeader, mimeType); request.setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
request.setRawHeader("Authorization", accessToken.toLocal8Bit()); request.setRawHeader("Authorization", accessToken.toLocal8Bit());
@ -56,8 +56,7 @@ public:
QObject::connect(reply, &QNetworkReply::uploadProgress, progressFn); QObject::connect(reply, &QNetworkReply::uploadProgress, progressFn);
} }
template<typename Fn> template<typename Fn> inline void post(const QString& path, Fn&& fn) {
inline void post(const QString& path, Fn&& fn) {
QNetworkRequest request(homeserverURL + path); QNetworkRequest request(homeserverURL + path);
request.setRawHeader("Authorization", accessToken.toLocal8Bit()); request.setRawHeader("Authorization", accessToken.toLocal8Bit());
@ -90,8 +89,7 @@ public:
manager->put(request, jsonPost); manager->put(request, jsonPost);
} }
template<typename Fn> template<typename Fn> inline void putJSON(const QString& path, const QJsonObject object, Fn&& fn) {
inline void putJSON(const QString& path, const QJsonObject object, Fn&& fn) {
QNetworkRequest request(homeserverURL + path); QNetworkRequest request(homeserverURL + path);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", accessToken.toLocal8Bit()); request.setRawHeader("Authorization", accessToken.toLocal8Bit());

View file

@ -1,12 +1,11 @@
#pragma once #pragma once
#include <functional>
#include <QObject>
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QObject>
#include <functional>
class RequestSender : public QObject class RequestSender : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
RequestSender(QObject* parent = nullptr) : QObject(parent) {} RequestSender(QObject* parent = nullptr) : QObject(parent) {}

View file

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <QObject>
#include <QString>
#include <QDateTime> #include <QDateTime>
#include <QObject>
#include <QSettings> #include <QSettings>
#include <QString>
class EncryptionInformation : public QObject { class EncryptionInformation : public QObject {
Q_OBJECT Q_OBJECT
@ -12,8 +12,7 @@ public:
QString sessionId; QString sessionId;
}; };
class Event : public QObject class Event : public QObject {
{
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString sender READ getSender NOTIFY senderChanged) Q_PROPERTY(QString sender READ getSender NOTIFY senderChanged)
Q_PROPERTY(QString msg READ getMsg NOTIFY msgChanged) Q_PROPERTY(QString msg READ getMsg NOTIFY msgChanged)
@ -175,8 +174,7 @@ private:
QString id, displayName, avatarURL; QString id, displayName, avatarURL;
}; };
class Room : public QObject class Room : public QObject {
{
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString id MEMBER id NOTIFY idChanged) Q_PROPERTY(QString id MEMBER id NOTIFY idChanged)
Q_PROPERTY(QString topic MEMBER topic NOTIFY topicChanged) Q_PROPERTY(QString topic MEMBER topic NOTIFY topicChanged)
@ -188,7 +186,8 @@ class Room : public QObject
Q_PROPERTY(QString highlightCount READ getHighlightCount NOTIFY highlightCountChanged) Q_PROPERTY(QString highlightCount READ getHighlightCount NOTIFY highlightCountChanged)
Q_PROPERTY(QString notificationCount READ getNotificationCount NOTIFY notificationCountChanged) Q_PROPERTY(QString notificationCount READ getNotificationCount NOTIFY notificationCountChanged)
Q_PROPERTY(bool direct READ getDirect NOTIFY directChanged) Q_PROPERTY(bool direct READ getDirect NOTIFY directChanged)
Q_PROPERTY(int notificationLevel READ getNotificationLevel WRITE setNotificationLevel NOTIFY notificationLevelChanged) Q_PROPERTY(
int notificationLevel READ getNotificationLevel WRITE setNotificationLevel NOTIFY notificationLevelChanged)
Q_PROPERTY(bool encrypted READ getEncrypted NOTIFY encryptionChanged) Q_PROPERTY(bool encrypted READ getEncrypted NOTIFY encryptionChanged)
public: public:
Room(QObject* parent = nullptr) : QObject(parent) {} Room(QObject* parent = nullptr) : QObject(parent) {}

View file

@ -4,8 +4,7 @@
class Room; class Room;
class RoomListModel : public QAbstractListModel class RoomListModel : public QAbstractListModel {
{
Q_OBJECT Q_OBJECT
public: public:
enum EventRoles { enum EventRoles {

View file

@ -2,8 +2,7 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
class RoomListSortModel : public QSortFilterProxyModel class RoomListSortModel : public QSortFilterProxyModel {
{
Q_OBJECT Q_OBJECT
public: public:
bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const; bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const;
@ -19,8 +18,7 @@ public:
} }
}; };
class MemberListSortModel : public QSortFilterProxyModel class MemberListSortModel : public QSortFilterProxyModel {
{
Q_OBJECT Q_OBJECT
public: public:
bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const; bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const;
@ -35,4 +33,3 @@ public:
return sourceIndex.row(); return sourceIndex.row();
} }
}; };

View file

@ -1,6 +1,3 @@
#include "directorymodel.h" #include "directorymodel.h"
DirectoryModel::DirectoryModel() DirectoryModel::DirectoryModel() {}
{
}

View file

@ -1,10 +1,10 @@
#include "encryption.h" #include "encryption.h"
#include <stdlib.h>
#include <QDebug> #include <QDebug>
#include <unistd.h>
#include <fcntl.h>
#include <QJsonDocument> #include <QJsonDocument>
#include <cstring> #include <cstring>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
void Encryption::createNewDeviceKeys() { void Encryption::createNewDeviceKeys() {
initAccount(); initAccount();
@ -35,7 +35,6 @@ void Encryption::createNewDeviceKeys() {
qDebug() << "identity keys: " << (char*)identity_memory; qDebug() << "identity keys: " << (char*)identity_memory;
qDebug() << "identity keys (parsed): " << identityKey; qDebug() << "identity keys (parsed): " << identityKey;
} }
QString Encryption::saveDeviceKeys() { QString Encryption::saveDeviceKeys() {
@ -108,11 +107,7 @@ QString Encryption::signMessage(QString message) {
void* memory = malloc(length); void* memory = malloc(length);
olm_account_sign( olm_account_sign(account, message.data(), message.length(), memory, length);
account,
message.data(), message.length(),
memory, length
);
qDebug() << "Signed: " << (char*)memory; qDebug() << "Signed: " << (char*)memory;
@ -169,7 +164,15 @@ OlmSession* Encryption::beginOutboundOlmSession(std::string identityKey, std::st
int fd = open("/dev/random", O_RDONLY); int fd = open("/dev/random", O_RDONLY);
read(fd, memory, length); read(fd, memory, length);
olm_create_outbound_session(outboundSession, account, identityKey.data(), identityKey.length(), oneTimeKey.data(), oneTimeKey.length(), (uint8_t*)memory, length); olm_create_outbound_session(
outboundSession,
account,
identityKey.data(),
identityKey.length(),
oneTimeKey.data(),
oneTimeKey.length(),
(uint8_t*)memory,
length);
// qDebug() << "ERR: " << olm_session_last_error(outboundSession); // qDebug() << "ERR: " << olm_session_last_error(outboundSession);
return outboundSession; return outboundSession;
@ -181,7 +184,6 @@ OlmSession* Encryption::beginOutboundOlmSession(std::string identityKey, std::st
void const * their_one_time_key, size_t their_one_time_key_length, void const * their_one_time_key, size_t their_one_time_key_length,
void * random, size_t random_length void * random, size_t random_length
);*/ );*/
} }
std::string Encryption::getSessionId(OlmSession* session) { std::string Encryption::getSessionId(OlmSession* session) {
@ -192,10 +194,7 @@ std::string Encryption::getSessionId(OlmSession* session) {
/** An identifier for this session. Will be the same for both ends of the /** An identifier for this session. Will be the same for both ends of the
* conversation. If the id buffer is too small then olm_session_last_error() * conversation. If the id buffer is too small then olm_session_last_error()
* will be "OUTPUT_BUFFER_TOO_SMALL". */ * will be "OUTPUT_BUFFER_TOO_SMALL". */
olm_session_id( olm_session_id(session, memory, length);
session,
memory, length
);
return memory; return memory;
} }
@ -229,7 +228,8 @@ OlmSession* Encryption::createInboundSession(std::string senderKey, std::string
void* inboundMemory = malloc(olm_session_size()); void* inboundMemory = malloc(olm_session_size());
auto inboundSession = olm_session(inboundMemory); auto inboundSession = olm_session(inboundMemory);
olm_create_inbound_session_from(inboundSession, account, (void*)senderKey.c_str(), senderKey.length(), (void*)body.c_str(), body.length()); olm_create_inbound_session_from(
inboundSession, account, (void*)senderKey.c_str(), senderKey.length(), (void*)body.c_str(), body.length());
return inboundSession; return inboundSession;
} }
@ -237,15 +237,15 @@ OlmSession* Encryption::createInboundSession(std::string senderKey, std::string
std::vector<std::uint8_t> Encryption::decrypt(OlmSession* session, int msgType, std::string cipherText) { std::vector<std::uint8_t> Encryption::decrypt(OlmSession* session, int msgType, std::string cipherText) {
/*std::string maxPlaintextLengthBuffer = cipherText; /*std::string maxPlaintextLengthBuffer = cipherText;
size_t maxPlaintextLength = olm_decrypt_max_plaintext_length(session, msgType, (void*)maxPlaintextLengthBuffer.data(), maxPlaintextLengthBuffer.length());; size_t maxPlaintextLength = olm_decrypt_max_plaintext_length(session, msgType,
qDebug() << "THE ERROR YOU ARE LOOKING FOR " << olm_session_last_error(session); (void*)maxPlaintextLengthBuffer.data(), maxPlaintextLengthBuffer.length());; qDebug() << "THE ERROR YOU ARE LOOKING
if(maxPlaintextLength == olm_error()) FOR " << olm_session_last_error(session); if(maxPlaintextLength == olm_error()) return "";
return "";
char* plaintext = new char[maxPlaintextLength]; char* plaintext = new char[maxPlaintextLength];
//plaintext[maxPlaintextLength] = '\0'; //plaintext[maxPlaintextLength] = '\0';
memset(plaintext, '\0', maxPlaintextLength); memset(plaintext, '\0', maxPlaintextLength);
int size = olm_decrypt(session, msgType, (void*)cipherText.data(), cipherText.length(), (void*)plaintext, maxPlaintextLength); int size = olm_decrypt(session, msgType, (void*)cipherText.data(), cipherText.length(), (void*)plaintext,
maxPlaintextLength);
//plaintext[size] = '\0'; //plaintext[size] = '\0';
plaintext[size + 1] = '\0'; plaintext[size + 1] = '\0';
@ -257,7 +257,8 @@ std::vector<std::uint8_t> Encryption::decrypt(OlmSession* session, int msgType,
std::copy(cipherText.begin(), cipherText.end(), tmp_plaintext_buffer.begin()); std::copy(cipherText.begin(), cipherText.end(), tmp_plaintext_buffer.begin());
// create the result buffer // create the result buffer
size_t length = olm_decrypt_max_plaintext_length(session, msgType, tmp_plaintext_buffer.data(), tmp_plaintext_buffer.size()); size_t length =
olm_decrypt_max_plaintext_length(session, msgType, tmp_plaintext_buffer.data(), tmp_plaintext_buffer.size());
if (length == olm_error()) if (length == olm_error())
return {}; return {};
@ -267,7 +268,8 @@ std::vector<std::uint8_t> Encryption::decrypt(OlmSession* session, int msgType,
auto tmp_buffer = std::vector<std::uint8_t>(cipherText.size()); auto tmp_buffer = std::vector<std::uint8_t>(cipherText.size());
std::copy(cipherText.begin(), cipherText.end(), tmp_buffer.begin()); std::copy(cipherText.begin(), cipherText.end(), tmp_buffer.begin());
auto size = olm_decrypt(session, msgType, tmp_buffer.data(), tmp_buffer.size(), result_buffer.data(), result_buffer.size()); auto size =
olm_decrypt(session, msgType, tmp_buffer.data(), tmp_buffer.size(), result_buffer.data(), result_buffer.size());
if (size == olm_error()) if (size == olm_error())
return {}; return {};
@ -315,7 +317,8 @@ std::vector<std::uint8_t> Encryption::decrypt(OlmInboundGroupSession* session, s
std::copy(cipherText.begin(), cipherText.end(), tmp_plaintext_buffer.begin()); std::copy(cipherText.begin(), cipherText.end(), tmp_plaintext_buffer.begin());
// create the result buffer // create the result buffer
size_t length = olm_group_decrypt_max_plaintext_length(session, tmp_plaintext_buffer.data(), tmp_plaintext_buffer.size()); size_t length =
olm_group_decrypt_max_plaintext_length(session, tmp_plaintext_buffer.data(), tmp_plaintext_buffer.size());
if (length == olm_error()) if (length == olm_error())
return {}; return {};
@ -326,7 +329,8 @@ std::vector<std::uint8_t> Encryption::decrypt(OlmInboundGroupSession* session, s
std::copy(cipherText.begin(), cipherText.end(), tmp_buffer.begin()); std::copy(cipherText.begin(), cipherText.end(), tmp_buffer.begin());
uint32_t msgIndex; uint32_t msgIndex;
auto size = olm_group_decrypt(session, tmp_buffer.data(), tmp_buffer.size(), result_buffer.data(), result_buffer.size(), &msgIndex); auto size = olm_group_decrypt(
session, tmp_buffer.data(), tmp_buffer.size(), result_buffer.data(), result_buffer.size(), &msgIndex);
if (size == olm_error()) if (size == olm_error())
return {}; return {};

View file

@ -2,8 +2,8 @@
#include <QDebug> #include <QDebug>
#include "room.h"
#include "matrixcore.h" #include "matrixcore.h"
#include "room.h"
EventModel::EventModel(MatrixCore& matrix) : matrix(matrix) {} EventModel::EventModel(MatrixCore& matrix) : matrix(matrix) {}

View file

@ -1,24 +1,24 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include <QJsonArray>
#include <QAction> #include <QAction>
#include <QMenu>
#include <QSystemTrayIcon>
#include <QMessageBox>
#include <QApplication> #include <QApplication>
#include <QGuiApplication>
#include <QJsonArray>
#include <QMenu>
#include <QMessageBox>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQmlContext>
#include <QSystemTrayIcon>
#include <QtQuick/QQuickWindow> #include <QtQuick/QQuickWindow>
#include <QtWebEngine/QtWebEngine> #include <QtWebEngine/QtWebEngine>
#include "eventmodel.h"
#include "membermodel.h"
#include "matrixcore.h"
#include "network.h"
#include "desktop.h"
#include "roomlistsortmodel.h"
#include "emote.h"
#include "appcore.h" #include "appcore.h"
#include "desktop.h"
#include "emote.h"
#include "eventmodel.h"
#include "matrixcore.h"
#include "membermodel.h"
#include "network.h"
#include "roomlistsortmodel.h"
void AppCore::addAccount(QString profileName) { void AppCore::addAccount(QString profileName) {
accounts.push_back(new MatrixCore(profileName)); accounts.push_back(new MatrixCore(profileName));

View file

@ -2,19 +2,21 @@
#include <cmark.h> #include <cmark.h>
#include "network.h"
#include <QDir>
#include <QJsonArray> #include <QJsonArray>
#include <QMimeData>
#include <QMimeDatabase>
#include <QPixmap>
#include <QRandomGenerator> #include <QRandomGenerator>
#include <QSettings> #include <QSettings>
#include <QPixmap>
#include <QStandardPaths> #include <QStandardPaths>
#include <QDir>
#include <QMimeDatabase>
#include <QMimeData>
#include <fstream> #include <fstream>
#include <iterator> #include <iterator>
#include "network.h"
MatrixCore::MatrixCore(QString profileName, QObject* parent) : QObject(parent), profileName(profileName), roomListModel(rooms), directoryListModel(publicRooms), eventModel(*this) { MatrixCore::MatrixCore(QString profileName, QObject* parent)
: QObject(parent), profileName(profileName), roomListModel(rooms), directoryListModel(publicRooms),
eventModel(*this) {
network = new Network(); network = new Network();
encryption = new Encryption(); encryption = new Encryption();
@ -80,39 +82,41 @@ MatrixCore::MatrixCore(QString profileName, QObject* parent) : QObject(parent),
localEmoteModel.setList(&emotes); localEmoteModel.setList(&emotes);
} }
void MatrixCore::registerAccount(const QString &username, const QString &password, const QString& session, const QString& type) { void MatrixCore::registerAccount(
const QString& username,
const QString& password,
const QString& session,
const QString& type) {
QJsonObject authObject; QJsonObject authObject;
if (!session.isEmpty()) { if (!session.isEmpty()) {
authObject["type"] = type; authObject["type"] = type;
authObject["session"] = session; authObject["session"] = session;
} }
const QJsonObject registerObject { const QJsonObject registerObject{{"auth", authObject}, {"username", username}, {"password", password}};
{"auth", authObject},
{"username", username},
{"password", password}
};
network->postJSON("/_matrix/client/r0/register?kind=user", registerObject, [this](QNetworkReply* reply) { network->postJSON("/_matrix/client/r0/register?kind=user", registerObject, [this](QNetworkReply* reply) {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
if (reply->error()) { if (reply->error()) {
if (document.object().contains("flows")) { if (document.object().contains("flows")) {
const QString stage = document.object()["flows"].toArray()[0].toObject()["stages"].toArray()[0].toString(); const QString stage =
document.object()["flows"].toArray()[0].toObject()["stages"].toArray()[0].toString();
if (stage == "m.login.recaptcha") { if (stage == "m.login.recaptcha") {
const QJsonObject data{ const QJsonObject data{
{"public_key", document.object()["params"].toObject()["m.login.recaptcha"].toObject()["public_key"].toString()}, {"public_key",
document.object()["params"]
.toObject()["m.login.recaptcha"]
.toObject()["public_key"]
.toString()},
{"session", document.object()["session"].toString()}, {"session", document.object()["session"].toString()},
{"type", "m.login.recaptcha"} {"type", "m.login.recaptcha"}};
};
emit registerFlow(data); emit registerFlow(data);
} else if (stage == "m.login.dummy") { } else if (stage == "m.login.dummy") {
const QJsonObject data{ const QJsonObject data{
{"session", document.object()["session"].toString()}, {"session", document.object()["session"].toString()}, {"type", "m.login.dummy"}};
{"type", "m.login.dummy"}
};
emit registerFlow(data); emit registerFlow(data);
} else { } else {
@ -141,8 +145,7 @@ void MatrixCore::login(const QString& username, const QString& password) {
{"type", "m.login.password"}, {"type", "m.login.password"},
{"user", username}, {"user", username},
{"password", password}, {"password", password},
{"initial_device_display_name", "Trinity"} {"initial_device_display_name", "Trinity"}};
};
network->postJSON("/_matrix/client/r0/login", loginObject, [this](QNetworkReply* reply) { network->postJSON("/_matrix/client/r0/login", loginObject, [this](QNetworkReply* reply) {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
@ -163,44 +166,37 @@ void MatrixCore::login(const QString& username, const QString& password) {
QJsonObject keysObject{ QJsonObject keysObject{
{"curve25519:" + document.object()["device_id"].toString(), encryption->identityKey["curve25519"]}, {"curve25519:" + document.object()["device_id"].toString(), encryption->identityKey["curve25519"]},
{"ed25519:" + document.object()["device_id"].toString(), encryption->identityKey["ed25519"]} {"ed25519:" + document.object()["device_id"].toString(), encryption->identityKey["ed25519"]}};
};
QJsonObject deviceKeysObject{ QJsonObject deviceKeysObject{
{"user_id", document.object()["user_id"].toString()}, {"user_id", document.object()["user_id"].toString()},
{"device_id", document.object()["device_id"].toString()}, {"device_id", document.object()["device_id"].toString()},
{"algorithms", QJsonArray({"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"})}, {"algorithms", QJsonArray({"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"})},
{"keys", keysObject} {"keys", keysObject}};
};
QJsonObject signature{ QJsonObject signature{
{"ed25519:" + document.object()["device_id"].toString(), encryption->signMessage(QJsonDocument(deviceKeysObject).toJson(QJsonDocument::Compact)) }}; {"ed25519:" + document.object()["device_id"].toString(),
encryption->signMessage(QJsonDocument(deviceKeysObject).toJson(QJsonDocument::Compact))}};
deviceKeysObject["signatures"] = QJsonObject { deviceKeysObject["signatures"] = QJsonObject{{document.object()["user_id"].toString(), signature}};
{document.object()["user_id"].toString(), signature}};
QJsonObject oneTimeKeyObject; QJsonObject oneTimeKeyObject;
auto one_time_keys = encryption->generateOneTimeKeys(encryption->getRecommendedNumberOfOneTimeKeys()); auto one_time_keys = encryption->generateOneTimeKeys(encryption->getRecommendedNumberOfOneTimeKeys());
for (auto key : one_time_keys["curve25519"].toObject().keys()) { for (auto key : one_time_keys["curve25519"].toObject().keys()) {
QJsonObject keyObject { QJsonObject keyObject{{"key", one_time_keys["curve25519"].toObject()[key]}};
{"key", one_time_keys["curve25519"].toObject()[key]}
};
QJsonObject signature{ QJsonObject signature{
{"ed25519:" + document.object()["device_id"].toString(), encryption->signMessage(QJsonDocument(keyObject).toJson(QJsonDocument::Compact)) }}; {"ed25519:" + document.object()["device_id"].toString(),
encryption->signMessage(QJsonDocument(keyObject).toJson(QJsonDocument::Compact))}};
keyObject["signatures"] = QJsonObject { keyObject["signatures"] = QJsonObject{{document.object()["user_id"].toString(), signature}};
{document.object()["user_id"].toString(), signature}};
oneTimeKeyObject["signed_curve25519:" + key] = keyObject; oneTimeKeyObject["signed_curve25519:" + key] = keyObject;
} }
QJsonObject masterKeysObject { QJsonObject masterKeysObject{{"device_keys", deviceKeysObject}, {"one_time_keys", oneTimeKeyObject}};
{"device_keys", deviceKeysObject},
{"one_time_keys", oneTimeKeyObject}
};
// qDebug() << masterKeysObject; // qDebug() << masterKeysObject;
@ -241,11 +237,10 @@ void MatrixCore::updateAccountInformation() {
void MatrixCore::setDisplayName(const QString& name) { void MatrixCore::setDisplayName(const QString& name) {
displayName = name; displayName = name;
const QJsonObject displayNameObject { const QJsonObject displayNameObject{{"displayname", name}};
{"displayname", name}
};
network->putJSON("/_matrix/client/r0/profile/" + userId + "/displayname", displayNameObject, [this, name](QNetworkReply* reply) { network->putJSON(
"/_matrix/client/r0/profile/" + userId + "/displayname", displayNameObject, [this, name](QNetworkReply* reply) {
emit displayNameChanged(); emit displayNameChanged();
}); });
} }
@ -268,30 +263,49 @@ void MatrixCore::sync() {
// qDebug() << document.object()["device_one_time_keys_count"]; // qDebug() << document.object()["device_one_time_keys_count"];
for (auto event : document.object()["to_device"].toObject()["events"].toArray()) { for (auto event : document.object()["to_device"].toObject()["events"].toArray()) {
if(event.toObject()["type"] == "m.room_key_request" && event.toObject()["content"].toObject()["action"] == "request") { if (event.toObject()["type"] == "m.room_key_request" &&
event.toObject()["content"].toObject()["action"] == "request") {
auto sender = event.toObject()["sender"].toString(); auto sender = event.toObject()["sender"].toString();
auto device_id = event.toObject()["content"].toObject()["requesting_device_id"].toString(); auto device_id = event.toObject()["content"].toObject()["requesting_device_id"].toString();
auto room_id = event.toObject()["content"].toObject()["body"].toObject()["room_id"].toString(); auto room_id = event.toObject()["content"].toObject()["body"].toObject()["room_id"].toString();
QJsonObject queryObject{ QJsonObject queryObject{
{"timeout", 10000}, {"timeout", 10000},
{"device_keys", QJsonObject{ {"device_keys", QJsonObject{{sender, QJsonArray({device_id})}}},
{sender, QJsonArray({device_id})}
}},
{"token", "string"}, {"token", "string"},
}; };
network->postJSON("/_matrix/client/r0/keys/query", queryObject, [this, event, sender, device_id, room_id](QNetworkReply* reply) { network->postJSON(
"/_matrix/client/r0/keys/query",
queryObject,
[this, event, sender, device_id, room_id](QNetworkReply* reply) {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
auto senderCurveKey = document.object()["device_keys"].toObject()[sender].toObject()[device_id].toObject()["keys"].toObject()["curve25519:" + device_id].toString(); auto senderCurveKey = document.object()["device_keys"]
auto senderEdKey = document.object()["device_keys"].toObject()[sender].toObject()[device_id].toObject()["keys"].toObject()["ed25519:" + device_id].toString(); .toObject()[sender]
.toObject()[device_id]
.toObject()["keys"]
.toObject()["curve25519:" + device_id]
.toString();
auto senderEdKey = document.object()["device_keys"]
.toObject()[sender]
.toObject()[device_id]
.toObject()["keys"]
.toObject()["ed25519:" + device_id]
.toString();
qDebug() << "sending keys to " << device_id; qDebug() << "sending keys to " << device_id;
createOrLoadSession(); createOrLoadSession();
sendKeyToDevice(room_id, senderCurveKey, senderEdKey, currentSessionId, currentSessionKey, sender, device_id); sendKeyToDevice(
room_id,
senderCurveKey,
senderEdKey,
currentSessionId,
currentSessionKey,
sender,
device_id);
}); });
} else if (event.toObject()["type"] == "m.room_key") { } else if (event.toObject()["type"] == "m.room_key") {
qDebug() << "we recieved a new key from a user in the room :-)"; qDebug() << "we recieved a new key from a user in the room :-)";
@ -300,21 +314,31 @@ void MatrixCore::sync() {
} else if (event.toObject()["type"] == "m.room.encrypted") { } else if (event.toObject()["type"] == "m.room.encrypted") {
auto curveKey = event.toObject()["content"].toObject()["ciphertext"].toObject().keys()[0]; auto curveKey = event.toObject()["content"].toObject()["ciphertext"].toObject().keys()[0];
auto senderKey = event.toObject()["content"].toObject()["sender_key"].toString(); auto senderKey = event.toObject()["content"].toObject()["sender_key"].toString();
int type = event.toObject()["content"].toObject()["ciphertext"].toObject()[curveKey].toObject()["type"].toInt(); int type = event.toObject()["content"]
auto body = event.toObject()["content"].toObject()["ciphertext"].toObject()[curveKey].toObject()["body"].toString(); .toObject()["ciphertext"]
.toObject()[curveKey]
.toObject()["type"]
.toInt();
auto body = event.toObject()["content"]
.toObject()["ciphertext"]
.toObject()[curveKey]
.toObject()["body"]
.toString();
// create a new inbound session // create a new inbound session
auto session = encryption->createInboundSession(senderKey.toStdString(), body.toStdString()); auto session = encryption->createInboundSession(senderKey.toStdString(), body.toStdString());
auto decryptedMsg = encryption->decrypt(session, type, body.toStdString()); auto decryptedMsg = encryption->decrypt(session, type, body.toStdString());
const QJsonDocument document = QJsonDocument::fromJson(QByteArray(reinterpret_cast<const char*>(decryptedMsg.data()), decryptedMsg.size())); const QJsonDocument document = QJsonDocument::fromJson(
QByteArray(reinterpret_cast<const char*>(decryptedMsg.data()), decryptedMsg.size()));
auto id = document.object()["content"].toObject()["session_id"].toString(); auto id = document.object()["content"].toObject()["session_id"].toString();
qDebug() << "NEW KEY " << id << " = " << document; qDebug() << "NEW KEY " << id << " = " << document;
// create new inbound session, append to list // create new inbound session, append to list
auto sess = encryption->beginInboundSession(document.object()["content"].toObject()["session_key"].toString().toStdString()); auto sess = encryption->beginInboundSession(
document.object()["content"].toObject()["session_key"].toString().toStdString());
inboundSessions[id] = sess; inboundSessions[id] = sess;
// if we recieved a new key, let's see if we can decrypt some old messages! // if we recieved a new key, let's see if we can decrypt some old messages!
@ -324,7 +348,8 @@ void MatrixCore::sync() {
if (event->encryptionInfo != nullptr && event->encryptionInfo->sessionId == id) { if (event->encryptionInfo != nullptr && event->encryptionInfo->sessionId == id) {
auto msg = encryption->decrypt(sess, event->encryptionInfo->cipherText.toStdString()); auto msg = encryption->decrypt(sess, event->encryptionInfo->cipherText.toStdString());
const QJsonDocument document = QJsonDocument::fromJson(QByteArray(reinterpret_cast<const char*>(msg.data()), msg.size())); const QJsonDocument document = QJsonDocument::fromJson(
QByteArray(reinterpret_cast<const char*>(msg.data()), msg.size()));
populateEvent(document.object(), event); populateEvent(document.object(), event);
@ -352,7 +377,8 @@ void MatrixCore::sync() {
settings.endGroup(); settings.endGroup();
if (autofill_data) { if (autofill_data) {
network->get("/_matrix/client/r0/rooms/" + id + "/state/m.room.name", [this, room](QNetworkReply* reply) { network->get(
"/_matrix/client/r0/rooms/" + id + "/state/m.room.name", [this, room](QNetworkReply* reply) {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
if (document.object()["errcode"].toString() == "M_GUEST_ACCESS_FORBIDDEN") { if (document.object()["errcode"].toString() == "M_GUEST_ACCESS_FORBIDDEN") {
room->setGuestDenied(true); room->setGuestDenied(true);
@ -365,7 +391,8 @@ void MatrixCore::sync() {
roomListModel.updateRoom(room); roomListModel.updateRoom(room);
}); });
network->get("/_matrix/client/r0/rooms/" + id + "/state/m.room.topic", [this, room](QNetworkReply* reply) { network->get(
"/_matrix/client/r0/rooms/" + id + "/state/m.room.topic", [this, room](QNetworkReply* reply) {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
if (document.object()["errcode"].toString() == "M_GUEST_ACCESS_FORBIDDEN") { if (document.object()["errcode"].toString() == "M_GUEST_ACCESS_FORBIDDEN") {
room->setGuestDenied(true); room->setGuestDenied(true);
@ -377,7 +404,8 @@ void MatrixCore::sync() {
roomListModel.updateRoom(room); roomListModel.updateRoom(room);
}); });
network->get("/_matrix/client/r0/rooms/" + id + "/state/m.room.avatar", [this, room](QNetworkReply* reply) { network->get(
"/_matrix/client/r0/rooms/" + id + "/state/m.room.avatar", [this, room](QNetworkReply* reply) {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
if (document.object()["errcode"].toString() == "M_GUEST_ACCESS_FORBIDDEN") { if (document.object()["errcode"].toString() == "M_GUEST_ACCESS_FORBIDDEN") {
room->setGuestDenied(true); room->setGuestDenied(true);
@ -386,13 +414,17 @@ void MatrixCore::sync() {
if (document.object().contains("url")) { if (document.object().contains("url")) {
const QString imageId = document.object()["url"].toString().remove("mxc://"); const QString imageId = document.object()["url"].toString().remove("mxc://");
room->setAvatar(network->homeserverURL + "/_matrix/media/r0/thumbnail/" + imageId + "?width=64&height=64&method=scale"); room->setAvatar(
network->homeserverURL + "/_matrix/media/r0/thumbnail/" + imageId +
"?width=64&height=64&method=scale");
} }
roomListModel.updateRoom(room); roomListModel.updateRoom(room);
}); });
network->get("/_matrix/client/r0/rooms/" + id + "/state/m.room.power_levels", [this, room](QNetworkReply* reply) { network->get(
"/_matrix/client/r0/rooms/" + id + "/state/m.room.power_levels",
[this, room](QNetworkReply* reply) {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
for (auto user : document.object()["users"].toObject().keys()) { for (auto user : document.object()["users"].toObject().keys()) {
@ -400,7 +432,8 @@ void MatrixCore::sync() {
} }
}); });
network->get("/_matrix/client/r0/rooms/" + id + "/state/m.room.encryption", [this, room](QNetworkReply* reply) { network->get(
"/_matrix/client/r0/rooms/" + id + "/state/m.room.encryption", [this, room](QNetworkReply* reply) {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
if (document.object().contains("algorithm")) { if (document.object().contains("algorithm")) {
@ -428,7 +461,12 @@ void MatrixCore::sync() {
Room* room = createRoom(id, "Invited", false); Room* room = createRoom(id, "Invited", false);
room->setGuestDenied(true); room->setGuestDenied(true);
for(auto event : document.object()["rooms"].toObject()["invite"].toObject()[id].toObject()["invite_state"].toObject()["events"].toArray()) { for (auto event : document.object()["rooms"]
.toObject()["invite"]
.toObject()[id]
.toObject()["invite_state"]
.toObject()["events"]
.toArray()) {
const QString type = event.toObject()["type"].toString(); const QString type = event.toObject()["type"].toString();
if (type == "m.room.member") if (type == "m.room.member")
@ -436,8 +474,11 @@ void MatrixCore::sync() {
else if (type == "m.room.name") { else if (type == "m.room.name") {
room->setName(event.toObject()["content"].toObject()["name"].toString()); room->setName(event.toObject()["content"].toObject()["name"].toString());
} else if (type == "m.room.avatar") { } else if (type == "m.room.avatar") {
const QString imageId = event.toObject()["content"].toObject()["url"].toString().remove("mxc://"); const QString imageId =
room->setAvatar(network->homeserverURL + "/_matrix/media/r0/thumbnail/" + imageId + "?width=64&height=64&method=scale"); event.toObject()["content"].toObject()["url"].toString().remove("mxc://");
room->setAvatar(
network->homeserverURL + "/_matrix/media/r0/thumbnail/" + imageId +
"?width=64&height=64&method=scale");
} }
roomListModel.updateRoom(room); roomListModel.updateRoom(room);
@ -482,7 +523,8 @@ void MatrixCore::sync() {
roomState->prevBatch = room.toObject()["timeline"].toObject()["prev_batch"].toString(); roomState->prevBatch = room.toObject()["timeline"].toObject()["prev_batch"].toString();
const int highlightCount = room.toObject()["unread_notifications"].toObject()["highlight_count"].toInt(); const int highlightCount = room.toObject()["unread_notifications"].toObject()["highlight_count"].toInt();
const int notificationCount = room.toObject()["unread_notifications"].toObject()["notification_count"].toInt(); const int notificationCount =
room.toObject()["unread_notifications"].toObject()["notification_count"].toInt();
if (highlightCount != roomState->getHighlightCount()) { if (highlightCount != roomState->getHighlightCount()) {
roomState->setNotificationCount(highlightCount); roomState->setNotificationCount(highlightCount);
@ -590,7 +632,8 @@ void MatrixCore::sendMessage(Room* room, const QString& message) {
char* formatted = nullptr; char* formatted = nullptr;
if (markdownEnabled) { if (markdownEnabled) {
formatted = cmark_markdown_to_html(message.toStdString().c_str(), message.length(), CMARK_OPT_DEFAULT | CMARK_OPT_HARDBREAKS); formatted = cmark_markdown_to_html(
message.toStdString().c_str(), message.length(), CMARK_OPT_DEFAULT | CMARK_OPT_HARDBREAKS);
shouldSendAsMarkdown = strlen(formatted) > 8 + message.length(); shouldSendAsMarkdown = strlen(formatted) > 8 + message.length();
} }
@ -601,15 +644,11 @@ void MatrixCore::sendMessage(Room* room, const QString& message) {
{"msgtype", "m.text"}, {"msgtype", "m.text"},
{"formatted_body", formatted}, {"formatted_body", formatted},
{"body", message}, {"body", message},
{"format", "org.matrix.custom.html"} {"format", "org.matrix.custom.html"}};
};
e->setMsg(formatted); e->setMsg(formatted);
} else { } else {
messageObject = QJsonObject { messageObject = QJsonObject{{"msgtype", "m.text"}, {"body", message}};
{"msgtype", "m.text"},
{"body", message}
};
e->setMsg(message); e->setMsg(message);
} }
@ -645,19 +684,22 @@ void MatrixCore::sendMessage(Room* room, const QString& message) {
}} }}
}; };
std::string identityKey = document.object()["device_keys"].toObject()[user_id].toObject()[device_id].toObject()["keys"].toObject()[QString("curve25519:") + device_id].toString().toStdString(); std::string identityKey =
document.object()["device_keys"].toObject()[user_id].toObject()[device_id].toObject()["keys"].toObject()[QString("curve25519:")
+ device_id].toString().toStdString();
std::string edIdentityKey = document.object()["device_keys"].toObject()[user_id].toObject()[device_id].toObject()["keys"].toObject()[QString("ed25519:") + device_id].toString().toStdString(); std::string edIdentityKey =
document.object()["device_keys"].toObject()[user_id].toObject()[device_id].toObject()["keys"].toObject()[QString("ed25519:")
+ device_id].toString().toStdString();
sendKeyToDevice(room->getId(), identityKey.c_str(), edIdentityKey.c_str(), currentSessionId, currentSessionKey, user_id, device_id); sendKeyToDevice(room->getId(), identityKey.c_str(), edIdentityKey.c_str(), currentSessionId,
currentSessionKey, user_id, device_id);
} }
} }
});*/ });*/
QJsonObject trueObject{ QJsonObject trueObject{
{"room_id", room->getId()}, {"room_id", room->getId()}, {"type", "m.room.message"}, {"content", messageObject}
{"type", "m.room.message"},
{"content", messageObject}
/*{"keys", QJsonObject { /*{"keys", QJsonObject {
{"ed25519", encryption->identityKey["ed25519"]} {"ed25519", encryption->identityKey["ed25519"]}
}}, }},
@ -668,26 +710,39 @@ void MatrixCore::sendMessage(Room* room, const QString& message) {
// construct the m.room.encrypted event // construct the m.room.encrypted event
const QJsonObject roomEncryptedObject{ const QJsonObject roomEncryptedObject{
{"algorithm", "m.megolm.v1.aes-sha2"}, {"algorithm", "m.megolm.v1.aes-sha2"},
{"ciphertext", encryption->encryptGroup(currentSession, QString(QJsonDocument(trueObject).toJson(QJsonDocument::Compact)).toStdString()).c_str()}, {"ciphertext",
encryption
->encryptGroup(
currentSession, QString(QJsonDocument(trueObject).toJson(QJsonDocument::Compact)).toStdString())
.c_str()},
{"sender_key", encryption->identityKey["curve25519"]}, {"sender_key", encryption->identityKey["curve25519"]},
{"session_id", currentSessionId}, {"session_id", currentSessionId},
{"device_id", deviceId} {"device_id", deviceId}};
};
network->putJSON("/_matrix/client/r0/rooms/" + room->getId() + "/send/m.room.encrypted/" + QRandomGenerator::global()->generate(), roomEncryptedObject, [](QNetworkReply* reply) { network->putJSON(
"/_matrix/client/r0/rooms/" + room->getId() + "/send/m.room.encrypted/" +
QRandomGenerator::global()->generate(),
roomEncryptedObject,
[](QNetworkReply* reply) {
// qDebug() << "reply from room send: " << reply->readAll(); // qDebug() << "reply from room send: " << reply->readAll();
}); });
} else { } else {
network->putJSON(QString("/_matrix/client/r0/rooms/" + room->getId() + "/send/m.room.message/") + QString::number(QRandomGenerator::global()->generate()), messageObject, onMessageFeedbackReceived); network->putJSON(
QString("/_matrix/client/r0/rooms/" + room->getId() + "/send/m.room.message/") +
QString::number(QRandomGenerator::global()->generate()),
messageObject,
onMessageFeedbackReceived);
} }
} }
void MatrixCore::removeMessage(const QString& eventId) { void MatrixCore::removeMessage(const QString& eventId) {
const QJsonObject reasonObject { const QJsonObject reasonObject{{"reason", ""}};
{"reason", ""}
};
network->putJSON("/_matrix/client/r0/rooms/" + currentRoom->getId() + "/redact/" + eventId + "/" + QString::number(QRandomGenerator::global()->generate()), reasonObject, [this, eventId](QNetworkReply* reply) { network->putJSON(
"/_matrix/client/r0/rooms/" + currentRoom->getId() + "/redact/" + eventId + "/" +
QString::number(QRandomGenerator::global()->generate()),
reasonObject,
[this, eventId](QNetworkReply* reply) {
auto& events = currentRoom->events; auto& events = currentRoom->events;
for (int i = 0; i < events.size(); i++) { for (int i = 0; i < events.size(); i++) {
if (events[i]->eventId == eventId) { if (events[i]->eventId == eventId) {
@ -730,7 +785,11 @@ void MatrixCore::uploadAttachment(Room* room, const QString& path) {
e->setMsg(fileName); e->setMsg(fileName);
e->setMsgType(mimeType.name().contains("image") ? "image" : "file"); e->setMsgType(mimeType.name().contains("image") ? "image" : "file");
network->postBinary("/_matrix/media/r0/upload?filename=" + f.fileName(), f.readAll(), mimeType.name(), [this, mimeType, fileName, fileSize, e](QNetworkReply* reply) { network->postBinary(
"/_matrix/media/r0/upload?filename=" + f.fileName(),
f.readAll(),
mimeType.name(),
[this, mimeType, fileName, fileSize, e](QNetworkReply* reply) {
if (!reply->error()) { if (!reply->error()) {
e->setSent(true); e->setSent(true);
@ -745,12 +804,15 @@ void MatrixCore::uploadAttachment(Room* room, const QString& path) {
{"msgtype", mimeType.name().contains("image") ? "m.image" : "m.file"}, {"msgtype", mimeType.name().contains("image") ? "m.image" : "m.file"},
{"body", fileName}, {"body", fileName},
{"url", document.object()["content_uri"].toString()}, {"url", document.object()["content_uri"].toString()},
{"info", infoObject} {"info", infoObject}};
};
network->putJSON("/_matrix/client/r0/rooms/" + currentRoom->getId() + "/send/m.room.message/" + QString::number(QRandomGenerator::global()->generate()), imageObject); network->putJSON(
"/_matrix/client/r0/rooms/" + currentRoom->getId() + "/send/m.room.message/" +
QString::number(QRandomGenerator::global()->generate()),
imageObject);
} }
}, [e](qint64 sent, qint64 total) { },
[e](qint64 sent, qint64 total) {
e->setSentProgress((double)sent / (double)total); e->setSentProgress((double)sent / (double)total);
}); });
} }
@ -761,17 +823,13 @@ void MatrixCore::startDirectChat(const QString& id) {
{"creation_content", QJsonObject{{"m.federate", false}}}, {"creation_content", QJsonObject{{"m.federate", false}}},
{"preset", "private_chat"}, {"preset", "private_chat"},
{"is_direct", true}, {"is_direct", true},
{"invite", QJsonArray{id}} {"invite", QJsonArray{id}}};
};
network->postJSON("/_matrix/client/r0/createRoom", roomObject, [](QNetworkReply*) {}); network->postJSON("/_matrix/client/r0/createRoom", roomObject, [](QNetworkReply*) {});
} }
void MatrixCore::setTyping(Room* room) { void MatrixCore::setTyping(Room* room) {
const QJsonObject typingObject { const QJsonObject typingObject{{"typing", true}, {"timeout", 15000}};
{"typing", true},
{"timeout", 15000}
};
network->putJSON("/_matrix/client/r0/rooms/" + room->getId() + "/typing/" + userId, typingObject); network->putJSON("/_matrix/client/r0/rooms/" + room->getId() + "/typing/" + userId, typingObject);
} }
@ -804,9 +862,7 @@ void MatrixCore::leaveRoom(const QString& id) {
} }
void MatrixCore::inviteToRoom(Room* room, const QString& userId) { void MatrixCore::inviteToRoom(Room* room, const QString& userId) {
const QJsonObject inviteObject { const QJsonObject inviteObject{{"user_id", userId}};
{"user_id", userId}
};
network->postJSON("/_matrix/client/r0/rooms/" + room->getId() + "/invite", inviteObject, [](QNetworkReply*) {}); network->postJSON("/_matrix/client/r0/rooms/" + room->getId() + "/invite", inviteObject, [](QNetworkReply*) {});
} }
@ -843,8 +899,11 @@ void MatrixCore::updateMembers(Room* room) {
m->setDisplayName(memberJson["content"].toObject()["displayname"].toString()); m->setDisplayName(memberJson["content"].toObject()["displayname"].toString());
if (!memberJson["content"].toObject()["avatar_url"].isNull()) { if (!memberJson["content"].toObject()["avatar_url"].isNull()) {
const QString imageId = memberJson["content"].toObject()["avatar_url"].toString().remove("mxc://"); const QString imageId =
m->setAvatar(network->homeserverURL + "/_matrix/media/r0/thumbnail/" + imageId + "?width=64&height=64&method=scale"); memberJson["content"].toObject()["avatar_url"].toString().remove("mxc://");
m->setAvatar(
network->homeserverURL + "/_matrix/media/r0/thumbnail/" + imageId +
"?width=64&height=64&method=scale");
} }
idToMember.insert(id, m); idToMember.insert(id, m);
@ -874,7 +933,9 @@ void MatrixCore::readMessageHistory(Room* room) {
qDebug() << "Reading message history..."; qDebug() << "Reading message history...";
network->get("/_matrix/client/r0/rooms/" + room->getId() + "/messages?from=" + room->prevBatch + "&dir=b", [this, room](QNetworkReply* reply) { network->get(
"/_matrix/client/r0/rooms/" + room->getId() + "/messages?from=" + room->prevBatch + "&dir=b",
[this, room](QNetworkReply* reply) {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
room->prevBatch = document.object()["end"].toString(); room->prevBatch = document.object()["end"].toString();
@ -973,7 +1034,6 @@ Member* MatrixCore::resolveMemberId(const QString& id) const {
return idToMember.value(id); return idToMember.value(id);
} }
Room* MatrixCore::resolveRoomId(const QString& id) const { Room* MatrixCore::resolveRoomId(const QString& id) const {
return idToRoom.value(id); return idToRoom.value(id);
} }
@ -1009,7 +1069,9 @@ void MatrixCore::loadDirectory(const QString& homeserver) {
if (!roomObject["avatar_url"].isNull()) { if (!roomObject["avatar_url"].isNull()) {
const QString imageId = roomObject["avatar_url"].toString().remove("mxc://"); const QString imageId = roomObject["avatar_url"].toString().remove("mxc://");
r->setAvatar(network->homeserverURL + "/_matrix/media/r0/thumbnail/" + imageId + "?width=64&height=64&method=scale"); r->setAvatar(
network->homeserverURL + "/_matrix/media/r0/thumbnail/" + imageId +
"?width=64&height=64&method=scale");
} }
r->setTopic(roomObject["topic"].toString()); r->setTopic(roomObject["topic"].toString());
@ -1128,7 +1190,9 @@ void MatrixCore::consumeEvent(const QJsonObject& event, Room& room, const bool i
if (!event["content"].toObject()["avatar_url"].isNull()) { if (!event["content"].toObject()["avatar_url"].isNull()) {
const QString imageId = event["content"].toObject()["avatar_url"].toString().remove("mxc://"); const QString imageId = event["content"].toObject()["avatar_url"].toString().remove("mxc://");
room.setAvatar(network->homeserverURL + "/_matrix/media/r0/thumbnail/" + imageId + "?width=64&height=64&method=scale"); room.setAvatar(
network->homeserverURL + "/_matrix/media/r0/thumbnail/" + imageId +
"?width=64&height=64&method=scale");
} }
} }
@ -1153,37 +1217,39 @@ void MatrixCore::consumeEvent(const QJsonObject& event, Room& room, const bool i
// construct m.room.key event // construct m.room.key event
const QJsonObject roomKeyObject{ const QJsonObject roomKeyObject{
{"action", "request"}, {"action", "request"},
{"body", QJsonObject { {"body",
QJsonObject{
{"algorithm", event["content"].toObject()["algorithm"]}, {"algorithm", event["content"].toObject()["algorithm"]},
{"room_id", room.getId()}, {"room_id", room.getId()},
{"sender_key", event["content"].toObject()["sender_key"]}, {"sender_key", event["content"].toObject()["sender_key"]},
{"session_id", event["content"].toObject()["session_id"]} {"session_id", event["content"].toObject()["session_id"]}}},
}},
{"requesting_device_id", deviceId}, {"requesting_device_id", deviceId},
{"request_id", QString("lololol") + QString::number(QRandomGenerator::global()->generate())} {"request_id", QString("lololol") + QString::number(QRandomGenerator::global()->generate())}};
};
const QJsonObject sendToDeviceObject{ const QJsonObject sendToDeviceObject{
{"messages", QJsonObject { {"messages",
{ event["sender"].toString(), QJsonObject { QJsonObject{
{ event["content"].toObject()["device_id"].toString(), roomKeyObject} {event["sender"].toString(),
}} QJsonObject{{event["content"].toObject()["device_id"].toString(), roomKeyObject}}}}}};
}}
};
// qDebug() << QJsonDocument(sendToDeviceObject).toJson(QJsonDocument::Compact); // qDebug() << QJsonDocument(sendToDeviceObject).toJson(QJsonDocument::Compact);
network->putJSON(QString("/_matrix/client/r0/sendToDevice/m.room_key_request/") + QString::number(QRandomGenerator::global()->generate()), sendToDeviceObject, [](QNetworkReply* reply) { network->putJSON(
QString("/_matrix/client/r0/sendToDevice/m.room_key_request/") +
QString::number(QRandomGenerator::global()->generate()),
sendToDeviceObject,
[](QNetworkReply* reply) {
// qDebug() << "REPLY FROM KEY REQUEST SEND: " << reply->readAll(); // qDebug() << "REPLY FROM KEY REQUEST SEND: " << reply->readAll();
}); });
} else { } else {
auto session = inboundSessions[event["content"].toObject()["session_id"].toString()]; auto session = inboundSessions[event["content"].toObject()["session_id"].toString()];
auto msg = encryption->decrypt(session, event["content"].toObject()["ciphertext"].toString().toStdString()); auto msg =
encryption->decrypt(session, event["content"].toObject()["ciphertext"].toString().toStdString());
const QJsonDocument document = QJsonDocument::fromJson(QByteArray(reinterpret_cast<const char*>(msg.data()), msg.size())); const QJsonDocument document =
QJsonDocument::fromJson(QByteArray(reinterpret_cast<const char*>(msg.data()), msg.size()));
populateEvent(document.object(), e); populateEvent(document.object(), e);
} }
} else { } else {
e->setMsg("** ERR: messages sent from the same device are not supported yet."); e->setMsg("** ERR: messages sent from the same device are not supported yet.");
@ -1234,7 +1300,8 @@ void MatrixCore::populateEvent(const QJsonObject& event, Event* e) {
e->setAttachmentSize(event["content"].toObject()["info"].toObject()["size"].toInt()); e->setAttachmentSize(event["content"].toObject()["info"].toObject()["size"].toInt());
if (event["content"].toObject()["info"].toObject().contains("thumbnail_url")) if (event["content"].toObject()["info"].toObject().contains("thumbnail_url"))
e->setThumbnail(getMXCThumbnailURL(event["content"].toObject()["info"].toObject()["thumbnail_url"].toString())); e->setThumbnail(
getMXCThumbnailURL(event["content"].toObject()["info"].toObject()["thumbnail_url"].toString()));
else else
e->setThumbnail(getMXCMediaURL(event["content"].toObject()["url"].toString())); e->setThumbnail(getMXCMediaURL(event["content"].toObject()["url"].toString()));
@ -1277,22 +1344,26 @@ bool MatrixCore::getMarkdownEnabled() const {
return markdownEnabled; return markdownEnabled;
} }
void MatrixCore::sendKeyToDevice(QString roomId, QString senderCurveIdentity, QString senderEdIdentity, QString session_id, QString session_key, QString user_id, QString device_id) void MatrixCore::sendKeyToDevice(
{ QString roomId,
QString senderCurveIdentity,
QString senderEdIdentity,
QString session_id,
QString session_key,
QString user_id,
QString device_id) {
// why we would we send ourselves a key? // why we would we send ourselves a key?
if (device_id == deviceId) if (device_id == deviceId)
return; return;
QJsonObject claimObject{ QJsonObject claimObject{
{"timeout", 10000}, {"timeout", 10000}, {"one_time_keys", QJsonObject{{user_id, QJsonObject{{device_id, "signed_curve25519"}}}}}};
{"one_time_keys", QJsonObject {
{user_id, QJsonObject {
{device_id, "signed_curve25519"}
}}
}}
};
network->postJSON("/_matrix/client/r0/keys/claim", claimObject, [this, device_id, user_id, senderCurveIdentity, senderEdIdentity, roomId, session_id, session_key](QNetworkReply* reply) { network->postJSON(
"/_matrix/client/r0/keys/claim",
claimObject,
[this, device_id, user_id, senderCurveIdentity, senderEdIdentity, roomId, session_id, session_key](
QNetworkReply* reply) {
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
std::string identityKey = senderCurveIdentity.toStdString(); std::string identityKey = senderCurveIdentity.toStdString();
@ -1301,50 +1372,60 @@ void MatrixCore::sendKeyToDevice(QString roomId, QString senderCurveIdentity, QS
if (document.object()["one_time_keys"].toObject()[user_id].toObject()[device_id].toObject().keys().empty()) if (document.object()["one_time_keys"].toObject()[user_id].toObject()[device_id].toObject().keys().empty())
return; return;
std::string oneTimeKey = document.object()["one_time_keys"].toObject()[user_id].toObject()[device_id].toObject()[document.object()["one_time_keys"].toObject()[user_id].toObject()[device_id].toObject().keys()[0]].toObject()["key"].toString().toStdString(); std::string oneTimeKey = document.object()["one_time_keys"]
.toObject()[user_id]
.toObject()[device_id]
.toObject()[document.object()["one_time_keys"]
.toObject()[user_id]
.toObject()[device_id]
.toObject()
.keys()[0]]
.toObject()["key"]
.toString()
.toStdString();
auto session = encryption->beginOutboundOlmSession(identityKey, oneTimeKey); auto session = encryption->beginOutboundOlmSession(identityKey, oneTimeKey);
// construct m.room.key event // construct m.room.key event
const QJsonObject roomKeyObject{ const QJsonObject roomKeyObject{
{"content", QJsonObject { {"content",
QJsonObject{
{"algorithm", "m.megolm.v1.aes-sha2"}, {"algorithm", "m.megolm.v1.aes-sha2"},
{"room_id", roomId}, {"room_id", roomId},
{"session_id", session_id}, {"session_id", session_id},
{"session_key", session_key} {"session_key", session_key}}},
}},
{"type", "m.room_key"}, {"type", "m.room_key"},
{"keys", QJsonObject { {"keys", QJsonObject{{"ed25519", encryption->identityKey["ed25519"]}}},
{"ed25519", encryption->identityKey["ed25519"]}
}},
{"sender", userId}, {"sender", userId},
{"sender_device", deviceId}, {"sender_device", deviceId},
{"recipient", user_id}, {"recipient", user_id},
{"recipient_keys", QJsonObject { {"recipient_keys", QJsonObject{{"ed25519", senderEdIdentity}}}};
{"ed25519", senderEdIdentity}
}}
};
// construct the m.room.encrypted event // construct the m.room.encrypted event
const QJsonObject roomEncryptedObject{ const QJsonObject roomEncryptedObject{
{"algorithm", "m.olm.v1.curve25519-aes-sha2"}, {"algorithm", "m.olm.v1.curve25519-aes-sha2"},
{"ciphertext", QJsonObject { {"ciphertext",
{identityKey.c_str(), QJsonObject { QJsonObject{
{"body", encryption->encrypt(session, QString(QJsonDocument(roomKeyObject).toJson(QJsonDocument::Compact)).toStdString()).c_str()}, {identityKey.c_str(),
{"type", (int)olm_encrypt_message_type(session)} QJsonObject{
}}}}, {"body",
encryption
->encrypt(
session,
QString(QJsonDocument(roomKeyObject).toJson(QJsonDocument::Compact)).toStdString())
.c_str()},
{"type", (int)olm_encrypt_message_type(session)}}}}},
{"sender_key", encryption->identityKey["curve25519"]}, {"sender_key", encryption->identityKey["curve25519"]},
}; };
const QJsonObject sendToDeviceObject{ const QJsonObject sendToDeviceObject{
{"messages", QJsonObject { {"messages", QJsonObject{{user_id, QJsonObject{{device_id, roomEncryptedObject}}}}}};
{ user_id, QJsonObject {
{ device_id, roomEncryptedObject}
}}
}}
};
network->putJSON(QString("/_matrix/client/r0/sendToDevice/m.room.encrypted/") + QString::number(QRandomGenerator::global()->generate()), sendToDeviceObject, [](QNetworkReply* reply) { network->putJSON(
QString("/_matrix/client/r0/sendToDevice/m.room.encrypted/") +
QString::number(QRandomGenerator::global()->generate()),
sendToDeviceObject,
[](QNetworkReply* reply) {
// qDebug() << "REPLY FROM KEY SEND: " << reply->readAll(); // qDebug() << "REPLY FROM KEY SEND: " << reply->readAll();
}); });
}); });

View file

@ -1,8 +1,8 @@
#include "roomlistsortmodel.h" #include "roomlistsortmodel.h"
#include "membermodel.h"
#include "room.h" #include "room.h"
#include "roomlistmodel.h" #include "roomlistmodel.h"
#include "membermodel.h"
bool RoomListSortModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { bool RoomListSortModel::lessThan(const QModelIndex& left, const QModelIndex& right) const {
const QString sectionLeft = sourceModel()->data(left, RoomListModel::SectionRole).toString(); const QString sectionLeft = sourceModel()->data(left, RoomListModel::SectionRole).toString();
@ -29,7 +29,8 @@ bool MemberListSortModel::lessThan(const QModelIndex& left, const QModelIndex& r
return true; return true;
if (sectionLeft == sectionRight) if (sectionLeft == sectionRight)
return sourceModel()->data(left, MemberModel::DisplayNameRole).toString() < sourceModel()->data(right, MemberModel::DisplayNameRole).toString(); return sourceModel()->data(left, MemberModel::DisplayNameRole).toString() <
sourceModel()->data(right, MemberModel::DisplayNameRole).toString();
return false; return false;
} }