blob: 05815ede1eedcd455731723d37930aaed9ad204c [file] [log] [blame]
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsua-lib/pjsua.h>
20#include <pjsua-lib/pjsua_internal.h>
21
22
23#define THIS_FILE "pjsua_acc.c"
24
25
26/*
27 * Get number of current accounts.
28 */
29PJ_DEF(unsigned) pjsua_acc_get_count(void)
30{
31 return pjsua_var.acc_cnt;
32}
33
34
35/*
36 * Check if the specified account ID is valid.
37 */
38PJ_DEF(pj_bool_t) pjsua_acc_is_valid(pjsua_acc_id acc_id)
39{
40 return acc_id>=0 && acc_id<PJ_ARRAY_SIZE(pjsua_var.acc) &&
41 pjsua_var.acc[acc_id].valid;
42}
43
44
45/*
46 * Copy account configuration.
47 */
48static void copy_acc_config(pj_pool_t *pool,
49 pjsua_acc_config *dst,
50 const pjsua_acc_config *src)
51{
52 unsigned i;
53
54 pj_memcpy(dst, src, sizeof(pjsua_acc_config));
55
56 pj_strdup_with_null(pool, &dst->id, &src->id);
57 pj_strdup_with_null(pool, &dst->reg_uri, &src->reg_uri);
Benny Prijonob4a17c92006-07-10 14:40:21 +000058 pj_strdup_with_null(pool, &dst->force_contact, &src->force_contact);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000059
60 dst->proxy_cnt = src->proxy_cnt;
61 for (i=0; i<src->proxy_cnt; ++i)
62 pj_strdup_with_null(pool, &dst->proxy[i], &src->proxy[i]);
63
64 dst->reg_timeout = src->reg_timeout;
65 dst->cred_count = src->cred_count;
66
67 for (i=0; i<src->cred_count; ++i) {
68 pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
69 }
70}
71
72
73/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000074 * Initialize a new account (after configuration is set).
75 */
76static pj_status_t initialize_acc(unsigned acc_id)
77{
78 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
79 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonoc570f2d2006-07-18 00:33:02 +000080 pjsip_name_addr *name_addr;
Benny Prijonob4a17c92006-07-10 14:40:21 +000081 pjsip_sip_uri *sip_uri, *sip_reg_uri;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000082 unsigned i;
83
84 /* Need to parse local_uri to get the elements: */
85
Benny Prijonoc570f2d2006-07-18 00:33:02 +000086 name_addr = (pjsip_name_addr*)
87 pjsip_parse_uri(pjsua_var.pool, acc_cfg->id.ptr,
88 acc_cfg->id.slen,
89 PJSIP_PARSE_URI_AS_NAMEADDR);
90 if (name_addr == NULL) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +000091 pjsua_perror(THIS_FILE, "Invalid local URI",
92 PJSIP_EINVALIDURI);
93 return PJSIP_EINVALIDURI;
94 }
95
96 /* Local URI MUST be a SIP or SIPS: */
97
Benny Prijonoc570f2d2006-07-18 00:33:02 +000098 if (!PJSIP_URI_SCHEME_IS_SIP(name_addr) &&
99 !PJSIP_URI_SCHEME_IS_SIPS(name_addr))
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000100 {
101 pjsua_perror(THIS_FILE, "Invalid local URI",
102 PJSIP_EINVALIDSCHEME);
103 return PJSIP_EINVALIDSCHEME;
104 }
105
106
107 /* Get the SIP URI object: */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000108 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(name_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000109
Benny Prijonob4a17c92006-07-10 14:40:21 +0000110
111 /* Parse registrar URI, if any */
112 if (acc_cfg->reg_uri.slen) {
113 pjsip_uri *reg_uri;
114
115 reg_uri = pjsip_parse_uri(pjsua_var.pool, acc_cfg->reg_uri.ptr,
116 acc_cfg->reg_uri.slen, 0);
117 if (reg_uri == NULL) {
118 pjsua_perror(THIS_FILE, "Invalid registrar URI",
119 PJSIP_EINVALIDURI);
120 return PJSIP_EINVALIDURI;
121 }
122
123 /* Registrar URI MUST be a SIP or SIPS: */
124 if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) &&
125 !PJSIP_URI_SCHEME_IS_SIPS(reg_uri))
126 {
127 pjsua_perror(THIS_FILE, "Invalid registar URI",
128 PJSIP_EINVALIDSCHEME);
129 return PJSIP_EINVALIDSCHEME;
130 }
131
132 sip_reg_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri);
133
134 } else {
135 sip_reg_uri = NULL;
136 }
137
138
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000139 /* Save the user and domain part. These will be used when finding an
140 * account for incoming requests.
141 */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000142 acc->display = name_addr->display;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000143 acc->user_part = sip_uri->user;
Benny Prijonob4a17c92006-07-10 14:40:21 +0000144 acc->srv_domain = sip_uri->host;
145 acc->srv_port = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000146
Benny Prijonob4a17c92006-07-10 14:40:21 +0000147 if (sip_reg_uri) {
148 acc->srv_port = sip_reg_uri->port;
149 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000150
151 /* Create Contact header if not present. */
Benny Prijonob4a17c92006-07-10 14:40:21 +0000152 //if (acc_cfg->contact.slen == 0) {
153 // acc_cfg->contact = acc_cfg->id;
154 //}
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000155
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000156 /* Build account route-set from outbound proxies and route set from
157 * account configuration.
158 */
159 pj_list_init(&acc->route_set);
160
161 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
162 pj_str_t hname = { "Route", 5};
163 pjsip_route_hdr *r;
164 pj_str_t tmp;
165
166 pj_strdup_with_null(pjsua_var.pool, &tmp,
167 &pjsua_var.ua_cfg.outbound_proxy[i]);
168 r = pjsip_parse_hdr(pjsua_var.pool, &hname, tmp.ptr, tmp.slen, NULL);
169 if (r == NULL) {
170 pjsua_perror(THIS_FILE, "Invalid outbound proxy URI",
171 PJSIP_EINVALIDURI);
172 return PJSIP_EINVALIDURI;
173 }
174 pj_list_push_back(&acc->route_set, r);
175 }
176
177 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
178 pj_str_t hname = { "Route", 5};
179 pjsip_route_hdr *r;
180 pj_str_t tmp;
181
182 pj_strdup_with_null(pjsua_var.pool, &tmp, &acc_cfg->proxy[i]);
183 r = pjsip_parse_hdr(pjsua_var.pool, &hname, tmp.ptr, tmp.slen, NULL);
184 if (r == NULL) {
185 pjsua_perror(THIS_FILE, "Invalid URI in account route set",
186 PJ_EINVAL);
187 return PJ_EINVAL;
188 }
189 pj_list_push_back(&acc->route_set, r);
190 }
191
192
193 /* Concatenate credentials from account config and global config */
194 acc->cred_cnt = 0;
195 for (i=0; i<acc_cfg->cred_count; ++i) {
196 acc->cred[acc->cred_cnt++] = acc_cfg->cred_info[i];
197 }
198 for (i=0; i<pjsua_var.ua_cfg.cred_count &&
199 acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i)
200 {
201 acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i];
202 }
203
204 /* Init presence subscription */
205 pj_list_init(&acc->pres_srv_list);
206
207 /* Mark account as valid */
208 pjsua_var.acc[acc_id].valid = PJ_TRUE;
209
210
211 return PJ_SUCCESS;
212}
213
214
215/*
216 * Add a new account to pjsua.
217 */
218PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
219 pj_bool_t is_default,
220 pjsua_acc_id *p_acc_id)
221{
222 unsigned id;
223 pj_status_t status;
224
225 PJ_ASSERT_RETURN(pjsua_var.acc_cnt < PJ_ARRAY_SIZE(pjsua_var.acc),
226 PJ_ETOOMANY);
227
228 /* Must have a transport */
Benny Prijonoe93e2872006-06-28 16:46:49 +0000229 PJ_TODO(associate_acc_with_transport);
230 PJ_ASSERT_RETURN(pjsua_var.tpdata[0].data.ptr != NULL, PJ_EINVALIDOP);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000231
232 PJSUA_LOCK();
233
234 /* Find empty account id. */
235 for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.acc); ++id) {
236 if (pjsua_var.acc[id].valid == PJ_FALSE)
237 break;
238 }
239
240 /* Expect to find a slot */
241 PJ_ASSERT_ON_FAIL( id < PJ_ARRAY_SIZE(pjsua_var.acc),
242 {PJSUA_UNLOCK(); return PJ_EBUG;});
243
244 /* Copy config */
245 copy_acc_config(pjsua_var.pool, &pjsua_var.acc[id].cfg, cfg);
246
247 /* Normalize registration timeout */
248 if (pjsua_var.acc[id].cfg.reg_uri.slen &&
249 pjsua_var.acc[id].cfg.reg_timeout == 0)
250 {
251 pjsua_var.acc[id].cfg.reg_timeout = PJSUA_REG_INTERVAL;
252 }
253
254 status = initialize_acc(id);
255 if (status != PJ_SUCCESS) {
256 pjsua_perror(THIS_FILE, "Error adding account", status);
257 PJSUA_UNLOCK();
258 return status;
259 }
260
261 if (is_default)
262 pjsua_var.default_acc = id;
263
264 if (p_acc_id)
265 *p_acc_id = id;
266
267 pjsua_var.acc_cnt++;
268
269 PJSUA_UNLOCK();
270
271 PJ_LOG(4,(THIS_FILE, "Account %.*s added with id %d",
272 (int)cfg->id.slen, cfg->id.ptr, id));
273
274 /* If accounts has registration enabled, start registration */
275 if (pjsua_var.acc[id].cfg.reg_uri.slen)
276 pjsua_acc_set_registration(id, PJ_TRUE);
277
278
279 return PJ_SUCCESS;
280}
281
282
283/*
284 * Add local account
285 */
286PJ_DEF(pj_status_t) pjsua_acc_add_local( pjsua_transport_id tid,
287 pj_bool_t is_default,
288 pjsua_acc_id *p_acc_id)
289{
290 pjsua_acc_config cfg;
Benny Prijonoe93e2872006-06-28 16:46:49 +0000291 struct transport_data *t = &pjsua_var.tpdata[tid];
Benny Prijonoe85bc412006-07-29 20:29:24 +0000292 char uri[PJSIP_MAX_URL_SIZE];
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000293
Benny Prijonoe93e2872006-06-28 16:46:49 +0000294 /* ID must be valid */
295 PJ_ASSERT_RETURN(tid>=0 && tid<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL);
296
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000297 /* Transport must be valid */
Benny Prijonoe93e2872006-06-28 16:46:49 +0000298 PJ_ASSERT_RETURN(t->data.ptr != NULL, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000299
300 pjsua_acc_config_default(&cfg);
301
302 /* Build URI for the account */
Benny Prijonoe85bc412006-07-29 20:29:24 +0000303 pj_ansi_snprintf(uri, PJSIP_MAX_URL_SIZE,
304 "<sip:%.*s:%d;transport=%s>",
305 (int)t->local_name.host.slen,
306 t->local_name.host.ptr,
307 t->local_name.port,
308 pjsip_transport_get_type_name(t->type));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000309
310 cfg.id = pj_str(uri);
311
312 return pjsua_acc_add(&cfg, is_default, p_acc_id);
313}
314
315
316/*
317 * Delete account.
318 */
319PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id)
320{
321 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
322 PJ_EINVAL);
323 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
324
325 PJSUA_LOCK();
326
327 /* Delete registration */
328 if (pjsua_var.acc[acc_id].regc != NULL)
329 pjsua_acc_set_registration(acc_id, PJ_FALSE);
330
331 /* Delete server presence subscription */
332 pjsua_pres_delete_acc(acc_id);
333
334 /* Invalidate */
335 pjsua_var.acc[acc_id].valid = PJ_FALSE;
336
337 PJ_TODO(may_need_to_scan_calls);
338
339 PJSUA_UNLOCK();
340
341 PJ_LOG(4,(THIS_FILE, "Account id %d deleted", acc_id));
342
343 return PJ_SUCCESS;
344}
345
346
347/*
348 * Modify account information.
349 */
350PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
351 const pjsua_acc_config *cfg)
352{
353 PJ_TODO(pjsua_acc_modify);
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000354 PJ_UNUSED_ARG(acc_id);
355 PJ_UNUSED_ARG(cfg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000356 return PJ_EINVALIDOP;
357}
358
359
360/*
361 * Modify account's presence status to be advertised to remote/presence
362 * subscribers.
363 */
364PJ_DEF(pj_status_t) pjsua_acc_set_online_status( pjsua_acc_id acc_id,
365 pj_bool_t is_online)
366{
367 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
368 PJ_EINVAL);
369 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
370
371 pjsua_var.acc[acc_id].online_status = is_online;
372 pjsua_pres_refresh();
373 return PJ_SUCCESS;
374}
375
376
377/*
378 * This callback is called by pjsip_regc when outgoing register
379 * request has completed.
380 */
381static void regc_cb(struct pjsip_regc_cbparam *param)
382{
383
384 pjsua_acc *acc = param->token;
385
386 PJSUA_LOCK();
387
388 /*
389 * Print registration status.
390 */
391 if (param->status!=PJ_SUCCESS) {
392 pjsua_perror(THIS_FILE, "SIP registration error",
393 param->status);
394 pjsip_regc_destroy(acc->regc);
395 acc->regc = NULL;
396
397 } else if (param->code < 0 || param->code >= 300) {
398 PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%.*s)",
399 param->code,
400 (int)param->reason.slen, param->reason.ptr));
401 pjsip_regc_destroy(acc->regc);
402 acc->regc = NULL;
403
404 } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
405
406 if (param->expiration < 1) {
407 pjsip_regc_destroy(acc->regc);
408 acc->regc = NULL;
409 PJ_LOG(3,(THIS_FILE, "%s: unregistration success",
410 pjsua_var.acc[acc->index].cfg.id.ptr));
411 } else {
412 PJ_LOG(3, (THIS_FILE,
413 "%s: registration success, status=%d (%.*s), "
414 "will re-register in %d seconds",
415 pjsua_var.acc[acc->index].cfg.id.ptr,
416 param->code,
417 (int)param->reason.slen, param->reason.ptr,
418 param->expiration));
419 }
420
421 } else {
422 PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
423 }
424
425 acc->reg_last_err = param->status;
426 acc->reg_last_code = param->code;
427
428 if (pjsua_var.ua_cfg.cb.on_reg_state)
429 (*pjsua_var.ua_cfg.cb.on_reg_state)(acc->index);
430
431 PJSUA_UNLOCK();
432}
433
434
435/*
436 * Initialize client registration.
437 */
438static pj_status_t pjsua_regc_init(int acc_id)
439{
440 pjsua_acc *acc;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000441 pj_str_t contact;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000442 pj_status_t status;
443
444 acc = &pjsua_var.acc[acc_id];
445
446 if (acc->cfg.reg_uri.slen == 0) {
447 PJ_LOG(3,(THIS_FILE, "Registrar URI is not specified"));
448 return PJ_SUCCESS;
449 }
450
451 /* initialize SIP registration if registrar is configured */
452
453 status = pjsip_regc_create( pjsua_var.endpt,
454 acc, &regc_cb, &acc->regc);
455
456 if (status != PJ_SUCCESS) {
457 pjsua_perror(THIS_FILE, "Unable to create client registration",
458 status);
459 return status;
460 }
461
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000462 status = pjsua_acc_create_uac_contact( pjsua_var.pool, &contact,
463 acc_id, &acc->cfg.reg_uri);
464 if (status != PJ_SUCCESS) {
465 pjsua_perror(THIS_FILE, "Unable to generate suitable Contact header"
466 " for registration",
467 status);
468 return status;
469 }
470
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000471 status = pjsip_regc_init( acc->regc,
472 &acc->cfg.reg_uri,
473 &acc->cfg.id,
474 &acc->cfg.id,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000475 1, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000476 acc->cfg.reg_timeout);
477 if (status != PJ_SUCCESS) {
478 pjsua_perror(THIS_FILE,
479 "Client registration initialization error",
480 status);
481 return status;
482 }
483
484 /* Set credentials
485 */
486 if (acc->cred_cnt) {
487 pjsip_regc_set_credentials( acc->regc, acc->cred_cnt, acc->cred);
488 }
489
490 /* Set route-set
491 */
492 if (!pj_list_empty(&acc->route_set)) {
493 pjsip_regc_set_route_set( acc->regc, &acc->route_set );
494 }
495
496 return PJ_SUCCESS;
497}
498
499
500/*
501 * Update registration or perform unregistration.
502 */
503PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id,
504 pj_bool_t renew)
505{
506 pj_status_t status = 0;
507 pjsip_tx_data *tdata = 0;
508
509 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
510 PJ_EINVAL);
511 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
512
513 PJSUA_LOCK();
514
515 if (renew) {
516 if (pjsua_var.acc[acc_id].regc == NULL) {
517 // Need route set.
518 status = pjsua_regc_init(acc_id);
519 if (status != PJ_SUCCESS) {
520 pjsua_perror(THIS_FILE, "Unable to create registration",
521 status);
522 goto on_return;
523 }
524 }
525 if (!pjsua_var.acc[acc_id].regc) {
526 status = PJ_EINVALIDOP;
527 goto on_return;
528 }
529
530 status = pjsip_regc_register(pjsua_var.acc[acc_id].regc, 1,
531 &tdata);
532
533 } else {
534 if (pjsua_var.acc[acc_id].regc == NULL) {
535 PJ_LOG(3,(THIS_FILE, "Currently not registered"));
536 status = PJ_EINVALIDOP;
537 goto on_return;
538 }
539 status = pjsip_regc_unregister(pjsua_var.acc[acc_id].regc, &tdata);
540 }
541
Benny Prijono56315612006-07-18 14:39:40 +0000542 if (status == PJ_SUCCESS) {
543 pjsua_process_msg_data(tdata, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000544 status = pjsip_regc_send( pjsua_var.acc[acc_id].regc, tdata );
Benny Prijono56315612006-07-18 14:39:40 +0000545 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000546
547 if (status != PJ_SUCCESS) {
548 pjsua_perror(THIS_FILE, "Unable to create/send REGISTER",
549 status);
550 } else {
551 PJ_LOG(3,(THIS_FILE, "%s sent",
552 (renew? "Registration" : "Unregistration")));
553 }
554
555on_return:
556 PJSUA_UNLOCK();
557 return status;
558}
559
560
561/*
562 * Get account information.
563 */
564PJ_DEF(pj_status_t) pjsua_acc_get_info( pjsua_acc_id acc_id,
565 pjsua_acc_info *info)
566{
567 pjsua_acc *acc = &pjsua_var.acc[acc_id];
568 pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
569
570 PJ_ASSERT_RETURN(info != NULL, PJ_EINVAL);
571
Benny Prijonoac623b32006-07-03 15:19:31 +0000572 pj_bzero(info, sizeof(pjsua_acc_info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000573
574 PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
575 PJ_EINVAL);
576 PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
577
578 PJSUA_LOCK();
579
580 if (pjsua_var.acc[acc_id].valid == PJ_FALSE) {
581 PJSUA_UNLOCK();
582 return PJ_EINVALIDOP;
583 }
584
585 info->id = acc_id;
586 info->is_default = (pjsua_var.default_acc == acc_id);
587 info->acc_uri = acc_cfg->id;
588 info->has_registration = (acc->cfg.reg_uri.slen > 0);
589 info->online_status = acc->online_status;
590
591 if (acc->reg_last_err) {
592 info->status = acc->reg_last_err;
593 pj_strerror(acc->reg_last_err, info->buf_, sizeof(info->buf_));
594 info->status_text = pj_str(info->buf_);
595 } else if (acc->reg_last_code) {
Benny Prijono6f979412006-06-15 12:25:46 +0000596 if (info->has_registration) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000597 info->status = acc->reg_last_code;
598 info->status_text = *pjsip_get_status_text(acc->reg_last_code);
599 } else {
600 info->status = 0;
601 info->status_text = pj_str("not registered");
602 }
603 } else if (acc->cfg.reg_uri.slen) {
604 info->status = 100;
605 info->status_text = pj_str("In Progress");
606 } else {
607 info->status = 0;
608 info->status_text = pj_str("does not register");
609 }
610
611 if (acc->regc) {
612 pjsip_regc_info regc_info;
613 pjsip_regc_get_info(acc->regc, &regc_info);
614 info->expires = regc_info.next_reg;
615 } else {
616 info->expires = -1;
617 }
618
619 PJSUA_UNLOCK();
620
621 return PJ_SUCCESS;
622
623}
624
625
626/*
627 * Enum accounts all account ids.
628 */
629PJ_DEF(pj_status_t) pjsua_enum_accs(pjsua_acc_id ids[],
630 unsigned *count )
631{
632 unsigned i, c;
633
634 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
635
636 PJSUA_LOCK();
637
638 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
639 if (!pjsua_var.acc[i].valid)
640 continue;
641 ids[c] = i;
642 ++c;
643 }
644
645 *count = c;
646
647 PJSUA_UNLOCK();
648
649 return PJ_SUCCESS;
650}
651
652
653/*
654 * Enum accounts info.
655 */
656PJ_DEF(pj_status_t) pjsua_acc_enum_info( pjsua_acc_info info[],
657 unsigned *count )
658{
659 unsigned i, c;
660
661 PJ_ASSERT_RETURN(info && *count, PJ_EINVAL);
662
663 PJSUA_LOCK();
664
665 for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
666 if (!pjsua_var.acc[i].valid)
667 continue;
668
669 pjsua_acc_get_info(i, &info[c]);
670 ++c;
671 }
672
673 *count = c;
674
675 PJSUA_UNLOCK();
676
677 return PJ_SUCCESS;
678}
679
680
681/*
682 * This is an internal function to find the most appropriate account to
683 * used to reach to the specified URL.
684 */
685PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_outgoing(const pj_str_t *url)
686{
687 pj_str_t tmp;
688 pjsip_uri *uri;
689 pjsip_sip_uri *sip_uri;
690 unsigned acc_id;
691
692 PJSUA_LOCK();
693
694 PJ_TODO(dont_use_pjsua_pool);
695
696 pj_strdup_with_null(pjsua_var.pool, &tmp, url);
697
698 uri = pjsip_parse_uri(pjsua_var.pool, tmp.ptr, tmp.slen, 0);
699 if (!uri) {
700 acc_id = pjsua_var.default_acc;
701 goto on_return;
702 }
703
704 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
705 !PJSIP_URI_SCHEME_IS_SIPS(uri))
706 {
707 /* Return the first account with proxy */
708 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
709 if (!pjsua_var.acc[acc_id].valid)
710 continue;
711 if (!pj_list_empty(&pjsua_var.acc[acc_id].route_set))
712 break;
713 }
714
715 if (acc_id != PJ_ARRAY_SIZE(pjsua_var.acc)) {
716 /* Found rather matching account */
717 goto on_return;
718 }
719
720 /* Not found, use default account */
721 acc_id = pjsua_var.default_acc;
722 goto on_return;
723 }
724
725 sip_uri = pjsip_uri_get_uri(uri);
726
Benny Prijonob4a17c92006-07-10 14:40:21 +0000727 /* Find matching domain AND port */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000728 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
729 if (!pjsua_var.acc[acc_id].valid)
730 continue;
Benny Prijonob4a17c92006-07-10 14:40:21 +0000731 if (pj_stricmp(&pjsua_var.acc[acc_id].srv_domain, &sip_uri->host)==0 &&
732 pjsua_var.acc[acc_id].srv_port == sip_uri->port)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000733 break;
734 }
735
Benny Prijonob4a17c92006-07-10 14:40:21 +0000736 /* If no match, try to match the domain part only */
737 if (acc_id == PJ_ARRAY_SIZE(pjsua_var.acc)) {
738 /* Just use default account */
739 for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {
740 if (!pjsua_var.acc[acc_id].valid)
741 continue;
742 if (pj_stricmp(&pjsua_var.acc[acc_id].srv_domain, &sip_uri->host)==0)
743 break;
744 }
745 }
746
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000747 if (acc_id == PJ_ARRAY_SIZE(pjsua_var.acc)) {
748 /* Just use default account */
749 acc_id = pjsua_var.default_acc;
750 }
751
752on_return:
753 PJSUA_UNLOCK();
754
755 return acc_id;
756}
757
758
759/*
760 * This is an internal function to find the most appropriate account to be
761 * used to handle incoming calls.
762 */
763PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata)
764{
765 pjsip_uri *uri;
766 pjsip_sip_uri *sip_uri;
767 unsigned acc_id;
768
769 uri = rdata->msg_info.to->uri;
770
771 /* Just return default account if To URI is not SIP: */
772 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
773 !PJSIP_URI_SCHEME_IS_SIPS(uri))
774 {
775 return pjsua_var.default_acc;
776 }
777
778
779 PJSUA_LOCK();
780
781 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
782
783 /* Find account which has matching username and domain. */
784 for (acc_id=0; acc_id < pjsua_var.acc_cnt; ++acc_id) {
785
786 pjsua_acc *acc = &pjsua_var.acc[acc_id];
787
788 if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
Benny Prijonob4a17c92006-07-10 14:40:21 +0000789 pj_stricmp(&acc->srv_domain, &sip_uri->host)==0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000790 {
791 /* Match ! */
792 PJSUA_UNLOCK();
793 return acc_id;
794 }
795 }
796
797 /* No matching, try match domain part only. */
798 for (acc_id=0; acc_id < pjsua_var.acc_cnt; ++acc_id) {
799
800 pjsua_acc *acc = &pjsua_var.acc[acc_id];
801
Benny Prijonob4a17c92006-07-10 14:40:21 +0000802 if (pj_stricmp(&acc->srv_domain, &sip_uri->host)==0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000803 /* Match ! */
804 PJSUA_UNLOCK();
805 return acc_id;
806 }
807 }
808
809 /* Still no match, use default account */
810 PJSUA_UNLOCK();
811 return pjsua_var.default_acc;
812}
813
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000814
815PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
816 pj_str_t *contact,
817 pjsua_acc_id acc_id,
818 const pj_str_t *suri)
819{
820 pjsua_acc *acc;
821 pjsip_sip_uri *sip_uri;
822 pj_status_t status;
823 pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
824 pj_str_t local_addr;
825 unsigned flag;
826 int secure;
827 int local_port;
828
829 acc = &pjsua_var.acc[acc_id];
830
831 /* If route-set is configured for the account, then URI is the
832 * first entry of the route-set.
833 */
834 if (!pj_list_empty(&acc->route_set)) {
835 sip_uri = (pjsip_sip_uri*) acc->route_set.next->name_addr.uri;
836 } else {
837 pj_str_t tmp;
838 pjsip_uri *uri;
839
840 pj_strdup_with_null(pool, &tmp, suri);
841
842 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
843 if (uri == NULL)
844 return PJSIP_EINVALIDURI;
845
846 /* For non-SIP scheme, route set should be configured */
847 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
848 return PJSIP_EINVALIDREQURI;
849
850 sip_uri = (pjsip_sip_uri*)uri;
851 }
852
853 /* Get transport type of the URI */
854 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri))
855 tp_type = PJSIP_TRANSPORT_TLS;
856 else if (sip_uri->transport_param.slen == 0) {
857 tp_type = PJSIP_TRANSPORT_UDP;
858 } else
859 tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
860
861 if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
862 return PJSIP_EUNSUPTRANSPORT;
863
864 flag = pjsip_transport_get_flag_from_type(tp_type);
865 secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
866
867 /* Get local address suitable to send request from */
868 status = pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
869 pool, tp_type, &local_addr, &local_port);
870 if (status != PJ_SUCCESS)
871 return status;
872
873 /* Create the contact header */
874 contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
875 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
876 "%.*s%s<%s:%.*s%s%.*s:%d;transport=%s>",
877 (int)acc->display.slen,
878 acc->display.ptr,
879 (acc->display.slen?" " : ""),
880 (secure ? "sips" : "sip"),
881 (int)acc->user_part.slen,
882 acc->user_part.ptr,
883 (acc->user_part.slen?"@":""),
884 (int)local_addr.slen,
885 local_addr.ptr,
886 local_port,
887 pjsip_transport_get_type_name(tp_type));
888
889 return PJ_SUCCESS;
890}
891
892
893
894PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool,
895 pj_str_t *contact,
896 pjsua_acc_id acc_id,
897 pjsip_rx_data *rdata )
898{
899 /*
900 * Section 12.1.1, paragraph about using SIPS URI in Contact.
901 * If the request that initiated the dialog contained a SIPS URI
902 * in the Request-URI or in the top Record-Route header field value,
903 * if there was any, or the Contact header field if there was no
904 * Record-Route header field, the Contact header field in the response
905 * MUST be a SIPS URI.
906 */
907 pjsua_acc *acc;
908 pjsip_sip_uri *sip_uri;
909 pj_status_t status;
910 pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
911 pj_str_t local_addr;
912 unsigned flag;
913 int secure;
914 int local_port;
915
916 acc = &pjsua_var.acc[acc_id];
917
918 /* If Record-Route is present, then URI is the top Record-Route. */
919 if (rdata->msg_info.record_route) {
920 sip_uri = (pjsip_sip_uri*) rdata->msg_info.record_route->name_addr.uri;
921 } else {
922 pjsip_contact_hdr *h_contact;
923 pjsip_uri *uri = NULL;
924
925 /* Otherwise URI is Contact URI */
926 h_contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT,
927 NULL);
928 if (h_contact)
929 uri = pjsip_uri_get_uri(h_contact->uri);
930
931
932 /* Or if Contact URI is not present, take the remote URI from
933 * the From URI.
934 */
935 if (uri == NULL)
936 uri = pjsip_uri_get_uri(rdata->msg_info.from->uri);
937
938
939 /* Can only do sip/sips scheme at present. */
940 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
941 return PJSIP_EINVALIDREQURI;
942
943 sip_uri = (pjsip_sip_uri*)uri;
944 }
945
946 /* Get transport type of the URI */
947 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri))
948 tp_type = PJSIP_TRANSPORT_TLS;
949 else if (sip_uri->transport_param.slen == 0) {
950 tp_type = PJSIP_TRANSPORT_UDP;
951 } else
952 tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
953
954 if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
955 return PJSIP_EUNSUPTRANSPORT;
956
957 flag = pjsip_transport_get_flag_from_type(tp_type);
958 secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
959
960 /* Get local address suitable to send request from */
961 status = pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
962 pool, tp_type, &local_addr, &local_port);
963 if (status != PJ_SUCCESS)
964 return status;
965
966 /* Create the contact header */
967 contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
968 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
969 "%.*s%s<%s:%.*s%s%.*s:%d;transport=%s>",
970 (int)acc->display.slen,
971 acc->display.ptr,
972 (acc->display.slen?" " : ""),
973 (secure ? "sips" : "sip"),
974 (int)acc->user_part.slen,
975 acc->user_part.ptr,
976 (acc->user_part.slen?"@":""),
977 (int)local_addr.slen,
978 local_addr.ptr,
979 local_port,
980 pjsip_transport_get_type_name(tp_type));
981
982 return PJ_SUCCESS;
983}
984
985
986