Ring Daemon
Loading...
Searching...
No Matches
conversation.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
18#pragma once
19
24#include "jamidht/typers.h"
25#include "string_utils.h"
26
27#include <json/json.h>
28#include <msgpack.hpp>
29
30#include <functional>
31#include <string>
32#include <vector>
33#include <map>
34#include <memory>
35#include <set>
36
37#include <asio.hpp>
38
39namespace dhtnet {
40class ChannelSocket;
41} // namespace dhtnet
42
43namespace jami {
44
45namespace ConversationMapKeys {
46static constexpr const char* ID {"id"};
47static constexpr const char* CREATED {"created"};
48static constexpr const char* REMOVED {"removed"};
49static constexpr const char* ERASED {"erased"};
50static constexpr const char* MEMBERS {"members"};
51static constexpr const char* LAST_DISPLAYED {"lastDisplayed"};
52static constexpr const char* RECEIVED {"received"};
53static constexpr const char* DECLINED {"declined"};
54static constexpr const char* FROM {"from"};
55static constexpr const char* CONVERSATIONID {"conversationId"};
56static constexpr const char* METADATAS {"metadatas"};
57} // namespace ConversationMapKeys
58
59namespace ConversationDirectories {
60static constexpr std::string_view PREFERENCES {"preferences"};
61static constexpr std::string_view STATUS {"status"};
62static constexpr std::string_view SENDING {"sending"};
63static constexpr std::string_view FETCHED {"fetched"};
64static constexpr std::string_view ACTIVE_CALLS {"activeCalls"};
65static constexpr std::string_view HOSTED_CALLS {"hostedCalls"};
66static constexpr std::string_view CACHED {"cached"};
67} // namespace ConversationDirectories
68
69namespace ConversationPreferences {
70static constexpr const char* HOST_CONFERENCES = "hostConferences";
71}
72
73class JamiAccount;
75class TransferManager;
76enum class ConversationMode;
77
85{
86 std::string conversationId;
87 std::string from;
88 std::map<std::string, std::string> metadatas;
89
92
94 ConversationRequest(const Json::Value& json);
95
96 Json::Value toJson() const;
97 std::map<std::string, std::string> toMap() const;
98
99 bool operator==(const ConversationRequest& o) const
100 {
101 auto m = toMap();
102 auto om = o.toMap();
103 return m.size() == om.size() && std::equal(m.begin(), m.end(), om.begin());
104 }
105
106 bool isOneToOne() const
107 {
108 try {
109 return metadatas.at("mode") == "0";
110 } catch (...) {
111 }
112 return true;
113 }
114
116 {
117 try {
118 return to_enum<ConversationMode>(metadatas.at("mode"));
119 } catch (...) {
120 }
122 }
123
124 MSGPACK_DEFINE_MAP(from, conversationId, metadatas, received, declined)
125};
126
128{
129 std::string id {};
133 std::set<std::string> members;
134 std::string lastDisplayed {};
136
137 ConvInfo() = default;
138 ConvInfo(const ConvInfo&) = default;
139 ConvInfo(ConvInfo&&) = default;
140 ConvInfo(const std::string& id)
141 : id(id) {};
142 explicit ConvInfo(const Json::Value& json);
143
144 bool isRemoved() const { return removed >= created; }
145
146 ConvInfo& operator=(const ConvInfo&) = default;
148
149 Json::Value toJson() const;
150
151 MSGPACK_DEFINE_MAP(id, created, removed, erased, members, lastDisplayed, mode)
152};
153
154using OnPullCb = std::function<void(bool fetchOk)>;
155using OnLoadMessages = std::function<void(std::vector<libjami::SwarmMessage>&& messages)>;
156using OnCommitCb = std::function<void(const std::string&)>;
157using OnDoneCb = std::function<void(bool, const std::string&)>;
158using OnMembersChanged = std::function<void(const std::set<std::string>&)>;
159using DeviceId = dht::PkId;
160using GitSocketList = std::map<DeviceId, std::shared_ptr<dhtnet::ChannelSocket>>;
161using ChannelCb = std::function<bool(const std::shared_ptr<dhtnet::ChannelSocket>&)>;
162using NeedSocketCb = std::function<void(const std::string&, const std::string&, ChannelCb&&, const std::string&)>;
163
164class Conversation : public std::enable_shared_from_this<Conversation>
165{
166public:
167 Conversation(const std::shared_ptr<JamiAccount>& account,
169 const std::string& otherMember = "");
170 Conversation(const std::shared_ptr<JamiAccount>& account, const std::string& conversationId = "");
171 Conversation(const std::shared_ptr<JamiAccount>& account,
172 const std::string& remoteDevice,
173 const std::string& conversationId);
175
179 void monitor();
180
181#ifdef LIBJAMI_TEST
182 enum class BootstrapStatus { FAILED, FALLBACK, SUCCESS };
186 void onBootstrapStatus(const std::function<void(std::string, BootstrapStatus)>& cb);
187
188 std::vector<libjami::SwarmMessage> loadMessagesSync(const LogOptions& options);
189 void announce(const std::vector<std::map<std::string, std::string>>& commits, bool commitFromSelf = false);
190 void announce(const std::string& commitId, bool commitFromSelf = false);
191#endif
192
198 void bootstrap(std::function<void()> onBootstrapped, const std::vector<DeviceId>& knownDevices = {});
199
205 void addKnownDevices(const std::vector<DeviceId>& devices, const std::string& memberUri);
206
213 std::vector<std::string> commitsEndedCalls();
214
216
226 void addSwarmChannel(std::shared_ptr<dhtnet::ChannelSocket> channel);
227
232 std::string id() const;
233
234 // Member management
240 void addMember(const std::string& contactUri, const OnDoneCb& cb = {});
241 void removeMember(const std::string& contactUri, bool isDevice, const OnDoneCb& cb = {});
254 std::vector<std::map<std::string, std::string>> getMembers(bool includeInvited = false,
255 bool includeLeft = false,
256 bool includeBanned = false) const;
257
263 std::set<std::string> memberUris(std::string_view filter = {},
264 const std::set<MemberRole>& filteredRoles = {MemberRole::INVITED,
266 MemberRole::BANNED}) const;
267
268 std::vector<std::map<std::string, std::string>> getTrackedMembers() const;
269
274 std::vector<NodeId> peersToSyncWith() const;
279 bool isBootstrapped() const;
286 std::string uriFromDevice(const std::string& deviceId) const;
287
292 std::string join();
293
299 bool isMember(const std::string& uri, bool includeInvited = false) const;
300 bool isBanned(const std::string& uri) const;
301
302 // Message send
303 void sendMessage(Json::Value&& message,
304 const std::string& replyTo = "",
305 OnCommitCb&& onCommit = {},
306 OnDoneCb&& cb = {});
312 void loadMessages(const OnLoadMessages& cb, const LogOptions& options);
316 void clearCache();
322 bool hasCommit(const std::string& commitId) const;
328 std::optional<std::map<std::string, std::string>> getCommit(const std::string& commitId) const;
333 std::string lastCommitId() const;
334
342 bool pull(const std::string& deviceId, OnPullCb&& cb, std::string commitId = "");
350 void sync(const std::string& member, const std::string& deviceId, OnPullCb&& cb, std::string commitId = "");
351
356 std::map<std::string, std::string> generateInvitation() const;
357
362 std::string leave();
363
369 void setRemovingFlag();
370
375 bool isRemoving();
376
380 void erase();
381
386 ConversationMode mode() const;
387
392 std::vector<std::string> getInitialMembers() const;
393 bool isInitialMember(const std::string& uri) const;
394
400 void updateInfos(const std::map<std::string, std::string>& map, const OnDoneCb& cb = {});
401
406 void updatePreferences(const std::map<std::string, std::string>& map);
407
412 std::map<std::string, std::string> infos() const;
418 std::map<std::string, std::string> preferences(bool includeLastModified) const;
419 std::vector<uint8_t> vCard() const;
420
422
426 std::shared_ptr<TransferManager> dataTransfer() const;
427
435 bool onFileChannelRequest(const std::string& member,
436 const std::string& fileId,
437 std::filesystem::path& path,
438 std::string& sha3sum) const;
448 bool downloadFile(const std::string& interactionId,
449 const std::string& fileId,
450 const std::string& path,
451 const std::string& member = "",
452 const std::string& deviceId = "");
453
464 void hasFetched(const std::string& deviceId, const std::string& commitId);
465
472 bool setMessageDisplayed(const std::string& uri, const std::string& interactionId);
484 std::map<std::string, std::map<std::string, std::string>> messageStatus() const;
496 void updateMessageStatus(const std::map<std::string, std::map<std::string, std::string>>& messageStatus);
498 const std::function<void(const std::map<std::string, std::map<std::string, std::string>>&)>& cb);
506 uint32_t countInteractions(const std::string& toId,
507 const std::string& fromId = "",
508 const std::string& authorUri = "") const;
516 void search(uint32_t req, const Filter& filter, const std::shared_ptr<std::atomic_int>& flag) const;
524 void hostConference(Json::Value&& message, OnDoneCb&& cb = {});
532 void removeActiveConference(Json::Value&& message, OnDoneCb&& cb = {});
538 bool isHosting(const std::string& confId) const;
543 std::vector<std::map<std::string, std::string>> currentCalls() const;
544
550 std::shared_ptr<dhtnet::ChannelSocket> gitSocket(const DeviceId& deviceId) const;
551 void addGitSocket(const DeviceId& deviceId, const std::shared_ptr<dhtnet::ChannelSocket>& socket);
552 void removeGitSocket(const DeviceId& deviceId);
553
557 void shutdownConnections();
563 bool hasSwarmChannel(const std::string& deviceId);
564
568 void connectivityChanged();
569
573 std::vector<jami::DeviceId> getDeviceIdList() const;
574
579 std::shared_ptr<Typers> typers() const;
580
585 std::vector<std::map<std::string, std::string>> getConnectivity() const;
586
587private:
588 std::shared_ptr<Conversation> shared() { return std::static_pointer_cast<Conversation>(shared_from_this()); }
589 std::shared_ptr<Conversation const> shared() const
590 {
591 return std::static_pointer_cast<Conversation const>(shared_from_this());
592 }
593 std::weak_ptr<Conversation> weak() { return std::static_pointer_cast<Conversation>(shared_from_this()); }
594 std::weak_ptr<Conversation const> weak() const
595 {
596 return std::static_pointer_cast<Conversation const>(shared_from_this());
597 }
598
599 // Private because of weak()
600 class Impl;
601 std::unique_ptr<Impl> pimpl_;
602};
603
604} // namespace jami
This class gives access to the git repository that represents the conversation.
bool pull(const std::string &deviceId, OnPullCb &&cb, std::string commitId="")
Fetch and merge from peer.
std::set< std::string > memberUris(std::string_view filter={}, const std::set< MemberRole > &filteredRoles={MemberRole::INVITED, MemberRole::LEFT, MemberRole::BANNED}) const
std::shared_ptr< Typers > typers() const
Get Typers object.
bool downloadFile(const std::string &interactionId, const std::string &fileId, const std::string &path, const std::string &member="", const std::string &deviceId="")
Adds a file to the waiting list and ask members.
void addGitSocket(const DeviceId &deviceId, const std::shared_ptr< dhtnet::ChannelSocket > &socket)
std::string leave()
Leave a conversation.
std::string id() const
Get conversation's id.
void clearCache()
Clear all cached messages.
std::vector< std::string > commitsEndedCalls()
Refresh active calls.
std::map< std::string, std::map< std::string, std::string > > messageStatus() const
Retrieve last displayed and fetch status per member.
bool isMember(const std::string &uri, bool includeInvited=false) const
Test if an URI is a member.
void search(uint32_t req, const Filter &filter, const std::shared_ptr< std::atomic_int > &flag) const
Search in the conversation via a filter.
std::vector< std::map< std::string, std::string > > getConnectivity() const
Get connectivity information for the conversation.
std::vector< uint8_t > vCard() const
std::vector< std::string > getInitialMembers() const
One to one util, get initial members.
void connectivityChanged()
If we change from one network to one another, we will need to update the state of the connections.
std::shared_ptr< TransferManager > dataTransfer() const
Access to transfer manager.
void removeGitSocket(const DeviceId &deviceId)
std::vector< std::map< std::string, std::string > > currentCalls() const
Return current detected calls.
void clearFetched()
Reset fetched information.
void onMembersChanged(OnMembersChanged &&cb)
std::map< std::string, std::string > infos() const
Retrieve current infos (title, description, avatar, mode)
void monitor()
Print the state of the DRT linked to the conversation.
std::vector< NodeId > peersToSyncWith() const
Get peers to sync with.
void sendMessage(Json::Value &&message, const std::string &replyTo="", OnCommitCb &&onCommit={}, OnDoneCb &&cb={})
void bootstrap(std::function< void()> onBootstrapped, const std::vector< DeviceId > &knownDevices={})
Bootstrap swarm manager to other peers.
void removeActiveConference(Json::Value &&message, OnDoneCb &&cb={})
Announce the end of a call.
bool isInitialMember(const std::string &uri) const
ConversationMode mode() const
Get conversation's mode.
std::string join()
Join a conversation.
void addSwarmChannel(std::shared_ptr< dhtnet::ChannelSocket > channel)
Add swarm connection to the DRT.
std::vector< jami::DeviceId > getDeviceIdList() const
bool setMessageDisplayed(const std::string &uri, const std::string &interactionId)
Store last read commit (returned in getMembers)
void updateInfos(const std::map< std::string, std::string > &map, const OnDoneCb &cb={})
Change repository's infos.
void updateMessageStatus(const std::map< std::string, std::map< std::string, std::string > > &messageStatus)
Update fetch/read status.
void onMessageStatusChanged(const std::function< void(const std::map< std::string, std::map< std::string, std::string > > &)> &cb)
void shutdownConnections()
Stop SwarmManager, bootstrap and gitSockets.
void updatePreferences(const std::map< std::string, std::string > &map)
Change user's preferences.
std::vector< std::map< std::string, std::string > > getMembers(bool includeInvited=false, bool includeLeft=false, bool includeBanned=false) const
std::map< std::string, std::string > preferences(bool includeLastModified) const
Retrieve current preferences (color, notification, etc)
bool hasCommit(const std::string &commitId) const
Check if a commit exists in the repository.
void sync(const std::string &member, const std::string &deviceId, OnPullCb &&cb, std::string commitId="")
Fetch new commits and re-ask for waiting files.
std::shared_ptr< dhtnet::ChannelSocket > gitSocket(const DeviceId &deviceId) const
Git operations will need a ChannelSocket for cloning/fetching commits Because libgit2 is a C library,...
void onNeedSocket(NeedSocketCb cb)
Set the callback that will be called whenever a new socket will be needed.
std::string uriFromDevice(const std::string &deviceId) const
Retrieve the uri from a deviceId.
std::optional< std::map< std::string, std::string > > getCommit(const std::string &commitId) const
Retrieve one commit.
void erase()
Erase all related datas.
void setRemovingFlag()
Set a conversation as removing (when loading convInfo and still not sync)
void hasFetched(const std::string &deviceId, const std::string &commitId)
Store information about who fetch or not.
bool isHosting(const std::string &confId) const
Check if we're currently hosting this conference.
std::string lastCommitId() const
Get last commit id.
bool isBanned(const std::string &uri) const
void loadMessages(const OnLoadMessages &cb, const LogOptions &options)
Get a range of messages.
bool onFileChannelRequest(const std::string &member, const std::string &fileId, std::filesystem::path &path, std::string &sha3sum) const
Choose if we can accept channel request.
void addMember(const std::string &contactUri, const OnDoneCb &cb={})
Add conversation member.
bool hasSwarmChannel(const std::string &deviceId)
Used to avoid multiple connections, we just check if we got a swarm channel with a specific device.
void addKnownDevices(const std::vector< DeviceId > &devices, const std::string &memberUri)
Add known devices to the swarm manager.
bool isBootstrapped() const
Check if we're at least connected to one node.
bool isRemoving()
Check if we are removing the conversation.
void removeMember(const std::string &contactUri, bool isDevice, const OnDoneCb &cb={})
void hostConference(Json::Value &&message, OnDoneCb &&cb={})
Host a conference in the conversation.
std::vector< std::map< std::string, std::string > > getTrackedMembers() const
uint32_t countInteractions(const std::string &toId, const std::string &fromId="", const std::string &authorUri="") const
Retrieve how many interactions there is from HEAD to interactionId.
std::map< std::string, std::string > generateInvitation() const
Generate an invitation to send to new contacts.
Ring Account is build on top of SIPAccountBase and uses DHT to handle call connectivity.
Definition jamiaccount.h:93
#define SUCCESS
static constexpr std::string_view SENDING
static constexpr std::string_view ACTIVE_CALLS
static constexpr std::string_view HOSTED_CALLS
static constexpr std::string_view PREFERENCES
static constexpr std::string_view CACHED
static constexpr std::string_view FETCHED
static constexpr std::string_view STATUS
static constexpr const char * CREATED
static constexpr const char * REMOVED
static constexpr const char * ID
static constexpr const char * RECEIVED
static constexpr const char * METADATAS
static constexpr const char * MEMBERS
static constexpr const char * DECLINED
static constexpr const char * ERASED
static constexpr const char * FROM
static constexpr const char * CONVERSATIONID
static constexpr const char * LAST_DISPLAYED
static constexpr const char * HOST_CONFERENCES
std::function< void(const std::string &, const std::string &, ChannelCb &&, const std::string &)> NeedSocketCb
dht::PkId DeviceId
void emitSignal(Args... args)
Definition jami_signal.h:64
std::function< void(std::vector< libjami::SwarmMessage > &&messages)> OnLoadMessages
std::function< void(bool, const std::string &)> OnDoneCb
std::function< bool(const std::shared_ptr< dhtnet::ChannelSocket > &)> ChannelCb
std::function< void(bool fetchOk)> OnPullCb
std::map< DeviceId, std::shared_ptr< dhtnet::ChannelSocket > > GitSocketList
std::function< void(const std::string &)> OnCommitCb
std::function< void(const std::set< std::string > &)> OnMembersChanged
std::set< std::string > members
ConvInfo & operator=(const ConvInfo &)=default
ConvInfo & operator=(ConvInfo &&)=default
ConvInfo(const ConvInfo &)=default
ConvInfo()=default
std::string lastDisplayed
Json::Value toJson() const
ConvInfo(const std::string &id)
ConvInfo(ConvInfo &&)=default
std::string id
ConversationMode mode
bool isRemoved() const
A ConversationRequest is a request which corresponds to a trust request, but for conversations It's s...
std::map< std::string, std::string > metadatas
Json::Value toJson() const
ConversationMode mode() const
std::map< std::string, std::string > toMap() const
bool operator==(const ConversationRequest &o) const