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