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