LCOV - code coverage report
Current view: top level - src/jamidht - contact_list.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 77.8 % 392 305
Test Date: 2026-08-23 08:52:56 Functions: 88.6 % 35 31

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

Generated by: LCOV version 2.0-1