blob: 1cf273f353a34d8928b74775ffdc741d7b5df33c [file] [log] [blame]
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 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;
102 dst->cred_count = src->cred_count;
103
104 for (i=0; i<src->cred_count; ++i) {
105 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
106 }
Benny Prijonobddef2c2007-10-31 13:28:08 +0000107
Nanang Izzuddin60e8aa92010-09-28 10:48:48 +0000108 pj_list_init(&dst->reg_hdr_list);
109 if (!pj_list_empty(&src->reg_hdr_list)) {
110 const pjsip_hdr *hdr;
111
112 hdr = src->reg_hdr_list.next;
113 while (hdr != &src->reg_hdr_list) {
114 pj_list_push_back(&dst->reg_hdr_list, pjsip_hdr_clone(pool, hdr));
115 hdr = hdr->next;
116 }
117 }
118
Nanang Izzuddin5fc7fcf2010-12-01 08:53:52 +0000119 pjsip_auth_clt_pref_dup(pool, &dst->auth_pref, &src->auth_pref);
120
Benny Prijonobddef2c2007-10-31 13:28:08 +0000121 dst->ka_interval = src->ka_interval;
122 pj_strdup(pool, &dst->ka_data, &src->ka_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000123}
124
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000125/*
126 * Calculate CRC of proxy list.
127 */
128static pj_uint32_t calc_proxy_crc(const pj_str_t proxy[], pj_size_t cnt)
129{
130 pj_crc32_context ctx;
131 unsigned i;
132
133 pj_crc32_init(&ctx);
134 for (i=0; i<cnt; ++i) {
135 pj_crc32_update(&ctx, (pj_uint8_t*)proxy[i].ptr, proxy[i].slen);
136 }
137
138 return pj_crc32_final(&ctx);
139}
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000140
141/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000142 * Initialize a new account (after configuration is set).
143 */
144static pj_status_t initialize_acc(unsigned acc_id)
145{
146 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
147 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000148 pjsip_name_addr *name_addr;
Benny Prijonoc7545782010-09-28 07:43:18 +0000149 pjsip_sip_uri *sip_reg_uri;
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000150 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151 unsigned i;
152
153 /* Need to parse local_uri to get the elements: */
154
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000155 name_addr = (pjsip_name_addr*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000156 pjsip_parse_uri(acc->pool, acc_cfg->id.ptr,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000157 acc_cfg->id.slen,
158 PJSIP_PARSE_URI_AS_NAMEADDR);
159 if (name_addr == NULL) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000160 pjsua_perror(THIS_FILE, "Invalid local URI",
161 PJSIP_EINVALIDURI);
162 return PJSIP_EINVALIDURI;
163 }
164
165 /* Local URI MUST be a SIP or SIPS: */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000166 if (!PJSIP_URI_SCHEME_IS_SIP(name_addr) &&
167 !PJSIP_URI_SCHEME_IS_SIPS(name_addr))
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000168 {
Benny Prijonoc7545782010-09-28 07:43:18 +0000169 acc->display = name_addr->display;
170 acc->user_part = name_addr->display;
171 acc->srv_domain = pj_str("");
172 acc->srv_port = 0;
173 } else {
174 pjsip_sip_uri *sip_uri;
175
176 /* Get the SIP URI object: */
177 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(name_addr);
178
179 /* Save the user and domain part. These will be used when finding an
180 * account for incoming requests.
181 */
182 acc->display = name_addr->display;
183 acc->user_part = sip_uri->user;
184 acc->srv_domain = sip_uri->host;
185 acc->srv_port = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000186 }
187
188
Benny Prijonob4a17c92006-07-10 14:40:21 +0000189 /* Parse registrar URI, if any */
190 if (acc_cfg->reg_uri.slen) {
191 pjsip_uri *reg_uri;
192
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000193 reg_uri = pjsip_parse_uri(acc->pool, acc_cfg->reg_uri.ptr,
Benny Prijonob4a17c92006-07-10 14:40:21 +0000194 acc_cfg->reg_uri.slen, 0);
195 if (reg_uri == NULL) {
196 pjsua_perror(THIS_FILE, "Invalid registrar URI",
197 PJSIP_EINVALIDURI);
198 return PJSIP_EINVALIDURI;
199 }
200
201 /* Registrar URI MUST be a SIP or SIPS: */
202 if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) &&
203 !PJSIP_URI_SCHEME_IS_SIPS(reg_uri))
204 {
205 pjsua_perror(THIS_FILE, "Invalid registar URI",
206 PJSIP_EINVALIDSCHEME);
207 return PJSIP_EINVALIDSCHEME;
208 }
209
210 sip_reg_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri);
211
212 } else {
213 sip_reg_uri = NULL;
214 }
215
Benny Prijonob4a17c92006-07-10 14:40:21 +0000216 if (sip_reg_uri) {
217 acc->srv_port = sip_reg_uri->port;
218 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000219
220 /* Create Contact header if not present. */
Benny Prijonob4a17c92006-07-10 14:40:21 +0000221 //if (acc_cfg->contact.slen == 0) {
222 // acc_cfg->contact = acc_cfg->id;
223 //}
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000224
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000225 /* Build account route-set from outbound proxies and route set from
226 * account configuration.
227 */
228 pj_list_init(&acc->route_set);
229
Benny Prijono29c8ca32010-06-22 06:02:13 +0000230 if (!pj_list_empty(&pjsua_var.outbound_proxy)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000231 pjsip_route_hdr *r;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000232
Benny Prijono29c8ca32010-06-22 06:02:13 +0000233 r = pjsua_var.outbound_proxy.next;
234 while (r != &pjsua_var.outbound_proxy) {
235 pj_list_push_back(&acc->route_set,
236 pjsip_hdr_shallow_clone(acc->pool, r));
237 r = r->next;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000238 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000239 }
240
241 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
242 pj_str_t hname = { "Route", 5};
243 pjsip_route_hdr *r;
244 pj_str_t tmp;
245
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000246 pj_strdup_with_null(acc->pool, &tmp, &acc_cfg->proxy[i]);
Benny Prijonoa1e69682007-05-11 15:14:34 +0000247 r = (pjsip_route_hdr*)
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000248 pjsip_parse_hdr(acc->pool, &hname, tmp.ptr, tmp.slen, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000249 if (r == NULL) {
250 pjsua_perror(THIS_FILE, "Invalid URI in account route set",
251 PJ_EINVAL);
252 return PJ_EINVAL;
253 }
254 pj_list_push_back(&acc->route_set, r);
255 }
256
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000257 /* Concatenate credentials from account config and global config */
258 acc->cred_cnt = 0;
259 for (i=0; i<acc_cfg->cred_count; ++i) {
260 acc->cred[acc->cred_cnt++] = acc_cfg->cred_info[i];
261 }
262 for (i=0; i<pjsua_var.ua_cfg.cred_count &&
263 acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i)
264 {
265 acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i];
266 }
267
Benny Prijono07fe2302010-06-24 12:33:18 +0000268 /* If ICE is enabled, add "+sip.ice" media feature tag in account's
269 * contact params.
270 */
271#if PJSUA_ADD_ICE_TAGS
272 if (pjsua_var.media_cfg.enable_ice) {
273 unsigned new_len;
274 pj_str_t new_prm;
275
276 new_len = acc_cfg->contact_params.slen + 10;
277 new_prm.ptr = (char*)pj_pool_alloc(acc->pool, new_len);
278 pj_strcpy(&new_prm, &acc_cfg->contact_params);
279 pj_strcat2(&new_prm, ";+sip.ice");
280 acc_cfg->contact_params = new_prm;
281 }
282#endif
283
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000284 status = pjsua_pres_init_acc(acc_id);
285 if (status != PJ_SUCCESS)
286 return status;
287
Benny Prijonob54719f2010-11-16 03:07:46 +0000288 /* If SIP outbound is enabled, generate instance and reg ID if they are
289 * not specified
290 */
291 if (acc_cfg->use_rfc5626) {
292 if (acc_cfg->rfc5626_instance_id.slen==0) {
293 const pj_str_t *hostname;
294 pj_uint32_t hval, pos;
295 char instprm[] = ";+sip.instance=\"<urn:uuid:00000000-0000-0000-0000-0000CCDDEEFF>\"";
296
297 hostname = pj_gethostname();
298 pos = pj_ansi_strlen(instprm) - 10;
299 hval = pj_hash_calc(0, hostname->ptr, hostname->slen);
300 pj_val_to_hex_digit( ((char*)&hval)[0], instprm+pos+0);
301 pj_val_to_hex_digit( ((char*)&hval)[1], instprm+pos+2);
302 pj_val_to_hex_digit( ((char*)&hval)[2], instprm+pos+4);
303 pj_val_to_hex_digit( ((char*)&hval)[3], instprm+pos+6);
304
305 pj_strdup2(acc->pool, &acc->rfc5626_instprm, instprm);
306 } else {
307 const char *prmname = ";+sip.instance=\"";
308 unsigned len;
309
310 len = pj_ansi_strlen(prmname) + acc_cfg->rfc5626_instance_id.slen + 1;
311 acc->rfc5626_instprm.ptr = (char*)pj_pool_alloc(acc->pool, len+1);
312 pj_ansi_snprintf(acc->rfc5626_instprm.ptr, len+1,
313 "%s%.*s\"",
314 prmname,
315 (int)acc_cfg->rfc5626_instance_id.slen,
316 acc_cfg->rfc5626_instance_id.ptr);
317 acc->rfc5626_instprm.slen = len;
318 }
319
320 if (acc_cfg->rfc5626_reg_id.slen==0) {
321 acc->rfc5626_regprm = pj_str(";reg-id=1");
322 } else {
323 const char *prmname = ";reg-id=";
324 unsigned len;
325
326 len = pj_ansi_strlen(prmname) + acc_cfg->rfc5626_reg_id.slen;
327 acc->rfc5626_regprm.ptr = (char*)pj_pool_alloc(acc->pool, len+1);
328 pj_ansi_snprintf(acc->rfc5626_regprm.ptr, len+1,
329 "%s%.*s\"",
330 prmname,
331 (int)acc_cfg->rfc5626_reg_id.slen,
332 acc_cfg->rfc5626_reg_id.ptr);
333 acc->rfc5626_regprm.slen = len;
334 }
335 }
336
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000337 /* Mark account as valid */
338 pjsua_var.acc[acc_id].valid = PJ_TRUE;
339
Benny Prijono093d3022006-09-24 00:07:11 +0000340 /* Insert account ID into account ID array, sorted by priority */
341 for (i=0; i<pjsua_var.acc_cnt; ++i) {
342 if ( pjsua_var.acc[pjsua_var.acc_ids[i]].cfg.priority <
343 pjsua_var.acc[acc_id].cfg.priority)
344 {
345 break;
346 }
347 }
348 pj_array_insert(pjsua_var.acc_ids, sizeof(pjsua_var.acc_ids[0]),
349 pjsua_var.acc_cnt, i, &acc_id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000350
351 return PJ_SUCCESS;
352}
353
354
355/*
356 * Add a new account to pjsua.
357 */
358PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
359 pj_bool_t is_default,
360 pjsua_acc_id *p_acc_id)
361{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000362 pjsua_acc *acc;
Benny Prijono91d06b62008-09-20 12:16:56 +0000363 unsigned i, id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000364 pj_status_t status;
365
366 PJ_ASSERT_RETURN(pjsua_var.acc_cnt < PJ_ARRAY_SIZE(pjsua_var.acc),
367 PJ_ETOOMANY);
368
369 /* Must have a transport */
Benny Prijonoe93e2872006-06-28 16:46:49 +0000370 PJ_ASSERT_RETURN(pjsua_var.tpdata[0].data.ptr != NULL, PJ_EINVALIDOP);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000371
372 PJSUA_LOCK();
373
374 /* Find empty account id. */
375 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.acc); ++id) {
376 if (pjsua_var.acc[id].valid == PJ_FALSE)
377 break;
378 }
379
380 /* Expect to find a slot */
381 PJ_ASSERT_ON_FAIL( id < PJ_ARRAY_SIZE(pjsua_var.acc),
382 {PJSUA_UNLOCK(); return PJ_EBUG;});
383
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000384 acc = &pjsua_var.acc[id];
385
386 /* Create pool for this account. */
387 if (acc->pool)
388 pj_pool_reset(acc->pool);
389 else
390 acc->pool = pjsua_pool_create("acc%p", 512, 256);
391
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000392 /* Copy config */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000393 pjsua_acc_config_dup(acc->pool, &pjsua_var.acc[id].cfg, cfg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000394
395 /* Normalize registration timeout */
396 if (pjsua_var.acc[id].cfg.reg_uri.slen &&
397 pjsua_var.acc[id].cfg.reg_timeout == 0)
398 {
399 pjsua_var.acc[id].cfg.reg_timeout = PJSUA_REG_INTERVAL;
400 }
401
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000402 /* Get CRC of account proxy setting */
403 acc->local_route_crc = calc_proxy_crc(acc->cfg.proxy, acc->cfg.proxy_cnt);
404
405 /* Get CRC of global outbound proxy setting */
406 acc->global_route_crc=calc_proxy_crc(pjsua_var.ua_cfg.outbound_proxy,
407 pjsua_var.ua_cfg.outbound_proxy_cnt);
408
Benny Prijono91d06b62008-09-20 12:16:56 +0000409 /* Check the route URI's and force loose route if required */
410 for (i=0; i<acc->cfg.proxy_cnt; ++i) {
411 status = normalize_route_uri(acc->pool, &acc->cfg.proxy[i]);
412 if (status != PJ_SUCCESS)
413 return status;
414 }
415
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000416 status = initialize_acc(id);
417 if (status != PJ_SUCCESS) {
418 pjsua_perror(THIS_FILE, "Error adding account", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000419 pj_pool_release(acc->pool);
420 acc->pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000421 PJSUA_UNLOCK();
422 return status;
423 }
424
425 if (is_default)
426 pjsua_var.default_acc = id;
427
428 if (p_acc_id)
429 *p_acc_id = id;
430
431 pjsua_var.acc_cnt++;
432
433 PJSUA_UNLOCK();
434
435 PJ_LOG(4,(THIS_FILE, "Account %.*s added with id %d",
436 (int)cfg->id.slen, cfg->id.ptr, id));
437
438 /* If accounts has registration enabled, start registration */
439 if (pjsua_var.acc[id].cfg.reg_uri.slen)
440 pjsua_acc_set_registration(id, PJ_TRUE);
Benny Prijono4dd961b2009-10-26 11:21:37 +0000441 else {
442 /* Otherwise subscribe to MWI, if it's enabled */
443 if (pjsua_var.acc[id].cfg.mwi_enabled)
444 pjsua_start_mwi(&pjsua_var.acc[id]);
445 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000446
447 return PJ_SUCCESS;
448}
449
450
451/*
452 * Add local account
453 */
454PJ_DEF(pj_status_t) pjsua_acc_add_local( pjsua_transport_id tid,
455 pj_bool_t is_default,
456 pjsua_acc_id *p_acc_id)
457{
458 pjsua_acc_config cfg;
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000459 pjsua_transport_data *t = &pjsua_var.tpdata[tid];
Benny Prijonod0bd4982007-12-02 15:40:52 +0000460 const char *beginquote, *endquote;
461 char transport_param[32];
Benny Prijonoe85bc412006-07-29 20:29:24 +0000462 char uri[PJSIP_MAX_URL_SIZE];
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000463
Benny Prijonoe93e2872006-06-28 16:46:49 +0000464 /* ID must be valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000465 PJ_ASSERT_RETURN(tid>=0 && tid<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
466 PJ_EINVAL);
Benny Prijonoe93e2872006-06-28 16:46:49 +0000467
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000468 /* Transport must be valid */
Benny Prijonoe93e2872006-06-28 16:46:49 +0000469 PJ_ASSERT_RETURN(t->data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000470
471 pjsua_acc_config_default(&cfg);
472
Benny Prijono093d3022006-09-24 00:07:11 +0000473 /* Lower the priority of local account */
474 --cfg.priority;
475
Benny Prijonod0bd4982007-12-02 15:40:52 +0000476 /* Enclose IPv6 address in square brackets */
477 if (t->type & PJSIP_TRANSPORT_IPV6) {
478 beginquote = "[";
479 endquote = "]";
480 } else {
481 beginquote = endquote = "";
482 }
483
484 /* Don't add transport parameter if it's UDP */
Benny Prijono4c82c1e2008-10-16 08:14:51 +0000485 if (t->type!=PJSIP_TRANSPORT_UDP && t->type!=PJSIP_TRANSPORT_UDP6) {
Benny Prijonod0bd4982007-12-02 15:40:52 +0000486 pj_ansi_snprintf(transport_param, sizeof(transport_param),
487 ";transport=%s",
488 pjsip_transport_get_type_name(t->type));
489 } else {
490 transport_param[0] = '\0';
491 }
492
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000493 /* Build URI for the account */
Benny Prijonoe85bc412006-07-29 20:29:24 +0000494 pj_ansi_snprintf(uri, PJSIP_MAX_URL_SIZE,
Benny Prijonod0bd4982007-12-02 15:40:52 +0000495 "<sip:%s%.*s%s:%d%s>",
496 beginquote,
Benny Prijonoe85bc412006-07-29 20:29:24 +0000497 (int)t->local_name.host.slen,
498 t->local_name.host.ptr,
Benny Prijonod0bd4982007-12-02 15:40:52 +0000499 endquote,
Benny Prijonoe85bc412006-07-29 20:29:24 +0000500 t->local_name.port,
Benny Prijonod0bd4982007-12-02 15:40:52 +0000501 transport_param);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000502
503 cfg.id = pj_str(uri);
504
505 return pjsua_acc_add(&cfg, is_default, p_acc_id);
506}
507
508
509/*
Benny Prijono705e7842008-07-21 18:12:51 +0000510 * Set arbitrary data to be associated with the account.
511 */
512PJ_DEF(pj_status_t) pjsua_acc_set_user_data(pjsua_acc_id acc_id,
513 void *user_data)
514{
515 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
516 PJ_EINVAL);
517 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
518
519 PJSUA_LOCK();
520
521 pjsua_var.acc[acc_id].cfg.user_data = user_data;
522
523 PJSUA_UNLOCK();
524
525 return PJ_SUCCESS;
526}
527
528
529/*
530 * Retrieve arbitrary data associated with the account.
531 */
532PJ_DEF(void*) pjsua_acc_get_user_data(pjsua_acc_id acc_id)
533{
534 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
535 NULL);
536 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, NULL);
537
538 return pjsua_var.acc[acc_id].cfg.user_data;
539}
540
541
542/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000543 * Delete account.
544 */
545PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id)
546{
Benny Prijono093d3022006-09-24 00:07:11 +0000547 unsigned i;
548
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000549 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
550 PJ_EINVAL);
551 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
552
553 PJSUA_LOCK();
554
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +0000555 /* Cancel any re-registration timer */
556 pjsua_cancel_timer(&pjsua_var.acc[acc_id].auto_rereg.timer);
557
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000558 /* Delete registration */
Benny Prijono922933b2007-01-21 16:23:56 +0000559 if (pjsua_var.acc[acc_id].regc != NULL) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000560 pjsua_acc_set_registration(acc_id, PJ_FALSE);
Benny Prijonoe68e99f2007-06-06 00:28:10 +0000561 if (pjsua_var.acc[acc_id].regc) {
562 pjsip_regc_destroy(pjsua_var.acc[acc_id].regc);
563 }
Benny Prijono922933b2007-01-21 16:23:56 +0000564 pjsua_var.acc[acc_id].regc = NULL;
565 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000566
567 /* Delete server presence subscription */
568 pjsua_pres_delete_acc(acc_id);
569
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000570 /* Release account pool */
571 if (pjsua_var.acc[acc_id].pool) {
572 pj_pool_release(pjsua_var.acc[acc_id].pool);
573 pjsua_var.acc[acc_id].pool = NULL;
574 }
575
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000576 /* Invalidate */
577 pjsua_var.acc[acc_id].valid = PJ_FALSE;
Benny Prijono978de6e2008-09-15 14:56:05 +0000578 pjsua_var.acc[acc_id].contact.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000579
Benny Prijono093d3022006-09-24 00:07:11 +0000580 /* Remove from array */
581 for (i=0; i<pjsua_var.acc_cnt; ++i) {
582 if (pjsua_var.acc_ids[i] == acc_id)
583 break;
584 }
585 if (i != pjsua_var.acc_cnt) {
586 pj_array_erase(pjsua_var.acc_ids, sizeof(pjsua_var.acc_ids[0]),
587 pjsua_var.acc_cnt, i);
588 --pjsua_var.acc_cnt;
589 }
590
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000591 /* Leave the calls intact, as I don't think calls need to
592 * access account once it's created
593 */
594
Benny Prijonofb2b3652007-06-28 07:15:03 +0000595 /* Update default account */
596 if (pjsua_var.default_acc == acc_id)
597 pjsua_var.default_acc = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000598
599 PJSUA_UNLOCK();
600
601 PJ_LOG(4,(THIS_FILE, "Account id %d deleted", acc_id));
602
603 return PJ_SUCCESS;
604}
605
606
607/*
608 * Modify account information.
609 */
610PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
611 const pjsua_acc_config *cfg)
612{
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000613 pjsua_acc *acc;
614 pjsip_name_addr *id_name_addr = NULL;
615 pjsip_sip_uri *id_sip_uri = NULL;
616 pjsip_sip_uri *reg_sip_uri = NULL;
617 pj_uint32_t local_route_crc, global_route_crc;
618 pjsip_route_hdr global_route;
619 pjsip_route_hdr local_route;
620 pj_str_t acc_proxy[PJSUA_ACC_MAX_PROXIES];
621 pj_bool_t update_reg = PJ_FALSE;
622 pj_status_t status = PJ_SUCCESS;
623
624 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
625 PJ_EINVAL);
626
627 PJSUA_LOCK();
628
629 acc = &pjsua_var.acc[acc_id];
630 if (!acc->valid) {
631 status = PJ_EINVAL;
632 goto on_return;
633 }
634
635 /* == Validate first == */
636
637 /* Account id */
638 if (pj_strcmp(&acc->cfg.id, &cfg->id)) {
639 /* Need to parse id to get the elements: */
640 id_name_addr = (pjsip_name_addr*)
641 pjsip_parse_uri(acc->pool, cfg->id.ptr, cfg->id.slen,
642 PJSIP_PARSE_URI_AS_NAMEADDR);
643 if (id_name_addr == NULL) {
644 status = PJSIP_EINVALIDURI;
645 pjsua_perror(THIS_FILE, "Invalid local URI", status);
646 goto on_return;
647 }
648
649 /* URI MUST be a SIP or SIPS: */
650 if (!PJSIP_URI_SCHEME_IS_SIP(id_name_addr) &&
651 !PJSIP_URI_SCHEME_IS_SIPS(id_name_addr))
652 {
653 status = PJSIP_EINVALIDSCHEME;
654 pjsua_perror(THIS_FILE, "Invalid local URI", status);
655 goto on_return;
656 }
657
658 /* Get the SIP URI object: */
659 id_sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(id_name_addr);
660 }
661
662 /* Registrar URI */
663 if (pj_strcmp(&acc->cfg.reg_uri, &cfg->reg_uri) && cfg->reg_uri.slen) {
664 pjsip_uri *reg_uri;
665
666 /* Need to parse reg_uri to get the elements: */
667 reg_uri = pjsip_parse_uri(acc->pool, cfg->reg_uri.ptr,
668 cfg->reg_uri.slen, 0);
669 if (reg_uri == NULL) {
670 status = PJSIP_EINVALIDURI;
671 pjsua_perror(THIS_FILE, "Invalid registrar URI", status);
672 goto on_return;
673 }
674
675 /* Registrar URI MUST be a SIP or SIPS: */
676 if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) &&
677 !PJSIP_URI_SCHEME_IS_SIPS(reg_uri))
678 {
679 status = PJSIP_EINVALIDSCHEME;
680 pjsua_perror(THIS_FILE, "Invalid registar URI", status);
681 goto on_return;
682 }
683
684 reg_sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri);
685 }
686
687 /* Global outbound proxy */
688 global_route_crc = calc_proxy_crc(pjsua_var.ua_cfg.outbound_proxy,
689 pjsua_var.ua_cfg.outbound_proxy_cnt);
690 if (global_route_crc != acc->global_route_crc) {
691 pjsip_route_hdr *r;
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000692
Benny Prijono29c8ca32010-06-22 06:02:13 +0000693 /* Copy from global outbound proxies */
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000694 pj_list_init(&global_route);
Benny Prijono29c8ca32010-06-22 06:02:13 +0000695 r = pjsua_var.outbound_proxy.next;
696 while (r != &pjsua_var.outbound_proxy) {
697 pj_list_push_back(&global_route,
698 pjsip_hdr_shallow_clone(acc->pool, r));
699 r = r->next;
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000700 }
701 }
702
703 /* Account proxy */
704 local_route_crc = calc_proxy_crc(cfg->proxy, cfg->proxy_cnt);
705 if (local_route_crc != acc->local_route_crc) {
706 pjsip_route_hdr *r;
707 unsigned i;
708
709 /* Validate the local route and save it to temporary var */
710 pj_list_init(&local_route);
711 for (i=0; i<cfg->proxy_cnt; ++i) {
712 pj_str_t hname = { "Route", 5};
713
714 pj_strdup_with_null(acc->pool, &acc_proxy[i], &cfg->proxy[i]);
715 status = normalize_route_uri(acc->pool, &acc_proxy[i]);
716 if (status != PJ_SUCCESS)
717 goto on_return;
718 r = (pjsip_route_hdr*)
719 pjsip_parse_hdr(acc->pool, &hname, acc_proxy[i].ptr,
720 acc_proxy[i].slen, NULL);
721 if (r == NULL) {
722 status = PJSIP_EINVALIDURI;
723 pjsua_perror(THIS_FILE, "Invalid URI in account route set",
724 status);
725 goto on_return;
726 }
727
728 pj_list_push_back(&local_route, r);
729 }
730 }
731
732
733 /* == Apply the new config == */
734
735 /* Account ID. */
736 if (id_name_addr && id_sip_uri) {
737 pj_strdup_with_null(acc->pool, &acc->cfg.id, &cfg->id);
738 acc->display = id_name_addr->display;
739 acc->user_part = id_sip_uri->user;
740 acc->srv_domain = id_sip_uri->host;
741 acc->srv_port = 0;
742 update_reg = PJ_TRUE;
743 }
744
745 /* User data */
746 acc->cfg.user_data = cfg->user_data;
747
748 /* Priority */
749 if (acc->cfg.priority != cfg->priority) {
750 unsigned i;
751
752 acc->cfg.priority = cfg->priority;
753
754 /* Resort accounts priority */
755 for (i=0; i<pjsua_var.acc_cnt; ++i) {
756 if (pjsua_var.acc_ids[i] == acc_id)
757 break;
758 }
759 pj_assert(i < pjsua_var.acc_cnt);
760 pj_array_erase(pjsua_var.acc_ids, sizeof(acc_id),
761 pjsua_var.acc_cnt, i);
762 for (i=0; i<pjsua_var.acc_cnt; ++i) {
763 if (pjsua_var.acc[pjsua_var.acc_ids[i]].cfg.priority <
764 acc->cfg.priority)
765 {
766 break;
767 }
768 }
769 pj_array_insert(pjsua_var.acc_ids, sizeof(acc_id),
770 pjsua_var.acc_cnt, i, &acc_id);
771 }
772
773 /* MWI */
774 acc->cfg.mwi_enabled = cfg->mwi_enabled;
775
776 /* PIDF tuple ID */
777 if (pj_strcmp(&acc->cfg.pidf_tuple_id, &cfg->pidf_tuple_id))
778 pj_strdup_with_null(acc->pool, &acc->cfg.pidf_tuple_id,
779 &cfg->pidf_tuple_id);
780
781 /* Publish */
782 acc->cfg.publish_opt = cfg->publish_opt;
783 acc->cfg.unpublish_max_wait_time_msec = cfg->unpublish_max_wait_time_msec;
784 if (acc->cfg.publish_enabled != cfg->publish_enabled) {
785 acc->cfg.publish_enabled = cfg->publish_enabled;
786 if (!acc->cfg.publish_enabled)
787 pjsua_pres_unpublish(acc);
788 else
789 update_reg = PJ_TRUE;
790 }
791
792 /* Force contact URI */
793 if (pj_strcmp(&acc->cfg.force_contact, &cfg->force_contact)) {
794 pj_strdup_with_null(acc->pool, &acc->cfg.force_contact,
795 &cfg->force_contact);
796 update_reg = PJ_TRUE;
797 }
798
799 /* Contact param */
800 if (pj_strcmp(&acc->cfg.contact_params, &cfg->contact_params)) {
801 pj_strdup_with_null(acc->pool, &acc->cfg.contact_params,
802 &cfg->contact_params);
803 update_reg = PJ_TRUE;
804 }
805
806 /* Contact URI params */
807 if (pj_strcmp(&acc->cfg.contact_uri_params, &cfg->contact_uri_params)) {
808 pj_strdup_with_null(acc->pool, &acc->cfg.contact_uri_params,
809 &cfg->contact_uri_params);
810 update_reg = PJ_TRUE;
811 }
812
813 /* Reliable provisional response */
814 acc->cfg.require_100rel = cfg->require_100rel;
815
816 /* Session timer */
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000817 acc->cfg.use_timer = cfg->use_timer;
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000818 acc->cfg.timer_setting = cfg->timer_setting;
819
820 /* Transport and keep-alive */
821 if (acc->cfg.transport_id != cfg->transport_id) {
822 acc->cfg.transport_id = cfg->transport_id;
823 update_reg = PJ_TRUE;
824 }
825 acc->cfg.ka_interval = cfg->ka_interval;
826 if (pj_strcmp(&acc->cfg.ka_data, &cfg->ka_data))
827 pj_strdup(acc->pool, &acc->cfg.ka_data, &cfg->ka_data);
828#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
829 acc->cfg.use_srtp = cfg->use_srtp;
830 acc->cfg.srtp_secure_signaling = cfg->srtp_secure_signaling;
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +0000831 acc->cfg.srtp_optional_dup_offer = cfg->srtp_optional_dup_offer;
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000832#endif
833
Nanang Izzuddin5e39a2b2010-09-20 06:13:02 +0000834#if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0)
835 acc->cfg.use_stream_ka = cfg->use_stream_ka;
836#endif
837
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000838 /* Global outbound proxy */
839 if (global_route_crc != acc->global_route_crc) {
840 unsigned i, rcnt;
841
842 /* Remove the outbound proxies from the route set */
843 rcnt = pj_list_size(&acc->route_set);
844 for (i=0; i < rcnt - acc->cfg.proxy_cnt; ++i) {
845 pjsip_route_hdr *r = acc->route_set.next;
846 pj_list_erase(r);
847 }
848
849 /* Insert the outbound proxies to the beginning of route set */
850 pj_list_merge_first(&acc->route_set, &global_route);
851
852 /* Update global route CRC */
853 acc->global_route_crc = global_route_crc;
854
855 update_reg = PJ_TRUE;
856 }
857
858 /* Account proxy */
859 if (local_route_crc != acc->local_route_crc) {
860 unsigned i;
861
862 /* Remove the current account proxies from the route set */
863 for (i=0; i < acc->cfg.proxy_cnt; ++i) {
864 pjsip_route_hdr *r = acc->route_set.prev;
865 pj_list_erase(r);
866 }
867
868 /* Insert new proxy setting to the route set */
869 pj_list_merge_last(&acc->route_set, &local_route);
870
871 /* Update the proxy setting */
872 acc->cfg.proxy_cnt = cfg->proxy_cnt;
873 for (i = 0; i < cfg->proxy_cnt; ++i)
874 acc->cfg.proxy[i] = acc_proxy[i];
875
876 /* Update local route CRC */
877 acc->local_route_crc = local_route_crc;
878
879 update_reg = PJ_TRUE;
880 }
881
882 /* Credential info */
883 {
884 unsigned i;
885
886 /* Selective update credential info. */
887 for (i = 0; i < cfg->cred_count; ++i) {
888 unsigned j;
889 pjsip_cred_info ci;
890
891 /* Find if this credential is already listed */
892 for (j = i; j < acc->cfg.cred_count; ++i) {
893 if (pjsip_cred_info_cmp(&acc->cfg.cred_info[j],
894 &cfg->cred_info[i]) == 0)
895 {
896 /* Found, but different index/position, swap */
897 if (j != i) {
898 ci = acc->cfg.cred_info[i];
899 acc->cfg.cred_info[i] = acc->cfg.cred_info[j];
900 acc->cfg.cred_info[j] = ci;
901 }
902 break;
903 }
904 }
905
906 /* Not found, insert this */
907 if (j == acc->cfg.cred_count) {
908 /* If account credential is full, discard the last one. */
909 if (acc->cfg.cred_count == PJ_ARRAY_SIZE(acc->cfg.cred_info)) {
910 pj_array_erase(acc->cfg.cred_info, sizeof(pjsip_cred_info),
911 acc->cfg.cred_count, acc->cfg.cred_count-1);
912 acc->cfg.cred_count--;
913 }
914
915 /* Insert this */
916 pjsip_cred_info_dup(acc->pool, &ci, &cfg->cred_info[i]);
917 pj_array_insert(acc->cfg.cred_info, sizeof(pjsip_cred_info),
918 acc->cfg.cred_count, i, &ci);
919 }
920 }
921 acc->cfg.cred_count = cfg->cred_count;
922
923 /* Concatenate credentials from account config and global config */
924 acc->cred_cnt = 0;
925 for (i=0; i<acc->cfg.cred_count; ++i) {
926 acc->cred[acc->cred_cnt++] = acc->cfg.cred_info[i];
927 }
928 for (i=0; i<pjsua_var.ua_cfg.cred_count &&
929 acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i)
930 {
931 acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i];
932 }
933 }
934
935 /* Authentication preference */
936 acc->cfg.auth_pref.initial_auth = cfg->auth_pref.initial_auth;
937 if (pj_strcmp(&acc->cfg.auth_pref.algorithm, &cfg->auth_pref.algorithm))
938 pj_strdup_with_null(acc->pool, &acc->cfg.auth_pref.algorithm,
939 &cfg->auth_pref.algorithm);
940
941 /* Registration */
942 acc->cfg.reg_timeout = cfg->reg_timeout;
943 acc->cfg.unreg_timeout = cfg->unreg_timeout;
944 acc->cfg.allow_contact_rewrite = cfg->allow_contact_rewrite;
945 acc->cfg.reg_retry_interval = cfg->reg_retry_interval;
946 acc->cfg.drop_calls_on_reg_fail = cfg->drop_calls_on_reg_fail;
947
948 /* Normalize registration timeout */
949 if (acc->cfg.reg_uri.slen && acc->cfg.reg_timeout == 0)
950 acc->cfg.reg_timeout = PJSUA_REG_INTERVAL;
951
952 /* Registrar URI */
953 if (pj_strcmp(&acc->cfg.reg_uri, &cfg->reg_uri)) {
954 if (cfg->reg_uri.slen) {
955 pj_strdup_with_null(acc->pool, &acc->cfg.reg_uri, &cfg->reg_uri);
956 if (reg_sip_uri)
957 acc->srv_port = reg_sip_uri->port;
958 } else {
959 /* Unregister if registration was set */
960 if (acc->cfg.reg_uri.slen)
961 pjsua_acc_set_registration(acc->index, PJ_FALSE);
962 pj_bzero(&acc->cfg.reg_uri, sizeof(acc->cfg.reg_uri));
963 }
964 update_reg = PJ_TRUE;
965 }
966
Benny Prijonob54719f2010-11-16 03:07:46 +0000967 /* SIP outbound setting */
968 if (acc->cfg.use_rfc5626 != cfg->use_rfc5626 ||
969 pj_strcmp(&acc->cfg.rfc5626_instance_id, &cfg->rfc5626_instance_id) ||
970 pj_strcmp(&acc->cfg.rfc5626_reg_id, &cfg->rfc5626_reg_id))
971 {
972 update_reg = PJ_TRUE;
973 }
974
Nanang Izzuddinc3ea16a2010-04-20 14:36:38 +0000975 /* Update registration */
976 if (update_reg) {
977 /* If accounts has registration enabled, start registration */
978 if (acc->cfg.reg_uri.slen)
979 pjsua_acc_set_registration(acc->index, PJ_TRUE);
980 else {
981 /* Otherwise subscribe to MWI, if it's enabled */
982 if (acc->cfg.mwi_enabled)
983 pjsua_start_mwi(acc);
984 }
985 }
986
987on_return:
988 PJSUA_UNLOCK();
989 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000990}
991
992
993/*
994 * Modify account's presence status to be advertised to remote/presence
995 * subscribers.
996 */
997PJ_DEF(pj_status_t) pjsua_acc_set_online_status( pjsua_acc_id acc_id,
998 pj_bool_t is_online)
999{
1000 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
1001 PJ_EINVAL);
1002 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
1003
1004 pjsua_var.acc[acc_id].online_status = is_online;
Benny Prijono4461c7d2007-08-25 13:36:15 +00001005 pj_bzero(&pjsua_var.acc[acc_id].rpid, sizeof(pjrpid_element));
1006 pjsua_pres_update_acc(acc_id, PJ_FALSE);
1007 return PJ_SUCCESS;
1008}
1009
1010
1011/*
1012 * Set online status with extended information
1013 */
1014PJ_DEF(pj_status_t) pjsua_acc_set_online_status2( pjsua_acc_id acc_id,
1015 pj_bool_t is_online,
1016 const pjrpid_element *pr)
1017{
1018 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
1019 PJ_EINVAL);
1020 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
1021
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001022 PJSUA_LOCK();
Benny Prijono4461c7d2007-08-25 13:36:15 +00001023 pjsua_var.acc[acc_id].online_status = is_online;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001024 pjrpid_element_dup(pjsua_var.acc[acc_id].pool, &pjsua_var.acc[acc_id].rpid, pr);
1025 PJSUA_UNLOCK();
1026
Benny Prijono4461c7d2007-08-25 13:36:15 +00001027 pjsua_pres_update_acc(acc_id, PJ_TRUE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001028 return PJ_SUCCESS;
1029}
1030
Benny Prijonob54719f2010-11-16 03:07:46 +00001031/* Create reg_contact, mainly for SIP outbound */
1032static void update_regc_contact(pjsua_acc *acc)
1033{
1034 pjsua_acc_config *acc_cfg = &acc->cfg;
1035 pj_bool_t need_outbound = PJ_FALSE;
1036 const pj_str_t tcp_param = pj_str(";transport=tcp");
1037 const pj_str_t tls_param = pj_str(";transport=tls");
1038
1039 if (!acc_cfg->use_rfc5626)
1040 goto done;
1041
Benny Prijono2562b752010-11-16 06:01:38 +00001042 /* Check if outbound has been requested and rejected */
1043 if (acc->rfc5626_status == OUTBOUND_NA)
1044 goto done;
1045
Benny Prijonob54719f2010-11-16 03:07:46 +00001046 if (pj_stristr(&acc->contact, &tcp_param)==NULL &&
1047 pj_stristr(&acc->contact, &tls_param)==NULL)
1048 {
1049 /* Currently we can only do SIP outbound for TCP
1050 * and TLS.
1051 */
1052 goto done;
1053 }
1054
1055 /* looks like we can use outbound */
1056 need_outbound = PJ_TRUE;
1057
1058done:
1059 if (!need_outbound) {
1060 /* Outbound is not needed/wanted for the account. acc->reg_contact
1061 * is set to the same as acc->contact.
1062 */
1063 acc->reg_contact = acc->contact;
Benny Prijono2562b752010-11-16 06:01:38 +00001064 acc->rfc5626_status = OUTBOUND_NA;
Benny Prijonob54719f2010-11-16 03:07:46 +00001065 } else {
1066 /* Need to use outbound, append the contact with +sip.instance and
1067 * reg-id parameters.
1068 */
1069 unsigned len;
1070 pj_str_t reg_contact;
1071
1072 acc->rfc5626_status = OUTBOUND_WANTED;
1073 len = acc->contact.slen + acc->rfc5626_instprm.slen +
1074 acc->rfc5626_regprm.slen;
Benny Prijonoe49e6202010-11-16 07:01:25 +00001075 reg_contact.ptr = (char*) pj_pool_alloc(acc->pool, len);
Benny Prijonob54719f2010-11-16 03:07:46 +00001076
1077 pj_strcpy(&reg_contact, &acc->contact);
1078 pj_strcat(&reg_contact, &acc->rfc5626_regprm);
1079 pj_strcat(&reg_contact, &acc->rfc5626_instprm);
1080
1081 acc->reg_contact = reg_contact;
1082
1083 PJ_LOG(4,(THIS_FILE,
1084 "Contact for acc %d updated for SIP outbound: %.*s",
1085 acc->index,
1086 (int)acc->reg_contact.slen,
1087 acc->reg_contact.ptr));
1088 }
1089}
1090
Benny Prijono7f630432008-09-24 16:52:41 +00001091/* Check if IP is private IP address */
1092static pj_bool_t is_private_ip(const pj_str_t *addr)
1093{
1094 const pj_str_t private_net[] =
1095 {
1096 { "10.", 3 },
1097 { "127.", 4 },
1098 { "172.16.", 7 },
1099 { "192.168.", 8 }
1100 };
1101 unsigned i;
1102
1103 for (i=0; i<PJ_ARRAY_SIZE(private_net); ++i) {
1104 if (pj_strncmp(addr, &private_net[i], private_net[i].slen)==0)
1105 return PJ_TRUE;
1106 }
1107
1108 return PJ_FALSE;
1109}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001110
Benny Prijono15b02302007-09-27 14:07:07 +00001111/* Update NAT address from the REGISTER response */
1112static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
1113 struct pjsip_regc_cbparam *param)
1114{
1115 pjsip_transport *tp;
1116 const pj_str_t *via_addr;
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001117 pj_pool_t *pool;
Benny Prijono15b02302007-09-27 14:07:07 +00001118 int rport;
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001119 pjsip_sip_uri *uri;
Benny Prijono15b02302007-09-27 14:07:07 +00001120 pjsip_via_hdr *via;
Benny Prijono84d24932009-06-04 15:51:39 +00001121 pj_sockaddr contact_addr;
1122 pj_sockaddr recv_addr;
1123 pj_status_t status;
1124 pj_bool_t matched;
Benny Prijono7f630432008-09-24 16:52:41 +00001125 pj_str_t srv_ip;
Nanang Izzuddin5af37ff2009-08-05 18:41:23 +00001126 pjsip_contact_hdr *contact_hdr;
1127 const pj_str_t STR_CONTACT = { "Contact", 7 };
Benny Prijono15b02302007-09-27 14:07:07 +00001128
1129 tp = param->rdata->tp_info.transport;
1130
1131 /* Only update if account is configured to auto-update */
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001132 if (acc->cfg.allow_contact_rewrite == PJ_FALSE)
Benny Prijono15b02302007-09-27 14:07:07 +00001133 return PJ_FALSE;
1134
Benny Prijonob54719f2010-11-16 03:07:46 +00001135 /* If SIP outbound is active, no need to update */
1136 if (acc->rfc5626_status == OUTBOUND_ACTIVE) {
1137 PJ_LOG(4,(THIS_FILE, "Acc %d has SIP outbound active, no need to "
1138 "update registration Contact", acc->index));
1139 return PJ_FALSE;
1140 }
1141
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001142#if 0
1143 // Always update
1144 // See http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/2008-March/002178.html
Benny Prijono15b02302007-09-27 14:07:07 +00001145
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001146 /* For UDP, only update if STUN is enabled (for now).
1147 * For TCP/TLS, always check.
1148 */
1149 if ((tp->key.type == PJSIP_TRANSPORT_UDP &&
1150 (pjsua_var.ua_cfg.stun_domain.slen != 0 ||
1151 (pjsua_var.ua_cfg.stun_host.slen != 0)) ||
1152 (tp->key.type == PJSIP_TRANSPORT_TCP) ||
1153 (tp->key.type == PJSIP_TRANSPORT_TLS))
Benny Prijono15b02302007-09-27 14:07:07 +00001154 {
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001155 /* Yes we will check */
1156 } else {
Benny Prijono15b02302007-09-27 14:07:07 +00001157 return PJ_FALSE;
1158 }
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001159#endif
Benny Prijono15b02302007-09-27 14:07:07 +00001160
1161 /* Get the received and rport info */
1162 via = param->rdata->msg_info.via;
1163 if (via->rport_param < 1) {
1164 /* Remote doesn't support rport */
1165 rport = via->sent_by.port;
Benny Prijono24a21852008-04-14 04:04:30 +00001166 if (rport==0) {
1167 pjsip_transport_type_e tp_type;
1168 tp_type = (pjsip_transport_type_e) tp->key.type;
1169 rport = pjsip_transport_get_default_port_for_type(tp_type);
1170 }
Benny Prijono15b02302007-09-27 14:07:07 +00001171 } else
1172 rport = via->rport_param;
1173
1174 if (via->recvd_param.slen != 0)
1175 via_addr = &via->recvd_param;
1176 else
1177 via_addr = &via->sent_by.host;
1178
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001179 /* Compare received and rport with the URI in our registration */
1180 pool = pjsua_pool_create("tmp", 512, 512);
Nanang Izzuddin5af37ff2009-08-05 18:41:23 +00001181 contact_hdr = (pjsip_contact_hdr*)
1182 pjsip_parse_hdr(pool, &STR_CONTACT, acc->contact.ptr,
1183 acc->contact.slen, NULL);
1184 pj_assert(contact_hdr != NULL);
1185 uri = (pjsip_sip_uri*) contact_hdr->uri;
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001186 pj_assert(uri != NULL);
Benny Prijono24a21852008-04-14 04:04:30 +00001187 uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001188
Benny Prijono24a21852008-04-14 04:04:30 +00001189 if (uri->port == 0) {
1190 pjsip_transport_type_e tp_type;
1191 tp_type = (pjsip_transport_type_e) tp->key.type;
1192 uri->port = pjsip_transport_get_default_port_for_type(tp_type);
1193 }
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001194
Benny Prijono84d24932009-06-04 15:51:39 +00001195 /* Convert IP address strings into sockaddr for comparison.
1196 * (http://trac.pjsip.org/repos/ticket/863)
1197 */
1198 status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &uri->host,
1199 &contact_addr);
1200 if (status == PJ_SUCCESS)
1201 status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, via_addr,
1202 &recv_addr);
1203 if (status == PJ_SUCCESS) {
1204 /* Compare the addresses as sockaddr according to the ticket above */
1205 matched = (uri->port == rport &&
1206 pj_sockaddr_cmp(&contact_addr, &recv_addr)==0);
1207 } else {
1208 /* Compare the addresses as string, as before */
1209 matched = (uri->port == rport &&
1210 pj_stricmp(&uri->host, via_addr)==0);
1211 }
1212
1213 if (matched) {
Benny Prijono15b02302007-09-27 14:07:07 +00001214 /* Address doesn't change */
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001215 pj_pool_release(pool);
Benny Prijono15b02302007-09-27 14:07:07 +00001216 return PJ_FALSE;
1217 }
1218
Benny Prijono7f630432008-09-24 16:52:41 +00001219 /* Get server IP */
1220 srv_ip = pj_str(param->rdata->pkt_info.src_name);
1221
Benny Prijono15b02302007-09-27 14:07:07 +00001222 /* At this point we've detected that the address as seen by registrar.
1223 * has changed.
1224 */
Benny Prijono7f630432008-09-24 16:52:41 +00001225
1226 /* Do not switch if both Contact and server's IP address are
1227 * public but response contains private IP. A NAT in the middle
Benny Prijono44e42e12009-06-03 08:37:24 +00001228 * might have messed up with the SIP packets. See:
1229 * http://trac.pjsip.org/repos/ticket/643
Benny Prijono247921b2008-09-26 22:06:11 +00001230 *
1231 * This exception can be disabled by setting allow_contact_rewrite
1232 * to 2. In this case, the switch will always be done whenever there
1233 * is difference in the IP address in the response.
Benny Prijono7f630432008-09-24 16:52:41 +00001234 */
Benny Prijono247921b2008-09-26 22:06:11 +00001235 if (acc->cfg.allow_contact_rewrite != 2 && !is_private_ip(&uri->host) &&
1236 !is_private_ip(&srv_ip) && is_private_ip(via_addr))
Benny Prijono7f630432008-09-24 16:52:41 +00001237 {
1238 /* Don't switch */
1239 pj_pool_release(pool);
1240 return PJ_FALSE;
1241 }
1242
Benny Prijono4f933762009-11-10 03:45:42 +00001243 /* Also don't switch if only the port number part is different, and
1244 * the Via received address is private.
1245 * See http://trac.pjsip.org/repos/ticket/864
1246 */
1247 if (acc->cfg.allow_contact_rewrite != 2 &&
1248 pj_sockaddr_cmp(&contact_addr, &recv_addr)==0 &&
1249 is_private_ip(via_addr))
1250 {
1251 /* Don't switch */
1252 pj_pool_release(pool);
1253 return PJ_FALSE;
1254 }
1255
Benny Prijono15b02302007-09-27 14:07:07 +00001256 PJ_LOG(3,(THIS_FILE, "IP address change detected for account %d "
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001257 "(%.*s:%d --> %.*s:%d). Updating registration "
1258 "(using method %d)",
Benny Prijono15b02302007-09-27 14:07:07 +00001259 acc->index,
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001260 (int)uri->host.slen,
1261 uri->host.ptr,
1262 uri->port,
Benny Prijono15b02302007-09-27 14:07:07 +00001263 (int)via_addr->slen,
1264 via_addr->ptr,
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001265 rport,
1266 acc->cfg.contact_rewrite_method));
Benny Prijono15b02302007-09-27 14:07:07 +00001267
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001268 pj_assert(acc->cfg.contact_rewrite_method == 1 ||
1269 acc->cfg.contact_rewrite_method == 2);
1270
1271 if (acc->cfg.contact_rewrite_method == 1) {
1272 /* Unregister current contact */
1273 pjsua_acc_set_registration(acc->index, PJ_FALSE);
1274 if (acc->regc != NULL) {
1275 pjsip_regc_destroy(acc->regc);
1276 acc->regc = NULL;
1277 acc->contact.slen = 0;
1278 }
Benny Prijono15b02302007-09-27 14:07:07 +00001279 }
1280
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001281 /*
1282 * Build new Contact header
1283 */
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001284 {
Benny Prijonob54719f2010-11-16 03:07:46 +00001285 const char *ob = ";ob";
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001286 char *tmp;
Benny Prijono8972bf02009-04-05 18:30:45 +00001287 const char *beginquote, *endquote;
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001288 int len;
1289
Benny Prijono8972bf02009-04-05 18:30:45 +00001290 /* Enclose IPv6 address in square brackets */
1291 if (tp->key.type & PJSIP_TRANSPORT_IPV6) {
1292 beginquote = "[";
1293 endquote = "]";
1294 } else {
1295 beginquote = endquote = "";
1296 }
1297
Benny Prijono24a21852008-04-14 04:04:30 +00001298 tmp = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001299 len = pj_ansi_snprintf(tmp, PJSIP_MAX_URL_SIZE,
Benny Prijonob54719f2010-11-16 03:07:46 +00001300 "<sip:%.*s%s%s%.*s%s:%d;transport=%s%.*s%s>%.*s",
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001301 (int)acc->user_part.slen,
1302 acc->user_part.ptr,
Benny Prijono83088f32008-04-22 18:33:55 +00001303 (acc->user_part.slen? "@" : ""),
Benny Prijono8972bf02009-04-05 18:30:45 +00001304 beginquote,
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001305 (int)via_addr->slen,
1306 via_addr->ptr,
Benny Prijono8972bf02009-04-05 18:30:45 +00001307 endquote,
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001308 rport,
Benny Prijono30fe4852008-12-10 16:54:16 +00001309 tp->type_name,
Nanang Izzuddine2c7e852009-08-04 14:36:17 +00001310 (int)acc->cfg.contact_uri_params.slen,
1311 acc->cfg.contact_uri_params.ptr,
Benny Prijonob54719f2010-11-16 03:07:46 +00001312 ob,
Benny Prijono30fe4852008-12-10 16:54:16 +00001313 (int)acc->cfg.contact_params.slen,
1314 acc->cfg.contact_params.ptr);
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001315 if (len < 1) {
1316 PJ_LOG(1,(THIS_FILE, "URI too long"));
1317 pj_pool_release(pool);
1318 return PJ_FALSE;
1319 }
Benny Prijono3d9b4b62008-07-14 17:55:40 +00001320 pj_strdup2_with_null(acc->pool, &acc->contact, tmp);
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001321
Benny Prijonob54719f2010-11-16 03:07:46 +00001322 update_regc_contact(acc);
1323
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001324 /* Always update, by http://trac.pjsip.org/repos/ticket/864. */
1325 pj_strdup_with_null(tp->pool, &tp->local_name.host, via_addr);
1326 tp->local_name.port = rport;
1327
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001328 }
1329
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001330 if (acc->cfg.contact_rewrite_method == 2 && acc->regc != NULL) {
Benny Prijonob54719f2010-11-16 03:07:46 +00001331 pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact);
Benny Prijonoc6d5fdc2010-06-20 08:58:26 +00001332 }
Benny Prijono15b02302007-09-27 14:07:07 +00001333
1334 /* Perform new registration */
1335 pjsua_acc_set_registration(acc->index, PJ_TRUE);
1336
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001337 pj_pool_release(pool);
1338
Benny Prijono15b02302007-09-27 14:07:07 +00001339 return PJ_TRUE;
1340}
1341
Benny Prijonoa2a2d412007-10-18 05:54:02 +00001342/* Check and update Service-Route header */
1343void update_service_route(pjsua_acc *acc, pjsip_rx_data *rdata)
1344{
1345 pjsip_generic_string_hdr *hsr = NULL;
1346 pjsip_route_hdr *hr, *h;
1347 const pj_str_t HNAME = { "Service-Route", 13 };
1348 const pj_str_t HROUTE = { "Route", 5 };
1349 pjsip_uri *uri[PJSUA_ACC_MAX_PROXIES];
Benny Prijonof020ab22007-10-18 15:28:33 +00001350 unsigned i, uri_cnt = 0, rcnt;
Benny Prijonoa2a2d412007-10-18 05:54:02 +00001351
1352 /* Find and parse Service-Route headers */
1353 for (;;) {
1354 char saved;
1355 int parsed_len;
1356
1357 /* Find Service-Route header */
1358 hsr = (pjsip_generic_string_hdr*)
1359 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &HNAME, hsr);
1360 if (!hsr)
1361 break;
1362
1363 /* Parse as Route header since the syntax is similar. This may
1364 * return more than one headers.
1365 */
1366 saved = hsr->hvalue.ptr[hsr->hvalue.slen];
1367 hsr->hvalue.ptr[hsr->hvalue.slen] = '\0';
1368 hr = (pjsip_route_hdr*)
1369 pjsip_parse_hdr(rdata->tp_info.pool, &HROUTE, hsr->hvalue.ptr,
1370 hsr->hvalue.slen, &parsed_len);
1371 hsr->hvalue.ptr[hsr->hvalue.slen] = saved;
1372
1373 if (hr == NULL) {
1374 /* Error */
1375 PJ_LOG(1,(THIS_FILE, "Error parsing Service-Route header"));
1376 return;
1377 }
1378
1379 /* Save each URI in the result */
1380 h = hr;
1381 do {
1382 if (!PJSIP_URI_SCHEME_IS_SIP(h->name_addr.uri) &&
1383 !PJSIP_URI_SCHEME_IS_SIPS(h->name_addr.uri))
1384 {
1385 PJ_LOG(1,(THIS_FILE,"Error: non SIP URI in Service-Route: %.*s",
1386 (int)hsr->hvalue.slen, hsr->hvalue.ptr));
1387 return;
1388 }
1389
1390 uri[uri_cnt++] = h->name_addr.uri;
1391 h = h->next;
1392 } while (h != hr && uri_cnt != PJ_ARRAY_SIZE(uri));
1393
1394 if (h != hr) {
1395 PJ_LOG(1,(THIS_FILE, "Error: too many Service-Route headers"));
1396 return;
1397 }
1398
1399 /* Prepare to find next Service-Route header */
1400 hsr = hsr->next;
1401 if ((void*)hsr == (void*)&rdata->msg_info.msg->hdr)
1402 break;
1403 }
1404
1405 if (uri_cnt == 0)
1406 return;
1407
1408 /*
1409 * Update account's route set
1410 */
1411
1412 /* First remove all routes which are not the outbound proxies */
Benny Prijonof020ab22007-10-18 15:28:33 +00001413 rcnt = pj_list_size(&acc->route_set);
Benny Prijono2568c742007-11-03 09:29:52 +00001414 if (rcnt != pjsua_var.ua_cfg.outbound_proxy_cnt + acc->cfg.proxy_cnt) {
1415 for (i=pjsua_var.ua_cfg.outbound_proxy_cnt + acc->cfg.proxy_cnt,
1416 hr=acc->route_set.prev;
Benny Prijonof020ab22007-10-18 15:28:33 +00001417 i<rcnt;
1418 ++i)
1419 {
1420 pjsip_route_hdr *prev = hr->prev;
1421 pj_list_erase(hr);
1422 hr = prev;
1423 }
1424 }
Benny Prijonoa2a2d412007-10-18 05:54:02 +00001425
1426 /* Then append the Service-Route URIs */
1427 for (i=0; i<uri_cnt; ++i) {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001428 hr = pjsip_route_hdr_create(acc->pool);
1429 hr->name_addr.uri = (pjsip_uri*)pjsip_uri_clone(acc->pool, uri[i]);
Benny Prijonoa2a2d412007-10-18 05:54:02 +00001430 pj_list_push_back(&acc->route_set, hr);
1431 }
1432
1433 /* Done */
1434
1435 PJ_LOG(4,(THIS_FILE, "Service-Route updated for acc %d with %d URI(s)",
1436 acc->index, uri_cnt));
1437}
1438
Benny Prijonobddef2c2007-10-31 13:28:08 +00001439
1440/* Keep alive timer callback */
1441static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
1442{
1443 pjsua_acc *acc;
1444 pjsip_tpselector tp_sel;
1445 pj_time_val delay;
Benny Prijonodb4eb812007-12-09 05:30:47 +00001446 char addrtxt[PJ_INET6_ADDRSTRLEN];
Benny Prijonobddef2c2007-10-31 13:28:08 +00001447 pj_status_t status;
1448
1449 PJ_UNUSED_ARG(th);
1450
1451 PJSUA_LOCK();
1452
1453 te->id = PJ_FALSE;
1454
1455 acc = (pjsua_acc*) te->user_data;
1456
1457 /* Select the transport to send the packet */
1458 pj_bzero(&tp_sel, sizeof(tp_sel));
1459 tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT;
1460 tp_sel.u.transport = acc->ka_transport;
1461
1462 PJ_LOG(5,(THIS_FILE,
Benny Prijonodb4eb812007-12-09 05:30:47 +00001463 "Sending %d bytes keep-alive packet for acc %d to %s",
Benny Prijonobddef2c2007-10-31 13:28:08 +00001464 acc->cfg.ka_data.slen, acc->index,
Benny Prijonodb4eb812007-12-09 05:30:47 +00001465 pj_sockaddr_print(&acc->ka_target, addrtxt, sizeof(addrtxt),3)));
Benny Prijonobddef2c2007-10-31 13:28:08 +00001466
1467 /* Send raw packet */
1468 status = pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
1469 PJSIP_TRANSPORT_UDP, &tp_sel,
1470 NULL, acc->cfg.ka_data.ptr,
1471 acc->cfg.ka_data.slen,
1472 &acc->ka_target, acc->ka_target_len,
1473 NULL, NULL);
1474
1475 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
1476 pjsua_perror(THIS_FILE, "Error sending keep-alive packet", status);
1477 }
1478
1479 /* Reschedule next timer */
1480 delay.sec = acc->cfg.ka_interval;
1481 delay.msec = 0;
1482 status = pjsip_endpt_schedule_timer(pjsua_var.endpt, te, &delay);
1483 if (status == PJ_SUCCESS) {
1484 te->id = PJ_TRUE;
1485 } else {
1486 pjsua_perror(THIS_FILE, "Error starting keep-alive timer", status);
1487 }
1488
1489 PJSUA_UNLOCK();
1490}
1491
1492
1493/* Update keep-alive for the account */
1494static void update_keep_alive(pjsua_acc *acc, pj_bool_t start,
1495 struct pjsip_regc_cbparam *param)
1496{
1497 /* In all cases, stop keep-alive timer if it's running. */
1498 if (acc->ka_timer.id) {
1499 pjsip_endpt_cancel_timer(pjsua_var.endpt, &acc->ka_timer);
1500 acc->ka_timer.id = PJ_FALSE;
1501
1502 pjsip_transport_dec_ref(acc->ka_transport);
1503 acc->ka_transport = NULL;
1504 }
1505
1506 if (start) {
1507 pj_time_val delay;
1508 pj_status_t status;
1509
1510 /* Only do keep-alive if:
Benny Prijonobddef2c2007-10-31 13:28:08 +00001511 * - ka_interval is not zero in the account, and
1512 * - transport is UDP.
Benny Prijono7fff9f92008-04-04 10:50:21 +00001513 *
1514 * Previously we only enabled keep-alive when STUN is enabled, since
1515 * we thought that keep-alive is only needed in Internet situation.
1516 * But it has been discovered that Windows Firewall on WinXP also
1517 * needs to be kept-alive, otherwise incoming packets will be dropped.
1518 * So because of this, now keep-alive is always enabled for UDP,
1519 * regardless of whether STUN is enabled or not.
1520 *
1521 * Note that this applies only for UDP. For TCP/TLS, the keep-alive
1522 * is done by the transport layer.
Benny Prijonobddef2c2007-10-31 13:28:08 +00001523 */
Benny Prijono7fff9f92008-04-04 10:50:21 +00001524 if (/*pjsua_var.stun_srv.ipv4.sin_family == 0 ||*/
Benny Prijonobddef2c2007-10-31 13:28:08 +00001525 acc->cfg.ka_interval == 0 ||
1526 param->rdata->tp_info.transport->key.type != PJSIP_TRANSPORT_UDP)
1527 {
1528 /* Keep alive is not necessary */
1529 return;
1530 }
1531
1532 /* Save transport and destination address. */
1533 acc->ka_transport = param->rdata->tp_info.transport;
1534 pjsip_transport_add_ref(acc->ka_transport);
1535 pj_memcpy(&acc->ka_target, &param->rdata->pkt_info.src_addr,
1536 param->rdata->pkt_info.src_addr_len);
1537 acc->ka_target_len = param->rdata->pkt_info.src_addr_len;
1538
1539 /* Setup and start the timer */
1540 acc->ka_timer.cb = &keep_alive_timer_cb;
1541 acc->ka_timer.user_data = (void*)acc;
1542
1543 delay.sec = acc->cfg.ka_interval;
1544 delay.msec = 0;
1545 status = pjsip_endpt_schedule_timer(pjsua_var.endpt, &acc->ka_timer,
1546 &delay);
1547 if (status == PJ_SUCCESS) {
1548 acc->ka_timer.id = PJ_TRUE;
1549 PJ_LOG(4,(THIS_FILE, "Keep-alive timer started for acc %d, "
1550 "destination:%s:%d, interval:%ds",
1551 acc->index,
1552 param->rdata->pkt_info.src_name,
1553 param->rdata->pkt_info.src_port,
1554 acc->cfg.ka_interval));
1555 } else {
1556 acc->ka_timer.id = PJ_FALSE;
1557 pjsip_transport_dec_ref(acc->ka_transport);
1558 acc->ka_transport = NULL;
1559 pjsua_perror(THIS_FILE, "Error starting keep-alive timer", status);
1560 }
1561 }
1562}
1563
1564
Benny Prijonob54719f2010-11-16 03:07:46 +00001565/* Update the status of SIP outbound registration request */
1566static void update_rfc5626_status(pjsua_acc *acc, pjsip_rx_data *rdata)
1567{
1568 pjsip_require_hdr *hreq;
1569 const pj_str_t STR_OUTBOUND = {"outbound", 8};
1570 unsigned i;
1571
Benny Prijono2562b752010-11-16 06:01:38 +00001572 if (acc->rfc5626_status == OUTBOUND_UNKNOWN) {
Benny Prijonob54719f2010-11-16 03:07:46 +00001573 goto on_return;
1574 }
1575
1576 hreq = rdata->msg_info.require;
1577 if (!hreq) {
Benny Prijono2562b752010-11-16 06:01:38 +00001578 acc->rfc5626_status = OUTBOUND_NA;
Benny Prijonob54719f2010-11-16 03:07:46 +00001579 goto on_return;
1580 }
1581
1582 for (i=0; i<hreq->count; ++i) {
1583 if (pj_stricmp(&hreq->values[i], &STR_OUTBOUND)==0) {
1584 acc->rfc5626_status = OUTBOUND_ACTIVE;
1585 goto on_return;
1586 }
1587 }
1588
1589 /* Server does not support outbound */
Benny Prijono2562b752010-11-16 06:01:38 +00001590 acc->rfc5626_status = OUTBOUND_NA;
Benny Prijonob54719f2010-11-16 03:07:46 +00001591
1592on_return:
Benny Prijono2562b752010-11-16 06:01:38 +00001593 if (acc->rfc5626_status != OUTBOUND_ACTIVE) {
1594 acc->reg_contact = acc->contact;
1595 }
Benny Prijonob54719f2010-11-16 03:07:46 +00001596 PJ_LOG(4,(THIS_FILE, "SIP outbound status for acc %d is %s",
1597 acc->index, (acc->rfc5626_status==OUTBOUND_ACTIVE?
1598 "active": "not active")));
1599}
1600
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001601/*
1602 * This callback is called by pjsip_regc when outgoing register
1603 * request has completed.
1604 */
1605static void regc_cb(struct pjsip_regc_cbparam *param)
1606{
1607
Benny Prijonoa1e69682007-05-11 15:14:34 +00001608 pjsua_acc *acc = (pjsua_acc*) param->token;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001609
Benny Prijono15b02302007-09-27 14:07:07 +00001610 if (param->regc != acc->regc)
1611 return;
1612
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001613 PJSUA_LOCK();
1614
1615 /*
1616 * Print registration status.
1617 */
1618 if (param->status!=PJ_SUCCESS) {
1619 pjsua_perror(THIS_FILE, "SIP registration error",
1620 param->status);
1621 pjsip_regc_destroy(acc->regc);
1622 acc->regc = NULL;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001623 acc->contact.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001624
Benny Prijonobddef2c2007-10-31 13:28:08 +00001625 /* Stop keep-alive timer if any. */
1626 update_keep_alive(acc, PJ_FALSE, NULL);
1627
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001628 } else if (param->code < 0 || param->code >= 300) {
1629 PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%.*s)",
1630 param->code,
1631 (int)param->reason.slen, param->reason.ptr));
1632 pjsip_regc_destroy(acc->regc);
1633 acc->regc = NULL;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001634 acc->contact.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001635
Benny Prijonobddef2c2007-10-31 13:28:08 +00001636 /* Stop keep-alive timer if any. */
1637 update_keep_alive(acc, PJ_FALSE, NULL);
1638
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001639 } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
1640
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001641 /* Update auto registration flag */
1642 acc->auto_rereg.active = PJ_FALSE;
1643 acc->auto_rereg.attempt_cnt = 0;
1644
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001645 if (param->expiration < 1) {
1646 pjsip_regc_destroy(acc->regc);
1647 acc->regc = NULL;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001648 acc->contact.slen = 0;
Benny Prijonobddef2c2007-10-31 13:28:08 +00001649
1650 /* Stop keep-alive timer if any. */
1651 update_keep_alive(acc, PJ_FALSE, NULL);
1652
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001653 PJ_LOG(3,(THIS_FILE, "%s: unregistration success",
1654 pjsua_var.acc[acc->index].cfg.id.ptr));
1655 } else {
Benny Prijonob54719f2010-11-16 03:07:46 +00001656 /* Check and update SIP outbound status first, since the result
1657 * will determine if we should update re-registration
1658 */
1659 update_rfc5626_status(acc, param->rdata);
1660
Benny Prijono15b02302007-09-27 14:07:07 +00001661 /* Check NAT bound address */
1662 if (acc_check_nat_addr(acc, param)) {
Benny Prijono15b02302007-09-27 14:07:07 +00001663 PJSUA_UNLOCK();
1664 return;
1665 }
1666
Benny Prijonoa2a2d412007-10-18 05:54:02 +00001667 /* Check and update Service-Route header */
1668 update_service_route(acc, param->rdata);
1669
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001670 PJ_LOG(3, (THIS_FILE,
1671 "%s: registration success, status=%d (%.*s), "
1672 "will re-register in %d seconds",
1673 pjsua_var.acc[acc->index].cfg.id.ptr,
1674 param->code,
1675 (int)param->reason.slen, param->reason.ptr,
1676 param->expiration));
Benny Prijono8b6834f2007-02-24 13:29:22 +00001677
Benny Prijonobddef2c2007-10-31 13:28:08 +00001678 /* Start keep-alive timer if necessary. */
1679 update_keep_alive(acc, PJ_TRUE, param);
1680
Benny Prijono8b6834f2007-02-24 13:29:22 +00001681 /* Send initial PUBLISH if it is enabled */
1682 if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
1683 pjsua_pres_init_publish_acc(acc->index);
Benny Prijono4dd961b2009-10-26 11:21:37 +00001684
1685 /* Subscribe to MWI, if it's enabled */
1686 if (acc->cfg.mwi_enabled)
1687 pjsua_start_mwi(acc);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001688 }
1689
1690 } else {
1691 PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
1692 }
1693
1694 acc->reg_last_err = param->status;
1695 acc->reg_last_code = param->code;
1696
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001697 /* Check if we need to auto retry registration. Basically, registration
1698 * failure codes triggering auto-retry are those of temporal failures
1699 * considered to be recoverable in relatively short term.
1700 */
1701 if (acc->cfg.reg_retry_interval &&
1702 (param->code == PJSIP_SC_REQUEST_TIMEOUT ||
1703 param->code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
1704 param->code == PJSIP_SC_BAD_GATEWAY ||
1705 param->code == PJSIP_SC_SERVICE_UNAVAILABLE ||
1706 param->code == PJSIP_SC_SERVER_TIMEOUT ||
1707 PJSIP_IS_STATUS_IN_CLASS(param->code, 600))) /* Global failure */
1708 {
1709 schedule_reregistration(acc);
1710 }
1711
Nanang Izzuddin4ea1bcc2010-09-28 04:57:01 +00001712 /* Call the registration status callback */
Nanang Izzuddinc71bed62010-05-26 15:04:43 +00001713
Nanang Izzuddin4ea1bcc2010-09-28 04:57:01 +00001714 if (pjsua_var.ua_cfg.cb.on_reg_state) {
1715 (*pjsua_var.ua_cfg.cb.on_reg_state)(acc->index);
1716 }
1717
1718 if (pjsua_var.ua_cfg.cb.on_reg_state2) {
1719 pjsua_reg_info reg_info;
1720
1721 reg_info.cbparam = param;
1722 (*pjsua_var.ua_cfg.cb.on_reg_state2)(acc->index, &reg_info);
1723 }
1724
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001725 PJSUA_UNLOCK();
1726}
1727
1728
1729/*
1730 * Initialize client registration.
1731 */
1732static pj_status_t pjsua_regc_init(int acc_id)
1733{
1734 pjsua_acc *acc;
Benny Prijonoe1a8bad2006-10-13 17:45:38 +00001735 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001736 pj_status_t status;
1737
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001738 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001739 acc = &pjsua_var.acc[acc_id];
1740
1741 if (acc->cfg.reg_uri.slen == 0) {
1742 PJ_LOG(3,(THIS_FILE, "Registrar URI is not specified"));
1743 return PJ_SUCCESS;
1744 }
1745
Benny Prijonoe1a8bad2006-10-13 17:45:38 +00001746 /* Destroy existing session, if any */
1747 if (acc->regc) {
1748 pjsip_regc_destroy(acc->regc);
1749 acc->regc = NULL;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001750 acc->contact.slen = 0;
Benny Prijonoe1a8bad2006-10-13 17:45:38 +00001751 }
1752
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001753 /* initialize SIP registration if registrar is configured */
1754
1755 status = pjsip_regc_create( pjsua_var.endpt,
1756 acc, &regc_cb, &acc->regc);
1757
1758 if (status != PJ_SUCCESS) {
1759 pjsua_perror(THIS_FILE, "Unable to create client registration",
1760 status);
1761 return status;
1762 }
1763
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001764 pool = pjsua_pool_create("tmpregc", 512, 512);
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001765
1766 if (acc->contact.slen == 0) {
1767 pj_str_t tmp_contact;
1768
1769 status = pjsua_acc_create_uac_contact( pool, &tmp_contact,
1770 acc_id, &acc->cfg.reg_uri);
1771 if (status != PJ_SUCCESS) {
1772 pjsua_perror(THIS_FILE, "Unable to generate suitable Contact header"
1773 " for registration",
1774 status);
1775 pjsip_regc_destroy(acc->regc);
1776 pj_pool_release(pool);
1777 acc->regc = NULL;
1778 return status;
1779 }
1780
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00001781 pj_strdup_with_null(acc->pool, &acc->contact, &tmp_contact);
Benny Prijonob54719f2010-11-16 03:07:46 +00001782 update_regc_contact(acc);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00001783 }
1784
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001785 status = pjsip_regc_init( acc->regc,
1786 &acc->cfg.reg_uri,
1787 &acc->cfg.id,
1788 &acc->cfg.id,
Benny Prijonob54719f2010-11-16 03:07:46 +00001789 1, &acc->reg_contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001790 acc->cfg.reg_timeout);
1791 if (status != PJ_SUCCESS) {
1792 pjsua_perror(THIS_FILE,
1793 "Client registration initialization error",
1794 status);
Benny Prijono19b29372007-12-05 04:08:40 +00001795 pjsip_regc_destroy(acc->regc);
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001796 pj_pool_release(pool);
Benny Prijono19b29372007-12-05 04:08:40 +00001797 acc->regc = NULL;
Benny Prijonoddaaf6a2008-04-15 10:37:19 +00001798 acc->contact.slen = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001799 return status;
1800 }
1801
Benny Prijono62c5c5b2007-01-13 23:22:40 +00001802 /* If account is locked to specific transport, then set transport to
1803 * the client registration.
1804 */
1805 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
1806 pjsip_tpselector tp_sel;
1807
1808 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
1809 pjsip_regc_set_transport(acc->regc, &tp_sel);
1810 }
1811
1812
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001813 /* Set credentials
1814 */
1815 if (acc->cred_cnt) {
1816 pjsip_regc_set_credentials( acc->regc, acc->cred_cnt, acc->cred);
1817 }
1818
Benny Prijono48ab2b72007-11-08 09:24:30 +00001819 /* Set authentication preference */
1820 pjsip_regc_set_prefs(acc->regc, &acc->cfg.auth_pref);
1821
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001822 /* Set route-set
1823 */
Benny Prijono29c8ca32010-06-22 06:02:13 +00001824 if (acc->cfg.reg_use_proxy) {
1825 pjsip_route_hdr route_set;
1826 const pjsip_route_hdr *r;
1827
1828 pj_list_init(&route_set);
1829
1830 if (acc->cfg.reg_use_proxy & PJSUA_REG_USE_OUTBOUND_PROXY) {
1831 r = pjsua_var.outbound_proxy.next;
1832 while (r != &pjsua_var.outbound_proxy) {
1833 pj_list_push_back(&route_set, pjsip_hdr_shallow_clone(pool, r));
1834 r = r->next;
1835 }
1836 }
1837
1838 if (acc->cfg.reg_use_proxy & PJSUA_REG_USE_ACC_PROXY &&
1839 acc->cfg.proxy_cnt)
1840 {
1841 int cnt = acc->cfg.proxy_cnt;
1842 pjsip_route_hdr *pos = route_set.prev;
1843 int i;
1844
1845 r = acc->route_set.prev;
1846 for (i=0; i<cnt; ++i) {
1847 pj_list_push_front(pos, pjsip_hdr_shallow_clone(pool, r));
1848 r = r->prev;
1849 }
1850 }
1851
1852 if (!pj_list_empty(&route_set))
1853 pjsip_regc_set_route_set( acc->regc, &route_set );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001854 }
1855
Nanang Izzuddin60e8aa92010-09-28 10:48:48 +00001856 /* Add custom request headers specified in the account config */
1857 pjsip_regc_add_headers(acc->regc, &acc->cfg.reg_hdr_list);
1858
Benny Prijono8fc6de02006-11-11 21:25:55 +00001859 /* Add other request headers. */
1860 if (pjsua_var.ua_cfg.user_agent.slen) {
1861 pjsip_hdr hdr_list;
1862 const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
1863 pjsip_generic_string_hdr *h;
1864
Benny Prijono8fc6de02006-11-11 21:25:55 +00001865 pj_list_init(&hdr_list);
1866
1867 h = pjsip_generic_string_hdr_create(pool, &STR_USER_AGENT,
1868 &pjsua_var.ua_cfg.user_agent);
1869 pj_list_push_back(&hdr_list, (pjsip_hdr*)h);
1870
1871 pjsip_regc_add_headers(acc->regc, &hdr_list);
1872 }
1873
Benny Prijonob54719f2010-11-16 03:07:46 +00001874 /* If SIP outbound is used, add "Supported: outbound, path header" */
1875 if (acc->rfc5626_status == OUTBOUND_WANTED) {
1876 pjsip_hdr hdr_list;
1877 pjsip_supported_hdr *hsup;
1878
1879 pj_list_init(&hdr_list);
1880 hsup = pjsip_supported_hdr_create(pool);
1881 pj_list_push_back(&hdr_list, hsup);
1882
1883 hsup->count = 2;
1884 hsup->values[0] = pj_str("outbound");
1885 hsup->values[1] = pj_str("path");
1886
1887 pjsip_regc_add_headers(acc->regc, &hdr_list);
1888 }
1889
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001890 pj_pool_release(pool);
1891
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001892 return PJ_SUCCESS;
1893}
1894
1895
1896/*
1897 * Update registration or perform unregistration.
1898 */
1899PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id,
1900 pj_bool_t renew)
1901{
1902 pj_status_t status = 0;
1903 pjsip_tx_data *tdata = 0;
1904
1905 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
1906 PJ_EINVAL);
1907 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
1908
1909 PJSUA_LOCK();
1910
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001911 /* Cancel any re-registration timer */
1912 pjsua_cancel_timer(&pjsua_var.acc[acc_id].auto_rereg.timer);
1913
1914 /* Reset pointer to registration transport */
1915 pjsua_var.acc[acc_id].auto_rereg.reg_tp = NULL;
1916
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001917 if (renew) {
1918 if (pjsua_var.acc[acc_id].regc == NULL) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001919 status = pjsua_regc_init(acc_id);
1920 if (status != PJ_SUCCESS) {
1921 pjsua_perror(THIS_FILE, "Unable to create registration",
1922 status);
1923 goto on_return;
1924 }
1925 }
1926 if (!pjsua_var.acc[acc_id].regc) {
1927 status = PJ_EINVALIDOP;
1928 goto on_return;
1929 }
1930
1931 status = pjsip_regc_register(pjsua_var.acc[acc_id].regc, 1,
1932 &tdata);
1933
Benny Prijono28f673a2007-10-15 07:04:59 +00001934 if (0 && status == PJ_SUCCESS && pjsua_var.acc[acc_id].cred_cnt) {
1935 pjsua_acc *acc = &pjsua_var.acc[acc_id];
1936 pjsip_authorization_hdr *h;
1937 char *uri;
1938 int d;
1939
1940 uri = (char*) pj_pool_alloc(tdata->pool, acc->cfg.reg_uri.slen+10);
1941 d = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri,
1942 uri, acc->cfg.reg_uri.slen+10);
1943 pj_assert(d > 0);
1944
1945 h = pjsip_authorization_hdr_create(tdata->pool);
1946 h->scheme = pj_str("Digest");
1947 h->credential.digest.username = acc->cred[0].username;
1948 h->credential.digest.realm = acc->srv_domain;
1949 h->credential.digest.uri = pj_str(uri);
1950 h->credential.digest.algorithm = pj_str("md5");
1951
1952 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h);
1953 }
1954
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001955 } else {
1956 if (pjsua_var.acc[acc_id].regc == NULL) {
1957 PJ_LOG(3,(THIS_FILE, "Currently not registered"));
1958 status = PJ_EINVALIDOP;
1959 goto on_return;
1960 }
Benny Prijono166d5022010-02-10 14:24:48 +00001961
1962 pjsua_pres_unpublish(&pjsua_var.acc[acc_id]);
1963
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001964 status = pjsip_regc_unregister(pjsua_var.acc[acc_id].regc, &tdata);
1965 }
1966
Benny Prijono56315612006-07-18 14:39:40 +00001967 if (status == PJ_SUCCESS) {
Benny Prijono8fc6de02006-11-11 21:25:55 +00001968 //pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001969 status = pjsip_regc_send( pjsua_var.acc[acc_id].regc, tdata );
Benny Prijono56315612006-07-18 14:39:40 +00001970 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001971
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001972 /* Update pointer to registration transport */
1973 if (status == PJ_SUCCESS) {
1974 pjsip_regc_info reg_info;
1975
1976 pjsip_regc_get_info(pjsua_var.acc[acc_id].regc, &reg_info);
1977 pjsua_var.acc[acc_id].auto_rereg.reg_tp = reg_info.transport;
1978 }
1979
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001980 if (status != PJ_SUCCESS) {
1981 pjsua_perror(THIS_FILE, "Unable to create/send REGISTER",
1982 status);
1983 } else {
1984 PJ_LOG(3,(THIS_FILE, "%s sent",
1985 (renew? "Registration" : "Unregistration")));
1986 }
1987
1988on_return:
1989 PJSUA_UNLOCK();
1990 return status;
1991}
1992
1993
1994/*
1995 * Get account information.
1996 */
1997PJ_DEF(pj_status_t) pjsua_acc_get_info( pjsua_acc_id acc_id,
1998 pjsua_acc_info *info)
1999{
2000 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2001 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
2002
2003 PJ_ASSERT_RETURN(info != NULL, PJ_EINVAL);
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002004 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002005
Benny Prijonoac623b32006-07-03 15:19:31 +00002006 pj_bzero(info, sizeof(pjsua_acc_info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002007
2008 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
2009 PJ_EINVAL);
2010 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
2011
2012 PJSUA_LOCK();
2013
2014 if (pjsua_var.acc[acc_id].valid == PJ_FALSE) {
2015 PJSUA_UNLOCK();
2016 return PJ_EINVALIDOP;
2017 }
2018
2019 info->id = acc_id;
2020 info->is_default = (pjsua_var.default_acc == acc_id);
2021 info->acc_uri = acc_cfg->id;
2022 info->has_registration = (acc->cfg.reg_uri.slen > 0);
2023 info->online_status = acc->online_status;
Benny Prijono4461c7d2007-08-25 13:36:15 +00002024 pj_memcpy(&info->rpid, &acc->rpid, sizeof(pjrpid_element));
2025 if (info->rpid.note.slen)
2026 info->online_status_text = info->rpid.note;
2027 else if (info->online_status)
2028 info->online_status_text = pj_str("Online");
2029 else
2030 info->online_status_text = pj_str("Offline");
2031
Sauw Ming48f6dbf2010-09-07 05:10:25 +00002032 if (acc->reg_last_code) {
Benny Prijono6f979412006-06-15 12:25:46 +00002033 if (info->has_registration) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002034 info->status = (pjsip_status_code) acc->reg_last_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002035 info->status_text = *pjsip_get_status_text(acc->reg_last_code);
Sauw Ming48f6dbf2010-09-07 05:10:25 +00002036 if (acc->reg_last_err)
2037 info->reg_last_err = acc->reg_last_err;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002038 } else {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002039 info->status = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002040 info->status_text = pj_str("not registered");
2041 }
2042 } else if (acc->cfg.reg_uri.slen) {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002043 info->status = PJSIP_SC_TRYING;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002044 info->status_text = pj_str("In Progress");
2045 } else {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002046 info->status = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002047 info->status_text = pj_str("does not register");
2048 }
2049
2050 if (acc->regc) {
2051 pjsip_regc_info regc_info;
2052 pjsip_regc_get_info(acc->regc, &regc_info);
2053 info->expires = regc_info.next_reg;
2054 } else {
2055 info->expires = -1;
2056 }
2057
2058 PJSUA_UNLOCK();
2059
2060 return PJ_SUCCESS;
2061
2062}
2063
2064
2065/*
2066 * Enum accounts all account ids.
2067 */
2068PJ_DEF(pj_status_t) pjsua_enum_accs(pjsua_acc_id ids[],
2069 unsigned *count )
2070{
2071 unsigned i, c;
2072
2073 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
2074
2075 PJSUA_LOCK();
2076
2077 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2078 if (!pjsua_var.acc[i].valid)
2079 continue;
2080 ids[c] = i;
2081 ++c;
2082 }
2083
2084 *count = c;
2085
2086 PJSUA_UNLOCK();
2087
2088 return PJ_SUCCESS;
2089}
2090
2091
2092/*
2093 * Enum accounts info.
2094 */
2095PJ_DEF(pj_status_t) pjsua_acc_enum_info( pjsua_acc_info info[],
2096 unsigned *count )
2097{
2098 unsigned i, c;
2099
2100 PJ_ASSERT_RETURN(info && *count, PJ_EINVAL);
2101
2102 PJSUA_LOCK();
2103
2104 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2105 if (!pjsua_var.acc[i].valid)
2106 continue;
2107
2108 pjsua_acc_get_info(i, &info[c]);
2109 ++c;
2110 }
2111
2112 *count = c;
2113
2114 PJSUA_UNLOCK();
2115
2116 return PJ_SUCCESS;
2117}
2118
2119
2120/*
2121 * This is an internal function to find the most appropriate account to
2122 * used to reach to the specified URL.
2123 */
2124PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_outgoing(const pj_str_t *url)
2125{
2126 pj_str_t tmp;
2127 pjsip_uri *uri;
2128 pjsip_sip_uri *sip_uri;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002129 pj_pool_t *tmp_pool;
Benny Prijono093d3022006-09-24 00:07:11 +00002130 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002131
2132 PJSUA_LOCK();
2133
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002134 tmp_pool = pjsua_pool_create("tmpacc10", 256, 256);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002135
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002136 pj_strdup_with_null(tmp_pool, &tmp, url);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002137
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002138 uri = pjsip_parse_uri(tmp_pool, tmp.ptr, tmp.slen, 0);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002139 if (!uri) {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002140 pj_pool_release(tmp_pool);
Benny Prijono093d3022006-09-24 00:07:11 +00002141 PJSUA_UNLOCK();
2142 return pjsua_var.default_acc;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002143 }
2144
2145 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
2146 !PJSIP_URI_SCHEME_IS_SIPS(uri))
2147 {
2148 /* Return the first account with proxy */
Benny Prijono093d3022006-09-24 00:07:11 +00002149 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2150 if (!pjsua_var.acc[i].valid)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002151 continue;
Benny Prijono093d3022006-09-24 00:07:11 +00002152 if (!pj_list_empty(&pjsua_var.acc[i].route_set))
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002153 break;
2154 }
2155
Benny Prijono093d3022006-09-24 00:07:11 +00002156 if (i != PJ_ARRAY_SIZE(pjsua_var.acc)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002157 /* Found rather matching account */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002158 pj_pool_release(tmp_pool);
Benny Prijono093d3022006-09-24 00:07:11 +00002159 PJSUA_UNLOCK();
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002160 return i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002161 }
2162
2163 /* Not found, use default account */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002164 pj_pool_release(tmp_pool);
Benny Prijono093d3022006-09-24 00:07:11 +00002165 PJSUA_UNLOCK();
2166 return pjsua_var.default_acc;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002167 }
2168
Benny Prijonoa1e69682007-05-11 15:14:34 +00002169 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002170
Benny Prijonob4a17c92006-07-10 14:40:21 +00002171 /* Find matching domain AND port */
Benny Prijono093d3022006-09-24 00:07:11 +00002172 for (i=0; i<pjsua_var.acc_cnt; ++i) {
2173 unsigned acc_id = pjsua_var.acc_ids[i];
2174 if (pj_stricmp(&pjsua_var.acc[acc_id].srv_domain, &sip_uri->host)==0 &&
2175 pjsua_var.acc[acc_id].srv_port == sip_uri->port)
2176 {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002177 pj_pool_release(tmp_pool);
Benny Prijono093d3022006-09-24 00:07:11 +00002178 PJSUA_UNLOCK();
2179 return acc_id;
Benny Prijono21b9ad92006-08-15 13:11:22 +00002180 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002181 }
2182
Benny Prijonob4a17c92006-07-10 14:40:21 +00002183 /* If no match, try to match the domain part only */
Benny Prijono093d3022006-09-24 00:07:11 +00002184 for (i=0; i<pjsua_var.acc_cnt; ++i) {
2185 unsigned acc_id = pjsua_var.acc_ids[i];
2186 if (pj_stricmp(&pjsua_var.acc[acc_id].srv_domain, &sip_uri->host)==0)
2187 {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002188 pj_pool_release(tmp_pool);
Benny Prijono093d3022006-09-24 00:07:11 +00002189 PJSUA_UNLOCK();
2190 return acc_id;
Benny Prijonob4a17c92006-07-10 14:40:21 +00002191 }
2192 }
2193
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002194
Benny Prijono093d3022006-09-24 00:07:11 +00002195 /* Still no match, just use default account */
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00002196 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002197 PJSUA_UNLOCK();
Benny Prijono093d3022006-09-24 00:07:11 +00002198 return pjsua_var.default_acc;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002199}
2200
2201
2202/*
2203 * This is an internal function to find the most appropriate account to be
2204 * used to handle incoming calls.
2205 */
2206PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata)
2207{
2208 pjsip_uri *uri;
2209 pjsip_sip_uri *sip_uri;
Benny Prijono093d3022006-09-24 00:07:11 +00002210 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002211
Benny Prijono371cf0a2007-06-19 00:35:37 +00002212 /* Check that there's at least one account configured */
2213 PJ_ASSERT_RETURN(pjsua_var.acc_cnt!=0, pjsua_var.default_acc);
2214
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002215 uri = rdata->msg_info.to->uri;
2216
2217 /* Just return default account if To URI is not SIP: */
2218 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
2219 !PJSIP_URI_SCHEME_IS_SIPS(uri))
2220 {
2221 return pjsua_var.default_acc;
2222 }
2223
2224
2225 PJSUA_LOCK();
2226
2227 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
2228
2229 /* Find account which has matching username and domain. */
Benny Prijono093d3022006-09-24 00:07:11 +00002230 for (i=0; i < pjsua_var.acc_cnt; ++i) {
2231 unsigned acc_id = pjsua_var.acc_ids[i];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002232 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2233
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002234 if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
Benny Prijonob4a17c92006-07-10 14:40:21 +00002235 pj_stricmp(&acc->srv_domain, &sip_uri->host)==0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002236 {
2237 /* Match ! */
2238 PJSUA_UNLOCK();
2239 return acc_id;
2240 }
2241 }
2242
Benny Prijono093d3022006-09-24 00:07:11 +00002243 /* No matching account, try match domain part only. */
2244 for (i=0; i < pjsua_var.acc_cnt; ++i) {
2245 unsigned acc_id = pjsua_var.acc_ids[i];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002246 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2247
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002248 if (acc->valid && pj_stricmp(&acc->srv_domain, &sip_uri->host)==0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002249 /* Match ! */
2250 PJSUA_UNLOCK();
2251 return acc_id;
2252 }
2253 }
2254
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002255 /* No matching account, try match user part (and transport type) only. */
Benny Prijono093d3022006-09-24 00:07:11 +00002256 for (i=0; i < pjsua_var.acc_cnt; ++i) {
2257 unsigned acc_id = pjsua_var.acc_ids[i];
2258 pjsua_acc *acc = &pjsua_var.acc[acc_id];
2259
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002260 if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0) {
2261
2262 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
2263 pjsip_transport_type_e type;
2264 type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
2265 if (type == PJSIP_TRANSPORT_UNSPECIFIED)
2266 type = PJSIP_TRANSPORT_UDP;
2267
2268 if (pjsua_var.tpdata[acc->cfg.transport_id].type != type)
2269 continue;
2270 }
2271
Benny Prijono093d3022006-09-24 00:07:11 +00002272 /* Match ! */
2273 PJSUA_UNLOCK();
2274 return acc_id;
2275 }
2276 }
2277
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002278 /* Still no match, use default account */
2279 PJSUA_UNLOCK();
2280 return pjsua_var.default_acc;
2281}
2282
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002283
Benny Prijonofff245c2007-04-02 11:44:47 +00002284/*
2285 * Create arbitrary requests for this account.
2286 */
2287PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id,
2288 const pjsip_method *method,
2289 const pj_str_t *target,
2290 pjsip_tx_data **p_tdata)
2291{
2292 pjsip_tx_data *tdata;
2293 pjsua_acc *acc;
2294 pjsip_route_hdr *r;
2295 pj_status_t status;
2296
2297 PJ_ASSERT_RETURN(method && target && p_tdata, PJ_EINVAL);
2298 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
2299
2300 acc = &pjsua_var.acc[acc_id];
2301
2302 status = pjsip_endpt_create_request(pjsua_var.endpt, method, target,
2303 &acc->cfg.id, target,
2304 NULL, NULL, -1, NULL, &tdata);
2305 if (status != PJ_SUCCESS) {
2306 pjsua_perror(THIS_FILE, "Unable to create request", status);
2307 return status;
2308 }
2309
2310 /* Copy routeset */
2311 r = acc->route_set.next;
2312 while (r != &acc->route_set) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00002313 pjsip_msg_add_hdr(tdata->msg,
2314 (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, r));
Benny Prijonofff245c2007-04-02 11:44:47 +00002315 r = r->next;
2316 }
Benny Prijonoe7911562009-12-14 11:13:45 +00002317
2318 /* If account is locked to specific transport, then set that transport to
2319 * the transmit data.
2320 */
2321 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
2322 pjsip_tpselector tp_sel;
2323
2324 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
2325 pjsip_tx_data_set_transport(tdata, &tp_sel);
2326 }
2327
Benny Prijonofff245c2007-04-02 11:44:47 +00002328 /* Done */
2329 *p_tdata = tdata;
2330 return PJ_SUCCESS;
2331}
2332
2333
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002334PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
2335 pj_str_t *contact,
2336 pjsua_acc_id acc_id,
2337 const pj_str_t *suri)
2338{
2339 pjsua_acc *acc;
2340 pjsip_sip_uri *sip_uri;
2341 pj_status_t status;
2342 pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
2343 pj_str_t local_addr;
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002344 pjsip_tpselector tp_sel;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002345 unsigned flag;
2346 int secure;
2347 int local_port;
Benny Prijonod0bd4982007-12-02 15:40:52 +00002348 const char *beginquote, *endquote;
2349 char transport_param[32];
Benny Prijonob54719f2010-11-16 03:07:46 +00002350 const char *ob = ";ob";
Benny Prijonod0bd4982007-12-02 15:40:52 +00002351
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002352
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002353 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002354 acc = &pjsua_var.acc[acc_id];
2355
Benny Prijonof75eceb2007-03-23 19:09:54 +00002356 /* If force_contact is configured, then use use it */
2357 if (acc->cfg.force_contact.slen) {
2358 *contact = acc->cfg.force_contact;
2359 return PJ_SUCCESS;
2360 }
2361
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002362 /* If route-set is configured for the account, then URI is the
2363 * first entry of the route-set.
2364 */
2365 if (!pj_list_empty(&acc->route_set)) {
Benny Prijono9c1528f2007-02-10 19:22:25 +00002366 sip_uri = (pjsip_sip_uri*)
2367 pjsip_uri_get_uri(acc->route_set.next->name_addr.uri);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002368 } else {
2369 pj_str_t tmp;
2370 pjsip_uri *uri;
2371
2372 pj_strdup_with_null(pool, &tmp, suri);
2373
2374 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
2375 if (uri == NULL)
2376 return PJSIP_EINVALIDURI;
2377
2378 /* For non-SIP scheme, route set should be configured */
2379 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
Benny Prijonoc7545782010-09-28 07:43:18 +00002380 return PJSIP_ENOROUTESET;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002381
Benny Prijono8c7a6172007-02-18 21:17:46 +00002382 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002383 }
2384
2385 /* Get transport type of the URI */
2386 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri))
2387 tp_type = PJSIP_TRANSPORT_TLS;
2388 else if (sip_uri->transport_param.slen == 0) {
2389 tp_type = PJSIP_TRANSPORT_UDP;
2390 } else
2391 tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
2392
2393 if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
2394 return PJSIP_EUNSUPTRANSPORT;
2395
Benny Prijonod0bd4982007-12-02 15:40:52 +00002396 /* If destination URI specifies IPv6, then set transport type
2397 * to use IPv6 as well.
2398 */
Benny Prijono19b29372007-12-05 04:08:40 +00002399 if (pj_strchr(&sip_uri->host, ':'))
Benny Prijonod0bd4982007-12-02 15:40:52 +00002400 tp_type = (pjsip_transport_type_e)(((int)tp_type) + PJSIP_TRANSPORT_IPV6);
2401
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002402 flag = pjsip_transport_get_flag_from_type(tp_type);
2403 secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
2404
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002405 /* Init transport selector. */
2406 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
2407
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002408 /* Get local address suitable to send request from */
2409 status = pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002410 pool, tp_type, &tp_sel,
2411 &local_addr, &local_port);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002412 if (status != PJ_SUCCESS)
2413 return status;
2414
Benny Prijonod0bd4982007-12-02 15:40:52 +00002415 /* Enclose IPv6 address in square brackets */
2416 if (tp_type & PJSIP_TRANSPORT_IPV6) {
2417 beginquote = "[";
2418 endquote = "]";
2419 } else {
2420 beginquote = endquote = "";
2421 }
2422
2423 /* Don't add transport parameter if it's UDP */
Benny Prijono4c82c1e2008-10-16 08:14:51 +00002424 if (tp_type!=PJSIP_TRANSPORT_UDP && tp_type!=PJSIP_TRANSPORT_UDP6) {
Benny Prijonod0bd4982007-12-02 15:40:52 +00002425 pj_ansi_snprintf(transport_param, sizeof(transport_param),
2426 ";transport=%s",
2427 pjsip_transport_get_type_name(tp_type));
2428 } else {
2429 transport_param[0] = '\0';
2430 }
2431
2432
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002433 /* Create the contact header */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002434 contact->ptr = (char*)pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002435 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
Benny Prijonob54719f2010-11-16 03:07:46 +00002436 "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s",
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002437 (int)acc->display.slen,
2438 acc->display.ptr,
2439 (acc->display.slen?" " : ""),
Benny Prijono8058a622007-06-08 04:37:05 +00002440 (secure ? PJSUA_SECURE_SCHEME : "sip"),
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002441 (int)acc->user_part.slen,
2442 acc->user_part.ptr,
2443 (acc->user_part.slen?"@":""),
Benny Prijonod0bd4982007-12-02 15:40:52 +00002444 beginquote,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002445 (int)local_addr.slen,
2446 local_addr.ptr,
Benny Prijonod0bd4982007-12-02 15:40:52 +00002447 endquote,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002448 local_port,
Benny Prijono30fe4852008-12-10 16:54:16 +00002449 transport_param,
Nanang Izzuddine2c7e852009-08-04 14:36:17 +00002450 (int)acc->cfg.contact_uri_params.slen,
2451 acc->cfg.contact_uri_params.ptr,
Benny Prijonob54719f2010-11-16 03:07:46 +00002452 ob,
Benny Prijono30fe4852008-12-10 16:54:16 +00002453 (int)acc->cfg.contact_params.slen,
2454 acc->cfg.contact_params.ptr);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002455
2456 return PJ_SUCCESS;
2457}
2458
2459
2460
2461PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool,
2462 pj_str_t *contact,
2463 pjsua_acc_id acc_id,
2464 pjsip_rx_data *rdata )
2465{
2466 /*
2467 * Section 12.1.1, paragraph about using SIPS URI in Contact.
2468 * If the request that initiated the dialog contained a SIPS URI
2469 * in the Request-URI or in the top Record-Route header field value,
2470 * if there was any, or the Contact header field if there was no
2471 * Record-Route header field, the Contact header field in the response
2472 * MUST be a SIPS URI.
2473 */
2474 pjsua_acc *acc;
2475 pjsip_sip_uri *sip_uri;
2476 pj_status_t status;
2477 pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
2478 pj_str_t local_addr;
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002479 pjsip_tpselector tp_sel;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002480 unsigned flag;
2481 int secure;
2482 int local_port;
Benny Prijonod0bd4982007-12-02 15:40:52 +00002483 const char *beginquote, *endquote;
2484 char transport_param[32];
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002485
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002486 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002487 acc = &pjsua_var.acc[acc_id];
2488
Benny Prijonof75eceb2007-03-23 19:09:54 +00002489 /* If force_contact is configured, then use use it */
2490 if (acc->cfg.force_contact.slen) {
2491 *contact = acc->cfg.force_contact;
2492 return PJ_SUCCESS;
2493 }
2494
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002495 /* If Record-Route is present, then URI is the top Record-Route. */
2496 if (rdata->msg_info.record_route) {
Benny Prijono9c1528f2007-02-10 19:22:25 +00002497 sip_uri = (pjsip_sip_uri*)
2498 pjsip_uri_get_uri(rdata->msg_info.record_route->name_addr.uri);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002499 } else {
Benny Prijonoa330d452008-08-05 20:14:39 +00002500 pjsip_hdr *pos = NULL;
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002501 pjsip_contact_hdr *h_contact;
2502 pjsip_uri *uri = NULL;
2503
Benny Prijonoa330d452008-08-05 20:14:39 +00002504 /* Otherwise URI is Contact URI.
2505 * Iterate the Contact URI until we find sip: or sips: scheme.
2506 */
2507 do {
2508 h_contact = (pjsip_contact_hdr*)
2509 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT,
2510 pos);
2511 if (h_contact) {
Benny Prijono8b33bba2010-06-02 03:03:43 +00002512 if (h_contact->uri)
2513 uri = (pjsip_uri*) pjsip_uri_get_uri(h_contact->uri);
2514 else
2515 uri = NULL;
2516 if (!uri || (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
2517 !PJSIP_URI_SCHEME_IS_SIPS(uri)))
Benny Prijonoa330d452008-08-05 20:14:39 +00002518 {
2519 pos = (pjsip_hdr*)h_contact->next;
2520 if (pos == &rdata->msg_info.msg->hdr)
2521 h_contact = NULL;
2522 } else {
2523 break;
2524 }
2525 }
2526 } while (h_contact);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002527
2528
2529 /* Or if Contact URI is not present, take the remote URI from
2530 * the From URI.
2531 */
2532 if (uri == NULL)
Benny Prijonoa1e69682007-05-11 15:14:34 +00002533 uri = (pjsip_uri*) pjsip_uri_get_uri(rdata->msg_info.from->uri);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002534
2535
2536 /* Can only do sip/sips scheme at present. */
2537 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
2538 return PJSIP_EINVALIDREQURI;
2539
Benny Prijono8c7a6172007-02-18 21:17:46 +00002540 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002541 }
2542
2543 /* Get transport type of the URI */
2544 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri))
2545 tp_type = PJSIP_TRANSPORT_TLS;
2546 else if (sip_uri->transport_param.slen == 0) {
2547 tp_type = PJSIP_TRANSPORT_UDP;
2548 } else
2549 tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
Benny Prijonod0bd4982007-12-02 15:40:52 +00002550
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002551 if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
2552 return PJSIP_EUNSUPTRANSPORT;
2553
Benny Prijonod0bd4982007-12-02 15:40:52 +00002554 /* If destination URI specifies IPv6, then set transport type
2555 * to use IPv6 as well.
2556 */
2557 if (pj_strchr(&sip_uri->host, ':'))
2558 tp_type = (pjsip_transport_type_e)(((int)tp_type) + PJSIP_TRANSPORT_IPV6);
2559
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002560 flag = pjsip_transport_get_flag_from_type(tp_type);
2561 secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
2562
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002563 /* Init transport selector. */
2564 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
2565
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002566 /* Get local address suitable to send request from */
2567 status = pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002568 pool, tp_type, &tp_sel,
2569 &local_addr, &local_port);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002570 if (status != PJ_SUCCESS)
2571 return status;
2572
Benny Prijonod0bd4982007-12-02 15:40:52 +00002573 /* Enclose IPv6 address in square brackets */
2574 if (tp_type & PJSIP_TRANSPORT_IPV6) {
2575 beginquote = "[";
2576 endquote = "]";
2577 } else {
2578 beginquote = endquote = "";
2579 }
2580
2581 /* Don't add transport parameter if it's UDP */
Benny Prijono4c82c1e2008-10-16 08:14:51 +00002582 if (tp_type!=PJSIP_TRANSPORT_UDP && tp_type!=PJSIP_TRANSPORT_UDP6) {
Benny Prijonod0bd4982007-12-02 15:40:52 +00002583 pj_ansi_snprintf(transport_param, sizeof(transport_param),
2584 ";transport=%s",
2585 pjsip_transport_get_type_name(tp_type));
2586 } else {
2587 transport_param[0] = '\0';
2588 }
2589
2590
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002591 /* Create the contact header */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002592 contact->ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002593 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
Nanang Izzuddine2c7e852009-08-04 14:36:17 +00002594 "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s",
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002595 (int)acc->display.slen,
2596 acc->display.ptr,
2597 (acc->display.slen?" " : ""),
Benny Prijono8058a622007-06-08 04:37:05 +00002598 (secure ? PJSUA_SECURE_SCHEME : "sip"),
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002599 (int)acc->user_part.slen,
2600 acc->user_part.ptr,
2601 (acc->user_part.slen?"@":""),
Benny Prijonod0bd4982007-12-02 15:40:52 +00002602 beginquote,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002603 (int)local_addr.slen,
2604 local_addr.ptr,
Benny Prijonod0bd4982007-12-02 15:40:52 +00002605 endquote,
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002606 local_port,
Benny Prijono30fe4852008-12-10 16:54:16 +00002607 transport_param,
Nanang Izzuddine2c7e852009-08-04 14:36:17 +00002608 (int)acc->cfg.contact_uri_params.slen,
2609 acc->cfg.contact_uri_params.ptr,
Benny Prijono30fe4852008-12-10 16:54:16 +00002610 (int)acc->cfg.contact_params.slen,
2611 acc->cfg.contact_params.ptr);
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002612
2613 return PJ_SUCCESS;
2614}
2615
2616
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002617PJ_DEF(pj_status_t) pjsua_acc_set_transport( pjsua_acc_id acc_id,
2618 pjsua_transport_id tp_id)
2619{
2620 pjsua_acc *acc;
2621
2622 PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
2623 acc = &pjsua_var.acc[acc_id];
2624
Benny Prijonoa1e69682007-05-11 15:14:34 +00002625 PJ_ASSERT_RETURN(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
Benny Prijono62c5c5b2007-01-13 23:22:40 +00002626 PJ_EINVAL);
2627
2628 acc->cfg.transport_id = tp_id;
2629
2630 return PJ_SUCCESS;
2631}
Benny Prijonoc570f2d2006-07-18 00:33:02 +00002632
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002633
2634/* Auto re-registration timeout callback */
2635static void auto_rereg_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
2636{
2637 pjsua_acc *acc;
2638 pj_status_t status;
2639
2640 PJ_UNUSED_ARG(th);
2641 acc = (pjsua_acc*) te->user_data;
2642 pj_assert(acc);
2643
2644 PJSUA_LOCK();
2645
Nanang Izzuddinc71bed62010-05-26 15:04:43 +00002646 /* Check if the reregistration timer is still valid, e.g: while waiting
2647 * timeout timer application might have deleted the account or disabled
2648 * the auto-reregistration.
2649 */
2650 if (!acc->valid || !acc->auto_rereg.active ||
2651 acc->cfg.reg_retry_interval == 0)
2652 {
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002653 goto on_return;
Nanang Izzuddinc71bed62010-05-26 15:04:43 +00002654 }
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002655
2656 /* Start re-registration */
2657 acc->auto_rereg.attempt_cnt++;
2658 status = pjsua_acc_set_registration(acc->index, PJ_TRUE);
2659 if (status != PJ_SUCCESS)
2660 schedule_reregistration(acc);
2661
Nanang Izzuddin66580002010-04-14 08:12:08 +00002662on_return:
2663 PJSUA_UNLOCK();
2664}
2665
2666
2667/* Schedule reregistration for specified account. Note that the first
2668 * re-registration after a registration failure will be done immediately.
2669 * Also note that this function should be called within PJSUA mutex.
2670 */
2671static void schedule_reregistration(pjsua_acc *acc)
2672{
2673 pj_time_val delay;
2674
Nanang Izzuddinc71bed62010-05-26 15:04:43 +00002675 pj_assert(acc);
2676
2677 /* Validate the account and re-registration feature status */
2678 if (!acc->valid || acc->cfg.reg_retry_interval == 0) {
2679 return;
2680 }
Nanang Izzuddin66580002010-04-14 08:12:08 +00002681
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002682 /* If configured, disconnect calls of this account after the first
2683 * reregistration attempt failed.
2684 */
Nanang Izzuddin66580002010-04-14 08:12:08 +00002685 if (acc->cfg.drop_calls_on_reg_fail && acc->auto_rereg.attempt_cnt >= 1)
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002686 {
2687 unsigned i, cnt;
2688
2689 for (i = 0, cnt = 0; i < pjsua_var.ua_cfg.max_calls; ++i) {
2690 if (pjsua_var.calls[i].acc_id == acc->index) {
2691 pjsua_call_hangup(i, 0, NULL, NULL);
2692 ++cnt;
2693 }
2694 }
2695
2696 if (cnt) {
2697 PJ_LOG(3, (THIS_FILE, "Disconnecting %d call(s) of account #%d "
2698 "after reregistration attempt failed",
2699 cnt, acc->index));
2700 }
2701 }
2702
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00002703 /* Cancel any re-registration timer */
2704 pjsua_cancel_timer(&acc->auto_rereg.timer);
2705
2706 /* Update re-registration flag */
2707 acc->auto_rereg.active = PJ_TRUE;
2708
2709 /* Set up timer for reregistration */
2710 acc->auto_rereg.timer.cb = &auto_rereg_timer_cb;
2711 acc->auto_rereg.timer.user_data = acc;
2712
2713 /* Reregistration attempt. The first attempt will be done immediately. */
2714 delay.sec = acc->auto_rereg.attempt_cnt? acc->cfg.reg_retry_interval : 0;
2715 delay.msec = 0;
2716 pjsua_schedule_timer(&acc->auto_rereg.timer, &delay);
2717}
2718
2719
2720/* Internal function to perform auto-reregistration on transport
2721 * connection/disconnection events.
2722 */
2723void pjsua_acc_on_tp_state_changed(pjsip_transport *tp,
2724 pjsip_transport_state state,
2725 const pjsip_transport_state_info *info)
2726{
2727 unsigned i;
2728
2729 PJ_UNUSED_ARG(info);
2730
2731 /* Only care for transport disconnection events */
2732 if (state != PJSIP_TP_STATE_DISCONNECTED)
2733 return;
2734
2735 /* Shutdown this transport, to make sure that the transport manager
2736 * will create a new transport for reconnection.
2737 */
2738 pjsip_transport_shutdown(tp);
2739
2740 PJSUA_LOCK();
2741
2742 /* Enumerate accounts using this transport and perform actions
2743 * based on the transport state.
2744 */
2745 for (i = 0; i < PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
2746 pjsua_acc *acc = &pjsua_var.acc[i];
2747
2748 /* Skip if this account is not valid OR auto re-registration
2749 * feature is disabled OR this transport is not used by this account.
2750 */
2751 if (!acc->valid || !acc->cfg.reg_retry_interval ||
2752 tp != acc->auto_rereg.reg_tp)
2753 {
2754 continue;
2755 }
2756
2757 /* Schedule reregistration for this account */
2758 schedule_reregistration(acc);
2759 }
2760
2761 PJSUA_UNLOCK();
2762}