blob: 234a5943ce342dccd7aff9b047d4af749949d851 [file] [log] [blame]
Benny Prijonof3195072006-02-14 21:15:30 +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 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
20#include <pjsua-lib/getopt.h>
Benny Prijonof3195072006-02-14 21:15:30 +000021
Benny Prijonob0808372006-03-02 21:18:58 +000022/*
23 * pjsua_settings.c
24 *
25 * Anything to do with configuration and state dump.
26 */
27
Benny Prijonof3195072006-02-14 21:15:30 +000028#define THIS_FILE "pjsua_opt.c"
29
30
31const char *pjsua_inv_state_names[] =
32{
33 "NULL ",
34 "CALLING ",
35 "INCOMING ",
36 "EARLY ",
37 "CONNECTING",
38 "CONFIRMED ",
39 "DISCONNCTD",
40 "TERMINATED",
41};
42
43
44
45/* Show usage */
46static void usage(void)
47{
Benny Prijono1c2bf462006-03-05 11:54:02 +000048 puts ("Usage:");
49 puts (" pjsua [options]");
50 puts ("");
51 puts ("General options:");
52 puts (" --help Display this help screen");
53 puts (" --version Display version info");
54 puts ("");
55 puts ("Logging options:");
56 puts (" --config-file=file Read the config/arguments from file.");
57 puts (" --log-file=fname Log to filename (default stderr)");
58 puts (" --log-level=N Set log max level to N (0(none) to 6(trace)) (default=5)");
59 puts (" --app-log-level=N Set log max level for stdout display (default=4)");
60 puts ("");
61 puts ("SIP Account options:");
62 puts (" --id=url Set the URL of local ID (used in From header)");
63 puts (" --contact=url Override the Contact information");
64 puts (" --proxy=url Set the URL of proxy server");
65 puts ("");
66 puts ("SIP Account Registration Options:");
67 puts (" --registrar=url Set the URL of registrar server");
68 puts (" --reg-timeout=secs Set registration interval to secs (default 3600)");
69 puts ("");
70 puts ("SIP Account Control:");
71 puts (" --next-account Add more account");
72 puts ("");
73 puts ("Authentication options:");
74 puts (" --realm=string Set realm");
75 puts (" --username=string Set authentication username");
76 puts (" --password=string Set authentication password");
77 puts (" --next-cred Add more credential");
78 puts ("");
79 puts ("Transport Options:");
80 puts (" --local-port=port Set TCP/UDP port");
81 puts (" --outbound=url Set the URL of outbound proxy server");
82 puts (" --use-stun1=host[:port]");
83 puts (" --use-stun2=host[:port] Resolve local IP with the specified STUN servers");
84 puts ("");
85 puts ("Media Options:");
86 puts (" --wb Enable wideband codecs and set clock-rate to 16KHz");
87 puts (" --uwb Enable ultra-wideband codecs and set clock-rate to 32KHz");
88 puts (" --null-audio Use NULL audio device");
89 puts (" --play-file=file Play WAV file in conference bridge");
90 puts (" --auto-play Automatically play the file (to incoming calls only)");
91 puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
92 puts (" --auto-conf Automatically put incoming calls to conference");
93 puts (" --rtp-port=N Base port to try for RTP (default=4000)");
94 puts (" --add-codec=name Specify alternate codec order");
Benny Prijonoccb03fa2006-03-06 13:35:47 +000095 puts (" --complexity=N Specify encoding complexity (0-10, default=none(-1))");
Benny Prijono1c2bf462006-03-05 11:54:02 +000096 puts (" --quality=N Specify encoding quality (0-10, default=4)");
97 puts ("");
98 puts ("Buddy List (can be more than one):");
99 puts (" --add-buddy url Add the specified URL to the buddy list.");
100 puts ("");
101 puts ("User Agent options:");
102 puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
103 puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
104 puts ("");
Benny Prijonof3195072006-02-14 21:15:30 +0000105 fflush(stdout);
106}
107
108
109
110/*
111 * Verify that valid SIP url is given.
112 */
113pj_status_t pjsua_verify_sip_url(const char *c_url)
114{
115 pjsip_uri *p;
116 pj_pool_t *pool;
117 char *url;
118 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
119
120 if (!len) return -1;
121
122 pool = pj_pool_create(&pjsua.cp.factory, "check%p", 1024, 0, NULL);
123 if (!pool) return -1;
124
125 url = pj_pool_alloc(pool, len+1);
126 pj_ansi_strcpy(url, c_url);
127
128 p = pjsip_parse_uri(pool, url, len, 0);
129 if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
130 p = NULL;
131
132 pj_pool_release(pool);
133 return p ? 0 : -1;
134}
135
136
137/*
138 * Read command arguments from config file.
139 */
140static int read_config_file(pj_pool_t *pool, const char *filename,
141 int *app_argc, char ***app_argv)
142{
143 int i;
144 FILE *fhnd;
145 char line[200];
146 int argc = 0;
147 char **argv;
148 enum { MAX_ARGS = 64 };
149
150 /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
151 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
152 argv[argc++] = *app_argv[0];
153
154 /* Open config file. */
155 fhnd = fopen(filename, "rt");
156 if (!fhnd) {
157 printf("Unable to open config file %s\n", filename);
158 fflush(stdout);
159 return -1;
160 }
161
162 /* Scan tokens in the file. */
163 while (argc < MAX_ARGS && !feof(fhnd)) {
164 char *token, *p = line;
165
166 if (fgets(line, sizeof(line), fhnd) == NULL) break;
167
168 for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS;
169 token = strtok(NULL, " \t\r\n"))
170 {
171 int token_len;
172
173 if (!token) break;
174 if (*token == '#') break;
175
176 token_len = strlen(token);
177 if (!token_len)
178 continue;
179 argv[argc] = pj_pool_alloc(pool, token_len+1);
180 pj_memcpy(argv[argc], token, token_len+1);
181 ++argc;
182 }
183 }
184
185 /* Copy arguments from command line */
186 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
187 argv[argc++] = (*app_argv)[i];
188
189 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
190 printf("Too many arguments specified in cmd line/config file\n");
191 fflush(stdout);
192 fclose(fhnd);
193 return -1;
194 }
195
196 fclose(fhnd);
197
198 /* Assign the new command line back to the original command line. */
199 *app_argc = argc;
200 *app_argv = argv;
201 return 0;
202
203}
204
Benny Prijono4f9f64e2006-02-27 00:00:30 +0000205static int my_atoi(const char *cs)
206{
207 pj_str_t s;
208 return pj_strtoul(pj_cstr(&s, cs));
209}
210
Benny Prijonof3195072006-02-14 21:15:30 +0000211
212/* Parse arguments. */
213pj_status_t pjsua_parse_args(int argc, char *argv[])
214{
215 int c;
216 int option_index;
217 enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
218 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
219 OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR,
220 OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT,
221 OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
222 OPT_USE_STUN1, OPT_USE_STUN2,
223 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000224 OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
225 OPT_AUTO_CONF,
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000226 OPT_PLAY_FILE, OPT_WB, OPT_UWB, OPT_RTP_PORT, OPT_ADD_CODEC,
Benny Prijono1c2bf462006-03-05 11:54:02 +0000227 OPT_COMPLEXITY, OPT_QUALITY,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000228 OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
229 };
Benny Prijonof9c668f2006-03-06 15:14:59 +0000230 struct pj_getopt_option long_options[] = {
Benny Prijonof3195072006-02-14 21:15:30 +0000231 { "config-file",1, 0, OPT_CONFIG_FILE},
232 { "log-file", 1, 0, OPT_LOG_FILE},
233 { "log-level", 1, 0, OPT_LOG_LEVEL},
234 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
235 { "help", 0, 0, OPT_HELP},
236 { "version", 0, 0, OPT_VERSION},
Benny Prijono08e0d062006-03-04 14:52:44 +0000237 { "wb", 0, 0, OPT_WB},
238 { "uwb", 0, 0, OPT_UWB},
Benny Prijonof3195072006-02-14 21:15:30 +0000239 { "null-audio", 0, 0, OPT_NULL_AUDIO},
240 { "local-port", 1, 0, OPT_LOCAL_PORT},
241 { "proxy", 1, 0, OPT_PROXY},
242 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
243 { "registrar", 1, 0, OPT_REGISTRAR},
244 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
245 { "id", 1, 0, OPT_ID},
246 { "contact", 1, 0, OPT_CONTACT},
247 { "realm", 1, 0, OPT_REALM},
248 { "username", 1, 0, OPT_USERNAME},
249 { "password", 1, 0, OPT_PASSWORD},
250 { "use-stun1", 1, 0, OPT_USE_STUN1},
251 { "use-stun2", 1, 0, OPT_USE_STUN2},
252 { "add-buddy", 1, 0, OPT_ADD_BUDDY},
253 { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
254 { "no-presence", 0, 0, OPT_NO_PRESENCE},
255 { "auto-answer",1, 0, OPT_AUTO_ANSWER},
256 { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
Benny Prijonoa91a0032006-02-26 21:23:45 +0000257 { "auto-play", 0, 0, OPT_AUTO_PLAY},
258 { "auto-loop", 0, 0, OPT_AUTO_LOOP},
259 { "auto-conf", 0, 0, OPT_AUTO_CONF},
260 { "play-file", 1, 0, OPT_PLAY_FILE},
Benny Prijonocbf37402006-03-01 19:29:10 +0000261 { "rtp-port", 1, 0, OPT_RTP_PORT},
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000262 { "add-codec", 1, 0, OPT_ADD_CODEC},
Benny Prijono1c2bf462006-03-05 11:54:02 +0000263 { "complexity", 1, 0, OPT_COMPLEXITY},
264 { "quality", 1, 0, OPT_QUALITY},
Benny Prijonoa91a0032006-02-26 21:23:45 +0000265 { "next-account",0,0, OPT_NEXT_ACCOUNT},
266 { "next-cred", 0, 0, OPT_NEXT_CRED},
267 { "max-calls", 1, 0, OPT_MAX_CALLS},
Benny Prijonof3195072006-02-14 21:15:30 +0000268 { NULL, 0, 0, 0}
269 };
270 pj_status_t status;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000271 pjsua_acc *cur_acc;
272 pjsip_cred_info *cur_cred;
Benny Prijonof3195072006-02-14 21:15:30 +0000273 char *config_file = NULL;
274
Benny Prijonof9c668f2006-03-06 15:14:59 +0000275 /* Run pj_getopt once to see if user specifies config file to read. */
276 while ((c=pj_getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
Benny Prijonof3195072006-02-14 21:15:30 +0000277 switch (c) {
278 case OPT_CONFIG_FILE:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000279 config_file = pj_optarg;
Benny Prijonof3195072006-02-14 21:15:30 +0000280 break;
281 }
282 if (config_file)
283 break;
284 }
285
286 if (config_file) {
287 status = read_config_file(pjsua.pool, config_file, &argc, &argv);
288 if (status != 0)
289 return status;
290 }
291
292
Benny Prijonoa91a0032006-02-26 21:23:45 +0000293 cur_acc = &pjsua.acc[0];
294 cur_cred = &pjsua.cred_info[0];
295
296
Benny Prijonof9c668f2006-03-06 15:14:59 +0000297 /* Reinitialize and re-run pj_getopt again, possibly with new arguments
Benny Prijonof3195072006-02-14 21:15:30 +0000298 * read from config file.
299 */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000300 pj_optind = 0;
301 while((c=pj_getopt_long(argc, argv, "", long_options, &option_index))!=-1) {
Benny Prijonof3195072006-02-14 21:15:30 +0000302 char *p;
303 pj_str_t tmp;
304 long lval;
305
306 switch (c) {
307
308 case OPT_LOG_FILE:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000309 pjsua.log_filename = pj_optarg;
Benny Prijonof3195072006-02-14 21:15:30 +0000310 break;
311
312 case OPT_LOG_LEVEL:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000313 c = pj_strtoul(pj_cstr(&tmp, pj_optarg));
Benny Prijonof3195072006-02-14 21:15:30 +0000314 if (c < 0 || c > 6) {
315 printf("Error: expecting integer value 0-6 for --log-level\n");
316 return PJ_EINVAL;
317 }
318 pj_log_set_level( c );
319 break;
320
321 case OPT_APP_LOG_LEVEL:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000322 pjsua.app_log_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
Benny Prijonof3195072006-02-14 21:15:30 +0000323 if (pjsua.app_log_level < 0 || pjsua.app_log_level > 6) {
324 printf("Error: expecting integer value 0-6 for --app-log-level\n");
325 return PJ_EINVAL;
326 }
327 break;
328
329 case OPT_HELP:
330 usage();
331 return PJ_EINVAL;
332
333 case OPT_VERSION: /* version */
334 pj_dump_config();
335 return PJ_EINVAL;
336
337 case OPT_NULL_AUDIO:
338 pjsua.null_audio = 1;
339 break;
340
Benny Prijono08e0d062006-03-04 14:52:44 +0000341 case OPT_WB:
342 pjsua.clock_rate = 16000;
343 break;
344
345 case OPT_UWB:
346 pjsua.clock_rate = 32000;
347 break;
348
Benny Prijonof3195072006-02-14 21:15:30 +0000349 case OPT_LOCAL_PORT: /* local-port */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000350 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
Benny Prijonof3195072006-02-14 21:15:30 +0000351 if (lval < 1 || lval > 65535) {
352 printf("Error: expecting integer value for --local-port\n");
353 return PJ_EINVAL;
354 }
355 pjsua.sip_port = (pj_uint16_t)lval;
356 break;
357
358 case OPT_PROXY: /* proxy */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000359 if (pjsua_verify_sip_url(pj_optarg) != 0) {
360 printf("Error: invalid SIP URL '%s' in proxy argument\n", pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000361 return PJ_EINVAL;
362 }
Benny Prijonof9c668f2006-03-06 15:14:59 +0000363 cur_acc->proxy = pj_str(pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000364 break;
365
366 case OPT_OUTBOUND_PROXY: /* outbound proxy */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000367 if (pjsua_verify_sip_url(pj_optarg) != 0) {
368 printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000369 return PJ_EINVAL;
370 }
Benny Prijonof9c668f2006-03-06 15:14:59 +0000371 pjsua.outbound_proxy = pj_str(pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000372 break;
373
374 case OPT_REGISTRAR: /* registrar */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000375 if (pjsua_verify_sip_url(pj_optarg) != 0) {
376 printf("Error: invalid SIP URL '%s' in registrar argument\n", pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000377 return PJ_EINVAL;
378 }
Benny Prijonof9c668f2006-03-06 15:14:59 +0000379 cur_acc->reg_uri = pj_str(pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000380 break;
381
382 case OPT_REG_TIMEOUT: /* reg-timeout */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000383 cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,pj_optarg));
Benny Prijonoa91a0032006-02-26 21:23:45 +0000384 if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
Benny Prijonof3195072006-02-14 21:15:30 +0000385 printf("Error: invalid value for --reg-timeout (expecting 1-3600)\n");
386 return PJ_EINVAL;
387 }
388 break;
389
390 case OPT_ID: /* id */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000391 if (pjsua_verify_sip_url(pj_optarg) != 0) {
392 printf("Error: invalid SIP URL '%s' in local id argument\n", pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000393 return PJ_EINVAL;
394 }
Benny Prijonof9c668f2006-03-06 15:14:59 +0000395 cur_acc->local_uri = pj_str(pj_optarg);
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000396 pjsua.has_acc = 1;
Benny Prijonof3195072006-02-14 21:15:30 +0000397 break;
398
399 case OPT_CONTACT: /* contact */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000400 if (pjsua_verify_sip_url(pj_optarg) != 0) {
401 printf("Error: invalid SIP URL '%s' in contact argument\n", pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000402 return PJ_EINVAL;
403 }
Benny Prijonof9c668f2006-03-06 15:14:59 +0000404 cur_acc->contact_uri = pj_str(pj_optarg);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000405 break;
406
407 case OPT_NEXT_ACCOUNT: /* Add more account. */
408 pjsua.acc_cnt++;
409 cur_acc = &pjsua.acc[pjsua.acc_cnt - 1];
Benny Prijonof3195072006-02-14 21:15:30 +0000410 break;
411
412 case OPT_USERNAME: /* Default authentication user */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000413 if (pjsua.cred_count==0) pjsua.cred_count=1;
Benny Prijonof9c668f2006-03-06 15:14:59 +0000414 cur_cred->username = pj_str(pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000415 break;
416
417 case OPT_REALM: /* Default authentication realm. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000418 if (pjsua.cred_count==0) pjsua.cred_count=1;
Benny Prijonof9c668f2006-03-06 15:14:59 +0000419 cur_cred->realm = pj_str(pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000420 break;
421
422 case OPT_PASSWORD: /* authentication password */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000423 if (pjsua.cred_count==0) pjsua.cred_count=1;
424 cur_cred->data_type = 0;
Benny Prijonof9c668f2006-03-06 15:14:59 +0000425 cur_cred->data = pj_str(pj_optarg);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000426 break;
427
428 case OPT_NEXT_CRED: /* Next credential */
429 pjsua.cred_count++;
430 cur_cred = &pjsua.cred_info[pjsua.cred_count - 1];
Benny Prijonof3195072006-02-14 21:15:30 +0000431 break;
432
433 case OPT_USE_STUN1: /* STUN server 1 */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000434 p = pj_ansi_strchr(pj_optarg, ':');
Benny Prijonof3195072006-02-14 21:15:30 +0000435 if (p) {
436 *p = '\0';
Benny Prijonof9c668f2006-03-06 15:14:59 +0000437 pjsua.stun_srv1 = pj_str(pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000438 pjsua.stun_port1 = pj_strtoul(pj_cstr(&tmp, p+1));
439 if (pjsua.stun_port1 < 1 || pjsua.stun_port1 > 65535) {
440 printf("Error: expecting port number with option --use-stun1\n");
441 return PJ_EINVAL;
442 }
443 } else {
444 pjsua.stun_port1 = 3478;
Benny Prijonof9c668f2006-03-06 15:14:59 +0000445 pjsua.stun_srv1 = pj_str(pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000446 }
447 break;
448
449 case OPT_USE_STUN2: /* STUN server 2 */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000450 p = pj_ansi_strchr(pj_optarg, ':');
Benny Prijonof3195072006-02-14 21:15:30 +0000451 if (p) {
452 *p = '\0';
Benny Prijonof9c668f2006-03-06 15:14:59 +0000453 pjsua.stun_srv2 = pj_str(pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000454 pjsua.stun_port2 = pj_strtoul(pj_cstr(&tmp,p+1));
455 if (pjsua.stun_port2 < 1 || pjsua.stun_port2 > 65535) {
456 printf("Error: expecting port number with option --use-stun2\n");
457 return PJ_EINVAL;
458 }
459 } else {
460 pjsua.stun_port2 = 3478;
Benny Prijonof9c668f2006-03-06 15:14:59 +0000461 pjsua.stun_srv2 = pj_str(pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000462 }
463 break;
464
465 case OPT_ADD_BUDDY: /* Add to buddy list. */
Benny Prijonof9c668f2006-03-06 15:14:59 +0000466 if (pjsua_verify_sip_url(pj_optarg) != 0) {
467 printf("Error: invalid URL '%s' in --add-buddy option\n", pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000468 return -1;
469 }
470 if (pjsua.buddy_cnt == PJSUA_MAX_BUDDIES) {
471 printf("Error: too many buddies in buddy list.\n");
472 return -1;
473 }
Benny Prijonof9c668f2006-03-06 15:14:59 +0000474 pjsua.buddies[pjsua.buddy_cnt++].uri = pj_str(pj_optarg);
Benny Prijonof3195072006-02-14 21:15:30 +0000475 break;
Benny Prijono39879152006-02-23 02:09:10 +0000476
Benny Prijono64f851e2006-02-23 13:49:28 +0000477 case OPT_AUTO_PLAY:
Benny Prijonoa91a0032006-02-26 21:23:45 +0000478 pjsua.auto_play = 1;
479 break;
480
481 case OPT_AUTO_LOOP:
482 pjsua.auto_loop = 1;
483 break;
484
485 case OPT_AUTO_CONF:
486 pjsua.auto_conf = 1;
487 break;
488
489 case OPT_PLAY_FILE:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000490 pjsua.wav_file = pj_optarg;
Benny Prijono39879152006-02-23 02:09:10 +0000491 break;
Benny Prijono64f851e2006-02-23 13:49:28 +0000492
Benny Prijonocbf37402006-03-01 19:29:10 +0000493 case OPT_RTP_PORT:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000494 pjsua.start_rtp_port = my_atoi(pj_optarg);
Benny Prijonocbf37402006-03-01 19:29:10 +0000495 if (pjsua.start_rtp_port < 1 || pjsua.start_rtp_port > 65535) {
496 PJ_LOG(1,(THIS_FILE,
497 "Error: rtp-port argument value (expecting 1-65535"));
498 return -1;
499 }
500
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000501 case OPT_ADD_CODEC:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000502 pjsua.codec_arg[pjsua.codec_cnt++] = pj_str(pj_optarg);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000503 break;
504
Benny Prijono1c2bf462006-03-05 11:54:02 +0000505 case OPT_COMPLEXITY:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000506 pjsua.complexity = my_atoi(pj_optarg);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000507 if (pjsua.complexity < 0 || pjsua.complexity > 10) {
508 PJ_LOG(1,(THIS_FILE,
509 "Error: invalid --complexity (expecting 0-10"));
510 return -1;
511 }
512 break;
513
514 case OPT_QUALITY:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000515 pjsua.quality = my_atoi(pj_optarg);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000516 if (pjsua.quality < 0 || pjsua.quality > 10) {
517 PJ_LOG(1,(THIS_FILE,
518 "Error: invalid --quality (expecting 0-10"));
519 return -1;
520 }
521 break;
522
Benny Prijono64f851e2006-02-23 13:49:28 +0000523 case OPT_AUTO_ANSWER:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000524 pjsua.auto_answer = my_atoi(pj_optarg);
Benny Prijono64f851e2006-02-23 13:49:28 +0000525 if (pjsua.auto_answer < 100 || pjsua.auto_answer > 699) {
Benny Prijonocbf37402006-03-01 19:29:10 +0000526 PJ_LOG(1,(THIS_FILE,
527 "Error: invalid code in --auto-answer (expecting 100-699"));
Benny Prijono64f851e2006-02-23 13:49:28 +0000528 return -1;
529 }
530 break;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000531
532 case OPT_MAX_CALLS:
Benny Prijonof9c668f2006-03-06 15:14:59 +0000533 pjsua.max_calls = my_atoi(pj_optarg);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000534 if (pjsua.max_calls < 1 || pjsua.max_calls > 255) {
Benny Prijonocbf37402006-03-01 19:29:10 +0000535 PJ_LOG(1,(THIS_FILE,"Too many calls for max-calls (1-255)"));
Benny Prijonoa91a0032006-02-26 21:23:45 +0000536 return -1;
537 }
538 break;
Benny Prijonof3195072006-02-14 21:15:30 +0000539 }
540 }
541
Benny Prijonof9c668f2006-03-06 15:14:59 +0000542 if (pj_optind != argc) {
543 printf("Error: unknown options %s\n", argv[pj_optind]);
Benny Prijonof3195072006-02-14 21:15:30 +0000544 return PJ_EINVAL;
545 }
546
Benny Prijonof3195072006-02-14 21:15:30 +0000547 return PJ_SUCCESS;
548}
549
550
551
Benny Prijonoa91a0032006-02-26 21:23:45 +0000552static void print_call(const char *title,
553 int call_index,
554 char *buf, pj_size_t size)
Benny Prijonof3195072006-02-14 21:15:30 +0000555{
556 int len;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000557 pjsip_inv_session *inv = pjsua.calls[call_index].inv;
Benny Prijonof3195072006-02-14 21:15:30 +0000558 pjsip_dialog *dlg = inv->dlg;
559 char userinfo[128];
560
561 /* Dump invite sesion info. */
562
563 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
564 if (len < 1)
565 pj_ansi_strcpy(userinfo, "<--uri too long-->");
566 else
567 userinfo[len] = '\0';
568
569 len = pj_snprintf(buf, size, "%s[%s] %s",
570 title,
571 pjsua_inv_state_names[inv->state],
572 userinfo);
573 if (len < 1 || len >= (int)size) {
574 pj_ansi_strcpy(buf, "<--uri too long-->");
575 len = 18;
576 } else
577 buf[len] = '\0';
578}
579
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000580static const char *good_number(char *buf, pj_int32_t val)
581{
582 if (val < 1000) {
583 pj_ansi_sprintf(buf, "%d", val);
584 } else if (val < 1000000) {
585 pj_ansi_sprintf(buf, "%d.%dK",
586 val / 1000,
587 (val % 1000) / 100);
588 } else {
589 pj_ansi_sprintf(buf, "%d.%02dM",
590 val / 1000000,
591 (val % 1000000) / 10000);
592 }
593
594 return buf;
595}
596
Benny Prijonof3195072006-02-14 21:15:30 +0000597static void dump_media_session(pjmedia_session *session)
598{
599 unsigned i;
600 pjmedia_session_info info;
601
602 pjmedia_session_get_info(session, &info);
603
604 for (i=0; i<info.stream_cnt; ++i) {
605 pjmedia_stream_stat strm_stat;
606 const char *rem_addr;
607 int rem_port;
608 const char *dir;
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000609 char stxpkt[10], stxoct[10], srxpkt[10], srxoct[10];
Benny Prijonof3195072006-02-14 21:15:30 +0000610
611 pjmedia_session_get_stream_stat(session, i, &strm_stat);
612 rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr);
613 rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port);
614
615 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
616 dir = "sendonly";
617 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
618 dir = "recvonly";
619 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
620 dir = "sendrecv";
621 else
622 dir = "inactive";
623
624
625 PJ_LOG(3,(THIS_FILE,
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000626 "%s#%d %.*s @%dKHz, %s, peer=%s:%d",
Benny Prijonof3195072006-02-14 21:15:30 +0000627 " ",
628 i,
629 info.stream_info[i].fmt.encoding_name.slen,
630 info.stream_info[i].fmt.encoding_name.ptr,
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000631 info.stream_info[i].fmt.sample_rate / 1000,
Benny Prijonof3195072006-02-14 21:15:30 +0000632 dir,
633 rem_addr, rem_port));
634 PJ_LOG(3,(THIS_FILE,
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000635 "%s tx{pt=%d,pkt=%s,oct=%s} rx{pt=%d,pkt=%s,oct=%s}",
636 " ",
637 info.stream_info[i].tx_pt,
638 good_number(stxpkt, strm_stat.enc.pkt),
639 good_number(stxoct, strm_stat.enc.bytes),
640 info.stream_info[i].fmt.pt,
641 good_number(srxpkt, strm_stat.dec.pkt),
642 good_number(srxoct, strm_stat.dec.bytes)));
Benny Prijonof3195072006-02-14 21:15:30 +0000643
644 }
645}
646
647/*
648 * Dump application states.
649 */
Benny Prijono1a174142006-03-01 20:46:13 +0000650void pjsua_dump(pj_bool_t detail)
Benny Prijonof3195072006-02-14 21:15:30 +0000651{
Benny Prijonof3195072006-02-14 21:15:30 +0000652 char buf[128];
653 unsigned old_decor;
654
655 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
656
657 old_decor = pj_log_get_decor();
658 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
659
Benny Prijono1a174142006-03-01 20:46:13 +0000660 pjsip_endpt_dump(pjsua.endpt, detail);
Benny Prijonof3195072006-02-14 21:15:30 +0000661 pjmedia_endpt_dump(pjsua.med_endpt);
Benny Prijono1a174142006-03-01 20:46:13 +0000662 pjsip_tsx_layer_dump(detail);
663 pjsip_ua_dump(detail);
Benny Prijonof3195072006-02-14 21:15:30 +0000664
665
666 /* Dump all invite sessions: */
Benny Prijono1a174142006-03-01 20:46:13 +0000667 if (detail) {
668 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
Benny Prijonof3195072006-02-14 21:15:30 +0000669
Benny Prijono1a174142006-03-01 20:46:13 +0000670 if (pjsua.call_cnt == 0) {
Benny Prijonof3195072006-02-14 21:15:30 +0000671
Benny Prijono1a174142006-03-01 20:46:13 +0000672 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
Benny Prijonof3195072006-02-14 21:15:30 +0000673
Benny Prijono1a174142006-03-01 20:46:13 +0000674 } else {
675 int i;
Benny Prijonof3195072006-02-14 21:15:30 +0000676
Benny Prijono1a174142006-03-01 20:46:13 +0000677 for (i=0; i<pjsua.max_calls; ++i) {
Benny Prijonof3195072006-02-14 21:15:30 +0000678
Benny Prijono1a174142006-03-01 20:46:13 +0000679 if (pjsua.calls[i].inv == NULL)
680 continue;
Benny Prijonof3195072006-02-14 21:15:30 +0000681
Benny Prijono1a174142006-03-01 20:46:13 +0000682 print_call(" ", i, buf, sizeof(buf));
683 PJ_LOG(3,(THIS_FILE, "%s", buf));
Benny Prijonof3195072006-02-14 21:15:30 +0000684
Benny Prijono1a174142006-03-01 20:46:13 +0000685 if (pjsua.calls[i].session)
686 dump_media_session(pjsua.calls[i].session);
687 }
Benny Prijonof3195072006-02-14 21:15:30 +0000688 }
689 }
690
Benny Prijono834aee32006-02-19 01:38:06 +0000691 /* Dump presence status */
Benny Prijono1a174142006-03-01 20:46:13 +0000692 pjsua_pres_dump(detail);
Benny Prijono834aee32006-02-19 01:38:06 +0000693
Benny Prijonof3195072006-02-14 21:15:30 +0000694 pj_log_set_decor(old_decor);
695 PJ_LOG(3,(THIS_FILE, "Dump complete"));
696}
697
698
699/*
700 * Load settings.
701 */
702pj_status_t pjsua_load_settings(const char *filename)
703{
704 int argc = 3;
Benny Prijono834aee32006-02-19 01:38:06 +0000705 char *argv[4] = { "pjsua", "--config-file", NULL, NULL};
Benny Prijonof3195072006-02-14 21:15:30 +0000706
Benny Prijono834aee32006-02-19 01:38:06 +0000707 argv[3] = (char*)filename;
Benny Prijonof3195072006-02-14 21:15:30 +0000708 return pjsua_parse_args(argc, argv);
709}
710
711
712/*
Benny Prijonoa91a0032006-02-26 21:23:45 +0000713 * Save account settings
Benny Prijonof3195072006-02-14 21:15:30 +0000714 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000715static void save_account_settings(int acc_index, pj_str_t *result)
Benny Prijonof3195072006-02-14 21:15:30 +0000716{
Benny Prijonof3195072006-02-14 21:15:30 +0000717 char line[128];
Benny Prijonoa91a0032006-02-26 21:23:45 +0000718 pjsua_acc *acc = &pjsua.acc[acc_index];
Benny Prijonof3195072006-02-14 21:15:30 +0000719
Benny Prijonoa91a0032006-02-26 21:23:45 +0000720
721 pj_ansi_sprintf(line, "#\n# Account %d:\n#\n", acc_index);
722 pj_strcat2(result, line);
Benny Prijonof3195072006-02-14 21:15:30 +0000723
724
725 /* Identity */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000726 if (acc->local_uri.slen) {
Benny Prijonof3195072006-02-14 21:15:30 +0000727 pj_ansi_sprintf(line, "--id %.*s\n",
Benny Prijonoa91a0032006-02-26 21:23:45 +0000728 (int)acc->local_uri.slen,
729 acc->local_uri.ptr);
730 pj_strcat2(result, line);
731 }
732
733 /* Registrar server */
734 if (acc->reg_uri.slen) {
735 pj_ansi_sprintf(line, "--registrar %.*s\n",
736 (int)acc->reg_uri.slen,
737 acc->reg_uri.ptr);
738 pj_strcat2(result, line);
739
740 pj_ansi_sprintf(line, "--reg-timeout %u\n",
741 acc->reg_timeout);
742 pj_strcat2(result, line);
743 }
744
745
746 /* Proxy */
747 if (acc->proxy.slen) {
748 pj_ansi_sprintf(line, "--proxy %.*s\n",
749 (int)acc->proxy.slen,
750 acc->proxy.ptr);
751 pj_strcat2(result, line);
752 }
753}
754
755
756
757/*
758 * Dump settings.
759 */
760int pjsua_dump_settings(char *buf, pj_size_t max)
761{
762 int acc_index;
763 int i;
764 pj_str_t cfg;
765 char line[128];
766
767 cfg.ptr = buf;
768 cfg.slen = 0;
769
770
771 /* Logging. */
772 pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
773 pj_ansi_sprintf(line, "--log-level %d\n",
774 pjsua.log_level);
775 pj_strcat2(&cfg, line);
776
777 pj_ansi_sprintf(line, "--app-log-level %d\n",
778 pjsua.app_log_level);
779 pj_strcat2(&cfg, line);
780
781 if (pjsua.log_filename) {
782 pj_ansi_sprintf(line, "--log-file %s\n",
783 pjsua.log_filename);
Benny Prijonof3195072006-02-14 21:15:30 +0000784 pj_strcat2(&cfg, line);
785 }
786
Benny Prijonoa91a0032006-02-26 21:23:45 +0000787
788 /* Save account settings. */
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000789 if (pjsua.has_acc) {
790 for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
791
792 save_account_settings(acc_index, &cfg);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000793
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000794 if (acc_index < pjsua.acc_cnt-1)
795 pj_strcat2(&cfg, "--next-account\n");
796 }
Benny Prijonoa91a0032006-02-26 21:23:45 +0000797 }
798
Benny Prijonof3195072006-02-14 21:15:30 +0000799 /* Credentials. */
800 for (i=0; i<pjsua.cred_count; ++i) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000801
802 pj_ansi_sprintf(line, "#\n# Credential %d:\n#\n", i);
803 pj_strcat2(&cfg, line);
804
Benny Prijonof3195072006-02-14 21:15:30 +0000805 if (pjsua.cred_info[i].realm.slen) {
806 pj_ansi_sprintf(line, "--realm %.*s\n",
807 (int)pjsua.cred_info[i].realm.slen,
808 pjsua.cred_info[i].realm.ptr);
809 pj_strcat2(&cfg, line);
810 }
811
812 pj_ansi_sprintf(line, "--username %.*s\n",
813 (int)pjsua.cred_info[i].username.slen,
814 pjsua.cred_info[i].username.ptr);
815 pj_strcat2(&cfg, line);
816
817 pj_ansi_sprintf(line, "--password %.*s\n",
818 (int)pjsua.cred_info[i].data.slen,
819 pjsua.cred_info[i].data.ptr);
820 pj_strcat2(&cfg, line);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000821
822 if (i < pjsua.cred_count-1)
823 pj_strcat2(&cfg, "--next-cred\n");
Benny Prijonof3195072006-02-14 21:15:30 +0000824 }
825
Benny Prijonof3195072006-02-14 21:15:30 +0000826
Benny Prijonoa91a0032006-02-26 21:23:45 +0000827 pj_strcat2(&cfg, "#\n# Network settings:\n#\n");
Benny Prijonof3195072006-02-14 21:15:30 +0000828
829 /* Outbound proxy */
830 if (pjsua.outbound_proxy.slen) {
831 pj_ansi_sprintf(line, "--outbound %.*s\n",
832 (int)pjsua.outbound_proxy.slen,
833 pjsua.outbound_proxy.ptr);
834 pj_strcat2(&cfg, line);
835 }
836
Benny Prijonof3195072006-02-14 21:15:30 +0000837
838 /* Transport. */
839 pj_ansi_sprintf(line, "--local-port %d\n", pjsua.sip_port);
840 pj_strcat2(&cfg, line);
841
842
843 /* STUN */
844 if (pjsua.stun_port1) {
845 pj_ansi_sprintf(line, "--use-stun1 %.*s:%d\n",
846 (int)pjsua.stun_srv1.slen,
847 pjsua.stun_srv1.ptr,
848 pjsua.stun_port1);
849 pj_strcat2(&cfg, line);
850 }
851
852 if (pjsua.stun_port2) {
853 pj_ansi_sprintf(line, "--use-stun2 %.*s:%d\n",
854 (int)pjsua.stun_srv2.slen,
855 pjsua.stun_srv2.ptr,
856 pjsua.stun_port2);
857 pj_strcat2(&cfg, line);
858 }
859
860
Benny Prijonoa91a0032006-02-26 21:23:45 +0000861 pj_strcat2(&cfg, "#\n# Media settings:\n#\n");
862
863
864 /* Media */
865 if (pjsua.null_audio)
866 pj_strcat2(&cfg, "--null-audio\n");
867 if (pjsua.auto_play)
868 pj_strcat2(&cfg, "--auto-play\n");
869 if (pjsua.auto_loop)
870 pj_strcat2(&cfg, "--auto-loop\n");
871 if (pjsua.auto_conf)
872 pj_strcat2(&cfg, "--auto-conf\n");
873 if (pjsua.wav_file) {
874 pj_ansi_sprintf(line, "--play-file %s\n",
875 pjsua.wav_file);
876 pj_strcat2(&cfg, line);
877 }
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000878 /* Media clock rate. */
879 if (pjsua.clock_rate >= 32000)
880 pj_strcat2(&cfg, "--uwb\n");
881 else if (pjsua.clock_rate >= 16000)
882 pj_strcat2(&cfg, "--wb\n");
Benny Prijonoa91a0032006-02-26 21:23:45 +0000883
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000884 /* Encoding quality and complexity */
885 pj_ansi_sprintf(line, "--quality %d\n",
886 pjsua.quality);
887 pj_strcat2(&cfg, line);
888 pj_ansi_sprintf(line, "--complexity %d\n",
889 pjsua.complexity);
890 pj_strcat2(&cfg, line);
891
892 /* Start RTP port. */
893 pj_ansi_sprintf(line, "--rtp-port %d\n",
894 pjsua.start_rtp_port);
895 pj_strcat2(&cfg, line);
896
897 /* Add codec. */
898 for (i=0; i<pjsua.codec_cnt; ++i) {
899 pj_ansi_sprintf(line, "--add-codec %s\n",
900 pjsua.codec_arg[i].ptr);
901 pj_strcat2(&cfg, line);
902 }
Benny Prijonoa91a0032006-02-26 21:23:45 +0000903
904 pj_strcat2(&cfg, "#\n# User agent:\n#\n");
905
906 /* Auto-answer. */
907 if (pjsua.auto_answer != 0) {
908 pj_ansi_sprintf(line, "--auto-answer %d\n",
909 pjsua.auto_answer);
910 pj_strcat2(&cfg, line);
911 }
912
913 /* Max calls. */
914 pj_ansi_sprintf(line, "--max-calls %d\n",
915 pjsua.max_calls);
916 pj_strcat2(&cfg, line);
917
918
919 pj_strcat2(&cfg, "#\n# Buddies:\n#\n");
920
Benny Prijonof3195072006-02-14 21:15:30 +0000921 /* Add buddies. */
922 for (i=0; i<pjsua.buddy_cnt; ++i) {
923 pj_ansi_sprintf(line, "--add-buddy %.*s\n",
Benny Prijono834aee32006-02-19 01:38:06 +0000924 (int)pjsua.buddies[i].uri.slen,
925 pjsua.buddies[i].uri.ptr);
Benny Prijonof3195072006-02-14 21:15:30 +0000926 pj_strcat2(&cfg, line);
927 }
928
929
Benny Prijonoa91a0032006-02-26 21:23:45 +0000930 *(cfg.ptr + cfg.slen) = '\0';
931 return cfg.slen;
932}
933
934/*
935 * Save settings.
936 */
937pj_status_t pjsua_save_settings(const char *filename)
938{
939 pj_str_t cfg;
940 pj_pool_t *pool;
941 FILE *fhnd;
942
943 /* Create pool for temporary buffer. */
944 pool = pj_pool_create(&pjsua.cp.factory, "settings", 4000, 0, NULL);
945 if (!pool)
946 return PJ_ENOMEM;
947
948
949 cfg.ptr = pj_pool_alloc(pool, 3800);
950 if (!cfg.ptr) {
951 pj_pool_release(pool);
952 return PJ_EBUG;
953 }
954
955
956 cfg.slen = pjsua_dump_settings(cfg.ptr, 3800);
957 if (cfg.slen < 1) {
958 pj_pool_release(pool);
959 return PJ_ENOMEM;
960 }
961
962
Benny Prijonof3195072006-02-14 21:15:30 +0000963 /* Write to file. */
964 fhnd = fopen(filename, "wt");
965 if (!fhnd) {
966 pj_pool_release(pool);
967 return pj_get_os_error();
968 }
969
970 fwrite(cfg.ptr, cfg.slen, 1, fhnd);
971 fclose(fhnd);
972
973 pj_pool_release(pool);
974 return PJ_SUCCESS;
975}