1 /* $Id$ */
2 /*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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_app_common.h"
21
22 #define THIS_FILE "pjsua_app_config.c"
23
24 #define MAX_APP_OPTIONS 128
25
26 #define str(s) #s
27 #define xstr(s) str(s)
28
29 char *stdout_refresh_text = "STDOUT_REFRESH";
30
31 /* Show usage */
usage(void)32 static void usage(void)
33 {
34 puts ("Usage:");
35 puts (" pjsua [options] [SIP URL to call]");
36 puts ("");
37 puts ("General options:");
38 puts (" --config-file=file Read the config/arguments from file.");
39 puts (" --help Display this help screen");
40 puts (" --version Display version info");
41 puts ("");
42 puts ("Logging options:");
43 puts (" --log-file=fname Log to filename (default stderr)");
44 puts (" --log-level=N Set log max level to N (0(none) to 6(trace)) (default=5)");
45 puts (" --app-log-level=N Set log max level for stdout display (default=4)");
46 puts (" --log-append Append instead of overwrite existing log file.\n");
47 puts (" --color Use colorful logging (default yes on Win32)");
48 puts (" --no-color Disable colorful logging");
49 puts (" --light-bg Use dark colors for light background (default is dark bg)");
50 puts (" --no-stderr Disable stderr");
51
52 puts ("");
53 puts ("SIP Account options:");
54 puts (" --registrar=url Set the URL of registrar server");
55 puts (" --id=url Set the URL of local ID (used in From header)");
56 puts (" --realm=string Set realm");
57 puts (" --username=string Set authentication username");
58 puts (" --password=string Set authentication password");
59 puts (" --contact=url Optionally override the Contact information");
60 puts (" --contact-params=S Append the specified parameters S in Contact header");
61 puts (" --contact-uri-params=S Append the specified parameters S in Contact URI");
62 puts (" --proxy=url Optional URL of proxy server to visit");
63 puts (" May be specified multiple times");
64 printf(" --reg-timeout=SEC Optional registration interval (default %d)\n",
65 PJSUA_REG_INTERVAL);
66 printf(" --rereg-delay=SEC Optional auto retry registration interval (default %d)\n",
67 PJSUA_REG_RETRY_INTERVAL);
68 puts (" --reg-use-proxy=N Control the use of proxy settings in REGISTER.");
69 puts (" 0=no proxy, 1=outbound only, 2=acc only, 3=all (default)");
70 puts (" --publish Send presence PUBLISH for this account");
71 puts (" --mwi Subscribe to message summary/waiting indication");
72 puts (" --use-ims Enable 3GPP/IMS related settings on this account");
73 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
74 puts (" --use-srtp=N Use SRTP? 0:disabled, 1:optional, 2:mandatory,");
75 puts (" 3:optional by duplicating media offer (def:0)");
76 puts (" --srtp-secure=N SRTP require secure SIP? 0:no, 1:tls, 2:sips (def:1)");
77 #endif
78 puts (" --use-100rel Require reliable provisional response (100rel)");
79 puts (" --use-timer=N Use SIP session timers? (default=1)");
80 puts (" 0:inactive, 1:optional, 2:mandatory, 3:always");
81 printf(" --timer-se=N Session timers expiration period, in secs (def:%d)\n",
82 PJSIP_SESS_TIMER_DEF_SE);
83 puts (" --timer-min-se=N Session timers minimum expiration period, in secs (def:90)");
84 puts (" --outb-rid=string Set SIP outbound reg-id (default:1)");
85 puts (" --auto-update-nat=N Where N is 0 or 1 to enable/disable SIP traversal behind");
86 puts (" symmetric NAT (default 1)");
87 puts (" --disable-stun Disable STUN for this account");
88 puts (" --next-cred Add another credentials");
89 puts ("");
90 puts ("SIP Account Control:");
91 puts (" --next-account Add more account");
92 puts ("");
93 puts ("Transport Options:");
94 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
95 puts (" --ipv6 Use IPv6 instead for SIP and media.");
96 #endif
97 puts (" --set-qos Enable QoS tagging for SIP and media.");
98 puts (" --local-port=port Set TCP/UDP port. This implicitly enables both ");
99 puts (" TCP and UDP transports on the specified port, unless");
100 puts (" if TCP or UDP is disabled.");
101 puts (" --ip-addr=IP Use the specifed address as SIP and RTP addresses.");
102 puts (" (Hint: the IP may be the public IP of the NAT/router)");
103 puts (" --bound-addr=IP Bind transports to this IP interface");
104 puts (" --no-tcp Disable TCP transport.");
105 puts (" --no-udp Disable UDP transport.");
106 puts (" --nameserver=NS Add the specified nameserver to enable SRV resolution");
107 puts (" This option can be specified multiple times.");
108 puts (" --outbound=url Set the URL of global outbound proxy server");
109 puts (" May be specified multiple times");
110 puts (" --stun-srv=FORMAT Set STUN server host or domain. This option may be");
111 puts (" specified more than once. FORMAT is hostdom[:PORT]");
112
113 #if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0)
114 puts ("");
115 puts ("TLS Options:");
116 puts (" --use-tls Enable TLS transport (default=no)");
117 puts (" --tls-ca-file Specify TLS CA file (default=none)");
118 puts (" --tls-cert-file Specify TLS certificate file (default=none)");
119 puts (" --tls-privkey-file Specify TLS private key file (default=none)");
120 puts (" --tls-password Specify TLS password to private key file (default=none)");
121 puts (" --tls-verify-server Verify server's certificate (default=no)");
122 puts (" --tls-verify-client Verify client's certificate (default=no)");
123 puts (" --tls-neg-timeout Specify TLS negotiation timeout (default=no)");
124 puts (" --tls-cipher Specify prefered TLS cipher (optional).");
125 puts (" May be specified multiple times");
126 #endif
127
128 puts ("");
129 puts ("Audio Options:");
130 puts (" --add-codec=name Manually add codec (default is to enable all)");
131 puts (" --dis-codec=name Disable codec (can be specified multiple times)");
132 puts (" --clock-rate=N Override conference bridge clock rate");
133 puts (" --snd-clock-rate=N Override sound device clock rate");
134 puts (" --stereo Audio device and conference bridge opened in stereo mode");
135 puts (" --null-audio Use NULL audio device");
136 puts (" --play-file=file Register WAV file in conference bridge.");
137 puts (" This can be specified multiple times.");
138 puts (" --play-tone=FORMAT Register tone to the conference bridge.");
139 puts (" FORMAT is 'F1,F2,ON,OFF', where F1,F2 are");
140 puts (" frequencies, and ON,OFF=on/off duration in msec.");
141 puts (" This can be specified multiple times.");
142 puts (" --auto-play Automatically play the file (to incoming calls only)");
143 puts (" --auto-play-hangup Automatically hangup the file after file play completes");
144 puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
145 puts (" --auto-conf Automatically put calls in conference with others");
146 puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
147 puts (" --auto-rec Automatically record conversation");
148 puts (" --quality=N Specify media quality (0-10, default="
149 xstr(PJSUA_DEFAULT_CODEC_QUALITY) ")");
150 puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
151 puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
152 puts (" --ec-tail=MSEC Set echo canceller tail length (default="
153 xstr(PJSUA_DEFAULT_EC_TAIL_LEN) ")");
154 puts (" --ec-opt=OPT Select echo canceller algorithm (0=default, ");
155 puts (" 1=speex, 2=suppressor, 3=WebRtc)");
156 puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is "
157 xstr(PJSUA_DEFAULT_ILBC_MODE) ")");
158 puts (" --capture-dev=id Audio capture device ID (default=-1)");
159 puts (" --playback-dev=id Audio playback device ID (default=-1)");
160 puts (" --capture-lat=N Audio capture latency, in ms (default="
161 xstr(PJMEDIA_SND_DEFAULT_REC_LATENCY) ")");
162 puts (" --playback-lat=N Audio playback latency, in ms (default="
163 xstr(PJMEDIA_SND_DEFAULT_PLAY_LATENCY) ")");
164 puts (" --snd-auto-close=N Auto close audio device when idle for N secs (default=1)");
165 puts (" Specify N=-1 to disable this feature.");
166 puts (" Specify N=0 for instant close when unused.");
167 puts (" --no-tones Disable audible tones");
168 puts (" --jb-max-size Specify jitter buffer maximum size, in frames (default=-1)");
169 puts (" --extra-audio Add one more audio stream");
170
171 #if PJSUA_HAS_VIDEO
172 puts ("");
173 puts ("Video Options:");
174 puts (" --video Enable video");
175 puts (" --vcapture-dev=id Video capture device ID (default=-1)");
176 puts (" --vrender-dev=id Video render device ID (default=-2)");
177 puts (" --play-avi=FILE Load this AVI as virtual capture device");
178 puts (" --auto-play-avi Automatically play the AVI media to call");
179 #endif
180
181 puts ("");
182 puts ("Media Transport Options:");
183 puts (" --use-ice Enable ICE (default:no)");
184 puts (" --ice-regular Use ICE regular nomination (default: aggressive)");
185 puts (" --ice-trickle=N Use trickle ICE? 0:disabled, 1:half, 2:full (default=0)");
186 puts (" --ice-max-hosts=N Set maximum number of ICE host candidates");
187 puts (" --ice-no-rtcp Disable RTCP component in ICE (default: no)");
188 puts (" --rtp-port=N Base port to try for RTP (default=4000)");
189 puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
190 puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
191 puts (" --use-turn Enable TURN relay with ICE (default:no)");
192 puts (" --turn-srv Domain or host name of TURN server (\"NAME:PORT\" format)");
193 puts (" --turn-tcp Use TCP connection to TURN server (default no)");
194 puts (" --turn-user TURN username");
195 puts (" --turn-passwd TURN password");
196 puts (" --rtcp-mux Enable RTP & RTCP multiplexing (default: no)");
197 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
198 puts (" --srtp-keying SRTP keying method for outgoing SDP offer.");
199 puts (" 0=SDES (default), 1=DTLS");
200 #endif
201 #if PJ_HAS_SSL_SOCK
202 puts ("");
203 puts ("TURN TLS Options:");
204 puts (" --turn-tls Use TLS connection to TURN server (default no)");
205 puts (" --turn-tls-ca-file Specify TURN TLS CA file (default=none)");
206 puts (" --turn-tls-cert-file Specify TURN TLS certificate file (default=none)");
207 puts (" --turn-tls-privkey-file Specify TURN TLS private key file (default=none)");
208 puts (" --turn-tls-privkey-pwd Specify TURN TLS password to private key file (default=none)");
209 puts (" --turn-tls-neg-timeout Specify TURN TLS negotiation timeout (default=no)");
210 puts (" --turn-tls-cipher Specify prefered TURN TLS cipher (optional).");
211 puts (" May be specified multiple times");
212 #endif
213 puts ("");
214 puts ("Buddy List (can be more than one):");
215 puts (" --add-buddy url Add the specified URL to the buddy list.");
216 puts ("");
217 puts ("User Agent options:");
218 puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
219 puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
220 puts (" --thread-cnt=N Number of worker threads (default:1)");
221 puts (" --duration=SEC Set maximum call duration (default:no limit)");
222 puts (" --norefersub Suppress event subscription when transferring calls");
223 puts (" --use-compact-form Minimize SIP message size");
224 puts (" --no-force-lr Allow strict-route to be used (i.e. do not force lr)");
225 puts (" --accept-redirect=N Specify how to handle call redirect (3xx) response.");
226 puts (" 0: reject, 1: follow automatically,");
227 puts (" 2: follow + replace To header (default), 3: ask");
228
229 puts ("");
230 puts ("CLI options:");
231 puts (" --use-cli Use CLI as user interface");
232 puts (" --cli-telnet-port=N CLI telnet port");
233 puts (" --no-cli-console Disable CLI console");
234 puts ("");
235
236 puts ("");
237 puts ("When URL is specified, pjsua will immediately initiate call to that URL");
238 puts ("");
239
240 fflush(stdout);
241 }
242
log_writer_nobuf(int level,const char * buffer,int len)243 static void log_writer_nobuf(int level, const char *buffer, int len)
244 {
245 pj_log_write(level, buffer, len);
246 fflush(stdout);
247 }
248
249 /*
250 * Read command arguments from config file.
251 */
read_config_file(pj_pool_t * pool,const char * filename,int * app_argc,char *** app_argv)252 static int read_config_file(pj_pool_t *pool, const char *filename,
253 int *app_argc, char ***app_argv)
254 {
255 int i;
256 FILE *fhnd;
257 char line[200];
258 int argc = 0;
259 char **argv;
260 enum { MAX_ARGS = 128 };
261
262 /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
263 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
264 argv[argc++] = *app_argv[0];
265
266 /* Open config file. */
267 fhnd = fopen(filename, "rt");
268 if (!fhnd) {
269 PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename));
270 fflush(stdout);
271 return -1;
272 }
273
274 /* Scan tokens in the file. */
275 while (argc < MAX_ARGS && !feof(fhnd)) {
276 char *token;
277 char *p;
278 const char *whitespace = " \t\r\n";
279 char cDelimiter;
280 pj_size_t len;
281 int token_len;
282
283 pj_bzero(line, sizeof(line));
284 if (fgets(line, sizeof(line), fhnd) == NULL) break;
285
286 // Trim ending newlines
287 len = strlen(line);
288 if (len > 0 && line[len-1]=='\n')
289 line[--len] = '\0';
290 if (len > 0 && line[len-1]=='\r')
291 line[--len] = '\0';
292
293 if (len==0) continue;
294
295 for (p = line; *p != '\0' && argc < MAX_ARGS; p++) {
296 // first, scan whitespaces
297 while (*p != '\0' && strchr(whitespace, *p) != NULL) p++;
298
299 if (*p == '\0') // are we done yet?
300 break;
301
302 if (*p == '"' || *p == '\'') { // is token a quoted string
303 cDelimiter = *p++; // save quote delimiter
304 token = p;
305
306 while (*p != '\0' && *p != cDelimiter) p++;
307
308 if (*p == '\0') // found end of the line, but,
309 cDelimiter = '\0'; // didn't find a matching quote
310
311 } else { // token's not a quoted string
312 token = p;
313
314 while (*p != '\0' && strchr(whitespace, *p) == NULL) p++;
315
316 cDelimiter = *p;
317 }
318
319 *p = '\0';
320 token_len = (int)(p-token);
321
322 if (token_len > 0) {
323 if (*token == '#')
324 break; // ignore remainder of line
325
326 argv[argc] = pj_pool_alloc(pool, token_len + 1);
327 pj_memcpy(argv[argc], token, token_len + 1);
328 ++argc;
329 }
330
331 *p = cDelimiter;
332 }
333 }
334
335 /* Copy arguments from command line */
336 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
337 argv[argc++] = (*app_argv)[i];
338
339 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
340 PJ_LOG(1,(THIS_FILE,
341 "Too many arguments specified in cmd line/config file"));
342 fflush(stdout);
343 fclose(fhnd);
344 return -1;
345 }
346
347 fclose(fhnd);
348
349 /* Assign the new command line back to the original command line. */
350 *app_argc = argc;
351 *app_argv = argv;
352 return 0;
353 }
354
355 /* Parse arguments. */
parse_args(int argc,char * argv[],pj_str_t * uri_to_call)356 static pj_status_t parse_args(int argc, char *argv[],
357 pj_str_t *uri_to_call)
358 {
359 int c;
360 int option_index;
361 pjsua_app_config *cfg = &app_config;
362 enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
363 OPT_LOG_APPEND, OPT_COLOR, OPT_NO_COLOR, OPT_LIGHT_BG, OPT_NO_STDERR,
364 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, OPT_SND_AUTO_CLOSE,
365 OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
366 OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
367 OPT_BOUND_ADDR, OPT_CONTACT_PARAMS, OPT_CONTACT_URI_PARAMS,
368 OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
369 OPT_REG_RETRY_INTERVAL, OPT_REG_USE_PROXY,
370 OPT_MWI, OPT_NAMESERVER, OPT_STUN_SRV, OPT_OUTB_RID,
371 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
372 OPT_AUTO_ANSWER, OPT_AUTO_PLAY, OPT_AUTO_PLAY_HANGUP, OPT_AUTO_LOOP,
373 OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
374 OPT_USE_ICE, OPT_ICE_REGULAR, OPT_ICE_TRICKLE,
375 OPT_USE_SRTP, OPT_SRTP_SECURE,
376 OPT_USE_TURN, OPT_ICE_MAX_HOSTS, OPT_ICE_NO_RTCP, OPT_TURN_SRV,
377 OPT_TURN_TCP, OPT_TURN_USER, OPT_TURN_PASSWD, OPT_TURN_TLS,
378 OPT_TURN_TLS_CA_FILE, OPT_TURN_TLS_CERT_FILE,
379 OPT_TURN_TLS_NEG_TIMEOUT, OPT_TURN_TLS_CIPHER,
380 OPT_TURN_TLS_PRIV_FILE, OPT_TURN_TLS_PASSWORD,
381 OPT_RTCP_MUX, OPT_SRTP_KEYING,
382 OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC,
383 OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
384 OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
385 OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL, OPT_EC_OPT,
386 OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
387 OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
388 OPT_NOREFERSUB, OPT_ACCEPT_REDIRECT,
389 OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE,
390 OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT,
391 OPT_TLS_NEG_TIMEOUT, OPT_TLS_CIPHER,
392 OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV,
393 OPT_CAPTURE_LAT, OPT_PLAYBACK_LAT, OPT_NO_TONES, OPT_JB_MAX_SIZE,
394 OPT_STDOUT_REFRESH, OPT_STDOUT_REFRESH_TEXT, OPT_IPV6, OPT_QOS,
395 #ifdef _IONBF
396 OPT_STDOUT_NO_BUF,
397 #endif
398 OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC,
399 OPT_DISABLE_STUN, OPT_NO_FORCE_LR,
400 OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE,
401 OPT_VIDEO, OPT_EXTRA_AUDIO,
402 OPT_VCAPTURE_DEV, OPT_VRENDER_DEV, OPT_PLAY_AVI, OPT_AUTO_PLAY_AVI,
403 OPT_USE_CLI, OPT_CLI_TELNET_PORT, OPT_DISABLE_CLI_CONSOLE
404 };
405 struct pj_getopt_option long_options[] = {
406 { "config-file",1, 0, OPT_CONFIG_FILE},
407 { "log-file", 1, 0, OPT_LOG_FILE},
408 { "log-level", 1, 0, OPT_LOG_LEVEL},
409 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
410 { "log-append", 0, 0, OPT_LOG_APPEND},
411 { "color", 0, 0, OPT_COLOR},
412 { "no-color", 0, 0, OPT_NO_COLOR},
413 { "light-bg", 0, 0, OPT_LIGHT_BG},
414 { "no-stderr", 0, 0, OPT_NO_STDERR},
415 { "help", 0, 0, OPT_HELP},
416 { "version", 0, 0, OPT_VERSION},
417 { "clock-rate", 1, 0, OPT_CLOCK_RATE},
418 { "snd-clock-rate", 1, 0, OPT_SND_CLOCK_RATE},
419 { "stereo", 0, 0, OPT_STEREO},
420 { "null-audio", 0, 0, OPT_NULL_AUDIO},
421 { "local-port", 1, 0, OPT_LOCAL_PORT},
422 { "ip-addr", 1, 0, OPT_IP_ADDR},
423 { "bound-addr", 1, 0, OPT_BOUND_ADDR},
424 { "no-tcp", 0, 0, OPT_NO_TCP},
425 { "no-udp", 0, 0, OPT_NO_UDP},
426 { "norefersub", 0, 0, OPT_NOREFERSUB},
427 { "proxy", 1, 0, OPT_PROXY},
428 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
429 { "registrar", 1, 0, OPT_REGISTRAR},
430 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
431 { "publish", 0, 0, OPT_PUBLISH},
432 { "mwi", 0, 0, OPT_MWI},
433 { "use-100rel", 0, 0, OPT_100REL},
434 { "use-ims", 0, 0, OPT_USE_IMS},
435 { "id", 1, 0, OPT_ID},
436 { "contact", 1, 0, OPT_CONTACT},
437 { "contact-params",1,0, OPT_CONTACT_PARAMS},
438 { "contact-uri-params",1,0, OPT_CONTACT_URI_PARAMS},
439 { "auto-update-nat", 1, 0, OPT_AUTO_UPDATE_NAT},
440 { "disable-stun",0,0, OPT_DISABLE_STUN},
441 { "use-compact-form", 0, 0, OPT_USE_COMPACT_FORM},
442 { "accept-redirect", 1, 0, OPT_ACCEPT_REDIRECT},
443 { "no-force-lr",0, 0, OPT_NO_FORCE_LR},
444 { "realm", 1, 0, OPT_REALM},
445 { "username", 1, 0, OPT_USERNAME},
446 { "password", 1, 0, OPT_PASSWORD},
447 { "rereg-delay",1, 0, OPT_REG_RETRY_INTERVAL},
448 { "reg-use-proxy", 1, 0, OPT_REG_USE_PROXY},
449 { "nameserver", 1, 0, OPT_NAMESERVER},
450 { "stun-srv", 1, 0, OPT_STUN_SRV},
451 { "add-buddy", 1, 0, OPT_ADD_BUDDY},
452 { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
453 { "no-presence", 0, 0, OPT_NO_PRESENCE},
454 { "auto-answer",1, 0, OPT_AUTO_ANSWER},
455 { "auto-play", 0, 0, OPT_AUTO_PLAY},
456 { "auto-play-hangup",0, 0, OPT_AUTO_PLAY_HANGUP},
457 { "auto-rec", 0, 0, OPT_AUTO_REC},
458 { "auto-loop", 0, 0, OPT_AUTO_LOOP},
459 { "auto-conf", 0, 0, OPT_AUTO_CONF},
460 { "play-file", 1, 0, OPT_PLAY_FILE},
461 { "play-tone", 1, 0, OPT_PLAY_TONE},
462 { "rec-file", 1, 0, OPT_REC_FILE},
463 { "rtp-port", 1, 0, OPT_RTP_PORT},
464
465 { "use-ice", 0, 0, OPT_USE_ICE},
466 { "ice-regular",0, 0, OPT_ICE_REGULAR},
467 { "ice-trickle",1, 0, OPT_ICE_TRICKLE},
468 { "ice-max-hosts",1, 0, OPT_ICE_MAX_HOSTS},
469 { "ice-no-rtcp",0, 0, OPT_ICE_NO_RTCP},
470 { "use-turn", 0, 0, OPT_USE_TURN},
471 { "turn-srv", 1, 0, OPT_TURN_SRV},
472 { "turn-tcp", 0, 0, OPT_TURN_TCP},
473 #if PJ_HAS_SSL_SOCK
474 { "turn-tls", 0, 0, OPT_TURN_TLS},
475 { "turn-tls-ca-file",1, 0, OPT_TURN_TLS_CA_FILE},
476 { "turn-tls-cert-file",1,0, OPT_TURN_TLS_CERT_FILE},
477 { "turn-tls-privkey-file",1,0, OPT_TURN_TLS_PRIV_FILE},
478 { "turn-tls-privkey-pwd",1,0, OPT_TURN_TLS_PASSWORD},
479 { "turn-tls-neg-timeout", 1, 0, OPT_TURN_TLS_NEG_TIMEOUT},
480 { "turn-tls-cipher", 1, 0, OPT_TURN_TLS_CIPHER},
481 #endif
482 { "turn-user", 1, 0, OPT_TURN_USER},
483 { "turn-passwd",1, 0, OPT_TURN_PASSWD},
484 { "rtcp-mux", 0, 0, OPT_RTCP_MUX},
485
486 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
487 { "use-srtp", 1, 0, OPT_USE_SRTP},
488 { "srtp-secure",1, 0, OPT_SRTP_SECURE},
489 { "srtp-keying",1, 0, OPT_SRTP_KEYING},
490 #endif
491 { "add-codec", 1, 0, OPT_ADD_CODEC},
492 { "dis-codec", 1, 0, OPT_DIS_CODEC},
493 { "complexity", 1, 0, OPT_COMPLEXITY},
494 { "quality", 1, 0, OPT_QUALITY},
495 { "ptime", 1, 0, OPT_PTIME},
496 { "no-vad", 0, 0, OPT_NO_VAD},
497 { "ec-tail", 1, 0, OPT_EC_TAIL},
498 { "ec-opt", 1, 0, OPT_EC_OPT},
499 { "ilbc-mode", 1, 0, OPT_ILBC_MODE},
500 { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT},
501 { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT},
502 { "next-account",0,0, OPT_NEXT_ACCOUNT},
503 { "next-cred", 0, 0, OPT_NEXT_CRED},
504 { "max-calls", 1, 0, OPT_MAX_CALLS},
505 { "duration", 1, 0, OPT_DURATION},
506 { "thread-cnt", 1, 0, OPT_THREAD_CNT},
507 #if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0)
508 { "use-tls", 0, 0, OPT_USE_TLS},
509 { "tls-ca-file",1, 0, OPT_TLS_CA_FILE},
510 { "tls-cert-file",1,0, OPT_TLS_CERT_FILE},
511 { "tls-privkey-file",1,0, OPT_TLS_PRIV_FILE},
512 { "tls-password",1,0, OPT_TLS_PASSWORD},
513 { "tls-verify-server", 0, 0, OPT_TLS_VERIFY_SERVER},
514 { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT},
515 { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT},
516 { "tls-cipher", 1, 0, OPT_TLS_CIPHER},
517 #endif
518 { "capture-dev", 1, 0, OPT_CAPTURE_DEV},
519 { "playback-dev", 1, 0, OPT_PLAYBACK_DEV},
520 { "capture-lat", 1, 0, OPT_CAPTURE_LAT},
521 { "playback-lat", 1, 0, OPT_PLAYBACK_LAT},
522 { "stdout-refresh", 1, 0, OPT_STDOUT_REFRESH},
523 { "stdout-refresh-text", 1, 0, OPT_STDOUT_REFRESH_TEXT},
524 #ifdef _IONBF
525 { "stdout-no-buf", 0, 0, OPT_STDOUT_NO_BUF },
526 #endif
527 { "snd-auto-close", 1, 0, OPT_SND_AUTO_CLOSE},
528 { "no-tones", 0, 0, OPT_NO_TONES},
529 { "jb-max-size", 1, 0, OPT_JB_MAX_SIZE},
530 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
531 { "ipv6", 0, 0, OPT_IPV6},
532 #endif
533 { "set-qos", 0, 0, OPT_QOS},
534 { "use-timer", 1, 0, OPT_TIMER},
535 { "timer-se", 1, 0, OPT_TIMER_SE},
536 { "timer-min-se", 1, 0, OPT_TIMER_MIN_SE},
537 { "outb-rid", 1, 0, OPT_OUTB_RID},
538 { "video", 0, 0, OPT_VIDEO},
539 { "extra-audio",0, 0, OPT_EXTRA_AUDIO},
540 { "vcapture-dev", 1, 0, OPT_VCAPTURE_DEV},
541 { "vrender-dev", 1, 0, OPT_VRENDER_DEV},
542 { "play-avi", 1, 0, OPT_PLAY_AVI},
543 { "auto-play-avi", 0, 0, OPT_AUTO_PLAY_AVI},
544 { "use-cli", 0, 0, OPT_USE_CLI},
545 { "cli-telnet-port", 1, 0, OPT_CLI_TELNET_PORT},
546 { "no-cli-console", 0, 0, OPT_DISABLE_CLI_CONSOLE},
547 { NULL, 0, 0, 0}
548 };
549 pj_status_t status;
550 pjsua_acc_config *cur_acc;
551 char *config_file = NULL;
552 unsigned i;
553
554 /* Run pj_getopt once to see if user specifies config file to read. */
555 pj_optind = 0;
556 while ((c=pj_getopt_long(argc, argv, "", long_options,
557 &option_index)) != -1)
558 {
559 switch (c) {
560 case OPT_CONFIG_FILE:
561 config_file = pj_optarg;
562 break;
563 }
564 if (config_file)
565 break;
566 }
567
568 if (config_file) {
569 status = read_config_file(cfg->pool, config_file, &argc, &argv);
570 if (status != 0)
571 return status;
572 }
573
574 cfg->acc_cnt = 0;
575 cur_acc = cfg->acc_cfg;
576
577
578 /* Reinitialize and re-run pj_getopt again, possibly with new arguments
579 * read from config file.
580 */
581 pj_optind = 0;
582 while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
583 pj_str_t tmp;
584 long lval;
585
586 switch (c) {
587
588 case OPT_CONFIG_FILE:
589 /* Ignore as this has been processed before */
590 break;
591
592 case OPT_LOG_FILE:
593 cfg->log_cfg.log_filename = pj_str(pj_optarg);
594 break;
595
596 case OPT_LOG_LEVEL:
597 c = (int)pj_strtoul(pj_cstr(&tmp, pj_optarg));
598 if (c < 0 || c > 6) {
599 PJ_LOG(1,(THIS_FILE,
600 "Error: expecting integer value 0-6 "
601 "for --log-level"));
602 return PJ_EINVAL;
603 }
604 cfg->log_cfg.level = c;
605 pj_log_set_level( c );
606 break;
607
608 case OPT_APP_LOG_LEVEL:
609 cfg->log_cfg.console_level = (unsigned)pj_strtoul(pj_cstr(&tmp, pj_optarg));
610 if (cfg->log_cfg.console_level > 6) {
611 PJ_LOG(1,(THIS_FILE,
612 "Error: expecting integer value 0-6 "
613 "for --app-log-level"));
614 return PJ_EINVAL;
615 }
616 break;
617
618 case OPT_LOG_APPEND:
619 cfg->log_cfg.log_file_flags |= PJ_O_APPEND;
620 break;
621
622 case OPT_COLOR:
623 cfg->log_cfg.decor |= PJ_LOG_HAS_COLOR;
624 break;
625
626 case OPT_NO_COLOR:
627 cfg->log_cfg.decor &= ~PJ_LOG_HAS_COLOR;
628 break;
629
630 case OPT_LIGHT_BG:
631 pj_log_set_color(1, PJ_TERM_COLOR_R);
632 pj_log_set_color(2, PJ_TERM_COLOR_R | PJ_TERM_COLOR_G);
633 pj_log_set_color(3, PJ_TERM_COLOR_B | PJ_TERM_COLOR_G);
634 pj_log_set_color(4, 0);
635 pj_log_set_color(5, 0);
636 pj_log_set_color(77, 0);
637 break;
638
639 case OPT_NO_STDERR:
640 #if !defined(PJ_WIN32_WINCE) || PJ_WIN32_WINCE==0
641 if (!freopen("/dev/null", "w", stderr)) {
642 PJ_LOG(4,(THIS_FILE, "Failed reopening dev/null"));
643 }
644 #endif
645 break;
646
647 case OPT_HELP:
648 usage();
649 return PJ_EINVAL;
650
651 case OPT_VERSION: /* version */
652 pj_dump_config();
653 return PJ_EINVAL;
654
655 case OPT_NULL_AUDIO:
656 cfg->null_audio = PJ_TRUE;
657 break;
658
659 case OPT_CLOCK_RATE:
660 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
661 if (lval < 8000 || lval > 192000) {
662 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
663 "8000-192000 for conference clock rate"));
664 return PJ_EINVAL;
665 }
666 cfg->media_cfg.clock_rate = (unsigned)lval;
667 break;
668
669 case OPT_SND_CLOCK_RATE:
670 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
671 if (lval < 8000 || lval > 192000) {
672 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
673 "8000-192000 for sound device clock rate"));
674 return PJ_EINVAL;
675 }
676 cfg->media_cfg.snd_clock_rate = (unsigned)lval;
677 break;
678
679 case OPT_STEREO:
680 cfg->media_cfg.channel_count = 2;
681 break;
682
683 case OPT_LOCAL_PORT: /* local-port */
684 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
685 if (lval < 0 || lval > 65535) {
686 PJ_LOG(1,(THIS_FILE,
687 "Error: expecting integer value for "
688 "--local-port"));
689 return PJ_EINVAL;
690 }
691 cfg->udp_cfg.port = (pj_uint16_t)lval;
692 break;
693
694 case OPT_IP_ADDR: /* ip-addr */
695 cfg->udp_cfg.public_addr = pj_str(pj_optarg);
696 cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
697 break;
698
699 case OPT_BOUND_ADDR: /* bound-addr */
700 cfg->udp_cfg.bound_addr = pj_str(pj_optarg);
701 cfg->rtp_cfg.bound_addr = pj_str(pj_optarg);
702 break;
703
704 case OPT_NO_UDP: /* no-udp */
705 if (cfg->no_tcp && !cfg->use_tls) {
706 PJ_LOG(1,(THIS_FILE,"Error: cannot disable both TCP and UDP"));
707 return PJ_EINVAL;
708 }
709
710 cfg->no_udp = PJ_TRUE;
711 break;
712
713 case OPT_NOREFERSUB: /* norefersub */
714 cfg->no_refersub = PJ_TRUE;
715 break;
716
717 case OPT_NO_TCP: /* no-tcp */
718 if (cfg->no_udp && !cfg->use_tls) {
719 PJ_LOG(1,(THIS_FILE,"Error: cannot disable both TCP and UDP"));
720 return PJ_EINVAL;
721 }
722
723 cfg->no_tcp = PJ_TRUE;
724 break;
725
726 case OPT_PROXY: /* proxy */
727 if (pjsua_verify_sip_url(pj_optarg) != 0) {
728 PJ_LOG(1,(THIS_FILE,
729 "Error: invalid SIP URL '%s' "
730 "in proxy argument", pj_optarg));
731 return PJ_EINVAL;
732 }
733 cur_acc->proxy[cur_acc->proxy_cnt++] = pj_str(pj_optarg);
734 break;
735
736 case OPT_OUTBOUND_PROXY: /* outbound proxy */
737 if (pjsua_verify_sip_url(pj_optarg) != 0) {
738 PJ_LOG(1,(THIS_FILE,
739 "Error: invalid SIP URL '%s' "
740 "in outbound proxy argument", pj_optarg));
741 return PJ_EINVAL;
742 }
743 cfg->cfg.outbound_proxy[cfg->cfg.outbound_proxy_cnt++] = pj_str(pj_optarg);
744 break;
745
746 case OPT_REGISTRAR: /* registrar */
747 if (pjsua_verify_sip_url(pj_optarg) != 0) {
748 PJ_LOG(1,(THIS_FILE,
749 "Error: invalid SIP URL '%s' in "
750 "registrar argument", pj_optarg));
751 return PJ_EINVAL;
752 }
753 cur_acc->reg_uri = pj_str(pj_optarg);
754 break;
755
756 case OPT_REG_TIMEOUT: /* reg-timeout */
757 cur_acc->reg_timeout = (unsigned)pj_strtoul(pj_cstr(&tmp,pj_optarg));
758 if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
759 PJ_LOG(1,(THIS_FILE,
760 "Error: invalid value for --reg-timeout "
761 "(expecting 1-3600)"));
762 return PJ_EINVAL;
763 }
764 break;
765
766 case OPT_PUBLISH: /* publish */
767 cur_acc->publish_enabled = PJ_TRUE;
768 break;
769
770 case OPT_MWI: /* mwi */
771 cur_acc->mwi_enabled = PJ_TRUE;
772 break;
773
774 case OPT_100REL: /** 100rel */
775 cur_acc->require_100rel = PJSUA_100REL_MANDATORY;
776 cfg->cfg.require_100rel = PJSUA_100REL_MANDATORY;
777 break;
778
779 case OPT_TIMER: /** session timer */
780 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
781 if (lval < 0 || lval > 3) {
782 PJ_LOG(1,(THIS_FILE,
783 "Error: expecting integer value 0-3 for --use-timer"));
784 return PJ_EINVAL;
785 }
786 cur_acc->use_timer = (pjsua_sip_timer_use)lval;
787 cfg->cfg.use_timer = (pjsua_sip_timer_use)lval;
788 break;
789
790 case OPT_TIMER_SE: /** session timer session expiration */
791 cur_acc->timer_setting.sess_expires = (unsigned)pj_strtoul(pj_cstr(&tmp, pj_optarg));
792 if (cur_acc->timer_setting.sess_expires < 90) {
793 PJ_LOG(1,(THIS_FILE,
794 "Error: invalid value for --timer-se "
795 "(expecting higher than 90)"));
796 return PJ_EINVAL;
797 }
798 cfg->cfg.timer_setting.sess_expires = cur_acc->timer_setting.sess_expires;
799 break;
800
801 case OPT_TIMER_MIN_SE: /** session timer minimum session expiration */
802 cur_acc->timer_setting.min_se = (unsigned)pj_strtoul(pj_cstr(&tmp, pj_optarg));
803 if (cur_acc->timer_setting.min_se < 90) {
804 PJ_LOG(1,(THIS_FILE,
805 "Error: invalid value for --timer-min-se "
806 "(expecting higher than 90)"));
807 return PJ_EINVAL;
808 }
809 cfg->cfg.timer_setting.min_se = cur_acc->timer_setting.min_se;
810 break;
811
812 case OPT_OUTB_RID: /* Outbound reg-id */
813 cur_acc->rfc5626_reg_id = pj_str(pj_optarg);
814 break;
815
816 case OPT_USE_IMS: /* Activate IMS settings */
817 cur_acc->auth_pref.initial_auth = PJ_TRUE;
818 break;
819
820 case OPT_ID: /* id */
821 if (pjsua_verify_url(pj_optarg) != 0) {
822 PJ_LOG(1,(THIS_FILE,
823 "Error: invalid SIP URL '%s' "
824 "in local id argument", pj_optarg));
825 return PJ_EINVAL;
826 }
827 cur_acc->id = pj_str(pj_optarg);
828 break;
829
830 case OPT_CONTACT: /* contact */
831 if (pjsua_verify_sip_url(pj_optarg) != 0) {
832 PJ_LOG(1,(THIS_FILE,
833 "Error: invalid SIP URL '%s' "
834 "in contact argument", pj_optarg));
835 return PJ_EINVAL;
836 }
837 cur_acc->force_contact = pj_str(pj_optarg);
838 break;
839
840 case OPT_CONTACT_PARAMS:
841 cur_acc->contact_params = pj_str(pj_optarg);
842 break;
843
844 case OPT_CONTACT_URI_PARAMS:
845 cur_acc->contact_uri_params = pj_str(pj_optarg);
846 break;
847
848 case OPT_AUTO_UPDATE_NAT: /* OPT_AUTO_UPDATE_NAT */
849 cur_acc->allow_contact_rewrite = (pj_bool_t)pj_strtoul(pj_cstr(&tmp, pj_optarg));
850 break;
851
852 case OPT_DISABLE_STUN:
853 cur_acc->sip_stun_use = PJSUA_STUN_USE_DISABLED;
854 cur_acc->media_stun_use = PJSUA_STUN_USE_DISABLED;
855 break;
856
857 case OPT_USE_COMPACT_FORM:
858 /* enable compact form - from Ticket #342 */
859 {
860 extern pj_bool_t pjsip_include_allow_hdr_in_dlg;
861 extern pj_bool_t pjmedia_add_rtpmap_for_static_pt;
862
863 pjsip_cfg()->endpt.use_compact_form = PJ_TRUE;
864 /* do not transmit Allow header */
865 pjsip_include_allow_hdr_in_dlg = PJ_FALSE;
866 /* Do not include rtpmap for static payload types (<96) */
867 pjmedia_add_rtpmap_for_static_pt = PJ_FALSE;
868 }
869 break;
870
871 case OPT_ACCEPT_REDIRECT:
872 cfg->redir_op = my_atoi(pj_optarg);
873 if (cfg->redir_op>PJSIP_REDIRECT_STOP) {
874 PJ_LOG(1,(THIS_FILE,
875 "Error: accept-redirect value '%s' ", pj_optarg));
876 return PJ_EINVAL;
877 }
878 break;
879
880 case OPT_NO_FORCE_LR:
881 cfg->cfg.force_lr = PJ_FALSE;
882 break;
883
884 case OPT_NEXT_ACCOUNT: /* Add more account. */
885 cfg->acc_cnt++;
886 cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
887 break;
888
889 case OPT_USERNAME: /* Default authentication user */
890 cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
891 cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("Digest");
892 break;
893
894 case OPT_REALM: /* Default authentication realm. */
895 cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
896 break;
897
898 case OPT_PASSWORD: /* authentication password */
899 cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
900 cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
901 #if PJSIP_HAS_DIGEST_AKA_AUTH
902 cur_acc->cred_info[cur_acc->cred_count].data_type |= PJSIP_CRED_DATA_EXT_AKA;
903 cur_acc->cred_info[cur_acc->cred_count].ext.aka.k = pj_str(pj_optarg);
904 cur_acc->cred_info[cur_acc->cred_count].ext.aka.cb = &pjsip_auth_create_aka_response;
905 #endif
906 break;
907
908 case OPT_REG_RETRY_INTERVAL:
909 cur_acc->reg_retry_interval = (unsigned)pj_strtoul(pj_cstr(&tmp, pj_optarg));
910 break;
911
912 case OPT_REG_USE_PROXY:
913 cur_acc->reg_use_proxy = (unsigned)pj_strtoul(pj_cstr(&tmp, pj_optarg));
914 if (cur_acc->reg_use_proxy > 3) {
915 PJ_LOG(1,(THIS_FILE, "Error: invalid --reg-use-proxy value '%s'",
916 pj_optarg));
917 return PJ_EINVAL;
918 }
919 break;
920
921 case OPT_NEXT_CRED: /* next credential */
922 cur_acc->cred_count++;
923 break;
924
925 case OPT_NAMESERVER: /* nameserver */
926 cfg->cfg.nameserver[cfg->cfg.nameserver_count++] = pj_str(pj_optarg);
927 if (cfg->cfg.nameserver_count > PJ_ARRAY_SIZE(cfg->cfg.nameserver)) {
928 PJ_LOG(1,(THIS_FILE, "Error: too many nameservers"));
929 return PJ_ETOOMANY;
930 }
931 break;
932
933 case OPT_STUN_SRV: /* STUN server */
934 cfg->cfg.stun_host = pj_str(pj_optarg);
935 if (cfg->cfg.stun_srv_cnt==PJ_ARRAY_SIZE(cfg->cfg.stun_srv)) {
936 PJ_LOG(1,(THIS_FILE, "Error: too many STUN servers"));
937 return PJ_ETOOMANY;
938 }
939 cfg->cfg.stun_srv[cfg->cfg.stun_srv_cnt++] = pj_str(pj_optarg);
940 break;
941
942 case OPT_ADD_BUDDY: /* Add to buddy list. */
943 if (pjsua_verify_url(pj_optarg) != 0) {
944 PJ_LOG(1,(THIS_FILE,
945 "Error: invalid URL '%s' in "
946 "--add-buddy option", pj_optarg));
947 return -1;
948 }
949 if (cfg->buddy_cnt == PJ_ARRAY_SIZE(cfg->buddy_cfg)) {
950 PJ_LOG(1,(THIS_FILE,
951 "Error: too many buddies in buddy list."));
952 return -1;
953 }
954 cfg->buddy_cfg[cfg->buddy_cnt].uri = pj_str(pj_optarg);
955 cfg->buddy_cnt++;
956 break;
957
958 case OPT_AUTO_PLAY:
959 cfg->auto_play = 1;
960 break;
961
962 case OPT_AUTO_PLAY_HANGUP:
963 cfg->auto_play_hangup = 1;
964 break;
965
966 case OPT_AUTO_REC:
967 cfg->auto_rec = 1;
968 break;
969
970 case OPT_AUTO_LOOP:
971 cfg->auto_loop = 1;
972 break;
973
974 case OPT_AUTO_CONF:
975 cfg->auto_conf = 1;
976 break;
977
978 case OPT_PLAY_FILE:
979 cfg->wav_files[cfg->wav_count++] = pj_str(pj_optarg);
980 break;
981
982 case OPT_PLAY_TONE:
983 {
984 int f1, f2, on, off;
985 int n;
986
987 n = sscanf(pj_optarg, "%d,%d,%d,%d", &f1, &f2, &on, &off);
988 if (n != 4) {
989 puts("Expecting f1,f2,on,off in --play-tone");
990 return -1;
991 }
992
993 cfg->tones[cfg->tone_count].freq1 = (short)f1;
994 cfg->tones[cfg->tone_count].freq2 = (short)f2;
995 cfg->tones[cfg->tone_count].on_msec = (short)on;
996 cfg->tones[cfg->tone_count].off_msec = (short)off;
997 ++cfg->tone_count;
998 }
999 break;
1000
1001 case OPT_REC_FILE:
1002 cfg->rec_file = pj_str(pj_optarg);
1003 break;
1004
1005 case OPT_USE_ICE:
1006 cfg->media_cfg.enable_ice =
1007 cur_acc->ice_cfg.enable_ice = PJ_TRUE;
1008 break;
1009
1010 case OPT_ICE_REGULAR:
1011 cfg->media_cfg.ice_opt.aggressive =
1012 cur_acc->ice_cfg.ice_opt.aggressive = PJ_FALSE;
1013 break;
1014
1015 case OPT_ICE_TRICKLE:
1016 cfg->media_cfg.ice_opt.trickle =
1017 cur_acc->ice_cfg.ice_opt.trickle = my_atoi(pj_optarg);
1018
1019 if (!pj_isdigit(*pj_optarg) || cfg->media_cfg.ice_opt.trickle>2) {
1020 PJ_LOG(1,(THIS_FILE, "Invalid value for --ice-trickle option"));
1021 return -1;
1022 }
1023
1024 /* Automatically disable ICE aggressive mode */
1025 if (cfg->media_cfg.ice_opt.trickle > 0) {
1026 cfg->media_cfg.ice_opt.aggressive =
1027 cur_acc->ice_cfg.ice_opt.aggressive = PJ_FALSE;
1028 }
1029 break;
1030
1031 case OPT_USE_TURN:
1032 cfg->media_cfg.enable_turn =
1033 cur_acc->turn_cfg.enable_turn = PJ_TRUE;
1034 break;
1035
1036 case OPT_ICE_MAX_HOSTS:
1037 cfg->media_cfg.ice_max_host_cands =
1038 cur_acc->ice_cfg.ice_max_host_cands = my_atoi(pj_optarg);
1039 break;
1040
1041 case OPT_ICE_NO_RTCP:
1042 cfg->media_cfg.ice_no_rtcp =
1043 cur_acc->ice_cfg.ice_no_rtcp = PJ_TRUE;
1044 break;
1045
1046 case OPT_TURN_SRV:
1047 cfg->media_cfg.turn_server =
1048 cur_acc->turn_cfg.turn_server = pj_str(pj_optarg);
1049 break;
1050
1051 case OPT_TURN_TCP:
1052 cfg->media_cfg.turn_conn_type =
1053 cur_acc->turn_cfg.turn_conn_type = PJ_TURN_TP_TCP;
1054 break;
1055
1056 #if PJ_HAS_SSL_SOCK
1057 case OPT_TURN_TLS:
1058 cfg->media_cfg.turn_conn_type =
1059 cur_acc->turn_cfg.turn_conn_type = PJ_TURN_TP_TLS;
1060 break;
1061 case OPT_TURN_TLS_CA_FILE:
1062 cfg->media_cfg.turn_tls_setting.ca_list_file =
1063 cur_acc->turn_cfg.turn_tls_setting.ca_list_file =
1064 pj_str(pj_optarg);
1065 break;
1066
1067 case OPT_TURN_TLS_CERT_FILE:
1068 cfg->media_cfg.turn_tls_setting.cert_file =
1069 cur_acc->turn_cfg.turn_tls_setting.cert_file =
1070 pj_str(pj_optarg);
1071 break;
1072
1073 case OPT_TURN_TLS_PRIV_FILE:
1074 cfg->media_cfg.turn_tls_setting.privkey_file =
1075 cur_acc->turn_cfg.turn_tls_setting.privkey_file =
1076 pj_str(pj_optarg);
1077 break;
1078
1079 case OPT_TURN_TLS_PASSWORD:
1080 cfg->media_cfg.turn_tls_setting.password =
1081 cur_acc->turn_cfg.turn_tls_setting.password =
1082 pj_str(pj_optarg);
1083 break;
1084
1085 case OPT_TURN_TLS_NEG_TIMEOUT:
1086 cfg->media_cfg.turn_tls_setting.ssock_param.timeout.sec =
1087 cur_acc->turn_cfg.turn_tls_setting.ssock_param.timeout.sec =
1088 atoi(pj_optarg);
1089 break;
1090
1091 case OPT_TURN_TLS_CIPHER:
1092 {
1093 pj_ssl_cipher cipher;
1094
1095 if (pj_ansi_strnicmp(pj_optarg, "0x", 2) == 0) {
1096 pj_str_t cipher_st = pj_str(pj_optarg + 2);
1097 cipher = (pj_ssl_cipher)pj_strtoul2(&cipher_st, NULL, 16);
1098 } else {
1099 cipher = atoi(pj_optarg);
1100 }
1101
1102 if (pj_ssl_cipher_is_supported(cipher)) {
1103 static pj_ssl_cipher tls_ciphers[PJ_SSL_SOCK_MAX_CIPHERS];
1104 pj_ssl_sock_param *ssock_param =
1105 &cfg->media_cfg.turn_tls_setting.ssock_param;
1106
1107 tls_ciphers[ssock_param->ciphers_num++] = cipher;
1108 ssock_param->ciphers =
1109 cur_acc->turn_cfg.turn_tls_setting.ssock_param.ciphers =
1110 tls_ciphers;
1111 } else {
1112 pj_ssl_cipher ciphers[PJ_SSL_SOCK_MAX_CIPHERS];
1113 unsigned j, ciphers_cnt;
1114
1115 ciphers_cnt = PJ_ARRAY_SIZE(ciphers);
1116 pj_ssl_cipher_get_availables(ciphers, &ciphers_cnt);
1117
1118 PJ_LOG(1,(THIS_FILE, "Cipher \"%s\" is not supported by "
1119 "TLS/SSL backend.", pj_optarg));
1120 printf("Available TLS/SSL ciphers (%d):\n", ciphers_cnt);
1121 for (j=0; j<ciphers_cnt; ++j)
1122 printf("- 0x%06X: %s\n", ciphers[j],
1123 pj_ssl_cipher_name(ciphers[j]));
1124 return -1;
1125 }
1126 }
1127 break;
1128 #endif
1129
1130 case OPT_TURN_USER:
1131 cfg->media_cfg.turn_auth_cred.type =
1132 cur_acc->turn_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
1133 cfg->media_cfg.turn_auth_cred.data.static_cred.realm =
1134 cur_acc->turn_cfg.turn_auth_cred.data.static_cred.realm = pj_str("*");
1135 cfg->media_cfg.turn_auth_cred.data.static_cred.username =
1136 cur_acc->turn_cfg.turn_auth_cred.data.static_cred.username = pj_str(pj_optarg);
1137 break;
1138
1139 case OPT_TURN_PASSWD:
1140 cfg->media_cfg.turn_auth_cred.data.static_cred.data_type =
1141 cur_acc->turn_cfg.turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
1142 cfg->media_cfg.turn_auth_cred.data.static_cred.data =
1143 cur_acc->turn_cfg.turn_auth_cred.data.static_cred.data = pj_str(pj_optarg);
1144 break;
1145
1146 case OPT_RTCP_MUX:
1147 cur_acc->enable_rtcp_mux = PJ_TRUE;
1148 break;
1149
1150 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
1151 case OPT_USE_SRTP:
1152 app_config.cfg.use_srtp = my_atoi(pj_optarg);
1153 if (!pj_isdigit(*pj_optarg) || app_config.cfg.use_srtp > 3) {
1154 PJ_LOG(1,(THIS_FILE, "Invalid value for --use-srtp option"));
1155 return -1;
1156 }
1157 if ((int)app_config.cfg.use_srtp == 3) {
1158 /* SRTP optional mode with duplicated media offer */
1159 app_config.cfg.use_srtp = PJMEDIA_SRTP_OPTIONAL;
1160 app_config.cfg.srtp_optional_dup_offer = PJ_TRUE;
1161 cur_acc->srtp_optional_dup_offer = PJ_TRUE;
1162 }
1163 cur_acc->use_srtp = app_config.cfg.use_srtp;
1164 break;
1165 case OPT_SRTP_SECURE:
1166 app_config.cfg.srtp_secure_signaling = my_atoi(pj_optarg);
1167 if (!pj_isdigit(*pj_optarg) ||
1168 app_config.cfg.srtp_secure_signaling > 2)
1169 {
1170 PJ_LOG(1,(THIS_FILE, "Invalid value for --srtp-secure option"));
1171 return -1;
1172 }
1173 cur_acc->srtp_secure_signaling = app_config.cfg.srtp_secure_signaling;
1174 break;
1175 case OPT_SRTP_KEYING:
1176 app_config.srtp_keying = my_atoi(pj_optarg);
1177 if (!pj_isdigit(*pj_optarg) || app_config.srtp_keying < 0 ||
1178 app_config.srtp_keying > 1)
1179 {
1180 PJ_LOG(1,(THIS_FILE, "Invalid value for --srtp-keying option"));
1181 return -1;
1182 }
1183 /* Set SRTP keying to use DTLS over SDES */
1184 if (app_config.srtp_keying == 1) {
1185 pjsua_srtp_opt *srtp_opt = &app_config.cfg.srtp_opt;
1186 srtp_opt->keying_count = 2;
1187 srtp_opt->keying[0] = PJMEDIA_SRTP_KEYING_DTLS_SRTP;
1188 srtp_opt->keying[1] = PJMEDIA_SRTP_KEYING_SDES;
1189
1190 cur_acc->srtp_opt.keying_count = 2;
1191 cur_acc->srtp_opt.keying[0] = PJMEDIA_SRTP_KEYING_DTLS_SRTP;
1192 cur_acc->srtp_opt.keying[1] = PJMEDIA_SRTP_KEYING_SDES;
1193 }
1194 break;
1195 #endif
1196
1197 case OPT_RTP_PORT:
1198 cfg->rtp_cfg.port = my_atoi(pj_optarg);
1199 if (cfg->rtp_cfg.port == 0) {
1200 enum { START_PORT=4000 };
1201 unsigned range;
1202
1203 range = (65535-START_PORT-PJSUA_MAX_CALLS*2);
1204 cfg->rtp_cfg.port = START_PORT +
1205 ((pj_rand() % range) & 0xFFFE);
1206 }
1207
1208 if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) {
1209 PJ_LOG(1,(THIS_FILE,
1210 "Error: rtp-port argument value "
1211 "(expecting 1-65535"));
1212 return -1;
1213 }
1214 break;
1215
1216 case OPT_DIS_CODEC:
1217 cfg->codec_dis[cfg->codec_dis_cnt++] = pj_str(pj_optarg);
1218 break;
1219
1220 case OPT_ADD_CODEC:
1221 cfg->codec_arg[cfg->codec_cnt++] = pj_str(pj_optarg);
1222 break;
1223
1224 /* These options were no longer valid after new pjsua */
1225 /*
1226 case OPT_COMPLEXITY:
1227 cfg->complexity = my_atoi(pj_optarg);
1228 if (cfg->complexity < 0 || cfg->complexity > 10) {
1229 PJ_LOG(1,(THIS_FILE,
1230 "Error: invalid --complexity (expecting 0-10"));
1231 return -1;
1232 }
1233 break;
1234 */
1235
1236 case OPT_DURATION:
1237 cfg->duration = my_atoi(pj_optarg);
1238 break;
1239
1240 case OPT_THREAD_CNT:
1241 cfg->cfg.thread_cnt = my_atoi(pj_optarg);
1242 if (cfg->cfg.thread_cnt > 128) {
1243 PJ_LOG(1,(THIS_FILE,
1244 "Error: invalid --thread-cnt option"));
1245 return -1;
1246 }
1247 break;
1248
1249 case OPT_PTIME:
1250 cfg->media_cfg.ptime = my_atoi(pj_optarg);
1251 if (cfg->media_cfg.ptime < 10 || cfg->media_cfg.ptime > 1000) {
1252 PJ_LOG(1,(THIS_FILE,
1253 "Error: invalid --ptime option"));
1254 return -1;
1255 }
1256 break;
1257
1258 case OPT_NO_VAD:
1259 cfg->media_cfg.no_vad = PJ_TRUE;
1260 break;
1261
1262 case OPT_EC_TAIL:
1263 cfg->media_cfg.ec_tail_len = my_atoi(pj_optarg);
1264 if (cfg->media_cfg.ec_tail_len > 1000) {
1265 PJ_LOG(1,(THIS_FILE, "I think the ec-tail length setting "
1266 "is too big"));
1267 return -1;
1268 }
1269 break;
1270
1271 case OPT_EC_OPT:
1272 cfg->media_cfg.ec_options = my_atoi(pj_optarg);
1273 break;
1274
1275 case OPT_QUALITY:
1276 cfg->media_cfg.quality = my_atoi(pj_optarg);
1277 if (cfg->media_cfg.quality > 10) {
1278 PJ_LOG(1,(THIS_FILE,
1279 "Error: invalid --quality (expecting 0-10"));
1280 return -1;
1281 }
1282 break;
1283
1284 case OPT_ILBC_MODE:
1285 cfg->media_cfg.ilbc_mode = my_atoi(pj_optarg);
1286 if (cfg->media_cfg.ilbc_mode!=20 && cfg->media_cfg.ilbc_mode!=30) {
1287 PJ_LOG(1,(THIS_FILE,
1288 "Error: invalid --ilbc-mode (expecting 20 or 30"));
1289 return -1;
1290 }
1291 break;
1292
1293 case OPT_RX_DROP_PCT:
1294 cfg->media_cfg.rx_drop_pct = my_atoi(pj_optarg);
1295 if (cfg->media_cfg.rx_drop_pct > 100) {
1296 PJ_LOG(1,(THIS_FILE,
1297 "Error: invalid --rx-drop-pct (expecting <= 100"));
1298 return -1;
1299 }
1300 break;
1301
1302 case OPT_TX_DROP_PCT:
1303 cfg->media_cfg.tx_drop_pct = my_atoi(pj_optarg);
1304 if (cfg->media_cfg.tx_drop_pct > 100) {
1305 PJ_LOG(1,(THIS_FILE,
1306 "Error: invalid --tx-drop-pct (expecting <= 100"));
1307 return -1;
1308 }
1309 break;
1310
1311 case OPT_AUTO_ANSWER:
1312 cfg->auto_answer = my_atoi(pj_optarg);
1313 if (cfg->auto_answer < 100 || cfg->auto_answer > 699) {
1314 PJ_LOG(1,(THIS_FILE,
1315 "Error: invalid code in --auto-answer "
1316 "(expecting 100-699"));
1317 return -1;
1318 }
1319 break;
1320
1321 case OPT_MAX_CALLS:
1322 cfg->cfg.max_calls = my_atoi(pj_optarg);
1323 if (cfg->cfg.max_calls < 1 || cfg->cfg.max_calls > PJSUA_MAX_CALLS) {
1324 PJ_LOG(1,(THIS_FILE,"Error: maximum call setting exceeds "
1325 "compile time limit (PJSUA_MAX_CALLS=%d)",
1326 PJSUA_MAX_CALLS));
1327 return -1;
1328 }
1329 break;
1330
1331 #if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0)
1332 case OPT_USE_TLS:
1333 cfg->use_tls = PJ_TRUE;
1334 break;
1335
1336 case OPT_TLS_CA_FILE:
1337 cfg->udp_cfg.tls_setting.ca_list_file = pj_str(pj_optarg);
1338 break;
1339
1340 case OPT_TLS_CERT_FILE:
1341 cfg->udp_cfg.tls_setting.cert_file = pj_str(pj_optarg);
1342 break;
1343
1344 case OPT_TLS_PRIV_FILE:
1345 cfg->udp_cfg.tls_setting.privkey_file = pj_str(pj_optarg);
1346 break;
1347
1348 case OPT_TLS_PASSWORD:
1349 cfg->udp_cfg.tls_setting.password = pj_str(pj_optarg);
1350 break;
1351
1352 case OPT_TLS_VERIFY_SERVER:
1353 cfg->udp_cfg.tls_setting.verify_server = PJ_TRUE;
1354 break;
1355
1356 case OPT_TLS_VERIFY_CLIENT:
1357 cfg->udp_cfg.tls_setting.verify_client = PJ_TRUE;
1358 cfg->udp_cfg.tls_setting.require_client_cert = PJ_TRUE;
1359 break;
1360
1361 case OPT_TLS_NEG_TIMEOUT:
1362 cfg->udp_cfg.tls_setting.timeout.sec = atoi(pj_optarg);
1363 break;
1364
1365 case OPT_TLS_CIPHER:
1366 {
1367 pj_ssl_cipher cipher;
1368
1369 if (pj_ansi_strnicmp(pj_optarg, "0x", 2) == 0) {
1370 pj_str_t cipher_st = pj_str(pj_optarg + 2);
1371 cipher = (pj_ssl_cipher)pj_strtoul2(&cipher_st, NULL, 16);
1372 } else {
1373 cipher = atoi(pj_optarg);
1374 }
1375
1376 if (pj_ssl_cipher_is_supported(cipher)) {
1377 static pj_ssl_cipher tls_ciphers[PJ_SSL_SOCK_MAX_CIPHERS];
1378
1379 tls_ciphers[cfg->udp_cfg.tls_setting.ciphers_num++] = cipher;
1380 cfg->udp_cfg.tls_setting.ciphers = tls_ciphers;
1381 } else {
1382 pj_ssl_cipher ciphers[PJ_SSL_SOCK_MAX_CIPHERS];
1383 unsigned j, ciphers_cnt;
1384
1385 ciphers_cnt = PJ_ARRAY_SIZE(ciphers);
1386 pj_ssl_cipher_get_availables(ciphers, &ciphers_cnt);
1387
1388 PJ_LOG(1,(THIS_FILE, "Cipher \"%s\" is not supported by "
1389 "TLS/SSL backend.", pj_optarg));
1390 printf("Available TLS/SSL ciphers (%d):\n", ciphers_cnt);
1391 for (j=0; j<ciphers_cnt; ++j)
1392 printf("- 0x%06X: %s\n", ciphers[j], pj_ssl_cipher_name(ciphers[j]));
1393 return -1;
1394 }
1395 }
1396 break;
1397 #endif /* PJSIP_HAS_TLS_TRANSPORT */
1398
1399 case OPT_CAPTURE_DEV:
1400 cfg->capture_dev = atoi(pj_optarg);
1401 break;
1402
1403 case OPT_PLAYBACK_DEV:
1404 cfg->playback_dev = atoi(pj_optarg);
1405 break;
1406
1407 case OPT_STDOUT_REFRESH:
1408 stdout_refresh = atoi(pj_optarg);
1409 break;
1410
1411 case OPT_STDOUT_REFRESH_TEXT:
1412 stdout_refresh_text = pj_optarg;
1413 break;
1414
1415 #ifdef _IONBF
1416 case OPT_STDOUT_NO_BUF:
1417 setvbuf(stdout, NULL, _IONBF, 0);
1418 cfg->log_cfg.cb = &log_writer_nobuf;
1419 break;
1420 #endif
1421
1422 case OPT_CAPTURE_LAT:
1423 cfg->capture_lat = atoi(pj_optarg);
1424 break;
1425
1426 case OPT_PLAYBACK_LAT:
1427 cfg->playback_lat = atoi(pj_optarg);
1428 break;
1429
1430 case OPT_SND_AUTO_CLOSE:
1431 cfg->media_cfg.snd_auto_close_time = atoi(pj_optarg);
1432 break;
1433
1434 case OPT_NO_TONES:
1435 cfg->no_tones = PJ_TRUE;
1436 break;
1437
1438 case OPT_JB_MAX_SIZE:
1439 cfg->media_cfg.jb_max = atoi(pj_optarg);
1440 break;
1441
1442 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
1443 case OPT_IPV6:
1444 cfg->ipv6 = PJ_TRUE;
1445 break;
1446 #endif
1447 case OPT_QOS:
1448 cfg->enable_qos = PJ_TRUE;
1449 /* Set RTP traffic type to Voice */
1450 cfg->rtp_cfg.qos_type = PJ_QOS_TYPE_VOICE;
1451 /* Directly apply DSCP value to SIP traffic. Say lets
1452 * set it to CS3 (DSCP 011000). Note that this will not
1453 * work on all platforms.
1454 */
1455 cfg->udp_cfg.qos_params.flags = PJ_QOS_PARAM_HAS_DSCP;
1456 cfg->udp_cfg.qos_params.dscp_val = 0x18;
1457 break;
1458 case OPT_VIDEO:
1459 cfg->vid.vid_cnt = 1;
1460 cfg->vid.in_auto_show = PJ_TRUE;
1461 cfg->vid.out_auto_transmit = PJ_TRUE;
1462 break;
1463 case OPT_EXTRA_AUDIO:
1464 cfg->aud_cnt++;
1465 break;
1466
1467 case OPT_VCAPTURE_DEV:
1468 cfg->vid.vcapture_dev = atoi(pj_optarg);
1469 cur_acc->vid_cap_dev = cfg->vid.vcapture_dev;
1470 break;
1471
1472 case OPT_VRENDER_DEV:
1473 cfg->vid.vrender_dev = atoi(pj_optarg);
1474 cur_acc->vid_rend_dev = cfg->vid.vrender_dev;
1475 break;
1476
1477 case OPT_PLAY_AVI:
1478 if (app_config.avi_cnt >= PJSUA_APP_MAX_AVI) {
1479 PJ_LOG(1,(THIS_FILE, "Too many AVIs"));
1480 return -1;
1481 }
1482 app_config.avi[app_config.avi_cnt++].path = pj_str(pj_optarg);
1483 break;
1484
1485 case OPT_AUTO_PLAY_AVI:
1486 app_config.avi_auto_play = PJ_TRUE;
1487 break;
1488
1489 case OPT_USE_CLI:
1490 cfg->use_cli = PJ_TRUE;
1491 break;
1492
1493 case OPT_CLI_TELNET_PORT:
1494 cfg->cli_cfg.telnet_cfg.port = (pj_uint16_t)atoi(pj_optarg);
1495 cfg->cli_cfg.cli_fe |= CLI_FE_TELNET;
1496 break;
1497
1498 case OPT_DISABLE_CLI_CONSOLE:
1499 cfg->cli_cfg.cli_fe &= (~CLI_FE_CONSOLE);
1500 break;
1501
1502 default:
1503 PJ_LOG(1,(THIS_FILE,
1504 "Argument \"%s\" is not valid. Use --help to see help",
1505 argv[pj_optind-1]));
1506 return -1;
1507 }
1508 }
1509
1510 if (pj_optind != argc) {
1511 pj_str_t uri_arg;
1512
1513 if (pjsua_verify_url(argv[pj_optind]) != PJ_SUCCESS) {
1514 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
1515 return -1;
1516 }
1517 uri_arg = pj_str(argv[pj_optind]);
1518 if (uri_to_call)
1519 *uri_to_call = uri_arg;
1520 pj_optind++;
1521
1522 /* Add URI to call to buddy list if it's not already there */
1523 for (i=0; i<cfg->buddy_cnt; ++i) {
1524 if (pj_stricmp(&cfg->buddy_cfg[i].uri, &uri_arg)==0)
1525 break;
1526 }
1527 if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) {
1528 cfg->buddy_cfg[cfg->buddy_cnt++].uri = uri_arg;
1529 }
1530
1531 } else {
1532 if (uri_to_call)
1533 uri_to_call->slen = 0;
1534 }
1535
1536 if (pj_optind != argc) {
1537 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
1538 return PJ_EINVAL;
1539 }
1540
1541 if (cfg->acc_cfg[cfg->acc_cnt].id.slen)
1542 cfg->acc_cnt++;
1543
1544 for (i=0; i<cfg->acc_cnt; ++i) {
1545 pjsua_acc_config *acfg = &cfg->acc_cfg[i];
1546
1547 if (acfg->cred_info[acfg->cred_count].username.slen)
1548 {
1549 acfg->cred_count++;
1550 }
1551
1552 if (acfg->ice_cfg.enable_ice) {
1553 acfg->ice_cfg_use = PJSUA_ICE_CONFIG_USE_CUSTOM;
1554 }
1555 if (acfg->turn_cfg.enable_turn) {
1556 acfg->turn_cfg_use = PJSUA_TURN_CONFIG_USE_CUSTOM;
1557 }
1558
1559 /* When IMS mode is enabled for the account, verify that settings
1560 * are okay.
1561 */
1562 /* For now we check if IMS mode is activated by looking if
1563 * initial_auth is set.
1564 */
1565 if (acfg->auth_pref.initial_auth && acfg->cred_count) {
1566 /* Realm must point to the real domain */
1567 if (*acfg->cred_info[0].realm.ptr=='*') {
1568 PJ_LOG(1,(THIS_FILE,
1569 "Error: cannot use '*' as realm with IMS"));
1570 return PJ_EINVAL;
1571 }
1572
1573 /* Username for authentication must be in a@b format */
1574 if (strchr(acfg->cred_info[0].username.ptr, '@')==0) {
1575 PJ_LOG(1,(THIS_FILE,
1576 "Error: Username for authentication must "
1577 "be in user@domain format with IMS"));
1578 return PJ_EINVAL;
1579 }
1580 }
1581 }
1582 return PJ_SUCCESS;
1583 }
1584
1585 /* Set default config. */
default_config()1586 static void default_config()
1587 {
1588 char tmp[80];
1589 unsigned i;
1590 pjsua_app_config *cfg = &app_config;
1591
1592 pjsua_config_default(&cfg->cfg);
1593 pj_ansi_sprintf(tmp, "PJSUA v%s %s", pj_get_version(),
1594 pj_get_sys_info()->info.ptr);
1595 pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
1596
1597 pjsua_logging_config_default(&cfg->log_cfg);
1598 pjsua_media_config_default(&cfg->media_cfg);
1599 pjsua_transport_config_default(&cfg->udp_cfg);
1600 cfg->udp_cfg.port = 5060;
1601 pjsua_transport_config_default(&cfg->rtp_cfg);
1602 cfg->rtp_cfg.port = 4000;
1603 cfg->redir_op = PJSIP_REDIRECT_ACCEPT_REPLACE;
1604 cfg->duration = PJSUA_APP_NO_LIMIT_DURATION;
1605 cfg->wav_id = PJSUA_INVALID_ID;
1606 cfg->rec_id = PJSUA_INVALID_ID;
1607 cfg->wav_port = PJSUA_INVALID_ID;
1608 cfg->rec_port = PJSUA_INVALID_ID;
1609 cfg->mic_level = cfg->speaker_level = 1.0;
1610 cfg->capture_dev = PJSUA_INVALID_ID;
1611 cfg->playback_dev = PJSUA_INVALID_ID;
1612 cfg->capture_lat = PJMEDIA_SND_DEFAULT_REC_LATENCY;
1613 cfg->playback_lat = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
1614 cfg->ringback_slot = PJSUA_INVALID_ID;
1615 cfg->ring_slot = PJSUA_INVALID_ID;
1616
1617 for (i=0; i<PJ_ARRAY_SIZE(cfg->acc_cfg); ++i)
1618 pjsua_acc_config_default(&cfg->acc_cfg[i]);
1619
1620 for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i)
1621 pjsua_buddy_config_default(&cfg->buddy_cfg[i]);
1622
1623 cfg->vid.vcapture_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
1624 cfg->vid.vrender_dev = PJMEDIA_VID_DEFAULT_RENDER_DEV;
1625 cfg->aud_cnt = 1;
1626
1627 cfg->avi_def_idx = PJSUA_INVALID_ID;
1628
1629 cfg->use_cli = PJ_FALSE;
1630 cfg->cli_cfg.cli_fe = CLI_FE_CONSOLE;
1631 cfg->cli_cfg.telnet_cfg.port = 0;
1632 }
1633
parse_config(int argc,char * argv[],pj_str_t * uri_arg)1634 static pj_status_t parse_config(int argc, char *argv[], pj_str_t *uri_arg)
1635 {
1636 pj_status_t status;
1637
1638 /* Initialize default config */
1639 default_config();
1640
1641 /* Parse the arguments */
1642 status = parse_args(argc, argv, uri_arg);
1643 return status;
1644 }
1645
load_config(int argc,char ** argv,pj_str_t * uri_arg)1646 pj_status_t load_config(int argc,
1647 char **argv,
1648 pj_str_t *uri_arg)
1649 {
1650 pj_status_t status;
1651 pj_bool_t use_cli = PJ_FALSE;
1652 int cli_fe = 0;
1653 pj_uint16_t cli_telnet_port = 0;
1654
1655 /** CLI options are not changable **/
1656 if (app_running) {
1657 use_cli = app_config.use_cli;
1658 cli_fe = app_config.cli_cfg.cli_fe;
1659 cli_telnet_port = app_config.cli_cfg.telnet_cfg.port;
1660 }
1661
1662 status = parse_config(argc, argv, uri_arg);
1663 if (status != PJ_SUCCESS)
1664 return status;
1665
1666 if (app_running) {
1667 app_config.use_cli = use_cli;
1668 app_config.cli_cfg.cli_fe = cli_fe;
1669 app_config.cli_cfg.telnet_cfg.port = cli_telnet_port;
1670 }
1671
1672 return status;
1673 }
1674
1675 /*
1676 * Save account settings
1677 */
write_account_settings(int acc_index,pj_str_t * result)1678 static void write_account_settings(int acc_index, pj_str_t *result)
1679 {
1680 unsigned i;
1681 char line[128];
1682 pjsua_acc_config *acc_cfg = &app_config.acc_cfg[acc_index];
1683
1684
1685 pj_ansi_sprintf(line, "\n#\n# Account %d:\n#\n", acc_index);
1686 pj_strcat2(result, line);
1687
1688
1689 /* Identity */
1690 if (acc_cfg->id.slen) {
1691 pj_ansi_sprintf(line, "--id %.*s\n",
1692 (int)acc_cfg->id.slen,
1693 acc_cfg->id.ptr);
1694 pj_strcat2(result, line);
1695 }
1696
1697 /* Registrar server */
1698 if (acc_cfg->reg_uri.slen) {
1699 pj_ansi_sprintf(line, "--registrar %.*s\n",
1700 (int)acc_cfg->reg_uri.slen,
1701 acc_cfg->reg_uri.ptr);
1702 pj_strcat2(result, line);
1703
1704 pj_ansi_sprintf(line, "--reg-timeout %u\n",
1705 acc_cfg->reg_timeout);
1706 pj_strcat2(result, line);
1707 }
1708
1709 /* Contact */
1710 if (acc_cfg->force_contact.slen) {
1711 pj_ansi_sprintf(line, "--contact %.*s\n",
1712 (int)acc_cfg->force_contact.slen,
1713 acc_cfg->force_contact.ptr);
1714 pj_strcat2(result, line);
1715 }
1716
1717 /* Contact header parameters */
1718 if (acc_cfg->contact_params.slen) {
1719 pj_ansi_sprintf(line, "--contact-params %.*s\n",
1720 (int)acc_cfg->contact_params.slen,
1721 acc_cfg->contact_params.ptr);
1722 pj_strcat2(result, line);
1723 }
1724
1725 /* Contact URI parameters */
1726 if (acc_cfg->contact_uri_params.slen) {
1727 pj_ansi_sprintf(line, "--contact-uri-params %.*s\n",
1728 (int)acc_cfg->contact_uri_params.slen,
1729 acc_cfg->contact_uri_params.ptr);
1730 pj_strcat2(result, line);
1731 }
1732
1733 /* */
1734 if (acc_cfg->allow_contact_rewrite!=1)
1735 {
1736 pj_ansi_sprintf(line, "--auto-update-nat %i\n",
1737 (int)acc_cfg->allow_contact_rewrite);
1738 pj_strcat2(result, line);
1739 }
1740
1741 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
1742 /* SRTP */
1743 if (acc_cfg->use_srtp) {
1744 int use_srtp = (int)acc_cfg->use_srtp;
1745 if (use_srtp == PJMEDIA_SRTP_OPTIONAL &&
1746 acc_cfg->srtp_optional_dup_offer)
1747 {
1748 use_srtp = 3;
1749 }
1750 pj_ansi_sprintf(line, "--use-srtp %i\n", use_srtp);
1751 pj_strcat2(result, line);
1752 }
1753 if (acc_cfg->srtp_secure_signaling !=
1754 PJSUA_DEFAULT_SRTP_SECURE_SIGNALING)
1755 {
1756 pj_ansi_sprintf(line, "--srtp-secure %d\n",
1757 acc_cfg->srtp_secure_signaling);
1758 pj_strcat2(result, line);
1759 }
1760 #endif
1761
1762 /* Proxy */
1763 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
1764 pj_ansi_sprintf(line, "--proxy %.*s\n",
1765 (int)acc_cfg->proxy[i].slen,
1766 acc_cfg->proxy[i].ptr);
1767 pj_strcat2(result, line);
1768 }
1769
1770 /* Credentials */
1771 for (i=0; i<acc_cfg->cred_count; ++i) {
1772 if (acc_cfg->cred_info[i].realm.slen) {
1773 pj_ansi_sprintf(line, "--realm %.*s\n",
1774 (int)acc_cfg->cred_info[i].realm.slen,
1775 acc_cfg->cred_info[i].realm.ptr);
1776 pj_strcat2(result, line);
1777 }
1778
1779 if (acc_cfg->cred_info[i].username.slen) {
1780 pj_ansi_sprintf(line, "--username %.*s\n",
1781 (int)acc_cfg->cred_info[i].username.slen,
1782 acc_cfg->cred_info[i].username.ptr);
1783 pj_strcat2(result, line);
1784 }
1785
1786 if (acc_cfg->cred_info[i].data.slen) {
1787 pj_ansi_sprintf(line, "--password %.*s\n",
1788 (int)acc_cfg->cred_info[i].data.slen,
1789 acc_cfg->cred_info[i].data.ptr);
1790 pj_strcat2(result, line);
1791 }
1792
1793 if (i != acc_cfg->cred_count - 1)
1794 pj_strcat2(result, "--next-cred\n");
1795 }
1796
1797 /* reg-use-proxy */
1798 if (acc_cfg->reg_use_proxy != 3) {
1799 pj_ansi_sprintf(line, "--reg-use-proxy %d\n",
1800 acc_cfg->reg_use_proxy);
1801 pj_strcat2(result, line);
1802 }
1803
1804 /* rereg-delay */
1805 if (acc_cfg->reg_retry_interval != PJSUA_REG_RETRY_INTERVAL) {
1806 pj_ansi_sprintf(line, "--rereg-delay %d\n",
1807 acc_cfg->reg_retry_interval);
1808 pj_strcat2(result, line);
1809 }
1810
1811 /* 100rel extension */
1812 if (acc_cfg->require_100rel == PJSUA_100REL_MANDATORY) {
1813 pj_strcat2(result, "--use-100rel\n");
1814 }
1815
1816 /* Session Timer extension */
1817 if (acc_cfg->use_timer) {
1818 pj_ansi_sprintf(line, "--use-timer %d\n",
1819 acc_cfg->use_timer);
1820 pj_strcat2(result, line);
1821 }
1822 if (acc_cfg->timer_setting.min_se != 90) {
1823 pj_ansi_sprintf(line, "--timer-min-se %d\n",
1824 acc_cfg->timer_setting.min_se);
1825 pj_strcat2(result, line);
1826 }
1827 if (acc_cfg->timer_setting.sess_expires != PJSIP_SESS_TIMER_DEF_SE) {
1828 pj_ansi_sprintf(line, "--timer-se %d\n",
1829 acc_cfg->timer_setting.sess_expires);
1830 pj_strcat2(result, line);
1831 }
1832
1833 /* Publish */
1834 if (acc_cfg->publish_enabled)
1835 pj_strcat2(result, "--publish\n");
1836
1837 /* MWI */
1838 if (acc_cfg->mwi_enabled)
1839 pj_strcat2(result, "--mwi\n");
1840
1841 if (acc_cfg->sip_stun_use != PJSUA_STUN_USE_DEFAULT ||
1842 acc_cfg->media_stun_use != PJSUA_STUN_USE_DEFAULT)
1843 {
1844 pj_strcat2(result, "--disable-stun\n");
1845 }
1846
1847 /* Media Transport*/
1848 if (acc_cfg->ice_cfg.enable_ice)
1849 pj_strcat2(result, "--use-ice\n");
1850
1851 if (acc_cfg->ice_cfg.ice_opt.aggressive == PJ_FALSE)
1852 pj_strcat2(result, "--ice-regular\n");
1853
1854 if (acc_cfg->ice_cfg.ice_opt.trickle > 0) {
1855 pj_ansi_sprintf(line, "--ice-trickle %d\n",
1856 acc_cfg->ice_cfg.ice_opt.trickle);
1857 pj_strcat2(result, line);
1858 }
1859
1860 if (acc_cfg->turn_cfg.enable_turn)
1861 pj_strcat2(result, "--use-turn\n");
1862
1863 if (acc_cfg->ice_cfg.ice_max_host_cands >= 0) {
1864 pj_ansi_sprintf(line, "--ice_max_host_cands %d\n",
1865 acc_cfg->ice_cfg.ice_max_host_cands);
1866 pj_strcat2(result, line);
1867 }
1868
1869 if (acc_cfg->ice_cfg.ice_no_rtcp)
1870 pj_strcat2(result, "--ice-no-rtcp\n");
1871
1872 if (acc_cfg->turn_cfg.turn_server.slen) {
1873 pj_ansi_sprintf(line, "--turn-srv %.*s\n",
1874 (int)acc_cfg->turn_cfg.turn_server.slen,
1875 acc_cfg->turn_cfg.turn_server.ptr);
1876 pj_strcat2(result, line);
1877 }
1878
1879 if (acc_cfg->turn_cfg.turn_conn_type == PJ_TURN_TP_TCP)
1880 pj_strcat2(result, "--turn-tcp\n");
1881
1882 if (acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.username.slen) {
1883 pj_ansi_sprintf(line, "--turn-user %.*s\n",
1884 (int)acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.username.slen,
1885 acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.username.ptr);
1886 pj_strcat2(result, line);
1887 }
1888
1889 if (acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.data.slen) {
1890 pj_ansi_sprintf(line, "--turn-passwd %.*s\n",
1891 (int)acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.data.slen,
1892 acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.data.ptr);
1893 pj_strcat2(result, line);
1894 }
1895
1896 if (acc_cfg->enable_rtcp_mux)
1897 pj_strcat2(result, "--rtcp-mux\n");
1898 }
1899
1900 /*
1901 * Write settings.
1902 */
write_settings(pjsua_app_config * config,char * buf,pj_size_t max)1903 int write_settings(pjsua_app_config *config, char *buf, pj_size_t max)
1904 {
1905 unsigned acc_index;
1906 unsigned i;
1907 pj_str_t cfg;
1908 char line[128];
1909
1910 PJ_UNUSED_ARG(max);
1911
1912 cfg.ptr = buf;
1913 cfg.slen = 0;
1914
1915 /* Logging. */
1916 pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
1917 pj_ansi_sprintf(line, "--log-level %d\n",
1918 config->log_cfg.level);
1919 pj_strcat2(&cfg, line);
1920
1921 pj_ansi_sprintf(line, "--app-log-level %d\n",
1922 config->log_cfg.console_level);
1923 pj_strcat2(&cfg, line);
1924
1925 if (config->log_cfg.log_filename.slen) {
1926 pj_ansi_sprintf(line, "--log-file %.*s\n",
1927 (int)config->log_cfg.log_filename.slen,
1928 config->log_cfg.log_filename.ptr);
1929 pj_strcat2(&cfg, line);
1930 }
1931
1932 if (config->log_cfg.log_file_flags & PJ_O_APPEND) {
1933 pj_strcat2(&cfg, "--log-append\n");
1934 }
1935
1936 /* Save account settings. */
1937 for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) {
1938
1939 write_account_settings(acc_index, &cfg);
1940
1941 if (acc_index < config->acc_cnt-1)
1942 pj_strcat2(&cfg, "--next-account\n");
1943 }
1944
1945 pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n");
1946
1947 /* Nameservers */
1948 for (i=0; i<config->cfg.nameserver_count; ++i) {
1949 pj_ansi_sprintf(line, "--nameserver %.*s\n",
1950 (int)config->cfg.nameserver[i].slen,
1951 config->cfg.nameserver[i].ptr);
1952 pj_strcat2(&cfg, line);
1953 }
1954
1955 /* Outbound proxy */
1956 for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) {
1957 pj_ansi_sprintf(line, "--outbound %.*s\n",
1958 (int)config->cfg.outbound_proxy[i].slen,
1959 config->cfg.outbound_proxy[i].ptr);
1960 pj_strcat2(&cfg, line);
1961 }
1962
1963 /* Transport options */
1964 if (config->ipv6) {
1965 pj_strcat2(&cfg, "--ipv6\n");
1966 }
1967 if (config->enable_qos) {
1968 pj_strcat2(&cfg, "--set-qos\n");
1969 }
1970
1971 /* UDP Transport. */
1972 pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port);
1973 pj_strcat2(&cfg, line);
1974
1975 /* IP address, if any. */
1976 if (config->udp_cfg.public_addr.slen) {
1977 pj_ansi_sprintf(line, "--ip-addr %.*s\n",
1978 (int)config->udp_cfg.public_addr.slen,
1979 config->udp_cfg.public_addr.ptr);
1980 pj_strcat2(&cfg, line);
1981 }
1982
1983 /* Bound IP address, if any. */
1984 if (config->udp_cfg.bound_addr.slen) {
1985 pj_ansi_sprintf(line, "--bound-addr %.*s\n",
1986 (int)config->udp_cfg.bound_addr.slen,
1987 config->udp_cfg.bound_addr.ptr);
1988 pj_strcat2(&cfg, line);
1989 }
1990
1991 /* No TCP ? */
1992 if (config->no_tcp) {
1993 pj_strcat2(&cfg, "--no-tcp\n");
1994 }
1995
1996 /* No UDP ? */
1997 if (config->no_udp) {
1998 pj_strcat2(&cfg, "--no-udp\n");
1999 }
2000
2001 /* STUN */
2002 for (i=0; i<config->cfg.stun_srv_cnt; ++i) {
2003 pj_ansi_sprintf(line, "--stun-srv %.*s\n",
2004 (int)config->cfg.stun_srv[i].slen,
2005 config->cfg.stun_srv[i].ptr);
2006 pj_strcat2(&cfg, line);
2007 }
2008
2009 #if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0)
2010 /* TLS */
2011 if (config->use_tls)
2012 pj_strcat2(&cfg, "--use-tls\n");
2013 if (config->udp_cfg.tls_setting.ca_list_file.slen) {
2014 pj_ansi_sprintf(line, "--tls-ca-file %.*s\n",
2015 (int)config->udp_cfg.tls_setting.ca_list_file.slen,
2016 config->udp_cfg.tls_setting.ca_list_file.ptr);
2017 pj_strcat2(&cfg, line);
2018 }
2019 if (config->udp_cfg.tls_setting.cert_file.slen) {
2020 pj_ansi_sprintf(line, "--tls-cert-file %.*s\n",
2021 (int)config->udp_cfg.tls_setting.cert_file.slen,
2022 config->udp_cfg.tls_setting.cert_file.ptr);
2023 pj_strcat2(&cfg, line);
2024 }
2025 if (config->udp_cfg.tls_setting.privkey_file.slen) {
2026 pj_ansi_sprintf(line, "--tls-privkey-file %.*s\n",
2027 (int)config->udp_cfg.tls_setting.privkey_file.slen,
2028 config->udp_cfg.tls_setting.privkey_file.ptr);
2029 pj_strcat2(&cfg, line);
2030 }
2031
2032 if (config->udp_cfg.tls_setting.password.slen) {
2033 pj_ansi_sprintf(line, "--tls-password %.*s\n",
2034 (int)config->udp_cfg.tls_setting.password.slen,
2035 config->udp_cfg.tls_setting.password.ptr);
2036 pj_strcat2(&cfg, line);
2037 }
2038
2039 if (config->udp_cfg.tls_setting.verify_server)
2040 pj_strcat2(&cfg, "--tls-verify-server\n");
2041
2042 if (config->udp_cfg.tls_setting.verify_client)
2043 pj_strcat2(&cfg, "--tls-verify-client\n");
2044
2045 if (config->udp_cfg.tls_setting.timeout.sec) {
2046 pj_ansi_sprintf(line, "--tls-neg-timeout %d\n",
2047 (int)config->udp_cfg.tls_setting.timeout.sec);
2048 pj_strcat2(&cfg, line);
2049 }
2050
2051 for (i=0; i<config->udp_cfg.tls_setting.ciphers_num; ++i) {
2052 pj_ansi_sprintf(line, "--tls-cipher 0x%06X # %s\n",
2053 config->udp_cfg.tls_setting.ciphers[i],
2054 pj_ssl_cipher_name(config->udp_cfg.tls_setting.ciphers[i]));
2055 pj_strcat2(&cfg, line);
2056 }
2057 #endif
2058
2059 pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n");
2060
2061 /* Video & extra audio */
2062 for (i=0; i<config->vid.vid_cnt; ++i) {
2063 pj_strcat2(&cfg, "--video\n");
2064 }
2065 for (i=1; i<config->aud_cnt; ++i) {
2066 pj_strcat2(&cfg, "--extra-audio\n");
2067 }
2068
2069 /* SRTP */
2070 #if PJMEDIA_HAS_SRTP
2071 if (app_config.cfg.use_srtp != PJSUA_DEFAULT_USE_SRTP) {
2072 int use_srtp = (int)app_config.cfg.use_srtp;
2073 if (use_srtp == PJMEDIA_SRTP_OPTIONAL &&
2074 app_config.cfg.srtp_optional_dup_offer)
2075 {
2076 use_srtp = 3;
2077 }
2078 pj_ansi_sprintf(line, "--use-srtp %d\n", use_srtp);
2079 pj_strcat2(&cfg, line);
2080 }
2081 if (app_config.cfg.srtp_secure_signaling !=
2082 PJSUA_DEFAULT_SRTP_SECURE_SIGNALING)
2083 {
2084 pj_ansi_sprintf(line, "--srtp-secure %d\n",
2085 app_config.cfg.srtp_secure_signaling);
2086 pj_strcat2(&cfg, line);
2087 }
2088 if (app_config.srtp_keying >= 0 && app_config.srtp_keying <= 1)
2089 {
2090 pj_ansi_sprintf(line, "--srtp-keying %d\n",
2091 app_config.srtp_keying);
2092 pj_strcat2(&cfg, line);
2093 }
2094 #endif
2095
2096 /* Media */
2097 if (config->null_audio)
2098 pj_strcat2(&cfg, "--null-audio\n");
2099 if (config->auto_play)
2100 pj_strcat2(&cfg, "--auto-play\n");
2101 if (config->auto_loop)
2102 pj_strcat2(&cfg, "--auto-loop\n");
2103 if (config->auto_conf)
2104 pj_strcat2(&cfg, "--auto-conf\n");
2105 for (i=0; i<config->wav_count; ++i) {
2106 pj_ansi_sprintf(line, "--play-file %s\n",
2107 config->wav_files[i].ptr);
2108 pj_strcat2(&cfg, line);
2109 }
2110 for (i=0; i<config->tone_count; ++i) {
2111 pj_ansi_sprintf(line, "--play-tone %d,%d,%d,%d\n",
2112 config->tones[i].freq1, config->tones[i].freq2,
2113 config->tones[i].on_msec, config->tones[i].off_msec);
2114 pj_strcat2(&cfg, line);
2115 }
2116 if (config->rec_file.slen) {
2117 pj_ansi_sprintf(line, "--rec-file %s\n",
2118 config->rec_file.ptr);
2119 pj_strcat2(&cfg, line);
2120 }
2121 if (config->auto_rec)
2122 pj_strcat2(&cfg, "--auto-rec\n");
2123 if (config->capture_dev != PJSUA_INVALID_ID) {
2124 pj_ansi_sprintf(line, "--capture-dev %d\n", config->capture_dev);
2125 pj_strcat2(&cfg, line);
2126 }
2127 if (config->playback_dev != PJSUA_INVALID_ID) {
2128 pj_ansi_sprintf(line, "--playback-dev %d\n", config->playback_dev);
2129 pj_strcat2(&cfg, line);
2130 }
2131 if (config->media_cfg.snd_auto_close_time != -1) {
2132 pj_ansi_sprintf(line, "--snd-auto-close %d\n",
2133 config->media_cfg.snd_auto_close_time);
2134 pj_strcat2(&cfg, line);
2135 }
2136 if (config->no_tones) {
2137 pj_strcat2(&cfg, "--no-tones\n");
2138 }
2139 if (config->media_cfg.jb_max != -1) {
2140 pj_ansi_sprintf(line, "--jb-max-size %d\n",
2141 config->media_cfg.jb_max);
2142 pj_strcat2(&cfg, line);
2143 }
2144
2145 /* Sound device latency */
2146 if (config->capture_lat != PJMEDIA_SND_DEFAULT_REC_LATENCY) {
2147 pj_ansi_sprintf(line, "--capture-lat %d\n", config->capture_lat);
2148 pj_strcat2(&cfg, line);
2149 }
2150 if (config->playback_lat != PJMEDIA_SND_DEFAULT_PLAY_LATENCY) {
2151 pj_ansi_sprintf(line, "--playback-lat %d\n", config->playback_lat);
2152 pj_strcat2(&cfg, line);
2153 }
2154
2155 /* Media clock rate. */
2156 if (config->media_cfg.clock_rate != PJSUA_DEFAULT_CLOCK_RATE) {
2157 pj_ansi_sprintf(line, "--clock-rate %d\n",
2158 config->media_cfg.clock_rate);
2159 pj_strcat2(&cfg, line);
2160 } else {
2161 pj_ansi_sprintf(line, "#using default --clock-rate %d\n",
2162 config->media_cfg.clock_rate);
2163 pj_strcat2(&cfg, line);
2164 }
2165
2166 if (config->media_cfg.snd_clock_rate &&
2167 config->media_cfg.snd_clock_rate != config->media_cfg.clock_rate)
2168 {
2169 pj_ansi_sprintf(line, "--snd-clock-rate %d\n",
2170 config->media_cfg.snd_clock_rate);
2171 pj_strcat2(&cfg, line);
2172 }
2173
2174 /* Stereo mode. */
2175 if (config->media_cfg.channel_count == 2) {
2176 pj_ansi_sprintf(line, "--stereo\n");
2177 pj_strcat2(&cfg, line);
2178 }
2179
2180 /* quality */
2181 if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) {
2182 pj_ansi_sprintf(line, "--quality %d\n",
2183 config->media_cfg.quality);
2184 pj_strcat2(&cfg, line);
2185 } else {
2186 pj_ansi_sprintf(line, "#using default --quality %d\n",
2187 config->media_cfg.quality);
2188 pj_strcat2(&cfg, line);
2189 }
2190
2191 if (config->vid.vcapture_dev != PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
2192 pj_ansi_sprintf(line, "--vcapture-dev %d\n", config->vid.vcapture_dev);
2193 pj_strcat2(&cfg, line);
2194 }
2195 if (config->vid.vrender_dev != PJMEDIA_VID_DEFAULT_RENDER_DEV) {
2196 pj_ansi_sprintf(line, "--vrender-dev %d\n", config->vid.vrender_dev);
2197 pj_strcat2(&cfg, line);
2198 }
2199 for (i=0; i<config->avi_cnt; ++i) {
2200 pj_ansi_sprintf(line, "--play-avi %s\n", config->avi[i].path.ptr);
2201 pj_strcat2(&cfg, line);
2202 }
2203 if (config->avi_auto_play) {
2204 pj_ansi_sprintf(line, "--auto-play-avi\n");
2205 pj_strcat2(&cfg, line);
2206 }
2207
2208 /* ptime */
2209 if (config->media_cfg.ptime) {
2210 pj_ansi_sprintf(line, "--ptime %d\n",
2211 config->media_cfg.ptime);
2212 pj_strcat2(&cfg, line);
2213 }
2214
2215 /* no-vad */
2216 if (config->media_cfg.no_vad) {
2217 pj_strcat2(&cfg, "--no-vad\n");
2218 }
2219
2220 /* ec-tail */
2221 if (config->media_cfg.ec_tail_len != PJSUA_DEFAULT_EC_TAIL_LEN) {
2222 pj_ansi_sprintf(line, "--ec-tail %d\n",
2223 config->media_cfg.ec_tail_len);
2224 pj_strcat2(&cfg, line);
2225 } else {
2226 pj_ansi_sprintf(line, "#using default --ec-tail %d\n",
2227 config->media_cfg.ec_tail_len);
2228 pj_strcat2(&cfg, line);
2229 }
2230
2231 /* ec-opt */
2232 if (config->media_cfg.ec_options != 0) {
2233 pj_ansi_sprintf(line, "--ec-opt %d\n",
2234 config->media_cfg.ec_options);
2235 pj_strcat2(&cfg, line);
2236 }
2237
2238 /* ilbc-mode */
2239 if (config->media_cfg.ilbc_mode != PJSUA_DEFAULT_ILBC_MODE) {
2240 pj_ansi_sprintf(line, "--ilbc-mode %d\n",
2241 config->media_cfg.ilbc_mode);
2242 pj_strcat2(&cfg, line);
2243 } else {
2244 pj_ansi_sprintf(line, "#using default --ilbc-mode %d\n",
2245 config->media_cfg.ilbc_mode);
2246 pj_strcat2(&cfg, line);
2247 }
2248
2249 /* RTP drop */
2250 if (config->media_cfg.tx_drop_pct) {
2251 pj_ansi_sprintf(line, "--tx-drop-pct %d\n",
2252 config->media_cfg.tx_drop_pct);
2253 pj_strcat2(&cfg, line);
2254
2255 }
2256 if (config->media_cfg.rx_drop_pct) {
2257 pj_ansi_sprintf(line, "--rx-drop-pct %d\n",
2258 config->media_cfg.rx_drop_pct);
2259 pj_strcat2(&cfg, line);
2260
2261 }
2262
2263 /* Start RTP port. */
2264 pj_ansi_sprintf(line, "--rtp-port %d\n",
2265 config->rtp_cfg.port);
2266 pj_strcat2(&cfg, line);
2267
2268 /* Disable codec */
2269 for (i=0; i<config->codec_dis_cnt; ++i) {
2270 pj_ansi_sprintf(line, "--dis-codec %s\n",
2271 config->codec_dis[i].ptr);
2272 pj_strcat2(&cfg, line);
2273 }
2274 /* Add codec. */
2275 for (i=0; i<config->codec_cnt; ++i) {
2276 pj_ansi_sprintf(line, "--add-codec %s\n",
2277 config->codec_arg[i].ptr);
2278 pj_strcat2(&cfg, line);
2279 }
2280
2281 pj_strcat2(&cfg, "\n#\n# User agent:\n#\n");
2282
2283 /* Auto-answer. */
2284 if (config->auto_answer != 0) {
2285 pj_ansi_sprintf(line, "--auto-answer %d\n",
2286 config->auto_answer);
2287 pj_strcat2(&cfg, line);
2288 }
2289
2290 /* accept-redirect */
2291 if (config->redir_op != PJSIP_REDIRECT_ACCEPT_REPLACE) {
2292 pj_ansi_sprintf(line, "--accept-redirect %d\n",
2293 config->redir_op);
2294 pj_strcat2(&cfg, line);
2295 }
2296
2297 /* Max calls. */
2298 pj_ansi_sprintf(line, "--max-calls %d\n",
2299 config->cfg.max_calls);
2300 pj_strcat2(&cfg, line);
2301
2302 /* Uas-duration. */
2303 if (config->duration != PJSUA_APP_NO_LIMIT_DURATION) {
2304 pj_ansi_sprintf(line, "--duration %d\n",
2305 config->duration);
2306 pj_strcat2(&cfg, line);
2307 }
2308
2309 /* norefersub ? */
2310 if (config->no_refersub) {
2311 pj_strcat2(&cfg, "--norefersub\n");
2312 }
2313
2314 if (pjsip_cfg()->endpt.use_compact_form)
2315 {
2316 pj_strcat2(&cfg, "--use-compact-form\n");
2317 }
2318
2319 if (!config->cfg.force_lr) {
2320 pj_strcat2(&cfg, "--no-force-lr\n");
2321 }
2322
2323 pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n");
2324
2325 /* Add buddies. */
2326 for (i=0; i<config->buddy_cnt; ++i) {
2327 pj_ansi_sprintf(line, "--add-buddy %.*s\n",
2328 (int)config->buddy_cfg[i].uri.slen,
2329 config->buddy_cfg[i].uri.ptr);
2330 pj_strcat2(&cfg, line);
2331 }
2332
2333 /* SIP extensions. */
2334 pj_strcat2(&cfg, "\n#\n# SIP extensions:\n#\n");
2335 /* 100rel extension */
2336 if (config->cfg.require_100rel == PJSUA_100REL_MANDATORY) {
2337 pj_strcat2(&cfg, "--use-100rel\n");
2338 }
2339 /* Session Timer extension */
2340 if (config->cfg.use_timer) {
2341 pj_ansi_sprintf(line, "--use-timer %d\n",
2342 config->cfg.use_timer);
2343 pj_strcat2(&cfg, line);
2344 }
2345 if (config->cfg.timer_setting.min_se != 90) {
2346 pj_ansi_sprintf(line, "--timer-min-se %d\n",
2347 config->cfg.timer_setting.min_se);
2348 pj_strcat2(&cfg, line);
2349 }
2350 if (config->cfg.timer_setting.sess_expires != PJSIP_SESS_TIMER_DEF_SE) {
2351 pj_ansi_sprintf(line, "--timer-se %d\n",
2352 config->cfg.timer_setting.sess_expires);
2353 pj_strcat2(&cfg, line);
2354 }
2355
2356 *(cfg.ptr + cfg.slen) = '\0';
2357 return (int)cfg.slen;
2358 }
2359