blob: e87cd45126109ad9482307b4aac36aa803cf1003 [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 Prijono834aee32006-02-19 01:38:06 +0000194 status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE, &sub);
195 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;
201 if (user_cb)
202 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000203
Benny Prijono834aee32006-02-19 01:38:06 +0000204 /* Attach to evsub */
205 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
206
207 *p_evsub = sub;
208
209on_return:
210 pjsip_dlg_dec_lock(dlg);
211 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000212}
213
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000214
215/*
Benny Prijono834aee32006-02-19 01:38:06 +0000216 * Create server subscription.
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000217 */
Benny Prijono834aee32006-02-19 01:38:06 +0000218PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
219 const pjsip_evsub_user *user_cb,
220 pjsip_rx_data *rdata,
221 pjsip_evsub **p_evsub )
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000222{
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000223 pjsip_accept_hdr *accept;
Benny Prijono834aee32006-02-19 01:38:06 +0000224 pjsip_event_hdr *event;
225 pjsip_expires_hdr *expires_hdr;
226 unsigned expires;
227 content_type content_type = CONTENT_TYPE_NONE;
228 pjsip_evsub *sub;
229 pjsip_pres *pres;
230 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000231
Benny Prijono834aee32006-02-19 01:38:06 +0000232 /* Check arguments */
233 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000234
Benny Prijono834aee32006-02-19 01:38:06 +0000235 /* Must be request message */
236 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
237 PJSIP_ENOTREQUESTMSG);
238
239 /* Check that request is SUBSCRIBE */
240 PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
241 &pjsip_subscribe_method)==0,
242 PJSIP_SIMPLE_ENOTSUBSCRIBE);
243
244 /* Check that Event header contains "presence" */
245 event = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
246 if (!event) {
247 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
248 }
249 if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) {
250 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
251 }
252
253 /* Check that request contains compatible Accept header. */
254 accept = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000255 if (accept) {
256 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +0000257 for (i=0; i<accept->count; ++i) {
258 if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) {
259 content_type = CONTENT_TYPE_PIDF;
260 break;
261 } else
262 if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) {
263 content_type = CONTENT_TYPE_XPIDF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000264 break;
265 }
266 }
267
Benny Prijono834aee32006-02-19 01:38:06 +0000268 if (i==accept->count) {
269 /* Nothing is acceptable */
270 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000271 }
272
Benny Prijono834aee32006-02-19 01:38:06 +0000273 } else {
274 /* No Accept header.
275 * Treat as "application/pidf+xml"
276 */
277 content_type = CONTENT_TYPE_PIDF;
278 }
279
280 /* Check that expires is not too short. */
281 expires_hdr=pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
282 if (expires_hdr) {
283 if (expires_hdr->ivalue < 5) {
284 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_INTERVAL_TOO_BRIEF);
285 }
286
287 expires = expires_hdr->ivalue;
288 if (expires > PRES_DEFAULT_EXPIRES)
289 expires = PRES_DEFAULT_EXPIRES;
290
291 } else {
292 expires = PRES_DEFAULT_EXPIRES;
293 }
294
295 /* Lock dialog */
296 pjsip_dlg_inc_lock(dlg);
297
298
299 /* Create server subscription */
300 status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, &sub);
301 if (status != PJ_SUCCESS)
302 goto on_return;
303
304 /* Create server presence subscription */
305 pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres));
306 pres->dlg = dlg;
307 pres->sub = sub;
308 pres->content_type = content_type;
309 if (user_cb)
310 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
311
312 /* Attach to evsub */
313 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
314
315 /* Done: */
316 *p_evsub = sub;
317
318on_return:
319 pjsip_dlg_dec_lock(dlg);
320 return status;
321}
322
323
324/*
325 * Create SUBSCRIBE
326 */
327PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
328 pj_int32_t expires,
329 pjsip_tx_data **p_tdata)
330{
331 return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
332 p_tdata);
333}
334
335
336/*
337 * Accept incoming subscription.
338 */
339PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
340 pjsip_rx_data *rdata,
341 int st_code,
342 const pjsip_hdr *hdr_list )
343{
344 return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
345}
346
347
348/*
349 * Get presence status.
350 */
351PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
352 pjsip_pres_status *status )
353{
354 pjsip_pres *pres;
355
356 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
357
358 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
359 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
360
361 if (pres->tmp_status._is_valid)
362 pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
363 else
364 pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
365
366 return PJ_SUCCESS;
367}
368
369
370/*
371 * Set presence status.
372 */
373PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
374 const pjsip_pres_status *status )
375{
376 unsigned i;
377 pjsip_pres *pres;
378
379 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
380
381 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
382 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
383
384 for (i=0; i<status->info_cnt; ++i) {
385 pres->status.info[i].basic_open = status->info[i].basic_open;
386 if (status->info[i].id.slen == 0) {
387 pj_create_unique_string(pres->dlg->pool,
388 &pres->status.info[i].id);
389 } else {
390 pj_strdup(pres->dlg->pool,
391 &pres->status.info[i].id,
392 &status->info[i].id);
393 }
394 pj_strdup(pres->dlg->pool,
395 &pres->status.info[i].contact,
396 &status->info[i].contact);
397 }
398
399 pres->status.info_cnt = status->info_cnt;
400
401 return PJ_SUCCESS;
402}
403
404
405/*
406 * Create PIDF document based on the presence info.
407 */
408static pjpidf_pres* pres_create_pidf( pj_pool_t *pool,
409 pjsip_pres *pres )
410{
411 pjpidf_pres *pidf;
412 unsigned i;
413 pj_str_t entity;
414
415 /* Get publisher URI */
416 entity.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
417 entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
418 pres->dlg->local.info->uri,
419 entity.ptr, PJSIP_MAX_URL_SIZE);
420 if (entity.slen < 1)
421 return NULL;
422
423 /* Create <presence>. */
424 pidf = pjpidf_create(pool, &entity);
425
426 /* Create <tuple> */
427 for (i=0; i<pres->status.info_cnt; ++i) {
428
429 pjpidf_tuple *pidf_tuple;
430 pjpidf_status *pidf_status;
431
432 /* Add tuple id. */
433 pidf_tuple = pjpidf_pres_add_tuple(pool, pidf,
434 &pres->status.info[i].id);
435
436 /* Set <contact> */
437 if (pres->status.info[i].contact.slen)
438 pjpidf_tuple_set_contact(pool, pidf_tuple,
439 &pres->status.info[i].contact);
440
441
442 /* Set basic status */
443 pidf_status = pjpidf_tuple_get_status(pidf_tuple);
444 pjpidf_status_set_basic_open(pidf_status,
445 pres->status.info[i].basic_open);
446 }
447
448 return pidf;
449}
450
451
452/*
453 * Create XPIDF document based on the presence info.
454 */
455static pjxpidf_pres* pres_create_xpidf( pj_pool_t *pool,
456 pjsip_pres *pres )
457{
458 /* Note: PJSIP implementation of XPIDF is not complete!
459 */
460 pjxpidf_pres *xpidf;
461 pj_str_t publisher_uri;
462
463 PJ_LOG(4,(THIS_FILE, "Warning: XPIDF format is not fully supported "
464 "by PJSIP"));
465
466 publisher_uri.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
467 publisher_uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
468 pres->dlg->local.info->uri,
469 publisher_uri.ptr,
470 PJSIP_MAX_URL_SIZE);
471 if (publisher_uri.slen < 1)
472 return NULL;
473
474 /* Create XPIDF document. */
475 xpidf = pjxpidf_create(pool, &publisher_uri);
476
477 /* Set basic status. */
478 if (pres->status.info_cnt > 0)
479 pjxpidf_set_status( xpidf, pres->status.info[0].basic_open);
480 else
481 pjxpidf_set_status( xpidf, PJ_FALSE);
482
483 return xpidf;
484}
485
486
487/*
488 * Function to print XML message body.
489 */
490static int pres_print_body(struct pjsip_msg_body *msg_body,
491 char *buf, pj_size_t size)
492{
493 return pj_xml_print(msg_body->data, buf, size, PJ_TRUE);
494}
495
496
497/*
498 * Function to clone XML document.
499 */
500static void* xml_clone_data(pj_pool_t *pool, const void *data, unsigned len)
501{
502 PJ_UNUSED_ARG(len);
503 return pj_xml_clone( pool, data);
504}
505
506
507/*
508 * Create message body.
509 */
510static pj_status_t pres_create_msg_body( pjsip_pres *pres,
511 pjsip_tx_data *tdata)
512{
513 pjsip_msg_body *body;
514
515 body = pj_pool_zalloc(tdata->pool, sizeof(pjsip_msg_body));
516
517 if (pres->content_type == CONTENT_TYPE_PIDF) {
518
519 body->data = pres_create_pidf(tdata->pool, pres);
520 body->content_type.type = pj_str("application");
521 body->content_type.subtype = pj_str("pidf+xml");
522
523 } else if (pres->content_type == CONTENT_TYPE_XPIDF) {
524
525 body->data = pres_create_xpidf(tdata->pool, pres);
526 body->content_type.type = pj_str("application");
527 body->content_type.subtype = pj_str("xpidf+xml");
528
529 } else {
530 return PJSIP_SIMPLE_EBADCONTENT;
531 }
532
533
534 body->print_body = &pres_print_body;
535 body->clone_data = &xml_clone_data;
536
537 tdata->msg->body = body;
538
539 return PJ_SUCCESS;
540}
541
542
543/*
544 * Create NOTIFY
545 */
546PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub,
547 pjsip_evsub_state state,
548 const pj_str_t *state_str,
549 const pj_str_t *reason,
550 pjsip_tx_data **p_tdata)
551{
552 pjsip_pres *pres;
553 pjsip_tx_data *tdata;
554 pj_status_t status;
555
556 /* Check arguments. */
557 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
558
559 /* Get the presence object. */
560 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
561 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
562
563 /* Must have at least one presence info. */
564 PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
565
566
567 /* Lock object. */
568 pjsip_dlg_inc_lock(pres->dlg);
569
570 /* Create the NOTIFY request. */
571 status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
572 if (status != PJ_SUCCESS)
573 goto on_return;
574
575
576 /* Create message body to reflect the presence status. */
577 status = pres_create_msg_body( pres, tdata );
578 if (status != PJ_SUCCESS)
579 goto on_return;
580
581
582 /* Done. */
583 *p_tdata = tdata;
584
585
586on_return:
587 pjsip_dlg_dec_lock(pres->dlg);
588 return status;
589}
590
591
592/*
593 * Create NOTIFY that reflect current state.
594 */
595PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
596 pjsip_tx_data **p_tdata )
597{
598 pjsip_pres *pres;
599 pjsip_tx_data *tdata;
600 pj_status_t status;
601
602 /* Check arguments. */
603 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
604
605 /* Get the presence object. */
606 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
607 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
608
609 /* Must have at least one presence info. */
610 PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
611
612
613 /* Lock object. */
614 pjsip_dlg_inc_lock(pres->dlg);
615
616 /* Create the NOTIFY request. */
617 status = pjsip_evsub_current_notify( sub, &tdata);
618 if (status != PJ_SUCCESS)
619 goto on_return;
620
621
622 /* Create message body to reflect the presence status. */
623 status = pres_create_msg_body( pres, tdata );
624 if (status != PJ_SUCCESS)
625 goto on_return;
626
627
628 /* Done. */
629 *p_tdata = tdata;
630
631
632on_return:
633 pjsip_dlg_dec_lock(pres->dlg);
634 return status;
635}
636
637
638/*
639 * Send request.
640 */
641PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
642 pjsip_tx_data *tdata )
643{
644 return pjsip_evsub_send_request(sub, tdata);
645}
646
647
648/*
649 * This callback is called by event subscription when subscription
650 * state has changed.
651 */
652static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
653{
654 pjsip_pres *pres;
655
656 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
657 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
658
659 if (pres->user_cb.on_evsub_state)
660 (*pres->user_cb.on_evsub_state)(sub, event);
661}
662
663/*
664 * Called when transaction state has changed.
665 */
666static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
667 pjsip_event *event)
668{
669 pjsip_pres *pres;
670
671 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
672 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
673
674 if (pres->user_cb.on_tsx_state)
675 (*pres->user_cb.on_tsx_state)(sub, tsx, event);
676}
677
678
679/*
680 * Called when SUBSCRIBE is received.
681 */
682static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
683 pjsip_rx_data *rdata,
684 int *p_st_code,
685 pj_str_t **p_st_text,
686 pjsip_hdr *res_hdr,
687 pjsip_msg_body **p_body)
688{
689 pjsip_pres *pres;
690
691 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
692 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
693
694 if (pres->user_cb.on_rx_refresh) {
695 (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
696 res_hdr, p_body);
697
698 } else {
699 /* Implementors MUST send NOTIFY if it implements on_rx_refresh */
700 pjsip_tx_data *tdata;
701 pj_str_t timeout = { "timeout", 7};
702 pj_status_t status;
703
704 if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
705 status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
706 NULL, &timeout, &tdata);
707 } else {
708 status = pjsip_pres_current_notify(sub, &tdata);
709 }
710
711 if (status == PJ_SUCCESS)
712 pjsip_pres_send_request(sub, tdata);
713 }
714}
715
716/*
717 * Parse PIDF to info.
718 */
719static pj_status_t pres_parse_pidf( pjsip_pres *pres,
720 pjsip_rx_data *rdata,
721 pjsip_pres_status *pres_status)
722{
723 pjpidf_pres *pidf;
724 pjpidf_tuple *pidf_tuple;
725
726 pidf = pjpidf_parse(rdata->tp_info.pool,
727 rdata->msg_info.msg->body->data,
728 rdata->msg_info.msg->body->len);
729 if (pidf == NULL)
730 return PJSIP_SIMPLE_EBADPIDF;
731
732 pres_status->info_cnt = 0;
733
734 pidf_tuple = pjpidf_pres_get_first_tuple(pidf);
735 while (pidf_tuple) {
736 pjpidf_status *pidf_status;
737
738 pj_strdup(pres->dlg->pool,
739 &pres_status->info[pres_status->info_cnt].id,
740 pjpidf_tuple_get_id(pidf_tuple));
741
742 pj_strdup(pres->dlg->pool,
743 &pres_status->info[pres_status->info_cnt].contact,
744 pjpidf_tuple_get_contact(pidf_tuple));
745
746 pidf_status = pjpidf_tuple_get_status(pidf_tuple);
747 if (pidf_status) {
748 pres_status->info[pres_status->info_cnt].basic_open =
749 pjpidf_status_is_basic_open(pidf_status);
750 } else {
751 pres_status->info[pres_status->info_cnt].basic_open = PJ_FALSE;
752 }
753
754 pidf_tuple = pjpidf_pres_get_next_tuple( pidf, pidf_tuple );
755 pres_status->info_cnt++;
756 }
757
758 return PJ_SUCCESS;
759}
760
761/*
762 * Parse XPIDF info.
763 */
764static pj_status_t pres_parse_xpidf( pjsip_pres *pres,
765 pjsip_rx_data *rdata,
766 pjsip_pres_status *pres_status)
767{
768 pjxpidf_pres *xpidf;
769
770 xpidf = pjxpidf_parse(rdata->tp_info.pool,
771 rdata->msg_info.msg->body->data,
772 rdata->msg_info.msg->body->len);
773 if (xpidf == NULL)
774 return PJSIP_SIMPLE_EBADXPIDF;
775
776 pres_status->info_cnt = 1;
777
778 pj_strdup(pres->dlg->pool,
779 &pres_status->info[0].contact,
780 pjxpidf_get_uri(xpidf));
781 pres_status->info[0].basic_open = pjxpidf_get_status(xpidf);
782 pres_status->info[0].id.slen = 0;
783
784 return PJ_SUCCESS;
785}
786
787
788/*
789 * Called when NOTIFY is received.
790 */
791static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
792 pjsip_rx_data *rdata,
793 int *p_st_code,
794 pj_str_t **p_st_text,
795 pjsip_hdr *res_hdr,
796 pjsip_msg_body **p_body)
797{
798 pjsip_ctype_hdr *ctype_hdr;
799 pjsip_pres *pres;
800 pj_status_t status;
801
802 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
803 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
804
805 /* Check Content-Type and msg body are present. */
806 ctype_hdr = rdata->msg_info.ctype;
807
808 if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) {
809
810 pjsip_warning_hdr *warn_hdr;
811 pj_str_t warn_text;
812
813 *p_st_code = PJSIP_SC_BAD_REQUEST;
814
815 warn_text = pj_str("Message body is not present");
816 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
817 pjsip_endpt_name(pres->dlg->endpt),
818 &warn_text);
819 pj_list_push_back(res_hdr, warn_hdr);
820
821 return;
822 }
823
824 /* Parse content. */
825
826 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
827 pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
828 {
829 status = pres_parse_pidf( pres, rdata, &pres->tmp_status);
830 }
831 else
832 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
833 pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
834 {
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000835 status = pres_parse_xpidf( pres, rdata, &pres->tmp_status);
Benny Prijono834aee32006-02-19 01:38:06 +0000836 }
837 else
838 {
839 status = PJSIP_SIMPLE_EBADCONTENT;
840 }
841
842 if (status != PJ_SUCCESS) {
843 /* Unsupported or bad Content-Type */
844 pjsip_accept_hdr *accept_hdr;
845 pjsip_warning_hdr *warn_hdr;
846
847 *p_st_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
848
849 /* Add Accept header */
850 accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool);
851 accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML;
852 accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML;
853 pj_list_push_back(res_hdr, accept_hdr);
854
855 /* Add Warning header */
856 warn_hdr = pjsip_warning_hdr_create_from_status(
857 rdata->tp_info.pool,
858 pjsip_endpt_name(pres->dlg->endpt),
859 status);
860 pj_list_push_back(res_hdr, warn_hdr);
861
862 return;
863 }
864
865 /* If application calls pres_get_status(), redirect the call to
866 * retrieve the temporary status.
867 */
868 pres->tmp_status._is_valid = PJ_TRUE;
869
870 /* Notify application. */
871 if (pres->user_cb.on_rx_notify) {
872 (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
873 res_hdr, p_body);
874 }
875
876
877 /* If application responded NOTIFY with 2xx, copy temporary status
878 * to main status, and mark the temporary status as invalid.
879 */
880 if ((*p_st_code)/100 == 2) {
881 pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
882 }
883
884 pres->tmp_status._is_valid = PJ_FALSE;
885
886 /* Done */
887}
888
889/*
890 * Called when it's time to send SUBSCRIBE.
891 */
892static void pres_on_evsub_client_refresh(pjsip_evsub *sub)
893{
894 pjsip_pres *pres;
895
896 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
897 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
898
899 if (pres->user_cb.on_client_refresh) {
900 (*pres->user_cb.on_client_refresh)(sub);
901 } else {
902 pj_status_t status;
903 pjsip_tx_data *tdata;
904
905 status = pjsip_pres_initiate(sub, -1, &tdata);
906 if (status == PJ_SUCCESS)
907 pjsip_pres_send_request(sub, tdata);
908 }
909}
910
911/*
912 * Called when no refresh is received after the interval.
913 */
914static void pres_on_evsub_server_timeout(pjsip_evsub *sub)
915{
916 pjsip_pres *pres;
917
918 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
919 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
920
921 if (pres->user_cb.on_server_timeout) {
922 (*pres->user_cb.on_server_timeout)(sub);
923 } else {
924 pj_status_t status;
925 pjsip_tx_data *tdata;
926 pj_str_t reason = { "timeout", 7 };
927
928 status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
929 NULL, &reason, &tdata);
930 if (status == PJ_SUCCESS)
931 pjsip_pres_send_request(sub, tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000932 }
933}
934