summary refs log tree commit diff
path: root/src/timeline
diff options
context:
space:
mode:
authortrilene <trilene@runbox.com>2020-10-27 15:29:53 -0400
committertrilene <trilene@runbox.com>2020-10-27 15:29:53 -0400
commitd9ca5309ac0e5f25e0af6cc75a9b31e79a33d4d7 (patch)
treeecf31e7dde13cae5c4c24b256542b77dd31f6d79 /src/timeline
parentSupport video calls (diff)
parentTranslated using Weblate (English) (diff)
downloadnheko-d9ca5309ac0e5f25e0af6cc75a9b31e79a33d4d7.tar.xz
Merge remote-tracking branch 'upstream/master' into webrtc-video
Diffstat (limited to 'src/timeline')
-rw-r--r--src/timeline/EventStore.cpp60
-rw-r--r--src/timeline/EventStore.h9
-rw-r--r--src/timeline/TimelineModel.cpp222
-rw-r--r--src/timeline/TimelineModel.h10
-rw-r--r--src/timeline/TimelineViewManager.cpp32
-rw-r--r--src/timeline/TimelineViewManager.h6
6 files changed, 106 insertions, 233 deletions
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp

index d3c5c3fa..38292f49 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp
@@ -54,6 +54,12 @@ EventStore::EventStore(std::string room_id, QObject *) &EventStore::oldMessagesRetrieved, this, [this](const mtx::responses::Messages &res) { + if (cache::client()->previousBatchToken(room_id_) == res.end) { + noMoreMessages = true; + emit fetchedMore(); + return; + } + uint64_t newFirst = cache::client()->saveOldMessages(room_id_, res); if (newFirst == first) fetchMore(); @@ -210,6 +216,28 @@ EventStore::clearTimeline() } void +EventStore::receivedSessionKey(const std::string &session_id) +{ + if (!pending_key_requests.count(session_id)) + return; + + auto request = pending_key_requests.at(session_id); + pending_key_requests.erase(session_id); + + olm::send_key_request_for(request.events.front(), request.request_id, true); + + for (const auto &e : request.events) { + auto idx = idToIndex(e.event_id); + if (idx) { + decryptedEvents_.remove({room_id_, e.event_id}); + events_by_id_.remove({room_id_, e.event_id}); + events_.remove({room_id_, toInternalIdx(*idx)}); + emit dataChanged(*idx, *idx); + } + } +} + +void EventStore::handleSync(const mtx::responses::Timeline &events) { if (this->thread() != QThread::currentThread()) @@ -291,18 +319,6 @@ EventStore::handleSync(const mtx::responses::Timeline &events) *d_event)) { handle_room_verification(*d_event); } - // else { - // // only the key.verification.ready sent by localuser's other - // device - // // is of significance as it is used for detecting accepted request - // if (std::get_if<mtx::events::RoomEvent< - // mtx::events::msg::KeyVerificationReady>>(d_event)) { - // auto msg = std::get_if<mtx::events::RoomEvent< - // mtx::events::msg::KeyVerificationReady>>(d_event); - // ChatPage::instance()->receivedDeviceVerificationReady( - // msg->content); - // } - //} } } } @@ -498,7 +514,7 @@ EventStore::decryptEvent(const IdIndex &idx, if (decryptionResult.error) { switch (*decryptionResult.error) { - case olm::DecryptionErrorCode::MissingSession: + case olm::DecryptionErrorCode::MissingSession: { dummy.content.body = tr("-- Encrypted Event (No keys found for decryption) --", "Placeholder, when the message was not decrypted yet or can't be " @@ -509,8 +525,21 @@ EventStore::decryptEvent(const IdIndex &idx, index.session_id, e.sender); // TODO: Check if this actually works and look in key backup - olm::send_key_request_for(room_id_, e); + auto copy = e; + copy.room_id = room_id_; + if (pending_key_requests.count(e.content.session_id)) { + pending_key_requests.at(e.content.session_id) + .events.push_back(copy); + } else { + PendingKeyRequests request; + request.request_id = + "key_request." + http::client()->generate_txn_id(); + request.events.push_back(copy); + olm::send_key_request_for(copy, request.request_id); + pending_key_requests[e.content.session_id] = request; + } break; + } case olm::DecryptionErrorCode::DbError: nhlog::db()->critical( "failed to retrieve megolm session with index ({}, {}, {})", @@ -687,6 +716,9 @@ EventStore::get(std::string_view id, std::string_view related_to, bool decrypt) void EventStore::fetchMore() { + if (noMoreMessages) + return; + mtx::http::MessagesOpts opts; opts.room_id = room_id_; opts.from = cache::client()->previousBatchToken(room_id_); diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h
index 954e271c..2d5fb1be 100644 --- a/src/timeline/EventStore.h +++ b/src/timeline/EventStore.h
@@ -104,6 +104,7 @@ signals: public slots: void addPending(mtx::events::collections::TimelineEvents event); + void receivedSessionKey(const std::string &session_id); void clearTimeline(); private: @@ -121,6 +122,14 @@ private: static QCache<Index, mtx::events::collections::TimelineEvents> events_; static QCache<IdIndex, mtx::events::collections::TimelineEvents> events_by_id_; + struct PendingKeyRequests + { + std::string request_id; + std::vector<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>> events; + }; + std::map<std::string, PendingKeyRequests> pending_key_requests; + std::string current_txn; int current_txn_error_count = 0; + bool noMoreMessages = false; }; diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 359e95bc..8b80ea51 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp
@@ -930,11 +930,12 @@ TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent<T> msg, mtx::events:: const auto session_id = mtx::crypto::session_id(outbound_session.get()); const auto session_key = mtx::crypto::session_key(outbound_session.get()); - // TODO: needs to be moved in the lib. - auto megolm_payload = json{{"algorithm", "m.megolm.v1.aes-sha2"}, - {"room_id", room_id}, - {"session_id", session_id}, - {"session_key", session_key}}; + mtx::events::DeviceEvent<mtx::events::msg::RoomKey> megolm_payload; + megolm_payload.content.algorithm = "m.megolm.v1.aes-sha2"; + megolm_payload.content.room_id = room_id; + megolm_payload.content.session_id = session_id; + megolm_payload.content.session_key = session_key; + megolm_payload.type = mtx::events::EventType::RoomKey; // Saving the new megolm session. // TODO: Maybe it's too early to save. @@ -958,122 +959,29 @@ TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent<T> msg, mtx::events:: const auto members = cache::roomMembers(room_id); nhlog::ui()->info("retrieved {} members for {}", members.size(), room_id); - auto keeper = - std::make_shared<StateKeeper>([room_id, doc, txn_id = msg.event_id, this]() { - try { - mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event; - event.content = olm::encrypt_group_message( - room_id, http::client()->device_id(), doc); - event.event_id = txn_id; - event.room_id = room_id; - event.sender = http::client()->user_id().to_string(); - event.type = mtx::events::EventType::RoomEncrypted; - event.origin_server_ts = QDateTime::currentMSecsSinceEpoch(); - - emit this->addPendingMessageToStore(event); - } catch (const lmdb::error &e) { - nhlog::db()->critical( - "failed to save megolm outbound session: {}", e.what()); - emit ChatPage::instance()->showNotification( - tr("Failed to encrypt event, sending aborted!")); - } - }); - - mtx::requests::QueryKeys req; + std::map<std::string, std::vector<std::string>> targets; for (const auto &member : members) - req.device_keys[member] = {}; + targets[member] = {}; - http::client()->query_keys( - req, - [keeper = std::move(keeper), megolm_payload, txn_id = msg.event_id, this]( - const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to query device keys: {} {}", - err->matrix_error.error, - static_cast<int>(err->status_code)); - emit ChatPage::instance()->showNotification( - tr("Failed to encrypt event, sending aborted!")); - return; - } + olm::send_encrypted_to_device_messages(targets, megolm_payload); - mtx::requests::ClaimKeys claim_keys; - - // Mapping from user id to a device_id with valid identity keys to the - // generated room_key event used for sharing the megolm session. - std::map<std::string, std::map<std::string, std::string>> room_key_msgs; - std::map<std::string, std::map<std::string, DevicePublicKeys>> deviceKeys; - - for (const auto &user : res.device_keys) { - for (const auto &dev : user.second) { - const auto user_id = ::UserId(dev.second.user_id); - const auto device_id = DeviceId(dev.second.device_id); - - if (user_id.get() == - http::client()->user_id().to_string() && - device_id.get() == http::client()->device_id()) - continue; - - const auto device_keys = dev.second.keys; - const auto curveKey = "curve25519:" + device_id.get(); - const auto edKey = "ed25519:" + device_id.get(); - - if ((device_keys.find(curveKey) == device_keys.end()) || - (device_keys.find(edKey) == device_keys.end())) { - nhlog::net()->debug( - "ignoring malformed keys for device {}", - device_id.get()); - continue; - } - - DevicePublicKeys pks; - pks.ed25519 = device_keys.at(edKey); - pks.curve25519 = device_keys.at(curveKey); - - try { - if (!mtx::crypto::verify_identity_signature( - dev.second, device_id, user_id)) { - nhlog::crypto()->warn( - "failed to verify identity keys: {}", - json(dev.second).dump(2)); - continue; - } - } catch (const json::exception &e) { - nhlog::crypto()->warn( - "failed to parse device key json: {}", - e.what()); - continue; - } catch (const mtx::crypto::olm_exception &e) { - nhlog::crypto()->warn( - "failed to verify device key json: {}", - e.what()); - continue; - } - - auto room_key = olm::client() - ->create_room_key_event( - user_id, pks.ed25519, megolm_payload) - .dump(); - - room_key_msgs[user_id].emplace(device_id, room_key); - deviceKeys[user_id].emplace(device_id, pks); - claim_keys.one_time_keys[user.first][device_id] = - mtx::crypto::SIGNED_CURVE25519; - - nhlog::net()->info("{}", device_id.get()); - nhlog::net()->info(" curve25519 {}", pks.curve25519); - nhlog::net()->info(" ed25519 {}", pks.ed25519); - } - } + try { + mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event; + event.content = + olm::encrypt_group_message(room_id, http::client()->device_id(), doc); + event.event_id = msg.event_id; + event.room_id = room_id; + event.sender = http::client()->user_id().to_string(); + event.type = mtx::events::EventType::RoomEncrypted; + event.origin_server_ts = QDateTime::currentMSecsSinceEpoch(); - http::client()->claim_keys(claim_keys, - std::bind(&TimelineModel::handleClaimedKeys, - this, - keeper, - room_key_msgs, - deviceKeys, - std::placeholders::_1, - std::placeholders::_2)); - }); + emit this->addPendingMessageToStore(event); + } catch (const lmdb::error &e) { + nhlog::db()->critical("failed to save megolm outbound session: {}", + e.what()); + emit ChatPage::instance()->showNotification( + tr("Failed to encrypt event, sending aborted!")); + } // TODO: Let the user know about the errors. } catch (const lmdb::error &e) { @@ -1089,86 +997,6 @@ TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent<T> msg, mtx::events:: } } -void -TimelineModel::handleClaimedKeys( - std::shared_ptr<StateKeeper> keeper, - const std::map<std::string, std::map<std::string, std::string>> &room_keys, - const std::map<std::string, std::map<std::string, DevicePublicKeys>> &pks, - const mtx::responses::ClaimKeys &res, - mtx::http::RequestErr err) -{ - if (err) { - nhlog::net()->warn("claim keys error: {} {} {}", - err->matrix_error.error, - err->parse_error, - static_cast<int>(err->status_code)); - return; - } - - // Payload with all the to_device message to be sent. - nlohmann::json body; - - for (const auto &[user_id, retrieved_devices] : res.one_time_keys) { - nhlog::net()->debug("claimed keys for {}", user_id); - if (retrieved_devices.size() == 0) { - nhlog::net()->debug("no one-time keys found for user_id: {}", user_id); - return; - } - - for (const auto &rd : retrieved_devices) { - const auto device_id = rd.first; - - nhlog::net()->debug("{} : \n {}", device_id, rd.second.dump(2)); - - if (rd.second.empty() || !rd.second.begin()->contains("key")) { - nhlog::net()->warn("Skipping device {} as it has no key.", - device_id); - continue; - } - - // TODO: Verify signatures - auto otk = rd.second.begin()->at("key"); - - auto id_key = pks.at(user_id).at(device_id).curve25519; - auto s = olm::client()->create_outbound_session(id_key, otk); - - auto device_msg = olm::client()->create_olm_encrypted_content( - s.get(), - room_keys.at(user_id).at(device_id), - pks.at(user_id).at(device_id).curve25519); - - try { - cache::saveOlmSession(id_key, std::move(s)); - } catch (const lmdb::error &e) { - nhlog::db()->critical("failed to save outbound olm session: {}", - e.what()); - } catch (const mtx::crypto::olm_exception &e) { - nhlog::crypto()->critical( - "failed to pickle outbound olm session: {}", e.what()); - } - - body["messages"][user_id][device_id] = device_msg; - } - - nhlog::net()->info("send_to_device: {}", user_id); - } - - http::client()->send_to_device( - mtx::events::to_string(mtx::events::EventType::RoomEncrypted), - http::client()->generate_txn_id(), - body, - [keeper](mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to send " - "send_to_device " - "message: {}", - err->matrix_error.error); - } - - (void)keeper; - }); -} - struct SendMessageVisitor { explicit SendMessageVisitor(TimelineModel *model) diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 3234a20c..e1fb9196 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h
@@ -264,6 +264,10 @@ public slots: } void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; } void clearTimeline() { events.clearTimeline(); } + void receivedSessionKey(const std::string &session_key) + { + events.receivedSessionKey(session_key); + } QString roomName() const; QString roomTopic() const; @@ -297,12 +301,6 @@ signals: private: template<typename T> void sendEncryptedMessage(mtx::events::RoomEvent<T> msg, mtx::events::EventType eventType); - void handleClaimedKeys( - std::shared_ptr<StateKeeper> keeper, - const std::map<std::string, std::map<std::string, std::string>> &room_keys, - const std::map<std::string, std::map<std::string, DevicePublicKeys>> &pks, - const mtx::responses::ClaimKeys &res, - mtx::http::RequestErr err); void readEvent(const std::string &id); void setPaginationInProgress(const bool paginationInProgress); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 353f7065..598af31e 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp
@@ -30,7 +30,7 @@ namespace msgs = mtx::events::msg; void TimelineViewManager::updateEncryptedDescriptions() { - auto decrypt = settings->decryptSidebar(); + auto decrypt = ChatPage::instance()->userSettings()->decryptSidebar(); QHash<QString, QSharedPointer<TimelineModel>>::iterator i; for (i = models.begin(); i != models.end(); ++i) { auto ptr = i.value(); @@ -47,10 +47,10 @@ TimelineViewManager::updateColorPalette() { userColors.clear(); - if (settings->theme() == "light") { + if (ChatPage::instance()->userSettings()->theme() == "light") { view->rootContext()->setContextProperty("currentActivePalette", QPalette()); view->rootContext()->setContextProperty("currentInactivePalette", QPalette()); - } else if (settings->theme() == "dark") { + } else if (ChatPage::instance()->userSettings()->theme() == "dark") { view->rootContext()->setContextProperty("currentActivePalette", QPalette()); view->rootContext()->setContextProperty("currentInactivePalette", QPalette()); } else { @@ -84,14 +84,11 @@ TimelineViewManager::userStatus(QString id) const return QString::fromStdString(cache::statusMessage(id.toStdString())); } -TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettings, - CallManager *callManager, - ChatPage *parent) +TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *parent) : imgProvider(new MxcImageProvider()) , colorImgProvider(new ColorImageProvider()) , blurhashProvider(new BlurhashProvider()) , callManager_(callManager) - , settings(userSettings) { qRegisterMetaType<mtx::events::msg::KeyVerificationAccept>(); qRegisterMetaType<mtx::events::msg::KeyVerificationCancel>(); @@ -133,7 +130,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettin }); qmlRegisterSingletonType<UserSettings>( "im.nheko", 1, 0, "Settings", [](QQmlEngine *, QJSEngine *) -> QObject * { - return self->settings.data(); + return ChatPage::instance()->userSettings().data(); }); qRegisterMetaType<mtx::events::collections::TimelineEvents>(); @@ -295,7 +292,8 @@ TimelineViewManager::addRoom(const QString &room_id) { if (!models.contains(room_id)) { QSharedPointer<TimelineModel> newRoom(new TimelineModel(this, room_id)); - newRoom->setDecryptDescription(settings->decryptSidebar()); + newRoom->setDecryptDescription( + ChatPage::instance()->userSettings()->decryptSidebar()); connect(newRoom.data(), &TimelineModel::newEncryptedImage, @@ -454,6 +452,15 @@ TimelineViewManager::updateReadReceipts(const QString &room_id, } void +TimelineViewManager::receivedSessionKey(const std::string &room_id, const std::string &session_id) +{ + auto room = models.find(QString::fromStdString(room_id)); + if (room != models.end()) { + room.value()->receivedSessionKey(session_id); + } +} + +void TimelineViewManager::initWithMessages(const std::map<QString, mtx::responses::Timeline> &msgs) { for (const auto &e : msgs) { @@ -472,7 +479,7 @@ TimelineViewManager::queueTextMessage(const QString &msg) mtx::events::msg::Text text = {}; text.body = msg.trimmed().toStdString(); - if (settings->markdown()) { + if (ChatPage::instance()->userSettings()->markdown()) { text.formatted_body = utils::markdownToHtml(msg).toStdString(); // Don't send formatted_body, when we don't need to @@ -500,7 +507,7 @@ TimelineViewManager::queueTextMessage(const QString &msg) // NOTE(Nico): rich replies always need a formatted_body! text.format = "org.matrix.custom.html"; - if (settings->markdown()) + if (ChatPage::instance()->userSettings()->markdown()) text.formatted_body = utils::getFormattedQuoteBody(related, utils::markdownToHtml(msg)) .toStdString(); @@ -523,7 +530,8 @@ TimelineViewManager::queueEmoteMessage(const QString &msg) mtx::events::msg::Emote emote; emote.body = msg.trimmed().toStdString(); - if (html != msg.trimmed().toHtmlEscaped() && settings->markdown()) { + if (html != msg.trimmed().toHtmlEscaped() && + ChatPage::instance()->userSettings()->markdown()) { emote.formatted_body = html.toStdString(); emote.format = "org.matrix.custom.html"; } diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 1a2d4c4e..895c4b39 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h
@@ -42,9 +42,7 @@ class TimelineViewManager : public QObject Q_PROPERTY(bool isMicMuted READ isMicMuted NOTIFY micMuteChanged) public: - TimelineViewManager(QSharedPointer<UserSettings> userSettings, - CallManager *callManager, - ChatPage *parent = nullptr); + TimelineViewManager(CallManager *callManager, ChatPage *parent = nullptr); QWidget *getWidget() const { return container; } void sync(const mtx::responses::Rooms &rooms); @@ -98,6 +96,7 @@ signals: public slots: void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids); + void receivedSessionKey(const std::string &room_id, const std::string &session_id); void initWithMessages(const std::map<QString, mtx::responses::Timeline> &msgs); void setHistoryView(const QString &room_id); @@ -180,7 +179,6 @@ private: bool isInitialSync_ = true; bool isNarrowView_ = false; - QSharedPointer<UserSettings> settings; QHash<QString, QColor> userColors; QHash<QString, QSharedPointer<DeviceVerificationFlow>> dvList;