Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 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"
27 : #include "connectivity/sip_utils.h"
28 : #include "compiler_intrinsics.h"
29 :
30 : namespace jami {
31 :
32 : using sip_utils::CONST_PJ_STR;
33 :
34 : /* Callback called when *server* subscription state has changed. */
35 : void
36 0 : PresSubServer::pres_evsub_on_srv_state(UNUSED pjsip_evsub* sub, UNUSED pjsip_event* event)
37 : {
38 0 : JAMI_ERR("PresSubServer::pres_evsub_on_srv_state() is deprecated and does nothing");
39 0 : 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();
64 : PresSubServer *presSubServer = static_cast<PresSubServer *>(pjsip_evsub_get_mod_data(sub, pres->getModId()));
65 :
66 : if (presSubServer) {
67 : JAMI_DBG("Presence_subscription_server to %s is %s",
68 : presSubServer->remote_, pjsip_evsub_get_state_name(sub));
69 : pjsip_evsub_state state;
70 :
71 : state = pjsip_evsub_get_state(sub);
72 :
73 : if (state == PJSIP_EVSUB_STATE_TERMINATED) {
74 : pjsip_evsub_set_mod_data(sub, pres->getModId(), NULL);
75 : pres->removePresSubServer(presSubServer);
76 : }
77 :
78 : /* TODO check if other cases should be handled*/
79 : }
80 :
81 : pres->unlock();
82 : #endif
83 : }
84 :
85 : pj_bool_t
86 403 : PresSubServer::pres_on_rx_subscribe_request(pjsip_rx_data* rdata)
87 : {
88 403 : pjsip_method* method = &rdata->msg_info.msg->line.req.method;
89 403 : pj_str_t* str = &method->name;
90 403 : 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;
94 : pjsip_dialog *dlg;
95 : pjsip_evsub *sub;
96 : pjsip_evsub_user pres_cb;
97 : pjsip_expires_hdr *expires_hdr;
98 : pjsip_status_code st_code;
99 : pj_str_t reason;
100 : pres_msg_data msg_data;
101 : pjsip_evsub_state ev_state;
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 403 : if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method()) != 0)
107 403 : return PJ_FALSE;
108 :
109 0 : JAMI_ERR("PresSubServer::pres_evsub_on_srv_state() is deprecated and does nothing");
110 0 : 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 :
129 : pjsip_endpoint *endpt = Manager::instance().sipVoIPLink().getEndpoint();
130 : SIPPresence * pres = sipaccount->getPresence();
131 : pres->lock();
132 :
133 : /* Create UAS dialog: */
134 : const pj_str_t contact(sipaccount->getContactHeader());
135 : status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, &contact, &dlg);
136 :
137 : if (status != PJ_SUCCESS) {
138 : char errmsg[PJ_ERR_MSG_SIZE];
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();
142 : pjsip_endpt_respond_stateless(endpt, rdata, 400, NULL, NULL, NULL);
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: */
151 : status = pjsip_pres_create_uas(dlg, &pres_cb, rdata, &sub);
152 :
153 : if (status != PJ_SUCCESS) {
154 : int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
155 : pjsip_tx_data *tdata;
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) {
166 : pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
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 */
185 : PresSubServer *presSubServer = new PresSubServer(pres, sub, remote, dlg);
186 : pjsip_evsub_set_mod_data(sub, pres->getModId(), presSubServer);
187 : // Notify the client.
188 : emitSignal<libjami::PresenceSignal::NewServerSubscriptionRequest>(presSubServer->remote_);
189 : pres->addPresSubServer(presSubServer);
190 :
191 : /* Capture the value of Expires header. */
192 : expires_hdr = (pjsip_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
193 :
194 : if (expires_hdr)
195 : presSubServer->setExpires(expires_hdr->ivalue);
196 : else
197 : presSubServer->setExpires(-1);
198 :
199 : st_code = (pjsip_status_code) 200;
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);
211 : pjsip_pres_terminate(sub, PJ_FALSE);
212 : pres->unlock();
213 : return PJ_FALSE;
214 : }
215 :
216 : // Unsubscribe case
217 : ev_state = PJSIP_EVSUB_STATE_ACTIVE;
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
230 : pjrpid_element rpid = {
231 : PJRPID_ELEMENT_TYPE_PERSON,
232 : CONST_PJ_STR("20"),
233 : PJRPID_ACTIVITY_UNKNOWN,
234 : CONST_PJ_STR("") // empty note by default
235 : };
236 : pjsip_pres_status fake_status_data;
237 : pj_bzero(&fake_status_data, sizeof(pjsip_pres_status));
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));
242 : pjsip_pres_set_status(sub, &fake_status_data);
243 :
244 : /* Create and send the the first NOTIFY to active subscription: */
245 : pj_str_t stateStr = CONST_PJ_STR("");
246 : pjsip_tx_data *tdata = NULL;
247 : status = pjsip_pres_notify(sub, ev_state, &stateStr, &reason, &tdata);
248 :
249 : if (status == PJ_SUCCESS) {
250 : pres->fillDoc(tdata, &msg_data);
251 : status = pjsip_pres_send_request(sub, tdata);
252 : }
253 :
254 : if (status != PJ_SUCCESS) {
255 : JAMI_WARN("Unable to create/send NOTIFY %d", status);
256 : pjsip_pres_terminate(sub, PJ_FALSE);
257 : pres->unlock();
258 : return status;
259 : }
260 :
261 : pres->unlock();
262 : return PJ_TRUE;
263 : #endif
264 403 : }
265 :
266 : pjsip_module PresSubServer::mod_presence_server = {
267 : NULL,
268 : NULL, /* prev, next. */
269 : CONST_PJ_STR("mod-presence-server"), /* Name. */
270 : -1, /* Id */
271 : PJSIP_MOD_PRIORITY_DIALOG_USAGE,
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 :
284 0 : PresSubServer::PresSubServer(SIPPresence* pres,
285 : pjsip_evsub* evsub,
286 : const char* remote,
287 0 : pjsip_dialog* d)
288 0 : : remote_(remote)
289 0 : , pres_(pres)
290 0 : , sub_(evsub)
291 0 : , dlg_(d)
292 0 : , expires_(-1)
293 0 : , approved_(false)
294 0 : {}
295 :
296 0 : PresSubServer::~PresSubServer()
297 : {
298 : // TODO: check if evsub needs to be forced TERMINATED.
299 0 : }
300 :
301 : void
302 0 : PresSubServer::setExpires(int ms)
303 : {
304 0 : expires_ = ms;
305 0 : }
306 :
307 : int
308 0 : PresSubServer::getExpires() const
309 : {
310 0 : return expires_;
311 : }
312 :
313 : bool
314 0 : PresSubServer::matches(const char* s) const
315 : {
316 : // servers match if they have the same remote uri and the account ID.
317 0 : return (!(strcmp(remote_, s)));
318 : }
319 :
320 : void
321 0 : PresSubServer::approve(bool flag)
322 : {
323 0 : approved_ = flag;
324 0 : JAMI_DBG("Approve Presence_subscription_server for %s: %s.", remote_, flag ? "true" : "false");
325 : // attach the real status data
326 0 : pjsip_pres_set_status(sub_, pres_->getStatus());
327 0 : }
328 :
329 : void
330 0 : PresSubServer::notify()
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 0 : if ((pjsip_evsub_get_state(sub_) == PJSIP_EVSUB_STATE_ACTIVE) && (approved_)) {
339 0 : JAMI_DBG("Notifying %s.", remote_);
340 :
341 : pjsip_tx_data* tdata;
342 0 : pjsip_pres_set_status(sub_, pres_->getStatus());
343 :
344 0 : if (pjsip_pres_current_notify(sub_, &tdata) == PJ_SUCCESS) {
345 : // add msg header and send
346 0 : pres_->fillDoc(tdata, NULL);
347 0 : pjsip_pres_send_request(sub_, tdata);
348 : } else {
349 0 : JAMI_WARN("Unable to create/send NOTIFY");
350 0 : pjsip_pres_terminate(sub_, PJ_FALSE);
351 : }
352 : }
353 0 : }
354 :
355 : } // namespace jami
|