summaryrefslogtreecommitdiff
path: root/pjsip/src/pjsip-simple/publishc.c
diff options
context:
space:
mode:
authorDavid M. Lee <dlee@digium.com>2013-01-07 14:24:28 -0600
committerDavid M. Lee <dlee@digium.com>2013-01-07 14:24:28 -0600
commitf3ab456a17af1c89a6e3be4d20c5944853df1cb0 (patch)
treed00e1a332cd038a6d906a1ea0ac91e1a4458e617 /pjsip/src/pjsip-simple/publishc.c
Import pjproject-2.0.1
Diffstat (limited to 'pjsip/src/pjsip-simple/publishc.c')
-rw-r--r--pjsip/src/pjsip-simple/publishc.c790
1 files changed, 790 insertions, 0 deletions
diff --git a/pjsip/src/pjsip-simple/publishc.c b/pjsip/src/pjsip-simple/publishc.c
new file mode 100644
index 0000000..efee6fb
--- /dev/null
+++ b/pjsip/src/pjsip-simple/publishc.c
@@ -0,0 +1,790 @@
+/* $Id: publishc.c 4173 2012-06-20 10:39:05Z ming $ */
+/*
+ * 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/publish.h>
+#include <pjsip/sip_auth.h>
+#include <pjsip/sip_endpoint.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 <pj/assert.h>
+#include <pj/guid.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+#include <pj/timer.h>
+
+
+#define REFRESH_TIMER 1
+#define DELAY_BEFORE_REFRESH PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH
+#define THIS_FILE "publishc.c"
+
+
+/* Let's define this enum, so that it'll trigger compilation error
+ * when somebody define the same enum in sip_msg.h
+ */
+enum
+{
+ PJSIP_PUBLISH_METHOD = PJSIP_OTHER_METHOD,
+};
+
+const pjsip_method pjsip_publish_method =
+{
+ (pjsip_method_e)PJSIP_PUBLISH_METHOD,
+ { "PUBLISH", 7 }
+};
+
+
+/**
+ * Pending request list.
+ */
+typedef struct pending_publish
+{
+ PJ_DECL_LIST_MEMBER(pjsip_tx_data);
+} pending_publish;
+
+
+/**
+ * SIP client publication structure.
+ */
+struct pjsip_publishc
+{
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pj_bool_t _delete_flag;
+ int pending_tsx;
+ pj_bool_t in_callback;
+ pj_mutex_t *mutex;
+
+ pjsip_publishc_opt opt;
+ void *token;
+ pjsip_publishc_cb *cb;
+
+ 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;
+ pj_str_t etag;
+ pjsip_expires_hdr *expires_hdr;
+ pj_uint32_t expires;
+ pjsip_route_hdr route_set;
+ pjsip_hdr usr_hdr;
+ pjsip_host_port via_addr;
+ const void *via_tp;
+
+ /* Authorization sessions. */
+ pjsip_auth_clt_sess auth_sess;
+
+ /* Auto refresh publication. */
+ pj_bool_t auto_refresh;
+ pj_time_val last_refresh;
+ pj_time_val next_refresh;
+ pj_timer_entry timer;
+
+ /* Pending PUBLISH request */
+ pending_publish pending_reqs;
+};
+
+
+PJ_DEF(void) pjsip_publishc_opt_default(pjsip_publishc_opt *opt)
+{
+ pj_bzero(opt, sizeof(*opt));
+ opt->queue_request = PJSIP_PUBLISHC_QUEUE_REQUEST;
+}
+
+
+/*
+ * Initialize client publication module.
+ */
+PJ_DEF(pj_status_t) pjsip_publishc_init_module(pjsip_endpoint *endpt)
+{
+ /* Note:
+ Commented out the capability registration below, since it's
+ wrong to include PUBLISH in Allow header of INVITE requests/
+ responses.
+
+ 13.2.1 Creating the Initial INVITE
+ An Allow header field (Section 20.5) SHOULD be present in the
+ INVITE. It indicates what methods can be invoked within a dialog
+
+ 20.5 Allow
+ The Allow header field lists the set of methods supported by the
+ UA generating the message.
+
+ While the semantic of Allow header in non-dialog requests is unclear,
+ it's probably best not to include PUBLISH in Allow header for now
+ until we can find out how to customize the inclusion of methods in
+ Allow header for in-dialog vs out-dialog requests.
+
+ return pjsip_endpt_add_capability( endpt, NULL, PJSIP_H_ALLOW, NULL,
+ 1, &pjsip_publish_method.name);
+ */
+ PJ_UNUSED_ARG(endpt);
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_publishc_create( pjsip_endpoint *endpt,
+ const pjsip_publishc_opt *opt,
+ void *token,
+ pjsip_publishc_cb *cb,
+ pjsip_publishc **p_pubc)
+{
+ pj_pool_t *pool;
+ pjsip_publishc *pubc;
+ pjsip_publishc_opt default_opt;
+ pj_status_t status;
+
+ /* Verify arguments. */
+ PJ_ASSERT_RETURN(endpt && cb && p_pubc, PJ_EINVAL);
+
+ pool = pjsip_endpt_create_pool(endpt, "pubc%p", 1024, 1024);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ pubc = PJ_POOL_ZALLOC_T(pool, pjsip_publishc);
+
+ pubc->pool = pool;
+ pubc->endpt = endpt;
+ pubc->token = token;
+ pubc->cb = cb;
+ pubc->expires = PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED;
+
+ if (!opt) {
+ pjsip_publishc_opt_default(&default_opt);
+ opt = &default_opt;
+ }
+ pj_memcpy(&pubc->opt, opt, sizeof(*opt));
+ pj_list_init(&pubc->pending_reqs);
+
+ status = pj_mutex_create_recursive(pubc->pool, "pubc%p", &pubc->mutex);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
+
+ status = pjsip_auth_clt_init(&pubc->auth_sess, endpt, pubc->pool, 0);
+ if (status != PJ_SUCCESS) {
+ pj_mutex_destroy(pubc->mutex);
+ pj_pool_release(pool);
+ return status;
+ }
+
+ pj_list_init(&pubc->route_set);
+ pj_list_init(&pubc->usr_hdr);
+
+ /* Done */
+ *p_pubc = pubc;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_publishc_destroy(pjsip_publishc *pubc)
+{
+ PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
+
+ if (pubc->pending_tsx || pubc->in_callback) {
+ pubc->_delete_flag = 1;
+ pubc->cb = NULL;
+ } else {
+ /* Cancel existing timer, if any */
+ if (pubc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
+ pubc->timer.id = 0;
+ }
+
+ if (pubc->mutex)
+ pj_mutex_destroy(pubc->mutex);
+ pjsip_endpt_release_pool(pubc->endpt, pubc->pool);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_pool_t*) pjsip_publishc_get_pool(pjsip_publishc *pubc)
+{
+ return pubc->pool;
+}
+
+static void set_expires( pjsip_publishc *pubc, pj_uint32_t expires)
+{
+ if (expires != pubc->expires &&
+ expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED)
+ {
+ pubc->expires_hdr = pjsip_expires_hdr_create(pubc->pool, expires);
+ } else {
+ pubc->expires_hdr = NULL;
+ }
+}
+
+
+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_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(pubc->pool, &pubc->str_target_uri, target_uri);
+
+ /* Set server URL. */
+ 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(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 (!pubc->from_hdr->uri) {
+ return PJSIP_EINVALIDURI;
+ }
+
+ /* Set "To" header. */
+ 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 (!pubc->to_hdr->uri) {
+ return PJSIP_EINVALIDURI;
+ }
+
+
+ /* Set "Expires" header, if required. */
+ set_expires( pubc, expires);
+
+ /* Set "Call-ID" header. */
+ pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool);
+ pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id);
+
+ /* Set "CSeq" header. */
+ 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_publishc_set_credentials( pjsip_publishc *pubc,
+ int count,
+ const pjsip_cred_info 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_publishc_set_route_set( pjsip_publishc *pubc,
+ const pjsip_route_hdr *route_set)
+{
+ const pjsip_route_hdr *chdr;
+
+ PJ_ASSERT_RETURN(pubc && route_set, PJ_EINVAL);
+
+ pj_list_init(&pubc->route_set);
+
+ chdr = route_set->next;
+ while (chdr != route_set) {
+ pj_list_push_back(&pubc->route_set, pjsip_hdr_clone(pubc->pool, chdr));
+ chdr = chdr->next;
+ }
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjsip_publishc_set_headers( pjsip_publishc *pubc,
+ const pjsip_hdr *hdr_list)
+{
+ const pjsip_hdr *h;
+
+ PJ_ASSERT_RETURN(pubc && hdr_list, PJ_EINVAL);
+
+ pj_list_init(&pubc->usr_hdr);
+ h = hdr_list->next;
+ while (h != hdr_list) {
+ pj_list_push_back(&pubc->usr_hdr, pjsip_hdr_clone(pubc->pool, h));
+ h = h->next;
+ }
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc,
+ pjsip_host_port *via_addr,
+ pjsip_transport *via_tp)
+{
+ PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
+
+ if (!via_addr)
+ pj_bzero(&pubc->via_addr, sizeof(pubc->via_addr));
+ else
+ pubc->via_addr = *via_addr;
+ pubc->via_tp = via_tp;
+
+ return PJ_SUCCESS;
+}
+
+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(pubc && p_tdata, PJ_EINVAL);
+
+ /* Create the request. */
+ status = pjsip_endpt_create_request_from_hdr( pubc->endpt,
+ &pjsip_publish_method,
+ pubc->target_uri,
+ pubc->from_hdr,
+ pubc->to_hdr,
+ NULL,
+ pubc->cid_hdr,
+ pubc->cseq_hdr->cseq,
+ NULL,
+ &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Add cached authorization headers. */
+ pjsip_auth_clt_init_req( &pubc->auth_sess, tdata );
+
+ /* Add Route headers from route set, ideally after Via header */
+ if (!pj_list_empty(&pubc->route_set)) {
+ pjsip_hdr *route_pos;
+ const pjsip_route_hdr *route;
+
+ route_pos = (pjsip_hdr*)
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+ if (!route_pos)
+ route_pos = &tdata->msg->hdr;
+
+ route = pubc->route_set.next;
+ while (route != &pubc->route_set) {
+ pjsip_hdr *new_hdr = (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tdata->pool, route);
+ pj_list_insert_after(route_pos, new_hdr);
+ route_pos = new_hdr;
+ route = route->next;
+ }
+ }
+
+ /* 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);
+ }
+
+ /* Add user headers */
+ if (!pj_list_empty(&pubc->usr_hdr)) {
+ const pjsip_hdr *hdr;
+
+ hdr = pubc->usr_hdr.next;
+ while (hdr != &pubc->usr_hdr) {
+ pjsip_hdr *new_hdr = (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tdata->pool, hdr);
+ pjsip_msg_add_hdr(tdata->msg, new_hdr);
+ hdr = hdr->next;
+ }
+ }
+
+
+ /* Done. */
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc,
+ pj_bool_t auto_refresh,
+ pjsip_tx_data **p_tdata)
+{
+ pj_status_t status;
+ pjsip_tx_data *tdata;
+
+ PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
+
+ status = create_request(pubc, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Add Expires header */
+ if (pubc->expires_hdr) {
+ pjsip_hdr *dup;
+
+ dup = (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr);
+ if (dup)
+ pjsip_msg_add_hdr(tdata->msg, dup);
+ }
+
+ /* 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;
+ return PJ_SUCCESS;
+}
+
+
+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(pubc && p_tdata, PJ_EINVAL);
+
+ if (pubc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
+ pubc->timer.id = 0;
+ }
+
+ status = create_request(pubc, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ msg = tdata->msg;
+
+ /* 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_publishc_update_expires( pjsip_publishc *pubc,
+ pj_uint32_t expires )
+{
+ PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
+ set_expires( pubc, expires );
+ return PJ_SUCCESS;
+}
+
+
+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_publishc_cbparam cbparam;
+
+
+ cbparam.pubc = pubc;
+ cbparam.token = pubc->token;
+ cbparam.status = status;
+ cbparam.code = st_code;
+ cbparam.reason = *reason;
+ cbparam.rdata = rdata;
+ cbparam.expiration = expiration;
+
+ (*pubc->cb)(&cbparam);
+}
+
+static void pubc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pjsip_publishc *pubc = (pjsip_publishc*) entry->user_data;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(timer_heap);
+
+ entry->id = 0;
+ status = pjsip_publishc_publish(pubc, 1, &tdata);
+ if (status != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
+ call_callback(pubc, status, 400, &reason, NULL, -1);
+ return;
+ }
+
+ status = pjsip_publishc_send(pubc, tdata);
+ /* No need to call callback as it should have been called */
+}
+
+static void tsx_callback(void *token, pjsip_event *event)
+{
+ pj_status_t status;
+ pjsip_publishc *pubc = (pjsip_publishc*) token;
+ pjsip_transaction *tsx = event->body.tsx_state.tsx;
+
+ /* Decrement pending transaction counter. */
+ pj_assert(pubc->pending_tsx > 0);
+ --pubc->pending_tsx;
+
+ /* Mark that we're in callback to prevent deletion (#1164) */
+ ++pubc->in_callback;
+
+ /* If publication data has been deleted by user then remove publication
+ * data from transaction's callback, and don't call callback.
+ */
+ if (pubc->_delete_flag) {
+
+ /* Nothing to do */
+ ;
+
+ } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
+ tsx->status_code == PJSIP_SC_UNAUTHORIZED)
+ {
+ pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
+ pjsip_tx_data *tdata;
+
+ status = pjsip_auth_clt_reinit_req( &pubc->auth_sess,
+ rdata,
+ tsx->last_tx,
+ &tdata);
+ if (status != PJ_SUCCESS) {
+ call_callback(pubc, status, tsx->status_code,
+ &rdata->msg_info.msg->line.status.reason,
+ rdata, -1);
+ } else {
+ status = pjsip_publishc_send(pubc, tdata);
+ }
+
+ } else {
+ pjsip_rx_data *rdata;
+ pj_int32_t expiration = 0xFFFF;
+
+ if (tsx->status_code/100 == 2) {
+ 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;
+
+ /* 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_expires_hdr*)
+ pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
+
+ if (pubc->auto_refresh && expires)
+ expiration = expires->ivalue;
+
+ if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) {
+ pj_time_val delay = { 0, 0};
+
+ /* Cancel existing timer, if any */
+ if (pubc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
+ pubc->timer.id = 0;
+ }
+
+ delay.sec = expiration - DELAY_BEFORE_REFRESH;
+ if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED &&
+ delay.sec > (pj_int32_t)pubc->expires)
+ {
+ delay.sec = pubc->expires;
+ }
+ if (delay.sec < DELAY_BEFORE_REFRESH)
+ delay.sec = DELAY_BEFORE_REFRESH;
+ 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 {
+ rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
+ event->body.tsx_state.src.rdata : NULL;
+ }
+
+
+ /* Call callback. */
+ if (expiration == 0xFFFF) expiration = -1;
+
+ /* Temporarily increment pending_tsx to prevent callback from
+ * destroying pubc.
+ */
+ ++pubc->pending_tsx;
+
+ 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);
+
+ --pubc->pending_tsx;
+
+ /* If we have pending request(s), send them now */
+ pj_mutex_lock(pubc->mutex);
+ while (!pj_list_empty(&pubc->pending_reqs)) {
+ pjsip_tx_data *tdata = pubc->pending_reqs.next;
+ pj_list_erase(tdata);
+
+ /* Add SIP-If-Match if we have etag and the request doesn't have
+ * one (http://trac.pjsip.org/repos/ticket/996)
+ */
+ if (pubc->etag.slen) {
+ const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
+ pjsip_generic_string_hdr *sim_hdr;
+
+ sim_hdr = (pjsip_generic_string_hdr*)
+ pjsip_msg_find_hdr_by_name(tdata->msg, &STR_HNAME, NULL);
+ if (!sim_hdr) {
+ /* Create the header */
+ sim_hdr = pjsip_generic_string_hdr_create(tdata->pool,
+ &STR_HNAME,
+ &pubc->etag);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sim_hdr);
+
+ } else {
+ /* Update */
+ if (pj_strcmp(&pubc->etag, &sim_hdr->hvalue))
+ pj_strdup(tdata->pool, &sim_hdr->hvalue, &pubc->etag);
+ }
+ }
+
+ status = pjsip_publishc_send(pubc, tdata);
+ if (status == PJ_EPENDING) {
+ pj_assert(!"Not expected");
+ pj_list_erase(tdata);
+ pjsip_tx_data_dec_ref(tdata);
+ } else if (status == PJ_SUCCESS) {
+ break;
+ }
+ }
+ pj_mutex_unlock(pubc->mutex);
+ }
+
+ /* No longer in callback. */
+ --pubc->in_callback;
+
+ /* 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_publishc_send(pjsip_publishc *pubc,
+ pjsip_tx_data *tdata)
+{
+ pj_status_t status;
+ pjsip_cseq_hdr *cseq_hdr;
+ pj_uint32_t cseq;
+
+ PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL);
+
+ /* Make sure we don't have pending transaction. */
+ pj_mutex_lock(pubc->mutex);
+ if (pubc->pending_tsx) {
+ if (pubc->opt.queue_request) {
+ pj_list_push_back(&pubc->pending_reqs, tdata);
+ pj_mutex_unlock(pubc->mutex);
+ PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another "
+ "transaction pending"));
+ return PJ_EPENDING;
+ } else {
+ pjsip_tx_data_dec_ref(tdata);
+ pj_mutex_unlock(pubc->mutex);
+ PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another "
+ "transaction pending"));
+ return PJ_EBUSY;
+ }
+ }
+ pj_mutex_unlock(pubc->mutex);
+
+ /* If via_addr is set, use this address for the Via header. */
+ if (pubc->via_addr.host.slen > 0) {
+ tdata->via_addr = pubc->via_addr;
+ tdata->via_tp = pubc->via_tp;
+ }
+
+ /* Invalidate message buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+
+ /* Increment CSeq */
+ cseq = ++pubc->cseq_hdr->cseq;
+ cseq_hdr = (pjsip_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!
+ */
+ ++pubc->pending_tsx;
+ status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc,
+ &tsx_callback);
+ if (status!=PJ_SUCCESS) {
+ // no need to decrement, callback has been called and it should
+ // already decremented pending_tsx. Decrementing this here may
+ // cause accessing freed memory location.
+ //--pubc->pending_tsx;
+ PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
+ }
+
+ return status;
+}
+