1 /* $Id$ */
2 /*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20 #include <pjsip-simple/presence.h>
21 #include <pjsip-simple/errno.h>
22 #include <pjsip-simple/evsub_msg.h>
23 #include <pjsip/sip_module.h>
24 #include <pjsip/sip_multipart.h>
25 #include <pjsip/sip_endpoint.h>
26 #include <pjsip/sip_dialog.h>
27 #include <pj/assert.h>
28 #include <pj/guid.h>
29 #include <pj/log.h>
30 #include <pj/os.h>
31 #include <pj/pool.h>
32 #include <pj/string.h>
33
34
35 #define THIS_FILE "presence.c"
36 #define PRES_DEFAULT_EXPIRES PJSIP_PRES_DEFAULT_EXPIRES
37
38 #if PJSIP_PRES_BAD_CONTENT_RESPONSE < 200 || \
39 PJSIP_PRES_BAD_CONTENT_RESPONSE > 699 || \
40 PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 3
41 # error Invalid PJSIP_PRES_BAD_CONTENT_RESPONSE value
42 #endif
43
44 /*
45 * Presence module (mod-presence)
46 */
47 static struct pjsip_module mod_presence =
48 {
49 NULL, NULL, /* prev, next. */
50 { "mod-presence", 12 }, /* Name. */
51 -1, /* Id */
52 PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */
53 NULL, /* load() */
54 NULL, /* start() */
55 NULL, /* stop() */
56 NULL, /* unload() */
57 NULL, /* on_rx_request() */
58 NULL, /* on_rx_response() */
59 NULL, /* on_tx_request. */
60 NULL, /* on_tx_response() */
61 NULL, /* on_tsx_state() */
62 };
63
64
65 /*
66 * Presence message body type.
67 */
68 typedef enum content_type_e
69 {
70 CONTENT_TYPE_NONE,
71 CONTENT_TYPE_PIDF,
72 CONTENT_TYPE_XPIDF,
73 } content_type_e;
74
75 /*
76 * This structure describe a presentity, for both subscriber and notifier.
77 */
78 struct pjsip_pres
79 {
80 pjsip_evsub *sub; /**< Event subscribtion record. */
81 pjsip_dialog *dlg; /**< The dialog. */
82 content_type_e content_type; /**< Content-Type. */
83 pj_pool_t *status_pool; /**< Pool for pres_status */
84 pjsip_pres_status status; /**< Presence status. */
85 pj_pool_t *tmp_pool; /**< Pool for tmp_status */
86 pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/
87 pjsip_evsub_user user_cb; /**< The user callback. */
88 };
89
90
91 typedef struct pjsip_pres pjsip_pres;
92
93
94 /*
95 * Forward decl for evsub callback.
96 */
97 static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
98 static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
99 pjsip_event *event);
100 static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
101 pjsip_rx_data *rdata,
102 int *p_st_code,
103 pj_str_t **p_st_text,
104 pjsip_hdr *res_hdr,
105 pjsip_msg_body **p_body);
106 static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
107 pjsip_rx_data *rdata,
108 int *p_st_code,
109 pj_str_t **p_st_text,
110 pjsip_hdr *res_hdr,
111 pjsip_msg_body **p_body);
112 static void pres_on_evsub_client_refresh(pjsip_evsub *sub);
113 static void pres_on_evsub_server_timeout(pjsip_evsub *sub);
114
115
116 /*
117 * Event subscription callback for presence.
118 */
119 static pjsip_evsub_user pres_user =
120 {
121 &pres_on_evsub_state,
122 &pres_on_evsub_tsx_state,
123 &pres_on_evsub_rx_refresh,
124 &pres_on_evsub_rx_notify,
125 &pres_on_evsub_client_refresh,
126 &pres_on_evsub_server_timeout,
127 };
128
129
130 /*
131 * Some static constants.
132 */
133 static const pj_str_t STR_EVENT = { "Event", 5 };
134 static const pj_str_t STR_PRESENCE = { "presence", 8 };
135 static const pj_str_t STR_APPLICATION = { "application", 11 };
136 static const pj_str_t STR_PIDF_XML = { "pidf+xml", 8};
137 static const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9};
138 static const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 };
139 static const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 };
140
141
142 /*
143 * Init presence module.
144 */
pjsip_pres_init_module(pjsip_endpoint * endpt,pjsip_module * mod_evsub)145 PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt,
146 pjsip_module *mod_evsub)
147 {
148 pj_status_t status;
149 pj_str_t accept[2];
150
151 /* Check arguments. */
152 PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
153
154 /* Must have not been registered */
155 PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP);
156
157 /* Register to endpoint */
158 status = pjsip_endpt_register_module(endpt, &mod_presence);
159 if (status != PJ_SUCCESS)
160 return status;
161
162 accept[0] = STR_APP_PIDF_XML;
163 accept[1] = STR_APP_XPIDF_XML;
164
165 /* Register event package to event module. */
166 status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE,
167 PRES_DEFAULT_EXPIRES,
168 PJ_ARRAY_SIZE(accept), accept);
169 if (status != PJ_SUCCESS) {
170 pjsip_endpt_unregister_module(endpt, &mod_presence);
171 return status;
172 }
173
174 return PJ_SUCCESS;
175 }
176
177
178 /*
179 * Get presence module instance.
180 */
pjsip_pres_instance(void)181 PJ_DEF(pjsip_module*) pjsip_pres_instance(void)
182 {
183 return &mod_presence;
184 }
185
186
187 /*
188 * Create client subscription.
189 */
pjsip_pres_create_uac(pjsip_dialog * dlg,const pjsip_evsub_user * user_cb,unsigned options,pjsip_evsub ** p_evsub)190 PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
191 const pjsip_evsub_user *user_cb,
192 unsigned options,
193 pjsip_evsub **p_evsub )
194 {
195 pj_status_t status;
196 pjsip_pres *pres;
197 char obj_name[PJ_MAX_OBJ_NAME];
198 pjsip_evsub *sub;
199
200 PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
201
202 pjsip_dlg_inc_lock(dlg);
203
204 /* Create event subscription */
205 status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE,
206 options, &sub);
207 if (status != PJ_SUCCESS)
208 goto on_return;
209
210 /* Create presence */
211 pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
212 pres->dlg = dlg;
213 pres->sub = sub;
214 if (user_cb)
215 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
216
217 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool);
218 pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name,
219 512, 512, NULL);
220 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool);
221 pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name,
222 512, 512, NULL);
223
224 /* Attach to evsub */
225 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
226
227 *p_evsub = sub;
228
229 on_return:
230 pjsip_dlg_dec_lock(dlg);
231 return status;
232 }
233
234
235 /*
236 * Create server subscription.
237 */
pjsip_pres_create_uas(pjsip_dialog * dlg,const pjsip_evsub_user * user_cb,pjsip_rx_data * rdata,pjsip_evsub ** p_evsub)238 PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
239 const pjsip_evsub_user *user_cb,
240 pjsip_rx_data *rdata,
241 pjsip_evsub **p_evsub )
242 {
243 pjsip_accept_hdr *accept;
244 pjsip_event_hdr *event;
245 content_type_e content_type = CONTENT_TYPE_NONE;
246 pjsip_evsub *sub;
247 pjsip_pres *pres;
248 char obj_name[PJ_MAX_OBJ_NAME];
249 pj_status_t status;
250
251 /* Check arguments */
252 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
253
254 /* Must be request message */
255 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
256 PJSIP_ENOTREQUESTMSG);
257
258 /* Check that request is SUBSCRIBE */
259 PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
260 &pjsip_subscribe_method)==0,
261 PJSIP_SIMPLE_ENOTSUBSCRIBE);
262
263 /* Check that Event header contains "presence" */
264 event = (pjsip_event_hdr*)
265 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
266 if (!event) {
267 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
268 }
269 if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) {
270 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
271 }
272
273 /* Check that request contains compatible Accept header. */
274 accept = (pjsip_accept_hdr*)
275 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
276 if (accept) {
277 unsigned i;
278 for (i=0; i<accept->count; ++i) {
279 if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) {
280 content_type = CONTENT_TYPE_PIDF;
281 break;
282 } else
283 if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) {
284 content_type = CONTENT_TYPE_XPIDF;
285 break;
286 }
287 }
288
289 if (i==accept->count) {
290 /* Nothing is acceptable */
291 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
292 }
293
294 } else {
295 /* No Accept header.
296 * Treat as "application/pidf+xml"
297 */
298 content_type = CONTENT_TYPE_PIDF;
299 }
300
301 /* Lock dialog */
302 pjsip_dlg_inc_lock(dlg);
303
304
305 /* Create server subscription */
306 status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub);
307 if (status != PJ_SUCCESS)
308 goto on_return;
309
310 /* Create server presence subscription */
311 pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
312 pres->dlg = dlg;
313 pres->sub = sub;
314 pres->content_type = content_type;
315 if (user_cb)
316 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
317
318 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool);
319 pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name,
320 512, 512, NULL);
321 pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool);
322 pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name,
323 512, 512, NULL);
324
325 /* Attach to evsub */
326 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
327
328 /* Done: */
329 *p_evsub = sub;
330
331 on_return:
332 pjsip_dlg_dec_lock(dlg);
333 return status;
334 }
335
336
337 /*
338 * Forcefully terminate presence.
339 */
pjsip_pres_terminate(pjsip_evsub * sub,pj_bool_t notify)340 PJ_DEF(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub,
341 pj_bool_t notify )
342 {
343 return pjsip_evsub_terminate(sub, notify);
344 }
345
346 /*
347 * Create SUBSCRIBE
348 */
pjsip_pres_initiate(pjsip_evsub * sub,pj_uint32_t expires,pjsip_tx_data ** p_tdata)349 PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
350 pj_uint32_t expires,
351 pjsip_tx_data **p_tdata)
352 {
353 return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
354 p_tdata);
355 }
356
357
358 /*
359 * Add custom headers.
360 */
pjsip_pres_add_header(pjsip_evsub * sub,const pjsip_hdr * hdr_list)361 PJ_DEF(pj_status_t) pjsip_pres_add_header( pjsip_evsub *sub,
362 const pjsip_hdr *hdr_list )
363 {
364 return pjsip_evsub_add_header( sub, hdr_list );
365 }
366
367
368 /*
369 * Accept incoming subscription.
370 */
pjsip_pres_accept(pjsip_evsub * sub,pjsip_rx_data * rdata,int st_code,const pjsip_hdr * hdr_list)371 PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
372 pjsip_rx_data *rdata,
373 int st_code,
374 const pjsip_hdr *hdr_list )
375 {
376 return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
377 }
378
379
380 /*
381 * Get presence status.
382 */
pjsip_pres_get_status(pjsip_evsub * sub,pjsip_pres_status * status)383 PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
384 pjsip_pres_status *status )
385 {
386 pjsip_pres *pres;
387
388 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
389
390 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
391 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
392
393 if (pres->tmp_status._is_valid) {
394 PJ_ASSERT_RETURN(pres->tmp_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
395 pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
396 } else {
397 PJ_ASSERT_RETURN(pres->status_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
398 pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
399 }
400
401 return PJ_SUCCESS;
402 }
403
404
405 /*
406 * Set presence status.
407 */
pjsip_pres_set_status(pjsip_evsub * sub,const pjsip_pres_status * status)408 PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
409 const pjsip_pres_status *status )
410 {
411 unsigned i;
412 pj_pool_t *tmp;
413 pjsip_pres *pres;
414
415 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
416
417 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
418 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
419
420 for (i=0; i<status->info_cnt; ++i) {
421 pres->status.info[i].basic_open = status->info[i].basic_open;
422 if (pres->status.info[i].id.slen) {
423 /* Id already set */
424 } else if (status->info[i].id.slen == 0) {
425 pj_create_unique_string(pres->dlg->pool,
426 &pres->status.info[i].id);
427 } else {
428 pj_strdup(pres->dlg->pool,
429 &pres->status.info[i].id,
430 &status->info[i].id);
431 }
432 pj_strdup(pres->tmp_pool,
433 &pres->status.info[i].contact,
434 &status->info[i].contact);
435
436 /* Duplicate <person> */
437 pres->status.info[i].rpid.activity =
438 status->info[i].rpid.activity;
439 pj_strdup(pres->tmp_pool,
440 &pres->status.info[i].rpid.id,
441 &status->info[i].rpid.id);
442 pj_strdup(pres->tmp_pool,
443 &pres->status.info[i].rpid.note,
444 &status->info[i].rpid.note);
445
446 }
447
448 pres->status.info_cnt = status->info_cnt;
449
450 /* Swap pools */
451 tmp = pres->tmp_pool;
452 pres->tmp_pool = pres->status_pool;
453 pres->status_pool = tmp;
454 pj_pool_reset(pres->tmp_pool);
455
456 return PJ_SUCCESS;
457 }
458
459
460 /*
461 * Create message body.
462 */
pres_create_msg_body(pjsip_pres * pres,pjsip_tx_data * tdata)463 static pj_status_t pres_create_msg_body( pjsip_pres *pres,
464 pjsip_tx_data *tdata)
465 {
466 pj_str_t entity;
467
468 /* Get publisher URI */
469 entity.ptr = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE);
470 entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
471 pres->dlg->local.info->uri,
472 entity.ptr, PJSIP_MAX_URL_SIZE);
473 if (entity.slen < 1)
474 return PJ_ENOMEM;
475
476 if (pres->content_type == CONTENT_TYPE_PIDF) {
477
478 return pjsip_pres_create_pidf(tdata->pool, &pres->status,
479 &entity, &tdata->msg->body);
480
481 } else if (pres->content_type == CONTENT_TYPE_XPIDF) {
482
483 return pjsip_pres_create_xpidf(tdata->pool, &pres->status,
484 &entity, &tdata->msg->body);
485
486 } else {
487 return PJSIP_SIMPLE_EBADCONTENT;
488 }
489 }
490
491
492 /*
493 * Create NOTIFY
494 */
pjsip_pres_notify(pjsip_evsub * sub,pjsip_evsub_state state,const pj_str_t * state_str,const pj_str_t * reason,pjsip_tx_data ** p_tdata)495 PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub,
496 pjsip_evsub_state state,
497 const pj_str_t *state_str,
498 const pj_str_t *reason,
499 pjsip_tx_data **p_tdata)
500 {
501 pjsip_pres *pres;
502 pjsip_tx_data *tdata;
503 pj_status_t status;
504
505 /* Check arguments. */
506 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
507
508 /* Get the presence object. */
509 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
510 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
511
512 /* Must have at least one presence info, unless state is
513 * PJSIP_EVSUB_STATE_TERMINATED. This could happen if subscription
514 * has not been active (e.g. we're waiting for user authorization)
515 * and remote cancels the subscription.
516 */
517 PJ_ASSERT_RETURN(state==PJSIP_EVSUB_STATE_TERMINATED ||
518 pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
519
520
521 /* Lock object. */
522 pjsip_dlg_inc_lock(pres->dlg);
523
524 /* Create the NOTIFY request. */
525 status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
526 if (status != PJ_SUCCESS)
527 goto on_return;
528
529
530 /* Create message body to reflect the presence status.
531 * Only do this if we have presence status info to send (see above).
532 */
533 if (pres->status.info_cnt > 0) {
534 status = pres_create_msg_body( pres, tdata );
535 if (status != PJ_SUCCESS)
536 goto on_return;
537 }
538
539 /* Done. */
540 *p_tdata = tdata;
541
542
543 on_return:
544 pjsip_dlg_dec_lock(pres->dlg);
545 return status;
546 }
547
548
549 /*
550 * Create NOTIFY that reflect current state.
551 */
pjsip_pres_current_notify(pjsip_evsub * sub,pjsip_tx_data ** p_tdata)552 PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
553 pjsip_tx_data **p_tdata )
554 {
555 pjsip_pres *pres;
556 pjsip_tx_data *tdata;
557 pj_status_t status;
558
559 /* Check arguments. */
560 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
561
562 /* Get the presence object. */
563 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
564 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
565
566 /* We may not have a presence info yet, e.g. when we receive SUBSCRIBE
567 * to refresh subscription while we're waiting for user authorization.
568 */
569 //PJ_ASSERT_RETURN(pres->status.info_cnt > 0,
570 // PJSIP_SIMPLE_ENOPRESENCEINFO);
571
572
573 /* Lock object. */
574 pjsip_dlg_inc_lock(pres->dlg);
575
576 /* Create the NOTIFY request. */
577 status = pjsip_evsub_current_notify( sub, &tdata);
578 if (status != PJ_SUCCESS)
579 goto on_return;
580
581
582 /* Create message body to reflect the presence status. */
583 if (pres->status.info_cnt > 0) {
584 status = pres_create_msg_body( pres, tdata );
585 if (status != PJ_SUCCESS)
586 goto on_return;
587 }
588
589 /* Done. */
590 *p_tdata = tdata;
591
592
593 on_return:
594 pjsip_dlg_dec_lock(pres->dlg);
595 return status;
596 }
597
598
599 /*
600 * Send request.
601 */
pjsip_pres_send_request(pjsip_evsub * sub,pjsip_tx_data * tdata)602 PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
603 pjsip_tx_data *tdata )
604 {
605 return pjsip_evsub_send_request(sub, tdata);
606 }
607
608
609 /*
610 * This callback is called by event subscription when subscription
611 * state has changed.
612 */
pres_on_evsub_state(pjsip_evsub * sub,pjsip_event * event)613 static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
614 {
615 pjsip_pres *pres;
616
617 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
618 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
619
620 if (pres->user_cb.on_evsub_state)
621 (*pres->user_cb.on_evsub_state)(sub, event);
622
623 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
624 if (pres->status_pool) {
625 pj_pool_release(pres->status_pool);
626 pres->status_pool = NULL;
627 }
628 if (pres->tmp_pool) {
629 pj_pool_release(pres->tmp_pool);
630 pres->tmp_pool = NULL;
631 }
632 }
633 }
634
635 /*
636 * Called when transaction state has changed.
637 */
pres_on_evsub_tsx_state(pjsip_evsub * sub,pjsip_transaction * tsx,pjsip_event * event)638 static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
639 pjsip_event *event)
640 {
641 pjsip_pres *pres;
642
643 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
644 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
645
646 if (pres->user_cb.on_tsx_state)
647 (*pres->user_cb.on_tsx_state)(sub, tsx, event);
648 }
649
650
651 /*
652 * Called when SUBSCRIBE is received.
653 */
pres_on_evsub_rx_refresh(pjsip_evsub * sub,pjsip_rx_data * rdata,int * p_st_code,pj_str_t ** p_st_text,pjsip_hdr * res_hdr,pjsip_msg_body ** p_body)654 static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
655 pjsip_rx_data *rdata,
656 int *p_st_code,
657 pj_str_t **p_st_text,
658 pjsip_hdr *res_hdr,
659 pjsip_msg_body **p_body)
660 {
661 pjsip_pres *pres;
662
663 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
664 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
665
666 if (pres->user_cb.on_rx_refresh) {
667 (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
668 res_hdr, p_body);
669
670 } else {
671 /* Implementors MUST send NOTIFY if it implements on_rx_refresh */
672 pjsip_tx_data *tdata;
673 pj_str_t timeout = { "timeout", 7};
674 pj_status_t status;
675
676 if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
677 status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
678 NULL, &timeout, &tdata);
679 } else {
680 status = pjsip_pres_current_notify(sub, &tdata);
681 }
682
683 if (status == PJ_SUCCESS)
684 pjsip_pres_send_request(sub, tdata);
685 }
686 }
687
688
689 /*
690 * Process the content of incoming NOTIFY request and update temporary
691 * status.
692 *
693 * return PJ_SUCCESS if incoming request is acceptable. If return value
694 * is not PJ_SUCCESS, res_hdr may be added with Warning header.
695 */
pres_process_rx_notify(pjsip_pres * pres,pjsip_rx_data * rdata,int * p_st_code,pj_str_t ** p_st_text,pjsip_hdr * res_hdr)696 static pj_status_t pres_process_rx_notify( pjsip_pres *pres,
697 pjsip_rx_data *rdata,
698 int *p_st_code,
699 pj_str_t **p_st_text,
700 pjsip_hdr *res_hdr)
701 {
702 const pj_str_t STR_MULTIPART = { "multipart", 9 };
703 pjsip_ctype_hdr *ctype_hdr;
704 pj_status_t status = PJ_SUCCESS;
705
706 *p_st_text = NULL;
707
708 /* Check Content-Type and msg body are present. */
709 ctype_hdr = rdata->msg_info.ctype;
710
711 if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) {
712
713 pjsip_warning_hdr *warn_hdr;
714 pj_str_t warn_text;
715
716 *p_st_code = PJSIP_SC_BAD_REQUEST;
717
718 warn_text = pj_str("Message body is not present");
719 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
720 pjsip_endpt_name(pres->dlg->endpt),
721 &warn_text);
722 pj_list_push_back(res_hdr, warn_hdr);
723
724 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
725 }
726
727 /* Parse content. */
728 if (pj_stricmp(&ctype_hdr->media.type, &STR_MULTIPART)==0) {
729 pjsip_multipart_part *mpart;
730 pjsip_media_type ctype;
731
732 pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION,
733 (pj_str_t*)&STR_PIDF_XML);
734 mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body,
735 &ctype, NULL);
736 if (mpart) {
737 status = pjsip_pres_parse_pidf2((char*)mpart->body->data,
738 mpart->body->len, pres->tmp_pool,
739 &pres->tmp_status);
740 }
741
742 if (mpart==NULL) {
743 pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION,
744 (pj_str_t*)&STR_XPIDF_XML);
745 mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body,
746 &ctype, NULL);
747 if (mpart) {
748 status = pjsip_pres_parse_xpidf2((char*)mpart->body->data,
749 mpart->body->len,
750 pres->tmp_pool,
751 &pres->tmp_status);
752 } else {
753 status = PJSIP_SIMPLE_EBADCONTENT;
754 }
755 }
756 }
757 else
758 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
759 pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
760 {
761 status = pjsip_pres_parse_pidf( rdata, pres->tmp_pool,
762 &pres->tmp_status);
763 }
764 else
765 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
766 pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
767 {
768 status = pjsip_pres_parse_xpidf( rdata, pres->tmp_pool,
769 &pres->tmp_status);
770 }
771 else
772 {
773 status = PJSIP_SIMPLE_EBADCONTENT;
774 }
775
776 if (status != PJ_SUCCESS) {
777 /* Unsupported or bad Content-Type */
778 if (PJSIP_PRES_BAD_CONTENT_RESPONSE >= 300) {
779 pjsip_accept_hdr *accept_hdr;
780 pjsip_warning_hdr *warn_hdr;
781
782 *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE;
783
784 /* Add Accept header */
785 accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool);
786 accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML;
787 accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML;
788 pj_list_push_back(res_hdr, accept_hdr);
789
790 /* Add Warning header */
791 warn_hdr = pjsip_warning_hdr_create_from_status(
792 rdata->tp_info.pool,
793 pjsip_endpt_name(pres->dlg->endpt),
794 status);
795 pj_list_push_back(res_hdr, warn_hdr);
796
797 return status;
798 } else {
799 pj_assert(PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 2);
800 PJ_PERROR(4,(THIS_FILE, status,
801 "Ignoring presence error due to "
802 "PJSIP_PRES_BAD_CONTENT_RESPONSE setting [%d]",
803 PJSIP_PRES_BAD_CONTENT_RESPONSE));
804 *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE;
805 status = PJ_SUCCESS;
806 }
807 }
808
809 /* If application calls pres_get_status(), redirect the call to
810 * retrieve the temporary status.
811 */
812 pres->tmp_status._is_valid = PJ_TRUE;
813
814 return PJ_SUCCESS;
815 }
816
817
818 /*
819 * Called when NOTIFY is received.
820 */
pres_on_evsub_rx_notify(pjsip_evsub * sub,pjsip_rx_data * rdata,int * p_st_code,pj_str_t ** p_st_text,pjsip_hdr * res_hdr,pjsip_msg_body ** p_body)821 static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
822 pjsip_rx_data *rdata,
823 int *p_st_code,
824 pj_str_t **p_st_text,
825 pjsip_hdr *res_hdr,
826 pjsip_msg_body **p_body)
827 {
828 pjsip_pres *pres;
829 pj_status_t status;
830
831 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
832 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
833
834 if (rdata->msg_info.msg->body) {
835 status = pres_process_rx_notify( pres, rdata, p_st_code, p_st_text,
836 res_hdr );
837 if (status != PJ_SUCCESS)
838 return;
839
840 } else {
841 #if 1
842 /* This is the newest change, http://trac.pjsip.org/repos/ticket/873
843 * Some app want to be notified about the empty NOTIFY, e.g. to
844 * decide whether it should consider the buddy as offline.
845 * In this case, leave the buddy state unchanged, but set the
846 * "tuple_node" in pjsip_pres_status to NULL.
847 */
848 unsigned i;
849 for (i=0; i<pres->status.info_cnt; ++i) {
850 pres->status.info[i].tuple_node = NULL;
851 }
852
853 #elif 0
854 /* This has just been changed. Previously, we treat incoming NOTIFY
855 * with no message body as having the presence subscription closed.
856 * Now we treat it as no change in presence status (ref: EyeBeam).
857 */
858 *p_st_code = 200;
859 return;
860 #else
861 unsigned i;
862 /* Subscription is terminated. Consider contact is offline */
863 pres->tmp_status._is_valid = PJ_TRUE;
864 for (i=0; i<pres->tmp_status.info_cnt; ++i)
865 pres->tmp_status.info[i].basic_open = PJ_FALSE;
866 #endif
867 }
868
869 /* Notify application. */
870 if (pres->user_cb.on_rx_notify) {
871 (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
872 res_hdr, p_body);
873 }
874
875
876 /* If application responded NOTIFY with 2xx, copy temporary status
877 * to main status, and mark the temporary status as invalid.
878 */
879 if ((*p_st_code)/100 == 2) {
880 pj_pool_t *tmp;
881
882 pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
883
884 /* Swap the pool */
885 tmp = pres->tmp_pool;
886 pres->tmp_pool = pres->status_pool;
887 pres->status_pool = tmp;
888 }
889
890 pres->tmp_status._is_valid = PJ_FALSE;
891 pj_pool_reset(pres->tmp_pool);
892
893 /* Done */
894 }
895
896 /*
897 * Called when it's time to send SUBSCRIBE.
898 */
pres_on_evsub_client_refresh(pjsip_evsub * sub)899 static void pres_on_evsub_client_refresh(pjsip_evsub *sub)
900 {
901 pjsip_pres *pres;
902
903 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
904 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
905
906 if (pres->user_cb.on_client_refresh) {
907 (*pres->user_cb.on_client_refresh)(sub);
908 } else {
909 pj_status_t status;
910 pjsip_tx_data *tdata;
911
912 status = pjsip_pres_initiate(sub, PJSIP_EXPIRES_NOT_SPECIFIED,
913 &tdata);
914 if (status == PJ_SUCCESS)
915 pjsip_pres_send_request(sub, tdata);
916 }
917 }
918
919 /*
920 * Called when no refresh is received after the interval.
921 */
pres_on_evsub_server_timeout(pjsip_evsub * sub)922 static void pres_on_evsub_server_timeout(pjsip_evsub *sub)
923 {
924 pjsip_pres *pres;
925
926 pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
927 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
928
929 if (pres->user_cb.on_server_timeout) {
930 (*pres->user_cb.on_server_timeout)(sub);
931 } else {
932 pj_status_t status;
933 pjsip_tx_data *tdata;
934 pj_str_t reason = { "timeout", 7 };
935
936 status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
937 NULL, &reason, &tdata);
938 if (status == PJ_SUCCESS)
939 pjsip_pres_send_request(sub, tdata);
940 }
941 }
942
943