LCOV - code coverage report
Current view: top level - src/jamidht - namedirectory.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 65.3 % 331 216
Test Date: 2026-11-09 10:41:14 Functions: 86.4 % 22 19

            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              : #ifdef HAVE_CONFIG_H
      19              : #include "config.h"
      20              : #endif
      21              : #include "namedirectory.h"
      22              : 
      23              : #include "logger.h"
      24              : #include "string_utils.h"
      25              : #include "fileutils.h"
      26              : #include "base64.h"
      27              : 
      28              : #include <asio.hpp>
      29              : 
      30              : #include "manager.h"
      31              : #include <opendht/crypto.h>
      32              : #include <opendht/utils.h>
      33              : #include <opendht/http.h>
      34              : #include <opendht/logger.h>
      35              : #include <opendht/thread_pool.h>
      36              : 
      37              : #include <cstddef>
      38              : #include <msgpack.hpp>
      39              : #include "json_utils.h"
      40              : 
      41              : /* for Visual Studio */
      42              : 
      43              : #include <sstream>
      44              : #include <regex>
      45              : #include <fstream>
      46              : 
      47              : namespace jami {
      48              : 
      49              : constexpr const char* const QUERY_NAME {"/name/"};
      50              : constexpr const char* const QUERY_ADDR {"/addr/"};
      51              : constexpr auto CACHE_DIRECTORY {"namecache"sv};
      52              : constexpr const char DEFAULT_SERVER_HOST[] = "https://ns.jami.net";
      53              : 
      54              : constexpr std::string_view HEX_PREFIX = "0x"sv;
      55              : constexpr std::chrono::seconds SAVE_INTERVAL {5};
      56              : 
      57              : /*
      58              :  *  Parser for URIs.         ( protocol        )    ( username         ) ( hostname )
      59              :  *  - Requires "@" if a username is present (e.g., "user@domain.com").
      60              :  *  - Allows common URL-safe special characters in usernames and domains.
      61              :  *
      62              :  *  Regex breakdown:
      63              :  *  1. `([a-zA-Z]+:(?://)?)?` → Optional scheme ("http://", "ftp://").
      64              :  *  2. `(?:([^\s@]{1,64})@)?` → Optional username (max 64 chars, Unicode allowed).
      65              :  *  3. `([^\s@]+)` → Domain or standalone name (Unicode allowed, no spaces or "@").
      66              :  */
      67              : const std::regex URI_VALIDATOR {R"(^([a-zA-Z]+:(?://)?)?(?:([\w\-.~%!$&'()*+,;=]{1,64}|[^\s@]{1,64})@)?([^\s@]+)$)"};
      68              : 
      69              : constexpr size_t MAX_RESPONSE_SIZE {1024ul * 1024};
      70              : 
      71              : using Request = dht::http::Request;
      72              : 
      73              : void
      74            1 : toLower(std::string& string)
      75              : {
      76            1 :     std::transform(string.begin(), string.end(), string.begin(), ::tolower);
      77            1 : }
      78              : 
      79              : NameDirectory&
      80            0 : NameDirectory::instance()
      81              : {
      82            0 :     return instance(DEFAULT_SERVER_HOST);
      83              : }
      84              : 
      85              : void
      86            3 : NameDirectory::lookupUri(std::string_view uri, const std::string& default_server, LookupCallback cb)
      87              : {
      88            3 :     const std::string& default_ns = default_server.empty() ? DEFAULT_SERVER_HOST : default_server;
      89            3 :     std::svmatch pieces_match;
      90            3 :     if (std::regex_match(uri, pieces_match, URI_VALIDATOR)) {
      91            3 :         if (pieces_match.size() == 4) {
      92            3 :             if (pieces_match[2].length() == 0)
      93            3 :                 instance(default_ns).lookupName(pieces_match[3], std::move(cb));
      94              :             else
      95            0 :                 instance(pieces_match[3].str()).lookupName(pieces_match[2], std::move(cb));
      96            3 :             return;
      97              :         }
      98              :     }
      99            0 :     JAMI_ERROR("Unable to parse URI: {}", uri);
     100            0 :     cb("", "", Response::invalidResponse);
     101            6 : }
     102              : 
     103           30 : NameDirectory::NameDirectory(const std::string& serverUrl, std::shared_ptr<dht::Logger> l)
     104           30 :     : serverUrl_(serverUrl)
     105           30 :     , logger_(std::move(l))
     106           30 :     , httpContext_(Manager::instance().ioContext())
     107           60 :     , saveTask_(*httpContext_)
     108              : {
     109           30 :     if (!serverUrl_.empty() && serverUrl_.back() == '/')
     110            0 :         serverUrl_.pop_back();
     111           30 :     resolver_ = std::make_shared<dht::http::Resolver>(*httpContext_, serverUrl, logger_);
     112           30 :     cachePath_ = fileutils::get_cache_dir() / CACHE_DIRECTORY / resolver_->get_url().host;
     113           30 : }
     114              : 
     115              : std::shared_ptr<dht::http::Resolver>
     116         1017 : NameDirectory::resolver()
     117              : {
     118         1017 :     std::lock_guard lk(resolverMtx_);
     119         1017 :     if (!resolver_)
     120            0 :         resolver_ = std::make_shared<dht::http::Resolver>(*httpContext_, serverUrl_, logger_);
     121         2034 :     return resolver_;
     122         1017 : }
     123              : 
     124              : void
     125            0 : NameDirectory::dropResolver(const std::shared_ptr<dht::http::Resolver>& failed)
     126              : {
     127            0 :     std::lock_guard lk(resolverMtx_);
     128            0 :     if (resolver_ && resolver_ == failed) {
     129            0 :         resolver_.reset();
     130            0 :         JAMI_WARNING("[NameDirectory] No HTTP response from {}; resolving the host again", serverUrl_);
     131              :     }
     132            0 : }
     133              : 
     134           30 : NameDirectory::~NameDirectory()
     135              : {
     136           30 :     decltype(requests_) requests;
     137              :     {
     138           30 :         std::lock_guard lk(requestsMtx_);
     139           30 :         requests = std::move(requests_);
     140           30 :     }
     141           30 :     for (auto& req : requests)
     142            0 :         req->cancel();
     143           30 : }
     144              : 
     145              : void
     146           30 : NameDirectory::load()
     147              : {
     148           30 :     loadCache();
     149           30 : }
     150              : 
     151              : std::string
     152           10 : canonicalName(const std::string& url)
     153              : {
     154           10 :     std::string name = url;
     155           10 :     std::transform(name.begin(), name.end(), name.begin(), ::tolower);
     156           10 :     if (name.find("://") == std::string::npos)
     157            0 :         name = "https://" + name;
     158           10 :     return name;
     159            0 : }
     160              : 
     161              : NameDirectory&
     162         1036 : NameDirectory::instance(const std::string& serverUrl, std::shared_ptr<dht::Logger> l)
     163              : {
     164         2062 :     const std::string& s = serverUrl.empty() ? DEFAULT_SERVER_HOST : canonicalName(serverUrl);
     165              :     static std::mutex instanceMtx {};
     166              : 
     167         1036 :     std::lock_guard lock(instanceMtx);
     168         1036 :     static std::map<std::string, NameDirectory> instances {};
     169         1036 :     auto it = instances.find(s);
     170         1036 :     if (it != instances.end())
     171         1006 :         return it->second;
     172           30 :     auto r = instances.emplace(std::piecewise_construct, std::forward_as_tuple(s), std::forward_as_tuple(s, l));
     173           30 :     if (r.second)
     174           30 :         r.first->second.load();
     175           30 :     return r.first->second;
     176         1036 : }
     177              : 
     178              : void
     179         1017 : NameDirectory::setHeaderFields(Request& request)
     180              : {
     181         1017 :     request.set_header_field(restinio::http_field_t::user_agent,
     182         2034 :                              fmt::format("Jami ({}/{})", jami::platform(), jami::arch()));
     183         2034 :     request.set_header_field(restinio::http_field_t::accept, "*/*");
     184         1017 :     request.set_header_field(restinio::http_field_t::content_type, "application/json");
     185         1017 : }
     186              : 
     187              : void
     188         1014 : NameDirectory::lookupAddress(const std::string& addr, LookupCallback cb)
     189              : {
     190         1014 :     auto cacheResult = nameCache(addr);
     191         1014 :     if (not cacheResult.first.empty()) {
     192            1 :         cb(cacheResult.first, cacheResult.second, Response::found);
     193            1 :         return;
     194              :     }
     195         1013 :     auto res = resolver();
     196         1013 :     auto request = std::make_shared<Request>(*httpContext_, res, serverUrl_ + QUERY_ADDR + addr);
     197              :     try {
     198         1013 :         request->set_method(restinio::http_method_get());
     199         1013 :         setHeaderFields(*request);
     200         1013 :         request->add_on_done_callback([this, res, cb, addr](const dht::http::Response& response) {
     201         1013 :             if (response.status_code == 0)
     202            0 :                 dropResolver(res);
     203         1013 :             if (response.status_code > 400 && response.status_code < 500) {
     204         1011 :                 auto cacheResult = nameCache(addr);
     205         1011 :                 if (not cacheResult.first.empty())
     206            1 :                     cb(cacheResult.first, cacheResult.second, Response::found);
     207              :                 else
     208         5050 :                     cb("", "", Response::notFound);
     209         1013 :             } else if (response.status_code == 400)
     210            5 :                 cb("", "", Response::invalidResponse);
     211            1 :             else if (response.status_code != 200) {
     212            0 :                 JAMI_ERROR("Address lookup for {} on {} failed with code={}", addr, serverUrl_, response.status_code);
     213            0 :                 cb("", "", Response::error);
     214              :             } else {
     215              :                 try {
     216            1 :                     Json::Value json;
     217            1 :                     if (!json::parse(response.body, json)) {
     218            0 :                         cb("", "", Response::error);
     219            0 :                         return;
     220              :                     }
     221            1 :                     auto name = json["name"].asString();
     222            1 :                     if (name.empty()) {
     223            0 :                         cb(name, addr, Response::notFound);
     224            0 :                         return;
     225              :                     }
     226            1 :                     JAMI_DEBUG("Found name for {}: {}", addr, name);
     227              :                     {
     228            1 :                         std::lock_guard l(cacheLock_);
     229            1 :                         addrCache_.emplace(name, std::pair(name, addr));
     230            1 :                         nameCache_.emplace(addr, std::pair(name, addr));
     231            1 :                         scheduleCacheSave();
     232            1 :                     }
     233            1 :                     cb(name, addr, Response::found);
     234            1 :                 } catch (const std::exception& e) {
     235            0 :                     JAMI_ERROR("Error when performing address lookup: {}", e.what());
     236            0 :                     cb("", "", Response::error);
     237            0 :                 }
     238              :             }
     239         1013 :             std::lock_guard lk(requestsMtx_);
     240         1013 :             if (auto req = response.request.lock())
     241         1013 :                 requests_.erase(req);
     242         1013 :         });
     243              :         {
     244         1013 :             std::lock_guard lk(requestsMtx_);
     245         1013 :             requests_.emplace(request);
     246         1013 :         }
     247         1013 :         request->send();
     248            0 :     } catch (const std::exception& e) {
     249            0 :         JAMI_ERROR("Error when performing address lookup: {}", e.what());
     250              :         {
     251            0 :             std::lock_guard lk(requestsMtx_);
     252            0 :             requests_.erase(request);
     253            0 :         }
     254              :         // The request will never complete, so answer here: callers must always get a reply.
     255            0 :         cb("", "", Response::error);
     256            0 :     }
     257         1014 : }
     258              : 
     259              : bool
     260            0 : NameDirectory::verify(const std::string& name, const dht::crypto::PublicKey& pk, const std::string& signature)
     261              : {
     262            0 :     return pk.checkSignature(std::vector<uint8_t>(name.begin(), name.end()), base64::decode(signature));
     263              : }
     264              : 
     265              : void
     266            3 : NameDirectory::lookupName(const std::string& name, LookupCallback cb)
     267              : {
     268            3 :     auto cacheResult = addrCache(name);
     269            3 :     if (not cacheResult.first.empty()) {
     270            0 :         cb(cacheResult.first, cacheResult.second, Response::found);
     271            0 :         return;
     272              :     }
     273            3 :     auto encodedName = urlEncode(name);
     274            3 :     auto res = resolver();
     275            3 :     auto request = std::make_shared<Request>(*httpContext_, res, serverUrl_ + QUERY_NAME + encodedName);
     276              :     try {
     277            3 :         request->set_method(restinio::http_method_get());
     278            3 :         setHeaderFields(*request);
     279            3 :         request->add_on_done_callback([this, res, name, cb](const dht::http::Response& response) {
     280            3 :             if (response.status_code == 0)
     281            0 :                 dropResolver(res);
     282            3 :             if (response.status_code > 400 && response.status_code < 500)
     283            5 :                 cb("", "", Response::notFound);
     284            2 :             else if (response.status_code == 400)
     285            5 :                 cb("", "", Response::invalidResponse);
     286            1 :             else if (response.status_code < 200 || response.status_code > 299) {
     287            0 :                 JAMI_ERROR("Name lookup for {} on {} failed with code={}", name, serverUrl_, response.status_code);
     288            0 :                 cb("", "", Response::error);
     289              :             } else {
     290              :                 try {
     291            1 :                     Json::Value json;
     292            1 :                     if (!json::parse(response.body, json)) {
     293            0 :                         cb("", "", Response::error);
     294            0 :                         return;
     295              :                     }
     296            1 :                     auto nameResult = json["name"].asString();
     297            1 :                     auto addr = json["addr"].asString();
     298            1 :                     auto publickey = json["publickey"].asString();
     299            1 :                     auto signature = json["signature"].asString();
     300              : 
     301            1 :                     if (starts_with(addr, HEX_PREFIX))
     302            0 :                         addr = addr.substr(HEX_PREFIX.size());
     303            1 :                     if (addr.empty()) {
     304            0 :                         cb("", "", Response::notFound);
     305            0 :                         return;
     306              :                     }
     307            1 :                     if (not publickey.empty() and not signature.empty()) {
     308              :                         try {
     309            0 :                             auto pk = dht::crypto::PublicKey(base64::decode(publickey));
     310            0 :                             if (pk.getId().toString() != addr or not verify(nameResult, pk, signature)) {
     311            0 :                                 cb("", "", Response::invalidResponse);
     312            0 :                                 return;
     313              :                             }
     314            0 :                         } catch (const std::exception& e) {
     315            0 :                             cb("", "", Response::invalidResponse);
     316            0 :                             return;
     317            0 :                         }
     318              :                     }
     319            1 :                     JAMI_DEBUG("Found address for {}: {}", name, addr);
     320              :                     {
     321            1 :                         std::lock_guard l(cacheLock_);
     322            1 :                         addrCache_.emplace(name, std::pair(nameResult, addr));
     323            1 :                         addrCache_.emplace(nameResult, std::pair(nameResult, addr));
     324            1 :                         nameCache_.emplace(addr, std::pair(nameResult, addr));
     325            1 :                         scheduleCacheSave();
     326            1 :                     }
     327            1 :                     cb(nameResult, addr, Response::found);
     328            1 :                 } catch (const std::exception& e) {
     329            0 :                     JAMI_ERROR("Error when performing name lookup: {}", e.what());
     330            0 :                     cb("", "", Response::error);
     331            0 :                 }
     332              :             }
     333            3 :             std::lock_guard lk(requestsMtx_);
     334            3 :             if (auto req = response.request.lock())
     335            3 :                 requests_.erase(req);
     336            3 :         });
     337              :         {
     338            3 :             std::lock_guard lk(requestsMtx_);
     339            3 :             requests_.emplace(request);
     340            3 :         }
     341            3 :         request->send();
     342            0 :     } catch (const std::exception& e) {
     343            0 :         JAMI_ERROR("Name lookup for {} failed: {}", name, e.what());
     344              :         {
     345            0 :             std::lock_guard lk(requestsMtx_);
     346            0 :             requests_.erase(request);
     347            0 :         }
     348              :         // The request will never complete, so answer here: callers must always get a reply.
     349            0 :         cb("", "", Response::error);
     350            0 :     }
     351            3 : }
     352              : 
     353              : using Blob = std::vector<uint8_t>;
     354              : void
     355            1 : NameDirectory::registerName(const std::string& addr,
     356              :                             const std::string& n,
     357              :                             const std::string& owner,
     358              :                             RegistrationCallback cb,
     359              :                             const std::string& signedname,
     360              :                             const std::string& publickey)
     361              : {
     362            1 :     std::string name {n};
     363            1 :     toLower(name);
     364            1 :     auto cacheResult = addrCache(name);
     365            1 :     if (not cacheResult.first.empty()) {
     366            0 :         if (cacheResult.second == addr)
     367            0 :             cb(RegistrationResponse::success, name);
     368              :         else
     369            0 :             cb(RegistrationResponse::alreadyTaken, name);
     370            0 :         return;
     371              :     }
     372              :     {
     373            1 :         std::lock_guard l(cacheLock_);
     374            1 :         if (not pendingRegistrations_.emplace(addr, name).second) {
     375            0 :             JAMI_WARNING("RegisterName: already registering name {} {}", addr, name);
     376            0 :             cb(RegistrationResponse::error, name);
     377            0 :             return;
     378              :         }
     379            1 :     }
     380              :     std::string body = fmt::format("{{\"addr\":\"{}\",\"owner\":\"{}\",\"signature\":\"{}\",\"publickey\":\"{}\"}}",
     381              :                                    addr,
     382              :                                    owner,
     383              :                                    signedname,
     384            2 :                                    base64::encode(publickey));
     385              : 
     386            1 :     auto encodedName = urlEncode(name);
     387            1 :     auto res = resolver();
     388            1 :     auto request = std::make_shared<Request>(*httpContext_, res, serverUrl_ + QUERY_NAME + encodedName);
     389              :     try {
     390            1 :         request->set_method(restinio::http_method_post());
     391            1 :         setHeaderFields(*request);
     392            1 :         request->set_body(body);
     393              : 
     394            1 :         JAMI_WARNING("RegisterName: sending request {} {}", addr, name);
     395              : 
     396            1 :         request->add_on_done_callback([this, res, name, addr, cb = std::move(cb)](const dht::http::Response& response) {
     397            1 :             if (response.status_code == 0)
     398            0 :                 dropResolver(res);
     399              :             {
     400            1 :                 std::lock_guard l(cacheLock_);
     401            1 :                 pendingRegistrations_.erase(name);
     402            1 :             }
     403            1 :             if (response.status_code == 400) {
     404            0 :                 cb(RegistrationResponse::incompleteRequest, name);
     405            0 :                 JAMI_ERROR("RegistrationResponse::incompleteRequest");
     406            1 :             } else if (response.status_code == 401) {
     407            0 :                 cb(RegistrationResponse::signatureVerificationFailed, name);
     408            0 :                 JAMI_ERROR("RegistrationResponse::signatureVerificationFailed");
     409            1 :             } else if (response.status_code == 403) {
     410            0 :                 cb(RegistrationResponse::alreadyTaken, name);
     411            0 :                 JAMI_ERROR("RegistrationResponse::alreadyTaken");
     412            1 :             } else if (response.status_code == 409) {
     413            0 :                 cb(RegistrationResponse::alreadyTaken, name);
     414            0 :                 JAMI_ERROR("RegistrationResponse::alreadyTaken");
     415            1 :             } else if (response.status_code > 400 && response.status_code < 500) {
     416            0 :                 cb(RegistrationResponse::alreadyTaken, name);
     417            0 :                 JAMI_ERROR("RegistrationResponse::alreadyTaken");
     418            1 :             } else if (response.status_code < 200 || response.status_code > 299) {
     419            0 :                 cb(RegistrationResponse::error, name);
     420            0 :                 JAMI_ERROR("RegistrationResponse::error");
     421              :             } else {
     422            1 :                 Json::Value json;
     423            1 :                 std::string err;
     424            1 :                 Json::CharReaderBuilder rbuilder;
     425              : 
     426            1 :                 auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
     427            1 :                 if (!reader->parse(response.body.data(), response.body.data() + response.body.size(), &json, &err)) {
     428            0 :                     cb(RegistrationResponse::error, name);
     429            0 :                     return;
     430              :                 }
     431            1 :                 auto success = json["success"].asBool();
     432            1 :                 JAMI_DEBUG("Got reply for registration of {} {}: {}", name, addr, success ? "success" : "failure");
     433            1 :                 if (success) {
     434            1 :                     std::lock_guard l(cacheLock_);
     435            1 :                     addrCache_.emplace(name, std::pair(name, addr));
     436            1 :                     nameCache_.emplace(addr, std::pair(name, addr));
     437            1 :                 }
     438            1 :                 cb(success ? RegistrationResponse::success : RegistrationResponse::error, name);
     439            1 :             }
     440            1 :             std::lock_guard lk(requestsMtx_);
     441            1 :             if (auto req = response.request.lock())
     442            1 :                 requests_.erase(req);
     443            1 :         });
     444              :         {
     445            1 :             std::lock_guard lk(requestsMtx_);
     446            1 :             requests_.emplace(request);
     447            1 :         }
     448            1 :         request->send();
     449            0 :     } catch (const std::exception& e) {
     450            0 :         JAMI_ERROR("Error when performing name registration: {}", e.what());
     451            0 :         cb(RegistrationResponse::error, name);
     452              :         {
     453            0 :             std::lock_guard l(cacheLock_);
     454            0 :             pendingRegistrations_.erase(name);
     455            0 :         }
     456            0 :         std::lock_guard lk(requestsMtx_);
     457            0 :         if (request)
     458            0 :             requests_.erase(request);
     459            0 :     }
     460            1 : }
     461              : 
     462              : void
     463            2 : NameDirectory::scheduleCacheSave()
     464              : {
     465            2 :     saveTask_.expires_after(SAVE_INTERVAL);
     466            2 :     saveTask_.async_wait([this](const asio::error_code& ec) {
     467            1 :         if (ec)
     468            0 :             return;
     469            1 :         saveCache();
     470              :     });
     471            2 : }
     472              : 
     473              : void
     474            1 : NameDirectory::saveCache()
     475              : {
     476            1 :     dhtnet::fileutils::recursive_mkdir(fileutils::get_cache_dir() / CACHE_DIRECTORY);
     477            1 :     std::lock_guard lock(dhtnet::fileutils::getFileLock(cachePath_));
     478            1 :     std::ofstream file(cachePath_, std::ios::trunc | std::ios::binary);
     479            1 :     if (!file.is_open()) {
     480            0 :         JAMI_ERROR("Unable to save cache to {}", cachePath_);
     481            0 :         return;
     482              :     }
     483              :     {
     484            1 :         std::lock_guard l(cacheLock_);
     485            1 :         msgpack::pack(file, nameCache_);
     486            1 :     }
     487            1 :     JAMI_DEBUG("Saved {:d} name-address mapping(s) to {}", nameCache_.size(), cachePath_);
     488            1 : }
     489              : 
     490              : void
     491           30 : NameDirectory::loadCache()
     492              : {
     493           30 :     msgpack::unpacker pac;
     494              : 
     495              :     // read file
     496              :     {
     497           30 :         std::lock_guard lock(dhtnet::fileutils::getFileLock(cachePath_));
     498           30 :         std::ifstream file(cachePath_);
     499           30 :         if (!file.is_open()) {
     500           30 :             JAMI_DEBUG("Unable to load {}", cachePath_);
     501           30 :             return;
     502              :         }
     503            0 :         std::string line;
     504            0 :         while (std::getline(file, line)) {
     505            0 :             pac.reserve_buffer(line.size());
     506            0 :             memcpy(pac.buffer(), line.data(), line.size());
     507            0 :             pac.buffer_consumed(line.size());
     508              :         }
     509           60 :     }
     510              : 
     511              :     try {
     512              :         // load values
     513            0 :         std::lock_guard l(cacheLock_);
     514            0 :         msgpack::object_handle oh;
     515            0 :         if (pac.next(oh))
     516            0 :             oh.get().convert(nameCache_);
     517            0 :         for (const auto& m : nameCache_)
     518            0 :             addrCache_.emplace(m.second.second, m.second);
     519            0 :     } catch (const msgpack::parse_error& e) {
     520            0 :         JAMI_ERROR("Error when parsing msgpack object: {}", e.what());
     521            0 :     } catch (const std::bad_cast& e) {
     522            0 :         JAMI_ERROR("Error when loading cache: {}", e.what());
     523            0 :     }
     524              : 
     525            0 :     JAMI_DEBUG("Loaded {:d} name-address mapping(s) from cache", nameCache_.size());
     526           30 : }
     527              : 
     528              : } // namespace jami
        

Generated by: LCOV version 2.0-1