Line data Source code
1 : /*
2 : * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 : #pragma once
18 :
19 : #include "def.h"
20 : #ifdef HAVE_CONFIG_H
21 : #include "config.h"
22 : #endif
23 :
24 : #include "sip/sipaccountbase.h"
25 : #include "jami/datatransfer_interface.h"
26 : #include "jamidht/conversation.h"
27 : #include "data_transfer.h"
28 : #include "uri.h"
29 : #include "jamiaccount_config.h"
30 :
31 : #include "noncopyable.h"
32 : #include "gitserver.h"
33 : #include "channel_handler.h"
34 : #include "conversation_module.h"
35 : #include "sync_module.h"
36 : #include "conversationrepository.h"
37 : #include "namedirectory.h"
38 :
39 : #include <dhtnet/diffie-hellman.h>
40 : #include <dhtnet/tls_session.h>
41 : #include <dhtnet/multiplexed_socket.h>
42 : #include <dhtnet/certstore.h>
43 : #include <dhtnet/connectionmanager.h>
44 : #include <dhtnet/upnp/mapping.h>
45 : #include <dhtnet/ip_utils.h>
46 : #include <dhtnet/fileutils.h>
47 :
48 : #include <opendht/dhtrunner.h>
49 : #include <opendht/default_types.h>
50 :
51 : #include <pjsip/sip_types.h>
52 : #include <json/json.h>
53 :
54 : #include <chrono>
55 : #include <functional>
56 : #include <future>
57 : #include <list>
58 : #include <map>
59 : #include <optional>
60 : #include <vector>
61 : #include <filesystem>
62 : #include <shared_mutex>
63 :
64 : namespace dev {
65 : template<unsigned N>
66 : class FixedHash;
67 : using h160 = FixedHash<20>;
68 : using Address = h160;
69 : } // namespace dev
70 :
71 : namespace jami {
72 :
73 : class IceTransport;
74 : struct Contact;
75 : struct AccountArchive;
76 : class DhtPeerConnector;
77 : class AccountManager;
78 : struct AccountInfo;
79 : class SipTransport;
80 : class ChanneledOutgoingTransfer;
81 : class SyncModule;
82 :
83 : using SipConnectionKey = std::pair<std::string /* uri */, DeviceId>;
84 :
85 : static constexpr const char MIME_TYPE_IM_COMPOSING[] {"application/im-iscomposing+xml"};
86 :
87 : /**
88 : * @brief Ring Account is build on top of SIPAccountBase and uses DHT to handle call connectivity.
89 : */
90 : class JamiAccount : public SIPAccountBase
91 : {
92 : public:
93 : constexpr static auto ACCOUNT_TYPE = ACCOUNT_TYPE_JAMI;
94 : constexpr static const std::pair<uint16_t, uint16_t> DHT_PORT_RANGE {4000, 8888};
95 : constexpr static int ICE_STREAMS_COUNT {1};
96 : constexpr static int ICE_COMP_COUNT_PER_STREAM {1};
97 :
98 3464 : std::string_view getAccountType() const override { return ACCOUNT_TYPE; }
99 :
100 5179 : std::shared_ptr<JamiAccount> shared() { return std::static_pointer_cast<JamiAccount>(shared_from_this()); }
101 : std::shared_ptr<JamiAccount const> shared() const
102 : {
103 : return std::static_pointer_cast<JamiAccount const>(shared_from_this());
104 : }
105 56367 : std::weak_ptr<JamiAccount> weak() { return std::static_pointer_cast<JamiAccount>(shared_from_this()); }
106 : std::weak_ptr<JamiAccount const> weak() const
107 : {
108 : return std::static_pointer_cast<JamiAccount const>(shared_from_this());
109 : }
110 :
111 20315 : const JamiAccountConfig& config() const { return *static_cast<const JamiAccountConfig*>(&Account::config()); }
112 :
113 792 : JamiAccountConfig::Credentials consumeConfigCredentials()
114 : {
115 792 : auto* conf = static_cast<JamiAccountConfig*>(config_.get());
116 792 : return std::move(conf->credentials);
117 : }
118 :
119 : void loadConfig() override;
120 :
121 : /**
122 : * Constructor
123 : * @param accountID The account identifier
124 : */
125 : JamiAccount(const std::string& accountId);
126 :
127 : ~JamiAccount() noexcept;
128 :
129 : /**
130 : * Retrieve volatile details such as recent registration errors
131 : * @return std::map< std::string, std::string > The account volatile details
132 : */
133 : virtual std::map<std::string, std::string> getVolatileAccountDetails() const override;
134 :
135 772 : std::unique_ptr<AccountConfig> buildConfig() const override
136 : {
137 772 : return std::make_unique<JamiAccountConfig>(getAccountID(), idPath_);
138 : }
139 :
140 : /**
141 : * Adds an account id to the list of accounts to track on the DHT for
142 : * buddy presence.
143 : *
144 : * @param buddy_id The buddy id.
145 : */
146 : void trackBuddyPresence(const std::string& buddy_id, bool track);
147 :
148 : /**
149 : * Tells for each tracked account id if it has been seen online so far
150 : * in the last DeviceAnnouncement::TYPE.expiration minutes.
151 : *
152 : * @return map of buddy_uri to bool (online or not)
153 : */
154 : std::map<std::string, bool> getTrackedBuddyPresence() const;
155 :
156 : void setActiveCodecs(const std::vector<unsigned>& list) override;
157 :
158 : /**
159 : * Connect to the DHT.
160 : */
161 : void doRegister() override;
162 :
163 : /**
164 : * Disconnect from the DHT.
165 : */
166 : void doUnregister(bool forceShutdownConnections = false) override;
167 :
168 : /**
169 : * Set the registration state of the specified link
170 : * @param state The registration state of underlying VoIPLink
171 : */
172 : void setRegistrationState(RegistrationState state, int detail_code = 0, const std::string& detail_str = {}) override;
173 :
174 : /**
175 : * @return pj_str_t "From" uri based on account information.
176 : * From RFC3261: "The To header field first and foremost specifies the desired
177 : * logical" recipient of the request, or the address-of-record of the
178 : * user or resource that is the target of this request. [...] As such, it is
179 : * very important that the From URI not contain IP addresses or the FQDN
180 : * of the host on which the UA is running, since these are not logical
181 : * names."
182 : */
183 : std::string getFromUri() const override;
184 :
185 : /**
186 : * This method adds the correct scheme, hostname and append
187 : * the ;transport= parameter at the end of the uri, in accordance with RFC3261.
188 : * It is expected that "port" is present in the internal hostname_.
189 : *
190 : * @return pj_str_t "To" uri based on @param username
191 : * @param username A string formatted as : "username"
192 : */
193 : std::string getToUri(const std::string& username) const override;
194 :
195 : /**
196 : * In the current version, "srv" uri is obtained in the preformated
197 : * way: hostname:port. This method adds the correct scheme and append
198 : * the ;transport= parameter at the end of the uri, in accordance with RFC3261.
199 : *
200 : * @return pj_str_t "server" uri based on @param hostPort
201 : * @param hostPort A string formatted as : "hostname:port"
202 : */
203 : std::string getServerUri() const { return ""; };
204 :
205 : void setIsComposing(const std::string& conversationUri, bool isWriting) override;
206 :
207 : bool setMessageDisplayed(const std::string& conversationUri, const std::string& messageId, int status) override;
208 :
209 : /**
210 : * Get the contact header for
211 : * @return The contact header based on account information
212 : */
213 : std::string getContactHeader(const std::shared_ptr<SipTransport>& sipTransport);
214 :
215 : /* Returns true if the username and/or hostname match this account */
216 : MatchRank matches(std::string_view username, std::string_view hostname) const override;
217 :
218 : /**
219 : * Create outgoing SIPCall.
220 : * @note Accepts several urls:
221 : * + jami:uri for calling someone
222 : * + swarm:id for calling a group (will host or join if an active call is detected)
223 : * + rdv:id/uri/device/confId to join a specific conference hosted on (uri, device)
224 : * @param[in] toUrl The address to call
225 : * @param[in] mediaList list of medias
226 : * @return A shared pointer on the created call.
227 : */
228 : std::shared_ptr<Call> newOutgoingCall(std::string_view toUrl,
229 : const std::vector<libjami::MediaMap>& mediaList) override;
230 :
231 : /**
232 : * Create incoming SIPCall.
233 : * @param[in] from The origin of the call
234 : * @param mediaList A list of media
235 : * @param sipTr: SIP Transport
236 : * @return A shared pointer on the created call.
237 : */
238 : std::shared_ptr<SIPCall> newIncomingCall(const std::string& from,
239 : const std::vector<libjami::MediaMap>& mediaList,
240 : const std::shared_ptr<SipTransport>& sipTr = {}) override;
241 :
242 : void onTextMessage(const std::string& id,
243 : const std::string& from,
244 : const std::shared_ptr<dht::crypto::Certificate>& peerCert,
245 : const std::map<std::string, std::string>& payloads) override;
246 : void loadConversation(const std::string& convId);
247 :
248 0 : virtual bool isTlsEnabled() const override { return true; }
249 426 : bool isSrtpEnabled() const override { return true; }
250 :
251 : bool setCertificateStatus(const std::string& cert_id, dhtnet::tls::TrustStore::PermissionStatus status);
252 : bool setCertificateStatus(const std::shared_ptr<crypto::Certificate>& cert,
253 : dhtnet::tls::TrustStore::PermissionStatus status,
254 : bool local = true);
255 : std::vector<std::string> getCertificatesByStatus(dhtnet::tls::TrustStore::PermissionStatus status);
256 :
257 : bool findCertificate(const std::string& id);
258 : bool findCertificate(const dht::InfoHash& h,
259 : std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb = {});
260 : bool findCertificate(const dht::PkId& h,
261 : std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb = {});
262 :
263 : /* contact requests */
264 : std::vector<std::map<std::string, std::string>> getTrustRequests() const;
265 : // Note: includeConversation used for compatibility test. Do not change
266 : bool acceptTrustRequest(const std::string& from, bool includeConversation = true);
267 : bool discardTrustRequest(const std::string& from);
268 : void declineConversationRequest(const std::string& conversationId);
269 :
270 : /**
271 : * Add contact to the account contact list.
272 : * Set confirmed if we know the contact also added us.
273 : */
274 : void addContact(const std::string& uri, bool confirmed = false);
275 : void removeContact(const std::string& uri, bool banned = true);
276 : std::vector<std::map<std::string, std::string>> getContacts(bool includeRemoved = false) const;
277 :
278 : ///
279 : /// Obtain details about one account contact in serializable form.
280 : ///
281 : std::map<std::string, std::string> getContactDetails(const std::string& uri) const;
282 : std::optional<Contact> getContactInfo(const std::string& uri) const;
283 :
284 : void sendTrustRequest(const std::string& to, const std::vector<uint8_t>& payload);
285 : void sendMessage(const std::string& to,
286 : const std::string& deviceId,
287 : const std::map<std::string, std::string>& payloads,
288 : uint64_t id,
289 : bool retryOnTimeout = true,
290 : bool onlyConnected = false) override;
291 :
292 : uint64_t sendTextMessage(const std::string& to,
293 : const std::string& deviceId,
294 : const std::map<std::string, std::string>& payloads,
295 : uint64_t refreshToken = 0,
296 : bool onlyConnected = false) override;
297 : void sendInstantMessage(const std::string& convId, const std::map<std::string, std::string>& msg);
298 :
299 : /**
300 : * Create and return ICE options.
301 : */
302 : dhtnet::IceTransportOptions getIceOptions() const override;
303 : void getIceOptions(std::function<void(dhtnet::IceTransportOptions&&)> cb) const;
304 : dhtnet::IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const override;
305 :
306 : /* Devices - existing device */
307 : /**
308 : * Initiates the process of adding a new device to this account
309 : * @param uriProvided The URI provided by the new device to be added
310 : * @return A positive operation ID if successful, or a negative value indicating an AddDeviceError:
311 : * - INVALID_URI (-1): The provided URI is invalid
312 : * - ALREADY_LINKING (-2): A device linking operation is already in progress
313 : * - GENERIC (-3): A generic error occurred during the process
314 : */
315 : int32_t addDevice(const std::string& uriProvided);
316 : bool cancelAddDevice(uint32_t op_token);
317 : bool confirmAddDevice(uint32_t op_token);
318 : /* Devices - new device */
319 : bool provideAccountAuthentication(const std::string& credentialsFromUser, const std::string& scheme);
320 :
321 : /**
322 : * Export the archive to a file
323 : * @param destinationPath
324 : * @param (optional) password, if not provided, will update the contacts only if the archive
325 : * doesn't have a password
326 : * @return if the archive was exported
327 : */
328 : bool exportArchive(const std::string& destinationPath,
329 : std::string_view scheme = {},
330 : const std::string& password = {});
331 : bool revokeDevice(const std::string& device, std::string_view scheme = {}, const std::string& password = {});
332 : std::map<std::string, std::string> getKnownDevices() const;
333 :
334 : bool isPasswordValid(const std::string& password);
335 : std::vector<uint8_t> getPasswordKey(const std::string& password);
336 :
337 : bool changeArchivePassword(const std::string& password_old, const std::string& password_new);
338 :
339 : void connectivityChanged() override;
340 :
341 : // overloaded methods
342 : void flush() override;
343 :
344 : void lookupName(const std::string& name);
345 : void lookupAddress(const std::string& address);
346 : void registerName(const std::string& name, const std::string& scheme, const std::string& password);
347 : bool searchUser(const std::string& nameQuery);
348 :
349 : /// \return true if the given DHT message identifier has been treated
350 : /// \note if message has not been treated yet this method store this id and returns true at
351 : /// further calls
352 : bool isMessageTreated(dht::Value::Id id);
353 :
354 670 : std::shared_ptr<dht::DhtRunner> dht() { return dht_; }
355 :
356 2174 : const dht::crypto::Identity& identity() const { return id_; }
357 :
358 : void forEachDevice(const dht::InfoHash& to,
359 : std::function<void(const std::shared_ptr<dht::crypto::PublicKey>&)>&& op,
360 : std::function<void(bool)>&& end = {});
361 :
362 : bool setPushNotificationToken(const std::string& pushDeviceToken = "") override;
363 : bool setPushNotificationTopic(const std::string& topic) override;
364 : bool setPushNotificationConfig(const std::map<std::string, std::string>& data) override;
365 :
366 : /**
367 : * To be called by clients with relevant data when a push notification is received.
368 : */
369 : void pushNotificationReceived(const std::string& from, const std::map<std::string, std::string>& data);
370 :
371 : std::string getUserUri() const override;
372 :
373 : /**
374 : * Get last messages (should be used to retrieve messages when launching the client)
375 : * @param base_timestamp
376 : */
377 : std::vector<libjami::Message> getLastMessages(const uint64_t& base_timestamp) override;
378 :
379 : /**
380 : * Start Publish the Jami Account onto the Network
381 : */
382 : void startAccountPublish();
383 :
384 : /**
385 : * Start Discovery the Jami Account from the Network
386 : */
387 : void startAccountDiscovery();
388 :
389 : void saveConfig() const override;
390 :
391 778 : inline void editConfig(std::function<void(JamiAccountConfig& conf)>&& edit)
392 : {
393 1556 : Account::editConfig([&](AccountConfig& conf) { edit(*static_cast<JamiAccountConfig*>(&conf)); });
394 778 : }
395 :
396 : /**
397 : * Get current discovered peers account id and display name
398 : */
399 : std::map<std::string, std::string> getNearbyPeers() const override;
400 :
401 : void sendProfileToPeers();
402 :
403 : /**
404 : * Update the profile vcard and send it to peers
405 : * @param displayName Current or new display name
406 : * @param avatar Current or new avatar
407 : * @param flag 0 for path to avatar, 1 for base64 avatar
408 : */
409 : void updateProfile(const std::string& displayName,
410 : const std::string& avatar,
411 : const std::string& fileType,
412 : int32_t flag) override;
413 :
414 : #ifdef LIBJAMI_TEST
415 1 : dhtnet::ConnectionManager& connectionManager()
416 : {
417 1 : return *connectionManager_;
418 : }
419 :
420 : /**
421 : * Only used for tests, disable sha3sum verification for transfers.
422 : * @param newValue
423 : */
424 : void noSha3sumVerification(bool newValue);
425 :
426 2 : void publishPresence(bool newValue)
427 : {
428 2 : publishPresence_ = newValue;
429 2 : }
430 : #endif
431 :
432 : /**
433 : * This should be called before flushing the account.
434 : * ConnectionManager needs the account to exists
435 : */
436 : void shutdownConnections();
437 :
438 : std::string_view currentDeviceId() const;
439 :
440 : // Received a new commit notification
441 :
442 : bool handleMessage(const std::shared_ptr<dht::crypto::Certificate>& cert,
443 : const std::string& from,
444 : const std::pair<std::string, std::string>& message) override;
445 :
446 : void monitor();
447 : // conversationId optional
448 : std::vector<std::map<std::string, std::string>> getConnectionList(const std::string& conversationId = "");
449 : std::vector<std::map<std::string, std::string>> getChannelList(const std::string& connectionId);
450 :
451 : // File transfer
452 : void sendFile(const std::string& conversationId,
453 : const std::filesystem::path& path,
454 : const std::string& name,
455 : const std::string& replyTo);
456 :
457 : void transferFile(const std::string& conversationId,
458 : const std::string& path,
459 : const std::string& deviceId,
460 : const std::string& fileId,
461 : const std::string& interactionId,
462 : size_t start = 0,
463 : size_t end = 0,
464 : const std::string& sha3Sum = "",
465 : uint64_t lastWriteTime = 0,
466 : std::function<void()> onFinished = {});
467 :
468 : void askForFileChannel(const std::string& conversationId,
469 : const std::string& deviceId,
470 : const std::string& interactionId,
471 : const std::string& fileId,
472 : size_t start = 0,
473 : size_t end = 0);
474 :
475 : void askForProfile(const std::string& conversationId, const std::string& deviceId, const std::string& memberUri);
476 :
477 : /**
478 : * Retrieve linked transfer manager
479 : * @param id conversationId or empty for fallback
480 : * @return linked transfer manager
481 : */
482 : std::shared_ptr<TransferManager> dataTransfer(const std::string& id = "");
483 :
484 : /**
485 : * Used to get the instance of the ConversationModule class which is
486 : * responsible for managing conversations and messages between users.
487 : * @param noCreate whether or not to create a new instance
488 : * @return conversationModule instance
489 : */
490 : ConversationModule* convModule(bool noCreation = false);
491 : SyncModule* syncModule();
492 :
493 : /**
494 : * Check (via the cache) if we need to send our profile to a specific device
495 : * @param peerUri Uri that will receive the profile
496 : * @param deviceId Device that will receive the profile
497 : * @param sha3Sum SHA3 hash of the profile
498 : */
499 : // Note: when swarm will be merged, this can be moved in transferManager
500 : bool needToSendProfile(const std::string& peerUri, const std::string& deviceId, const std::string& sha3Sum);
501 : /**
502 : * Send Profile via cached SIP connection
503 : * @param convId Conversation's identifier (can be empty for self profile on sync)
504 : * @param peerUri Uri that will receive the profile
505 : * @param deviceId Device that will receive the profile
506 : */
507 : void sendProfile(const std::string& convId, const std::string& peerUri, const std::string& deviceId);
508 : /**
509 : * Send profile via cached SIP connection
510 : * @param peerUri Uri that will receive the profile
511 : * @param deviceId Device that will receive the profile
512 : */
513 : void sendProfile(const std::string& peerUri, const std::string& deviceId);
514 : /**
515 : * Clear sent profiles (because of a removed contact or new trust request)
516 : * @param peerUri Uri used to clear cache
517 : */
518 : void clearProfileCache(const std::string& peerUri);
519 :
520 : std::filesystem::path profilePath() const;
521 :
522 33232 : const std::shared_ptr<AccountManager>& accountManager()
523 : {
524 33232 : return accountManager_;
525 : }
526 :
527 : bool sha3SumVerify() const;
528 :
529 : /**
530 : * Change certificate's validity period
531 : * @param pwd Password for the archive
532 : * @param id Certificate to update ({} for updating the whole chain)
533 : * @param validity New validity
534 : * @note forceReloadAccount may be necessary to retrigger the migration
535 : */
536 : bool setValidity(std::string_view scheme, const std::string& pwd, const dht::InfoHash& id, int64_t validity);
537 : /**
538 : * Try to reload the account to force the identity to be updated
539 : */
540 : void forceReloadAccount();
541 :
542 : void reloadContacts();
543 :
544 : /**
545 : * Make sure appdata/contacts.yml contains correct information
546 : * @param removedConv The current removed conversations
547 : */
548 : void unlinkConversations(const std::set<std::string>& removedConv);
549 :
550 : bool isValidAccountDevice(const dht::crypto::Certificate& cert) const;
551 :
552 : /**
553 : * Join incoming call to hosted conference
554 : * @param callId The call to join
555 : * @param destination conversation/uri/device/confId to join
556 : */
557 : void handleIncomingConversationCall(const std::string& callId, const std::string& destination);
558 :
559 : /**
560 : * The DRT component is composed on some special nodes, that are usually present but not
561 : * connected. This kind of node corresponds to devices with push notifications & proxy and are
562 : * stored in the mobile nodes
563 : */
564 : bool isMobile() const
565 : {
566 : return config().proxyEnabled and not config().deviceKey.empty();
567 : }
568 :
569 : #ifdef LIBJAMI_TEST
570 1 : std::map<Uri::Scheme, std::unique_ptr<ChannelHandlerInterface>>& channelHandlers()
571 : {
572 1 : return channelHandlers_;
573 : };
574 : #endif
575 :
576 29258 : dhtnet::tls::CertificateStore& certStore() const
577 : {
578 29258 : return *certStore_;
579 : }
580 : /**
581 : * Check if a Device is connected
582 : * @param deviceId
583 : * @return true if connected
584 : */
585 : bool isConnectedWith(const DeviceId& deviceId) const;
586 :
587 : /**
588 : * Send a presence note
589 : * @param note
590 : */
591 : void sendPresenceNote(const std::string& note);
592 :
593 : private:
594 : NON_COPYABLE(JamiAccount);
595 :
596 : using clock = std::chrono::system_clock;
597 : using time_point = clock::time_point;
598 :
599 : /**
600 : * Private structures
601 : */
602 : struct PendingCall;
603 : struct PendingMessage;
604 : struct BuddyInfo;
605 : struct DiscoveredPeer;
606 : class SendMessageContext;
607 :
608 0 : inline std::string getProxyConfigKey() const
609 : {
610 0 : const auto& conf = config();
611 0 : return dht::InfoHash::get(conf.proxyServer + conf.proxyListUrl).toString();
612 : }
613 :
614 : /**
615 : * Compute archive encryption key and DHT storage location from password and PIN.
616 : */
617 : /* static std::pair<std::vector<uint8_t>, dht::InfoHash> computeKeys(const std::string&
618 : password, const std::string& pin, bool previous = false);
619 : */
620 :
621 : void trackPresence(const dht::InfoHash& h, BuddyInfo& buddy);
622 : void onPeerConnected(const std::string& peerId, bool connected);
623 :
624 : void doRegister_();
625 :
626 : void lookupRegisteredName(const std::string& regName, const NameDirectory::Response& response);
627 : dht::DhtRunner::Config initDhtConfig(const JamiAccountConfig& conf);
628 : dht::DhtRunner::Context initDhtContext();
629 : void onAccountDeviceFound(const std::shared_ptr<dht::crypto::Certificate>& crt);
630 : void onAccountDeviceAnnounced();
631 : bool onICERequest(const DeviceId& deviceId);
632 : bool onChannelRequest(const std::shared_ptr<dht::crypto::Certificate>& cert, const std::string& name);
633 : void onConnectionReady(const DeviceId& deviceId,
634 : const std::string& name,
635 : std::shared_ptr<dhtnet::ChannelSocket> channel);
636 :
637 777 : const dht::ValueType USER_PROFILE_TYPE = {9, "User profile", std::chrono::hours(24 * 7)};
638 :
639 : void startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& toUri);
640 :
641 : void onConnectedOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to_id, dhtnet::IpAddr target);
642 :
643 : /**
644 : * Start a SIP Call
645 : * @param call The current call
646 : * @return true if all is correct
647 : */
648 : bool SIPStartCall(SIPCall& call, const dhtnet::IpAddr& target);
649 :
650 : /**
651 : * Update tracking info when buddy appears offline.
652 : */
653 : void onTrackedBuddyOffline(const dht::InfoHash&);
654 :
655 : /**
656 : * Update tracking info when buddy appears offline.
657 : */
658 : void onTrackedBuddyOnline(const dht::InfoHash&);
659 :
660 : /**
661 : * Maps require port via UPnP and other async ops
662 : */
663 : void registerAsyncOps();
664 : /**
665 : * Add port mapping callback function.
666 : */
667 : void onPortMappingAdded(uint16_t port_used, bool success);
668 : void forEachPendingCall(const DeviceId& deviceId, const std::function<void(const std::shared_ptr<SIPCall>&)>& cb);
669 :
670 : void loadAccount(const std::string& archive_password_scheme = {},
671 : const std::string& archive_password = {},
672 : const std::string& archive_path = {});
673 :
674 : std::vector<std::string> loadBootstrap() const;
675 :
676 : static std::pair<std::string, std::string> saveIdentity(const dht::crypto::Identity& id,
677 : const std::filesystem::path& path,
678 : const std::string& name);
679 :
680 : void replyToIncomingIceMsg(const std::shared_ptr<SIPCall>&,
681 : const std::shared_ptr<IceTransport>&,
682 : const std::shared_ptr<IceTransport>&,
683 : const dht::IceCandidates&,
684 : const std::shared_ptr<dht::crypto::Certificate>& from_cert,
685 : const dht::InfoHash& from);
686 :
687 : void loadCachedUrl(const std::string& url,
688 : const std::filesystem::path& cachePath,
689 : const std::chrono::seconds& cacheDuration,
690 : const std::function<void(const dht::http::Response& response)>& cb);
691 :
692 : std::string getDhtProxyServer(const std::string& serverList);
693 : void loadCachedProxyServer(std::function<void(const std::string&)> cb);
694 :
695 : void newOutgoingCallHelper(const std::shared_ptr<SIPCall>& call, const Uri& uri);
696 : std::shared_ptr<SIPCall> newSwarmOutgoingCallHelper(const Uri& uri, const std::vector<libjami::MediaMap>& mediaList);
697 : std::shared_ptr<SIPCall> createSubCall(const std::shared_ptr<SIPCall>& mainCall);
698 :
699 : std::filesystem::path cachePath_ {};
700 : std::filesystem::path dataPath_ {};
701 :
702 : mutable std::mutex registeredNameMutex_;
703 : std::string registeredName_;
704 :
705 691 : bool setRegisteredName(const std::string& name)
706 : {
707 691 : std::lock_guard<std::mutex> lock(registeredNameMutex_);
708 691 : if (registeredName_ != name) {
709 1 : registeredName_ = name;
710 1 : return true;
711 : }
712 690 : return false;
713 691 : }
714 4591 : std::string getRegisteredName() const
715 : {
716 4591 : std::lock_guard<std::mutex> lock(registeredNameMutex_);
717 9182 : return registeredName_;
718 4591 : }
719 :
720 : std::shared_ptr<dht::Logger> logger_;
721 : std::shared_ptr<dhtnet::tls::CertificateStore> certStore_;
722 :
723 : std::shared_ptr<dht::DhtRunner> dht_ {};
724 : std::shared_ptr<AccountManager> accountManager_;
725 : dht::crypto::Identity id_ {};
726 :
727 : mutable std::mutex messageMutex_ {};
728 : std::map<dht::Value::Id, PendingMessage> sentMessages_;
729 : dhtnet::fileutils::IdList treatedMessages_;
730 :
731 : /* tracked buddies presence */
732 : mutable std::mutex buddyInfoMtx;
733 : std::map<dht::InfoHash, BuddyInfo> trackedBuddies_;
734 :
735 : mutable std::mutex dhtValuesMtx_;
736 :
737 : std::atomic_int syncCnt_ {0};
738 :
739 : /**
740 : * DHT port actually used.
741 : * This holds the actual DHT port, which might different from the port
742 : * set in the configuration. This can be the case if UPnP is used.
743 : */
744 690 : in_port_t dhtPortUsed()
745 : {
746 690 : return (upnpCtrl_ and dhtUpnpMapping_.isValid()) ? dhtUpnpMapping_.getExternalPort() : config().dhtPort;
747 : }
748 :
749 : /* Current UPNP mapping */
750 : dhtnet::upnp::Mapping dhtUpnpMapping_ {dhtnet::upnp::PortType::UDP};
751 :
752 : /**
753 : * Proxy
754 : */
755 : std::string proxyServerCached_ {};
756 :
757 : /**
758 : * Optional: via_addr construct from received parameters
759 : */
760 : pjsip_host_port via_addr_ {};
761 :
762 : pjsip_transport* via_tp_ {nullptr};
763 :
764 : /** ConnectionManager is thread-safe.
765 : * The shared mutex protects the pointer while allowing
766 : * multiple threads to access the ConnectionManager concurrently */
767 : mutable std::shared_mutex connManagerMtx_ {};
768 : std::unique_ptr<dhtnet::ConnectionManager> connectionManager_;
769 :
770 : virtual void updateUpnpController() override;
771 :
772 : std::mutex discoveryMapMtx_;
773 : std::shared_ptr<dht::PeerDiscovery> peerDiscovery_;
774 : std::map<dht::InfoHash, DiscoveredPeer> discoveredPeers_;
775 : std::map<std::string, std::string> discoveredPeerMap_;
776 :
777 : std::set<std::shared_ptr<dht::http::Request>> requests_;
778 :
779 : mutable std::mutex sipConnsMtx_ {};
780 : struct SipConnection
781 : {
782 : std::shared_ptr<SipTransport> transport;
783 : // Needs to keep track of that channel to access underlying ICE
784 : // information, as the SipTransport use a generic transport
785 : std::shared_ptr<dhtnet::ChannelSocket> channel;
786 : };
787 : // NOTE: here we use a vector to avoid race conditions. In fact the contact
788 : // can ask for a SIP channel when we are creating a new SIP Channel with this
789 : // peer too.
790 : std::map<SipConnectionKey, std::vector<SipConnection>> sipConns_;
791 :
792 : std::mutex pendingCallsMutex_;
793 : std::map<DeviceId, std::vector<std::shared_ptr<SIPCall>>> pendingCalls_;
794 :
795 : std::mutex onConnectionClosedMtx_ {};
796 : std::map<DeviceId, std::function<void(const DeviceId&, bool)>> onConnectionClosed_ {};
797 : /**
798 : * onConnectionClosed contains callbacks that need to be called if a sub call is failing
799 : * @param deviceId The device we are calling
800 : * @param eraseDummy Erase the dummy call (a temporary subcall that must be stop when we will
801 : * not create new subcalls)
802 : */
803 : void callConnectionClosed(const DeviceId& deviceId, bool eraseDummy);
804 :
805 : /**
806 : * Ask a device to open a channeled SIP socket
807 : * @param peerId The contact who owns the device
808 : * @param deviceId The device to ask
809 : * @param forceNewConnection If we want a new SIP connection
810 : * @param pc A pending call to stop if the request fails
811 : * @note triggers cacheSIPConnection
812 : */
813 : void requestSIPConnection(const std::string& peerId,
814 : const DeviceId& deviceId,
815 : const std::string& connectionType,
816 : bool forceNewConnection = false,
817 : const std::shared_ptr<SIPCall>& pc = {});
818 : /**
819 : * Store a new SIP connection into sipConnections_
820 : * @param channel The new sip channel
821 : * @param peerId The contact who owns the device
822 : * @param deviceId Device linked to that transport
823 : */
824 : void cacheSIPConnection(std::shared_ptr<dhtnet::ChannelSocket>&& channel,
825 : const std::string& peerId,
826 : const DeviceId& deviceId);
827 : /**
828 : * Shutdown a SIP connection
829 : * @param channel The channel to close
830 : * @param peerId The contact who owns the device
831 : * @param deviceId Device linked to that transport
832 : */
833 : void shutdownSIPConnection(const std::shared_ptr<dhtnet::ChannelSocket>& channel,
834 : const std::string& peerId,
835 : const DeviceId& deviceId);
836 :
837 : void requestMessageConnection(const std::string& peerId,
838 : const DeviceId& deviceId,
839 : const std::string& connectionType);
840 :
841 : // File transfers
842 : std::mutex transfersMtx_ {};
843 : std::set<std::string> incomingFileTransfers_ {};
844 :
845 : void onMessageSent(
846 : const std::string& to, uint64_t id, const std::string& deviceId, bool success, bool onlyConnected, bool retry);
847 :
848 : std::mutex gitServersMtx_ {};
849 : std::map<dht::Value::Id, std::unique_ptr<GitServer>> gitServers_ {};
850 :
851 : //// File transfer (for profiles)
852 : std::shared_ptr<TransferManager> nonSwarmTransferManager_;
853 :
854 : std::atomic_bool deviceAnnounced_ {false};
855 : std::atomic_bool noSha3sumVerification_ {false};
856 :
857 : bool publishPresence_ {true};
858 :
859 : std::map<Uri::Scheme, std::unique_ptr<ChannelHandlerInterface>> channelHandlers_ {};
860 :
861 : std::unique_ptr<ConversationModule> convModule_;
862 : std::mutex moduleMtx_;
863 : std::unique_ptr<SyncModule> syncModule_;
864 :
865 : std::mutex rdvMtx_;
866 :
867 : int dhtBoundPort_ {0};
868 :
869 : void initConnectionManager();
870 :
871 : enum class PresenceState : int { DISCONNECTED = 0, AVAILABLE, CONNECTED };
872 : std::map<std::string, PresenceState> presenceState_;
873 : std::string presenceNote_;
874 : };
875 :
876 : static inline std::ostream&
877 : operator<<(std::ostream& os, const JamiAccount& acc)
878 : {
879 : os << "[Account " << acc.getAccountID() << "] ";
880 : return os;
881 : }
882 :
883 : } // namespace jami
|