diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml index 1cda603b1163b01eafff8b14341b84f839b0ae28..c48e950162021f807c434570586f2ae533fa57e6 100644 --- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml +++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml @@ -1251,6 +1251,60 @@ </arg> </method> + <method name="addContact" tp:name-for-bindings="addContact"> + <tp:added version="3.0.0"/> + <arg type="s" name="accountID" direction="in"> + </arg> + <arg type="s" name="uri" direction="in"> + </arg> + </method> + + <method name="removeContact" tp:name-for-bindings="removeContact"> + <tp:added version="3.0.0"/> + <arg type="s" name="accountID" direction="in"> + </arg> + <arg type="s" name="uri" direction="in"> + </arg> + </method> + + <method name="getContacts" tp:name-for-bindings="getContacts"> + <tp:added version="3.0.0"/> + <arg type="s" name="accountID" direction="in"> + </arg> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="VectorMapStringString"/> + <arg type="aa{ss}" name="contacts" direction="out" > + <tp:docstring> + The list of contact IDs. + </tp:docstring> + </arg> + </method> + + <signal name="contactAdded" tp:name-for-bindings="contactAdded"> + <tp:added version="2.2.0"/> + <tp:docstring> + Notify clients that a new contact has been added. + </tp:docstring> + <arg type="s" name="accountID"> + </arg> + <arg type="s" name="uri"> + </arg> + <arg type="b" name="confirmed"> + </arg> + </signal> + + <signal name="contactRemoved" tp:name-for-bindings="contactRemoved"> + <tp:added version="2.2.0"/> + <tp:docstring> + Notify clients that a new contact has been removed. + </tp:docstring> + <arg type="s" name="accountID"> + </arg> + <arg type="s" name="uri"> + </arg> + <arg type="b" name="banned"> + </arg> + </signal> + <method name="getAddrFromInterfaceName" tp:name-for-bindings="getAddrFromInterfaceName"> <arg type="s" name="interface" direction="in"> </arg> diff --git a/bin/dbus/dbusclient.cpp b/bin/dbus/dbusclient.cpp index 6c5853a7caa4c196bbc9afbf39428a36d563a59e..5dca85827ad27ec991251b90731daf1b8f2def81 100644 --- a/bin/dbus/dbusclient.cpp +++ b/bin/dbus/dbusclient.cpp @@ -176,6 +176,8 @@ DBusClient::initLibrary(int flags) exportable_callback<ConfigurationSignal::IncomingAccountMessage>(bind(&DBusConfigurationManager::incomingAccountMessage, confM, _1, _2, _3 )), exportable_callback<ConfigurationSignal::AccountMessageStatusChanged>(bind(&DBusConfigurationManager::accountMessageStatusChanged, confM, _1, _2, _3, _4 )), exportable_callback<ConfigurationSignal::IncomingTrustRequest>(bind(&DBusConfigurationManager::incomingTrustRequest, confM, _1, _2, _3, _4 )), + exportable_callback<ConfigurationSignal::ContactAdded>(bind(&DBusConfigurationManager::contactAdded, confM, _1, _2, _3 )), + exportable_callback<ConfigurationSignal::ContactRemoved>(bind(&DBusConfigurationManager::contactRemoved, confM, _1, _2, _3 )), exportable_callback<ConfigurationSignal::ExportOnRingEnded>(bind(&DBusConfigurationManager::exportOnRingEnded, confM, _1, _2, _3 )), exportable_callback<ConfigurationSignal::KnownDevicesChanged>(bind(&DBusConfigurationManager::knownDevicesChanged, confM, _1, _2 )), exportable_callback<ConfigurationSignal::NameRegistrationEnded>(bind(&DBusConfigurationManager::nameRegistrationEnded, confM, _1, _2, _3 )), diff --git a/bin/dbus/dbusconfigurationmanager.cpp b/bin/dbus/dbusconfigurationmanager.cpp index a782e901724f07ffc832e7d6af237b4a5093d4f7..7f65e35c8a2946e0483bfd319af9e7f471130770 100644 --- a/bin/dbus/dbusconfigurationmanager.cpp +++ b/bin/dbus/dbusconfigurationmanager.cpp @@ -511,6 +511,24 @@ DBusConfigurationManager::sendTrustRequest(const std::string& accountId, const s DRing::sendTrustRequest(accountId, to, payload); } +void +DBusConfigurationManager::addContact(const std::string& accountId, const std::string& uri) +{ + DRing::addContact(accountId, uri); +} + +void +DBusConfigurationManager::removeContact(const std::string& accountId, const std::string& uri) +{ + DRing::removeContact(accountId, uri); +} + +auto +DBusConfigurationManager::getContacts(const std::string& accountId) -> decltype(DRing::getContacts(accountId)) +{ + return DRing::getContacts(accountId); +} + auto DBusConfigurationManager::getCredentials(const std::string& accountID) -> decltype(DRing::getCredentials(accountID)) { diff --git a/bin/dbus/dbusconfigurationmanager.h b/bin/dbus/dbusconfigurationmanager.h index 7b6573c7f52ad2b5b45166bd791f42843dc5d799..17b02b4afa1b764c4bae617fec729abca812e306 100644 --- a/bin/dbus/dbusconfigurationmanager.h +++ b/bin/dbus/dbusconfigurationmanager.h @@ -143,6 +143,9 @@ class DBusConfigurationManager : bool acceptTrustRequest(const std::string& accountId, const std::string& from); bool discardTrustRequest(const std::string& accountId, const std::string& from); void sendTrustRequest(const std::string& accountId, const std::string& to, const std::vector<uint8_t>& payload); + void addContact(const std::string& accountId, const std::string& uri); + void removeContact(const std::string& accountId, const std::string& uri); + std::vector<std::map<std::string, std::string>> getContacts(const std::string& accountId); int exportAccounts(const std::vector<std::string>& accountIDs, const std::string& filepath, const std::string& password); int importAccounts(const std::string& archivePath, const std::string& password); void connectivityChanged(); diff --git a/src/client/configurationmanager.cpp b/src/client/configurationmanager.cpp index 36324bf8a8c1ff1f66d7e290480441eeca3f000f..8de368ffafd137f13966d8d6783709e55af3c885 100644 --- a/src/client/configurationmanager.cpp +++ b/src/client/configurationmanager.cpp @@ -304,6 +304,28 @@ getKnownRingDevices(const std::string& accountId) return {}; } +/* contacts */ + +void addContact(const std::string& accountId, const std::string& uri) +{ + if (auto acc = ring::Manager::instance().getAccount<ring::RingAccount>(accountId)) + return acc->addContact(uri); +} + +void removeContact(const std::string& accountId, const std::string& uri) +{ + if (auto acc = ring::Manager::instance().getAccount<ring::RingAccount>(accountId)) + return acc->removeContact(uri); +} + +std::vector<std::map<std::string, std::string>> +getContacts(const std::string& accountId) +{ + if (auto acc = ring::Manager::instance().getAccount<ring::RingAccount>(accountId)) + return acc->getContacts(); + return {}; +} + /* contact requests */ std::map<std::string, std::string> getTrustRequests(const std::string& accountId) diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp index 77f5b65ac7d58046340d57f28fac0cb895ace666..5cbbaecbfea97d99fa0e5e16fdb026c088fdcb9a 100644 --- a/src/client/ring_signal.cpp +++ b/src/client/ring_signal.cpp @@ -63,6 +63,8 @@ getSignalHandlers() exported_callback<DRing::ConfigurationSignal::IncomingAccountMessage>(), exported_callback<DRing::ConfigurationSignal::AccountMessageStatusChanged>(), exported_callback<DRing::ConfigurationSignal::IncomingTrustRequest>(), + exported_callback<DRing::ConfigurationSignal::ContactAdded>(), + exported_callback<DRing::ConfigurationSignal::ContactRemoved>(), exported_callback<DRing::ConfigurationSignal::ExportOnRingEnded>(), exported_callback<DRing::ConfigurationSignal::KnownDevicesChanged>(), exported_callback<DRing::ConfigurationSignal::NameRegistrationEnded>(), diff --git a/src/dring/configurationmanager_interface.h b/src/dring/configurationmanager_interface.h index 02db6f7403c474d2241a3c6a2bffc7fa6befc7aa..502ddb0aea31b37d4a6a6f5d5500d102988b53b6 100644 --- a/src/dring/configurationmanager_interface.h +++ b/src/dring/configurationmanager_interface.h @@ -156,9 +156,14 @@ std::vector<std::string> getCertificatesByStatus(const std::string& account, con std::map<std::string, std::string> getTrustRequests(const std::string& accountId); bool acceptTrustRequest(const std::string& accountId, const std::string& from); bool discardTrustRequest(const std::string& accountId, const std::string& from); - void sendTrustRequest(const std::string& accountId, const std::string& to, const std::vector<uint8_t>& payload = {}); +/* Contacts */ + +void addContact(const std::string& accountId, const std::string& uri); +void removeContact(const std::string& accountId, const std::string& uri); +std::vector<std::map<std::string, std::string>> getContacts(const std::string& accountId); + /* * Import/Export accounts */ @@ -217,6 +222,14 @@ struct ConfigurationSignal { constexpr static const char* name = "IncomingTrustRequest"; using cb_type = void(const std::string& /*account_id*/, const std::string& /*from*/, const std::vector<uint8_t>& payload, time_t received); }; + struct ContactAdded { + constexpr static const char* name = "ContactAdded"; + using cb_type = void(const std::string& /*account_id*/, const std::string& /*uri*/, bool confirmed); + }; + struct ContactRemoved { + constexpr static const char* name = "ContactRemoved"; + using cb_type = void(const std::string& /*account_id*/, const std::string& /*uri*/, bool banned); + }; struct ExportOnRingEnded { constexpr static const char* name = "ExportOnRingEnded"; using cb_type = void(const std::string& /*account_id*/, int state, const std::string& pin); diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp index 0413efc40ae43bd9936132a4a1db1f5961f59406..514ad760c5c3d7d890767ae5b0a509e805f77fdc 100644 --- a/src/ringdht/ringaccount.cpp +++ b/src/ringdht/ringaccount.cpp @@ -131,6 +131,24 @@ RingAccount::TrustRequest { : from_device(sr.device), received(system_clock::from_time_t(sr.received)), payload(std::move(sr.payload)) {} }; +struct RingAccount::Contact +{ + //dht::InfoHash uri; + + /** Time of contact addition */ + time_t added {0}; + + /** Time of contact removal */ + time_t removed {0}; + + /** True if we got confirmation that this contact also added us */ + bool confirmed {false}; + + bool isActive() const { return added > removed; } + + MSGPACK_DEFINE_MAP(added, removed, confirmed) +}; + /** * Represents a known device attached to this Ring account */ @@ -195,9 +213,8 @@ struct RingAccount::DeviceSync : public dht::EncryptedValue<DeviceSync> uint64_t date; std::string device_name; std::map<dht::InfoHash, std::string> devices_known; - std::set<dht::InfoHash> peers_trusted; - std::set<dht::InfoHash> peers_banned; - MSGPACK_DEFINE_MAP(date, device_name, devices_known, peers_trusted, peers_banned) + std::map<dht::InfoHash, Contact> peers; + MSGPACK_DEFINE_MAP(date, device_name, devices_known, peers) }; static constexpr int ICE_COMPONENTS {1}; @@ -829,7 +846,7 @@ RingAccount::hasSignedReceipt() dht::crypto::Identity RingAccount::loadIdentity() { - RING_WARN("loadIdentity() %s %s", tlsCertificateFile_.c_str(), tlsPrivateKeyFile_.c_str()); + RING_DBG("loadIdentity() %s %s", tlsCertificateFile_.c_str(), tlsPrivateKeyFile_.c_str()); dht::crypto::Certificate dht_cert; dht::crypto::PrivateKey dht_key; try { @@ -1085,8 +1102,9 @@ RingAccount::revokeDevice(const std::string& password, const std::string& device auto fa = ThreadPool::instance().getShared<ArchiveContent>( std::bind(&RingAccount::readArchive, this, password) ); + auto sthis = shared(); findCertificate(dht::InfoHash(device), - [fa,sthis=shared(),password](const std::shared_ptr<dht::crypto::Certificate>& crt) mutable + [fa,sthis,password](const std::shared_ptr<dht::crypto::Certificate>& crt) mutable { sthis->foundAccountDevice(crt); auto a = fa->get(); @@ -1339,10 +1357,11 @@ RingAccount::loadAccount(const std::string& archive_password, const std::string& if (registrationState_ == RegistrationState::INITIALIZING) return; - RING_WARN("RingAccount::loadAccount"); + RING_DBG("RingAccount::loadAccount"); try { loadIdentity(); loadKnownDevices(); + loadContacts(); loadTrustRequests(); bool hasArchive = not archivePath_.empty() and fileutils::isFile(archivePath_); @@ -2024,23 +2043,34 @@ RingAccount::doRegister_() } RING_WARN("Got trust request from: %s / %s", peer_account.toString().c_str(), v.from.toString().c_str()); - auto req = this_.trustRequests_.find(peer_account); - if (req == this_.trustRequests_.end()) { - req = this_.trustRequests_.emplace(peer_account, TrustRequest{ - v.from, std::chrono::system_clock::now(), std::move(v.payload) - }).first; + + // Check existing contact + auto contact = this_.contacts_.find(peer_account); + if (contact != this_.contacts_.end()) { + // Contact exists, update confirmation status + contact->second.confirmed = true; + emitSignal<DRing::ConfigurationSignal::ContactAdded>(this_.getAccountID(), peer_account.toString(), true); + this_.syncDevices(); } else { - req->second.from_device = v.from; - req->second.received = std::chrono::system_clock::now(); - req->second.payload = std::move(v.payload); + // Add trust request + auto req = this_.trustRequests_.find(peer_account); + if (req == this_.trustRequests_.end()) { + req = this_.trustRequests_.emplace(peer_account, TrustRequest{ + v.from, std::chrono::system_clock::now(), std::move(v.payload) + }).first; + } else { + req->second.from_device = v.from; + req->second.received = std::chrono::system_clock::now(); + req->second.payload = std::move(v.payload); + } + this_.saveTrustRequests(); + emitSignal<DRing::ConfigurationSignal::IncomingTrustRequest>( + this_.getAccountID(), + req->first.toString(), + req->second.payload, + std::chrono::system_clock::to_time_t(req->second.received) + ); } - this_.saveTrustRequests(); - emitSignal<DRing::ConfigurationSignal::IncomingTrustRequest>( - this_.getAccountID(), - req->first.toString(), - req->second.payload, - std::chrono::system_clock::to_time_t(req->second.received) - ); }); return true; } @@ -2225,6 +2255,8 @@ RingAccount::foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& cr return false; } + RING_WARN("foundPeerDevice dev:%s CA:%s", crt->getId().toString().c_str(), top_issuer->getId().toString().c_str()); + // Check peer certificate chain // Trust store with top issuer as the only CA tls::TrustStore peer_trust; @@ -2691,7 +2723,101 @@ RingAccount::getContactHeader(pjsip_transport* t) return contact_; } +/* contacts */ + +void +RingAccount::addContact(const std::string& uri) +{ + dht::InfoHash h (uri); + auto c = contacts_.find(h); + if (c == contacts_.end()) + c = contacts_.emplace(h, Contact{}).first; + c->second.added = std::time(nullptr); + trust_.setCertificateStatus(uri, tls::TrustStore::PermissionStatus::ALLOWED); + saveContacts(); + emitSignal<DRing::ConfigurationSignal::ContactAdded>(getAccountID(), uri, c->second.confirmed); + syncDevices(); +} + +void +RingAccount::removeContact(const std::string& uri) +{ + dht::InfoHash h (uri); + auto c = contacts_.find(h); + if (c != contacts_.end()) { + c->second.removed = std::time(nullptr); + trust_.setCertificateStatus(uri, tls::TrustStore::PermissionStatus::BANNED); + saveContacts(); + emitSignal<DRing::ConfigurationSignal::ContactRemoved>(getAccountID(), uri, c->second.confirmed); + syncDevices(); + } +} + +std::vector<std::map<std::string, std::string>> +RingAccount::getContacts() const +{ + std::vector<std::map<std::string, std::string>> ret; + ret.reserve(contacts_.size()); + for (const auto& c : contacts_) { + std::map<std::string, std::string> cm { + {"id", c.first.toString()}, + {"added", std::to_string(c.second.added)} + }; + if (c.second.isActive()) + cm.emplace("confirmed", c.second.confirmed ? TRUE_STR : FALSE_STR); + else + cm.emplace("banned", TRUE_STR); + ret.emplace_back(std::move(cm)); + } + return ret; +} + +void +RingAccount::updateContact(const dht::InfoHash& id, const Contact& contact) +{ + auto c = contacts_.find(id); + if (c == contacts_.end()) { + c = contacts_.emplace(id, contact).first; + emitSignal<DRing::ConfigurationSignal::ContactAdded>(getAccountID(), id.toString(), c->second.confirmed); + } else { + c->second.added = std::max(contact.added, c->second.added); + c->second.removed = std::max(contact.removed, c->second.removed); + if (contact.confirmed != c->second.confirmed) { + c->second.confirmed = std::max(contact.confirmed, c->second.confirmed); + emitSignal<DRing::ConfigurationSignal::ContactAdded>(getAccountID(), id.toString(), c->second.confirmed); + } + } + trust_.setCertificateStatus(id.toString(), c->second.isActive() ? tls::TrustStore::PermissionStatus::ALLOWED : tls::TrustStore::PermissionStatus::BANNED); +} + +void +RingAccount::loadContacts() +{ + decltype(contacts_) contacts; + try { + // read file + auto file = fileutils::loadFile(idPath_+DIR_SEPARATOR_STR "contacts"); + // load values + msgpack::object_handle oh = msgpack::unpack((const char*)file.data(), file.size()); + oh.get().convert(contacts); + } catch (const std::exception& e) { + RING_WARN("Error loading contacts: %s", e.what()); + return; + } + + for (auto& peer : contacts) + updateContact(peer.first, peer.second); +} + +void +RingAccount::saveContacts() const +{ + std::ofstream file(idPath_+DIR_SEPARATOR_STR "contacts", std::ios::trunc); + msgpack::pack(file, contacts_); +} + /* trust requests */ + std::map<std::string, std::string> RingAccount::getTrustRequests() const { @@ -2707,7 +2833,7 @@ RingAccount::acceptTrustRequest(const std::string& from) dht::InfoHash f(from); auto i = trustRequests_.find(f); if (i != trustRequests_.end()) { - trust_.setCertificateStatus(from, tls::TrustStore::PermissionStatus::ALLOWED); + addContact(from); trustRequests_.erase(i); saveTrustRequests(); return true; @@ -2729,7 +2855,7 @@ RingAccount::discardTrustRequest(const std::string& from) void RingAccount::sendTrustRequest(const std::string& to, const std::vector<uint8_t>& payload) { - setCertificateStatus(to, tls::TrustStore::PermissionStatus::ALLOWED); + addContact(to); auto toH = dht::InfoHash(to); forEachDevice(toH, [toH,payload](const std::shared_ptr<RingAccount>& shared, const dht::InfoHash& dev) { @@ -2741,7 +2867,7 @@ RingAccount::sendTrustRequest(const std::string& to, const std::vector<uint8_t>& } void -RingAccount::saveTrustRequests() +RingAccount::saveTrustRequests() const { std::ofstream file(idPath_+DIR_SEPARATOR_STR "incomingTrustRequests", std::ios::trunc); @@ -2779,12 +2905,15 @@ RingAccount::loadTrustRequests() } } +/* sync */ + void RingAccount::syncDevices() { DeviceSync sync_data; sync_data.date = std::chrono::system_clock::now().time_since_epoch().count(); sync_data.device_name = ringDeviceName_; + sync_data.peers = contacts_; RING_WARN("Building device sync for %s %s", ringDeviceName_.c_str(), ringDeviceId_.c_str()); for (const auto& dev : knownDevices_) { if (dev.first.toString() == ringDeviceId_) @@ -2816,7 +2945,9 @@ RingAccount::onReceiveDeviceSync(DeviceSync&& sync) RING_DBG("onReceiveDeviceSync: dropping outdated sync data"); return; } - RING_WARN("Received device sync data %zu", sync.devices_known.size()); + + // Sync known devices + RING_WARN("Received device sync data %lu", sync.devices_known.size()); for (const auto& d : sync.devices_known) { auto shared = std::static_pointer_cast<RingAccount>(shared_from_this()); findCertificate(d.first, [shared,d](const std::shared_ptr<dht::crypto::Certificate>& crt) { @@ -2825,8 +2956,14 @@ RingAccount::onReceiveDeviceSync(DeviceSync&& sync) shared->foundAccountDevice(crt, d.second); }); } - it->second.last_sync = sync_date; saveKnownDevices(); + + // Sync contacts + for (const auto& peer : sync.peers) + updateContact(peer.first, peer.second); + saveContacts(); + + it->second.last_sync = sync_date; } void diff --git a/src/ringdht/ringaccount.h b/src/ringdht/ringaccount.h index 768faea65ee8b42db91619186950e2854b0b41d6..86edc17f5e8c60547d07986976831eb515b4db6f 100644 --- a/src/ringdht/ringaccount.h +++ b/src/ringdht/ringaccount.h @@ -290,6 +290,10 @@ class RingAccount : public SIPAccountBase { bool acceptTrustRequest(const std::string& from); bool discardTrustRequest(const std::string& from); + void addContact(const std::string& uri); + void removeContact(const std::string& uri); + std::vector<std::map<std::string, std::string>> getContacts() const; + void sendTrustRequest(const std::string& to, const std::vector<uint8_t>& payload); virtual void sendTextMessage(const std::string& to, const std::map<std::string, std::string>& payloads, uint64_t id) override; @@ -325,6 +329,7 @@ class RingAccount : public SIPAccountBase { struct DeviceAnnouncement; struct DeviceSync; struct BuddyInfo; + struct Contact; void syncDevices(); void onReceiveDeviceSync(DeviceSync&& sync); @@ -449,7 +454,12 @@ class RingAccount : public SIPAccountBase { std::map<dht::InfoHash, TrustRequest> trustRequests_; void loadTrustRequests(); - void saveTrustRequests(); + void saveTrustRequests() const; + + std::map<dht::InfoHash, Contact> contacts_; + void loadContacts(); + void saveContacts() const; + void updateContact(const dht::InfoHash&, const Contact&); tls::TrustStore trust_;