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 : 18 : #include "presence_manager.h" 19 : #include "jami_contact.h" // For DeviceAnnouncement 20 : #include "logger.h" 21 : 22 : namespace jami { 23 : 24 684 : PresenceManager::PresenceManager(const std::shared_ptr<dht::DhtRunner>& dht) 25 684 : : dht_(dht) 26 684 : {} 27 : 28 684 : PresenceManager::~PresenceManager() 29 : { 30 684 : std::lock_guard lock(mutex_); 31 1020 : for (auto& [h, buddy] : trackedBuddies_) { 32 336 : if (dht_ && dht_->isRunning()) { 33 0 : dht_->cancelListen(h, std::move(buddy.listenToken)); 34 : } 35 : } 36 684 : } 37 : 38 : void 39 832 : PresenceManager::trackBuddy(const std::string& uri) 40 : { 41 832 : dht::InfoHash h(uri); 42 832 : if (!h) 43 0 : return; 44 : 45 832 : std::lock_guard lock(mutex_); 46 832 : auto it = trackedBuddies_.find(h); 47 832 : if (it == trackedBuddies_.end()) { 48 799 : it = trackedBuddies_.emplace(h, TrackedBuddy {h}).first; 49 : } 50 : 51 832 : it->second.refCount++; 52 832 : if (it->second.refCount == 1) { 53 799 : trackPresence(h, it->second); 54 : } 55 832 : } 56 : 57 : void 58 482 : PresenceManager::untrackBuddy(const std::string& uri) 59 : { 60 482 : dht::InfoHash h(uri); 61 482 : if (!h) 62 0 : return; 63 : 64 482 : std::lock_guard lock(mutex_); 65 482 : auto it = trackedBuddies_.find(h); 66 482 : if (it != trackedBuddies_.end()) { 67 482 : it->second.refCount--; 68 482 : if (it->second.refCount <= 0) { 69 463 : if (dht_ && dht_->isRunning()) { 70 457 : dht_->cancelListen(h, std::move(it->second.listenToken)); 71 : } 72 463 : trackedBuddies_.erase(it); 73 : } 74 : } 75 482 : } 76 : 77 : bool 78 2199 : PresenceManager::isOnline(const std::string& uri) const 79 : { 80 2199 : dht::InfoHash h(uri); 81 2199 : if (!h) 82 0 : return false; 83 2199 : std::lock_guard lock(mutex_); 84 2199 : auto it = trackedBuddies_.find(h); 85 2199 : return it != trackedBuddies_.end() && !it->second.devices.empty(); 86 2199 : } 87 : 88 : std::map<std::string, bool> 89 2 : PresenceManager::getTrackedBuddyPresence() const 90 : { 91 2 : std::lock_guard lock(mutex_); 92 2 : std::map<std::string, bool> presence_info; 93 4 : for (const auto& [h, buddy] : trackedBuddies_) { 94 2 : presence_info.emplace(h.toString(), !buddy.devices.empty()); 95 : } 96 4 : return presence_info; 97 2 : } 98 : 99 : std::vector<dht::PkId> 100 1031 : PresenceManager::getDevices(const std::string& uri) const 101 : { 102 1031 : dht::InfoHash h(uri); 103 1031 : if (!h) 104 0 : return {}; 105 1031 : std::lock_guard lock(mutex_); 106 1031 : auto it = trackedBuddies_.find(h); 107 1031 : if (it == trackedBuddies_.end()) 108 221 : return {}; 109 810 : return {it->second.devices.begin(), it->second.devices.end()}; 110 1031 : } 111 : 112 : uint64_t 113 699 : PresenceManager::addListener(PresenceCallback cb) 114 : { 115 699 : std::lock_guard lock(listenersMutex_); 116 699 : auto id = nextListenerId_++; 117 699 : listeners_.emplace(id, std::move(cb)); 118 699 : return id; 119 699 : } 120 : 121 : void 122 0 : PresenceManager::removeListener(uint64_t token) 123 : { 124 0 : std::lock_guard lock(listenersMutex_); 125 0 : listeners_.erase(token); 126 0 : } 127 : 128 : uint64_t 129 401 : PresenceManager::addDeviceListener(DevicePresenceCallback cb) 130 : { 131 401 : std::lock_guard lock(listenersMutex_); 132 401 : auto id = nextListenerId_++; 133 401 : deviceListeners_.emplace(id, std::move(cb)); 134 401 : return id; 135 401 : } 136 : 137 : void 138 191 : PresenceManager::removeDeviceListener(uint64_t token) 139 : { 140 191 : std::lock_guard lock(listenersMutex_); 141 191 : deviceListeners_.erase(token); 142 191 : } 143 : 144 : void 145 600 : PresenceManager::refresh() 146 : { 147 600 : std::lock_guard lock(mutex_); 148 612 : for (auto& [h, buddy] : trackedBuddies_) { 149 12 : buddy.listenToken = {}; 150 12 : buddy.devices.clear(); 151 12 : trackPresence(h, buddy); 152 : } 153 600 : } 154 : 155 : void 156 811 : PresenceManager::trackPresence(const dht::InfoHash& h, TrackedBuddy& buddy) 157 : { 158 811 : if (!dht_ || !dht_->isRunning()) 159 15 : return; 160 : 161 796 : if (buddy.listenToken.valid()) { 162 0 : JAMI_ERROR("PresenceManager: Already tracking presence for {}", h.toString()); 163 0 : return; 164 : } 165 : 166 1592 : buddy.listenToken = dht_->listen<DeviceAnnouncement>(h, [this, h](DeviceAnnouncement&& dev, bool expired) { 167 743 : if (!dev.pk) { 168 0 : JAMI_WARNING("PresenceManager: Received DeviceAnnouncement without public key for {}", h.toString()); 169 0 : return true; 170 : } 171 : bool wasConnected, isConnected; 172 743 : auto deviceId = dev.pk->getLongId(); 173 743 : bool deviceOnline = !expired; 174 : { 175 743 : std::lock_guard lock(mutex_); 176 743 : auto it = trackedBuddies_.find(h); 177 743 : if (it == trackedBuddies_.end()) 178 13 : return true; 179 : 180 730 : wasConnected = !it->second.devices.empty(); 181 730 : if (expired) { 182 78 : it->second.devices.erase(deviceId); 183 : } else { 184 652 : it->second.devices.insert(deviceId); 185 : } 186 730 : isConnected = !it->second.devices.empty(); 187 743 : } 188 : 189 730 : notifyDeviceListeners(h.toString(), deviceId, deviceOnline); 190 : 191 730 : if (isConnected != wasConnected) { 192 644 : notifyListeners(h.toString(), isConnected); 193 : } 194 730 : return true; 195 796 : }); 196 : } 197 : 198 : void 199 644 : PresenceManager::notifyListeners(const std::string& uri, bool online) 200 : { 201 644 : std::vector<PresenceCallback> cbs; 202 : { 203 644 : std::lock_guard lock(listenersMutex_); 204 644 : cbs.reserve(listeners_.size()); 205 1293 : for (const auto& [id, cb] : listeners_) { 206 649 : cbs.emplace_back(cb); 207 : } 208 644 : } 209 1293 : for (const auto& cb : cbs) { 210 649 : cb(uri, online); 211 : } 212 644 : } 213 : 214 : void 215 730 : PresenceManager::notifyDeviceListeners(const std::string& uri, const dht::PkId& deviceId, bool online) 216 : { 217 730 : std::vector<DevicePresenceCallback> cbs; 218 : { 219 730 : std::lock_guard lock(listenersMutex_); 220 730 : cbs.reserve(deviceListeners_.size()); 221 1452 : for (const auto& [id, cb] : deviceListeners_) { 222 722 : cbs.emplace_back(cb); 223 : } 224 730 : } 225 1452 : for (const auto& cb : cbs) { 226 722 : cb(uri, deviceId, online); 227 : } 228 730 : } 229 : 230 : } // namespace jami