blob: d5a1c9975bdf9f6e26c1a3fc5ba200e1d7a1754d [file] [log] [blame]
Benny Prijonoe7224612005-11-13 19:40:44 +00001/* $Id$
2 *
3 */
4/*
5 * PJSIP - SIP Stack
6 * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
7 *
8 * Author:
9 * Benny Prijono <bennylp@bulukucing.org>
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27/*
28 * THIS FILE IS INCLUDED BY main.c.
29 * IT WON'T COMPILE BY ITSELF.
30 */
31
32#include "getopt.h"
33#include <stdio.h>
34
35
36/*
37 * Display program usage
38 */
39static void usage()
40{
41 puts("Usage:");
42 puts(" pjsua [options] [sip-url]");
43 puts("");
44 puts(" [sip-url] Default URL to invite.");
45 puts("");
46 puts("General options:");
47 puts(" --config-file=file Read the config/arguments from file.");
48 puts(" --log-file=fname Log to filename (default stderr)");
49 puts(" --log-level=N Set log max level to N (0(none) to 6(trace))");
50 puts(" --app-log-level=N Set log max level for stdout display to N");
51 puts(" --help Display this help screen");
52 puts(" --version Display version info");
53 puts("");
54 puts("Media options:");
55 puts(" --null-audio Use NULL audio device");
56 puts("");
57 puts("User Agent options:");
58 puts(" --auto-answer=sec Auto-answer all incoming calls after sec seconds.");
59 puts(" --auto-hangup=sec Auto-hangup all calls after sec seconds.");
60 puts("");
61 puts("SIP options:");
62 puts(" --local-port=port Set TCP/UDP port");
63 puts(" --id=url Set the URL of local ID (used in From header)");
64 puts(" --contact=url Override the Contact information");
65 puts(" --proxy=url Set the URL of proxy server");
66 puts(" --outbound=url Set the URL of outbound proxy server");
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("Authentication options:");
71 puts(" --realm=string Set realm");
72 puts(" --username=string Set authentication username");
73 puts(" --password=string Set authentication password");
74 puts("");
75 puts("STUN options (all must be specified):");
76 puts(" --use-stun1=host[:port]");
77 puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers");
78 puts("");
79 puts("SIMPLE options (may be specified more than once):");
80 puts(" --add-buddy url Add the specified URL to the buddy list.");
81 puts(" --offer-x-ms-msg Offer \"x-ms-message\" in outgoing INVITE");
82 puts(" --no-presence Do not subscribe presence of buddies");
83 puts("");
84 fflush(stdout);
85}
86
87/* Display keystroke help. */
88static void keystroke_help()
89{
90 int i;
91
92 printf("Advertise status as: %s\n", (global.hide_status ? "Offline" : "Online"));
93 puts("");
94 puts("Buddy list:");
95 puts("-------------------------------------------------------------------------------");
96 for (i=0; i<global.buddy_cnt; ++i) {
97 printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr,
98 (global.buddy_status[i]?"Online":"Offline"));
99 }
100 //printf("-------------------------------------\n");
101 puts("");
102 //puts("Commands:");
103 puts("+=============================================================================+");
104 puts("| Call Commands: | IM & Presence: | Misc: |");
105 puts("| | | |");
106 puts("| m Make new call | i Send IM | o Send OPTIONS |");
107 puts("| a Answer call | su Subscribe presence | d Dump status |");
108 puts("| h Hangup call | us Unsubscribe presence | d1 Dump detailed |");
109 puts("| ] Select next dialog | t Toggle Online status | |");
110 puts("| [ Select previous dialog | | |");
111 puts("+-----------------------------------------------------------------------------+");
112 puts("| q QUIT |");
113 puts("+=============================================================================+");
114 puts("");
115
116
117 fflush(stdout);
118}
119
120/*
121 * Verify that valid SIP url is given.
122 */
123static pj_status_t verify_sip_url(char *url)
124{
125 pjsip_uri *p;
126 pj_pool_t *pool;
127 int len = (url ? strlen(url) : 0);
128
129 if (!len) return -1;
130
131 pool = pj_pool_create(global.pf, "check%p", 1024, 0, NULL);
132 if (!pool) return -1;
133
134 p = pjsip_parse_uri(pool, url, len, 0);
135 if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
136 p = NULL;
137
138 pj_pool_release(pool);
139 return p ? 0 : -1;
140}
141
142/*
143 * Read command arguments from config file.
144 */
145static int read_config_file(pj_pool_t *pool, const char *filename,
146 int *app_argc, char ***app_argv)
147{
148 int i;
149 FILE *fhnd;
150 char line[200];
151 int argc = 0;
152 char **argv;
153 enum { MAX_ARGS = 64 };
154
155 /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
156 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
157 argv[argc++] = *app_argv[0];
158
159 /* Open config file. */
160 fhnd = fopen(filename, "rt");
161 if (!fhnd) {
162 printf("Unable to open config file %s\n", filename);
163 return -1;
164 }
165
166 /* Scan tokens in the file. */
167 while (argc < MAX_ARGS && !feof(fhnd)) {
168 char *token, *p = line;
169
170 if (fgets(line, sizeof(line), fhnd) == NULL) break;
171
172 for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS;
173 token = strtok(NULL, " \t\r\n"))
174 {
175 int token_len;
176
177 if (!token) break;
178 if (*token == '#') break;
179
180 token_len = strlen(token);
181 if (!token_len)
182 continue;
183 argv[argc] = pj_pool_alloc(pool, token_len+1);
184 pj_memcpy(argv[argc], token, token_len+1);
185 ++argc;
186 }
187 }
188
189 /* Copy arguments from command line */
190 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
191 argv[argc++] = (*app_argv)[i];
192
193 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
194 printf("Too many arguments specified in cmd line/config file\n");
195 fclose(fhnd);
196 return -1;
197 }
198
199 fclose(fhnd);
200
201 /* Assign the new command line back to the original command line. */
202 *app_argc = argc;
203 *app_argv = argv;
204 return 0;
205
206}
207
208/*
209 * Parse program arguments
210 */
211static int parse_args(pj_pool_t *pool, int argc, char *argv[])
212{
213 int c;
214 int option_index;
215 enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
216 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
217 OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR,
218 OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT,
219 OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
220 OPT_USE_STUN1, OPT_USE_STUN2,
221 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
222 OPT_AUTO_ANSWER, OPT_AUTO_HANGUP};
223 struct option long_options[] = {
224 { "config-file",1, 0, OPT_CONFIG_FILE},
225 { "log-file", 1, 0, OPT_LOG_FILE},
226 { "log-level", 1, 0, OPT_LOG_LEVEL},
227 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
228 { "help", 0, 0, OPT_HELP},
229 { "version", 0, 0, OPT_VERSION},
230 { "null-audio", 0, 0, OPT_NULL_AUDIO},
231 { "local-port", 1, 0, OPT_LOCAL_PORT},
232 { "proxy", 1, 0, OPT_PROXY},
233 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
234 { "registrar", 1, 0, OPT_REGISTRAR},
235 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
236 { "id", 1, 0, OPT_ID},
237 { "contact", 1, 0, OPT_CONTACT},
238 { "realm", 1, 0, OPT_REALM},
239 { "username", 1, 0, OPT_USERNAME},
240 { "password", 1, 0, OPT_PASSWORD},
241 { "use-stun1", 1, 0, OPT_USE_STUN1},
242 { "use-stun2", 1, 0, OPT_USE_STUN2},
243 { "add-buddy", 1, 0, OPT_ADD_BUDDY},
244 { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
245 { "no-presence", 0, 0, OPT_NO_PRESENCE},
246 { "auto-answer",1, 0, OPT_AUTO_ANSWER},
247 { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
248 { NULL, 0, 0, 0}
249 };
250 char *config_file = NULL;
251
252 /* Run getopt once to see if user specifies config file to read. */
253 while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
254 switch (c) {
255 case 0:
256 config_file = optarg;
257 break;
258 }
259 if (config_file)
260 break;
261 }
262
263 if (config_file) {
264 if (read_config_file(pool, config_file, &argc, &argv) != 0)
265 return -1;
266 }
267
268 /* Reinitialize and re-run getopt again, possibly with new arguments
269 * read from config file.
270 */
271 optind = 0;
272 while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
273 char *err, *p;
274
275 switch (c) {
276 case OPT_LOG_FILE:
277 global.log_filename = optarg;
278 break;
279 case OPT_LOG_LEVEL:
280 c = strtoul(optarg, &err, 10);
281 if (*err) {
282 printf("Error: expecting integer value 0-6 for --log-level\n");
283 return -1;
284 }
285 pj_log_set_level( c );
286 break;
287 case OPT_APP_LOG_LEVEL:
288 global.app_log_level = strtoul(optarg, &err, 10);
289 if (*err) {
290 printf("Error: expecting integer value 0-6 for --app-log-level\n");
291 return -1;
292 }
293 break;
294 case OPT_HELP:
295 usage();
296 return -1;
297 case OPT_VERSION: /* version */
298 pj_dump_config();
299 return -1;
300 case OPT_NULL_AUDIO:
301 global.null_audio = 1;
302 break;
303 case OPT_LOCAL_PORT: /* local-port */
304 global.sip_port = strtoul(optarg, &err, 10);
305 if (*err) {
306 printf("Error: expecting integer value for --local-port\n");
307 return -1;
308 }
309 break;
310 case OPT_PROXY: /* proxy */
311 if (verify_sip_url(optarg) != 0) {
312 printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg);
313 return -1;
314 }
315 global.proxy = pj_str(optarg);
316 break;
317 case OPT_OUTBOUND_PROXY: /* outbound proxy */
318 if (verify_sip_url(optarg) != 0) {
319 printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg);
320 return -1;
321 }
322 global.outbound_proxy = pj_str(optarg);
323 break;
324 case OPT_REGISTRAR: /* registrar */
325 if (verify_sip_url(optarg) != 0) {
326 printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg);
327 return -1;
328 }
329 global.registrar_uri = pj_str(optarg);
330 break;
331 case OPT_REG_TIMEOUT: /* reg-timeout */
332 global.reg_timeout = strtoul(optarg, &err, 10);
333 if (*err) {
334 printf("Error: expecting integer value for --reg-timeout\n");
335 return -1;
336 }
337 break;
338 case OPT_ID: /* id */
339 if (verify_sip_url(optarg) != 0) {
340 printf("Error: invalid SIP URL '%s' in local id argument\n", optarg);
341 return -1;
342 }
343 global.local_uri = pj_str(optarg);
344 break;
345 case OPT_CONTACT: /* contact */
346 if (verify_sip_url(optarg) != 0) {
347 printf("Error: invalid SIP URL '%s' in contact argument\n", optarg);
348 return -1;
349 }
350 global.contact = pj_str(optarg);
351 break;
352 case OPT_USERNAME: /* Default authentication user */
353 if (!global.cred_count) global.cred_count = 1;
354 global.cred_info[0].username = pj_str(optarg);
355 break;
356 case OPT_REALM: /* Default authentication realm. */
357 if (!global.cred_count) global.cred_count = 1;
358 global.cred_info[0].realm = pj_str(optarg);
359 break;
360 case OPT_PASSWORD: /* authentication password */
361 if (!global.cred_count) global.cred_count = 1;
362 global.cred_info[0].data_type = 0;
363 global.cred_info[0].data = pj_str(optarg);
364 break;
365 case OPT_USE_STUN1: /* STUN server 1 */
366 p = pj_native_strchr(optarg, ':');
367 if (p) {
368 *p = '\0';
369 global.stun_srv1 = pj_str(optarg);
370 global.stun_port1 = strtoul(p+1, &err, 10);
371 if (*err || global.stun_port1==0) {
372 printf("Error: expecting port number with option --use-stun1\n");
373 return -1;
374 }
375 } else {
376 global.stun_port1 = 3478;
377 global.stun_srv1 = pj_str(optarg);
378 }
379 break;
380 case OPT_USE_STUN2: /* STUN server 2 */
381 p = pj_native_strchr(optarg, ':');
382 if (p) {
383 *p = '\0';
384 global.stun_srv2 = pj_str(optarg);
385 global.stun_port2 = strtoul(p+1, &err, 10);
386 if (*err || global.stun_port2==0) {
387 printf("Error: expecting port number with option --use-stun2\n");
388 return -1;
389 }
390 } else {
391 global.stun_port2 = 3478;
392 global.stun_srv2 = pj_str(optarg);
393 }
394 break;
395 case OPT_ADD_BUDDY: /* Add to buddy list. */
396 if (verify_sip_url(optarg) != 0) {
397 printf("Error: invalid URL '%s' in --add-buddy option\n", optarg);
398 return -1;
399 }
400 if (global.buddy_cnt == MAX_BUDDIES) {
401 printf("Error: too many buddies in buddy list.\n");
402 return -1;
403 }
404 global.buddy[global.buddy_cnt++] = pj_str(optarg);
405 break;
406 case OPT_OFFER_X_MS_MSG:
407 global.offer_x_ms_msg = 1;
408 break;
409 case OPT_NO_PRESENCE:
410 global.no_presence = 1;
411 break;
412 case OPT_AUTO_ANSWER:
413 global.auto_answer = strtoul(optarg, &err, 10);
414 if (*err) {
415 printf("Error: expecting integer value for --auto-answer option\n");
416 return -1;
417 }
418 break;
419 case OPT_AUTO_HANGUP:
420 global.auto_hangup = strtoul(optarg, &err, 10);
421 if (*err) {
422 printf("Error: expecting integer value for --auto-hangup option\n");
423 return -1;
424 }
425 break;
426 }
427 }
428
429 if (optind != argc) {
430 printf("Error: unknown options %s\n", argv[optind]);
431 return -1;
432 }
433
434 if (global.reg_timeout == 0)
435 global.reg_timeout = 3600;
436
437 return 0;
438}
439
440/* Print dialog. */
441static void print_dialog(pjsip_dlg *dlg)
442{
443 if (!dlg) {
444 puts("none");
445 return;
446 }
447
448 printf("%s: call-id=%.*s", dlg->obj_name,
449 (int)dlg->call_id->id.slen,
450 dlg->call_id->id.ptr);
451
452 printf(" (%s, %s)\n", pjsip_role_name(dlg->role),
453 pjsip_dlg_state_str(dlg->state));
454}
455
456/* Dump media statistic */
457void dump_media_statistic(pjsip_dlg *dlg)
458{
459 struct dialog_data *dlg_data = dlg->user_data;
460 pj_media_stream_stat stat[2];
461 const char *statname[2] = { "TX", "RX" };
462 int i;
463
464 pj_media_session_get_stat (dlg_data->msession, 0, &stat[0], &stat[1]);
465
466 printf("Media statistic:\n");
467 for (i=0; i<2; ++i) {
468 printf(" %s statistics:\n", statname[i]);
469 printf(" Pkt TX=%d RX=%d\n", stat[i].pkt_tx, stat[i].pkt_rx);
470 printf(" Octets TX=%d RX=%d\n", stat[i].oct_tx, stat[i].oct_rx);
471 printf(" Jitter %d ms\n", stat[i].jitter);
472 printf(" Pkt lost %d\n", stat[i].pkt_lost);
473 }
474 printf("\n");
475}
476
477/* Print all dialogs. */
478static void print_all_dialogs()
479{
480 pjsip_dlg *dlg = (pjsip_dlg *)global.user_agent->dlg_list.next;
481
482 puts("List all dialogs:");
483
484 while (dlg != (pjsip_dlg *) &global.user_agent->dlg_list) {
485 printf("%c", (dlg==global.cur_dlg ? '*' : ' '));
486 print_dialog(dlg);
487 dlg = dlg->next;
488 }
489
490 puts("");
491}
492