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 : #ifdef HAVE_CONFIG_H
20 : #include "config.h"
21 : #endif
22 :
23 : #include "client/ring_signal.h"
24 : #include "configurationmanager_interface.h"
25 : #include "noncopyable.h"
26 : #include "config/serializable.h"
27 : #include "registration_states.h"
28 : #include "im/message_engine.h"
29 : #include "media/media_codec.h"
30 : #include "media/media_attribute.h"
31 : #include "logger.h"
32 : #include "compiler_intrinsics.h" // include the "UNUSED" macro
33 : #include "call_set.h"
34 : #include "account_config.h"
35 :
36 : #include <dhtnet/ip_utils.h>
37 : #include <dhtnet/upnp/upnp_control.h>
38 :
39 : #include <functional>
40 : #include <string>
41 : #include <vector>
42 : #include <memory>
43 : #include <map>
44 : #include <set>
45 : #include <random>
46 : #include <stdexcept>
47 : #include <atomic>
48 : #include <mutex>
49 : #include <chrono>
50 :
51 : namespace Json {
52 : class Value;
53 : }
54 :
55 : namespace dht {
56 : namespace crypto {
57 : struct Certificate;
58 : }
59 : }
60 :
61 : namespace jami {
62 : static constexpr uint64_t JAMI_ID_MAX_VAL = 9007199254740992;
63 : constexpr static const char RINGDIR[] = "ringtones";
64 :
65 : class Call;
66 : class SystemCodecContainer;
67 :
68 : class VoipLinkException : public std::runtime_error
69 : {
70 : public:
71 1 : VoipLinkException(const std::string& str = "")
72 1 : : std::runtime_error("VoipLinkException occurred: " + str)
73 1 : {}
74 : };
75 :
76 : /**
77 : * @file account.h
78 : * @brief Interface to protocol account (ex: SIPAccount)
79 : * It can be enable on loading or activate after.
80 : * It contains account, configuration, VoIP Link and Calls (inside the VoIPLink)
81 : */
82 :
83 : class Account : public std::enable_shared_from_this<Account>
84 : {
85 : public:
86 : Account(const std::string& accountID);
87 :
88 : /**
89 : * Virtual destructor
90 : */
91 : virtual ~Account();
92 :
93 : /**
94 : * Free all ressources related to this account.
95 : * ***Current calls using this account are HANG-UP***
96 : */
97 : void hangupCalls();
98 :
99 : virtual std::unique_ptr<AccountConfig> buildConfig() const = 0;
100 :
101 0 : void setConfig(std::unique_ptr<AccountConfig>&& config)
102 : {
103 0 : std::lock_guard lock(configurationMutex_);
104 0 : config_ = std::move(config);
105 0 : loadConfig();
106 0 : }
107 :
108 : /**
109 : * Load the settings in this account.
110 : */
111 : virtual void loadConfig();
112 :
113 37693 : const AccountConfig& config() const
114 : {
115 37693 : if (config_)
116 37688 : return *config_;
117 : else
118 0 : throw std::runtime_error("Account doesn't have a configuration");
119 : }
120 :
121 666 : inline void editConfig(std::function<void(AccountConfig& config)>&& edit)
122 : {
123 666 : std::lock_guard lock(configurationMutex_);
124 666 : edit(*config_);
125 666 : saveConfig();
126 666 : }
127 :
128 : virtual void saveConfig() const;
129 :
130 671 : void setAccountDetails(const std::map<std::string, std::string>& details)
131 : {
132 671 : std::lock_guard lock(configurationMutex_);
133 671 : if (not config_)
134 654 : config_ = buildConfig();
135 671 : config_->fromMap(details);
136 671 : loadConfig();
137 671 : saveConfig();
138 671 : }
139 :
140 357 : std::map<std::string, std::string> getAccountDetails() const
141 : {
142 357 : std::lock_guard lock(configurationMutex_);
143 714 : return config().toMap();
144 357 : }
145 :
146 : virtual std::map<std::string, std::string> getVolatileAccountDetails() const;
147 :
148 : virtual std::string getFromUri() const = 0;
149 :
150 : /**
151 : * Get the account ID
152 : * @return constant account id
153 : */
154 82681 : inline const std::string& getAccountID() const { return accountID_; }
155 :
156 : virtual std::string_view getAccountType() const = 0;
157 :
158 3194 : const std::filesystem::path& getPath() const { return idPath_; }
159 :
160 : /**
161 : * Returns true if this is the IP2IP account
162 : */
163 4406 : virtual bool isIP2IP() const { return false; }
164 :
165 : /**
166 : * Register the account.
167 : * This should update the getRegistrationState() return value.
168 : */
169 : virtual void doRegister() = 0;
170 :
171 : /**
172 : * Unregister the account.
173 : * This should update the getRegistrationState() return value.
174 : */
175 : virtual void doUnregister(bool forceShutdownConnections = false) = 0;
176 :
177 2 : RegistrationState getRegistrationState() const { return registrationState_; }
178 :
179 : /**
180 : * Create a new outgoing call.
181 : *
182 : * @param toUrl The address to call
183 : * @param mediaList A list of media
184 : * @return The created call
185 : */
186 : virtual std::shared_ptr<Call> newOutgoingCall(std::string_view toUrl,
187 : const std::vector<libjami::MediaMap>& mediaList)
188 : = 0;
189 :
190 : /**
191 : * If supported, send a text message from this account.
192 : * @return a token to query the message status
193 : */
194 0 : virtual uint64_t sendTextMessage(const std::string& /*to*/,
195 : const std::string& /*deviceId*/,
196 : const std::map<std::string, std::string>& /*payloads*/,
197 : uint64_t /*refreshToken*/ = 0,
198 : bool /*onlyConnected*/ = false)
199 : {
200 0 : return 0;
201 : }
202 :
203 0 : virtual void setIsComposing(const std::string& /*conversationUri*/, bool /*isWriting*/) {};
204 :
205 0 : virtual bool setMessageDisplayed(const std::string& /*conversationUri*/,
206 : const std::string& /*messageId*/,
207 : int /*status*/)
208 : {
209 0 : return false;
210 : };
211 :
212 0 : virtual std::vector<libjami::Message> getLastMessages(const uint64_t& /*base_timestamp*/)
213 : {
214 0 : return {};
215 : }
216 :
217 0 : virtual std::map<std::string, std::string> getNearbyPeers() const { return {}; }
218 :
219 : virtual void updateProfile(const std::string& /*displayName*/, const std::string& /*avatar*/, const std::string& /*fileType*/, int32_t /*flag*/) = 0;
220 :
221 : std::map<std::string, std::string> getProfileVcard() const;
222 :
223 : /**
224 : * Return the status corresponding to the token.
225 : */
226 0 : virtual im::MessageStatus getMessageStatus(uint64_t /*id*/) const
227 : {
228 0 : return im::MessageStatus::UNKNOWN;
229 : }
230 :
231 0 : virtual bool cancelMessage(uint64_t /*id*/) { return false; }
232 :
233 0 : virtual bool setPushNotificationToken(const std::string& pushDeviceToken = "")
234 : {
235 0 : std::lock_guard lock(configurationMutex_);
236 0 : if (config_ && config_->deviceKey != pushDeviceToken) {
237 0 : config_->deviceKey = pushDeviceToken;
238 0 : saveConfig();
239 0 : return true;
240 : }
241 0 : return false;
242 0 : }
243 :
244 0 : virtual bool setPushNotificationTopic(const std::string& topic = "")
245 : {
246 0 : std::lock_guard lock(configurationMutex_);
247 0 : if (config_ && config_->notificationTopic != topic) {
248 0 : config_->notificationTopic = topic;
249 0 : saveConfig();
250 0 : return true;
251 : }
252 0 : return false;
253 0 : }
254 :
255 : virtual bool setPushNotificationConfig(const std::map<std::string, std::string>& data);
256 :
257 : /**
258 : * Tell if the account is enable or not.
259 : * @return true if enabled, false otherwise
260 : */
261 1460 : bool isEnabled() const { return config().enabled; }
262 :
263 149 : void setEnabled(bool enable)
264 : {
265 149 : config_->enabled = enable;
266 : // Update the UPnP controller to make sure it's in the correct state since this
267 : // depends on whether the account is enabled or not (in particular, we don't want
268 : // disabled accounts to generate UPnP activity).
269 149 : updateUpnpController();
270 149 : }
271 :
272 : /**
273 : * Tell if the account is activated
274 : * (can currently be used).
275 : */
276 0 : bool isActive() const noexcept { return active_; }
277 :
278 0 : void setActive(bool active) noexcept { active_ = active; }
279 :
280 2222 : bool isUsable() const { return config().enabled and active_; }
281 :
282 7 : void enableVideo(bool enable)
283 : {
284 7 : editConfig([&](AccountConfig& config) { config.videoEnabled = enable; });
285 7 : }
286 507 : bool isVideoEnabled() const { return config().videoEnabled; }
287 :
288 : /**
289 : * Set the registration state of the specified link
290 : * @param state The registration state of underlying VoIPLink
291 : */
292 : virtual void setRegistrationState(RegistrationState state,
293 : int detail_code = 0,
294 : const std::string& detail_str = {});
295 :
296 10308 : const std::string& getUsername() const { return config().username; }
297 : const std::string& getHostname() const { return config().hostname; }
298 : const std::string& getAlias() const { return config().alias; }
299 :
300 : static std::vector<unsigned> getDefaultCodecsId();
301 : static std::map<std::string, std::string> getDefaultCodecDetails(const unsigned& codecId);
302 :
303 : /* Accessor to data structures
304 : * @return The list that reflects the user's choice
305 : */
306 : std::vector<unsigned> getActiveCodecs(MediaType mediaType = MEDIA_ALL) const;
307 : bool hasActiveCodec(MediaType mediaType) const;
308 :
309 : /**
310 : * Update both the codec order structure and the codec string used for
311 : * SDP offer and configuration respectively
312 : */
313 : virtual void setActiveCodecs(const std::vector<unsigned>& list);
314 : std::shared_ptr<SystemCodecInfo> searchCodecById(unsigned codecId, MediaType mediaType);
315 : std::vector<std::shared_ptr<SystemCodecInfo>> getActiveAccountCodecInfoList(
316 : MediaType mediaType) const;
317 : std::shared_ptr<SystemCodecInfo> searchCodecByPayload(unsigned payload, MediaType mediaType);
318 :
319 60 : std::filesystem::path getRingtonePath() const { return ringtonePath_; }
320 60 : bool getRingtoneEnabled() const { return config().ringtoneEnabled; }
321 531 : std::string getDisplayName() const { return config().displayName; }
322 : std::string getMailBox() const { return config().mailbox; }
323 :
324 214 : bool isRendezVous() const { return config().isRendezVous; }
325 104 : bool isAutoAnswerEnabled() const { return config().autoAnswerEnabled; }
326 36 : bool isDenySecondCallEnabled() const
327 : {
328 36 : return config().denySecondCallEnabled;
329 : }
330 12 : bool isReadReceiptEnabled() const { return config().sendReadReceipt; }
331 30 : bool isComposingEnabled() const { return config().sendComposing; }
332 :
333 : /**
334 : * returns whether or not UPnP is enabled and active
335 : * ie: if it is able to make port mappings
336 : */
337 : bool getUPnPActive() const;
338 :
339 : /**
340 : * Get the UPnP IP (external router) address.
341 : * If use UPnP is set to false, the address will be empty.
342 : */
343 : dhtnet::IpAddr getUPnPIpAddress() const;
344 :
345 : /**
346 : * Random generator engine
347 : * Logical account state shall never rely on the state of the random generator.
348 : */
349 : mutable std::mt19937_64 rand;
350 :
351 : /**
352 : * Inform the account that the network status has changed.
353 : */
354 0 : virtual void connectivityChanged() {};
355 :
356 0 : virtual bool handleMessage(const std::shared_ptr<dht::crypto::Certificate>& /*cert*/,
357 : const std::string& /*from*/,
358 : const std::pair<std::string, std::string>& /*message*/)
359 : {
360 0 : return false;
361 : };
362 :
363 : /**
364 : * Helper function used to load the default codec order from the codec factory
365 : */
366 : void loadDefaultCodecs();
367 :
368 : void setCodecActive(unsigned codecId);
369 :
370 : void setCodecInactive(unsigned codecId);
371 :
372 : /**
373 : * Get the user-agent
374 : */
375 : const std::string& getUserAgentName();
376 :
377 69 : std::set<std::string> getDefaultModerators() const { return config().defaultModerators; }
378 :
379 : void addDefaultModerator(const std::string& peerURI);
380 : void removeDefaultModerator(const std::string& peerURI);
381 :
382 69 : bool isLocalModeratorsEnabled() const { return config().localModeratorsEnabled; }
383 69 : bool isAllModerators() const { return config().allModeratorsEnabled; }
384 :
385 : // Enable/disable ICE for media
386 395 : bool isIceForMediaEnabled() const { return iceForMediaEnabled_; }
387 3 : void enableIceForMedia(bool enable) { iceForMediaEnabled_ = enable; }
388 :
389 : // Enable/disable generation of empty offers
390 10 : bool isEmptyOffersEnabled() const { return false; }
391 :
392 : // Check if a Daemon version (typically peer's version) satisfies the
393 : // minimum required version. This check is typically used to disable a
394 : // feature if it's not backward compatible with the peer's version.
395 : static bool meetMinimumRequiredVersion(const std::vector<unsigned>& jamiVersion,
396 : const std::vector<unsigned>& minRequiredVersion);
397 :
398 : // Enable/disable compliancy with RFC-5245 for component IDs format.
399 : // The ICE component IDs are enumerated relative to the SDP session,
400 : // i.e., starts from 1 and incremented for each component.
401 : // However, RFC-5245 requires that the ICE component IDs are enumerated
402 : // relative to the media stream, e.g., component IDs 1 and 2 for audio,
403 : // and component IDs 1 and 2 for video. This non-conformity can cause
404 : // inter-operability issues.
405 : // When the compliancy feature is enabled, the component ID in the
406 : // generated SDP will be compliant to RFC-5245. This feature should be
407 : // enabled only when the peer is compliant to RFC-5245 as well.
408 : // The current version is able to correctly parse both formats.
409 : // This feature is needed for backward compatiblity, and should be removed
410 : // once the backward compatibility is no more required.
411 227 : bool isIceCompIdRfc5245Compliant() const { return iceCompIdRfc5245Compliant_; }
412 4 : void enableIceCompIdRfc5245Compliance(bool enable) { iceCompIdRfc5245Compliant_ = enable; }
413 0 : void enableAutoLoadConversations(bool enable) { autoLoadConversations_ = enable; }
414 :
415 704 : std::shared_ptr<Call> getCall(const std::string& callId) const
416 : {
417 704 : return callSet_.getCall(callId);
418 : }
419 1 : std::vector<std::string> getCallList() const { return callSet_.getCallIds(); }
420 104 : std::shared_ptr<Conference> getConference(const std::string& confId) const
421 : {
422 104 : return callSet_.getConference(confId);
423 : }
424 4 : std::vector<std::string> getConferenceList() const { return callSet_.getConferenceIds(); }
425 395 : void attach(const std::shared_ptr<Call>& call) { callSet_.add(call); }
426 407 : bool detach(const std::shared_ptr<Call>& call) { return callSet_.remove(call); }
427 38 : void attach(const std::shared_ptr<Conference>& conf) { callSet_.add(conf); }
428 59 : bool removeConference(const std::string& confId)
429 : {
430 59 : auto result = callSet_.removeConference(confId);
431 59 : if (result)
432 35 : emitSignal<libjami::CallSignal::ConferenceRemoved>(getAccountID(), confId);
433 59 : return result;
434 : }
435 :
436 : public:
437 : // virtual methods that has to be implemented by concrete classes
438 : /**
439 : * This method is called to request removal of possible account traces on the system,
440 : * like internal account setup files.
441 : */
442 654 : virtual void flush() {/* nothing to do here - overload */};
443 :
444 : private:
445 : NON_COPYABLE(Account);
446 :
447 : /**
448 : * Set of calls attached to the account.
449 : */
450 : CallSet callSet_;
451 :
452 : protected:
453 : virtual void updateUpnpController();
454 :
455 : std::unique_ptr<AccountConfig> config_ {};
456 :
457 : friend class ConfigurationTest;
458 :
459 : static const std::string DEFAULT_USER_AGENT;
460 :
461 : static std::string mapStateNumberToString(RegistrationState state);
462 :
463 : /**
464 : * Build the user-agent string
465 : */
466 : static std::string getDefaultUserAgent();
467 :
468 : /**
469 : * Account ID are assign in constructor and shall not changed
470 : */
471 : const std::string accountID_;
472 :
473 : mutable std::recursive_mutex configurationMutex_ {};
474 :
475 : /**
476 : * Tells if the account is active now.
477 : * This allows doRegister to be called.
478 : * When an account is unactivated, doUnregister must be called.
479 : */
480 : bool active_ {true};
481 :
482 : /*
483 : * The general, protocol neutral registration
484 : * state of the account
485 : */
486 : RegistrationState registrationState_ {RegistrationState::UNLOADED};
487 :
488 : /**
489 : * Vector containing all system codecs (with default parameters)
490 : */
491 : std::shared_ptr<SystemCodecContainer> systemCodecContainer_;
492 : /**
493 : * Vector containing all account codecs (set of system codecs with custom parameters)
494 : */
495 : std::vector<std::shared_ptr<SystemCodecInfo>> accountCodecInfoList_;
496 :
497 : /**
498 : * path to account
499 : */
500 : std::filesystem::path idPath_ {};
501 :
502 : /**
503 : * Ringtone .au file used for this account
504 : */
505 : std::filesystem::path ringtonePath_;
506 :
507 : /**
508 : * UPnP IGD controller and the mutex to access it
509 : */
510 : mutable std::mutex upnp_mtx {};
511 : std::shared_ptr<dhtnet::upnp::Controller> upnpCtrl_;
512 :
513 : bool iceForMediaEnabled_ {true};
514 : bool iceCompIdRfc5245Compliant_ {false};
515 : /**
516 : * Auto load conversations when creatinf convModule()
517 : */
518 : bool autoLoadConversations_ {true};
519 :
520 : /**
521 : * private account codec searching functions
522 : */
523 : std::shared_ptr<SystemCodecInfo> searchCodecByName(const std::string& name, MediaType mediaType);
524 : std::vector<unsigned> getAccountCodecInfoIdList(MediaType mediaType) const;
525 : void setAllCodecsActive(MediaType mediaType, bool active);
526 : void sortCodec();
527 : };
528 :
529 : static inline std::ostream&
530 10 : operator<<(std::ostream& os, const Account& acc)
531 : {
532 10 : os << "[Account " << acc.getAccountID() << "] ";
533 10 : return os;
534 : }
535 :
536 : } // namespace jami
|