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