LCOV - code coverage report
Current view: top level - src/jamidht - account_manager.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 74.1 % 463 343
Test Date: 2026-07-06 08:25:38 Functions: 51.5 % 171 88

            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 "account_manager.h"
      18              : #include "accountarchive.h"
      19              : #include "jamiaccount.h"
      20              : #include "base64.h"
      21              : #include "jami/account_const.h"
      22              : #include "account_schema.h"
      23              : #include "archiver.h"
      24              : #include "manager.h"
      25              : 
      26              : #include "libdevcrypto/Common.h"
      27              : #include "json_utils.h"
      28              : 
      29              : #include <opendht/thread_pool.h>
      30              : #include <opendht/crypto.h>
      31              : 
      32              : #include <exception>
      33              : #include <future>
      34              : #include <fstream>
      35              : #include <gnutls/ocsp.h>
      36              : 
      37              : namespace jami {
      38              : 
      39              : AccountManager::CertRequest
      40          797 : AccountManager::buildRequest(PrivateKey fDeviceKey)
      41              : {
      42          797 :     return dht::ThreadPool::computation().get<std::unique_ptr<dht::crypto::CertificateRequest>>(
      43         1594 :         [fDeviceKey = std::move(fDeviceKey)] {
      44          797 :             auto request = std::make_unique<dht::crypto::CertificateRequest>();
      45         1594 :             request->setName("Jami device");
      46          797 :             const auto& deviceKey = fDeviceKey.get();
      47          797 :             request->setUID(deviceKey->getPublicKey().getId().toString());
      48          797 :             request->sign(*deviceKey);
      49          797 :             return request;
      50         1594 :         });
      51              : }
      52              : 
      53         2439 : AccountManager::~AccountManager()
      54              : {
      55          813 :     if (dht_)
      56          704 :         dht_->join();
      57          813 : }
      58              : 
      59              : void
      60          352 : AccountManager::onSyncData(DeviceSync&& sync, bool checkDevice)
      61              : {
      62          352 :     auto sync_date = clock::time_point(clock::duration(sync.date));
      63          351 :     if (checkDevice) {
      64              :         // If the DHT is used, we need to check the device here
      65            0 :         if (not info_->contacts->syncDevice(sync.owner->getLongId(), sync_date)) {
      66            0 :             return;
      67              :         }
      68              :     }
      69              : 
      70              :     // Sync known devices
      71         1405 :     JAMI_DEBUG("[Account {}] [Contacts] received device sync data ({:d} devices, {:d} contacts, {:d} requests)",
      72              :                accountId_,
      73              :                sync.devices.size(),
      74              :                sync.peers.size(),
      75              :                sync.trust_requests.size());
      76          743 :     for (const auto& d : sync.devices) {
      77          390 :         findCertificate(d.first, [this, d](const std::shared_ptr<dht::crypto::Certificate>& crt) {
      78          390 :             if (not crt)
      79            0 :                 return;
      80              :             // std::lock_guard lock(deviceListMutex_);
      81          390 :             foundAccountDevice(crt, d.second.name);
      82              :         });
      83              :     }
      84              :     // saveKnownDevices();
      85              : 
      86              :     // Sync contacts
      87          353 :     if (!sync.peers.empty()) {
      88          148 :         for (const auto& peer : sync.peers) {
      89           74 :             info_->contacts->updateContact(peer.first, peer.second);
      90              :         }
      91           74 :         info_->contacts->saveContacts();
      92              :     }
      93              : 
      94              :     // Sync trust requests
      95          358 :     for (const auto& tr : sync.trust_requests)
      96            5 :         info_->contacts
      97            5 :             ->onTrustRequest(tr.first, tr.second.device, tr.second.received, false, tr.second.conversationId, {});
      98              : }
      99              : 
     100              : dht::crypto::Identity
     101          813 : AccountManager::loadIdentity(const std::string& crt_path, const std::string& key_path, const std::string& key_pwd) const
     102              : {
     103              :     // Return to avoid unnecessary log if certificate or key is missing. Example case: when
     104              :     // importing an account when the certificate has not been unpacked from the archive.
     105          813 :     if (crt_path.empty() or key_path.empty())
     106          793 :         return {};
     107              : 
     108           80 :     JAMI_DEBUG("[Account {}] [Auth] Loading certificate from '{}' and key from '{}' at {}",
     109              :                accountId_,
     110              :                crt_path,
     111              :                key_path,
     112              :                path_);
     113              :     try {
     114           20 :         dht::crypto::Certificate dht_cert(fileutils::loadFile(crt_path, path_));
     115           20 :         dht::crypto::PrivateKey dht_key(fileutils::loadFile(key_path, path_), key_pwd);
     116           20 :         auto crt_id = dht_cert.getLongId();
     117           20 :         if (!crt_id or crt_id != dht_key.getPublicKey().getLongId()) {
     118            0 :             JAMI_ERROR("[Account {}] [Auth] Device certificate not matching public key!", accountId_);
     119            0 :             return {};
     120              :         }
     121           20 :         auto& issuer = dht_cert.issuer;
     122           20 :         if (not issuer) {
     123            0 :             JAMI_ERROR("[Account {}] [Auth] Device certificate {:s} has no issuer",
     124              :                        accountId_,
     125              :                        dht_cert.getId().to_view());
     126            0 :             return {};
     127              :         }
     128              :         // load revocation lists for device authority (account certificate).
     129           20 :         Manager::instance().certStore(accountId_).loadRevocations(*issuer);
     130              : 
     131           40 :         return {std::make_shared<dht::crypto::PrivateKey>(std::move(dht_key)),
     132           20 :                 std::make_shared<dht::crypto::Certificate>(std::move(dht_cert))};
     133           20 :     } catch (const std::exception& e) {
     134            0 :         JAMI_ERROR("[Account {}] [Auth] Error loading identity: {}", accountId_, e.what());
     135            0 :     }
     136            0 :     return {};
     137              : }
     138              : 
     139              : std::shared_ptr<dht::Value>
     140           16 : AccountManager::parseAnnounce(const std::string& announceBase64,
     141              :                               const std::string& accountId,
     142              :                               const std::string& deviceSha1,
     143              :                               const std::string& deviceSha256)
     144              : {
     145           16 :     auto announce_val = std::make_shared<dht::Value>();
     146              :     try {
     147           16 :         auto announce = base64::decode(announceBase64);
     148           16 :         msgpack::object_handle announce_msg = msgpack::unpack((const char*) announce.data(), announce.size());
     149           16 :         announce_val->msgpack_unpack(announce_msg.get());
     150           16 :         if (not announce_val->checkSignature()) {
     151            0 :             JAMI_ERROR("[Auth] announce signature check failed");
     152            0 :             return {};
     153              :         }
     154           16 :         DeviceAnnouncement da;
     155           16 :         da.unpackValue(*announce_val);
     156           16 :         if (da.from.toString() != accountId) {
     157            0 :             JAMI_ERROR("[Auth] Account ID mismatch in announce (account: {}, in announce: {})",
     158              :                        accountId,
     159              :                        da.from.toString());
     160            0 :             return {};
     161              :         }
     162           16 :         if (da.dev.toString() != deviceSha1) {
     163            0 :             JAMI_ERROR("[Auth] Device ID mismatch in announce (device: {}, in announce: {})", deviceSha1, da.dev);
     164            0 :             return {};
     165              :         }
     166           16 :         if (!da.pk) {
     167            0 :             JAMI_ERROR("[Auth] No public key found in announce (account: {}, device: {})", accountId, da.dev);
     168            0 :             return {};
     169              :         }
     170           16 :         if (da.pk->getLongId().to_view() != deviceSha256) {
     171            0 :             JAMI_ERROR("[Auth] Public key ID mismatch in announce (device: {}, in announce: {})",
     172              :                        deviceSha256,
     173              :                        da.pk->getLongId().to_view());
     174            0 :             return {};
     175              :         }
     176           16 :     } catch (const std::exception& e) {
     177            0 :         JAMI_ERROR("[Auth] unable to read announce: {}", e.what());
     178            0 :         return {};
     179            0 :     }
     180              :     // Device announcements are presence-only — use normal priority to avoid
     181              :     // waking peers with high-priority push notifications.
     182           16 :     announce_val->priority = 1;
     183           16 :     return announce_val;
     184           16 : }
     185              : 
     186              : const AccountInfo*
     187          813 : AccountManager::useIdentity(const dht::crypto::Identity& identity,
     188              :                             const std::string& receipt,
     189              :                             const std::vector<uint8_t>& receiptSignature,
     190              :                             const std::string& username,
     191              :                             const OnChangeCallback& onChange)
     192              : {
     193          813 :     if (receipt.empty() or receiptSignature.empty())
     194          797 :         return nullptr;
     195              : 
     196           16 :     if (not identity.first or not identity.second) {
     197            0 :         JAMI_ERROR("[Account {}] [Auth] no identity provided", accountId_);
     198            0 :         return nullptr;
     199              :     }
     200              : 
     201           16 :     auto accountCertificate = identity.second->issuer;
     202           16 :     if (not accountCertificate) {
     203            0 :         JAMI_ERROR("[Account {}] [Auth] device certificate must be issued by the account certificate", accountId_);
     204            0 :         return nullptr;
     205              :     }
     206              : 
     207              :     // match certificate chain
     208           16 :     auto contactList = std::make_unique<ContactList>(accountId_, accountCertificate, path_, onChange);
     209           16 :     auto result = contactList->isValidAccountDevice(*identity.second);
     210           16 :     if (not result) {
     211            0 :         JAMI_ERROR("[Account {}] [Auth] unable to use identity: device certificate chain is unable to be verified: {}",
     212              :                    accountId_,
     213              :                    result.toString());
     214            0 :         return nullptr;
     215              :     }
     216              : 
     217           16 :     auto pk = accountCertificate->getSharedPublicKey();
     218           64 :     JAMI_LOG("[Account {}] [Auth] checking device receipt for account:{} device:{}",
     219              :              accountId_,
     220              :              pk->getId().toString(),
     221              :              identity.second->getLongId().toString());
     222           48 :     if (!pk->checkSignature({receipt.begin(), receipt.end()}, receiptSignature)) {
     223            0 :         JAMI_ERROR("[Account {}] [Auth] device receipt signature check failed", accountId_);
     224            0 :         return nullptr;
     225              :     }
     226              : 
     227           16 :     Json::Value root;
     228           16 :     if (!json::parse(receipt, root) || !root.isMember("announce")) {
     229            0 :         JAMI_ERROR("[Account {}] [Auth] device receipt parsing error", accountId_);
     230            0 :         return nullptr;
     231              :     }
     232              : 
     233           16 :     auto dev_id = root["dev"].asString();
     234           16 :     if (dev_id != identity.second->getId().toString()) {
     235            0 :         JAMI_ERROR("[Account {}] [Auth] device ID mismatch between receipt and certificate", accountId_);
     236            0 :         return nullptr;
     237              :     }
     238           16 :     auto id = root["id"].asString();
     239           16 :     if (id != pk->getId().toString()) {
     240            0 :         JAMI_ERROR("[Account {}] [Auth] account ID mismatch between receipt and certificate", accountId_);
     241            0 :         return nullptr;
     242              :     }
     243              : 
     244           16 :     auto devicePk = identity.first->getSharedPublicKey();
     245           16 :     if (!devicePk) {
     246            0 :         JAMI_ERROR("[Account {}] [Auth] No device pk found", accountId_);
     247            0 :         return nullptr;
     248              :     }
     249              : 
     250           32 :     auto announce = parseAnnounce(root["announce"].asString(),
     251              :                                   id,
     252           32 :                                   devicePk->getId().toString(),
     253           48 :                                   devicePk->getLongId().toString());
     254           16 :     if (not announce) {
     255            0 :         return nullptr;
     256              :     }
     257              : 
     258           16 :     onChange_ = std::move(onChange);
     259              : 
     260           16 :     auto info = std::make_unique<AccountInfo>();
     261           16 :     info->identity = identity;
     262           16 :     info->contacts = std::move(contactList);
     263           16 :     info->contacts->load();
     264           16 :     info->accountId = id;
     265           16 :     info->devicePk = std::move(devicePk);
     266           16 :     info->deviceId = info->devicePk->getLongId().toString();
     267           16 :     info->announce = std::move(announce);
     268           16 :     info->ethAccount = root["eth"].asString();
     269           16 :     info->username = username;
     270           16 :     info_ = std::move(info);
     271              : 
     272           64 :     JAMI_LOG("[Account {}] [Auth] Device {} receipt checked successfully for user {}", accountId_, info_->deviceId, id);
     273           16 :     return info_.get();
     274           16 : }
     275              : 
     276              : void
     277            0 : AccountManager::reloadContacts()
     278              : {
     279            0 :     if (info_) {
     280            0 :         info_->contacts->load();
     281              :     }
     282            0 : }
     283              : 
     284              : void
     285          697 : AccountManager::startSync(const OnNewDeviceCb& cb, const OnDeviceAnnouncedCb& dcb, bool publishPresence)
     286              : {
     287              :     // Put device announcement
     288          697 :     if (info_->announce) {
     289          697 :         auto h = dht::InfoHash(info_->accountId);
     290          697 :         if (publishPresence) {
     291         1392 :             dht_->put(
     292              :                 h,
     293          696 :                 info_->announce,
     294         1392 :                 [dcb = std::move(dcb), h, accountId = accountId_](bool ok) {
     295          696 :                     if (ok)
     296         2744 :                         JAMI_DEBUG("[Account {}] device announced at {}", accountId, h.toString());
     297              :                     // We do not care about the status, it's a permanent put, if this fail,
     298              :                     // this means the DHT is disconnected but the put will be retried when connected.
     299          696 :                     if (dcb)
     300          696 :                         dcb();
     301          696 :                 },
     302              :                 {},
     303              :                 true);
     304              :         }
     305          697 :         for (const auto& crl : info_->identity.second->issuer->getRevocationLists()) {
     306            0 :             auto crlVal = std::make_shared<dht::Value>(*crl);
     307            0 :             crlVal->priority = 1; // CRLs are not urgent — use normal priority
     308            0 :             dht_->put(h, crlVal, dht::DoneCallback {}, {}, true);
     309            0 :         }
     310          697 :         dht_->listen<DeviceAnnouncement>(h, [this, cb = std::move(cb)](DeviceAnnouncement&& dev) {
     311          768 :             if (dev.pk) {
     312          768 :                 findCertificate(dev.pk->getLongId(), [this, cb](const std::shared_ptr<dht::crypto::Certificate>& crt) {
     313          768 :                     foundAccountDevice(crt);
     314          768 :                     if (cb)
     315          768 :                         cb(crt);
     316          768 :                 });
     317              :             } else {
     318            0 :                 findCertificate(dev.dev, [this, cb](const std::shared_ptr<dht::crypto::Certificate>& crt) {
     319            0 :                     foundAccountDevice(crt);
     320            0 :                     if (cb)
     321            0 :                         cb(crt);
     322            0 :                 });
     323              :             }
     324          768 :             return true;
     325              :         });
     326          697 :         dht_->listen<dht::crypto::RevocationList>(h, [this](dht::crypto::RevocationList&& crl) {
     327            3 :             if (crl.isSignedBy(*info_->identity.second->issuer)) {
     328           12 :                 JAMI_DEBUG("[Account {}] Found CRL for account.", accountId_);
     329            6 :                 certStore().pinRevocationList(info_->accountId,
     330            6 :                                               std::make_shared<dht::crypto::RevocationList>(std::move(crl)));
     331              :             }
     332            3 :             return true;
     333              :         });
     334          697 :         syncDevices();
     335              :     } else {
     336            0 :         JAMI_ERROR("[Account {}] Unable to announce device: no announcement.", accountId_);
     337              :     }
     338              : 
     339          697 :     auto inboxKey = dht::InfoHash::get("inbox:" + info_->devicePk->getId().toString());
     340          697 :     dht_->listen<dht::TrustRequest>(inboxKey, [this](dht::TrustRequest&& v) {
     341          189 :         if (v.service != DHT_TYPE_NS)
     342            0 :             return true;
     343              : 
     344              :         // allowPublic always true for trust requests (only forbidden if banned)
     345          189 :         onPeerMessage(*v.owner,
     346              :                       true,
     347          378 :                       [this, v](const std::shared_ptr<dht::crypto::Certificate>&, dht::InfoHash peer_account) mutable {
     348          748 :                           JAMI_WARNING(
     349              :                               "[Account {}] [device {}] Got trust request (confirm: {}) from: {}. ConversationId: {}",
     350              :                               accountId_,
     351              :                               v.owner->getLongId().toString(),
     352              :                               v.confirm,
     353              :                               peer_account.toString(),
     354              :                               v.conversationId);
     355          187 :                           if (info_)
     356          374 :                               if (info_->contacts->onTrustRequest(peer_account,
     357          187 :                                                                   v.owner,
     358              :                                                                   nowMs(),
     359          187 :                                                                   v.confirm,
     360          187 :                                                                   v.conversationId,
     361          187 :                                                                   std::move(v.payload))) {
     362           35 :                                   if (v.confirm) // No need to send a confirmation as already accepted here
     363            0 :                                       return;
     364           35 :                                   sendTrustRequestConfirm(peer_account, v.conversationId);
     365              :                               }
     366              :                       });
     367          189 :         return true;
     368              :     });
     369          697 : }
     370              : 
     371              : const std::map<dht::PkId, KnownDevice>&
     372          353 : AccountManager::getKnownDevices() const
     373              : {
     374          353 :     return info_->contacts->getKnownDevices();
     375              : }
     376              : 
     377              : bool
     378         1158 : AccountManager::foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
     379              :                                    const std::string& name,
     380              :                                    const time_point& last_sync)
     381              : {
     382         1158 :     return info_->contacts->foundAccountDevice(crt, name, last_sync);
     383              : }
     384              : 
     385              : void
     386           20 : AccountManager::setAccountDeviceName(const std::string& name)
     387              : {
     388           20 :     if (info_)
     389           16 :         info_->contacts->setAccountDeviceName(DeviceId(info_->deviceId), name);
     390           20 : }
     391              : 
     392              : std::string
     393          795 : AccountManager::getAccountDeviceName() const
     394              : {
     395          795 :     if (info_)
     396          795 :         return info_->contacts->getAccountDeviceName(DeviceId(info_->deviceId));
     397            0 :     return {};
     398              : }
     399              : 
     400              : bool
     401          975 : AccountManager::foundPeerDevice(const std::string& accountId,
     402              :                                 const std::shared_ptr<dht::crypto::Certificate>& crt,
     403              :                                 dht::InfoHash& peer_id)
     404              : {
     405          975 :     if (not crt)
     406            0 :         return false;
     407              : 
     408          975 :     auto top_issuer = crt;
     409         2925 :     while (top_issuer->issuer)
     410         1950 :         top_issuer = top_issuer->issuer;
     411              : 
     412              :     // Device certificate is unable to be self-signed
     413          975 :     if (top_issuer == crt) {
     414            0 :         JAMI_WARNING("[Account {}] Found invalid peer device: {}", accountId, crt->getLongId().toString());
     415            0 :         return false;
     416              :     }
     417              : 
     418              :     // Check peer certificate chain
     419              :     // Trust store with top issuer as the only CA
     420          975 :     dht::crypto::TrustList peer_trust;
     421          975 :     peer_trust.add(*top_issuer);
     422          975 :     if (not peer_trust.verify(*crt)) {
     423            0 :         JAMI_WARNING("[Account {}] Found invalid peer device: {}", accountId, crt->getLongId().toString());
     424            0 :         return false;
     425              :     }
     426              : 
     427              :     // Check cached OCSP response
     428          975 :     if (crt->ocspResponse and crt->ocspResponse->getCertificateStatus() != GNUTLS_OCSP_CERT_GOOD) {
     429            0 :         JAMI_WARNING("[Account {}] Certificate {} is disabled by cached OCSP response", accountId, crt->getLongId());
     430            0 :         return false;
     431              :     }
     432              : 
     433          975 :     peer_id = crt->issuer->getId();
     434         3900 :     JAMI_LOG("[Account {}] [device {}] Found device for peer: {} CA:{}",
     435              :              accountId,
     436              :              crt->getLongId().toString(),
     437              :              peer_id.toString(),
     438              :              top_issuer->getId().toString());
     439          975 :     return true;
     440          975 : }
     441              : 
     442              : void
     443          189 : AccountManager::onPeerMessage(
     444              :     const dht::crypto::PublicKey& peer_device,
     445              :     bool allowPublic,
     446              :     std::function<void(const std::shared_ptr<dht::crypto::Certificate>& crt, const dht::InfoHash& peer_account)>&& cb)
     447              : {
     448              :     // quick check in case we already explicilty banned this device
     449          189 :     auto trustStatus = getCertificateStatus(peer_device.getLongId().toString());
     450          189 :     if (trustStatus == dhtnet::tls::TrustStore::PermissionStatus::BANNED) {
     451            4 :         JAMI_WARNING("[Account {}] [Auth] Discarding message from banned device {}",
     452              :                      accountId_,
     453              :                      peer_device.getLongId().to_view());
     454            1 :         return;
     455              :     }
     456              : 
     457          188 :     findCertificate(peer_device.getLongId(),
     458          376 :                     [this, cb = std::move(cb), allowPublic](const std::shared_ptr<dht::crypto::Certificate>& cert) {
     459          188 :                         dht::InfoHash peer_account_id;
     460          188 :                         if (onPeerCertificate(cert, allowPublic, peer_account_id)) {
     461          187 :                             cb(cert, peer_account_id);
     462              :                         }
     463          188 :                     });
     464              : }
     465              : 
     466              : bool
     467          975 : AccountManager::onPeerCertificate(const std::shared_ptr<dht::crypto::Certificate>& cert,
     468              :                                   bool allowPublic,
     469              :                                   dht::InfoHash& account_id)
     470              : {
     471          975 :     dht::InfoHash peer_account_id;
     472          975 :     if (not foundPeerDevice(accountId_, cert, peer_account_id)) {
     473            0 :         JAMI_WARNING("[Account {}] [Auth] Discarding message from invalid peer certificate", accountId_);
     474            0 :         return false;
     475              :     }
     476              : 
     477          975 :     if (not isAllowed(*cert, allowPublic)) {
     478           24 :         JAMI_WARNING("[Account {}] [Auth] Discarding message from unauthorized peer {}.",
     479              :                      accountId_,
     480              :                      peer_account_id.toString());
     481            6 :         return false;
     482              :     }
     483              : 
     484          969 :     account_id = peer_account_id;
     485          969 :     return true;
     486              : }
     487              : 
     488              : bool
     489           66 : AccountManager::addContact(const dht::InfoHash& uri, bool confirmed, const std::string& conversationId)
     490              : {
     491           66 :     if (not info_) {
     492            0 :         JAMI_ERROR("addContact(): account not loaded");
     493            0 :         return false;
     494              :     }
     495          264 :     JAMI_WARNING("[Account {}] addContact {}", accountId_, confirmed);
     496           66 :     if (info_->contacts->addContact(uri, confirmed, conversationId)) {
     497           66 :         syncDevices();
     498           66 :         return true;
     499              :     }
     500            0 :     return false;
     501              : }
     502              : 
     503              : void
     504           19 : AccountManager::removeContact(const std::string& uri, bool banned)
     505              : {
     506           19 :     dht::InfoHash h(uri);
     507           19 :     if (not h) {
     508            0 :         JAMI_ERROR("[Account {}] removeContact: invalid contact URI: {}", accountId_, uri);
     509            0 :         return;
     510              :     }
     511           19 :     if (not info_) {
     512            0 :         JAMI_ERROR("[Account {}] removeContact: account not loaded", accountId_);
     513            0 :         return;
     514              :     }
     515           19 :     if (info_->contacts->removeContact(h, banned))
     516           19 :         syncDevices();
     517              : }
     518              : 
     519              : void
     520            0 : AccountManager::removeContactConversation(const std::string& uri)
     521              : {
     522            0 :     dht::InfoHash h(uri);
     523            0 :     if (not h) {
     524            0 :         JAMI_ERROR("[Account {}] removeContactConversation: invalid contact URI: {}", accountId_, uri);
     525            0 :         return;
     526              :     }
     527            0 :     if (not info_) {
     528            0 :         JAMI_ERROR("[Account {}] removeContactConversation: account not loaded", accountId_);
     529            0 :         return;
     530              :     }
     531            0 :     if (info_->contacts->removeContactConversation(h))
     532            0 :         syncDevices();
     533              : }
     534              : 
     535              : void
     536           27 : AccountManager::updateContactConversation(const std::string& uri, const std::string& convId, bool added)
     537              : {
     538           27 :     dht::InfoHash h(uri);
     539           27 :     if (not h) {
     540            0 :         JAMI_ERROR("[Account {}] updateContactConversation: invalid contact URI: {}", accountId_, uri);
     541            0 :         return;
     542              :     }
     543           27 :     if (not info_) {
     544            0 :         JAMI_ERROR("[Account {}] updateContactConversation: account not loaded", accountId_);
     545            0 :         return;
     546              :     }
     547           27 :     if (info_->contacts->updateConversation(h, convId, added)) {
     548              :         // Also decline trust request if there is one
     549           11 :         auto req = info_->contacts->getTrustRequest(h);
     550           11 :         auto convIt = req.find(libjami::Account::TrustRequest::CONVERSATIONID);
     551           11 :         if (convIt != req.end() && convIt->second == convId) {
     552            0 :             discardTrustRequest(uri);
     553              :         }
     554           11 :         syncDevices();
     555           11 :     }
     556              : }
     557              : 
     558              : std::map<dht::InfoHash, Contact>
     559          703 : AccountManager::getContacts(bool includeRemoved) const
     560              : {
     561          703 :     if (not info_) {
     562            0 :         JAMI_ERROR("[Account {}] getContacts(): account not loaded", accountId_);
     563            0 :         return {};
     564              :     }
     565          703 :     const auto& contacts = info_->contacts->getContacts();
     566          703 :     std::map<dht::InfoHash, Contact> ret;
     567          712 :     for (const auto& c : contacts) {
     568            9 :         if (!c.second.isActive() && !includeRemoved && !c.second.isBanned())
     569            0 :             continue;
     570            9 :         ret.emplace(c.first, c.second);
     571              :     }
     572          703 :     return ret;
     573          703 : }
     574              : 
     575              : /** Obtain details about one account contact in serializable form. */
     576              : std::map<std::string, std::string>
     577            6 : AccountManager::getContactDetails(const std::string& uri) const
     578              : {
     579            6 :     if (!info_) {
     580            0 :         JAMI_ERROR("[Account {}] getContactDetails(): account not loaded", accountId_);
     581            0 :         return {};
     582              :     }
     583            6 :     dht::InfoHash h(uri);
     584            6 :     if (not h) {
     585            0 :         JAMI_ERROR("[Account {}] getContactDetails: invalid contact URI: {}", accountId_, uri);
     586            0 :         return {};
     587              :     }
     588            6 :     return info_->contacts->getContactDetails(h);
     589              : }
     590              : 
     591              : std::optional<Contact>
     592         1236 : AccountManager::getContactInfo(const std::string& uri) const
     593              : {
     594         1236 :     if (!info_) {
     595            0 :         JAMI_ERROR("[Account {}] getContactInfo(): account not loaded", accountId_);
     596            0 :         return {};
     597              :     }
     598         1236 :     dht::InfoHash h(uri);
     599         1235 :     if (not h) {
     600            0 :         JAMI_ERROR("[Account {}] getContactInfo: invalid contact URI: {}", accountId_, uri);
     601            0 :         return {};
     602              :     }
     603         1235 :     return info_->contacts->getContactInfo(h);
     604              : }
     605              : 
     606              : bool
     607          898 : AccountManager::findCertificate(const dht::InfoHash& h,
     608              :                                 std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
     609              : {
     610          898 :     if (auto cert = certStore().getCertificate(h.toString())) {
     611            5 :         if (cb)
     612            1 :             cb(cert);
     613          893 :     } else if (cb)
     614          898 :         cb(nullptr);
     615          898 :     return true;
     616              : }
     617              : 
     618              : bool
     619         5748 : AccountManager::findCertificate(const dht::PkId& id,
     620              :                                 std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
     621              : {
     622         5748 :     if (auto cert = certStore().getCertificate(id.toString())) {
     623         4728 :         if (cb)
     624         4728 :             cb(cert);
     625         1020 :     } else if (dht_) {
     626         1020 :         dht_->findCertificate(id, [cb = std::move(cb), this](const std::shared_ptr<dht::crypto::Certificate>& crt) {
     627         1020 :             if (crt && info_) {
     628         1019 :                 certStore().pinCertificate(crt);
     629              :             }
     630         1020 :             if (cb)
     631         1020 :                 cb(crt);
     632         1020 :         });
     633            0 :     } else if (cb)
     634         5748 :         cb(nullptr);
     635         5748 :     return true;
     636              : }
     637              : 
     638              : bool
     639           89 : AccountManager::setCertificateStatus(const std::string& cert_id, dhtnet::tls::TrustStore::PermissionStatus status)
     640              : {
     641           89 :     return info_ and info_->contacts->setCertificateStatus(cert_id, status);
     642              : }
     643              : 
     644              : bool
     645          811 : AccountManager::setCertificateStatus(const std::shared_ptr<crypto::Certificate>& cert,
     646              :                                      dhtnet::tls::TrustStore::PermissionStatus status,
     647              :                                      bool local)
     648              : {
     649          811 :     return info_ and info_->contacts->setCertificateStatus(cert, status, local);
     650              : }
     651              : 
     652              : std::vector<std::string>
     653            0 : AccountManager::getCertificatesByStatus(dhtnet::tls::TrustStore::PermissionStatus status)
     654              : {
     655            0 :     return info_ ? info_->contacts->getCertificatesByStatus(status) : std::vector<std::string> {};
     656              : }
     657              : 
     658              : dhtnet::tls::TrustStore::PermissionStatus
     659        20790 : AccountManager::getCertificateStatus(const std::string& cert_id) const
     660              : {
     661        20790 :     return info_ ? info_->contacts->getCertificateStatus(cert_id)
     662        20802 :                  : dhtnet::tls::TrustStore::PermissionStatus::UNDEFINED;
     663              : }
     664              : 
     665              : bool
     666          975 : AccountManager::isAllowed(const crypto::Certificate& crt, bool allowPublic)
     667              : {
     668          975 :     return info_ and info_->contacts->isAllowed(crt, allowPublic);
     669              : }
     670              : 
     671              : std::vector<std::map<std::string, std::string>>
     672          722 : AccountManager::getTrustRequests() const
     673              : {
     674          722 :     if (not info_) {
     675            0 :         JAMI_ERROR("[Account {}] getTrustRequests(): account not loaded", accountId_);
     676            0 :         return {};
     677              :     }
     678          722 :     return info_->contacts->getTrustRequests();
     679              : }
     680              : 
     681              : bool
     682          193 : AccountManager::acceptTrustRequest(const std::string& from, bool includeConversation)
     683              : {
     684          193 :     dht::InfoHash f(from);
     685          193 :     if (info_) {
     686          193 :         auto req = info_->contacts->getTrustRequest(dht::InfoHash(from));
     687          193 :         if (info_->contacts->acceptTrustRequest(f)) {
     688           84 :             sendTrustRequestConfirm(f, includeConversation ? req[libjami::Account::TrustRequest::CONVERSATIONID] : "");
     689           42 :             syncDevices();
     690           42 :             return true;
     691              :         }
     692          151 :         return false;
     693          193 :     }
     694            0 :     return false;
     695              : }
     696              : 
     697              : bool
     698            3 : AccountManager::discardTrustRequest(const std::string& from)
     699              : {
     700            3 :     dht::InfoHash f(from);
     701            6 :     return info_ and info_->contacts->discardTrustRequest(f);
     702              : }
     703              : 
     704              : void
     705          102 : AccountManager::sendTrustRequest(const std::string& to, const std::string& convId, const std::vector<uint8_t>& payload)
     706              : {
     707          408 :     JAMI_WARNING("[Account {}] AccountManager::sendTrustRequest", accountId_);
     708          102 :     auto toH = dht::InfoHash(to);
     709          102 :     if (not toH) {
     710            0 :         JAMI_ERROR("[Account {}] Unable to send trust request to invalid hash: {}", accountId_, to);
     711            0 :         return;
     712              :     }
     713          102 :     if (not info_) {
     714            0 :         JAMI_ERROR("[Account {}] sendTrustRequest(): account not loaded", accountId_);
     715            0 :         return;
     716              :     }
     717          102 :     if (info_->contacts->addContact(toH, false, convId)) {
     718            0 :         syncDevices();
     719              :     }
     720          102 :     forEachDevice(toH, [this, toH, convId, payload](const std::shared_ptr<dht::crypto::PublicKey>& dev) {
     721          117 :         auto to = toH.toString();
     722          468 :         JAMI_WARNING("[Account {}] [device {}] Sending trust request (size {:d}) to: {:s}",
     723              :                      accountId_,
     724              :                      dev->getLongId(),
     725              :                      payload.size(),
     726              :                      to);
     727          234 :         dht_->putEncrypted(dht::InfoHash::get(concat("inbox:"sv, dev->getId().to_view())),
     728              :                            dev,
     729          468 :                            dht::TrustRequest(DHT_TYPE_NS, convId, payload),
     730          234 :                            [to, size = payload.size()](bool ok) {
     731          117 :                                if (!ok)
     732          108 :                                    JAMI_ERROR("Tried to send request {:s} (size: "
     733              :                                               "{:d}), but put failed",
     734              :                                               to,
     735              :                                               size);
     736          117 :                            });
     737          117 :     });
     738              : }
     739              : 
     740              : void
     741           77 : AccountManager::sendTrustRequestConfirm(const dht::InfoHash& toH, const std::string& convId)
     742              : {
     743          308 :     JAMI_WARNING("[Account {}] AccountManager::sendTrustRequestConfirm to {} (conversation {})",
     744              :                  accountId_,
     745              :                  toH,
     746              :                  convId);
     747           77 :     dht::TrustRequest answer {DHT_TYPE_NS, convId};
     748           77 :     answer.confirm = true;
     749              : 
     750           77 :     if (!convId.empty() && info_)
     751          231 :         info_->contacts->acceptConversation(convId);
     752              : 
     753           77 :     forEachDevice(toH, [this, toH, answer](const std::shared_ptr<dht::crypto::PublicKey>& dev) {
     754          316 :         JAMI_WARNING("[Account {}] sending trust request reply: {} / {}", accountId_, toH, dev->getLongId());
     755           79 :         dht_->putEncrypted(dht::InfoHash::get("inbox:" + dev->getId().toString()), dev, answer);
     756           79 :     });
     757           77 : }
     758              : 
     759              : void
     760         3591 : AccountManager::forEachDevice(const dht::InfoHash& to,
     761              :                               std::function<void(const std::shared_ptr<dht::crypto::PublicKey>&)>&& op,
     762              :                               std::function<void(bool)>&& end)
     763              : {
     764         3591 :     if (not dht_) {
     765            0 :         JAMI_ERROR("[Account {}] forEachDevice: no dht", accountId_);
     766            0 :         if (end)
     767            0 :             end(false);
     768            0 :         return;
     769              :     }
     770         3591 :     dht_->get<dht::crypto::RevocationList>(to, [to, this](dht::crypto::RevocationList&& crl) {
     771            0 :         certStore().pinRevocationList(to.toString(), std::move(crl));
     772            0 :         return true;
     773              :     });
     774              : 
     775              :     struct State
     776              :     {
     777              :         const dht::InfoHash to;
     778              :         const std::string accountId;
     779              :         // Note: state is initialized to 1, because we need to wait that the get is finished
     780              :         unsigned remaining {1};
     781              :         std::set<dht::PkId> treatedDevices {};
     782              :         std::function<void(const std::shared_ptr<dht::crypto::PublicKey>&)> onDevice;
     783              :         std::function<void(bool)> onEnd;
     784              : 
     785         3590 :         State(dht::InfoHash to, std::string accountId)
     786         3590 :             : to(std::move(to))
     787         3590 :             , accountId(std::move(accountId))
     788         3591 :         {}
     789              : 
     790         7203 :         void found(const std::shared_ptr<dht::crypto::PublicKey>& pk)
     791              :         {
     792         7203 :             remaining--;
     793         7203 :             if (pk && *pk) {
     794         3612 :                 auto longId = pk->getLongId();
     795         3612 :                 if (treatedDevices.emplace(longId).second) {
     796         3603 :                     onDevice(pk);
     797              :                 }
     798              :             }
     799         7203 :             ended();
     800         7203 :         }
     801              : 
     802         7203 :         void ended()
     803              :         {
     804         7203 :             if (remaining == 0 && onEnd) {
     805          356 :                 JAMI_LOG("[Account {}] Found {:d} device(s) for {}", accountId, treatedDevices.size(), to);
     806           89 :                 onEnd(not treatedDevices.empty());
     807           89 :                 onDevice = {};
     808           89 :                 onEnd = {};
     809              :             }
     810         7203 :         }
     811              :     };
     812         3590 :     auto state = std::make_shared<State>(to, accountId_);
     813         3590 :     state->onDevice = std::move(op);
     814         3591 :     state->onEnd = std::move(end);
     815              : 
     816        10771 :     dht_->get<DeviceAnnouncement>(
     817              :         to,
     818         7181 :         [this, to, state](DeviceAnnouncement&& dev) {
     819         3612 :             if (dev.from != to)
     820            0 :                 return true;
     821         3612 :             state->remaining++;
     822         3612 :             if (dev.pk) {
     823         3612 :                 findCertificate(dev.pk->getLongId(), [state](const std::shared_ptr<dht::crypto::Certificate>& cert) {
     824         3612 :                     state->found(cert ? cert->getSharedPublicKey() : std::shared_ptr<dht::crypto::PublicKey> {});
     825         3612 :                 });
     826              :             } else {
     827            0 :                 findCertificate(dev.dev, [state](const std::shared_ptr<dht::crypto::Certificate>& cert) {
     828            0 :                     state->found(cert ? cert->getSharedPublicKey() : std::shared_ptr<dht::crypto::PublicKey> {});
     829            0 :                 });
     830              :             }
     831         3612 :             return true;
     832              :         },
     833        10772 :         [state](bool /*ok*/) { state->found({}); });
     834         3591 : }
     835              : 
     836              : void
     837            3 : AccountManager::lookupUri(const std::string& name, const std::string& defaultServer, LookupCallback cb)
     838              : {
     839            3 :     nameDir_.get().lookupUri(name, defaultServer, std::move(cb));
     840            3 : }
     841              : 
     842              : void
     843          808 : AccountManager::lookupAddress(const std::string& addr, LookupCallback cb)
     844              : {
     845          808 :     nameDir_.get().lookupAddress(addr, cb);
     846          808 : }
     847              : 
     848              : dhtnet::tls::CertificateStore&
     849         7672 : AccountManager::certStore() const
     850              : {
     851         7672 :     return Manager::instance().certStore(info_->contacts->accountId());
     852              : }
     853              : 
     854              : } // namespace jami
        

Generated by: LCOV version 2.0-1