From d8545316f8f6c2f147dd8ae19d2efba3dec8ef73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Blin?=
 <sebastien.blin@savoirfairelinux.com>
Date: Tue, 22 Jun 2021 09:45:57 -0400
Subject: [PATCH] jamiaccount: check if conversation is valid before getting
 infos

Also check repo pointer in conversationRepository. If the conversation
is not correctly cloned (can be caused by a lot of things during the clone),
the infos will be invalid, so avoid to crash in this case.

Change-Id: I42a15200a06ca700bac972deea4cfc07ae87cae8
GitLab: #568
---
 src/jamidht/conversationrepository.cpp | 92 ++++++++++++++++++--------
 src/jamidht/jamiaccount.cpp            |  2 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/src/jamidht/conversationrepository.cpp b/src/jamidht/conversationrepository.cpp
index 8d2f37ad1e..2dc39a0b64 100644
--- a/src/jamidht/conversationrepository.cpp
+++ b/src/jamidht/conversationrepository.cpp
@@ -403,7 +403,7 @@ ConversationRepository::Impl::createMergeCommit(git_index* index, const std::str
     // The merge will occur between current HEAD and wanted_ref
     git_reference* head_ref_ptr = nullptr;
     auto repo = repository();
-    if (git_repository_head(&head_ref_ptr, repo.get()) < 0) {
+    if (!repo || git_repository_head(&head_ref_ptr, repo.get()) < 0) {
         JAMI_ERR("Could not get HEAD reference");
         return {};
     }
@@ -537,6 +537,10 @@ ConversationRepository::Impl::mergeFastforward(const git_oid* target_oid, int is
     // Initialize target
     git_reference* target_ref_ptr = nullptr;
     auto repo = repository();
+    if (!repo) {
+        JAMI_ERR() << "No repository found";
+        return false;
+    }
     if (is_unborn) {
         git_reference* head_ref_ptr = nullptr;
         // HEAD reference is unborn, lookup manually so we don't try to resolve it
@@ -633,6 +637,8 @@ ConversationRepository::Impl::checkOnlyDeviceCertificate(const std::string& user
 
     // Retrieve tree for recent commit
     auto repo = repository();
+    if (!repo)
+        return false;
     auto treeNew = treeAtCommit(repo.get(), commitId);
     auto treeOld = treeAtCommit(repo.get(), parentId);
     if (not treeNew or not treeOld)
@@ -679,6 +685,8 @@ ConversationRepository::Impl::checkVote(const std::string& userDevice,
     }
 
     auto repo = repository();
+    if (!repo)
+        return false;
     auto treeNew = treeAtCommit(repo.get(), commitId);
     auto treeOld = treeAtCommit(repo.get(), parentId);
     if (not treeNew or not treeOld)
@@ -778,11 +786,11 @@ ConversationRepository::Impl::checkValidAdd(const std::string& userDevice,
                                             const std::string& parentId) const
 {
     auto cert = tls::CertificateStore::instance().getCertificate(userDevice);
-    if (!cert)
+    auto repo = repository();
+    if (not cert or not repo)
         return false;
     auto userUri = cert->getIssuerUID();
 
-    auto repo = repository();
     std::string repoPath = git_repository_workdir(repo.get());
     if (mode() == ConversationMode::ONE_TO_ONE) {
         auto initialMembers = getInitialMembers();
@@ -881,6 +889,8 @@ ConversationRepository::Impl::checkValidJoins(const std::string& userDevice,
 
     // Retrieve tree for commits
     auto repo = repository();
+    if (!repo)
+        return false;
     auto treeNew = treeAtCommit(repo.get(), commitId);
     auto treeOld = treeAtCommit(repo.get(), parentId);
     if (not treeNew or not treeOld)
@@ -935,6 +945,8 @@ ConversationRepository::Impl::checkValidRemove(const std::string& userDevice,
 
     // Retrieve tree for recent commit
     auto repo = repository();
+    if (!repo)
+        return false;
     auto treeNew = treeAtCommit(repo.get(), commitId);
     auto treeOld = treeAtCommit(repo.get(), parentId);
     if (not treeNew or not treeOld)
@@ -1063,6 +1075,8 @@ ConversationRepository::Impl::checkValidProfileUpdate(const std::string& userDev
 
     // Retrieve tree for recent commit
     auto repo = repository();
+    if (!repo)
+        return false;
     auto treeNew = treeAtCommit(repo.get(), commitId);
     auto treeOld = treeAtCommit(repo.get(), parentId);
     if (not treeNew or not treeOld)
@@ -1089,11 +1103,11 @@ ConversationRepository::Impl::isValidUserAtCommit(const std::string& userDevice,
     auto cert = tls::CertificateStore::instance().getCertificate(userDevice);
     auto userUri = cert->getIssuerUID();
     auto parentCrt = tls::CertificateStore::instance().getCertificate(userUri);
-    if (!cert || !parentCrt)
+    auto repo = repository();
+    if (!cert || !parentCrt || !repo)
         return false;
 
     // Retrieve tree for commit
-    auto repo = repository();
     auto tree = treeAtCommit(repo.get(), commitId);
     if (not tree)
         return false;
@@ -1197,6 +1211,8 @@ ConversationRepository::Impl::commit(const std::string& msg)
     // Retrieve current index
     git_index* index_ptr = nullptr;
     auto repo = repository();
+    if (!repo)
+        return {};
     if (git_repository_index(&index_ptr, repo.get()) < 0) {
         JAMI_ERR("Could not open repository index");
         return {};
@@ -1348,6 +1364,8 @@ std::string
 ConversationRepository::Impl::diffStats(const std::string& newId, const std::string& oldId) const
 {
     auto repo = repository();
+    if (!repo)
+        return {};
     if (auto d = diff(repo.get(), newId, oldId))
         return diffStats(d);
     return {};
@@ -1430,6 +1448,8 @@ ConversationRepository::Impl::behind(const std::string& from) const
 {
     git_oid oid_local, oid_head, oid_remote;
     auto repo = repository();
+    if (!repo)
+        return {};
     if (git_reference_name_to_id(&oid_local, repo.get(), "HEAD") < 0) {
         JAMI_ERR("Cannot get reference for HEAD");
         return {};
@@ -1677,7 +1697,7 @@ ConversationRepository::Impl::resolveConflicts(git_index* index, const std::stri
 
     git_oid head_commit_id;
     auto repo = repository();
-    if (git_reference_name_to_id(&head_commit_id, repo.get(), "HEAD") < 0) {
+    if (!repo || git_reference_name_to_id(&head_commit_id, repo.get(), "HEAD") < 0) {
         JAMI_ERR("Cannot get reference for HEAD");
         return false;
     }
@@ -2113,7 +2133,8 @@ std::string
 ConversationRepository::addMember(const std::string& uri)
 {
     auto account = pimpl_->account_.lock();
-    if (!account)
+    auto repo = pimpl_->repository();
+    if (!account or not repo)
         return {};
     auto deviceId = account->currentDeviceId();
     auto name = account->getUsername();
@@ -2121,7 +2142,6 @@ ConversationRepository::addMember(const std::string& uri)
         name = deviceId;
 
     // First, we need to add the member file to the repository if not present
-    auto repo = pimpl_->repository();
     std::string repoPath = git_repository_workdir(repo.get());
 
     std::string invitedPath = repoPath + "invited";
@@ -2182,7 +2202,7 @@ ConversationRepository::amend(const std::string& id, const std::string& msg)
 
     git_commit* commit_ptr = nullptr;
     auto repo = pimpl_->repository();
-    if (git_oid_fromstr(&tree_id, id.c_str()) < 0
+    if (!repo || git_oid_fromstr(&tree_id, id.c_str()) < 0
         || git_commit_lookup(&commit_ptr, repo.get(), &tree_id) < 0) {
         GitCommit commit {commit_ptr, git_commit_free};
         JAMI_WARN("Failed to look up commit %s", id.c_str());
@@ -2222,13 +2242,14 @@ ConversationRepository::fetch(const std::string& remoteDeviceId)
     git_fetch_options_init(&fetch_opts, GIT_FETCH_OPTIONS_VERSION);
 
     auto lastMsg = logN("", 1);
-    if (lastMsg.size() == 0) {
+    if (lastMsg.size() == 0)
         return false;
-    }
     auto lastCommit = lastMsg[0].id;
 
     // Assert that repository exists
     auto repo = pimpl_->repository();
+    if (!repo)
+        return false;
     std::string channelName = "git://" + remoteDeviceId + '/' + pimpl_->id_;
     auto res = git_remote_lookup(&remote_ptr, repo.get(), remoteDeviceId.c_str());
     if (res != 0) {
@@ -2282,7 +2303,7 @@ ConversationRepository::remoteHead(const std::string& remoteDeviceId,
 {
     git_remote* remote_ptr = nullptr;
     auto repo = pimpl_->repository();
-    if (git_remote_lookup(&remote_ptr, repo.get(), remoteDeviceId.c_str()) < 0) {
+    if (!repo || git_remote_lookup(&remote_ptr, repo.get(), remoteDeviceId.c_str()) < 0) {
         JAMI_WARN("No remote found with id: %s", remoteDeviceId.c_str());
         return {};
     }
@@ -2315,6 +2336,8 @@ ConversationRepository::commitMessage(const std::string& msg)
 
     // First, we need to add device file to the repository if not present
     auto repo = pimpl_->repository();
+    if (!repo)
+        return {};
     std::string repoPath = git_repository_workdir(repo.get());
     // NOTE: libgit2 uses / for files
     std::string path = std::string("devices") + "/" + deviceId + ".crt";
@@ -2363,6 +2386,10 @@ ConversationRepository::merge(const std::string& merge_id)
 {
     // First, the repository must be in a clean state
     auto repo = pimpl_->repository();
+    if (!repo) {
+        JAMI_ERR() << "Can't merge without repo";
+        return {false, ""};
+    }
     int state = git_repository_state(repo.get());
     if (state != GIT_REPOSITORY_STATE_NONE) {
         JAMI_ERR("Merge operation aborted: repository is in unexpected state %d", state);
@@ -2490,6 +2517,8 @@ ConversationRepository::join()
 {
     // Check that not already member
     auto repo = pimpl_->repository();
+    if (!repo)
+        return {};
     std::string repoPath = git_repository_workdir(repo.get());
     auto account = pimpl_->account_.lock();
     if (!account)
@@ -2558,7 +2587,8 @@ ConversationRepository::leave()
 {
     // TODO simplify
     auto account = pimpl_->account_.lock();
-    if (!account)
+    auto repo = pimpl_->repository();
+    if (!account || !repo)
         return {};
     auto details = account->getAccountDetails();
     auto deviceId = details[DRing::Account::ConfProperties::DEVICE_ID];
@@ -2571,7 +2601,6 @@ ConversationRepository::leave()
         name = deviceId;
 
     // Remove related files
-    auto repo = pimpl_->repository();
     std::string repoPath = git_repository_workdir(repo.get());
 
     std::string adminFile = repoPath + "admins" + "/" + uri + ".crt";
@@ -2655,10 +2684,10 @@ ConversationRepository::voteKick(const std::string& uri, bool isDevice)
     // Add vote + commit
     // TODO simplify
     auto repo = pimpl_->repository();
-    std::string repoPath = git_repository_workdir(repo.get());
     auto account = pimpl_->account_.lock();
-    if (!account)
+    if (!account || !repo)
         return {};
+    std::string repoPath = git_repository_workdir(repo.get());
     auto cert = account->identity().second;
     auto adminUri = cert->getIssuerUID();
     if (adminUri == uri) {
@@ -2702,6 +2731,8 @@ ConversationRepository::resolveVote(const std::string& uri, bool isDevice)
     auto nbAdmins = 0, nbVotes = 0;
     // For each admin, check if voted
     auto repo = pimpl_->repository();
+    if (!repo)
+        return {};
     std::string repoPath = git_repository_workdir(repo.get());
     std::string adminsPath = repoPath + "admins";
     std::string membersPath = repoPath + "members";
@@ -2834,7 +2865,7 @@ ConversationRepository::removeBranchWith(const std::string& remoteDevice)
 {
     git_remote* remote_ptr = nullptr;
     auto repo = pimpl_->repository();
-    if (git_remote_lookup(&remote_ptr, repo.get(), remoteDevice.c_str()) < 0) {
+    if (!repo || git_remote_lookup(&remote_ptr, repo.get(), remoteDevice.c_str()) < 0) {
         JAMI_WARN("No remote found with id: %s", remoteDevice.c_str());
         return;
     }
@@ -2919,6 +2950,8 @@ ConversationRepository::updateInfos(const std::map<std::string, std::string>& pr
         infosMap[k] = v;
     }
     auto repo = pimpl_->repository();
+    if (!repo)
+        return {};
     std::string repoPath = git_repository_workdir(repo.get());
     auto profilePath = repoPath + "profile.vcf";
     auto file = fileutils::ofstream(profilePath, std::ios::trunc | std::ios::binary);
@@ -2972,18 +3005,19 @@ ConversationRepository::updateInfos(const std::map<std::string, std::string>& pr
 std::map<std::string, std::string>
 ConversationRepository::infos() const
 {
-    try {
-        auto repo = pimpl_->repository();
-        std::string repoPath = git_repository_workdir(repo.get());
-        auto profilePath = repoPath + "profile.vcf";
-        std::map<std::string, std::string> result;
-        if (fileutils::isFile(profilePath)) {
-            auto content = fileutils::loadTextFile(profilePath);
-            result = ConversationRepository::infosFromVCard(vCard::utils::toMap(content));
-        }
-        result["mode"] = std::to_string(static_cast<int>(mode()));
-        return result;
-    } catch (...) {
+    if (auto repo = pimpl_->repository()) {
+        try {
+            std::string repoPath = git_repository_workdir(repo.get());
+            auto profilePath = repoPath + "profile.vcf";
+            std::map<std::string, std::string> result;
+            if (fileutils::isFile(profilePath)) {
+                auto content = fileutils::loadTextFile(profilePath);
+                result = ConversationRepository::infosFromVCard(vCard::utils::toMap(content));
+            }
+            result["mode"] = std::to_string(static_cast<int>(mode()));
+            return result;
+        } catch (...) {
+        }
     }
     return {};
 }
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index b2920157d3..82e6b962a5 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -3979,7 +3979,7 @@ JamiAccount::conversationInfos(const std::string& conversationId) const
     std::lock_guard<std::mutex> lk(conversationsMtx_);
     // Add a new member in the conversation
     auto it = conversations_.find(conversationId);
-    if (it == conversations_.end()) {
+    if (it == conversations_.end() or not it->second) {
         JAMI_ERR("Conversation %s doesn't exist", conversationId.c_str());
         return {};
     }
-- 
GitLab