diff options
author | Konstantinos Sideris <sideris.konstantin@gmail.com> | 2018-06-10 20:03:45 +0300 |
---|---|---|
committer | Konstantinos Sideris <sideris.konstantin@gmail.com> | 2018-06-10 20:03:45 +0300 |
commit | 626c68091126f84819091840a011c50e26dcbd8d (patch) | |
tree | 132afdb12f858a6a2a20e00e0c2a0417caf4ae13 /src/ChatPage.cc | |
parent | Migrate to mtxclient for the http calls (diff) | |
download | nheko-626c68091126f84819091840a011c50e26dcbd8d.tar.xz |
Add support for displaying decrypted messages
Diffstat (limited to 'src/ChatPage.cc')
-rw-r--r-- | src/ChatPage.cc | 199 |
1 files changed, 158 insertions, 41 deletions
diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 64ce69d6..a5a6a8c0 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -25,6 +25,7 @@ #include "Logging.hpp" #include "MainWindow.h" #include "MatrixClient.h" +#include "Olm.hpp" #include "OverlayModal.h" #include "QuickSwitcher.h" #include "RoomList.h" @@ -43,8 +44,12 @@ #include "dialogs/ReadReceipts.h" #include "timeline/TimelineViewManager.h" +// TODO: Needs to be updated with an actual secret. +static const std::string STORAGE_SECRET_KEY("secret"); + ChatPage *ChatPage::instance_ = nullptr; constexpr int CHECK_CONNECTIVITY_INTERVAL = 15'000; +constexpr size_t MAX_ONETIME_KEYS = 50; ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) : QWidget(parent) @@ -612,6 +617,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) connect(this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync); connect(this, &ChatPage::trySyncCb, this, &ChatPage::trySync); + connect(this, &ChatPage::tryDelayedSyncCb, this, [this]() { + QTimer::singleShot(5000, this, &ChatPage::trySync); + }); connect(this, &ChatPage::dropToLoginPageCb, this, &ChatPage::dropToLoginPage); @@ -728,6 +736,11 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) }); // TODO http::client()->getOwnCommunities(); + // The Olm client needs the user_id & device_id that will be included + // in the generated payloads & keys. + olm::client()->set_user_id(http::v2::client()->user_id().to_string()); + olm::client()->set_device_id(http::v2::client()->device_id()); + cache::init(userid); try { @@ -741,6 +754,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) if (cache::client()->isInitialized()) { loadStateFromCache(); + // TODO: Bootstrap olm client with saved data. return; } } catch (const lmdb::error &e) { @@ -749,6 +763,22 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) log::net()->info("falling back to initial sync"); } + try { + // It's the first time syncing with this device + // There isn't a saved olm account to restore. + log::crypto()->info("creating new olm account"); + olm::client()->create_new_account(); + cache::client()->saveOlmAccount(olm::client()->save(STORAGE_SECRET_KEY)); + } catch (const lmdb::error &e) { + log::crypto()->critical("failed to save olm account {}", e.what()); + emit dropToLoginPageCb(QString::fromStdString(e.what())); + return; + } catch (const mtx::crypto::olm_exception &e) { + log::crypto()->critical("failed to create new olm account {}", e.what()); + emit dropToLoginPageCb(QString::fromStdString(e.what())); + return; + } + tryInitialSync(); } @@ -826,16 +856,29 @@ ChatPage::loadStateFromCache() QtConcurrent::run([this]() { try { + cache::client()->restoreSessions(); + olm::client()->load(cache::client()->restoreOlmAccount(), + STORAGE_SECRET_KEY); + cache::client()->populateMembers(); emit initializeEmptyViews(cache::client()->joinedRooms()); emit initializeRoomList(cache::client()->roomInfo()); + } catch (const mtx::crypto::olm_exception &e) { + log::crypto()->critical("failed to restore olm account: {}", e.what()); + emit dropToLoginPageCb( + tr("Failed to restore OLM account. Please login again.")); + return; } catch (const lmdb::error &e) { - std::cout << "load cache error:" << e.what() << '\n'; - // TODO Clear cache and restart. + log::db()->critical("failed to restore cache: {}", e.what()); + emit dropToLoginPageCb( + tr("Failed to restore save data. Please login again.")); return; } + log::crypto()->info("ed25519 : {}", olm::client()->identity_keys().ed25519); + log::crypto()->info("curve25519: {}", olm::client()->identity_keys().curve25519); + // Start receiving events. emit trySyncCb(); @@ -1008,49 +1051,40 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) void ChatPage::tryInitialSync() { - mtx::http::SyncOpts opts; - opts.timeout = 0; + log::crypto()->info("ed25519 : {}", olm::client()->identity_keys().ed25519); + log::crypto()->info("curve25519: {}", olm::client()->identity_keys().curve25519); - log::net()->info("trying initial sync"); + // Upload one time keys for the device. + log::crypto()->info("generating one time keys"); + olm::client()->generate_one_time_keys(MAX_ONETIME_KEYS); - http::v2::client()->sync( - opts, [this](const mtx::responses::Sync &res, mtx::http::RequestErr err) { + http::v2::client()->upload_keys( + olm::client()->create_upload_keys_request(), + [this](const mtx::responses::UploadKeys &res, mtx::http::RequestErr err) { if (err) { - const auto error = QString::fromStdString(err->matrix_error.error); - const auto msg = tr("Please try to login again: %1").arg(error); - const auto err_code = mtx::errors::to_string(err->matrix_error.errcode); const int status_code = static_cast<int>(err->status_code); - - log::net()->error("sync error: {} {}", status_code, err_code); - - switch (status_code) { - case 502: - case 504: - case 524: { - emit tryInitialSyncCb(); - return; - } - default: { - emit dropToLoginPageCb(msg); - return; - } - } - } - - log::net()->info("initial sync completed"); - - try { - cache::client()->saveState(res); - emit initializeViews(std::move(res.rooms)); - emit initializeRoomList(cache::client()->roomInfo()); - } catch (const lmdb::error &e) { - log::db()->error("{}", e.what()); + log::crypto()->critical("failed to upload one time keys: {} {}", + err->matrix_error.error, + status_code); + // TODO We should have a timeout instead of keeping hammering the server. emit tryInitialSyncCb(); return; } - emit trySyncCb(); - emit contentLoaded(); + olm::client()->mark_keys_as_published(); + for (const auto &entry : res.one_time_key_counts) + log::net()->info( + "uploaded {} {} one-time keys", entry.second, entry.first); + + log::net()->info("trying initial sync"); + + mtx::http::SyncOpts opts; + opts.timeout = 0; + http::v2::client()->sync(opts, + std::bind(&ChatPage::initialSyncHandler, + this, + std::placeholders::_1, + std::placeholders::_2)); }); } @@ -1079,24 +1113,31 @@ ChatPage::trySync() log::net()->error("sync error: {} {}", status_code, err_code); + if (status_code <= 0 || status_code >= 600) { + if (!http::v2::is_logged_in()) + return; + + emit dropToLoginPageCb(msg); + return; + } + switch (status_code) { case 502: case 504: case 524: { - emit trySync(); + emit trySyncCb(); return; } case 401: case 403: { - // We are logged out. - if (http::v2::client()->access_token().empty()) + if (!http::v2::is_logged_in()) return; emit dropToLoginPageCb(msg); return; } default: { - emit trySync(); + emit tryDelayedSyncCb(); return; } } @@ -1104,9 +1145,14 @@ ChatPage::trySync() log::net()->debug("sync completed: {}", res.next_batch); + // Ensure that we have enough one-time keys available. + ensureOneTimeKeyCount(res.device_one_time_keys_count); + // TODO: fine grained error handling try { cache::client()->saveState(res); + olm::handle_to_device_messages(res.to_device); + emit syncUI(res.rooms); auto updates = cache::client()->roomUpdates(res); @@ -1194,3 +1240,74 @@ ChatPage::sendTypingNotifications() } }); } + +void +ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::RequestErr err) +{ + if (err) { + const auto error = QString::fromStdString(err->matrix_error.error); + const auto msg = tr("Please try to login again: %1").arg(error); + const auto err_code = mtx::errors::to_string(err->matrix_error.errcode); + const int status_code = static_cast<int>(err->status_code); + + log::net()->error("sync error: {} {}", status_code, err_code); + + switch (status_code) { + case 502: + case 504: + case 524: { + emit tryInitialSyncCb(); + return; + } + default: { + emit dropToLoginPageCb(msg); + return; + } + } + } + + log::net()->info("initial sync completed"); + + try { + cache::client()->saveState(res); + + olm::handle_to_device_messages(res.to_device); + + emit initializeViews(std::move(res.rooms)); + emit initializeRoomList(cache::client()->roomInfo()); + } catch (const lmdb::error &e) { + log::db()->error("{}", e.what()); + emit tryInitialSyncCb(); + return; + } + + emit trySyncCb(); + emit contentLoaded(); +} + +void +ChatPage::ensureOneTimeKeyCount(const std::map<std::string, uint16_t> &counts) +{ + for (const auto &entry : counts) { + if (entry.second < MAX_ONETIME_KEYS) { + const int nkeys = MAX_ONETIME_KEYS - entry.second; + + log::crypto()->info("uploading {} {} keys", nkeys, entry.first); + olm::client()->generate_one_time_keys(nkeys); + + http::v2::client()->upload_keys( + olm::client()->create_upload_keys_request(), + [](const mtx::responses::UploadKeys &, mtx::http::RequestErr err) { + if (err) { + log::crypto()->warn( + "failed to update one-time keys: {} {}", + err->matrix_error.error, + static_cast<int>(err->status_code)); + return; + } + + olm::client()->mark_keys_as_published(); + }); + } + } +} |