diff options
author | David M. Lee <dlee@digium.com> | 2013-01-07 14:24:28 -0600 |
---|---|---|
committer | David M. Lee <dlee@digium.com> | 2013-01-07 14:24:28 -0600 |
commit | f3ab456a17af1c89a6e3be4d20c5944853df1cb0 (patch) | |
tree | d00e1a332cd038a6d906a1ea0ac91e1a4458e617 /pjsip/src/pjsip-simple/presence.c |
Import pjproject-2.0.1
Diffstat (limited to 'pjsip/src/pjsip-simple/presence.c')
-rw-r--r-- | pjsip/src/pjsip-simple/presence.c | 941 |
1 files changed, 941 insertions, 0 deletions
diff --git a/pjsip/src/pjsip-simple/presence.c b/pjsip/src/pjsip-simple/presence.c new file mode 100644 index 0000000..5b96ec7 --- /dev/null +++ b/pjsip/src/pjsip-simple/presence.c @@ -0,0 +1,941 @@ +/* $Id: presence.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip-simple/presence.h> +#include <pjsip-simple/errno.h> +#include <pjsip-simple/evsub_msg.h> +#include <pjsip/sip_module.h> +#include <pjsip/sip_multipart.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_dialog.h> +#include <pj/assert.h> +#include <pj/guid.h> +#include <pj/log.h> +#include <pj/os.h> +#include <pj/pool.h> +#include <pj/string.h> + + +#define THIS_FILE "presence.c" +#define PRES_DEFAULT_EXPIRES PJSIP_PRES_DEFAULT_EXPIRES + +#if PJSIP_PRES_BAD_CONTENT_RESPONSE < 200 || \ + PJSIP_PRES_BAD_CONTENT_RESPONSE > 699 || \ + PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 3 +# error Invalid PJSIP_PRES_BAD_CONTENT_RESPONSE value +#endif + +/* + * Presence module (mod-presence) + */ +static struct pjsip_module mod_presence = +{ + NULL, NULL, /* prev, next. */ + { "mod-presence", 12 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + NULL, /* on_rx_request() */ + NULL, /* on_rx_response() */ + NULL, /* on_tx_request. */ + NULL, /* on_tx_response() */ + NULL, /* on_tsx_state() */ +}; + + +/* + * Presence message body type. + */ +typedef enum content_type_e +{ + CONTENT_TYPE_NONE, + CONTENT_TYPE_PIDF, + CONTENT_TYPE_XPIDF, +} content_type_e; + +/* + * This structure describe a presentity, for both subscriber and notifier. + */ +struct pjsip_pres +{ + pjsip_evsub *sub; /**< Event subscribtion record. */ + pjsip_dialog *dlg; /**< The dialog. */ + content_type_e content_type; /**< Content-Type. */ + pj_pool_t *status_pool; /**< Pool for pres_status */ + pjsip_pres_status status; /**< Presence status. */ + pj_pool_t *tmp_pool; /**< Pool for tmp_status */ + pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/ + pjsip_evsub_user user_cb; /**< The user callback. */ +}; + + +typedef struct pjsip_pres pjsip_pres; + + +/* + * Forward decl for evsub callback. + */ +static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); +static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, + pjsip_event *event); +static void 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); +static void 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); +static void pres_on_evsub_client_refresh(pjsip_evsub *sub); +static void pres_on_evsub_server_timeout(pjsip_evsub *sub); + + +/* + * Event subscription callback for presence. + */ +static pjsip_evsub_user pres_user = +{ + &pres_on_evsub_state, + &pres_on_evsub_tsx_state, + &pres_on_evsub_rx_refresh, + &pres_on_evsub_rx_notify, + &pres_on_evsub_client_refresh, + &pres_on_evsub_server_timeout, +}; + + +/* + * Some static constants. + */ +const pj_str_t STR_EVENT = { "Event", 5 }; +const pj_str_t STR_PRESENCE = { "presence", 8 }; +const pj_str_t STR_APPLICATION = { "application", 11 }; +const pj_str_t STR_PIDF_XML = { "pidf+xml", 8}; +const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9}; +const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 }; +const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 }; + + +/* + * Init presence module. + */ +PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt, + pjsip_module *mod_evsub) +{ + pj_status_t status; + pj_str_t accept[2]; + + /* Check arguments. */ + PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL); + + /* Must have not been registered */ + PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP); + + /* Register to endpoint */ + status = pjsip_endpt_register_module(endpt, &mod_presence); + if (status != PJ_SUCCESS) + return status; + + accept[0] = STR_APP_PIDF_XML; + accept[1] = STR_APP_XPIDF_XML; + + /* Register event package to event module. */ + status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE, + PRES_DEFAULT_EXPIRES, + PJ_ARRAY_SIZE(accept), accept); + if (status != PJ_SUCCESS) { + pjsip_endpt_unregister_module(endpt, &mod_presence); + return status; + } + + return PJ_SUCCESS; +} + + +/* + * Get presence module instance. + */ +PJ_DEF(pjsip_module*) pjsip_pres_instance(void) +{ + return &mod_presence; +} + + +/* + * Create client subscription. + */ +PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg, + const pjsip_evsub_user *user_cb, + unsigned options, + pjsip_evsub **p_evsub ) +{ + pj_status_t status; + pjsip_pres *pres; + char obj_name[PJ_MAX_OBJ_NAME]; + pjsip_evsub *sub; + + PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL); + + pjsip_dlg_inc_lock(dlg); + + /* Create event subscription */ + status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE, + options, &sub); + if (status != PJ_SUCCESS) + goto on_return; + + /* Create presence */ + pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres); + pres->dlg = dlg; + pres->sub = sub; + if (user_cb) + pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); + + pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool); + pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name, + 512, 512, NULL); + pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool); + pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name, + 512, 512, NULL); + + /* Attach to evsub */ + pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); + + *p_evsub = sub; + +on_return: + pjsip_dlg_dec_lock(dlg); + return status; +} + + +/* + * Create server subscription. + */ +PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg, + const pjsip_evsub_user *user_cb, + pjsip_rx_data *rdata, + pjsip_evsub **p_evsub ) +{ + pjsip_accept_hdr *accept; + pjsip_event_hdr *event; + content_type_e content_type = CONTENT_TYPE_NONE; + pjsip_evsub *sub; + pjsip_pres *pres; + char obj_name[PJ_MAX_OBJ_NAME]; + pj_status_t status; + + /* Check arguments */ + PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); + + /* Must be request message */ + PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, + PJSIP_ENOTREQUESTMSG); + + /* Check that request is SUBSCRIBE */ + PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, + &pjsip_subscribe_method)==0, + PJSIP_SIMPLE_ENOTSUBSCRIBE); + + /* Check that Event header contains "presence" */ + event = (pjsip_event_hdr*) + pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); + if (!event) { + return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); + } + if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) { + return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT); + } + + /* Check that request contains compatible Accept header. */ + accept = (pjsip_accept_hdr*) + pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); + if (accept) { + unsigned i; + for (i=0; i<accept->count; ++i) { + if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) { + content_type = CONTENT_TYPE_PIDF; + break; + } else + if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) { + content_type = CONTENT_TYPE_XPIDF; + break; + } + } + + if (i==accept->count) { + /* Nothing is acceptable */ + return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); + } + + } else { + /* No Accept header. + * Treat as "application/pidf+xml" + */ + content_type = CONTENT_TYPE_PIDF; + } + + /* Lock dialog */ + pjsip_dlg_inc_lock(dlg); + + + /* Create server subscription */ + status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub); + if (status != PJ_SUCCESS) + goto on_return; + + /* Create server presence subscription */ + pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres); + pres->dlg = dlg; + pres->sub = sub; + pres->content_type = content_type; + if (user_cb) + pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); + + pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool); + pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name, + 512, 512, NULL); + pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool); + pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name, + 512, 512, NULL); + + /* Attach to evsub */ + pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); + + /* Done: */ + *p_evsub = sub; + +on_return: + pjsip_dlg_dec_lock(dlg); + return status; +} + + +/* + * Forcefully terminate presence. + */ +PJ_DEF(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub, + pj_bool_t notify ) +{ + return pjsip_evsub_terminate(sub, notify); +} + +/* + * Create SUBSCRIBE + */ +PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub, + pj_int32_t expires, + pjsip_tx_data **p_tdata) +{ + return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires, + p_tdata); +} + + +/* + * Add custom headers. + */ +PJ_DEF(pj_status_t) pjsip_pres_add_header( pjsip_evsub *sub, + const pjsip_hdr *hdr_list ) +{ + return pjsip_evsub_add_header( sub, hdr_list ); +} + + +/* + * Accept incoming subscription. + */ +PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub, + pjsip_rx_data *rdata, + int st_code, + const pjsip_hdr *hdr_list ) +{ + return pjsip_evsub_accept( sub, rdata, st_code, hdr_list ); +} + + +/* + * Get presence status. + */ +PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub, + pjsip_pres_status *status ) +{ + pjsip_pres *pres; + + PJ_ASSERT_RETURN(sub && status, PJ_EINVAL); + + pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); + PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE); + + if (pres->tmp_status._is_valid) { + PJ_ASSERT_RETURN(pres->tmp_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE); + pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status)); + } else { + PJ_ASSERT_RETURN(pres->status_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE); + pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status)); + } + + return PJ_SUCCESS; +} + + +/* + * Set presence status. + */ +PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub, + const pjsip_pres_status *status ) +{ + unsigned i; + pj_pool_t *tmp; + pjsip_pres *pres; + + PJ_ASSERT_RETURN(sub && status, PJ_EINVAL); + + pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); + PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE); + + for (i=0; i<status->info_cnt; ++i) { + pres->status.info[i].basic_open = status->info[i].basic_open; + if (pres->status.info[i].id.slen) { + /* Id already set */ + } else if (status->info[i].id.slen == 0) { + pj_create_unique_string(pres->dlg->pool, + &pres->status.info[i].id); + } else { + pj_strdup(pres->dlg->pool, + &pres->status.info[i].id, + &status->info[i].id); + } + pj_strdup(pres->tmp_pool, + &pres->status.info[i].contact, + &status->info[i].contact); + + /* Duplicate <person> */ + pres->status.info[i].rpid.activity = + status->info[i].rpid.activity; + pj_strdup(pres->tmp_pool, + &pres->status.info[i].rpid.id, + &status->info[i].rpid.id); + pj_strdup(pres->tmp_pool, + &pres->status.info[i].rpid.note, + &status->info[i].rpid.note); + + } + + pres->status.info_cnt = status->info_cnt; + + /* Swap pools */ + tmp = pres->tmp_pool; + pres->tmp_pool = pres->status_pool; + pres->status_pool = tmp; + pj_pool_reset(pres->tmp_pool); + + return PJ_SUCCESS; +} + + +/* + * Create message body. + */ +static pj_status_t pres_create_msg_body( pjsip_pres *pres, + pjsip_tx_data *tdata) +{ + pj_str_t entity; + + /* Get publisher URI */ + entity.ptr = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE); + entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, + pres->dlg->local.info->uri, + entity.ptr, PJSIP_MAX_URL_SIZE); + if (entity.slen < 1) + return PJ_ENOMEM; + + if (pres->content_type == CONTENT_TYPE_PIDF) { + + return pjsip_pres_create_pidf(tdata->pool, &pres->status, + &entity, &tdata->msg->body); + + } else if (pres->content_type == CONTENT_TYPE_XPIDF) { + + return pjsip_pres_create_xpidf(tdata->pool, &pres->status, + &entity, &tdata->msg->body); + + } else { + return PJSIP_SIMPLE_EBADCONTENT; + } +} + + +/* + * Create NOTIFY + */ +PJ_DEF(pj_status_t) 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) +{ + pjsip_pres *pres; + pjsip_tx_data *tdata; + pj_status_t status; + + /* Check arguments. */ + PJ_ASSERT_RETURN(sub, PJ_EINVAL); + + /* Get the presence object. */ + pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); + PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE); + + /* Must have at least one presence info, unless state is + * PJSIP_EVSUB_STATE_TERMINATED. This could happen if subscription + * has not been active (e.g. we're waiting for user authorization) + * and remote cancels the subscription. + */ + PJ_ASSERT_RETURN(state==PJSIP_EVSUB_STATE_TERMINATED || + pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO); + + + /* Lock object. */ + pjsip_dlg_inc_lock(pres->dlg); + + /* Create the NOTIFY request. */ + status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata); + if (status != PJ_SUCCESS) + goto on_return; + + + /* Create message body to reflect the presence status. + * Only do this if we have presence status info to send (see above). + */ + if (pres->status.info_cnt > 0) { + status = pres_create_msg_body( pres, tdata ); + if (status != PJ_SUCCESS) + goto on_return; + } + + /* Done. */ + *p_tdata = tdata; + + +on_return: + pjsip_dlg_dec_lock(pres->dlg); + return status; +} + + +/* + * Create NOTIFY that reflect current state. + */ +PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub, + pjsip_tx_data **p_tdata ) +{ + pjsip_pres *pres; + pjsip_tx_data *tdata; + pj_status_t status; + + /* Check arguments. */ + PJ_ASSERT_RETURN(sub, PJ_EINVAL); + + /* Get the presence object. */ + pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); + PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE); + + /* We may not have a presence info yet, e.g. when we receive SUBSCRIBE + * to refresh subscription while we're waiting for user authorization. + */ + //PJ_ASSERT_RETURN(pres->status.info_cnt > 0, + // PJSIP_SIMPLE_ENOPRESENCEINFO); + + + /* Lock object. */ + pjsip_dlg_inc_lock(pres->dlg); + + /* Create the NOTIFY request. */ + status = pjsip_evsub_current_notify( sub, &tdata); + if (status != PJ_SUCCESS) + goto on_return; + + + /* Create message body to reflect the presence status. */ + if (pres->status.info_cnt > 0) { + status = pres_create_msg_body( pres, tdata ); + if (status != PJ_SUCCESS) + goto on_return; + } + + /* Done. */ + *p_tdata = tdata; + + +on_return: + pjsip_dlg_dec_lock(pres->dlg); + return status; +} + + +/* + * Send request. + */ +PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub, + pjsip_tx_data *tdata ) +{ + return pjsip_evsub_send_request(sub, tdata); +} + + +/* + * This callback is called by event subscription when subscription + * state has changed. + */ +static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) +{ + pjsip_pres *pres; + + pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); + PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); + + if (pres->user_cb.on_evsub_state) + (*pres->user_cb.on_evsub_state)(sub, event); + + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { + if (pres->status_pool) { + pj_pool_release(pres->status_pool); + pres->status_pool = NULL; + } + if (pres->tmp_pool) { + pj_pool_release(pres->tmp_pool); + pres->tmp_pool = NULL; + } + } +} + +/* + * Called when transaction state has changed. + */ +static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, + pjsip_event *event) +{ + pjsip_pres *pres; + + pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); + PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); + + if (pres->user_cb.on_tsx_state) + (*pres->user_cb.on_tsx_state)(sub, tsx, event); +} + + +/* + * Called when SUBSCRIBE is received. + */ +static void 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) +{ + pjsip_pres *pres; + + pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); + PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); + + if (pres->user_cb.on_rx_refresh) { + (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text, + res_hdr, p_body); + + } else { + /* Implementors MUST send NOTIFY if it implements on_rx_refresh */ + pjsip_tx_data *tdata; + pj_str_t timeout = { "timeout", 7}; + pj_status_t status; + + if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) { + status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED, + NULL, &timeout, &tdata); + } else { + status = pjsip_pres_current_notify(sub, &tdata); + } + + if (status == PJ_SUCCESS) + pjsip_pres_send_request(sub, tdata); + } +} + + +/* + * Process the content of incoming NOTIFY request and update temporary + * status. + * + * return PJ_SUCCESS if incoming request is acceptable. If return value + * is not PJ_SUCCESS, res_hdr may be added with Warning header. + */ +static pj_status_t 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) +{ + const pj_str_t STR_MULTIPART = { "multipart", 9 }; + pjsip_ctype_hdr *ctype_hdr; + pj_status_t status = PJ_SUCCESS; + + *p_st_text = NULL; + + /* Check Content-Type and msg body are present. */ + ctype_hdr = rdata->msg_info.ctype; + + if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) { + + pjsip_warning_hdr *warn_hdr; + pj_str_t warn_text; + + *p_st_code = PJSIP_SC_BAD_REQUEST; + + warn_text = pj_str("Message body is not present"); + warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399, + pjsip_endpt_name(pres->dlg->endpt), + &warn_text); + pj_list_push_back(res_hdr, warn_hdr); + + return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); + } + + /* Parse content. */ + if (pj_stricmp(&ctype_hdr->media.type, &STR_MULTIPART)==0) { + pjsip_multipart_part *mpart; + pjsip_media_type ctype; + + pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION, + (pj_str_t*)&STR_PIDF_XML); + mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body, + &ctype, NULL); + if (mpart) { + status = pjsip_pres_parse_pidf2((char*)mpart->body->data, + mpart->body->len, pres->tmp_pool, + &pres->tmp_status); + } + + if (mpart==NULL) { + pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION, + (pj_str_t*)&STR_XPIDF_XML); + mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body, + &ctype, NULL); + if (mpart) { + status = pjsip_pres_parse_xpidf2((char*)mpart->body->data, + mpart->body->len, + pres->tmp_pool, + &pres->tmp_status); + } else { + status = PJSIP_SIMPLE_EBADCONTENT; + } + } + } + else + if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 && + pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0) + { + status = pjsip_pres_parse_pidf( rdata, pres->tmp_pool, + &pres->tmp_status); + } + else + if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 && + pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0) + { + status = pjsip_pres_parse_xpidf( rdata, pres->tmp_pool, + &pres->tmp_status); + } + else + { + status = PJSIP_SIMPLE_EBADCONTENT; + } + + if (status != PJ_SUCCESS) { + /* Unsupported or bad Content-Type */ + if (PJSIP_PRES_BAD_CONTENT_RESPONSE >= 300) { + pjsip_accept_hdr *accept_hdr; + pjsip_warning_hdr *warn_hdr; + + *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE; + + /* Add Accept header */ + accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool); + accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML; + accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML; + pj_list_push_back(res_hdr, accept_hdr); + + /* Add Warning header */ + warn_hdr = pjsip_warning_hdr_create_from_status( + rdata->tp_info.pool, + pjsip_endpt_name(pres->dlg->endpt), + status); + pj_list_push_back(res_hdr, warn_hdr); + + return status; + } else { + pj_assert(PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 2); + PJ_PERROR(4,(THIS_FILE, status, + "Ignoring presence error due to " + "PJSIP_PRES_BAD_CONTENT_RESPONSE setting [%d]", + PJSIP_PRES_BAD_CONTENT_RESPONSE)); + *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE; + status = PJ_SUCCESS; + } + } + + /* If application calls pres_get_status(), redirect the call to + * retrieve the temporary status. + */ + pres->tmp_status._is_valid = PJ_TRUE; + + return PJ_SUCCESS; +} + + +/* + * Called when NOTIFY is received. + */ +static void 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) +{ + pjsip_pres *pres; + pj_status_t status; + + pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); + PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); + + if (rdata->msg_info.msg->body) { + status = pres_process_rx_notify( pres, rdata, p_st_code, p_st_text, + res_hdr ); + if (status != PJ_SUCCESS) + return; + + } else { +#if 1 + /* This is the newest change, http://trac.pjsip.org/repos/ticket/873 + * Some app want to be notified about the empty NOTIFY, e.g. to + * decide whether it should consider the buddy as offline. + * In this case, leave the buddy state unchanged, but set the + * "tuple_node" in pjsip_pres_status to NULL. + */ + unsigned i; + for (i=0; i<pres->status.info_cnt; ++i) { + pres->status.info[i].tuple_node = NULL; + } + +#elif 0 + /* This has just been changed. Previously, we treat incoming NOTIFY + * with no message body as having the presence subscription closed. + * Now we treat it as no change in presence status (ref: EyeBeam). + */ + *p_st_code = 200; + return; +#else + unsigned i; + /* Subscription is terminated. Consider contact is offline */ + pres->tmp_status._is_valid = PJ_TRUE; + for (i=0; i<pres->tmp_status.info_cnt; ++i) + pres->tmp_status.info[i].basic_open = PJ_FALSE; +#endif + } + + /* Notify application. */ + if (pres->user_cb.on_rx_notify) { + (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text, + res_hdr, p_body); + } + + + /* If application responded NOTIFY with 2xx, copy temporary status + * to main status, and mark the temporary status as invalid. + */ + if ((*p_st_code)/100 == 2) { + pj_pool_t *tmp; + + pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status)); + + /* Swap the pool */ + tmp = pres->tmp_pool; + pres->tmp_pool = pres->status_pool; + pres->status_pool = tmp; + } + + pres->tmp_status._is_valid = PJ_FALSE; + pj_pool_reset(pres->tmp_pool); + + /* Done */ +} + +/* + * Called when it's time to send SUBSCRIBE. + */ +static void pres_on_evsub_client_refresh(pjsip_evsub *sub) +{ + pjsip_pres *pres; + + pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); + PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); + + if (pres->user_cb.on_client_refresh) { + (*pres->user_cb.on_client_refresh)(sub); + } else { + pj_status_t status; + pjsip_tx_data *tdata; + + status = pjsip_pres_initiate(sub, -1, &tdata); + if (status == PJ_SUCCESS) + pjsip_pres_send_request(sub, tdata); + } +} + +/* + * Called when no refresh is received after the interval. + */ +static void pres_on_evsub_server_timeout(pjsip_evsub *sub) +{ + pjsip_pres *pres; + + pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); + PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); + + if (pres->user_cb.on_server_timeout) { + (*pres->user_cb.on_server_timeout)(sub); + } else { + pj_status_t status; + pjsip_tx_data *tdata; + pj_str_t reason = { "timeout", 7 }; + + status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, + NULL, &reason, &tdata); + if (status == PJ_SUCCESS) + pjsip_pres_send_request(sub, tdata); + } +} + |