blob: aec9627c2aaf9c9ed7382cb9197cec487494db87 [file] [log] [blame]
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001/* $Id$ */
2/*
Nanang Izzuddina62ffc92011-05-05 06:14:19 +00003 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonoeebe9af2006-06-13 22:57:13 +00005 *
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
Benny Prijonob54719f2010-11-16 03:07:46 +000026enum
27{
Benny Prijono2562b752010-11-16 06:01:38 +000028 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
Benny Prijonob54719f2010-11-16 03:07:46 +000032};
33
Benny Prijonoeebe9af2006-06-13 22:57:13 +000034
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +000035static void schedule_reregistration(pjsua_acc *acc);
36
Benny Prijonoeebe9af2006-06-13 22:57:13 +000037/*
38 * Get number of current accounts.
39 */
40PJ_DEF(unsigned) pjsua_acc_get_count(void)
41{
42 return pjsua_var.acc_cnt;
43}
44
45
46/*
47 * Check if the specified account ID is valid.
48 */
49PJ_DEF(pj_bool_t) pjsua_acc_is_valid(pjsua_acc_id acc_id)
50{
Benny Prijonoa1e69682007-05-11 15:14:34 +000051 return acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc) &&
Benny Prijonoeebe9af2006-06-13 22:57:13 +000052 pjsua_var.acc[acc_id].valid;
53}
54
55
56/*
Benny Prijono21b9ad92006-08-15 13:11:22 +000057 * Set default account
58 */
59PJ_DEF(pj_status_t) pjsua_acc_set_default(pjsua_acc_id acc_id)
60{
61 pjsua_var.default_acc = acc_id;
62 return PJ_SUCCESS;
63}
64
65
66/*
67 * Get default account.
68 */
69PJ_DEF(pjsua_acc_id) pjsua_acc_get_default(void)
70{
71 return pjsua_var.default_acc;
72}
73
74
75/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000076 * Copy account configuration.
77 */
Benny Prijonobddef2c2007-10-31 13:28:08 +000078PJ_DEF(void) pjsua_acc_config_dup( pj_pool_t *pool,
79 pjsua_acc_config *dst,
80 const pjsua_acc_config *src)
Benny Prijonoeebe9af2006-06-13 22:57:13 +000081{
82 unsigned i;
83
84 pj_memcpy(dst, src, sizeof(pjsua_acc_config));
85
86 pj_strdup_with_null(pool, &dst->id, &src->id);
87 pj_strdup_with_null(pool, &dst->reg_uri, &src->reg_uri);
Benny Prijonob4a17c92006-07-10 14:40:21 +000088 pj_strdup_with_null(pool, &dst->force_contact, &src->force_contact);
Nanang Izzuddin5fc7fcf2010-12-01 08:53:52 +000089 pj_strdup_with_null(pool, &dst->contact_params, &src->contact_params);
90 pj_strdup_with_null(pool, &dst->contact_uri_params,
91 &src->contact_uri_params);
Benny Prijonofe04fb52007-08-24 08:28:52 +000092 pj_strdup_with_null(pool, &dst->pidf_tuple_id, &src->pidf_tuple_id);
Nanang Izzuddin5fc7fcf2010-12-01 08:53:52 +000093 pj_strdup_with_null(pool, &dst->rfc5626_instance_id,
94 &src->rfc5626_instance_id);
Benny Prijonob54719f2010-11-16 03:07:46 +000095 pj_strdup_with_null(pool, &dst->rfc5626_reg_id, &src->rfc5626_reg_id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000096
97 dst->proxy_cnt = src->proxy_cnt;
98 for (i=0; i<src->proxy_cnt; ++i)
99 pj_strdup_with_null(pool, &dst->proxy[i], &src->proxy[i]);
100
101 dst->reg_timeout = src->reg_timeout;
Sauw Ming9b80d512011-03-15 03:20:37 +0000102 dst->reg_delay_before_refresh = src->reg_delay_before_refresh;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 dst->cred_count = src->cred_count;
104
105 for (i=0; i<src->cred_count; ++i) {
106 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
107 }
Benny Prijonobddef2c2007-10-31 13:28:08 +0000108
Nanang Izzuddin60e8aa92010-09-28 10:48:48 +0000109 pj_list_init(&dst->reg_hdr_list);
110 if (!pj_list_empty(&src->reg_hdr_list)) {
111 const pjsip_hdr *hdr;
112
113 hdr = src->reg_hdr_list.next;
114 while (hdr != &src->reg_hdr_list) {
115 pj_list_push_back(&dst->reg_hdr_list, pjsip_hdr_clone(pool, hdr));
116 hdr = hdr->next;
117 }
118 }
119
Benny Prijono639873e2011-03-23 03:46:26 +0000120 pj_list_init(&dst->sub_hdr_list);
121 if (!pj_list_empty(&src->sub_hdr_list)) {
122 const pjsip_hdr *hdr;
123
124 hdr = src->sub_hdr_list.next;
125 while (hdr != &src->sub_hdr_list) {
126 pj_list_push_back(&dst->sub_hdr_list, pjsip_hdr_clone(pool, hdr));
127 hdr = hdr->next;
128 }
129 }
130
Nanang Izzuddin5fc7fcf2010-12-01 08:53:52 +0000131 pjsip_auth_clt_pref_dup(pool, &dst->auth_pref, &src->auth_pref);
132
Benny Prijonobddef2c2007-10-31 13:28:08 +0000133 dst->ka_interval = src->ka_interval;
134 pj_strdup(pool, &dst->ka_data, &src->ka_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000135}
136
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000137/*
138 * Calculate CRC of proxy list.
139 */
140static pj_uint32_t calc_proxy_crc(const pj_str_t proxy[], pj_size_t cnt)
141{
142 pj_crc32_context ctx;
143 unsigned i;
144
145 pj_crc32_init(&ctx);
146 for (i=0; i<cnt; ++i) {
147 pj_crc32_update(&ctx, (pj_uint8_t*)proxy[i].ptr, proxy[i].slen);
148 }
149
150 return pj_crc32_final(&ctx);
151}
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000152
153/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000154 * Initialize a new account (after configuration is set).
155 */
156static pj_status_t initialize_acc(unsigned acc_id)
157{
158 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
159 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000160 pjsip_name_addr *name_addr;
Benny Prijonoc7545782010-09-28 07:43:18 +0000161 pjsip_sip_uri *sip_reg_uri;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000162 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000163 unsigned i;
164
165 /* Need to parse local_uri to get the elements: */
166
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000167 name_addr = (pjsip_name_addr*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000168 pjsip_parse_uri(acc->pool, acc_cfg->id.ptr,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000169 acc_cfg->id.slen,
170 PJSIP_PARSE_URI_AS_NAMEADDR);
171 if (name_addr == NULL) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172 pjsua_perror(THIS_FILE, "Invalid local URI",
173 PJSIP_EINVALIDURI);
174 return PJSIP_EINVALIDURI;
175 }
176
177 /* Local URI MUST be a SIP or SIPS: */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000178 if (!PJSIP_URI_SCHEME_IS_SIP(name_addr) &&
179 !PJSIP_URI_SCHEME_IS_SIPS(name_addr))
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000180 {
Benny Prijonoc7545782010-09-28 07:43:18 +0000181 acc->display = name_addr->display;
182 acc->user_part = name_addr->display;
183 acc->srv_domain = pj_str("");
184 acc->srv_port = 0;
185 } else {
186 pjsip_sip_uri *sip_uri;
187
188 /* Get the SIP URI object: */
189 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(name_addr);
190
191 /* Save the user and domain part. These will be used when finding an
192 * account for incoming requests.
193 */
194 acc->display = name_addr->display;
195 acc->user_part = sip_uri->user;
196 acc->srv_domain = sip_uri->host;
197 acc->srv_port = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198 }
199
200
Benny Prijonob4a17c92006-07-10 14:40:21 +0000201 /* Parse registrar URI, if any */
202 if (acc_cfg->reg_uri.slen) {
203 pjsip_uri *reg_uri;
204
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000205 reg_uri = pjsip_parse_uri(acc->pool, acc_cfg->reg_uri.ptr,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000206 acc_cfg->reg_uri.slen, 0);
207 if (reg_uri == NULL) {
208 pjsua_perror(THIS_FILE, "Invalid registrar URI",
209 PJSIP_EINVALIDURI);
210 return PJSIP_EINVALIDURI;
211 }
212
213 /* Registrar URI MUST be a SIP or SIPS: */
214 if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) &&
215 !PJSIP_URI_SCHEME_IS_SIPS(reg_uri))
216 {
217 pjsua_perror(THIS_FILE, "Invalid registar URI",
218 PJSIP_EINVALIDSCHEME);
219 return PJSIP_EINVALIDSCHEME;
220 }
221
222 sip_reg_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri);
223
224 } else {
225 sip_reg_uri = NULL;
226 }
227
Benny Prijonob4a17c92006-07-10 14:40:21 +0000228 if (sip_reg_uri) {
229 acc->srv_port = sip_reg_uri->port;
230 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000231
232 /* Create Contact header if not present. */
Benny Prijonob4a17c92006-07-10 14:40:21 +0000233 //if (acc_cfg->contact.slen == 0) {
234 // acc_cfg->contact = acc_cfg->id;
235 //}
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000236
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 /* Build account route-set from outbound proxies and route set from
238 * account configuration.
239 */
240 pj_list_init(&acc->route_set);
241
Benny Prijono29c8ca32010-06-22 06:02:13 +0000242 if (!pj_list_empty(&pjsua_var.outbound_proxy)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000243 pjsip_route_hdr *r;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000244
Benny Prijono29c8ca32010-06-22 06:02:13 +0000245 r = pjsua_var.outbound_proxy.next;
246 while (r != &pjsua_var.outbound_proxy) {
247 pj_list_push_back(&acc->route_set,
248 pjsip_hdr_shallow_clone(acc->pool, r));
249 r = r->next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000250 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000251 }
252
253 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
254 pj_str_t hname = { "Route", 5};
255 pjsip_route_hdr *r;
256 pj_str_t tmp;
257
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000258 pj_strdup_with_null(acc->pool, &tmp, &acc_cfg->proxy[i]);
Benny Prijonoa1e69682007-05-11 15:14:34 +0000259 r = (pjsip_route_hdr*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000260 pjsip_parse_hdr(acc->pool, &hname, tmp.ptr, tmp.slen, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000261 if (r == NULL) {
262 pjsua_perror(THIS_FILE, "Invalid URI in account route set",
263 PJ_EINVAL);
264 return PJ_EINVAL;
265 }
266 pj_list_push_back(&acc->route_set, r);
267 }
268
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000269 /* Concatenate credentials from account config and global config */
270 acc->cred_cnt = 0;
271 for (i=0; i<acc_cfg->cred_count; ++i) {
272 acc->cred[acc->cred_cnt++] = acc_cfg->cred_info[i];
273 }
274 for (i=0; i<pjsua_var.ua_cfg.cred_count &&
275 acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i)
276 {
277 acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i];
278 }
279
Benny Prijono07fe2302010-06-24 12:33:18 +0000280 /* If ICE is enabled, add "+sip.ice" media feature tag in account's
281 * contact params.
282 */
283#if PJSUA_ADD_ICE_TAGS
284 if (pjsua_var.media_cfg.enable_ice) {
285 unsigned new_len;
286 pj_str_t new_prm;
287
288 new_len = acc_cfg->contact_params.slen + 10;
289 new_prm.ptr = (char*)pj_pool_alloc(acc->pool, new_len);
290 pj_strcpy(&new_prm, &acc_cfg->contact_params);
291 pj_strcat2(&new_prm, ";+sip.ice");
292 acc_cfg->contact_params = new_prm;
293 }
294#endif
295
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000296 status = pjsua_pres_init_acc(acc_id);
297 if (status != PJ_SUCCESS)
298 return status;
299
Benny Prijonob54719f2010-11-16 03:07:46 +0000300 /* If SIP outbound is enabled, generate instance and reg ID if they are
301 * not specified
302 */
303 if (acc_cfg->use_rfc5626) {
304 if (acc_cfg->rfc5626_instance_id.slen==0) {
305 const pj_str_t *hostname;
306 pj_uint32_t hval, pos;
307 char instprm[] = ";+sip.instance=\"<urn:uuid:00000000-0000-0000-0000-0000CCDDEEFF>\"";
308
309 hostname = pj_gethostname();
310 pos = pj_ansi_strlen(instprm) - 10;
311 hval = pj_hash_calc(0, hostname->ptr, hostname->slen);
312 pj_val_to_hex_digit( ((char*)&hval)[0], instprm+pos+0);
313 pj_val_to_hex_digit( ((char*)&hval)[1], instprm+pos+2);
314 pj_val_to_hex_digit( ((char*)&hval)[2], instprm+pos+4);
315 pj_val_to_hex_digit( ((char*)&hval)[3], instprm+pos+6);
316
317 pj_strdup2(acc->pool, &acc->rfc5626_instprm, instprm);
318 } else {
319 const char *prmname = ";+sip.instance=\"";
320 unsigned len;
321
322 len = pj_ansi_strlen(prmname) + acc_cfg->rfc5626_instance_id.slen + 1;
323 acc->rfc5626_instprm.ptr = (char*)pj_pool_alloc(acc->pool, len+1);
324 pj_ansi_snprintf(acc->rfc5626_instprm.ptr, len+1,
325 "%s%.*s\"",
326 prmname,
327 (int)acc_cfg->rfc5626_instance_id.slen,
328 acc_cfg->rfc5626_instance_id.ptr);
329 acc->rfc5626_instprm.slen = len;
330 }
331
332 if (acc_cfg->rfc5626_reg_id.slen==0) {
333 acc->rfc5626_regprm = pj_str(";reg-id=1");
334 } else {
335 const char *prmname = ";reg-id=";
336 unsigned len;
337
338 len = pj_ansi_strlen(prmname) + acc_cfg->rfc5626_reg_id.slen;
339 acc->rfc5626_regprm.ptr = (char*)pj_pool_alloc(acc->pool, len+1);
340 pj_ansi_snprintf(acc->rfc5626_regprm.ptr, len+1,
341 "%s%.*s\"",
342 prmname,
343 (int)acc_cfg->rfc5626_reg_id.slen,
344 acc_cfg->rfc5626_reg_id.ptr);
345 acc->rfc5626_regprm.slen = len;
346 }
347 }
348
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000349 /* Mark account as valid */
350 pjsua_var.acc[acc_id].valid = PJ_TRUE;
351
Benny Prijono093d3022006-09-24 00:07:11 +0000352 /* Insert account ID into account ID array, sorted by priority */
353 for (i=0; i<pjsua_var.acc_cnt; ++i) {
354 if ( pjsua_var.acc[pjsua_var.acc_ids[i]].cfg.priority <
355 pjsua_var.acc[acc_id].cfg.priority)
356 {
357 break;
358 }
359 }
360 pj_array_insert(pjsua_var.acc_ids, sizeof(pjsua_var.acc_ids[0]),
361 pjsua_var.acc_cnt, i, &acc_id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000362
363 return PJ_SUCCESS;
364}
365
366
367/*
368 * Add a new account to pjsua.
369 */
370PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
371 pj_bool_t is_default,
372 pjsua_acc_id *p_acc_id)
373{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000374 pjsua_acc *acc;
Benny Prijono91d06b62008-09-20 12:16:56 +0000375 unsigned i, id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000376 pj_status_t status;
377
378 PJ_ASSERT_RETURN(pjsua_var.acc_cnt < PJ_ARRAY_SIZE(pjsua_var.acc),
379 PJ_ETOOMANY);
380
381 /* Must have a transport */
Benny Prijonoe93e2872006-06-28 16:46:49 +0000382 PJ_ASSERT_RETURN(pjsua_var.tpdata[0].data.ptr != NULL, PJ_EINVALIDOP);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000383
384 PJSUA_LOCK();
385
386 /* Find empty account id. */
387 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.acc); ++id) {
388 if (pjsua_var.acc[id].valid == PJ_FALSE)
389 break;
390 }
391
392 /* Expect to find a slot */
393 PJ_ASSERT_ON_FAIL( id < PJ_ARRAY_SIZE(pjsua_var.acc),
394 {PJSUA_UNLOCK(); return PJ_EBUG;});
395
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000396 acc = &pjsua_var.acc[id];
397
398 /* Create pool for this account. */
399 if (acc->pool)
400 pj_pool_reset(acc->pool);
401 else
402 acc->pool = pjsua_pool_create("acc%p", 512, 256);
403
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000404 /* Copy config */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000405 pjsua_acc_config_dup(acc->pool, &pjsua_var.acc[id].cfg, cfg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000406
Sauw Ming9b80d512011-03-15 03:20:37 +0000407 /* Normalize registration timeout and refresh delay */
408 if (pjsua_var.acc[id].cfg.reg_uri.slen) {
409 if (pjsua_var.acc[id].cfg.reg_timeout == 0) {
410 pjsua_var.acc[id].cfg.reg_timeout = PJSUA_REG_INTERVAL;
411 }
412 if (pjsua_var.acc[id].cfg.reg_delay_before_refresh == 0) {
413 pjsua_var.acc[id].cfg.reg_delay_before_refresh =
414 PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH;
415 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000416 }
417
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000418 /* Get CRC of account proxy setting */
419 acc->local_route_crc = calc_proxy_crc(acc->cfg.proxy, acc->cfg.proxy_cnt);
420
421 /* Get CRC of global outbound proxy setting */
422 acc->global_route_crc=calc_proxy_crc(pjsua_var.ua_cfg.outbound_proxy,
423 pjsua_var.ua_cfg.outbound_proxy_cnt);
424
Benny Prijono91d06b62008-09-20 12:16:56 +0000425 /* Check the route URI's and force loose route if required */
426 for (i=0; i<acc->cfg.proxy_cnt; ++i) {
427 status = normalize_route_uri(acc->pool, &acc->cfg.proxy[i]);
428 if (status != PJ_SUCCESS)
429 return status;
430 }
431
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000432 status = initialize_acc(id);
433 if (status != PJ_SUCCESS) {
434 pjsua_perror(THIS_FILE, "Error adding account", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000435 pj_pool_release(acc->pool);
436 acc->pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000437 PJSUA_UNLOCK();
438 return status;
439 }
440
441 if (is_default)
442 pjsua_var.default_acc = id;
443
444 if (p_acc_id)
445 *p_acc_id = id;
446
447 pjsua_var.acc_cnt++;
448
449 PJSUA_UNLOCK();
450
451 PJ_LOG(4,(THIS_FILE, "Account %.*s added with id %d",
452 (int)cfg->id.slen, cfg->id.ptr, id));
453
454 /* If accounts has registration enabled, start registration */
455 if (pjsua_var.acc[id].cfg.reg_uri.slen)
456 pjsua_acc_set_registration(id, PJ_TRUE);
Benny Prijono4dd961b2009-10-26 11:21:37 +0000457 else {
458 /* Otherwise subscribe to MWI, if it's enabled */
459 if (pjsua_var.acc[id].cfg.mwi_enabled)
460 pjsua_start_mwi(&pjsua_var.acc[id]);
461 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000462
463 return PJ_SUCCESS;
464}
465
466
467/*
468 * Add local account
469 */
470PJ_DEF(pj_status_t) pjsua_acc_add_local( pjsua_transport_id tid,
471 pj_bool_t is_default,
472 pjsua_acc_id *p_acc_id)
473{
474 pjsua_acc_config cfg;
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000475 pjsua_transport_data *t = &pjsua_var.tpdata[tid];
Benny Prijonod0bd4982007-12-02 15:40:52 +0000476 const char *beginquote, *endquote;
477 char transport_param[32];
Benny Prijonoe85bc412006-07-29 20:29:24 +0000478 char uri[PJSIP_MAX_URL_SIZE];
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000479
Benny Prijonoe93e2872006-06-28 16:46:49 +0000480 /* ID must be valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000481 PJ_ASSERT_RETURN(tid>=0 && tid<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
482 PJ_EINVAL);
Benny Prijonoe93e2872006-06-28 16:46:49 +0000483
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000484 /* Transport must be valid */
Benny Prijonoe93e2872006-06-28 16:46:49 +0000485 PJ_ASSERT_RETURN(t->data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000486
487 pjsua_acc_config_default(&cfg);
488
Benny Prijono093d3022006-09-24 00:07:11 +0000489 /* Lower the priority of local account */
490 --cfg.priority;
491
Benny Prijonod0bd4982007-12-02 15:40:52 +0000492 /* Enclose IPv6 address in square brackets */
493 if (t->type & PJSIP_TRANSPORT_IPV6) {
494 beginquote = "[";
495 endquote = "]";
496 } else {
497 beginquote = endquote = "";
498 }
499
500 /* Don't add transport parameter if it's UDP */
Benny Prijono4c82c1e2008-10-16 08:14:51 +0000501 if (t->type!=PJSIP_TRANSPORT_UDP && t->type!=PJSIP_TRANSPORT_UDP6) {
Benny Prijonod0bd4982007-12-02 15:40:52 +0000502 pj_ansi_snprintf(transport_param, sizeof(transport_param),
503 ";transport=%s",
504 pjsip_transport_get_type_name(t->type));
505 } else {
506 transport_param[0] = '\0';
507 }
508
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000509 /* Build URI for the account */
Benny Prijonoe85bc412006-07-29 20:29:24 +0000510 pj_ansi_snprintf(uri, PJSIP_MAX_URL_SIZE,
Benny Prijonod0bd4982007-12-02 15:40:52 +0000511 "<sip:%s%.*s%s:%d%s>",
512 beginquote,
Benny Prijonoe85bc412006-07-29 20:29:24 +0000513 (int)t->local_name.host.slen,
514 t->local_name.host.ptr,
Benny Prijonod0bd4982007-12-02 15:40:52 +0000515 endquote,
Benny Prijonoe85bc412006-07-29 20:29:24 +0000516 t->local_name.port,
Benny Prijonod0bd4982007-12-02 15:40:52 +0000517 transport_param);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518
519 cfg.id = pj_str(uri);
520
521 return pjsua_acc_add(&cfg, is_default, p_acc_id);
522}
523
524
525/*
Benny Prijono705e7842008-07-21 18:12:51 +0000526 * Set arbitrary data to be associated with the account.
527 */
528PJ_DEF(pj_status_t) pjsua_acc_set_user_data(pjsua_acc_id acc_id,
529 void *user_data)
530{
531 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
532 PJ_EINVAL);
533 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
534
535 PJSUA_LOCK();
536
537 pjsua_var.acc[acc_id].cfg.user_data = user_data;
538
539 PJSUA_UNLOCK();
540
541 return PJ_SUCCESS;
542}
543
544
545/*
546 * Retrieve arbitrary data associated with the account.
547 */
548PJ_DEF(void*) pjsua_acc_get_user_data(pjsua_acc_id acc_id)
549{
550 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
551 NULL);
552 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, NULL);
553
554 return pjsua_var.acc[acc_id].cfg.user_data;
555}
556
557
558/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000559 * Delete account.
560 */
561PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id)
562{
Benny Prijono093d3022006-09-24 00:07:11 +0000563 unsigned i;
564
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000565 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
566 PJ_EINVAL);
567 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
568
569 PJSUA_LOCK();
570
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +0000571 /* Cancel any re-registration timer */
572 pjsua_cancel_timer(&pjsua_var.acc[acc_id].auto_rereg.timer);
573
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000574 /* Delete registration */
Benny Prijono922933b2007-01-21 16:23:56 +0000575 if (pjsua_var.acc[acc_id].regc != NULL) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000576 pjsua_acc_set_registration(acc_id, PJ_FALSE);
Benny Prijonoe68e99f2007-06-06 00:28:10 +0000577 if (pjsua_var.acc[acc_id].regc) {
578 pjsip_regc_destroy(pjsua_var.acc[acc_id].regc);
579 }
Benny Prijono922933b2007-01-21 16:23:56 +0000580 pjsua_var.acc[acc_id].regc = NULL;
581 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000582
583 /* Delete server presence subscription */
584 pjsua_pres_delete_acc(acc_id);
585
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000586 /* Release account pool */
587 if (pjsua_var.acc[acc_id].pool) {
588 pj_pool_release(pjsua_var.acc[acc_id].pool);
589 pjsua_var.acc[acc_id].pool = NULL;
590 }
591
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000592 /* Invalidate */
593 pjsua_var.acc[acc_id].valid = PJ_FALSE;
Benny Prijono978de6e2008-09-15 14:56:05 +0000594 pjsua_var.acc[acc_id].contact.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000595
Benny Prijono093d3022006-09-24 00:07:11 +0000596 /* Remove from array */
597 for (i=0; i<pjsua_var.acc_cnt; ++i) {
598 if (pjsua_var.acc_ids[i] == acc_id)
599 break;
600 }
601 if (i != pjsua_var.acc_cnt) {
602 pj_array_erase(pjsua_var.acc_ids, sizeof(pjsua_var.acc_ids[0]),
603 pjsua_var.acc_cnt, i);
604 --pjsua_var.acc_cnt;
605 }
606
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000607 /* Leave the calls intact, as I don't think calls need to
608 * access account once it's created
609 */
610
Benny Prijonofb2b3652007-06-28 07:15:03 +0000611 /* Update default account */
612 if (pjsua_var.default_acc == acc_id)
613 pjsua_var.default_acc = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000614
615 PJSUA_UNLOCK();
616
617 PJ_LOG(4,(THIS_FILE, "Account id %d deleted", acc_id));
618
619 return PJ_SUCCESS;
620}
621
622
623/*
624 * Modify account information.
625 */
626PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
627 const pjsua_acc_config *cfg)
628{
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000629 pjsua_acc *acc;
630 pjsip_name_addr *id_name_addr = NULL;
631 pjsip_sip_uri *id_sip_uri = NULL;
632 pjsip_sip_uri *reg_sip_uri = NULL;
633 pj_uint32_t local_route_crc, global_route_crc;
634 pjsip_route_hdr global_route;
635 pjsip_route_hdr local_route;
636 pj_str_t acc_proxy[PJSUA_ACC_MAX_PROXIES];
637 pj_bool_t update_reg = PJ_FALSE;
638 pj_status_t status = PJ_SUCCESS;
639
640 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
641 PJ_EINVAL);
642
643 PJSUA_LOCK();
644
645 acc = &pjsua_var.acc[acc_id];
646 if (!acc->valid) {
647 status = PJ_EINVAL;
648 goto on_return;
649 }
650
651 /* == Validate first == */
652
653 /* Account id */
654 if (pj_strcmp(&acc->cfg.id, &cfg->id)) {
655 /* Need to parse id to get the elements: */
656 id_name_addr = (pjsip_name_addr*)
657 pjsip_parse_uri(acc->pool, cfg->id.ptr, cfg->id.slen,
658 PJSIP_PARSE_URI_AS_NAMEADDR);
659 if (id_name_addr == NULL) {
660 status = PJSIP_EINVALIDURI;
661 pjsua_perror(THIS_FILE, "Invalid local URI", status);
662 goto on_return;
663 }
664
665 /* URI MUST be a SIP or SIPS: */
666 if (!PJSIP_URI_SCHEME_IS_SIP(id_name_addr) &&
667 !PJSIP_URI_SCHEME_IS_SIPS(id_name_addr))
668 {
669 status = PJSIP_EINVALIDSCHEME;
670 pjsua_perror(THIS_FILE, "Invalid local URI", status);
671 goto on_return;
672 }
673
674 /* Get the SIP URI object: */
675 id_sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(id_name_addr);
676 }
677
678 /* Registrar URI */
679 if (pj_strcmp(&acc->cfg.reg_uri, &cfg->reg_uri) && cfg->reg_uri.slen) {
680 pjsip_uri *reg_uri;
681
682 /* Need to parse reg_uri to get the elements: */
683 reg_uri = pjsip_parse_uri(acc->pool, cfg->reg_uri.ptr,
684 cfg->reg_uri.slen, 0);
685 if (reg_uri == NULL) {
686 status = PJSIP_EINVALIDURI;
687 pjsua_perror(THIS_FILE, "Invalid registrar URI", status);
688 goto on_return;
689 }
690
691 /* Registrar URI MUST be a SIP or SIPS: */
692 if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) &&
693 !PJSIP_URI_SCHEME_IS_SIPS(reg_uri))
694 {
695 status = PJSIP_EINVALIDSCHEME;
696 pjsua_perror(THIS_FILE, "Invalid registar URI", status);
697 goto on_return;
698 }
699
700 reg_sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri);
701 }
702
703 /* Global outbound proxy */
704 global_route_crc = calc_proxy_crc(pjsua_var.ua_cfg.outbound_proxy,
705 pjsua_var.ua_cfg.outbound_proxy_cnt);
706 if (global_route_crc != acc->global_route_crc) {
707 pjsip_route_hdr *r;
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000708
Benny Prijono29c8ca32010-06-22 06:02:13 +0000709 /* Copy from global outbound proxies */
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000710 pj_list_init(&global_route);
Benny Prijono29c8ca32010-06-22 06:02:13 +0000711 r = pjsua_var.outbound_proxy.next;
712 while (r != &pjsua_var.outbound_proxy) {
713 pj_list_push_back(&global_route,
714 pjsip_hdr_shallow_clone(acc->pool, r));
715 r = r->next;
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000716 }
717 }
718
719 /* Account proxy */
720 local_route_crc = calc_proxy_crc(cfg->proxy, cfg->proxy_cnt);
721 if (local_route_crc != acc->local_route_crc) {
722 pjsip_route_hdr *r;
723 unsigned i;
724
725 /* Validate the local route and save it to temporary var */
726 pj_list_init(&local_route);
727 for (i=0; i<cfg->proxy_cnt; ++i) {
728 pj_str_t hname = { "Route", 5};
729
730 pj_strdup_with_null(acc->pool, &acc_proxy[i], &cfg->proxy[i]);
731 status = normalize_route_uri(acc->pool, &acc_proxy[i]);
732 if (status != PJ_SUCCESS)
733 goto on_return;
734 r = (pjsip_route_hdr*)
735 pjsip_parse_hdr(acc->pool, &hname, acc_proxy[i].ptr,
736 acc_proxy[i].slen, NULL);
737 if (r == NULL) {
738 status = PJSIP_EINVALIDURI;
739 pjsua_perror(THIS_FILE, "Invalid URI in account route set",
740 status);
741 goto on_return;
742 }
743
744 pj_list_push_back(&local_route, r);
745 }
746 }
747
748
749 /* == Apply the new config == */
750
751 /* Account ID. */
752 if (id_name_addr && id_sip_uri) {
753 pj_strdup_with_null(acc->pool, &acc->cfg.id, &cfg->id);
Benny Prijonof0ba2dc2011-04-14 15:38:23 +0000754 pj_strdup_with_null(acc->pool, &acc->display, &id_name_addr->display);
755 pj_strdup_with_null(acc->pool, &acc->user_part, &id_sip_uri->user);
756 pj_strdup_with_null(acc->pool, &acc->srv_domain, &id_sip_uri->host);
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000757 acc->srv_port = 0;
758 update_reg = PJ_TRUE;
759 }
760
761 /* User data */
762 acc->cfg.user_data = cfg->user_data;
763
764 /* Priority */
765 if (acc->cfg.priority != cfg->priority) {
766 unsigned i;
767
768 acc->cfg.priority = cfg->priority;
769
770 /* Resort accounts priority */
771 for (i=0; i<pjsua_var.acc_cnt; ++i) {
772 if (pjsua_var.acc_ids[i] == acc_id)
773 break;
774 }
775 pj_assert(i < pjsua_var.acc_cnt);
776 pj_array_erase(pjsua_var.acc_ids, sizeof(acc_id),
777 pjsua_var.acc_cnt, i);
778 for (i=0; i<pjsua_var.acc_cnt; ++i) {
779 if (pjsua_var.acc[pjsua_var.acc_ids[i]].cfg.priority <
780 acc->cfg.priority)
781 {
782 break;
783 }
784 }
785 pj_array_insert(pjsua_var.acc_ids, sizeof(acc_id),
786 pjsua_var.acc_cnt, i, &acc_id);
787 }
788
789 /* MWI */
790 acc->cfg.mwi_enabled = cfg->mwi_enabled;
791
792 /* PIDF tuple ID */
793 if (pj_strcmp(&acc->cfg.pidf_tuple_id, &cfg->pidf_tuple_id))
794 pj_strdup_with_null(acc->pool, &acc->cfg.pidf_tuple_id,
795 &cfg->pidf_tuple_id);
796
797 /* Publish */
798 acc->cfg.publish_opt = cfg->publish_opt;
799 acc->cfg.unpublish_max_wait_time_msec = cfg->unpublish_max_wait_time_msec;
800 if (acc->cfg.publish_enabled != cfg->publish_enabled) {
801 acc->cfg.publish_enabled = cfg->publish_enabled;
802 if (!acc->cfg.publish_enabled)
803 pjsua_pres_unpublish(acc);
804 else
805 update_reg = PJ_TRUE;
806 }
807
808 /* Force contact URI */
809 if (pj_strcmp(&acc->cfg.force_contact, &cfg->force_contact)) {
810 pj_strdup_with_null(acc->pool, &acc->cfg.force_contact,
811 &cfg->force_contact);
812 update_reg = PJ_TRUE;
813 }
814
815 /* Contact param */
816 if (pj_strcmp(&acc->cfg.contact_params, &cfg->contact_params)) {
817 pj_strdup_with_null(acc->pool, &acc->cfg.contact_params,
818 &cfg->contact_params);
819 update_reg = PJ_TRUE;
820 }
821
822 /* Contact URI params */
823 if (pj_strcmp(&acc->cfg.contact_uri_params, &cfg->contact_uri_params)) {
824 pj_strdup_with_null(acc->pool, &acc->cfg.contact_uri_params,
825 &cfg->contact_uri_params);
826 update_reg = PJ_TRUE;
827 }
828
829 /* Reliable provisional response */
830 acc->cfg.require_100rel = cfg->require_100rel;
831
832 /* Session timer */
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000833 acc->cfg.use_timer = cfg->use_timer;
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000834 acc->cfg.timer_setting = cfg->timer_setting;
835
836 /* Transport and keep-alive */
837 if (acc->cfg.transport_id != cfg->transport_id) {
838 acc->cfg.transport_id = cfg->transport_id;
839 update_reg = PJ_TRUE;
840 }
841 acc->cfg.ka_interval = cfg->ka_interval;
842 if (pj_strcmp(&acc->cfg.ka_data, &cfg->ka_data))
843 pj_strdup(acc->pool, &acc->cfg.ka_data, &cfg->ka_data);
844#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
845 acc->cfg.use_srtp = cfg->use_srtp;
846 acc->cfg.srtp_secure_signaling = cfg->srtp_secure_signaling;
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +0000847 acc->cfg.srtp_optional_dup_offer = cfg->srtp_optional_dup_offer;
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000848#endif
849
Nanang Izzuddin5e39a2b2010-09-20 06:13:02 +0000850#if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0)
851 acc->cfg.use_stream_ka = cfg->use_stream_ka;
852#endif
853
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000854 /* Global outbound proxy */
855 if (global_route_crc != acc->global_route_crc) {
856 unsigned i, rcnt;
857
858 /* Remove the outbound proxies from the route set */
859 rcnt = pj_list_size(&acc->route_set);
860 for (i=0; i < rcnt - acc->cfg.proxy_cnt; ++i) {
861 pjsip_route_hdr *r = acc->route_set.next;
862 pj_list_erase(r);
863 }
864
865 /* Insert the outbound proxies to the beginning of route set */
866 pj_list_merge_first(&acc->route_set, &global_route);
867
868 /* Update global route CRC */
869 acc->global_route_crc = global_route_crc;
870
871 update_reg = PJ_TRUE;
872 }
873
874 /* Account proxy */
875 if (local_route_crc != acc->local_route_crc) {
876 unsigned i;
877
878 /* Remove the current account proxies from the route set */
879 for (i=0; i < acc->cfg.proxy_cnt; ++i) {
880 pjsip_route_hdr *r = acc->route_set.prev;
881 pj_list_erase(r);
882 }
883
884 /* Insert new proxy setting to the route set */
885 pj_list_merge_last(&acc->route_set, &local_route);
886
887 /* Update the proxy setting */
888 acc->cfg.proxy_cnt = cfg->proxy_cnt;
889 for (i = 0; i < cfg->proxy_cnt; ++i)
890 acc->cfg.proxy[i] = acc_proxy[i];
891
892 /* Update local route CRC */
893 acc->local_route_crc = local_route_crc;
894
895 update_reg = PJ_TRUE;
896 }
897
898 /* Credential info */
899 {
900 unsigned i;
901
902 /* Selective update credential info. */
903 for (i = 0; i < cfg->cred_count; ++i) {
904 unsigned j;
905 pjsip_cred_info ci;
906
907 /* Find if this credential is already listed */
Nanang Izzuddin7f8d76f2011-03-29 03:47:16 +0000908 for (j = i; j < acc->cfg.cred_count; ++j) {
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000909 if (pjsip_cred_info_cmp(&acc->cfg.cred_info[j],
910 &cfg->cred_info[i]) == 0)
911 {
912 /* Found, but different index/position, swap */
913 if (j != i) {
914 ci = acc->cfg.cred_info[i];
915 acc->cfg.cred_info[i] = acc->cfg.cred_info[j];
916 acc->cfg.cred_info[j] = ci;
917 }
918 break;
919 }
920 }
921
922 /* Not found, insert this */
923 if (j == acc->cfg.cred_count) {
924 /* If account credential is full, discard the last one. */
925 if (acc->cfg.cred_count == PJ_ARRAY_SIZE(acc->cfg.cred_info)) {
926 pj_array_erase(acc->cfg.cred_info, sizeof(pjsip_cred_info),
927 acc->cfg.cred_count, acc->cfg.cred_count-1);
928 acc->cfg.cred_count--;
929 }
930
931 /* Insert this */
932 pjsip_cred_info_dup(acc->pool, &ci, &cfg->cred_info[i]);
933 pj_array_insert(acc->cfg.cred_info, sizeof(pjsip_cred_info),
934 acc->cfg.cred_count, i, &ci);
935 }
936 }
937 acc->cfg.cred_count = cfg->cred_count;
938
939 /* Concatenate credentials from account config and global config */
940 acc->cred_cnt = 0;
941 for (i=0; i<acc->cfg.cred_count; ++i) {
942 acc->cred[acc->cred_cnt++] = acc->cfg.cred_info[i];
943 }
944 for (i=0; i<pjsua_var.ua_cfg.cred_count &&
945 acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i)
946 {
947 acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i];
948 }
949 }
950
951 /* Authentication preference */
952 acc->cfg.auth_pref.initial_auth = cfg->auth_pref.initial_auth;
953 if (pj_strcmp(&acc->cfg.auth_pref.algorithm, &cfg->auth_pref.algorithm))
954 pj_strdup_with_null(acc->pool, &acc->cfg.auth_pref.algorithm,
955 &cfg->auth_pref.algorithm);
956
957 /* Registration */
958 acc->cfg.reg_timeout = cfg->reg_timeout;
959 acc->cfg.unreg_timeout = cfg->unreg_timeout;
960 acc->cfg.allow_contact_rewrite = cfg->allow_contact_rewrite;
961 acc->cfg.reg_retry_interval = cfg->reg_retry_interval;
962 acc->cfg.drop_calls_on_reg_fail = cfg->drop_calls_on_reg_fail;
Sauw Ming8ad06c52011-03-15 10:49:59 +0000963 if (acc->cfg.reg_delay_before_refresh != cfg->reg_delay_before_refresh) {
964 acc->cfg.reg_delay_before_refresh = cfg->reg_delay_before_refresh;
965 pjsip_regc_set_delay_before_refresh(acc->regc,
966 cfg->reg_delay_before_refresh);
967 }
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000968
Sauw Ming9b80d512011-03-15 03:20:37 +0000969 /* Normalize registration timeout and refresh delay */
970 if (acc->cfg.reg_uri.slen ) {
971 if (acc->cfg.reg_timeout == 0) {
972 acc->cfg.reg_timeout = PJSUA_REG_INTERVAL;
973 }
974 if (acc->cfg.reg_delay_before_refresh == 0) {
975 acc->cfg.reg_delay_before_refresh =
976 PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH;
977 }
978 }
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000979
980 /* Registrar URI */
981 if (pj_strcmp(&acc->cfg.reg_uri, &cfg->reg_uri)) {
982 if (cfg->reg_uri.slen) {
983 pj_strdup_with_null(acc->pool, &acc->cfg.reg_uri, &cfg->reg_uri);
984 if (reg_sip_uri)
985 acc->srv_port = reg_sip_uri->port;
986 } else {
987 /* Unregister if registration was set */
988 if (acc->cfg.reg_uri.slen)
989 pjsua_acc_set_registration(acc->index, PJ_FALSE);
990 pj_bzero(&acc->cfg.reg_uri, sizeof(acc->cfg.reg_uri));
991 }
992 update_reg = PJ_TRUE;
993 }
994
Benny Prijonob54719f2010-11-16 03:07:46 +0000995 /* SIP outbound setting */
996 if (acc->cfg.use_rfc5626 != cfg->use_rfc5626 ||
997 pj_strcmp(&acc->cfg.rfc5626_instance_id, &cfg->rfc5626_instance_id) ||
998 pj_strcmp(&acc->cfg.rfc5626_reg_id, &cfg->rfc5626_reg_id))
999 {
1000 update_reg = PJ_TRUE;
1001 }
1002
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +00001003 /* Update registration */
1004 if (update_reg) {
1005 /* If accounts has registration enabled, start registration */
1006 if (acc->cfg.reg_uri.slen)
1007 pjsua_acc_set_registration(acc->index, PJ_TRUE);
1008 else {
1009 /* Otherwise subscribe to MWI, if it's enabled */
1010 if (acc->cfg.mwi_enabled)
1011 pjsua_start_mwi(acc);
1012 }
1013 }
1014
1015on_return:
1016 PJSUA_UNLOCK();
1017 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001018}
1019
1020
1021/*
1022 * Modify account's presence status to be advertised to remote/presence
1023 * subscribers.
1024 */
1025PJ_DEF(pj_status_t) pjsua_acc_set_online_status( pjsua_acc_id acc_id,
1026 pj_bool_t is_online)
1027{
1028 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
1029 PJ_EINVAL);
1030 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
1031
1032 pjsua_var.acc[acc_id].online_status = is_online;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001033 pj_bzero(&pjsua_var.acc[acc_id].rpid, sizeof(pjrpid_element));
1034 pjsua_pres_update_acc(acc_id, PJ_FALSE);
1035 return PJ_SUCCESS;
1036}
1037
1038
1039/*
1040 * Set online status with extended information
1041 */
1042PJ_DEF(pj_status_t) pjsua_acc_set_online_status2( pjsua_acc_id acc_id,
1043 pj_bool_t is_online,
1044 const pjrpid_element *pr)
1045{
1046 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
1047 PJ_EINVAL);
1048 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
1049
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001050 PJSUA_LOCK();
Benny Prijono4461c7d2007-08-25 13:36:15 +00001051 pjsua_var.acc[acc_id].online_status = is_online;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001052 pjrpid_element_dup(pjsua_var.acc[acc_id].pool, &pjsua_var.acc[acc_id].rpid, pr);
1053 PJSUA_UNLOCK();
1054
Benny Prijono4461c7d2007-08-25 13:36:15 +00001055 pjsua_pres_update_acc(acc_id, PJ_TRUE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001056 return PJ_SUCCESS;
1057}
1058
Benny Prijonob54719f2010-11-16 03:07:46 +00001059/* Create reg_contact, mainly for SIP outbound */
1060static void update_regc_contact(pjsua_acc *acc)
1061{
1062 pjsua_acc_config *acc_cfg = &acc->cfg;
1063 pj_bool_t need_outbound = PJ_FALSE;
1064 const pj_str_t tcp_param = pj_str(";transport=tcp");
1065 const pj_str_t tls_param = pj_str(";transport=tls");
1066
1067 if (!acc_cfg->use_rfc5626)
1068 goto done;
1069
Benny Prijono2562b752010-11-16 06:01:38 +00001070 /* Check if outbound has been requested and rejected */
1071 if (acc->rfc5626_status == OUTBOUND_NA)
1072 goto done;
1073
Benny Prijonob54719f2010-11-16 03:07:46 +00001074 if (pj_stristr(&acc->contact, &tcp_param)==NULL &&
1075 pj_stristr(&acc->contact, &tls_param)==NULL)
1076 {
1077 /* Currently we can only do SIP outbound for TCP
1078 * and TLS.
1079 */
1080 goto done;
1081 }
1082
1083 /* looks like we can use outbound */
1084 need_outbound = PJ_TRUE;
1085
1086done:
1087 if (!need_outbound) {
1088 /* Outbound is not needed/wanted for the account. acc->reg_contact
1089 * is set to the same as acc->contact.
1090 */
1091 acc->reg_contact = acc->contact;
Benny Prijono2562b752010-11-16 06:01:38 +00001092 acc->rfc5626_status = OUTBOUND_NA;
Benny Prijonob54719f2010-11-16 03:07:46 +00001093 } else {
1094 /* Need to use outbound, append the contact with +sip.instance and
1095 * reg-id parameters.
1096 */
1097 unsigned len;
1098 pj_str_t reg_contact;
1099
1100 acc->rfc5626_status = OUTBOUND_WANTED;
1101 len = acc->contact.slen + acc->rfc5626_instprm.slen +
1102 acc->rfc5626_regprm.slen;
Benny Prijonoe49e6202010-11-16 07:01:25 +00001103 reg_contact.ptr = (char*) pj_pool_alloc(acc->pool, len);
Benny Prijonob54719f2010-11-16 03:07:46 +00001104
1105 pj_strcpy(&reg_contact, &acc->contact);
1106 pj_strcat(&reg_contact, &acc->rfc5626_regprm);
1107 pj_strcat(&reg_contact, &acc->rfc5626_instprm);
1108
1109 acc->reg_contact = reg_contact;
1110
1111 PJ_LOG(4,(THIS_FILE,
1112 "Contact for acc %d updated for SIP outbound: %.*s",
1113 acc->index,
1114 (int)acc->reg_contact.slen,
1115 acc->reg_contact.ptr));
1116 }
1117}
1118
Benny Prijono7f630432008-09-24 16:52:41 +00001119/* Check if IP is private IP address */
1120static pj_bool_t is_private_ip(const pj_str_t *addr)
1121{
1122 const pj_str_t private_net[] =
1123 {
1124 { "10.", 3 },
1125 { "127.", 4 },
1126 { "172.16.", 7 },
1127 { "192.168.", 8 }
1128 };
1129 unsigned i;
1130
1131 for (i=0; i<PJ_ARRAY_SIZE(private_net); ++i) {
1132 if (pj_strncmp(addr, &private_net[i], private_net[i].slen)==0)
1133 return PJ_TRUE;
1134 }
1135
1136 return PJ_FALSE;
1137}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001138
Benny Prijono15b02302007-09-27 14:07:07 +00001139/* Update NAT address from the REGISTER response */
1140static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
1141 struct pjsip_regc_cbparam *param)
1142{
1143 pjsip_transport *tp;
1144 const pj_str_t *via_addr;
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001145 pj_pool_t *pool;
Benny Prijono15b02302007-09-27 14:07:07 +00001146 int rport;
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001147 pjsip_sip_uri *uri;
Benny Prijono15b02302007-09-27 14:07:07 +00001148 pjsip_via_hdr *via;
Benny Prijono84d24932009-06-04 15:51:39 +00001149 pj_sockaddr contact_addr;
1150 pj_sockaddr recv_addr;
1151 pj_status_t status;
1152 pj_bool_t matched;
Benny Prijono7f630432008-09-24 16:52:41 +00001153 pj_str_t srv_ip;
Nanang Izzuddin5af37ff2009-08-05 18:41:23 +00001154 pjsip_contact_hdr *contact_hdr;
1155 const pj_str_t STR_CONTACT = { "Contact", 7 };
Benny Prijono15b02302007-09-27 14:07:07 +00001156
1157 tp = param->rdata->tp_info.transport;
1158
1159 /* Only update if account is configured to auto-update */
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001160 if (acc->cfg.allow_contact_rewrite == PJ_FALSE)
Benny Prijono15b02302007-09-27 14:07:07 +00001161 return PJ_FALSE;
1162
Benny Prijonob54719f2010-11-16 03:07:46 +00001163 /* If SIP outbound is active, no need to update */
1164 if (acc->rfc5626_status == OUTBOUND_ACTIVE) {
1165 PJ_LOG(4,(THIS_FILE, "Acc %d has SIP outbound active, no need to "
1166 "update registration Contact", acc->index));
1167 return PJ_FALSE;
1168 }
1169
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001170#if 0
1171 // Always update
1172 // See http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/2008-March/002178.html
Benny Prijono15b02302007-09-27 14:07:07 +00001173
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001174 /* For UDP, only update if STUN is enabled (for now).
1175 * For TCP/TLS, always check.
1176 */
1177 if ((tp->key.type == PJSIP_TRANSPORT_UDP &&
1178 (pjsua_var.ua_cfg.stun_domain.slen != 0 ||
1179 (pjsua_var.ua_cfg.stun_host.slen != 0)) ||
1180 (tp->key.type == PJSIP_TRANSPORT_TCP) ||
1181 (tp->key.type == PJSIP_TRANSPORT_TLS))
Benny Prijono15b02302007-09-27 14:07:07 +00001182 {
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001183 /* Yes we will check */
1184 } else {
Benny Prijono15b02302007-09-27 14:07:07 +00001185 return PJ_FALSE;
1186 }
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001187#endif
Benny Prijono15b02302007-09-27 14:07:07 +00001188
1189 /* Get the received and rport info */
1190 via = param->rdata->msg_info.via;
1191 if (via->rport_param < 1) {
1192 /* Remote doesn't support rport */
1193 rport = via->sent_by.port;
Benny Prijono24a21852008-04-14 04:04:30 +00001194 if (rport==0) {
1195 pjsip_transport_type_e tp_type;
1196 tp_type = (pjsip_transport_type_e) tp->key.type;
1197 rport = pjsip_transport_get_default_port_for_type(tp_type);
1198 }
Benny Prijono15b02302007-09-27 14:07:07 +00001199 } else
1200 rport = via->rport_param;
1201
1202 if (via->recvd_param.slen != 0)
1203 via_addr = &via->recvd_param;
1204 else
1205 via_addr = &via->sent_by.host;
1206
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001207 /* Compare received and rport with the URI in our registration */
1208 pool = pjsua_pool_create("tmp", 512, 512);
Nanang Izzuddin5af37ff2009-08-05 18:41:23 +00001209 contact_hdr = (pjsip_contact_hdr*)
1210 pjsip_parse_hdr(pool, &STR_CONTACT, acc->contact.ptr,
1211 acc->contact.slen, NULL);
1212 pj_assert(contact_hdr != NULL);
1213 uri = (pjsip_sip_uri*) contact_hdr->uri;
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001214 pj_assert(uri != NULL);
Benny Prijono24a21852008-04-14 04:04:30 +00001215 uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001216
Benny Prijono24a21852008-04-14 04:04:30 +00001217 if (uri->port == 0) {
1218 pjsip_transport_type_e tp_type;
1219 tp_type = (pjsip_transport_type_e) tp->key.type;
1220 uri->port = pjsip_transport_get_default_port_for_type(tp_type);
1221 }
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001222
Benny Prijono84d24932009-06-04 15:51:39 +00001223 /* Convert IP address strings into sockaddr for comparison.
1224 * (http://trac.pjsip.org/repos/ticket/863)
1225 */
1226 status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &uri->host,
1227 &contact_addr);
1228 if (status == PJ_SUCCESS)
1229 status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, via_addr,
1230 &recv_addr);
1231 if (status == PJ_SUCCESS) {
1232 /* Compare the addresses as sockaddr according to the ticket above */
1233 matched = (uri->port == rport &&
1234 pj_sockaddr_cmp(&contact_addr, &recv_addr)==0);
1235 } else {
1236 /* Compare the addresses as string, as before */
1237 matched = (uri->port == rport &&
1238 pj_stricmp(&uri->host, via_addr)==0);
1239 }
1240
1241 if (matched) {
Benny Prijono15b02302007-09-27 14:07:07 +00001242 /* Address doesn't change */
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001243 pj_pool_release(pool);
Benny Prijono15b02302007-09-27 14:07:07 +00001244 return PJ_FALSE;
1245 }
1246
Benny Prijono7f630432008-09-24 16:52:41 +00001247 /* Get server IP */
1248 srv_ip = pj_str(param->rdata->pkt_info.src_name);
1249
Benny Prijono15b02302007-09-27 14:07:07 +00001250 /* At this point we've detected that the address as seen by registrar.
1251 * has changed.
1252 */
Benny Prijono7f630432008-09-24 16:52:41 +00001253
1254 /* Do not switch if both Contact and server's IP address are
1255 * public but response contains private IP. A NAT in the middle
Benny Prijono44e42e12009-06-03 08:37:24 +00001256 * might have messed up with the SIP packets. See:
1257 * http://trac.pjsip.org/repos/ticket/643
Benny Prijono247921b2008-09-26 22:06:11 +00001258 *
1259 * This exception can be disabled by setting allow_contact_rewrite
1260 * to 2. In this case, the switch will always be done whenever there
1261 * is difference in the IP address in the response.
Benny Prijono7f630432008-09-24 16:52:41 +00001262 */
Benny Prijono247921b2008-09-26 22:06:11 +00001263 if (acc->cfg.allow_contact_rewrite != 2 && !is_private_ip(&uri->host) &&
1264 !is_private_ip(&srv_ip) && is_private_ip(via_addr))
Benny Prijono7f630432008-09-24 16:52:41 +00001265 {
1266 /* Don't switch */
1267 pj_pool_release(pool);
1268 return PJ_FALSE;
1269 }
1270
Benny Prijono4f933762009-11-10 03:45:42 +00001271 /* Also don't switch if only the port number part is different, and
1272 * the Via received address is private.
1273 * See http://trac.pjsip.org/repos/ticket/864
1274 */
1275 if (acc->cfg.allow_contact_rewrite != 2 &&
1276 pj_sockaddr_cmp(&contact_addr, &recv_addr)==0 &&
1277 is_private_ip(via_addr))
1278 {
1279 /* Don't switch */
1280 pj_pool_release(pool);
1281 return PJ_FALSE;
1282 }
1283
Benny Prijono15b02302007-09-27 14:07:07 +00001284 PJ_LOG(3,(THIS_FILE, "IP address change detected for account %d "
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001285 "(%.*s:%d --> %.*s:%d). Updating registration "
1286 "(using method %d)",
Benny Prijono15b02302007-09-27 14:07:07 +00001287 acc->index,
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001288 (int)uri->host.slen,
1289 uri->host.ptr,
1290 uri->port,
Benny Prijono15b02302007-09-27 14:07:07 +00001291 (int)via_addr->slen,
1292 via_addr->ptr,
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001293 rport,
1294 acc->cfg.contact_rewrite_method));
Benny Prijono15b02302007-09-27 14:07:07 +00001295
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001296 pj_assert(acc->cfg.contact_rewrite_method == 1 ||
1297 acc->cfg.contact_rewrite_method == 2);
1298
1299 if (acc->cfg.contact_rewrite_method == 1) {
1300 /* Unregister current contact */
1301 pjsua_acc_set_registration(acc->index, PJ_FALSE);
1302 if (acc->regc != NULL) {
1303 pjsip_regc_destroy(acc->regc);
1304 acc->regc = NULL;
1305 acc->contact.slen = 0;
1306 }
Benny Prijono15b02302007-09-27 14:07:07 +00001307 }
1308
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001309 /*
1310 * Build new Contact header
1311 */
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001312 {
Benny Prijonob54719f2010-11-16 03:07:46 +00001313 const char *ob = ";ob";
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001314 char *tmp;
Benny Prijono8972bf02009-04-05 18:30:45 +00001315 const char *beginquote, *endquote;
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001316 int len;
1317
Benny Prijono8972bf02009-04-05 18:30:45 +00001318 /* Enclose IPv6 address in square brackets */
1319 if (tp->key.type & PJSIP_TRANSPORT_IPV6) {
1320 beginquote = "[";
1321 endquote = "]";
1322 } else {
1323 beginquote = endquote = "";
1324 }
1325
Benny Prijono24a21852008-04-14 04:04:30 +00001326 tmp = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001327 len = pj_ansi_snprintf(tmp, PJSIP_MAX_URL_SIZE,
Benny Prijonob54719f2010-11-16 03:07:46 +00001328 "<sip:%.*s%s%s%.*s%s:%d;transport=%s%.*s%s>%.*s",
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001329 (int)acc->user_part.slen,
1330 acc->user_part.ptr,
Benny Prijono83088f32008-04-22 18:33:55 +00001331 (acc->user_part.slen? "@" : ""),
Benny Prijono8972bf02009-04-05 18:30:45 +00001332 beginquote,
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001333 (int)via_addr->slen,
1334 via_addr->ptr,
Benny Prijono8972bf02009-04-05 18:30:45 +00001335 endquote,
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001336 rport,
Benny Prijono30fe4852008-12-10 16:54:16 +00001337 tp->type_name,
Nanang Izzuddine2c7e852009-08-04 14:36:17 +00001338 (int)acc->cfg.contact_uri_params.slen,
1339 acc->cfg.contact_uri_params.ptr,
Benny Prijonob54719f2010-11-16 03:07:46 +00001340 ob,
Benny Prijono30fe4852008-12-10 16:54:16 +00001341 (int)acc->cfg.contact_params.slen,
1342 acc->cfg.contact_params.ptr);
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001343 if (len < 1) {
1344 PJ_LOG(1,(THIS_FILE, "URI too long"));
1345 pj_pool_release(pool);
1346 return PJ_FALSE;
1347 }
Benny Prijono3d9b4b62008-07-14 17:55:40 +00001348 pj_strdup2_with_null(acc->pool, &acc->contact, tmp);
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001349
Benny Prijonob54719f2010-11-16 03:07:46 +00001350 update_regc_contact(acc);
1351
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001352 /* Always update, by http://trac.pjsip.org/repos/ticket/864. */
1353 pj_strdup_with_null(tp->pool, &tp->local_name.host, via_addr);
1354 tp->local_name.port = rport;
1355
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001356 }
1357
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001358 if (acc->cfg.contact_rewrite_method == 2 && acc->regc != NULL) {
Benny Prijonob54719f2010-11-16 03:07:46 +00001359 pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact);
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001360 }
Benny Prijono15b02302007-09-27 14:07:07 +00001361
1362 /* Perform new registration */
1363 pjsua_acc_set_registration(acc->index, PJ_TRUE);
1364
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001365 pj_pool_release(pool);
1366
Benny Prijono15b02302007-09-27 14:07:07 +00001367 return PJ_TRUE;
1368}
1369
Benny Prijonoa2a2d412007-10-18 05:54:02 +00001370/* Check and update Service-Route header */
1371void update_service_route(pjsua_acc *acc, pjsip_rx_data *rdata)
1372{
1373 pjsip_generic_string_hdr *hsr = NULL;
1374 pjsip_route_hdr *hr, *h;
1375 const pj_str_t HNAME = { "Service-Route", 13 };
1376 const pj_str_t HROUTE = { "Route", 5 };
1377 pjsip_uri *uri[PJSUA_ACC_MAX_PROXIES];
Benny Prijonof020ab22007-10-18 15:28:33 +00001378 unsigned i, uri_cnt = 0, rcnt;
Benny Prijonoa2a2d412007-10-18 05:54:02 +00001379
1380 /* Find and parse Service-Route headers */
1381 for (;;) {
1382 char saved;
1383 int parsed_len;
1384
1385 /* Find Service-Route header */
1386 hsr = (pjsip_generic_string_hdr*)
1387 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &HNAME, hsr);
1388 if (!hsr)
1389 break;
1390
1391 /* Parse as Route header since the syntax is similar. This may
1392 * return more than one headers.
1393 */
1394 saved = hsr->hvalue.ptr[hsr->hvalue.slen];
1395 hsr->hvalue.ptr[hsr->hvalue.slen] = '\0';
1396 hr = (pjsip_route_hdr*)
1397 pjsip_parse_hdr(rdata->tp_info.pool, &HROUTE, hsr->hvalue.ptr,
1398 hsr->hvalue.slen, &parsed_len);
1399 hsr->hvalue.ptr[hsr->hvalue.slen] = saved;
1400
1401 if (hr == NULL) {
1402 /* Error */
1403 PJ_LOG(1,(THIS_FILE, "Error parsing Service-Route header"));
1404 return;
1405 }
1406
1407 /* Save each URI in the result */
1408 h = hr;
1409 do {
1410 if (!PJSIP_URI_SCHEME_IS_SIP(h->name_addr.uri) &&
1411 !PJSIP_URI_SCHEME_IS_SIPS(h->name_addr.uri))
1412 {
1413 PJ_LOG(1,(THIS_FILE,"Error: non SIP URI in Service-Route: %.*s",
1414 (int)hsr->hvalue.slen, hsr->hvalue.ptr));
1415 return;
1416 }
1417
1418 uri[uri_cnt++] = h->name_addr.uri;
1419 h = h->next;
1420 } while (h != hr && uri_cnt != PJ_ARRAY_SIZE(uri));
1421
1422 if (h != hr) {
1423 PJ_LOG(1,(THIS_FILE, "Error: too many Service-Route headers"));
1424 return;
1425 }
1426
1427 /* Prepare to find next Service-Route header */
1428 hsr = hsr->next;
1429 if ((void*)hsr == (void*)&rdata->msg_info.msg->hdr)
1430 break;
1431 }
1432
1433 if (uri_cnt == 0)
1434 return;
1435
1436 /*
1437 * Update account's route set
1438 */
1439
1440 /* First remove all routes which are not the outbound proxies */
Benny Prijonof020ab22007-10-18 15:28:33 +00001441 rcnt = pj_list_size(&acc->route_set);
Benny Prijono2568c742007-11-03 09:29:52 +00001442 if (rcnt != pjsua_var.ua_cfg.outbound_proxy_cnt + acc->cfg.proxy_cnt) {
1443 for (i=pjsua_var.ua_cfg.outbound_proxy_cnt + acc->cfg.proxy_cnt,
1444 hr=acc->route_set.prev;
Benny Prijonof020ab22007-10-18 15:28:33 +00001445 i<rcnt;
1446 ++i)
1447 {
1448 pjsip_route_hdr *prev = hr->prev;
1449 pj_list_erase(hr);
1450 hr = prev;
1451 }
1452 }
Benny Prijonoa2a2d412007-10-18 05:54:02 +00001453
1454 /* Then append the Service-Route URIs */
1455 for (i=0; i<uri_cnt; ++i) {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001456 hr = pjsip_route_hdr_create(acc->pool);
1457 hr->name_addr.uri = (pjsip_uri*)pjsip_uri_clone(acc->pool, uri[i]);
Benny Prijonoa2a2d412007-10-18 05:54:02 +00001458 pj_list_push_back(&acc->route_set, hr);
1459 }
1460
1461 /* Done */
1462
1463 PJ_LOG(4,(THIS_FILE, "Service-Route updated for acc %d with %d URI(s)",
1464 acc->index, uri_cnt));
1465}
1466
Benny Prijonobddef2c2007-10-31 13:28:08 +00001467
1468/* Keep alive timer callback */
1469static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
1470{
1471 pjsua_acc *acc;
1472 pjsip_tpselector tp_sel;
1473 pj_time_val delay;
Benny Prijonodb4eb812007-12-09 05:30:47 +00001474 char addrtxt[PJ_INET6_ADDRSTRLEN];
Benny Prijonobddef2c2007-10-31 13:28:08 +00001475 pj_status_t status;
1476
1477 PJ_UNUSED_ARG(th);
1478
1479 PJSUA_LOCK();
1480
1481 te->id = PJ_FALSE;
1482
1483 acc = (pjsua_acc*) te->user_data;
1484
1485 /* Select the transport to send the packet */
1486 pj_bzero(&tp_sel, sizeof(tp_sel));
1487 tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT;
1488 tp_sel.u.transport = acc->ka_transport;
1489
1490 PJ_LOG(5,(THIS_FILE,
Benny Prijonodb4eb812007-12-09 05:30:47 +00001491 "Sending %d bytes keep-alive packet for acc %d to %s",
Benny Prijonobddef2c2007-10-31 13:28:08 +00001492 acc->cfg.ka_data.slen, acc->index,
Benny Prijonodb4eb812007-12-09 05:30:47 +00001493 pj_sockaddr_print(&acc->ka_target, addrtxt, sizeof(addrtxt),3)));
Benny Prijonobddef2c2007-10-31 13:28:08 +00001494
1495 /* Send raw packet */
1496 status = pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
1497 PJSIP_TRANSPORT_UDP, &tp_sel,
1498 NULL, acc->cfg.ka_data.ptr,
1499 acc->cfg.ka_data.slen,
1500 &acc->ka_target, acc->ka_target_len,
1501 NULL, NULL);
1502
1503 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
1504 pjsua_perror(THIS_FILE, "Error sending keep-alive packet", status);
1505 }
1506
1507 /* Reschedule next timer */
1508 delay.sec = acc->cfg.ka_interval;
1509 delay.msec = 0;
1510 status = pjsip_endpt_schedule_timer(pjsua_var.endpt, te, &delay);
1511 if (status == PJ_SUCCESS) {
1512 te->id = PJ_TRUE;
1513 } else {
1514 pjsua_perror(THIS_FILE, "Error starting keep-alive timer", status);
1515 }
1516
1517 PJSUA_UNLOCK();
1518}
1519
1520
1521/* Update keep-alive for the account */
1522static void update_keep_alive(pjsua_acc *acc, pj_bool_t start,
1523 struct pjsip_regc_cbparam *param)
1524{
1525 /* In all cases, stop keep-alive timer if it's running. */
1526 if (acc->ka_timer.id) {
1527 pjsip_endpt_cancel_timer(pjsua_var.endpt, &acc->ka_timer);
1528 acc->ka_timer.id = PJ_FALSE;
1529
1530 pjsip_transport_dec_ref(acc->ka_transport);
1531 acc->ka_transport = NULL;
1532 }
1533
1534 if (start) {
1535 pj_time_val delay;
1536 pj_status_t status;
1537
1538 /* Only do keep-alive if:
Benny Prijonobddef2c2007-10-31 13:28:08 +00001539 * - ka_interval is not zero in the account, and
1540 * - transport is UDP.
Benny Prijono7fff9f92008-04-04 10:50:21 +00001541 *
1542 * Previously we only enabled keep-alive when STUN is enabled, since
1543 * we thought that keep-alive is only needed in Internet situation.
1544 * But it has been discovered that Windows Firewall on WinXP also
1545 * needs to be kept-alive, otherwise incoming packets will be dropped.
1546 * So because of this, now keep-alive is always enabled for UDP,
1547 * regardless of whether STUN is enabled or not.
1548 *
1549 * Note that this applies only for UDP. For TCP/TLS, the keep-alive
1550 * is done by the transport layer.
Benny Prijonobddef2c2007-10-31 13:28:08 +00001551 */
Benny Prijono7fff9f92008-04-04 10:50:21 +00001552 if (/*pjsua_var.stun_srv.ipv4.sin_family == 0 ||*/
Benny Prijonobddef2c2007-10-31 13:28:08 +00001553 acc->cfg.ka_interval == 0 ||
1554 param->rdata->tp_info.transport->key.type != PJSIP_TRANSPORT_UDP)
1555 {
1556 /* Keep alive is not necessary */
1557 return;
1558 }
1559
1560 /* Save transport and destination address. */
1561 acc->ka_transport = param->rdata->tp_info.transport;
1562 pjsip_transport_add_ref(acc->ka_transport);
1563 pj_memcpy(&acc->ka_target, &param->rdata->pkt_info.src_addr,
1564 param->rdata->pkt_info.src_addr_len);
1565 acc->ka_target_len = param->rdata->pkt_info.src_addr_len;
1566
1567 /* Setup and start the timer */
1568 acc->ka_timer.cb = &keep_alive_timer_cb;
1569 acc->ka_timer.user_data = (void*)acc;
1570
1571 delay.sec = acc->cfg.ka_interval;
1572 delay.msec = 0;
1573 status = pjsip_endpt_schedule_timer(pjsua_var.endpt, &acc->ka_timer,
1574 &delay);
1575 if (status == PJ_SUCCESS) {
1576 acc->ka_timer.id = PJ_TRUE;
1577 PJ_LOG(4,(THIS_FILE, "Keep-alive timer started for acc %d, "
1578 "destination:%s:%d, interval:%ds",
1579 acc->index,
1580 param->rdata->pkt_info.src_name,
1581 param->rdata->pkt_info.src_port,
1582 acc->cfg.ka_interval));
1583 } else {
1584 acc->ka_timer.id = PJ_FALSE;
1585 pjsip_transport_dec_ref(acc->ka_transport);
1586 acc->ka_transport = NULL;
1587 pjsua_perror(THIS_FILE, "Error starting keep-alive timer", status);
1588 }
1589 }
1590}
1591
1592
Benny Prijonob54719f2010-11-16 03:07:46 +00001593/* Update the status of SIP outbound registration request */
1594static void update_rfc5626_status(pjsua_acc *acc, pjsip_rx_data *rdata)
1595{
1596 pjsip_require_hdr *hreq;
1597 const pj_str_t STR_OUTBOUND = {"outbound", 8};
1598 unsigned i;
1599
Benny Prijono2562b752010-11-16 06:01:38 +00001600 if (acc->rfc5626_status == OUTBOUND_UNKNOWN) {
Benny Prijonob54719f2010-11-16 03:07:46 +00001601 goto on_return;
1602 }
1603
1604 hreq = rdata->msg_info.require;
1605 if (!hreq) {
Benny Prijono2562b752010-11-16 06:01:38 +00001606 acc->rfc5626_status = OUTBOUND_NA;
Benny Prijonob54719f2010-11-16 03:07:46 +00001607 goto on_return;
1608 }
1609
1610 for (i=0; i<hreq->count; ++i) {
1611 if (pj_stricmp(&hreq->values[i], &STR_OUTBOUND)==0) {
1612 acc->rfc5626_status = OUTBOUND_ACTIVE;
1613 goto on_return;
1614 }
1615 }
1616
1617 /* Server does not support outbound */
Benny Prijono2562b752010-11-16 06:01:38 +00001618 acc->rfc5626_status = OUTBOUND_NA;
Benny Prijonob54719f2010-11-16 03:07:46 +00001619
1620on_return:
Benny Prijono2562b752010-11-16 06:01:38 +00001621 if (acc->rfc5626_status != OUTBOUND_ACTIVE) {
1622 acc->reg_contact = acc->contact;
1623 }
Benny Prijonob54719f2010-11-16 03:07:46 +00001624 PJ_LOG(4,(THIS_FILE, "SIP outbound status for acc %d is %s",
1625 acc->index, (acc->rfc5626_status==OUTBOUND_ACTIVE?
1626 "active": "not active")));
1627}
1628
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001629/*
1630 * This callback is called by pjsip_regc when outgoing register
1631 * request has completed.
1632 */
1633static void regc_cb(struct pjsip_regc_cbparam *param)
1634{
1635
Benny Prijonoa1e69682007-05-11 15:14:34 +00001636 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001637
Benny Prijono15b02302007-09-27 14:07:07 +00001638 if (param->regc != acc->regc)
1639 return;
1640
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001641 PJSUA_LOCK();
1642
1643 /*
1644 * Print registration status.
1645 */
1646 if (param->status!=PJ_SUCCESS) {
1647 pjsua_perror(THIS_FILE, "SIP registration error",
1648 param->status);
1649 pjsip_regc_destroy(acc->regc);
1650 acc->regc = NULL;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001651 acc->contact.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001652
Benny Prijonobddef2c2007-10-31 13:28:08 +00001653 /* Stop keep-alive timer if any. */
1654 update_keep_alive(acc, PJ_FALSE, NULL);
1655
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001656 } else if (param->code < 0 || param->code >= 300) {
1657 PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%.*s)",
1658 param->code,
1659 (int)param->reason.slen, param->reason.ptr));
1660 pjsip_regc_destroy(acc->regc);
1661 acc->regc = NULL;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001662 acc->contact.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001663
Benny Prijonobddef2c2007-10-31 13:28:08 +00001664 /* Stop keep-alive timer if any. */
1665 update_keep_alive(acc, PJ_FALSE, NULL);
1666
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001667 } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
1668
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001669 /* Update auto registration flag */
1670 acc->auto_rereg.active = PJ_FALSE;
1671 acc->auto_rereg.attempt_cnt = 0;
1672
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001673 if (param->expiration < 1) {
1674 pjsip_regc_destroy(acc->regc);
1675 acc->regc = NULL;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001676 acc->contact.slen = 0;
Benny Prijonobddef2c2007-10-31 13:28:08 +00001677
1678 /* Stop keep-alive timer if any. */
1679 update_keep_alive(acc, PJ_FALSE, NULL);
1680
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001681 PJ_LOG(3,(THIS_FILE, "%s: unregistration success",
1682 pjsua_var.acc[acc->index].cfg.id.ptr));
1683 } else {
Benny Prijonob54719f2010-11-16 03:07:46 +00001684 /* Check and update SIP outbound status first, since the result
1685 * will determine if we should update re-registration
1686 */
1687 update_rfc5626_status(acc, param->rdata);
1688
Benny Prijono15b02302007-09-27 14:07:07 +00001689 /* Check NAT bound address */
1690 if (acc_check_nat_addr(acc, param)) {
Benny Prijono15b02302007-09-27 14:07:07 +00001691 PJSUA_UNLOCK();
1692 return;
1693 }
1694
Benny Prijonoa2a2d412007-10-18 05:54:02 +00001695 /* Check and update Service-Route header */
1696 update_service_route(acc, param->rdata);
1697
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001698 PJ_LOG(3, (THIS_FILE,
1699 "%s: registration success, status=%d (%.*s), "
1700 "will re-register in %d seconds",
1701 pjsua_var.acc[acc->index].cfg.id.ptr,
1702 param->code,
1703 (int)param->reason.slen, param->reason.ptr,
1704 param->expiration));
Benny Prijono8b6834f2007-02-24 13:29:22 +00001705
Benny Prijonobddef2c2007-10-31 13:28:08 +00001706 /* Start keep-alive timer if necessary. */
1707 update_keep_alive(acc, PJ_TRUE, param);
1708
Benny Prijono8b6834f2007-02-24 13:29:22 +00001709 /* Send initial PUBLISH if it is enabled */
1710 if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
1711 pjsua_pres_init_publish_acc(acc->index);
Benny Prijono4dd961b2009-10-26 11:21:37 +00001712
1713 /* Subscribe to MWI, if it's enabled */
1714 if (acc->cfg.mwi_enabled)
1715 pjsua_start_mwi(acc);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001716 }
1717
1718 } else {
1719 PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
1720 }
1721
1722 acc->reg_last_err = param->status;
1723 acc->reg_last_code = param->code;
1724
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001725 /* Check if we need to auto retry registration. Basically, registration
1726 * failure codes triggering auto-retry are those of temporal failures
1727 * considered to be recoverable in relatively short term.
1728 */
1729 if (acc->cfg.reg_retry_interval &&
1730 (param->code == PJSIP_SC_REQUEST_TIMEOUT ||
1731 param->code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
1732 param->code == PJSIP_SC_BAD_GATEWAY ||
1733 param->code == PJSIP_SC_SERVICE_UNAVAILABLE ||
1734 param->code == PJSIP_SC_SERVER_TIMEOUT ||
1735 PJSIP_IS_STATUS_IN_CLASS(param->code, 600))) /* Global failure */
1736 {
1737 schedule_reregistration(acc);
1738 }
1739
Nanang Izzuddin4ea1bcc2010-09-28 04:57:01 +00001740 /* Call the registration status callback */
Nanang Izzuddinc71bed62010-05-26 15:04:43 +00001741
Nanang Izzuddin4ea1bcc2010-09-28 04:57:01 +00001742 if (pjsua_var.ua_cfg.cb.on_reg_state) {
1743 (*pjsua_var.ua_cfg.cb.on_reg_state)(acc->index);
1744 }
1745
1746 if (pjsua_var.ua_cfg.cb.on_reg_state2) {
1747 pjsua_reg_info reg_info;
1748
1749 reg_info.cbparam = param;
1750 (*pjsua_var.ua_cfg.cb.on_reg_state2)(acc->index, &reg_info);
1751 }
1752
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001753 PJSUA_UNLOCK();
1754}
1755
1756
1757/*
1758 * Initialize client registration.
1759 */
1760static pj_status_t pjsua_regc_init(int acc_id)
1761{
1762 pjsua_acc *acc;
Benny Prijonoe1a8bad2006-10-13 17:45:38 +00001763 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001764 pj_status_t status;
1765
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001766 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001767 acc = &pjsua_var.acc[acc_id];
1768
1769 if (acc->cfg.reg_uri.slen == 0) {
1770 PJ_LOG(3,(THIS_FILE, "Registrar URI is not specified"));
1771 return PJ_SUCCESS;
1772 }
1773
Benny Prijonoe1a8bad2006-10-13 17:45:38 +00001774 /* Destroy existing session, if any */
1775 if (acc->regc) {
1776 pjsip_regc_destroy(acc->regc);
1777 acc->regc = NULL;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001778 acc->contact.slen = 0;
Benny Prijonoe1a8bad2006-10-13 17:45:38 +00001779 }
1780
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001781 /* initialize SIP registration if registrar is configured */
1782
1783 status = pjsip_regc_create( pjsua_var.endpt,
1784 acc, &regc_cb, &acc->regc);
1785
1786 if (status != PJ_SUCCESS) {
1787 pjsua_perror(THIS_FILE, "Unable to create client registration",
1788 status);
1789 return status;
1790 }
1791
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001792 pool = pjsua_pool_create("tmpregc", 512, 512);
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001793
1794 if (acc->contact.slen == 0) {
1795 pj_str_t tmp_contact;
1796
1797 status = pjsua_acc_create_uac_contact( pool, &tmp_contact,
1798 acc_id, &acc->cfg.reg_uri);
1799 if (status != PJ_SUCCESS) {
1800 pjsua_perror(THIS_FILE, "Unable to generate suitable Contact header"
1801 " for registration",
1802 status);
1803 pjsip_regc_destroy(acc->regc);
1804 pj_pool_release(pool);
1805 acc->regc = NULL;
1806 return status;
1807 }
1808
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001809 pj_strdup_with_null(acc->pool, &acc->contact, &tmp_contact);
Benny Prijonob54719f2010-11-16 03:07:46 +00001810 update_regc_contact(acc);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001811 }
1812
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001813 status = pjsip_regc_init( acc->regc,
1814 &acc->cfg.reg_uri,
1815 &acc->cfg.id,
1816 &acc->cfg.id,
Benny Prijonob54719f2010-11-16 03:07:46 +00001817 1, &acc->reg_contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001818 acc->cfg.reg_timeout);
1819 if (status != PJ_SUCCESS) {
1820 pjsua_perror(THIS_FILE,
1821 "Client registration initialization error",
1822 status);
Benny Prijono19b29372007-12-05 04:08:40 +00001823 pjsip_regc_destroy(acc->regc);
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001824 pj_pool_release(pool);
Benny Prijono19b29372007-12-05 04:08:40 +00001825 acc->regc = NULL;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001826 acc->contact.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001827 return status;
1828 }
1829
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001830 /* If account is locked to specific transport, then set transport to
1831 * the client registration.
1832 */
1833 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
1834 pjsip_tpselector tp_sel;
1835
1836 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
1837 pjsip_regc_set_transport(acc->regc, &tp_sel);
1838 }
1839
1840
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001841 /* Set credentials
1842 */
1843 if (acc->cred_cnt) {
1844 pjsip_regc_set_credentials( acc->regc, acc->cred_cnt, acc->cred);
1845 }
1846
Sauw Ming9b80d512011-03-15 03:20:37 +00001847 /* Set delay before registration refresh */
1848 pjsip_regc_set_delay_before_refresh(acc->regc,
1849 acc->cfg.reg_delay_before_refresh);
1850
Benny Prijono48ab2b72007-11-08 09:24:30 +00001851 /* Set authentication preference */
1852 pjsip_regc_set_prefs(acc->regc, &acc->cfg.auth_pref);
1853
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001854 /* Set route-set
1855 */
Benny Prijono29c8ca32010-06-22 06:02:13 +00001856 if (acc->cfg.reg_use_proxy) {
1857 pjsip_route_hdr route_set;
1858 const pjsip_route_hdr *r;
1859
1860 pj_list_init(&route_set);
1861
1862 if (acc->cfg.reg_use_proxy & PJSUA_REG_USE_OUTBOUND_PROXY) {
1863 r = pjsua_var.outbound_proxy.next;
1864 while (r != &pjsua_var.outbound_proxy) {
1865 pj_list_push_back(&route_set, pjsip_hdr_shallow_clone(pool, r));
1866 r = r->next;
1867 }
1868 }
1869
1870 if (acc->cfg.reg_use_proxy & PJSUA_REG_USE_ACC_PROXY &&
1871 acc->cfg.proxy_cnt)
1872 {
1873 int cnt = acc->cfg.proxy_cnt;
1874 pjsip_route_hdr *pos = route_set.prev;
1875 int i;
1876
1877 r = acc->route_set.prev;
1878 for (i=0; i<cnt; ++i) {
1879 pj_list_push_front(pos, pjsip_hdr_shallow_clone(pool, r));
1880 r = r->prev;
1881 }
1882 }
1883
1884 if (!pj_list_empty(&route_set))
1885 pjsip_regc_set_route_set( acc->regc, &route_set );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001886 }
1887
Nanang Izzuddin60e8aa92010-09-28 10:48:48 +00001888 /* Add custom request headers specified in the account config */
1889 pjsip_regc_add_headers(acc->regc, &acc->cfg.reg_hdr_list);
1890
Benny Prijono8fc6de02006-11-11 21:25:55 +00001891 /* Add other request headers. */
1892 if (pjsua_var.ua_cfg.user_agent.slen) {
1893 pjsip_hdr hdr_list;
1894 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1895 pjsip_generic_string_hdr *h;
1896
Benny Prijono8fc6de02006-11-11 21:25:55 +00001897 pj_list_init(&hdr_list);
1898
1899 h = pjsip_generic_string_hdr_create(pool, &STR_USER_AGENT,
1900 &pjsua_var.ua_cfg.user_agent);
1901 pj_list_push_back(&hdr_list, (pjsip_hdr*)h);
1902
1903 pjsip_regc_add_headers(acc->regc, &hdr_list);
1904 }
1905
Benny Prijonob54719f2010-11-16 03:07:46 +00001906 /* If SIP outbound is used, add "Supported: outbound, path header" */
1907 if (acc->rfc5626_status == OUTBOUND_WANTED) {
1908 pjsip_hdr hdr_list;
1909 pjsip_supported_hdr *hsup;
1910
1911 pj_list_init(&hdr_list);
1912 hsup = pjsip_supported_hdr_create(pool);
1913 pj_list_push_back(&hdr_list, hsup);
1914
1915 hsup->count = 2;
1916 hsup->values[0] = pj_str("outbound");
1917 hsup->values[1] = pj_str("path");
1918
1919 pjsip_regc_add_headers(acc->regc, &hdr_list);
1920 }
1921
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001922 pj_pool_release(pool);
1923
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001924 return PJ_SUCCESS;
1925}
1926
1927
1928/*
1929 * Update registration or perform unregistration.
1930 */
1931PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id,
1932 pj_bool_t renew)
1933{
1934 pj_status_t status = 0;
1935 pjsip_tx_data *tdata = 0;
1936
1937 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
1938 PJ_EINVAL);
1939 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
1940
1941 PJSUA_LOCK();
1942
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001943 /* Cancel any re-registration timer */
1944 pjsua_cancel_timer(&pjsua_var.acc[acc_id].auto_rereg.timer);
1945
1946 /* Reset pointer to registration transport */
1947 pjsua_var.acc[acc_id].auto_rereg.reg_tp = NULL;
1948
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001949 if (renew) {
1950 if (pjsua_var.acc[acc_id].regc == NULL) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001951 status = pjsua_regc_init(acc_id);
1952 if (status != PJ_SUCCESS) {
1953 pjsua_perror(THIS_FILE, "Unable to create registration",
1954 status);
1955 goto on_return;
1956 }
1957 }
1958 if (!pjsua_var.acc[acc_id].regc) {
1959 status = PJ_EINVALIDOP;
1960 goto on_return;
1961 }
1962
1963 status = pjsip_regc_register(pjsua_var.acc[acc_id].regc, 1,
1964 &tdata);
1965
Benny Prijono28f673a2007-10-15 07:04:59 +00001966 if (0 && status == PJ_SUCCESS && pjsua_var.acc[acc_id].cred_cnt) {
1967 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1968 pjsip_authorization_hdr *h;
1969 char *uri;
1970 int d;
1971
1972 uri = (char*) pj_pool_alloc(tdata->pool, acc->cfg.reg_uri.slen+10);
1973 d = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri,
1974 uri, acc->cfg.reg_uri.slen+10);
1975 pj_assert(d > 0);
1976
1977 h = pjsip_authorization_hdr_create(tdata->pool);
1978 h->scheme = pj_str("Digest");
1979 h->credential.digest.username = acc->cred[0].username;
1980 h->credential.digest.realm = acc->srv_domain;
1981 h->credential.digest.uri = pj_str(uri);
1982 h->credential.digest.algorithm = pj_str("md5");
1983
1984 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h);
1985 }
1986
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001987 } else {
1988 if (pjsua_var.acc[acc_id].regc == NULL) {
1989 PJ_LOG(3,(THIS_FILE, "Currently not registered"));
1990 status = PJ_EINVALIDOP;
1991 goto on_return;
1992 }
Benny Prijono166d5022010-02-10 14:24:48 +00001993
1994 pjsua_pres_unpublish(&pjsua_var.acc[acc_id]);
1995
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001996 status = pjsip_regc_unregister(pjsua_var.acc[acc_id].regc, &tdata);
1997 }
1998
Benny Prijono56315612006-07-18 14:39:40 +00001999 if (status == PJ_SUCCESS) {
Benny Prijono8fc6de02006-11-11 21:25:55 +00002000 //pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002001 status = pjsip_regc_send( pjsua_var.acc[acc_id].regc, tdata );
Benny Prijono56315612006-07-18 14:39:40 +00002002 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002003
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002004 /* Update pointer to registration transport */
2005 if (status == PJ_SUCCESS) {
2006 pjsip_regc_info reg_info;
2007
2008 pjsip_regc_get_info(pjsua_var.acc[acc_id].regc, &reg_info);
2009 pjsua_var.acc[acc_id].auto_rereg.reg_tp = reg_info.transport;
2010 }
2011
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002012 if (status != PJ_SUCCESS) {
2013 pjsua_perror(THIS_FILE, "Unable to create/send REGISTER",
2014 status);
2015 } else {
2016 PJ_LOG(3,(THIS_FILE, "%s sent",
2017 (renew? "Registration" : "Unregistration")));
2018 }
2019
2020on_return:
2021 PJSUA_UNLOCK();
2022 return status;
2023}
2024
2025
2026/*
2027 * Get account information.
2028 */
2029PJ_DEF(pj_status_t) pjsua_acc_get_info( pjsua_acc_id acc_id,
2030 pjsua_acc_info *info)
2031{
2032 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2033 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
2034
2035 PJ_ASSERT_RETURN(info != NULL, PJ_EINVAL);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002036 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002037
Benny Prijonoac623b32006-07-03 15:19:31 +00002038 pj_bzero(info, sizeof(pjsua_acc_info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002039
2040 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
2041 PJ_EINVAL);
2042 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
2043
2044 PJSUA_LOCK();
2045
2046 if (pjsua_var.acc[acc_id].valid == PJ_FALSE) {
2047 PJSUA_UNLOCK();
2048 return PJ_EINVALIDOP;
2049 }
2050
2051 info->id = acc_id;
2052 info->is_default = (pjsua_var.default_acc == acc_id);
2053 info->acc_uri = acc_cfg->id;
2054 info->has_registration = (acc->cfg.reg_uri.slen > 0);
2055 info->online_status = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00002056 pj_memcpy(&info->rpid, &acc->rpid, sizeof(pjrpid_element));
2057 if (info->rpid.note.slen)
2058 info->online_status_text = info->rpid.note;
2059 else if (info->online_status)
2060 info->online_status_text = pj_str("Online");
2061 else
2062 info->online_status_text = pj_str("Offline");
2063
Sauw Ming48f6dbf2010-09-07 05:10:25 +00002064 if (acc->reg_last_code) {
Benny Prijono6f979412006-06-15 12:25:46 +00002065 if (info->has_registration) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002066 info->status = (pjsip_status_code) acc->reg_last_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002067 info->status_text = *pjsip_get_status_text(acc->reg_last_code);
Sauw Ming48f6dbf2010-09-07 05:10:25 +00002068 if (acc->reg_last_err)
2069 info->reg_last_err = acc->reg_last_err;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002070 } else {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002071 info->status = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002072 info->status_text = pj_str("not registered");
2073 }
2074 } else if (acc->cfg.reg_uri.slen) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002075 info->status = PJSIP_SC_TRYING;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002076 info->status_text = pj_str("In Progress");
2077 } else {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002078 info->status = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002079 info->status_text = pj_str("does not register");
2080 }
2081
2082 if (acc->regc) {
2083 pjsip_regc_info regc_info;
2084 pjsip_regc_get_info(acc->regc, &regc_info);
2085 info->expires = regc_info.next_reg;
2086 } else {
2087 info->expires = -1;
2088 }
2089
2090 PJSUA_UNLOCK();
2091
2092 return PJ_SUCCESS;
2093
2094}
2095
2096
2097/*
2098 * Enum accounts all account ids.
2099 */
2100PJ_DEF(pj_status_t) pjsua_enum_accs(pjsua_acc_id ids[],
2101 unsigned *count )
2102{
2103 unsigned i, c;
2104
2105 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
2106
2107 PJSUA_LOCK();
2108
2109 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2110 if (!pjsua_var.acc[i].valid)
2111 continue;
2112 ids[c] = i;
2113 ++c;
2114 }
2115
2116 *count = c;
2117
2118 PJSUA_UNLOCK();
2119
2120 return PJ_SUCCESS;
2121}
2122
2123
2124/*
2125 * Enum accounts info.
2126 */
2127PJ_DEF(pj_status_t) pjsua_acc_enum_info( pjsua_acc_info info[],
2128 unsigned *count )
2129{
2130 unsigned i, c;
2131
2132 PJ_ASSERT_RETURN(info && *count, PJ_EINVAL);
2133
2134 PJSUA_LOCK();
2135
2136 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2137 if (!pjsua_var.acc[i].valid)
2138 continue;
2139
2140 pjsua_acc_get_info(i, &info[c]);
2141 ++c;
2142 }
2143
2144 *count = c;
2145
2146 PJSUA_UNLOCK();
2147
2148 return PJ_SUCCESS;
2149}
2150
2151
2152/*
2153 * This is an internal function to find the most appropriate account to
2154 * used to reach to the specified URL.
2155 */
2156PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_outgoing(const pj_str_t *url)
2157{
2158 pj_str_t tmp;
2159 pjsip_uri *uri;
2160 pjsip_sip_uri *sip_uri;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002161 pj_pool_t *tmp_pool;
Benny Prijono093d3022006-09-24 00:07:11 +00002162 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002163
2164 PJSUA_LOCK();
2165
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002166 tmp_pool = pjsua_pool_create("tmpacc10", 256, 256);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002167
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002168 pj_strdup_with_null(tmp_pool, &tmp, url);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002169
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002170 uri = pjsip_parse_uri(tmp_pool, tmp.ptr, tmp.slen, 0);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002171 if (!uri) {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002172 pj_pool_release(tmp_pool);
Benny Prijono093d3022006-09-24 00:07:11 +00002173 PJSUA_UNLOCK();
2174 return pjsua_var.default_acc;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002175 }
2176
2177 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
2178 !PJSIP_URI_SCHEME_IS_SIPS(uri))
2179 {
2180 /* Return the first account with proxy */
Benny Prijono093d3022006-09-24 00:07:11 +00002181 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2182 if (!pjsua_var.acc[i].valid)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002183 continue;
Benny Prijono093d3022006-09-24 00:07:11 +00002184 if (!pj_list_empty(&pjsua_var.acc[i].route_set))
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002185 break;
2186 }
2187
Benny Prijono093d3022006-09-24 00:07:11 +00002188 if (i != PJ_ARRAY_SIZE(pjsua_var.acc)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002189 /* Found rather matching account */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002190 pj_pool_release(tmp_pool);
Benny Prijono093d3022006-09-24 00:07:11 +00002191 PJSUA_UNLOCK();
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002192 return i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002193 }
2194
2195 /* Not found, use default account */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002196 pj_pool_release(tmp_pool);
Benny Prijono093d3022006-09-24 00:07:11 +00002197 PJSUA_UNLOCK();
2198 return pjsua_var.default_acc;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002199 }
2200
Benny Prijonoa1e69682007-05-11 15:14:34 +00002201 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002202
Benny Prijonob4a17c92006-07-10 14:40:21 +00002203 /* Find matching domain AND port */
Benny Prijono093d3022006-09-24 00:07:11 +00002204 for (i=0; i<pjsua_var.acc_cnt; ++i) {
2205 unsigned acc_id = pjsua_var.acc_ids[i];
2206 if (pj_stricmp(&pjsua_var.acc[acc_id].srv_domain, &sip_uri->host)==0 &&
2207 pjsua_var.acc[acc_id].srv_port == sip_uri->port)
2208 {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002209 pj_pool_release(tmp_pool);
Benny Prijono093d3022006-09-24 00:07:11 +00002210 PJSUA_UNLOCK();
2211 return acc_id;
Benny Prijono21b9ad92006-08-15 13:11:22 +00002212 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002213 }
2214
Benny Prijonob4a17c92006-07-10 14:40:21 +00002215 /* If no match, try to match the domain part only */
Benny Prijono093d3022006-09-24 00:07:11 +00002216 for (i=0; i<pjsua_var.acc_cnt; ++i) {
2217 unsigned acc_id = pjsua_var.acc_ids[i];
2218 if (pj_stricmp(&pjsua_var.acc[acc_id].srv_domain, &sip_uri->host)==0)
2219 {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002220 pj_pool_release(tmp_pool);
Benny Prijono093d3022006-09-24 00:07:11 +00002221 PJSUA_UNLOCK();
2222 return acc_id;
Benny Prijonob4a17c92006-07-10 14:40:21 +00002223 }
2224 }
2225
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002226
Benny Prijono093d3022006-09-24 00:07:11 +00002227 /* Still no match, just use default account */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002228 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002229 PJSUA_UNLOCK();
Benny Prijono093d3022006-09-24 00:07:11 +00002230 return pjsua_var.default_acc;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002231}
2232
2233
2234/*
2235 * This is an internal function to find the most appropriate account to be
2236 * used to handle incoming calls.
2237 */
2238PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata)
2239{
2240 pjsip_uri *uri;
2241 pjsip_sip_uri *sip_uri;
Benny Prijono093d3022006-09-24 00:07:11 +00002242 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002243
Benny Prijono371cf0a2007-06-19 00:35:37 +00002244 /* Check that there's at least one account configured */
2245 PJ_ASSERT_RETURN(pjsua_var.acc_cnt!=0, pjsua_var.default_acc);
2246
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002247 uri = rdata->msg_info.to->uri;
2248
2249 /* Just return default account if To URI is not SIP: */
2250 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
2251 !PJSIP_URI_SCHEME_IS_SIPS(uri))
2252 {
2253 return pjsua_var.default_acc;
2254 }
2255
2256
2257 PJSUA_LOCK();
2258
2259 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
2260
2261 /* Find account which has matching username and domain. */
Benny Prijono093d3022006-09-24 00:07:11 +00002262 for (i=0; i < pjsua_var.acc_cnt; ++i) {
2263 unsigned acc_id = pjsua_var.acc_ids[i];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002264 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2265
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002266 if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
Benny Prijonob4a17c92006-07-10 14:40:21 +00002267 pj_stricmp(&acc->srv_domain, &sip_uri->host)==0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002268 {
2269 /* Match ! */
2270 PJSUA_UNLOCK();
2271 return acc_id;
2272 }
2273 }
2274
Benny Prijono093d3022006-09-24 00:07:11 +00002275 /* No matching account, try match domain part only. */
2276 for (i=0; i < pjsua_var.acc_cnt; ++i) {
2277 unsigned acc_id = pjsua_var.acc_ids[i];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002278 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2279
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002280 if (acc->valid && pj_stricmp(&acc->srv_domain, &sip_uri->host)==0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002281 /* Match ! */
2282 PJSUA_UNLOCK();
2283 return acc_id;
2284 }
2285 }
2286
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002287 /* No matching account, try match user part (and transport type) only. */
Benny Prijono093d3022006-09-24 00:07:11 +00002288 for (i=0; i < pjsua_var.acc_cnt; ++i) {
2289 unsigned acc_id = pjsua_var.acc_ids[i];
2290 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2291
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002292 if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0) {
2293
2294 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
2295 pjsip_transport_type_e type;
2296 type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
2297 if (type == PJSIP_TRANSPORT_UNSPECIFIED)
2298 type = PJSIP_TRANSPORT_UDP;
2299
2300 if (pjsua_var.tpdata[acc->cfg.transport_id].type != type)
2301 continue;
2302 }
2303
Benny Prijono093d3022006-09-24 00:07:11 +00002304 /* Match ! */
2305 PJSUA_UNLOCK();
2306 return acc_id;
2307 }
2308 }
2309
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002310 /* Still no match, use default account */
2311 PJSUA_UNLOCK();
2312 return pjsua_var.default_acc;
2313}
2314
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002315
Benny Prijonofff245c2007-04-02 11:44:47 +00002316/*
2317 * Create arbitrary requests for this account.
2318 */
2319PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id,
2320 const pjsip_method *method,
2321 const pj_str_t *target,
2322 pjsip_tx_data **p_tdata)
2323{
2324 pjsip_tx_data *tdata;
2325 pjsua_acc *acc;
2326 pjsip_route_hdr *r;
2327 pj_status_t status;
2328
2329 PJ_ASSERT_RETURN(method && target && p_tdata, PJ_EINVAL);
2330 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
2331
2332 acc = &pjsua_var.acc[acc_id];
2333
2334 status = pjsip_endpt_create_request(pjsua_var.endpt, method, target,
2335 &acc->cfg.id, target,
2336 NULL, NULL, -1, NULL, &tdata);
2337 if (status != PJ_SUCCESS) {
2338 pjsua_perror(THIS_FILE, "Unable to create request", status);
2339 return status;
2340 }
2341
2342 /* Copy routeset */
2343 r = acc->route_set.next;
2344 while (r != &acc->route_set) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00002345 pjsip_msg_add_hdr(tdata->msg,
2346 (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, r));
Benny Prijonofff245c2007-04-02 11:44:47 +00002347 r = r->next;
2348 }
Benny Prijonoe7911562009-12-14 11:13:45 +00002349
2350 /* If account is locked to specific transport, then set that transport to
2351 * the transmit data.
2352 */
2353 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
2354 pjsip_tpselector tp_sel;
2355
2356 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
2357 pjsip_tx_data_set_transport(tdata, &tp_sel);
2358 }
2359
Benny Prijonofff245c2007-04-02 11:44:47 +00002360 /* Done */
2361 *p_tdata = tdata;
2362 return PJ_SUCCESS;
2363}
2364
2365
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002366PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
2367 pj_str_t *contact,
2368 pjsua_acc_id acc_id,
2369 const pj_str_t *suri)
2370{
2371 pjsua_acc *acc;
2372 pjsip_sip_uri *sip_uri;
2373 pj_status_t status;
2374 pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
2375 pj_str_t local_addr;
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002376 pjsip_tpselector tp_sel;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002377 unsigned flag;
2378 int secure;
2379 int local_port;
Benny Prijonod0bd4982007-12-02 15:40:52 +00002380 const char *beginquote, *endquote;
2381 char transport_param[32];
Benny Prijonob54719f2010-11-16 03:07:46 +00002382 const char *ob = ";ob";
Benny Prijonod0bd4982007-12-02 15:40:52 +00002383
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002384
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002385 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002386 acc = &pjsua_var.acc[acc_id];
2387
Benny Prijonof75eceb2007-03-23 19:09:54 +00002388 /* If force_contact is configured, then use use it */
2389 if (acc->cfg.force_contact.slen) {
2390 *contact = acc->cfg.force_contact;
2391 return PJ_SUCCESS;
2392 }
2393
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002394 /* If route-set is configured for the account, then URI is the
2395 * first entry of the route-set.
2396 */
2397 if (!pj_list_empty(&acc->route_set)) {
Benny Prijono9c1528f2007-02-10 19:22:25 +00002398 sip_uri = (pjsip_sip_uri*)
2399 pjsip_uri_get_uri(acc->route_set.next->name_addr.uri);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002400 } else {
2401 pj_str_t tmp;
2402 pjsip_uri *uri;
2403
2404 pj_strdup_with_null(pool, &tmp, suri);
2405
2406 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
2407 if (uri == NULL)
2408 return PJSIP_EINVALIDURI;
2409
2410 /* For non-SIP scheme, route set should be configured */
2411 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
Benny Prijonoc7545782010-09-28 07:43:18 +00002412 return PJSIP_ENOROUTESET;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002413
Benny Prijono8c7a6172007-02-18 21:17:46 +00002414 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002415 }
2416
2417 /* Get transport type of the URI */
2418 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri))
2419 tp_type = PJSIP_TRANSPORT_TLS;
2420 else if (sip_uri->transport_param.slen == 0) {
2421 tp_type = PJSIP_TRANSPORT_UDP;
2422 } else
2423 tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
2424
2425 if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
2426 return PJSIP_EUNSUPTRANSPORT;
2427
Benny Prijonod0bd4982007-12-02 15:40:52 +00002428 /* If destination URI specifies IPv6, then set transport type
2429 * to use IPv6 as well.
2430 */
Benny Prijono19b29372007-12-05 04:08:40 +00002431 if (pj_strchr(&sip_uri->host, ':'))
Benny Prijonod0bd4982007-12-02 15:40:52 +00002432 tp_type = (pjsip_transport_type_e)(((int)tp_type) + PJSIP_TRANSPORT_IPV6);
2433
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002434 flag = pjsip_transport_get_flag_from_type(tp_type);
2435 secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
2436
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002437 /* Init transport selector. */
2438 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
2439
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002440 /* Get local address suitable to send request from */
2441 status = pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002442 pool, tp_type, &tp_sel,
2443 &local_addr, &local_port);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002444 if (status != PJ_SUCCESS)
2445 return status;
2446
Benny Prijonod0bd4982007-12-02 15:40:52 +00002447 /* Enclose IPv6 address in square brackets */
2448 if (tp_type & PJSIP_TRANSPORT_IPV6) {
2449 beginquote = "[";
2450 endquote = "]";
2451 } else {
2452 beginquote = endquote = "";
2453 }
2454
2455 /* Don't add transport parameter if it's UDP */
Benny Prijono4c82c1e2008-10-16 08:14:51 +00002456 if (tp_type!=PJSIP_TRANSPORT_UDP && tp_type!=PJSIP_TRANSPORT_UDP6) {
Benny Prijonod0bd4982007-12-02 15:40:52 +00002457 pj_ansi_snprintf(transport_param, sizeof(transport_param),
2458 ";transport=%s",
2459 pjsip_transport_get_type_name(tp_type));
2460 } else {
2461 transport_param[0] = '\0';
2462 }
2463
2464
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002465 /* Create the contact header */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002466 contact->ptr = (char*)pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002467 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
Benny Prijonob54719f2010-11-16 03:07:46 +00002468 "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s",
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002469 (int)acc->display.slen,
2470 acc->display.ptr,
2471 (acc->display.slen?" " : ""),
Benny Prijono8058a622007-06-08 04:37:05 +00002472 (secure ? PJSUA_SECURE_SCHEME : "sip"),
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002473 (int)acc->user_part.slen,
2474 acc->user_part.ptr,
2475 (acc->user_part.slen?"@":""),
Benny Prijonod0bd4982007-12-02 15:40:52 +00002476 beginquote,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002477 (int)local_addr.slen,
2478 local_addr.ptr,
Benny Prijonod0bd4982007-12-02 15:40:52 +00002479 endquote,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002480 local_port,
Benny Prijono30fe4852008-12-10 16:54:16 +00002481 transport_param,
Nanang Izzuddine2c7e852009-08-04 14:36:17 +00002482 (int)acc->cfg.contact_uri_params.slen,
2483 acc->cfg.contact_uri_params.ptr,
Benny Prijonob54719f2010-11-16 03:07:46 +00002484 ob,
Benny Prijono30fe4852008-12-10 16:54:16 +00002485 (int)acc->cfg.contact_params.slen,
2486 acc->cfg.contact_params.ptr);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002487
2488 return PJ_SUCCESS;
2489}
2490
2491
2492
2493PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool,
2494 pj_str_t *contact,
2495 pjsua_acc_id acc_id,
2496 pjsip_rx_data *rdata )
2497{
2498 /*
2499 * Section 12.1.1, paragraph about using SIPS URI in Contact.
2500 * If the request that initiated the dialog contained a SIPS URI
2501 * in the Request-URI or in the top Record-Route header field value,
2502 * if there was any, or the Contact header field if there was no
2503 * Record-Route header field, the Contact header field in the response
2504 * MUST be a SIPS URI.
2505 */
2506 pjsua_acc *acc;
2507 pjsip_sip_uri *sip_uri;
2508 pj_status_t status;
2509 pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
2510 pj_str_t local_addr;
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002511 pjsip_tpselector tp_sel;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002512 unsigned flag;
2513 int secure;
2514 int local_port;
Benny Prijonod0bd4982007-12-02 15:40:52 +00002515 const char *beginquote, *endquote;
2516 char transport_param[32];
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002517
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002518 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002519 acc = &pjsua_var.acc[acc_id];
2520
Benny Prijonof75eceb2007-03-23 19:09:54 +00002521 /* If force_contact is configured, then use use it */
2522 if (acc->cfg.force_contact.slen) {
2523 *contact = acc->cfg.force_contact;
2524 return PJ_SUCCESS;
2525 }
2526
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002527 /* If Record-Route is present, then URI is the top Record-Route. */
2528 if (rdata->msg_info.record_route) {
Benny Prijono9c1528f2007-02-10 19:22:25 +00002529 sip_uri = (pjsip_sip_uri*)
2530 pjsip_uri_get_uri(rdata->msg_info.record_route->name_addr.uri);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002531 } else {
Benny Prijonoa330d452008-08-05 20:14:39 +00002532 pjsip_hdr *pos = NULL;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002533 pjsip_contact_hdr *h_contact;
2534 pjsip_uri *uri = NULL;
2535
Benny Prijonoa330d452008-08-05 20:14:39 +00002536 /* Otherwise URI is Contact URI.
2537 * Iterate the Contact URI until we find sip: or sips: scheme.
2538 */
2539 do {
2540 h_contact = (pjsip_contact_hdr*)
2541 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT,
2542 pos);
2543 if (h_contact) {
Benny Prijono8b33bba2010-06-02 03:03:43 +00002544 if (h_contact->uri)
2545 uri = (pjsip_uri*) pjsip_uri_get_uri(h_contact->uri);
2546 else
2547 uri = NULL;
2548 if (!uri || (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
2549 !PJSIP_URI_SCHEME_IS_SIPS(uri)))
Benny Prijonoa330d452008-08-05 20:14:39 +00002550 {
2551 pos = (pjsip_hdr*)h_contact->next;
2552 if (pos == &rdata->msg_info.msg->hdr)
2553 h_contact = NULL;
2554 } else {
2555 break;
2556 }
2557 }
2558 } while (h_contact);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002559
2560
2561 /* Or if Contact URI is not present, take the remote URI from
2562 * the From URI.
2563 */
2564 if (uri == NULL)
Benny Prijonoa1e69682007-05-11 15:14:34 +00002565 uri = (pjsip_uri*) pjsip_uri_get_uri(rdata->msg_info.from->uri);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002566
2567
2568 /* Can only do sip/sips scheme at present. */
2569 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
2570 return PJSIP_EINVALIDREQURI;
2571
Benny Prijono8c7a6172007-02-18 21:17:46 +00002572 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002573 }
2574
2575 /* Get transport type of the URI */
2576 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri))
2577 tp_type = PJSIP_TRANSPORT_TLS;
2578 else if (sip_uri->transport_param.slen == 0) {
2579 tp_type = PJSIP_TRANSPORT_UDP;
2580 } else
2581 tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
Benny Prijonod0bd4982007-12-02 15:40:52 +00002582
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002583 if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
2584 return PJSIP_EUNSUPTRANSPORT;
2585
Benny Prijonod0bd4982007-12-02 15:40:52 +00002586 /* If destination URI specifies IPv6, then set transport type
2587 * to use IPv6 as well.
2588 */
2589 if (pj_strchr(&sip_uri->host, ':'))
2590 tp_type = (pjsip_transport_type_e)(((int)tp_type) + PJSIP_TRANSPORT_IPV6);
2591
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002592 flag = pjsip_transport_get_flag_from_type(tp_type);
2593 secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
2594
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002595 /* Init transport selector. */
2596 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
2597
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002598 /* Get local address suitable to send request from */
2599 status = pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002600 pool, tp_type, &tp_sel,
2601 &local_addr, &local_port);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002602 if (status != PJ_SUCCESS)
2603 return status;
2604
Benny Prijonod0bd4982007-12-02 15:40:52 +00002605 /* Enclose IPv6 address in square brackets */
2606 if (tp_type & PJSIP_TRANSPORT_IPV6) {
2607 beginquote = "[";
2608 endquote = "]";
2609 } else {
2610 beginquote = endquote = "";
2611 }
2612
2613 /* Don't add transport parameter if it's UDP */
Benny Prijono4c82c1e2008-10-16 08:14:51 +00002614 if (tp_type!=PJSIP_TRANSPORT_UDP && tp_type!=PJSIP_TRANSPORT_UDP6) {
Benny Prijonod0bd4982007-12-02 15:40:52 +00002615 pj_ansi_snprintf(transport_param, sizeof(transport_param),
2616 ";transport=%s",
2617 pjsip_transport_get_type_name(tp_type));
2618 } else {
2619 transport_param[0] = '\0';
2620 }
2621
2622
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002623 /* Create the contact header */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002624 contact->ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002625 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
Nanang Izzuddine2c7e852009-08-04 14:36:17 +00002626 "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s",
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002627 (int)acc->display.slen,
2628 acc->display.ptr,
2629 (acc->display.slen?" " : ""),
Benny Prijono8058a622007-06-08 04:37:05 +00002630 (secure ? PJSUA_SECURE_SCHEME : "sip"),
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002631 (int)acc->user_part.slen,
2632 acc->user_part.ptr,
2633 (acc->user_part.slen?"@":""),
Benny Prijonod0bd4982007-12-02 15:40:52 +00002634 beginquote,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002635 (int)local_addr.slen,
2636 local_addr.ptr,
Benny Prijonod0bd4982007-12-02 15:40:52 +00002637 endquote,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002638 local_port,
Benny Prijono30fe4852008-12-10 16:54:16 +00002639 transport_param,
Nanang Izzuddine2c7e852009-08-04 14:36:17 +00002640 (int)acc->cfg.contact_uri_params.slen,
2641 acc->cfg.contact_uri_params.ptr,
Benny Prijono30fe4852008-12-10 16:54:16 +00002642 (int)acc->cfg.contact_params.slen,
2643 acc->cfg.contact_params.ptr);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002644
2645 return PJ_SUCCESS;
2646}
2647
2648
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002649PJ_DEF(pj_status_t) pjsua_acc_set_transport( pjsua_acc_id acc_id,
2650 pjsua_transport_id tp_id)
2651{
2652 pjsua_acc *acc;
2653
2654 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
2655 acc = &pjsua_var.acc[acc_id];
2656
Benny Prijonoa1e69682007-05-11 15:14:34 +00002657 PJ_ASSERT_RETURN(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002658 PJ_EINVAL);
2659
2660 acc->cfg.transport_id = tp_id;
2661
2662 return PJ_SUCCESS;
2663}
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002664
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002665
2666/* Auto re-registration timeout callback */
2667static void auto_rereg_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
2668{
2669 pjsua_acc *acc;
2670 pj_status_t status;
2671
2672 PJ_UNUSED_ARG(th);
2673 acc = (pjsua_acc*) te->user_data;
2674 pj_assert(acc);
2675
2676 PJSUA_LOCK();
2677
Nanang Izzuddinc71bed62010-05-26 15:04:43 +00002678 /* Check if the reregistration timer is still valid, e.g: while waiting
2679 * timeout timer application might have deleted the account or disabled
2680 * the auto-reregistration.
2681 */
2682 if (!acc->valid || !acc->auto_rereg.active ||
2683 acc->cfg.reg_retry_interval == 0)
2684 {
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002685 goto on_return;
Nanang Izzuddinc71bed62010-05-26 15:04:43 +00002686 }
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002687
2688 /* Start re-registration */
2689 acc->auto_rereg.attempt_cnt++;
2690 status = pjsua_acc_set_registration(acc->index, PJ_TRUE);
2691 if (status != PJ_SUCCESS)
2692 schedule_reregistration(acc);
2693
Nanang Izzuddin66580002010-04-14 08:12:08 +00002694on_return:
2695 PJSUA_UNLOCK();
2696}
2697
2698
2699/* Schedule reregistration for specified account. Note that the first
2700 * re-registration after a registration failure will be done immediately.
2701 * Also note that this function should be called within PJSUA mutex.
2702 */
2703static void schedule_reregistration(pjsua_acc *acc)
2704{
2705 pj_time_val delay;
2706
Nanang Izzuddinc71bed62010-05-26 15:04:43 +00002707 pj_assert(acc);
2708
2709 /* Validate the account and re-registration feature status */
2710 if (!acc->valid || acc->cfg.reg_retry_interval == 0) {
2711 return;
2712 }
Nanang Izzuddin66580002010-04-14 08:12:08 +00002713
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002714 /* If configured, disconnect calls of this account after the first
2715 * reregistration attempt failed.
2716 */
Nanang Izzuddin66580002010-04-14 08:12:08 +00002717 if (acc->cfg.drop_calls_on_reg_fail && acc->auto_rereg.attempt_cnt >= 1)
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002718 {
2719 unsigned i, cnt;
2720
2721 for (i = 0, cnt = 0; i < pjsua_var.ua_cfg.max_calls; ++i) {
2722 if (pjsua_var.calls[i].acc_id == acc->index) {
2723 pjsua_call_hangup(i, 0, NULL, NULL);
2724 ++cnt;
2725 }
2726 }
2727
2728 if (cnt) {
2729 PJ_LOG(3, (THIS_FILE, "Disconnecting %d call(s) of account #%d "
2730 "after reregistration attempt failed",
2731 cnt, acc->index));
2732 }
2733 }
2734
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002735 /* Cancel any re-registration timer */
2736 pjsua_cancel_timer(&acc->auto_rereg.timer);
2737
2738 /* Update re-registration flag */
2739 acc->auto_rereg.active = PJ_TRUE;
2740
2741 /* Set up timer for reregistration */
2742 acc->auto_rereg.timer.cb = &auto_rereg_timer_cb;
2743 acc->auto_rereg.timer.user_data = acc;
2744
2745 /* Reregistration attempt. The first attempt will be done immediately. */
2746 delay.sec = acc->auto_rereg.attempt_cnt? acc->cfg.reg_retry_interval : 0;
2747 delay.msec = 0;
2748 pjsua_schedule_timer(&acc->auto_rereg.timer, &delay);
2749}
2750
2751
2752/* Internal function to perform auto-reregistration on transport
2753 * connection/disconnection events.
2754 */
2755void pjsua_acc_on_tp_state_changed(pjsip_transport *tp,
2756 pjsip_transport_state state,
2757 const pjsip_transport_state_info *info)
2758{
2759 unsigned i;
2760
2761 PJ_UNUSED_ARG(info);
2762
2763 /* Only care for transport disconnection events */
2764 if (state != PJSIP_TP_STATE_DISCONNECTED)
2765 return;
2766
2767 /* Shutdown this transport, to make sure that the transport manager
2768 * will create a new transport for reconnection.
2769 */
2770 pjsip_transport_shutdown(tp);
2771
2772 PJSUA_LOCK();
2773
2774 /* Enumerate accounts using this transport and perform actions
2775 * based on the transport state.
2776 */
2777 for (i = 0; i < PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2778 pjsua_acc *acc = &pjsua_var.acc[i];
2779
2780 /* Skip if this account is not valid OR auto re-registration
2781 * feature is disabled OR this transport is not used by this account.
2782 */
2783 if (!acc->valid || !acc->cfg.reg_retry_interval ||
2784 tp != acc->auto_rereg.reg_tp)
2785 {
2786 continue;
2787 }
2788
2789 /* Schedule reregistration for this account */
2790 schedule_reregistration(acc);
2791 }
2792
2793 PJSUA_UNLOCK();
2794}