1
/* $Id: publishc.c 4173 2012-06-20 10:39:05Z ming $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjsip-simple/publish.h>
21
#include <pjsip/sip_auth.h>
22
#include <pjsip/sip_endpoint.h>
23
#include <pjsip/sip_errno.h>
24
#include <pjsip/sip_event.h>
25
#include <pjsip/sip_msg.h>
26
#include <pjsip/sip_transaction.h>
27
#include <pjsip/sip_uri.h>
28
#include <pjsip/sip_util.h>
29
#include <pj/assert.h>
35
#include <pj/string.h>
39
#define REFRESH_TIMER 1
40
#define DELAY_BEFORE_REFRESH PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH
41
#define THIS_FILE "publishc.c"
44
/* Let's define this enum, so that it'll trigger compilation error
45
* when somebody define the same enum in sip_msg.h
49
PJSIP_PUBLISH_METHOD = PJSIP_OTHER_METHOD,
52
const pjsip_method pjsip_publish_method =
54
(pjsip_method_e)PJSIP_PUBLISH_METHOD,
60
* Pending request list.
62
typedef struct pending_publish
64
PJ_DECL_LIST_MEMBER(pjsip_tx_data);
69
* SIP client publication structure.
74
pjsip_endpoint *endpt;
75
pj_bool_t _delete_flag;
77
pj_bool_t in_callback;
80
pjsip_publishc_opt opt;
82
pjsip_publishc_cb *cb;
85
pj_str_t str_target_uri;
86
pjsip_uri *target_uri;
87
pjsip_cid_hdr *cid_hdr;
88
pjsip_cseq_hdr *cseq_hdr;
90
pjsip_from_hdr *from_hdr;
93
pjsip_expires_hdr *expires_hdr;
95
pjsip_route_hdr route_set;
97
pjsip_host_port via_addr;
100
/* Authorization sessions. */
101
pjsip_auth_clt_sess auth_sess;
103
/* Auto refresh publication. */
104
pj_bool_t auto_refresh;
105
pj_time_val last_refresh;
106
pj_time_val next_refresh;
107
pj_timer_entry timer;
109
/* Pending PUBLISH request */
110
pending_publish pending_reqs;
114
PJ_DEF(void) pjsip_publishc_opt_default(pjsip_publishc_opt *opt)
116
pj_bzero(opt, sizeof(*opt));
117
opt->queue_request = PJSIP_PUBLISHC_QUEUE_REQUEST;
122
* Initialize client publication module.
124
PJ_DEF(pj_status_t) pjsip_publishc_init_module(pjsip_endpoint *endpt)
127
Commented out the capability registration below, since it's
128
wrong to include PUBLISH in Allow header of INVITE requests/
131
13.2.1 Creating the Initial INVITE
132
An Allow header field (Section 20.5) SHOULD be present in the
133
INVITE. It indicates what methods can be invoked within a dialog
136
The Allow header field lists the set of methods supported by the
137
UA generating the message.
139
While the semantic of Allow header in non-dialog requests is unclear,
140
it's probably best not to include PUBLISH in Allow header for now
141
until we can find out how to customize the inclusion of methods in
142
Allow header for in-dialog vs out-dialog requests.
144
return pjsip_endpt_add_capability( endpt, NULL, PJSIP_H_ALLOW, NULL,
145
1, &pjsip_publish_method.name);
147
PJ_UNUSED_ARG(endpt);
152
PJ_DEF(pj_status_t) pjsip_publishc_create( pjsip_endpoint *endpt,
153
const pjsip_publishc_opt *opt,
155
pjsip_publishc_cb *cb,
156
pjsip_publishc **p_pubc)
159
pjsip_publishc *pubc;
160
pjsip_publishc_opt default_opt;
163
/* Verify arguments. */
164
PJ_ASSERT_RETURN(endpt && cb && p_pubc, PJ_EINVAL);
166
pool = pjsip_endpt_create_pool(endpt, "pubc%p", 1024, 1024);
167
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
169
pubc = PJ_POOL_ZALLOC_T(pool, pjsip_publishc);
175
pubc->expires = PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED;
178
pjsip_publishc_opt_default(&default_opt);
181
pj_memcpy(&pubc->opt, opt, sizeof(*opt));
182
pj_list_init(&pubc->pending_reqs);
184
status = pj_mutex_create_recursive(pubc->pool, "pubc%p", &pubc->mutex);
185
if (status != PJ_SUCCESS) {
186
pj_pool_release(pool);
190
status = pjsip_auth_clt_init(&pubc->auth_sess, endpt, pubc->pool, 0);
191
if (status != PJ_SUCCESS) {
192
pj_mutex_destroy(pubc->mutex);
193
pj_pool_release(pool);
197
pj_list_init(&pubc->route_set);
198
pj_list_init(&pubc->usr_hdr);
206
PJ_DEF(pj_status_t) pjsip_publishc_destroy(pjsip_publishc *pubc)
208
PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
210
if (pubc->pending_tsx || pubc->in_callback) {
211
pubc->_delete_flag = 1;
214
/* Cancel existing timer, if any */
215
if (pubc->timer.id != 0) {
216
pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
221
pj_mutex_destroy(pubc->mutex);
222
pjsip_endpt_release_pool(pubc->endpt, pubc->pool);
229
PJ_DEF(pj_pool_t*) pjsip_publishc_get_pool(pjsip_publishc *pubc)
234
static void set_expires( pjsip_publishc *pubc, pj_uint32_t expires)
236
if (expires != pubc->expires &&
237
expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED)
239
pubc->expires_hdr = pjsip_expires_hdr_create(pubc->pool, expires);
241
pubc->expires_hdr = NULL;
246
PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc,
247
const pj_str_t *event,
248
const pj_str_t *target_uri,
249
const pj_str_t *from_uri,
250
const pj_str_t *to_uri,
255
PJ_ASSERT_RETURN(pubc && event && target_uri && from_uri && to_uri &&
258
/* Copy event type */
259
pj_strdup_with_null(pubc->pool, &pubc->event, event);
261
/* Copy server URL. */
262
pj_strdup_with_null(pubc->pool, &pubc->str_target_uri, target_uri);
264
/* Set server URL. */
265
tmp = pubc->str_target_uri;
266
pubc->target_uri = pjsip_parse_uri( pubc->pool, tmp.ptr, tmp.slen, 0);
267
if (pubc->target_uri == NULL) {
268
return PJSIP_EINVALIDURI;
271
/* Set "From" header. */
272
pj_strdup_with_null(pubc->pool, &pubc->from_uri, from_uri);
273
tmp = pubc->from_uri;
274
pubc->from_hdr = pjsip_from_hdr_create(pubc->pool);
275
pubc->from_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen,
276
PJSIP_PARSE_URI_AS_NAMEADDR);
277
if (!pubc->from_hdr->uri) {
278
return PJSIP_EINVALIDURI;
281
/* Set "To" header. */
282
pj_strdup_with_null(pubc->pool, &tmp, to_uri);
283
pubc->to_hdr = pjsip_to_hdr_create(pubc->pool);
284
pubc->to_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen,
285
PJSIP_PARSE_URI_AS_NAMEADDR);
286
if (!pubc->to_hdr->uri) {
287
return PJSIP_EINVALIDURI;
291
/* Set "Expires" header, if required. */
292
set_expires( pubc, expires);
294
/* Set "Call-ID" header. */
295
pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool);
296
pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id);
298
/* Set "CSeq" header. */
299
pubc->cseq_hdr = pjsip_cseq_hdr_create(pubc->pool);
300
pubc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
301
pjsip_method_set( &pubc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
307
PJ_DEF(pj_status_t) pjsip_publishc_set_credentials( pjsip_publishc *pubc,
309
const pjsip_cred_info cred[] )
311
PJ_ASSERT_RETURN(pubc && count && cred, PJ_EINVAL);
312
return pjsip_auth_clt_set_credentials(&pubc->auth_sess, count, cred);
315
PJ_DEF(pj_status_t) pjsip_publishc_set_route_set( pjsip_publishc *pubc,
316
const pjsip_route_hdr *route_set)
318
const pjsip_route_hdr *chdr;
320
PJ_ASSERT_RETURN(pubc && route_set, PJ_EINVAL);
322
pj_list_init(&pubc->route_set);
324
chdr = route_set->next;
325
while (chdr != route_set) {
326
pj_list_push_back(&pubc->route_set, pjsip_hdr_clone(pubc->pool, chdr));
333
PJ_DEF(pj_status_t) pjsip_publishc_set_headers( pjsip_publishc *pubc,
334
const pjsip_hdr *hdr_list)
338
PJ_ASSERT_RETURN(pubc && hdr_list, PJ_EINVAL);
340
pj_list_init(&pubc->usr_hdr);
342
while (h != hdr_list) {
343
pj_list_push_back(&pubc->usr_hdr, pjsip_hdr_clone(pubc->pool, h));
350
PJ_DEF(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc,
351
pjsip_host_port *via_addr,
352
pjsip_transport *via_tp)
354
PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
357
pj_bzero(&pubc->via_addr, sizeof(pubc->via_addr));
359
pubc->via_addr = *via_addr;
360
pubc->via_tp = via_tp;
365
static pj_status_t create_request(pjsip_publishc *pubc,
366
pjsip_tx_data **p_tdata)
368
const pj_str_t STR_EVENT = { "Event", 5 };
370
pjsip_generic_string_hdr *hdr;
371
pjsip_tx_data *tdata;
373
PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
375
/* Create the request. */
376
status = pjsip_endpt_create_request_from_hdr( pubc->endpt,
377
&pjsip_publish_method,
383
pubc->cseq_hdr->cseq,
386
if (status != PJ_SUCCESS)
389
/* Add cached authorization headers. */
390
pjsip_auth_clt_init_req( &pubc->auth_sess, tdata );
392
/* Add Route headers from route set, ideally after Via header */
393
if (!pj_list_empty(&pubc->route_set)) {
394
pjsip_hdr *route_pos;
395
const pjsip_route_hdr *route;
397
route_pos = (pjsip_hdr*)
398
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
400
route_pos = &tdata->msg->hdr;
402
route = pubc->route_set.next;
403
while (route != &pubc->route_set) {
404
pjsip_hdr *new_hdr = (pjsip_hdr*)
405
pjsip_hdr_shallow_clone(tdata->pool, route);
406
pj_list_insert_after(route_pos, new_hdr);
412
/* Add Event header */
413
hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT,
416
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
419
/* Add SIP-If-Match if we have etag */
420
if (pubc->etag.slen) {
421
const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
423
hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME,
426
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
429
/* Add user headers */
430
if (!pj_list_empty(&pubc->usr_hdr)) {
431
const pjsip_hdr *hdr;
433
hdr = pubc->usr_hdr.next;
434
while (hdr != &pubc->usr_hdr) {
435
pjsip_hdr *new_hdr = (pjsip_hdr*)
436
pjsip_hdr_shallow_clone(tdata->pool, hdr);
437
pjsip_msg_add_hdr(tdata->msg, new_hdr);
449
PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc,
450
pj_bool_t auto_refresh,
451
pjsip_tx_data **p_tdata)
454
pjsip_tx_data *tdata;
456
PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
458
status = create_request(pubc, &tdata);
459
if (status != PJ_SUCCESS)
462
/* Add Expires header */
463
if (pubc->expires_hdr) {
467
pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr);
469
pjsip_msg_add_hdr(tdata->msg, dup);
472
/* Cancel existing timer */
473
if (pubc->timer.id != 0) {
474
pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
478
pubc->auto_refresh = auto_refresh;
486
PJ_DEF(pj_status_t) pjsip_publishc_unpublish(pjsip_publishc *pubc,
487
pjsip_tx_data **p_tdata)
489
pjsip_tx_data *tdata;
491
pjsip_expires_hdr *expires;
494
PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
496
if (pubc->timer.id != 0) {
497
pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
501
status = create_request(pubc, &tdata);
502
if (status != PJ_SUCCESS)
507
/* Add Expires:0 header */
508
expires = pjsip_expires_hdr_create(tdata->pool, 0);
509
pjsip_msg_add_hdr( msg, (pjsip_hdr*)expires);
516
PJ_DEF(pj_status_t) pjsip_publishc_update_expires( pjsip_publishc *pubc,
517
pj_uint32_t expires )
519
PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
520
set_expires( pubc, expires );
525
static void call_callback(pjsip_publishc *pubc, pj_status_t status,
526
int st_code, const pj_str_t *reason,
527
pjsip_rx_data *rdata, pj_int32_t expiration)
529
struct pjsip_publishc_cbparam cbparam;
533
cbparam.token = pubc->token;
534
cbparam.status = status;
535
cbparam.code = st_code;
536
cbparam.reason = *reason;
537
cbparam.rdata = rdata;
538
cbparam.expiration = expiration;
540
(*pubc->cb)(&cbparam);
543
static void pubc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
544
struct pj_timer_entry *entry)
546
pjsip_publishc *pubc = (pjsip_publishc*) entry->user_data;
547
pjsip_tx_data *tdata;
550
PJ_UNUSED_ARG(timer_heap);
553
status = pjsip_publishc_publish(pubc, 1, &tdata);
554
if (status != PJ_SUCCESS) {
555
char errmsg[PJ_ERR_MSG_SIZE];
556
pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
557
call_callback(pubc, status, 400, &reason, NULL, -1);
561
status = pjsip_publishc_send(pubc, tdata);
562
/* No need to call callback as it should have been called */
565
static void tsx_callback(void *token, pjsip_event *event)
568
pjsip_publishc *pubc = (pjsip_publishc*) token;
569
pjsip_transaction *tsx = event->body.tsx_state.tsx;
571
/* Decrement pending transaction counter. */
572
pj_assert(pubc->pending_tsx > 0);
575
/* Mark that we're in callback to prevent deletion (#1164) */
578
/* If publication data has been deleted by user then remove publication
579
* data from transaction's callback, and don't call callback.
581
if (pubc->_delete_flag) {
586
} else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
587
tsx->status_code == PJSIP_SC_UNAUTHORIZED)
589
pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
590
pjsip_tx_data *tdata;
592
status = pjsip_auth_clt_reinit_req( &pubc->auth_sess,
596
if (status != PJ_SUCCESS) {
597
call_callback(pubc, status, tsx->status_code,
598
&rdata->msg_info.msg->line.status.reason,
601
status = pjsip_publishc_send(pubc, tdata);
605
pjsip_rx_data *rdata;
606
pj_int32_t expiration = 0xFFFF;
608
if (tsx->status_code/100 == 2) {
610
pjsip_expires_hdr *expires;
611
pjsip_generic_string_hdr *etag_hdr;
612
const pj_str_t STR_ETAG = { "SIP-ETag", 8 };
614
rdata = event->body.tsx_state.src.rdata;
615
msg = rdata->msg_info.msg;
617
/* Save ETag value */
618
etag_hdr = (pjsip_generic_string_hdr*)
619
pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL);
621
pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue);
626
/* Update expires value */
627
expires = (pjsip_expires_hdr*)
628
pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
630
if (pubc->auto_refresh && expires)
631
expiration = expires->ivalue;
633
if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) {
634
pj_time_val delay = { 0, 0};
636
/* Cancel existing timer, if any */
637
if (pubc->timer.id != 0) {
638
pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
642
delay.sec = expiration - DELAY_BEFORE_REFRESH;
643
if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED &&
644
delay.sec > (pj_int32_t)pubc->expires)
646
delay.sec = pubc->expires;
648
if (delay.sec < DELAY_BEFORE_REFRESH)
649
delay.sec = DELAY_BEFORE_REFRESH;
650
pubc->timer.cb = &pubc_refresh_timer_cb;
651
pubc->timer.id = REFRESH_TIMER;
652
pubc->timer.user_data = pubc;
653
pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay);
654
pj_gettimeofday(&pubc->last_refresh);
655
pubc->next_refresh = pubc->last_refresh;
656
pubc->next_refresh.sec += delay.sec;
660
rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
661
event->body.tsx_state.src.rdata : NULL;
666
if (expiration == 0xFFFF) expiration = -1;
668
/* Temporarily increment pending_tsx to prevent callback from
673
call_callback(pubc, PJ_SUCCESS, tsx->status_code,
674
(rdata ? &rdata->msg_info.msg->line.status.reason
675
: pjsip_get_status_text(tsx->status_code)),
680
/* If we have pending request(s), send them now */
681
pj_mutex_lock(pubc->mutex);
682
while (!pj_list_empty(&pubc->pending_reqs)) {
683
pjsip_tx_data *tdata = pubc->pending_reqs.next;
684
pj_list_erase(tdata);
686
/* Add SIP-If-Match if we have etag and the request doesn't have
687
* one (http://trac.pjsip.org/repos/ticket/996)
689
if (pubc->etag.slen) {
690
const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
691
pjsip_generic_string_hdr *sim_hdr;
693
sim_hdr = (pjsip_generic_string_hdr*)
694
pjsip_msg_find_hdr_by_name(tdata->msg, &STR_HNAME, NULL);
696
/* Create the header */
697
sim_hdr = pjsip_generic_string_hdr_create(tdata->pool,
700
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sim_hdr);
704
if (pj_strcmp(&pubc->etag, &sim_hdr->hvalue))
705
pj_strdup(tdata->pool, &sim_hdr->hvalue, &pubc->etag);
709
status = pjsip_publishc_send(pubc, tdata);
710
if (status == PJ_EPENDING) {
711
pj_assert(!"Not expected");
712
pj_list_erase(tdata);
713
pjsip_tx_data_dec_ref(tdata);
714
} else if (status == PJ_SUCCESS) {
718
pj_mutex_unlock(pubc->mutex);
721
/* No longer in callback. */
724
/* Delete the record if user destroy pubc during the callback. */
725
if (pubc->_delete_flag && pubc->pending_tsx==0) {
726
pjsip_publishc_destroy(pubc);
731
PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc,
732
pjsip_tx_data *tdata)
735
pjsip_cseq_hdr *cseq_hdr;
738
PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL);
740
/* Make sure we don't have pending transaction. */
741
pj_mutex_lock(pubc->mutex);
742
if (pubc->pending_tsx) {
743
if (pubc->opt.queue_request) {
744
pj_list_push_back(&pubc->pending_reqs, tdata);
745
pj_mutex_unlock(pubc->mutex);
746
PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another "
747
"transaction pending"));
750
pjsip_tx_data_dec_ref(tdata);
751
pj_mutex_unlock(pubc->mutex);
752
PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another "
753
"transaction pending"));
757
pj_mutex_unlock(pubc->mutex);
759
/* If via_addr is set, use this address for the Via header. */
760
if (pubc->via_addr.host.slen > 0) {
761
tdata->via_addr = pubc->via_addr;
762
tdata->via_tp = pubc->via_tp;
765
/* Invalidate message buffer. */
766
pjsip_tx_data_invalidate_msg(tdata);
769
cseq = ++pubc->cseq_hdr->cseq;
770
cseq_hdr = (pjsip_cseq_hdr*)
771
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
772
cseq_hdr->cseq = cseq;
774
/* Increment pending transaction first, since transaction callback
775
* may be called even before send_request() returns!
778
status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc,
780
if (status!=PJ_SUCCESS) {
781
// no need to decrement, callback has been called and it should
782
// already decremented pending_tsx. Decrementing this here may
783
// cause accessing freed memory location.
784
//--pubc->pending_tsx;
785
PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));