[WIP] Room-Verification Messages

This commit is contained in:
CH Chethan Reddy 2020-08-09 08:35:15 +05:30
parent 3635c185e9
commit 2e20049b36
15 changed files with 392 additions and 484 deletions

View file

@ -35,13 +35,8 @@ Flow {
ToolTip.text: modelData.users ToolTip.text: modelData.users
onClicked: { onClicked: {
<<<<<<< HEAD
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + modelData.selfReactedEvent) console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + modelData.selfReactedEvent)
timelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key) TimelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key)
=======
console.debug("Picked " + model.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + model.selfReactedEvent)
TimelineManager.reactToMessage(reactionFlow.roomId, reactionFlow.eventId, model.key, model.selfReactedEvent)
>>>>>>> Fix presence indicator
} }

View file

@ -48,7 +48,7 @@ Item {
// fancy reply, if this is a reply // fancy reply, if this is a reply
Reply { Reply {
visible: model.replyTo visible: model.replyTo
modelData: chat.model.getDump(model.replyTo) modelData: chat.model.getDump(model.replyTo,model.id)
userColor: TimelineManager.userColor(modelData.userId, colors.window) userColor: TimelineManager.userColor(modelData.userId, colors.window)
} }

View file

@ -388,13 +388,8 @@ Page {
anchors.rightMargin: 20 anchors.rightMargin: 20
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
<<<<<<< HEAD
modelData: chat.model ? chat.model.getDump(chat.model.reply, chat.model.id) : {} modelData: chat.model ? chat.model.getDump(chat.model.reply, chat.model.id) : {}
userColor: timelineManager.userColor(modelData.userId, colors.window)
=======
modelData: chat.model ? chat.model.getDump(chat.model.reply) : {}
userColor: TimelineManager.userColor(modelData.userId, colors.window) userColor: TimelineManager.userColor(modelData.userId, colors.window)
>>>>>>> Fix presence indicator
} }
ImageButton { ImageButton {

View file

@ -90,7 +90,12 @@ ApplicationWindow{
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
onClicked: { onClicked: {
profile.verifyUser(); var newFlow = profile.createFlow(true);
newFlow.userId = profile.userid;
newFlow.sender = true;
deviceVerificationList.add(newFlow.tranId);
var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest: true});
dialog.show();
} }
} }
@ -192,14 +197,16 @@ ApplicationWindow{
id: verifyButton id: verifyButton
text:(model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify" text:(model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify"
onClicked: { onClicked: {
var newFlow = deviceVerificationFlow.createObject(userProfileDialog, var newFlow = profile.createFlow(false);
{userId : profile.userid, sender: true, deviceId : model.deviceId}); newFlow.userId = profile.userid;
newFlow.sender = true;
newFlow.deviceId = model.deviceId;
if(model.verificationStatus == VerificationStatus.VERIFIED){ if(model.verificationStatus == VerificationStatus.VERIFIED){
newFlow.unverify(); newFlow.unverify();
deviceVerificationList.updateProfile(newFlow.userId); deviceVerificationList.updateProfile(newFlow.userId);
}else{ }else{
deviceVerificationList.add(newFlow.tranId); deviceVerificationList.add(newFlow.tranId);
var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow}); var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest:false});
dialog.show(); dialog.show();
} }
} }

View file

@ -100,7 +100,9 @@ ApplicationWindow {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.startVerificationRequest(); } onClicked: {
stack.replace(awaitingVerificationRequestAccept);
isRequest?flow.sendVerificationRequest():flow.startVerificationRequest(); }
} }
} }
} }

View file

@ -562,8 +562,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
connect( connect(
this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync, Qt::QueuedConnection); this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync, Qt::QueuedConnection);
connect(this, &ChatPage::trySyncCb, this, &ChatPage::trySync, Qt::QueuedConnection); connect(this, &ChatPage::trySyncCb, this, &ChatPage::trySync, Qt::QueuedConnection);
connect( connect(this,
this,
&ChatPage::tryDelayedSyncCb, &ChatPage::tryDelayedSyncCb,
this, this,
[this]() { QTimer::singleShot(RETRY_TIMEOUT, this, &ChatPage::trySync); }, [this]() { QTimer::singleShot(RETRY_TIMEOUT, this, &ChatPage::trySync); },

View file

@ -22,6 +22,11 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow
this->sas = olm::client()->sas_init(); this->sas = olm::client()->sas_init();
this->isMacVerified = false; this->isMacVerified = false;
connect(this->model_,
&TimelineModel::updateFlowEventId,
this,
[this](std::string event_id) { this->relation.in_reply_to.event_id = event_id; });
connect(timeout, &QTimer::timeout, this, [this]() { connect(timeout, &QTimer::timeout, this, [this]() {
emit timedout(); emit timedout();
this->cancelVerification(DeviceVerificationFlow::Error::Timeout); this->cancelVerification(DeviceVerificationFlow::Error::Timeout);
@ -222,6 +227,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow
if (msg.transaction_id.value() != this->transaction_id) if (msg.transaction_id.value() != this->transaction_id)
return; return;
} else if (msg.relates_to.has_value()) { } else if (msg.relates_to.has_value()) {
// this is just a workaround
this->relation.in_reply_to.event_id =
msg.relates_to.value().in_reply_to.event_id;
if (msg.relates_to.value().in_reply_to.event_id != if (msg.relates_to.value().in_reply_to.event_id !=
this->relation.in_reply_to.event_id) this->relation.in_reply_to.event_id)
return; return;
@ -344,10 +352,7 @@ void
DeviceVerificationFlow::setSender(bool sender_) DeviceVerificationFlow::setSender(bool sender_)
{ {
this->sender = sender_; this->sender = sender_;
if (this->sender == true && this->type == DeviceVerificationFlow::Type::ToDevice)
this->transaction_id = http::client()->generate_txn_id(); this->transaction_id = http::client()->generate_txn_id();
else if (this->sender == true && this->type == DeviceVerificationFlow::Type::RoomMsg)
this->relation.in_reply_to.event_id = http::client()->generate_txn_id();
} }
void void
@ -380,19 +385,16 @@ DeviceVerificationFlow::acceptVerificationRequest()
body[this->toClient][this->deviceId.toStdString()] = req; body[this->toClient][this->deviceId.toStdString()] = req;
http::client() http::client()->send_to_device<mtx::events::msg::KeyVerificationAccept>(
->send_to_device<mtx::events::msg::KeyVerificationAccept,
mtx::events::EventType::KeyVerificationAccept>(
this->transaction_id, body, [](mtx::http::RequestErr err) { this->transaction_id, body, [](mtx::http::RequestErr err) {
if (err) if (err)
nhlog::net()->warn( nhlog::net()->warn("failed to accept verification request: {} {}",
"failed to accept verification request: {} {}",
err->matrix_error.error, err->matrix_error.error,
static_cast<int>(err->status_code)); static_cast<int>(err->status_code));
}); });
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
req.relates_to = this->relation; req.relates_to = this->relation;
(model_.value())->sendMessage(req); (model_)->sendMessage(req);
} }
} }
//! responds verification request //! responds verification request
@ -410,18 +412,16 @@ DeviceVerificationFlow::sendVerificationReady()
body[this->toClient][this->deviceId.toStdString()] = req; body[this->toClient][this->deviceId.toStdString()] = req;
http::client() http::client()->send_to_device<mtx::events::msg::KeyVerificationReady>(
->send_to_device<mtx::events::msg::KeyVerificationReady,
mtx::events::EventType::KeyVerificationReady>(
this->transaction_id, body, [](mtx::http::RequestErr err) { this->transaction_id, body, [](mtx::http::RequestErr err) {
if (err) if (err)
nhlog::net()->warn("failed to send verification ready: {} {}", nhlog::net()->warn("failed to send verification ready: {} {}",
err->matrix_error.error, err->matrix_error.error,
static_cast<int>(err->status_code)); static_cast<int>(err->status_code));
}); });
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
req.relates_to = this->relation; req.relates_to = this->relation;
(model_.value())->sendMessage(req); (model_)->sendMessage(req);
} }
} }
//! accepts a verification //! accepts a verification
@ -436,18 +436,16 @@ DeviceVerificationFlow::sendVerificationDone()
body[this->toClient][this->deviceId.toStdString()] = req; body[this->toClient][this->deviceId.toStdString()] = req;
http::client() http::client()->send_to_device<mtx::events::msg::KeyVerificationDone>(
->send_to_device<mtx::events::msg::KeyVerificationDone,
mtx::events::EventType::KeyVerificationDone>(
this->transaction_id, body, [](mtx::http::RequestErr err) { this->transaction_id, body, [](mtx::http::RequestErr err) {
if (err) if (err)
nhlog::net()->warn("failed to send verification done: {} {}", nhlog::net()->warn("failed to send verification done: {} {}",
err->matrix_error.error, err->matrix_error.error,
static_cast<int>(err->status_code)); static_cast<int>(err->status_code));
}); });
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
req.relates_to = this->relation; req.relates_to = this->relation;
(model_.value())->sendMessage(req); (model_)->sendMessage(req);
} }
} }
//! starts the verification flow //! starts the verification flow
@ -470,19 +468,16 @@ DeviceVerificationFlow::startVerificationRequest()
this->canonical_json = nlohmann::json(req); this->canonical_json = nlohmann::json(req);
body[this->toClient][this->deviceId.toStdString()] = req; body[this->toClient][this->deviceId.toStdString()] = req;
http::client() http::client()->send_to_device<mtx::events::msg::KeyVerificationStart>(
->send_to_device<mtx::events::msg::KeyVerificationStart,
mtx::events::EventType::KeyVerificationStart>(
this->transaction_id, body, [body](mtx::http::RequestErr err) { this->transaction_id, body, [body](mtx::http::RequestErr err) {
if (err) if (err)
nhlog::net()->warn( nhlog::net()->warn("failed to start verification request: {} {}",
"failed to start verification request: {} {}",
err->matrix_error.error, err->matrix_error.error,
static_cast<int>(err->status_code)); static_cast<int>(err->status_code));
}); });
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
req.relates_to = this->relation; req.relates_to = this->relation;
(model_.value())->sendMessage(req); (model_)->sendMessage(req);
} }
} }
//! sends a verification request //! sends a verification request
@ -505,17 +500,20 @@ DeviceVerificationFlow::sendVerificationRequest()
body[this->toClient][this->deviceId.toStdString()] = req; body[this->toClient][this->deviceId.toStdString()] = req;
http::client() http::client()->send_to_device<mtx::events::msg::KeyVerificationRequest>(
->send_to_device<mtx::events::msg::KeyVerificationRequest,
mtx::events::EventType::KeyVerificationRequest>(
this->transaction_id, body, [](mtx::http::RequestErr err) { this->transaction_id, body, [](mtx::http::RequestErr err) {
if (err) if (err)
nhlog::net()->warn("failed to send verification request: {} {}", nhlog::net()->warn("failed to send verification request: {} {}",
err->matrix_error.error, err->matrix_error.error,
static_cast<int>(err->status_code)); static_cast<int>(err->status_code));
}); });
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
(model_.value())->sendMessage(req); req.to = this->userId.toStdString();
req.msgtype = "m.key.verification.request";
req.body = "User is requesting to verify keys with you. However, your client does "
"not support this method, so you will need to use the legacy method of "
"key verification.";
(model_)->sendMessage(req);
} }
} }
//! cancels a verification flow //! cancels a verification flow
@ -552,21 +550,18 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c
body[this->toClient][deviceId.toStdString()] = req; body[this->toClient][deviceId.toStdString()] = req;
http::client() http::client()->send_to_device<mtx::events::msg::KeyVerificationCancel>(
->send_to_device<mtx::events::msg::KeyVerificationCancel,
mtx::events::EventType::KeyVerificationCancel>(
this->transaction_id, body, [this](mtx::http::RequestErr err) { this->transaction_id, body, [this](mtx::http::RequestErr err) {
if (err) if (err)
nhlog::net()->warn( nhlog::net()->warn("failed to cancel verification request: {} {}",
"failed to cancel verification request: {} {}",
err->matrix_error.error, err->matrix_error.error,
static_cast<int>(err->status_code)); static_cast<int>(err->status_code));
this->deleteLater(); this->deleteLater();
}); });
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
req.relates_to = this->relation; req.relates_to = this->relation;
(model_.value())->sendMessage(req); (model_)->sendMessage(req);
} }
// TODO : Handle Blocking user better // TODO : Handle Blocking user better
@ -595,18 +590,16 @@ DeviceVerificationFlow::sendVerificationKey()
body[this->toClient][deviceId.toStdString()] = req; body[this->toClient][deviceId.toStdString()] = req;
http::client() http::client()->send_to_device<mtx::events::msg::KeyVerificationKey>(
->send_to_device<mtx::events::msg::KeyVerificationKey,
mtx::events::EventType::KeyVerificationKey>(
this->transaction_id, body, [](mtx::http::RequestErr err) { this->transaction_id, body, [](mtx::http::RequestErr err) {
if (err) if (err)
nhlog::net()->warn("failed to send verification key: {} {}", nhlog::net()->warn("failed to send verification key: {} {}",
err->matrix_error.error, err->matrix_error.error,
static_cast<int>(err->status_code)); static_cast<int>(err->status_code));
}); });
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
req.relates_to = this->relation; req.relates_to = this->relation;
(model_.value())->sendMessage(req); (model_)->sendMessage(req);
} }
} }
//! sends the mac of the keys //! sends the mac of the keys
@ -639,9 +632,7 @@ DeviceVerificationFlow::sendVerificationMac()
req.transaction_id = this->transaction_id; req.transaction_id = this->transaction_id;
body[this->toClient][deviceId.toStdString()] = req; body[this->toClient][deviceId.toStdString()] = req;
http::client() http::client()->send_to_device<mtx::events::msg::KeyVerificationMac>(
->send_to_device<mtx::events::msg::KeyVerificationMac,
mtx::events::EventType::KeyVerificationMac>(
this->transaction_id, body, [this](mtx::http::RequestErr err) { this->transaction_id, body, [this](mtx::http::RequestErr err) {
if (err) if (err)
nhlog::net()->warn("failed to send verification MAC: {} {}", nhlog::net()->warn("failed to send verification MAC: {} {}",
@ -653,9 +644,9 @@ DeviceVerificationFlow::sendVerificationMac()
else else
this->isMacVerified = true; this->isMacVerified = true;
}); });
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) { } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
req.relates_to = this->relation; req.relates_to = this->relation;
(model_.value())->sendMessage(req); (model_)->sendMessage(req);
} }
} }
//! Completes the verification flow //! Completes the verification flow

View file

@ -126,6 +126,6 @@ private:
// for room messages // for room messages
std::optional<std::string> room_id; std::optional<std::string> room_id;
std::optional<std::string> event_id; std::optional<std::string> event_id;
std::optional<TimelineModel *> model_; TimelineModel *model_;
mtx::common::ReplyRelatesTo relation; mtx::common::ReplyRelatesTo relation;
}; };

View file

@ -37,8 +37,15 @@ struct EventMsgType
template<class T> template<class T>
mtx::events::MessageType operator()(const mtx::events::Event<T> &e) mtx::events::MessageType operator()(const mtx::events::Event<T> &e)
{ {
if constexpr (is_detected<msgtype_t, T>::value) if constexpr (is_detected<msgtype_t, T>::value) {
if constexpr (std::is_same_v<std::optional<std::string>,
std::remove_cv_t<decltype(e.content.msgtype)>>)
return mtx::events::getMessageType(e.content.msgtype.value());
else if constexpr (std::is_same_v<
std::string,
std::remove_cv_t<decltype(e.content.msgtype)>>)
return mtx::events::getMessageType(e.content.msgtype); return mtx::events::getMessageType(e.content.msgtype);
}
return mtx::events::MessageType::Unknown; return mtx::events::MessageType::Unknown;
} }
}; };

View file

@ -5,6 +5,7 @@
#include "Cache.h" #include "Cache.h"
#include "Cache_p.h" #include "Cache_p.h"
#include "ChatPage.h"
#include "EventAccessors.h" #include "EventAccessors.h"
#include "Logging.h" #include "Logging.h"
#include "MatrixClient.h" #include "MatrixClient.h"
@ -31,8 +32,7 @@ EventStore::EventStore(std::string room_id, QObject *)
this->last = range->last; this->last = range->last;
} }
connect( connect(this,
this,
&EventStore::eventFetched, &EventStore::eventFetched,
this, this,
[this](std::string id, [this](std::string id,
@ -48,12 +48,10 @@ EventStore::EventStore(std::string room_id, QObject *)
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
connect( connect(this,
this,
&EventStore::oldMessagesRetrieved, &EventStore::oldMessagesRetrieved,
this, this,
[this](const mtx::responses::Messages &res) { [this](const mtx::responses::Messages &res) {
//
uint64_t newFirst = cache::client()->saveOldMessages(room_id_, res); uint64_t newFirst = cache::client()->saveOldMessages(room_id_, res);
if (newFirst == first) if (newFirst == first)
fetchMore(); fetchMore();
@ -116,8 +114,7 @@ EventStore::EventStore(std::string room_id, QObject *)
event->data); event->data);
}); });
connect( connect(this,
this,
&EventStore::messageFailed, &EventStore::messageFailed,
this, this,
[this](std::string txn_id) { [this](std::string txn_id) {
@ -137,8 +134,7 @@ EventStore::EventStore(std::string room_id, QObject *)
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
connect( connect(this,
this,
&EventStore::messageSent, &EventStore::messageSent,
this, this,
[this](std::string txn_id, std::string event_id) { [this](std::string txn_id, std::string event_id) {
@ -245,6 +241,58 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
emit dataChanged(toExternalIdx(*idx), toExternalIdx(*idx)); emit dataChanged(toExternalIdx(*idx), toExternalIdx(*idx));
} }
} }
// decrypting and checking some encrypted messages
if (auto encrypted =
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
&event)) {
auto event = decryptEvent({room_id_, encrypted->event_id}, *encrypted);
if (std::visit(
[](auto e) { return (e.sender != utils::localUser().toStdString()); },
*event)) {
if (auto msg = std::get_if<mtx::events::RoomEvent<
mtx::events::msg::KeyVerificationRequest>>(event)) {
last_verification_request_event = *msg;
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
mtx::events::msg::KeyVerificationCancel>>(event)) {
last_verification_cancel_event = *msg;
ChatPage::instance()->recievedDeviceVerificationCancel(
msg->content);
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
mtx::events::msg::KeyVerificationAccept>>(event)) {
ChatPage::instance()->recievedDeviceVerificationAccept(
msg->content);
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
mtx::events::msg::KeyVerificationKey>>(event)) {
ChatPage::instance()->recievedDeviceVerificationKey(
msg->content);
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
mtx::events::msg::KeyVerificationMac>>(event)) {
ChatPage::instance()->recievedDeviceVerificationMac(
msg->content);
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
mtx::events::msg::KeyVerificationReady>>(event)) {
ChatPage::instance()->recievedDeviceVerificationReady(
msg->content);
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
mtx::events::msg::KeyVerificationDone>>(event)) {
ChatPage::instance()->recievedDeviceVerificationDone(
msg->content);
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
mtx::events::msg::KeyVerificationStart>>(event)) {
ChatPage::instance()->recievedDeviceVerificationStart(
msg->content, msg->sender);
}
}
}
}
if (last_verification_request_event.has_value()) {
if (last_verification_request_event.value().origin_server_ts >
last_verification_cancel_event.origin_server_ts) {
emit startDMVerification(last_verification_request_event.value());
last_verification_request_event = {};
}
} }
} }
@ -425,7 +473,8 @@ EventStore::decryptEvent(const IdIndex &idx,
e.what()); e.what());
dummy.content.body = dummy.content.body =
tr("-- Decryption Error (failed to retrieve megolm keys from db) --", tr("-- Decryption Error (failed to retrieve megolm keys from db) --",
"Placeholder, when the message can't be decrypted, because the DB access " "Placeholder, when the message can't be decrypted, because the DB "
"access "
"failed.") "failed.")
.toStdString(); .toStdString();
return asCacheEntry(std::move(dummy)); return asCacheEntry(std::move(dummy));
@ -437,7 +486,8 @@ EventStore::decryptEvent(const IdIndex &idx,
e.what()); e.what());
dummy.content.body = dummy.content.body =
tr("-- Decryption Error (%1) --", tr("-- Decryption Error (%1) --",
"Placeholder, when the message can't be decrypted. In this case, the Olm " "Placeholder, when the message can't be decrypted. In this case, the "
"Olm "
"decrytion returned an error, which is passed as %1.") "decrytion returned an error, which is passed as %1.")
.arg(e.what()) .arg(e.what())
.toStdString(); .toStdString();
@ -470,9 +520,9 @@ EventStore::decryptEvent(const IdIndex &idx,
return asCacheEntry(std::move(temp_events[0])); return asCacheEntry(std::move(temp_events[0]));
} }
dummy.content.body = dummy.content.body = tr("-- Encrypted Event (Unknown event type) --",
tr("-- Encrypted Event (Unknown event type) --", "Placeholder, when the message was decrypted, but we "
"Placeholder, when the message was decrypted, but we couldn't parse it, because " "couldn't parse it, because "
"Nheko/mtxclient don't support that event type yet.") "Nheko/mtxclient don't support that event type yet.")
.toStdString(); .toStdString();
return asCacheEntry(std::move(dummy)); return asCacheEntry(std::move(dummy));
@ -502,7 +552,8 @@ EventStore::get(std::string_view id, std::string_view related_to, bool decrypt)
mtx::http::RequestErr err) { mtx::http::RequestErr err) {
if (err) { if (err) {
nhlog::net()->error( nhlog::net()->error(
"Failed to retrieve event with id {}, which was " "Failed to retrieve event with id {}, which "
"was "
"requested to show the replyTo for event {}", "requested to show the replyTo for event {}",
relatedTo, relatedTo,
id); id);

View file

@ -98,6 +98,8 @@ signals:
void processPending(); void processPending();
void messageSent(std::string txn_id, std::string event_id); void messageSent(std::string txn_id, std::string event_id);
void messageFailed(std::string txn_id); void messageFailed(std::string txn_id);
void startDMVerification(
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &msg);
public slots: public slots:
void addPending(mtx::events::collections::TimelineEvents event); void addPending(mtx::events::collections::TimelineEvents event);
@ -118,4 +120,10 @@ private:
std::string current_txn; std::string current_txn;
int current_txn_error_count = 0; int current_txn_error_count = 0;
// probably not the best way to do
std::optional<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest>>
last_verification_request_event;
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel>
last_verification_cancel_event;
}; };

View file

@ -186,8 +186,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
, room_id_(room_id) , room_id_(room_id)
, manager_(manager) , manager_(manager)
{ {
connect( connect(this,
this,
&TimelineModel::redactionFailed, &TimelineModel::redactionFailed,
this, this,
[](const QString &msg) { emit ChatPage::instance()->showNotification(msg); }, [](const QString &msg) { emit ChatPage::instance()->showNotification(msg); },
@ -200,13 +199,13 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
Qt::QueuedConnection); Qt::QueuedConnection);
connect(this, &TimelineModel::addPendingMessageToStore, &events, &EventStore::addPending); connect(this, &TimelineModel::addPendingMessageToStore, &events, &EventStore::addPending);
connect( connect(&events,
&events,
&EventStore::dataChanged, &EventStore::dataChanged,
this, this,
[this](int from, int to) { [this](int from, int to) {
nhlog::ui()->debug( nhlog::ui()->debug("data changed {} to {}",
"data changed {} to {}", events.size() - to - 1, events.size() - from - 1); events.size() - to - 1,
events.size() - from - 1);
emit dataChanged(index(events.size() - to - 1, 0), emit dataChanged(index(events.size() - to - 1, 0),
index(events.size() - from - 1, 0)); index(events.size() - from - 1, 0));
}, },
@ -232,6 +231,12 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
connect(&events, &EventStore::newEncryptedImage, this, &TimelineModel::newEncryptedImage); connect(&events, &EventStore::newEncryptedImage, this, &TimelineModel::newEncryptedImage);
connect( connect(
&events, &EventStore::fetchedMore, this, [this]() { setPaginationInProgress(false); }); &events, &EventStore::fetchedMore, this, [this]() { setPaginationInProgress(false); });
connect(&events,
&EventStore::startDMVerification,
this,
[this](mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> msg) {
ChatPage::instance()->recievedRoomDeviceVerificationRequest(msg, this);
});
} }
QHash<int, QByteArray> QHash<int, QByteArray>
@ -613,187 +618,6 @@ TimelineModel::updateLastMessage()
} }
} }
std::vector<QString>
TimelineModel::internalAddEvents(
const std::vector<mtx::events::collections::TimelineEvents> &timeline)
{
std::vector<QString> ids;
for (auto e : timeline) {
QString id = QString::fromStdString(mtx::accessors::event_id(e));
if (this->events.contains(id)) {
this->events.insert(id, e);
int idx = idToIndex(id);
emit dataChanged(index(idx, 0), index(idx, 0));
continue;
}
QString txid = QString::fromStdString(mtx::accessors::transaction_id(e));
if (this->pending.removeOne(txid)) {
this->events.insert(id, e);
this->events.remove(txid);
int idx = idToIndex(txid);
if (idx < 0) {
nhlog::ui()->warn("Received index out of range");
continue;
}
eventOrder[idx] = id;
emit dataChanged(index(idx, 0), index(idx, 0));
continue;
}
if (auto redaction =
std::get_if<mtx::events::RedactionEvent<mtx::events::msg::Redaction>>(&e)) {
QString redacts = QString::fromStdString(redaction->redacts);
auto redacted = std::find(eventOrder.begin(), eventOrder.end(), redacts);
auto event = events.value(redacts);
if (auto reaction =
std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(
&event)) {
QString reactedTo =
QString::fromStdString(reaction->content.relates_to.event_id);
reactions[reactedTo].removeReaction(*reaction);
int idx = idToIndex(reactedTo);
if (idx >= 0)
emit dataChanged(index(idx, 0), index(idx, 0));
}
if (redacted != eventOrder.end()) {
auto redactedEvent = std::visit(
[](const auto &ev)
-> mtx::events::RoomEvent<mtx::events::msg::Redacted> {
mtx::events::RoomEvent<mtx::events::msg::Redacted>
replacement = {};
replacement.event_id = ev.event_id;
replacement.room_id = ev.room_id;
replacement.sender = ev.sender;
replacement.origin_server_ts = ev.origin_server_ts;
replacement.type = ev.type;
return replacement;
},
e);
events.insert(redacts, redactedEvent);
int row = (int)std::distance(eventOrder.begin(), redacted);
emit dataChanged(index(row, 0), index(row, 0));
}
continue; // don't insert redaction into timeline
}
if (auto reaction =
std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(&e)) {
QString reactedTo =
QString::fromStdString(reaction->content.relates_to.event_id);
events.insert(id, e);
// remove local echo
if (!txid.isEmpty()) {
auto rCopy = *reaction;
rCopy.event_id = txid.toStdString();
reactions[reactedTo].removeReaction(rCopy);
}
reactions[reactedTo].addReaction(room_id_.toStdString(), *reaction);
int idx = idToIndex(reactedTo);
if (idx >= 0)
emit dataChanged(index(idx, 0), index(idx, 0));
continue; // don't insert reaction into timeline
}
if (auto event =
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&e)) {
auto e_ = decryptEvent(*event).event;
auto encInfo = mtx::accessors::file(e_);
if (encInfo)
emit newEncryptedImage(encInfo.value());
if (auto msg = std::get_if<
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest>>(
&e_)) {
last_verification_request_event = *msg;
}
if (auto msg = std::get_if<
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel>>(
&e_)) {
last_verification_cancel_event = *msg;
ChatPage::instance()->recievedDeviceVerificationCancel(
msg->content);
}
if (auto msg = std::get_if<
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationAccept>>(
&e_)) {
ChatPage::instance()->recievedDeviceVerificationAccept(
msg->content);
}
if (auto msg = std::get_if<
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationKey>>(&e_)) {
ChatPage::instance()->recievedDeviceVerificationKey(msg->content);
}
if (auto msg = std::get_if<
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationMac>>(&e_)) {
ChatPage::instance()->recievedDeviceVerificationMac(msg->content);
}
if (auto msg = std::get_if<
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationReady>>(
&e_)) {
ChatPage::instance()->recievedDeviceVerificationReady(msg->content);
}
if (auto msg = std::get_if<
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationDone>>(&e_)) {
ChatPage::instance()->recievedDeviceVerificationDone(msg->content);
}
if (auto msg = std::get_if<
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationStart>>(
&e_)) {
ChatPage::instance()->recievedDeviceVerificationStart(msg->content,
msg->sender);
}
}
this->events.insert(id, e);
ids.push_back(id);
auto replyTo = mtx::accessors::in_reply_to_event(e);
auto qReplyTo = QString::fromStdString(replyTo);
if (!replyTo.empty() && !events.contains(qReplyTo)) {
http::client()->get_event(
this->room_id_.toStdString(),
replyTo,
[this, id, replyTo](
const mtx::events::collections::TimelineEvents &timeline,
mtx::http::RequestErr err) {
if (err) {
nhlog::net()->error(
"Failed to retrieve event with id {}, which was "
"requested to show the replyTo for event {}",
replyTo,
id.toStdString());
return;
}
emit eventFetched(id, timeline);
});
}
}
if (last_verification_request_event.origin_server_ts >
last_verification_cancel_event.origin_server_ts) {
ChatPage::instance()->recievedRoomDeviceVerificationRequest(
last_verification_request_event, this);
}
return ids;
}
void void
TimelineModel::setCurrentIndex(int index) TimelineModel::setCurrentIndex(int index)
{ {
@ -979,15 +803,18 @@ TimelineModel::markEventsAsRead(const std::vector<QString> &event_ids)
} }
} }
template<typename T>
void void
TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json content) TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent<T> msg)
{ {
const auto room_id = room_id_.toStdString(); const auto room_id = room_id_.toStdString();
using namespace mtx::events; using namespace mtx::events;
using namespace mtx::identifiers; using namespace mtx::identifiers;
json doc = {{"type", "m.room.message"}, {"content", content}, {"room_id", room_id}}; json doc = {
{"type", to_string(msg.type)}, {"content", json(msg.content)}, {"room_id", room_id}};
std::cout << doc.dump(2) << std::endl;
try { try {
// Check if we have already an outbound megolm session then we can use. // Check if we have already an outbound megolm session then we can use.
@ -995,7 +822,7 @@ TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json con
mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event; mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event;
event.content = event.content =
olm::encrypt_group_message(room_id, http::client()->device_id(), doc); olm::encrypt_group_message(room_id, http::client()->device_id(), doc);
event.event_id = txn_id; event.event_id = msg.event_id;
event.room_id = room_id; event.room_id = room_id;
event.sender = http::client()->user_id().to_string(); event.sender = http::client()->user_id().to_string();
event.type = mtx::events::EventType::RoomEncrypted; event.type = mtx::events::EventType::RoomEncrypted;
@ -1030,7 +857,8 @@ TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json con
const auto members = cache::roomMembers(room_id); const auto members = cache::roomMembers(room_id);
nhlog::ui()->info("retrieved {} members for {}", members.size(), room_id); nhlog::ui()->info("retrieved {} members for {}", members.size(), room_id);
auto keeper = std::make_shared<StateKeeper>([room_id, doc, txn_id, this]() { auto keeper =
std::make_shared<StateKeeper>([room_id, doc, txn_id = msg.event_id, this]() {
try { try {
mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event; mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event;
event.content = olm::encrypt_group_message( event.content = olm::encrypt_group_message(
@ -1043,8 +871,8 @@ TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json con
emit this->addPendingMessageToStore(event); emit this->addPendingMessageToStore(event);
} catch (const lmdb::error &e) { } catch (const lmdb::error &e) {
nhlog::db()->critical("failed to save megolm outbound session: {}", nhlog::db()->critical(
e.what()); "failed to save megolm outbound session: {}", e.what());
emit ChatPage::instance()->showNotification( emit ChatPage::instance()->showNotification(
tr("Failed to encrypt event, sending aborted!")); tr("Failed to encrypt event, sending aborted!"));
} }
@ -1056,7 +884,7 @@ TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json con
http::client()->query_keys( http::client()->query_keys(
req, req,
[keeper = std::move(keeper), megolm_payload, txn_id, this]( [keeper = std::move(keeper), megolm_payload, txn_id = msg.event_id, this](
const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) {
if (err) { if (err) {
nhlog::net()->warn("failed to query device keys: {} {}", nhlog::net()->warn("failed to query device keys: {} {}",
@ -1265,6 +1093,40 @@ struct SendMessageVisitor
: model_(model) : model_(model)
{} {}
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &msg)
{
emit model_->updateFlowEventId(msg.event_id);
model_->sendEncryptedMessage(msg);
}
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationReady> &msg)
{
model_->sendEncryptedMessage(msg);
}
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationStart> &msg)
{
model_->sendEncryptedMessage(msg);
}
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationAccept> &msg)
{
model_->sendEncryptedMessage(msg);
}
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationMac> &msg)
{
model_->sendEncryptedMessage(msg);
}
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationKey> &msg)
{
model_->sendEncryptedMessage(msg);
}
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationDone> &msg)
{
model_->sendEncryptedMessage(msg);
}
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel> &msg)
{
model_->sendEncryptedMessage(msg);
}
// Do-nothing operator for all unhandled events // Do-nothing operator for all unhandled events
template<typename T> template<typename T>
void operator()(const mtx::events::Event<T> &) void operator()(const mtx::events::Event<T> &)
@ -1280,7 +1142,7 @@ struct SendMessageVisitor
if (encInfo) if (encInfo)
emit model_->newEncryptedImage(encInfo.value()); emit model_->newEncryptedImage(encInfo.value());
model_->sendEncryptedMessage(msg.event_id, nlohmann::json(msg.content)); model_->sendEncryptedMessage(msg);
} else { } else {
emit model_->addPendingMessageToStore(msg); emit model_->addPendingMessageToStore(msg);
} }
@ -1300,20 +1162,6 @@ struct SendMessageVisitor
TimelineModel *model_; TimelineModel *model_;
}; };
void
TimelineModel::processOnePendingMessage()
{
if (pending.isEmpty())
return;
QString txn_id_qstr = pending.first();
auto event = events.value(txn_id_qstr);
std::cout << "Inside the process one pending message" << std::endl;
std::cout << std::visit([](auto &e) { return json(e); }, event).dump(2) << std::endl;
std::visit(SendMessageVisitor{txn_id_qstr, this}, event);
}
void void
TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event) TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event)
{ {
@ -1359,18 +1207,7 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event)
event); event);
} }
internalAddEvents({event}); std::visit(SendMessageVisitor{this}, event);
QString txn_id_qstr = QString::fromStdString(mtx::accessors::event_id(event));
pending.push_back(txn_id_qstr);
if (!std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(&event)) {
beginInsertRows(QModelIndex(), 0, 0);
this->eventOrder.insert(this->eventOrder.begin(), txn_id_qstr);
endInsertRows();
}
updateLastMessage();
emit nextPendingMessage();
} }
bool bool

View file

@ -9,12 +9,8 @@
#include <mtxclient/http/errors.hpp> #include <mtxclient/http/errors.hpp>
#include "CacheCryptoStructs.h" #include "CacheCryptoStructs.h"
<<<<<<< HEAD
#include "EventStore.h" #include "EventStore.h"
=======
#include "ReactionsModel.h"
#include "ui/UserProfile.h" #include "ui/UserProfile.h"
>>>>>>> Refactor UserProfile
namespace mtx::http { namespace mtx::http {
using RequestErr = const std::optional<mtx::http::ClientError> &; using RequestErr = const std::optional<mtx::http::ClientError> &;
@ -271,8 +267,13 @@ signals:
void openProfile(UserProfile *profile); void openProfile(UserProfile *profile);
void newMessageToSend(mtx::events::collections::TimelineEvents event);
void addPendingMessageToStore(mtx::events::collections::TimelineEvents event);
void updateFlowEventId(std::string event_id);
private: private:
void sendEncryptedMessage(const std::string txn_id, nlohmann::json content); template<typename T>
void sendEncryptedMessage(mtx::events::RoomEvent<T> msg);
void handleClaimedKeys(std::shared_ptr<StateKeeper> keeper, void handleClaimedKeys(std::shared_ptr<StateKeeper> keeper,
const std::map<std::string, std::string> &room_key, const std::map<std::string, std::string> &room_key,
const std::map<std::string, DevicePublicKeys> &pks, const std::map<std::string, DevicePublicKeys> &pks,
@ -297,11 +298,6 @@ private:
std::vector<QString> typingUsers_; std::vector<QString> typingUsers_;
TimelineViewManager *manager_; TimelineViewManager *manager_;
// probably not the best way to do
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest>
last_verification_request_event;
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel>
last_verification_cancel_event;
friend struct SendMessageVisitor; friend struct SendMessageVisitor;
}; };

View file

@ -5,13 +5,15 @@
#include "Logging.h" #include "Logging.h"
#include "Utils.h" #include "Utils.h"
#include "mtx/responses/crypto.hpp" #include "mtx/responses/crypto.hpp"
#include "timeline/TimelineModel.h"
#include <iostream> // only for debugging #include <iostream> // only for debugging
UserProfile::UserProfile(QString roomid, QString userid, QObject *parent) UserProfile::UserProfile(QString roomid, QString userid, TimelineModel *parent)
: QObject(parent) : QObject(parent)
, roomid_(roomid) , roomid_(roomid)
, userid_(userid) , userid_(userid)
, model(parent)
{ {
fetchDeviceList(this->userid_); fetchDeviceList(this->userid_);
} }
@ -185,10 +187,13 @@ UserProfile::startChat()
emit ChatPage::instance()->createRoom(req); emit ChatPage::instance()->createRoom(req);
} }
void DeviceVerificationFlow *
UserProfile::verifyUser() UserProfile::createFlow(bool isVerifyUser)
{ {
std::cout << "Checking if to start to device verification or room message verification" if (!isVerifyUser)
return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice));
else {
std::cout << "CHECKING IF IT TO START ROOM_VERIFICATION OR TO_DEVICE VERIFICATION"
<< std::endl; << std::endl;
auto joined_rooms = cache::joinedRooms(); auto joined_rooms = cache::joinedRooms();
auto room_infos = cache::getRoomInfo(joined_rooms); auto room_infos = cache::getRoomInfo(joined_rooms);
@ -199,13 +204,26 @@ UserProfile::verifyUser()
auto room_members = cache::roomMembers(room_id); auto room_members = cache::roomMembers(room_id);
if (std::find(room_members.begin(), if (std::find(room_members.begin(),
room_members.end(), room_members.end(),
(this->userid()).toStdString()) != room_members.end()) { (this->userid()).toStdString()) !=
std::cout << "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id room_members.end()) {
std::cout
<< "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id
<< std::endl; << std::endl;
return; if (this->roomid_.toStdString() == room_id) {
auto newflow = new DeviceVerificationFlow(
this, DeviceVerificationFlow::Type::RoomMsg);
newflow->setModel(this->model);
return (std::move(newflow));
} else {
std::cout << "FOUND A ENCRYPTED ROOM BUT CURRENTLY "
"NOT IN THAT ROOM : "
<< room_id << std::endl;
}
} }
} }
} }
std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl; std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl;
return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice));
}
} }

View file

@ -20,6 +20,7 @@ Q_ENUM_NS(Status)
} }
class DeviceVerificationFlow; class DeviceVerificationFlow;
class TimelineModel;
class DeviceInfo class DeviceInfo
{ {
@ -83,7 +84,7 @@ class UserProfile : public QObject
Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT) Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT)
Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT) Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT)
public: public:
UserProfile(QString roomid, QString userid, QObject *parent = 0); UserProfile(QString roomid, QString userid, TimelineModel *parent = nullptr);
DeviceInfoModel *deviceList(); DeviceInfoModel *deviceList();
@ -92,18 +93,19 @@ public:
QString avatarUrl(); QString avatarUrl();
bool getUserStatus(); bool getUserStatus();
Q_INVOKABLE DeviceVerificationFlow *createFlow(bool isVerifyUser);
Q_INVOKABLE void fetchDeviceList(const QString &userID); Q_INVOKABLE void fetchDeviceList(const QString &userID);
Q_INVOKABLE void banUser(); Q_INVOKABLE void banUser();
// Q_INVOKABLE void ignoreUser(); // Q_INVOKABLE void ignoreUser();
Q_INVOKABLE void kickUser(); Q_INVOKABLE void kickUser();
Q_INVOKABLE void startChat(); Q_INVOKABLE void startChat();
Q_INVOKABLE void verifyUser();
private: private:
QString roomid_, userid_; QString roomid_, userid_;
std::optional<std::string> cross_verified; std::optional<std::string> cross_verified;
DeviceInfoModel deviceList_; DeviceInfoModel deviceList_;
bool isUserVerified = false; bool isUserVerified = false;
TimelineModel *model;
void callback_fn(const mtx::responses::QueryKeys &res, void callback_fn(const mtx::responses::QueryKeys &res,
mtx::http::RequestErr err, mtx::http::RequestErr err,