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