blob: ca907bab08fcd8e7a9c8caf06fe45cb2996197bb [file] [log] [blame]
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001/* $Id$ */
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002/*
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-ua/sip_regc.h>
21#include <pjsip/sip_endpoint.h>
22#include <pjsip/sip_parser.h>
23#include <pjsip/sip_module.h>
24#include <pjsip/sip_transaction.h>
25#include <pjsip/sip_event.h>
26#include <pjsip/sip_util.h>
27#include <pjsip/sip_auth_msg.h>
28#include <pjsip/sip_errno.h>
29#include <pj/assert.h>
30#include <pj/guid.h>
31#include <pj/lock.h>
32#include <pj/os.h>
33#include <pj/pool.h>
34#include <pj/log.h>
35#include <pj/rand.h>
36#include <pj/string.h>
37
38
39#define REFRESH_TIMER 1
40#define DELAY_BEFORE_REFRESH PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH
41#define THIS_FILE "sip_reg.c"
42
43/* Outgoing transaction timeout when server sends 100 but never replies
44 * with final response. Value is in MILISECONDS!
45 */
46#define REGC_TSX_TIMEOUT 33000
47
48enum { NOEXP = 0x1FFFFFFF };
49
50static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 };
51
52
53/* Current/pending operation */
54enum regc_op
55{
56 REGC_IDLE,
57 REGC_REGISTERING,
58 REGC_UNREGISTERING
59};
60
61/**
62 * SIP client registration structure.
63 */
64struct pjsip_regc
65{
66 pj_pool_t *pool;
67 pjsip_endpoint *endpt;
68 pj_lock_t *lock;
69 pj_bool_t _delete_flag;
70 pj_bool_t has_tsx;
71 pj_atomic_t *busy_ctr;
72 enum regc_op current_op;
73
74 pj_bool_t add_xuid_param;
75
76 void *token;
77 pjsip_regc_cb *cb;
Alexandre Lision94f06ba2013-12-09 16:28:33 -050078 pjsip_regc_tsx_cb *tsx_cb;
Tristan Matthews0a329cc2013-07-17 13:20:14 -040079
80 pj_str_t str_srv_url;
81 pjsip_uri *srv_url;
82 pjsip_cid_hdr *cid_hdr;
83 pjsip_cseq_hdr *cseq_hdr;
84 pj_str_t from_uri;
85 pjsip_from_hdr *from_hdr;
86 pjsip_to_hdr *to_hdr;
87 pjsip_contact_hdr contact_hdr_list;
88 pjsip_contact_hdr removed_contact_hdr_list;
89 pjsip_expires_hdr *expires_hdr;
90 pj_uint32_t expires;
91 pj_uint32_t delay_before_refresh;
92 pjsip_route_hdr route_set;
93 pjsip_hdr hdr_list;
94 pjsip_host_port via_addr;
95 const void *via_tp;
96
97 /* Authorization sessions. */
98 pjsip_auth_clt_sess auth_sess;
99
100 /* Auto refresh registration. */
101 pj_bool_t auto_reg;
102 pj_time_val last_reg;
103 pj_time_val next_reg;
104 pj_timer_entry timer;
105
106 /* Transport selector */
107 pjsip_tpselector tp_sel;
108
109 /* Last transport used. We acquire the transport to keep
110 * it open.
111 */
112 pjsip_transport *last_transport;
113};
114
115
116PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
117 pjsip_regc_cb *cb,
118 pjsip_regc **p_regc)
119{
120 pj_pool_t *pool;
121 pjsip_regc *regc;
122 pj_status_t status;
123
124 /* Verify arguments. */
125 PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
126
127 pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
128 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
129
130 regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
131
132 regc->pool = pool;
133 regc->endpt = endpt;
134 regc->token = token;
135 regc->cb = cb;
136 regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
137 regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param;
138
139 status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
140 &regc->lock);
141 if (status != PJ_SUCCESS) {
142 pj_pool_release(pool);
143 return status;
144 }
145
146 status = pj_atomic_create(pool, 0, &regc->busy_ctr);
147 if (status != PJ_SUCCESS) {
148 pj_lock_destroy(regc->lock);
149 pj_pool_release(pool);
150 return status;
151 }
152
153 status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0);
154 if (status != PJ_SUCCESS)
155 return status;
156
157 pj_list_init(&regc->route_set);
158 pj_list_init(&regc->hdr_list);
159 pj_list_init(&regc->contact_hdr_list);
160 pj_list_init(&regc->removed_contact_hdr_list);
161
162 /* Done */
163 *p_regc = regc;
164 return PJ_SUCCESS;
165}
166
167
168PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
169{
170 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
171
172 pj_lock_acquire(regc->lock);
173 if (regc->has_tsx || pj_atomic_get(regc->busy_ctr) != 0) {
174 regc->_delete_flag = 1;
175 regc->cb = NULL;
176 pj_lock_release(regc->lock);
177 } else {
178 pjsip_tpselector_dec_ref(&regc->tp_sel);
179 if (regc->last_transport) {
180 pjsip_transport_dec_ref(regc->last_transport);
181 regc->last_transport = NULL;
182 }
183 if (regc->timer.id != 0) {
184 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
185 regc->timer.id = 0;
186 }
187 pj_atomic_destroy(regc->busy_ctr);
188 pj_lock_release(regc->lock);
189 pj_lock_destroy(regc->lock);
190 regc->lock = NULL;
191 pjsip_endpt_release_pool(regc->endpt, regc->pool);
192 }
193
194 return PJ_SUCCESS;
195}
196
197
198PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
199 pjsip_regc_info *info )
200{
201 PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
202
203 pj_lock_acquire(regc->lock);
204
205 info->server_uri = regc->str_srv_url;
206 info->client_uri = regc->from_uri;
207 info->is_busy = (pj_atomic_get(regc->busy_ctr) || regc->has_tsx);
208 info->auto_reg = regc->auto_reg;
209 info->interval = regc->expires;
210 info->transport = regc->last_transport;
211
212 if (regc->has_tsx)
213 info->next_reg = 0;
214 else if (regc->auto_reg == 0)
215 info->next_reg = 0;
216 else if (regc->expires < 0)
217 info->next_reg = regc->expires;
218 else {
219 pj_time_val now, next_reg;
220
221 next_reg = regc->next_reg;
222 pj_gettimeofday(&now);
223 PJ_TIME_VAL_SUB(next_reg, now);
224 info->next_reg = next_reg.sec;
225 }
226
227 pj_lock_release(regc->lock);
228
229 return PJ_SUCCESS;
230}
231
232
233PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
234{
235 return regc->pool;
236}
237
238static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
239{
240 if (expires != regc->expires) {
241 regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
242 } else {
243 regc->expires_hdr = NULL;
244 }
245}
246
247
248static pj_status_t set_contact( pjsip_regc *regc,
249 int contact_cnt,
250 const pj_str_t contact[] )
251{
252 const pj_str_t CONTACT = { "Contact", 7 };
253 pjsip_contact_hdr *h;
254 int i;
255
256 /* Save existing contact list to removed_contact_hdr_list and
257 * clear contact_hdr_list.
258 */
259 pj_list_merge_last(&regc->removed_contact_hdr_list,
260 &regc->contact_hdr_list);
261
262 /* Set the expiration of Contacts in to removed_contact_hdr_list
263 * zero.
264 */
265 h = regc->removed_contact_hdr_list.next;
266 while (h != &regc->removed_contact_hdr_list) {
267 h->expires = 0;
268 h = h->next;
269 }
270
271 /* Process new contacts */
272 for (i=0; i<contact_cnt; ++i) {
273 pjsip_contact_hdr *hdr;
274 pj_str_t tmp;
275
276 pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
277 hdr = (pjsip_contact_hdr*)
278 pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
279 if (hdr == NULL) {
280 PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"",
281 (int)tmp.slen, tmp.ptr));
282 return PJSIP_EINVALIDURI;
283 }
284
285 /* Find the new contact in old contact list. If found, remove
286 * the old header from the old header list.
287 */
288 h = regc->removed_contact_hdr_list.next;
289 while (h != &regc->removed_contact_hdr_list) {
290 int rc;
291
292 rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
293 h->uri, hdr->uri);
294 if (rc == 0) {
295 /* Match */
296 pj_list_erase(h);
297 break;
298 }
299
300 h = h->next;
301 }
302
303 /* If add_xuid_param option is enabled and Contact URI is sip/sips,
304 * add xuid parameter to assist matching the Contact URI in the
305 * REGISTER response later.
306 */
307 if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
308 PJSIP_URI_SCHEME_IS_SIPS(hdr->uri)))
309 {
310 pjsip_param *xuid_param;
311 pjsip_sip_uri *sip_uri;
312
313 xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
314 xuid_param->name = XUID_PARAM_NAME;
315 pj_create_unique_string(regc->pool, &xuid_param->value);
316
317 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
318 pj_list_push_back(&sip_uri->other_param, xuid_param);
319 }
320
321 pj_list_push_back(&regc->contact_hdr_list, hdr);
322 }
323
324 return PJ_SUCCESS;
325}
326
327
328PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
329 const pj_str_t *srv_url,
330 const pj_str_t *from_url,
331 const pj_str_t *to_url,
332 int contact_cnt,
333 const pj_str_t contact[],
334 pj_uint32_t expires)
335{
336 pj_str_t tmp;
337 pj_status_t status;
338
339 PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
340 expires, PJ_EINVAL);
341
342 /* Copy server URL. */
343 pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
344
345 /* Set server URL. */
346 tmp = regc->str_srv_url;
347 regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
348 if (regc->srv_url == NULL) {
349 return PJSIP_EINVALIDURI;
350 }
351
352 /* Set "From" header. */
353 pj_strdup_with_null(regc->pool, &regc->from_uri, from_url);
354 tmp = regc->from_uri;
355 regc->from_hdr = pjsip_from_hdr_create(regc->pool);
356 regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
357 PJSIP_PARSE_URI_AS_NAMEADDR);
358 if (!regc->from_hdr->uri) {
359 PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
360 from_url->slen, from_url->ptr));
361 return PJSIP_EINVALIDURI;
362 }
363
364 /* Set "To" header. */
365 pj_strdup_with_null(regc->pool, &tmp, to_url);
366 regc->to_hdr = pjsip_to_hdr_create(regc->pool);
367 regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
368 PJSIP_PARSE_URI_AS_NAMEADDR);
369 if (!regc->to_hdr->uri) {
370 PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
371 return PJSIP_EINVALIDURI;
372 }
373
374
375 /* Set "Contact" header. */
376 status = set_contact( regc, contact_cnt, contact);
377 if (status != PJ_SUCCESS)
378 return status;
379
380 /* Set "Expires" header, if required. */
381 set_expires( regc, expires);
382 regc->delay_before_refresh = DELAY_BEFORE_REFRESH;
383
384 /* Set "Call-ID" header. */
385 regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
386 pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
387
388 /* Set "CSeq" header. */
389 regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
390 regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
391 pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
392
393 /* Done. */
394 return PJ_SUCCESS;
395}
396
397PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
398 int count,
399 const pjsip_cred_info cred[] )
400{
401 PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
402 return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
403}
404
405PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
406 const pjsip_auth_clt_pref *pref)
407{
408 PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
409 return pjsip_auth_clt_set_prefs(&regc->auth_sess, pref);
410}
411
412PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
413 const pjsip_route_hdr *route_set)
414{
415 const pjsip_route_hdr *chdr;
416
417 PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
418
419 pj_list_init(&regc->route_set);
420
421 chdr = route_set->next;
422 while (chdr != route_set) {
423 pj_list_push_back(&regc->route_set, pjsip_hdr_clone(regc->pool, chdr));
424 chdr = chdr->next;
425 }
426
427 return PJ_SUCCESS;
428}
429
430
431/*
432 * Bind client registration to a specific transport/listener.
433 */
434PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
435 const pjsip_tpselector *sel)
436{
437 PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
438
439 pjsip_tpselector_dec_ref(&regc->tp_sel);
440 pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
441 pjsip_tpselector_add_ref(&regc->tp_sel);
442
443 return PJ_SUCCESS;
444}
445
446/* Release transport */
447PJ_DEF(pj_status_t) pjsip_regc_release_transport(pjsip_regc *regc)
448{
449 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
450 if (regc->last_transport) {
451 pjsip_transport_dec_ref(regc->last_transport);
452 regc->last_transport = NULL;
453 }
454 return PJ_SUCCESS;
455}
456
457
458PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
459 const pjsip_hdr *hdr_list)
460{
461 const pjsip_hdr *hdr;
462
463 PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
464
465 //This is "add" operation, so don't remove headers.
466 //pj_list_init(&regc->hdr_list);
467
468 hdr = hdr_list->next;
469 while (hdr != hdr_list) {
470 pj_list_push_back(&regc->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
471 hdr = hdr->next;
472 }
473
474 return PJ_SUCCESS;
475}
476
477static pj_status_t create_request(pjsip_regc *regc,
478 pjsip_tx_data **p_tdata)
479{
480 pj_status_t status;
481 pjsip_tx_data *tdata;
482
483 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
484
485 /* Create the request. */
486 status = pjsip_endpt_create_request_from_hdr( regc->endpt,
487 pjsip_get_register_method(),
488 regc->srv_url,
489 regc->from_hdr,
490 regc->to_hdr,
491 NULL,
492 regc->cid_hdr,
493 regc->cseq_hdr->cseq,
494 NULL,
495 &tdata);
496 if (status != PJ_SUCCESS)
497 return status;
498
499 /* Add cached authorization headers. */
500 pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
501
502 /* Add Route headers from route set, ideally after Via header */
503 if (!pj_list_empty(&regc->route_set)) {
504 pjsip_hdr *route_pos;
505 const pjsip_route_hdr *route;
506
507 route_pos = (pjsip_hdr*)
508 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
509 if (!route_pos)
510 route_pos = &tdata->msg->hdr;
511
512 route = regc->route_set.next;
513 while (route != &regc->route_set) {
514 pjsip_hdr *new_hdr = (pjsip_hdr*)
515 pjsip_hdr_shallow_clone(tdata->pool, route);
516 pj_list_insert_after(route_pos, new_hdr);
517 route_pos = new_hdr;
518 route = route->next;
519 }
520 }
521
522 /* Add additional request headers */
523 if (!pj_list_empty(&regc->hdr_list)) {
524 const pjsip_hdr *hdr;
525
526 hdr = regc->hdr_list.next;
527 while (hdr != &regc->hdr_list) {
528 pjsip_hdr *new_hdr = (pjsip_hdr*)
529 pjsip_hdr_shallow_clone(tdata->pool, hdr);
530 pjsip_msg_add_hdr(tdata->msg, new_hdr);
531 hdr = hdr->next;
532 }
533 }
534
535 /* Done. */
536 *p_tdata = tdata;
537 return PJ_SUCCESS;
538}
539
540
541PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
542 pjsip_tx_data **p_tdata)
543{
544 pjsip_msg *msg;
545 pjsip_contact_hdr *hdr;
546 const pjsip_hdr *h_allow;
547 pj_status_t status;
548 pjsip_tx_data *tdata;
549
550 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
551
552 pj_lock_acquire(regc->lock);
553
554 status = create_request(regc, &tdata);
555 if (status != PJ_SUCCESS) {
556 pj_lock_release(regc->lock);
557 return status;
558 }
559
560 msg = tdata->msg;
561
562 /* Add Contact headers. */
563 hdr = regc->contact_hdr_list.next;
564 while (hdr != &regc->contact_hdr_list) {
565 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
566 pjsip_hdr_shallow_clone(tdata->pool, hdr));
567 hdr = hdr->next;
568 }
569
570 /* Also add bindings which are to be removed */
571 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
572 hdr = regc->removed_contact_hdr_list.next;
573 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
574 pjsip_hdr_clone(tdata->pool, hdr));
575 pj_list_erase(hdr);
576 }
577
578
579 if (regc->expires_hdr)
580 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
581 pjsip_hdr_shallow_clone(tdata->pool,
582 regc->expires_hdr));
583
584 if (regc->timer.id != 0) {
585 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
586 regc->timer.id = 0;
587 }
588
589 /* Add Allow header (http://trac.pjsip.org/repos/ticket/1039) */
590 h_allow = pjsip_endpt_get_capability(regc->endpt, PJSIP_H_ALLOW, NULL);
591 if (h_allow) {
592 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
593 pjsip_hdr_shallow_clone(tdata->pool, h_allow));
594
595 }
596
597 regc->auto_reg = autoreg;
598
599 pj_lock_release(regc->lock);
600
601 /* Done */
602 *p_tdata = tdata;
603 return PJ_SUCCESS;
604}
605
606
607PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
608 pjsip_tx_data **p_tdata)
609{
610 pjsip_tx_data *tdata;
611 pjsip_msg *msg;
612 pjsip_hdr *hdr;
613 pj_status_t status;
614
615 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
616
617 pj_lock_acquire(regc->lock);
618
619 if (regc->timer.id != 0) {
620 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
621 regc->timer.id = 0;
622 }
623
624 status = create_request(regc, &tdata);
625 if (status != PJ_SUCCESS) {
626 pj_lock_release(regc->lock);
627 return status;
628 }
629
630 msg = tdata->msg;
631
632 /* Add Contact headers. */
633 hdr = (pjsip_hdr*)regc->contact_hdr_list.next;
634 while ((void*)hdr != (void*)&regc->contact_hdr_list) {
635 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
636 pjsip_hdr_shallow_clone(tdata->pool, hdr));
637 hdr = hdr->next;
638 }
639
640 /* Also add bindings which are to be removed */
641 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
642 hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next;
643 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
644 pjsip_hdr_clone(tdata->pool, hdr));
645 pj_list_erase(hdr);
646 }
647
648 /* Add Expires:0 header */
649 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
650 pjsip_msg_add_hdr(msg, hdr);
651
652 pj_lock_release(regc->lock);
653
654 *p_tdata = tdata;
655 return PJ_SUCCESS;
656}
657
658PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
659 pjsip_tx_data **p_tdata)
660{
661 pjsip_tx_data *tdata;
662 pjsip_contact_hdr *hcontact;
663 pjsip_hdr *hdr;
664 pjsip_msg *msg;
665 pj_status_t status;
666
667 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
668
669 pj_lock_acquire(regc->lock);
670
671 if (regc->timer.id != 0) {
672 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
673 regc->timer.id = 0;
674 }
675
676 status = create_request(regc, &tdata);
677 if (status != PJ_SUCCESS) {
678 pj_lock_release(regc->lock);
679 return status;
680 }
681
682 msg = tdata->msg;
683
684 /* Clear removed_contact_hdr_list */
685 pj_list_init(&regc->removed_contact_hdr_list);
686
687 /* Add Contact:* header */
688 hcontact = pjsip_contact_hdr_create(tdata->pool);
689 hcontact->star = 1;
690 pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact);
691
692 /* Add Expires:0 header */
693 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
694 pjsip_msg_add_hdr(msg, hdr);
695
696 pj_lock_release(regc->lock);
697
698 *p_tdata = tdata;
699 return PJ_SUCCESS;
700}
701
702
703PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
704 int contact_cnt,
705 const pj_str_t contact[] )
706{
707 pj_status_t status;
708
709 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
710
711 pj_lock_acquire(regc->lock);
712 status = set_contact( regc, contact_cnt, contact );
713 pj_lock_release(regc->lock);
714
715 return status;
716}
717
718
719PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
720 pj_uint32_t expires )
721{
722 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
723
724 pj_lock_acquire(regc->lock);
725 set_expires( regc, expires );
726 pj_lock_release(regc->lock);
727
728 return PJ_SUCCESS;
729}
730
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500731static void cbparam_init( struct pjsip_regc_cbparam *cbparam,
732 pjsip_regc *regc,
733 pj_status_t status, int st_code,
734 const pj_str_t *reason,
735 pjsip_rx_data *rdata, pj_int32_t expiration,
736 int contact_cnt, pjsip_contact_hdr *contact[])
737{
738 cbparam->regc = regc;
739 cbparam->token = regc->token;
740 cbparam->status = status;
741 cbparam->code = st_code;
742 cbparam->reason = *reason;
743 cbparam->rdata = rdata;
744 cbparam->contact_cnt = contact_cnt;
745 cbparam->expiration = expiration;
746 if (contact_cnt) {
747 pj_memcpy( cbparam->contact, contact,
748 contact_cnt*sizeof(pjsip_contact_hdr*));
749 }
750}
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400751
752static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
753 const pj_str_t *reason,
754 pjsip_rx_data *rdata, pj_int32_t expiration,
755 int contact_cnt, pjsip_contact_hdr *contact[])
756{
757 struct pjsip_regc_cbparam cbparam;
758
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400759 if (!regc->cb)
760 return;
761
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500762 cbparam_init(&cbparam, regc, status, st_code, reason, rdata, expiration,
763 contact_cnt, contact);
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400764 (*regc->cb)(&cbparam);
765}
766
767static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
768 struct pj_timer_entry *entry)
769{
770 pjsip_regc *regc = (pjsip_regc*) entry->user_data;
771 pjsip_tx_data *tdata;
772 pj_status_t status;
773
774 PJ_UNUSED_ARG(timer_heap);
775
776 /* Temporarily increase busy flag to prevent regc from being deleted
777 * in pjsip_regc_send() or in the callback
778 */
779 pj_atomic_inc(regc->busy_ctr);
780
781 entry->id = 0;
782 status = pjsip_regc_register(regc, 1, &tdata);
783 if (status == PJ_SUCCESS) {
784 status = pjsip_regc_send(regc, tdata);
785 }
786
787 if (status != PJ_SUCCESS && regc->cb) {
788 char errmsg[PJ_ERR_MSG_SIZE];
789 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
790 call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
791 }
792
793 /* Delete the record if user destroy regc during the callback. */
794 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
795 pjsip_regc_destroy(regc);
796 }
797}
798
799static void schedule_registration ( pjsip_regc *regc, pj_int32_t expiration )
800{
801 if (regc->auto_reg && expiration > 0) {
802 pj_time_val delay = { 0, 0};
803
804 pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(regc->endpt),
805 &regc->timer, 0);
806
807 delay.sec = expiration - regc->delay_before_refresh;
808 if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
809 delay.sec > (pj_int32_t)regc->expires)
810 {
811 delay.sec = regc->expires;
812 }
813 if (delay.sec < DELAY_BEFORE_REFRESH)
814 delay.sec = DELAY_BEFORE_REFRESH;
815 regc->timer.cb = &regc_refresh_timer_cb;
816 regc->timer.id = REFRESH_TIMER;
817 regc->timer.user_data = regc;
818 pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
819 pj_gettimeofday(&regc->last_reg);
820 regc->next_reg = regc->last_reg;
821 regc->next_reg.sec += delay.sec;
822 }
823}
824
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500825PJ_DEF(pj_status_t) pjsip_regc_set_reg_tsx_cb( pjsip_regc *regc,
826 pjsip_regc_tsx_cb *tsx_cb)
827{
828 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
829 regc->tsx_cb = tsx_cb;
830 return PJ_SUCCESS;
831}
832
833
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400834PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc,
835 pjsip_host_port *via_addr,
836 pjsip_transport *via_tp)
837{
838 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
839
840 if (!via_addr)
841 pj_bzero(&regc->via_addr, sizeof(regc->via_addr));
842 else {
843 if (pj_strcmp(&regc->via_addr.host, &via_addr->host))
844 pj_strdup(regc->pool, &regc->via_addr.host, &via_addr->host);
845 regc->via_addr.port = via_addr->port;
846 }
847 regc->via_tp = via_tp;
848
849 return PJ_SUCCESS;
850}
851
852PJ_DEF(pj_status_t)
853pjsip_regc_set_delay_before_refresh( pjsip_regc *regc,
854 pj_uint32_t delay )
855{
856 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
857
858 if (delay > regc->expires)
859 return PJ_ETOOBIG;
860
861 pj_lock_acquire(regc->lock);
862
863 if (regc->delay_before_refresh != delay)
864 {
865 regc->delay_before_refresh = delay;
866
867 if (regc->timer.id != 0) {
868 /* Cancel registration timer */
869 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
870 regc->timer.id = 0;
871
872 /* Schedule next registration */
873 schedule_registration(regc, regc->expires);
874 }
875 }
876
877 pj_lock_release(regc->lock);
878
879 return PJ_SUCCESS;
880}
881
882
883static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
884 const pjsip_rx_data *rdata,
885 unsigned *contact_cnt,
886 unsigned max_contact,
887 pjsip_contact_hdr *contacts[])
888{
889 pj_int32_t expiration = NOEXP;
890 const pjsip_msg *msg = rdata->msg_info.msg;
891 const pjsip_hdr *hdr;
892
893 /* Enumerate all Contact headers in the response */
894 *contact_cnt = 0;
895 for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
896 if (hdr->type == PJSIP_H_CONTACT &&
897 *contact_cnt < max_contact)
898 {
899 contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
900 ++(*contact_cnt);
901 }
902 }
903
904 if (regc->current_op == REGC_REGISTERING) {
905 pj_bool_t has_our_contact = PJ_FALSE;
906 const pjsip_expires_hdr *expires;
907
908 /* Get Expires header */
909 expires = (const pjsip_expires_hdr*)
910 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
911
912 /* Try to find the Contact URIs that we register, in the response
913 * to get the expires value. We'll try both with comparing the URI
914 * and comparing the extension param only.
915 */
916 if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
917 unsigned i;
918 for (i=0; i<*contact_cnt; ++i) {
919 const pjsip_contact_hdr *our_hdr;
920
921 our_hdr = (const pjsip_contact_hdr*)
922 regc->contact_hdr_list.next;
923
924 /* Match with our Contact header(s) */
925 while ((void*)our_hdr != (void*)&regc->contact_hdr_list) {
926
927 const pjsip_uri *uri1, *uri2;
928 pj_bool_t matched = PJ_FALSE;
929
930 /* Exclude the display name when comparing the URI
931 * since server may not return it.
932 */
933 uri1 = (const pjsip_uri*)
934 pjsip_uri_get_uri(contacts[i]->uri);
935 uri2 = (const pjsip_uri*)
936 pjsip_uri_get_uri(our_hdr->uri);
937
938 /* First try with exact matching, according to RFC 3261
939 * Section 19.1.4 URI Comparison
940 */
941 if (pjsip_cfg()->regc.check_contact) {
942 matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
943 uri1, uri2)==0;
944 }
945
946 /* If no match is found, try with matching the extension
947 * parameter only if extension parameter was added.
948 */
949 if (!matched && regc->add_xuid_param &&
950 (PJSIP_URI_SCHEME_IS_SIP(uri1) ||
951 PJSIP_URI_SCHEME_IS_SIPS(uri1)) &&
952 (PJSIP_URI_SCHEME_IS_SIP(uri2) ||
953 PJSIP_URI_SCHEME_IS_SIPS(uri2)))
954 {
955 const pjsip_sip_uri *sip_uri1, *sip_uri2;
956 const pjsip_param *p1, *p2;
957
958 sip_uri1 = (const pjsip_sip_uri*)uri1;
959 sip_uri2 = (const pjsip_sip_uri*)uri2;
960
961 p1 = pjsip_param_cfind(&sip_uri1->other_param,
962 &XUID_PARAM_NAME);
963 p2 = pjsip_param_cfind(&sip_uri2->other_param,
964 &XUID_PARAM_NAME);
965 matched = p1 && p2 &&
966 pj_strcmp(&p1->value, &p2->value)==0;
967
968 }
969
970 if (matched) {
971 has_our_contact = PJ_TRUE;
972
973 if (contacts[i]->expires >= 0 &&
974 contacts[i]->expires < expiration)
975 {
976 /* Get the lowest expiration time. */
977 expiration = contacts[i]->expires;
978 }
979
980 break;
981 }
982
983 our_hdr = our_hdr->next;
984
985 } /* while ((void.. */
986
987 } /* for (i=.. */
988
989 /* If matching Contact header(s) are found but the
990 * header doesn't contain expires parameter, get the
991 * expiration value from the Expires header. And
992 * if Expires header is not present, get the expiration
993 * value from the request.
994 */
995 if (has_our_contact && expiration == NOEXP) {
996 if (expires) {
997 expiration = expires->ivalue;
998 } else if (regc->expires_hdr) {
999 expiration = regc->expires_hdr->ivalue;
1000 } else {
1001 /* We didn't request explicit expiration value,
1002 * and server doesn't specify it either. This
1003 * shouldn't happen unless we have a broken
1004 * registrar.
1005 */
1006 expiration = 3600;
1007 }
1008 }
1009
1010 }
1011
1012 /* If we still couldn't get matching Contact header(s), it means
1013 * there must be something wrong with the registrar (e.g. it may
1014 * have modified the URI's in the response, which is prohibited).
1015 */
1016 if (expiration==NOEXP) {
1017 /* If the number of Contact headers in the response matches
1018 * ours, they're all probably ours. Get the expiration
1019 * from there if this is the case, or from Expires header
1020 * if we don't have exact Contact header count, or
1021 * from the request as the last resort.
1022 */
1023 pj_size_t our_contact_cnt;
1024
1025 our_contact_cnt = pj_list_size(&regc->contact_hdr_list);
1026
1027 if (*contact_cnt == our_contact_cnt && *contact_cnt &&
1028 contacts[0]->expires >= 0)
1029 {
1030 expiration = contacts[0]->expires;
1031 } else if (expires)
1032 expiration = expires->ivalue;
1033 else if (regc->expires_hdr)
1034 expiration = regc->expires_hdr->ivalue;
1035 else
1036 expiration = 3600;
1037 }
1038
1039 } else {
1040 /* Just assume that the unregistration has been successful. */
1041 expiration = 0;
1042 }
1043
1044 /* Must have expiration value by now */
1045 pj_assert(expiration != NOEXP);
1046
1047 return expiration;
1048}
1049
1050static void regc_tsx_callback(void *token, pjsip_event *event)
1051{
1052 pj_status_t status;
1053 pjsip_regc *regc = (pjsip_regc*) token;
1054 pjsip_transaction *tsx = event->body.tsx_state.tsx;
1055 pj_bool_t handled = PJ_TRUE;
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001056 pj_bool_t update_contact = PJ_FALSE;
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001057
1058 pj_atomic_inc(regc->busy_ctr);
1059 pj_lock_acquire(regc->lock);
1060
1061 /* Decrement pending transaction counter. */
1062 pj_assert(regc->has_tsx);
1063 regc->has_tsx = PJ_FALSE;
1064
1065 /* Add reference to the transport */
1066 if (tsx->transport != regc->last_transport) {
1067 if (regc->last_transport) {
1068 pjsip_transport_dec_ref(regc->last_transport);
1069 regc->last_transport = NULL;
1070 }
1071
1072 if (tsx->transport) {
1073 regc->last_transport = tsx->transport;
1074 pjsip_transport_add_ref(regc->last_transport);
1075 }
1076 }
1077
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001078 if (regc->_delete_flag == 0 && regc->tsx_cb &&
1079 regc->current_op == REGC_REGISTERING)
1080 {
1081 struct pjsip_regc_tsx_cb_param param;
1082
1083 param.contact_cnt = -1;
1084 cbparam_init(&param.cbparam, regc, PJ_SUCCESS, tsx->status_code,
1085 &tsx->status_text,
1086 (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
1087 event->body.tsx_state.src.rdata : NULL,
1088 -1, 0, NULL);
1089
1090 /* Call regc tsx callback before handling any response */
1091 pj_lock_release(regc->lock);
1092 (*regc->tsx_cb)(&param);
1093 pj_lock_acquire(regc->lock);
1094
1095 if (param.contact_cnt >= 0) {
1096 /* Since we receive non-2xx response, it means that (some) contact
1097 * bindings haven't been established so we can safely remove these
1098 * contact headers. This is to avoid removing non-existent contact
1099 * bindings later.
1100 */
1101 if (tsx->status_code/100 != 2) {
1102 pjsip_contact_hdr *h;
1103
1104 h = regc->contact_hdr_list.next;
1105 while (h != &regc->contact_hdr_list) {
1106 pjsip_contact_hdr *next = h->next;
1107
1108 if (h->expires == -1) {
1109 pj_list_erase(h);
1110 }
1111 h = next;
1112 }
1113 }
1114
1115 /* Update contact address */
1116 pjsip_regc_update_contact(regc, param.contact_cnt, param.contact);
1117 update_contact = PJ_TRUE;
1118 }
1119 }
1120
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001121 /* Handle 401/407 challenge (even when _delete_flag is set) */
1122 if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
1123 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
1124 {
1125 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1126 pjsip_tx_data *tdata;
1127
1128 /* reset current op */
1129 regc->current_op = REGC_IDLE;
1130
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001131 if (update_contact) {
1132 pjsip_msg *msg;
1133 pjsip_hdr *hdr, *ins_hdr;
1134 pjsip_contact_hdr *chdr;
1135
1136 /* Delete Contact headers, but we shouldn't delete headers
1137 * which are supposed to remove contact bindings since
1138 * we cannot reconstruct those headers.
1139 */
1140 msg = tsx->last_tx->msg;
1141 hdr = msg->hdr.next;
1142 ins_hdr = &msg->hdr;
1143 while (hdr != &msg->hdr) {
1144 pjsip_hdr *next = hdr->next;
1145
1146 if (hdr->type == PJSIP_H_CONTACT) {
1147 chdr = (pjsip_contact_hdr *)hdr;
1148 if (chdr->expires != 0) {
1149 pj_list_erase(hdr);
1150 ins_hdr = next;
1151 }
1152 }
1153 hdr = next;
1154 }
1155
1156 /* Add Contact headers. */
1157 chdr = regc->contact_hdr_list.next;
1158 while (chdr != &regc->contact_hdr_list) {
1159 pj_list_insert_before(ins_hdr, (pjsip_hdr*)
1160 pjsip_hdr_shallow_clone(tsx->last_tx->pool, chdr));
1161 chdr = chdr->next;
1162 }
1163
1164 /* Also add bindings which are to be removed */
1165 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
1166 chdr = regc->removed_contact_hdr_list.next;
1167 pj_list_insert_before(ins_hdr, (pjsip_hdr*)
1168 pjsip_hdr_clone(tsx->last_tx->pool, chdr));
1169 pj_list_erase(chdr);
1170 }
1171 }
1172
1173 status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001174 rdata,
1175 tsx->last_tx,
1176 &tdata);
1177
1178 if (status == PJ_SUCCESS) {
1179 status = pjsip_regc_send(regc, tdata);
1180 }
1181
1182 if (status != PJ_SUCCESS) {
1183
1184 /* Only call callback if application is still interested
1185 * in it.
1186 */
1187 if (regc->_delete_flag == 0) {
1188 /* Should be safe to release the lock temporarily.
1189 * We do this to avoid deadlock.
1190 */
1191 pj_lock_release(regc->lock);
1192 call_callback(regc, status, tsx->status_code,
1193 &rdata->msg_info.msg->line.status.reason,
1194 rdata, -1, 0, NULL);
1195 pj_lock_acquire(regc->lock);
1196 }
1197 }
1198
1199 } else if (regc->_delete_flag) {
1200
1201 /* User has called pjsip_regc_destroy(), so don't call callback.
1202 * This regc will be destroyed later in this function.
1203 */
1204
1205 /* Just reset current op */
1206 regc->current_op = REGC_IDLE;
1207
1208 } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF &&
1209 regc->current_op == REGC_REGISTERING)
1210 {
1211 /* Handle 423 response automatically:
1212 * - set requested expiration to Min-Expires header, ONLY IF
1213 * the original request is a registration (as opposed to
1214 * unregistration) and the requested expiration was indeed
1215 * lower than Min-Expires)
1216 * - resend the request
1217 */
1218 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1219 pjsip_min_expires_hdr *me_hdr;
1220 pjsip_tx_data *tdata;
1221 pj_int32_t min_exp;
1222
1223 /* reset current op */
1224 regc->current_op = REGC_IDLE;
1225
1226 /* Update requested expiration */
1227 me_hdr = (pjsip_min_expires_hdr*)
1228 pjsip_msg_find_hdr(rdata->msg_info.msg,
1229 PJSIP_H_MIN_EXPIRES, NULL);
1230 if (me_hdr) {
1231 min_exp = me_hdr->ivalue;
1232 } else {
1233 /* Broken server, Min-Expires doesn't exist.
1234 * Just guestimate then, BUT ONLY if if this is the
1235 * first time we received such response.
1236 */
1237 enum {
1238 /* Note: changing this value would require changing couple of
1239 * Python test scripts.
1240 */
1241 UNSPECIFIED_MIN_EXPIRES = 3601
1242 };
1243 if (!regc->expires_hdr ||
1244 regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES)
1245 {
1246 min_exp = UNSPECIFIED_MIN_EXPIRES;
1247 } else {
1248 handled = PJ_FALSE;
1249 PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response "
1250 "without Min-Expires header is invalid"));
1251 goto handle_err;
1252 }
1253 }
1254
1255 if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) {
1256 /* But we already send with greater expiration time, why does
1257 * the server send us with 423? Oh well, just fail the request.
1258 */
1259 handled = PJ_FALSE;
1260 PJ_LOG(4,(THIS_FILE, "Registration failed: invalid "
1261 "Min-Expires header value in response"));
1262 goto handle_err;
1263 }
1264
1265 set_expires(regc, min_exp);
1266
1267 status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
1268 if (status == PJ_SUCCESS) {
1269 status = pjsip_regc_send(regc, tdata);
1270 }
1271
1272 if (status != PJ_SUCCESS) {
1273 /* Only call callback if application is still interested
1274 * in it.
1275 */
1276 if (!regc->_delete_flag) {
1277 /* Should be safe to release the lock temporarily.
1278 * We do this to avoid deadlock.
1279 */
1280 pj_lock_release(regc->lock);
1281 call_callback(regc, status, tsx->status_code,
1282 &rdata->msg_info.msg->line.status.reason,
1283 rdata, -1, 0, NULL);
1284 pj_lock_acquire(regc->lock);
1285 }
1286 }
1287
1288 } else {
1289 handled = PJ_FALSE;
1290 }
1291
1292handle_err:
1293 if (!handled) {
1294 pjsip_rx_data *rdata;
1295 pj_int32_t expiration = NOEXP;
1296 unsigned contact_cnt = 0;
1297 pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
1298
1299 if (tsx->status_code/100 == 2) {
1300
1301 rdata = event->body.tsx_state.src.rdata;
1302
1303 /* Calculate expiration */
1304 expiration = calculate_response_expiration(regc, rdata,
1305 &contact_cnt,
1306 PJSIP_REGC_MAX_CONTACT,
1307 contact);
1308
1309 /* Schedule next registration */
1310 schedule_registration(regc, expiration);
1311
1312 } else {
1313 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
1314 event->body.tsx_state.src.rdata : NULL;
1315 }
1316
1317 /* Update registration */
1318 if (expiration==NOEXP) expiration=-1;
1319 regc->expires = expiration;
1320
1321 /* Mark operation as complete */
1322 regc->current_op = REGC_IDLE;
1323
1324 /* Call callback. */
1325 /* Should be safe to release the lock temporarily.
1326 * We do this to avoid deadlock.
1327 */
1328 pj_lock_release(regc->lock);
1329 call_callback(regc, PJ_SUCCESS, tsx->status_code,
1330 (rdata ? &rdata->msg_info.msg->line.status.reason
1331 : &tsx->status_text),
1332 rdata, expiration,
1333 contact_cnt, contact);
1334 pj_lock_acquire(regc->lock);
1335 }
1336
1337 pj_lock_release(regc->lock);
1338
1339 /* Delete the record if user destroy regc during the callback. */
1340 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
1341 pjsip_regc_destroy(regc);
1342 }
1343}
1344
1345PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
1346{
1347 pj_status_t status;
1348 pjsip_cseq_hdr *cseq_hdr;
1349 pjsip_expires_hdr *expires_hdr;
1350 pj_uint32_t cseq;
1351
1352 pj_atomic_inc(regc->busy_ctr);
1353 pj_lock_acquire(regc->lock);
1354
1355 /* Make sure we don't have pending transaction. */
1356 if (regc->has_tsx) {
1357 PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
1358 "transaction pending"));
1359 pjsip_tx_data_dec_ref( tdata );
1360 pj_lock_release(regc->lock);
1361 pj_atomic_dec(regc->busy_ctr);
1362 return PJSIP_EBUSY;
1363 }
1364
1365 pj_assert(regc->current_op == REGC_IDLE);
1366
1367 /* Invalidate message buffer. */
1368 pjsip_tx_data_invalidate_msg(tdata);
1369
1370 /* Increment CSeq */
1371 cseq = ++regc->cseq_hdr->cseq;
1372 cseq_hdr = (pjsip_cseq_hdr*)
1373 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
1374 cseq_hdr->cseq = cseq;
1375
1376 /* Find Expires header */
1377 expires_hdr = (pjsip_expires_hdr*)
1378 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);
1379
1380 /* Bind to transport selector */
1381 pjsip_tx_data_set_transport(tdata, &regc->tp_sel);
1382
1383 regc->has_tsx = PJ_TRUE;
1384
1385 /* Set current operation based on the value of Expires header */
1386 if (expires_hdr && expires_hdr->ivalue==0)
1387 regc->current_op = REGC_UNREGISTERING;
1388 else
1389 regc->current_op = REGC_REGISTERING;
1390
1391 /* Prevent deletion of tdata, e.g: when something wrong in sending,
1392 * we need tdata to retrieve the transport.
1393 */
1394 pjsip_tx_data_add_ref(tdata);
1395
1396 /* If via_addr is set, use this address for the Via header. */
1397 if (regc->via_addr.host.slen > 0) {
1398 tdata->via_addr = regc->via_addr;
1399 tdata->via_tp = regc->via_tp;
1400 }
1401
1402 /* Need to unlock the regc temporarily while sending the message to
1403 * prevent deadlock (https://trac.pjsip.org/repos/ticket/1247).
1404 * It should be safe to do this since the regc's refcount has been
1405 * incremented.
1406 */
1407 pj_lock_release(regc->lock);
1408
1409 /* Now send the message */
1410 status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
1411 regc, &regc_tsx_callback);
1412 if (status!=PJ_SUCCESS) {
1413 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
1414 }
1415
1416 /* Reacquire the lock */
1417 pj_lock_acquire(regc->lock);
1418
1419 /* Get last transport used and add reference to it */
1420 if (tdata->tp_info.transport != regc->last_transport &&
1421 status==PJ_SUCCESS)
1422 {
1423 if (regc->last_transport) {
1424 pjsip_transport_dec_ref(regc->last_transport);
1425 regc->last_transport = NULL;
1426 }
1427
1428 if (tdata->tp_info.transport) {
1429 regc->last_transport = tdata->tp_info.transport;
1430 pjsip_transport_add_ref(regc->last_transport);
1431 }
1432 }
1433
1434 /* Release tdata */
1435 pjsip_tx_data_dec_ref(tdata);
1436
1437 pj_lock_release(regc->lock);
1438
1439 /* Delete the record if user destroy regc during the callback. */
1440 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
1441 pjsip_regc_destroy(regc);
1442 }
1443
1444 return status;
1445}
1446
1447