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);
|