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