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