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 778 : PresenceManager::PresenceManager(const std::shared_ptr<dht::DhtRunner>& dht)
25 778 : : dht_(dht)
26 778 : {}
27 :
28 778 : PresenceManager::~PresenceManager()
29 : {
30 778 : std::lock_guard lock(mutex_);
31 1071 : for (auto& [h, buddy] : trackedBuddies_) {
32 293 : if (dht_ && dht_->isRunning()) {
33 0 : dht_->cancelListen(h, std::move(buddy.listenToken));
34 : }
35 : }
36 778 : }
37 :
38 : void
39 831 : PresenceManager::trackBuddy(const std::string& uri)
40 : {
41 831 : dht::InfoHash h(uri);
42 831 : if (!h)
43 0 : return;
44 :
45 831 : std::lock_guard lock(mutex_);
46 831 : auto it = trackedBuddies_.find(h);
47 831 : if (it == trackedBuddies_.end()) {
48 803 : it = trackedBuddies_.emplace(h, TrackedBuddy {h}).first;
49 : }
50 :
51 831 : it->second.refCount++;
52 831 : if (it->second.refCount == 1) {
53 803 : trackPresence(h, it->second);
54 : }
55 831 : }
56 :
57 : void
58 524 : PresenceManager::untrackBuddy(const std::string& uri)
59 : {
60 524 : dht::InfoHash h(uri);
61 524 : if (!h)
62 0 : return;
63 :
64 523 : std::lock_guard lock(mutex_);
65 524 : auto it = trackedBuddies_.find(h);
66 524 : if (it != trackedBuddies_.end()) {
67 523 : it->second.refCount--;
68 523 : if (it->second.refCount <= 0) {
69 509 : if (dht_ && dht_->isRunning()) {
70 503 : dht_->cancelListen(h, std::move(it->second.listenToken));
71 : }
72 510 : trackedBuddies_.erase(it);
73 : }
74 : }
75 525 : }
76 :
77 : bool
78 2456 : PresenceManager::isOnline(const std::string& uri) const
79 : {
80 2456 : dht::InfoHash h(uri);
81 2456 : if (!h)
82 0 : return false;
83 2456 : std::lock_guard lock(mutex_);
84 2456 : auto it = trackedBuddies_.find(h);
85 2456 : return it != trackedBuddies_.end() && !it->second.devices.empty();
86 2456 : }
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 1021 : PresenceManager::getDevices(const std::string& uri) const
101 : {
102 1021 : dht::InfoHash h(uri);
103 1021 : if (!h)
104 0 : return {};
105 1021 : std::lock_guard lock(mutex_);
106 1021 : auto it = trackedBuddies_.find(h);
107 1021 : if (it == trackedBuddies_.end())
108 223 : return {};
109 2394 : return {it->second.devices.begin(), it->second.devices.end()};
110 1021 : }
111 :
112 : uint64_t
113 793 : PresenceManager::addListener(PresenceCallback cb)
114 : {
115 793 : std::lock_guard lock(listenersMutex_);
116 793 : auto id = nextListenerId_++;
117 793 : listeners_.emplace(id, std::move(cb));
118 793 : return id;
119 793 : }
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 1179 : PresenceManager::addDeviceListener(DevicePresenceCallback cb)
130 : {
131 1179 : std::lock_guard lock(listenersMutex_);
132 1179 : auto id = nextListenerId_++;
133 1179 : deviceListeners_.emplace(id, std::move(cb));
134 1179 : return id;
135 1179 : }
136 :
137 : void
138 220 : PresenceManager::removeDeviceListener(uint64_t token)
139 : {
140 220 : std::lock_guard lock(listenersMutex_);
141 220 : deviceListeners_.erase(token);
142 220 : }
143 :
144 : void
145 694 : PresenceManager::refresh()
146 : {
147 694 : std::lock_guard lock(mutex_);
148 706 : for (auto& [h, buddy] : trackedBuddies_) {
149 12 : buddy.listenToken = {};
150 12 : buddy.devices.clear();
151 12 : trackPresence(h, buddy);
152 : }
153 694 : }
154 :
155 : void
156 815 : PresenceManager::trackPresence(const dht::InfoHash& h, TrackedBuddy& buddy)
157 : {
158 815 : if (!dht_ || !dht_->isRunning())
159 17 : return;
160 :
161 798 : if (buddy.listenToken.valid()) {
162 0 : JAMI_ERROR("PresenceManager: Already tracking presence for {}", h.toString());
163 0 : return;
164 : }
165 :
166 1596 : buddy.listenToken = dht_->listen<DeviceAnnouncement>(h, [this, h](DeviceAnnouncement&& dev, bool expired) {
167 721 : 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 721 : auto deviceId = dev.pk->getLongId();
173 721 : bool deviceOnline = !expired;
174 : {
175 721 : std::lock_guard lock(mutex_);
176 721 : auto it = trackedBuddies_.find(h);
177 721 : if (it == trackedBuddies_.end())
178 11 : return true;
179 :
180 710 : wasConnected = !it->second.devices.empty();
181 710 : if (expired) {
182 77 : it->second.devices.erase(deviceId);
183 : } else {
184 633 : it->second.devices.insert(deviceId);
185 : }
186 710 : isConnected = !it->second.devices.empty();
187 721 : }
188 :
189 710 : notifyDeviceListeners(h.toString(), deviceId, deviceOnline);
190 :
191 710 : if (isConnected != wasConnected) {
192 627 : notifyListeners(h.toString(), isConnected);
193 : }
194 710 : return true;
195 798 : });
196 : }
197 :
198 : void
199 627 : PresenceManager::notifyListeners(const std::string& uri, bool online)
200 : {
201 627 : std::vector<PresenceCallback> cbs;
202 : {
203 627 : std::lock_guard lock(listenersMutex_);
204 627 : cbs.reserve(listeners_.size());
205 1259 : for (const auto& [id, cb] : listeners_) {
206 632 : cbs.emplace_back(cb);
207 : }
208 627 : }
209 1259 : for (const auto& cb : cbs) {
210 632 : cb(uri, online);
211 : }
212 627 : }
213 :
214 : void
215 710 : PresenceManager::notifyDeviceListeners(const std::string& uri, const dht::PkId& deviceId, bool online)
216 : {
217 710 : std::vector<DevicePresenceCallback> cbs;
218 : {
219 710 : std::lock_guard lock(listenersMutex_);
220 710 : cbs.reserve(deviceListeners_.size());
221 2114 : for (const auto& [id, cb] : deviceListeners_) {
222 1404 : cbs.emplace_back(cb);
223 : }
224 710 : }
225 2114 : for (const auto& cb : cbs) {
226 1404 : cb(uri, deviceId, online);
227 : }
228 710 : }
229 :
230 : } // namespace jami
|