LCOV - code coverage report
Current view: top level - foo/src - account.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 138 234 59.0 %
Date: 2026-04-01 09:29:43 Functions: 26 40 65.0 %

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

Generated by: LCOV version 1.14