From 5de9d9eee338fd1c5fbfe137662118ffee875dae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com>
Date: Thu, 15 Feb 2024 14:51:12 -0500
Subject: [PATCH] conversation: cache account config

A Conversation is now initialized for a specific userId
and deviceId and is less dependent from the account.

GitLab: #952
Change-Id: I0a0e14f6b1877c04a7853a36a3b08fbc1c2aba42
---
 src/jamidht/conversation.cpp | 199 +++++++++++++++--------------------
 1 file changed, 87 insertions(+), 112 deletions(-)

diff --git a/src/jamidht/conversation.cpp b/src/jamidht/conversation.cpp
index 7f176a4dec..47469580c3 100644
--- a/src/jamidht/conversation.cpp
+++ b/src/jamidht/conversation.cpp
@@ -139,27 +139,36 @@ public:
          const std::string& otherMember = "")
         : repository_(ConversationRepository::createConversation(account, mode, otherMember))
         , account_(account)
+        , accountId_(account->getAccountID())
+        , userId_(account->getUsername())
+        , deviceId_(account->currentDeviceId())
     {
         if (!repository_) {
             throw std::logic_error("Couldn't create repository");
         }
-        init();
+        init(*account);
     }
 
     Impl(const std::shared_ptr<JamiAccount>& account, const std::string& conversationId)
         : account_(account)
+        , accountId_(account->getAccountID())
+        , userId_(account->getUsername())
+        , deviceId_(account->currentDeviceId())
     {
         repository_ = std::make_unique<ConversationRepository>(account, conversationId);
         if (!repository_) {
             throw std::logic_error("Couldn't create repository");
         }
-        init();
+        init(*account);
     }
 
     Impl(const std::shared_ptr<JamiAccount>& account,
          const std::string& remoteDevice,
          const std::string& conversationId)
         : account_(account)
+        , accountId_(account->getAccountID())
+        , userId_(account->getUsername())
+        , deviceId_(account->currentDeviceId())
     {
         std::vector<ConversationCommit> commits;
         repository_ = ConversationRepository::cloneConversation(account,
@@ -170,66 +179,60 @@ public:
                                                                 });
         if (!repository_) {
             emitSignal<libjami::ConversationSignal::OnConversationError>(
-                account->getAccountID(), conversationId, EFETCH, "Couldn't clone repository");
+                accountId_, conversationId, EFETCH, "Couldn't clone repository");
             throw std::logic_error("Couldn't clone repository");
         }
         // To detect current active calls, we need to check history
-        conversationDataPath_ = fileutils::get_data_dir() / account->getAccountID()
+        conversationDataPath_ = fileutils::get_data_dir() / accountId_
                                 / "conversation_data" / conversationId;
         activeCallsPath_ = conversationDataPath_ / ConversationMapKeys::ACTIVE_CALLS;
         for (const auto& c : repository_->convCommitsToMap(commits))
             updateActiveCalls(c);
-        init();
-    }
-
-    void init()
-    {
-        if (auto shared = account_.lock()) {
-            ioContext_ = Manager::instance().ioContext();
-            fallbackTimer_ = std::make_unique<asio::steady_timer>(*ioContext_);
-            swarmManager_
-                = std::make_shared<SwarmManager>(NodeId(shared->currentDeviceId()),
-                                                 Manager::instance().getSeededRandomEngine(),
-                                                 [account = account_](const DeviceId& deviceId) {
-                                                     if (auto acc = account.lock()) {
-                                                         return acc->isConnectedWith(deviceId);
-                                                     }
-                                                     return false;
-                                                 });
-            swarmManager_->setMobility(shared->isMobile());
-            accountId_ = shared->getAccountID();
-            transferManager_
-                = std::make_shared<TransferManager>(shared->getAccountID(),
-                                                    "",
-                                                    repository_->id(),
-                                                    Manager::instance().getSeededRandomEngine());
-            conversationDataPath_ = fileutils::get_data_dir() / shared->getAccountID()
-                                    / "conversation_data" / repository_->id();
-            fetchedPath_ = conversationDataPath_ / "fetched";
-            statusPath_ = conversationDataPath_ / "status";
-            sendingPath_ = conversationDataPath_ / "sending";
-            preferencesPath_ = conversationDataPath_ / ConversationMapKeys::PREFERENCES;
-            activeCallsPath_ = conversationDataPath_ / ConversationMapKeys::ACTIVE_CALLS;
-            hostedCallsPath_ = conversationDataPath_ / ConversationMapKeys::HOSTED_CALLS;
-            loadActiveCalls();
-            loadStatus();
-            typers_ = std::make_shared<Typers>(shared, repository_->id());
-        }
+        init(*account);
+    }
+
+    void init(JamiAccount& account) {
+        ioContext_ = Manager::instance().ioContext();
+        fallbackTimer_ = std::make_unique<asio::steady_timer>(*ioContext_);
+        swarmManager_
+            = std::make_shared<SwarmManager>(NodeId(deviceId_),
+                                             Manager::instance().getSeededRandomEngine(),
+                                            [account = account_](const DeviceId& deviceId) {
+                                                if (auto acc = account.lock()) {
+                                                    return acc->isConnectedWith(deviceId);
+                                                }
+                                                return false;
+                                            });
+        swarmManager_->setMobility(account.isMobile());
+        transferManager_
+            = std::make_shared<TransferManager>(accountId_,
+                                                "",
+                                                repository_->id(),
+                                                Manager::instance().getSeededRandomEngine());
+        conversationDataPath_ = fileutils::get_data_dir() / accountId_
+                                / "conversation_data" / repository_->id();
+        fetchedPath_ = conversationDataPath_ / "fetched";
+        statusPath_ = conversationDataPath_ / "status";
+        sendingPath_ = conversationDataPath_ / "sending";
+        preferencesPath_ = conversationDataPath_ / ConversationMapKeys::PREFERENCES;
+        activeCallsPath_ = conversationDataPath_ / ConversationMapKeys::ACTIVE_CALLS;
+        hostedCallsPath_ = conversationDataPath_ / ConversationMapKeys::HOSTED_CALLS;
+        loadActiveCalls();
+        loadStatus();
+        typers_ = std::make_shared<Typers>(shared, repository_->id());
     }
 
     const std::string& toString() const
     {
         if (fmtStr_.empty()) {
             if (repository_->mode() == ConversationMode::ONE_TO_ONE) {
-                if (auto acc = account_.lock()) {
-                    auto peer = acc->getUsername();
-                    for (const auto& member : repository_->getInitialMembers()) {
-                        if (member != acc->getUsername()) {
-                            peer = member;
-                        }
+                auto peer = userId_;
+                for (const auto& member : repository_->getInitialMembers()) {
+                    if (member != userId_) {
+                        peer = member;
                     }
-                    fmtStr_ = fmt::format("[Conversation (1:1) {}]", peer);
                 }
+                fmtStr_ = fmt::format("[Conversation (1:1) {}]", peer);
             } else {
                 fmtStr_ = fmt::format("[Conversation {}]", repository_->id());
             }
@@ -392,8 +395,7 @@ public:
 
     void announce(const std::vector<std::map<std::string, std::string>>& commits, bool commitFromSelf = false) const
     {
-        auto shared = account_.lock();
-        if (!shared or !repository_)
+        if (!repository_)
             return;
         auto convId = repository_->id();
         auto ok = !commits.empty();
@@ -438,7 +440,7 @@ public:
                 if (pluginChatManager.hasHandlers()) {
                     auto cm = std::make_shared<JamiMessage>(accountId_,
                                                             convId,
-                                                            c.at("author") != shared->getUsername(),
+                                                            c.at("author") != userId_,
                                                             c,
                                                             false);
                     cm->isSwarm = true;
@@ -521,17 +523,18 @@ public:
 
     std::string_view bannedType(const std::string& uri) const
     {
+        auto repo = repoPath();
         auto crt = fmt::format("{}.crt", uri);
-        auto bannedMember = repoPath() / "banned" / "members" / crt;
+        auto bannedMember = repo / "banned" / "members" / crt;
         if (std::filesystem::is_regular_file(bannedMember))
             return "members"sv;
-        auto bannedAdmin = repoPath() / "banned" / "admins" / crt;
+        auto bannedAdmin = repo / "banned" / "admins" / crt;
         if (std::filesystem::is_regular_file(bannedAdmin))
             return "admins"sv;
-        auto bannedInvited = repoPath() / "banned" / "invited" / uri;
+        auto bannedInvited = repo / "banned" / "invited" / uri;
         if (std::filesystem::is_regular_file(bannedInvited))
             return "invited"sv;
-        auto bannedDevice = repoPath() / "banned" / "devices" / crt;
+        auto bannedDevice = repo / "banned" / "devices" / crt;
         if (std::filesystem::is_regular_file(bannedDevice))
             return "devices"sv;
         return {};
@@ -567,6 +570,9 @@ public:
     std::unique_ptr<ConversationRepository> repository_;
     std::shared_ptr<SwarmManager> swarmManager_;
     std::weak_ptr<JamiAccount> account_;
+    std::string accountId_ {};
+    std::string userId_;
+    std::string deviceId_;
     std::atomic_bool isRemoving_ {false};
     std::vector<std::map<std::string, std::string>> loadMessages(const LogOptions& options);
     std::vector<libjami::SwarmMessage> loadMessages2(const LogOptions& options,
@@ -584,8 +590,6 @@ public:
 
     // Manage last message displayed and status
     std::filesystem::path sendingPath_ {};
-    // Manage last message displayed
-    std::string accountId_ {};
     std::filesystem::path preferencesPath_ {};
     OnMembersChanged onMembersChanged_ {};
 
@@ -603,7 +607,6 @@ public:
     std::shared_ptr<asio::io_context> ioContext_;
     std::unique_ptr<asio::steady_timer> fallbackTimer_;
 
-    bool isMobile {false};
 
     /**
      * Loaded history represents the linearized history to show for clients
@@ -667,16 +670,8 @@ public:
 bool
 Conversation::Impl::isAdmin() const
 {
-    auto shared = account_.lock();
-    if (!shared)
-        return false;
-
     auto adminsPath = repoPath() / "admins";
-    auto cert = shared->identity().second;
-    if (!cert->issuer)
-        return false;
-    auto uri = cert->issuer->getId().toString();
-    return std::filesystem::is_regular_file(fileutils::getFullPath(adminsPath, uri + ".crt"));
+    return std::filesystem::is_regular_file(fileutils::getFullPath(adminsPath, userId_ + ".crt"));
 }
 
 std::vector<std::map<std::string, std::string>>
@@ -703,7 +698,6 @@ Conversation::Impl::getMembers(bool includeInvited, bool includeLeft, bool inclu
 std::vector<std::string>
 Conversation::Impl::commitsEndedCalls()
 {
-    auto shared = account_.lock();
     // Handle current calls
     std::vector<std::string> commits {};
     std::unique_lock lk(writeMtx_);
@@ -714,10 +708,8 @@ Conversation::Impl::commitsEndedCalls()
         // will not be correctly updated
         // We don't need to send notifications there, as peers will sync with presence
         Json::Value value;
-        auto uri = shared->getUsername();
-        auto device = std::string(shared->currentDeviceId());
-        value["uri"] = uri;
-        value["device"] = device;
+        value["uri"] = userId_;
+        value["device"] = deviceId_;
         value["confId"] = hostedCall.first;
         value["type"] = "application/call-history+json";
         auto now = std::chrono::system_clock::now();
@@ -726,9 +718,9 @@ Conversation::Impl::commitsEndedCalls()
         value["duration"] = std::to_string((nowConverted - hostedCall.second) * 1000);
         auto itActive = std::find_if(activeCalls_.begin(),
                                      activeCalls_.end(),
-                                     [confId = hostedCall.first, uri, device](auto value) {
-                                         return value.at("id") == confId && value.at("uri") == uri
-                                                && value.at("device") == device;
+                                     [this, confId = hostedCall.first](auto value) {
+                                         return value.at("id") == confId && value.at("uri") == userId_
+                                                && value.at("device") == deviceId_;
                                      });
         if (itActive != activeCalls_.end())
             activeCalls_.erase(itActive);
@@ -1526,9 +1518,6 @@ Conversation::sendMessage(Json::Value&& value,
     dht::ThreadPool::io().run(
         [w = weak(), value = std::move(value), onCommit = std::move(onCommit), cb = std::move(cb)] {
             if (auto sthis = w.lock()) {
-                auto acc = sthis->pimpl_->account_.lock();
-                if (!acc)
-                    return;
                 std::unique_lock lk(sthis->pimpl_->writeMtx_);
                 auto commit = sthis->pimpl_->repository_->commitMessage(
                     Json::writeString(jsonBuilder, value));
@@ -1777,9 +1766,8 @@ Conversation::Impl::pull()
             auto changedFiles = repo->changedFiles(diffStats);
             if (find(changedFiles.begin(), changedFiles.end(), "profile.vcf")
                 != changedFiles.end()) {
-                if (auto account = account_.lock())
-                    emitSignal<libjami::ConversationSignal::ConversationProfileUpdated>(
-                        account->getAccountID(), repo->id(), repo->infos());
+                emitSignal<libjami::ConversationSignal::ConversationProfileUpdated>(
+                    accountId_, repo->id(), repo->infos());
             }
         }
     }
@@ -1791,20 +1779,17 @@ Conversation::sync(const std::string& member,
                    OnPullCb&& cb,
                    std::string commitId)
 {
-    if (!pull(deviceId, std::move(cb), commitId))
-        return;
-    dht::ThreadPool::io().run([member, deviceId, a = pimpl_->account_, w = weak_from_this()] {
+    pull(deviceId, std::move(cb), commitId);
+    dht::ThreadPool::io().run([member, deviceId, w = weak_from_this()] {
         auto sthis = w.lock();
-        if (auto account = a.lock()) {
-            // For waiting request, downloadFile
-            for (const auto& wr : sthis->dataTransfer()->waitingRequests()) {
-                auto path = fileutils::get_data_dir() / account->getAccountID()
-                            / "conversation_data" / sthis->id() / wr.fileId;
-                auto start = fileutils::size(path.string());
-                if (start < 0)
-                    start = 0;
-                sthis->downloadFile(wr.interactionId, wr.fileId, wr.path, member, deviceId, start);
-            }
+        // For waiting request, downloadFile
+        for (const auto& wr : sthis->dataTransfer()->waitingRequests()) {
+            auto path = fileutils::get_data_dir() / sthis->pimpl_->accountId_
+                        / "conversation_data" / sthis->id() / wr.fileId;
+            auto start = fileutils::size(path);
+            if (start < 0)
+                start = 0;
+            sthis->downloadFile(wr.interactionId, wr.fileId, wr.path, member, deviceId, start);
         }
     });
 }
@@ -1888,9 +1873,8 @@ Conversation::updateInfos(const std::map<std::string, std::string>& map, const O
             lk.unlock();
             if (cb)
                 cb(!commit.empty(), commit);
-            if (auto account = sthis->pimpl_->account_.lock())
-                emitSignal<libjami::ConversationSignal::ConversationProfileUpdated>(
-                    account->getAccountID(), repo->id(), repo->infos());
+            emitSignal<libjami::ConversationSignal::ConversationProfileUpdated>(
+                sthis->pimpl_->accountId_, repo->id(), repo->infos());
         }
     });
 }
@@ -2065,10 +2049,9 @@ Conversation::hasFetched(const std::string& deviceId, const std::string& commitI
         auto sthis = w.lock();
         if (!sthis)
             return;
-        auto acc = sthis->pimpl_->account_.lock();
         // Update fetched for Uri
         auto uri = sthis->uriFromDevice(deviceId);
-        if (uri.empty() || !acc || uri == acc->getUsername())
+        if (uri.empty() || uri == sthis->pimpl_->userId_)
             return;
         // When a user fetches a commit, the message is sent for this person
         sthis->pimpl_->updateStatus(uri, libjami::Account::MessageStates::SENT, commitId, std::to_string(std::time(nullptr)), true);
@@ -2158,7 +2141,6 @@ Conversation::messageStatus() const
 void
 Conversation::updateMessageStatus(const std::map<std::string, std::map<std::string, std::string>>& messageStatus)
 {
-    auto acc = pimpl_->account_.lock();
     std::unique_lock lk(pimpl_->messageStatusMtx_);
     std::vector<std::tuple<libjami::Account::MessageStates, std::string, std::string, std::string>> stVec;
     for (const auto& [uri, status] : messageStatus) {
@@ -2213,7 +2195,7 @@ Conversation::checkBootstrapMember(const asio::error_code& ec,
         auto member = members.back();
         members.pop_back();
         uri = member.at("uri");
-        if (uri != acc->getUsername()
+        if (uri != pimpl_->userId_
             && pimpl_->checkedMembers_.find(uri) == pimpl_->checkedMembers_.end())
             break;
     }
@@ -2256,7 +2238,7 @@ Conversation::checkBootstrapMember(const asio::error_code& ec,
 #endif
                 JAMI_WARNING("{}[SwarmManager {}] Bootstrap: Fallback with member: {}",
                              sthis->pimpl_->toString(),
-                             fmt::ptr(sthis->pimpl_->swarmManager_.get()),
+                             fmt::ptr(sthis->pimpl_->swarmManager_),
                              uri);
                 if (sthis->pimpl_->swarmManager_->setKnownNodes(*devices))
                     checkNext = false;
@@ -2291,7 +2273,7 @@ Conversation::bootstrap(std::function<void()> onBootstraped,
         devices.insert(devices.end(), m.second.begin(), m.second.end());
     JAMI_DEBUG("{}[SwarmManager {}] Bootstrap with {} devices",
                pimpl_->toString(),
-               fmt::ptr(pimpl_->swarmManager_.get()),
+               fmt::ptr(pimpl_->swarmManager_),
                devices.size());
     // set callback
     auto fallback = [](auto sthis, bool now = false) {
@@ -2426,10 +2408,6 @@ Conversation::search(uint32_t req,
     // do it asynchronously
     dht::ThreadPool::io().run([w = weak(), req, filter, flag] {
         if (auto sthis = w.lock()) {
-            auto acc = sthis->pimpl_->account_.lock();
-            if (!acc)
-                return;
-
             History history;
             std::vector<std::map<std::string, std::string>> commits {};
             // std::regex_constants::ECMAScript is the default flag.
@@ -2502,14 +2480,14 @@ Conversation::search(uint32_t req,
 
             if (commits.size() > 0)
                 emitSignal<libjami::ConversationSignal::MessagesFound>(req,
-                                                                       acc->getAccountID(),
+                                                                       sthis->pimpl_->accountId_,
                                                                        sthis->id(),
                                                                        std::move(commits));
             // If we're the latest thread, inform client that the search is finished
             if ((*flag)-- == 1 /* decrement return the old value */) {
                 emitSignal<libjami::ConversationSignal::MessagesFound>(
                     req,
-                    acc->getAccountID(),
+                    sthis->pimpl_->accountId_,
                     std::string {},
                     std::vector<std::map<std::string, std::string>> {});
             }
@@ -2539,11 +2517,8 @@ Conversation::hostConference(Json::Value&& message, OnDoneCb&& cb)
 bool
 Conversation::isHosting(const std::string& confId) const
 {
-    auto shared = pimpl_->account_.lock();
-    if (!shared)
-        return false;
     auto info = infos();
-    if (info["rdvDevice"] == shared->currentDeviceId() && info["rdvHost"] == shared->getUsername())
+    if (info["rdvDevice"] == pimpl_->deviceId_ && info["rdvHost"] == pimpl_->userId_)
         return true; // We are the current device Host
     std::lock_guard lk(pimpl_->activeCallsMtx_);
     return pimpl_->hostedCalls_.find(confId) != pimpl_->hostedCalls_.end();
-- 
GitLab