Ring Daemon 16.0.0
Loading...
Searching...
No Matches
pres_sub_server.cpp
Go to the documentation of this file.
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
18#include "pjsip/sip_multipart.h"
19
20#include "sip/sipaccount.h"
21#include "sip/sipvoiplink.h"
22#include "manager.h"
23#include "sip/sippresence.h"
24#include "logger.h"
25#include "pres_sub_server.h"
26#include "client/ring_signal.h"
28#include "compiler_intrinsics.h"
29
30namespace jami {
31
33
34/* Callback called when *server* subscription state has changed. */
35void
36PresSubServer::pres_evsub_on_srv_state(UNUSED pjsip_evsub* sub, UNUSED pjsip_event* event)
37{
38 JAMI_ERR("PresSubServer::pres_evsub_on_srv_state() is deprecated and does nothing");
39 return;
40
41#if 0 // DISABLED: removed IP2IP support, tuleap: #448
42 pjsip_rx_data *rdata = event->body.rx_msg.rdata;
43
44 if (!rdata) {
45 JAMI_DBG("Presence_subscription_server estate has changed but no rdata.");
46 return;
47 }
48
49 auto account = Manager::instance().getIP2IPAccount();
50 auto sipaccount = static_cast<SIPAccount *>(account.get());
51 if (!sipaccount) {
52 JAMI_ERR("Unable to find account IP2IP");
53 return;
54 }
55
56 auto pres = sipaccount->getPresence();
57
58 if (!pres) {
59 JAMI_ERR("Presence not initialized");
60 return;
61 }
62
63 pres->lock();
65
66 if (presSubServer) {
67 JAMI_DBG("Presence_subscription_server to %s is %s",
70
72
73 if (state == PJSIP_EVSUB_STATE_TERMINATED) {
75 pres->removePresSubServer(presSubServer);
76 }
77
78 /* TODO check if other cases should be handled*/
79 }
80
81 pres->unlock();
82#endif
83}
84
86PresSubServer::pres_on_rx_subscribe_request(pjsip_rx_data* rdata)
87{
88 pjsip_method* method = &rdata->msg_info.msg->line.req.method;
89 pj_str_t* str = &method->name;
90 std::string request(str->ptr, str->slen);
91// pj_str_t contact;
92#if 0 // DISABLED: removed IP2IP support, tuleap: #448
93 pj_status_t status;
100 pres_msg_data msg_data;
102#endif
103
104 /* Only hande incoming subscribe messages should be processed here.
105 * Otherwise we return FALSE to let other modules handle it */
106 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method()) != 0)
107 return PJ_FALSE;
108
109 JAMI_ERR("PresSubServer::pres_evsub_on_srv_state() is deprecated and does nothing");
110 return PJ_FALSE;
111
112#if 0 // DISABLED: removed IP2IP support, tuleap: #448
113 /* debug msg */
114 std::string name(rdata->msg_info.to->name.ptr, rdata->msg_info.to->name.slen);
115 std::string server(rdata->msg_info.from->name.ptr, rdata->msg_info.from->name.slen);
116 JAMI_DBG("Incoming pres_on_rx_subscribe_request for %s, name:%s, server:%s."
117 , request.c_str()
118 , name.c_str()
119 , server.c_str());
120
121 /* get parents*/
122 auto account = Manager::instance().getIP2IPAccount();
123 auto sipaccount = static_cast<SIPAccount *>(account.get());
124 if (!sipaccount) {
125 JAMI_ERR("Unable to find account IP2IP");
126 return PJ_FALSE;
127 }
128
130 SIPPresence * pres = sipaccount->getPresence();
131 pres->lock();
132
133 /* Create UAS dialog: */
134 const pj_str_t contact(sipaccount->getContactHeader());
136
137 if (status != PJ_SUCCESS) {
139 pj_strerror(status, errmsg, sizeof(errmsg));
140 JAMI_WARN("Unable to create UAS dialog for subscription: %s [status=%d]", errmsg, status);
141 pres->unlock();
143 return PJ_TRUE;
144 }
145
146 /* Init callback: */
147 pj_bzero(&pres_cb, sizeof(pres_cb));
148 pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
149
150 /* Create server presence subscription: */
152
153 if (status != PJ_SUCCESS) {
154 int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
156
157 JAMI_WARN("Unable to create server subscription %d", status);
158
159 if (code == 599 || code > 699 || code < 300) {
160 code = 400;
161 }
162
163 status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
164
165 if (status == PJ_SUCCESS) {
167 }
168
169 pres->unlock();
170 return PJ_FALSE;
171 }
172
173 /* Attach our data to the subscription: */
174 char* remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
175 status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri, remote, PJSIP_MAX_URL_SIZE);
176
177 if (status < 1)
178 pj_ansi_strcpy(remote, "<-- url is too long-->");
179 else
180 remote[status] = '\0';
181
182 //pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, dlg->local.info->uri, contact.ptr, PJSIP_MAX_URL_SIZE);
183
184 /* Create a new PresSubServer server and wait for client approve */
187 // Notify the client.
189 pres->addPresSubServer(presSubServer);
190
191 /* Capture the value of Expires header. */
193
194 if (expires_hdr)
195 presSubServer->setExpires(expires_hdr->ivalue);
196 else
197 presSubServer->setExpires(-1);
198
200 reason = CONST_PJ_STR("OK");
201 pj_bzero(&msg_data, sizeof(msg_data));
202 pj_list_init(&msg_data.hdr_list);
203 pjsip_media_type_init(&msg_data.multipart_ctype, NULL, NULL);
204 pj_list_init(&msg_data.multipart_parts);
205
206 /* Create and send 2xx response to the SUBSCRIBE request: */
207 status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
208
209 if (status != PJ_SUCCESS) {
210 JAMI_WARN("Unable to accept presence subscription %d", status);
212 pres->unlock();
213 return PJ_FALSE;
214 }
215
216 // Unsubscribe case
218
219 if (presSubServer->getExpires() == 0) {
220 // PJSIP_EVSUB_STATE_TERMINATED
221 pres->unlock();
222 return PJ_TRUE;
223 }
224
225 /*Send notify immediately. Replace real status with fake.*/
226
227 // pjsip_pres_set_status(sub, pres->getStatus()); // real status
228
229 // fake temporary status
232 CONST_PJ_STR("20"),
234 CONST_PJ_STR("") // empty note by default
235 };
238 fake_status_data.info_cnt = 1;
239 fake_status_data.info[0].basic_open = false;
240 fake_status_data.info[0].id = CONST_PJ_STR("0"); /* todo: tuplie_id*/
241 pj_memcpy(&fake_status_data.info[0].rpid, &rpid, sizeof(pjrpid_element));
243
244 /* Create and send the the first NOTIFY to active subscription: */
248
249 if (status == PJ_SUCCESS) {
250 pres->fillDoc(tdata, &msg_data);
252 }
253
254 if (status != PJ_SUCCESS) {
255 JAMI_WARN("Unable to create/send NOTIFY %d", status);
257 pres->unlock();
258 return status;
259 }
260
261 pres->unlock();
262 return PJ_TRUE;
263#endif
264}
265
267 NULL,
268 NULL, /* prev, next. */
269 CONST_PJ_STR("mod-presence-server"), /* Name. */
270 -1, /* Id */
272 NULL, /* load() */
273 NULL, /* start() */
274 NULL, /* stop() */
275 NULL, /* unload() */
276 &pres_on_rx_subscribe_request, /* on_rx_request() */
277 NULL, /* on_rx_response() */
278 NULL, /* on_tx_request. */
279 NULL, /* on_tx_response() */
280 NULL, /* on_tsx_state() */
281
282};
283
286 const char* remote,
288 : remote_(remote)
289 , pres_(pres)
290 , sub_(evsub)
291 , dlg_(d)
292 , expires_(-1)
293 , approved_(false)
294{}
295
297{
298 // TODO: check if evsub needs to be forced TERMINATED.
299}
300
301void
303{
304 expires_ = ms;
305}
306
307int
309{
310 return expires_;
311}
312
313bool
314PresSubServer::matches(const char* s) const
315{
316 // servers match if they have the same remote uri and the account ID.
317 return (!(strcmp(remote_, s)));
318}
319
320void
322{
323 approved_ = flag;
324 JAMI_DBG("Approve Presence_subscription_server for %s: %s.", remote_, flag ? "true" : "false");
325 // attach the real status data
326 pjsip_pres_set_status(sub_, pres_->getStatus());
327}
328
329void
331{
332 /* Only send NOTIFY once subscription is active. Some subscriptions
333 * may still be in NULL (when app is adding a new buddy while in the
334 * on_incoming_subscribe() callback) or PENDING (when user approval is
335 * being requested) state and we don't send NOTIFY to these subs until
336 * the user accepted the request.
337 */
338 if ((pjsip_evsub_get_state(sub_) == PJSIP_EVSUB_STATE_ACTIVE) && (approved_)) {
339 JAMI_DBG("Notifying %s.", remote_);
340
342 pjsip_pres_set_status(sub_, pres_->getStatus());
343
345 // add msg header and send
346 pres_->fillDoc(tdata, NULL);
348 } else {
349 JAMI_WARN("Unable to create/send NOTIFY");
351 }
352 }
353}
354
355} // namespace jami
static LIBJAMI_TEST_EXPORT Manager & instance()
Definition manager.cpp:676
SIPVoIPLink & sipVoIPLink() const
Definition manager.cpp:3189
void approve(bool flag)
static pjsip_module mod_presence_server
PresSubServer(SIPPresence *pres, pjsip_evsub *evsub, const char *remote, pjsip_dialog *d)
bool matches(const char *s) const
pjsip_pres_status * getStatus()
Return presence data.
void fillDoc(pjsip_tx_data *tdata, const pres_msg_data *msg_data)
Fill xml document, the header and the body.
#define UNUSED
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_DBG(...)
Definition logger.h:216
#define JAMI_WARN(...)
Definition logger.h:217
constexpr const pj_str_t CONST_PJ_STR(T(&a)[N]) noexcept
Definition sip_utils.h:89
void emitSignal(Args... args)
Definition ring_signal.h:64
pj_bool_t pres_on_rx_subscribe_request(pjsip_rx_data *rdata)
A SIP Account specify SIP specific functions and object = SIPCall/SIPVoIPLink)
A SIP Presence manages buddy subscription in both PBX and IP2IP contexts.