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: 287 375 76.5 %
Date: 2025-08-24 09:11:10 Functions: 49 84 58.3 %

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

Generated by: LCOV version 1.14