1
/* $Id: presence.c 3553 2011-05-05 06:14:19Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
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>
32
#include <pj/string.h>
35
#define THIS_FILE "presence.c"
36
#define PRES_DEFAULT_EXPIRES PJSIP_PRES_DEFAULT_EXPIRES
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
45
* Presence module (mod-presence)
47
static struct pjsip_module mod_presence =
49
NULL, NULL, /* prev, next. */
50
{ "mod-presence", 12 }, /* Name. */
52
PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */
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() */
66
* Presence message body type.
68
typedef enum content_type_e
76
* This structure describe a presentity, for both subscriber and notifier.
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. */
91
typedef struct pjsip_pres pjsip_pres;
95
* Forward decl for evsub callback.
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,
100
static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
101
pjsip_rx_data *rdata,
103
pj_str_t **p_st_text,
105
pjsip_msg_body **p_body);
106
static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
107
pjsip_rx_data *rdata,
109
pj_str_t **p_st_text,
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);
117
* Event subscription callback for presence.
119
static pjsip_evsub_user pres_user =
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,
131
* Some static constants.
133
const pj_str_t STR_EVENT = { "Event", 5 };
134
const pj_str_t STR_PRESENCE = { "presence", 8 };
135
const pj_str_t STR_APPLICATION = { "application", 11 };
136
const pj_str_t STR_PIDF_XML = { "pidf+xml", 8};
137
const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9};
138
const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 };
139
const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 };
143
* Init presence module.
145
PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt,
146
pjsip_module *mod_evsub)
151
/* Check arguments. */
152
PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
154
/* Must have not been registered */
155
PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP);
157
/* Register to endpoint */
158
status = pjsip_endpt_register_module(endpt, &mod_presence);
159
if (status != PJ_SUCCESS)
162
accept[0] = STR_APP_PIDF_XML;
163
accept[1] = STR_APP_XPIDF_XML;
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);
179
* Get presence module instance.
181
PJ_DEF(pjsip_module*) pjsip_pres_instance(void)
183
return &mod_presence;
188
* Create client subscription.
190
PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
191
const pjsip_evsub_user *user_cb,
193
pjsip_evsub **p_evsub )
197
char obj_name[PJ_MAX_OBJ_NAME];
200
PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
202
pjsip_dlg_inc_lock(dlg);
204
/* Create event subscription */
205
status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE,
207
if (status != PJ_SUCCESS)
210
/* Create presence */
211
pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
215
pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
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,
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,
224
/* Attach to evsub */
225
pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
230
pjsip_dlg_dec_lock(dlg);
236
* Create server subscription.
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 )
243
pjsip_accept_hdr *accept;
244
pjsip_event_hdr *event;
245
content_type_e content_type = CONTENT_TYPE_NONE;
248
char obj_name[PJ_MAX_OBJ_NAME];
251
/* Check arguments */
252
PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
254
/* Must be request message */
255
PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
256
PJSIP_ENOTREQUESTMSG);
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);
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);
267
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
269
if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) {
270
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
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);
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;
283
if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) {
284
content_type = CONTENT_TYPE_XPIDF;
289
if (i==accept->count) {
290
/* Nothing is acceptable */
291
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
296
* Treat as "application/pidf+xml"
298
content_type = CONTENT_TYPE_PIDF;
302
pjsip_dlg_inc_lock(dlg);
305
/* Create server subscription */
306
status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub);
307
if (status != PJ_SUCCESS)
310
/* Create server presence subscription */
311
pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres);
314
pres->content_type = content_type;
316
pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
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,
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,
325
/* Attach to evsub */
326
pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
332
pjsip_dlg_dec_lock(dlg);
338
* Forcefully terminate presence.
340
PJ_DEF(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub,
343
return pjsip_evsub_terminate(sub, notify);
349
PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
351
pjsip_tx_data **p_tdata)
353
return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
359
* Add custom headers.
361
PJ_DEF(pj_status_t) pjsip_pres_add_header( pjsip_evsub *sub,
362
const pjsip_hdr *hdr_list )
364
return pjsip_evsub_add_header( sub, hdr_list );
369
* Accept incoming subscription.
371
PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
372
pjsip_rx_data *rdata,
374
const pjsip_hdr *hdr_list )
376
return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
381
* Get presence status.
383
PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
384
pjsip_pres_status *status )
388
PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
390
pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
391
PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
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));
397
PJ_ASSERT_RETURN(pres->status_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
398
pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
406
* Set presence status.
408
PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
409
const pjsip_pres_status *status )
415
PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
417
pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
418
PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
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) {
424
} else if (status->info[i].id.slen == 0) {
425
pj_create_unique_string(pres->dlg->pool,
426
&pres->status.info[i].id);
428
pj_strdup(pres->dlg->pool,
429
&pres->status.info[i].id,
430
&status->info[i].id);
432
pj_strdup(pres->tmp_pool,
433
&pres->status.info[i].contact,
434
&status->info[i].contact);
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);
448
pres->status.info_cnt = status->info_cnt;
451
tmp = pres->tmp_pool;
452
pres->tmp_pool = pres->status_pool;
453
pres->status_pool = tmp;
454
pj_pool_reset(pres->tmp_pool);
461
* Create message body.
463
static pj_status_t pres_create_msg_body( pjsip_pres *pres,
464
pjsip_tx_data *tdata)
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);
476
if (pres->content_type == CONTENT_TYPE_PIDF) {
478
return pjsip_pres_create_pidf(tdata->pool, &pres->status,
479
&entity, &tdata->msg->body);
481
} else if (pres->content_type == CONTENT_TYPE_XPIDF) {
483
return pjsip_pres_create_xpidf(tdata->pool, &pres->status,
484
&entity, &tdata->msg->body);
487
return PJSIP_SIMPLE_EBADCONTENT;
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)
502
pjsip_tx_data *tdata;
505
/* Check arguments. */
506
PJ_ASSERT_RETURN(sub, PJ_EINVAL);
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);
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.
517
PJ_ASSERT_RETURN(state==PJSIP_EVSUB_STATE_TERMINATED ||
518
pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
522
pjsip_dlg_inc_lock(pres->dlg);
524
/* Create the NOTIFY request. */
525
status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
526
if (status != PJ_SUCCESS)
530
/* Create message body to reflect the presence status.
531
* Only do this if we have presence status info to send (see above).
533
if (pres->status.info_cnt > 0) {
534
status = pres_create_msg_body( pres, tdata );
535
if (status != PJ_SUCCESS)
544
pjsip_dlg_dec_lock(pres->dlg);
550
* Create NOTIFY that reflect current state.
552
PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
553
pjsip_tx_data **p_tdata )
556
pjsip_tx_data *tdata;
559
/* Check arguments. */
560
PJ_ASSERT_RETURN(sub, PJ_EINVAL);
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);
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.
569
//PJ_ASSERT_RETURN(pres->status.info_cnt > 0,
570
// PJSIP_SIMPLE_ENOPRESENCEINFO);
574
pjsip_dlg_inc_lock(pres->dlg);
576
/* Create the NOTIFY request. */
577
status = pjsip_evsub_current_notify( sub, &tdata);
578
if (status != PJ_SUCCESS)
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)
594
pjsip_dlg_dec_lock(pres->dlg);
602
PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
603
pjsip_tx_data *tdata )
605
return pjsip_evsub_send_request(sub, tdata);
610
* This callback is called by event subscription when subscription
613
static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
617
pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
618
PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
620
if (pres->user_cb.on_evsub_state)
621
(*pres->user_cb.on_evsub_state)(sub, event);
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;
628
if (pres->tmp_pool) {
629
pj_pool_release(pres->tmp_pool);
630
pres->tmp_pool = NULL;
636
* Called when transaction state has changed.
638
static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
643
pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
644
PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
646
if (pres->user_cb.on_tsx_state)
647
(*pres->user_cb.on_tsx_state)(sub, tsx, event);
652
* Called when SUBSCRIBE is received.
654
static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
655
pjsip_rx_data *rdata,
657
pj_str_t **p_st_text,
659
pjsip_msg_body **p_body)
663
pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
664
PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
666
if (pres->user_cb.on_rx_refresh) {
667
(*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
671
/* Implementors MUST send NOTIFY if it implements on_rx_refresh */
672
pjsip_tx_data *tdata;
673
pj_str_t timeout = { "timeout", 7};
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);
680
status = pjsip_pres_current_notify(sub, &tdata);
683
if (status == PJ_SUCCESS)
684
pjsip_pres_send_request(sub, tdata);
690
* Process the content of incoming NOTIFY request and update temporary
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.
696
static pj_status_t pres_process_rx_notify( pjsip_pres *pres,
697
pjsip_rx_data *rdata,
699
pj_str_t **p_st_text,
702
const pj_str_t STR_MULTIPART = { "multipart", 9 };
703
pjsip_ctype_hdr *ctype_hdr;
704
pj_status_t status = PJ_SUCCESS;
708
/* Check Content-Type and msg body are present. */
709
ctype_hdr = rdata->msg_info.ctype;
711
if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) {
713
pjsip_warning_hdr *warn_hdr;
716
*p_st_code = PJSIP_SC_BAD_REQUEST;
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),
722
pj_list_push_back(res_hdr, warn_hdr);
724
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
728
if (pj_stricmp(&ctype_hdr->media.type, &STR_MULTIPART)==0) {
729
pjsip_multipart_part *mpart;
730
pjsip_media_type ctype;
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,
737
status = pjsip_pres_parse_pidf2((char*)mpart->body->data,
738
mpart->body->len, pres->tmp_pool,
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,
748
status = pjsip_pres_parse_xpidf2((char*)mpart->body->data,
753
status = PJSIP_SIMPLE_EBADCONTENT;
758
if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
759
pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
761
status = pjsip_pres_parse_pidf( rdata, pres->tmp_pool,
765
if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
766
pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
768
status = pjsip_pres_parse_xpidf( rdata, pres->tmp_pool,
773
status = PJSIP_SIMPLE_EBADCONTENT;
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;
782
*p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE;
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);
790
/* Add Warning header */
791
warn_hdr = pjsip_warning_hdr_create_from_status(
793
pjsip_endpt_name(pres->dlg->endpt),
795
pj_list_push_back(res_hdr, warn_hdr);
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;
809
/* If application calls pres_get_status(), redirect the call to
810
* retrieve the temporary status.
812
pres->tmp_status._is_valid = PJ_TRUE;
819
* Called when NOTIFY is received.
821
static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
822
pjsip_rx_data *rdata,
824
pj_str_t **p_st_text,
826
pjsip_msg_body **p_body)
831
pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
832
PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
834
if (rdata->msg_info.msg->body) {
835
status = pres_process_rx_notify( pres, rdata, p_st_code, p_st_text,
837
if (status != PJ_SUCCESS)
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.
849
for (i=0; i<pres->status.info_cnt; ++i) {
850
pres->status.info[i].tuple_node = NULL;
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).
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;
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,
876
/* If application responded NOTIFY with 2xx, copy temporary status
877
* to main status, and mark the temporary status as invalid.
879
if ((*p_st_code)/100 == 2) {
882
pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
885
tmp = pres->tmp_pool;
886
pres->tmp_pool = pres->status_pool;
887
pres->status_pool = tmp;
890
pres->tmp_status._is_valid = PJ_FALSE;
891
pj_pool_reset(pres->tmp_pool);
897
* Called when it's time to send SUBSCRIBE.
899
static void pres_on_evsub_client_refresh(pjsip_evsub *sub)
903
pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
904
PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
906
if (pres->user_cb.on_client_refresh) {
907
(*pres->user_cb.on_client_refresh)(sub);
910
pjsip_tx_data *tdata;
912
status = pjsip_pres_initiate(sub, -1, &tdata);
913
if (status == PJ_SUCCESS)
914
pjsip_pres_send_request(sub, tdata);
919
* Called when no refresh is received after the interval.
921
static void pres_on_evsub_server_timeout(pjsip_evsub *sub)
925
pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
926
PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
928
if (pres->user_cb.on_server_timeout) {
929
(*pres->user_cb.on_server_timeout)(sub);
932
pjsip_tx_data *tdata;
933
pj_str_t reason = { "timeout", 7 };
935
status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
936
NULL, &reason, &tdata);
937
if (status == PJ_SUCCESS)
938
pjsip_pres_send_request(sub, tdata);