Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc.
3 : *
4 : * Author: Patrick Keroulas <patrick.keroulas@savoirfairelinux.com>
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, write to the Free Software
18 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 : */
20 :
21 : #include "pjsip/sip_multipart.h"
22 :
23 : #include "sip/sipaccount.h"
24 : #include "sip/sipvoiplink.h"
25 : #include "manager.h"
26 : #include "sip/sippresence.h"
27 : #include "logger.h"
28 : #include "pres_sub_server.h"
29 : #include "client/ring_signal.h"
30 : #include "connectivity/sip_utils.h"
31 : #include "compiler_intrinsics.h"
32 :
33 : namespace jami {
34 :
35 : using sip_utils::CONST_PJ_STR;
36 :
37 : /* Callback called when *server* subscription state has changed. */
38 : void
39 0 : PresSubServer::pres_evsub_on_srv_state(UNUSED pjsip_evsub* sub, UNUSED pjsip_event* event)
40 : {
41 0 : JAMI_ERR("PresSubServer::pres_evsub_on_srv_state() is deprecated and does nothing");
42 0 : return;
43 :
44 : #if 0 // DISABLED: removed IP2IP support, tuleap: #448
45 : pjsip_rx_data *rdata = event->body.rx_msg.rdata;
46 :
47 : if (!rdata) {
48 : JAMI_DBG("Presence_subscription_server estate has changed but no rdata.");
49 : return;
50 : }
51 :
52 : auto account = Manager::instance().getIP2IPAccount();
53 : auto sipaccount = static_cast<SIPAccount *>(account.get());
54 : if (!sipaccount) {
55 : JAMI_ERR("Could not find account IP2IP");
56 : return;
57 : }
58 :
59 : auto pres = sipaccount->getPresence();
60 :
61 : if (!pres) {
62 : JAMI_ERR("Presence not initialized");
63 : return;
64 : }
65 :
66 : pres->lock();
67 : PresSubServer *presSubServer = static_cast<PresSubServer *>(pjsip_evsub_get_mod_data(sub, pres->getModId()));
68 :
69 : if (presSubServer) {
70 : JAMI_DBG("Presence_subscription_server to %s is %s",
71 : presSubServer->remote_, pjsip_evsub_get_state_name(sub));
72 : pjsip_evsub_state state;
73 :
74 : state = pjsip_evsub_get_state(sub);
75 :
76 : if (state == PJSIP_EVSUB_STATE_TERMINATED) {
77 : pjsip_evsub_set_mod_data(sub, pres->getModId(), NULL);
78 : pres->removePresSubServer(presSubServer);
79 : }
80 :
81 : /* TODO check if other cases should be handled*/
82 : }
83 :
84 : pres->unlock();
85 : #endif
86 : }
87 :
88 : pj_bool_t
89 32992 : PresSubServer::pres_on_rx_subscribe_request(pjsip_rx_data* rdata)
90 : {
91 32992 : pjsip_method* method = &rdata->msg_info.msg->line.req.method;
92 32992 : pj_str_t* str = &method->name;
93 32992 : std::string request(str->ptr, str->slen);
94 : // pj_str_t contact;
95 : #if 0 // DISABLED: removed IP2IP support, tuleap: #448
96 : pj_status_t status;
97 : pjsip_dialog *dlg;
98 : pjsip_evsub *sub;
99 : pjsip_evsub_user pres_cb;
100 : pjsip_expires_hdr *expires_hdr;
101 : pjsip_status_code st_code;
102 : pj_str_t reason;
103 : pres_msg_data msg_data;
104 : pjsip_evsub_state ev_state;
105 : #endif
106 :
107 : /* Only hande incoming subscribe messages should be processed here.
108 : * Otherwise we return FALSE to let other modules handle it */
109 32992 : if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method()) != 0)
110 32992 : return PJ_FALSE;
111 :
112 0 : JAMI_ERR("PresSubServer::pres_evsub_on_srv_state() is deprecated and does nothing");
113 0 : return PJ_FALSE;
114 :
115 : #if 0 // DISABLED: removed IP2IP support, tuleap: #448
116 : /* debug msg */
117 : std::string name(rdata->msg_info.to->name.ptr, rdata->msg_info.to->name.slen);
118 : std::string server(rdata->msg_info.from->name.ptr, rdata->msg_info.from->name.slen);
119 : JAMI_DBG("Incoming pres_on_rx_subscribe_request for %s, name:%s, server:%s."
120 : , request.c_str()
121 : , name.c_str()
122 : , server.c_str());
123 :
124 : /* get parents*/
125 : auto account = Manager::instance().getIP2IPAccount();
126 : auto sipaccount = static_cast<SIPAccount *>(account.get());
127 : if (!sipaccount) {
128 : JAMI_ERR("Could not find account IP2IP");
129 : return PJ_FALSE;
130 : }
131 :
132 : pjsip_endpoint *endpt = Manager::instance().sipVoIPLink().getEndpoint();
133 : SIPPresence * pres = sipaccount->getPresence();
134 : pres->lock();
135 :
136 : /* Create UAS dialog: */
137 : const pj_str_t contact(sipaccount->getContactHeader());
138 : status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, &contact, &dlg);
139 :
140 : if (status != PJ_SUCCESS) {
141 : char errmsg[PJ_ERR_MSG_SIZE];
142 : pj_strerror(status, errmsg, sizeof(errmsg));
143 : JAMI_WARN("Unable to create UAS dialog for subscription: %s [status=%d]", errmsg, status);
144 : pres->unlock();
145 : pjsip_endpt_respond_stateless(endpt, rdata, 400, NULL, NULL, NULL);
146 : return PJ_TRUE;
147 : }
148 :
149 : /* Init callback: */
150 : pj_bzero(&pres_cb, sizeof(pres_cb));
151 : pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
152 :
153 : /* Create server presence subscription: */
154 : status = pjsip_pres_create_uas(dlg, &pres_cb, rdata, &sub);
155 :
156 : if (status != PJ_SUCCESS) {
157 : int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
158 : pjsip_tx_data *tdata;
159 :
160 : JAMI_WARN("Unable to create server subscription %d", status);
161 :
162 : if (code == 599 || code > 699 || code < 300) {
163 : code = 400;
164 : }
165 :
166 : status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
167 :
168 : if (status == PJ_SUCCESS) {
169 : pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
170 : }
171 :
172 : pres->unlock();
173 : return PJ_FALSE;
174 : }
175 :
176 : /* Attach our data to the subscription: */
177 : char* remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
178 : status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri, remote, PJSIP_MAX_URL_SIZE);
179 :
180 : if (status < 1)
181 : pj_ansi_strcpy(remote, "<-- url is too long-->");
182 : else
183 : remote[status] = '\0';
184 :
185 : //pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, dlg->local.info->uri, contact.ptr, PJSIP_MAX_URL_SIZE);
186 :
187 : /* Create a new PresSubServer server and wait for client approve */
188 : PresSubServer *presSubServer = new PresSubServer(pres, sub, remote, dlg);
189 : pjsip_evsub_set_mod_data(sub, pres->getModId(), presSubServer);
190 : // Notify the client.
191 : emitSignal<libjami::PresenceSignal::NewServerSubscriptionRequest>(presSubServer->remote_);
192 : pres->addPresSubServer(presSubServer);
193 :
194 : /* Capture the value of Expires header. */
195 : expires_hdr = (pjsip_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
196 :
197 : if (expires_hdr)
198 : presSubServer->setExpires(expires_hdr->ivalue);
199 : else
200 : presSubServer->setExpires(-1);
201 :
202 : st_code = (pjsip_status_code) 200;
203 : reason = CONST_PJ_STR("OK");
204 : pj_bzero(&msg_data, sizeof(msg_data));
205 : pj_list_init(&msg_data.hdr_list);
206 : pjsip_media_type_init(&msg_data.multipart_ctype, NULL, NULL);
207 : pj_list_init(&msg_data.multipart_parts);
208 :
209 : /* Create and send 2xx response to the SUBSCRIBE request: */
210 : status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
211 :
212 : if (status != PJ_SUCCESS) {
213 : JAMI_WARN("Unable to accept presence subscription %d", status);
214 : pjsip_pres_terminate(sub, PJ_FALSE);
215 : pres->unlock();
216 : return PJ_FALSE;
217 : }
218 :
219 : // Unsubscribe case
220 : ev_state = PJSIP_EVSUB_STATE_ACTIVE;
221 :
222 : if (presSubServer->getExpires() == 0) {
223 : // PJSIP_EVSUB_STATE_TERMINATED
224 : pres->unlock();
225 : return PJ_TRUE;
226 : }
227 :
228 : /*Send notify immediately. Replace real status with fake.*/
229 :
230 : // pjsip_pres_set_status(sub, pres->getStatus()); // real status
231 :
232 : // fake temporary status
233 : pjrpid_element rpid = {
234 : PJRPID_ELEMENT_TYPE_PERSON,
235 : CONST_PJ_STR("20"),
236 : PJRPID_ACTIVITY_UNKNOWN,
237 : CONST_PJ_STR("") // empty note by default
238 : };
239 : pjsip_pres_status fake_status_data;
240 : pj_bzero(&fake_status_data, sizeof(pjsip_pres_status));
241 : fake_status_data.info_cnt = 1;
242 : fake_status_data.info[0].basic_open = false;
243 : fake_status_data.info[0].id = CONST_PJ_STR("0"); /* todo: tuplie_id*/
244 : pj_memcpy(&fake_status_data.info[0].rpid, &rpid, sizeof(pjrpid_element));
245 : pjsip_pres_set_status(sub, &fake_status_data);
246 :
247 : /* Create and send the the first NOTIFY to active subscription: */
248 : pj_str_t stateStr = CONST_PJ_STR("");
249 : pjsip_tx_data *tdata = NULL;
250 : status = pjsip_pres_notify(sub, ev_state, &stateStr, &reason, &tdata);
251 :
252 : if (status == PJ_SUCCESS) {
253 : pres->fillDoc(tdata, &msg_data);
254 : status = pjsip_pres_send_request(sub, tdata);
255 : }
256 :
257 : if (status != PJ_SUCCESS) {
258 : JAMI_WARN("Unable to create/send NOTIFY %d", status);
259 : pjsip_pres_terminate(sub, PJ_FALSE);
260 : pres->unlock();
261 : return status;
262 : }
263 :
264 : pres->unlock();
265 : return PJ_TRUE;
266 : #endif
267 32992 : }
268 :
269 : pjsip_module PresSubServer::mod_presence_server = {
270 : NULL,
271 : NULL, /* prev, next. */
272 : CONST_PJ_STR("mod-presence-server"), /* Name. */
273 : -1, /* Id */
274 : PJSIP_MOD_PRIORITY_DIALOG_USAGE,
275 : NULL, /* load() */
276 : NULL, /* start() */
277 : NULL, /* stop() */
278 : NULL, /* unload() */
279 : &pres_on_rx_subscribe_request, /* on_rx_request() */
280 : NULL, /* on_rx_response() */
281 : NULL, /* on_tx_request. */
282 : NULL, /* on_tx_response() */
283 : NULL, /* on_tsx_state() */
284 :
285 : };
286 :
287 0 : PresSubServer::PresSubServer(SIPPresence* pres,
288 : pjsip_evsub* evsub,
289 : const char* remote,
290 0 : pjsip_dialog* d)
291 0 : : remote_(remote)
292 0 : , pres_(pres)
293 0 : , sub_(evsub)
294 0 : , dlg_(d)
295 0 : , expires_(-1)
296 0 : , approved_(false)
297 0 : {}
298 :
299 0 : PresSubServer::~PresSubServer()
300 : {
301 : // TODO: check if evsub needs to be forced TERMINATED.
302 0 : }
303 :
304 : void
305 0 : PresSubServer::setExpires(int ms)
306 : {
307 0 : expires_ = ms;
308 0 : }
309 :
310 : int
311 0 : PresSubServer::getExpires() const
312 : {
313 0 : return expires_;
314 : }
315 :
316 : bool
317 0 : PresSubServer::matches(const char* s) const
318 : {
319 : // servers match if they have the same remote uri and the account ID.
320 0 : return (!(strcmp(remote_, s)));
321 : }
322 :
323 : void
324 0 : PresSubServer::approve(bool flag)
325 : {
326 0 : approved_ = flag;
327 0 : JAMI_DBG("Approve Presence_subscription_server for %s: %s.", remote_, flag ? "true" : "false");
328 : // attach the real status data
329 0 : pjsip_pres_set_status(sub_, pres_->getStatus());
330 0 : }
331 :
332 : void
333 0 : PresSubServer::notify()
334 : {
335 : /* Only send NOTIFY once subscription is active. Some subscriptions
336 : * may still be in NULL (when app is adding a new buddy while in the
337 : * on_incoming_subscribe() callback) or PENDING (when user approval is
338 : * being requested) state and we don't send NOTIFY to these subs until
339 : * the user accepted the request.
340 : */
341 0 : if ((pjsip_evsub_get_state(sub_) == PJSIP_EVSUB_STATE_ACTIVE) && (approved_)) {
342 0 : JAMI_DBG("Notifying %s.", remote_);
343 :
344 : pjsip_tx_data* tdata;
345 0 : pjsip_pres_set_status(sub_, pres_->getStatus());
346 :
347 0 : if (pjsip_pres_current_notify(sub_, &tdata) == PJ_SUCCESS) {
348 : // add msg header and send
349 0 : pres_->fillDoc(tdata, NULL);
350 0 : pjsip_pres_send_request(sub_, tdata);
351 : } else {
352 0 : JAMI_WARN("Unable to create/send NOTIFY");
353 0 : pjsip_pres_terminate(sub_, PJ_FALSE);
354 : }
355 : }
356 0 : }
357 :
358 : } // namespace jami
|