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: Alexandre Bourget <alexandre.bourget@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 :
23 : #include "media/media_codec.h"
24 : #ifdef HAVE_CONFIG_H
25 : #include "config.h"
26 : #endif
27 : #include "account.h"
28 :
29 : #include <algorithm>
30 : #include <iterator>
31 : #include <mutex>
32 :
33 : #ifdef ENABLE_VIDEO
34 : #include "libav_utils.h"
35 : #endif
36 :
37 : #include "logger.h"
38 : #include "manager.h"
39 :
40 : #include <opendht/rng.h>
41 :
42 : #include "client/ring_signal.h"
43 : #include "account_schema.h"
44 : #include "jami/account_const.h"
45 : #include "string_utils.h"
46 : #include "fileutils.h"
47 : #include "config/yamlparser.h"
48 : #include "system_codec_container.h"
49 :
50 : #pragma GCC diagnostic push
51 : #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
52 : #include <yaml-cpp/yaml.h>
53 : #pragma GCC diagnostic pop
54 :
55 : #include "compiler_intrinsics.h"
56 : #include "jami/account_const.h"
57 :
58 : #include <dhtnet/upnp/upnp_control.h>
59 : #include <dhtnet/ip_utils.h>
60 :
61 : #include <fmt/ranges.h>
62 :
63 : using namespace std::literals;
64 :
65 : namespace jami {
66 :
67 : // For portability, do not specify the absolute file name of the ringtone.
68 : // Instead, specify its base name to be looked in
69 : // JAMI_DATADIR/ringtones/, where JAMI_DATADIR is a preprocessor macro denoting
70 : // the data directory prefix that must be set at build time.
71 : const std::string Account::DEFAULT_USER_AGENT = Account::getDefaultUserAgent();
72 :
73 799 : Account::Account(const std::string& accountID)
74 799 : : rand(Manager::instance().getSeededRandomEngine())
75 799 : , accountID_(accountID)
76 2397 : , systemCodecContainer_(getSystemCodecContainer())
77 : {
78 : // Initialize the codec order, used when creating a new account
79 799 : loadDefaultCodecs();
80 799 : }
81 :
82 729 : Account::~Account() {}
83 :
84 : void
85 770 : Account::hangupCalls()
86 : {
87 797 : for (const auto& callId : callSet_.getCallIds())
88 797 : Manager::instance().hangupCall(getAccountID(), callId);
89 770 : }
90 :
91 : void
92 814 : Account::updateUpnpController()
93 : {
94 814 : std::lock_guard lk {upnp_mtx};
95 :
96 814 : if (not config().upnpEnabled or not isUsable()) {
97 69 : upnpCtrl_.reset();
98 69 : return;
99 : }
100 :
101 : // UPNP enabled. Create new controller if needed.
102 745 : if (not upnpCtrl_) {
103 729 : upnpCtrl_ = std::make_shared<dhtnet::upnp::Controller>(Manager::instance().upnpContext());
104 729 : if (not upnpCtrl_) {
105 0 : throw std::runtime_error("Failed to create a UPNP Controller instance!");
106 : }
107 : }
108 814 : }
109 :
110 : void
111 5103 : Account::setRegistrationState(RegistrationState state,
112 : int detail_code,
113 : const std::string& detail_str)
114 : {
115 5103 : if (state != registrationState_) {
116 3666 : registrationState_ = state;
117 : // Notify the client
118 7332 : runOnMainThread([accountId = accountID_,
119 : state = mapStateNumberToString(registrationState_),
120 : detail_code,
121 : detail_str,
122 3666 : details = getVolatileAccountDetails()] {
123 3666 : emitSignal<libjami::ConfigurationSignal::RegistrationStateChanged>(accountId,
124 3666 : state,
125 : detail_code,
126 3666 : detail_str);
127 :
128 3666 : emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>(accountId, details);
129 3666 : });
130 : }
131 5103 : }
132 :
133 : void
134 799 : Account::loadDefaultCodecs()
135 : {
136 : // default codec are system codecs
137 799 : const auto& systemCodecList = systemCodecContainer_->getSystemCodecInfoList();
138 799 : accountCodecInfoList_.clear();
139 799 : accountCodecInfoList_.reserve(systemCodecList.size());
140 10387 : for (const auto& systemCodec : systemCodecList) {
141 : // As defined in SDP RFC, only select a codec if it can encode and decode
142 9588 : if ((systemCodec->codecType & CODEC_ENCODER_DECODER) != CODEC_ENCODER_DECODER)
143 0 : continue;
144 :
145 9588 : if (systemCodec->mediaType & MEDIA_AUDIO) {
146 6392 : accountCodecInfoList_.emplace_back(std::make_shared<SystemAudioCodecInfo>(
147 12784 : *std::static_pointer_cast<SystemAudioCodecInfo>(systemCodec)));
148 : }
149 :
150 9588 : if (systemCodec->mediaType & MEDIA_VIDEO) {
151 3196 : accountCodecInfoList_.emplace_back(std::make_shared<SystemVideoCodecInfo>(
152 6392 : *std::static_pointer_cast<SystemVideoCodecInfo>(systemCodec)));
153 : }
154 : }
155 799 : }
156 :
157 : void
158 814 : Account::loadConfig() {
159 814 : setActiveCodecs(config_->activeCodecs);
160 0 : auto ringtoneDir = fmt::format("{}/{}", JAMI_DATADIR, RINGDIR);
161 814 : ringtonePath_ = fileutils::getFullPath(ringtoneDir, config_->ringtonePath);
162 : // If the user defined a custom ringtone, the file may not exists
163 : // In this case, fallback on the default ringtone path
164 814 : if (not std::filesystem::is_regular_file(ringtonePath_)) {
165 2442 : JAMI_WARNING("Ringtone {} is not a valid file", ringtonePath_);
166 814 : config_->ringtonePath = DEFAULT_RINGTONE_PATH;
167 814 : ringtonePath_ = fileutils::getFullPath(ringtoneDir, config_->ringtonePath);
168 : }
169 814 : updateUpnpController();
170 814 : }
171 :
172 : void
173 52 : Account::saveConfig() const
174 : {
175 52 : Manager::instance().saveConfig();
176 52 : }
177 :
178 : std::map<std::string, std::string>
179 4605 : Account::getVolatileAccountDetails() const
180 : {
181 9210 : return {{Conf::CONFIG_ACCOUNT_REGISTRATION_STATUS, mapStateNumberToString(registrationState_)},
182 18420 : {libjami::Account::VolatileProperties::ACTIVE, active_ ? TRUE_STR : FALSE_STR}};
183 : }
184 :
185 : bool
186 1735 : Account::hasActiveCodec(MediaType mediaType) const
187 : {
188 21647 : for (auto& codecIt : accountCodecInfoList_)
189 20059 : if ((codecIt->mediaType & mediaType) && codecIt->isActive)
190 147 : return true;
191 1588 : return false;
192 : }
193 :
194 : void
195 814 : Account::setActiveCodecs(const std::vector<unsigned>& list)
196 : {
197 : // first clear the previously stored codecs
198 : // TODO: mutex to protect isActive
199 814 : setAllCodecsActive(MEDIA_ALL, false);
200 :
201 : // list contains the ordered payload of active codecs picked by the user for this account
202 : // we used the codec vector to save the order.
203 814 : uint16_t order = 1;
204 874 : for (const auto& item : list) {
205 60 : if (auto accCodec = searchCodecById(item, MEDIA_ALL)) {
206 60 : accCodec->isActive = true;
207 60 : accCodec->order = order;
208 60 : ++order;
209 60 : }
210 : }
211 814 : sortCodec();
212 814 : }
213 :
214 : void
215 814 : Account::sortCodec()
216 : {
217 814 : std::sort(std::begin(accountCodecInfoList_),
218 814 : std::end(accountCodecInfoList_),
219 18326 : [](const std::shared_ptr<SystemCodecInfo>& a,
220 18326 : const std::shared_ptr<SystemCodecInfo>& b) { return a->order < b->order; });
221 814 : }
222 :
223 : std::string
224 8271 : Account::mapStateNumberToString(RegistrationState state)
225 : {
226 : #define CASE_STATE(X) \
227 : case RegistrationState::X: \
228 : return #X
229 :
230 8271 : switch (state) {
231 0 : CASE_STATE(UNLOADED);
232 3025 : CASE_STATE(UNREGISTERED);
233 1462 : CASE_STATE(TRYING);
234 2234 : CASE_STATE(REGISTERED);
235 0 : CASE_STATE(ERROR_GENERIC);
236 0 : CASE_STATE(ERROR_AUTH);
237 0 : CASE_STATE(ERROR_NETWORK);
238 0 : CASE_STATE(ERROR_HOST);
239 0 : CASE_STATE(ERROR_SERVICE_UNAVAILABLE);
240 0 : CASE_STATE(ERROR_NEED_MIGRATION);
241 1550 : CASE_STATE(INITIALIZING);
242 0 : default:
243 0 : return libjami::Account::States::ERROR_GENERIC;
244 : }
245 :
246 : #undef CASE_STATE
247 : }
248 :
249 : std::vector<unsigned>
250 0 : Account::getDefaultCodecsId()
251 : {
252 0 : return getSystemCodecContainer()->getSystemCodecInfoIdList(MEDIA_ALL);
253 : }
254 :
255 : std::map<std::string, std::string>
256 0 : Account::getDefaultCodecDetails(const unsigned& codecId)
257 : {
258 0 : auto codec = jami::getSystemCodecContainer()->searchCodecById(codecId, jami::MEDIA_ALL);
259 0 : if (codec) {
260 0 : if (codec->mediaType & jami::MEDIA_AUDIO) {
261 0 : auto audioCodec = std::static_pointer_cast<jami::SystemAudioCodecInfo>(codec);
262 0 : return audioCodec->getCodecSpecifications();
263 0 : }
264 0 : if (codec->mediaType & jami::MEDIA_VIDEO) {
265 0 : auto videoCodec = std::static_pointer_cast<jami::SystemVideoCodecInfo>(codec);
266 0 : return videoCodec->getCodecSpecifications();
267 0 : }
268 : }
269 0 : return {};
270 0 : }
271 :
272 : /**
273 : * Get the UPnP IP (external router) address.
274 : * If use UPnP is set to false, the address will be empty.
275 : */
276 : dhtnet::IpAddr
277 0 : Account::getUPnPIpAddress() const
278 : {
279 0 : std::lock_guard lk(upnp_mtx);
280 0 : if (upnpCtrl_)
281 0 : return upnpCtrl_->getExternalIP();
282 0 : return {};
283 0 : }
284 :
285 : /**
286 : * returns whether or not UPnP is enabled and active_
287 : * ie: if it is able to make port mappings
288 : */
289 : bool
290 568 : Account::getUPnPActive() const
291 : {
292 568 : std::lock_guard lk(upnp_mtx);
293 568 : if (upnpCtrl_)
294 430 : return upnpCtrl_->isReady();
295 138 : return false;
296 568 : }
297 :
298 : /*
299 : * private account codec searching functions
300 : *
301 : * */
302 : std::shared_ptr<SystemCodecInfo>
303 60 : Account::searchCodecById(unsigned codecId, MediaType mediaType)
304 : {
305 60 : if (mediaType != MEDIA_NONE) {
306 185 : for (auto& codecIt : accountCodecInfoList_) {
307 185 : if ((codecIt->id == codecId)
308 185 : && (codecIt->mediaType & mediaType))
309 60 : return codecIt;
310 : }
311 : }
312 0 : return {};
313 : }
314 :
315 : std::shared_ptr<SystemCodecInfo>
316 0 : Account::searchCodecByName(const std::string& name, MediaType mediaType)
317 : {
318 0 : if (mediaType != MEDIA_NONE) {
319 0 : for (auto& codecIt : accountCodecInfoList_) {
320 0 : if (codecIt->name == name
321 0 : && (codecIt->mediaType & mediaType))
322 0 : return codecIt;
323 : }
324 : }
325 0 : return {};
326 : }
327 :
328 : std::shared_ptr<SystemCodecInfo>
329 0 : Account::searchCodecByPayload(unsigned payload, MediaType mediaType)
330 : {
331 0 : if (mediaType != MEDIA_NONE) {
332 0 : for (auto& codecIt : accountCodecInfoList_) {
333 0 : if ((codecIt->payloadType == payload)
334 0 : && (codecIt->mediaType & mediaType))
335 0 : return codecIt;
336 : }
337 : }
338 0 : return {};
339 : }
340 :
341 : std::vector<unsigned>
342 814 : Account::getActiveCodecs(MediaType mediaType) const
343 : {
344 814 : if (mediaType == MEDIA_NONE)
345 0 : return {};
346 :
347 814 : std::vector<unsigned> idList;
348 10582 : for (auto& codecIt : accountCodecInfoList_) {
349 9768 : if ((codecIt->mediaType & mediaType) && (codecIt->isActive))
350 2658 : idList.push_back(codecIt->id);
351 : }
352 814 : return idList;
353 814 : }
354 :
355 : std::vector<unsigned>
356 0 : Account::getAccountCodecInfoIdList(MediaType mediaType) const
357 : {
358 0 : if (mediaType == MEDIA_NONE)
359 0 : return {};
360 :
361 0 : std::vector<unsigned> idList;
362 0 : for (auto& codecIt : accountCodecInfoList_) {
363 0 : if (codecIt->mediaType & mediaType)
364 0 : idList.push_back(codecIt->id);
365 : }
366 :
367 0 : return idList;
368 0 : }
369 :
370 : void
371 862 : Account::setAllCodecsActive(MediaType mediaType, bool active)
372 : {
373 862 : if (mediaType == MEDIA_NONE)
374 0 : return;
375 11206 : for (auto& codecIt : accountCodecInfoList_) {
376 10344 : if (codecIt->mediaType & mediaType)
377 10056 : codecIt->isActive = active;
378 : }
379 : }
380 :
381 : void
382 3080 : Account::setCodecActive(unsigned codecId)
383 : {
384 40040 : for (auto& codecIt : accountCodecInfoList_) {
385 36960 : if (codecIt->avcodecId == codecId)
386 2310 : codecIt->isActive = true;
387 : }
388 3080 : }
389 :
390 : void
391 0 : Account::setCodecInactive(unsigned codecId)
392 : {
393 0 : for (auto& codecIt : accountCodecInfoList_) {
394 0 : if (codecIt->avcodecId == codecId)
395 0 : codecIt->isActive = false;
396 : }
397 0 : }
398 :
399 : std::vector<std::shared_ptr<SystemCodecInfo>>
400 812 : Account::getActiveAccountCodecInfoList(MediaType mediaType) const
401 : {
402 812 : if (mediaType == MEDIA_NONE)
403 0 : return {};
404 :
405 812 : std::vector<std::shared_ptr<SystemCodecInfo>> accountCodecList;
406 10556 : for (auto& codecIt : accountCodecInfoList_) {
407 9744 : if ((codecIt->mediaType & mediaType) && (codecIt->isActive))
408 1398 : accountCodecList.push_back(codecIt);
409 : }
410 :
411 812 : return accountCodecList;
412 812 : }
413 :
414 : const std::string&
415 32750 : Account::getUserAgentName()
416 : {
417 32750 : return config_->customUserAgent.empty() ? DEFAULT_USER_AGENT : config_->customUserAgent;
418 : }
419 :
420 : std::string
421 44 : Account::getDefaultUserAgent()
422 : {
423 88 : return fmt::format("{:s} {:s} ({:s})", PACKAGE_NAME, libjami::version(), libjami::platform());
424 : }
425 :
426 : void
427 0 : Account::addDefaultModerator(const std::string& uri)
428 : {
429 0 : config_->defaultModerators.insert(uri);
430 0 : }
431 :
432 : void
433 0 : Account::removeDefaultModerator(const std::string& uri)
434 : {
435 0 : config_->defaultModerators.erase(uri);
436 0 : }
437 :
438 : bool
439 859 : Account::meetMinimumRequiredVersion(const std::vector<unsigned>& version,
440 : const std::vector<unsigned>& minRequiredVersion)
441 : {
442 876 : for (size_t i = 0; i < minRequiredVersion.size(); i++) {
443 872 : if (i == version.size() or version[i] < minRequiredVersion[i])
444 5 : return false;
445 867 : if (version[i] > minRequiredVersion[i])
446 850 : return true;
447 : }
448 4 : return true;
449 : }
450 :
451 : bool
452 0 : Account::setPushNotificationConfig(const std::map<std::string, std::string>& data)
453 : {
454 0 : std::lock_guard lock(configurationMutex_);
455 0 : auto platform = data.find("platform");
456 0 : auto topic = data.find("topic");
457 0 : auto token = data.find("token");
458 0 : bool changed = false;
459 0 : if (platform != data.end() && config_->platform != platform->second) {
460 0 : config_->platform = platform->second;
461 0 : changed = true;
462 : }
463 0 : if (topic != data.end() && config_->notificationTopic != topic->second) {
464 0 : config_->notificationTopic = topic->second;
465 0 : changed = true;
466 : }
467 0 : if (token != data.end() && config_->deviceKey != token->second) {
468 0 : config_->deviceKey = token->second;
469 0 : changed = true;
470 : }
471 0 : if (changed)
472 0 : saveConfig();
473 0 : return changed;
474 0 : }
475 :
476 : } // namespace jami
|