1 /* $Id$ */
2 /*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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 */
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>
30 #include <pj/guid.h>
31 #include <pj/log.h>
32 #include <pj/os.h>
33 #include <pj/pool.h>
34 #include <pj/rand.h>
35 #include <pj/string.h>
36 #include <pj/timer.h>
37
38
39 #define REFRESH_TIMER 1
40 #define DELAY_BEFORE_REFRESH PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH
41 #define THIS_FILE "publishc.c"
42
43
44 /* Let's define this enum, so that it'll trigger compilation error
45 * when somebody define the same enum in sip_msg.h
46 */
47 enum
48 {
49 PJSIP_PUBLISH_METHOD = PJSIP_OTHER_METHOD,
50 };
51
52 const pjsip_method pjsip_publish_method =
53 {
54 (pjsip_method_e)PJSIP_PUBLISH_METHOD,
55 { "PUBLISH", 7 }
56 };
57
58
59 /**
60 * Pending request list.
61 */
62 typedef struct pending_publish
63 {
64 PJ_DECL_LIST_MEMBER(struct pending_publish);
65 pjsip_tx_data *tdata;
66 } pending_publish;
67
68
69 /**
70 * SIP client publication structure.
71 */
72 struct pjsip_publishc
73 {
74 pj_pool_t *pool;
75 pjsip_endpoint *endpt;
76 pj_bool_t _delete_flag;
77 int pending_tsx;
78 pj_bool_t in_callback;
79 pj_mutex_t *mutex;
80
81 pjsip_publishc_opt opt;
82 void *token;
83 pjsip_publishc_cb *cb;
84
85 pj_str_t event;
86 pj_str_t str_target_uri;
87 pjsip_uri *target_uri;
88 pjsip_cid_hdr *cid_hdr;
89 pjsip_cseq_hdr *cseq_hdr;
90 pj_str_t from_uri;
91 pjsip_from_hdr *from_hdr;
92 pjsip_to_hdr *to_hdr;
93 pj_str_t etag;
94 pjsip_expires_hdr *expires_hdr;
95 pj_uint32_t expires;
96 pjsip_route_hdr route_set;
97 pjsip_hdr usr_hdr;
98 pjsip_host_port via_addr;
99 const void *via_tp;
100
101 /* Authorization sessions. */
102 pjsip_auth_clt_sess auth_sess;
103
104 /* Auto refresh publication. */
105 pj_bool_t auto_refresh;
106 pj_time_val last_refresh;
107 pj_time_val next_refresh;
108 pj_timer_entry timer;
109
110 /* Pending PUBLISH request */
111 pending_publish pending_reqs;
112 pending_publish pending_reqs_empty;
113 };
114
115
pjsip_publishc_opt_default(pjsip_publishc_opt * opt)116 PJ_DEF(void) pjsip_publishc_opt_default(pjsip_publishc_opt *opt)
117 {
118 pj_bzero(opt, sizeof(*opt));
119 opt->queue_request = PJSIP_PUBLISHC_QUEUE_REQUEST;
120 }
121
122
123 /*
124 * Initialize client publication module.
125 */
pjsip_publishc_init_module(pjsip_endpoint * endpt)126 PJ_DEF(pj_status_t) pjsip_publishc_init_module(pjsip_endpoint *endpt)
127 {
128 /* Note:
129 Commented out the capability registration below, since it's
130 wrong to include PUBLISH in Allow header of INVITE requests/
131 responses.
132
133 13.2.1 Creating the Initial INVITE
134 An Allow header field (Section 20.5) SHOULD be present in the
135 INVITE. It indicates what methods can be invoked within a dialog
136
137 20.5 Allow
138 The Allow header field lists the set of methods supported by the
139 UA generating the message.
140
141 While the semantic of Allow header in non-dialog requests is unclear,
142 it's probably best not to include PUBLISH in Allow header for now
143 until we can find out how to customize the inclusion of methods in
144 Allow header for in-dialog vs out-dialog requests.
145
146 return pjsip_endpt_add_capability( endpt, NULL, PJSIP_H_ALLOW, NULL,
147 1, &pjsip_publish_method.name);
148 */
149 PJ_UNUSED_ARG(endpt);
150 return PJ_SUCCESS;
151 }
152
153
pjsip_publishc_create(pjsip_endpoint * endpt,const pjsip_publishc_opt * opt,void * token,pjsip_publishc_cb * cb,pjsip_publishc ** p_pubc)154 PJ_DEF(pj_status_t) pjsip_publishc_create( pjsip_endpoint *endpt,
155 const pjsip_publishc_opt *opt,
156 void *token,
157 pjsip_publishc_cb *cb,
158 pjsip_publishc **p_pubc)
159 {
160 pj_pool_t *pool;
161 pjsip_publishc *pubc;
162 pjsip_publishc_opt default_opt;
163 pj_status_t status;
164
165 /* Verify arguments. */
166 PJ_ASSERT_RETURN(endpt && cb && p_pubc, PJ_EINVAL);
167
168 pool = pjsip_endpt_create_pool(endpt, "pubc%p", 1024, 1024);
169 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
170
171 pubc = PJ_POOL_ZALLOC_T(pool, pjsip_publishc);
172
173 pubc->pool = pool;
174 pubc->endpt = endpt;
175 pubc->token = token;
176 pubc->cb = cb;
177 pubc->expires = PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED;
178
179 if (!opt) {
180 pjsip_publishc_opt_default(&default_opt);
181 opt = &default_opt;
182 }
183 pj_memcpy(&pubc->opt, opt, sizeof(*opt));
184 pj_list_init(&pubc->pending_reqs);
185 pj_list_init(&pubc->pending_reqs_empty);
186
187 status = pj_mutex_create_recursive(pubc->pool, "pubc%p", &pubc->mutex);
188 if (status != PJ_SUCCESS) {
189 pj_pool_release(pool);
190 return status;
191 }
192
193 status = pjsip_auth_clt_init(&pubc->auth_sess, endpt, pubc->pool, 0);
194 if (status != PJ_SUCCESS) {
195 pj_mutex_destroy(pubc->mutex);
196 pj_pool_release(pool);
197 return status;
198 }
199
200 pj_list_init(&pubc->route_set);
201 pj_list_init(&pubc->usr_hdr);
202
203 /* Done */
204 *p_pubc = pubc;
205 return PJ_SUCCESS;
206 }
207
208
pjsip_publishc_destroy(pjsip_publishc * pubc)209 PJ_DEF(pj_status_t) pjsip_publishc_destroy(pjsip_publishc *pubc)
210 {
211 PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
212
213 if (pubc->pending_tsx || pubc->in_callback) {
214 pubc->_delete_flag = 1;
215 pubc->cb = NULL;
216 } else {
217 /* Cancel existing timer, if any */
218 if (pubc->timer.id != 0) {
219 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
220 pubc->timer.id = 0;
221 }
222
223 if (pubc->mutex)
224 pj_mutex_destroy(pubc->mutex);
225
226 pjsip_auth_clt_deinit(&pubc->auth_sess);
227 pjsip_endpt_release_pool(pubc->endpt, pubc->pool);
228 }
229
230 return PJ_SUCCESS;
231 }
232
233
pjsip_publishc_get_pool(pjsip_publishc * pubc)234 PJ_DEF(pj_pool_t*) pjsip_publishc_get_pool(pjsip_publishc *pubc)
235 {
236 return pubc->pool;
237 }
238
set_expires(pjsip_publishc * pubc,pj_uint32_t expires)239 static void set_expires( pjsip_publishc *pubc, pj_uint32_t expires)
240 {
241 if (expires != pubc->expires &&
242 expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED)
243 {
244 pubc->expires_hdr = pjsip_expires_hdr_create(pubc->pool, expires);
245 } else {
246 pubc->expires_hdr = NULL;
247 }
248 }
249
250
pjsip_publishc_init(pjsip_publishc * pubc,const pj_str_t * event,const pj_str_t * target_uri,const pj_str_t * from_uri,const pj_str_t * to_uri,pj_uint32_t expires)251 PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc,
252 const pj_str_t *event,
253 const pj_str_t *target_uri,
254 const pj_str_t *from_uri,
255 const pj_str_t *to_uri,
256 pj_uint32_t expires)
257 {
258 pj_str_t tmp;
259
260 PJ_ASSERT_RETURN(pubc && event && target_uri && from_uri && to_uri &&
261 expires, PJ_EINVAL);
262
263 /* Copy event type */
264 pj_strdup_with_null(pubc->pool, &pubc->event, event);
265
266 /* Copy server URL. */
267 pj_strdup_with_null(pubc->pool, &pubc->str_target_uri, target_uri);
268
269 /* Set server URL. */
270 tmp = pubc->str_target_uri;
271 pubc->target_uri = pjsip_parse_uri( pubc->pool, tmp.ptr, tmp.slen, 0);
272 if (pubc->target_uri == NULL) {
273 return PJSIP_EINVALIDURI;
274 }
275
276 /* Set "From" header. */
277 pj_strdup_with_null(pubc->pool, &pubc->from_uri, from_uri);
278 tmp = pubc->from_uri;
279 pubc->from_hdr = pjsip_from_hdr_create(pubc->pool);
280 pubc->from_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen,
281 PJSIP_PARSE_URI_AS_NAMEADDR);
282 if (!pubc->from_hdr->uri) {
283 return PJSIP_EINVALIDURI;
284 }
285
286 /* Set "To" header. */
287 pj_strdup_with_null(pubc->pool, &tmp, to_uri);
288 pubc->to_hdr = pjsip_to_hdr_create(pubc->pool);
289 pubc->to_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen,
290 PJSIP_PARSE_URI_AS_NAMEADDR);
291 if (!pubc->to_hdr->uri) {
292 return PJSIP_EINVALIDURI;
293 }
294
295
296 /* Set "Expires" header, if required. */
297 set_expires( pubc, expires);
298
299 /* Set "Call-ID" header. */
300 pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool);
301 pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id);
302
303 /* Set "CSeq" header. */
304 pubc->cseq_hdr = pjsip_cseq_hdr_create(pubc->pool);
305 pubc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
306 pjsip_method_set( &pubc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
307
308 /* Done. */
309 return PJ_SUCCESS;
310 }
311
pjsip_publishc_set_credentials(pjsip_publishc * pubc,int count,const pjsip_cred_info cred[])312 PJ_DEF(pj_status_t) pjsip_publishc_set_credentials( pjsip_publishc *pubc,
313 int count,
314 const pjsip_cred_info cred[] )
315 {
316 PJ_ASSERT_RETURN(pubc && count && cred, PJ_EINVAL);
317 return pjsip_auth_clt_set_credentials(&pubc->auth_sess, count, cred);
318 }
319
pjsip_publishc_set_route_set(pjsip_publishc * pubc,const pjsip_route_hdr * route_set)320 PJ_DEF(pj_status_t) pjsip_publishc_set_route_set( pjsip_publishc *pubc,
321 const pjsip_route_hdr *route_set)
322 {
323 const pjsip_route_hdr *chdr;
324
325 PJ_ASSERT_RETURN(pubc && route_set, PJ_EINVAL);
326
327 pj_list_init(&pubc->route_set);
328
329 chdr = route_set->next;
330 while (chdr != route_set) {
331 pj_list_push_back(&pubc->route_set, pjsip_hdr_clone(pubc->pool, chdr));
332 chdr = chdr->next;
333 }
334
335 return PJ_SUCCESS;
336 }
337
pjsip_publishc_set_headers(pjsip_publishc * pubc,const pjsip_hdr * hdr_list)338 PJ_DEF(pj_status_t) pjsip_publishc_set_headers( pjsip_publishc *pubc,
339 const pjsip_hdr *hdr_list)
340 {
341 const pjsip_hdr *h;
342
343 PJ_ASSERT_RETURN(pubc && hdr_list, PJ_EINVAL);
344
345 pj_list_init(&pubc->usr_hdr);
346 h = hdr_list->next;
347 while (h != hdr_list) {
348 pj_list_push_back(&pubc->usr_hdr, pjsip_hdr_clone(pubc->pool, h));
349 h = h->next;
350 }
351
352 return PJ_SUCCESS;
353 }
354
pjsip_publishc_set_via_sent_by(pjsip_publishc * pubc,pjsip_host_port * via_addr,pjsip_transport * via_tp)355 PJ_DEF(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc,
356 pjsip_host_port *via_addr,
357 pjsip_transport *via_tp)
358 {
359 PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
360
361 if (!via_addr)
362 pj_bzero(&pubc->via_addr, sizeof(pubc->via_addr));
363 else {
364 if (pj_strcmp(&pubc->via_addr.host, &via_addr->host))
365 pj_strdup(pubc->pool, &pubc->via_addr.host, &via_addr->host);
366 pubc->via_addr.port = via_addr->port;
367 }
368 pubc->via_tp = via_tp;
369
370 return PJ_SUCCESS;
371 }
372
create_request(pjsip_publishc * pubc,pjsip_tx_data ** p_tdata)373 static pj_status_t create_request(pjsip_publishc *pubc,
374 pjsip_tx_data **p_tdata)
375 {
376 const pj_str_t STR_EVENT = { "Event", 5 };
377 pj_status_t status;
378 pjsip_generic_string_hdr *hdr;
379 pjsip_tx_data *tdata;
380
381 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
382
383 /* Create the request. */
384 status = pjsip_endpt_create_request_from_hdr( pubc->endpt,
385 &pjsip_publish_method,
386 pubc->target_uri,
387 pubc->from_hdr,
388 pubc->to_hdr,
389 NULL,
390 pubc->cid_hdr,
391 pubc->cseq_hdr->cseq,
392 NULL,
393 &tdata);
394 if (status != PJ_SUCCESS)
395 return status;
396
397 /* Add cached authorization headers. */
398 pjsip_auth_clt_init_req( &pubc->auth_sess, tdata );
399
400 /* Add Route headers from route set, ideally after Via header */
401 if (!pj_list_empty(&pubc->route_set)) {
402 pjsip_hdr *route_pos;
403 const pjsip_route_hdr *route;
404
405 route_pos = (pjsip_hdr*)
406 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
407 if (!route_pos)
408 route_pos = &tdata->msg->hdr;
409
410 route = pubc->route_set.next;
411 while (route != &pubc->route_set) {
412 pjsip_hdr *new_hdr = (pjsip_hdr*)
413 pjsip_hdr_shallow_clone(tdata->pool, route);
414 pj_list_insert_after(route_pos, new_hdr);
415 route_pos = new_hdr;
416 route = route->next;
417 }
418 }
419
420 /* Add Event header */
421 hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT,
422 &pubc->event);
423 if (hdr)
424 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
425
426
427 /* Add SIP-If-Match if we have etag */
428 if (pubc->etag.slen) {
429 const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
430
431 hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME,
432 &pubc->etag);
433 if (hdr)
434 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
435 }
436
437 /* Add user headers */
438 if (!pj_list_empty(&pubc->usr_hdr)) {
439 const pjsip_hdr *uhdr;
440
441 uhdr = pubc->usr_hdr.next;
442 while (uhdr != &pubc->usr_hdr) {
443 pjsip_hdr *new_hdr = (pjsip_hdr*)
444 pjsip_hdr_shallow_clone(tdata->pool, uhdr);
445 pjsip_msg_add_hdr(tdata->msg, new_hdr);
446 uhdr = uhdr->next;
447 }
448 }
449
450
451 /* Done. */
452 *p_tdata = tdata;
453 return PJ_SUCCESS;
454 }
455
456
pjsip_publishc_publish(pjsip_publishc * pubc,pj_bool_t auto_refresh,pjsip_tx_data ** p_tdata)457 PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc,
458 pj_bool_t auto_refresh,
459 pjsip_tx_data **p_tdata)
460 {
461 pj_status_t status;
462 pjsip_tx_data *tdata;
463
464 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
465
466 status = create_request(pubc, &tdata);
467 if (status != PJ_SUCCESS)
468 return status;
469
470 /* Add Expires header */
471 if (pubc->expires_hdr) {
472 pjsip_hdr *dup;
473
474 dup = (pjsip_hdr*)
475 pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr);
476 if (dup)
477 pjsip_msg_add_hdr(tdata->msg, dup);
478 }
479
480 /* Cancel existing timer */
481 if (pubc->timer.id != 0) {
482 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
483 pubc->timer.id = 0;
484 }
485
486 pubc->auto_refresh = auto_refresh;
487
488 /* Done */
489 *p_tdata = tdata;
490 return PJ_SUCCESS;
491 }
492
493
pjsip_publishc_unpublish(pjsip_publishc * pubc,pjsip_tx_data ** p_tdata)494 PJ_DEF(pj_status_t) pjsip_publishc_unpublish(pjsip_publishc *pubc,
495 pjsip_tx_data **p_tdata)
496 {
497 pjsip_tx_data *tdata;
498 pjsip_msg *msg;
499 pjsip_expires_hdr *expires;
500 pj_status_t status;
501
502 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
503
504 if (pubc->timer.id != 0) {
505 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
506 pubc->timer.id = 0;
507 }
508
509 status = create_request(pubc, &tdata);
510 if (status != PJ_SUCCESS)
511 return status;
512
513 msg = tdata->msg;
514
515 /* Add Expires:0 header */
516 expires = pjsip_expires_hdr_create(tdata->pool, 0);
517 pjsip_msg_add_hdr( msg, (pjsip_hdr*)expires);
518
519 *p_tdata = tdata;
520 return PJ_SUCCESS;
521 }
522
523
pjsip_publishc_update_expires(pjsip_publishc * pubc,pj_uint32_t expires)524 PJ_DEF(pj_status_t) pjsip_publishc_update_expires( pjsip_publishc *pubc,
525 pj_uint32_t expires )
526 {
527 PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
528 set_expires( pubc, expires );
529 return PJ_SUCCESS;
530 }
531
532
call_callback(pjsip_publishc * pubc,pj_status_t status,int st_code,const pj_str_t * reason,pjsip_rx_data * rdata,pj_uint32_t expiration)533 static void call_callback(pjsip_publishc *pubc, pj_status_t status,
534 int st_code, const pj_str_t *reason,
535 pjsip_rx_data *rdata, pj_uint32_t expiration)
536 {
537 struct pjsip_publishc_cbparam cbparam;
538
539
540 cbparam.pubc = pubc;
541 cbparam.token = pubc->token;
542 cbparam.status = status;
543 cbparam.code = st_code;
544 cbparam.reason = *reason;
545 cbparam.rdata = rdata;
546 cbparam.expiration = expiration;
547
548 (*pubc->cb)(&cbparam);
549 }
550
pubc_refresh_timer_cb(pj_timer_heap_t * timer_heap,struct pj_timer_entry * entry)551 static void pubc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
552 struct pj_timer_entry *entry)
553 {
554 pjsip_publishc *pubc = (pjsip_publishc*) entry->user_data;
555 pjsip_tx_data *tdata;
556 pj_status_t status;
557
558 PJ_UNUSED_ARG(timer_heap);
559
560 entry->id = 0;
561 status = pjsip_publishc_publish(pubc, 1, &tdata);
562 if (status != PJ_SUCCESS) {
563 char errmsg[PJ_ERR_MSG_SIZE];
564 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
565 call_callback(pubc, status, 400, &reason, NULL,
566 PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED);
567 return;
568 }
569
570 status = pjsip_publishc_send(pubc, tdata);
571 /* No need to call callback as it should have been called */
572 }
573
tsx_callback(void * token,pjsip_event * event)574 static void tsx_callback(void *token, pjsip_event *event)
575 {
576 pj_status_t status;
577 pjsip_publishc *pubc = (pjsip_publishc*) token;
578 pjsip_transaction *tsx = event->body.tsx_state.tsx;
579
580 /* Decrement pending transaction counter. */
581 pj_assert(pubc->pending_tsx > 0);
582 --pubc->pending_tsx;
583
584 /* Mark that we're in callback to prevent deletion (#1164) */
585 ++pubc->in_callback;
586
587 /* If publication data has been deleted by user then remove publication
588 * data from transaction's callback, and don't call callback.
589 */
590 if (pubc->_delete_flag) {
591
592 /* Nothing to do */
593 ;
594
595 } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
596 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
597 {
598 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
599 pjsip_tx_data *tdata;
600
601 status = pjsip_auth_clt_reinit_req( &pubc->auth_sess,
602 rdata,
603 tsx->last_tx,
604 &tdata);
605 if (status != PJ_SUCCESS) {
606 call_callback(pubc, status, tsx->status_code,
607 &rdata->msg_info.msg->line.status.reason,
608 rdata, PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED);
609 } else {
610 status = pjsip_publishc_send(pubc, tdata);
611 }
612
613 } else {
614 pjsip_rx_data *rdata;
615 pj_uint32_t expiration = PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED;
616
617 if (tsx->status_code/100 == 2) {
618 pjsip_msg *msg;
619 pjsip_expires_hdr *expires;
620 pjsip_generic_string_hdr *etag_hdr;
621 const pj_str_t STR_ETAG = { "SIP-ETag", 8 };
622
623 rdata = event->body.tsx_state.src.rdata;
624 msg = rdata->msg_info.msg;
625
626 /* Save ETag value */
627 etag_hdr = (pjsip_generic_string_hdr*)
628 pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL);
629 if (etag_hdr) {
630 pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue);
631 } else {
632 pubc->etag.slen = 0;
633 }
634
635 /* Update expires value */
636 expires = (pjsip_expires_hdr*)
637 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
638
639 if (pubc->auto_refresh && expires)
640 expiration = expires->ivalue;
641
642 if (pubc->auto_refresh && expiration!=0 &&
643 expiration!=PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED)
644 {
645 pj_time_val delay = { 0, 0};
646
647 /* Cancel existing timer, if any */
648 if (pubc->timer.id != 0) {
649 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
650 pubc->timer.id = 0;
651 }
652
653 delay.sec = expiration - DELAY_BEFORE_REFRESH;
654 if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED &&
655 delay.sec > (pj_int32_t)pubc->expires)
656 {
657 delay.sec = pubc->expires;
658 }
659 if (delay.sec < DELAY_BEFORE_REFRESH)
660 delay.sec = DELAY_BEFORE_REFRESH;
661 pubc->timer.cb = &pubc_refresh_timer_cb;
662 pubc->timer.id = REFRESH_TIMER;
663 pubc->timer.user_data = pubc;
664 pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay);
665 pj_gettimeofday(&pubc->last_refresh);
666 pubc->next_refresh = pubc->last_refresh;
667 pubc->next_refresh.sec += delay.sec;
668 }
669
670 } else {
671 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
672 event->body.tsx_state.src.rdata : NULL;
673 }
674
675
676 /* Call callback. */
677 // if (expiration == 0xFFFF) expiration = -1;
678
679 /* Temporarily increment pending_tsx to prevent callback from
680 * destroying pubc.
681 */
682 ++pubc->pending_tsx;
683
684 call_callback(pubc, PJ_SUCCESS, tsx->status_code,
685 (rdata ? &rdata->msg_info.msg->line.status.reason
686 : pjsip_get_status_text(tsx->status_code)),
687 rdata, expiration);
688
689 --pubc->pending_tsx;
690
691 /* If we have pending request(s), send them now */
692 pj_mutex_lock(pubc->mutex);
693 while (!pj_list_empty(&pubc->pending_reqs)) {
694 pending_publish *pp = pubc->pending_reqs.next;
695 pjsip_tx_data *tdata = pp->tdata;
696
697 /* Remove the request from pending request list,
698 * and keep the unused entry into pending_reqs_empty pool.
699 */
700 pj_list_erase(pp);
701 pj_list_push_back(&pubc->pending_reqs_empty, pp);
702
703 /* Add SIP-If-Match if we have etag and the request doesn't have
704 * one (http://trac.pjsip.org/repos/ticket/996)
705 */
706 if (pubc->etag.slen) {
707 const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
708 pjsip_generic_string_hdr *sim_hdr;
709
710 sim_hdr = (pjsip_generic_string_hdr*)
711 pjsip_msg_find_hdr_by_name(tdata->msg, &STR_HNAME, NULL);
712 if (!sim_hdr) {
713 /* Create the header */
714 sim_hdr = pjsip_generic_string_hdr_create(tdata->pool,
715 &STR_HNAME,
716 &pubc->etag);
717 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sim_hdr);
718
719 } else {
720 /* Update */
721 if (pj_strcmp(&pubc->etag, &sim_hdr->hvalue))
722 pj_strdup(tdata->pool, &sim_hdr->hvalue, &pubc->etag);
723 }
724 }
725
726 status = pjsip_publishc_send(pubc, tdata);
727 if (status == PJ_EPENDING) {
728 pj_assert(!"Not expected");
729 pjsip_tx_data_dec_ref(tdata);
730 } else if (status == PJ_SUCCESS) {
731 break;
732 }
733 }
734 pj_mutex_unlock(pubc->mutex);
735 }
736
737 /* No longer in callback. */
738 --pubc->in_callback;
739
740 /* Delete the record if user destroy pubc during the callback. */
741 if (pubc->_delete_flag && pubc->pending_tsx==0) {
742 pjsip_publishc_destroy(pubc);
743 }
744 }
745
746
pjsip_publishc_send(pjsip_publishc * pubc,pjsip_tx_data * tdata)747 PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc,
748 pjsip_tx_data *tdata)
749 {
750 pj_status_t status;
751 pjsip_cseq_hdr *cseq_hdr;
752 pj_uint32_t cseq;
753
754 PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL);
755
756 /* Make sure we don't have pending transaction. */
757 pj_mutex_lock(pubc->mutex);
758 if (pubc->pending_tsx) {
759 if (pubc->opt.queue_request) {
760 pending_publish *pp = NULL;
761 if (pj_list_empty(&pubc->pending_reqs_empty)) {
762 pp = PJ_POOL_ZALLOC_T(pubc->pool, pending_publish);
763 } else {
764 pp = pubc->pending_reqs_empty.next;
765 pj_list_erase(pp);
766 }
767 pp->tdata = tdata;
768 pj_list_push_back(&pubc->pending_reqs, pp);
769 pj_mutex_unlock(pubc->mutex);
770 PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another "
771 "transaction pending"));
772 return PJ_EPENDING;
773 } else {
774 pjsip_tx_data_dec_ref(tdata);
775 pj_mutex_unlock(pubc->mutex);
776 PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another "
777 "transaction pending"));
778 return PJ_EBUSY;
779 }
780 }
781 pj_mutex_unlock(pubc->mutex);
782
783 /* If via_addr is set, use this address for the Via header. */
784 if (pubc->via_addr.host.slen > 0) {
785 tdata->via_addr = pubc->via_addr;
786 tdata->via_tp = pubc->via_tp;
787 }
788
789 /* Invalidate message buffer. */
790 pjsip_tx_data_invalidate_msg(tdata);
791
792 /* Increment CSeq */
793 cseq = ++pubc->cseq_hdr->cseq;
794 cseq_hdr = (pjsip_cseq_hdr*)
795 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
796 cseq_hdr->cseq = cseq;
797
798 /* Increment pending transaction first, since transaction callback
799 * may be called even before send_request() returns!
800 */
801 ++pubc->pending_tsx;
802 status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc,
803 &tsx_callback);
804 if (status!=PJ_SUCCESS) {
805 // no need to decrement, callback has been called and it should
806 // already decremented pending_tsx. Decrementing this here may
807 // cause accessing freed memory location.
808 //--pubc->pending_tsx;
809 PJ_PERROR(4,(THIS_FILE, status, "Error sending request"));
810 }
811
812 return status;
813 }
814
815