blob: 77d9a12c60fb3a463979036ff4f53dacb79bb84d [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 <pjsua-lib/pjsua.h>
21#include <pjsua-lib/pjsua_internal.h>
22
23
24#define THIS_FILE "pjsua_acc.c"
25
26enum
27{
28 OUTBOUND_UNKNOWN, // status unknown
29 OUTBOUND_WANTED, // initiated in registration
30 OUTBOUND_ACTIVE, // got positive response from server
31 OUTBOUND_NA // not wanted or got negative response from server
32};
33
34
35static void schedule_reregistration(pjsua_acc *acc);
36static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te);
37
38/*
39 * Get number of current accounts.
40 */
41PJ_DEF(unsigned) pjsua_acc_get_count(void)
42{
43 return pjsua_var.acc_cnt;
44}
45
46
47/*
48 * Check if the specified account ID is valid.
49 */
50PJ_DEF(pj_bool_t) pjsua_acc_is_valid(pjsua_acc_id acc_id)
51{
52 return acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc) &&
53 pjsua_var.acc[acc_id].valid;
54}
55
56
57/*
58 * Set default account
59 */
60PJ_DEF(pj_status_t) pjsua_acc_set_default(pjsua_acc_id acc_id)
61{
62 pjsua_var.default_acc = acc_id;
63 return PJ_SUCCESS;
64}
65
66
67/*
68 * Get default account.
69 */
70PJ_DEF(pjsua_acc_id) pjsua_acc_get_default(void)
71{
72 return pjsua_var.default_acc;
73}
74
75
76/*
77 * Copy account configuration.
78 */
79PJ_DEF(void) pjsua_acc_config_dup( pj_pool_t *pool,
80 pjsua_acc_config *dst,
81 const pjsua_acc_config *src)
82{
83 unsigned i;
84
85 pj_memcpy(dst, src, sizeof(pjsua_acc_config));
86
87 pj_strdup_with_null(pool, &dst->id, &src->id);
88 pj_strdup_with_null(pool, &dst->reg_uri, &src->reg_uri);
89 pj_strdup_with_null(pool, &dst->force_contact, &src->force_contact);
90 pj_strdup_with_null(pool, &dst->contact_params, &src->contact_params);
91 pj_strdup_with_null(pool, &dst->contact_uri_params,
92 &src->contact_uri_params);
93 pj_strdup_with_null(pool, &dst->pidf_tuple_id, &src->pidf_tuple_id);
94 pj_strdup_with_null(pool, &dst->rfc5626_instance_id,
95 &src->rfc5626_instance_id);
96 pj_strdup_with_null(pool, &dst->rfc5626_reg_id, &src->rfc5626_reg_id);
97
98 dst->proxy_cnt = src->proxy_cnt;
99 for (i=0; i<src->proxy_cnt; ++i)
100 pj_strdup_with_null(pool, &dst->proxy[i], &src->proxy[i]);
101
102 dst->reg_timeout = src->reg_timeout;
103 dst->reg_delay_before_refresh = src->reg_delay_before_refresh;
104 dst->cred_count = src->cred_count;
105
106 for (i=0; i<src->cred_count; ++i) {
107 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
108 }
109
110 pj_list_init(&dst->reg_hdr_list);
111 if (!pj_list_empty(&src->reg_hdr_list)) {
112 const pjsip_hdr *hdr;
113
114 hdr = src->reg_hdr_list.next;
115 while (hdr != &src->reg_hdr_list) {
116 pj_list_push_back(&dst->reg_hdr_list, pjsip_hdr_clone(pool, hdr));
117 hdr = hdr->next;
118 }
119 }
120
121 pj_list_init(&dst->sub_hdr_list);
122 if (!pj_list_empty(&src->sub_hdr_list)) {
123 const pjsip_hdr *hdr;
124
125 hdr = src->sub_hdr_list.next;
126 while (hdr != &src->sub_hdr_list) {
127 pj_list_push_back(&dst->sub_hdr_list, pjsip_hdr_clone(pool, hdr));
128 hdr = hdr->next;
129 }
130 }
131
132 pjsip_auth_clt_pref_dup(pool, &dst->auth_pref, &src->auth_pref);
133
134 pjsua_transport_config_dup(pool, &dst->rtp_cfg, &src->rtp_cfg);
135
136 pjsua_ice_config_dup(pool, &dst->ice_cfg, &src->ice_cfg);
137 pjsua_turn_config_dup(pool, &dst->turn_cfg, &src->turn_cfg);
138
139 pj_strdup(pool, &dst->ka_data, &src->ka_data);
140}
141
142/*
143 * Calculate CRC of proxy list.
144 */
145static pj_uint32_t calc_proxy_crc(const pj_str_t proxy[], pj_size_t cnt)
146{
147 pj_crc32_context ctx;
148 unsigned i;
149
150 pj_crc32_init(&ctx);
151 for (i=0; i<cnt; ++i) {
152 pj_crc32_update(&ctx, (pj_uint8_t*)proxy[i].ptr, proxy[i].slen);
153 }
154
155 return pj_crc32_final(&ctx);
156}
157
158/*
159 * Initialize a new account (after configuration is set).
160 */
161static pj_status_t initialize_acc(unsigned acc_id)
162{
163 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
164 pjsua_acc *acc = &pjsua_var.acc[acc_id];
165 pjsip_name_addr *name_addr;
166 pjsip_sip_uri *sip_reg_uri;
167 pj_status_t status;
168 unsigned i;
169
170 /* Need to parse local_uri to get the elements: */
171
172 name_addr = (pjsip_name_addr*)
173 pjsip_parse_uri(acc->pool, acc_cfg->id.ptr,
174 acc_cfg->id.slen,
175 PJSIP_PARSE_URI_AS_NAMEADDR);
176 if (name_addr == NULL) {
177 pjsua_perror(THIS_FILE, "Invalid local URI",
178 PJSIP_EINVALIDURI);
179 return PJSIP_EINVALIDURI;
180 }
181
182 /* Local URI MUST be a SIP or SIPS: */
183 if (!PJSIP_URI_SCHEME_IS_SIP(name_addr) &&
184 !PJSIP_URI_SCHEME_IS_SIPS(name_addr))
185 {
186 acc->display = name_addr->display;
187 acc->user_part = name_addr->display;
188 acc->srv_domain = pj_str("");
189 acc->srv_port = 0;
190 } else {
191 pjsip_sip_uri *sip_uri;
192
193 /* Get the SIP URI object: */
194 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(name_addr);
195
196 /* Save the user and domain part. These will be used when finding an
197 * account for incoming requests.
198 */
199 acc->display = name_addr->display;
200 acc->user_part = sip_uri->user;
201 acc->srv_domain = sip_uri->host;
202 acc->srv_port = 0;
203 }
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500204 acc->is_sips = PJSIP_URI_SCHEME_IS_SIPS(name_addr);
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400205
206
207 /* Parse registrar URI, if any */
208 if (acc_cfg->reg_uri.slen) {
209 pjsip_uri *reg_uri;
210
211 reg_uri = pjsip_parse_uri(acc->pool, acc_cfg->reg_uri.ptr,
212 acc_cfg->reg_uri.slen, 0);
213 if (reg_uri == NULL) {
214 pjsua_perror(THIS_FILE, "Invalid registrar URI",
215 PJSIP_EINVALIDURI);
216 return PJSIP_EINVALIDURI;
217 }
218
219 /* Registrar URI MUST be a SIP or SIPS: */
220 if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) &&
221 !PJSIP_URI_SCHEME_IS_SIPS(reg_uri))
222 {
223 pjsua_perror(THIS_FILE, "Invalid registar URI",
224 PJSIP_EINVALIDSCHEME);
225 return PJSIP_EINVALIDSCHEME;
226 }
227
228 sip_reg_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri);
229
230 } else {
231 sip_reg_uri = NULL;
232 }
233
234 if (sip_reg_uri) {
235 acc->srv_port = sip_reg_uri->port;
236 }
237
238 /* Create Contact header if not present. */
239 //if (acc_cfg->contact.slen == 0) {
240 // acc_cfg->contact = acc_cfg->id;
241 //}
242
243 /* Build account route-set from outbound proxies and route set from
244 * account configuration.
245 */
246 pj_list_init(&acc->route_set);
247
248 if (!pj_list_empty(&pjsua_var.outbound_proxy)) {
249 pjsip_route_hdr *r;
250
251 r = pjsua_var.outbound_proxy.next;
252 while (r != &pjsua_var.outbound_proxy) {
253 pj_list_push_back(&acc->route_set,
254 pjsip_hdr_shallow_clone(acc->pool, r));
255 r = r->next;
256 }
257 }
258
259 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
260 pj_str_t hname = { "Route", 5};
261 pjsip_route_hdr *r;
262 pj_str_t tmp;
263
264 pj_strdup_with_null(acc->pool, &tmp, &acc_cfg->proxy[i]);
265 r = (pjsip_route_hdr*)
266 pjsip_parse_hdr(acc->pool, &hname, tmp.ptr, tmp.slen, NULL);
267 if (r == NULL) {
268 pjsua_perror(THIS_FILE, "Invalid URI in account route set",
269 PJ_EINVAL);
270 return PJ_EINVAL;
271 }
272 pj_list_push_back(&acc->route_set, r);
273 }
274
275 /* Concatenate credentials from account config and global config */
276 acc->cred_cnt = 0;
277 for (i=0; i<acc_cfg->cred_count; ++i) {
278 acc->cred[acc->cred_cnt++] = acc_cfg->cred_info[i];
279 }
280 for (i=0; i<pjsua_var.ua_cfg.cred_count &&
281 acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i)
282 {
283 acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i];
284 }
285
286 /* If account's ICE and TURN customization is not set, then
287 * initialize it with the settings from the global media config.
288 */
289 if (acc->cfg.ice_cfg_use == PJSUA_ICE_CONFIG_USE_DEFAULT) {
290 pjsua_ice_config_from_media_config(NULL, &acc->cfg.ice_cfg,
291 &pjsua_var.media_cfg);
292 }
293 if (acc->cfg.turn_cfg_use == PJSUA_TURN_CONFIG_USE_DEFAULT) {
294 pjsua_turn_config_from_media_config(NULL, &acc->cfg.turn_cfg,
295 &pjsua_var.media_cfg);
296 }
297
298 /* If ICE is enabled, add "+sip.ice" media feature tag in account's
299 * contact params.
300 */
301#if PJSUA_ADD_ICE_TAGS
302 if (acc_cfg->ice_cfg.enable_ice) {
303 pj_ssize_t new_len;
304 pj_str_t new_prm;
305
306 new_len = acc_cfg->contact_params.slen + 10;
307 new_prm.ptr = (char*)pj_pool_alloc(acc->pool, new_len);
308 pj_strcpy(&new_prm, &acc_cfg->contact_params);
309 pj_strcat2(&new_prm, ";+sip.ice");
310 acc_cfg->contact_params = new_prm;
311 }
312#endif
313
314 status = pjsua_pres_init_acc(acc_id);
315 if (status != PJ_SUCCESS)
316 return status;
317
318 /* If SIP outbound is enabled, generate instance and reg ID if they are
319 * not specified
320 */
321 if (acc_cfg->use_rfc5626) {
322 if (acc_cfg->rfc5626_instance_id.slen==0) {
323 const pj_str_t *hostname;
324 pj_uint32_t hval;
325 pj_size_t pos;
326 char instprm[] = ";+sip.instance=\"<urn:uuid:00000000-0000-0000-0000-0000CCDDEEFF>\"";
327
328 hostname = pj_gethostname();
329 pos = pj_ansi_strlen(instprm) - 10;
330 hval = pj_hash_calc(0, hostname->ptr, (unsigned)hostname->slen);
331 pj_val_to_hex_digit( ((char*)&hval)[0], instprm+pos+0);
332 pj_val_to_hex_digit( ((char*)&hval)[1], instprm+pos+2);
333 pj_val_to_hex_digit( ((char*)&hval)[2], instprm+pos+4);
334 pj_val_to_hex_digit( ((char*)&hval)[3], instprm+pos+6);
335
336 pj_strdup2(acc->pool, &acc->rfc5626_instprm, instprm);
337 } else {
338 const char *prmname = ";+sip.instance=\"";
339 pj_size_t len;
340
341 len = pj_ansi_strlen(prmname) + acc_cfg->rfc5626_instance_id.slen + 1;
342 acc->rfc5626_instprm.ptr = (char*)pj_pool_alloc(acc->pool, len+1);
343 pj_ansi_snprintf(acc->rfc5626_instprm.ptr, len+1,
344 "%s%.*s\"",
345 prmname,
346 (int)acc_cfg->rfc5626_instance_id.slen,
347 acc_cfg->rfc5626_instance_id.ptr);
348 acc->rfc5626_instprm.slen = len;
349 }
350
351 if (acc_cfg->rfc5626_reg_id.slen==0) {
352 acc->rfc5626_regprm = pj_str(";reg-id=1");
353 } else {
354 const char *prmname = ";reg-id=";
355 pj_size_t len;
356
357 len = pj_ansi_strlen(prmname) + acc_cfg->rfc5626_reg_id.slen;
358 acc->rfc5626_regprm.ptr = (char*)pj_pool_alloc(acc->pool, len+1);
359 pj_ansi_snprintf(acc->rfc5626_regprm.ptr, len+1,
360 "%s%.*s\"",
361 prmname,
362 (int)acc_cfg->rfc5626_reg_id.slen,
363 acc_cfg->rfc5626_reg_id.ptr);
364 acc->rfc5626_regprm.slen = len;
365 }
366 }
367
368 /* Mark account as valid */
369 pjsua_var.acc[acc_id].valid = PJ_TRUE;
370
371 /* Insert account ID into account ID array, sorted by priority */
372 for (i=0; i<pjsua_var.acc_cnt; ++i) {
373 if ( pjsua_var.acc[pjsua_var.acc_ids[i]].cfg.priority <
374 pjsua_var.acc[acc_id].cfg.priority)
375 {
376 break;
377 }
378 }
379 pj_array_insert(pjsua_var.acc_ids, sizeof(pjsua_var.acc_ids[0]),
380 pjsua_var.acc_cnt, i, &acc_id);
381
382 return PJ_SUCCESS;
383}
384
385
386/*
387 * Add a new account to pjsua.
388 */
389PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
390 pj_bool_t is_default,
391 pjsua_acc_id *p_acc_id)
392{
393 pjsua_acc *acc;
394 unsigned i, id;
395 pj_status_t status = PJ_SUCCESS;
396
397 PJ_ASSERT_RETURN(cfg, PJ_EINVAL);
398 PJ_ASSERT_RETURN(pjsua_var.acc_cnt < PJ_ARRAY_SIZE(pjsua_var.acc),
399 PJ_ETOOMANY);
400
401 /* Must have a transport */
402 PJ_ASSERT_RETURN(pjsua_var.tpdata[0].data.ptr != NULL, PJ_EINVALIDOP);
403
404 PJ_LOG(4,(THIS_FILE, "Adding account: id=%.*s",
405 (int)cfg->id.slen, cfg->id.ptr));
406 pj_log_push_indent();
407
408 PJSUA_LOCK();
409
410 /* Find empty account id. */
411 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.acc); ++id) {
412 if (pjsua_var.acc[id].valid == PJ_FALSE)
413 break;
414 }
415
416 /* Expect to find a slot */
417 PJ_ASSERT_ON_FAIL( id < PJ_ARRAY_SIZE(pjsua_var.acc),
418 {PJSUA_UNLOCK(); return PJ_EBUG;});
419
420 acc = &pjsua_var.acc[id];
421
422 /* Create pool for this account. */
423 if (acc->pool)
424 pj_pool_reset(acc->pool);
425 else
426 acc->pool = pjsua_pool_create("acc%p", 512, 256);
427
428 /* Copy config */
429 pjsua_acc_config_dup(acc->pool, &pjsua_var.acc[id].cfg, cfg);
430
431 /* Normalize registration timeout and refresh delay */
432 if (pjsua_var.acc[id].cfg.reg_uri.slen) {
433 if (pjsua_var.acc[id].cfg.reg_timeout == 0) {
434 pjsua_var.acc[id].cfg.reg_timeout = PJSUA_REG_INTERVAL;
435 }
436 if (pjsua_var.acc[id].cfg.reg_delay_before_refresh == 0) {
437 pjsua_var.acc[id].cfg.reg_delay_before_refresh =
438 PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH;
439 }
440 }
441
442 /* Check the route URI's and force loose route if required */
443 for (i=0; i<acc->cfg.proxy_cnt; ++i) {
444 status = normalize_route_uri(acc->pool, &acc->cfg.proxy[i]);
445 if (status != PJ_SUCCESS) {
446 PJSUA_UNLOCK();
447 pj_log_pop_indent();
448 return status;
449 }
450 }
451
452 /* Get CRC of account proxy setting */
453 acc->local_route_crc = calc_proxy_crc(acc->cfg.proxy, acc->cfg.proxy_cnt);
454
455 /* Get CRC of global outbound proxy setting */
456 acc->global_route_crc=calc_proxy_crc(pjsua_var.ua_cfg.outbound_proxy,
457 pjsua_var.ua_cfg.outbound_proxy_cnt);
458
459 status = initialize_acc(id);
460 if (status != PJ_SUCCESS) {
461 pjsua_perror(THIS_FILE, "Error adding account", status);
462 pj_pool_release(acc->pool);
463 acc->pool = NULL;
464 PJSUA_UNLOCK();
465 pj_log_pop_indent();
466 return status;
467 }
468
469 if (is_default)
470 pjsua_var.default_acc = id;
471
472 if (p_acc_id)
473 *p_acc_id = id;
474
475 pjsua_var.acc_cnt++;
476
477 PJSUA_UNLOCK();
478
479 PJ_LOG(4,(THIS_FILE, "Account %.*s added with id %d",
480 (int)cfg->id.slen, cfg->id.ptr, id));
481
482 /* If accounts has registration enabled, start registration */
483 if (pjsua_var.acc[id].cfg.reg_uri.slen) {
484 if (pjsua_var.acc[id].cfg.register_on_acc_add)
485 pjsua_acc_set_registration(id, PJ_TRUE);
486 } else {
487 /* Otherwise subscribe to MWI, if it's enabled */
488 if (pjsua_var.acc[id].cfg.mwi_enabled)
489 pjsua_start_mwi(id, PJ_TRUE);
490
491 /* Start publish too */
492 if (acc->cfg.publish_enabled)
493 pjsua_pres_init_publish_acc(id);
494 }
495
496 pj_log_pop_indent();
497 return PJ_SUCCESS;
498}
499
500
501/*
502 * Add local account
503 */
504PJ_DEF(pj_status_t) pjsua_acc_add_local( pjsua_transport_id tid,
505 pj_bool_t is_default,
506 pjsua_acc_id *p_acc_id)
507{
508 pjsua_acc_config cfg;
509 pjsua_transport_data *t = &pjsua_var.tpdata[tid];
510 const char *beginquote, *endquote;
511 char transport_param[32];
512 char uri[PJSIP_MAX_URL_SIZE];
513
514 /* ID must be valid */
515 PJ_ASSERT_RETURN(tid>=0 && tid<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
516 PJ_EINVAL);
517
518 /* Transport must be valid */
519 PJ_ASSERT_RETURN(t->data.ptr != NULL, PJ_EINVAL);
520
521 pjsua_acc_config_default(&cfg);
522
523 /* Lower the priority of local account */
524 --cfg.priority;
525
526 /* Enclose IPv6 address in square brackets */
527 if (t->type & PJSIP_TRANSPORT_IPV6) {
528 beginquote = "[";
529 endquote = "]";
530 } else {
531 beginquote = endquote = "";
532 }
533
534 /* Don't add transport parameter if it's UDP */
535 if (t->type!=PJSIP_TRANSPORT_UDP && t->type!=PJSIP_TRANSPORT_UDP6) {
536 pj_ansi_snprintf(transport_param, sizeof(transport_param),
537 ";transport=%s",
538 pjsip_transport_get_type_name(t->type));
539 } else {
540 transport_param[0] = '\0';
541 }
542
543 /* Build URI for the account */
544 pj_ansi_snprintf(uri, PJSIP_MAX_URL_SIZE,
545 "<sip:%s%.*s%s:%d%s>",
546 beginquote,
547 (int)t->local_name.host.slen,
548 t->local_name.host.ptr,
549 endquote,
550 t->local_name.port,
551 transport_param);
552
553 cfg.id = pj_str(uri);
554
555 return pjsua_acc_add(&cfg, is_default, p_acc_id);
556}
557
558
559/*
560 * Set arbitrary data to be associated with the account.
561 */
562PJ_DEF(pj_status_t) pjsua_acc_set_user_data(pjsua_acc_id acc_id,
563 void *user_data)
564{
565 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
566 PJ_EINVAL);
567 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
568
569 PJSUA_LOCK();
570
571 pjsua_var.acc[acc_id].cfg.user_data = user_data;
572
573 PJSUA_UNLOCK();
574
575 return PJ_SUCCESS;
576}
577
578
579/*
580 * Retrieve arbitrary data associated with the account.
581 */
582PJ_DEF(void*) pjsua_acc_get_user_data(pjsua_acc_id acc_id)
583{
584 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
585 NULL);
586 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, NULL);
587
588 return pjsua_var.acc[acc_id].cfg.user_data;
589}
590
591
592/*
593 * Delete account.
594 */
595PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id)
596{
597 pjsua_acc *acc;
598 unsigned i;
599
600 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
601 PJ_EINVAL);
602 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
603
604 PJ_LOG(4,(THIS_FILE, "Deleting account %d..", acc_id));
605 pj_log_push_indent();
606
607 PJSUA_LOCK();
608
609 acc = &pjsua_var.acc[acc_id];
610
611 /* Cancel keep-alive timer, if any */
612 if (acc->ka_timer.id) {
613 pjsip_endpt_cancel_timer(pjsua_var.endpt, &acc->ka_timer);
614 acc->ka_timer.id = PJ_FALSE;
615 }
616 if (acc->ka_transport) {
617 pjsip_transport_dec_ref(acc->ka_transport);
618 acc->ka_transport = NULL;
619 }
620
621 /* Cancel any re-registration timer */
622 if (acc->auto_rereg.timer.id) {
623 acc->auto_rereg.timer.id = PJ_FALSE;
624 pjsua_cancel_timer(&acc->auto_rereg.timer);
625 }
626
627 /* Delete registration */
628 if (acc->regc != NULL) {
629 pjsua_acc_set_registration(acc_id, PJ_FALSE);
630 if (acc->regc) {
631 pjsip_regc_destroy(acc->regc);
632 }
633 acc->regc = NULL;
634 }
635
636 /* Terminate mwi subscription */
637 if (acc->cfg.mwi_enabled) {
638 acc->cfg.mwi_enabled = PJ_FALSE;
639 pjsua_start_mwi(acc_id, PJ_FALSE);
640 }
641
642 /* Delete server presence subscription */
643 pjsua_pres_delete_acc(acc_id, 0);
644
645 /* Release account pool */
646 if (acc->pool) {
647 pj_pool_release(acc->pool);
648 acc->pool = NULL;
649 }
650
651 /* Invalidate */
652 acc->valid = PJ_FALSE;
653 acc->contact.slen = 0;
654 acc->reg_mapped_addr.slen = 0;
655 pj_bzero(&acc->via_addr, sizeof(acc->via_addr));
656 acc->via_tp = NULL;
657 acc->next_rtp_port = 0;
658
659 /* Remove from array */
660 for (i=0; i<pjsua_var.acc_cnt; ++i) {
661 if (pjsua_var.acc_ids[i] == acc_id)
662 break;
663 }
664 if (i != pjsua_var.acc_cnt) {
665 pj_array_erase(pjsua_var.acc_ids, sizeof(pjsua_var.acc_ids[0]),
666 pjsua_var.acc_cnt, i);
667 --pjsua_var.acc_cnt;
668 }
669
670 /* Leave the calls intact, as I don't think calls need to
671 * access account once it's created
672 */
673
674 /* Update default account */
675 if (pjsua_var.default_acc == acc_id)
676 pjsua_var.default_acc = 0;
677
678 PJSUA_UNLOCK();
679
680 PJ_LOG(4,(THIS_FILE, "Account id %d deleted", acc_id));
681
682 pj_log_pop_indent();
683 return PJ_SUCCESS;
684}
685
686
687/* Get config */
688PJ_DEF(pj_status_t) pjsua_acc_get_config(pjsua_acc_id acc_id,
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500689 pj_pool_t *pool,
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400690 pjsua_acc_config *acc_cfg)
691{
692 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc)
693 && pjsua_var.acc[acc_id].valid, PJ_EINVAL);
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500694 //this now would not work due to corrupt header list
695 //pj_memcpy(acc_cfg, &pjsua_var.acc[acc_id].cfg, sizeof(*acc_cfg));
696 pjsua_acc_config_dup(pool, acc_cfg, &pjsua_var.acc[acc_id].cfg);
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400697 return PJ_SUCCESS;
698}
699
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500700/* Compare two SIP headers. Return zero if equal */
701static int pjsip_hdr_cmp(const pjsip_hdr *h1, const pjsip_hdr *h2)
702{
703 char buf1[PJSIP_MAX_URL_SIZE];
704 char buf2[PJSIP_MAX_URL_SIZE];
705 pj_str_t p1, p2;
706
707 p1.ptr = buf1;
708 p1.slen = 0;
709 p2.ptr = buf2;
710 p2.slen = 0;
711
712 p1.slen = pjsip_hdr_print_on((void*)h1, buf1, sizeof(buf1));
713 if (p1.slen < 0)
714 p1.slen = 0;
715 p2.slen = pjsip_hdr_print_on((void*)h2, buf2, sizeof(buf2));
716 if (p2.slen < 0)
717 p2.slen = 0;
718
719 return pj_strcmp(&p1, &p2);
720}
721
722/* Update SIP header list from another list. Return PJ_TRUE if
723 * the list has been updated */
724static pj_bool_t update_hdr_list(pj_pool_t *pool, pjsip_hdr *dst,
725 const pjsip_hdr *src)
726{
727 pjsip_hdr *dst_i;
728 const pjsip_hdr *src_i;
729 pj_bool_t changed = PJ_FALSE;
730
731 /* Remove header that's no longer needed */
732 for (dst_i = dst->next; dst_i != dst; ) {
733 for (src_i = src->next; src_i != src; src_i = src_i->next) {
734 if (pjsip_hdr_cmp(dst_i, src_i) == 0)
735 break;
736 }
737 if (src_i == src) {
738 pjsip_hdr *next = dst_i->next;
739 pj_list_erase(dst_i);
740 changed = PJ_TRUE;
741 dst_i = next;
742 } else {
743 dst_i = dst_i->next;
744 }
745 }
746
747 /* Add new header */
748 for (src_i = src->next; src_i != src; src_i = src_i->next) {
749 for (dst_i = dst->next; dst_i != dst; dst_i = dst_i->next) {
750 if (pjsip_hdr_cmp(dst_i, src_i) == 0)
751 break;
752 }
753 if (dst_i == dst) {
754 dst_i = pjsip_hdr_clone(pool, src_i);
755 pj_list_push_back(dst, dst_i);
756 changed = PJ_TRUE;
757 }
758 }
759
760 return changed;
761}
762
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400763/*
764 * Modify account information.
765 */
766PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
767 const pjsua_acc_config *cfg)
768{
769 pjsua_acc *acc;
770 pjsip_name_addr *id_name_addr = NULL;
771 pjsip_sip_uri *id_sip_uri = NULL;
772 pjsip_sip_uri *reg_sip_uri = NULL;
773 pj_uint32_t local_route_crc, global_route_crc;
774 pjsip_route_hdr global_route;
775 pjsip_route_hdr local_route;
776 pj_str_t acc_proxy[PJSUA_ACC_MAX_PROXIES];
777 pj_bool_t update_reg = PJ_FALSE;
778 pj_bool_t unreg_first = PJ_FALSE;
779 pj_bool_t update_mwi = PJ_FALSE;
780 pj_status_t status = PJ_SUCCESS;
781
782 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
783 PJ_EINVAL);
784
785 PJ_LOG(4,(THIS_FILE, "Modifying accunt %d", acc_id));
786 pj_log_push_indent();
787
788 PJSUA_LOCK();
789
790 acc = &pjsua_var.acc[acc_id];
791 if (!acc->valid) {
792 status = PJ_EINVAL;
793 goto on_return;
794 }
795
796 /* == Validate first == */
797
798 /* Account id */
799 if (pj_strcmp(&acc->cfg.id, &cfg->id)) {
800 /* Need to parse id to get the elements: */
801 id_name_addr = (pjsip_name_addr*)
802 pjsip_parse_uri(acc->pool, cfg->id.ptr, cfg->id.slen,
803 PJSIP_PARSE_URI_AS_NAMEADDR);
804 if (id_name_addr == NULL) {
805 status = PJSIP_EINVALIDURI;
806 pjsua_perror(THIS_FILE, "Invalid local URI", status);
807 goto on_return;
808 }
809
810 /* URI MUST be a SIP or SIPS: */
811 if (!PJSIP_URI_SCHEME_IS_SIP(id_name_addr) &&
812 !PJSIP_URI_SCHEME_IS_SIPS(id_name_addr))
813 {
814 status = PJSIP_EINVALIDSCHEME;
815 pjsua_perror(THIS_FILE, "Invalid local URI", status);
816 goto on_return;
817 }
818
819 /* Get the SIP URI object: */
820 id_sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(id_name_addr);
821 }
822
823 /* Registrar URI */
824 if (pj_strcmp(&acc->cfg.reg_uri, &cfg->reg_uri) && cfg->reg_uri.slen) {
825 pjsip_uri *reg_uri;
826
827 /* Need to parse reg_uri to get the elements: */
828 reg_uri = pjsip_parse_uri(acc->pool, cfg->reg_uri.ptr,
829 cfg->reg_uri.slen, 0);
830 if (reg_uri == NULL) {
831 status = PJSIP_EINVALIDURI;
832 pjsua_perror(THIS_FILE, "Invalid registrar URI", status);
833 goto on_return;
834 }
835
836 /* Registrar URI MUST be a SIP or SIPS: */
837 if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) &&
838 !PJSIP_URI_SCHEME_IS_SIPS(reg_uri))
839 {
840 status = PJSIP_EINVALIDSCHEME;
841 pjsua_perror(THIS_FILE, "Invalid registar URI", status);
842 goto on_return;
843 }
844
845 reg_sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri);
846 }
847
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500848 /* REGISTER header list */
849 if (update_hdr_list(acc->pool, &acc->cfg.reg_hdr_list, &cfg->reg_hdr_list)) {
850 update_reg = PJ_TRUE;
851 unreg_first = PJ_TRUE;
852 }
853
854 /* SUBSCRIBE header list */
855 update_hdr_list(acc->pool, &acc->cfg.sub_hdr_list, &cfg->sub_hdr_list);
856
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400857 /* Global outbound proxy */
858 global_route_crc = calc_proxy_crc(pjsua_var.ua_cfg.outbound_proxy,
859 pjsua_var.ua_cfg.outbound_proxy_cnt);
860 if (global_route_crc != acc->global_route_crc) {
861 pjsip_route_hdr *r;
862
863 /* Copy from global outbound proxies */
864 pj_list_init(&global_route);
865 r = pjsua_var.outbound_proxy.next;
866 while (r != &pjsua_var.outbound_proxy) {
867 pj_list_push_back(&global_route,
868 pjsip_hdr_shallow_clone(acc->pool, r));
869 r = r->next;
870 }
871 }
872
873 /* Account proxy */
874 local_route_crc = calc_proxy_crc(cfg->proxy, cfg->proxy_cnt);
875 if (local_route_crc != acc->local_route_crc) {
876 pjsip_route_hdr *r;
877 unsigned i;
878
879 /* Validate the local route and save it to temporary var */
880 pj_list_init(&local_route);
881 for (i=0; i<cfg->proxy_cnt; ++i) {
882 pj_str_t hname = { "Route", 5};
883
884 pj_strdup_with_null(acc->pool, &acc_proxy[i], &cfg->proxy[i]);
885 status = normalize_route_uri(acc->pool, &acc_proxy[i]);
886 if (status != PJ_SUCCESS)
887 goto on_return;
888 r = (pjsip_route_hdr*)
889 pjsip_parse_hdr(acc->pool, &hname, acc_proxy[i].ptr,
890 acc_proxy[i].slen, NULL);
891 if (r == NULL) {
892 status = PJSIP_EINVALIDURI;
893 pjsua_perror(THIS_FILE, "Invalid URI in account route set",
894 status);
895 goto on_return;
896 }
897
898 pj_list_push_back(&local_route, r);
899 }
900
901 /* Recalculate the CRC again after route URI normalization */
902 local_route_crc = calc_proxy_crc(acc_proxy, cfg->proxy_cnt);
903 }
904
905
906 /* == Apply the new config == */
907
908 /* Account ID. */
909 if (id_name_addr && id_sip_uri) {
910 pj_strdup_with_null(acc->pool, &acc->cfg.id, &cfg->id);
911 pj_strdup_with_null(acc->pool, &acc->display, &id_name_addr->display);
912 pj_strdup_with_null(acc->pool, &acc->user_part, &id_sip_uri->user);
913 pj_strdup_with_null(acc->pool, &acc->srv_domain, &id_sip_uri->host);
914 acc->srv_port = 0;
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500915 acc->is_sips = PJSIP_URI_SCHEME_IS_SIPS(id_name_addr);
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400916 update_reg = PJ_TRUE;
917 unreg_first = PJ_TRUE;
918 }
919
920 /* User data */
921 acc->cfg.user_data = cfg->user_data;
922
923 /* Priority */
924 if (acc->cfg.priority != cfg->priority) {
925 unsigned i;
926
927 acc->cfg.priority = cfg->priority;
928
929 /* Resort accounts priority */
930 for (i=0; i<pjsua_var.acc_cnt; ++i) {
931 if (pjsua_var.acc_ids[i] == acc_id)
932 break;
933 }
934 pj_assert(i < pjsua_var.acc_cnt);
935 pj_array_erase(pjsua_var.acc_ids, sizeof(acc_id),
936 pjsua_var.acc_cnt, i);
937 for (i=0; i<pjsua_var.acc_cnt; ++i) {
938 if (pjsua_var.acc[pjsua_var.acc_ids[i]].cfg.priority <
939 acc->cfg.priority)
940 {
941 break;
942 }
943 }
944 pj_array_insert(pjsua_var.acc_ids, sizeof(acc_id),
945 pjsua_var.acc_cnt, i, &acc_id);
946 }
947
948 /* MWI */
949 if (acc->cfg.mwi_enabled != cfg->mwi_enabled) {
950 acc->cfg.mwi_enabled = cfg->mwi_enabled;
951 update_mwi = PJ_TRUE;
952 }
953 if (acc->cfg.mwi_expires != cfg->mwi_expires && cfg->mwi_expires > 0) {
954 acc->cfg.mwi_expires = cfg->mwi_expires;
955 update_mwi = PJ_TRUE;
956 }
957
958 /* PIDF tuple ID */
959 if (pj_strcmp(&acc->cfg.pidf_tuple_id, &cfg->pidf_tuple_id))
960 pj_strdup_with_null(acc->pool, &acc->cfg.pidf_tuple_id,
961 &cfg->pidf_tuple_id);
962
963 /* Publish */
964 acc->cfg.publish_opt = cfg->publish_opt;
965 acc->cfg.unpublish_max_wait_time_msec = cfg->unpublish_max_wait_time_msec;
966 if (acc->cfg.publish_enabled != cfg->publish_enabled) {
967 acc->cfg.publish_enabled = cfg->publish_enabled;
968 if (!acc->cfg.publish_enabled)
969 pjsua_pres_unpublish(acc, 0);
970 else
971 update_reg = PJ_TRUE;
972 }
973
974 /* Force contact URI */
975 if (pj_strcmp(&acc->cfg.force_contact, &cfg->force_contact)) {
976 pj_strdup_with_null(acc->pool, &acc->cfg.force_contact,
977 &cfg->force_contact);
978 update_reg = PJ_TRUE;
979 unreg_first = PJ_TRUE;
980 }
981
982 /* Contact param */
983 if (pj_strcmp(&acc->cfg.contact_params, &cfg->contact_params)) {
984 pj_strdup_with_null(acc->pool, &acc->cfg.contact_params,
985 &cfg->contact_params);
986 update_reg = PJ_TRUE;
987 }
988
989 /* Contact URI params */
990 if (pj_strcmp(&acc->cfg.contact_uri_params, &cfg->contact_uri_params)) {
991 pj_strdup_with_null(acc->pool, &acc->cfg.contact_uri_params,
992 &cfg->contact_uri_params);
993 update_reg = PJ_TRUE;
994 }
995
996 /* Reliable provisional response */
997 acc->cfg.require_100rel = cfg->require_100rel;
998
999 /* Session timer */
1000 acc->cfg.use_timer = cfg->use_timer;
1001 acc->cfg.timer_setting = cfg->timer_setting;
1002
1003 /* Transport */
1004 if (acc->cfg.transport_id != cfg->transport_id) {
1005 acc->cfg.transport_id = cfg->transport_id;
1006 update_reg = PJ_TRUE;
1007 unreg_first = PJ_TRUE;
1008 }
1009
1010 /* Update keep-alive */
1011 if (acc->cfg.ka_interval != cfg->ka_interval ||
1012 pj_strcmp(&acc->cfg.ka_data, &cfg->ka_data))
1013 {
1014 pjsip_transport *ka_transport = acc->ka_transport;
1015
1016 if (acc->ka_timer.id) {
1017 pjsip_endpt_cancel_timer(pjsua_var.endpt, &acc->ka_timer);
1018 acc->ka_timer.id = PJ_FALSE;
1019 }
1020 if (acc->ka_transport) {
1021 pjsip_transport_dec_ref(acc->ka_transport);
1022 acc->ka_transport = NULL;
1023 }
1024
1025 acc->cfg.ka_interval = cfg->ka_interval;
1026
1027 if (cfg->ka_interval) {
1028 if (ka_transport) {
1029 /* Keep-alive has been running so we can just restart it */
1030 pj_time_val delay;
1031
1032 pjsip_transport_add_ref(ka_transport);
1033 acc->ka_transport = ka_transport;
1034
1035 acc->ka_timer.cb = &keep_alive_timer_cb;
1036 acc->ka_timer.user_data = (void*)acc;
1037
1038 delay.sec = acc->cfg.ka_interval;
1039 delay.msec = 0;
1040 status = pjsua_schedule_timer(&acc->ka_timer, &delay);
1041 if (status == PJ_SUCCESS) {
1042 acc->ka_timer.id = PJ_TRUE;
1043 } else {
1044 pjsip_transport_dec_ref(ka_transport);
1045 acc->ka_transport = NULL;
1046 pjsua_perror(THIS_FILE, "Error starting keep-alive timer",
1047 status);
1048 }
1049
1050 } else {
1051 /* Keep-alive has not been running, we need to (re)register
1052 * first.
1053 */
1054 update_reg = PJ_TRUE;
1055 }
1056 }
1057 }
1058
1059 if (pj_strcmp(&acc->cfg.ka_data, &cfg->ka_data))
1060 pj_strdup(acc->pool, &acc->cfg.ka_data, &cfg->ka_data);
1061#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
1062 acc->cfg.use_srtp = cfg->use_srtp;
1063 acc->cfg.srtp_secure_signaling = cfg->srtp_secure_signaling;
1064 acc->cfg.srtp_optional_dup_offer = cfg->srtp_optional_dup_offer;
1065#endif
1066
1067#if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0)
1068 acc->cfg.use_stream_ka = cfg->use_stream_ka;
1069#endif
1070
1071 /* Use of proxy */
1072 if (acc->cfg.reg_use_proxy != cfg->reg_use_proxy) {
1073 acc->cfg.reg_use_proxy = cfg->reg_use_proxy;
1074 update_reg = PJ_TRUE;
1075 unreg_first = PJ_TRUE;
1076 }
1077
1078 /* Global outbound proxy */
1079 if (global_route_crc != acc->global_route_crc) {
1080 unsigned i;
1081 pj_size_t rcnt;
1082
1083 /* Remove the outbound proxies from the route set */
1084 rcnt = pj_list_size(&acc->route_set);
1085 for (i=0; i < rcnt - acc->cfg.proxy_cnt; ++i) {
1086 pjsip_route_hdr *r = acc->route_set.next;
1087 pj_list_erase(r);
1088 }
1089
1090 /* Insert the outbound proxies to the beginning of route set */
1091 pj_list_merge_first(&acc->route_set, &global_route);
1092
1093 /* Update global route CRC */
1094 acc->global_route_crc = global_route_crc;
1095
1096 update_reg = PJ_TRUE;
1097 unreg_first = PJ_TRUE;
1098 }
1099
1100 /* Account proxy */
1101 if (local_route_crc != acc->local_route_crc) {
1102 unsigned i;
1103
1104 /* Remove the current account proxies from the route set */
1105 for (i=0; i < acc->cfg.proxy_cnt; ++i) {
1106 pjsip_route_hdr *r = acc->route_set.prev;
1107 pj_list_erase(r);
1108 }
1109
1110 /* Insert new proxy setting to the route set */
1111 pj_list_merge_last(&acc->route_set, &local_route);
1112
1113 /* Update the proxy setting */
1114 acc->cfg.proxy_cnt = cfg->proxy_cnt;
1115 for (i = 0; i < cfg->proxy_cnt; ++i)
1116 acc->cfg.proxy[i] = acc_proxy[i];
1117
1118 /* Update local route CRC */
1119 acc->local_route_crc = local_route_crc;
1120
1121 update_reg = PJ_TRUE;
1122 unreg_first = PJ_TRUE;
1123 }
1124
1125 /* Credential info */
1126 {
1127 unsigned i;
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001128 pj_bool_t cred_changed = PJ_FALSE;
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001129
1130 /* Selective update credential info. */
1131 for (i = 0; i < cfg->cred_count; ++i) {
1132 unsigned j;
1133 pjsip_cred_info ci;
1134
1135 /* Find if this credential is already listed */
1136 for (j = i; j < acc->cfg.cred_count; ++j) {
1137 if (pjsip_cred_info_cmp(&acc->cfg.cred_info[j],
1138 &cfg->cred_info[i]) == 0)
1139 {
1140 /* Found, but different index/position, swap */
1141 if (j != i) {
1142 ci = acc->cfg.cred_info[i];
1143 acc->cfg.cred_info[i] = acc->cfg.cred_info[j];
1144 acc->cfg.cred_info[j] = ci;
1145 }
1146 break;
1147 }
1148 }
1149
1150 /* Not found, insert this */
1151 if (j == acc->cfg.cred_count) {
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001152 cred_changed = PJ_TRUE;
1153
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001154 /* If account credential is full, discard the last one. */
1155 if (acc->cfg.cred_count == PJ_ARRAY_SIZE(acc->cfg.cred_info)) {
1156 pj_array_erase(acc->cfg.cred_info, sizeof(pjsip_cred_info),
1157 acc->cfg.cred_count, acc->cfg.cred_count-1);
1158 acc->cfg.cred_count--;
1159 }
1160
1161 /* Insert this */
1162 pjsip_cred_info_dup(acc->pool, &ci, &cfg->cred_info[i]);
1163 pj_array_insert(acc->cfg.cred_info, sizeof(pjsip_cred_info),
1164 acc->cfg.cred_count, i, &ci);
1165 }
1166 }
1167 acc->cfg.cred_count = cfg->cred_count;
1168
1169 /* Concatenate credentials from account config and global config */
1170 acc->cred_cnt = 0;
1171 for (i=0; i<acc->cfg.cred_count; ++i) {
1172 acc->cred[acc->cred_cnt++] = acc->cfg.cred_info[i];
1173 }
1174 for (i=0; i<pjsua_var.ua_cfg.cred_count &&
1175 acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i)
1176 {
1177 acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i];
1178 }
1179
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001180 if (cred_changed) {
1181 update_reg = PJ_TRUE;
1182 unreg_first = PJ_TRUE;
1183 }
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001184 }
1185
1186 /* Authentication preference */
1187 acc->cfg.auth_pref.initial_auth = cfg->auth_pref.initial_auth;
1188 if (pj_strcmp(&acc->cfg.auth_pref.algorithm, &cfg->auth_pref.algorithm)) {
1189 pj_strdup_with_null(acc->pool, &acc->cfg.auth_pref.algorithm,
1190 &cfg->auth_pref.algorithm);
1191 update_reg = PJ_TRUE;
1192 unreg_first = PJ_TRUE;
1193 }
1194
1195 /* Registration */
1196 if (acc->cfg.reg_timeout != cfg->reg_timeout) {
1197 acc->cfg.reg_timeout = cfg->reg_timeout;
1198 if (acc->regc != NULL)
1199 pjsip_regc_update_expires(acc->regc, acc->cfg.reg_timeout);
1200
1201 update_reg = PJ_TRUE;
1202 }
1203 acc->cfg.unreg_timeout = cfg->unreg_timeout;
1204 acc->cfg.allow_contact_rewrite = cfg->allow_contact_rewrite;
1205 acc->cfg.reg_retry_interval = cfg->reg_retry_interval;
1206 acc->cfg.reg_first_retry_interval = cfg->reg_first_retry_interval;
1207 acc->cfg.drop_calls_on_reg_fail = cfg->drop_calls_on_reg_fail;
1208 acc->cfg.register_on_acc_add = cfg->register_on_acc_add;
1209 if (acc->cfg.reg_delay_before_refresh != cfg->reg_delay_before_refresh) {
1210 acc->cfg.reg_delay_before_refresh = cfg->reg_delay_before_refresh;
1211 if (acc->regc != NULL)
1212 pjsip_regc_set_delay_before_refresh(acc->regc,
1213 cfg->reg_delay_before_refresh);
1214 }
1215
1216 /* Allow via rewrite */
1217 if (acc->cfg.allow_via_rewrite != cfg->allow_via_rewrite) {
1218 if (acc->regc != NULL) {
1219 if (cfg->allow_via_rewrite) {
1220 pjsip_regc_set_via_sent_by(acc->regc, &acc->via_addr,
1221 acc->via_tp);
1222 } else
1223 pjsip_regc_set_via_sent_by(acc->regc, NULL, NULL);
1224 }
1225 if (acc->publish_sess != NULL) {
1226 if (cfg->allow_via_rewrite) {
1227 pjsip_publishc_set_via_sent_by(acc->publish_sess,
1228 &acc->via_addr, acc->via_tp);
1229 } else
1230 pjsip_publishc_set_via_sent_by(acc->publish_sess, NULL, NULL);
1231 }
1232 acc->cfg.allow_via_rewrite = cfg->allow_via_rewrite;
1233 }
1234
1235 /* Normalize registration timeout and refresh delay */
1236 if (acc->cfg.reg_uri.slen ) {
1237 if (acc->cfg.reg_timeout == 0) {
1238 acc->cfg.reg_timeout = PJSUA_REG_INTERVAL;
1239 }
1240 if (acc->cfg.reg_delay_before_refresh == 0) {
1241 acc->cfg.reg_delay_before_refresh =
1242 PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH;
1243 }
1244 }
1245
1246 /* Registrar URI */
1247 if (pj_strcmp(&acc->cfg.reg_uri, &cfg->reg_uri)) {
1248 if (cfg->reg_uri.slen) {
1249 pj_strdup_with_null(acc->pool, &acc->cfg.reg_uri, &cfg->reg_uri);
1250 if (reg_sip_uri)
1251 acc->srv_port = reg_sip_uri->port;
1252 } else {
1253 /* Unregister if registration was set */
1254 if (acc->cfg.reg_uri.slen)
1255 pjsua_acc_set_registration(acc->index, PJ_FALSE);
1256 pj_bzero(&acc->cfg.reg_uri, sizeof(acc->cfg.reg_uri));
1257 }
1258 update_reg = PJ_TRUE;
1259 unreg_first = PJ_TRUE;
1260 }
1261
1262 /* SIP outbound setting */
1263 if (acc->cfg.use_rfc5626 != cfg->use_rfc5626 ||
1264 pj_strcmp(&acc->cfg.rfc5626_instance_id, &cfg->rfc5626_instance_id) ||
1265 pj_strcmp(&acc->cfg.rfc5626_reg_id, &cfg->rfc5626_reg_id))
1266 {
1267 update_reg = PJ_TRUE;
1268 }
1269
1270 /* Video settings */
1271 acc->cfg.vid_in_auto_show = cfg->vid_in_auto_show;
1272 acc->cfg.vid_out_auto_transmit = cfg->vid_out_auto_transmit;
1273 acc->cfg.vid_wnd_flags = cfg->vid_wnd_flags;
1274 acc->cfg.vid_cap_dev = cfg->vid_cap_dev;
1275 acc->cfg.vid_rend_dev = cfg->vid_rend_dev;
1276 acc->cfg.vid_stream_rc_cfg = cfg->vid_stream_rc_cfg;
1277
1278 /* Media settings */
1279 if (pj_stricmp(&acc->cfg.rtp_cfg.public_addr, &cfg->rtp_cfg.public_addr) ||
1280 pj_stricmp(&acc->cfg.rtp_cfg.bound_addr, &cfg->rtp_cfg.bound_addr))
1281 {
1282 pjsua_transport_config_dup(acc->pool, &acc->cfg.rtp_cfg,
1283 &cfg->rtp_cfg);
1284 } else {
1285 /* ..to save memory by not using the pool */
1286 acc->cfg.rtp_cfg = cfg->rtp_cfg;
1287 }
1288
1289 acc->cfg.ipv6_media_use = cfg->ipv6_media_use;
1290
1291 /* STUN and Media customization */
1292 if (acc->cfg.sip_stun_use != cfg->sip_stun_use) {
1293 acc->cfg.sip_stun_use = cfg->sip_stun_use;
1294 update_reg = PJ_TRUE;
1295 }
1296 acc->cfg.media_stun_use = cfg->media_stun_use;
1297
1298 /* ICE settings */
1299 acc->cfg.ice_cfg_use = cfg->ice_cfg_use;
1300 switch (acc->cfg.ice_cfg_use) {
1301 case PJSUA_ICE_CONFIG_USE_DEFAULT:
1302 /* Copy ICE settings from media settings so that we don't need to
1303 * check the media config if we look for ICE config.
1304 */
1305 pjsua_ice_config_from_media_config(NULL, &acc->cfg.ice_cfg,
1306 &pjsua_var.media_cfg);
1307 break;
1308 case PJSUA_ICE_CONFIG_USE_CUSTOM:
1309 pjsua_ice_config_dup(acc->pool, &acc->cfg.ice_cfg, &cfg->ice_cfg);
1310 break;
1311 }
1312
1313 /* TURN settings */
1314 acc->cfg.turn_cfg_use = cfg->turn_cfg_use;
1315 switch (acc->cfg.turn_cfg_use) {
1316 case PJSUA_TURN_CONFIG_USE_DEFAULT:
1317 /* Copy TURN settings from media settings so that we don't need to
1318 * check the media config if we look for TURN config.
1319 */
1320 pjsua_turn_config_from_media_config(NULL, &acc->cfg.turn_cfg,
1321 &pjsua_var.media_cfg);
1322 break;
1323 case PJSUA_TURN_CONFIG_USE_CUSTOM:
1324 pjsua_turn_config_dup(acc->pool, &acc->cfg.turn_cfg,
1325 &cfg->turn_cfg);
1326 break;
1327 }
1328
1329 acc->cfg.use_srtp = cfg->use_srtp;
1330
1331 /* Call hold type */
1332 acc->cfg.call_hold_type = cfg->call_hold_type;
1333
1334 /* Unregister first */
1335 if (unreg_first) {
1336 pjsua_acc_set_registration(acc->index, PJ_FALSE);
1337 if (acc->regc != NULL) {
1338 pjsip_regc_destroy(acc->regc);
1339 acc->regc = NULL;
1340 acc->contact.slen = 0;
1341 acc->reg_mapped_addr.slen = 0;
1342 }
1343 }
1344
1345 /* Update registration */
1346 if (update_reg) {
1347 /* If accounts has registration enabled, start registration */
1348 if (acc->cfg.reg_uri.slen)
1349 pjsua_acc_set_registration(acc->index, PJ_TRUE);
1350 }
1351
1352 /* Update MWI subscription */
1353 if (update_mwi) {
1354 pjsua_start_mwi(acc_id, PJ_TRUE);
1355 }
1356
1357on_return:
1358 PJSUA_UNLOCK();
1359 pj_log_pop_indent();
1360 return status;
1361}
1362
1363
1364/*
1365 * Modify account's presence status to be advertised to remote/presence
1366 * subscribers.
1367 */
1368PJ_DEF(pj_status_t) pjsua_acc_set_online_status( pjsua_acc_id acc_id,
1369 pj_bool_t is_online)
1370{
1371 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
1372 PJ_EINVAL);
1373 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
1374
1375 PJ_LOG(4,(THIS_FILE, "Acc %d: setting online status to %d..",
1376 acc_id, is_online));
1377 pj_log_push_indent();
1378
1379 pjsua_var.acc[acc_id].online_status = is_online;
1380 pj_bzero(&pjsua_var.acc[acc_id].rpid, sizeof(pjrpid_element));
1381 pjsua_pres_update_acc(acc_id, PJ_FALSE);
1382
1383 pj_log_pop_indent();
1384 return PJ_SUCCESS;
1385}
1386
1387
1388/*
1389 * Set online status with extended information
1390 */
1391PJ_DEF(pj_status_t) pjsua_acc_set_online_status2( pjsua_acc_id acc_id,
1392 pj_bool_t is_online,
1393 const pjrpid_element *pr)
1394{
1395 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
1396 PJ_EINVAL);
1397 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
1398
1399 PJ_LOG(4,(THIS_FILE, "Acc %d: setting online status to %d..",
1400 acc_id, is_online));
1401 pj_log_push_indent();
1402
1403 PJSUA_LOCK();
1404 pjsua_var.acc[acc_id].online_status = is_online;
1405 pjrpid_element_dup(pjsua_var.acc[acc_id].pool, &pjsua_var.acc[acc_id].rpid, pr);
1406 PJSUA_UNLOCK();
1407
1408 pjsua_pres_update_acc(acc_id, PJ_TRUE);
1409 pj_log_pop_indent();
1410
1411 return PJ_SUCCESS;
1412}
1413
1414/* Create reg_contact, mainly for SIP outbound */
1415static void update_regc_contact(pjsua_acc *acc)
1416{
1417 pjsua_acc_config *acc_cfg = &acc->cfg;
1418 pj_bool_t need_outbound = PJ_FALSE;
1419 const pj_str_t tcp_param = pj_str(";transport=tcp");
1420 const pj_str_t tls_param = pj_str(";transport=tls");
1421
1422 if (!acc_cfg->use_rfc5626)
1423 goto done;
1424
1425 /* Check if outbound has been requested and rejected */
1426 if (acc->rfc5626_status == OUTBOUND_NA)
1427 goto done;
1428
1429 if (pj_stristr(&acc->contact, &tcp_param)==NULL &&
1430 pj_stristr(&acc->contact, &tls_param)==NULL)
1431 {
1432 /* Currently we can only do SIP outbound for TCP
1433 * and TLS.
1434 */
1435 goto done;
1436 }
1437
1438 /* looks like we can use outbound */
1439 need_outbound = PJ_TRUE;
1440
1441done:
1442 if (!need_outbound) {
1443 /* Outbound is not needed/wanted for the account. acc->reg_contact
1444 * is set to the same as acc->contact.
1445 */
1446 acc->reg_contact = acc->contact;
1447 acc->rfc5626_status = OUTBOUND_NA;
1448 } else {
1449 /* Need to use outbound, append the contact with +sip.instance and
1450 * reg-id parameters.
1451 */
1452 pj_ssize_t len;
1453 pj_str_t reg_contact;
1454
1455 acc->rfc5626_status = OUTBOUND_WANTED;
1456 len = acc->contact.slen + acc->rfc5626_instprm.slen +
1457 acc->rfc5626_regprm.slen;
1458 reg_contact.ptr = (char*) pj_pool_alloc(acc->pool, len);
1459
1460 pj_strcpy(&reg_contact, &acc->contact);
1461 pj_strcat(&reg_contact, &acc->rfc5626_regprm);
1462 pj_strcat(&reg_contact, &acc->rfc5626_instprm);
1463
1464 acc->reg_contact = reg_contact;
1465
1466 PJ_LOG(4,(THIS_FILE,
1467 "Contact for acc %d updated for SIP outbound: %.*s",
1468 acc->index,
1469 (int)acc->reg_contact.slen,
1470 acc->reg_contact.ptr));
1471 }
1472}
1473
1474/* Check if IP is private IP address */
1475static pj_bool_t is_private_ip(const pj_str_t *addr)
1476{
1477 const pj_str_t private_net[] =
1478 {
1479 { "10.", 3 },
1480 { "127.", 4 },
1481 { "172.16.", 7 }, { "172.17.", 7 }, { "172.18.", 7 }, { "172.19.", 7 },
1482 { "172.20.", 7 }, { "172.21.", 7 }, { "172.22.", 7 }, { "172.23.", 7 },
1483 { "172.24.", 7 }, { "172.25.", 7 }, { "172.26.", 7 }, { "172.27.", 7 },
1484 { "172.28.", 7 }, { "172.29.", 7 }, { "172.30.", 7 }, { "172.31.", 7 },
1485 { "192.168.", 8 }
1486 };
1487 unsigned i;
1488
1489 for (i=0; i<PJ_ARRAY_SIZE(private_net); ++i) {
1490 if (pj_strncmp(addr, &private_net[i], private_net[i].slen)==0)
1491 return PJ_TRUE;
1492 }
1493
1494 return PJ_FALSE;
1495}
1496
1497/* Update NAT address from the REGISTER response */
1498static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001499 int contact_rewrite_method,
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001500 struct pjsip_regc_cbparam *param)
1501{
1502 pjsip_transport *tp;
1503 const pj_str_t *via_addr;
1504 pj_pool_t *pool;
1505 int rport;
1506 pjsip_sip_uri *uri;
1507 pjsip_via_hdr *via;
1508 pj_sockaddr contact_addr;
1509 pj_sockaddr recv_addr;
1510 pj_status_t status;
1511 pj_bool_t matched;
1512 pj_str_t srv_ip;
1513 pjsip_contact_hdr *contact_hdr;
1514 const pj_str_t STR_CONTACT = { "Contact", 7 };
1515
1516 tp = param->rdata->tp_info.transport;
1517
1518 /* Get the received and rport info */
1519 via = param->rdata->msg_info.via;
1520 if (via->rport_param < 1) {
1521 /* Remote doesn't support rport */
1522 rport = via->sent_by.port;
1523 if (rport==0) {
1524 pjsip_transport_type_e tp_type;
1525 tp_type = (pjsip_transport_type_e) tp->key.type;
1526 rport = pjsip_transport_get_default_port_for_type(tp_type);
1527 }
1528 } else
1529 rport = via->rport_param;
1530
1531 if (via->recvd_param.slen != 0)
1532 via_addr = &via->recvd_param;
1533 else
1534 via_addr = &via->sent_by.host;
1535
1536 /* If allow_via_rewrite is enabled, we save the Via "received" address
1537 * from the response, if either of the following condition is met:
1538 * - the Via "received" address differs from saved one (or we haven't
1539 * saved any yet)
1540 * - transport is different
1541 * - only the port has changed, AND either the received address is
1542 * public IP or allow_contact_rewrite is 2
1543 */
1544 if (acc->cfg.allow_via_rewrite &&
1545 (pj_strcmp(&acc->via_addr.host, via_addr) || acc->via_tp != tp ||
1546 (acc->via_addr.port != rport &&
1547 (!is_private_ip(via_addr) || acc->cfg.allow_contact_rewrite == 2))))
1548 {
1549 if (pj_strcmp(&acc->via_addr.host, via_addr))
1550 pj_strdup(acc->pool, &acc->via_addr.host, via_addr);
1551 acc->via_addr.port = rport;
1552 acc->via_tp = tp;
1553 pjsip_regc_set_via_sent_by(acc->regc, &acc->via_addr, acc->via_tp);
1554 if (acc->publish_sess != NULL) {
1555 pjsip_publishc_set_via_sent_by(acc->publish_sess,
1556 &acc->via_addr, acc->via_tp);
1557 }
1558 }
1559
1560 /* Save mapped address if needed */
1561 if (acc->cfg.allow_sdp_nat_rewrite &&
1562 pj_strcmp(&acc->reg_mapped_addr, via_addr))
1563 {
1564 pj_strdup(acc->pool, &acc->reg_mapped_addr, via_addr);
1565 }
1566
1567 /* Only update if account is configured to auto-update */
1568 if (acc->cfg.allow_contact_rewrite == PJ_FALSE)
1569 return PJ_FALSE;
1570
1571 /* If SIP outbound is active, no need to update */
1572 if (acc->rfc5626_status == OUTBOUND_ACTIVE) {
1573 PJ_LOG(4,(THIS_FILE, "Acc %d has SIP outbound active, no need to "
1574 "update registration Contact", acc->index));
1575 return PJ_FALSE;
1576 }
1577
1578#if 0
1579 // Always update
1580 // See http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/2008-March/002178.html
1581
1582 /* For UDP, only update if STUN is enabled (for now).
1583 * For TCP/TLS, always check.
1584 */
1585 if ((tp->key.type == PJSIP_TRANSPORT_UDP &&
1586 (pjsua_var.ua_cfg.stun_domain.slen != 0 ||
1587 (pjsua_var.ua_cfg.stun_host.slen != 0)) ||
1588 (tp->key.type == PJSIP_TRANSPORT_TCP) ||
1589 (tp->key.type == PJSIP_TRANSPORT_TLS))
1590 {
1591 /* Yes we will check */
1592 } else {
1593 return PJ_FALSE;
1594 }
1595#endif
1596
1597 /* Compare received and rport with the URI in our registration */
1598 pool = pjsua_pool_create("tmp", 512, 512);
1599 contact_hdr = (pjsip_contact_hdr*)
1600 pjsip_parse_hdr(pool, &STR_CONTACT, acc->contact.ptr,
1601 acc->contact.slen, NULL);
1602 pj_assert(contact_hdr != NULL);
1603 uri = (pjsip_sip_uri*) contact_hdr->uri;
1604 pj_assert(uri != NULL);
1605 uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
1606
1607 if (uri->port == 0) {
1608 pjsip_transport_type_e tp_type;
1609 tp_type = (pjsip_transport_type_e) tp->key.type;
1610 uri->port = pjsip_transport_get_default_port_for_type(tp_type);
1611 }
1612
1613 /* Convert IP address strings into sockaddr for comparison.
1614 * (http://trac.pjsip.org/repos/ticket/863)
1615 */
1616 status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &uri->host,
1617 &contact_addr);
1618 if (status == PJ_SUCCESS)
1619 status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, via_addr,
1620 &recv_addr);
1621 if (status == PJ_SUCCESS) {
1622 /* Compare the addresses as sockaddr according to the ticket above */
1623 matched = (uri->port == rport &&
1624 pj_sockaddr_cmp(&contact_addr, &recv_addr)==0);
1625 } else {
1626 /* Compare the addresses as string, as before */
1627 matched = (uri->port == rport &&
1628 pj_stricmp(&uri->host, via_addr)==0);
1629 }
1630
1631 if (matched) {
1632 /* Address doesn't change */
1633 pj_pool_release(pool);
1634 return PJ_FALSE;
1635 }
1636
1637 /* Get server IP */
1638 srv_ip = pj_str(param->rdata->pkt_info.src_name);
1639
1640 /* At this point we've detected that the address as seen by registrar.
1641 * has changed.
1642 */
1643
1644 /* Do not switch if both Contact and server's IP address are
1645 * public but response contains private IP. A NAT in the middle
1646 * might have messed up with the SIP packets. See:
1647 * http://trac.pjsip.org/repos/ticket/643
1648 *
1649 * This exception can be disabled by setting allow_contact_rewrite
1650 * to 2. In this case, the switch will always be done whenever there
1651 * is difference in the IP address in the response.
1652 */
1653 if (acc->cfg.allow_contact_rewrite != 2 && !is_private_ip(&uri->host) &&
1654 !is_private_ip(&srv_ip) && is_private_ip(via_addr))
1655 {
1656 /* Don't switch */
1657 pj_pool_release(pool);
1658 return PJ_FALSE;
1659 }
1660
1661 /* Also don't switch if only the port number part is different, and
1662 * the Via received address is private.
1663 * See http://trac.pjsip.org/repos/ticket/864
1664 */
1665 if (acc->cfg.allow_contact_rewrite != 2 &&
1666 pj_sockaddr_cmp(&contact_addr, &recv_addr)==0 &&
1667 is_private_ip(via_addr))
1668 {
1669 /* Don't switch */
1670 pj_pool_release(pool);
1671 return PJ_FALSE;
1672 }
1673
1674 PJ_LOG(3,(THIS_FILE, "IP address change detected for account %d "
1675 "(%.*s:%d --> %.*s:%d). Updating registration "
1676 "(using method %d)",
1677 acc->index,
1678 (int)uri->host.slen,
1679 uri->host.ptr,
1680 uri->port,
1681 (int)via_addr->slen,
1682 via_addr->ptr,
1683 rport,
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001684 contact_rewrite_method));
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001685
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001686 pj_assert(contact_rewrite_method == PJSUA_CONTACT_REWRITE_UNREGISTER ||
1687 contact_rewrite_method == PJSUA_CONTACT_REWRITE_NO_UNREG ||
1688 contact_rewrite_method == PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE);
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001689
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001690 if (contact_rewrite_method == PJSUA_CONTACT_REWRITE_UNREGISTER) {
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001691 /* Unregister current contact */
1692 pjsua_acc_set_registration(acc->index, PJ_FALSE);
1693 if (acc->regc != NULL) {
1694 pjsip_regc_destroy(acc->regc);
1695 acc->regc = NULL;
1696 acc->contact.slen = 0;
1697 }
1698 }
1699
1700 /*
1701 * Build new Contact header
1702 */
1703 {
1704 const char *ob = ";ob";
1705 char *tmp;
1706 const char *beginquote, *endquote;
1707 char transport_param[32];
1708 int len;
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001709 pj_bool_t secure;
1710
1711 secure = pjsip_transport_get_flag_from_type(tp->key.type) &
1712 PJSIP_TRANSPORT_SECURE;
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001713
1714 /* Enclose IPv6 address in square brackets */
1715 if (tp->key.type & PJSIP_TRANSPORT_IPV6) {
1716 beginquote = "[";
1717 endquote = "]";
1718 } else {
1719 beginquote = endquote = "";
1720 }
1721
1722 /* Don't add transport parameter if it's UDP */
1723 if (tp->key.type != PJSIP_TRANSPORT_UDP &&
1724 tp->key.type != PJSIP_TRANSPORT_UDP6)
1725 {
1726 pj_ansi_snprintf(transport_param, sizeof(transport_param),
1727 ";transport=%s",
1728 pjsip_transport_get_type_name(
1729 (pjsip_transport_type_e)tp->key.type));
1730 } else {
1731 transport_param[0] = '\0';
1732 }
1733
1734 tmp = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
1735 len = pj_ansi_snprintf(tmp, PJSIP_MAX_URL_SIZE,
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001736 "<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s",
1737 ((secure && acc->is_sips)? "sips" : "sip"),
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001738 (int)acc->user_part.slen,
1739 acc->user_part.ptr,
1740 (acc->user_part.slen? "@" : ""),
1741 beginquote,
1742 (int)via_addr->slen,
1743 via_addr->ptr,
1744 endquote,
1745 rport,
1746 transport_param,
1747 (int)acc->cfg.contact_uri_params.slen,
1748 acc->cfg.contact_uri_params.ptr,
1749 (acc->cfg.use_rfc5626? ob: ""),
1750 (int)acc->cfg.contact_params.slen,
1751 acc->cfg.contact_params.ptr);
1752 if (len < 1) {
1753 PJ_LOG(1,(THIS_FILE, "URI too long"));
1754 pj_pool_release(pool);
1755 return PJ_FALSE;
1756 }
1757 pj_strdup2_with_null(acc->pool, &acc->contact, tmp);
1758
1759 update_regc_contact(acc);
1760
1761 /* Always update, by http://trac.pjsip.org/repos/ticket/864. */
1762 /* Since the Via address will now be overwritten to the correct
1763 * address by https://trac.pjsip.org/repos/ticket/1537, we do
1764 * not need to update the transport address.
1765 */
1766 /*
1767 pj_strdup_with_null(tp->pool, &tp->local_name.host, via_addr);
1768 tp->local_name.port = rport;
1769 */
1770
1771 }
1772
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001773 if (contact_rewrite_method == PJSUA_CONTACT_REWRITE_NO_UNREG &&
1774 acc->regc != NULL)
1775 {
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001776 pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact);
1777 }
1778
1779 /* Perform new registration */
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001780 if (contact_rewrite_method < PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE) {
1781 pjsua_acc_set_registration(acc->index, PJ_TRUE);
1782 }
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001783
1784 pj_pool_release(pool);
1785
1786 return PJ_TRUE;
1787}
1788
1789/* Check and update Service-Route header */
1790void update_service_route(pjsua_acc *acc, pjsip_rx_data *rdata)
1791{
1792 pjsip_generic_string_hdr *hsr = NULL;
1793 pjsip_route_hdr *hr, *h;
1794 const pj_str_t HNAME = { "Service-Route", 13 };
1795 const pj_str_t HROUTE = { "Route", 5 };
1796 pjsip_uri *uri[PJSUA_ACC_MAX_PROXIES];
1797 unsigned i, uri_cnt = 0;
1798 pj_size_t rcnt;
1799
1800 /* Find and parse Service-Route headers */
1801 for (;;) {
1802 char saved;
1803 int parsed_len;
1804
1805 /* Find Service-Route header */
1806 hsr = (pjsip_generic_string_hdr*)
1807 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &HNAME, hsr);
1808 if (!hsr)
1809 break;
1810
1811 /* Parse as Route header since the syntax is similar. This may
1812 * return more than one headers.
1813 */
1814 saved = hsr->hvalue.ptr[hsr->hvalue.slen];
1815 hsr->hvalue.ptr[hsr->hvalue.slen] = '\0';
1816 hr = (pjsip_route_hdr*)
1817 pjsip_parse_hdr(rdata->tp_info.pool, &HROUTE, hsr->hvalue.ptr,
1818 hsr->hvalue.slen, &parsed_len);
1819 hsr->hvalue.ptr[hsr->hvalue.slen] = saved;
1820
1821 if (hr == NULL) {
1822 /* Error */
1823 PJ_LOG(1,(THIS_FILE, "Error parsing Service-Route header"));
1824 return;
1825 }
1826
1827 /* Save each URI in the result */
1828 h = hr;
1829 do {
1830 if (!PJSIP_URI_SCHEME_IS_SIP(h->name_addr.uri) &&
1831 !PJSIP_URI_SCHEME_IS_SIPS(h->name_addr.uri))
1832 {
1833 PJ_LOG(1,(THIS_FILE,"Error: non SIP URI in Service-Route: %.*s",
1834 (int)hsr->hvalue.slen, hsr->hvalue.ptr));
1835 return;
1836 }
1837
1838 uri[uri_cnt++] = h->name_addr.uri;
1839 h = h->next;
1840 } while (h != hr && uri_cnt != PJ_ARRAY_SIZE(uri));
1841
1842 if (h != hr) {
1843 PJ_LOG(1,(THIS_FILE, "Error: too many Service-Route headers"));
1844 return;
1845 }
1846
1847 /* Prepare to find next Service-Route header */
1848 hsr = hsr->next;
1849 if ((void*)hsr == (void*)&rdata->msg_info.msg->hdr)
1850 break;
1851 }
1852
1853 if (uri_cnt == 0)
1854 return;
1855
1856 /*
1857 * Update account's route set
1858 */
1859
1860 /* First remove all routes which are not the outbound proxies */
1861 rcnt = pj_list_size(&acc->route_set);
1862 if (rcnt != pjsua_var.ua_cfg.outbound_proxy_cnt + acc->cfg.proxy_cnt) {
1863 for (i=pjsua_var.ua_cfg.outbound_proxy_cnt + acc->cfg.proxy_cnt,
1864 hr=acc->route_set.prev;
1865 i<rcnt;
1866 ++i)
1867 {
1868 pjsip_route_hdr *prev = hr->prev;
1869 pj_list_erase(hr);
1870 hr = prev;
1871 }
1872 }
1873
1874 /* Then append the Service-Route URIs */
1875 for (i=0; i<uri_cnt; ++i) {
1876 hr = pjsip_route_hdr_create(acc->pool);
1877 hr->name_addr.uri = (pjsip_uri*)pjsip_uri_clone(acc->pool, uri[i]);
1878 pj_list_push_back(&acc->route_set, hr);
1879 }
1880
1881 /* Done */
1882
1883 PJ_LOG(4,(THIS_FILE, "Service-Route updated for acc %d with %d URI(s)",
1884 acc->index, uri_cnt));
1885}
1886
1887
1888/* Keep alive timer callback */
1889static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
1890{
1891 pjsua_acc *acc;
1892 pjsip_tpselector tp_sel;
1893 pj_time_val delay;
1894 char addrtxt[PJ_INET6_ADDRSTRLEN];
1895 pj_status_t status;
1896
1897 PJ_UNUSED_ARG(th);
1898
1899 PJSUA_LOCK();
1900
1901 te->id = PJ_FALSE;
1902
1903 acc = (pjsua_acc*) te->user_data;
1904
1905 /* Select the transport to send the packet */
1906 pj_bzero(&tp_sel, sizeof(tp_sel));
1907 tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT;
1908 tp_sel.u.transport = acc->ka_transport;
1909
1910 PJ_LOG(5,(THIS_FILE,
1911 "Sending %d bytes keep-alive packet for acc %d to %s",
1912 acc->cfg.ka_data.slen, acc->index,
1913 pj_sockaddr_print(&acc->ka_target, addrtxt, sizeof(addrtxt),3)));
1914
1915 /* Send raw packet */
1916 status = pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
1917 PJSIP_TRANSPORT_UDP, &tp_sel,
1918 NULL, acc->cfg.ka_data.ptr,
1919 acc->cfg.ka_data.slen,
1920 &acc->ka_target, acc->ka_target_len,
1921 NULL, NULL);
1922
1923 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
1924 pjsua_perror(THIS_FILE, "Error sending keep-alive packet", status);
1925 }
1926
1927 /* Check just in case keep-alive has been disabled. This shouldn't happen
1928 * though as when ka_interval is changed this timer should have been
1929 * cancelled.
1930 */
1931 if (acc->cfg.ka_interval == 0)
1932 goto on_return;
1933
1934 /* Reschedule next timer */
1935 delay.sec = acc->cfg.ka_interval;
1936 delay.msec = 0;
1937 status = pjsip_endpt_schedule_timer(pjsua_var.endpt, te, &delay);
1938 if (status == PJ_SUCCESS) {
1939 te->id = PJ_TRUE;
1940 } else {
1941 pjsua_perror(THIS_FILE, "Error starting keep-alive timer", status);
1942 }
1943
1944on_return:
1945 PJSUA_UNLOCK();
1946}
1947
1948
1949/* Update keep-alive for the account */
1950static void update_keep_alive(pjsua_acc *acc, pj_bool_t start,
1951 struct pjsip_regc_cbparam *param)
1952{
1953 /* In all cases, stop keep-alive timer if it's running. */
1954 if (acc->ka_timer.id) {
1955 pjsip_endpt_cancel_timer(pjsua_var.endpt, &acc->ka_timer);
1956 acc->ka_timer.id = PJ_FALSE;
1957
1958 pjsip_transport_dec_ref(acc->ka_transport);
1959 acc->ka_transport = NULL;
1960 }
1961
1962 if (start) {
1963 pj_time_val delay;
1964 pj_status_t status;
1965
1966 /* Only do keep-alive if:
1967 * - ka_interval is not zero in the account, and
1968 * - transport is UDP.
1969 *
1970 * Previously we only enabled keep-alive when STUN is enabled, since
1971 * we thought that keep-alive is only needed in Internet situation.
1972 * But it has been discovered that Windows Firewall on WinXP also
1973 * needs to be kept-alive, otherwise incoming packets will be dropped.
1974 * So because of this, now keep-alive is always enabled for UDP,
1975 * regardless of whether STUN is enabled or not.
1976 *
1977 * Note that this applies only for UDP. For TCP/TLS, the keep-alive
1978 * is done by the transport layer.
1979 */
1980 if (/*pjsua_var.stun_srv.ipv4.sin_family == 0 ||*/
1981 acc->cfg.ka_interval == 0 ||
1982 param->rdata->tp_info.transport->key.type != PJSIP_TRANSPORT_UDP)
1983 {
1984 /* Keep alive is not necessary */
1985 return;
1986 }
1987
1988 /* Save transport and destination address. */
1989 acc->ka_transport = param->rdata->tp_info.transport;
1990 pjsip_transport_add_ref(acc->ka_transport);
1991
1992 /* https://trac.pjsip.org/repos/ticket/1607:
1993 * Calculate the destination address from the original request. Some
1994 * (broken) servers send the response using different source address
1995 * than the one that receives the request, which is forbidden by RFC
1996 * 3581.
1997 */
1998 {
1999 pjsip_transaction *tsx;
2000 pjsip_tx_data *req;
2001
2002 tsx = pjsip_rdata_get_tsx(param->rdata);
2003 PJ_ASSERT_ON_FAIL(tsx, return);
2004
2005 req = tsx->last_tx;
2006
2007 pj_memcpy(&acc->ka_target, &req->tp_info.dst_addr,
2008 req->tp_info.dst_addr_len);
2009 acc->ka_target_len = req->tp_info.dst_addr_len;
2010 }
2011
2012 /* Setup and start the timer */
2013 acc->ka_timer.cb = &keep_alive_timer_cb;
2014 acc->ka_timer.user_data = (void*)acc;
2015
2016 delay.sec = acc->cfg.ka_interval;
2017 delay.msec = 0;
2018 status = pjsip_endpt_schedule_timer(pjsua_var.endpt, &acc->ka_timer,
2019 &delay);
2020 if (status == PJ_SUCCESS) {
2021 acc->ka_timer.id = PJ_TRUE;
2022 PJ_LOG(4,(THIS_FILE, "Keep-alive timer started for acc %d, "
2023 "destination:%s:%d, interval:%ds",
2024 acc->index,
2025 param->rdata->pkt_info.src_name,
2026 param->rdata->pkt_info.src_port,
2027 acc->cfg.ka_interval));
2028 } else {
2029 acc->ka_timer.id = PJ_FALSE;
2030 pjsip_transport_dec_ref(acc->ka_transport);
2031 acc->ka_transport = NULL;
2032 pjsua_perror(THIS_FILE, "Error starting keep-alive timer", status);
2033 }
2034 }
2035}
2036
2037
2038/* Update the status of SIP outbound registration request */
2039static void update_rfc5626_status(pjsua_acc *acc, pjsip_rx_data *rdata)
2040{
2041 pjsip_require_hdr *hreq;
2042 const pj_str_t STR_OUTBOUND = {"outbound", 8};
2043 unsigned i;
2044
2045 if (acc->rfc5626_status == OUTBOUND_UNKNOWN) {
2046 goto on_return;
2047 }
2048
2049 hreq = rdata->msg_info.require;
2050 if (!hreq) {
2051 acc->rfc5626_status = OUTBOUND_NA;
2052 goto on_return;
2053 }
2054
2055 for (i=0; i<hreq->count; ++i) {
2056 if (pj_stricmp(&hreq->values[i], &STR_OUTBOUND)==0) {
2057 acc->rfc5626_status = OUTBOUND_ACTIVE;
2058 goto on_return;
2059 }
2060 }
2061
2062 /* Server does not support outbound */
2063 acc->rfc5626_status = OUTBOUND_NA;
2064
2065on_return:
2066 if (acc->rfc5626_status != OUTBOUND_ACTIVE) {
2067 acc->reg_contact = acc->contact;
2068 }
2069 PJ_LOG(4,(THIS_FILE, "SIP outbound status for acc %d is %s",
2070 acc->index, (acc->rfc5626_status==OUTBOUND_ACTIVE?
2071 "active": "not active")));
2072}
2073
Alexandre Lision94f06ba2013-12-09 16:28:33 -05002074static void regc_tsx_cb(struct pjsip_regc_tsx_cb_param *param)
2075{
2076 pjsua_acc *acc = (pjsua_acc*) param->cbparam.token;
2077
2078 PJSUA_LOCK();
2079
2080 if (param->cbparam.regc != acc->regc) {
2081 PJSUA_UNLOCK();
2082 return;
2083 }
2084
2085 pj_log_push_indent();
2086
2087 if ((acc->cfg.contact_rewrite_method &
2088 PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE) ==
2089 PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE &&
2090 param->cbparam.code >= 400 &&
2091 param->cbparam.rdata)
2092 {
2093 if (acc_check_nat_addr(acc, PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE,
2094 &param->cbparam))
2095 {
2096 param->contact_cnt = 1;
2097 param->contact[0] = acc->reg_contact;
2098 }
2099 }
2100
2101 PJSUA_UNLOCK();
2102 pj_log_pop_indent();
2103}
2104
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002105/*
2106 * This callback is called by pjsip_regc when outgoing register
2107 * request has completed.
2108 */
2109static void regc_cb(struct pjsip_regc_cbparam *param)
2110{
2111
2112 pjsua_acc *acc = (pjsua_acc*) param->token;
2113
2114 PJSUA_LOCK();
2115
2116 if (param->regc != acc->regc) {
2117 PJSUA_UNLOCK();
2118 return;
2119 }
2120
2121 pj_log_push_indent();
2122
2123 /*
2124 * Print registration status.
2125 */
2126 if (param->status!=PJ_SUCCESS) {
2127 pjsua_perror(THIS_FILE, "SIP registration error",
2128 param->status);
2129 pjsip_regc_destroy(acc->regc);
2130 acc->regc = NULL;
2131 acc->contact.slen = 0;
2132 acc->reg_mapped_addr.slen = 0;
2133
2134 /* Stop keep-alive timer if any. */
2135 update_keep_alive(acc, PJ_FALSE, NULL);
2136
2137 } else if (param->code < 0 || param->code >= 300) {
2138 PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%.*s)",
2139 param->code,
2140 (int)param->reason.slen, param->reason.ptr));
2141 pjsip_regc_destroy(acc->regc);
2142 acc->regc = NULL;
2143 acc->contact.slen = 0;
2144 acc->reg_mapped_addr.slen = 0;
2145
2146 /* Stop keep-alive timer if any. */
2147 update_keep_alive(acc, PJ_FALSE, NULL);
2148
2149 } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
2150
2151 /* Update auto registration flag */
2152 acc->auto_rereg.active = PJ_FALSE;
2153 acc->auto_rereg.attempt_cnt = 0;
2154
2155 if (param->expiration < 1) {
2156 pjsip_regc_destroy(acc->regc);
2157 acc->regc = NULL;
2158 acc->contact.slen = 0;
2159 acc->reg_mapped_addr.slen = 0;
2160
2161 /* Stop keep-alive timer if any. */
2162 update_keep_alive(acc, PJ_FALSE, NULL);
2163
2164 PJ_LOG(3,(THIS_FILE, "%s: unregistration success",
2165 pjsua_var.acc[acc->index].cfg.id.ptr));
2166 } else {
2167 /* Check and update SIP outbound status first, since the result
2168 * will determine if we should update re-registration
2169 */
2170 update_rfc5626_status(acc, param->rdata);
2171
2172 /* Check NAT bound address */
Alexandre Lision94f06ba2013-12-09 16:28:33 -05002173 if (acc_check_nat_addr(acc, (acc->cfg.contact_rewrite_method & 3),
2174 param))
2175 {
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002176 PJSUA_UNLOCK();
2177 pj_log_pop_indent();
2178 return;
2179 }
2180
2181 /* Check and update Service-Route header */
2182 update_service_route(acc, param->rdata);
2183
2184 PJ_LOG(3, (THIS_FILE,
2185 "%s: registration success, status=%d (%.*s), "
2186 "will re-register in %d seconds",
2187 pjsua_var.acc[acc->index].cfg.id.ptr,
2188 param->code,
2189 (int)param->reason.slen, param->reason.ptr,
2190 param->expiration));
2191
2192 /* Start keep-alive timer if necessary. */
2193 update_keep_alive(acc, PJ_TRUE, param);
2194
2195 /* Send initial PUBLISH if it is enabled */
2196 if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
2197 pjsua_pres_init_publish_acc(acc->index);
2198
2199 /* Subscribe to MWI, if it's enabled */
2200 if (acc->cfg.mwi_enabled)
2201 pjsua_start_mwi(acc->index, PJ_FALSE);
2202 }
2203
2204 } else {
2205 PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
2206 }
2207
2208 acc->reg_last_err = param->status;
2209 acc->reg_last_code = param->code;
2210
2211 /* Check if we need to auto retry registration. Basically, registration
2212 * failure codes triggering auto-retry are those of temporal failures
2213 * considered to be recoverable in relatively short term.
2214 */
2215 if (acc->cfg.reg_retry_interval &&
2216 (param->code == PJSIP_SC_REQUEST_TIMEOUT ||
2217 param->code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
2218 param->code == PJSIP_SC_BAD_GATEWAY ||
2219 param->code == PJSIP_SC_SERVICE_UNAVAILABLE ||
2220 param->code == PJSIP_SC_SERVER_TIMEOUT ||
2221 param->code == PJSIP_SC_TEMPORARILY_UNAVAILABLE ||
2222 PJSIP_IS_STATUS_IN_CLASS(param->code, 600))) /* Global failure */
2223 {
2224 schedule_reregistration(acc);
2225 }
2226
2227 /* Call the registration status callback */
2228
2229 if (pjsua_var.ua_cfg.cb.on_reg_state) {
2230 (*pjsua_var.ua_cfg.cb.on_reg_state)(acc->index);
2231 }
2232
2233 if (pjsua_var.ua_cfg.cb.on_reg_state2) {
2234 pjsua_reg_info reg_info;
2235
2236 reg_info.cbparam = param;
2237 (*pjsua_var.ua_cfg.cb.on_reg_state2)(acc->index, &reg_info);
2238 }
2239
2240 PJSUA_UNLOCK();
2241 pj_log_pop_indent();
2242}
2243
2244
2245/*
2246 * Initialize client registration.
2247 */
2248static pj_status_t pjsua_regc_init(int acc_id)
2249{
2250 pjsua_acc *acc;
2251 pj_pool_t *pool;
2252 pj_status_t status;
2253
2254 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
2255 acc = &pjsua_var.acc[acc_id];
2256
2257 if (acc->cfg.reg_uri.slen == 0) {
2258 PJ_LOG(3,(THIS_FILE, "Registrar URI is not specified"));
2259 return PJ_SUCCESS;
2260 }
2261
2262 /* Destroy existing session, if any */
2263 if (acc->regc) {
2264 pjsip_regc_destroy(acc->regc);
2265 acc->regc = NULL;
2266 acc->contact.slen = 0;
2267 acc->reg_mapped_addr.slen = 0;
2268 }
2269
2270 /* initialize SIP registration if registrar is configured */
2271
2272 status = pjsip_regc_create( pjsua_var.endpt,
2273 acc, &regc_cb, &acc->regc);
2274
2275 if (status != PJ_SUCCESS) {
2276 pjsua_perror(THIS_FILE, "Unable to create client registration",
2277 status);
2278 return status;
2279 }
2280
2281 pool = pjsua_pool_create("tmpregc", 512, 512);
2282
2283 if (acc->contact.slen == 0) {
2284 pj_str_t tmp_contact;
2285
2286 status = pjsua_acc_create_uac_contact( pool, &tmp_contact,
2287 acc_id, &acc->cfg.reg_uri);
2288 if (status != PJ_SUCCESS) {
2289 pjsua_perror(THIS_FILE, "Unable to generate suitable Contact header"
2290 " for registration",
2291 status);
2292 pjsip_regc_destroy(acc->regc);
2293 pj_pool_release(pool);
2294 acc->regc = NULL;
2295 return status;
2296 }
2297
2298 pj_strdup_with_null(acc->pool, &acc->contact, &tmp_contact);
2299 update_regc_contact(acc);
2300 }
2301
2302 status = pjsip_regc_init( acc->regc,
2303 &acc->cfg.reg_uri,
2304 &acc->cfg.id,
2305 &acc->cfg.id,
2306 1, &acc->reg_contact,
2307 acc->cfg.reg_timeout);
2308 if (status != PJ_SUCCESS) {
2309 pjsua_perror(THIS_FILE,
2310 "Client registration initialization error",
2311 status);
2312 pjsip_regc_destroy(acc->regc);
2313 pj_pool_release(pool);
2314 acc->regc = NULL;
2315 acc->contact.slen = 0;
2316 acc->reg_mapped_addr.slen = 0;
2317 return status;
2318 }
2319
Alexandre Lision94f06ba2013-12-09 16:28:33 -05002320 pjsip_regc_set_reg_tsx_cb(acc->regc, regc_tsx_cb);
2321
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002322 /* If account is locked to specific transport, then set transport to
2323 * the client registration.
2324 */
2325 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
2326 pjsip_tpselector tp_sel;
2327
2328 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
2329 pjsip_regc_set_transport(acc->regc, &tp_sel);
2330 }
2331
2332
2333 /* Set credentials
2334 */
2335 if (acc->cred_cnt) {
2336 pjsip_regc_set_credentials( acc->regc, acc->cred_cnt, acc->cred);
2337 }
2338
2339 /* Set delay before registration refresh */
2340 pjsip_regc_set_delay_before_refresh(acc->regc,
2341 acc->cfg.reg_delay_before_refresh);
2342
2343 /* Set authentication preference */
2344 pjsip_regc_set_prefs(acc->regc, &acc->cfg.auth_pref);
2345
2346 /* Set route-set
2347 */
2348 if (acc->cfg.reg_use_proxy) {
2349 pjsip_route_hdr route_set;
2350 const pjsip_route_hdr *r;
2351
2352 pj_list_init(&route_set);
2353
2354 if (acc->cfg.reg_use_proxy & PJSUA_REG_USE_OUTBOUND_PROXY) {
2355 r = pjsua_var.outbound_proxy.next;
2356 while (r != &pjsua_var.outbound_proxy) {
2357 pj_list_push_back(&route_set, pjsip_hdr_shallow_clone(pool, r));
2358 r = r->next;
2359 }
2360 }
2361
2362 if (acc->cfg.reg_use_proxy & PJSUA_REG_USE_ACC_PROXY &&
2363 acc->cfg.proxy_cnt)
2364 {
2365 int cnt = acc->cfg.proxy_cnt;
2366 pjsip_route_hdr *pos = route_set.prev;
2367 int i;
2368
2369 r = acc->route_set.prev;
2370 for (i=0; i<cnt; ++i) {
2371 pj_list_push_front(pos, pjsip_hdr_shallow_clone(pool, r));
2372 r = r->prev;
2373 }
2374 }
2375
2376 if (!pj_list_empty(&route_set))
2377 pjsip_regc_set_route_set( acc->regc, &route_set );
2378 }
2379
2380 /* Add custom request headers specified in the account config */
2381 pjsip_regc_add_headers(acc->regc, &acc->cfg.reg_hdr_list);
2382
2383 /* Add other request headers. */
2384 if (pjsua_var.ua_cfg.user_agent.slen) {
2385 pjsip_hdr hdr_list;
2386 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
2387 pjsip_generic_string_hdr *h;
2388
2389 pj_list_init(&hdr_list);
2390
2391 h = pjsip_generic_string_hdr_create(pool, &STR_USER_AGENT,
2392 &pjsua_var.ua_cfg.user_agent);
2393 pj_list_push_back(&hdr_list, (pjsip_hdr*)h);
2394
2395 pjsip_regc_add_headers(acc->regc, &hdr_list);
2396 }
2397
2398 /* If SIP outbound is used, add "Supported: outbound, path header" */
2399 if (acc->rfc5626_status == OUTBOUND_WANTED) {
2400 pjsip_hdr hdr_list;
2401 pjsip_supported_hdr *hsup;
2402
2403 pj_list_init(&hdr_list);
2404 hsup = pjsip_supported_hdr_create(pool);
2405 pj_list_push_back(&hdr_list, hsup);
2406
2407 hsup->count = 2;
2408 hsup->values[0] = pj_str("outbound");
2409 hsup->values[1] = pj_str("path");
2410
2411 pjsip_regc_add_headers(acc->regc, &hdr_list);
2412 }
2413
2414 pj_pool_release(pool);
2415
2416 return PJ_SUCCESS;
2417}
2418
2419pj_bool_t pjsua_sip_acc_is_using_stun(pjsua_acc_id acc_id)
2420{
2421 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2422
2423 return acc->cfg.sip_stun_use != PJSUA_STUN_USE_DISABLED &&
2424 pjsua_var.ua_cfg.stun_srv_cnt != 0;
2425}
2426
2427/*
2428 * Update registration or perform unregistration.
2429 */
2430PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id,
2431 pj_bool_t renew)
2432{
2433 pjsua_acc *acc;
2434 pj_status_t status = 0;
2435 pjsip_tx_data *tdata = 0;
2436
2437 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
2438 PJ_EINVAL);
2439 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
2440
2441 PJ_LOG(4,(THIS_FILE, "Acc %d: setting %sregistration..",
2442 acc_id, (renew? "" : "un")));
2443 pj_log_push_indent();
2444
2445 PJSUA_LOCK();
2446
2447 acc = &pjsua_var.acc[acc_id];
2448
2449 /* Cancel any re-registration timer */
2450 if (pjsua_var.acc[acc_id].auto_rereg.timer.id) {
2451 pjsua_var.acc[acc_id].auto_rereg.timer.id = PJ_FALSE;
2452 pjsua_cancel_timer(&pjsua_var.acc[acc_id].auto_rereg.timer);
2453 }
2454
2455 /* Reset pointer to registration transport */
2456 pjsua_var.acc[acc_id].auto_rereg.reg_tp = NULL;
2457
2458 if (renew) {
2459 if (pjsua_var.acc[acc_id].regc == NULL) {
2460 status = pjsua_regc_init(acc_id);
2461 if (status != PJ_SUCCESS) {
2462 pjsua_perror(THIS_FILE, "Unable to create registration",
2463 status);
2464 goto on_return;
2465 }
2466 }
2467 if (!pjsua_var.acc[acc_id].regc) {
2468 status = PJ_EINVALIDOP;
2469 goto on_return;
2470 }
2471
2472 status = pjsip_regc_register(pjsua_var.acc[acc_id].regc, 1,
2473 &tdata);
2474
2475 if (0 && status == PJ_SUCCESS && pjsua_var.acc[acc_id].cred_cnt) {
2476 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2477 pjsip_authorization_hdr *h;
2478 char *uri;
2479 int d;
2480
2481 uri = (char*) pj_pool_alloc(tdata->pool, acc->cfg.reg_uri.slen+10);
2482 d = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri,
2483 uri, acc->cfg.reg_uri.slen+10);
2484 pj_assert(d > 0);
2485
2486 h = pjsip_authorization_hdr_create(tdata->pool);
2487 h->scheme = pj_str("Digest");
2488 h->credential.digest.username = acc->cred[0].username;
2489 h->credential.digest.realm = acc->srv_domain;
2490 h->credential.digest.uri = pj_str(uri);
2491 h->credential.digest.algorithm = pj_str("md5");
2492
2493 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h);
2494 }
2495
2496 } else {
2497 if (pjsua_var.acc[acc_id].regc == NULL) {
2498 PJ_LOG(3,(THIS_FILE, "Currently not registered"));
2499 status = PJ_EINVALIDOP;
2500 goto on_return;
2501 }
2502
2503 pjsua_pres_unpublish(&pjsua_var.acc[acc_id], 0);
2504
2505 status = pjsip_regc_unregister(pjsua_var.acc[acc_id].regc, &tdata);
2506 }
2507
2508 if (status == PJ_SUCCESS) {
2509 if (pjsua_var.acc[acc_id].cfg.allow_via_rewrite &&
2510 pjsua_var.acc[acc_id].via_addr.host.slen > 0)
2511 {
2512 pjsip_regc_set_via_sent_by(pjsua_var.acc[acc_id].regc,
2513 &pjsua_var.acc[acc_id].via_addr,
2514 pjsua_var.acc[acc_id].via_tp);
2515 } else if (!pjsua_sip_acc_is_using_stun(acc_id)) {
2516 /* Choose local interface to use in Via if acc is not using
2517 * STUN
2518 */
2519 pjsua_acc_get_uac_addr(acc_id, tdata->pool,
2520 &acc->cfg.reg_uri,
2521 &tdata->via_addr,
2522 NULL, NULL,
2523 &tdata->via_tp);
2524 }
2525
2526 //pjsua_process_msg_data(tdata, NULL);
2527 status = pjsip_regc_send( pjsua_var.acc[acc_id].regc, tdata );
2528 }
2529
2530 /* Update pointer to registration transport */
2531 if (status == PJ_SUCCESS) {
2532 pjsip_regc_info reg_info;
2533
2534 pjsip_regc_get_info(pjsua_var.acc[acc_id].regc, &reg_info);
2535 pjsua_var.acc[acc_id].auto_rereg.reg_tp = reg_info.transport;
2536
2537 if (pjsua_var.ua_cfg.cb.on_reg_started) {
2538 (*pjsua_var.ua_cfg.cb.on_reg_started)(acc_id, renew);
2539 }
2540 }
2541
2542 if (status != PJ_SUCCESS) {
2543 pjsua_perror(THIS_FILE, "Unable to create/send REGISTER",
2544 status);
2545 } else {
2546 PJ_LOG(4,(THIS_FILE, "Acc %d: %s sent", acc_id,
2547 (renew? "Registration" : "Unregistration")));
2548 }
2549
2550on_return:
2551 PJSUA_UNLOCK();
2552 pj_log_pop_indent();
2553 return status;
2554}
2555
2556
2557/*
2558 * Get account information.
2559 */
2560PJ_DEF(pj_status_t) pjsua_acc_get_info( pjsua_acc_id acc_id,
2561 pjsua_acc_info *info)
2562{
2563 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2564 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
2565
2566 PJ_ASSERT_RETURN(info != NULL, PJ_EINVAL);
2567 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
2568
2569 pj_bzero(info, sizeof(pjsua_acc_info));
2570
2571 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
2572 PJ_EINVAL);
2573 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
2574
2575 PJSUA_LOCK();
2576
2577 if (pjsua_var.acc[acc_id].valid == PJ_FALSE) {
2578 PJSUA_UNLOCK();
2579 return PJ_EINVALIDOP;
2580 }
2581
2582 info->id = acc_id;
2583 info->is_default = (pjsua_var.default_acc == acc_id);
2584 info->acc_uri = acc_cfg->id;
2585 info->has_registration = (acc->cfg.reg_uri.slen > 0);
2586 info->online_status = acc->online_status;
2587 pj_memcpy(&info->rpid, &acc->rpid, sizeof(pjrpid_element));
2588 if (info->rpid.note.slen)
2589 info->online_status_text = info->rpid.note;
2590 else if (info->online_status)
2591 info->online_status_text = pj_str("Online");
2592 else
2593 info->online_status_text = pj_str("Offline");
2594
2595 if (acc->reg_last_code) {
2596 if (info->has_registration) {
2597 info->status = (pjsip_status_code) acc->reg_last_code;
2598 info->status_text = *pjsip_get_status_text(acc->reg_last_code);
2599 if (acc->reg_last_err)
2600 info->reg_last_err = acc->reg_last_err;
2601 } else {
2602 info->status = (pjsip_status_code) 0;
2603 info->status_text = pj_str("not registered");
2604 }
2605 } else if (acc->cfg.reg_uri.slen) {
2606 info->status = PJSIP_SC_TRYING;
2607 info->status_text = pj_str("In Progress");
2608 } else {
2609 info->status = (pjsip_status_code) 0;
2610 info->status_text = pj_str("does not register");
2611 }
2612
2613 if (acc->regc) {
2614 pjsip_regc_info regc_info;
2615 pjsip_regc_get_info(acc->regc, &regc_info);
2616 info->expires = regc_info.next_reg;
2617 } else {
2618 info->expires = -1;
2619 }
2620
2621 PJSUA_UNLOCK();
2622
2623 return PJ_SUCCESS;
2624
2625}
2626
2627
2628/*
2629 * Enum accounts all account ids.
2630 */
2631PJ_DEF(pj_status_t) pjsua_enum_accs(pjsua_acc_id ids[],
2632 unsigned *count )
2633{
2634 unsigned i, c;
2635
2636 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
2637
2638 PJSUA_LOCK();
2639
2640 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2641 if (!pjsua_var.acc[i].valid)
2642 continue;
2643 ids[c] = i;
2644 ++c;
2645 }
2646
2647 *count = c;
2648
2649 PJSUA_UNLOCK();
2650
2651 return PJ_SUCCESS;
2652}
2653
2654
2655/*
2656 * Enum accounts info.
2657 */
2658PJ_DEF(pj_status_t) pjsua_acc_enum_info( pjsua_acc_info info[],
2659 unsigned *count )
2660{
2661 unsigned i, c;
2662
2663 PJ_ASSERT_RETURN(info && *count, PJ_EINVAL);
2664
2665 PJSUA_LOCK();
2666
2667 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2668 if (!pjsua_var.acc[i].valid)
2669 continue;
2670
2671 pjsua_acc_get_info(i, &info[c]);
2672 ++c;
2673 }
2674
2675 *count = c;
2676
2677 PJSUA_UNLOCK();
2678
2679 return PJ_SUCCESS;
2680}
2681
2682
2683/*
2684 * This is an internal function to find the most appropriate account to
2685 * used to reach to the specified URL.
2686 */
2687PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_outgoing(const pj_str_t *url)
2688{
2689 pj_str_t tmp;
2690 pjsip_uri *uri;
2691 pjsip_sip_uri *sip_uri;
2692 pj_pool_t *tmp_pool;
2693 unsigned i;
2694
2695 PJSUA_LOCK();
2696
2697 tmp_pool = pjsua_pool_create("tmpacc10", 256, 256);
2698
2699 pj_strdup_with_null(tmp_pool, &tmp, url);
2700
2701 uri = pjsip_parse_uri(tmp_pool, tmp.ptr, tmp.slen, 0);
2702 if (!uri) {
2703 pj_pool_release(tmp_pool);
2704 PJSUA_UNLOCK();
2705 return pjsua_var.default_acc;
2706 }
2707
2708 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
2709 !PJSIP_URI_SCHEME_IS_SIPS(uri))
2710 {
2711 /* Return the first account with proxy */
2712 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2713 if (!pjsua_var.acc[i].valid)
2714 continue;
2715 if (!pj_list_empty(&pjsua_var.acc[i].route_set))
2716 break;
2717 }
2718
2719 if (i != PJ_ARRAY_SIZE(pjsua_var.acc)) {
2720 /* Found rather matching account */
2721 pj_pool_release(tmp_pool);
2722 PJSUA_UNLOCK();
2723 return i;
2724 }
2725
2726 /* Not found, use default account */
2727 pj_pool_release(tmp_pool);
2728 PJSUA_UNLOCK();
2729 return pjsua_var.default_acc;
2730 }
2731
2732 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
2733
2734 /* Find matching domain AND port */
2735 for (i=0; i<pjsua_var.acc_cnt; ++i) {
2736 unsigned acc_id = pjsua_var.acc_ids[i];
2737 if (pj_stricmp(&pjsua_var.acc[acc_id].srv_domain, &sip_uri->host)==0 &&
2738 pjsua_var.acc[acc_id].srv_port == sip_uri->port)
2739 {
2740 pj_pool_release(tmp_pool);
2741 PJSUA_UNLOCK();
2742 return acc_id;
2743 }
2744 }
2745
2746 /* If no match, try to match the domain part only */
2747 for (i=0; i<pjsua_var.acc_cnt; ++i) {
2748 unsigned acc_id = pjsua_var.acc_ids[i];
2749 if (pj_stricmp(&pjsua_var.acc[acc_id].srv_domain, &sip_uri->host)==0)
2750 {
2751 pj_pool_release(tmp_pool);
2752 PJSUA_UNLOCK();
2753 return acc_id;
2754 }
2755 }
2756
2757
2758 /* Still no match, just use default account */
2759 pj_pool_release(tmp_pool);
2760 PJSUA_UNLOCK();
2761 return pjsua_var.default_acc;
2762}
2763
2764
2765/*
2766 * This is an internal function to find the most appropriate account to be
2767 * used to handle incoming calls.
2768 */
2769PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata)
2770{
2771 pjsip_uri *uri;
2772 pjsip_sip_uri *sip_uri;
2773 pjsua_acc_id id = PJSUA_INVALID_ID;
2774 unsigned i;
2775
2776 /* Check that there's at least one account configured */
2777 PJ_ASSERT_RETURN(pjsua_var.acc_cnt!=0, pjsua_var.default_acc);
2778
2779 uri = rdata->msg_info.to->uri;
2780
2781 PJSUA_LOCK();
2782
2783 /* Use Req URI if To URI is not SIP */
2784 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
2785 !PJSIP_URI_SCHEME_IS_SIPS(uri))
2786 {
2787 if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG)
2788 uri = rdata->msg_info.msg->line.req.uri;
2789 else
2790 goto on_return;
2791 }
2792
2793 /* Just return default account if both To and Req URI are not SIP: */
2794 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
2795 !PJSIP_URI_SCHEME_IS_SIPS(uri))
2796 {
2797 goto on_return;
2798 }
2799
2800 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
2801
2802 /* Find account which has matching username and domain. */
2803 for (i=0; i < pjsua_var.acc_cnt; ++i) {
2804 unsigned acc_id = pjsua_var.acc_ids[i];
2805 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2806
2807 if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
2808 pj_stricmp(&acc->srv_domain, &sip_uri->host)==0)
2809 {
2810 /* Match ! */
2811 id = acc_id;
2812 goto on_return;
2813 }
2814 }
2815
2816 /* No matching account, try match domain part only. */
2817 for (i=0; i < pjsua_var.acc_cnt; ++i) {
2818 unsigned acc_id = pjsua_var.acc_ids[i];
2819 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2820
2821 if (acc->valid && pj_stricmp(&acc->srv_domain, &sip_uri->host)==0) {
2822 /* Match ! */
2823 id = acc_id;
2824 goto on_return;
2825 }
2826 }
2827
2828 /* No matching account, try match user part (and transport type) only. */
2829 for (i=0; i < pjsua_var.acc_cnt; ++i) {
2830 unsigned acc_id = pjsua_var.acc_ids[i];
2831 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2832
2833 if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0) {
2834
2835 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
2836 pjsip_transport_type_e type;
2837 type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
2838 if (type == PJSIP_TRANSPORT_UNSPECIFIED)
2839 type = PJSIP_TRANSPORT_UDP;
2840
2841 if (pjsua_var.tpdata[acc->cfg.transport_id].type != type)
2842 continue;
2843 }
2844
2845 /* Match ! */
2846 id = acc_id;
2847 goto on_return;
2848 }
2849 }
2850
2851on_return:
2852 PJSUA_UNLOCK();
2853
2854 /* Still no match, use default account */
2855 if (id == PJSUA_INVALID_ID)
2856 id = pjsua_var.default_acc;
2857
2858 /* Invoke account find callback */
2859 if (pjsua_var.ua_cfg.cb.on_acc_find_for_incoming)
2860 (*pjsua_var.ua_cfg.cb.on_acc_find_for_incoming)(rdata, &id);
2861
2862 /* Verify if the specified account id is valid */
2863 if (!pjsua_acc_is_valid(id))
2864 id = pjsua_var.default_acc;
2865
2866 return id;
2867}
2868
2869
2870/*
2871 * Create arbitrary requests for this account.
2872 */
2873PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id,
2874 const pjsip_method *method,
2875 const pj_str_t *target,
2876 pjsip_tx_data **p_tdata)
2877{
2878 pjsip_tx_data *tdata;
2879 pjsua_acc *acc;
2880 pjsip_route_hdr *r;
2881 pj_status_t status;
2882
2883 PJ_ASSERT_RETURN(method && target && p_tdata, PJ_EINVAL);
2884 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
2885
2886 acc = &pjsua_var.acc[acc_id];
2887
2888 status = pjsip_endpt_create_request(pjsua_var.endpt, method, target,
2889 &acc->cfg.id, target,
2890 NULL, NULL, -1, NULL, &tdata);
2891 if (status != PJ_SUCCESS) {
2892 pjsua_perror(THIS_FILE, "Unable to create request", status);
2893 return status;
2894 }
2895
2896 /* Copy routeset */
2897 r = acc->route_set.next;
2898 while (r != &acc->route_set) {
2899 pjsip_msg_add_hdr(tdata->msg,
2900 (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, r));
2901 r = r->next;
2902 }
2903
2904 /* If account is locked to specific transport, then set that transport to
2905 * the transmit data.
2906 */
2907 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
2908 pjsip_tpselector tp_sel;
2909
2910 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
2911 pjsip_tx_data_set_transport(tdata, &tp_sel);
2912 }
2913
2914 /* If via_addr is set, use this address for the Via header. */
2915 if (pjsua_var.acc[acc_id].cfg.allow_via_rewrite &&
2916 pjsua_var.acc[acc_id].via_addr.host.slen > 0)
2917 {
2918 tdata->via_addr = pjsua_var.acc[acc_id].via_addr;
2919 tdata->via_tp = pjsua_var.acc[acc_id].via_tp;
2920 } else if (!pjsua_sip_acc_is_using_stun(acc_id)) {
2921 /* Choose local interface to use in Via if acc is not using
2922 * STUN
2923 */
2924 pjsua_acc_get_uac_addr(acc_id, tdata->pool,
2925 target,
2926 &tdata->via_addr,
2927 NULL, NULL,
2928 &tdata->via_tp);
2929 }
2930
2931 /* Done */
2932 *p_tdata = tdata;
2933 return PJ_SUCCESS;
2934}
2935
2936/* Get local transport address suitable to be used for Via or Contact address
2937 * to send request to the specified destination URI.
2938 */
2939pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id,
2940 pj_pool_t *pool,
2941 const pj_str_t *dst_uri,
2942 pjsip_host_port *addr,
2943 pjsip_transport_type_e *p_tp_type,
2944 int *secure,
2945 const void **p_tp)
2946{
2947 pjsua_acc *acc;
2948 pjsip_sip_uri *sip_uri;
2949 pj_status_t status;
2950 pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
2951 unsigned flag;
2952 pjsip_tpselector tp_sel;
2953 pjsip_tpmgr *tpmgr;
2954 pjsip_tpmgr_fla2_param tfla2_prm;
2955
2956 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
2957 acc = &pjsua_var.acc[acc_id];
2958
2959 /* If route-set is configured for the account, then URI is the
2960 * first entry of the route-set.
2961 */
2962 if (!pj_list_empty(&acc->route_set)) {
2963 sip_uri = (pjsip_sip_uri*)
2964 pjsip_uri_get_uri(acc->route_set.next->name_addr.uri);
2965 } else {
2966 pj_str_t tmp;
2967 pjsip_uri *uri;
2968
2969 pj_strdup_with_null(pool, &tmp, dst_uri);
2970
2971 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
2972 if (uri == NULL)
2973 return PJSIP_EINVALIDURI;
2974
2975 /* For non-SIP scheme, route set should be configured */
2976 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
2977 return PJSIP_ENOROUTESET;
2978
2979 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
2980 }
2981
2982 /* Get transport type of the URI */
2983 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri))
2984 tp_type = PJSIP_TRANSPORT_TLS;
2985 else if (sip_uri->transport_param.slen == 0) {
2986 tp_type = PJSIP_TRANSPORT_UDP;
2987 } else
2988 tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
2989
2990 if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
2991 return PJSIP_EUNSUPTRANSPORT;
2992
2993 /* If destination URI specifies IPv6, then set transport type
2994 * to use IPv6 as well.
2995 */
2996 if (pj_strchr(&sip_uri->host, ':'))
2997 tp_type = (pjsip_transport_type_e)(((int)tp_type) + PJSIP_TRANSPORT_IPV6);
2998
2999 flag = pjsip_transport_get_flag_from_type(tp_type);
3000
3001 /* Init transport selector. */
3002 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
3003
3004 /* Get local address suitable to send request from */
3005 pjsip_tpmgr_fla2_param_default(&tfla2_prm);
3006 tfla2_prm.tp_type = tp_type;
3007 tfla2_prm.tp_sel = &tp_sel;
3008 tfla2_prm.dst_host = sip_uri->host;
3009 tfla2_prm.local_if = (!pjsua_sip_acc_is_using_stun(acc_id) ||
3010 (flag & PJSIP_TRANSPORT_RELIABLE));
3011
3012 tpmgr = pjsip_endpt_get_tpmgr(pjsua_var.endpt);
3013 status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm);
3014 if (status != PJ_SUCCESS)
3015 return status;
3016
3017 addr->host = tfla2_prm.ret_addr;
3018 addr->port = tfla2_prm.ret_port;
3019
3020 if (p_tp_type)
3021 *p_tp_type = tp_type;
3022
3023 if (secure) {
3024 *secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
3025 }
3026
3027 if (p_tp)
3028 *p_tp = tfla2_prm.ret_tp;
3029
3030 return PJ_SUCCESS;
3031}
3032
3033
3034PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
3035 pj_str_t *contact,
3036 pjsua_acc_id acc_id,
3037 const pj_str_t *suri)
3038{
3039 pjsua_acc *acc;
3040 pj_status_t status;
3041 pjsip_transport_type_e tp_type;
3042 pjsip_host_port addr;
3043 int secure;
3044 const char *beginquote, *endquote;
3045 char transport_param[32];
3046 const char *ob = ";ob";
3047
3048
3049 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
3050 acc = &pjsua_var.acc[acc_id];
3051
3052 /* If force_contact is configured, then use use it */
3053 if (acc->cfg.force_contact.slen) {
3054 *contact = acc->cfg.force_contact;
3055 return PJ_SUCCESS;
3056 }
3057
3058 status = pjsua_acc_get_uac_addr(acc_id, pool, suri, &addr,
3059 &tp_type, &secure, NULL);
3060 if (status != PJ_SUCCESS)
3061 return status;
3062
3063 /* Enclose IPv6 address in square brackets */
3064 if (tp_type & PJSIP_TRANSPORT_IPV6) {
3065 beginquote = "[";
3066 endquote = "]";
3067 } else {
3068 beginquote = endquote = "";
3069 }
3070
3071 /* Don't add transport parameter if it's UDP */
3072 if (tp_type!=PJSIP_TRANSPORT_UDP && tp_type!=PJSIP_TRANSPORT_UDP6) {
3073 pj_ansi_snprintf(transport_param, sizeof(transport_param),
3074 ";transport=%s",
3075 pjsip_transport_get_type_name(tp_type));
3076 } else {
3077 transport_param[0] = '\0';
3078 }
3079
3080
3081 /* Create the contact header */
3082 contact->ptr = (char*)pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
3083 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
3084 "%s%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s",
3085 (acc->display.slen?"\"" : ""),
3086 (int)acc->display.slen,
3087 acc->display.ptr,
3088 (acc->display.slen?"\" " : ""),
Alexandre Lision94f06ba2013-12-09 16:28:33 -05003089 ((secure && acc->is_sips)? "sips" : "sip"),
Tristan Matthews0a329cc2013-07-17 13:20:14 -04003090 (int)acc->user_part.slen,
3091 acc->user_part.ptr,
3092 (acc->user_part.slen?"@":""),
3093 beginquote,
3094 (int)addr.host.slen,
3095 addr.host.ptr,
3096 endquote,
3097 addr.port,
3098 transport_param,
3099 (int)acc->cfg.contact_uri_params.slen,
3100 acc->cfg.contact_uri_params.ptr,
3101 (acc->cfg.use_rfc5626? ob: ""),
3102 (int)acc->cfg.contact_params.slen,
3103 acc->cfg.contact_params.ptr);
3104
3105 return PJ_SUCCESS;
3106}
3107
3108
3109
3110PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool,
3111 pj_str_t *contact,
3112 pjsua_acc_id acc_id,
3113 pjsip_rx_data *rdata )
3114{
3115 /*
3116 * Section 12.1.1, paragraph about using SIPS URI in Contact.
3117 * If the request that initiated the dialog contained a SIPS URI
3118 * in the Request-URI or in the top Record-Route header field value,
3119 * if there was any, or the Contact header field if there was no
3120 * Record-Route header field, the Contact header field in the response
3121 * MUST be a SIPS URI.
3122 */
3123 pjsua_acc *acc;
3124 pjsip_sip_uri *sip_uri;
3125 pj_status_t status;
3126 pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
3127 pj_str_t local_addr;
3128 pjsip_tpselector tp_sel;
3129 pjsip_tpmgr *tpmgr;
3130 pjsip_tpmgr_fla2_param tfla2_prm;
3131 unsigned flag;
3132 int secure;
3133 int local_port;
3134 const char *beginquote, *endquote;
3135 char transport_param[32];
3136
3137 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
3138 acc = &pjsua_var.acc[acc_id];
3139
3140 /* If force_contact is configured, then use use it */
3141 if (acc->cfg.force_contact.slen) {
3142 *contact = acc->cfg.force_contact;
3143 return PJ_SUCCESS;
3144 }
3145
3146 /* If Record-Route is present, then URI is the top Record-Route. */
3147 if (rdata->msg_info.record_route) {
3148 sip_uri = (pjsip_sip_uri*)
3149 pjsip_uri_get_uri(rdata->msg_info.record_route->name_addr.uri);
3150 } else {
3151 pjsip_hdr *pos = NULL;
3152 pjsip_contact_hdr *h_contact;
3153 pjsip_uri *uri = NULL;
3154
3155 /* Otherwise URI is Contact URI.
3156 * Iterate the Contact URI until we find sip: or sips: scheme.
3157 */
3158 do {
3159 h_contact = (pjsip_contact_hdr*)
3160 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT,
3161 pos);
3162 if (h_contact) {
3163 if (h_contact->uri)
3164 uri = (pjsip_uri*) pjsip_uri_get_uri(h_contact->uri);
3165 else
3166 uri = NULL;
3167 if (!uri || (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
3168 !PJSIP_URI_SCHEME_IS_SIPS(uri)))
3169 {
3170 pos = (pjsip_hdr*)h_contact->next;
3171 if (pos == &rdata->msg_info.msg->hdr)
3172 h_contact = NULL;
3173 } else {
3174 break;
3175 }
3176 }
3177 } while (h_contact);
3178
3179
3180 /* Or if Contact URI is not present, take the remote URI from
3181 * the From URI.
3182 */
3183 if (uri == NULL)
3184 uri = (pjsip_uri*) pjsip_uri_get_uri(rdata->msg_info.from->uri);
3185
3186
3187 /* Can only do sip/sips scheme at present. */
3188 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
3189 return PJSIP_EINVALIDREQURI;
3190
3191 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
3192 }
3193
3194 /* Get transport type of the URI */
3195 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri))
3196 tp_type = PJSIP_TRANSPORT_TLS;
3197 else if (sip_uri->transport_param.slen == 0) {
3198 tp_type = PJSIP_TRANSPORT_UDP;
3199 } else
3200 tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
3201
3202 if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
3203 return PJSIP_EUNSUPTRANSPORT;
3204
3205 /* If destination URI specifies IPv6, then set transport type
3206 * to use IPv6 as well.
3207 */
3208 if (pj_strchr(&sip_uri->host, ':'))
3209 tp_type = (pjsip_transport_type_e)(((int)tp_type) + PJSIP_TRANSPORT_IPV6);
3210
3211 flag = pjsip_transport_get_flag_from_type(tp_type);
3212 secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
3213
3214 /* Init transport selector. */
3215 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
3216
3217 /* Get local address suitable to send request from */
3218 pjsip_tpmgr_fla2_param_default(&tfla2_prm);
3219 tfla2_prm.tp_type = tp_type;
3220 tfla2_prm.tp_sel = &tp_sel;
3221 tfla2_prm.dst_host = sip_uri->host;
3222 tfla2_prm.local_if = (!pjsua_sip_acc_is_using_stun(acc_id) ||
3223 (flag & PJSIP_TRANSPORT_RELIABLE));
3224
3225 tpmgr = pjsip_endpt_get_tpmgr(pjsua_var.endpt);
3226 status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm);
3227 if (status != PJ_SUCCESS)
3228 return status;
3229
3230 local_addr = tfla2_prm.ret_addr;
3231 local_port = tfla2_prm.ret_port;
3232
3233
3234 /* Enclose IPv6 address in square brackets */
3235 if (tp_type & PJSIP_TRANSPORT_IPV6) {
3236 beginquote = "[";
3237 endquote = "]";
3238 } else {
3239 beginquote = endquote = "";
3240 }
3241
3242 /* Don't add transport parameter if it's UDP */
3243 if (tp_type!=PJSIP_TRANSPORT_UDP && tp_type!=PJSIP_TRANSPORT_UDP6) {
3244 pj_ansi_snprintf(transport_param, sizeof(transport_param),
3245 ";transport=%s",
3246 pjsip_transport_get_type_name(tp_type));
3247 } else {
3248 transport_param[0] = '\0';
3249 }
3250
3251
3252 /* Create the contact header */
3253 contact->ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
3254 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
3255 "%s%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s",
3256 (acc->display.slen?"\"" : ""),
3257 (int)acc->display.slen,
3258 acc->display.ptr,
3259 (acc->display.slen?"\" " : ""),
Alexandre Lision94f06ba2013-12-09 16:28:33 -05003260 ((secure && acc->is_sips)? "sips" : "sip"),
Tristan Matthews0a329cc2013-07-17 13:20:14 -04003261 (int)acc->user_part.slen,
3262 acc->user_part.ptr,
3263 (acc->user_part.slen?"@":""),
3264 beginquote,
3265 (int)local_addr.slen,
3266 local_addr.ptr,
3267 endquote,
3268 local_port,
3269 transport_param,
3270 (int)acc->cfg.contact_uri_params.slen,
3271 acc->cfg.contact_uri_params.ptr,
3272 (int)acc->cfg.contact_params.slen,
3273 acc->cfg.contact_params.ptr);
3274
3275 return PJ_SUCCESS;
3276}
3277
3278
3279PJ_DEF(pj_status_t) pjsua_acc_set_transport( pjsua_acc_id acc_id,
3280 pjsua_transport_id tp_id)
3281{
3282 pjsua_acc *acc;
3283
3284 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
3285 acc = &pjsua_var.acc[acc_id];
3286
3287 PJ_ASSERT_RETURN(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
3288 PJ_EINVAL);
3289
3290 acc->cfg.transport_id = tp_id;
3291
3292 return PJ_SUCCESS;
3293}
3294
3295
3296/* Auto re-registration timeout callback */
3297static void auto_rereg_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
3298{
3299 pjsua_acc *acc;
3300 pj_status_t status;
3301
3302 PJ_UNUSED_ARG(th);
3303 acc = (pjsua_acc*) te->user_data;
3304 pj_assert(acc);
3305
3306 PJSUA_LOCK();
3307
3308 /* Check if the reregistration timer is still valid, e.g: while waiting
3309 * timeout timer application might have deleted the account or disabled
3310 * the auto-reregistration.
3311 */
3312 if (!acc->valid || !acc->auto_rereg.active ||
3313 acc->cfg.reg_retry_interval == 0)
3314 {
3315 goto on_return;
3316 }
3317
3318 /* Start re-registration */
3319 acc->auto_rereg.attempt_cnt++;
3320 status = pjsua_acc_set_registration(acc->index, PJ_TRUE);
3321 if (status != PJ_SUCCESS)
3322 schedule_reregistration(acc);
3323
3324on_return:
3325 PJSUA_UNLOCK();
3326}
3327
3328
3329/* Schedule reregistration for specified account. Note that the first
3330 * re-registration after a registration failure will be done immediately.
3331 * Also note that this function should be called within PJSUA mutex.
3332 */
3333static void schedule_reregistration(pjsua_acc *acc)
3334{
3335 pj_time_val delay;
3336
3337 pj_assert(acc);
3338
3339 /* Validate the account and re-registration feature status */
3340 if (!acc->valid || acc->cfg.reg_retry_interval == 0) {
3341 return;
3342 }
3343
3344 /* If configured, disconnect calls of this account after the first
3345 * reregistration attempt failed.
3346 */
3347 if (acc->cfg.drop_calls_on_reg_fail && acc->auto_rereg.attempt_cnt >= 1)
3348 {
3349 unsigned i, cnt;
3350
3351 for (i = 0, cnt = 0; i < pjsua_var.ua_cfg.max_calls; ++i) {
3352 if (pjsua_var.calls[i].acc_id == acc->index) {
3353 pjsua_call_hangup(i, 0, NULL, NULL);
3354 ++cnt;
3355 }
3356 }
3357
3358 if (cnt) {
3359 PJ_LOG(3, (THIS_FILE, "Disconnecting %d call(s) of account #%d "
3360 "after reregistration attempt failed",
3361 cnt, acc->index));
3362 }
3363 }
3364
3365 /* Cancel any re-registration timer */
3366 if (acc->auto_rereg.timer.id) {
3367 acc->auto_rereg.timer.id = PJ_FALSE;
3368 pjsua_cancel_timer(&acc->auto_rereg.timer);
3369 }
3370
3371 /* Update re-registration flag */
3372 acc->auto_rereg.active = PJ_TRUE;
3373
3374 /* Set up timer for reregistration */
3375 acc->auto_rereg.timer.cb = &auto_rereg_timer_cb;
3376 acc->auto_rereg.timer.user_data = acc;
3377
3378 /* Reregistration attempt. The first attempt will be done immediately. */
3379 delay.sec = acc->auto_rereg.attempt_cnt? acc->cfg.reg_retry_interval :
3380 acc->cfg.reg_first_retry_interval;
3381 delay.msec = 0;
3382
3383 /* Randomize interval by +/- 10 secs */
3384 if (delay.sec >= 10) {
3385 delay.msec = -10000 + (pj_rand() % 20000);
3386 } else {
3387 delay.sec = 0;
3388 delay.msec = (pj_rand() % 10000);
3389 }
3390 pj_time_val_normalize(&delay);
3391
3392 PJ_LOG(4,(THIS_FILE,
3393 "Scheduling re-registration retry for acc %d in %u seconds..",
3394 acc->index, delay.sec));
3395
3396 acc->auto_rereg.timer.id = PJ_TRUE;
3397 if (pjsua_schedule_timer(&acc->auto_rereg.timer, &delay) != PJ_SUCCESS)
3398 acc->auto_rereg.timer.id = PJ_FALSE;
3399}
3400
3401
3402/* Internal function to perform auto-reregistration on transport
3403 * connection/disconnection events.
3404 */
3405void pjsua_acc_on_tp_state_changed(pjsip_transport *tp,
3406 pjsip_transport_state state,
3407 const pjsip_transport_state_info *info)
3408{
3409 unsigned i;
3410
3411 PJ_UNUSED_ARG(info);
3412
3413 /* Only care for transport disconnection events */
3414 if (state != PJSIP_TP_STATE_DISCONNECTED)
3415 return;
3416
3417 PJ_LOG(4,(THIS_FILE, "Disconnected notification for transport %s",
3418 tp->obj_name));
3419 pj_log_push_indent();
3420
3421 /* Shutdown this transport, to make sure that the transport manager
3422 * will create a new transport for reconnection.
3423 */
3424 pjsip_transport_shutdown(tp);
3425
3426 PJSUA_LOCK();
3427
3428 /* Enumerate accounts using this transport and perform actions
3429 * based on the transport state.
3430 */
3431 for (i = 0; i < PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
3432 pjsua_acc *acc = &pjsua_var.acc[i];
3433
3434 /* Skip if this account is not valid OR auto re-registration
3435 * feature is disabled OR this transport is not used by this account.
3436 */
3437 if (!acc->valid || !acc->cfg.reg_retry_interval ||
3438 tp != acc->auto_rereg.reg_tp)
3439 {
3440 continue;
3441 }
3442
3443 /* Release regc transport immediately
3444 * See https://trac.pjsip.org/repos/ticket/1481
3445 */
3446 if (pjsua_var.acc[i].regc) {
3447 pjsip_regc_release_transport(pjsua_var.acc[i].regc);
3448 }
3449
3450 /* Schedule reregistration for this account */
3451 schedule_reregistration(acc);
3452 }
3453
3454 PJSUA_UNLOCK();
3455 pj_log_pop_indent();
3456}