Line data Source code
1 : /*
2 : * Copyright (C) 2004-2025 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 791 : ContactList::ContactList(const std::string& accountId,
35 : const std::shared_ptr<crypto::Certificate>& cert,
36 : const std::filesystem::path& path,
37 791 : OnChangeCallback cb)
38 791 : : accountId_(accountId)
39 791 : , path_(path)
40 791 : , callbacks_(std::move(cb))
41 : {
42 791 : if (cert) {
43 791 : trust_ = std::make_unique<dhtnet::tls::TrustStore>(jami::Manager::instance().certStore(accountId_));
44 789 : accountTrust_.add(*cert);
45 : }
46 809 : }
47 :
48 789 : 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 170 : ContactList::addContact(const dht::InfoHash& h, bool confirmed, const std::string& conversationId)
89 : {
90 170 : std::unique_lock lk(mutex_);
91 680 : JAMI_WARNING("[Account {}] [Contacts] addContact: {}, conversation: {}", accountId_, h, conversationId);
92 170 : auto c = contacts_.find(h);
93 170 : if (c == contacts_.end())
94 100 : c = contacts_.emplace(h, Contact {}).first;
95 70 : else if (c->second.isActive() and c->second.confirmed == confirmed && c->second.conversationId == conversationId)
96 62 : 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 170 : }
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 8 : ContactList::getContactDetails(const dht::InfoHash& h) const
170 : {
171 8 : std::unique_lock lk(mutex_);
172 8 : const auto c = contacts_.find(h);
173 8 : if (c == std::end(contacts_)) {
174 0 : JAMI_WARNING("[Account {}] [Contacts] Contact '{}' not found", accountId_, h.to_view());
175 0 : return {};
176 : }
177 :
178 8 : auto details = c->second.toMap();
179 8 : if (not details.empty())
180 8 : details["id"] = c->first.toString();
181 :
182 8 : return details;
183 8 : }
184 :
185 : std::optional<Contact>
186 616 : ContactList::getContactInfo(const dht::InfoHash& h) const
187 : {
188 616 : const auto c = contacts_.find(h);
189 616 : if (c == std::end(contacts_)) {
190 1248 : JAMI_WARNING("[Account {}] [Contacts] Contact '{}' not found", accountId_, h.to_view());
191 312 : return {};
192 : }
193 304 : return c->second;
194 : }
195 :
196 : const std::map<dht::InfoHash, Contact>&
197 1727 : ContactList::getContacts() const
198 : {
199 1727 : return contacts_;
200 : }
201 :
202 : void
203 772 : ContactList::setContacts(const std::map<dht::InfoHash, Contact>& contacts)
204 : {
205 3088 : JAMI_LOG("[Account {}] [Contacts] replacing contact list (old: {} new: {})",
206 : accountId_,
207 : contacts_.size(),
208 : contacts.size());
209 772 : contacts_ = contacts;
210 772 : saveContacts();
211 : // Set contacts is used when creating a new device, so just announce new contacts
212 775 : for (auto& peer : contacts)
213 3 : if (peer.second.isActive())
214 3 : callbacks_.contactAdded(peer.first.toString(), peer.second.confirmed);
215 772 : }
216 :
217 : void
218 64 : ContactList::updateContact(const dht::InfoHash& id, const Contact& contact, bool emit)
219 : {
220 64 : if (not id) {
221 0 : JAMI_ERROR("[Account {}] [Contacts] updateContact: invalid contact ID", accountId_);
222 0 : return;
223 : }
224 64 : bool stateChanged {false};
225 64 : auto c = contacts_.find(id);
226 64 : 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 51 : stateChanged = c->second.update(contact);
233 : }
234 64 : if (stateChanged) {
235 : {
236 17 : std::lock_guard lk(mutex_);
237 17 : if (trustRequests_.erase(id) > 0)
238 8 : 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 : void
254 16 : ContactList::loadContacts()
255 : {
256 16 : decltype(contacts_) contacts;
257 : try {
258 32 : std::lock_guard fileLock(dhtnet::fileutils::getFileLock(path_ / "contacts"));
259 : // read file
260 16 : auto file = fileutils::loadFile("contacts", path_);
261 : // load values
262 16 : msgpack::object_handle oh = msgpack::unpack((const char*) file.data(), file.size());
263 16 : oh.get().convert(contacts);
264 16 : } catch (const std::exception& e) {
265 0 : JAMI_WARNING("[Account {}] [Contacts] Error loading contacts: {}", accountId_, e.what());
266 0 : return;
267 0 : }
268 :
269 64 : JAMI_WARNING("[Account {}] [Contacts] Loaded {} contacts", accountId_, contacts.size());
270 16 : for (auto& peer : contacts)
271 0 : updateContact(peer.first, peer.second, false);
272 16 : }
273 :
274 : void
275 1013 : ContactList::saveContacts() const
276 : {
277 4052 : JAMI_LOG("[Account {}] [Contacts] saving {} contacts", accountId_, contacts_.size());
278 2026 : std::lock_guard fileLock(dhtnet::fileutils::getFileLock(path_ / "contacts"));
279 2026 : std::ofstream file(path_ / "contacts", std::ios::trunc | std::ios::binary);
280 1013 : msgpack::pack(file, contacts_);
281 1013 : }
282 :
283 : void
284 128 : ContactList::saveTrustRequests() const
285 : {
286 : // mutex_ MUST BE locked
287 256 : std::ofstream file(path_ / "incomingTrustRequests", std::ios::trunc | std::ios::binary);
288 128 : msgpack::pack(file, trustRequests_);
289 128 : }
290 :
291 : void
292 16 : ContactList::loadTrustRequests()
293 : {
294 16 : if (!std::filesystem::is_regular_file(fileutils::getFullPath(path_, "incomingTrustRequests")))
295 16 : return;
296 0 : std::map<dht::InfoHash, TrustRequest> requests;
297 : try {
298 : // read file
299 0 : auto file = fileutils::loadFile("incomingTrustRequests", path_);
300 : // load values
301 0 : msgpack::object_handle oh = msgpack::unpack((const char*) file.data(), file.size());
302 0 : oh.get().convert(requests);
303 0 : } catch (const std::exception& e) {
304 0 : JAMI_WARNING("[Account {}] [Contacts] Error loading trust requests: {}", accountId_, e.what());
305 0 : return;
306 0 : }
307 :
308 0 : JAMI_WARNING("[Account {}] [Contacts] Loaded {} contact requests", accountId_, requests.size());
309 0 : for (auto& tr : requests)
310 0 : onTrustRequest(tr.first,
311 0 : tr.second.device,
312 : tr.second.received,
313 : false,
314 0 : tr.second.conversationId,
315 0 : std::move(tr.second.payload));
316 0 : }
317 :
318 : bool
319 122 : ContactList::onTrustRequest(const dht::InfoHash& peer_account,
320 : const std::shared_ptr<dht::crypto::PublicKey>& peer_device,
321 : time_t received,
322 : bool confirm,
323 : const std::string& conversationId,
324 : std::vector<uint8_t>&& payload)
325 : {
326 122 : bool accept = false;
327 : // Check existing contact
328 122 : std::unique_lock lk(mutex_);
329 122 : auto contact = contacts_.find(peer_account);
330 122 : bool active = false;
331 122 : if (contact != contacts_.end()) {
332 : // Banned contact: discard request
333 55 : if (contact->second.isBanned())
334 0 : return false;
335 :
336 55 : if (contact->second.isActive()) {
337 50 : active = true;
338 : // Send confirmation
339 50 : if (not confirm)
340 12 : accept = true;
341 50 : if (not contact->second.confirmed) {
342 38 : contact->second.confirmed = true;
343 38 : saveContacts();
344 38 : callbacks_.contactAdded(peer_account.toString(), true);
345 : }
346 : }
347 : }
348 122 : if (not active) {
349 72 : auto req = trustRequests_.find(peer_account);
350 72 : if (req == trustRequests_.end()) {
351 : // Add trust request
352 67 : req = trustRequests_.emplace(peer_account, TrustRequest {peer_device, conversationId, received, payload})
353 : .first;
354 : } else {
355 : // Update trust request
356 5 : if (received > req->second.received) {
357 1 : req->second.device = peer_device;
358 1 : req->second.conversationId = conversationId;
359 1 : req->second.received = received;
360 1 : req->second.payload = payload;
361 : } else {
362 16 : JAMI_LOG("[Account {}] [Contacts] Ignoring outdated trust request from {}", accountId_, peer_account);
363 : }
364 : }
365 72 : saveTrustRequests();
366 : }
367 122 : lk.unlock();
368 : // Note: call JamiAccount's callback to build ConversationRequest anyway
369 122 : if (!confirm)
370 83 : callbacks_.trustRequest(peer_account.toString(), conversationId, std::move(payload), received);
371 39 : else if (active) {
372 : // Only notify if confirmed + not removed
373 38 : callbacks_.onConfirmation(peer_account.toString(), conversationId);
374 : }
375 122 : return accept;
376 122 : }
377 :
378 : /* trust requests */
379 :
380 : std::vector<std::map<std::string, std::string>>
381 700 : ContactList::getTrustRequests() const
382 : {
383 : using Map = std::map<std::string, std::string>;
384 700 : std::vector<Map> ret;
385 700 : std::lock_guard lk(mutex_);
386 700 : ret.reserve(trustRequests_.size());
387 716 : for (const auto& r : trustRequests_) {
388 144 : ret.emplace_back(Map {{libjami::Account::TrustRequest::FROM, r.first.toString()},
389 32 : {libjami::Account::TrustRequest::RECEIVED, std::to_string(r.second.received)},
390 16 : {libjami::Account::TrustRequest::CONVERSATIONID, r.second.conversationId},
391 : {libjami::Account::TrustRequest::PAYLOAD,
392 112 : std::string(r.second.payload.begin(), r.second.payload.end())}});
393 : }
394 1400 : return ret;
395 700 : }
396 :
397 : std::map<std::string, std::string>
398 207 : ContactList::getTrustRequest(const dht::InfoHash& from) const
399 : {
400 : using Map = std::map<std::string, std::string>;
401 207 : std::lock_guard lk(mutex_);
402 207 : auto r = trustRequests_.find(from);
403 207 : if (r == trustRequests_.end())
404 164 : return {};
405 86 : return Map {{libjami::Account::TrustRequest::FROM, r->first.toString()},
406 86 : {libjami::Account::TrustRequest::RECEIVED, std::to_string(r->second.received)},
407 43 : {libjami::Account::TrustRequest::CONVERSATIONID, r->second.conversationId},
408 : {libjami::Account::TrustRequest::PAYLOAD,
409 301 : std::string(r->second.payload.begin(), r->second.payload.end())}};
410 207 : }
411 :
412 : bool
413 193 : ContactList::acceptTrustRequest(const dht::InfoHash& from)
414 : {
415 : // The contact sent us a TR so we are in its contact list
416 193 : std::unique_lock lk(mutex_);
417 193 : auto i = trustRequests_.find(from);
418 193 : if (i == trustRequests_.end())
419 151 : return false;
420 42 : auto convId = i->second.conversationId;
421 : // Clear trust request
422 42 : trustRequests_.erase(i);
423 42 : saveTrustRequests();
424 42 : lk.unlock();
425 42 : addContact(from, true, convId);
426 42 : return true;
427 193 : }
428 :
429 : void
430 53 : ContactList::acceptConversation(const std::string& convId, const std::string& deviceId)
431 : {
432 53 : if (callbacks_.acceptConversation)
433 53 : callbacks_.acceptConversation(convId, deviceId);
434 53 : }
435 :
436 : bool
437 3 : ContactList::discardTrustRequest(const dht::InfoHash& from)
438 : {
439 3 : std::lock_guard lk(mutex_);
440 3 : if (trustRequests_.erase(from) > 0) {
441 3 : saveTrustRequests();
442 3 : return true;
443 : }
444 0 : return false;
445 3 : }
446 :
447 : void
448 16 : ContactList::loadKnownDevices()
449 : {
450 16 : auto& certStore = jami::Manager::instance().certStore(accountId_);
451 : try {
452 : // read file
453 16 : auto file = fileutils::loadFile("knownDevices", path_);
454 : // load values
455 16 : msgpack::object_handle oh = msgpack::unpack((const char*) file.data(), file.size());
456 :
457 16 : std::map<dht::PkId, std::pair<std::string, uint64_t>> knownDevices;
458 16 : oh.get().convert(knownDevices);
459 32 : for (const auto& d : knownDevices) {
460 32 : if (auto crt = certStore.getCertificate(d.first.toString())) {
461 16 : if (not foundAccountDevice(crt, d.second.first, clock::from_time_t(d.second.second), false))
462 0 : JAMI_WARNING("[Account {}] [Contacts] Unable to add device {}", accountId_, d.first);
463 : } else {
464 0 : JAMI_WARNING("[Account {}] [Contacts] Unable to find certificate for device {}", accountId_, d.first);
465 16 : }
466 : }
467 16 : if (not knownDevices.empty()) {
468 16 : callbacks_.devicesChanged(knownDevices_);
469 : }
470 16 : } catch (const std::exception& e) {
471 0 : JAMI_WARNING("[Account {}] [Contacts] Error loading devices: {}", accountId_, e.what());
472 0 : return;
473 0 : }
474 : }
475 :
476 : void
477 2910 : ContactList::saveKnownDevices() const
478 : {
479 5820 : std::ofstream file(path_ / "knownDevices", std::ios::trunc | std::ios::binary);
480 :
481 2910 : std::map<dht::PkId, std::pair<std::string, uint64_t>> devices;
482 1010955 : for (const auto& id : knownDevices_) {
483 1008045 : devices.emplace(id.first, std::make_pair(id.second.name, clock::to_time_t(id.second.last_sync)));
484 : }
485 :
486 2910 : msgpack::pack(file, devices);
487 2910 : }
488 :
489 : void
490 2000 : ContactList::foundAccountDevice(const dht::PkId& device, const std::string& name, const time_point& updated)
491 : {
492 : // insert device
493 2000 : auto it = knownDevices_.emplace(device, KnownDevice {{}, name, updated});
494 2000 : if (it.second) {
495 8000 : JAMI_LOG("[Account {}] [Contacts] Found account device: {} {}", accountId_, name, device);
496 2000 : saveKnownDevices();
497 2000 : callbacks_.devicesChanged(knownDevices_);
498 : } else {
499 : // update device name
500 0 : if (not name.empty() and it.first->second.name != name) {
501 0 : JAMI_LOG("[Account {}] [Contacts] Updating device name: {} {}", accountId_, name, device);
502 0 : it.first->second.name = name;
503 0 : saveKnownDevices();
504 0 : callbacks_.devicesChanged(knownDevices_);
505 : }
506 : }
507 2000 : }
508 :
509 : bool
510 1936 : ContactList::foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
511 : const std::string& name,
512 : const time_point& updated,
513 : bool notify)
514 : {
515 1936 : if (not crt)
516 0 : return false;
517 :
518 1936 : auto id = crt->getLongId();
519 :
520 : // match certificate chain
521 1936 : auto verifyResult = accountTrust_.verify(*crt);
522 1937 : if (not verifyResult) {
523 0 : JAMI_WARNING("[Account {}] [Contacts] Found invalid account device: {:s}: {:s}",
524 : accountId_,
525 : id,
526 : verifyResult.toString());
527 0 : return false;
528 : }
529 :
530 : // insert device
531 1937 : auto it = knownDevices_.emplace(id, KnownDevice {crt, name, updated});
532 1937 : if (it.second) {
533 3444 : JAMI_LOG("[Account {}] [Contacts] Found account device: {} {}", accountId_, name, id);
534 861 : jami::Manager::instance().certStore(accountId_).pinCertificate(crt);
535 861 : if (crt->ocspResponse) {
536 0 : unsigned int status = crt->ocspResponse->getCertificateStatus();
537 0 : if (status == GNUTLS_OCSP_CERT_REVOKED) {
538 0 : JAMI_ERROR("[Account {}] Certificate {} has revoked OCSP status", accountId_, id);
539 0 : trust_->setCertificateStatus(crt, dhtnet::tls::TrustStore::PermissionStatus::BANNED, false);
540 : }
541 : }
542 861 : if (notify) {
543 845 : saveKnownDevices();
544 845 : callbacks_.devicesChanged(knownDevices_);
545 : }
546 : } else {
547 : // update device name
548 1076 : if (not name.empty() and it.first->second.name != name) {
549 248 : JAMI_LOG("[Account {}] [Contacts] updating device name: {} {}", accountId_, name, id);
550 62 : it.first->second.name = name;
551 62 : if (notify) {
552 62 : saveKnownDevices();
553 62 : callbacks_.devicesChanged(knownDevices_);
554 : }
555 : }
556 : }
557 1937 : return true;
558 : }
559 :
560 : bool
561 2 : ContactList::removeAccountDevice(const dht::PkId& device)
562 : {
563 2 : if (knownDevices_.erase(device) > 0) {
564 2 : saveKnownDevices();
565 2 : return true;
566 : }
567 0 : return false;
568 : }
569 :
570 : void
571 16 : ContactList::setAccountDeviceName(const dht::PkId& device, const std::string& name)
572 : {
573 16 : auto dev = knownDevices_.find(device);
574 16 : if (dev != knownDevices_.end()) {
575 16 : if (dev->second.name != name) {
576 1 : dev->second.name = name;
577 1 : saveKnownDevices();
578 1 : callbacks_.devicesChanged(knownDevices_);
579 : }
580 : }
581 16 : }
582 :
583 : std::string
584 772 : ContactList::getAccountDeviceName(const dht::PkId& device) const
585 : {
586 772 : auto dev = knownDevices_.find(device);
587 772 : if (dev != knownDevices_.end()) {
588 772 : return dev->second.name;
589 : }
590 0 : return {};
591 : }
592 :
593 : DeviceSync
594 1000 : ContactList::getSyncData() const
595 : {
596 1000 : DeviceSync sync_data;
597 1000 : sync_data.date = clock::now().time_since_epoch().count();
598 : // sync_data.device_name = deviceName_;
599 1001 : sync_data.peers = getContacts();
600 :
601 : static constexpr size_t MAX_TRUST_REQUESTS = 20;
602 1000 : std::lock_guard lk(mutex_);
603 1001 : if (trustRequests_.size() <= MAX_TRUST_REQUESTS)
604 1013 : for (const auto& req : trustRequests_)
605 : sync_data.trust_requests
606 13 : .emplace(req.first,
607 26 : TrustRequest {req.second.device, req.second.conversationId, req.second.received, {}});
608 : else {
609 0 : size_t inserted = 0;
610 0 : auto req = trustRequests_.lower_bound(dht::InfoHash::getRandom());
611 0 : while (inserted++ < MAX_TRUST_REQUESTS) {
612 0 : if (req == trustRequests_.end())
613 0 : req = trustRequests_.begin();
614 : sync_data.trust_requests
615 0 : .emplace(req->first,
616 0 : TrustRequest {req->second.device, req->second.conversationId, req->second.received, {}});
617 0 : ++req;
618 : }
619 : }
620 :
621 8201 : for (const auto& dev : knownDevices_) {
622 7201 : if (!dev.second.certificate) {
623 24000 : JAMI_WARNING("[Account {}] [Contacts] No certificate found for {}", accountId_, dev.first);
624 6000 : continue;
625 6000 : }
626 1201 : sync_data.devices.emplace(dev.second.certificate->getLongId(), KnownDeviceSync {dev.second.name});
627 : }
628 2002 : return sync_data;
629 1001 : }
630 :
631 : bool
632 0 : ContactList::syncDevice(const dht::PkId& device, const time_point& syncDate)
633 : {
634 0 : auto it = knownDevices_.find(device);
635 0 : if (it == knownDevices_.end()) {
636 0 : JAMI_WARNING("[Account {}] [Contacts] Dropping sync data from unknown device", accountId_);
637 0 : return false;
638 : }
639 0 : if (it->second.last_sync >= syncDate) {
640 0 : JAMI_LOG("[Account {}] [Contacts] Dropping outdated sync data", accountId_);
641 0 : return false;
642 : }
643 0 : it->second.last_sync = syncDate;
644 0 : return true;
645 : }
646 :
647 : } // namespace jami
|