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