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: 2025-12-18 10:07:43 Functions: 26 40 65.0 %

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

Generated by: LCOV version 1.14