1
/* $Id: publishc.c 4206 2012-07-16 02:45:09Z 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
if (pj_strcmp(&pubc->via_addr.host, &via_addr->host))
360
pj_strdup(pubc->pool, &pubc->via_addr.host, &via_addr->host);
361
pubc->via_addr.port = via_addr->port;
363
pubc->via_tp = via_tp;
368
static pj_status_t create_request(pjsip_publishc *pubc,
369
pjsip_tx_data **p_tdata)
371
const pj_str_t STR_EVENT = { "Event", 5 };
373
pjsip_generic_string_hdr *hdr;
374
pjsip_tx_data *tdata;
376
PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
378
/* Create the request. */
379
status = pjsip_endpt_create_request_from_hdr( pubc->endpt,
380
&pjsip_publish_method,
386
pubc->cseq_hdr->cseq,
389
if (status != PJ_SUCCESS)
392
/* Add cached authorization headers. */
393
pjsip_auth_clt_init_req( &pubc->auth_sess, tdata );
395
/* Add Route headers from route set, ideally after Via header */
396
if (!pj_list_empty(&pubc->route_set)) {
397
pjsip_hdr *route_pos;
398
const pjsip_route_hdr *route;
400
route_pos = (pjsip_hdr*)
401
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
403
route_pos = &tdata->msg->hdr;
405
route = pubc->route_set.next;
406
while (route != &pubc->route_set) {
407
pjsip_hdr *new_hdr = (pjsip_hdr*)
408
pjsip_hdr_shallow_clone(tdata->pool, route);
409
pj_list_insert_after(route_pos, new_hdr);
415
/* Add Event header */
416
hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT,
419
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
422
/* Add SIP-If-Match if we have etag */
423
if (pubc->etag.slen) {
424
const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
426
hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME,
429
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
432
/* Add user headers */
433
if (!pj_list_empty(&pubc->usr_hdr)) {
434
const pjsip_hdr *hdr;
436
hdr = pubc->usr_hdr.next;
437
while (hdr != &pubc->usr_hdr) {
438
pjsip_hdr *new_hdr = (pjsip_hdr*)
439
pjsip_hdr_shallow_clone(tdata->pool, hdr);
440
pjsip_msg_add_hdr(tdata->msg, new_hdr);
452
PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc,
453
pj_bool_t auto_refresh,
454
pjsip_tx_data **p_tdata)
457
pjsip_tx_data *tdata;
459
PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
461
status = create_request(pubc, &tdata);
462
if (status != PJ_SUCCESS)
465
/* Add Expires header */
466
if (pubc->expires_hdr) {
470
pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr);
472
pjsip_msg_add_hdr(tdata->msg, dup);
475
/* Cancel existing timer */
476
if (pubc->timer.id != 0) {
477
pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
481
pubc->auto_refresh = auto_refresh;
489
PJ_DEF(pj_status_t) pjsip_publishc_unpublish(pjsip_publishc *pubc,
490
pjsip_tx_data **p_tdata)
492
pjsip_tx_data *tdata;
494
pjsip_expires_hdr *expires;
497
PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
499
if (pubc->timer.id != 0) {
500
pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
504
status = create_request(pubc, &tdata);
505
if (status != PJ_SUCCESS)
510
/* Add Expires:0 header */
511
expires = pjsip_expires_hdr_create(tdata->pool, 0);
512
pjsip_msg_add_hdr( msg, (pjsip_hdr*)expires);
519
PJ_DEF(pj_status_t) pjsip_publishc_update_expires( pjsip_publishc *pubc,
520
pj_uint32_t expires )
522
PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
523
set_expires( pubc, expires );
528
static void call_callback(pjsip_publishc *pubc, pj_status_t status,
529
int st_code, const pj_str_t *reason,
530
pjsip_rx_data *rdata, pj_int32_t expiration)
532
struct pjsip_publishc_cbparam cbparam;
536
cbparam.token = pubc->token;
537
cbparam.status = status;
538
cbparam.code = st_code;
539
cbparam.reason = *reason;
540
cbparam.rdata = rdata;
541
cbparam.expiration = expiration;
543
(*pubc->cb)(&cbparam);
546
static void pubc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
547
struct pj_timer_entry *entry)
549
pjsip_publishc *pubc = (pjsip_publishc*) entry->user_data;
550
pjsip_tx_data *tdata;
553
PJ_UNUSED_ARG(timer_heap);
556
status = pjsip_publishc_publish(pubc, 1, &tdata);
557
if (status != PJ_SUCCESS) {
558
char errmsg[PJ_ERR_MSG_SIZE];
559
pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
560
call_callback(pubc, status, 400, &reason, NULL, -1);
564
status = pjsip_publishc_send(pubc, tdata);
565
/* No need to call callback as it should have been called */
568
static void tsx_callback(void *token, pjsip_event *event)
571
pjsip_publishc *pubc = (pjsip_publishc*) token;
572
pjsip_transaction *tsx = event->body.tsx_state.tsx;
574
/* Decrement pending transaction counter. */
575
pj_assert(pubc->pending_tsx > 0);
578
/* Mark that we're in callback to prevent deletion (#1164) */
581
/* If publication data has been deleted by user then remove publication
582
* data from transaction's callback, and don't call callback.
584
if (pubc->_delete_flag) {
589
} else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
590
tsx->status_code == PJSIP_SC_UNAUTHORIZED)
592
pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
593
pjsip_tx_data *tdata;
595
status = pjsip_auth_clt_reinit_req( &pubc->auth_sess,
599
if (status != PJ_SUCCESS) {
600
call_callback(pubc, status, tsx->status_code,
601
&rdata->msg_info.msg->line.status.reason,
604
status = pjsip_publishc_send(pubc, tdata);
608
pjsip_rx_data *rdata;
609
pj_int32_t expiration = 0xFFFF;
611
if (tsx->status_code/100 == 2) {
613
pjsip_expires_hdr *expires;
614
pjsip_generic_string_hdr *etag_hdr;
615
const pj_str_t STR_ETAG = { "SIP-ETag", 8 };
617
rdata = event->body.tsx_state.src.rdata;
618
msg = rdata->msg_info.msg;
620
/* Save ETag value */
621
etag_hdr = (pjsip_generic_string_hdr*)
622
pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL);
624
pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue);
629
/* Update expires value */
630
expires = (pjsip_expires_hdr*)
631
pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
633
if (pubc->auto_refresh && expires)
634
expiration = expires->ivalue;
636
if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) {
637
pj_time_val delay = { 0, 0};
639
/* Cancel existing timer, if any */
640
if (pubc->timer.id != 0) {
641
pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
645
delay.sec = expiration - DELAY_BEFORE_REFRESH;
646
if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED &&
647
delay.sec > (pj_int32_t)pubc->expires)
649
delay.sec = pubc->expires;
651
if (delay.sec < DELAY_BEFORE_REFRESH)
652
delay.sec = DELAY_BEFORE_REFRESH;
653
pubc->timer.cb = &pubc_refresh_timer_cb;
654
pubc->timer.id = REFRESH_TIMER;
655
pubc->timer.user_data = pubc;
656
pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay);
657
pj_gettimeofday(&pubc->last_refresh);
658
pubc->next_refresh = pubc->last_refresh;
659
pubc->next_refresh.sec += delay.sec;
663
rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
664
event->body.tsx_state.src.rdata : NULL;
669
if (expiration == 0xFFFF) expiration = -1;
671
/* Temporarily increment pending_tsx to prevent callback from
676
call_callback(pubc, PJ_SUCCESS, tsx->status_code,
677
(rdata ? &rdata->msg_info.msg->line.status.reason
678
: pjsip_get_status_text(tsx->status_code)),
683
/* If we have pending request(s), send them now */
684
pj_mutex_lock(pubc->mutex);
685
while (!pj_list_empty(&pubc->pending_reqs)) {
686
pjsip_tx_data *tdata = pubc->pending_reqs.next;
687
pj_list_erase(tdata);
689
/* Add SIP-If-Match if we have etag and the request doesn't have
690
* one (http://trac.pjsip.org/repos/ticket/996)
692
if (pubc->etag.slen) {
693
const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
694
pjsip_generic_string_hdr *sim_hdr;
696
sim_hdr = (pjsip_generic_string_hdr*)
697
pjsip_msg_find_hdr_by_name(tdata->msg, &STR_HNAME, NULL);
699
/* Create the header */
700
sim_hdr = pjsip_generic_string_hdr_create(tdata->pool,
703
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sim_hdr);
707
if (pj_strcmp(&pubc->etag, &sim_hdr->hvalue))
708
pj_strdup(tdata->pool, &sim_hdr->hvalue, &pubc->etag);
712
status = pjsip_publishc_send(pubc, tdata);
713
if (status == PJ_EPENDING) {
714
pj_assert(!"Not expected");
715
pj_list_erase(tdata);
716
pjsip_tx_data_dec_ref(tdata);
717
} else if (status == PJ_SUCCESS) {
721
pj_mutex_unlock(pubc->mutex);
724
/* No longer in callback. */
727
/* Delete the record if user destroy pubc during the callback. */
728
if (pubc->_delete_flag && pubc->pending_tsx==0) {
729
pjsip_publishc_destroy(pubc);
734
PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc,
735
pjsip_tx_data *tdata)
738
pjsip_cseq_hdr *cseq_hdr;
741
PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL);
743
/* Make sure we don't have pending transaction. */
744
pj_mutex_lock(pubc->mutex);
745
if (pubc->pending_tsx) {
746
if (pubc->opt.queue_request) {
747
pj_list_push_back(&pubc->pending_reqs, tdata);
748
pj_mutex_unlock(pubc->mutex);
749
PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another "
750
"transaction pending"));
753
pjsip_tx_data_dec_ref(tdata);
754
pj_mutex_unlock(pubc->mutex);
755
PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another "
756
"transaction pending"));
760
pj_mutex_unlock(pubc->mutex);
762
/* If via_addr is set, use this address for the Via header. */
763
if (pubc->via_addr.host.slen > 0) {
764
tdata->via_addr = pubc->via_addr;
765
tdata->via_tp = pubc->via_tp;
768
/* Invalidate message buffer. */
769
pjsip_tx_data_invalidate_msg(tdata);
772
cseq = ++pubc->cseq_hdr->cseq;
773
cseq_hdr = (pjsip_cseq_hdr*)
774
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
775
cseq_hdr->cseq = cseq;
777
/* Increment pending transaction first, since transaction callback
778
* may be called even before send_request() returns!
781
status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc,
783
if (status!=PJ_SUCCESS) {
784
// no need to decrement, callback has been called and it should
785
// already decremented pending_tsx. Decrementing this here may
786
// cause accessing freed memory location.
787
//--pubc->pending_tsx;
788
PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));