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