LCOV - code coverage report
Current view: top level - src/jamidht - contact_list.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 288 373 77.2 %
Date: 2024-04-24 08:04:06 Functions: 32 35 91.4 %

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

Generated by: LCOV version 1.14