Ring Daemon
Loading...
Searching...
No Matches
presence_manager.cpp
Go to the documentation of this file.
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
22namespace jami {
23
24PresenceManager::PresenceManager(const std::shared_ptr<dht::DhtRunner>& dht)
25 : dht_(dht)
26{}
27
29{
30 std::lock_guard lock(mutex_);
31 for (auto& [h, buddy] : trackedBuddies_) {
32 if (dht_ && dht_->isRunning()) {
33 dht_->cancelListen(h, std::move(buddy.listenToken));
34 }
35 }
36}
37
38void
39PresenceManager::trackBuddy(const std::string& uri)
40{
41 dht::InfoHash h(uri);
42 if (!h)
43 return;
44
45 std::lock_guard lock(mutex_);
46 auto it = trackedBuddies_.find(h);
47 if (it == trackedBuddies_.end()) {
48 it = trackedBuddies_.emplace(h, TrackedBuddy {h}).first;
49 }
50
51 it->second.refCount++;
52 if (it->second.refCount == 1) {
53 trackPresence(h, it->second);
54 }
55}
56
57void
58PresenceManager::untrackBuddy(const std::string& uri)
59{
60 dht::InfoHash h(uri);
61 if (!h)
62 return;
63
64 std::lock_guard lock(mutex_);
65 auto it = trackedBuddies_.find(h);
66 if (it != trackedBuddies_.end()) {
67 it->second.refCount--;
68 if (it->second.refCount <= 0) {
69 if (dht_ && dht_->isRunning()) {
70 dht_->cancelListen(h, std::move(it->second.listenToken));
71 }
72 trackedBuddies_.erase(it);
73 }
74 }
75}
76
77bool
78PresenceManager::isOnline(const std::string& uri) const
79{
80 dht::InfoHash h(uri);
81 if (!h)
82 return false;
83 std::lock_guard lock(mutex_);
84 auto it = trackedBuddies_.find(h);
85 return it != trackedBuddies_.end() && !it->second.devices.empty();
86}
87
88std::map<std::string, bool>
90{
91 std::lock_guard lock(mutex_);
92 std::map<std::string, bool> presence_info;
93 for (const auto& [h, buddy] : trackedBuddies_) {
94 presence_info.emplace(h.toString(), !buddy.devices.empty());
95 }
96 return presence_info;
97}
98
99std::vector<dht::PkId>
100PresenceManager::getDevices(const std::string& uri) const
101{
102 dht::InfoHash h(uri);
103 if (!h)
104 return {};
105 std::lock_guard lock(mutex_);
106 auto it = trackedBuddies_.find(h);
107 if (it == trackedBuddies_.end())
108 return {};
109 return {it->second.devices.begin(), it->second.devices.end()};
110}
111
114{
115 std::lock_guard lock(listenersMutex_);
116 auto id = nextListenerId_++;
117 listeners_.emplace(id, std::move(cb));
118 return id;
119}
120
121void
123{
124 std::lock_guard lock(listenersMutex_);
125 listeners_.erase(token);
126}
127
130{
131 std::lock_guard lock(listenersMutex_);
132 auto id = nextListenerId_++;
133 deviceListeners_.emplace(id, std::move(cb));
134 return id;
135}
136
137void
139{
140 std::lock_guard lock(listenersMutex_);
141 deviceListeners_.erase(token);
142}
143
144void
146{
147 std::lock_guard lock(mutex_);
148 for (auto& [h, buddy] : trackedBuddies_) {
149 buddy.listenToken = {};
150 buddy.devices.clear();
151 trackPresence(h, buddy);
152 }
153}
154
155void
156PresenceManager::trackPresence(const dht::InfoHash& h, TrackedBuddy& buddy)
157{
158 if (!dht_ || !dht_->isRunning())
159 return;
160
161 if (buddy.listenToken.valid()) {
162 JAMI_ERROR("PresenceManager: Already tracking presence for {}", h.toString());
163 return;
164 }
165
166 buddy.listenToken = dht_->listen<DeviceAnnouncement>(h, [this, h](DeviceAnnouncement&& dev, bool expired) {
167 if (!dev.pk) {
168 JAMI_WARNING("PresenceManager: Received DeviceAnnouncement without public key for {}", h.toString());
169 return true;
170 }
171 bool wasConnected, isConnected;
172 auto deviceId = dev.pk->getLongId();
173 bool deviceOnline = !expired;
174 {
175 std::lock_guard lock(mutex_);
176 auto it = trackedBuddies_.find(h);
177 if (it == trackedBuddies_.end())
178 return true;
179
180 wasConnected = !it->second.devices.empty();
181 if (expired) {
182 it->second.devices.erase(deviceId);
183 } else {
184 it->second.devices.insert(deviceId);
185 }
186 isConnected = !it->second.devices.empty();
187 }
188
189 notifyDeviceListeners(h.toString(), deviceId, deviceOnline);
190
191 if (isConnected != wasConnected) {
192 notifyListeners(h.toString(), isConnected);
193 }
194 return true;
195 });
196}
197
198void
199PresenceManager::notifyListeners(const std::string& uri, bool online)
200{
201 std::vector<PresenceCallback> cbs;
202 {
203 std::lock_guard lock(listenersMutex_);
204 cbs.reserve(listeners_.size());
205 for (const auto& [id, cb] : listeners_) {
206 cbs.emplace_back(cb);
207 }
208 }
209 for (const auto& cb : cbs) {
210 cb(uri, online);
211 }
212}
213
214void
215PresenceManager::notifyDeviceListeners(const std::string& uri, const dht::PkId& deviceId, bool online)
216{
217 std::vector<DevicePresenceCallback> cbs;
218 {
219 std::lock_guard lock(listenersMutex_);
220 cbs.reserve(deviceListeners_.size());
221 for (const auto& [id, cb] : deviceListeners_) {
222 cbs.emplace_back(cb);
223 }
224 }
225 for (const auto& cb : cbs) {
226 cb(uri, deviceId, online);
227 }
228}
229
230} // namespace jami
std::function< void(const std::string &uri, const dht::PkId &deviceId, bool online)> DevicePresenceCallback
PresenceManager(const std::shared_ptr< dht::DhtRunner > &dht)
uint64_t addListener(PresenceCallback cb)
Add a listener for presence changes.
void refresh()
Refresh all tracked buddies.
bool isOnline(const std::string &uri) const
Check if a buddy is currently online.
std::map< std::string, bool > getTrackedBuddyPresence() const
Get the presence status of all tracked buddies.
void removeDeviceListener(uint64_t token)
Remove a listener using the token returned by addDeviceListener.
uint64_t addDeviceListener(DevicePresenceCallback cb)
Add a listener for device presence changes.
std::function< void(const std::string &uri, bool online)> PresenceCallback
void trackBuddy(const std::string &uri)
Start tracking a buddy.
void removeListener(uint64_t token)
Remove a listener using the token returned by addListener.
void untrackBuddy(const std::string &uri)
Stop tracking a buddy.
std::vector< dht::PkId > getDevices(const std::string &uri) const
Get the list of devices for a tracked buddy.
#define JAMI_ERROR(formatstr,...)
Definition logger.h:243
#define JAMI_WARNING(formatstr,...)
Definition logger.h:242
Definition Address.h:25
Definition account.h:50
void emitSignal(Args... args)
Definition jami_signal.h:64