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