LCOV - code coverage report
Current view: top level - src/jamidht - conversation.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 98.0 % 49 48
Test Date: 2026-11-09 10:41:14 Functions: 93.8 % 16 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              : 
      18              : #pragma once
      19              : 
      20              : #include "jamidht/commit_message.h"
      21              : #include "jamidht/conversationrepository.h"
      22              : #include "jamidht/gitsocket.h"
      23              : #include "conversationrepository.h"
      24              : #include "swarm/swarm_protocol.h"
      25              : #include "jami/conversation_interface.h"
      26              : #include "jamidht/typers.h"
      27              : #include "string_utils.h"
      28              : #include "timestamp.h"
      29              : 
      30              : #include <json/json.h>
      31              : #include <msgpack.hpp>
      32              : 
      33              : #include <chrono>
      34              : #include <functional>
      35              : #include <string>
      36              : #include <vector>
      37              : #include <map>
      38              : #include <memory>
      39              : #include <set>
      40              : 
      41              : #include <asio.hpp>
      42              : 
      43              : namespace dhtnet {
      44              : class ChannelSocket;
      45              : } // namespace dhtnet
      46              : 
      47              : namespace jami {
      48              : 
      49              : namespace ConversationMapKeys {
      50              : static constexpr const char* ID {"id"};
      51              : static constexpr const char* CREATED {"created"};
      52              : static constexpr const char* REMOVED {"removed"};
      53              : static constexpr const char* ERASED {"erased"};
      54              : static constexpr const char* MEMBERS {"members"};
      55              : static constexpr const char* LAST_DISPLAYED {"lastDisplayed"};
      56              : static constexpr const char* RECEIVED {"received"};
      57              : static constexpr const char* DECLINED {"declined"};
      58              : static constexpr const char* FROM {"from"};
      59              : static constexpr const char* CONVERSATIONID {"conversationId"};
      60              : static constexpr const char* METADATAS {"metadatas"};
      61              : static constexpr const char* MODE {"mode"};
      62              : static constexpr const char* PARENT {"parent"};
      63              : // Millisecond-resolution variants. Legacy keys above keep carrying seconds
      64              : // so that older devices (which ignore unknown keys) remain compatible.
      65              : static constexpr const char* CREATED_MS {"createdMs"};
      66              : static constexpr const char* REMOVED_MS {"removedMs"};
      67              : static constexpr const char* ERASED_MS {"erasedMs"};
      68              : static constexpr const char* RECEIVED_MS {"receivedMs"};
      69              : static constexpr const char* DECLINED_MS {"declinedMs"};
      70              : static constexpr const char* INVITED {"invited"};
      71              : } // namespace ConversationMapKeys
      72              : 
      73              : namespace ConversationDirectories {
      74              : static constexpr std::string_view PREFERENCES {"preferences"};
      75              : static constexpr std::string_view STATUS {"status"};
      76              : static constexpr std::string_view SENDING {"sending"};
      77              : static constexpr std::string_view FETCHED {"fetched"};
      78              : static constexpr std::string_view ACTIVE_CALLS {"activeCalls"};
      79              : static constexpr std::string_view HOSTED_CALLS {"hostedCalls"};
      80              : static constexpr std::string_view CACHED {"cached"};
      81              : static constexpr std::string_view MOBILE_NODES {"mobileNodes"};
      82              : } // namespace ConversationDirectories
      83              : 
      84              : namespace ConversationPreferences {
      85              : static constexpr const char* HOST_CONFERENCES = "hostConferences";
      86              : }
      87              : 
      88              : struct MobileNodeTarget
      89              : {
      90              :     NodeId device;
      91              :     std::string uri;
      92              : };
      93              : 
      94              : class JamiAccount;
      95              : class ConversationRepository;
      96              : class TransferManager;
      97              : enum class ConversationMode;
      98              : 
      99              : /**
     100              :  * A ConversationRequest is a request which corresponds to a trust request, but for conversations
     101              :  * It's signed by the sender and contains the members list, the conversationId, and the metadatas
     102              :  * such as the conversation's vcard, etc. (TODO determine)
     103              :  * Transmitted via the UDP DHT
     104              :  */
     105              : struct ConversationRequest
     106              : {
     107              :     std::string conversationId;
     108              :     std::string from;
     109              :     std::map<std::string, std::string> metadatas;
     110              : 
     111              :     TimePoint received {};
     112              :     TimePoint declined {};
     113              : 
     114          771 :     ConversationRequest() = default;
     115              :     ConversationRequest(const Json::Value& json);
     116              : 
     117              :     Json::Value toJson() const;
     118              :     std::map<std::string, std::string> toMap() const;
     119              : 
     120              :     bool operator==(const ConversationRequest& o) const
     121              :     {
     122              :         auto m = toMap();
     123              :         auto om = o.toMap();
     124              :         return m.size() == om.size() && std::equal(m.begin(), m.end(), om.begin());
     125              :     }
     126              : 
     127          767 :     bool isOneToOne() const
     128              :     {
     129              :         try {
     130         2301 :             return metadatas.at("mode") == "0";
     131          272 :         } catch (...) {
     132          272 :         }
     133          272 :         return true;
     134              :     }
     135              : 
     136          401 :     ConversationMode mode() const
     137              :     {
     138              :         try {
     139         1203 :             return to_enum<ConversationMode>(metadatas.at("mode"));
     140          245 :         } catch (...) {
     141          245 :         }
     142          245 :         return ConversationMode::ONE_TO_ONE;
     143              :     }
     144              : 
     145              :     // Hand-written msgpack serialization (replaces MSGPACK_DEFINE_MAP) to emit
     146              :     // dual keys: legacy seconds (received/declined) + milliseconds (receivedMs/declinedMs).
     147              :     // Readers prefer the ms keys and fall back to seconds * 1000.
     148              :     template<typename Packer>
     149          477 :     void msgpack_pack(Packer& pk) const
     150              :     {
     151          477 :         int64_t receivedSec = toSecondsSinceEpoch(received);
     152          477 :         int64_t declinedSec = toSecondsSinceEpoch(declined);
     153          477 :         int64_t receivedMs = toMillisecondsSinceEpoch(received);
     154          477 :         int64_t declinedMs = toMillisecondsSinceEpoch(declined);
     155              :         msgpack::type::make_define_map(ConversationMapKeys::FROM,
     156          477 :                                        from,
     157              :                                        ConversationMapKeys::CONVERSATIONID,
     158          477 :                                        conversationId,
     159              :                                        ConversationMapKeys::METADATAS,
     160          477 :                                        metadatas,
     161              :                                        ConversationMapKeys::RECEIVED,
     162              :                                        receivedSec,
     163              :                                        ConversationMapKeys::DECLINED,
     164              :                                        declinedSec,
     165              :                                        ConversationMapKeys::RECEIVED_MS,
     166              :                                        receivedMs,
     167              :                                        ConversationMapKeys::DECLINED_MS,
     168              :                                        declinedMs)
     169          477 :             .msgpack_pack(pk);
     170          477 :     }
     171              :     void msgpack_unpack(const msgpack::object& o);
     172              :     void msgpack_object(msgpack::object* o, msgpack::zone& z) const;
     173              : };
     174              : 
     175              : struct ConvInfo
     176              : {
     177              :     std::string id {};
     178              :     TimePoint created {};
     179              :     TimePoint removed {};
     180              :     TimePoint erased {};
     181              :     std::set<std::string> members;
     182              :     std::string lastDisplayed {};
     183              :     ConversationMode mode {0};
     184              :     std::string parent {};
     185              :     // Last time we explicitly (re-)invited a given peer (via addConversationMember()/
     186              :     // sendTrustRequest()), keyed by peer URI.
     187              :     std::map<std::string, TimePoint> invited {};
     188              : 
     189         1557 :     ConvInfo() = default;
     190          231 :     ConvInfo(const ConvInfo&) = default;
     191            0 :     ConvInfo(ConvInfo&&) = default;
     192         1313 :     ConvInfo(const std::string& id)
     193         1313 :         : id(id) {};
     194              :     explicit ConvInfo(const Json::Value& json);
     195              : 
     196        16090 :     bool isRemoved() const { return removed >= created; }
     197              : 
     198         4723 :     ConvInfo& operator=(const ConvInfo&) = default;
     199          602 :     ConvInfo& operator=(ConvInfo&&) = default;
     200              : 
     201              :     Json::Value toJson() const;
     202              : 
     203              :     // Hand-written msgpack serialization (replaces MSGPACK_DEFINE_MAP) to emit
     204              :     // dual keys: legacy seconds (created/removed/erased) + milliseconds
     205              :     // (createdMs/removedMs/erasedMs). Readers prefer the ms keys and fall back
     206              :     // to seconds * 1000.
     207              :     template<typename Packer>
     208        13362 :     void msgpack_pack(Packer& pk) const
     209              :     {
     210        13362 :         int64_t createdSec = toSecondsSinceEpoch(created);
     211        13362 :         int64_t removedSec = toSecondsSinceEpoch(removed);
     212        13362 :         int64_t erasedSec = toSecondsSinceEpoch(erased);
     213        13362 :         int64_t createdMs = toMillisecondsSinceEpoch(created);
     214        13362 :         int64_t removedMs = toMillisecondsSinceEpoch(removed);
     215        13362 :         int64_t erasedMs = toMillisecondsSinceEpoch(erased);
     216        13362 :         std::map<std::string, int64_t> invitedMs;
     217        16070 :         for (const auto& [uri, t] : invited)
     218         2708 :             invitedMs[uri] = toMillisecondsSinceEpoch(t);
     219              :         msgpack::type::make_define_map(ConversationMapKeys::ID,
     220        13362 :                                        id,
     221              :                                        ConversationMapKeys::CREATED,
     222              :                                        createdSec,
     223              :                                        ConversationMapKeys::REMOVED,
     224              :                                        removedSec,
     225              :                                        ConversationMapKeys::ERASED,
     226              :                                        erasedSec,
     227              :                                        ConversationMapKeys::MEMBERS,
     228        13362 :                                        members,
     229              :                                        ConversationMapKeys::LAST_DISPLAYED,
     230        13362 :                                        lastDisplayed,
     231              :                                        ConversationMapKeys::MODE,
     232        13362 :                                        mode,
     233              :                                        ConversationMapKeys::PARENT,
     234        13362 :                                        parent,
     235              :                                        ConversationMapKeys::CREATED_MS,
     236              :                                        createdMs,
     237              :                                        ConversationMapKeys::REMOVED_MS,
     238              :                                        removedMs,
     239              :                                        ConversationMapKeys::ERASED_MS,
     240              :                                        erasedMs,
     241              :                                        ConversationMapKeys::INVITED,
     242              :                                        invitedMs)
     243        13362 :             .msgpack_pack(pk);
     244        13362 :     }
     245              :     void msgpack_unpack(const msgpack::object& o);
     246              :     void msgpack_object(msgpack::object* o, msgpack::zone& z) const;
     247              : };
     248              : 
     249              : using OnPullCb = std::function<void(bool fetchOk)>;
     250              : using OnLoadMessages = std::function<void(std::vector<libjami::SwarmMessage>&& messages)>;
     251              : using OnCommitCb = std::function<void(const std::string&)>;
     252              : using OnDoneCb = std::function<void(bool, const std::string&)>;
     253              : using OnMembersChanged = std::function<void(const std::set<std::string>&)>;
     254              : using DeviceId = dht::PkId;
     255              : using GitSocketList = std::map<DeviceId, GitSocket>;
     256              : using ChannelCb = std::function<bool(const std::shared_ptr<dhtnet::ChannelSocket>&)>;
     257              : using NeedSocketCb
     258              :     = std::function<void(const std::string&, const std::string&, ChannelCb&&, const std::string&, bool noNewSocket)>;
     259              : 
     260              : class Conversation : public std::enable_shared_from_this<Conversation>
     261              : {
     262              : public:
     263              :     Conversation(const std::shared_ptr<JamiAccount>& account,
     264              :                  ConversationMode mode,
     265              :                  const std::string& otherMember = "");
     266              :     Conversation(const std::shared_ptr<JamiAccount>& account, const std::string& conversationId = "");
     267              :     Conversation(const std::shared_ptr<JamiAccount>& account,
     268              :                  const std::string& remoteDevice,
     269              :                  const std::string& conversationId);
     270              :     ~Conversation();
     271              : 
     272              :     /**
     273              :      * Print the state of the DRT linked to the conversation
     274              :      */
     275              :     void monitor();
     276              : 
     277              : #ifdef LIBJAMI_TEST
     278              :     enum class BootstrapStatus { FAILED, FALLBACK, SUCCESS };
     279              :     /**
     280              :      * Used by the tests to get whenever the DRT is connected/disconnected
     281              :      */
     282              :     void onBootstrapStatus(const std::function<void(std::string, BootstrapStatus)>& cb);
     283              : 
     284              :     std::vector<libjami::SwarmMessage> loadMessagesSync(const LogOptions& options);
     285              :     void announce(const std::vector<std::map<std::string, std::string>>& commits, bool commitFromSelf = false);
     286              :     void announce(const std::string& commitId, bool commitFromSelf = false);
     287              : 
     288              :     /**
     289              :      * Construct a Conversation directly from a repository and its commits, forwarding to the
     290              :      * corresponding Conversation::Impl constructor. Used by the tests to open a conversation while
     291              :      * initializing the active-calls list from the given commits (as the daemon does after a clone).
     292              :      */
     293              :     Conversation(std::unique_ptr<ConversationRepository>&& repository,
     294              :                  const std::shared_ptr<JamiAccount>& account,
     295              :                  std::vector<ConversationCommit>&& commits = {});
     296              : #endif
     297              : 
     298              :     /**
     299              :      * Bootstrap swarm manager to other peers
     300              :      * @param onBootstrapped     Callback called when connection is established successfully
     301              :      * @param knownDevices       Optional list of live devices to seed the DRT with.
     302              :      *                           Normally empty: candidates are fed by the per-device
     303              :      *                           presence monitoring through addKnownDevices().
     304              :      */
     305              :     void bootstrap(std::function<void()> onBootstrapped, const std::vector<DeviceId>& knownDevices = {});
     306              : 
     307              :     /**
     308              :      * Add known devices to the swarm manager
     309              :      * @param devices
     310              :      * @param memberUri
     311              :      */
     312              :     void addKnownDevices(const std::vector<DeviceId>& devices, const std::string& memberUri);
     313              : 
     314              :     /**
     315              :      * Proactively connect a device in the swarm, bypassing DRT bucket checks.
     316              :      * Used when a TCP link to the device already exists.
     317              :      * @param deviceId
     318              :      */
     319              :     void connectNode(const DeviceId& deviceId);
     320              : 
     321              :     /**
     322              :      * Refresh active calls.
     323              :      * @note: If the host crash during a call, when initializing, we need to update
     324              :      * and commit all the crashed calls
     325              :      * @return  Commits added
     326              :      */
     327              :     std::vector<std::string> commitsEndedCalls();
     328              : 
     329              :     void onMembersChanged(OnMembersChanged&& cb);
     330              : 
     331              :     /**
     332              :      * Set the callback that will be called whenever a new socket will be needed
     333              :      * @param cb
     334              :      */
     335              :     void onNeedSocket(NeedSocketCb cb);
     336              :     /**
     337              :      * Add swarm connection to the DRT
     338              :      * @param channel       Related channel
     339              :      */
     340              :     void addSwarmChannel(std::shared_ptr<dhtnet::ChannelSocket> channel);
     341              : 
     342              :     /**
     343              :      * Get conversation's id
     344              :      * @return conversation Id
     345              :      */
     346              :     std::string id() const;
     347              : 
     348              :     // Member management
     349              :     /**
     350              :      * Add conversation member
     351              :      * @param uri   Member to add
     352              :      * @param cb    On done cb
     353              :      */
     354              :     void addMember(const std::string& contactUri, const OnDoneCb& cb = {});
     355              :     void removeMember(const std::string& contactUri, bool isDevice, const OnDoneCb& cb = {});
     356              :     /**
     357              :      * @param includeInvited        If we want invited members
     358              :      * @param includeLeft           If we want left members
     359              :      * @param includeBanned         If we want banned members
     360              :      * @return a vector of member details:
     361              :      * {
     362              :      *  "uri":"xxx",
     363              :      *  "role":"member/admin/invited",
     364              :      *  "lastDisplayed":"id"
     365              :      *  ...
     366              :      * }
     367              :      */
     368              :     std::vector<std::map<std::string, std::string>> getMembers(bool includeInvited = false,
     369              :                                                                bool includeLeft = false,
     370              :                                                                bool includeBanned = false) const;
     371              : 
     372              :     /**
     373              :      * @param filter           If we want to remove one member
     374              :      * @param filteredRoles    If we want to ignore some roles
     375              :      * @return members' uris
     376              :      */
     377              :     std::set<std::string> memberUris(std::string_view filter = {},
     378              :                                      const std::set<MemberRole>& filteredRoles = {MemberRole::INVITED,
     379              :                                                                                   MemberRole::LEFT,
     380              :                                                                                   MemberRole::BANNED}) const;
     381              : 
     382              :     std::vector<std::map<std::string, std::string>> getTrackedMembers() const;
     383              : 
     384              :     /**
     385              :      * Get peers to sync with. This is mostly managed by the DRT
     386              :      * @return all connected nodes and nodes with a git socket
     387              :      */
     388              :     std::vector<NodeId> peersToSyncWith() const;
     389              :     /**
     390              :      * Get the mobile nodes this device is responsible for waking up
     391              :      * when a new message must be synced. Mobile nodes are not actively
     392              :      * connected to the DRT, so they are notified via a DHT message
     393              :      * (converted to a push notification by the proxy).
     394              :      * @return mobile nodes we are closer to than any connected node
     395              :      */
     396              :     std::vector<MobileNodeTarget> mobileNodesToNotify() const;
     397              :     /**
     398              :      * Check if we're at least connected to one node
     399              :      * @return if the DRT is connected
     400              :      */
     401              :     bool isBootstrapped() const;
     402              :     /**
     403              :      * Retrieve the uri from a deviceId
     404              :      * @note used by swarm manager (peersToSyncWith)
     405              :      * @param deviceId
     406              :      * @return corresponding issuer
     407              :      */
     408              :     std::string uriFromDevice(const std::string& deviceId) const;
     409              : 
     410              :     /**
     411              :      * The devices recorded in the repository, per member.
     412              :      * @return member uri -> that member's device ids
     413              :      */
     414              :     std::map<std::string, std::vector<DeviceId>> memberDevices() const;
     415              : 
     416              :     /**
     417              :      * Join a conversation
     418              :      * @return commit id to send
     419              :      */
     420              :     std::string join();
     421              : 
     422              :     /**
     423              :      * Test if an URI is a member
     424              :      * @param uri       URI to test
     425              :      * @return true if uri is a member
     426              :      */
     427              :     bool isMember(const std::string& uri, bool includeInvited = false) const;
     428              :     bool isMemberBanned(const std::string& uri) const;
     429              :     bool isDeviceBanned(const std::string& deviceId) const;
     430              : 
     431              :     /**
     432              :      * Get the permissions granted to a member
     433              :      * @param uri   Member to check
     434              :      * @return permission names, empty if uri is not a member
     435              :      */
     436              :     std::vector<std::string> permissions(const std::string& uri) const;
     437              : 
     438              :     /**
     439              :      * Check if a device is authorized to clone or interact with the conversation.
     440              :      * Assumption: the deviceId must already be a confirmed device of the user.
     441              :      * @param uri               URI of the user
     442              :      * @param deviceId          Device id to check
     443              :      * @param includeInvited    If true, consider invited members as authorized.
     444              :      */
     445              :     bool isPeerAuthorized(const std::string& uri, const std::string& deviceId, bool includeInvited = false) const;
     446              : 
     447              :     /**
     448              :      * Check whether a device certificate belongs to a confirmed, non-banned member.
     449              :      * The device certificate is verified against the member certificate stored in the repository.
     450              :      */
     451              :     bool isPeerCertificateAuthorized(const dht::crypto::Certificate& certificate) const;
     452              : 
     453              :     void createCommit(CommitMessage&& message, OnCommitCb&& onCommit = {}, OnDoneCb&& cb = {});
     454              : 
     455              :     /**
     456              :      * Get a range of messages
     457              :      * @param cb        The callback when loaded
     458              :      * @param options   The log options
     459              :      */
     460              :     void loadMessages(const OnLoadMessages& cb, const LogOptions& options);
     461              :     /**
     462              :      * For every loaded TEXT message that has no bodyOverwrite in pluginData, runs the plugin
     463              :      * ChatServicesManager transform to obtain one.  If the plugin produces a bodyOverwrite,
     464              :      * stores it in pluginData and emits SwarmMessageUpdated for the affected message
     465              :      * (edition commits signal the original message; originals signal themselves).
     466              :      * No-op when no plugin chat handlers are registered.
     467              :      */
     468              :     void loadMissingBodyOverwrites() const;
     469              :     /**
     470              :      * Drops all bodyOverwrite entries from the loaded message history, then calls
     471              :      * loadMissingBodyOverwrites() to re-derive them from scratch via the plugin transform.
     472              :      * Use when plugin chat handlers are reloaded or their transform logic has changed.
     473              :      */
     474              :     void reloadBodyOverwriteMessages() const;
     475              :     /**
     476              :      * Stores or clears the plugin-provided bodyOverwrite for a single message.
     477              :      * An empty bodyOverwrite sets the entry to "" (marks the message as processed with no
     478              :      * overwrite, preventing reprocessing); a non-empty value replaces it.
     479              :      * If messageId refers to an edition commit, SwarmMessageUpdated is emitted for the
     480              :      * original message (the one clients display); otherwise it is emitted for messageId itself.
     481              :      * @param messageId     Id of the message (original or edition commit) to update.
     482              :      * @param bodyOverwrite Plugin-transformed body text, or empty to clear the override.
     483              :      */
     484              :     void updateMessageBodyOverwrite(const std::string& messageId, const std::string& bodyOverwrite) const;
     485              :     /**
     486              :      * Removes all bodyOverwrite entries from every message in the loaded history and emits
     487              :      * SwarmMessageUpdated for each affected message so clients revert to the original
     488              :      * body.  Edition commits are resolved to their original message before signalling.
     489              :      */
     490              :     void clearBodyOverwrites() const;
     491              :     /**
     492              :      * Clear all cached messages
     493              :      */
     494              :     void clearCache();
     495              :     /**
     496              :      * Check if a commit exists in the repository
     497              :      * @param commitId The commit id to check
     498              :      * @return true if the commit was found, false if not or if an error occurred
     499              :      */
     500              :     bool hasCommit(const std::string& commitId) const;
     501              :     /**
     502              :      * Retrieve one commit
     503              :      * @param   commitId
     504              :      * @return  The commit if found
     505              :      */
     506              :     std::optional<ConversationCommit> getCommit(const std::string& commitId) const;
     507              :     /**
     508              :      * List every collaborative document announced in this conversation, by scanning the
     509              :      * repository for COLLAB_DOC commits rather than the loaded message history. Lets a
     510              :      * client show the editable documents without first paging in the (possibly old)
     511              :      * announcing messages.
     512              :      * @return  one map per COLLAB_DOC commit ("uri" = document id, "displayName",
     513              :      *          "mimeType", "author", "timestamp"), newest first
     514              :      */
     515              :     std::vector<std::map<std::string, std::string>> collaborativeDocuments() const;
     516              :     /**
     517              :      * Get last commit id
     518              :      * @return last commit id
     519              :      */
     520              :     std::string lastCommitId() const;
     521              : 
     522              :     /**
     523              :      * Fetch and merge from peer
     524              :      * @param deviceId  Peer device
     525              :      * @param cb        On pulled callback
     526              :      * @param commitId  Commit id that triggered this fetch
     527              :      * @return true if callback will be called later
     528              :      */
     529              :     bool pull(const std::string& deviceId, OnPullCb&& cb, std::string commitId = "");
     530              :     /**
     531              :      * Fetch new commits and re-ask for waiting files
     532              :      * @param member
     533              :      * @param deviceId
     534              :      * @param cb        cf pull()
     535              :      * @param commitId  cf pull()
     536              :      */
     537              :     void sync(const std::string& member, const std::string& deviceId, OnPullCb&& cb, std::string commitId = "");
     538              : 
     539              :     /**
     540              :      * Generate an invitation to send to new contacts
     541              :      * @param sent  timestamp of the explicit action that caused this invite to be (re)sent
     542              :      *              (e.g. addConversationMember()). Callers that merely relay/resend an
     543              :      *              already-generated invite must pass the originally recorded timestamp.
     544              :      * @return the invite to send
     545              :      */
     546              :     std::map<std::string, std::string> generateInvitation(TimePoint sent) const;
     547              : 
     548              :     /**
     549              :      * Leave a conversation
     550              :      * @return commit id to send
     551              :      */
     552              :     std::string leave();
     553              : 
     554              :     /**
     555              :      * Set a conversation as removing (when loading convInfo and still not sync)
     556              :      * @todo: not a big fan to see this here. can be set in the constructor
     557              :      * cause it's used by jamiaccount when loading conversations
     558              :      */
     559              :     void setRemovingFlag();
     560              : 
     561              :     /**
     562              :      * Check if we are removing the conversation
     563              :      * @return true if left the room
     564              :      */
     565              :     bool isRemoving();
     566              : 
     567              :     /**
     568              :      * Erase all related datas
     569              :      */
     570              :     void erase();
     571              : 
     572              :     /**
     573              :      * Get conversation's mode
     574              :      * @return the mode
     575              :      */
     576              :     ConversationMode mode() const;
     577              : 
     578              :     /**
     579              :      * For a collaborative document (mode DOCUMENT), the id of the conversation
     580              :      * that announced it, read from the initial commit.
     581              :      * @return empty for any other mode
     582              :      */
     583              :     std::string parentConversationId() const;
     584              : 
     585              :     /**
     586              :      * For a collaborative document, the MIME type of what it holds, read from
     587              :      * the initial commit.
     588              :      * @return empty for any other mode
     589              :      */
     590              :     std::string documentMimeType() const;
     591              : 
     592              :     /**
     593              :      * For a collaborative document, every CRDT update persisted in the
     594              :      * repository, oldest first: the base64-encoded lines of the body of each
     595              :      * checkpoint commit, in the order they must be replayed.
     596              :      */
     597              :     std::vector<std::string> documentUpdates() const;
     598              : 
     599              :     /**
     600              :      * Same as documentUpdates() but only up to (and including) the given
     601              :      * commit, to rebuild the document as it was at that point.
     602              :      * @return std::nullopt when the commit is unknown, as opposed to an empty
     603              :      *         document at a known commit
     604              :      */
     605              :     std::optional<std::vector<std::string>> documentUpdatesAt(const std::string& commitId) const;
     606              : 
     607              :     /**
     608              :      * For a collaborative document, its persisted history: one map per
     609              :      * checkpoint commit, newest first, with keys "id", "author", "device",
     610              :      * "timestamp" and "deltas" (the number of updates the checkpoint carries).
     611              :      * @param max  stop after this many entries; 0 for no limit
     612              :      */
     613              :     std::vector<std::map<std::string, std::string>> documentHistory(size_t max) const;
     614              : 
     615              :     /**
     616              :      * For a collaborative document, store an attachment and announce the
     617              :      * commit to the swarm.
     618              :      * @return {attachmentId, commitId}; commitId is empty when the same
     619              :      *         content was already attached (nothing new to announce) and both
     620              :      *         are empty on failure
     621              :      */
     622              :     std::pair<std::string, std::string> addDocumentAttachment(const std::vector<uint8_t>& data);
     623              : 
     624              :     /**
     625              :      * For a collaborative document, read an attachment's content at HEAD.
     626              :      */
     627              :     std::vector<uint8_t> documentAttachment(const std::string& attachmentId) const;
     628              : 
     629              :     /**
     630              :      * For a collaborative document, list the attachment ids present at HEAD.
     631              :      */
     632              :     std::vector<std::string> documentAttachmentIds() const;
     633              : 
     634              :     /**
     635              :      * One to one util, get initial members
     636              :      * @return initial members
     637              :      */
     638              :     std::vector<std::string> getInitialMembers() const;
     639              :     bool isInitialMember(const std::string& uri) const;
     640              : 
     641              :     /**
     642              :      * Change repository's infos
     643              :      * @param map       New infos (supported keys: title, description, avatar)
     644              :      * @param cb        On commited
     645              :      */
     646              :     void updateInfos(const std::map<std::string, std::string>& map, const OnDoneCb& cb = {});
     647              : 
     648              :     /**
     649              :      * Change user's preferences
     650              :      * @param map       New preferences
     651              :      */
     652              :     void updatePreferences(const std::map<std::string, std::string>& map);
     653              : 
     654              :     /**
     655              :      * Retrieve current infos (title, description, avatar, mode)
     656              :      * @return infos
     657              :      */
     658              :     std::map<std::string, std::string> infos() const;
     659              :     /**
     660              :      * Retrieve current preferences (color, notification, etc)
     661              :      * @param includeLastModified       If we want to know when the preferences were modified
     662              :      * @return preferences
     663              :      */
     664              :     std::map<std::string, std::string> preferences(bool includeLastModified) const;
     665              :     std::vector<uint8_t> vCard() const;
     666              : 
     667              :     /////// File transfer
     668              : 
     669              :     /**
     670              :      * Access to transfer manager
     671              :      */
     672              :     std::shared_ptr<TransferManager> dataTransfer() const;
     673              : 
     674              :     /**
     675              :      * Choose if we can accept channel request
     676              :      * @param member        member to check
     677              :      * @param fileId        file transfer to check (needs to be waiting)
     678              :      * @param verifyShaSum  for debug only
     679              :      * @return if we accept the channel request
     680              :      */
     681              :     bool onFileChannelRequest(const std::string& member,
     682              :                               const std::string& fileId,
     683              :                               std::filesystem::path& path,
     684              :                               std::string& sha3sum) const;
     685              :     /**
     686              :      * Adds a file to the waiting list and ask members
     687              :      * @param interactionId     Related interaction id
     688              :      * @param fileId            Related id
     689              :      * @param path              Destination
     690              :      * @param member            Member if we know from who to pull file
     691              :      * @param deviceId          Device if we know from who to pull file
     692              :      * @return id of the file
     693              :      */
     694              :     bool downloadFile(const std::string& interactionId,
     695              :                       const std::string& fileId,
     696              :                       const std::string& path,
     697              :                       const std::string& member = "",
     698              :                       const std::string& deviceId = "");
     699              : 
     700              :     /**
     701              :      * Reset fetched information
     702              :      */
     703              :     void clearFetched();
     704              :     /**
     705              :      * Store information about who fetch or not. This simplify sync (sync when a device without the
     706              :      * last fetch is detected)
     707              :      * @param deviceId
     708              :      * @param commitId
     709              :      */
     710              :     void hasFetched(const std::string& deviceId, const std::string& commitId);
     711              : 
     712              :     /**
     713              :      * Store last read commit (returned in getMembers)
     714              :      * @param uri               Of the member
     715              :      * @param interactionId     Last interaction displayed
     716              :      * @return if updated
     717              :      */
     718              :     bool setMessageDisplayed(const std::string& uri, const std::string& interactionId);
     719              :     /**
     720              :      * Retrieve last displayed and fetch status per member
     721              :      * @return A map with the following structure:
     722              :      * {uri, {
     723              :      *          {"fetch", "commitId"},
     724              :      *          {"fetched_ts", "timestamp"},
     725              :      *          {"read", "commitId"},
     726              :      *          {"read_ts", "timestamp"}
     727              :      *       }
     728              :      * }
     729              :      */
     730              :     std::map<std::string, std::map<std::string, std::string>> messageStatus() const;
     731              :     /**
     732              :      * Update fetch/read status
     733              :      * @param messageStatus     A map with the following structure:
     734              :      * {uri, {
     735              :      *          {"fetch", "commitId"},
     736              :      *          {"fetched_ts", "timestamp"},
     737              :      *          {"read", "commitId"},
     738              :      *          {"read_ts", "timestamp"}
     739              :      *       }
     740              :      * }
     741              :      */
     742              :     void updateMessageStatus(const std::map<std::string, std::map<std::string, std::string>>& messageStatus);
     743              :     void onMessageStatusChanged(
     744              :         const std::function<void(const std::map<std::string, std::map<std::string, std::string>>&)>& cb);
     745              :     /**
     746              :      * Retrieve how many interactions there is from HEAD to interactionId
     747              :      * @param toId      "" for getting the whole history
     748              :      * @param fromId    "" => HEAD
     749              :      * @param authorURI author to stop counting
     750              :      * @return number of interactions since interactionId
     751              :      */
     752              :     uint32_t countInteractions(const std::string& toId,
     753              :                                const std::string& fromId = "",
     754              :                                const std::string& authorUri = "") const;
     755              :     /**
     756              :      * Search in the conversation via a filter
     757              :      * @param req       Id of the request
     758              :      * @param filter    Parameters for the search
     759              :      * @param flag      To check when search is finished
     760              :      * @note triggers messagesFound
     761              :      */
     762              :     void search(uint32_t req, const Filter& filter, const std::shared_ptr<std::atomic_int>& flag) const;
     763              :     /**
     764              :      * Host a conference in the conversation
     765              :      * @note the message must have "confId"
     766              :      * @note Update hostedCalls_ and commit in the conversation
     767              :      * @param message       message to commit
     768              :      * @param cb            callback triggered when committed
     769              :      */
     770              :     void hostConference(CommitMessage&& message, OnDoneCb&& cb = {});
     771              :     /**
     772              :      * Announce the end of a call
     773              :      * @note the message must have "confId"
     774              :      * @note called when conference is finished
     775              :      * @param message       message to commit
     776              :      * @param cb            callback triggered when committed
     777              :      */
     778              :     void removeActiveConference(CommitMessage&& message, OnDoneCb&& cb = {});
     779              :     /**
     780              :      * Check if we're currently hosting this conference
     781              :      * @param confId
     782              :      * @return true if hosting
     783              :      */
     784              :     bool isHosting(const std::string& confId) const;
     785              :     /**
     786              :      * Return current detected calls
     787              :      * @return a vector of map with the following keys: "id", "uri", "device"
     788              :      */
     789              :     std::vector<std::map<std::string, std::string>> currentCalls() const;
     790              : 
     791              :     /**
     792              :      * Git operations will need a ChannelSocket for cloning/fetching commits
     793              :      * Because libgit2 is a C library, we store the pointer in the corresponding conversation
     794              :      * and the GitTransport will inject to libgit2 whenever needed
     795              :      */
     796              :     std::shared_ptr<dhtnet::ChannelSocket> gitSocket(const DeviceId& deviceId) const;
     797              :     void addGitSocket(const DeviceId& deviceId, const std::shared_ptr<dhtnet::ChannelSocket>& socket);
     798              :     /** If @p expected is set, only remove the socket if it is still the registered one. */
     799              :     void removeGitSocket(const DeviceId& deviceId, const std::shared_ptr<dhtnet::ChannelSocket>& expected = {});
     800              : 
     801              :     /**
     802              :      * Stop SwarmManager, bootstrap and gitSockets
     803              :      */
     804              :     void shutdownConnections();
     805              : 
     806              :     /**
     807              :      * If we change from one network to one another, we will need to update the state of the connections
     808              :      */
     809              :     void connectivityChanged();
     810              : 
     811              :     /**
     812              :      * @return getAllNodes()    Nodes that are linked to the conversation
     813              :      */
     814              :     std::vector<jami::DeviceId> getDeviceIdList() const;
     815              : 
     816              :     /**
     817              :      * Get Typers object
     818              :      * @return Typers object
     819              :      */
     820              :     std::shared_ptr<Typers> typers() const;
     821              : 
     822              :     /**
     823              :      * Get connectivity information for the conversation
     824              :      * @return map of connectivity info
     825              :      */
     826              :     std::vector<std::map<std::string, std::string>> getConnectivity() const;
     827              : 
     828              : private:
     829              :     std::shared_ptr<Conversation> shared() { return std::static_pointer_cast<Conversation>(shared_from_this()); }
     830              :     std::shared_ptr<Conversation const> shared() const
     831              :     {
     832              :         return std::static_pointer_cast<Conversation const>(shared_from_this());
     833              :     }
     834         5473 :     std::weak_ptr<Conversation> weak() { return std::static_pointer_cast<Conversation>(shared_from_this()); }
     835            4 :     std::weak_ptr<Conversation const> weak() const
     836              :     {
     837            4 :         return std::static_pointer_cast<Conversation const>(shared_from_this());
     838              :     }
     839              : 
     840              :     // Private because of weak()
     841              :     class Impl;
     842              :     std::unique_ptr<Impl> pimpl_;
     843              : };
     844              : 
     845              : } // namespace jami
        

Generated by: LCOV version 2.0-1