LCOV - code coverage report
Current view: top level - src - account.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 58.7 % 235 138
Test Date: 2026-06-13 09:18:46 Functions: 65.0 % 40 26

            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          802 : Account::Account(const std::string& accountId)
      65          802 :     : rand(Manager::instance().getSeededRandomEngine())
      66          802 :     , accountID_(accountId)
      67          802 :     , systemCodecContainer_(Manager::instance().getSystemCodecContainer())
      68         3208 :     , idPath_(fileutils::get_data_dir() / accountId)
      69              : {
      70              :     // Initialize the codec order, used when creating a new account
      71          802 :     loadDefaultCodecs();
      72          802 : }
      73              : 
      74          802 : Account::~Account() {}
      75              : 
      76              : void
      77          773 : Account::hangupCalls()
      78              : {
      79          795 :     for (const auto& callId : callSet_.getCallIds())
      80          795 :         Manager::instance().hangupCall(getAccountID(), callId);
      81          773 : }
      82              : 
      83              : void
      84         1029 : Account::updateUpnpController()
      85              : {
      86         1029 :     std::lock_guard lk {upnp_mtx};
      87              : 
      88         1029 :     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          811 :     if (not upnpCtrl_) {
      95          793 :         upnpCtrl_ = std::make_shared<dhtnet::upnp::Controller>(Manager::instance().upnpContext());
      96          793 :         if (not upnpCtrl_) {
      97            0 :             throw std::runtime_error("Failed to create a UPNP Controller instance!");
      98              :         }
      99              :     }
     100         1029 : }
     101              : 
     102              : void
     103         5064 : Account::setRegistrationState(RegistrationState state, int detail_code, const std::string& detail_str)
     104              : {
     105         5064 :     if (state != registrationState_) {
     106         3676 :         registrationState_ = state;
     107              :         // Notify the client
     108         7352 :         runOnMainThread([accountId = accountID_,
     109              :                          state = mapStateNumberToString(registrationState_),
     110              :                          detail_code,
     111              :                          detail_str,
     112         3676 :                          details = getVolatileAccountDetails()] {
     113         3676 :             emitSignal<libjami::ConfigurationSignal::RegistrationStateChanged>(accountId,
     114         3676 :                                                                                state,
     115              :                                                                                detail_code,
     116         3676 :                                                                                detail_str);
     117              : 
     118         3676 :             emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>(accountId, details);
     119         3676 :         });
     120              :     }
     121         5064 : }
     122              : 
     123              : void
     124          802 : Account::loadDefaultCodecs()
     125              : {
     126              :     // default codec are system codecs
     127          802 :     const auto& systemCodecList = systemCodecContainer_->getSystemCodecInfoList();
     128          802 :     accountCodecInfoList_.clear();
     129          802 :     accountCodecInfoList_.reserve(systemCodecList.size());
     130        10426 :     for (const auto& systemCodec : systemCodecList) {
     131              :         // As defined in SDP RFC, only select a codec if it can encode and decode
     132         9624 :         if ((systemCodec->codecType & CODEC_ENCODER_DECODER) != CODEC_ENCODER_DECODER)
     133            0 :             continue;
     134              : 
     135         9624 :         if (systemCodec->mediaType & MEDIA_AUDIO) {
     136         6416 :             accountCodecInfoList_.emplace_back(
     137        12832 :                 std::make_shared<SystemAudioCodecInfo>(*std::static_pointer_cast<SystemAudioCodecInfo>(systemCodec)));
     138              :         }
     139              : 
     140         9624 :         if (systemCodec->mediaType & MEDIA_VIDEO) {
     141         3208 :             accountCodecInfoList_.emplace_back(
     142         6416 :                 std::make_shared<SystemVideoCodecInfo>(*std::static_pointer_cast<SystemVideoCodecInfo>(systemCodec)));
     143              :         }
     144              :     }
     145          802 : }
     146              : 
     147              : void
     148          817 : Account::loadConfig()
     149              : {
     150          817 :     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          817 :     auto ringtoneDir = fileutils::get_resource_dir_path() / RINGDIR;
     155          817 :     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          817 :     std::ifstream f(ringtonePath_);
     159          817 :     if (not std::filesystem::is_regular_file(ringtonePath_) || !f.is_open()) {
     160         3268 :         JAMI_WARNING("Ringtone {} is not a valid file", ringtonePath_);
     161          817 :         config_->ringtonePath = DEFAULT_RINGTONE_PATH;
     162          817 :         ringtonePath_ = fileutils::getFullPath(ringtoneDir, config_->ringtonePath);
     163              :     }
     164          817 :     updateUpnpController();
     165          817 : }
     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         4658 : Account::getVolatileAccountDetails() const
     175              : {
     176            0 :     return {{Conf::CONFIG_ACCOUNT_REGISTRATION_STATUS, mapStateNumberToString(registrationState_)},
     177        23290 :             {libjami::Account::VolatileProperties::ACTIVE, active_ ? TRUE_STR : FALSE_STR}};
     178         9316 : }
     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         1735 : Account::hasActiveCodec(MediaType mediaType) const
     196              : {
     197        21695 :     for (auto& codecIt : accountCodecInfoList_)
     198        20101 :         if ((codecIt->mediaType & mediaType) && codecIt->isActive)
     199          141 :             return true;
     200         1594 :     return false;
     201              : }
     202              : 
     203              : void
     204          817 : Account::setActiveCodecs(const std::vector<unsigned>& list)
     205              : {
     206              :     // first clear the previously stored codecs
     207              :     // TODO: mutex to protect isActive
     208          817 :     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          817 :     uint16_t order = 1;
     213          877 :     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          817 :     sortCodec();
     221          817 : }
     222              : 
     223              : void
     224          817 : Account::sortCodec()
     225              : {
     226         1634 :     std::sort(std::begin(accountCodecInfoList_),
     227          817 :               std::end(accountCodecInfoList_),
     228        18392 :               [](const std::shared_ptr<SystemCodecInfo>& a, const std::shared_ptr<SystemCodecInfo>& b) {
     229        18392 :                   return a->order < b->order;
     230              :               });
     231          817 : }
     232              : 
     233              : std::string
     234         8334 : Account::mapStateNumberToString(RegistrationState state)
     235              : {
     236              : #define CASE_STATE(X) \
     237              :     case RegistrationState::X: \
     238              :         return #X
     239              : 
     240         8334 :     switch (state) {
     241            0 :         CASE_STATE(UNLOADED);
     242         9093 :         CASE_STATE(UNREGISTERED);
     243         4419 :         CASE_STATE(TRYING);
     244         6708 :         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         4782 :         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 Manager::instance().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 = Manager::instance().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          531 : Account::getUPnPActive() const
     301              : {
     302          531 :     std::lock_guard lk(upnp_mtx);
     303          531 :     if (upnpCtrl_)
     304          398 :         return upnpCtrl_->isReady();
     305          133 :     return false;
     306          531 : }
     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          817 : Account::getActiveCodecs(MediaType mediaType) const
     350              : {
     351          817 :     if (mediaType == MEDIA_NONE)
     352            0 :         return {};
     353              : 
     354          817 :     std::vector<unsigned> idList;
     355        10621 :     for (auto& codecIt : accountCodecInfoList_) {
     356         9804 :         if ((codecIt->mediaType & mediaType) && (codecIt->isActive))
     357         2667 :             idList.push_back(codecIt->id);
     358              :     }
     359          817 :     return idList;
     360          817 : }
     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          865 : Account::setAllCodecsActive(MediaType mediaType, bool active)
     379              : {
     380          865 :     if (mediaType == MEDIA_NONE)
     381            0 :         return;
     382        11245 :     for (auto& codecIt : accountCodecInfoList_) {
     383        10380 :         if (codecIt->mediaType & mediaType)
     384        10092 :             codecIt->isActive = active;
     385              :     }
     386              : }
     387              : 
     388              : void
     389         3092 : Account::setCodecActive(unsigned codecId)
     390              : {
     391        40196 :     for (auto& codecIt : accountCodecInfoList_) {
     392        37104 :         if (codecIt->avcodecId == codecId)
     393         2319 :             codecIt->isActive = true;
     394              :     }
     395         3092 : }
     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          742 : Account::getActiveAccountCodecInfoList(MediaType mediaType) const
     408              : {
     409          742 :     if (mediaType == MEDIA_NONE)
     410            0 :         return {};
     411              : 
     412          742 :     std::vector<std::shared_ptr<SystemCodecInfo>> accountCodecList;
     413         9646 :     for (auto& codecIt : accountCodecInfoList_) {
     414         8904 :         if ((codecIt->mediaType & mediaType) && (codecIt->isActive))
     415         1293 :             accountCodecList.push_back(codecIt);
     416              :     }
     417              : 
     418          742 :     return accountCodecList;
     419          742 : }
     420              : 
     421              : const std::string&
     422          442 : Account::getUserAgentName()
     423              : {
     424          442 :     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          811 : Account::meetMinimumRequiredVersion(const std::vector<unsigned>& version,
     447              :                                     const std::vector<unsigned>& minRequiredVersion)
     448              : {
     449          828 :     for (size_t i = 0; i < minRequiredVersion.size(); i++) {
     450          824 :         if (i == version.size() or version[i] < minRequiredVersion[i])
     451            5 :             return false;
     452          819 :         if (version[i] > minRequiredVersion[i])
     453          802 :             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 2.0-1