diff options
Diffstat (limited to 'pjsip')
-rw-r--r-- | pjsip/build/Makefile | 3 | ||||
-rw-r--r-- | pjsip/build/pjsip_simple.dsp | 6 | ||||
-rw-r--r-- | pjsip/include/pjsip-simple/presence.h | 74 | ||||
-rw-r--r-- | pjsip/include/pjsip-simple/publish.h | 25 | ||||
-rw-r--r-- | pjsip/include/pjsip_simple.h | 1 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua.h | 13 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua_internal.h | 12 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/presence.c | 205 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/presence_body.c | 228 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/publishc.c | 512 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_endpoint.c | 6 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_acc.c | 7 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_core.c | 2 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_pres.c | 181 |
14 files changed, 790 insertions, 485 deletions
diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile index 24755553..6bdec74d 100644 --- a/pjsip/build/Makefile +++ b/pjsip/build/Makefile @@ -66,7 +66,8 @@ export PJSIP_UA_CFLAGS += $(_CFLAGS) # export PJSIP_SIMPLE_SRCDIR = ../src/pjsip-simple export PJSIP_SIMPLE_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ - errno.o evsub.o evsub_msg.o iscomposing.o pidf.o presence.o \ + errno.o evsub.o evsub_msg.o iscomposing.o \ + pidf.o presence.o presence_body.o publishc.o \ xpidf.o export PJSIP_SIMPLE_CFLAGS += $(_CFLAGS) diff --git a/pjsip/build/pjsip_simple.dsp b/pjsip/build/pjsip_simple.dsp index 3df884ea..c5523fcc 100644 --- a/pjsip/build/pjsip_simple.dsp +++ b/pjsip/build/pjsip_simple.dsp @@ -109,14 +109,16 @@ SOURCE="..\src\pjsip-simple\presence.c" # End Source File
# Begin Source File
+SOURCE="..\src\pjsip-simple\presence_body.c"
+# End Source File
+# Begin Source File
+
SOURCE="..\src\pjsip-simple\publishc.c"
!IF "$(CFG)" == "pjsip_simple - Win32 Release"
!ELSEIF "$(CFG)" == "pjsip_simple - Win32 Debug"
-# PROP Exclude_From_Build 1
-
!ENDIF
# End Source File
diff --git a/pjsip/include/pjsip-simple/presence.h b/pjsip/include/pjsip-simple/presence.h index ee2805f4..668b723c 100644 --- a/pjsip/include/pjsip-simple/presence.h +++ b/pjsip/include/pjsip-simple/presence.h @@ -103,6 +103,8 @@ typedef struct pjsip_pres_status pjsip_pres_status; * @param dlg The underlying dialog to use. * @param user_cb Pointer to callbacks to receive presence subscription * events. + * @param options Option flags. Currently only PJSIP_EVSUB_NO_EVENT_ID + * is recognized. * @param p_evsub Pointer to receive the presence subscription * session. * @@ -110,6 +112,7 @@ typedef struct pjsip_pres_status pjsip_pres_status; */ PJ_DECL(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, + unsigned options, pjsip_evsub **p_evsub ); @@ -265,6 +268,77 @@ PJ_DECL(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub, /** + * This is a utility function to create PIDF message body from PJSIP + * presence status (pjsip_pres_status). + * + * @param pool The pool to allocate memory for the message body. + * @param status Presence status to be converted into PIDF message + * body. + * @param entity The entity ID, which normally is equal to the + * presentity ID publishing this presence info. + * @param p_body Pointer to receive the SIP message body. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_pres_create_pidf( pj_pool_t *pool, + const pjsip_pres_status *status, + const pj_str_t *entity, + pjsip_msg_body **p_body ); + + +/** + * This is a utility function to create X-PIDF message body from PJSIP + * presence status (pjsip_pres_status). + * + * @param pool The pool to allocate memory for the message body. + * @param status Presence status to be converted into X-PIDF message + * body. + * @param entity The entity ID, which normally is equal to the + * presentity ID publishing this presence info. + * @param p_body Pointer to receive the SIP message body. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_pres_create_xpidf(pj_pool_t *pool, + const pjsip_pres_status *status, + const pj_str_t *entity, + pjsip_msg_body **p_body ); + + + +/** + * This is a utility function to parse PIDF body into PJSIP presence status. + * + * @param rdata The incoming SIP message containing the PIDF body. + * @param pool Pool to allocate memory to copy the strings into + * the presence status structure. + * @param status The presence status to be initialized. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_pres_parse_pidf(pjsip_rx_data *rdata, + pj_pool_t *pool, + pjsip_pres_status *status); + + + +/** + * This is a utility function to parse X-PIDF body into PJSIP presence status. + * + * @param rdata The incoming SIP message containing the X-PIDF body. + * @param pool Pool to allocate memory to copy the strings into + * the presence status structure. + * @param status The presence status to be initialized. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_pres_parse_xpidf(pjsip_rx_data *rdata, + pj_pool_t *pool, + pjsip_pres_status *status); + + + +/** * @} */ diff --git a/pjsip/include/pjsip-simple/publish.h b/pjsip/include/pjsip-simple/publish.h index 141bbf6a..bebc4d97 100644 --- a/pjsip/include/pjsip-simple/publish.h +++ b/pjsip/include/pjsip-simple/publish.h @@ -16,14 +16,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef __PJSIP_SIMPLE_PRESENCE_H__ -#define __PJSIP_SIMPLE_PRESENCE_H__ +#ifndef __PJSIP_SIMPLE_PUBLISH_H__ +#define __PJSIP_SIMPLE_PUBLISH_H__ /** * @file publish.h * @brief SIP Extension for Event State Publication (PUBLISH, RFC 3903) */ +#include <pjsip/sip_util.h> + PJ_BEGIN_DECL @@ -38,6 +40,11 @@ PJ_BEGIN_DECL Extension for Event State Publication (PUBLISH) as defined by RFC 3856. */ +/** + * The SIP PUBLISH method constant. + */ +extern const pjsip_method pjsip_publish_method; + /***************************************************************************** * @defgroup PJSIP_SIMPLE_PUBLISH_CLIENT SIP Event State Publication Client @@ -77,11 +84,22 @@ struct pjsip_publishc_cbparam typedef void pjsip_publishc_cb(struct pjsip_publishc_cbparam *param); +/** + * Initialize client publication module. + * + * @param endpt SIP endpoint. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_publishc_init_module(pjsip_endpoint *endpt); + + /** * Create client publication structure. * * @param endpt Endpoint, used to allocate pool from. + * @param options Option flags. * @param token Opaque data to be associated with the client publication. * @param cb Pointer to callback function to receive publication status. * @param p_pubc Pointer to receive client publication structure. @@ -89,6 +107,7 @@ typedef void pjsip_publishc_cb(struct pjsip_publishc_cbparam *param); * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_create( pjsip_endpoint *endpt, + unsigned options, void *token, pjsip_publishc_cb *cb, pjsip_publishc **p_pubc); @@ -248,5 +267,5 @@ PJ_DECL(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc, PJ_END_DECL -#endif /* __PJSIP_SIMPLE_PRESENCE_H__ */ +#endif /* __PJSIP_SIMPLE_PUBLISH_H__ */ diff --git a/pjsip/include/pjsip_simple.h b/pjsip/include/pjsip_simple.h index 97af4bc4..0230426f 100644 --- a/pjsip/include/pjsip_simple.h +++ b/pjsip/include/pjsip_simple.h @@ -38,6 +38,7 @@ #include <pjsip-simple/iscomposing.h> #include <pjsip-simple/presence.h> #include <pjsip-simple/pidf.h> +#include <pjsip-simple/publish.h> #include <pjsip-simple/xpidf.h> #endif /* __PJSIP_SIMPLE_H__ */ diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 1ca2ad21..2ffea4cc 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -1003,6 +1003,14 @@ PJ_DECL(pj_status_t) pjsua_transport_close( pjsua_transport_id id, /** + * Default PUBLISH expiration + */ +#ifndef PJSUA_PUBLISH_EXPIRATION +# define PJSUA_PUBLISH_EXPIRATION 600 +#endif + + +/** * Account configuration. */ typedef struct pjsua_acc_config @@ -1024,6 +1032,11 @@ typedef struct pjsua_acc_config */ pj_str_t reg_uri; + /** + * Publish presence? + */ + pj_bool_t publish_enabled; + /** * Optional URI to be put as Contact for this account. It is recommended * that this field is left empty, so that the value will be calculated diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index c862219f..8b1002b4 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -97,6 +97,8 @@ typedef struct pjsua_acc pj_bool_t online_status; /**< Our online status. */ pjsua_srv_pres pres_srv_list; /**< Server subscription list. */ + pjsip_publishc *publish_sess; /**< Client publication session. */ + pj_bool_t publish_state; /**< Last published online status */ } pjsua_acc; @@ -255,8 +257,13 @@ PJ_INLINE(pjsua_im_data*) pjsua_im_data_dup(pj_pool_t *pool, } +#if 0 #define PJSUA_LOCK() pj_mutex_lock(pjsua_var.mutex); #define PJSUA_UNLOCK() pj_mutex_unlock(pjsua_var.mutex); +#else +#define PJSUA_LOCK() +#define PJSUA_UNLOCK() +#endif @@ -286,6 +293,11 @@ void pjsua_pres_refresh(void); void pjsua_pres_shutdown(void); /** + * Init presence for aoocunt. + */ +pj_status_t pjsua_pres_init_acc(int acc_id); + +/** * Terminate server subscription for the account */ void pjsua_pres_delete_acc(int acc_id); diff --git a/pjsip/src/pjsip-simple/presence.c b/pjsip/src/pjsip-simple/presence.c index 9694c81c..5b919ebd 100644 --- a/pjsip/src/pjsip-simple/presence.c +++ b/pjsip/src/pjsip-simple/presence.c @@ -179,6 +179,7 @@ PJ_DEF(pjsip_module*) pjsip_pres_instance(void) */ 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; @@ -190,7 +191,8 @@ PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg, pjsip_dlg_inc_lock(dlg); /* Create event subscription */ - status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE, 0, &sub); + status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE, + options, &sub); if (status != PJ_SUCCESS) goto on_return; @@ -412,140 +414,34 @@ PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub, /* - * Create PIDF document based on the presence info. + * Create message body. */ -static pjpidf_pres* pres_create_pidf( pj_pool_t *pool, - pjsip_pres *pres ) +static pj_status_t pres_create_msg_body( pjsip_pres *pres, + pjsip_tx_data *tdata) { - pjpidf_pres *pidf; - unsigned i; pj_str_t entity; /* Get publisher URI */ - entity.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); + entity.ptr = 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 NULL; - - /* Create <presence>. */ - pidf = pjpidf_create(pool, &entity); - - /* Create <tuple> */ - for (i=0; i<pres->status.info_cnt; ++i) { - - pjpidf_tuple *pidf_tuple; - pjpidf_status *pidf_status; - - /* Add tuple id. */ - pidf_tuple = pjpidf_pres_add_tuple(pool, pidf, - &pres->status.info[i].id); - - /* Set <contact> */ - if (pres->status.info[i].contact.slen) - pjpidf_tuple_set_contact(pool, pidf_tuple, - &pres->status.info[i].contact); - - - /* Set basic status */ - pidf_status = pjpidf_tuple_get_status(pidf_tuple); - pjpidf_status_set_basic_open(pidf_status, - pres->status.info[i].basic_open); - } - - return pidf; -} - - -/* - * Create XPIDF document based on the presence info. - */ -static pjxpidf_pres* pres_create_xpidf( pj_pool_t *pool, - pjsip_pres *pres ) -{ - /* Note: PJSIP implementation of XPIDF is not complete! - */ - pjxpidf_pres *xpidf; - pj_str_t publisher_uri; - - PJ_LOG(4,(THIS_FILE, "Warning: XPIDF format is not fully supported " - "by PJSIP")); - - publisher_uri.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); - publisher_uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, - pres->dlg->local.info->uri, - publisher_uri.ptr, - PJSIP_MAX_URL_SIZE); - if (publisher_uri.slen < 1) - return NULL; - - /* Create XPIDF document. */ - xpidf = pjxpidf_create(pool, &publisher_uri); - - /* Set basic status. */ - if (pres->status.info_cnt > 0) - pjxpidf_set_status( xpidf, pres->status.info[0].basic_open); - else - pjxpidf_set_status( xpidf, PJ_FALSE); - - return xpidf; -} - - -/* - * Function to print XML message body. - */ -static int pres_print_body(struct pjsip_msg_body *msg_body, - char *buf, pj_size_t size) -{ - return pj_xml_print(msg_body->data, buf, size, PJ_TRUE); -} - + return PJ_ENOMEM; -/* - * Function to clone XML document. - */ -static void* xml_clone_data(pj_pool_t *pool, const void *data, unsigned len) -{ - PJ_UNUSED_ARG(len); - return pj_xml_clone( pool, data); -} - - -/* - * Create message body. - */ -static pj_status_t pres_create_msg_body( pjsip_pres *pres, - pjsip_tx_data *tdata) -{ - pjsip_msg_body *body; - - body = pj_pool_zalloc(tdata->pool, sizeof(pjsip_msg_body)); - if (pres->content_type == CONTENT_TYPE_PIDF) { - body->data = pres_create_pidf(tdata->pool, pres); - body->content_type.type = pj_str("application"); - body->content_type.subtype = pj_str("pidf+xml"); + return pjsip_pres_create_pidf(tdata->pool, &pres->status, + &entity, &tdata->msg->body); } else if (pres->content_type == CONTENT_TYPE_XPIDF) { - body->data = pres_create_xpidf(tdata->pool, pres); - body->content_type.type = pj_str("application"); - body->content_type.subtype = pj_str("xpidf+xml"); + return pjsip_pres_create_xpidf(tdata->pool, &pres->status, + &entity, &tdata->msg->body); } else { return PJSIP_SIMPLE_EBADCONTENT; } - - - body->print_body = &pres_print_body; - body->clone_data = &xml_clone_data; - - tdata->msg->body = body; - - return PJ_SUCCESS; } @@ -722,77 +618,6 @@ static void pres_on_evsub_rx_refresh( pjsip_evsub *sub, } } -/* - * Parse PIDF to info. - */ -static pj_status_t pres_parse_pidf( pjsip_pres *pres, - pjsip_rx_data *rdata, - pjsip_pres_status *pres_status) -{ - pjpidf_pres *pidf; - pjpidf_tuple *pidf_tuple; - - pidf = pjpidf_parse(rdata->tp_info.pool, - rdata->msg_info.msg->body->data, - rdata->msg_info.msg->body->len); - if (pidf == NULL) - return PJSIP_SIMPLE_EBADPIDF; - - pres_status->info_cnt = 0; - - pidf_tuple = pjpidf_pres_get_first_tuple(pidf); - while (pidf_tuple) { - pjpidf_status *pidf_status; - - pj_strdup(pres->dlg->pool, - &pres_status->info[pres_status->info_cnt].id, - pjpidf_tuple_get_id(pidf_tuple)); - - pj_strdup(pres->dlg->pool, - &pres_status->info[pres_status->info_cnt].contact, - pjpidf_tuple_get_contact(pidf_tuple)); - - pidf_status = pjpidf_tuple_get_status(pidf_tuple); - if (pidf_status) { - pres_status->info[pres_status->info_cnt].basic_open = - pjpidf_status_is_basic_open(pidf_status); - } else { - pres_status->info[pres_status->info_cnt].basic_open = PJ_FALSE; - } - - pidf_tuple = pjpidf_pres_get_next_tuple( pidf, pidf_tuple ); - pres_status->info_cnt++; - } - - return PJ_SUCCESS; -} - -/* - * Parse XPIDF info. - */ -static pj_status_t pres_parse_xpidf( pjsip_pres *pres, - pjsip_rx_data *rdata, - pjsip_pres_status *pres_status) -{ - pjxpidf_pres *xpidf; - - xpidf = pjxpidf_parse(rdata->tp_info.pool, - rdata->msg_info.msg->body->data, - rdata->msg_info.msg->body->len); - if (xpidf == NULL) - return PJSIP_SIMPLE_EBADXPIDF; - - pres_status->info_cnt = 1; - - pj_strdup(pres->dlg->pool, - &pres_status->info[0].contact, - pjxpidf_get_uri(xpidf)); - pres_status->info[0].basic_open = pjxpidf_get_status(xpidf); - pres_status->info[0].id.slen = 0; - - return PJ_SUCCESS; -} - /* * Process the content of incoming NOTIFY request and update temporary @@ -836,13 +661,15 @@ static pj_status_t pres_process_rx_notify( pjsip_pres *pres, if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 && pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0) { - status = pres_parse_pidf( pres, rdata, &pres->tmp_status); + status = pjsip_pres_parse_pidf( rdata, pres->dlg->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 = pres_parse_xpidf( pres, rdata, &pres->tmp_status); + status = pjsip_pres_parse_xpidf( rdata, pres->dlg->pool, + &pres->tmp_status); } else { diff --git a/pjsip/src/pjsip-simple/presence_body.c b/pjsip/src/pjsip-simple/presence_body.c new file mode 100644 index 00000000..c67890c1 --- /dev/null +++ b/pjsip/src/pjsip-simple/presence_body.c @@ -0,0 +1,228 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 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/sip_msg.h> +#include <pjsip/sip_transport.h> +#include <pj/guid.h> +#include <pj/log.h> +#include <pj/pool.h> +#include <pj/string.h> + + +#define THIS_FILE "presence_body.c" + + +static const pj_str_t STR_APPLICATION = { "application", 11 }; +static const pj_str_t STR_PIDF_XML = { "pidf+xml", 8 }; +static const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9 }; + + + + +/* + * Function to print XML message body. + */ +static int pres_print_body(struct pjsip_msg_body *msg_body, + char *buf, pj_size_t size) +{ + return pj_xml_print(msg_body->data, buf, size, PJ_TRUE); +} + + +/* + * Function to clone XML document. + */ +static void* xml_clone_data(pj_pool_t *pool, const void *data, unsigned len) +{ + PJ_UNUSED_ARG(len); + return pj_xml_clone( pool, data); +} + + +/* + * This is a utility function to create PIDF message body from PJSIP + * presence status (pjsip_pres_status). + */ +PJ_DEF(pj_status_t) pjsip_pres_create_pidf( pj_pool_t *pool, + const pjsip_pres_status *status, + const pj_str_t *entity, + pjsip_msg_body **p_body ) +{ + pjpidf_pres *pidf; + pjsip_msg_body *body; + unsigned i; + + /* Create <presence>. */ + pidf = pjpidf_create(pool, entity); + + /* Create <tuple> */ + for (i=0; i<status->info_cnt; ++i) { + + pjpidf_tuple *pidf_tuple; + pjpidf_status *pidf_status; + pj_str_t id; + + /* Add tuple id. */ + if (status->info[i].id.slen == 0) { + pj_create_unique_string(pool, &id); + } else { + id = status->info[i].id; + } + + pidf_tuple = pjpidf_pres_add_tuple(pool, pidf, &id); + + /* Set <contact> */ + if (status->info[i].contact.slen) + pjpidf_tuple_set_contact(pool, pidf_tuple, + &status->info[i].contact); + + + /* Set basic status */ + pidf_status = pjpidf_tuple_get_status(pidf_tuple); + pjpidf_status_set_basic_open(pidf_status, + status->info[i].basic_open); + } + + body = pj_pool_zalloc(pool, sizeof(pjsip_msg_body)); + body->data = pidf; + body->content_type.type = STR_APPLICATION; + body->content_type.subtype = STR_PIDF_XML; + body->print_body = &pres_print_body; + body->clone_data = &xml_clone_data; + + *p_body = body; + + return PJ_SUCCESS; +} + + +/* + * This is a utility function to create X-PIDF message body from PJSIP + * presence status (pjsip_pres_status). + */ +PJ_DEF(pj_status_t) pjsip_pres_create_xpidf( pj_pool_t *pool, + const pjsip_pres_status *status, + const pj_str_t *entity, + pjsip_msg_body **p_body ) +{ + /* Note: PJSIP implementation of XPIDF is not complete! + */ + pjxpidf_pres *xpidf; + pjsip_msg_body *body; + + PJ_LOG(4,(THIS_FILE, "Warning: XPIDF format is not fully supported " + "by PJSIP")); + + /* Create XPIDF document. */ + xpidf = pjxpidf_create(pool, entity); + + /* Set basic status. */ + if (status->info_cnt > 0) + pjxpidf_set_status( xpidf, status->info[0].basic_open); + else + pjxpidf_set_status( xpidf, PJ_FALSE); + + body = pj_pool_zalloc(pool, sizeof(pjsip_msg_body)); + body->data = xpidf; + body->content_type.type = STR_APPLICATION; + body->content_type.subtype = STR_XPIDF_XML; + body->print_body = &pres_print_body; + body->clone_data = &xml_clone_data; + + *p_body = body; + + return PJ_SUCCESS; +} + + + +/* + * This is a utility function to parse PIDF body into PJSIP presence status. + */ +PJ_DEF(pj_status_t) pjsip_pres_parse_pidf( pjsip_rx_data *rdata, + pj_pool_t *pool, + pjsip_pres_status *pres_status) +{ + pjpidf_pres *pidf; + pjpidf_tuple *pidf_tuple; + + pidf = pjpidf_parse(rdata->tp_info.pool, + rdata->msg_info.msg->body->data, + rdata->msg_info.msg->body->len); + if (pidf == NULL) + return PJSIP_SIMPLE_EBADPIDF; + + pres_status->info_cnt = 0; + + pidf_tuple = pjpidf_pres_get_first_tuple(pidf); + while (pidf_tuple) { + pjpidf_status *pidf_status; + + pj_strdup(pool, + &pres_status->info[pres_status->info_cnt].id, + pjpidf_tuple_get_id(pidf_tuple)); + + pj_strdup(pool, + &pres_status->info[pres_status->info_cnt].contact, + pjpidf_tuple_get_contact(pidf_tuple)); + + pidf_status = pjpidf_tuple_get_status(pidf_tuple); + if (pidf_status) { + pres_status->info[pres_status->info_cnt].basic_open = + pjpidf_status_is_basic_open(pidf_status); + } else { + pres_status->info[pres_status->info_cnt].basic_open = PJ_FALSE; + } + + pidf_tuple = pjpidf_pres_get_next_tuple( pidf, pidf_tuple ); + pres_status->info_cnt++; + } + + return PJ_SUCCESS; +} + + +/* + * This is a utility function to parse X-PIDF body into PJSIP presence status. + */ +PJ_DEF(pj_status_t) pjsip_pres_parse_xpidf(pjsip_rx_data *rdata, + pj_pool_t *pool, + pjsip_pres_status *pres_status) +{ + pjxpidf_pres *xpidf; + + xpidf = pjxpidf_parse(rdata->tp_info.pool, + rdata->msg_info.msg->body->data, + rdata->msg_info.msg->body->len); + if (xpidf == NULL) + return PJSIP_SIMPLE_EBADXPIDF; + + pres_status->info_cnt = 1; + + pj_strdup(pool, + &pres_status->info[0].contact, + pjxpidf_get_uri(xpidf)); + pres_status->info[0].basic_open = pjxpidf_get_status(xpidf); + pres_status->info[0].id.slen = 0; + + return PJ_SUCCESS; +} + + diff --git a/pjsip/src/pjsip-simple/publishc.c b/pjsip/src/pjsip-simple/publishc.c index b61fa469..5a0df963 100644 --- a/pjsip/src/pjsip-simple/publishc.c +++ b/pjsip/src/pjsip-simple/publishc.c @@ -16,32 +16,41 @@ * 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-ua/sip_regc.h> +#include <pjsip-simple/publish.h> +#include <pjsip/sip_auth.h> #include <pjsip/sip_endpoint.h> -#include <pjsip/sip_parser.h> -#include <pjsip/sip_module.h> -#include <pjsip/sip_transaction.h> +#include <pjsip/sip_errno.h> #include <pjsip/sip_event.h> +#include <pjsip/sip_msg.h> +#include <pjsip/sip_transaction.h> +#include <pjsip/sip_uri.h> #include <pjsip/sip_util.h> -#include <pjsip/sip_auth_msg.h> -#include <pjsip/sip_errno.h> #include <pj/assert.h> #include <pj/guid.h> +#include <pj/log.h> #include <pj/os.h> #include <pj/pool.h> -#include <pj/log.h> #include <pj/rand.h> #include <pj/string.h> +#include <pj/timer.h> #define REFRESH_TIMER 1 #define DELAY_BEFORE_REFRESH 5 -#define THIS_FILE "sip_regc.c" +#define THIS_FILE "publishc.c" + + +const pjsip_method pjsip_publish_method = +{ + PJSIP_OTHER_METHOD, + { "PUBLISH", 7 } +}; + /** - * SIP client registration structure. + * SIP client publication structure. */ -struct pjsip_regc +struct pjsip_publishc { pj_pool_t *pool; pjsip_endpoint *endpt; @@ -49,291 +58,228 @@ struct pjsip_regc int pending_tsx; void *token; - pjsip_regc_cb *cb; + pjsip_publishc_cb *cb; - pj_str_t str_srv_url; - pjsip_uri *srv_url; + pj_str_t event; + pj_str_t str_target_uri; + pjsip_uri *target_uri; pjsip_cid_hdr *cid_hdr; pjsip_cseq_hdr *cseq_hdr; pj_str_t from_uri; pjsip_from_hdr *from_hdr; pjsip_to_hdr *to_hdr; - char *contact_buf; - pjsip_generic_string_hdr *contact_hdr; + pj_str_t etag; pjsip_expires_hdr *expires_hdr; - pjsip_contact_hdr *unreg_contact_hdr; - pjsip_expires_hdr *unreg_expires_hdr; pj_uint32_t expires; pjsip_route_hdr route_set; /* Authorization sessions. */ pjsip_auth_clt_sess auth_sess; - /* Auto refresh registration. */ - pj_bool_t auto_reg; - pj_time_val last_reg; - pj_time_val next_reg; + /* Auto refresh publication. */ + pj_bool_t auto_refresh; + pj_time_val last_refresh; + pj_time_val next_refresh; pj_timer_entry timer; }; -PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token, - pjsip_regc_cb *cb, - pjsip_regc **p_regc) +/* + * Initialize client publication module. + */ +PJ_DEF(pj_status_t) pjsip_publishc_init_module(pjsip_endpoint *endpt) +{ + return pjsip_endpt_add_capability( endpt, NULL, PJSIP_H_ALLOW, NULL, + 1, &pjsip_publish_method.name); +} + + +PJ_DEF(pj_status_t) pjsip_publishc_create( pjsip_endpoint *endpt, + unsigned options, + void *token, + pjsip_publishc_cb *cb, + pjsip_publishc **p_pubc) { pj_pool_t *pool; - pjsip_regc *regc; + pjsip_publishc *pubc; pj_status_t status; /* Verify arguments. */ - PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL); + PJ_ASSERT_RETURN(endpt && cb && p_pubc, PJ_EINVAL); + PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); - pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024); + PJ_UNUSED_ARG(options); + + pool = pjsip_endpt_create_pool(endpt, "pubc%p", 1024, 1024); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); - regc = pj_pool_zalloc(pool, sizeof(struct pjsip_regc)); + pubc = pj_pool_zalloc(pool, sizeof(struct pjsip_publishc)); - regc->pool = pool; - regc->endpt = endpt; - regc->token = token; - regc->cb = cb; - regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE); - regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED; + pubc->pool = pool; + pubc->endpt = endpt; + pubc->token = token; + pubc->cb = cb; + pubc->expires = PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED; - status = pjsip_auth_clt_init(®c->auth_sess, endpt, regc->pool, 0); + status = pjsip_auth_clt_init(&pubc->auth_sess, endpt, pubc->pool, 0); if (status != PJ_SUCCESS) return status; - pj_list_init(®c->route_set); + pj_list_init(&pubc->route_set); /* Done */ - *p_regc = regc; + *p_pubc = pubc; return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc) +PJ_DEF(pj_status_t) pjsip_publishc_destroy(pjsip_publishc *pubc) { - PJ_ASSERT_RETURN(regc, PJ_EINVAL); + PJ_ASSERT_RETURN(pubc, PJ_EINVAL); - if (regc->pending_tsx) { - regc->_delete_flag = 1; - regc->cb = NULL; + if (pubc->pending_tsx) { + pubc->_delete_flag = 1; + pubc->cb = NULL; } else { - pjsip_endpt_release_pool(regc->endpt, regc->pool); - } - - return PJ_SUCCESS; -} - - -PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc, - pjsip_regc_info *info ) -{ - PJ_ASSERT_RETURN(regc && info, PJ_EINVAL); - - info->server_uri = regc->str_srv_url; - info->client_uri = regc->from_uri; - info->is_busy = (regc->pending_tsx != 0); - info->auto_reg = regc->auto_reg; - info->interval = regc->expires; - - if (regc->pending_tsx) - info->next_reg = 0; - else if (regc->auto_reg == 0) - info->next_reg = 0; - else if (regc->expires < 0) - info->next_reg = regc->expires; - else { - pj_time_val now, next_reg; - - next_reg = regc->next_reg; - pj_gettimeofday(&now); - PJ_TIME_VAL_SUB(next_reg, now); - info->next_reg = next_reg.sec; + pjsip_endpt_release_pool(pubc->endpt, pubc->pool); } return PJ_SUCCESS; } -PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc) +PJ_DEF(pj_pool_t*) pjsip_publishc_get_pool(pjsip_publishc *pubc) { - return regc->pool; + return pubc->pool; } -static void set_expires( pjsip_regc *regc, pj_uint32_t expires) +static void set_expires( pjsip_publishc *pubc, pj_uint32_t expires) { - if (expires != regc->expires) { - regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires); + if (expires != pubc->expires) { + pubc->expires_hdr = pjsip_expires_hdr_create(pubc->pool, expires); } else { - regc->expires_hdr = NULL; - } -} - - -static pj_status_t set_contact( pjsip_regc *regc, - int contact_cnt, - const pj_str_t contact[] ) -{ - int i; - char *s; - const pj_str_t contact_STR = { "Contact", 7}; - - /* Concatenate contacts. */ - for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) { - if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) { - return PJSIP_EURITOOLONG; - } - pj_memcpy(s, contact[i].ptr, contact[i].slen); - s += contact[i].slen; - - if (i != contact_cnt - 1) { - *s++ = ','; - *s++ = ' '; - } + pubc->expires_hdr = NULL; } - - /* Set "Contact" header. */ - regc->contact_hdr = pjsip_generic_string_hdr_create(regc->pool, - &contact_STR, - NULL); - regc->contact_hdr->hvalue.ptr = regc->contact_buf; - regc->contact_hdr->hvalue.slen = (s - regc->contact_buf); - - return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, - const pj_str_t *srv_url, - const pj_str_t *from_url, - const pj_str_t *to_url, - int contact_cnt, - const pj_str_t contact[], - pj_uint32_t expires) +PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc, + const pj_str_t *event, + const pj_str_t *target_uri, + const pj_str_t *from_uri, + const pj_str_t *to_uri, + pj_uint32_t expires) { pj_str_t tmp; - pj_status_t status; - PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url && - contact_cnt && contact && expires, PJ_EINVAL); + PJ_ASSERT_RETURN(pubc && event && target_uri && from_uri && to_uri && + expires, PJ_EINVAL); + + /* Copy event type */ + pj_strdup_with_null(pubc->pool, &pubc->event, event); /* Copy server URL. */ - pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url); + pj_strdup_with_null(pubc->pool, &pubc->str_target_uri, target_uri); /* Set server URL. */ - tmp = regc->str_srv_url; - regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0); - if (regc->srv_url == NULL) { + tmp = pubc->str_target_uri; + pubc->target_uri = pjsip_parse_uri( pubc->pool, tmp.ptr, tmp.slen, 0); + if (pubc->target_uri == NULL) { return PJSIP_EINVALIDURI; } /* Set "From" header. */ - pj_strdup_with_null(regc->pool, ®c->from_uri, from_url); - tmp = regc->from_uri; - regc->from_hdr = pjsip_from_hdr_create(regc->pool); - regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, + pj_strdup_with_null(pubc->pool, &pubc->from_uri, from_uri); + tmp = pubc->from_uri; + pubc->from_hdr = pjsip_from_hdr_create(pubc->pool); + pubc->from_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); - if (!regc->from_hdr->uri) { - PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", - from_url->slen, from_url->ptr)); + if (!pubc->from_hdr->uri) { return PJSIP_EINVALIDURI; } /* Set "To" header. */ - pj_strdup_with_null(regc->pool, &tmp, to_url); - regc->to_hdr = pjsip_to_hdr_create(regc->pool); - regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, + pj_strdup_with_null(pubc->pool, &tmp, to_uri); + pubc->to_hdr = pjsip_to_hdr_create(pubc->pool); + pubc->to_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); - if (!regc->to_hdr->uri) { - PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr)); + if (!pubc->to_hdr->uri) { return PJSIP_EINVALIDURI; } - /* Set "Contact" header. */ - status = set_contact( regc, contact_cnt, contact); - if (status != PJ_SUCCESS) - return status; - /* Set "Expires" header, if required. */ - set_expires( regc, expires); + set_expires( pubc, expires); /* Set "Call-ID" header. */ - regc->cid_hdr = pjsip_cid_hdr_create(regc->pool); - pj_create_unique_string(regc->pool, ®c->cid_hdr->id); + pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool); + pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id); /* Set "CSeq" header. */ - regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool); - regc->cseq_hdr->cseq = pj_rand() % 0xFFFF; - pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD); - - /* Create "Contact" header used in unregistration. */ - regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool); - regc->unreg_contact_hdr->star = 1; - - /* Create "Expires" header used in unregistration. */ - regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool, 0); + pubc->cseq_hdr = pjsip_cseq_hdr_create(pubc->pool); + pubc->cseq_hdr->cseq = pj_rand() % 0xFFFF; + pjsip_method_set( &pubc->cseq_hdr->method, PJSIP_REGISTER_METHOD); /* Done. */ return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, +PJ_DEF(pj_status_t) pjsip_publishc_set_credentials( pjsip_publishc *pubc, int count, const pjsip_cred_info cred[] ) { - PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL); - return pjsip_auth_clt_set_credentials(®c->auth_sess, count, cred); + PJ_ASSERT_RETURN(pubc && count && cred, PJ_EINVAL); + return pjsip_auth_clt_set_credentials(&pubc->auth_sess, count, cred); } -PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc, +PJ_DEF(pj_status_t) pjsip_publishc_set_route_set( pjsip_publishc *pubc, const pjsip_route_hdr *route_set) { const pjsip_route_hdr *chdr; - PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL); + PJ_ASSERT_RETURN(pubc && route_set, PJ_EINVAL); - pj_list_init(®c->route_set); + pj_list_init(&pubc->route_set); chdr = route_set->next; while (chdr != route_set) { - pj_list_push_back(®c->route_set, pjsip_hdr_clone(regc->pool, chdr)); + pj_list_push_back(&pubc->route_set, pjsip_hdr_clone(pubc->pool, chdr)); chdr = chdr->next; } return PJ_SUCCESS; } -static pj_status_t create_request(pjsip_regc *regc, +static pj_status_t create_request(pjsip_publishc *pubc, pjsip_tx_data **p_tdata) { + const pj_str_t STR_EVENT = { "Event", 5 }; pj_status_t status; + pjsip_generic_string_hdr *hdr; pjsip_tx_data *tdata; - PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); + PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL); /* Create the request. */ - status = pjsip_endpt_create_request_from_hdr( regc->endpt, - &pjsip_register_method, - regc->srv_url, - regc->from_hdr, - regc->to_hdr, + status = pjsip_endpt_create_request_from_hdr( pubc->endpt, + &pjsip_publish_method, + pubc->target_uri, + pubc->from_hdr, + pubc->to_hdr, NULL, - regc->cid_hdr, - regc->cseq_hdr->cseq, + pubc->cid_hdr, + pubc->cseq_hdr->cseq, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add cached authorization headers. */ - pjsip_auth_clt_init_req( ®c->auth_sess, tdata ); + pjsip_auth_clt_init_req( &pubc->auth_sess, tdata ); /* Add Route headers from route set, ideally after Via header */ - if (!pj_list_empty(®c->route_set)) { + if (!pj_list_empty(&pubc->route_set)) { pjsip_hdr *route_pos; const pjsip_route_hdr *route; @@ -341,8 +287,8 @@ static pj_status_t create_request(pjsip_regc *regc, if (!route_pos) route_pos = &tdata->msg->hdr; - route = regc->route_set.next; - while (route != ®c->route_set) { + route = pubc->route_set.next; + while (route != &pubc->route_set) { pjsip_hdr *new_hdr = pjsip_hdr_shallow_clone(tdata->pool, route); pj_list_insert_after(route_pos, new_hdr); route_pos = new_hdr; @@ -350,37 +296,59 @@ static pj_status_t create_request(pjsip_regc *regc, } } + /* Add Event header */ + hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT, + &pubc->event); + if (hdr) + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); + + + /* Add SIP-If-Match if we have etag */ + if (pubc->etag.slen) { + const pj_str_t STR_HNAME = { "SIP-If-Match", 12 }; + + hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME, + &pubc->etag); + if (hdr) + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); + } + + /* Done. */ *p_tdata = tdata; return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg, - pjsip_tx_data **p_tdata) +PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc, + pj_bool_t auto_refresh, + pjsip_tx_data **p_tdata) { - pjsip_msg *msg; pj_status_t status; pjsip_tx_data *tdata; - PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); + PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL); - status = create_request(regc, &tdata); + status = create_request(pubc, &tdata); if (status != PJ_SUCCESS) return status; - /* Add Contact header. */ - msg = tdata->msg; - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr); - if (regc->expires_hdr) - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr); + /* Add Expires header */ + if (pubc->expires_hdr) { + pjsip_hdr *dup; - if (regc->timer.id != 0) { - pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); - regc->timer.id = 0; + dup = pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr); + if (dup) + pjsip_msg_add_hdr(tdata->msg, dup); } - regc->auto_reg = autoreg; + /* Cancel existing timer */ + if (pubc->timer.id != 0) { + pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer); + pubc->timer.id = 0; + } + + pubc->auto_refresh = auto_refresh; /* Done */ *p_tdata = tdata; @@ -388,111 +356,99 @@ PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg, } -PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc, - pjsip_tx_data **p_tdata) +PJ_DEF(pj_status_t) pjsip_publishc_unpublish(pjsip_publishc *pubc, + pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_msg *msg; + pjsip_expires_hdr *expires; pj_status_t status; - PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); + PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL); - if (regc->timer.id != 0) { - pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); - regc->timer.id = 0; + if (pubc->timer.id != 0) { + pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer); + pubc->timer.id = 0; } - status = create_request(regc, &tdata); + status = create_request(pubc, &tdata); if (status != PJ_SUCCESS) return status; msg = tdata->msg; - pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr); - pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr); + + /* Add Expires:0 header */ + expires = pjsip_expires_hdr_create(tdata->pool, 0); + pjsip_msg_add_hdr( msg, (pjsip_hdr*)expires); *p_tdata = tdata; return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc, - int contact_cnt, - const pj_str_t contact[] ) -{ - PJ_ASSERT_RETURN(regc, PJ_EINVAL); - return set_contact( regc, contact_cnt, contact ); -} - - -PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, - pj_uint32_t expires ) +PJ_DEF(pj_status_t) pjsip_publishc_update_expires( pjsip_publishc *pubc, + pj_uint32_t expires ) { - PJ_ASSERT_RETURN(regc, PJ_EINVAL); - set_expires( regc, expires ); + PJ_ASSERT_RETURN(pubc, PJ_EINVAL); + set_expires( pubc, expires ); return PJ_SUCCESS; } -static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code, - const pj_str_t *reason, - pjsip_rx_data *rdata, pj_int32_t expiration, - int contact_cnt, pjsip_contact_hdr *contact[]) +static void call_callback(pjsip_publishc *pubc, pj_status_t status, + int st_code, const pj_str_t *reason, + pjsip_rx_data *rdata, pj_int32_t expiration) { - struct pjsip_regc_cbparam cbparam; + struct pjsip_publishc_cbparam cbparam; - cbparam.regc = regc; - cbparam.token = regc->token; + cbparam.pubc = pubc; + cbparam.token = pubc->token; cbparam.status = status; cbparam.code = st_code; cbparam.reason = *reason; cbparam.rdata = rdata; - cbparam.contact_cnt = contact_cnt; cbparam.expiration = expiration; - if (contact_cnt) { - pj_memcpy( cbparam.contact, contact, - contact_cnt*sizeof(pjsip_contact_hdr*)); - } - (*regc->cb)(&cbparam); + (*pubc->cb)(&cbparam); } -static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap, +static void pubc_refresh_timer_cb( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { - pjsip_regc *regc = entry->user_data; + pjsip_publishc *pubc = entry->user_data; pjsip_tx_data *tdata; pj_status_t status; PJ_UNUSED_ARG(timer_heap); entry->id = 0; - status = pjsip_regc_register(regc, 1, &tdata); + status = pjsip_publishc_publish(pubc, 1, &tdata); if (status == PJ_SUCCESS) { - status = pjsip_regc_send(regc, tdata); + status = pjsip_publishc_send(pubc, tdata); } if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg)); - call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL); + call_callback(pubc, status, 400, &reason, NULL, -1); } } static void tsx_callback(void *token, pjsip_event *event) { pj_status_t status; - pjsip_regc *regc = token; + pjsip_publishc *pubc = token; pjsip_transaction *tsx = event->body.tsx_state.tsx; /* Decrement pending transaction counter. */ - pj_assert(regc->pending_tsx > 0); - --regc->pending_tsx; + pj_assert(pubc->pending_tsx > 0); + --pubc->pending_tsx; - /* If registration data has been deleted by user then remove registration + /* If publication data has been deleted by user then remove publication * data from transaction's callback, and don't call callback. */ - if (regc->_delete_flag) { + if (pubc->_delete_flag) { /* Nothing to do */ ; @@ -503,75 +459,69 @@ static void tsx_callback(void *token, pjsip_event *event) pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; pjsip_tx_data *tdata; - status = pjsip_auth_clt_reinit_req( ®c->auth_sess, + status = pjsip_auth_clt_reinit_req( &pubc->auth_sess, rdata, tsx->last_tx, &tdata); if (status == PJ_SUCCESS) { - status = pjsip_regc_send(regc, tdata); + status = pjsip_publishc_send(pubc, tdata); } if (status != PJ_SUCCESS) { - call_callback(regc, status, tsx->status_code, + call_callback(pubc, status, tsx->status_code, &rdata->msg_info.msg->line.status.reason, - rdata, -1, 0, NULL); + rdata, -1); } return; } else { - int contact_cnt = 0; - pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; pjsip_rx_data *rdata; pj_int32_t expiration = 0xFFFF; if (tsx->status_code/100 == 2) { - int i; - pjsip_contact_hdr *hdr; pjsip_msg *msg; pjsip_expires_hdr *expires; + pjsip_generic_string_hdr *etag_hdr; + const pj_str_t STR_ETAG = { "SIP-ETag", 8 }; rdata = event->body.tsx_state.src.rdata; msg = rdata->msg_info.msg; - hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); - while (hdr) { - contact[contact_cnt++] = hdr; - hdr = hdr->next; - if (hdr == (void*)&msg->hdr) - break; - hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr); + + /* Save ETag value */ + etag_hdr = (pjsip_generic_string_hdr*) + pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL); + if (etag_hdr) { + pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue); + } else { + pubc->etag.slen = 0; } + /* Update expires value */ expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); if (expires) expiration = expires->ivalue; - for (i=0; i<contact_cnt; ++i) { - hdr = contact[i]; - if (hdr->expires >= 0 && hdr->expires < expiration) - expiration = contact[i]->expires; - } - - if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) { + if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) { pj_time_val delay = { 0, 0}; delay.sec = expiration - DELAY_BEFORE_REFRESH; - if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED && - delay.sec > (pj_int32_t)regc->expires) + if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED && + delay.sec > (pj_int32_t)pubc->expires) { - delay.sec = regc->expires; + delay.sec = pubc->expires; } if (delay.sec < DELAY_BEFORE_REFRESH) delay.sec = DELAY_BEFORE_REFRESH; - regc->timer.cb = ®c_refresh_timer_cb; - regc->timer.id = REFRESH_TIMER; - regc->timer.user_data = regc; - pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay); - pj_gettimeofday(®c->last_reg); - regc->next_reg = regc->last_reg; - regc->next_reg.sec += delay.sec; + pubc->timer.cb = &pubc_refresh_timer_cb; + pubc->timer.id = REFRESH_TIMER; + pubc->timer.user_data = pubc; + pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay); + pj_gettimeofday(&pubc->last_refresh); + pubc->next_refresh = pubc->last_refresh; + pubc->next_refresh.sec += delay.sec; } } else { @@ -582,29 +532,30 @@ static void tsx_callback(void *token, pjsip_event *event) /* Call callback. */ if (expiration == 0xFFFF) expiration = -1; - call_callback(regc, PJ_SUCCESS, tsx->status_code, + call_callback(pubc, PJ_SUCCESS, tsx->status_code, (rdata ? &rdata->msg_info.msg->line.status.reason : pjsip_get_status_text(tsx->status_code)), - rdata, expiration, - contact_cnt, contact); + rdata, expiration); } - /* Delete the record if user destroy regc during the callback. */ - if (regc->_delete_flag && regc->pending_tsx==0) { - pjsip_regc_destroy(regc); + /* Delete the record if user destroy pubc during the callback. */ + if (pubc->_delete_flag && pubc->pending_tsx==0) { + pjsip_publishc_destroy(pubc); } } -PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) + +PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc, + pjsip_tx_data *tdata) { pj_status_t status; pjsip_cseq_hdr *cseq_hdr; pj_uint32_t cseq; /* Make sure we don't have pending transaction. */ - if (regc->pending_tsx) { - PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another " + if (pubc->pending_tsx) { + PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another " "transaction pending")); pjsip_tx_data_dec_ref( tdata ); return PJSIP_EBUSY; @@ -614,17 +565,18 @@ PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) pjsip_tx_data_invalidate_msg(tdata); /* Increment CSeq */ - cseq = ++regc->cseq_hdr->cseq; + cseq = ++pubc->cseq_hdr->cseq; cseq_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); cseq_hdr->cseq = cseq; /* Increment pending transaction first, since transaction callback * may be called even before send_request() returns! */ - ++regc->pending_tsx; - status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback); + ++pubc->pending_tsx; + status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc, + &tsx_callback); if (status!=PJ_SUCCESS) { - --regc->pending_tsx; + --pubc->pending_tsx; PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status)); } diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index aedab9d7..1fa39831 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -333,15 +333,15 @@ PJ_DEF(pj_status_t) pjsip_endpt_add_capability( pjsip_endpoint *endpt, pjsip_generic_array_hdr *hdr; unsigned i; + PJ_UNUSED_ARG(mod); + /* Check arguments. */ - PJ_ASSERT_RETURN(endpt!=NULL && mod!=NULL && count>0 && tags, PJ_EINVAL); + PJ_ASSERT_RETURN(endpt!=NULL && count>0 && tags, PJ_EINVAL); PJ_ASSERT_RETURN(htype==PJSIP_H_ACCEPT || htype==PJSIP_H_ALLOW || htype==PJSIP_H_SUPPORTED, PJ_EINVAL); - PJ_UNUSED_ARG(mod); - /* Find the header. */ hdr = (pjsip_generic_array_hdr*) pjsip_endpt_get_capability(endpt, htype, hname); diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 7860c33f..d545bdfb 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -98,6 +98,7 @@ static pj_status_t initialize_acc(unsigned acc_id) pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsip_name_addr *name_addr; pjsip_sip_uri *sip_uri, *sip_reg_uri; + pj_status_t status; unsigned i; /* Need to parse local_uri to get the elements: */ @@ -220,8 +221,10 @@ static pj_status_t initialize_acc(unsigned acc_id) acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i]; } - /* Init presence subscription */ - pj_list_init(&acc->pres_srv_list); + status = pjsua_pres_init_acc(acc_id); + if (status != PJ_SUCCESS) + return status; + /* Mark account as valid */ pjsua_var.acc[acc_id].valid = PJ_TRUE; diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 9c011b84..c9bd2482 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -524,6 +524,8 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance()); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + /* Init PUBLISH module */ + pjsip_publishc_init_module(pjsua_var.endpt); /* Init xfer/REFER module */ status = pjsip_xfer_init_module( pjsua_var.endpt ); diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index ceb1b762..b3205cc5 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -566,14 +566,163 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata) } +/* + * Client presence publication callback. + */ +static void publish_cb(struct pjsip_publishc_cbparam *param) +{ + pjsua_acc *acc = param->token; + + if (param->code/100 != 2 || param->status != PJ_SUCCESS) { + if (param->status != PJ_SUCCESS) { + char errmsg[PJ_ERR_MSG_SIZE]; + + pj_strerror(param->status, errmsg, sizeof(errmsg)); + PJ_LOG(1,(THIS_FILE, + "Client publication (PUBLISH) failed, status=%d, msg=%s", + param->status, errmsg)); + } else { + PJ_LOG(1,(THIS_FILE, + "Client publication (PUBLISH) failed (%d/%.*s)", + param->code, (int)param->reason.slen, + param->reason.ptr)); + } + + pjsip_publishc_destroy(param->pubc); + acc->publish_sess = NULL; + } +} + + +/* + * Send PUBLISH request. + */ +static pj_status_t send_publish(int acc_id, pj_bool_t active) +{ + pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; + pjsua_acc *acc = &pjsua_var.acc[acc_id]; + pjsip_pres_status pres_status; + pjsip_tx_data *tdata; + pj_status_t status; + + + /* Create PUBLISH request */ + if (active) { + status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status); + goto on_error; + } + + /* Set our online status: */ + pj_bzero(&pres_status, sizeof(pres_status)); + pres_status.info_cnt = 1; + pres_status.info[0].basic_open = acc->online_status; + + /* Create and add PIDF message body */ + status = pjsip_pres_create_pidf(tdata->pool, &pres_status, + &acc_cfg->id, &tdata->msg->body); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request", + status); + pjsip_tx_data_dec_ref(tdata); + goto on_error; + } + } else { + status = pjsip_publishc_unpublish(acc->publish_sess, &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status); + goto on_error; + } + } + + /* Add headers etc */ + pjsua_process_msg_data(tdata, NULL); + + /* Send the PUBLISH request */ + status = pjsip_publishc_send(acc->publish_sess, tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status); + goto on_error; + } + + acc->publish_state = acc->online_status; + return PJ_SUCCESS; + +on_error: + pjsip_publishc_destroy(acc->publish_sess); + acc->publish_sess = NULL; + return status; +} + + +/* Create client publish session */ +static pj_status_t create_publish(int acc_id) +{ + const pj_str_t STR_PRESENCE = { "presence", 8 }; + pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; + pjsua_acc *acc = &pjsua_var.acc[acc_id]; + pj_status_t status; + + /* Create and init client publication session */ + if (acc_cfg->publish_enabled) { + + /* Create client publication */ + status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb, + &acc->publish_sess); + if (status != PJ_SUCCESS) { + acc->publish_sess = NULL; + return status; + } + + /* Initialize client publication */ + status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE, + &acc_cfg->id, &acc_cfg->id, + &acc_cfg->id, + PJSUA_PUBLISH_EXPIRATION); + if (status != PJ_SUCCESS) { + acc->publish_sess = NULL; + return status; + } + + /* Send initial PUBLISH request */ + if (acc->online_status != 0) { + status = send_publish(acc_id, PJ_TRUE); + if (status != PJ_SUCCESS) + return status; + } + + } else { + acc->publish_sess = NULL; + } + + return PJ_SUCCESS; +} + + +/* Init presence for account */ +pj_status_t pjsua_pres_init_acc(int acc_id) +{ + pjsua_acc *acc = &pjsua_var.acc[acc_id]; + + /* Init presence subscription */ + pj_list_init(&acc->pres_srv_list); + + + return create_publish(acc_id); +} + + /* Terminate server subscription for the account */ void pjsua_pres_delete_acc(int acc_id) { + pjsua_acc *acc = &pjsua_var.acc[acc_id]; + pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_srv_pres *uapres; uapres = pjsua_var.acc[acc_id].pres_srv_list.next; - while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) { + while (uapres != &acc->pres_srv_list) { pjsip_pres_status pres_status; pj_str_t reason = { "noresource", 10 }; @@ -593,24 +742,36 @@ void pjsua_pres_delete_acc(int acc_id) uapres = uapres->next; } + + if (acc->publish_sess) { + acc->online_status = PJ_FALSE; + send_publish(acc_id, PJ_FALSE); + if (acc->publish_sess) { + pjsip_publishc_destroy(acc->publish_sess); + acc->publish_sess = NULL; + } + acc_cfg->publish_enabled = PJ_FALSE; + } } /* Refresh subscription (e.g. when our online status has changed) */ static void refresh_server_subscription(int acc_id) { + pjsua_acc *acc = &pjsua_var.acc[acc_id]; + pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_srv_pres *uapres; uapres = pjsua_var.acc[acc_id].pres_srv_list.next; - while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) { + while (uapres != &acc->pres_srv_list) { pjsip_pres_status pres_status; pjsip_tx_data *tdata; pjsip_pres_get_status(uapres->sub, &pres_status); - if (pres_status.info[0].basic_open != pjsua_var.acc[acc_id].online_status) { - pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status; + if (pres_status.info[0].basic_open != acc->online_status) { + pres_status.info[0].basic_open = acc->online_status; pjsip_pres_set_status(uapres->sub, &pres_status); if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) { @@ -621,6 +782,16 @@ static void refresh_server_subscription(int acc_id) uapres = uapres->next; } + + /* Send PUBLISH if required */ + if (acc_cfg->publish_enabled) { + if (acc->publish_sess == NULL) + create_publish(acc_id); + + if (acc->publish_sess && acc->publish_state != acc->online_status) { + send_publish(acc_id, PJ_TRUE); + } + } } @@ -819,7 +990,7 @@ static void subscribe_buddy_presence(unsigned index) } status = pjsip_pres_create_uac( dlg, &pres_callback, - &buddy->sub); + PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub); if (status != PJ_SUCCESS) { pjsua_var.buddy[index].sub = NULL; pjsua_perror(THIS_FILE, "Unable to create presence client", |