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