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