LCOV - code coverage report
Current view: top level - src/jamidht - conversation.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 95.8 % 24 23
Test Date: 2026-06-13 09:18:46 Functions: 94.4 % 18 17

            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 "conversationrepository.h"
      23              : #include "swarm/swarm_protocol.h"
      24              : #include "jami/conversation_interface.h"
      25              : #include "jamidht/typers.h"
      26              : #include "string_utils.h"
      27              : 
      28              : #include <json/json.h>
      29              : #include <msgpack.hpp>
      30              : 
      31              : #include <functional>
      32              : #include <string>
      33              : #include <vector>
      34              : #include <map>
      35              : #include <memory>
      36              : #include <set>
      37              : 
      38              : #include <asio.hpp>
      39              : 
      40              : namespace dhtnet {
      41              : class ChannelSocket;
      42              : } // namespace dhtnet
      43              : 
      44              : namespace jami {
      45              : 
      46              : namespace ConversationMapKeys {
      47              : static constexpr const char* ID {"id"};
      48              : static constexpr const char* CREATED {"created"};
      49              : static constexpr const char* REMOVED {"removed"};
      50              : static constexpr const char* ERASED {"erased"};
      51              : static constexpr const char* MEMBERS {"members"};
      52              : static constexpr const char* LAST_DISPLAYED {"lastDisplayed"};
      53              : static constexpr const char* RECEIVED {"received"};
      54              : static constexpr const char* DECLINED {"declined"};
      55              : static constexpr const char* FROM {"from"};
      56              : static constexpr const char* CONVERSATIONID {"conversationId"};
      57              : static constexpr const char* METADATAS {"metadatas"};
      58              : } // namespace ConversationMapKeys
      59              : 
      60              : namespace ConversationDirectories {
      61              : static constexpr std::string_view PREFERENCES {"preferences"};
      62              : static constexpr std::string_view STATUS {"status"};
      63              : static constexpr std::string_view SENDING {"sending"};
      64              : static constexpr std::string_view FETCHED {"fetched"};
      65              : static constexpr std::string_view ACTIVE_CALLS {"activeCalls"};
      66              : static constexpr std::string_view HOSTED_CALLS {"hostedCalls"};
      67              : static constexpr std::string_view CACHED {"cached"};
      68              : } // namespace ConversationDirectories
      69              : 
      70              : namespace ConversationPreferences {
      71              : static constexpr const char* HOST_CONFERENCES = "hostConferences";
      72              : }
      73              : 
      74              : class JamiAccount;
      75              : class ConversationRepository;
      76              : class TransferManager;
      77              : enum class ConversationMode;
      78              : 
      79              : /**
      80              :  * A ConversationRequest is a request which corresponds to a trust request, but for conversations
      81              :  * It's signed by the sender and contains the members list, the conversationId, and the metadatas
      82              :  * such as the conversation's vcard, etc. (TODO determine)
      83              :  * Transmitted via the UDP DHT
      84              :  */
      85              : struct ConversationRequest
      86              : {
      87              :     std::string conversationId;
      88              :     std::string from;
      89              :     std::map<std::string, std::string> metadatas;
      90              : 
      91              :     time_t received {0};
      92              :     time_t declined {0};
      93              : 
      94          346 :     ConversationRequest() = default;
      95              :     ConversationRequest(const Json::Value& json);
      96              : 
      97              :     Json::Value toJson() const;
      98              :     std::map<std::string, std::string> toMap() const;
      99              : 
     100              :     bool operator==(const ConversationRequest& o) const
     101              :     {
     102              :         auto m = toMap();
     103              :         auto om = o.toMap();
     104              :         return m.size() == om.size() && std::equal(m.begin(), m.end(), om.begin());
     105              :     }
     106              : 
     107          475 :     bool isOneToOne() const
     108              :     {
     109              :         try {
     110         1425 :             return metadatas.at("mode") == "0";
     111           75 :         } catch (...) {
     112           75 :         }
     113           75 :         return true;
     114              :     }
     115              : 
     116          174 :     ConversationMode mode() const
     117              :     {
     118              :         try {
     119          522 :             return to_enum<ConversationMode>(metadatas.at("mode"));
     120           50 :         } catch (...) {
     121           50 :         }
     122           50 :         return ConversationMode::ONE_TO_ONE;
     123              :     }
     124              : 
     125          296 :     MSGPACK_DEFINE_MAP(from, conversationId, metadatas, received, declined)
     126              : };
     127              : 
     128              : struct ConvInfo
     129              : {
     130              :     std::string id {};
     131              :     time_t created {0};
     132              :     time_t removed {0};
     133              :     time_t erased {0};
     134              :     std::set<std::string> members;
     135              :     std::string lastDisplayed {};
     136              :     ConversationMode mode {0};
     137              : 
     138          676 :     ConvInfo() = default;
     139           27 :     ConvInfo(const ConvInfo&) = default;
     140            0 :     ConvInfo(ConvInfo&&) = default;
     141          552 :     ConvInfo(const std::string& id)
     142          552 :         : id(id) {};
     143              :     explicit ConvInfo(const Json::Value& json);
     144              : 
     145        16400 :     bool isRemoved() const { return removed >= created; }
     146              : 
     147         2857 :     ConvInfo& operator=(const ConvInfo&) = default;
     148          187 :     ConvInfo& operator=(ConvInfo&&) = default;
     149              : 
     150              :     Json::Value toJson() const;
     151              : 
     152         3329 :     MSGPACK_DEFINE_MAP(id, created, removed, erased, members, lastDisplayed, mode)
     153              : };
     154              : 
     155              : using OnPullCb = std::function<void(bool fetchOk)>;
     156              : using OnLoadMessages = std::function<void(std::vector<libjami::SwarmMessage>&& messages)>;
     157              : using OnCommitCb = std::function<void(const std::string&)>;
     158              : using OnDoneCb = std::function<void(bool, const std::string&)>;
     159              : using OnMembersChanged = std::function<void(const std::set<std::string>&)>;
     160              : using DeviceId = dht::PkId;
     161              : using GitSocketList = std::map<DeviceId, std::shared_ptr<dhtnet::ChannelSocket>>;
     162              : using ChannelCb = std::function<bool(const std::shared_ptr<dhtnet::ChannelSocket>&)>;
     163              : using NeedSocketCb
     164              :     = std::function<void(const std::string&, const std::string&, ChannelCb&&, const std::string&, bool noNewSocket)>;
     165              : 
     166              : class Conversation : public std::enable_shared_from_this<Conversation>
     167              : {
     168              : public:
     169              :     Conversation(const std::shared_ptr<JamiAccount>& account,
     170              :                  ConversationMode mode,
     171              :                  const std::string& otherMember = "");
     172              :     Conversation(const std::shared_ptr<JamiAccount>& account, const std::string& conversationId = "");
     173              :     Conversation(const std::shared_ptr<JamiAccount>& account,
     174              :                  const std::string& remoteDevice,
     175              :                  const std::string& conversationId);
     176              :     ~Conversation();
     177              : 
     178              :     /**
     179              :      * Print the state of the DRT linked to the conversation
     180              :      */
     181              :     void monitor();
     182              : 
     183              : #ifdef LIBJAMI_TEST
     184              :     enum class BootstrapStatus { FAILED, FALLBACK, SUCCESS };
     185              :     /**
     186              :      * Used by the tests to get whenever the DRT is connected/disconnected
     187              :      */
     188              :     void onBootstrapStatus(const std::function<void(std::string, BootstrapStatus)>& cb);
     189              : 
     190              :     std::vector<libjami::SwarmMessage> loadMessagesSync(const LogOptions& options);
     191              :     void announce(const std::vector<std::map<std::string, std::string>>& commits, bool commitFromSelf = false);
     192              :     void announce(const std::string& commitId, bool commitFromSelf = false);
     193              : #endif
     194              : 
     195              :     /**
     196              :      * Bootstrap swarm manager to other peers
     197              :      * @param onBootstrapped     Callback called when connection is established successfully
     198              :      * @param knownDevices       Optional list of live devices to seed the DRT with.
     199              :      *                           Normally empty: candidates are fed by the per-device
     200              :      *                           presence monitoring through addKnownDevices().
     201              :      */
     202              :     void bootstrap(std::function<void()> onBootstrapped, const std::vector<DeviceId>& knownDevices = {});
     203              : 
     204              :     /**
     205              :      * Add known devices to the swarm manager
     206              :      * @param devices
     207              :      * @param memberUri
     208              :      */
     209              :     void addKnownDevices(const std::vector<DeviceId>& devices, const std::string& memberUri);
     210              : 
     211              :     /**
     212              :      * Proactively connect a device in the swarm, bypassing DRT bucket checks.
     213              :      * Used when a TCP link to the device already exists.
     214              :      * @param deviceId
     215              :      */
     216              :     void connectNode(const DeviceId& deviceId);
     217              : 
     218              :     /**
     219              :      * Refresh active calls.
     220              :      * @note: If the host crash during a call, when initializing, we need to update
     221              :      * and commit all the crashed calls
     222              :      * @return  Commits added
     223              :      */
     224              :     std::vector<std::string> commitsEndedCalls();
     225              : 
     226              :     void onMembersChanged(OnMembersChanged&& cb);
     227              : 
     228              :     /**
     229              :      * Set the callback that will be called whenever a new socket will be needed
     230              :      * @param cb
     231              :      */
     232              :     void onNeedSocket(NeedSocketCb cb);
     233              :     /**
     234              :      * Add swarm connection to the DRT
     235              :      * @param channel       Related channel
     236              :      */
     237              :     void addSwarmChannel(std::shared_ptr<dhtnet::ChannelSocket> channel);
     238              : 
     239              :     /**
     240              :      * Get conversation's id
     241              :      * @return conversation Id
     242              :      */
     243              :     std::string id() const;
     244              : 
     245              :     // Member management
     246              :     /**
     247              :      * Add conversation member
     248              :      * @param uri   Member to add
     249              :      * @param cb    On done cb
     250              :      */
     251              :     void addMember(const std::string& contactUri, const OnDoneCb& cb = {});
     252              :     void removeMember(const std::string& contactUri, bool isDevice, const OnDoneCb& cb = {});
     253              :     /**
     254              :      * @param includeInvited        If we want invited members
     255              :      * @param includeLeft           If we want left members
     256              :      * @param includeBanned         If we want banned members
     257              :      * @return a vector of member details:
     258              :      * {
     259              :      *  "uri":"xxx",
     260              :      *  "role":"member/admin/invited",
     261              :      *  "lastDisplayed":"id"
     262              :      *  ...
     263              :      * }
     264              :      */
     265              :     std::vector<std::map<std::string, std::string>> getMembers(bool includeInvited = false,
     266              :                                                                bool includeLeft = false,
     267              :                                                                bool includeBanned = false) const;
     268              : 
     269              :     /**
     270              :      * @param filter           If we want to remove one member
     271              :      * @param filteredRoles    If we want to ignore some roles
     272              :      * @return members' uris
     273              :      */
     274              :     std::set<std::string> memberUris(std::string_view filter = {},
     275              :                                      const std::set<MemberRole>& filteredRoles = {MemberRole::INVITED,
     276              :                                                                                   MemberRole::LEFT,
     277              :                                                                                   MemberRole::BANNED}) const;
     278              : 
     279              :     std::vector<std::map<std::string, std::string>> getTrackedMembers() const;
     280              : 
     281              :     /**
     282              :      * Get peers to sync with. This is mostly managed by the DRT
     283              :      * @return some mobile nodes and all connected nodes
     284              :      */
     285              :     std::vector<NodeId> peersToSyncWith() const;
     286              :     /**
     287              :      * Check if we're at least connected to one node
     288              :      * @return if the DRT is connected
     289              :      */
     290              :     bool isBootstrapped() const;
     291              :     /**
     292              :      * Retrieve the uri from a deviceId
     293              :      * @note used by swarm manager (peersToSyncWith)
     294              :      * @param deviceId
     295              :      * @return corresponding issuer
     296              :      */
     297              :     std::string uriFromDevice(const std::string& deviceId) const;
     298              : 
     299              :     /**
     300              :      * Join a conversation
     301              :      * @return commit id to send
     302              :      */
     303              :     std::string join();
     304              : 
     305              :     /**
     306              :      * Test if an URI is a member
     307              :      * @param uri       URI to test
     308              :      * @return true if uri is a member
     309              :      */
     310              :     bool isMember(const std::string& uri, bool includeInvited = false) const;
     311              :     bool isMemberBanned(const std::string& uri) const;
     312              :     bool isDeviceBanned(const std::string& deviceId) const;
     313              : 
     314              :     /**
     315              :      * Check if a device is authorized to clone or interact with the conversation.
     316              :      * Assumption: the deviceId must already be a confirmed device of the user.
     317              :      * @param uri               URI of the user
     318              :      * @param deviceId          Device id to check
     319              :      * @param includeInvited    If true, consider invited members as authorized.
     320              :      */
     321              :     bool isPeerAuthorized(const std::string& uri, const std::string& deviceId, bool includeInvited = false) const;
     322              : 
     323              :     void createCommit(CommitMessage&& message, OnCommitCb&& onCommit = {}, OnDoneCb&& cb = {});
     324              : 
     325              :     /**
     326              :      * Get a range of messages
     327              :      * @param cb        The callback when loaded
     328              :      * @param options   The log options
     329              :      */
     330              :     void loadMessages(const OnLoadMessages& cb, const LogOptions& options);
     331              :     /**
     332              :      * Clear all cached messages
     333              :      */
     334              :     void clearCache();
     335              :     /**
     336              :      * Check if a commit exists in the repository
     337              :      * @param commitId The commit id to check
     338              :      * @return true if the commit was found, false if not or if an error occurred
     339              :      */
     340              :     bool hasCommit(const std::string& commitId) const;
     341              :     /**
     342              :      * Retrieve one commit
     343              :      * @param   commitId
     344              :      * @return  The commit if found
     345              :      */
     346              :     std::optional<ConversationCommit> getCommit(const std::string& commitId) const;
     347              :     /**
     348              :      * Get last commit id
     349              :      * @return last commit id
     350              :      */
     351              :     std::string lastCommitId() const;
     352              : 
     353              :     /**
     354              :      * Fetch and merge from peer
     355              :      * @param deviceId  Peer device
     356              :      * @param cb        On pulled callback
     357              :      * @param commitId  Commit id that triggered this fetch
     358              :      * @return true if callback will be called later
     359              :      */
     360              :     bool pull(const std::string& deviceId, OnPullCb&& cb, std::string commitId = "");
     361              :     /**
     362              :      * Fetch new commits and re-ask for waiting files
     363              :      * @param member
     364              :      * @param deviceId
     365              :      * @param cb        cf pull()
     366              :      * @param commitId  cf pull()
     367              :      */
     368              :     void sync(const std::string& member, const std::string& deviceId, OnPullCb&& cb, std::string commitId = "");
     369              : 
     370              :     /**
     371              :      * Generate an invitation to send to new contacts
     372              :      * @return the invite to send
     373              :      */
     374              :     std::map<std::string, std::string> generateInvitation() const;
     375              : 
     376              :     /**
     377              :      * Leave a conversation
     378              :      * @return commit id to send
     379              :      */
     380              :     std::string leave();
     381              : 
     382              :     /**
     383              :      * Set a conversation as removing (when loading convInfo and still not sync)
     384              :      * @todo: not a big fan to see this here. can be set in the constructor
     385              :      * cause it's used by jamiaccount when loading conversations
     386              :      */
     387              :     void setRemovingFlag();
     388              : 
     389              :     /**
     390              :      * Check if we are removing the conversation
     391              :      * @return true if left the room
     392              :      */
     393              :     bool isRemoving();
     394              : 
     395              :     /**
     396              :      * Erase all related datas
     397              :      */
     398              :     void erase();
     399              : 
     400              :     /**
     401              :      * Get conversation's mode
     402              :      * @return the mode
     403              :      */
     404              :     ConversationMode mode() const;
     405              : 
     406              :     /**
     407              :      * One to one util, get initial members
     408              :      * @return initial members
     409              :      */
     410              :     std::vector<std::string> getInitialMembers() const;
     411              :     bool isInitialMember(const std::string& uri) const;
     412              : 
     413              :     /**
     414              :      * Change repository's infos
     415              :      * @param map       New infos (supported keys: title, description, avatar)
     416              :      * @param cb        On commited
     417              :      */
     418              :     void updateInfos(const std::map<std::string, std::string>& map, const OnDoneCb& cb = {});
     419              : 
     420              :     /**
     421              :      * Change user's preferences
     422              :      * @param map       New preferences
     423              :      */
     424              :     void updatePreferences(const std::map<std::string, std::string>& map);
     425              : 
     426              :     /**
     427              :      * Retrieve current infos (title, description, avatar, mode)
     428              :      * @return infos
     429              :      */
     430              :     std::map<std::string, std::string> infos() const;
     431              :     /**
     432              :      * Retrieve current preferences (color, notification, etc)
     433              :      * @param includeLastModified       If we want to know when the preferences were modified
     434              :      * @return preferences
     435              :      */
     436              :     std::map<std::string, std::string> preferences(bool includeLastModified) const;
     437              :     std::vector<uint8_t> vCard() const;
     438              : 
     439              :     /////// File transfer
     440              : 
     441              :     /**
     442              :      * Access to transfer manager
     443              :      */
     444              :     std::shared_ptr<TransferManager> dataTransfer() const;
     445              : 
     446              :     /**
     447              :      * Choose if we can accept channel request
     448              :      * @param member        member to check
     449              :      * @param fileId        file transfer to check (needs to be waiting)
     450              :      * @param verifyShaSum  for debug only
     451              :      * @return if we accept the channel request
     452              :      */
     453              :     bool onFileChannelRequest(const std::string& member,
     454              :                               const std::string& fileId,
     455              :                               std::filesystem::path& path,
     456              :                               std::string& sha3sum) const;
     457              :     /**
     458              :      * Adds a file to the waiting list and ask members
     459              :      * @param interactionId     Related interaction id
     460              :      * @param fileId            Related id
     461              :      * @param path              Destination
     462              :      * @param member            Member if we know from who to pull file
     463              :      * @param deviceId          Device if we know from who to pull file
     464              :      * @return id of the file
     465              :      */
     466              :     bool downloadFile(const std::string& interactionId,
     467              :                       const std::string& fileId,
     468              :                       const std::string& path,
     469              :                       const std::string& member = "",
     470              :                       const std::string& deviceId = "");
     471              : 
     472              :     /**
     473              :      * Reset fetched information
     474              :      */
     475              :     void clearFetched();
     476              :     /**
     477              :      * Store information about who fetch or not. This simplify sync (sync when a device without the
     478              :      * last fetch is detected)
     479              :      * @param deviceId
     480              :      * @param commitId
     481              :      */
     482              :     void hasFetched(const std::string& deviceId, const std::string& commitId);
     483              : 
     484              :     /**
     485              :      * Store last read commit (returned in getMembers)
     486              :      * @param uri               Of the member
     487              :      * @param interactionId     Last interaction displayed
     488              :      * @return if updated
     489              :      */
     490              :     bool setMessageDisplayed(const std::string& uri, const std::string& interactionId);
     491              :     /**
     492              :      * Retrieve last displayed and fetch status per member
     493              :      * @return A map with the following structure:
     494              :      * {uri, {
     495              :      *          {"fetch", "commitId"},
     496              :      *          {"fetched_ts", "timestamp"},
     497              :      *          {"read", "commitId"},
     498              :      *          {"read_ts", "timestamp"}
     499              :      *       }
     500              :      * }
     501              :      */
     502              :     std::map<std::string, std::map<std::string, std::string>> messageStatus() const;
     503              :     /**
     504              :      * Update fetch/read status
     505              :      * @param messageStatus     A map with the following structure:
     506              :      * {uri, {
     507              :      *          {"fetch", "commitId"},
     508              :      *          {"fetched_ts", "timestamp"},
     509              :      *          {"read", "commitId"},
     510              :      *          {"read_ts", "timestamp"}
     511              :      *       }
     512              :      * }
     513              :      */
     514              :     void updateMessageStatus(const std::map<std::string, std::map<std::string, std::string>>& messageStatus);
     515              :     void onMessageStatusChanged(
     516              :         const std::function<void(const std::map<std::string, std::map<std::string, std::string>>&)>& cb);
     517              :     /**
     518              :      * Retrieve how many interactions there is from HEAD to interactionId
     519              :      * @param toId      "" for getting the whole history
     520              :      * @param fromId    "" => HEAD
     521              :      * @param authorURI author to stop counting
     522              :      * @return number of interactions since interactionId
     523              :      */
     524              :     uint32_t countInteractions(const std::string& toId,
     525              :                                const std::string& fromId = "",
     526              :                                const std::string& authorUri = "") const;
     527              :     /**
     528              :      * Search in the conversation via a filter
     529              :      * @param req       Id of the request
     530              :      * @param filter    Parameters for the search
     531              :      * @param flag      To check when search is finished
     532              :      * @note triggers messagesFound
     533              :      */
     534              :     void search(uint32_t req, const Filter& filter, const std::shared_ptr<std::atomic_int>& flag) const;
     535              :     /**
     536              :      * Host a conference in the conversation
     537              :      * @note the message must have "confId"
     538              :      * @note Update hostedCalls_ and commit in the conversation
     539              :      * @param message       message to commit
     540              :      * @param cb            callback triggered when committed
     541              :      */
     542              :     void hostConference(CommitMessage&& message, OnDoneCb&& cb = {});
     543              :     /**
     544              :      * Announce the end of a call
     545              :      * @note the message must have "confId"
     546              :      * @note called when conference is finished
     547              :      * @param message       message to commit
     548              :      * @param cb            callback triggered when committed
     549              :      */
     550              :     void removeActiveConference(CommitMessage&& message, OnDoneCb&& cb = {});
     551              :     /**
     552              :      * Check if we're currently hosting this conference
     553              :      * @param confId
     554              :      * @return true if hosting
     555              :      */
     556              :     bool isHosting(const std::string& confId) const;
     557              :     /**
     558              :      * Return current detected calls
     559              :      * @return a vector of map with the following keys: "id", "uri", "device"
     560              :      */
     561              :     std::vector<std::map<std::string, std::string>> currentCalls() const;
     562              : 
     563              :     /**
     564              :      * Git operations will need a ChannelSocket for cloning/fetching commits
     565              :      * Because libgit2 is a C library, we store the pointer in the corresponding conversation
     566              :      * and the GitTransport will inject to libgit2 whenever needed
     567              :      */
     568              :     std::shared_ptr<dhtnet::ChannelSocket> gitSocket(const DeviceId& deviceId) const;
     569              :     void addGitSocket(const DeviceId& deviceId, const std::shared_ptr<dhtnet::ChannelSocket>& socket);
     570              :     void removeGitSocket(const DeviceId& deviceId);
     571              : 
     572              :     /**
     573              :      * Stop SwarmManager, bootstrap and gitSockets
     574              :      */
     575              :     void shutdownConnections();
     576              : 
     577              :     /**
     578              :      * If we change from one network to one another, we will need to update the state of the connections
     579              :      */
     580              :     void connectivityChanged();
     581              : 
     582              :     /**
     583              :      * @return getAllNodes()    Nodes that are linked to the conversation
     584              :      */
     585              :     std::vector<jami::DeviceId> getDeviceIdList() const;
     586              : 
     587              :     /**
     588              :      * Get Typers object
     589              :      * @return Typers object
     590              :      */
     591              :     std::shared_ptr<Typers> typers() const;
     592              : 
     593              :     /**
     594              :      * Get connectivity information for the conversation
     595              :      * @return map of connectivity info
     596              :      */
     597              :     std::vector<std::map<std::string, std::string>> getConnectivity() const;
     598              : 
     599              : private:
     600              :     std::shared_ptr<Conversation> shared() { return std::static_pointer_cast<Conversation>(shared_from_this()); }
     601              :     std::shared_ptr<Conversation const> shared() const
     602              :     {
     603              :         return std::static_pointer_cast<Conversation const>(shared_from_this());
     604              :     }
     605         3924 :     std::weak_ptr<Conversation> weak() { return std::static_pointer_cast<Conversation>(shared_from_this()); }
     606            4 :     std::weak_ptr<Conversation const> weak() const
     607              :     {
     608            4 :         return std::static_pointer_cast<Conversation const>(shared_from_this());
     609              :     }
     610              : 
     611              :     // Private because of weak()
     612              :     class Impl;
     613              :     std::unique_ptr<Impl> pimpl_;
     614              : };
     615              : 
     616              : } // namespace jami
        

Generated by: LCOV version 2.0-1