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