Ring Daemon
Loading...
Searching...
No Matches
conversation_module.h
Go to the documentation of this file.
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
19#include "jami.h"
24
25#include <mutex>
26#include <msgpack.hpp>
27
28namespace jami {
29static constexpr const char MIME_TYPE_INVITE[] {"application/invite"};
30static constexpr const char MIME_TYPE_GIT[] {"application/im-gitmessage-id"};
31
32class SIPCall;
33
34struct SyncMsg
35{
37 std::map<std::string, ConvInfo> c;
38 std::map<std::string, ConversationRequest> cr;
39 // p is conversation's preferences. It's not stored in c, as
40 // we can update the preferences without touching any confInfo.
41 std::map<std::string, std::map<std::string, std::string>> p;
42 // Last displayed messages [[deprecated]]
43 std::map<std::string, std::map<std::string, std::string>> ld;
44 // Read & fetched status
45 /*
46 * {{ convId,
47 * { memberUri,, {
48 * {"fetch", "commitId"},
49 * {"fetched_ts", "timestamp"},
50 * {"read", "commitId"},
51 * {"read_ts", "timestamp"}
52 * }
53 * }}
54 */
55 std::map<std::string, std::map<std::string, std::map<std::string, std::string>>> ms;
56
57 MSGPACK_DEFINE(ds, c, cr, p, ld, ms)
58};
59
60using ChannelCb = std::function<bool(const std::shared_ptr<dhtnet::ChannelSocket>&)>;
61using NeedSocketCb = std::function<void(const std::string&, const std::string&, ChannelCb&&, const std::string&)>;
63 = std::function<uint64_t(const std::string&, const DeviceId&, std::map<std::string, std::string>, uint64_t)>;
64using NeedsSyncingCb = std::function<void(std::shared_ptr<SyncMsg>&&)>;
65using OneToOneRecvCb = std::function<void(const std::string&, const std::string&)>;
66
68{
69public:
70 ConversationModule(std::shared_ptr<JamiAccount> account,
71 std::shared_ptr<AccountManager> accountManager,
74 NeedSocketCb&& onNeedSocket,
77 bool autoLoadConversations = true);
79
80 void setAccountManager(std::shared_ptr<AccountManager> accountManager);
81
85 void loadConversations();
86
87 void initPresence();
88
89 void loadSingleConversation(const std::string& convId);
90
91#ifdef LIBJAMI_TEST
92 void onBootstrapStatus(const std::function<void(std::string, Conversation::BootstrapStatus)>& cb);
93#endif
94
95 void monitor();
96
100 void bootstrap(const std::string& convId = "");
101
105 void clearPendingFetch();
106
110 void reloadRequests();
111
115 std::vector<std::string> getConversations() const;
116
122 std::string getOneToOneConversation(const std::string& uri) const noexcept;
123
131 bool updateConvForContact(const std::string& uri, const std::string& oldConv, const std::string& newConv);
132
136 std::vector<std::map<std::string, std::string>> getConversationRequests() const;
137
145 void onTrustRequest(const std::string& uri,
146 const std::string& conversationId,
147 const std::vector<uint8_t>& payload,
148 time_t received);
149
155 void onConversationRequest(const std::string& from, const Json::Value& value);
156
162 std::string peerFromConversationRequest(const std::string& convId) const;
163
170 void onNeedConversationRequest(const std::string& from, const std::string& conversationId);
171
177 void acceptConversationRequest(const std::string& conversationId, const std::string& deviceId = "");
178
183 void declineConversationRequest(const std::string& conversationId);
184
192 void cloneConversationFrom(const std::string& conversationId,
193 const std::string& uri,
194 const std::string& oldConvId = "");
195
203 const dht::InfoHash& otherMember = {});
204
205 // Message send/load
206 void sendMessage(const std::string& conversationId,
207 Json::Value&& value,
208 const std::string& replyTo = "",
209 bool announce = true,
210 OnCommitCb&& onCommit = {},
211 OnDoneCb&& cb = {});
212
213 void sendMessage(const std::string& conversationId,
214 std::string message,
215 const std::string& replyTo = "",
216 const std::string& type = "text/plain",
217 bool announce = true,
218 OnCommitCb&& onCommit = {},
219 OnDoneCb&& cb = {});
220
221 void editMessage(const std::string& conversationId, const std::string& newBody, const std::string& editedId);
222 void reactToMessage(const std::string& conversationId, const std::string& newBody, const std::string& reactToId);
223
230 void addCallHistoryMessage(const std::string& uri, uint64_t duration_ms, const std::string& reason);
231
232 // Received that a peer displayed a message
233 bool onMessageDisplayed(const std::string& peer,
234 const std::string& conversationId,
235 const std::string& interactionId);
236 std::map<std::string, std::map<std::string, std::map<std::string, std::string>>> convMessageStatus() const;
237
245 uint32_t loadConversation(const std::string& conversationId, const std::string& fromMessage = "", size_t n = 0);
246 uint32_t loadSwarmUntil(const std::string& conversationId,
247 const std::string& fromMessage,
248 const std::string& toMessage);
253 void clearCache(const std::string& conversationId);
254
255 // File transfer
261 std::shared_ptr<TransferManager> dataTransfer(const std::string& id) const;
262
270 bool onFileChannelRequest(const std::string& conversationId,
271 const std::string& member,
272 const std::string& fileId,
273 bool verifyShaSum = true) const;
274
282 bool downloadFile(const std::string& conversationId,
283 const std::string& interactionId,
284 const std::string& fileId,
285 const std::string& path);
286
287 // Sync
291 void syncConversations(const std::string& peer, const std::string& deviceId);
292
299 void onSyncData(const SyncMsg& msg, const std::string& peerId, const std::string& deviceId);
300
306 bool needsSyncingWith(const std::string& memberUri) const;
307
316 void setFetched(const std::string& conversationId, const std::string& deviceId, const std::string& commit);
317
325 void fetchNewCommits(const std::string& peer,
326 const std::string& deviceId,
327 const std::string& conversationId,
328 const std::string& commitId);
329
330 // Conversation's member
337 void addConversationMember(const std::string& conversationId,
338 const dht::InfoHash& contactUri,
339 bool sendRequest = true);
346 void removeConversationMember(const std::string& conversationId,
347 const dht::InfoHash& contactUri,
348 bool isDevice = false);
355 std::vector<std::map<std::string, std::string>> getConversationMembers(const std::string& conversationId,
356 bool includeBanned = false) const;
364 uint32_t countInteractions(const std::string& convId,
365 const std::string& toId,
366 const std::string& fromId,
367 const std::string& authorUri) const;
368
376 void search(uint32_t req, const std::string& convId, const Filter& filter) const;
377
378 // Conversation's infos management
385 void updateConversationInfos(const std::string& conversationId,
386 const std::map<std::string, std::string>& infos,
387 bool sync = true);
388 std::map<std::string, std::string> conversationInfos(const std::string& conversationId) const;
394 void setConversationPreferences(const std::string& conversationId, const std::map<std::string, std::string>& prefs);
395 std::map<std::string, std::string> getConversationPreferences(const std::string& conversationId,
396 bool includeCreated = false) const;
400 std::map<std::string, std::map<std::string, std::string>> convPreferences() const;
401 // Get the map into a VCard format for storing
402 std::vector<uint8_t> conversationVCard(const std::string& conversationId) const;
403
409 bool isBanned(const std::string& convId, const std::string& uri) const;
410
411 // Remove swarm
417 void removeContact(const std::string& uri, bool ban);
418
424 bool removeConversation(const std::string& conversationId);
432 std::string findMatchingOneToOneConversation(const std::string& excludedConversationId,
433 const std::set<std::string>& targetUris) const;
440 bool isHosting(const std::string& conversationId, const std::string& confId) const;
446 std::vector<std::map<std::string, std::string>> getActiveCalls(const std::string& conversationId) const;
454 std::shared_ptr<SIPCall> call(
455 const std::string& url,
456 const std::vector<libjami::MediaMap>& mediaList,
457 std::function<void(const std::string&, const DeviceId&, const std::shared_ptr<SIPCall>&)>&& cb);
458 void hostConference(const std::string& conversationId,
459 const std::string& confId,
460 const std::string& callId,
461 const std::vector<libjami::MediaMap>& mediaList = {});
462
463 // The following methods modify what is stored on the disk
464 static void saveConvInfos(const std::string& accountId, const std::map<std::string, ConvInfo>& conversations);
465 static void saveConvInfosToPath(const std::filesystem::path& path,
466 const std::map<std::string, ConvInfo>& conversations);
467 static void saveConvRequests(const std::string& accountId,
468 const std::map<std::string, ConversationRequest>& conversationsRequests);
469 static void saveConvRequestsToPath(const std::filesystem::path& path,
470 const std::map<std::string, ConversationRequest>& conversationsRequests);
471
472 static std::map<std::string, ConvInfo> convInfos(const std::string& accountId);
473 static std::map<std::string, ConvInfo> convInfosFromPath(const std::filesystem::path& path);
474 static std::map<std::string, ConversationRequest> convRequests(const std::string& accountId);
475 static std::map<std::string, ConversationRequest> convRequestsFromPath(const std::filesystem::path& path);
476 void addConvInfo(const ConvInfo& info);
477
482 std::shared_ptr<Conversation> getConversation(const std::string& convId);
489 std::shared_ptr<dhtnet::ChannelSocket> gitSocket(std::string_view deviceId, std::string_view convId) const;
490 void removeGitSocket(std::string_view deviceId, std::string_view convId);
491 void addGitSocket(std::string_view deviceId,
492 std::string_view convId,
493 const std::shared_ptr<dhtnet::ChannelSocket>& channel);
497 void shutdownConnections();
503 void addSwarmChannel(const std::string& conversationId, std::shared_ptr<dhtnet::ChannelSocket> socket);
507 void connectivityChanged();
508
514 std::shared_ptr<Typers> getTypers(const std::string& convId);
515
516private:
517 class Impl;
518 std::shared_ptr<Impl> pimpl_;
519};
520
521} // namespace jami
void removeContact(const std::string &uri, bool ban)
Remove one to one conversations related to a contact.
void addSwarmChannel(const std::string &conversationId, std::shared_ptr< dhtnet::ChannelSocket > socket)
Add a swarm connection.
void onConversationRequest(const std::string &from, const Json::Value &value)
Called when receiving a new conversation's request.
bool onMessageDisplayed(const std::string &peer, const std::string &conversationId, const std::string &interactionId)
void editMessage(const std::string &conversationId, const std::string &newBody, const std::string &editedId)
static void saveConvInfosToPath(const std::filesystem::path &path, const std::map< std::string, ConvInfo > &conversations)
static void saveConvInfos(const std::string &accountId, const std::map< std::string, ConvInfo > &conversations)
void search(uint32_t req, const std::string &convId, const Filter &filter) const
Search in conversations via a filter.
std::vector< std::map< std::string, std::string > > getConversationRequests() const
Return conversation's requests.
void syncConversations(const std::string &peer, const std::string &deviceId)
Sync conversations with detected peer.
std::shared_ptr< Conversation > getConversation(const std::string &convId)
Get a conversation.
void removeGitSocket(std::string_view deviceId, std::string_view convId)
void connectivityChanged()
Triggers a bucket maintainance for DRTs.
void setAccountManager(std::shared_ptr< AccountManager > accountManager)
void addConversationMember(const std::string &conversationId, const dht::InfoHash &contactUri, bool sendRequest=true)
Adds a new member to a conversation (this will triggers a member event + new message on success)
bool onFileChannelRequest(const std::string &conversationId, const std::string &member, const std::string &fileId, bool verifyShaSum=true) const
Choose if we can accept channel request.
void clearPendingFetch()
Clear not removed fetch.
void reloadRequests()
Reload requests from file.
void onNeedConversationRequest(const std::string &from, const std::string &conversationId)
Called when a peer needs an invite for a conversation (generally after that they received a commit no...
static void saveConvRequestsToPath(const std::filesystem::path &path, const std::map< std::string, ConversationRequest > &conversationsRequests)
bool needsSyncingWith(const std::string &memberUri) const
Check if we need to share infos with a contact.
std::map< std::string, std::map< std::string, std::string > > convPreferences() const
Retrieve all conversation preferences to sync with other devices.
std::vector< std::string > getConversations() const
Return all conversation's id (including syncing ones)
static void saveConvRequests(const std::string &accountId, const std::map< std::string, ConversationRequest > &conversationsRequests)
void setConversationPreferences(const std::string &conversationId, const std::map< std::string, std::string > &prefs)
Update user's preferences (like color, notifications, etc) to be synced across devices.
std::map< std::string, std::string > getConversationPreferences(const std::string &conversationId, bool includeCreated=false) const
static std::map< std::string, ConversationRequest > convRequests(const std::string &accountId)
std::map< std::string, std::string > conversationInfos(const std::string &conversationId) const
void acceptConversationRequest(const std::string &conversationId, const std::string &deviceId="")
Accept a conversation's request.
std::shared_ptr< SIPCall > call(const std::string &url, const std::vector< libjami::MediaMap > &mediaList, std::function< void(const std::string &, const DeviceId &, const std::shared_ptr< SIPCall > &)> &&cb)
Call the conversation.
bool updateConvForContact(const std::string &uri, const std::string &oldConv, const std::string &newConv)
Replace linked conversation in contact's details.
std::string getOneToOneConversation(const std::string &uri) const noexcept
Get related conversation with member.
void shutdownConnections()
Clear all connection (swarm channels)
std::string findMatchingOneToOneConversation(const std::string &excludedConversationId, const std::set< std::string > &targetUris) const
Search for an existing one-to-one conversation that exactly matches the given set of member URIs.
void bootstrap(const std::string &convId="")
Bootstrap swarm managers to other peers.
std::string startConversation(ConversationMode mode=ConversationMode::INVITES_ONLY, const dht::InfoHash &otherMember={})
Starts a new conversation.
void fetchNewCommits(const std::string &peer, const std::string &deviceId, const std::string &conversationId, const std::string &commitId)
Launch fetch on new commit.
void setFetched(const std::string &conversationId, const std::string &deviceId, const std::string &commit)
Notify that a peer fetched a commit.
void sendMessage(const std::string &conversationId, Json::Value &&value, const std::string &replyTo="", bool announce=true, OnCommitCb &&onCommit={}, OnDoneCb &&cb={})
bool isHosting(const std::string &conversationId, const std::string &confId) const
Check if we're hosting a specific conference.
uint32_t loadConversation(const std::string &conversationId, const std::string &fromMessage="", size_t n=0)
Load conversation's messages.
void removeConversationMember(const std::string &conversationId, const dht::InfoHash &contactUri, bool isDevice=false)
Remove a member from a conversation (this will trigger a member event + new message on success)
uint32_t countInteractions(const std::string &convId, const std::string &toId, const std::string &fromId, const std::string &authorUri) const
Retrieve the number of interactions from interactionId to HEAD.
static std::map< std::string, ConversationRequest > convRequestsFromPath(const std::filesystem::path &path)
bool downloadFile(const std::string &conversationId, const std::string &interactionId, const std::string &fileId, const std::string &path)
Ask conversation's members to send a file to this device.
std::vector< uint8_t > conversationVCard(const std::string &conversationId) const
static std::map< std::string, ConvInfo > convInfos(const std::string &accountId)
void addCallHistoryMessage(const std::string &uri, uint64_t duration_ms, const std::string &reason)
Add to the related conversation the call history message.
void addGitSocket(std::string_view deviceId, std::string_view convId, const std::shared_ptr< dhtnet::ChannelSocket > &channel)
void clearCache(const std::string &conversationId)
Clear loaded interactions.
void reactToMessage(const std::string &conversationId, const std::string &newBody, const std::string &reactToId)
std::vector< std::map< std::string, std::string > > getConversationMembers(const std::string &conversationId, bool includeBanned=false) const
Get members.
std::shared_ptr< dhtnet::ChannelSocket > gitSocket(std::string_view deviceId, std::string_view convId) const
Return current git socket used for a conversation.
void hostConference(const std::string &conversationId, const std::string &confId, const std::string &callId, const std::vector< libjami::MediaMap > &mediaList={})
uint32_t loadSwarmUntil(const std::string &conversationId, const std::string &fromMessage, const std::string &toMessage)
void onSyncData(const SyncMsg &msg, const std::string &peerId, const std::string &deviceId)
Detect new conversations and request from other devices.
void loadConversations()
Refresh information about conversations.
void addConvInfo(const ConvInfo &info)
static std::map< std::string, ConvInfo > convInfosFromPath(const std::filesystem::path &path)
void loadSingleConversation(const std::string &convId)
bool isBanned(const std::string &convId, const std::string &uri) const
Return if a device or member is banned from a conversation.
std::vector< std::map< std::string, std::string > > getActiveCalls(const std::string &conversationId) const
Return active calls.
void updateConversationInfos(const std::string &conversationId, const std::map< std::string, std::string > &infos, bool sync=true)
Update metadatas from conversations (like title, avatar, etc)
std::shared_ptr< Typers > getTypers(const std::string &convId)
Get Typers object for a conversation.
void cloneConversationFrom(const std::string &conversationId, const std::string &uri, const std::string &oldConvId="")
Clone conversation from a member.
std::string peerFromConversationRequest(const std::string &convId) const
Retrieve author of a conversation request.
void declineConversationRequest(const std::string &conversationId)
Decline a conversation's request.
void onTrustRequest(const std::string &uri, const std::string &conversationId, const std::vector< uint8_t > &payload, time_t received)
Called when detecting a new trust request with linked one to one.
std::shared_ptr< TransferManager > dataTransfer(const std::string &id) const
Returns related transfer manager.
bool removeConversation(const std::string &conversationId)
Remove a conversation, but not the contact.
std::map< std::string, std::map< std::string, std::map< std::string, std::string > > > convMessageStatus() const
static constexpr const char MIME_TYPE_INVITE[]
std::function< void(const std::string &, const std::string &, ChannelCb &&, const std::string &)> NeedSocketCb
dht::PkId DeviceId
static constexpr const char MIME_TYPE_GIT[]
void emitSignal(Args... args)
Definition jami_signal.h:64
std::function< void(bool, const std::string &)> OnDoneCb
std::function< void(const std::string &, const std::string &)> OneToOneRecvCb
std::function< bool(const std::shared_ptr< dhtnet::ChannelSocket > &)> ChannelCb
std::function< void(const std::string &)> OnCommitCb
std::function< void(std::shared_ptr< SyncMsg > &&)> NeedsSyncingCb
std::function< uint64_t(const std::string &, const DeviceId &, std::map< std::string, std::string >, uint64_t)> SengMsgCb
std::map< std::string, std::map< std::string, std::string > > p
std::map< std::string, std::map< std::string, std::string > > ld
std::map< std::string, std::map< std::string, std::map< std::string, std::string > > > ms
std::map< std::string, ConversationRequest > cr
std::map< std::string, ConvInfo > c