LCOV - code coverage report
Current view: top level - foo/src/jamidht - contact_list.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 306 378 81.0 %
Date: 2025-10-16 08:11:43 Functions: 55 84 65.5 %

          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             : #include "contact_list.h"
      18             : #include "logger.h"
      19             : #include "jamiaccount.h"
      20             : #include "fileutils.h"
      21             : 
      22             : #include "manager.h"
      23             : #ifdef ENABLE_PLUGIN
      24             : #include "plugin/jamipluginmanager.h"
      25             : #endif
      26             : 
      27             : #include "account_const.h"
      28             : 
      29             : #include <fstream>
      30             : #include <gnutls/ocsp.h>
      31             : 
      32             : namespace jami {
      33             : 
      34         820 : ContactList::ContactList(const std::string& accountId,
      35             :                          const std::shared_ptr<crypto::Certificate>& cert,
      36             :                          const std::filesystem::path& path,
      37         820 :                          OnChangeCallback cb)
      38         820 :     : accountId_(accountId)
      39         820 :     , path_(path)
      40         820 :     , callbacks_(std::move(cb))
      41             : {
      42         820 :     if (cert) {
      43         820 :         trust_ = std::make_unique<dhtnet::tls::TrustStore>(jami::Manager::instance().certStore(accountId_));
      44         818 :         accountTrust_.add(*cert);
      45             :     }
      46         838 : }
      47             : 
      48         818 : ContactList::~ContactList() {}
      49             : 
      50             : void
      51          16 : ContactList::load()
      52             : {
      53          16 :     loadContacts();
      54          16 :     loadTrustRequests();
      55          16 :     loadKnownDevices();
      56          16 : }
      57             : 
      58             : void
      59           0 : ContactList::save()
      60             : {
      61           0 :     saveContacts();
      62           0 :     saveTrustRequests();
      63           0 :     saveKnownDevices();
      64           0 : }
      65             : 
      66             : bool
      67          89 : ContactList::setCertificateStatus(const std::string& cert_id,
      68             :                                   const dhtnet::tls::TrustStore::PermissionStatus status)
      69             : {
      70          89 :     if (contacts_.find(dht::InfoHash(cert_id)) != contacts_.end()) {
      71           9 :         JAMI_LOG("[Account {}] [Contacts] Unable to set certificate status for existing contacts {}", accountId_, cert_id);
      72           3 :         return false;
      73             :     }
      74          86 :     return trust_->setCertificateStatus(cert_id, status);
      75             : }
      76             : 
      77             : bool
      78           0 : ContactList::setCertificateStatus(const std::shared_ptr<crypto::Certificate>& cert,
      79             :                             dhtnet::tls::TrustStore::PermissionStatus status,
      80             :                             bool local)
      81             : {
      82           0 :     return trust_->setCertificateStatus(cert, status, local);
      83             : }
      84             : 
      85             : bool
      86         165 : ContactList::addContact(const dht::InfoHash& h, bool confirmed, const std::string& conversationId)
      87             : {
      88         495 :     JAMI_WARNING("[Account {}] [Contacts] addContact: {}, conversation: {}", accountId_, h, conversationId);
      89         165 :     auto c = contacts_.find(h);
      90         165 :     if (c == contacts_.end())
      91          98 :         c = contacts_.emplace(h, Contact {}).first;
      92          67 :     else if (c->second.isActive() and c->second.confirmed == confirmed && c->second.conversationId == conversationId)
      93          58 :         return false;
      94         107 :     c->second.added = std::time(nullptr);
      95             :     // NOTE: because we can re-add a contact after removing it
      96             :     // we should reset removed (as not removed anymore). This fix isActive()
      97             :     // if addContact is called just after removeContact during the same second
      98         107 :     c->second.removed = 0;
      99         107 :     c->second.conversationId = conversationId;
     100         107 :     c->second.confirmed |= confirmed;
     101         107 :     auto hStr = h.toString();
     102         107 :     trust_->setCertificateStatus(hStr, dhtnet::tls::TrustStore::PermissionStatus::ALLOWED);
     103         107 :     saveContacts();
     104         107 :     callbacks_.contactAdded(hStr, c->second.confirmed);
     105         107 :     return true;
     106         107 : }
     107             : 
     108             : void
     109       17692 : ContactList::updateConversation(
     110             :     const dht::InfoHash& h, const std::string& conversationId, bool added)
     111             : {
     112       17692 :     auto c = contacts_.find(h);
     113       17692 :     if (c != contacts_.end() && c->second.conversationId != conversationId) {
     114          27 :         c->second.conversationId = conversationId;
     115          27 :         if (added) {
     116           2 :             c->second.added = std::time(nullptr);
     117             :         }
     118          27 :         saveContacts();
     119             :     }
     120       17692 : }
     121             : 
     122             : bool
     123          19 : ContactList::removeContact(const dht::InfoHash& h, bool ban)
     124             : {
     125          19 :     std::unique_lock lk(mutex_);
     126          57 :     JAMI_WARNING("[Account {}] [Contacts] removeContact: {} (banned: {})", accountId_, h, ban);
     127          19 :     auto c = contacts_.find(h);
     128          19 :     if (c == contacts_.end())
     129           4 :         c = contacts_.emplace(h, Contact {}).first;
     130          19 :     c->second.removed = std::time(nullptr);
     131          19 :     c->second.confirmed = false;
     132          19 :     c->second.banned = ban;
     133          19 :     auto uri = h.toString();
     134          19 :     trust_->setCertificateStatus(uri,
     135             :                                 ban ? dhtnet::tls::TrustStore::PermissionStatus::BANNED
     136             :                                     : dhtnet::tls::TrustStore::PermissionStatus::UNDEFINED);
     137          19 :     if (trustRequests_.erase(h) > 0)
     138           3 :         saveTrustRequests();
     139          19 :     saveContacts();
     140          19 :     lk.unlock();
     141             : #ifdef ENABLE_PLUGIN
     142          19 :     auto filename = path_.filename().string();
     143          19 :     jami::Manager::instance()
     144          19 :         .getJamiPluginManager()
     145          19 :         .getChatServicesManager()
     146          19 :         .cleanChatSubjects(filename, uri);
     147             : #endif
     148          19 :     callbacks_.contactRemoved(uri, ban);
     149          19 :     return true;
     150          19 : }
     151             : 
     152             : bool
     153           1 : ContactList::removeContactConversation(const dht::InfoHash& h)
     154             : {
     155           1 :     auto c = contacts_.find(h);
     156           1 :     if (c == contacts_.end())
     157           0 :         return false;
     158           1 :     c->second.conversationId = "";
     159           1 :     saveContacts();
     160           1 :     return true;
     161             : }
     162             : 
     163             : std::map<std::string, std::string>
     164           8 : ContactList::getContactDetails(const dht::InfoHash& h) const
     165             : {
     166           8 :     const auto c = contacts_.find(h);
     167           8 :     if (c == std::end(contacts_)) {
     168           0 :         JAMI_WARNING("[Account {}] [Contacts] Contact '{}' not found", accountId_, h.to_view());
     169           0 :         return {};
     170             :     }
     171             : 
     172           8 :     auto details = c->second.toMap();
     173           8 :     if (not details.empty())
     174           8 :         details["id"] = c->first.toString();
     175             : 
     176           8 :     return details;
     177           8 : }
     178             : 
     179             : std::optional<Contact>
     180         613 : ContactList::getContactInfo(const dht::InfoHash& h) const
     181             : {
     182         613 :     const auto c = contacts_.find(h);
     183         613 :     if (c == std::end(contacts_)) {
     184         921 :         JAMI_WARNING("[Account {}] [Contacts] Contact '{}' not found", accountId_, h.to_view());
     185         307 :         return {};
     186             :     }
     187         306 :     return c->second;
     188             : }
     189             : 
     190             : const std::map<dht::InfoHash, Contact>&
     191       19468 : ContactList::getContacts() const
     192             : {
     193       19468 :     return contacts_;
     194             : }
     195             : 
     196             : void
     197         801 : ContactList::setContacts(const std::map<dht::InfoHash, Contact>& contacts)
     198             : {
     199        2403 :     JAMI_LOG("[Account {}] [Contacts] replacing contact list (old: {} new: {})", accountId_, contacts_.size(), contacts.size());
     200         801 :     contacts_ = contacts;
     201         801 :     saveContacts();
     202             :     // Set contacts is used when creating a new device, so just announce new contacts
     203         804 :     for (auto& peer : contacts)
     204           3 :         if (peer.second.isActive())
     205           3 :             callbacks_.contactAdded(peer.first.toString(), peer.second.confirmed);
     206         801 : }
     207             : 
     208             : void
     209       17734 : ContactList::updateContact(const dht::InfoHash& id, const Contact& contact, bool emit)
     210             : {
     211       17734 :     if (not id) {
     212           0 :         JAMI_ERROR("[Account {}] [Contacts] updateContact: invalid contact ID", accountId_);
     213           0 :         return;
     214             :     }
     215       17734 :     bool stateChanged {false};
     216       17734 :     auto c = contacts_.find(id);
     217       17734 :     if (c == contacts_.end()) {
     218             :         // JAMI_DBG("[Contacts] New contact: %s", id.toString().c_str());
     219          13 :         c = contacts_.emplace(id, contact).first;
     220          13 :         stateChanged = c->second.isActive() or c->second.isBanned();
     221             :     } else {
     222             :         // JAMI_DBG("[Contacts] Updated contact: %s", id.toString().c_str());
     223       17721 :         stateChanged = c->second.update(contact);
     224             :     }
     225       17734 :     if (stateChanged) {
     226             :         {
     227       17685 :             std::lock_guard lk(mutex_);
     228       17685 :             if (trustRequests_.erase(id) > 0)
     229           7 :                 saveTrustRequests();
     230       17685 :         }
     231       17685 :         if (c->second.isActive()) {
     232          16 :             trust_->setCertificateStatus(id.toString(), dhtnet::tls::TrustStore::PermissionStatus::ALLOWED);
     233          16 :             if (emit)
     234          16 :                 callbacks_.contactAdded(id.toString(), c->second.confirmed);
     235             :         } else {
     236       17669 :             if (c->second.banned)
     237           3 :                 trust_->setCertificateStatus(id.toString(),
     238             :                                             dhtnet::tls::TrustStore::PermissionStatus::BANNED);
     239       17669 :             if (emit)
     240       17669 :                 callbacks_.contactRemoved(id.toString(), c->second.banned);
     241             :         }
     242             :     }
     243             : }
     244             : 
     245             : void
     246          16 : ContactList::loadContacts()
     247             : {
     248          16 :     decltype(contacts_) contacts;
     249             :     try {
     250             :         // read file
     251          16 :         auto file = fileutils::loadFile("contacts", path_);
     252             :         // load values
     253          16 :         msgpack::object_handle oh = msgpack::unpack((const char*) file.data(), file.size());
     254          16 :         oh.get().convert(contacts);
     255          16 :     } catch (const std::exception& e) {
     256           0 :         JAMI_WARNING("[Account {}] [Contacts] Error loading contacts: {}", accountId_, e.what());
     257           0 :         return;
     258           0 :     }
     259             : 
     260          48 :     JAMI_WARNING("[Account {}] [Contacts] Loaded {} contacts", accountId_, contacts.size());
     261          16 :     for (auto& peer : contacts)
     262           0 :         updateContact(peer.first, peer.second, false);
     263          16 : }
     264             : 
     265             : void
     266       18730 : ContactList::saveContacts() const
     267             : {
     268       56190 :     JAMI_LOG("[Account {}] [Contacts] saving {} contacts", accountId_, contacts_.size());
     269       37460 :     std::ofstream file(path_ / "contacts", std::ios::trunc | std::ios::binary);
     270       18730 :     msgpack::pack(file, contacts_);
     271       18730 : }
     272             : 
     273             : void
     274         121 : ContactList::saveTrustRequests() const
     275             : {
     276             :     // mutex_ MUST BE locked
     277         242 :     std::ofstream file(path_ / "incomingTrustRequests",
     278         242 :                        std::ios::trunc | std::ios::binary);
     279         121 :     msgpack::pack(file, trustRequests_);
     280         121 : }
     281             : 
     282             : void
     283          16 : ContactList::loadTrustRequests()
     284             : {
     285          16 :     if (!std::filesystem::is_regular_file(fileutils::getFullPath(path_, "incomingTrustRequests")))
     286          16 :         return;
     287           0 :     std::map<dht::InfoHash, TrustRequest> requests;
     288             :     try {
     289             :         // read file
     290           0 :         auto file = fileutils::loadFile("incomingTrustRequests", path_);
     291             :         // load values
     292           0 :         msgpack::object_handle oh = msgpack::unpack((const char*) file.data(), file.size());
     293           0 :         oh.get().convert(requests);
     294           0 :     } catch (const std::exception& e) {
     295           0 :         JAMI_WARNING("[Account {}] [Contacts] Error loading trust requests: {}", accountId_, e.what());
     296           0 :         return;
     297           0 :     }
     298             : 
     299           0 :     JAMI_WARNING("[Account {}] [Contacts] Loaded {} contact requests", accountId_, requests.size());
     300           0 :     for (auto& tr : requests)
     301           0 :         onTrustRequest(tr.first,
     302           0 :                        tr.second.device,
     303             :                        tr.second.received,
     304             :                        false,
     305           0 :                        tr.second.conversationId,
     306           0 :                        std::move(tr.second.payload));
     307           0 : }
     308             : 
     309             : bool
     310         124 : ContactList::onTrustRequest(const dht::InfoHash& peer_account,
     311             :                             const std::shared_ptr<dht::crypto::PublicKey>& peer_device,
     312             :                             time_t received,
     313             :                             bool confirm,
     314             :                             const std::string& conversationId,
     315             :                             std::vector<uint8_t>&& payload)
     316             : {
     317         124 :     bool accept = false;
     318             :     // Check existing contact
     319         124 :     std::unique_lock lk(mutex_);
     320         124 :     auto contact = contacts_.find(peer_account);
     321         124 :     bool active = false;
     322         124 :     if (contact != contacts_.end()) {
     323             :         // Banned contact: discard request
     324          61 :         if (contact->second.isBanned())
     325           0 :             return false;
     326             : 
     327          61 :         if (contact->second.isActive()) {
     328          58 :             active = true;
     329             :             // Send confirmation
     330          58 :             if (not confirm)
     331          15 :                 accept = true;
     332          58 :             if (not contact->second.confirmed) {
     333          41 :                 contact->second.confirmed = true;
     334          41 :                 saveContacts();
     335          41 :                 callbacks_.contactAdded(peer_account.toString(), true);
     336             :             }
     337             :         }
     338             :     }
     339         124 :     if (not active) {
     340          66 :         auto req = trustRequests_.find(peer_account);
     341          66 :         if (req == trustRequests_.end()) {
     342             :             // Add trust request
     343          62 :             req = trustRequests_
     344          62 :                       .emplace(peer_account,
     345         124 :                                TrustRequest {peer_device, conversationId, received, payload})
     346             :                       .first;
     347             :         } else {
     348             :             // Update trust request
     349           4 :             if (received > req->second.received) {
     350           1 :                 req->second.device = peer_device;
     351           1 :                 req->second.conversationId = conversationId;
     352           1 :                 req->second.received = received;
     353           1 :                 req->second.payload = payload;
     354             :             } else {
     355           9 :                 JAMI_LOG("[Account {}] [Contacts] Ignoring outdated trust request from {}",
     356             :                          accountId_,
     357             :                          peer_account);
     358             :             }
     359             :         }
     360          66 :         saveTrustRequests();
     361             :     }
     362         124 :     lk.unlock();
     363             :     // Note: call JamiAccount's callback to build ConversationRequest anyway
     364         124 :     if (!confirm)
     365          81 :         callbacks_.trustRequest(peer_account.toString(),
     366             :                                 conversationId,
     367          81 :                                 std::move(payload),
     368             :                                 received);
     369          43 :     else if (active) {
     370             :         // Only notify if confirmed + not removed
     371          43 :         callbacks_.onConfirmation(peer_account.toString(), conversationId);
     372             :     }
     373         124 :     return accept;
     374         124 : }
     375             : 
     376             : /* trust requests */
     377             : 
     378             : std::vector<std::map<std::string, std::string>>
     379         728 : ContactList::getTrustRequests() const
     380             : {
     381             :     using Map = std::map<std::string, std::string>;
     382         728 :     std::vector<Map> ret;
     383         728 :     std::lock_guard lk(mutex_);
     384         728 :     ret.reserve(trustRequests_.size());
     385         743 :     for (const auto& r : trustRequests_) {
     386          15 :         ret.emplace_back(
     387         150 :             Map {{libjami::Account::TrustRequest::FROM, r.first.toString()},
     388          30 :                  {libjami::Account::TrustRequest::RECEIVED, std::to_string(r.second.received)},
     389          15 :                  {libjami::Account::TrustRequest::CONVERSATIONID, r.second.conversationId},
     390             :                  {libjami::Account::TrustRequest::PAYLOAD,
     391         105 :                   std::string(r.second.payload.begin(), r.second.payload.end())}});
     392             :     }
     393        1456 :     return ret;
     394         728 : }
     395             : 
     396             : std::map<std::string, std::string>
     397       17894 : ContactList::getTrustRequest(const dht::InfoHash& from) const
     398             : {
     399             :     using Map = std::map<std::string, std::string>;
     400       17894 :     std::lock_guard lk(mutex_);
     401       17894 :     auto r = trustRequests_.find(from);
     402       17894 :     if (r == trustRequests_.end())
     403       17851 :         return {};
     404          86 :     return Map {{libjami::Account::TrustRequest::FROM, r->first.toString()},
     405          86 :                 {libjami::Account::TrustRequest::RECEIVED, std::to_string(r->second.received)},
     406          43 :                 {libjami::Account::TrustRequest::CONVERSATIONID, r->second.conversationId},
     407             :                 {libjami::Account::TrustRequest::PAYLOAD,
     408         301 :                  std::string(r->second.payload.begin(), r->second.payload.end())}};
     409       17894 : }
     410             : 
     411             : bool
     412         200 : ContactList::acceptTrustRequest(const dht::InfoHash& from)
     413             : {
     414             :     // The contact sent us a TR so we are in its contact list
     415         200 :     std::unique_lock lk(mutex_);
     416         200 :     auto i = trustRequests_.find(from);
     417         200 :     if (i == trustRequests_.end())
     418         158 :         return false;
     419          42 :     auto convId =  i->second.conversationId;
     420             :     // Clear trust request
     421          42 :     trustRequests_.erase(i);
     422          42 :     saveTrustRequests();
     423          42 :     lk.unlock();
     424          42 :     addContact(from, true, convId);
     425          42 :     return true;
     426         200 : }
     427             : 
     428             : void
     429          55 : ContactList::acceptConversation(const std::string& convId, const std::string& deviceId)
     430             : {
     431          55 :     if (callbacks_.acceptConversation)
     432          55 :         callbacks_.acceptConversation(convId, deviceId);
     433          55 : }
     434             : 
     435             : bool
     436           3 : ContactList::discardTrustRequest(const dht::InfoHash& from)
     437             : {
     438           3 :     std::lock_guard lk(mutex_);
     439           3 :     if (trustRequests_.erase(from) > 0) {
     440           3 :         saveTrustRequests();
     441           3 :         return true;
     442             :     }
     443           0 :     return false;
     444           3 : }
     445             : 
     446             : void
     447          16 : ContactList::loadKnownDevices()
     448             : {
     449          16 :     auto& certStore = jami::Manager::instance().certStore(accountId_);
     450             :     try {
     451             :         // read file
     452          16 :         auto file = fileutils::loadFile("knownDevices", path_);
     453             :         // load values
     454          16 :         msgpack::object_handle oh = msgpack::unpack((const char*) file.data(), file.size());
     455             : 
     456          16 :         std::map<dht::PkId, std::pair<std::string, uint64_t>> knownDevices;
     457          16 :         oh.get().convert(knownDevices);
     458          32 :         for (const auto& d : knownDevices) {
     459          32 :             if (auto crt = certStore.getCertificate(d.first.toString())) {
     460          16 :                 if (not foundAccountDevice(crt, d.second.first, clock::from_time_t(d.second.second), false))
     461           0 :                     JAMI_WARNING("[Account {}] [Contacts] Unable to add device {}", accountId_, d.first);
     462             :             } else {
     463           0 :                 JAMI_WARNING("[Account {}] [Contacts] Unable to find certificate for device {}", accountId_,
     464             :                           d.first);
     465          16 :             }
     466             :         }
     467          16 :         if (not knownDevices.empty()) {
     468          16 :             callbacks_.devicesChanged(knownDevices_);
     469             :         }
     470          16 :     } catch (const std::exception& e) {
     471           0 :         JAMI_WARNING("[Account {}] [Contacts] Error loading devices: {}", accountId_, e.what());
     472           0 :         return;
     473           0 :     }
     474             : }
     475             : 
     476             : void
     477        2941 : ContactList::saveKnownDevices() const
     478             : {
     479        5882 :     std::ofstream file(path_ / "knownDevices", std::ios::trunc | std::ios::binary);
     480             : 
     481        2941 :     std::map<dht::PkId, std::pair<std::string, uint64_t>> devices;
     482     1011019 :     for (const auto& id : knownDevices_) {
     483     1008078 :         devices.emplace(id.first,
     484     2016156 :                         std::make_pair(id.second.name, clock::to_time_t(id.second.last_sync)));
     485             :     }
     486             : 
     487        2941 :     msgpack::pack(file, devices);
     488        2941 : }
     489             : 
     490             : void
     491        2000 : ContactList::foundAccountDevice(const dht::PkId& device,
     492             :                                 const std::string& name,
     493             :                                 const time_point& updated)
     494             : {
     495             :     // insert device
     496        2000 :     auto it = knownDevices_.emplace(device, KnownDevice {{}, name, updated});
     497        2000 :     if (it.second) {
     498        6000 :         JAMI_LOG("[Account {}] [Contacts] Found account device: {} {}", accountId_, name, device);
     499        2000 :         saveKnownDevices();
     500        2000 :         callbacks_.devicesChanged(knownDevices_);
     501             :     } else {
     502             :         // update device name
     503           0 :         if (not name.empty() and it.first->second.name != name) {
     504           0 :             JAMI_LOG("[Account {}] [Contacts] Updating device name: {} {}", accountId_,
     505             :                      name, device);
     506           0 :             it.first->second.name = name;
     507           0 :             saveKnownDevices();
     508           0 :             callbacks_.devicesChanged(knownDevices_);
     509             :         }
     510             :     }
     511        2000 : }
     512             : 
     513             : bool
     514       37349 : ContactList::foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
     515             :                                 const std::string& name,
     516             :                                 const time_point& updated,
     517             :                                 bool notify)
     518             : {
     519       37349 :     if (not crt)
     520           0 :         return false;
     521             : 
     522       37349 :     auto id = crt->getLongId();
     523             : 
     524             :     // match certificate chain
     525       37349 :     auto verifyResult = accountTrust_.verify(*crt);
     526       37349 :     if (not verifyResult) {
     527           0 :         JAMI_WARNING("[Account {}] [Contacts] Found invalid account device: {:s}: {:s}",
     528             :                   accountId_, id, verifyResult.toString());
     529           0 :         return false;
     530             :     }
     531             : 
     532             :     // insert device
     533       37349 :     auto it = knownDevices_.emplace(id, KnownDevice {crt, name, updated});
     534       37349 :     if (it.second) {
     535        2670 :         JAMI_LOG("[Account {}] [Contacts] Found account device: {} {}", accountId_, name, id);
     536         890 :         jami::Manager::instance().certStore(accountId_).pinCertificate(crt);
     537         890 :         if (crt->ocspResponse) {
     538           0 :             unsigned int status = crt->ocspResponse->getCertificateStatus();
     539           0 :             if (status == GNUTLS_OCSP_CERT_REVOKED) {
     540           0 :                 JAMI_ERROR("[Account {}] Certificate {} has revoked OCSP status", accountId_, id);
     541           0 :                 trust_->setCertificateStatus(crt, dhtnet::tls::TrustStore::PermissionStatus::BANNED, false);
     542             :             }
     543             :         }
     544         890 :         if (notify) {
     545         874 :             saveKnownDevices();
     546         874 :             callbacks_.devicesChanged(knownDevices_);
     547             :         }
     548             :     } else {
     549             :         // update device name
     550       36459 :         if (not name.empty() and it.first->second.name != name) {
     551         192 :             JAMI_LOG("[Account {}] [Contacts] updating device name: {} {}", accountId_, name, id);
     552          64 :             it.first->second.name = name;
     553          64 :             if (notify) {
     554          64 :                 saveKnownDevices();
     555          64 :                 callbacks_.devicesChanged(knownDevices_);
     556             :             }
     557             :         }
     558             :     }
     559       37349 :     return true;
     560             : }
     561             : 
     562             : bool
     563           2 : ContactList::removeAccountDevice(const dht::PkId& device)
     564             : {
     565           2 :     if (knownDevices_.erase(device) > 0) {
     566           2 :         saveKnownDevices();
     567           2 :         return true;
     568             :     }
     569           0 :     return false;
     570             : }
     571             : 
     572             : void
     573          16 : ContactList::setAccountDeviceName(const dht::PkId& device, const std::string& name)
     574             : {
     575          16 :     auto dev = knownDevices_.find(device);
     576          16 :     if (dev != knownDevices_.end()) {
     577          16 :         if (dev->second.name != name) {
     578           1 :             dev->second.name = name;
     579           1 :             saveKnownDevices();
     580           1 :             callbacks_.devicesChanged(knownDevices_);
     581             :         }
     582             :     }
     583          16 : }
     584             : 
     585             : std::string
     586         801 : ContactList::getAccountDeviceName(const dht::PkId& device) const
     587             : {
     588         801 :     auto dev = knownDevices_.find(device);
     589         801 :     if (dev != knownDevices_.end()) {
     590         801 :         return dev->second.name;
     591             :     }
     592           0 :     return {};
     593             : }
     594             : 
     595             : DeviceSync
     596       18715 : ContactList::getSyncData() const
     597             : {
     598       18715 :     DeviceSync sync_data;
     599       18715 :     sync_data.date = clock::now().time_since_epoch().count();
     600             :     // sync_data.device_name = deviceName_;
     601       18715 :     sync_data.peers = getContacts();
     602             : 
     603             :     static constexpr size_t MAX_TRUST_REQUESTS = 20;
     604       18715 :     std::lock_guard lk(mutex_);
     605       18715 :     if (trustRequests_.size() <= MAX_TRUST_REQUESTS)
     606       18728 :         for (const auto& req : trustRequests_)
     607          13 :             sync_data.trust_requests.emplace(req.first,
     608          26 :                                              TrustRequest {req.second.device,
     609          13 :                                                            req.second.conversationId,
     610          13 :                                                            req.second.received,
     611             :                                                            {}});
     612             :     else {
     613           0 :         size_t inserted = 0;
     614           0 :         auto req = trustRequests_.lower_bound(dht::InfoHash::getRandom());
     615           0 :         while (inserted++ < MAX_TRUST_REQUESTS) {
     616           0 :             if (req == trustRequests_.end())
     617           0 :                 req = trustRequests_.begin();
     618           0 :             sync_data.trust_requests.emplace(req->first,
     619           0 :                                              TrustRequest {req->second.device,
     620           0 :                                                            req->second.conversationId,
     621           0 :                                                            req->second.received,
     622             :                                                            {}});
     623           0 :             ++req;
     624             :         }
     625             :     }
     626             : 
     627       61308 :     for (const auto& dev : knownDevices_) {
     628       42593 :         if (!dev.second.certificate) {
     629       18000 :             JAMI_WARNING("[Account {}] [Contacts] No certificate found for {}", accountId_, dev.first);
     630        6000 :             continue;
     631        6000 :         }
     632       36593 :         sync_data.devices.emplace(dev.second.certificate->getLongId(),
     633       73186 :                                   KnownDeviceSync {dev.second.name,
     634       36593 :                                                    dev.second.certificate->getId()});
     635             :     }
     636       37430 :     return sync_data;
     637       18715 : }
     638             : 
     639             : bool
     640           0 : ContactList::syncDevice(const dht::PkId& device, const time_point& syncDate)
     641             : {
     642           0 :     auto it = knownDevices_.find(device);
     643           0 :     if (it == knownDevices_.end()) {
     644           0 :         JAMI_WARNING("[Account {}] [Contacts] Dropping sync data from unknown device", accountId_);
     645           0 :         return false;
     646             :     }
     647           0 :     if (it->second.last_sync >= syncDate) {
     648           0 :         JAMI_LOG("[Account {}] [Contacts] Dropping outdated sync data", accountId_);
     649           0 :         return false;
     650             :     }
     651           0 :     it->second.last_sync = syncDate;
     652           0 :     return true;
     653             : }
     654             : 
     655             : } // namespace jami

Generated by: LCOV version 1.14