blob: c47ae1fa752b3e96494bf81a74c90778fc56ec1a [file] [log] [blame]
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono834aee32006-02-19 01:38:06 +000019#include <pjsip-simple/presence.h>
20#include <pjsip-simple/errno.h>
21#include <pjsip-simple/evsub_msg.h>
22#include <pjsip/sip_module.h>
23#include <pjsip/sip_endpoint.h>
24#include <pjsip/sip_dialog.h>
25#include <pj/assert.h>
26#include <pj/guid.h>
27#include <pj/log.h>
28#include <pj/os.h>
Benny Prijono5dcb38d2005-11-21 01:55:47 +000029#include <pj/pool.h>
30#include <pj/string.h>
Benny Prijono5dcb38d2005-11-21 01:55:47 +000031
Benny Prijono5dcb38d2005-11-21 01:55:47 +000032
Benny Prijono834aee32006-02-19 01:38:06 +000033#define THIS_FILE "presence.c"
34#define PRES_DEFAULT_EXPIRES 600
Benny Prijono5dcb38d2005-11-21 01:55:47 +000035
36/*
Benny Prijono834aee32006-02-19 01:38:06 +000037 * Presence module (mod-presence)
Benny Prijono5dcb38d2005-11-21 01:55:47 +000038 */
Benny Prijono834aee32006-02-19 01:38:06 +000039static struct pjsip_module mod_presence =
Benny Prijono5dcb38d2005-11-21 01:55:47 +000040{
Benny Prijono834aee32006-02-19 01:38:06 +000041 NULL, NULL, /* prev, next. */
42 { "mod-presence", 12 }, /* Name. */
43 -1, /* Id */
44 PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */
45 NULL, /* User data. */
46 NULL, /* load() */
47 NULL, /* start() */
48 NULL, /* stop() */
49 NULL, /* unload() */
50 NULL, /* on_rx_request() */
51 NULL, /* on_rx_response() */
52 NULL, /* on_tx_request. */
53 NULL, /* on_tx_response() */
54 NULL, /* on_tsx_state() */
55};
56
57
58/*
59 * Presence message body type.
60 */
61typedef enum content_type
62{
63 CONTENT_TYPE_NONE,
64 CONTENT_TYPE_PIDF,
65 CONTENT_TYPE_XPIDF,
66} content_type;
67
68/*
69 * This structure describe a presentity, for both subscriber and notifier.
70 */
71struct pjsip_pres
72{
73 pjsip_evsub *sub; /**< Event subscribtion record. */
74 pjsip_dialog *dlg; /**< The dialog. */
75 content_type content_type; /**< Content-Type. */
76 pjsip_pres_status status; /**< Presence status. */
77 pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/
78 pjsip_evsub_user user_cb; /**< The user callback. */
79};
80
81
82typedef struct pjsip_pres pjsip_pres;
83
84
85/*
86 * Forward decl for evsub callback.
87 */
88static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
89static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
90 pjsip_event *event);
91static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
92 pjsip_rx_data *rdata,
93 int *p_st_code,
94 pj_str_t **p_st_text,
95 pjsip_hdr *res_hdr,
96 pjsip_msg_body **p_body);
97static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
98 pjsip_rx_data *rdata,
99 int *p_st_code,
100 pj_str_t **p_st_text,
101 pjsip_hdr *res_hdr,
102 pjsip_msg_body **p_body);
103static void pres_on_evsub_client_refresh(pjsip_evsub *sub);
104static void pres_on_evsub_server_timeout(pjsip_evsub *sub);
105
106
107/*
108 * Event subscription callback for presence.
109 */
110static pjsip_evsub_user pres_user =
111{
112 &pres_on_evsub_state,
113 &pres_on_evsub_tsx_state,
114 &pres_on_evsub_rx_refresh,
115 &pres_on_evsub_rx_notify,
116 &pres_on_evsub_client_refresh,
117 &pres_on_evsub_server_timeout,
118};
119
120
121/*
122 * Some static constants.
123 */
124const pj_str_t STR_EVENT = { "Event", 5 };
125const pj_str_t STR_PRESENCE = { "presence", 8 };
126const pj_str_t STR_APPLICATION = { "application", 11 };
127const pj_str_t STR_PIDF_XML = { "pidf+xml", 8};
128const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9};
129const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 };
130const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 };
131
132
133/*
134 * Init presence module.
135 */
136PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt,
137 pjsip_module *mod_evsub)
138{
139 pj_status_t status;
140 pj_str_t accept[2];
141
142 /* Check arguments. */
143 PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
144
145 /* Must have not been registered */
146 PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP);
147
148 /* Register to endpoint */
149 status = pjsip_endpt_register_module(endpt, &mod_presence);
150 if (status != PJ_SUCCESS)
151 return status;
152
153 accept[0] = STR_APP_PIDF_XML;
154 accept[1] = STR_APP_XPIDF_XML;
155
156 /* Register event package to event module. */
157 status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE,
158 PRES_DEFAULT_EXPIRES,
159 PJ_ARRAY_SIZE(accept), accept);
160 if (status != PJ_SUCCESS) {
161 pjsip_endpt_unregister_module(endpt, &mod_presence);
162 return status;
163 }
164
165 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000166}
167
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000168
Benny Prijono834aee32006-02-19 01:38:06 +0000169/*
170 * Get presence module instance.
171 */
172PJ_DEF(pjsip_module*) pjsip_pres_instance(void)
173{
174 return &mod_presence;
175}
176
177
178/*
179 * Create client subscription.
180 */
181PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
182 const pjsip_evsub_user *user_cb,
183 pjsip_evsub **p_evsub )
184{
185 pj_status_t status;
186 pjsip_pres *pres;
187 pjsip_evsub *sub;
188
189 PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
190
191 pjsip_dlg_inc_lock(dlg);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000192
193 /* Create event subscription */
Benny Prijono26ff9062006-02-21 23:47:00 +0000194 status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE, 0, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000195 if (status != PJ_SUCCESS)
196 goto on_return;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000197
Benny Prijono834aee32006-02-19 01:38:06 +0000198 /* Create presence */
199 pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres));
200 pres->dlg = dlg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000201 pres->sub = sub;
Benny Prijono834aee32006-02-19 01:38:06 +0000202 if (user_cb)
203 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000204
Benny Prijono834aee32006-02-19 01:38:06 +0000205 /* Attach to evsub */
206 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
207
208 *p_evsub = sub;
209
210on_return:
211 pjsip_dlg_dec_lock(dlg);
212 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000213}
214
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000215
216/*
Benny Prijono834aee32006-02-19 01:38:06 +0000217 * Create server subscription.
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000218 */
Benny Prijono834aee32006-02-19 01:38:06 +0000219PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
220 const pjsip_evsub_user *user_cb,
221 pjsip_rx_data *rdata,
222 pjsip_evsub **p_evsub )
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000223{
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000224 pjsip_accept_hdr *accept;
Benny Prijono834aee32006-02-19 01:38:06 +0000225 pjsip_event_hdr *event;
226 pjsip_expires_hdr *expires_hdr;
227 unsigned expires;
228 content_type content_type = CONTENT_TYPE_NONE;
229 pjsip_evsub *sub;
230 pjsip_pres *pres;
231 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000232
Benny Prijono834aee32006-02-19 01:38:06 +0000233 /* Check arguments */
234 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000235
Benny Prijono834aee32006-02-19 01:38:06 +0000236 /* Must be request message */
237 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
238 PJSIP_ENOTREQUESTMSG);
239
240 /* Check that request is SUBSCRIBE */
241 PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
242 &pjsip_subscribe_method)==0,
243 PJSIP_SIMPLE_ENOTSUBSCRIBE);
244
245 /* Check that Event header contains "presence" */
246 event = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
247 if (!event) {
248 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
249 }
250 if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) {
251 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
252 }
253
254 /* Check that request contains compatible Accept header. */
255 accept = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000256 if (accept) {
257 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +0000258 for (i=0; i<accept->count; ++i) {
259 if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) {
260 content_type = CONTENT_TYPE_PIDF;
261 break;
262 } else
263 if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) {
264 content_type = CONTENT_TYPE_XPIDF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000265 break;
266 }
267 }
268
Benny Prijono834aee32006-02-19 01:38:06 +0000269 if (i==accept->count) {
270 /* Nothing is acceptable */
271 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000272 }
273
Benny Prijono834aee32006-02-19 01:38:06 +0000274 } else {
275 /* No Accept header.
276 * Treat as "application/pidf+xml"
277 */
278 content_type = CONTENT_TYPE_PIDF;
279 }
280
281 /* Check that expires is not too short. */
282 expires_hdr=pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
283 if (expires_hdr) {
284 if (expires_hdr->ivalue < 5) {
285 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_INTERVAL_TOO_BRIEF);
286 }
287
288 expires = expires_hdr->ivalue;
289 if (expires > PRES_DEFAULT_EXPIRES)
290 expires = PRES_DEFAULT_EXPIRES;
291
292 } else {
293 expires = PRES_DEFAULT_EXPIRES;
294 }
295
296 /* Lock dialog */
297 pjsip_dlg_inc_lock(dlg);
298
299
300 /* Create server subscription */
Benny Prijono26ff9062006-02-21 23:47:00 +0000301 status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000302 if (status != PJ_SUCCESS)
303 goto on_return;
304
305 /* Create server presence subscription */
306 pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres));
307 pres->dlg = dlg;
308 pres->sub = sub;
309 pres->content_type = content_type;
310 if (user_cb)
311 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
312
313 /* Attach to evsub */
314 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
315
316 /* Done: */
317 *p_evsub = sub;
318
319on_return:
320 pjsip_dlg_dec_lock(dlg);
321 return status;
322}
323
324
325/*
326 * Create SUBSCRIBE
327 */
328PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
329 pj_int32_t expires,
330 pjsip_tx_data **p_tdata)
331{
332 return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
333 p_tdata);
334}
335
336
337/*
338 * Accept incoming subscription.
339 */
340PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
341 pjsip_rx_data *rdata,
342 int st_code,
343 const pjsip_hdr *hdr_list )
344{
345 return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
346}
347
348
349/*
350 * Get presence status.
351 */
352PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
353 pjsip_pres_status *status )
354{
355 pjsip_pres *pres;
356
357 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
358
359 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
360 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
361
362 if (pres->tmp_status._is_valid)
363 pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
364 else
365 pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
366
367 return PJ_SUCCESS;
368}
369
370
371/*
372 * Set presence status.
373 */
374PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
375 const pjsip_pres_status *status )
376{
377 unsigned i;
378 pjsip_pres *pres;
379
380 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
381
382 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
383 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
384
385 for (i=0; i<status->info_cnt; ++i) {
386 pres->status.info[i].basic_open = status->info[i].basic_open;
387 if (status->info[i].id.slen == 0) {
388 pj_create_unique_string(pres->dlg->pool,
389 &pres->status.info[i].id);
390 } else {
391 pj_strdup(pres->dlg->pool,
392 &pres->status.info[i].id,
393 &status->info[i].id);
394 }
395 pj_strdup(pres->dlg->pool,
396 &pres->status.info[i].contact,
397 &status->info[i].contact);
398 }
399
400 pres->status.info_cnt = status->info_cnt;
401
402 return PJ_SUCCESS;
403}
404
405
406/*
407 * Create PIDF document based on the presence info.
408 */
409static pjpidf_pres* pres_create_pidf( pj_pool_t *pool,
410 pjsip_pres *pres )
411{
412 pjpidf_pres *pidf;
413 unsigned i;
414 pj_str_t entity;
415
416 /* Get publisher URI */
417 entity.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
418 entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
419 pres->dlg->local.info->uri,
420 entity.ptr, PJSIP_MAX_URL_SIZE);
421 if (entity.slen < 1)
422 return NULL;
423
424 /* Create <presence>. */
425 pidf = pjpidf_create(pool, &entity);
426
427 /* Create <tuple> */
428 for (i=0; i<pres->status.info_cnt; ++i) {
429
430 pjpidf_tuple *pidf_tuple;
431 pjpidf_status *pidf_status;
432
433 /* Add tuple id. */
434 pidf_tuple = pjpidf_pres_add_tuple(pool, pidf,
435 &pres->status.info[i].id);
436
437 /* Set <contact> */
438 if (pres->status.info[i].contact.slen)
439 pjpidf_tuple_set_contact(pool, pidf_tuple,
440 &pres->status.info[i].contact);
441
442
443 /* Set basic status */
444 pidf_status = pjpidf_tuple_get_status(pidf_tuple);
445 pjpidf_status_set_basic_open(pidf_status,
446 pres->status.info[i].basic_open);
447 }
448
449 return pidf;
450}
451
452
453/*
454 * Create XPIDF document based on the presence info.
455 */
456static pjxpidf_pres* pres_create_xpidf( pj_pool_t *pool,
457 pjsip_pres *pres )
458{
459 /* Note: PJSIP implementation of XPIDF is not complete!
460 */
461 pjxpidf_pres *xpidf;
462 pj_str_t publisher_uri;
463
464 PJ_LOG(4,(THIS_FILE, "Warning: XPIDF format is not fully supported "
465 "by PJSIP"));
466
467 publisher_uri.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
468 publisher_uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
469 pres->dlg->local.info->uri,
470 publisher_uri.ptr,
471 PJSIP_MAX_URL_SIZE);
472 if (publisher_uri.slen < 1)
473 return NULL;
474
475 /* Create XPIDF document. */
476 xpidf = pjxpidf_create(pool, &publisher_uri);
477
478 /* Set basic status. */
479 if (pres->status.info_cnt > 0)
480 pjxpidf_set_status( xpidf, pres->status.info[0].basic_open);
481 else
482 pjxpidf_set_status( xpidf, PJ_FALSE);
483
484 return xpidf;
485}
486
487
488/*
489 * Function to print XML message body.
490 */
491static int pres_print_body(struct pjsip_msg_body *msg_body,
492 char *buf, pj_size_t size)
493{
494 return pj_xml_print(msg_body->data, buf, size, PJ_TRUE);
495}
496
497
498/*
499 * Function to clone XML document.
500 */
501static void* xml_clone_data(pj_pool_t *pool, const void *data, unsigned len)
502{
503 PJ_UNUSED_ARG(len);
504 return pj_xml_clone( pool, data);
505}
506
507
508/*
509 * Create message body.
510 */
511static pj_status_t pres_create_msg_body( pjsip_pres *pres,
512 pjsip_tx_data *tdata)
513{
514 pjsip_msg_body *body;
515
516 body = pj_pool_zalloc(tdata->pool, sizeof(pjsip_msg_body));
517
518 if (pres->content_type == CONTENT_TYPE_PIDF) {
519
520 body->data = pres_create_pidf(tdata->pool, pres);
521 body->content_type.type = pj_str("application");
522 body->content_type.subtype = pj_str("pidf+xml");
523
524 } else if (pres->content_type == CONTENT_TYPE_XPIDF) {
525
526 body->data = pres_create_xpidf(tdata->pool, pres);
527 body->content_type.type = pj_str("application");
528 body->content_type.subtype = pj_str("xpidf+xml");
529
530 } else {
531 return PJSIP_SIMPLE_EBADCONTENT;
532 }
533
534
535 body->print_body = &pres_print_body;
536 body->clone_data = &xml_clone_data;
537
538 tdata->msg->body = body;
539
540 return PJ_SUCCESS;
541}
542
543
544/*
545 * Create NOTIFY
546 */
547PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub,
548 pjsip_evsub_state state,
549 const pj_str_t *state_str,
550 const pj_str_t *reason,
551 pjsip_tx_data **p_tdata)
552{
553 pjsip_pres *pres;
554 pjsip_tx_data *tdata;
555 pj_status_t status;
556
557 /* Check arguments. */
558 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
559
560 /* Get the presence object. */
561 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
562 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
563
564 /* Must have at least one presence info. */
565 PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
566
567
568 /* Lock object. */
569 pjsip_dlg_inc_lock(pres->dlg);
570
571 /* Create the NOTIFY request. */
572 status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
573 if (status != PJ_SUCCESS)
574 goto on_return;
575
576
577 /* Create message body to reflect the presence status. */
578 status = pres_create_msg_body( pres, tdata );
579 if (status != PJ_SUCCESS)
580 goto on_return;
581
582
583 /* Done. */
584 *p_tdata = tdata;
585
586
587on_return:
588 pjsip_dlg_dec_lock(pres->dlg);
589 return status;
590}
591
592
593/*
594 * Create NOTIFY that reflect current state.
595 */
596PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
597 pjsip_tx_data **p_tdata )
598{
599 pjsip_pres *pres;
600 pjsip_tx_data *tdata;
601 pj_status_t status;
602
603 /* Check arguments. */
604 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
605
606 /* Get the presence object. */
607 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
608 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
609
610 /* Must have at least one presence info. */
611 PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
612
613
614 /* Lock object. */
615 pjsip_dlg_inc_lock(pres->dlg);
616
617 /* Create the NOTIFY request. */
618 status = pjsip_evsub_current_notify( sub, &tdata);
619 if (status != PJ_SUCCESS)
620 goto on_return;
621
622
623 /* Create message body to reflect the presence status. */
624 status = pres_create_msg_body( pres, tdata );
625 if (status != PJ_SUCCESS)
626 goto on_return;
627
628
629 /* Done. */
630 *p_tdata = tdata;
631
632
633on_return:
634 pjsip_dlg_dec_lock(pres->dlg);
635 return status;
636}
637
638
639/*
640 * Send request.
641 */
642PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
643 pjsip_tx_data *tdata )
644{
645 return pjsip_evsub_send_request(sub, tdata);
646}
647
648
649/*
650 * This callback is called by event subscription when subscription
651 * state has changed.
652 */
653static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
654{
655 pjsip_pres *pres;
656
657 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
658 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
659
660 if (pres->user_cb.on_evsub_state)
661 (*pres->user_cb.on_evsub_state)(sub, event);
662}
663
664/*
665 * Called when transaction state has changed.
666 */
667static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
668 pjsip_event *event)
669{
670 pjsip_pres *pres;
671
672 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
673 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
674
675 if (pres->user_cb.on_tsx_state)
676 (*pres->user_cb.on_tsx_state)(sub, tsx, event);
677}
678
679
680/*
681 * Called when SUBSCRIBE is received.
682 */
683static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
684 pjsip_rx_data *rdata,
685 int *p_st_code,
686 pj_str_t **p_st_text,
687 pjsip_hdr *res_hdr,
688 pjsip_msg_body **p_body)
689{
690 pjsip_pres *pres;
691
692 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
693 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
694
695 if (pres->user_cb.on_rx_refresh) {
696 (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
697 res_hdr, p_body);
698
699 } else {
700 /* Implementors MUST send NOTIFY if it implements on_rx_refresh */
701 pjsip_tx_data *tdata;
702 pj_str_t timeout = { "timeout", 7};
703 pj_status_t status;
704
705 if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
706 status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
707 NULL, &timeout, &tdata);
708 } else {
709 status = pjsip_pres_current_notify(sub, &tdata);
710 }
711
712 if (status == PJ_SUCCESS)
713 pjsip_pres_send_request(sub, tdata);
714 }
715}
716
717/*
718 * Parse PIDF to info.
719 */
720static pj_status_t pres_parse_pidf( pjsip_pres *pres,
721 pjsip_rx_data *rdata,
722 pjsip_pres_status *pres_status)
723{
724 pjpidf_pres *pidf;
725 pjpidf_tuple *pidf_tuple;
726
727 pidf = pjpidf_parse(rdata->tp_info.pool,
728 rdata->msg_info.msg->body->data,
729 rdata->msg_info.msg->body->len);
730 if (pidf == NULL)
731 return PJSIP_SIMPLE_EBADPIDF;
732
733 pres_status->info_cnt = 0;
734
735 pidf_tuple = pjpidf_pres_get_first_tuple(pidf);
736 while (pidf_tuple) {
737 pjpidf_status *pidf_status;
738
739 pj_strdup(pres->dlg->pool,
740 &pres_status->info[pres_status->info_cnt].id,
741 pjpidf_tuple_get_id(pidf_tuple));
742
743 pj_strdup(pres->dlg->pool,
744 &pres_status->info[pres_status->info_cnt].contact,
745 pjpidf_tuple_get_contact(pidf_tuple));
746
747 pidf_status = pjpidf_tuple_get_status(pidf_tuple);
748 if (pidf_status) {
749 pres_status->info[pres_status->info_cnt].basic_open =
750 pjpidf_status_is_basic_open(pidf_status);
751 } else {
752 pres_status->info[pres_status->info_cnt].basic_open = PJ_FALSE;
753 }
754
755 pidf_tuple = pjpidf_pres_get_next_tuple( pidf, pidf_tuple );
756 pres_status->info_cnt++;
757 }
758
759 return PJ_SUCCESS;
760}
761
762/*
763 * Parse XPIDF info.
764 */
765static pj_status_t pres_parse_xpidf( pjsip_pres *pres,
766 pjsip_rx_data *rdata,
767 pjsip_pres_status *pres_status)
768{
769 pjxpidf_pres *xpidf;
770
771 xpidf = pjxpidf_parse(rdata->tp_info.pool,
772 rdata->msg_info.msg->body->data,
773 rdata->msg_info.msg->body->len);
774 if (xpidf == NULL)
775 return PJSIP_SIMPLE_EBADXPIDF;
776
777 pres_status->info_cnt = 1;
778
779 pj_strdup(pres->dlg->pool,
780 &pres_status->info[0].contact,
781 pjxpidf_get_uri(xpidf));
782 pres_status->info[0].basic_open = pjxpidf_get_status(xpidf);
783 pres_status->info[0].id.slen = 0;
784
785 return PJ_SUCCESS;
786}
787
788
789/*
790 * Called when NOTIFY is received.
791 */
792static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
793 pjsip_rx_data *rdata,
794 int *p_st_code,
795 pj_str_t **p_st_text,
796 pjsip_hdr *res_hdr,
797 pjsip_msg_body **p_body)
798{
799 pjsip_ctype_hdr *ctype_hdr;
800 pjsip_pres *pres;
801 pj_status_t status;
802
803 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
804 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
805
806 /* Check Content-Type and msg body are present. */
807 ctype_hdr = rdata->msg_info.ctype;
808
809 if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) {
810
811 pjsip_warning_hdr *warn_hdr;
812 pj_str_t warn_text;
813
814 *p_st_code = PJSIP_SC_BAD_REQUEST;
815
816 warn_text = pj_str("Message body is not present");
817 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
818 pjsip_endpt_name(pres->dlg->endpt),
819 &warn_text);
820 pj_list_push_back(res_hdr, warn_hdr);
821
822 return;
823 }
824
825 /* Parse content. */
826
827 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
828 pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
829 {
830 status = pres_parse_pidf( pres, rdata, &pres->tmp_status);
831 }
832 else
833 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
834 pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
835 {
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000836 status = pres_parse_xpidf( pres, rdata, &pres->tmp_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000837 }
838 else
839 {
840 status = PJSIP_SIMPLE_EBADCONTENT;
841 }
842
843 if (status != PJ_SUCCESS) {
844 /* Unsupported or bad Content-Type */
845 pjsip_accept_hdr *accept_hdr;
846 pjsip_warning_hdr *warn_hdr;
847
848 *p_st_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
849
850 /* Add Accept header */
851 accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool);
852 accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML;
853 accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML;
854 pj_list_push_back(res_hdr, accept_hdr);
855
856 /* Add Warning header */
857 warn_hdr = pjsip_warning_hdr_create_from_status(
858 rdata->tp_info.pool,
859 pjsip_endpt_name(pres->dlg->endpt),
860 status);
861 pj_list_push_back(res_hdr, warn_hdr);
862
863 return;
864 }
865
866 /* If application calls pres_get_status(), redirect the call to
867 * retrieve the temporary status.
868 */
869 pres->tmp_status._is_valid = PJ_TRUE;
870
871 /* Notify application. */
872 if (pres->user_cb.on_rx_notify) {
873 (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
874 res_hdr, p_body);
875 }
876
877
878 /* If application responded NOTIFY with 2xx, copy temporary status
879 * to main status, and mark the temporary status as invalid.
880 */
881 if ((*p_st_code)/100 == 2) {
882 pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
883 }
884
885 pres->tmp_status._is_valid = PJ_FALSE;
886
887 /* Done */
888}
889
890/*
891 * Called when it's time to send SUBSCRIBE.
892 */
893static void pres_on_evsub_client_refresh(pjsip_evsub *sub)
894{
895 pjsip_pres *pres;
896
897 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
898 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
899
900 if (pres->user_cb.on_client_refresh) {
901 (*pres->user_cb.on_client_refresh)(sub);
902 } else {
903 pj_status_t status;
904 pjsip_tx_data *tdata;
905
906 status = pjsip_pres_initiate(sub, -1, &tdata);
907 if (status == PJ_SUCCESS)
908 pjsip_pres_send_request(sub, tdata);
909 }
910}
911
912/*
913 * Called when no refresh is received after the interval.
914 */
915static void pres_on_evsub_server_timeout(pjsip_evsub *sub)
916{
917 pjsip_pres *pres;
918
919 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
920 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
921
922 if (pres->user_cb.on_server_timeout) {
923 (*pres->user_cb.on_server_timeout)(sub);
924 } else {
925 pj_status_t status;
926 pjsip_tx_data *tdata;
927 pj_str_t reason = { "timeout", 7 };
928
929 status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
930 NULL, &reason, &tdata);
931 if (status == PJ_SUCCESS)
932 pjsip_pres_send_request(sub, tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000933 }
934}
935