blob: fcb412dd9a5b72a154b62ca44397bb48d3f187d9 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $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 */
47enum
48{
49 PJSIP_PUBLISH_METHOD = PJSIP_OTHER_METHOD,
50};
51
52const 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 */
62typedef 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 */
72struct 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
116PJ_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 */
126PJ_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
154PJ_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
209PJ_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 pjsip_endpt_release_pool(pubc->endpt, pubc->pool);
226 }
227
228 return PJ_SUCCESS;
229}
230
231
232PJ_DEF(pj_pool_t*) pjsip_publishc_get_pool(pjsip_publishc *pubc)
233{
234 return pubc->pool;
235}
236
237static void set_expires( pjsip_publishc *pubc, pj_uint32_t expires)
238{
239 if (expires != pubc->expires &&
240 expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED)
241 {
242 pubc->expires_hdr = pjsip_expires_hdr_create(pubc->pool, expires);
243 } else {
244 pubc->expires_hdr = NULL;
245 }
246}
247
248
249PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc,
250 const pj_str_t *event,
251 const pj_str_t *target_uri,
252 const pj_str_t *from_uri,
253 const pj_str_t *to_uri,
254 pj_uint32_t expires)
255{
256 pj_str_t tmp;
257
258 PJ_ASSERT_RETURN(pubc && event && target_uri && from_uri && to_uri &&
259 expires, PJ_EINVAL);
260
261 /* Copy event type */
262 pj_strdup_with_null(pubc->pool, &pubc->event, event);
263
264 /* Copy server URL. */
265 pj_strdup_with_null(pubc->pool, &pubc->str_target_uri, target_uri);
266
267 /* Set server URL. */
268 tmp = pubc->str_target_uri;
269 pubc->target_uri = pjsip_parse_uri( pubc->pool, tmp.ptr, tmp.slen, 0);
270 if (pubc->target_uri == NULL) {
271 return PJSIP_EINVALIDURI;
272 }
273
274 /* Set "From" header. */
275 pj_strdup_with_null(pubc->pool, &pubc->from_uri, from_uri);
276 tmp = pubc->from_uri;
277 pubc->from_hdr = pjsip_from_hdr_create(pubc->pool);
278 pubc->from_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen,
279 PJSIP_PARSE_URI_AS_NAMEADDR);
280 if (!pubc->from_hdr->uri) {
281 return PJSIP_EINVALIDURI;
282 }
283
284 /* Set "To" header. */
285 pj_strdup_with_null(pubc->pool, &tmp, to_uri);
286 pubc->to_hdr = pjsip_to_hdr_create(pubc->pool);
287 pubc->to_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen,
288 PJSIP_PARSE_URI_AS_NAMEADDR);
289 if (!pubc->to_hdr->uri) {
290 return PJSIP_EINVALIDURI;
291 }
292
293
294 /* Set "Expires" header, if required. */
295 set_expires( pubc, expires);
296
297 /* Set "Call-ID" header. */
298 pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool);
299 pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id);
300
301 /* Set "CSeq" header. */
302 pubc->cseq_hdr = pjsip_cseq_hdr_create(pubc->pool);
303 pubc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
304 pjsip_method_set( &pubc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
305
306 /* Done. */
307 return PJ_SUCCESS;
308}
309
310PJ_DEF(pj_status_t) pjsip_publishc_set_credentials( pjsip_publishc *pubc,
311 int count,
312 const pjsip_cred_info cred[] )
313{
314 PJ_ASSERT_RETURN(pubc && count && cred, PJ_EINVAL);
315 return pjsip_auth_clt_set_credentials(&pubc->auth_sess, count, cred);
316}
317
318PJ_DEF(pj_status_t) pjsip_publishc_set_route_set( pjsip_publishc *pubc,
319 const pjsip_route_hdr *route_set)
320{
321 const pjsip_route_hdr *chdr;
322
323 PJ_ASSERT_RETURN(pubc && route_set, PJ_EINVAL);
324
325 pj_list_init(&pubc->route_set);
326
327 chdr = route_set->next;
328 while (chdr != route_set) {
329 pj_list_push_back(&pubc->route_set, pjsip_hdr_clone(pubc->pool, chdr));
330 chdr = chdr->next;
331 }
332
333 return PJ_SUCCESS;
334}
335
336PJ_DEF(pj_status_t) pjsip_publishc_set_headers( pjsip_publishc *pubc,
337 const pjsip_hdr *hdr_list)
338{
339 const pjsip_hdr *h;
340
341 PJ_ASSERT_RETURN(pubc && hdr_list, PJ_EINVAL);
342
343 pj_list_init(&pubc->usr_hdr);
344 h = hdr_list->next;
345 while (h != hdr_list) {
346 pj_list_push_back(&pubc->usr_hdr, pjsip_hdr_clone(pubc->pool, h));
347 h = h->next;
348 }
349
350 return PJ_SUCCESS;
351}
352
353PJ_DEF(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc,
354 pjsip_host_port *via_addr,
355 pjsip_transport *via_tp)
356{
357 PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
358
359 if (!via_addr)
360 pj_bzero(&pubc->via_addr, sizeof(pubc->via_addr));
361 else {
362 if (pj_strcmp(&pubc->via_addr.host, &via_addr->host))
363 pj_strdup(pubc->pool, &pubc->via_addr.host, &via_addr->host);
364 pubc->via_addr.port = via_addr->port;
365 }
366 pubc->via_tp = via_tp;
367
368 return PJ_SUCCESS;
369}
370
371static pj_status_t create_request(pjsip_publishc *pubc,
372 pjsip_tx_data **p_tdata)
373{
374 const pj_str_t STR_EVENT = { "Event", 5 };
375 pj_status_t status;
376 pjsip_generic_string_hdr *hdr;
377 pjsip_tx_data *tdata;
378
379 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
380
381 /* Create the request. */
382 status = pjsip_endpt_create_request_from_hdr( pubc->endpt,
383 &pjsip_publish_method,
384 pubc->target_uri,
385 pubc->from_hdr,
386 pubc->to_hdr,
387 NULL,
388 pubc->cid_hdr,
389 pubc->cseq_hdr->cseq,
390 NULL,
391 &tdata);
392 if (status != PJ_SUCCESS)
393 return status;
394
395 /* Add cached authorization headers. */
396 pjsip_auth_clt_init_req( &pubc->auth_sess, tdata );
397
398 /* Add Route headers from route set, ideally after Via header */
399 if (!pj_list_empty(&pubc->route_set)) {
400 pjsip_hdr *route_pos;
401 const pjsip_route_hdr *route;
402
403 route_pos = (pjsip_hdr*)
404 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
405 if (!route_pos)
406 route_pos = &tdata->msg->hdr;
407
408 route = pubc->route_set.next;
409 while (route != &pubc->route_set) {
410 pjsip_hdr *new_hdr = (pjsip_hdr*)
411 pjsip_hdr_shallow_clone(tdata->pool, route);
412 pj_list_insert_after(route_pos, new_hdr);
413 route_pos = new_hdr;
414 route = route->next;
415 }
416 }
417
418 /* Add Event header */
419 hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT,
420 &pubc->event);
421 if (hdr)
422 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
423
424
425 /* Add SIP-If-Match if we have etag */
426 if (pubc->etag.slen) {
427 const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
428
429 hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME,
430 &pubc->etag);
431 if (hdr)
432 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
433 }
434
435 /* Add user headers */
436 if (!pj_list_empty(&pubc->usr_hdr)) {
437 const pjsip_hdr *hdr;
438
439 hdr = pubc->usr_hdr.next;
440 while (hdr != &pubc->usr_hdr) {
441 pjsip_hdr *new_hdr = (pjsip_hdr*)
442 pjsip_hdr_shallow_clone(tdata->pool, hdr);
443 pjsip_msg_add_hdr(tdata->msg, new_hdr);
444 hdr = hdr->next;
445 }
446 }
447
448
449 /* Done. */
450 *p_tdata = tdata;
451 return PJ_SUCCESS;
452}
453
454
455PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc,
456 pj_bool_t auto_refresh,
457 pjsip_tx_data **p_tdata)
458{
459 pj_status_t status;
460 pjsip_tx_data *tdata;
461
462 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
463
464 status = create_request(pubc, &tdata);
465 if (status != PJ_SUCCESS)
466 return status;
467
468 /* Add Expires header */
469 if (pubc->expires_hdr) {
470 pjsip_hdr *dup;
471
472 dup = (pjsip_hdr*)
473 pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr);
474 if (dup)
475 pjsip_msg_add_hdr(tdata->msg, dup);
476 }
477
478 /* Cancel existing timer */
479 if (pubc->timer.id != 0) {
480 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
481 pubc->timer.id = 0;
482 }
483
484 pubc->auto_refresh = auto_refresh;
485
486 /* Done */
487 *p_tdata = tdata;
488 return PJ_SUCCESS;
489}
490
491
492PJ_DEF(pj_status_t) pjsip_publishc_unpublish(pjsip_publishc *pubc,
493 pjsip_tx_data **p_tdata)
494{
495 pjsip_tx_data *tdata;
496 pjsip_msg *msg;
497 pjsip_expires_hdr *expires;
498 pj_status_t status;
499
500 PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);
501
502 if (pubc->timer.id != 0) {
503 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
504 pubc->timer.id = 0;
505 }
506
507 status = create_request(pubc, &tdata);
508 if (status != PJ_SUCCESS)
509 return status;
510
511 msg = tdata->msg;
512
513 /* Add Expires:0 header */
514 expires = pjsip_expires_hdr_create(tdata->pool, 0);
515 pjsip_msg_add_hdr( msg, (pjsip_hdr*)expires);
516
517 *p_tdata = tdata;
518 return PJ_SUCCESS;
519}
520
521
522PJ_DEF(pj_status_t) pjsip_publishc_update_expires( pjsip_publishc *pubc,
523 pj_uint32_t expires )
524{
525 PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
526 set_expires( pubc, expires );
527 return PJ_SUCCESS;
528}
529
530
531static void call_callback(pjsip_publishc *pubc, pj_status_t status,
532 int st_code, const pj_str_t *reason,
533 pjsip_rx_data *rdata, pj_int32_t expiration)
534{
535 struct pjsip_publishc_cbparam cbparam;
536
537
538 cbparam.pubc = pubc;
539 cbparam.token = pubc->token;
540 cbparam.status = status;
541 cbparam.code = st_code;
542 cbparam.reason = *reason;
543 cbparam.rdata = rdata;
544 cbparam.expiration = expiration;
545
546 (*pubc->cb)(&cbparam);
547}
548
549static void pubc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
550 struct pj_timer_entry *entry)
551{
552 pjsip_publishc *pubc = (pjsip_publishc*) entry->user_data;
553 pjsip_tx_data *tdata;
554 pj_status_t status;
555
556 PJ_UNUSED_ARG(timer_heap);
557
558 entry->id = 0;
559 status = pjsip_publishc_publish(pubc, 1, &tdata);
560 if (status != PJ_SUCCESS) {
561 char errmsg[PJ_ERR_MSG_SIZE];
562 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
563 call_callback(pubc, status, 400, &reason, NULL, -1);
564 return;
565 }
566
567 status = pjsip_publishc_send(pubc, tdata);
568 /* No need to call callback as it should have been called */
569}
570
571static void tsx_callback(void *token, pjsip_event *event)
572{
573 pj_status_t status;
574 pjsip_publishc *pubc = (pjsip_publishc*) token;
575 pjsip_transaction *tsx = event->body.tsx_state.tsx;
576
577 /* Decrement pending transaction counter. */
578 pj_assert(pubc->pending_tsx > 0);
579 --pubc->pending_tsx;
580
581 /* Mark that we're in callback to prevent deletion (#1164) */
582 ++pubc->in_callback;
583
584 /* If publication data has been deleted by user then remove publication
585 * data from transaction's callback, and don't call callback.
586 */
587 if (pubc->_delete_flag) {
588
589 /* Nothing to do */
590 ;
591
592 } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
593 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
594 {
595 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
596 pjsip_tx_data *tdata;
597
598 status = pjsip_auth_clt_reinit_req( &pubc->auth_sess,
599 rdata,
600 tsx->last_tx,
601 &tdata);
602 if (status != PJ_SUCCESS) {
603 call_callback(pubc, status, tsx->status_code,
604 &rdata->msg_info.msg->line.status.reason,
605 rdata, -1);
606 } else {
607 status = pjsip_publishc_send(pubc, tdata);
608 }
609
610 } else {
611 pjsip_rx_data *rdata;
612 pj_int32_t expiration = 0xFFFF;
613
614 if (tsx->status_code/100 == 2) {
615 pjsip_msg *msg;
616 pjsip_expires_hdr *expires;
617 pjsip_generic_string_hdr *etag_hdr;
618 const pj_str_t STR_ETAG = { "SIP-ETag", 8 };
619
620 rdata = event->body.tsx_state.src.rdata;
621 msg = rdata->msg_info.msg;
622
623 /* Save ETag value */
624 etag_hdr = (pjsip_generic_string_hdr*)
625 pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL);
626 if (etag_hdr) {
627 pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue);
628 } else {
629 pubc->etag.slen = 0;
630 }
631
632 /* Update expires value */
633 expires = (pjsip_expires_hdr*)
634 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
635
636 if (pubc->auto_refresh && expires)
637 expiration = expires->ivalue;
638
639 if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) {
640 pj_time_val delay = { 0, 0};
641
642 /* Cancel existing timer, if any */
643 if (pubc->timer.id != 0) {
644 pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
645 pubc->timer.id = 0;
646 }
647
648 delay.sec = expiration - DELAY_BEFORE_REFRESH;
649 if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED &&
650 delay.sec > (pj_int32_t)pubc->expires)
651 {
652 delay.sec = pubc->expires;
653 }
654 if (delay.sec < DELAY_BEFORE_REFRESH)
655 delay.sec = DELAY_BEFORE_REFRESH;
656 pubc->timer.cb = &pubc_refresh_timer_cb;
657 pubc->timer.id = REFRESH_TIMER;
658 pubc->timer.user_data = pubc;
659 pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay);
660 pj_gettimeofday(&pubc->last_refresh);
661 pubc->next_refresh = pubc->last_refresh;
662 pubc->next_refresh.sec += delay.sec;
663 }
664
665 } else {
666 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
667 event->body.tsx_state.src.rdata : NULL;
668 }
669
670
671 /* Call callback. */
672 if (expiration == 0xFFFF) expiration = -1;
673
674 /* Temporarily increment pending_tsx to prevent callback from
675 * destroying pubc.
676 */
677 ++pubc->pending_tsx;
678
679 call_callback(pubc, PJ_SUCCESS, tsx->status_code,
680 (rdata ? &rdata->msg_info.msg->line.status.reason
681 : pjsip_get_status_text(tsx->status_code)),
682 rdata, expiration);
683
684 --pubc->pending_tsx;
685
686 /* If we have pending request(s), send them now */
687 pj_mutex_lock(pubc->mutex);
688 while (!pj_list_empty(&pubc->pending_reqs)) {
689 pending_publish *pp = pubc->pending_reqs.next;
690 pjsip_tx_data *tdata = pp->tdata;
691
692 /* Remove the request from pending request list,
693 * and keep the unused entry into pending_reqs_empty pool.
694 */
695 pj_list_erase(pp);
696 pj_list_push_back(&pubc->pending_reqs_empty, pp);
697
698 /* Add SIP-If-Match if we have etag and the request doesn't have
699 * one (http://trac.pjsip.org/repos/ticket/996)
700 */
701 if (pubc->etag.slen) {
702 const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };
703 pjsip_generic_string_hdr *sim_hdr;
704
705 sim_hdr = (pjsip_generic_string_hdr*)
706 pjsip_msg_find_hdr_by_name(tdata->msg, &STR_HNAME, NULL);
707 if (!sim_hdr) {
708 /* Create the header */
709 sim_hdr = pjsip_generic_string_hdr_create(tdata->pool,
710 &STR_HNAME,
711 &pubc->etag);
712 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sim_hdr);
713
714 } else {
715 /* Update */
716 if (pj_strcmp(&pubc->etag, &sim_hdr->hvalue))
717 pj_strdup(tdata->pool, &sim_hdr->hvalue, &pubc->etag);
718 }
719 }
720
721 status = pjsip_publishc_send(pubc, tdata);
722 if (status == PJ_EPENDING) {
723 pj_assert(!"Not expected");
724 pjsip_tx_data_dec_ref(tdata);
725 } else if (status == PJ_SUCCESS) {
726 break;
727 }
728 }
729 pj_mutex_unlock(pubc->mutex);
730 }
731
732 /* No longer in callback. */
733 --pubc->in_callback;
734
735 /* Delete the record if user destroy pubc during the callback. */
736 if (pubc->_delete_flag && pubc->pending_tsx==0) {
737 pjsip_publishc_destroy(pubc);
738 }
739}
740
741
742PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc,
743 pjsip_tx_data *tdata)
744{
745 pj_status_t status;
746 pjsip_cseq_hdr *cseq_hdr;
747 pj_uint32_t cseq;
748
749 PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL);
750
751 /* Make sure we don't have pending transaction. */
752 pj_mutex_lock(pubc->mutex);
753 if (pubc->pending_tsx) {
754 if (pubc->opt.queue_request) {
755 pending_publish *pp = NULL;
756 if (pj_list_empty(&pubc->pending_reqs_empty)) {
757 pp = PJ_POOL_ZALLOC_T(pubc->pool, pending_publish);
758 } else {
759 pp = pubc->pending_reqs_empty.next;
760 pj_list_erase(pp);
761 }
762 pp->tdata = tdata;
763 pj_list_push_back(&pubc->pending_reqs, pp);
764 pj_mutex_unlock(pubc->mutex);
765 PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another "
766 "transaction pending"));
767 return PJ_EPENDING;
768 } else {
769 pjsip_tx_data_dec_ref(tdata);
770 pj_mutex_unlock(pubc->mutex);
771 PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another "
772 "transaction pending"));
773 return PJ_EBUSY;
774 }
775 }
776 pj_mutex_unlock(pubc->mutex);
777
778 /* If via_addr is set, use this address for the Via header. */
779 if (pubc->via_addr.host.slen > 0) {
780 tdata->via_addr = pubc->via_addr;
781 tdata->via_tp = pubc->via_tp;
782 }
783
784 /* Invalidate message buffer. */
785 pjsip_tx_data_invalidate_msg(tdata);
786
787 /* Increment CSeq */
788 cseq = ++pubc->cseq_hdr->cseq;
789 cseq_hdr = (pjsip_cseq_hdr*)
790 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
791 cseq_hdr->cseq = cseq;
792
793 /* Increment pending transaction first, since transaction callback
794 * may be called even before send_request() returns!
795 */
796 ++pubc->pending_tsx;
797 status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc,
798 &tsx_callback);
799 if (status!=PJ_SUCCESS) {
800 // no need to decrement, callback has been called and it should
801 // already decremented pending_tsx. Decrementing this here may
802 // cause accessing freed memory location.
803 //--pubc->pending_tsx;
804 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
805 }
806
807 return status;
808}
809