LCOV - code coverage report
Current view: top level - src/jamidht - conversationrepository.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 92.3 % 78 72
Test Date: 2026-11-09 10:41:14 Functions: 100.0 % 15 15

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 Savoir-faire Linux Inc.
       3              :  *
       4              :  *  This program is free software: you can redistribute it and/or modify
       5              :  *  it under the terms of the GNU General Public License as published by
       6              :  *  the Free Software Foundation, either version 3 of the License, or
       7              :  *  (at your option) any later version.
       8              :  *
       9              :  *  This program is distributed in the hope that it will be useful,
      10              :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12              :  *  GNU General Public License for more details.
      13              :  *
      14              :  *  You should have received a copy of the GNU General Public License
      15              :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : #pragma once
      18              : #include "def.h"
      19              : #include "vcard.h"
      20              : #include "git_def.h"
      21              : #include "jamidht/commit_message.h"
      22              : 
      23              : #include <opendht/default_types.h>
      24              : 
      25              : #include <algorithm>
      26              : #include <array>
      27              : #include <filesystem>
      28              : #include <optional>
      29              : #include <memory>
      30              : #include <stdexcept>
      31              : #include <string>
      32              : #include <string_view>
      33              : #include <utility>
      34              : #include <vector>
      35              : 
      36              : namespace jami {
      37              : 
      38              : using DeviceId = dht::PkId;
      39              : 
      40              : constexpr auto EFETCH = 1;
      41              : constexpr auto EINVALIDMODE = 2;
      42              : constexpr auto EVALIDFETCH = 3;
      43              : constexpr auto EUNAUTHORIZED = 4;
      44              : constexpr auto ECOMMIT = 5;
      45              : constexpr auto EUNRECOVERABLE = 6;
      46              : 
      47              : constexpr size_t CONVERSATION_ID_LENGTH = 40;
      48              : 
      49              : constexpr bool
      50          505 : isConversationId(std::string_view id) noexcept
      51              : {
      52          505 :     return id.size() == CONVERSATION_ID_LENGTH && std::all_of(id.begin(), id.end(), [](char c) {
      53        20200 :                return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
      54          505 :            });
      55              : }
      56              : 
      57              : class JamiAccount;
      58              : 
      59              : /**
      60              :  * Exception thrown when a cloned conversation repository fails commit validation.
      61              :  * Unlike network errors, this failure is permanent: the remote history is immutable,
      62              :  * so cloning the same conversation again will fail the same way.
      63              :  */
      64              : class InvalidRepositoryError : public std::runtime_error
      65              : {
      66              : public:
      67            2 :     explicit InvalidRepositoryError(const std::string& what)
      68            2 :         : std::runtime_error(what)
      69            2 :     {}
      70              : };
      71              : 
      72              : struct LogOptions
      73              : {
      74              :     std::string from {};
      75              :     std::string to {};
      76              :     uint64_t nbOfCommits {0};  // maximum number of commits wanted
      77              :     bool skipMerge {false};    // Do not include merge commits in the log. Used by the module to get
      78              :                                // last interaction without potential merges
      79              :     bool includeTo {false};    // If we want or not the "to" commit [from-to] or [from-to)
      80              :     bool fastLog {false};      // Do not parse content, used mostly to count
      81              :     bool logIfNotFound {true}; // Add a warning in the log if commit is not found
      82              : 
      83              :     std::string authorUri {}; // filter commits from author
      84              : };
      85              : 
      86              : struct Filter
      87              : {
      88              :     std::string author;
      89              :     std::string lastId;
      90              :     std::string regexSearch;
      91              :     std::string type;
      92              :     int64_t after {0};
      93              :     int64_t before {0};
      94              :     uint32_t maxResult {0};
      95              :     bool caseSensitive {false};
      96              : };
      97              : 
      98              : struct GitAuthor
      99              : {
     100              :     std::string name {};
     101              :     std::string email {};
     102              : };
     103              : 
     104              : struct ConversationCommit
     105              : {
     106              :     std::string id {};
     107              :     std::vector<std::string> parents {};
     108              :     GitAuthor author {};
     109              :     std::vector<uint8_t> signed_content {};
     110              :     std::vector<uint8_t> signature {};
     111              :     CommitMessage commitMsg {};
     112              :     std::string linearized_parent {};
     113              :     std::string authorId {};
     114              :     int64_t timestamp {0};
     115              : };
     116              : 
     117              : enum class MemberRole { ADMIN = 0, MEMBER, INVITED, BANNED, LEFT };
     118              : 
     119              : enum class Permission {
     120              :     Text,
     121              :     TextReply,
     122              :     File,
     123              :     FileReply,
     124              :     Reaction,
     125              :     Call,
     126              :     HostConference,
     127              :     Edit,
     128              :     Delete,
     129              :     DeleteFile,
     130              :     CreateDocument,
     131              :     DeleteDocument,
     132              :     UpdateDocument,
     133              :     AddDocumentAttachment,
     134              :     Join,
     135              :     Leave,
     136              :     AddMember,
     137              :     BanUnbanMember,
     138              :     UpdateProfile,
     139              :     Count,
     140              : };
     141              : 
     142              : constexpr bool
     143         3749 : hasPermission(ConversationMode mode, MemberRole role, Permission permission) noexcept
     144              : {
     145         3749 :     if (role == MemberRole::INVITED) {
     146         1473 :         switch (mode) {
     147         1473 :         case ConversationMode::ONE_TO_ONE:
     148              :         case ConversationMode::INVITES_ONLY:
     149              :         case ConversationMode::DOCUMENT:
     150              :         case ConversationMode::FEED:
     151         1473 :             return permission == Permission::Join;
     152            0 :         default:
     153            0 :             return false;
     154              :         }
     155              :     }
     156         2276 :     if (role != MemberRole::ADMIN && role != MemberRole::MEMBER)
     157           31 :         return false;
     158              : 
     159         2245 :     switch (mode) {
     160          122 :     case ConversationMode::ONE_TO_ONE:
     161          122 :         switch (permission) {
     162          103 :         case Permission::Text:
     163              :         case Permission::TextReply:
     164              :         case Permission::File:
     165              :         case Permission::FileReply:
     166              :         case Permission::Reaction:
     167              :         case Permission::Call:
     168              :         case Permission::Edit:
     169              :         case Permission::Delete:
     170              :         case Permission::DeleteFile:
     171              :         case Permission::CreateDocument:
     172              :         case Permission::DeleteDocument:
     173              :         case Permission::Leave:
     174              :         case Permission::AddMember:
     175          103 :             return true;
     176           19 :         default:
     177           19 :             return false;
     178              :         }
     179         1808 :     case ConversationMode::INVITES_ONLY:
     180         1808 :         switch (permission) {
     181         1654 :         case Permission::Text:
     182              :         case Permission::TextReply:
     183              :         case Permission::File:
     184              :         case Permission::FileReply:
     185              :         case Permission::Reaction:
     186              :         case Permission::HostConference:
     187              :         case Permission::Edit:
     188              :         case Permission::Delete:
     189              :         case Permission::DeleteFile:
     190              :         case Permission::CreateDocument:
     191              :         case Permission::DeleteDocument:
     192              :         case Permission::Leave:
     193              :         case Permission::AddMember:
     194         1654 :             return true;
     195          124 :         case Permission::BanUnbanMember:
     196              :         case Permission::UpdateProfile:
     197          124 :             return role == MemberRole::ADMIN;
     198           30 :         default:
     199           30 :             return false;
     200              :         }
     201          109 :     case ConversationMode::FEED:
     202          109 :         switch (permission) {
     203           43 :         case Permission::Text:
     204              :         case Permission::File:
     205              :         case Permission::AddMember:
     206              :         case Permission::BanUnbanMember:
     207              :         case Permission::UpdateProfile:
     208           43 :             return role == MemberRole::ADMIN;
     209           34 :         case Permission::TextReply:
     210              :         case Permission::FileReply:
     211              :         case Permission::Reaction:
     212              :         case Permission::Edit:
     213              :         case Permission::Delete:
     214              :         case Permission::DeleteFile:
     215           34 :             return true;
     216           32 :         default:
     217           32 :             return false;
     218              :         }
     219          204 :     case ConversationMode::DOCUMENT:
     220          204 :         switch (permission) {
     221          116 :         case Permission::UpdateDocument:
     222              :         case Permission::AddDocumentAttachment:
     223              :         case Permission::Leave:
     224              :         case Permission::AddMember:
     225          116 :             return true;
     226           83 :         case Permission::BanUnbanMember:
     227              :         case Permission::UpdateProfile:
     228           83 :             return role == MemberRole::ADMIN;
     229            5 :         default:
     230            5 :             return false;
     231              :         }
     232            2 :     default:
     233            2 :         return false;
     234              :     }
     235              : }
     236              : 
     237              : constexpr auto PERMISSION_NAME_TABLE = std::to_array<std::pair<Permission, std::string_view>>({
     238              :     {Permission::Text, "text"},
     239              :     {Permission::TextReply, "textReply"},
     240              :     {Permission::File, "file"},
     241              :     {Permission::FileReply, "fileReply"},
     242              :     {Permission::Reaction, "reaction"},
     243              :     {Permission::Call, "call"},
     244              :     {Permission::HostConference, "hostConference"},
     245              :     {Permission::Edit, "edit"},
     246              :     {Permission::Delete, "delete"},
     247              :     {Permission::DeleteFile, "deleteFile"},
     248              :     {Permission::CreateDocument, "createDocument"},
     249              :     {Permission::DeleteDocument, "deleteDocument"},
     250              :     {Permission::UpdateDocument, "updateDocument"},
     251              :     {Permission::AddDocumentAttachment, "addDocumentAttachment"},
     252              :     {Permission::Join, "join"},
     253              :     {Permission::Leave, "leave"},
     254              :     {Permission::AddMember, "addMember"},
     255              :     {Permission::BanUnbanMember, "banUnbanMember"},
     256              :     {Permission::UpdateProfile, "updateProfile"},
     257              : });
     258              : 
     259              : static_assert(PERMISSION_NAME_TABLE.size() == static_cast<size_t>(Permission::Count),
     260              :               "Every permission must have a client-visible name");
     261              : 
     262              : /**
     263              :  * Name of a permission, as exposed to clients
     264              :  */
     265              : constexpr std::string_view
     266           19 : permissionName(Permission permission) noexcept
     267              : {
     268          190 :     for (const auto& [knownPermission, name] : PERMISSION_NAME_TABLE) {
     269          190 :         if (knownPermission == permission)
     270           19 :             return name;
     271              :     }
     272            0 :     return {};
     273              : }
     274              : 
     275              : /**
     276              :  * Names of every permission granted to a role in a conversation mode
     277              :  */
     278              : inline std::vector<std::string>
     279           16 : permissionNames(ConversationMode mode, MemberRole role)
     280              : {
     281           16 :     std::vector<std::string> names;
     282          320 :     for (const auto& [permission, name] : PERMISSION_NAME_TABLE) {
     283          304 :         if (hasPermission(mode, role, permission))
     284          151 :             names.emplace_back(name);
     285              :     }
     286           16 :     return names;
     287            0 : }
     288              : 
     289              : namespace MemberPath {
     290              : 
     291              : static const std::filesystem::path ADMINS {"admins"};
     292              : static const std::filesystem::path MEMBERS {"members"};
     293              : static const std::filesystem::path INVITED {"invited"};
     294              : static const std::filesystem::path BANNED {"banned"};
     295              : static const std::filesystem::path DEVICES {"devices"};
     296              : 
     297              : } // namespace MemberPath
     298              : 
     299              : struct ConversationMember
     300              : {
     301              :     std::string uri;
     302              :     MemberRole role;
     303              : 
     304          573 :     std::map<std::string, std::string> map() const
     305              :     {
     306          573 :         std::string rolestr;
     307          573 :         if (role == MemberRole::ADMIN) {
     308          287 :             rolestr = "admin";
     309          286 :         } else if (role == MemberRole::MEMBER) {
     310           63 :             rolestr = "member";
     311          223 :         } else if (role == MemberRole::INVITED) {
     312          219 :             rolestr = "invited";
     313            4 :         } else if (role == MemberRole::BANNED) {
     314            4 :             rolestr = "banned";
     315            0 :         } else if (role == MemberRole::LEFT) {
     316            0 :             rolestr = "left"; // For one to one
     317              :         }
     318              : 
     319         2865 :         return {{"uri", uri}, {"role", rolestr}};
     320         1146 :     }
     321        16047 :     MSGPACK_DEFINE(uri, role)
     322              : };
     323              : 
     324              : enum class CallbackResult { Skip, Break, Ok };
     325              : 
     326              : using PreConditionCb = std::function<CallbackResult(const std::string&, const GitAuthor&, const GitCommit&)>;
     327              : using PostConditionCb = std::function<bool(const std::string&, const GitAuthor&, ConversationCommit&)>;
     328              : using OnMembersChanged = std::function<void(const std::set<std::string>&)>;
     329              : 
     330              : /**
     331              :  * This class gives access to the git repository that represents the conversation
     332              :  */
     333              : class LIBJAMI_TEST_EXPORT ConversationRepository
     334              : {
     335              : public:
     336              : #ifdef LIBJAMI_TEST
     337              :     static bool DISABLE_RESET; // Some tests inject bad files so resetHard() will break the test
     338              : 
     339              :     // If true, clone and fetch operations will be performed directly using the target repo's path,
     340              :     // avoiding the need for setting up a GitServer and a DHTNet connection.
     341              :     static bool FETCH_FROM_LOCAL_REPOS;
     342              : #endif
     343              :     /**
     344              :      * Check that an identifier can be the id of a conversation (or document) repository:
     345              :      * the hex SHA-1 of its initial commit, exactly 40 lowercase hex digits.
     346              :      * Ids received from peers or read from disk are used as directory names and
     347              :      * MUST pass this check before touching the filesystem.
     348              :      */
     349              :     static LIBJAMI_TEST_EXPORT bool isValidConversationId(std::string_view id) noexcept;
     350              : 
     351              :     /**
     352              :      * Creates a new repository, with initial files, where the first commit hash is the conversation id
     353              :      * @param account       The related account
     354              :      * @param mode          The wanted mode
     355              :      * @param otherMember   The other uri
     356              :      * @return  the conversation repository object
     357              :      */
     358              :     static LIBJAMI_TEST_EXPORT std::unique_ptr<ConversationRepository> createConversation(
     359              :         const std::shared_ptr<JamiAccount>& account,
     360              :         ConversationMode mode = ConversationMode::INVITES_ONLY,
     361              :         const std::string& otherMember = "");
     362              : 
     363              :     /**
     364              :      * Creates a new collaborative document repository (a swarm exactly like a conversation,
     365              :      * with mode ConversationMode::DOCUMENT), where the first commit hash is the document id.
     366              :      * @param account                  The related account
     367              :      * @param parentConversationId     The conversation the document is announced in
     368              :      * @param mimeType                 The media type of what the document holds
     369              :      * @return the repository object
     370              :      */
     371              :     static LIBJAMI_TEST_EXPORT std::unique_ptr<ConversationRepository> createDocument(
     372              :         const std::shared_ptr<JamiAccount>& account,
     373              :         const std::string& parentConversationId,
     374              :         const std::string& mimeType);
     375              : 
     376              :     /**
     377              :      * Clones a conversation on a remote device
     378              :      * @note This will use the socket registered for the conversation with
     379              :      * Conversation::addGitSocket()
     380              :      * @param account           The account getting the conversation
     381              :      * @param deviceId          Remote device
     382              :      * @param conversationId    Conversation to clone
     383              :      * @throws InvalidRepositoryError if the cloned repository fails commit validation. This is a
     384              :      *         permanent failure (the remote history is immutable); transient network errors do not
     385              :      *         throw but return an empty repository instead. The sole caller,
     386              :      *         ConversationModule::Impl::handlePendingConversation(), catches this to stop retrying;
     387              :      *         any new caller must handle it as well.
     388              :      */
     389              :     static LIBJAMI_TEST_EXPORT std::pair<std::unique_ptr<ConversationRepository>, std::vector<ConversationCommit>>
     390              :     cloneConversation(const std::shared_ptr<JamiAccount>& account,
     391              :                       const std::string& deviceId,
     392              :                       const std::string& conversationId);
     393              : 
     394              :     /**
     395              :      * Open a conversation repository for an account and an id
     396              :      * @param account       The related account
     397              :      * @param id            The conversation id
     398              :      */
     399              :     ConversationRepository(const std::shared_ptr<JamiAccount>& account, const std::string& id);
     400              :     ~ConversationRepository();
     401              : 
     402              :     /**
     403              :      * Write the certificate in /members and commit the change
     404              :      * @param uri    Member to add
     405              :      * @return the commit id if successful
     406              :      */
     407              :     std::string addMember(const std::string& uri);
     408              : 
     409              :     /**
     410              :      * Fetch a remote repository via the given socket
     411              :      * @note This will use the socket registered for the conversation with
     412              :      * Conversation::addGitSocket()
     413              :      * @note will create a remote identified by the deviceId
     414              :      * @param remoteDeviceId    Remote device id to fetch
     415              :      * @return if the operation was successful
     416              :      */
     417              :     bool fetch(const std::string& remoteDeviceId);
     418              : 
     419              :     /**
     420              :      * Merge the pack files that fetches leave behind, once there are too many
     421              :      * of them. Does nothing unless a fetch stored a pack since the last call.
     422              :      * See repack(const std::filesystem::path&, std::size_t).
     423              :      */
     424              :     void repack();
     425              : 
     426              :     /**
     427              :      * Each fetch stores what it receives in a pack file of its own, and
     428              :      * libgit2 goes through all of them, in quadratic time, whenever it opens
     429              :      * the object database. Once the repository at path holds more than
     430              :      * maxPacks packs, merge the smallest ones, as git repack --geometric=2
     431              :      * does. Nothing is pruned: every object of a merged pack is kept.
     432              :      * @return the number of packs removed
     433              :      */
     434              :     static LIBJAMI_TEST_EXPORT std::size_t repack(const std::filesystem::path& path, std::size_t maxPacks);
     435              : 
     436              :     /**
     437              :      * Merge the history of the conversation with another peer
     438              :      * @param uri                    The peer uri
     439              :      * @param disconnectFromPeerCb   Callback to disconnect from peer when banning
     440              :      * @return                       A vector of media maps representing the merged history
     441              :      */
     442              :     std::vector<std::map<std::string, std::string>> mergeHistory(
     443              :         const std::string& uri, std::function<void(const std::string&)>&& disconnectFromPeerCb = {});
     444              : 
     445              :     /**
     446              :      * Retrieve remote head. Can be useful after a fetch operation
     447              :      * @param remoteDeviceId        The remote name
     448              :      * @param branch                Remote branch to check (default: main)
     449              :      * @return the commit id pointed
     450              :      */
     451              :     std::string remoteHead(const std::string& remoteDeviceId, const std::string& branch = "main") const;
     452              : 
     453              :     /**
     454              :      * Return the conversation id
     455              :      */
     456              :     const std::string& id() const;
     457              : 
     458              :     /**
     459              :      * Add a new commit to the conversation
     460              :      * @param msg           The commit message of the commit
     461              :      * @param verifyDevice  If we need to validate that certificates are correct (used for testing)
     462              :      * @return <empty> on failure, else the message id
     463              :      */
     464              :     std::string commitMessage(const std::string& msg, bool verifyDevice = true);
     465              : 
     466              :     std::vector<std::string> commitMessages(const std::vector<std::string>& msgs);
     467              : 
     468              :     /**
     469              :      * Amend a commit message
     470              :      * @param id      The commit to amend
     471              :      * @param msg     The commit message of the commit
     472              :      * @return <empty> on failure, else the message id
     473              :      */
     474              :     std::string amend(const std::string& id, const std::string& msg);
     475              : 
     476              :     /**
     477              :      * Get commits depending on the options we pass
     478              :      * @return a list of commits
     479              :      */
     480              :     std::vector<ConversationCommit> log(const LogOptions& options = {}) const;
     481              :     void log(PreConditionCb&& preCondition,
     482              :              std::function<void(ConversationCommit&&)>&& emplaceCb,
     483              :              PostConditionCb&& postCondition,
     484              :              const std::string& from = "",
     485              :              bool logIfNotFound = true) const;
     486              : 
     487              :     /**
     488              :      * Check if a commit exists in the repository
     489              :      * @param commitId The commit id to check
     490              :      * @return true if the commit was found, false if not or if an error occurred
     491              :      */
     492              :     bool hasCommit(const std::string& commitId) const;
     493              :     std::optional<ConversationCommit> getCommit(const std::string& commitId) const;
     494              : 
     495              :     /**
     496              :      * Get parent via topological + date sort in branch main of a commit
     497              :      * @param commitId      id to choice
     498              :      */
     499              :     std::optional<std::string> linearizedParent(const std::string& commitId) const;
     500              : 
     501              :     /**
     502              :      * Merge another branch into the main branch
     503              :      * @param merge_id      The reference to merge
     504              :      * @param force         Should be false, skip validateDevice() ; used for test purpose
     505              :      * @return a pair containing if the merge was successful and the merge commit id
     506              :      * generated if one (can be a fast forward merge without commit)
     507              :      */
     508              :     std::pair<bool, std::string> merge(const std::string& merge_id, bool force = false);
     509              : 
     510              :     /**
     511              :      * Get current diff stats between two commits
     512              :      * @param oldId     Old commit
     513              :      * @param newId     Recent commit (empty value will compare to the empty repository)
     514              :      * @note "HEAD" is also accepted as parameter for newId
     515              :      * @return diff stats
     516              :      */
     517              :     std::string diffStats(const std::string& newId, const std::string& oldId = "") const;
     518              : 
     519              :     /**
     520              :      * Get changed files from a git diff
     521              :      * @param diffStats     The stats to analyze
     522              :      * @return get the changed files from a git diff
     523              :      */
     524              :     static std::vector<std::string> changedFiles(std::string_view diffStats);
     525              : 
     526              :     /**
     527              :      * Join a repository
     528              :      * @return commit Id
     529              :      */
     530              :     std::string join();
     531              : 
     532              :     /**
     533              :      * Erase self from repository
     534              :      * @return commit Id
     535              :      */
     536              :     std::string leave();
     537              : 
     538              :     /**
     539              :      * Erase repository
     540              :      */
     541              :     void erase();
     542              : 
     543              :     /**
     544              :      * Get conversation's mode
     545              :      * @return the mode
     546              :      */
     547              :     ConversationMode mode() const;
     548              : 
     549              :     /**
     550              :      * Document repositories only (mode() == ConversationMode::DOCUMENT).
     551              :      * Read back the fields of the initial commit: the id of the conversation
     552              :      * the document was announced in and the media type of what it holds.
     553              :      */
     554              :     std::string parentConversationId() const;
     555              :     std::string documentMimeType() const;
     556              : 
     557              :     /**
     558              :      * Document repositories only. Attachments are content-addressed blobs
     559              :      * stored under attachments/<oid>: the file name is the git oid of the
     560              :      * content, so the same bytes added twice converge to the same entry.
     561              :      */
     562              :     /**
     563              :      * Store an attachment and commit it
     564              :      * @param data  The attachment content
     565              :      * @return the attachment id (blob oid) or empty on failure
     566              :      */
     567              :     std::string addAttachment(const std::vector<uint8_t>& data);
     568              :     /**
     569              :      * Read an attachment's content at HEAD
     570              :      */
     571              :     std::vector<uint8_t> attachment(const std::string& attachmentId) const;
     572              :     /**
     573              :      * List attachment ids present at HEAD
     574              :      */
     575              :     std::vector<std::string> attachmentIds() const;
     576              : 
     577              :     /**
     578              :      * The voting system is divided in two parts. The voting phase where
     579              :      * admins can decide an action (such as kicking someone)
     580              :      * and the resolving phase, when > 50% of the admins voted, we can
     581              :      * considered the vote as finished
     582              :      */
     583              :     /**
     584              :      * Add a vote to kick a device or a user
     585              :      * @param uri       identified of the user/device
     586              :      * @param type      device, members, admins or invited
     587              :      * @return the commit id or empty if failed
     588              :      */
     589              :     std::string voteKick(const std::string& uri, const std::string& type);
     590              :     /**
     591              :      * Add a vote to re-add a user
     592              :      * @param uri       identified of the user
     593              :      * @param type      device, members, admins or invited
     594              :      * @return the commit id or empty if failed
     595              :      */
     596              :     std::string voteUnban(const std::string& uri, const std::string_view type);
     597              :     /**
     598              :      * Validate if a vote is finished
     599              :      * @param uri       identified of the user/device
     600              :      * @param type      device, members, admins or invited
     601              :      * @param voteType  "ban" or "unban"
     602              :      * @return the commit id or empty if failed
     603              :      */
     604              :     std::string resolveVote(const std::string& uri, const std::string_view type, const std::string& voteType);
     605              : 
     606              :     /**
     607              :      * Validate a fetch with remote device
     608              :      * @param remotedevice
     609              :      * @return the validated commits and if an error occurs
     610              :      */
     611              :     std::pair<std::vector<ConversationCommit>, bool> validFetch(const std::string& remoteDevice) const;
     612              : 
     613              :     /**
     614              :      * Validate a clone
     615              :      * @return the validated commits and false if an error occurs
     616              :      */
     617              :     std::pair<std::vector<ConversationCommit>, bool> validClone() const;
     618              : 
     619              :     /**
     620              :      * Verify the signature against the given commit
     621              :      * @param userDevice    the email of the sender (i.e. their device's public key)
     622              :      * @param commitId      the id of the commit
     623              :      */
     624              :     bool isValidUserAtCommit(const std::string& userDevice,
     625              :                              const std::string& commitId,
     626              :                              const git_buf& sig,
     627              :                              const git_buf& sig_data) const;
     628              : 
     629              :     /**
     630              :      * Validate that commits are not malformed
     631              :      * @param commitsToValidate     the list of commits
     632              :      */
     633              :     bool validCommits(const std::vector<ConversationCommit>& commitsToValidate) const;
     634              : 
     635              :     /**
     636              :      * Delete branch with remote
     637              :      * @param remoteDevice
     638              :      */
     639              :     void removeBranchWith(const std::string& remoteDevice);
     640              : 
     641              :     /**
     642              :      * One to one util, get initial members
     643              :      * @return initial members
     644              :      */
     645              :     std::vector<std::string> getInitialMembers() const;
     646              : 
     647              :     /**
     648              :      * Get conversation's members
     649              :      * @return members
     650              :      */
     651              :     std::vector<ConversationMember> members() const;
     652              : 
     653              :     /**
     654              :      * Get the permissions granted to a member
     655              :      * @param uri   Member to check
     656              :      * @return permission names, empty if uri is not a member
     657              :      */
     658              :     std::vector<std::string> permissions(const std::string& uri) const;
     659              : 
     660              :     /**
     661              :      * Get conversation's devices
     662              :      * @param ignoreExpired     If we want to ignore expired devices
     663              :      * @return members
     664              :      */
     665              :     std::map<std::string, std::vector<DeviceId>> devices(bool ignoreExpired = true) const;
     666              : 
     667              :     /**
     668              :      * Verify that a device certificate is signed by the corresponding confirmed member
     669              :      * certificate stored in the repository.
     670              :      * @param certificate    Device certificate to verify
     671              :      * @return true if the certificate belongs to a confirmed member
     672              :      */
     673              :     bool isDeviceCertificateFromMember(const dht::crypto::Certificate& certificate) const;
     674              : 
     675              :     /**
     676              :      * @param filter           If we want to remove one member
     677              :      * @param filteredRoles    If we want to ignore some roles
     678              :      * @return members' uris
     679              :      */
     680              :     std::set<std::string> memberUris(std::string_view filter, const std::set<MemberRole>& filteredRoles) const;
     681              : 
     682              :     /**
     683              :      * To use after a merge with member's events, refresh members knowledge
     684              :      */
     685              :     void refreshMembers() const;
     686              : 
     687              :     void onMembersChanged(OnMembersChanged&& cb);
     688              : 
     689              :     /**
     690              :      * Because conversations can contains non contacts certificates, this methods
     691              :      * loads certificates in conversations into the cert store
     692              :      * @param blocking      if we need to wait that certificates are pinned
     693              :      */
     694              :     void pinCertificates(bool blocking = false);
     695              :     /**
     696              :      * Retrieve the uri from a deviceId
     697              :      * @note used by swarm manager (peersToSyncWith)
     698              :      * @param deviceId
     699              :      * @return corresponding issuer
     700              :      */
     701              :     std::string uriFromDevice(const std::string& deviceId) const;
     702              : 
     703              :     /**
     704              :      * Change repository's infos
     705              :      * @param map       New infos (supported keys: title, description, avatar)
     706              :      * @return the commit id
     707              :      */
     708              :     std::string updateInfos(const std::map<std::string, std::string>& map);
     709              : 
     710              :     /**
     711              :      * Retrieve current infos (title, description, avatar, mode)
     712              :      * @return infos
     713              :      */
     714              :     std::map<std::string, std::string> infos() const;
     715              :     static std::map<std::string, std::string> infosFromVCard(vCard::utils::VCardData&& details);
     716              : 
     717              :     /**
     718              :      * Convert ConversationCommit to MapStringString for the client
     719              :      */
     720              :     std::vector<std::map<std::string, std::string>> convCommitsToMap(
     721              :         const std::vector<ConversationCommit>& commits) const;
     722              :     std::optional<std::map<std::string, std::string>> convCommitToMap(const ConversationCommit& commit) const;
     723              : 
     724              :     /**
     725              :      * Get current HEAD hash
     726              :      */
     727              :     std::string getHead() const;
     728              : 
     729              : private:
     730              :     ConversationRepository() = delete;
     731              :     static std::unique_ptr<ConversationRepository> createRepository(const std::shared_ptr<JamiAccount>& account,
     732              :                                                                     ConversationMode mode,
     733              :                                                                     const std::string& otherMember,
     734              :                                                                     const CommitMessage& initialMessage);
     735              :     class Impl;
     736              :     std::unique_ptr<Impl> pimpl_;
     737              : };
     738              : 
     739              : } // namespace jami
     740        16099 : MSGPACK_ADD_ENUM(jami::MemberRole);
     741        14308 : MSGPACK_ADD_ENUM(jami::ConversationMode);
        

Generated by: LCOV version 2.0-1