LCOV - code coverage report
Current view: top level - src/jamidht - contact_list.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 88.4 % 422 373
Test Date: 2026-11-09 10:41:14 Functions: 97.3 % 37 36

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

Generated by: LCOV version 2.0-1