blob: 6313ba9689455f5f3796378156a65ce03a5d53df [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 Prijonofe50c9e2009-10-12 07:44:14 +000077 pj_mutex_t *mutex;
Benny Prijono21b9ad92006-08-15 13:11:22 +000078
Benny Prijonofe50c9e2009-10-12 07:44:14 +000079 pjsip_publishc_opt opt;
Benny Prijono21b9ad92006-08-15 13:11:22 +000080 void *token;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000081 pjsip_publishc_cb *cb;
Benny Prijono21b9ad92006-08-15 13:11:22 +000082
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000083 pj_str_t event;
84 pj_str_t str_target_uri;
85 pjsip_uri *target_uri;
Benny Prijono21b9ad92006-08-15 13:11:22 +000086 pjsip_cid_hdr *cid_hdr;
87 pjsip_cseq_hdr *cseq_hdr;
88 pj_str_t from_uri;
89 pjsip_from_hdr *from_hdr;
90 pjsip_to_hdr *to_hdr;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +000091 pj_str_t etag;
Benny Prijono21b9ad92006-08-15 13:11:22 +000092 pjsip_expires_hdr *expires_hdr;
Benny Prijono21b9ad92006-08-15 13:11:22 +000093 pj_uint32_t expires;
94 pjsip_route_hdr route_set;
Benny Prijono53984d12009-04-28 22:19:49 +000095 pjsip_hdr usr_hdr;
Benny Prijono21b9ad92006-08-15 13:11:22 +000096
97 /* Authorization sessions. */
98 pjsip_auth_clt_sess auth_sess;
99
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000100 /* Auto refresh publication. */
101 pj_bool_t auto_refresh;
102 pj_time_val last_refresh;
103 pj_time_val next_refresh;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000104 pj_timer_entry timer;
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000105
106 /* Pending PUBLISH request */
107 pending_publish pending_reqs;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000108};
109
110
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000111PJ_DEF(void) pjsip_publishc_opt_default(pjsip_publishc_opt *opt)
112{
113 pj_bzero(opt, sizeof(*opt));
114 opt->queue_request = PJSIP_PUBLISHC_QUEUE_REQUEST;
115}
116
Benny Prijono21b9ad92006-08-15 13:11:22 +0000117
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000118/*
119 * Initialize client publication module.
120 */
121PJ_DEF(pj_status_t) pjsip_publishc_init_module(pjsip_endpoint *endpt)
122{
Benny Prijono7741c8a2008-07-07 19:57:16 +0000123 /* Note:
124 Commented out the capability registration below, since it's
125 wrong to include PUBLISH in Allow header of INVITE requests/
126 responses.
127
128 13.2.1 Creating the Initial INVITE
129 An Allow header field (Section 20.5) SHOULD be present in the
130 INVITE. It indicates what methods can be invoked within a dialog
131
132 20.5 Allow
133 The Allow header field lists the set of methods supported by the
134 UA generating the message.
135
136 While the semantic of Allow header in non-dialog requests is unclear,
137 it's probably best not to include PUBLISH in Allow header for now
138 until we can find out how to customize the inclusion of methods in
139 Allow header for in-dialog vs out-dialog requests.
140
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000141 return pjsip_endpt_add_capability( endpt, NULL, PJSIP_H_ALLOW, NULL,
142 1, &pjsip_publish_method.name);
Benny Prijono7741c8a2008-07-07 19:57:16 +0000143 */
Benny Prijono10d8dbd2008-07-13 13:12:36 +0000144 PJ_UNUSED_ARG(endpt);
Benny Prijono7741c8a2008-07-07 19:57:16 +0000145 return PJ_SUCCESS;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000146}
147
148
149PJ_DEF(pj_status_t) pjsip_publishc_create( pjsip_endpoint *endpt,
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000150 const pjsip_publishc_opt *opt,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000151 void *token,
152 pjsip_publishc_cb *cb,
153 pjsip_publishc **p_pubc)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000154{
155 pj_pool_t *pool;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000156 pjsip_publishc *pubc;
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000157 pjsip_publishc_opt default_opt;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000158 pj_status_t status;
159
160 /* Verify arguments. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000161 PJ_ASSERT_RETURN(endpt && cb && p_pubc, PJ_EINVAL);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000162
163 pool = pjsip_endpt_create_pool(endpt, "pubc%p", 1024, 1024);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000164 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
165
Benny Prijono9d4469d2007-05-02 05:14:29 +0000166 pubc = PJ_POOL_ZALLOC_T(pool, pjsip_publishc);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000167
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000168 pubc->pool = pool;
169 pubc->endpt = endpt;
170 pubc->token = token;
171 pubc->cb = cb;
172 pubc->expires = PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000173
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000174 if (!opt) {
175 pjsip_publishc_opt_default(&default_opt);
176 opt = &default_opt;
177 }
178 pj_memcpy(&pubc->opt, opt, sizeof(*opt));
179 pj_list_init(&pubc->pending_reqs);
180
181 status = pj_mutex_create_recursive(pubc->pool, "pubc%p", &pubc->mutex);
182 if (status != PJ_SUCCESS) {
183 pj_pool_release(pool);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000184 return status;
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000185 }
186
187 status = pjsip_auth_clt_init(&pubc->auth_sess, endpt, pubc->pool, 0);
188 if (status != PJ_SUCCESS) {
189 pj_mutex_destroy(pubc->mutex);
190 pj_pool_release(pool);
191 return status;
192 }
Benny Prijono21b9ad92006-08-15 13:11:22 +0000193
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000194 pj_list_init(&pubc->route_set);
Benny Prijono53984d12009-04-28 22:19:49 +0000195 pj_list_init(&pubc->usr_hdr);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000196
197 /* Done */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000198 *p_pubc = pubc;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000199 return PJ_SUCCESS;
200}
201
202
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000203PJ_DEF(pj_status_t) pjsip_publishc_destroy(pjsip_publishc *pubc)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000204{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000205 PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000206
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000207 if (pubc->pending_tsx) {
208 pubc->_delete_flag = 1;
209 pubc->cb = NULL;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000210 } else {
Benny Prijono6ad77662009-10-12 01:27:40 +0000211 /* Cancel existing timer, if any */
212 if (pubc->timer.id != 0) {
213 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
214 pubc->timer.id = 0;
215 }
216
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000217 if (pubc->mutex)
218 pj_mutex_destroy(pubc->mutex);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000219 pjsip_endpt_release_pool(pubc->endpt, pubc->pool);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000220 }
221
222 return PJ_SUCCESS;
223}
224
225
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000226PJ_DEF(pj_pool_t*) pjsip_publishc_get_pool(pjsip_publishc *pubc)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000227{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000228 return pubc->pool;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000229}
230
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000231static void set_expires( pjsip_publishc *pubc, pj_uint32_t expires)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000232{
Benny Prijono53984d12009-04-28 22:19:49 +0000233 if (expires != pubc->expires &&
234 expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED)
235 {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000236 pubc->expires_hdr = pjsip_expires_hdr_create(pubc->pool, expires);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000237 } else {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000238 pubc->expires_hdr = NULL;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000239 }
240}
241
242
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000243PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc,
244 const pj_str_t *event,
245 const pj_str_t *target_uri,
246 const pj_str_t *from_uri,
247 const pj_str_t *to_uri,
248 pj_uint32_t expires)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000249{
250 pj_str_t tmp;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000251
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000252 PJ_ASSERT_RETURN(pubc && event && target_uri && from_uri && to_uri &&
253 expires, PJ_EINVAL);
254
255 /* Copy event type */
256 pj_strdup_with_null(pubc->pool, &pubc->event, event);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000257
258 /* Copy server URL. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000259 pj_strdup_with_null(pubc->pool, &pubc->str_target_uri, target_uri);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000260
261 /* Set server URL. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000262 tmp = pubc->str_target_uri;
263 pubc->target_uri = pjsip_parse_uri( pubc->pool, tmp.ptr, tmp.slen, 0);
264 if (pubc->target_uri == NULL) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000265 return PJSIP_EINVALIDURI;
266 }
267
268 /* Set "From" header. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000269 pj_strdup_with_null(pubc->pool, &pubc->from_uri, from_uri);
270 tmp = pubc->from_uri;
271 pubc->from_hdr = pjsip_from_hdr_create(pubc->pool);
272 pubc->from_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000273 PJSIP_PARSE_URI_AS_NAMEADDR);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000274 if (!pubc->from_hdr->uri) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000275 return PJSIP_EINVALIDURI;
276 }
277
278 /* Set "To" header. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000279 pj_strdup_with_null(pubc->pool, &tmp, to_uri);
280 pubc->to_hdr = pjsip_to_hdr_create(pubc->pool);
281 pubc->to_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000282 PJSIP_PARSE_URI_AS_NAMEADDR);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000283 if (!pubc->to_hdr->uri) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000284 return PJSIP_EINVALIDURI;
285 }
286
287
Benny Prijono21b9ad92006-08-15 13:11:22 +0000288 /* Set "Expires" header, if required. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000289 set_expires( pubc, expires);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000290
291 /* Set "Call-ID" header. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000292 pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool);
293 pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000294
295 /* Set "CSeq" header. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000296 pubc->cseq_hdr = pjsip_cseq_hdr_create(pubc->pool);
297 pubc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
298 pjsip_method_set( &pubc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000299
300 /* Done. */
301 return PJ_SUCCESS;
302}
303
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000304PJ_DEF(pj_status_t) pjsip_publishc_set_credentials( pjsip_publishc *pubc,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000305 int count,
306 const pjsip_cred_info cred[] )
307{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000308 PJ_ASSERT_RETURN(pubc && count && cred, PJ_EINVAL);
309 return pjsip_auth_clt_set_credentials(&pubc->auth_sess, count, cred);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000310}
311
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000312PJ_DEF(pj_status_t) pjsip_publishc_set_route_set( pjsip_publishc *pubc,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000313 const pjsip_route_hdr *route_set)
314{
315 const pjsip_route_hdr *chdr;
316
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000317 PJ_ASSERT_RETURN(pubc && route_set, PJ_EINVAL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000318
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000319 pj_list_init(&pubc->route_set);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000320
321 chdr = route_set->next;
322 while (chdr != route_set) {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000323 pj_list_push_back(&pubc->route_set, pjsip_hdr_clone(pubc->pool, chdr));
Benny Prijono21b9ad92006-08-15 13:11:22 +0000324 chdr = chdr->next;
325 }
326
327 return PJ_SUCCESS;
328}
329
Benny Prijono53984d12009-04-28 22:19:49 +0000330PJ_DEF(pj_status_t) pjsip_publishc_set_headers( pjsip_publishc *pubc,
331 const pjsip_hdr *hdr_list)
332{
333 const pjsip_hdr *h;
334
335 PJ_ASSERT_RETURN(pubc && hdr_list, PJ_EINVAL);
336
337 pj_list_init(&pubc->usr_hdr);
338 h = hdr_list->next;
339 while (h != hdr_list) {
340 pj_list_push_back(&pubc->usr_hdr, pjsip_hdr_clone(pubc->pool, h));
341 h = h->next;
342 }
343
344 return PJ_SUCCESS;
345}
346
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000347static pj_status_t create_request(pjsip_publishc *pubc,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000348 pjsip_tx_data **p_tdata)
349{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000350 const pj_str_t STR_EVENT = { "Event", 5 };
Benny Prijono21b9ad92006-08-15 13:11:22 +0000351 pj_status_t status;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000352 pjsip_generic_string_hdr *hdr;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000353 pjsip_tx_data *tdata;
354
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000355 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000356
357 /* Create the request. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000358 status = pjsip_endpt_create_request_from_hdr( pubc->endpt,
359 &pjsip_publish_method,
360 pubc->target_uri,
361 pubc->from_hdr,
362 pubc->to_hdr,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000363 NULL,
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000364 pubc->cid_hdr,
365 pubc->cseq_hdr->cseq,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000366 NULL,
367 &tdata);
368 if (status != PJ_SUCCESS)
369 return status;
370
371 /* Add cached authorization headers. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000372 pjsip_auth_clt_init_req( &pubc->auth_sess, tdata );
Benny Prijono21b9ad92006-08-15 13:11:22 +0000373
374 /* Add Route headers from route set, ideally after Via header */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000375 if (!pj_list_empty(&pubc->route_set)) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000376 pjsip_hdr *route_pos;
377 const pjsip_route_hdr *route;
378
Benny Prijono9d4469d2007-05-02 05:14:29 +0000379 route_pos = (pjsip_hdr*)
380 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000381 if (!route_pos)
382 route_pos = &tdata->msg->hdr;
383
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000384 route = pubc->route_set.next;
385 while (route != &pubc->route_set) {
Benny Prijono9d4469d2007-05-02 05:14:29 +0000386 pjsip_hdr *new_hdr = (pjsip_hdr*)
387 pjsip_hdr_shallow_clone(tdata->pool, route);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000388 pj_list_insert_after(route_pos, new_hdr);
389 route_pos = new_hdr;
390 route = route->next;
391 }
392 }
393
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000394 /* Add Event header */
395 hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT,
396 &pubc->event);
397 if (hdr)
398 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
399
400
401 /* Add SIP-If-Match if we have etag */
402 if (pubc->etag.slen) {
403 const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
404
405 hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME,
406 &pubc->etag);
407 if (hdr)
408 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
409 }
410
Benny Prijono53984d12009-04-28 22:19:49 +0000411 /* Add user headers */
412 if (!pj_list_empty(&pubc->usr_hdr)) {
413 const pjsip_hdr *hdr;
414
415 hdr = pubc->usr_hdr.next;
416 while (hdr != &pubc->usr_hdr) {
417 pjsip_hdr *new_hdr = (pjsip_hdr*)
418 pjsip_hdr_shallow_clone(tdata->pool, hdr);
419 pjsip_msg_add_hdr(tdata->msg, new_hdr);
420 hdr = hdr->next;
421 }
422 }
423
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000424
Benny Prijono21b9ad92006-08-15 13:11:22 +0000425 /* Done. */
426 *p_tdata = tdata;
427 return PJ_SUCCESS;
428}
429
430
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000431PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc,
432 pj_bool_t auto_refresh,
433 pjsip_tx_data **p_tdata)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000434{
Benny Prijono21b9ad92006-08-15 13:11:22 +0000435 pj_status_t status;
436 pjsip_tx_data *tdata;
437
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000438 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000439
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000440 status = create_request(pubc, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000441 if (status != PJ_SUCCESS)
442 return status;
443
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000444 /* Add Expires header */
445 if (pubc->expires_hdr) {
446 pjsip_hdr *dup;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000447
Benny Prijono9d4469d2007-05-02 05:14:29 +0000448 dup = (pjsip_hdr*)
449 pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000450 if (dup)
451 pjsip_msg_add_hdr(tdata->msg, dup);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000452 }
453
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000454 /* Cancel existing timer */
455 if (pubc->timer.id != 0) {
456 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
457 pubc->timer.id = 0;
458 }
459
460 pubc->auto_refresh = auto_refresh;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000461
462 /* Done */
463 *p_tdata = tdata;
464 return PJ_SUCCESS;
465}
466
467
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000468PJ_DEF(pj_status_t) pjsip_publishc_unpublish(pjsip_publishc *pubc,
469 pjsip_tx_data **p_tdata)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000470{
471 pjsip_tx_data *tdata;
472 pjsip_msg *msg;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000473 pjsip_expires_hdr *expires;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000474 pj_status_t status;
475
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000476 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000477
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000478 if (pubc->timer.id != 0) {
479 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
480 pubc->timer.id = 0;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000481 }
482
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000483 status = create_request(pubc, &tdata);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000484 if (status != PJ_SUCCESS)
485 return status;
486
487 msg = tdata->msg;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000488
489 /* Add Expires:0 header */
490 expires = pjsip_expires_hdr_create(tdata->pool, 0);
491 pjsip_msg_add_hdr( msg, (pjsip_hdr*)expires);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000492
493 *p_tdata = tdata;
494 return PJ_SUCCESS;
495}
496
497
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000498PJ_DEF(pj_status_t) pjsip_publishc_update_expires( pjsip_publishc *pubc,
499 pj_uint32_t expires )
Benny Prijono21b9ad92006-08-15 13:11:22 +0000500{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000501 PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
502 set_expires( pubc, expires );
Benny Prijono21b9ad92006-08-15 13:11:22 +0000503 return PJ_SUCCESS;
504}
505
506
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000507static void call_callback(pjsip_publishc *pubc, pj_status_t status,
508 int st_code, const pj_str_t *reason,
509 pjsip_rx_data *rdata, pj_int32_t expiration)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000510{
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000511 struct pjsip_publishc_cbparam cbparam;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000512
513
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000514 cbparam.pubc = pubc;
515 cbparam.token = pubc->token;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000516 cbparam.status = status;
517 cbparam.code = st_code;
518 cbparam.reason = *reason;
519 cbparam.rdata = rdata;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000520 cbparam.expiration = expiration;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000521
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000522 (*pubc->cb)(&cbparam);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000523}
524
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000525static void pubc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000526 struct pj_timer_entry *entry)
527{
Benny Prijono9d4469d2007-05-02 05:14:29 +0000528 pjsip_publishc *pubc = (pjsip_publishc*) entry->user_data;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000529 pjsip_tx_data *tdata;
530 pj_status_t status;
531
532 PJ_UNUSED_ARG(timer_heap);
533
534 entry->id = 0;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000535 status = pjsip_publishc_publish(pubc, 1, &tdata);
Benny Prijono0e9f7622008-07-01 19:11:55 +0000536 if (status != PJ_SUCCESS) {
537 char errmsg[PJ_ERR_MSG_SIZE];
538 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
539 call_callback(pubc, status, 400, &reason, NULL, -1);
540 return;
541 }
542
543 status = pjsip_publishc_send(pubc, tdata);
544 /* No need to call callback as it should have been called */
Benny Prijono21b9ad92006-08-15 13:11:22 +0000545}
546
547static void tsx_callback(void *token, pjsip_event *event)
548{
549 pj_status_t status;
Benny Prijono9d4469d2007-05-02 05:14:29 +0000550 pjsip_publishc *pubc = (pjsip_publishc*) token;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000551 pjsip_transaction *tsx = event->body.tsx_state.tsx;
552
553 /* Decrement pending transaction counter. */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000554 pj_assert(pubc->pending_tsx > 0);
555 --pubc->pending_tsx;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000556
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000557 /* If publication data has been deleted by user then remove publication
Benny Prijono21b9ad92006-08-15 13:11:22 +0000558 * data from transaction's callback, and don't call callback.
559 */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000560 if (pubc->_delete_flag) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000561
562 /* Nothing to do */
563 ;
564
565 } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
566 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
567 {
568 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
569 pjsip_tx_data *tdata;
570
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000571 status = pjsip_auth_clt_reinit_req( &pubc->auth_sess,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000572 rdata,
573 tsx->last_tx,
574 &tdata);
Benny Prijono0e9f7622008-07-01 19:11:55 +0000575 if (status != PJ_SUCCESS) {
576 call_callback(pubc, status, tsx->status_code,
577 &rdata->msg_info.msg->line.status.reason,
578 rdata, -1);
579 } else {
580 status = pjsip_publishc_send(pubc, tdata);
581 }
Benny Prijono21b9ad92006-08-15 13:11:22 +0000582
583 } else {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000584 pjsip_rx_data *rdata;
585 pj_int32_t expiration = 0xFFFF;
586
587 if (tsx->status_code/100 == 2) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000588 pjsip_msg *msg;
589 pjsip_expires_hdr *expires;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000590 pjsip_generic_string_hdr *etag_hdr;
591 const pj_str_t STR_ETAG = { "SIP-ETag", 8 };
Benny Prijono21b9ad92006-08-15 13:11:22 +0000592
593 rdata = event->body.tsx_state.src.rdata;
594 msg = rdata->msg_info.msg;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000595
596 /* Save ETag value */
597 etag_hdr = (pjsip_generic_string_hdr*)
598 pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL);
599 if (etag_hdr) {
600 pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue);
601 } else {
602 pubc->etag.slen = 0;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000603 }
604
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000605 /* Update expires value */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000606 expires = (pjsip_expires_hdr*)
607 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000608
Benny Prijono53984d12009-04-28 22:19:49 +0000609 if (pubc->auto_refresh && expires)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000610 expiration = expires->ivalue;
611
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000612 if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) {
Benny Prijono21b9ad92006-08-15 13:11:22 +0000613 pj_time_val delay = { 0, 0};
614
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000615 /* Cancel existing timer, if any */
616 if (pubc->timer.id != 0) {
617 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
618 pubc->timer.id = 0;
619 }
620
Benny Prijono21b9ad92006-08-15 13:11:22 +0000621 delay.sec = expiration - DELAY_BEFORE_REFRESH;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000622 if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED &&
623 delay.sec > (pj_int32_t)pubc->expires)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000624 {
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000625 delay.sec = pubc->expires;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000626 }
627 if (delay.sec < DELAY_BEFORE_REFRESH)
628 delay.sec = DELAY_BEFORE_REFRESH;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000629 pubc->timer.cb = &pubc_refresh_timer_cb;
630 pubc->timer.id = REFRESH_TIMER;
631 pubc->timer.user_data = pubc;
632 pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay);
633 pj_gettimeofday(&pubc->last_refresh);
634 pubc->next_refresh = pubc->last_refresh;
635 pubc->next_refresh.sec += delay.sec;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000636 }
637
638 } else {
639 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
640 event->body.tsx_state.src.rdata : NULL;
641 }
642
643
644 /* Call callback. */
645 if (expiration == 0xFFFF) expiration = -1;
Benny Prijonoda1e0632007-05-23 14:12:35 +0000646
647 /* Temporarily increment pending_tsx to prevent callback from
648 * destroying pubc.
649 */
650 ++pubc->pending_tsx;
651
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000652 call_callback(pubc, PJ_SUCCESS, tsx->status_code,
Benny Prijono21b9ad92006-08-15 13:11:22 +0000653 (rdata ? &rdata->msg_info.msg->line.status.reason
654 : pjsip_get_status_text(tsx->status_code)),
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000655 rdata, expiration);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000656
Benny Prijonoda1e0632007-05-23 14:12:35 +0000657 --pubc->pending_tsx;
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000658
659 /* If we have pending request(s), send them now */
660 pj_mutex_lock(pubc->mutex);
661 while (!pj_list_empty(&pubc->pending_reqs)) {
662 pjsip_tx_data *tdata = pubc->pending_reqs.next;
663 pj_list_erase(tdata);
664 status = pjsip_publishc_send(pubc, tdata);
665 if (status == PJ_EPENDING) {
666 pj_assert(!"Not expected");
667 pj_list_erase(tdata);
668 pjsip_tx_data_dec_ref(tdata);
669 } else if (status == PJ_SUCCESS) {
670 break;
671 }
672 }
673 pj_mutex_unlock(pubc->mutex);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000674 }
675
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000676 /* Delete the record if user destroy pubc during the callback. */
677 if (pubc->_delete_flag && pubc->pending_tsx==0) {
678 pjsip_publishc_destroy(pubc);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000679 }
680}
681
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000682
683PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc,
684 pjsip_tx_data *tdata)
Benny Prijono21b9ad92006-08-15 13:11:22 +0000685{
686 pj_status_t status;
687 pjsip_cseq_hdr *cseq_hdr;
688 pj_uint32_t cseq;
689
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000690 PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL);
691
Benny Prijono21b9ad92006-08-15 13:11:22 +0000692 /* Make sure we don't have pending transaction. */
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000693 pj_mutex_lock(pubc->mutex);
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000694 if (pubc->pending_tsx) {
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000695 if (pubc->opt.queue_request) {
696 pj_list_push_back(&pubc->pending_reqs, tdata);
697 pj_mutex_unlock(pubc->mutex);
698 PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another "
699 "transaction pending"));
700 return PJ_EPENDING;
701 } else {
702 pjsip_tx_data_dec_ref(tdata);
703 pj_mutex_unlock(pubc->mutex);
704 PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another "
705 "transaction pending"));
706 return PJ_EBUSY;
707 }
Benny Prijono21b9ad92006-08-15 13:11:22 +0000708 }
Benny Prijonofe50c9e2009-10-12 07:44:14 +0000709 pj_mutex_unlock(pubc->mutex);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000710
711 /* Invalidate message buffer. */
712 pjsip_tx_data_invalidate_msg(tdata);
713
714 /* Increment CSeq */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000715 cseq = ++pubc->cseq_hdr->cseq;
Benny Prijono9d4469d2007-05-02 05:14:29 +0000716 cseq_hdr = (pjsip_cseq_hdr*)
717 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000718 cseq_hdr->cseq = cseq;
719
720 /* Increment pending transaction first, since transaction callback
721 * may be called even before send_request() returns!
722 */
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000723 ++pubc->pending_tsx;
724 status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc,
725 &tsx_callback);
Benny Prijono21b9ad92006-08-15 13:11:22 +0000726 if (status!=PJ_SUCCESS) {
Benny Prijono29438152007-06-28 02:47:32 +0000727 // no need to decrement, callback has been called and it should
728 // already decremented pending_tsx. Decrementing this here may
729 // cause accessing freed memory location.
730 //--pubc->pending_tsx;
Benny Prijono21b9ad92006-08-15 13:11:22 +0000731 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
732 }
733
734 return status;
735}
736