blob: 84bd19e61387741f6d84641d82a76378004ee8e7 [file] [log] [blame]
Benny Prijono21b9ad92006-08-15 13:11:22 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono21b9ad92006-08-15 13:11:22 +00005 *
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.
10 *
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.
15 *
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
19 */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000020#include <pjsip-simple/publish.h>
21#include <pjsip/sip_auth.h>
Benny Prijono21b9ad92006-08-15 13:11:22 +000022#include <pjsip/sip_endpoint.h>
Benny Prijono21b9ad92006-08-15 13:11:22 +000023#include <pjsip/sip_errno.h>
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000024#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>
Benny Prijono21b9ad92006-08-15 13:11:22 +000029#include <pj/assert.h>
30#include <pj/guid.h>
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000031#include <pj/log.h>
Benny Prijono21b9ad92006-08-15 13:11:22 +000032#include <pj/os.h>
33#include <pj/pool.h>
Benny Prijono21b9ad92006-08-15 13:11:22 +000034#include <pj/rand.h>
35#include <pj/string.h>
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000036#include <pj/timer.h>
Benny Prijono21b9ad92006-08-15 13:11:22 +000037
38
39#define REFRESH_TIMER 1
Benny Prijonob4d22b02009-06-05 16:10:36 +000040#define DELAY_BEFORE_REFRESH PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000041#define THIS_FILE "publishc.c"
42
43
Benny Prijonoc8141a82006-08-20 09:12:19 +000044/* Let's define this enum, so that it'll trigger compilation error
45 * when somebody define the same enum in sip_msg.h
46 */
47enum
48{
49 PJSIP_PUBLISH_METHOD = PJSIP_OTHER_METHOD,
50};
51
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000052const pjsip_method pjsip_publish_method =
53{
Benny Prijono9d4469d2007-05-02 05:14:29 +000054 (pjsip_method_e)PJSIP_PUBLISH_METHOD,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000055 { "PUBLISH", 7 }
56};
57
Benny Prijono21b9ad92006-08-15 13:11:22 +000058
59/**
Benny Prijonofe50c9e2009-10-12 07:44:14 +000060 * Pending request list.
61 */
62typedef struct pending_publish
63{
64 PJ_DECL_LIST_MEMBER(pjsip_tx_data);
65} pending_publish;
66
67
68/**
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000069 * SIP client publication structure.
Benny Prijono21b9ad92006-08-15 13:11:22 +000070 */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000071struct pjsip_publishc
Benny Prijono21b9ad92006-08-15 13:11:22 +000072{
73 pj_pool_t *pool;
74 pjsip_endpoint *endpt;
75 pj_bool_t _delete_flag;
76 int pending_tsx;
Benny Prijono78979492010-11-29 14:49:37 +000077 pj_bool_t in_callback;
Benny Prijonofe50c9e2009-10-12 07:44:14 +000078 pj_mutex_t *mutex;
Benny Prijono21b9ad92006-08-15 13:11:22 +000079
Benny Prijonofe50c9e2009-10-12 07:44:14 +000080 pjsip_publishc_opt opt;
Benny Prijono21b9ad92006-08-15 13:11:22 +000081 void *token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000082 pjsip_publishc_cb *cb;
Benny Prijono21b9ad92006-08-15 13:11:22 +000083
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000084 pj_str_t event;
85 pj_str_t str_target_uri;
86 pjsip_uri *target_uri;
Benny Prijono21b9ad92006-08-15 13:11:22 +000087 pjsip_cid_hdr *cid_hdr;
88 pjsip_cseq_hdr *cseq_hdr;
89 pj_str_t from_uri;
90 pjsip_from_hdr *from_hdr;
91 pjsip_to_hdr *to_hdr;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000092 pj_str_t etag;
Benny Prijono21b9ad92006-08-15 13:11:22 +000093 pjsip_expires_hdr *expires_hdr;
Benny Prijono21b9ad92006-08-15 13:11:22 +000094 pj_uint32_t expires;
95 pjsip_route_hdr route_set;
Benny Prijono53984d12009-04-28 22:19:49 +000096 pjsip_hdr usr_hdr;
Benny Prijono21b9ad92006-08-15 13:11:22 +000097
98 /* Authorization sessions. */
99 pjsip_auth_clt_sess auth_sess;
100
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000101 /* Auto refresh publication. */
102 pj_bool_t auto_refresh;
103 pj_time_val last_refresh;
104 pj_time_val next_refresh;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000105 pj_timer_entry timer;
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000106
107 /* Pending PUBLISH request */
108 pending_publish pending_reqs;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000109};
110
111
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000112PJ_DEF(void) pjsip_publishc_opt_default(pjsip_publishc_opt *opt)
113{
114 pj_bzero(opt, sizeof(*opt));
115 opt->queue_request = PJSIP_PUBLISHC_QUEUE_REQUEST;
116}
117
Benny Prijono21b9ad92006-08-15 13:11:22 +0000118
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000119/*
120 * Initialize client publication module.
121 */
122PJ_DEF(pj_status_t) pjsip_publishc_init_module(pjsip_endpoint *endpt)
123{
Benny Prijono7741c8a2008-07-07 19:57:16 +0000124 /* Note:
125 Commented out the capability registration below, since it's
126 wrong to include PUBLISH in Allow header of INVITE requests/
127 responses.
128
129 13.2.1 Creating the Initial INVITE
130 An Allow header field (Section 20.5) SHOULD be present in the
131 INVITE. It indicates what methods can be invoked within a dialog
132
133 20.5 Allow
134 The Allow header field lists the set of methods supported by the
135 UA generating the message.
136
137 While the semantic of Allow header in non-dialog requests is unclear,
138 it's probably best not to include PUBLISH in Allow header for now
139 until we can find out how to customize the inclusion of methods in
140 Allow header for in-dialog vs out-dialog requests.
141
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000142 return pjsip_endpt_add_capability( endpt, NULL, PJSIP_H_ALLOW, NULL,
143 1, &pjsip_publish_method.name);
Benny Prijono7741c8a2008-07-07 19:57:16 +0000144 */
Benny Prijono10d8dbd2008-07-13 13:12:36 +0000145 PJ_UNUSED_ARG(endpt);
Benny Prijono7741c8a2008-07-07 19:57:16 +0000146 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000147}
148
149
150PJ_DEF(pj_status_t) pjsip_publishc_create( pjsip_endpoint *endpt,
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000151 const pjsip_publishc_opt *opt,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000152 void *token,
153 pjsip_publishc_cb *cb,
154 pjsip_publishc **p_pubc)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000155{
156 pj_pool_t *pool;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000157 pjsip_publishc *pubc;
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000158 pjsip_publishc_opt default_opt;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000159 pj_status_t status;
160
161 /* Verify arguments. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000162 PJ_ASSERT_RETURN(endpt && cb && p_pubc, PJ_EINVAL);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000163
164 pool = pjsip_endpt_create_pool(endpt, "pubc%p", 1024, 1024);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000165 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
166
Benny Prijono9d4469d2007-05-02 05:14:29 +0000167 pubc = PJ_POOL_ZALLOC_T(pool, pjsip_publishc);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000168
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000169 pubc->pool = pool;
170 pubc->endpt = endpt;
171 pubc->token = token;
172 pubc->cb = cb;
173 pubc->expires = PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000174
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000175 if (!opt) {
176 pjsip_publishc_opt_default(&default_opt);
177 opt = &default_opt;
178 }
179 pj_memcpy(&pubc->opt, opt, sizeof(*opt));
180 pj_list_init(&pubc->pending_reqs);
181
182 status = pj_mutex_create_recursive(pubc->pool, "pubc%p", &pubc->mutex);
183 if (status != PJ_SUCCESS) {
184 pj_pool_release(pool);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000185 return status;
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000186 }
187
188 status = pjsip_auth_clt_init(&pubc->auth_sess, endpt, pubc->pool, 0);
189 if (status != PJ_SUCCESS) {
190 pj_mutex_destroy(pubc->mutex);
191 pj_pool_release(pool);
192 return status;
193 }
Benny Prijono21b9ad92006-08-15 13:11:22 +0000194
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000195 pj_list_init(&pubc->route_set);
Benny Prijono53984d12009-04-28 22:19:49 +0000196 pj_list_init(&pubc->usr_hdr);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000197
198 /* Done */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000199 *p_pubc = pubc;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000200 return PJ_SUCCESS;
201}
202
203
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000204PJ_DEF(pj_status_t) pjsip_publishc_destroy(pjsip_publishc *pubc)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000205{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000206 PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000207
Benny Prijono78979492010-11-29 14:49:37 +0000208 if (pubc->pending_tsx || pubc->in_callback) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000209 pubc->_delete_flag = 1;
210 pubc->cb = NULL;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000211 } else {
Benny Prijono6ad77662009-10-12 01:27:40 +0000212 /* Cancel existing timer, if any */
213 if (pubc->timer.id != 0) {
214 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
215 pubc->timer.id = 0;
216 }
217
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000218 if (pubc->mutex)
219 pj_mutex_destroy(pubc->mutex);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000220 pjsip_endpt_release_pool(pubc->endpt, pubc->pool);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000221 }
222
223 return PJ_SUCCESS;
224}
225
226
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000227PJ_DEF(pj_pool_t*) pjsip_publishc_get_pool(pjsip_publishc *pubc)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000228{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000229 return pubc->pool;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000230}
231
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000232static void set_expires( pjsip_publishc *pubc, pj_uint32_t expires)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000233{
Benny Prijono53984d12009-04-28 22:19:49 +0000234 if (expires != pubc->expires &&
235 expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED)
236 {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000237 pubc->expires_hdr = pjsip_expires_hdr_create(pubc->pool, expires);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000238 } else {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000239 pubc->expires_hdr = NULL;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000240 }
241}
242
243
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000244PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc,
245 const pj_str_t *event,
246 const pj_str_t *target_uri,
247 const pj_str_t *from_uri,
248 const pj_str_t *to_uri,
249 pj_uint32_t expires)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000250{
251 pj_str_t tmp;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000252
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000253 PJ_ASSERT_RETURN(pubc && event && target_uri && from_uri && to_uri &&
254 expires, PJ_EINVAL);
255
256 /* Copy event type */
257 pj_strdup_with_null(pubc->pool, &pubc->event, event);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000258
259 /* Copy server URL. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000260 pj_strdup_with_null(pubc->pool, &pubc->str_target_uri, target_uri);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000261
262 /* Set server URL. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000263 tmp = pubc->str_target_uri;
264 pubc->target_uri = pjsip_parse_uri( pubc->pool, tmp.ptr, tmp.slen, 0);
265 if (pubc->target_uri == NULL) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000266 return PJSIP_EINVALIDURI;
267 }
268
269 /* Set "From" header. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000270 pj_strdup_with_null(pubc->pool, &pubc->from_uri, from_uri);
271 tmp = pubc->from_uri;
272 pubc->from_hdr = pjsip_from_hdr_create(pubc->pool);
273 pubc->from_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000274 PJSIP_PARSE_URI_AS_NAMEADDR);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000275 if (!pubc->from_hdr->uri) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000276 return PJSIP_EINVALIDURI;
277 }
278
279 /* Set "To" header. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000280 pj_strdup_with_null(pubc->pool, &tmp, to_uri);
281 pubc->to_hdr = pjsip_to_hdr_create(pubc->pool);
282 pubc->to_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000283 PJSIP_PARSE_URI_AS_NAMEADDR);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000284 if (!pubc->to_hdr->uri) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000285 return PJSIP_EINVALIDURI;
286 }
287
288
Benny Prijono21b9ad92006-08-15 13:11:22 +0000289 /* Set "Expires" header, if required. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000290 set_expires( pubc, expires);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000291
292 /* Set "Call-ID" header. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000293 pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool);
294 pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000295
296 /* Set "CSeq" header. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000297 pubc->cseq_hdr = pjsip_cseq_hdr_create(pubc->pool);
298 pubc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
299 pjsip_method_set( &pubc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000300
301 /* Done. */
302 return PJ_SUCCESS;
303}
304
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000305PJ_DEF(pj_status_t) pjsip_publishc_set_credentials( pjsip_publishc *pubc,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000306 int count,
307 const pjsip_cred_info cred[] )
308{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000309 PJ_ASSERT_RETURN(pubc && count && cred, PJ_EINVAL);
310 return pjsip_auth_clt_set_credentials(&pubc->auth_sess, count, cred);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000311}
312
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000313PJ_DEF(pj_status_t) pjsip_publishc_set_route_set( pjsip_publishc *pubc,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000314 const pjsip_route_hdr *route_set)
315{
316 const pjsip_route_hdr *chdr;
317
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000318 PJ_ASSERT_RETURN(pubc && route_set, PJ_EINVAL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000319
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000320 pj_list_init(&pubc->route_set);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000321
322 chdr = route_set->next;
323 while (chdr != route_set) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000324 pj_list_push_back(&pubc->route_set, pjsip_hdr_clone(pubc->pool, chdr));
Benny Prijono21b9ad92006-08-15 13:11:22 +0000325 chdr = chdr->next;
326 }
327
328 return PJ_SUCCESS;
329}
330
Benny Prijono53984d12009-04-28 22:19:49 +0000331PJ_DEF(pj_status_t) pjsip_publishc_set_headers( pjsip_publishc *pubc,
332 const pjsip_hdr *hdr_list)
333{
334 const pjsip_hdr *h;
335
336 PJ_ASSERT_RETURN(pubc && hdr_list, PJ_EINVAL);
337
338 pj_list_init(&pubc->usr_hdr);
339 h = hdr_list->next;
340 while (h != hdr_list) {
341 pj_list_push_back(&pubc->usr_hdr, pjsip_hdr_clone(pubc->pool, h));
342 h = h->next;
343 }
344
345 return PJ_SUCCESS;
346}
347
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000348static pj_status_t create_request(pjsip_publishc *pubc,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000349 pjsip_tx_data **p_tdata)
350{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000351 const pj_str_t STR_EVENT = { "Event", 5 };
Benny Prijono21b9ad92006-08-15 13:11:22 +0000352 pj_status_t status;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000353 pjsip_generic_string_hdr *hdr;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000354 pjsip_tx_data *tdata;
355
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000356 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000357
358 /* Create the request. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000359 status = pjsip_endpt_create_request_from_hdr( pubc->endpt,
360 &pjsip_publish_method,
361 pubc->target_uri,
362 pubc->from_hdr,
363 pubc->to_hdr,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000364 NULL,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000365 pubc->cid_hdr,
366 pubc->cseq_hdr->cseq,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000367 NULL,
368 &tdata);
369 if (status != PJ_SUCCESS)
370 return status;
371
372 /* Add cached authorization headers. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000373 pjsip_auth_clt_init_req( &pubc->auth_sess, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +0000374
375 /* Add Route headers from route set, ideally after Via header */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000376 if (!pj_list_empty(&pubc->route_set)) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000377 pjsip_hdr *route_pos;
378 const pjsip_route_hdr *route;
379
Benny Prijono9d4469d2007-05-02 05:14:29 +0000380 route_pos = (pjsip_hdr*)
381 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000382 if (!route_pos)
383 route_pos = &tdata->msg->hdr;
384
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000385 route = pubc->route_set.next;
386 while (route != &pubc->route_set) {
Benny Prijono9d4469d2007-05-02 05:14:29 +0000387 pjsip_hdr *new_hdr = (pjsip_hdr*)
388 pjsip_hdr_shallow_clone(tdata->pool, route);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000389 pj_list_insert_after(route_pos, new_hdr);
390 route_pos = new_hdr;
391 route = route->next;
392 }
393 }
394
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000395 /* Add Event header */
396 hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT,
397 &pubc->event);
398 if (hdr)
399 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
400
401
402 /* Add SIP-If-Match if we have etag */
403 if (pubc->etag.slen) {
404 const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
405
406 hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME,
407 &pubc->etag);
408 if (hdr)
409 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
410 }
411
Benny Prijono53984d12009-04-28 22:19:49 +0000412 /* Add user headers */
413 if (!pj_list_empty(&pubc->usr_hdr)) {
414 const pjsip_hdr *hdr;
415
416 hdr = pubc->usr_hdr.next;
417 while (hdr != &pubc->usr_hdr) {
418 pjsip_hdr *new_hdr = (pjsip_hdr*)
419 pjsip_hdr_shallow_clone(tdata->pool, hdr);
420 pjsip_msg_add_hdr(tdata->msg, new_hdr);
421 hdr = hdr->next;
422 }
423 }
424
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000425
Benny Prijono21b9ad92006-08-15 13:11:22 +0000426 /* Done. */
427 *p_tdata = tdata;
428 return PJ_SUCCESS;
429}
430
431
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000432PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc,
433 pj_bool_t auto_refresh,
434 pjsip_tx_data **p_tdata)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000435{
Benny Prijono21b9ad92006-08-15 13:11:22 +0000436 pj_status_t status;
437 pjsip_tx_data *tdata;
438
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000439 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000440
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000441 status = create_request(pubc, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000442 if (status != PJ_SUCCESS)
443 return status;
444
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000445 /* Add Expires header */
446 if (pubc->expires_hdr) {
447 pjsip_hdr *dup;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000448
Benny Prijono9d4469d2007-05-02 05:14:29 +0000449 dup = (pjsip_hdr*)
450 pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000451 if (dup)
452 pjsip_msg_add_hdr(tdata->msg, dup);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000453 }
454
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000455 /* Cancel existing timer */
456 if (pubc->timer.id != 0) {
457 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
458 pubc->timer.id = 0;
459 }
460
461 pubc->auto_refresh = auto_refresh;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000462
463 /* Done */
464 *p_tdata = tdata;
465 return PJ_SUCCESS;
466}
467
468
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000469PJ_DEF(pj_status_t) pjsip_publishc_unpublish(pjsip_publishc *pubc,
470 pjsip_tx_data **p_tdata)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000471{
472 pjsip_tx_data *tdata;
473 pjsip_msg *msg;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000474 pjsip_expires_hdr *expires;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000475 pj_status_t status;
476
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000477 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000478
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000479 if (pubc->timer.id != 0) {
480 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
481 pubc->timer.id = 0;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000482 }
483
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000484 status = create_request(pubc, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000485 if (status != PJ_SUCCESS)
486 return status;
487
488 msg = tdata->msg;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000489
490 /* Add Expires:0 header */
491 expires = pjsip_expires_hdr_create(tdata->pool, 0);
492 pjsip_msg_add_hdr( msg, (pjsip_hdr*)expires);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000493
494 *p_tdata = tdata;
495 return PJ_SUCCESS;
496}
497
498
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000499PJ_DEF(pj_status_t) pjsip_publishc_update_expires( pjsip_publishc *pubc,
500 pj_uint32_t expires )
Benny Prijono21b9ad92006-08-15 13:11:22 +0000501{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000502 PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
503 set_expires( pubc, expires );
Benny Prijono21b9ad92006-08-15 13:11:22 +0000504 return PJ_SUCCESS;
505}
506
507
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000508static void call_callback(pjsip_publishc *pubc, pj_status_t status,
509 int st_code, const pj_str_t *reason,
510 pjsip_rx_data *rdata, pj_int32_t expiration)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000511{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000512 struct pjsip_publishc_cbparam cbparam;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000513
514
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000515 cbparam.pubc = pubc;
516 cbparam.token = pubc->token;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000517 cbparam.status = status;
518 cbparam.code = st_code;
519 cbparam.reason = *reason;
520 cbparam.rdata = rdata;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000521 cbparam.expiration = expiration;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000522
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000523 (*pubc->cb)(&cbparam);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000524}
525
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000526static void pubc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000527 struct pj_timer_entry *entry)
528{
Benny Prijono9d4469d2007-05-02 05:14:29 +0000529 pjsip_publishc *pubc = (pjsip_publishc*) entry->user_data;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000530 pjsip_tx_data *tdata;
531 pj_status_t status;
532
533 PJ_UNUSED_ARG(timer_heap);
534
535 entry->id = 0;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000536 status = pjsip_publishc_publish(pubc, 1, &tdata);
Benny Prijono0e9f7622008-07-01 19:11:55 +0000537 if (status != PJ_SUCCESS) {
538 char errmsg[PJ_ERR_MSG_SIZE];
539 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
540 call_callback(pubc, status, 400, &reason, NULL, -1);
541 return;
542 }
543
544 status = pjsip_publishc_send(pubc, tdata);
545 /* No need to call callback as it should have been called */
Benny Prijono21b9ad92006-08-15 13:11:22 +0000546}
547
548static void tsx_callback(void *token, pjsip_event *event)
549{
550 pj_status_t status;
Benny Prijono9d4469d2007-05-02 05:14:29 +0000551 pjsip_publishc *pubc = (pjsip_publishc*) token;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000552 pjsip_transaction *tsx = event->body.tsx_state.tsx;
553
554 /* Decrement pending transaction counter. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000555 pj_assert(pubc->pending_tsx > 0);
556 --pubc->pending_tsx;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000557
Benny Prijono78979492010-11-29 14:49:37 +0000558 /* Mark that we're in callback to prevent deletion (#1164) */
559 ++pubc->in_callback;
560
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000561 /* If publication data has been deleted by user then remove publication
Benny Prijono21b9ad92006-08-15 13:11:22 +0000562 * data from transaction's callback, and don't call callback.
563 */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000564 if (pubc->_delete_flag) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000565
566 /* Nothing to do */
567 ;
568
569 } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
570 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
571 {
572 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
573 pjsip_tx_data *tdata;
574
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000575 status = pjsip_auth_clt_reinit_req( &pubc->auth_sess,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000576 rdata,
577 tsx->last_tx,
578 &tdata);
Benny Prijono0e9f7622008-07-01 19:11:55 +0000579 if (status != PJ_SUCCESS) {
580 call_callback(pubc, status, tsx->status_code,
581 &rdata->msg_info.msg->line.status.reason,
582 rdata, -1);
583 } else {
584 status = pjsip_publishc_send(pubc, tdata);
585 }
Benny Prijono21b9ad92006-08-15 13:11:22 +0000586
587 } else {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000588 pjsip_rx_data *rdata;
589 pj_int32_t expiration = 0xFFFF;
590
591 if (tsx->status_code/100 == 2) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000592 pjsip_msg *msg;
593 pjsip_expires_hdr *expires;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000594 pjsip_generic_string_hdr *etag_hdr;
595 const pj_str_t STR_ETAG = { "SIP-ETag", 8 };
Benny Prijono21b9ad92006-08-15 13:11:22 +0000596
597 rdata = event->body.tsx_state.src.rdata;
598 msg = rdata->msg_info.msg;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000599
600 /* Save ETag value */
601 etag_hdr = (pjsip_generic_string_hdr*)
602 pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL);
603 if (etag_hdr) {
604 pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue);
605 } else {
606 pubc->etag.slen = 0;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000607 }
608
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000609 /* Update expires value */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000610 expires = (pjsip_expires_hdr*)
611 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000612
Benny Prijono53984d12009-04-28 22:19:49 +0000613 if (pubc->auto_refresh && expires)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000614 expiration = expires->ivalue;
615
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000616 if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000617 pj_time_val delay = { 0, 0};
618
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000619 /* Cancel existing timer, if any */
620 if (pubc->timer.id != 0) {
621 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
622 pubc->timer.id = 0;
623 }
624
Benny Prijono21b9ad92006-08-15 13:11:22 +0000625 delay.sec = expiration - DELAY_BEFORE_REFRESH;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000626 if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED &&
627 delay.sec > (pj_int32_t)pubc->expires)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000628 {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000629 delay.sec = pubc->expires;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000630 }
631 if (delay.sec < DELAY_BEFORE_REFRESH)
632 delay.sec = DELAY_BEFORE_REFRESH;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000633 pubc->timer.cb = &pubc_refresh_timer_cb;
634 pubc->timer.id = REFRESH_TIMER;
635 pubc->timer.user_data = pubc;
636 pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay);
637 pj_gettimeofday(&pubc->last_refresh);
638 pubc->next_refresh = pubc->last_refresh;
639 pubc->next_refresh.sec += delay.sec;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000640 }
641
642 } else {
643 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
644 event->body.tsx_state.src.rdata : NULL;
645 }
646
647
648 /* Call callback. */
649 if (expiration == 0xFFFF) expiration = -1;
Benny Prijonoda1e0632007-05-23 14:12:35 +0000650
651 /* Temporarily increment pending_tsx to prevent callback from
652 * destroying pubc.
653 */
654 ++pubc->pending_tsx;
655
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000656 call_callback(pubc, PJ_SUCCESS, tsx->status_code,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000657 (rdata ? &rdata->msg_info.msg->line.status.reason
658 : pjsip_get_status_text(tsx->status_code)),
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000659 rdata, expiration);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000660
Benny Prijonoda1e0632007-05-23 14:12:35 +0000661 --pubc->pending_tsx;
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000662
663 /* If we have pending request(s), send them now */
664 pj_mutex_lock(pubc->mutex);
665 while (!pj_list_empty(&pubc->pending_reqs)) {
666 pjsip_tx_data *tdata = pubc->pending_reqs.next;
667 pj_list_erase(tdata);
Benny Prijonoc7b3c3b2010-02-24 11:22:21 +0000668
669 /* Add SIP-If-Match if we have etag and the request doesn't have
670 * one (http://trac.pjsip.org/repos/ticket/996)
671 */
672 if (pubc->etag.slen) {
673 const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
674 pjsip_generic_string_hdr *sim_hdr;
675
676 sim_hdr = (pjsip_generic_string_hdr*)
677 pjsip_msg_find_hdr_by_name(tdata->msg, &STR_HNAME, NULL);
678 if (!sim_hdr) {
679 /* Create the header */
680 sim_hdr = pjsip_generic_string_hdr_create(tdata->pool,
681 &STR_HNAME,
682 &pubc->etag);
683 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sim_hdr);
684
685 } else {
686 /* Update */
687 if (pj_strcmp(&pubc->etag, &sim_hdr->hvalue))
688 pj_strdup(tdata->pool, &sim_hdr->hvalue, &pubc->etag);
689 }
690 }
691
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000692 status = pjsip_publishc_send(pubc, tdata);
693 if (status == PJ_EPENDING) {
694 pj_assert(!"Not expected");
695 pj_list_erase(tdata);
696 pjsip_tx_data_dec_ref(tdata);
697 } else if (status == PJ_SUCCESS) {
698 break;
699 }
700 }
701 pj_mutex_unlock(pubc->mutex);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000702 }
703
Benny Prijono78979492010-11-29 14:49:37 +0000704 /* No longer in callback. */
705 --pubc->in_callback;
706
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000707 /* Delete the record if user destroy pubc during the callback. */
708 if (pubc->_delete_flag && pubc->pending_tsx==0) {
709 pjsip_publishc_destroy(pubc);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000710 }
711}
712
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000713
714PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc,
715 pjsip_tx_data *tdata)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000716{
717 pj_status_t status;
718 pjsip_cseq_hdr *cseq_hdr;
719 pj_uint32_t cseq;
720
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000721 PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL);
722
Benny Prijono21b9ad92006-08-15 13:11:22 +0000723 /* Make sure we don't have pending transaction. */
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000724 pj_mutex_lock(pubc->mutex);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000725 if (pubc->pending_tsx) {
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000726 if (pubc->opt.queue_request) {
727 pj_list_push_back(&pubc->pending_reqs, tdata);
728 pj_mutex_unlock(pubc->mutex);
729 PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another "
730 "transaction pending"));
731 return PJ_EPENDING;
732 } else {
733 pjsip_tx_data_dec_ref(tdata);
734 pj_mutex_unlock(pubc->mutex);
735 PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another "
736 "transaction pending"));
737 return PJ_EBUSY;
738 }
Benny Prijono21b9ad92006-08-15 13:11:22 +0000739 }
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000740 pj_mutex_unlock(pubc->mutex);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000741
742 /* Invalidate message buffer. */
743 pjsip_tx_data_invalidate_msg(tdata);
744
745 /* Increment CSeq */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000746 cseq = ++pubc->cseq_hdr->cseq;
Benny Prijono9d4469d2007-05-02 05:14:29 +0000747 cseq_hdr = (pjsip_cseq_hdr*)
748 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000749 cseq_hdr->cseq = cseq;
750
751 /* Increment pending transaction first, since transaction callback
752 * may be called even before send_request() returns!
753 */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000754 ++pubc->pending_tsx;
755 status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc,
756 &tsx_callback);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000757 if (status!=PJ_SUCCESS) {
Benny Prijono29438152007-06-28 02:47:32 +0000758 // no need to decrement, callback has been called and it should
759 // already decremented pending_tsx. Decrementing this here may
760 // cause accessing freed memory location.
761 //--pubc->pending_tsx;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000762 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
763 }
764
765 return status;
766}
767