30#include <gnutls/ocsp.h>
35 const std::shared_ptr<crypto::Certificate>&
cert,
36 const std::filesystem::path& path,
38 : accountId_(accountId)
44 accountTrust_.add(*
cert);
69 std::unique_lock
lk(mutex_);
70 if (contacts_.find(dht::InfoHash(
cert_id)) != contacts_.end()) {
71 JAMI_LOG(
"[Account {}] [Contacts] Unable to set certificate status for existing contacts {}",
76 return trust_->setCertificateStatus(
cert_id, status);
81 dhtnet::tls::TrustStore::PermissionStatus status,
84 return trust_->setCertificateStatus(
cert, status,
local);
90 std::unique_lock
lk(mutex_);
91 JAMI_WARNING(
"[Account {}] [Contacts] addContact: {}, conversation: {}", accountId_, h, conversationId);
92 auto c = contacts_.find(h);
93 if (c == contacts_.end())
95 else if (c->second.isActive()
and c->second.confirmed == confirmed && c->second.conversationId == conversationId)
97 c->second.added = std::time(
nullptr);
101 c->second.removed = 0;
102 c->second.conversationId = conversationId;
103 c->second.confirmed |= confirmed;
104 auto hStr = h.toString();
105 trust_->setCertificateStatus(
hStr, dhtnet::tls::TrustStore::PermissionStatus::ALLOWED);
115 std::lock_guard
lk(mutex_);
116 auto c = contacts_.find(h);
117 if (c != contacts_.end() && c->second.conversationId != conversationId) {
118 c->second.conversationId = conversationId;
120 c->second.added = std::time(
nullptr);
131 std::unique_lock
lk(mutex_);
132 JAMI_WARNING(
"[Account {}] [Contacts] removeContact: {} (banned: {})", accountId_, h,
ban);
133 auto c = contacts_.find(h);
134 if (c == contacts_.end())
136 c->second.removed = std::time(
nullptr);
137 c->second.confirmed =
false;
138 c->second.banned =
ban;
139 c->second.conversationId =
"";
140 auto uri = h.toString();
141 trust_->setCertificateStatus(uri,
142 ban ? dhtnet::tls::TrustStore::PermissionStatus::BANNED
143 : dhtnet::tls::TrustStore::PermissionStatus::UNDEFINED);
144 if (trustRequests_.erase(h) > 0)
149 auto filename = path_.filename().string();
159 std::unique_lock
lk(mutex_);
160 auto c = contacts_.find(h);
161 if (c == contacts_.end())
163 c->second.conversationId =
"";
168std::map<std::string, std::string>
171 std::unique_lock
lk(mutex_);
172 const auto c = contacts_.find(h);
173 if (c == std::end(contacts_)) {
174 JAMI_WARNING(
"[Account {}] [Contacts] Contact '{}' not found", accountId_, h.to_view());
178 auto details = c->second.toMap();
180 details[
"id"] = c->first.toString();
185std::optional<Contact>
188 const auto c = contacts_.find(h);
189 if (c == std::end(contacts_)) {
190 JAMI_WARNING(
"[Account {}] [Contacts] Contact '{}' not found", accountId_, h.to_view());
196const std::map<dht::InfoHash, Contact>&
205 JAMI_LOG(
"[Account {}] [Contacts] replacing contact list (old: {} new: {})",
209 contacts_ = contacts;
212 for (
auto& peer : contacts)
213 if (peer.second.isActive())
214 callbacks_.
contactAdded(peer.first.toString(), peer.second.confirmed);
221 JAMI_ERROR(
"[Account {}] [Contacts] updateContact: invalid contact ID", accountId_);
224 bool stateChanged {
false};
225 auto c = contacts_.find(
id);
226 if (c == contacts_.end()) {
228 c = contacts_.emplace(
id,
contact).first;
229 stateChanged = c->second.isActive()
or c->second.isBanned();
232 stateChanged = c->second.update(
contact);
236 std::lock_guard
lk(mutex_);
237 if (trustRequests_.erase(
id) > 0)
240 if (c->second.isActive()) {
241 trust_->setCertificateStatus(
id.
toString(), dhtnet::tls::TrustStore::PermissionStatus::ALLOWED);
245 if (c->second.banned)
246 trust_->setCertificateStatus(
id.
toString(), dhtnet::tls::TrustStore::PermissionStatus::BANNED);
254ContactList::loadContacts()
256 decltype(contacts_) contacts;
258 std::lock_guard
fileLock(dhtnet::fileutils::getFileLock(path_ /
"contacts"));
262 msgpack::object_handle
oh = msgpack::unpack((
const char*)
file.data(),
file.size());
263 oh.get().convert(contacts);
264 }
catch (
const std::exception&
e) {
265 JAMI_WARNING(
"[Account {}] [Contacts] Error loading contacts: {}", accountId_,
e.what());
269 JAMI_WARNING(
"[Account {}] [Contacts] Loaded {} contacts", accountId_, contacts.size());
270 for (
auto& peer : contacts)
277 JAMI_LOG(
"[Account {}] [Contacts] saving {} contacts", accountId_, contacts_.size());
278 std::lock_guard
fileLock(dhtnet::fileutils::getFileLock(path_ /
"contacts"));
279 std::ofstream
file(path_ /
"contacts", std::ios::trunc | std::ios::binary);
280 msgpack::pack(
file, contacts_);
284ContactList::saveTrustRequests()
const
287 std::ofstream
file(path_ /
"incomingTrustRequests", std::ios::trunc | std::ios::binary);
288 msgpack::pack(
file, trustRequests_);
292ContactList::loadTrustRequests()
296 std::map<dht::InfoHash, TrustRequest>
requests;
301 msgpack::object_handle
oh = msgpack::unpack((
const char*)
file.data(),
file.size());
303 }
catch (
const std::exception&
e) {
304 JAMI_WARNING(
"[Account {}] [Contacts] Error loading trust requests: {}", accountId_,
e.what());
308 JAMI_WARNING(
"[Account {}] [Contacts] Loaded {} contact requests", accountId_,
requests.size());
320 const std::shared_ptr<dht::crypto::PublicKey>&
peer_device,
323 const std::string& conversationId,
324 std::vector<uint8_t>&& payload)
328 std::unique_lock
lk(mutex_);
331 if (
contact != contacts_.end()) {
333 if (
contact->second.isBanned())
336 if (
contact->second.isActive()) {
342 contact->second.confirmed =
true;
350 if (req == trustRequests_.end()) {
356 if (received > req->second.received) {
358 req->second.conversationId = conversationId;
359 req->second.received = received;
360 req->second.payload = payload;
362 JAMI_LOG(
"[Account {}] [Contacts] Ignoring outdated trust request from {}", accountId_,
peer_account);
380std::vector<std::map<std::string, std::string>>
383 using Map = std::map<std::string, std::string>;
384 std::vector<Map>
ret;
385 std::lock_guard
lk(mutex_);
386 ret.reserve(trustRequests_.size());
387 for (
const auto& r : trustRequests_) {
392 std::string(r.second.payload.begin(), r.second.payload.end())}});
397std::map<std::string, std::string>
400 using Map = std::map<std::string, std::string>;
401 std::lock_guard
lk(mutex_);
402 auto r = trustRequests_.find(from);
403 if (r == trustRequests_.end())
409 std::string(r->second.payload.begin(), r->second.payload.end())}};
416 std::unique_lock
lk(mutex_);
417 auto i = trustRequests_.find(from);
418 if (
i == trustRequests_.end())
420 auto convId =
i->second.conversationId;
422 trustRequests_.erase(
i);
439 std::lock_guard
lk(mutex_);
440 if (trustRequests_.erase(from) > 0) {
448ContactList::loadKnownDevices()
455 msgpack::object_handle
oh = msgpack::unpack((
const char*)
file.data(),
file.size());
457 std::map<dht::PkId, std::pair<std::string, uint64_t>>
knownDevices;
460 if (
auto crt = certStore.getCertificate(
d.first.toString())) {
462 JAMI_WARNING(
"[Account {}] [Contacts] Unable to add device {}", accountId_,
d.first);
464 JAMI_WARNING(
"[Account {}] [Contacts] Unable to find certificate for device {}", accountId_,
d.first);
470 }
catch (
const std::exception&
e) {
471 JAMI_WARNING(
"[Account {}] [Contacts] Error loading devices: {}", accountId_,
e.what());
477ContactList::saveKnownDevices()
const
479 std::ofstream
file(path_ /
"knownDevices", std::ios::trunc | std::ios::binary);
481 std::map<dht::PkId, std::pair<std::string, uint64_t>> devices;
482 for (
const auto&
id : knownDevices_) {
483 devices.emplace(
id.
first, std::make_pair(
id.
second.name, clock::to_time_t(
id.second.last_sync)));
486 msgpack::pack(
file, devices);
495 JAMI_LOG(
"[Account {}] [Contacts] Found account device: {} {}", accountId_, name, device);
500 if (
not name.empty()
and it.first->second.name != name) {
501 JAMI_LOG(
"[Account {}] [Contacts] Updating device name: {} {}", accountId_, name, device);
502 it.first->second.name = name;
511 const std::string& name,
518 auto id =
crt->getLongId();
523 JAMI_WARNING(
"[Account {}] [Contacts] Found invalid account device: {:s}: {:s}",
533 JAMI_LOG(
"[Account {}] [Contacts] Found account device: {} {}", accountId_, name,
id);
535 if (
crt->ocspResponse) {
536 unsigned int status =
crt->ocspResponse->getCertificateStatus();
538 JAMI_ERROR(
"[Account {}] Certificate {} has revoked OCSP status", accountId_,
id);
539 trust_->setCertificateStatus(
crt, dhtnet::tls::TrustStore::PermissionStatus::BANNED,
false);
548 if (
not name.empty()
and it.first->second.name != name) {
549 JAMI_LOG(
"[Account {}] [Contacts] updating device name: {} {}", accountId_, name,
id);
550 it.first->second.name = name;
563 if (knownDevices_.erase(device) > 0) {
573 auto dev = knownDevices_.find(device);
574 if (
dev != knownDevices_.end()) {
575 if (
dev->second.name != name) {
576 dev->second.name = name;
586 auto dev = knownDevices_.find(device);
587 if (
dev != knownDevices_.end()) {
588 return dev->second.name;
602 std::lock_guard
lk(mutex_);
604 for (
const auto& req : trustRequests_)
607 TrustRequest {req.second.
device, req.second.conversationId, req.second.received, {}});
610 auto req = trustRequests_.lower_bound(dht::InfoHash::getRandom());
611 while (inserted++ < MAX_TRUST_REQUESTS) {
612 if (req == trustRequests_.end())
613 req = trustRequests_.begin();
614 sync_data.trust_requests
616 TrustRequest {req->second.device, req->second.conversationId, req->second.received, {}});
621 for (
const auto&
dev : knownDevices_) {
622 if (!
dev.second.certificate) {
623 JAMI_WARNING(
"[Account {}] [Contacts] No certificate found for {}", accountId_,
dev.first);
626 sync_data.devices.emplace(
dev.second.certificate->getLongId(), KnownDeviceSync {dev.second.name});
632ContactList::syncDevice(
const dht::PkId& device,
const time_point& syncDate)
634 auto it = knownDevices_.find(device);
635 if (it == knownDevices_.end()) {
636 JAMI_WARNING(
"[Account {}] [Contacts] Dropping sync data from unknown device", accountId_);
639 if (it->second.last_sync >= syncDate) {
640 JAMI_LOG(
"[Account {}] [Contacts] Dropping outdated sync data", accountId_);
643 it->second.last_sync = syncDate;
static LIBJAMI_TEST_EXPORT Manager & instance()
dhtnet::tls::CertificateStore & certStore(const std::string &accountId) const
#define JAMI_ERROR(formatstr,...)
#define JAMI_WARNING(formatstr,...)
#define JAMI_LOG(formatstr,...)
std::vector< uint8_t > loadFile(const std::filesystem::path &path, const std::filesystem::path &default_dir)
Read the full content of a file at path.
std::filesystem::path getFullPath(const std::filesystem::path &base, const std::filesystem::path &path)
If path is relative, it is appended to base.
static constexpr std::string_view toString(AuthDecodingState state)
void emitSignal(Args... args)
static constexpr const char PAYLOAD[]
static constexpr const char RECEIVED[]
static constexpr const char CONVERSATIONID[]
static constexpr const char FROM[]
std::shared_ptr< dht::crypto::PublicKey > device