LCOV - code coverage report
Current view: top level - src/plugin - jamipluginmanager.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 64.1 % 329 211
Test Date: 2026-07-06 08:25:38 Functions: 56.1 % 57 32

            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 "jamipluginmanager.h"
      19              : #include "pluginsutils.h"
      20              : #include "fileutils.h"
      21              : #include "archiver.h"
      22              : #include "logger.h"
      23              : #include "manager.h"
      24              : #include "jamidht/jamiaccount.h"
      25              : #include "jami/plugin_manager_interface.h"
      26              : // NOLINTNEXTLINE
      27              : #include "store_ca_crt.cpp"
      28              : 
      29              : #include <fstream>
      30              : #include <msgpack.hpp>
      31              : 
      32              : #define FAILURE                         -1
      33              : #define SUCCESS                         0
      34              : #define PLUGIN_ALREADY_INSTALLED        100 /* Plugin already installed with the same version */
      35              : #define PLUGIN_OLD_VERSION              200 /* Plugin already installed with a newer version */
      36              : #define SIGNATURE_VERIFICATION_FAILED   300
      37              : #define CERTIFICATE_VERIFICATION_FAILED 400
      38              : #define INVALID_PLUGIN                  500
      39              : 
      40              : #ifdef WIN32
      41              : #define LIB_TYPE   ".dll"
      42              : #define LIB_PREFIX ""
      43              : #else
      44              : #ifdef __APPLE__
      45              : #define LIB_TYPE   ".dylib"
      46              : #define LIB_PREFIX "lib"
      47              : #else
      48              : #define LIB_TYPE   ".so"
      49              : #define LIB_PREFIX "lib"
      50              : #endif
      51              : #endif
      52              : 
      53              : namespace jami {
      54              : 
      55           33 : JamiPluginManager::JamiPluginManager()
      56           33 :     : callsm_ {pm_}
      57           33 :     , chatsm_ {pm_}
      58           33 :     , webviewsm_ {pm_}
      59           66 :     , preferencesm_ {pm_}
      60              : {
      61           33 :     registerServices();
      62           33 : }
      63              : 
      64              : std::string
      65           10 : JamiPluginManager::getPluginAuthor(const std::string& rootPath, const std::string& pluginId)
      66              : {
      67           10 :     auto cert = PluginUtils::readPluginCertificate(rootPath, pluginId);
      68           10 :     if (!cert) {
      69            0 :         JAMI_ERROR("Unable to read plugin certificate");
      70            0 :         return {};
      71              :     }
      72           10 :     return cert->getIssuerName();
      73           10 : }
      74              : 
      75              : std::map<std::string, std::string>
      76           19 : JamiPluginManager::getPluginDetails(const std::string& rootPath, bool reset)
      77              : {
      78           19 :     auto detailsIt = pluginDetailsMap_.find(rootPath);
      79           19 :     if (detailsIt != pluginDetailsMap_.end()) {
      80           12 :         if (!reset)
      81            9 :             return detailsIt->second;
      82            3 :         pluginDetailsMap_.erase(detailsIt);
      83              :     }
      84              : 
      85           20 :     std::map<std::string, std::string> details = PluginUtils::parseManifestFile(PluginUtils::manifestPath(rootPath),
      86           10 :                                                                                 rootPath);
      87           10 :     if (!details.empty()) {
      88           10 :         auto itIcon = details.find("iconPath");
      89           10 :         itIcon->second.insert(0, rootPath + DIR_SEPARATOR_CH + "data" + DIR_SEPARATOR_CH);
      90              : 
      91           10 :         auto itImage = details.find("backgroundPath");
      92           10 :         itImage->second.insert(0, rootPath + DIR_SEPARATOR_CH + "data" + DIR_SEPARATOR_CH);
      93              : 
      94           40 :         details["soPath"] = rootPath + DIR_SEPARATOR_CH + LIB_PREFIX + details["id"] + LIB_TYPE;
      95           30 :         details["author"] = getPluginAuthor(rootPath, details["id"]);
      96           10 :         detailsIt = pluginDetailsMap_.emplace(rootPath, std::move(details)).first;
      97           10 :         return detailsIt->second;
      98              :     }
      99            0 :     return {};
     100           10 : }
     101              : 
     102              : std::vector<std::string>
     103            2 : JamiPluginManager::getInstalledPlugins()
     104              : {
     105              :     // Gets all plugins in standard path
     106            2 :     auto pluginsPath = fileutils::get_data_dir() / "plugins";
     107            2 :     std::vector<std::string> pluginsPaths;
     108            2 :     std::error_code ec;
     109            4 :     for (const auto& entry : std::filesystem::directory_iterator(pluginsPath, ec)) {
     110            1 :         const auto& p = entry.path();
     111            1 :         if (PluginUtils::checkPluginValidity(p))
     112            1 :             pluginsPaths.emplace_back(p.string());
     113            2 :     }
     114              : 
     115              :     // Gets plugins installed in non standard path
     116            2 :     std::vector<std::string> nonStandardInstalls = jami::Manager::instance().pluginPreferences.getInstalledPlugins();
     117            2 :     for (auto& path : nonStandardInstalls) {
     118            0 :         if (PluginUtils::checkPluginValidity(path))
     119            0 :             pluginsPaths.emplace_back(path);
     120              :     }
     121              : 
     122            4 :     return pluginsPaths;
     123            2 : }
     124              : 
     125              : bool
     126            0 : JamiPluginManager::checkPluginCertificatePublicKey(const std::string& oldJplPath, const std::string& newJplPath)
     127              : {
     128            0 :     std::map<std::string, std::string> oldDetails = PluginUtils::parseManifestFile(PluginUtils::manifestPath(oldJplPath),
     129            0 :                                                                                    oldJplPath);
     130            0 :     std::error_code ec;
     131            0 :     if (oldDetails.empty()
     132            0 :         || !std::filesystem::is_regular_file(oldJplPath + DIR_SEPARATOR_CH + oldDetails["id"] + ".crt", ec)
     133            0 :         || !std::filesystem::is_regular_file(newJplPath, ec))
     134            0 :         return false;
     135              :     try {
     136            0 :         auto oldCert = PluginUtils::readPluginCertificate(oldJplPath, oldDetails["id"]);
     137            0 :         auto newCert = PluginUtils::readPluginCertificateFromArchive(newJplPath);
     138            0 :         if (!oldCert || !newCert) {
     139            0 :             return false;
     140              :         }
     141            0 :         return oldCert->getPublicKey() == newCert->getPublicKey();
     142            0 :     } catch (const std::exception& e) {
     143            0 :         JAMI_ERROR("{}", e.what());
     144            0 :         return false;
     145            0 :     }
     146              :     return true;
     147            0 : }
     148              : 
     149              : bool
     150           12 : JamiPluginManager::checkPluginCertificateValidity(dht::crypto::Certificate* cert)
     151              : {
     152           12 :     if (!cert || !*cert)
     153            0 :         return false;
     154           12 :     trust_.add(crypto::Certificate(store_ca_crt, sizeof(store_ca_crt)));
     155           12 :     auto result = trust_.verify(*cert);
     156           12 :     if (!result) {
     157           12 :         JAMI_ERROR("Certificate verification failed: {}", result.toString());
     158              :     }
     159           12 :     return (bool) result;
     160              : }
     161              : 
     162              : std::map<std::string, std::string>
     163            0 : JamiPluginManager::getPlatformInfo()
     164              : {
     165            0 :     return PluginUtils::getPlatformInfo();
     166              : }
     167              : 
     168              : bool
     169           12 : JamiPluginManager::checkPluginSignatureFile(const std::string& jplPath)
     170              : {
     171              :     // check if the file exists
     172           12 :     std::error_code ec;
     173           12 :     if (!std::filesystem::is_regular_file(jplPath, ec)) {
     174            1 :         return false;
     175              :     }
     176              :     try {
     177           11 :         auto signatures = PluginUtils::readPluginSignatureFromArchive(jplPath);
     178           11 :         auto manifest = PluginUtils::readPluginManifestFromArchive(jplPath);
     179           11 :         const std::string& name = manifest["id"];
     180           11 :         auto filesPath = archiver::listFilesFromArchive(jplPath);
     181          156 :         for (const auto& file : filesPath) {
     182              :             // we skip the signatures and signatures.sig file
     183          147 :             if (file == "signatures" || file == "signatures.sig")
     184           20 :                 continue;
     185              :             // we also skip the plugin certificate
     186          127 :             if (file == name + ".crt")
     187            9 :                 continue;
     188              : 
     189          118 :             if (signatures.count(file) == 0) {
     190            2 :                 return false;
     191              :             }
     192              :         }
     193           15 :     } catch (const std::exception& e) {
     194            0 :         return false;
     195            0 :     }
     196            9 :     return true;
     197              : }
     198              : 
     199              : bool
     200           11 : JamiPluginManager::checkPluginSignatureValidity(const std::string& jplPath, dht::crypto::Certificate* cert)
     201              : {
     202           11 :     if (!std::filesystem::is_regular_file(jplPath))
     203            0 :         return false;
     204              :     try {
     205           11 :         const auto& pk = cert->getPublicKey();
     206           10 :         auto signaturesData = archiver::readFileFromArchive(jplPath, "signatures");
     207           10 :         auto signatureFile = PluginUtils::readSignatureFileFromArchive(jplPath);
     208           10 :         if (!pk.checkSignature(signaturesData, signatureFile))
     209            1 :             return false;
     210            9 :         auto signatures = PluginUtils::readPluginSignatureFromArchive(jplPath);
     211          117 :         for (const auto& signature : signatures) {
     212          108 :             auto file = archiver::readFileFromArchive(jplPath, signature.first);
     213          108 :             if (!pk.checkSignature(file, signature.second)) {
     214            0 :                 JAMI_ERROR("{} not correctly signed", signature.first);
     215            0 :                 return false;
     216              :             }
     217          108 :         }
     218           12 :     } catch (const std::exception& e) {
     219            1 :         return false;
     220            1 :     }
     221              : 
     222            9 :     return true;
     223              : }
     224              : 
     225              : bool
     226            9 : JamiPluginManager::checkPluginSignature(const std::string& jplPath, dht::crypto::Certificate* cert)
     227              : {
     228            9 :     if (!std::filesystem::is_regular_file(jplPath) || !cert || !*cert)
     229            1 :         return false;
     230              :     try {
     231            8 :         return checkPluginSignatureValidity(jplPath, cert) && checkPluginSignatureFile(jplPath);
     232            0 :     } catch (const std::exception& e) {
     233            0 :         return false;
     234            0 :     }
     235              : }
     236              : 
     237              : std::unique_ptr<dht::crypto::Certificate>
     238           13 : JamiPluginManager::checkPluginCertificate(const std::string& jplPath, bool force)
     239              : {
     240           13 :     if (!std::filesystem::is_regular_file(jplPath))
     241            1 :         return {};
     242              :     try {
     243           12 :         auto cert = PluginUtils::readPluginCertificateFromArchive(jplPath);
     244           12 :         if (checkPluginCertificateValidity(cert.get()) || force) {
     245           10 :             return cert;
     246              :         }
     247            2 :         return {};
     248           12 :     } catch (const std::exception& e) {
     249            0 :         return {};
     250            0 :     }
     251              : }
     252              : 
     253              : int
     254            7 : JamiPluginManager::installPlugin(const std::string& jplPath, bool force)
     255              : {
     256            7 :     int r {SUCCESS};
     257            7 :     std::error_code ec;
     258            7 :     if (std::filesystem::is_regular_file(jplPath, ec)) {
     259              :         try {
     260            7 :             auto manifestMap = PluginUtils::readPluginManifestFromArchive(jplPath);
     261            7 :             const std::string& name = manifestMap["id"];
     262            7 :             if (name.empty())
     263            0 :                 return INVALID_PLUGIN;
     264            7 :             auto cert = checkPluginCertificate(jplPath, force);
     265            7 :             if (!cert)
     266            0 :                 return CERTIFICATE_VERIFICATION_FAILED;
     267            7 :             if (!checkPluginSignature(jplPath, cert.get()))
     268            0 :                 return SIGNATURE_VERIFICATION_FAILED;
     269            7 :             const std::string& version = manifestMap["version"];
     270            7 :             auto destinationDir = (fileutils::get_data_dir() / "plugins" / name).string();
     271              :             // Find if there is an existing version of this plugin
     272           14 :             const auto alreadyInstalledManifestMap = PluginUtils::parseManifestFile(PluginUtils::manifestPath(
     273              :                                                                                         destinationDir),
     274            7 :                                                                                     destinationDir);
     275              : 
     276            7 :             if (!alreadyInstalledManifestMap.empty()) {
     277            2 :                 if (force) {
     278            2 :                     r = uninstallPlugin(destinationDir);
     279            2 :                     if (r == SUCCESS) {
     280            2 :                         archiver::uncompressArchive(jplPath, destinationDir, PluginUtils::uncompressJplFunction);
     281              :                     }
     282              :                 } else {
     283            0 :                     std::string installedVersion = alreadyInstalledManifestMap.at("version");
     284            0 :                     if (version > installedVersion) {
     285            0 :                         if (!checkPluginCertificatePublicKey(destinationDir, jplPath))
     286            0 :                             return CERTIFICATE_VERIFICATION_FAILED;
     287            0 :                         r = uninstallPlugin(destinationDir);
     288            0 :                         if (r == SUCCESS) {
     289            0 :                             archiver::uncompressArchive(jplPath, destinationDir, PluginUtils::uncompressJplFunction);
     290              :                         }
     291            0 :                     } else if (version == installedVersion) {
     292            0 :                         r = PLUGIN_ALREADY_INSTALLED;
     293              :                     } else {
     294            0 :                         r = PLUGIN_OLD_VERSION;
     295              :                     }
     296            0 :                 }
     297              :             } else {
     298            5 :                 archiver::uncompressArchive(jplPath, destinationDir, PluginUtils::uncompressJplFunction);
     299              :             }
     300            7 :             if (!libjami::getPluginsEnabled()) {
     301            0 :                 libjami::setPluginsEnabled(true);
     302            0 :                 Manager::instance().saveConfig();
     303            0 :                 loadPlugins();
     304            0 :                 return r;
     305              :             }
     306            7 :             libjami::loadPlugin(destinationDir);
     307            7 :         } catch (const std::exception& e) {
     308            0 :             JAMI_ERROR("{}", e.what());
     309            0 :         }
     310              :     }
     311            7 :     return r;
     312              : }
     313              : 
     314              : int
     315            7 : JamiPluginManager::uninstallPlugin(const std::string& rootPath)
     316              : {
     317            7 :     std::error_code ec;
     318            7 :     if (PluginUtils::checkPluginValidity(rootPath)) {
     319            7 :         auto detailsIt = pluginDetailsMap_.find(rootPath);
     320            7 :         if (detailsIt != pluginDetailsMap_.end()) {
     321            7 :             bool loaded = pm_.checkLoadedPlugin(rootPath);
     322            7 :             if (loaded) {
     323           20 :                 JAMI_LOG("PLUGIN: unloading before uninstall.");
     324            5 :                 bool status = libjami::unloadPlugin(rootPath);
     325            5 :                 if (!status) {
     326            0 :                     JAMI_LOG("PLUGIN: unable to unload, not performing uninstall.");
     327            0 :                     return FAILURE;
     328              :                 }
     329              :             }
     330           21 :             for (const auto& accId : jami::Manager::instance().getAccountList())
     331           42 :                 std::filesystem::remove_all(fileutils::get_data_dir() / accId / "plugins" / detailsIt->second.at("id"),
     332            7 :                                             ec);
     333            7 :             pluginDetailsMap_.erase(detailsIt);
     334              :         }
     335            7 :         return std::filesystem::remove_all(rootPath, ec) ? SUCCESS : FAILURE;
     336              :     } else {
     337            0 :         JAMI_LOG("PLUGIN: not installed.");
     338            0 :         return FAILURE;
     339              :     }
     340              : }
     341              : 
     342              : bool
     343            8 : JamiPluginManager::loadPlugin(const std::string& rootPath)
     344              : {
     345              : #ifdef ENABLE_PLUGIN
     346              :     try {
     347           24 :         bool status = pm_.load(getPluginDetails(rootPath).at("soPath"));
     348           32 :         JAMI_LOG("PLUGIN: load status - {}", status);
     349              : 
     350            8 :         return status;
     351              : 
     352            0 :     } catch (const std::exception& e) {
     353            0 :         JAMI_ERROR("{}", e.what());
     354            0 :         return false;
     355            0 :     }
     356              : #endif
     357              :     return false;
     358              : }
     359              : 
     360              : bool
     361           10 : JamiPluginManager::loadPlugins()
     362              : {
     363              : #ifdef ENABLE_PLUGIN
     364           10 :     bool status = true;
     365           10 :     auto loadedPlugins = jami::Manager::instance().pluginPreferences.getLoadedPlugins();
     366           11 :     for (const auto& pluginPath : loadedPlugins) {
     367            1 :         status &= loadPlugin(pluginPath);
     368              :     }
     369           10 :     return status;
     370              : #endif
     371              :     return false;
     372           10 : }
     373              : 
     374              : bool
     375            7 : JamiPluginManager::unloadPlugin(const std::string& rootPath)
     376              : {
     377              : #ifdef ENABLE_PLUGIN
     378              :     try {
     379           21 :         bool status = pm_.unload(getPluginDetails(rootPath).at("soPath"));
     380           28 :         JAMI_LOG("PLUGIN: unload status - {}", status);
     381              : 
     382            7 :         return status;
     383            0 :     } catch (const std::exception& e) {
     384            0 :         JAMI_ERROR("{}", e.what());
     385            0 :         return false;
     386            0 :     }
     387              : #endif
     388              :     return false;
     389              : }
     390              : 
     391              : std::vector<std::string>
     392            4 : JamiPluginManager::getLoadedPlugins() const
     393              : {
     394            4 :     std::vector<std::string> loadedSoPlugins = pm_.getLoadedPlugins();
     395            4 :     std::vector<std::string> loadedPlugins {};
     396            4 :     loadedPlugins.reserve(loadedSoPlugins.size());
     397            4 :     std::transform(loadedSoPlugins.begin(),
     398              :                    loadedSoPlugins.end(),
     399              :                    std::back_inserter(loadedPlugins),
     400            2 :                    [](const std::string& soPath) { return PluginUtils::getRootPathFromSoPath(soPath).string(); });
     401            8 :     return loadedPlugins;
     402            4 : }
     403              : 
     404              : std::vector<std::map<std::string, std::string>>
     405            2 : JamiPluginManager::getPluginPreferences(const std::string& rootPath, const std::string& accountId)
     406              : {
     407            2 :     return PluginPreferencesUtils::getPreferences(rootPath, accountId);
     408              : }
     409              : 
     410              : bool
     411            2 : JamiPluginManager::setPluginPreference(const std::filesystem::path& rootPath,
     412              :                                        const std::string& accountId,
     413              :                                        const std::string& key,
     414              :                                        const std::string& value)
     415              : {
     416            2 :     std::string acc = accountId;
     417              : 
     418              :     // If we try to change a preference value linked to an account
     419              :     // but that preference is global, we must ignore accountId and
     420              :     // change the preference for every account
     421            2 :     if (!accountId.empty()) {
     422              :         // Get global preferences
     423            1 :         auto preferences = PluginPreferencesUtils::getPreferences(rootPath, "");
     424              :         // Check if the preference we want to change is global
     425            1 :         auto it = std::find_if(preferences.cbegin(),
     426              :                                preferences.cend(),
     427            2 :                                [key](const std::map<std::string, std::string>& preference) {
     428            3 :                                    return preference.at("key") == key;
     429              :                                });
     430              :         // Ignore accountId if global preference
     431            1 :         if (it != preferences.cend())
     432            0 :             acc.clear();
     433            1 :     }
     434              : 
     435              :     std::map<std::string, std::string> pluginUserPreferencesMap
     436            2 :         = PluginPreferencesUtils::getUserPreferencesValuesMap(rootPath, acc);
     437              :     std::map<std::string, std::string> pluginPreferencesMap = PluginPreferencesUtils::getPreferencesValuesMap(rootPath,
     438            2 :                                                                                                               acc);
     439              : 
     440              :     // If any plugin handler is active we may have to reload it
     441            2 :     bool force {pm_.checkLoadedPlugin(rootPath.string())};
     442              : 
     443              :     // We check if the preference is modified without having to reload plugin
     444            2 :     force &= preferencesm_.setPreference(key, value, rootPath.string(), acc);
     445            2 :     force &= callsm_.setPreference(key, value, rootPath.string());
     446            2 :     force &= chatsm_.setPreference(key, value, rootPath.string());
     447              : 
     448            2 :     if (force)
     449            0 :         unloadPlugin(rootPath.string());
     450              : 
     451              :     // Save preferences.msgpack with modified preferences values
     452            2 :     auto find = pluginPreferencesMap.find(key);
     453            2 :     if (find != pluginPreferencesMap.end()) {
     454            2 :         pluginUserPreferencesMap[key] = value;
     455            2 :         auto preferencesValuesFilePath = PluginPreferencesUtils::valuesFilePath(rootPath, acc);
     456            2 :         std::lock_guard guard(dhtnet::fileutils::getFileLock(preferencesValuesFilePath));
     457            2 :         std::ofstream fs(preferencesValuesFilePath, std::ios::binary);
     458            2 :         if (!fs.good()) {
     459            0 :             if (force) {
     460            0 :                 loadPlugin(rootPath.string());
     461              :             }
     462            0 :             return false;
     463              :         }
     464              :         try {
     465            2 :             msgpack::pack(fs, pluginUserPreferencesMap);
     466            0 :         } catch (const std::exception& e) {
     467            0 :             JAMI_ERROR("{}", e.what());
     468            0 :             if (force) {
     469            0 :                 loadPlugin(rootPath.string());
     470              :             }
     471            0 :             return false;
     472            0 :         }
     473            2 :     }
     474            2 :     if (force) {
     475            0 :         loadPlugin(rootPath.string());
     476              :     }
     477            2 :     return true;
     478            2 : }
     479              : 
     480              : std::map<std::string, std::string>
     481           15 : JamiPluginManager::getPluginPreferencesValuesMap(const std::string& rootPath, const std::string& accountId)
     482              : {
     483           15 :     return PluginPreferencesUtils::getPreferencesValuesMap(rootPath, accountId);
     484              : }
     485              : 
     486              : bool
     487            3 : JamiPluginManager::resetPluginPreferencesValuesMap(const std::string& rootPath, const std::string& accountId)
     488              : {
     489            3 :     bool acc {accountId.empty()};
     490            3 :     bool loaded {pm_.checkLoadedPlugin(rootPath)};
     491            3 :     if (loaded && acc)
     492            0 :         unloadPlugin(rootPath);
     493            3 :     auto status = PluginPreferencesUtils::resetPreferencesValuesMap(rootPath, accountId);
     494            3 :     preferencesm_.resetPreferences(rootPath, accountId);
     495            3 :     if (loaded && acc) {
     496            0 :         loadPlugin(rootPath);
     497              :     }
     498            3 :     return status;
     499              : }
     500              : 
     501              : void
     502           33 : JamiPluginManager::registerServices()
     503              : {
     504              :     // Register getPluginPreferences so that plugin's can receive it's preferences
     505           99 :     pm_.registerService("getPluginPreferences", [](const DLPlugin* plugin, void* data) {
     506            0 :         auto* ppp = static_cast<std::map<std::string, std::string>*>(data);
     507            0 :         *ppp = PluginPreferencesUtils::getPreferencesValuesMap(PluginUtils::getRootPathFromSoPath(plugin->getPath()));
     508            0 :         return SUCCESS;
     509              :     });
     510              : 
     511              :     // Register getPluginDataPath so that plugin's can receive the path to it's data folder
     512           99 :     pm_.registerService("getPluginDataPath", [](const DLPlugin* plugin, void* data) {
     513            8 :         auto* dataPath = static_cast<std::string*>(data);
     514            8 :         dataPath->assign(PluginUtils::dataPath(plugin->getPath()).string());
     515            8 :         return SUCCESS;
     516              :     });
     517              : 
     518              :     // getPluginAccPreferences is a service that allows plugins to load saved per account preferences.
     519            0 :     auto getPluginAccPreferences = [](const DLPlugin* plugin, void* data) {
     520            0 :         const auto path = PluginUtils::getRootPathFromSoPath(plugin->getPath());
     521            0 :         auto* preferencesPtr {(static_cast<PreferencesMap*>(data))};
     522            0 :         if (!preferencesPtr)
     523            0 :             return FAILURE;
     524              : 
     525            0 :         preferencesPtr->emplace("default", PluginPreferencesUtils::getPreferencesValuesMap(path, "default"));
     526              : 
     527            0 :         for (const auto& accId : jami::Manager::instance().getAccountList())
     528            0 :             preferencesPtr->emplace(accId, PluginPreferencesUtils::getPreferencesValuesMap(path, accId));
     529            0 :         return SUCCESS;
     530            0 :     };
     531              : 
     532           99 :     pm_.registerService("getPluginAccPreferences", getPluginAccPreferences);
     533              : 
     534              :     // getAccountUri returns the Jami URI (username) for a given account ID.
     535              :     // data must point to a std::pair<std::string, std::string> where
     536              :     // first = accountId (input) and second = uri (output).
     537           99 :     pm_.registerService("getAccountUri", [](const DLPlugin* /*plugin*/, void* data) {
     538            0 :         auto* p = static_cast<std::pair<std::string, std::string>*>(data);
     539            0 :         if (!p) {
     540            0 :             return FAILURE;
     541              :         }
     542            0 :         if (const auto acc = Manager::instance().getAccount<JamiAccount>(p->first)) {
     543            0 :             p->second = acc->getUsername();
     544            0 :         }
     545            0 :         return SUCCESS;
     546              :     });
     547              : 
     548              :     // getAccountIds returns all account IDs known to the daemon.
     549              :     // data must point to a std::vector<std::string>.
     550           99 :     pm_.registerService("getAccountIds", [](const DLPlugin* /*plugin*/, void* data) {
     551            0 :         auto* p = static_cast<std::vector<std::string>*>(data);
     552            0 :         if (!p) {
     553            0 :             return FAILURE;
     554              :         }
     555            0 :         *p = Manager::instance().getAccountList();
     556            0 :         return SUCCESS;
     557              :     });
     558              : 
     559              :     // setPluginAccPreference writes a single per-account preference to disk without
     560              :     // triggering handler callbacks.
     561              :     // data must point to std::tuple<std::string,std::string,std::string>: {accountId, key, value}.
     562           99 :     pm_.registerService("setPluginAccPreference", [](const DLPlugin* plugin, void* data) {
     563              :         using T = std::tuple<std::string, std::string, std::string>;
     564            0 :         auto* p = static_cast<T*>(data);
     565            0 :         if (!p) {
     566            0 :             return FAILURE;
     567              :         }
     568            0 :         const auto& [accountId, key, value] = *p;
     569            0 :         const auto rootPath = PluginUtils::getRootPathFromSoPath(plugin->getPath());
     570              : 
     571              :         // The key must exist in this account's effective preferences map.
     572            0 :         if (const auto prefsMap = PluginPreferencesUtils::getPreferencesValuesMap(rootPath, accountId);
     573            0 :             !prefsMap.contains(key)) {
     574            0 :             return FAILURE;
     575            0 :         }
     576              : 
     577            0 :         return PluginPreferencesUtils::setUserPreferenceValue(rootPath, accountId, key, value) ? SUCCESS : FAILURE;
     578            0 :     });
     579           33 : }
     580              : 
     581              : #ifdef LIBJAMI_TEST
     582              : void
     583            1 : JamiPluginManager::addPluginAuthority(const dht::crypto::Certificate& cert)
     584              : {
     585            1 :     trust_.add(cert);
     586            1 : }
     587              : #endif
     588              : 
     589              : } // namespace jami
        

Generated by: LCOV version 2.0-1