blob: 3c92b988d3f2d79bbf8c2ce0cc8d3b90a883cdef [file] [log] [blame]
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsua-lib/pjsua.h>
20
21
Benny Prijono4ab9fbb2007-10-12 12:14:27 +000022#define THIS_FILE "pjsua_app.c"
Benny Prijono804ff0a2006-09-14 11:17:48 +000023#define NO_LIMIT (int)0x7FFFFFFF
Benny Prijonoeebe9af2006-06-13 22:57:13 +000024
Benny Prijonoe909eac2006-07-27 22:04:56 +000025//#define STEREO_DEMO
Benny Prijonoeebe9af2006-06-13 22:57:13 +000026
Benny Prijono804ff0a2006-09-14 11:17:48 +000027/* Call specific data */
28struct call_data
29{
30 pj_timer_entry timer;
31};
32
Benny Prijonoeebe9af2006-06-13 22:57:13 +000033
34/* Pjsua application data */
35static struct app_config
36{
37 pjsua_config cfg;
38 pjsua_logging_config log_cfg;
39 pjsua_media_config media_cfg;
Benny Prijono4ddad2c2006-10-18 17:16:34 +000040 pj_bool_t no_refersub;
Benny Prijonoe93e2872006-06-28 16:46:49 +000041 pj_bool_t no_tcp;
42 pj_bool_t no_udp;
Benny Prijono6e0e54b2006-12-08 21:58:31 +000043 pj_bool_t use_tls;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000044 pjsua_transport_config udp_cfg;
45 pjsua_transport_config rtp_cfg;
46
47 unsigned acc_cnt;
48 pjsua_acc_config acc_cfg[PJSUA_MAX_ACC];
49
50 unsigned buddy_cnt;
51 pjsua_buddy_config buddy_cfg[PJSUA_MAX_BUDDIES];
52
Benny Prijono804ff0a2006-09-14 11:17:48 +000053 struct call_data call_data[PJSUA_MAX_CALLS];
54
Benny Prijonoeebe9af2006-06-13 22:57:13 +000055 pj_pool_t *pool;
56 /* Compatibility with older pjsua */
57
58 unsigned codec_cnt;
59 pj_str_t codec_arg[32];
Benny Prijonofce28542007-12-09 15:41:10 +000060 unsigned codec_dis_cnt;
61 pj_str_t codec_dis[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +000062 pj_bool_t null_audio;
Benny Prijono32e4f492007-01-24 00:44:26 +000063 unsigned wav_count;
64 pj_str_t wav_files[32];
Benny Prijono4af234b2007-01-24 02:02:09 +000065 unsigned tone_count;
66 pjmedia_tone_desc tones[32];
67 pjsua_conf_port_id tone_slots[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +000068 pjsua_player_id wav_id;
69 pjsua_conf_port_id wav_port;
70 pj_bool_t auto_play;
71 pj_bool_t auto_loop;
Benny Prijono7ca96da2006-08-07 12:11:40 +000072 pj_bool_t auto_conf;
Benny Prijono1ebd6142006-10-19 15:48:02 +000073 pj_str_t rec_file;
74 pj_bool_t auto_rec;
75 pjsua_recorder_id rec_id;
76 pjsua_conf_port_id rec_port;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000077 unsigned auto_answer;
78 unsigned duration;
Benny Prijonoe909eac2006-07-27 22:04:56 +000079
80#ifdef STEREO_DEMO
81 pjmedia_snd_port *snd;
82#endif
83
Benny Prijono6dd967c2006-12-26 02:27:14 +000084 float mic_level,
85 speaker_level;
86
Benny Prijono4e5d5512007-03-06 18:11:30 +000087 int capture_dev, playback_dev;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000088} app_config;
89
90
Benny Prijono21b9ad92006-08-15 13:11:22 +000091//static pjsua_acc_id current_acc;
92#define current_acc pjsua_acc_get_default()
Benny Prijonof7b1c392006-11-11 16:46:34 +000093static pjsua_call_id current_call = PJSUA_INVALID_ID;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000094static pj_str_t uri_arg;
95
Benny Prijono6eddd872008-03-21 13:46:08 +000096static char some_buf[2048];
97
Benny Prijono594e4c52006-09-14 18:51:01 +000098#ifdef STEREO_DEMO
Benny Prijonoe909eac2006-07-27 22:04:56 +000099static void stereo_demo();
Benny Prijono594e4c52006-09-14 18:51:01 +0000100#endif
Benny Prijonoad2e0ca2007-04-29 12:31:51 +0000101pj_status_t app_destroy(void);
Benny Prijonoe909eac2006-07-27 22:04:56 +0000102
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103/*****************************************************************************
104 * Configuration manipulation
105 */
106
107/* Show usage */
108static void usage(void)
109{
110 puts ("Usage:");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000111 puts (" pjsua [options] [SIP URL to call]");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000112 puts ("");
113 puts ("General options:");
Benny Prijono804ff0a2006-09-14 11:17:48 +0000114 puts (" --config-file=file Read the config/arguments from file.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000115 puts (" --help Display this help screen");
116 puts (" --version Display version info");
117 puts ("");
118 puts ("Logging options:");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000119 puts (" --log-file=fname Log to filename (default stderr)");
120 puts (" --log-level=N Set log max level to N (0(none) to 6(trace)) (default=5)");
121 puts (" --app-log-level=N Set log max level for stdout display (default=4)");
122 puts ("");
123 puts ("SIP Account options:");
Benny Prijono48ab2b72007-11-08 09:24:30 +0000124 puts (" --use-ims Enable 3GPP/IMS related settings on this account");
Benny Prijonod8179652008-01-23 20:39:07 +0000125#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
Benny Prijonof6508982008-01-25 09:02:33 +0000126 puts (" --use-srtp=N Use SRTP? 0:disabled, 1:optional, 2:mandatory (def:0)");
127 puts (" --srtp-secure=N SRTP require secure SIP? 0:no, 1:tls, 1:sips (def:1)");
Benny Prijonod8179652008-01-23 20:39:07 +0000128#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000129 puts (" --registrar=url Set the URL of registrar server");
130 puts (" --id=url Set the URL of local ID (used in From header)");
131 puts (" --contact=url Optionally override the Contact information");
132 puts (" --proxy=url Optional URL of proxy server to visit");
133 puts (" May be specified multiple times");
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000134 puts (" --reg-timeout=SEC Optional registration interval (default 55)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000135 puts (" --realm=string Set realm");
136 puts (" --username=string Set authentication username");
137 puts (" --password=string Set authentication password");
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000138 puts (" --publish Send presence PUBLISH for this account");
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000139 puts (" --use-100rel Require reliable provisional response (100rel)");
Benny Prijonofce28542007-12-09 15:41:10 +0000140 puts (" --auto-update-nat=N Where N is 0 or 1 to enable/disable SIP traversal behind");
141 puts (" symmetric NAT (default 1)");
Benny Prijonob67eaac2007-02-18 20:56:00 +0000142 puts (" --next-cred Add another credentials");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000143 puts ("");
144 puts ("SIP Account Control:");
145 puts (" --next-account Add more account");
146 puts ("");
147 puts ("Transport Options:");
Benny Prijonoe93e2872006-06-28 16:46:49 +0000148 puts (" --local-port=port Set TCP/UDP port. This implicitly enables both ");
149 puts (" TCP and UDP transports on the specified port, unless");
150 puts (" if TCP or UDP is disabled.");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000151 puts (" --ip-addr=IP Use the specifed address as SIP and RTP addresses.");
152 puts (" (Hint: the IP may be the public IP of the NAT/router)");
Benny Prijonoe93e2872006-06-28 16:46:49 +0000153 puts (" --no-tcp Disable TCP transport.");
154 puts (" --no-udp Disable UDP transport.");
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000155 puts (" --nameserver=NS Add the specified nameserver to enable SRV resolution");
156 puts (" This option can be specified multiple times.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000157 puts (" --outbound=url Set the URL of global outbound proxy server");
158 puts (" May be specified multiple times");
Benny Prijonoc97608e2007-03-23 16:34:20 +0000159 puts (" --stun-srv=name Set STUN server host or domain");
Benny Prijonof3bbc132006-12-25 06:43:59 +0000160 puts ("");
161 puts ("TLS Options:");
Benny Prijonob67eaac2007-02-18 20:56:00 +0000162 puts (" --use-tls Enable TLS transport (default=no)");
Benny Prijonof3bbc132006-12-25 06:43:59 +0000163 puts (" --tls-ca-file Specify TLS CA file (default=none)");
164 puts (" --tls-cert-file Specify TLS certificate file (default=none)");
165 puts (" --tls-privkey-file Specify TLS private key file (default=none)");
166 puts (" --tls-password Specify TLS password to private key file (default=none)");
167 puts (" --tls-verify-server Verify server's certificate (default=no)");
168 puts (" --tls-verify-client Verify client's certificate (default=no)");
169 puts (" --tls-neg-timeout Specify TLS negotiation timeout (default=no)");
170
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000171 puts ("");
172 puts ("Media Options:");
173 puts (" --add-codec=name Manually add codec (default is to enable all)");
Benny Prijonofce28542007-12-09 15:41:10 +0000174 puts (" --dis-codec=name Disable codec (can be specified multiple times)");
Benny Prijonof3758ee2008-02-26 15:32:16 +0000175 puts (" --clock-rate=N Override conference bridge clock rate");
176 puts (" --snd-clock-rate=N Override sound device clock rate");
Benny Prijono7d60d052008-03-29 12:24:20 +0000177 puts (" --stereo Audio device and conference bridge opened in stereo mode");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000178 puts (" --null-audio Use NULL audio device");
Benny Prijono4af234b2007-01-24 02:02:09 +0000179 puts (" --play-file=file Register WAV file in conference bridge.");
180 puts (" This can be specified multiple times.");
Benny Prijonob67eaac2007-02-18 20:56:00 +0000181 puts (" --play-tone=FORMAT Register tone to the conference bridge.");
182 puts (" FORMAT is 'F1,F2,ON,OFF', where F1,F2 are");
183 puts (" frequencies, and ON,OFF=on/off duration in msec.");
Benny Prijono4af234b2007-01-24 02:02:09 +0000184 puts (" This can be specified multiple times.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000185 puts (" --auto-play Automatically play the file (to incoming calls only)");
186 puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
Benny Prijono7ca96da2006-08-07 12:11:40 +0000187 puts (" --auto-conf Automatically put calls in conference with others");
Benny Prijono1ebd6142006-10-19 15:48:02 +0000188 puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
189 puts (" --auto-rec Automatically record conversation");
Benny Prijono00cae612006-07-31 15:19:36 +0000190 puts (" --quality=N Specify media quality (0-10, default=6)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191 puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
Benny Prijono0a12f002006-07-26 17:05:39 +0000192 puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
Benny Prijonod79f25c2006-08-02 19:41:37 +0000193 puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)");
Benny Prijono00cae612006-07-31 15:19:36 +0000194 puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 20)");
Benny Prijono4e5d5512007-03-06 18:11:30 +0000195 puts (" --capture-dev=id Audio capture device ID (default=-1)");
196 puts (" --playback-dev=id Audio playback device ID (default=-1)");
Benny Prijono00cae612006-07-31 15:19:36 +0000197
Benny Prijonof76e1392008-06-06 14:51:48 +0000198 puts ("");
199 puts ("Media Transport Options:");
200 puts (" --use-ice Enable ICE (default:no)");
201 puts (" --ice-no-host Disable ICE host candidates");
202 puts (" --rtp-port=N Base port to try for RTP (default=4000)");
203 puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
204 puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
205 puts (" --use-turn Enable TURN relay with ICE (default:no)");
206 puts (" --turn-srv Domain or host name of TURN server (\"NAME:PORT\" format)");
207 puts (" --turn-tcp Use TCP connection to TURN server (default no)");
208 puts (" --turn-user TURN username");
209 puts (" --turn-passwd TURN password");
Benny Prijono0a12f002006-07-26 17:05:39 +0000210
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000211 puts ("");
212 puts ("Buddy List (can be more than one):");
213 puts (" --add-buddy url Add the specified URL to the buddy list.");
214 puts ("");
215 puts ("User Agent options:");
216 puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
217 puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
Benny Prijonof521eb02006-08-06 23:07:25 +0000218 puts (" --thread-cnt=N Number of worker threads (default:1)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000219 puts (" --duration=SEC Set maximum call duration (default:no limit)");
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000220 puts (" --norefersub Suppress event subscription when transfering calls");
Benny Prijonofce28542007-12-09 15:41:10 +0000221 puts (" --use-compact-form Minimize SIP message size");
Benny Prijono804ff0a2006-09-14 11:17:48 +0000222
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000223 puts ("");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000224 puts ("When URL is specified, pjsua will immediately initiate call to that URL");
225 puts ("");
226
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000227 fflush(stdout);
228}
229
230
231/* Set default config. */
232static void default_config(struct app_config *cfg)
233{
Benny Prijono56315612006-07-18 14:39:40 +0000234 char tmp[80];
Benny Prijono1febfdf2007-02-18 01:35:04 +0000235 unsigned i;
Benny Prijono56315612006-07-18 14:39:40 +0000236
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 pjsua_config_default(&cfg->cfg);
Benny Prijono106f5b72007-08-30 13:49:33 +0000238 pj_ansi_sprintf(tmp, "PJSUA v%s/%s", pj_get_version(), PJ_OS_NAME);
Benny Prijono56315612006-07-18 14:39:40 +0000239 pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
240
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000241 pjsua_logging_config_default(&cfg->log_cfg);
242 pjsua_media_config_default(&cfg->media_cfg);
243 pjsua_transport_config_default(&cfg->udp_cfg);
244 cfg->udp_cfg.port = 5060;
245 pjsua_transport_config_default(&cfg->rtp_cfg);
246 cfg->rtp_cfg.port = 4000;
Benny Prijono804ff0a2006-09-14 11:17:48 +0000247 cfg->duration = NO_LIMIT;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000248 cfg->wav_id = PJSUA_INVALID_ID;
Benny Prijono1ebd6142006-10-19 15:48:02 +0000249 cfg->rec_id = PJSUA_INVALID_ID;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000250 cfg->wav_port = PJSUA_INVALID_ID;
Benny Prijono1ebd6142006-10-19 15:48:02 +0000251 cfg->rec_port = PJSUA_INVALID_ID;
Benny Prijono6dd967c2006-12-26 02:27:14 +0000252 cfg->mic_level = cfg->speaker_level = 1.0;
Benny Prijono4e5d5512007-03-06 18:11:30 +0000253 cfg->capture_dev = PJSUA_INVALID_ID;
254 cfg->playback_dev = PJSUA_INVALID_ID;
Benny Prijono1febfdf2007-02-18 01:35:04 +0000255
256 for (i=0; i<PJ_ARRAY_SIZE(cfg->acc_cfg); ++i)
257 pjsua_acc_config_default(&cfg->acc_cfg[i]);
258
259 for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i)
260 pjsua_buddy_config_default(&cfg->buddy_cfg[i]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000261}
262
263
264/*
265 * Read command arguments from config file.
266 */
267static int read_config_file(pj_pool_t *pool, const char *filename,
268 int *app_argc, char ***app_argv)
269{
270 int i;
271 FILE *fhnd;
272 char line[200];
273 int argc = 0;
274 char **argv;
275 enum { MAX_ARGS = 64 };
276
277 /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
278 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
279 argv[argc++] = *app_argv[0];
280
281 /* Open config file. */
282 fhnd = fopen(filename, "rt");
283 if (!fhnd) {
284 PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename));
285 fflush(stdout);
286 return -1;
287 }
288
289 /* Scan tokens in the file. */
290 while (argc < MAX_ARGS && !feof(fhnd)) {
Benny Prijonobf5b4692007-06-28 03:20:17 +0000291 char *token;
292 char *p;
293 const char *whitespace = " \t\r\n";
294 char cDelimiter;
295 int len, token_len;
296
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000297 if (fgets(line, sizeof(line), fhnd) == NULL) break;
Benny Prijonobf5b4692007-06-28 03:20:17 +0000298
299 // Trim ending newlines
300 len = strlen(line);
301 if (line[len-1]=='\n')
302 line[--len] = '\0';
303 if (line[len-1]=='\r')
304 line[--len] = '\0';
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000305
Benny Prijonobf5b4692007-06-28 03:20:17 +0000306 if (len==0) continue;
307
308 for (p = line; *p != '\0' && argc < MAX_ARGS; p++) {
309 // first, scan whitespaces
310 while (*p != '\0' && strchr(whitespace, *p) != NULL) p++;
311
312 if (*p == '\0') // are we done yet?
313 break;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000314
Benny Prijonobf5b4692007-06-28 03:20:17 +0000315 if (*p == '"' || *p == '\'') { // is token a quoted string
316 cDelimiter = *p++; // save quote delimiter
317 token = p;
318
319 while (*p != '\0' && *p != cDelimiter) p++;
320
321 if (*p == '\0') // found end of the line, but,
322 cDelimiter = '\0'; // didn't find a matching quote
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000323
Benny Prijonobf5b4692007-06-28 03:20:17 +0000324 } else { // token's not a quoted string
325 token = p;
326
327 while (*p != '\0' && strchr(whitespace, *p) == NULL) p++;
328
329 cDelimiter = *p;
330 }
331
332 *p = '\0';
333 token_len = p-token;
334
335 if (token_len > 0) {
336 if (*token == '#')
337 break; // ignore remainder of line
338
339 argv[argc] = pj_pool_alloc(pool, token_len + 1);
340 pj_memcpy(argv[argc], token, token_len + 1);
341 ++argc;
342 }
343
344 *p = cDelimiter;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000345 }
346 }
347
348 /* Copy arguments from command line */
349 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
350 argv[argc++] = (*app_argv)[i];
351
352 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
353 PJ_LOG(1,(THIS_FILE,
354 "Too many arguments specified in cmd line/config file"));
355 fflush(stdout);
356 fclose(fhnd);
357 return -1;
358 }
359
360 fclose(fhnd);
361
362 /* Assign the new command line back to the original command line. */
363 *app_argc = argc;
364 *app_argv = argv;
365 return 0;
366
367}
368
369static int my_atoi(const char *cs)
370{
371 pj_str_t s;
Benny Prijono1e2dbe62007-06-15 04:15:16 +0000372
373 pj_cstr(&s, cs);
374 if (cs[0] == '-') {
375 s.ptr++, s.slen--;
376 return 0 - (int)pj_strtoul(&s);
377 } else if (cs[0] == '+') {
378 s.ptr++, s.slen--;
379 return pj_strtoul(&s);
380 } else {
381 return pj_strtoul(&s);
382 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000383}
384
385
386/* Parse arguments. */
387static pj_status_t parse_args(int argc, char *argv[],
388 struct app_config *cfg,
389 pj_str_t *uri_to_call)
390{
391 int c;
392 int option_index;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000393 enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000394 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
Benny Prijono0a5cad82006-09-26 13:21:02 +0000395 OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
396 OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
Benny Prijono48ab2b72007-11-08 09:24:30 +0000397 OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
Benny Prijonoebbf6892007-03-24 17:37:25 +0000398 OPT_NAMESERVER, OPT_STUN_DOMAIN, OPT_STUN_SRV,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000399 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
400 OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
Benny Prijono7d60d052008-03-29 12:24:20 +0000401 OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
402 OPT_USE_ICE, OPT_USE_SRTP, OPT_SRTP_SECURE,
Benny Prijonof76e1392008-06-06 14:51:48 +0000403 OPT_USE_TURN,OPT_ICE_NO_HOST, OPT_TURN_SRV, OPT_TURN_TCP,
404 OPT_TURN_USER, OPT_TURN_PASSWD,
Benny Prijono4af234b2007-01-24 02:02:09 +0000405 OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC,
406 OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
Benny Prijono0a12f002006-07-26 17:05:39 +0000407 OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
Benny Prijonod79f25c2006-08-02 19:41:37 +0000408 OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL,
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000409 OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
Benny Prijonof521eb02006-08-06 23:07:25 +0000410 OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000411 OPT_NOREFERSUB,
Benny Prijonof3bbc132006-12-25 06:43:59 +0000412 OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE,
413 OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT,
414 OPT_TLS_NEG_TIMEOUT,
Benny Prijono4e5d5512007-03-06 18:11:30 +0000415 OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV,
Benny Prijonofce28542007-12-09 15:41:10 +0000416 OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000417 };
418 struct pj_getopt_option long_options[] = {
419 { "config-file",1, 0, OPT_CONFIG_FILE},
420 { "log-file", 1, 0, OPT_LOG_FILE},
421 { "log-level", 1, 0, OPT_LOG_LEVEL},
422 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
423 { "help", 0, 0, OPT_HELP},
424 { "version", 0, 0, OPT_VERSION},
425 { "clock-rate", 1, 0, OPT_CLOCK_RATE},
Benny Prijonof3758ee2008-02-26 15:32:16 +0000426 { "snd-clock-rate", 1, 0, OPT_SND_CLOCK_RATE},
Benny Prijono7d60d052008-03-29 12:24:20 +0000427 { "stereo", 0, 0, OPT_STEREO},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000428 { "null-audio", 0, 0, OPT_NULL_AUDIO},
429 { "local-port", 1, 0, OPT_LOCAL_PORT},
Benny Prijono0a5cad82006-09-26 13:21:02 +0000430 { "ip-addr", 1, 0, OPT_IP_ADDR},
Benny Prijonoe93e2872006-06-28 16:46:49 +0000431 { "no-tcp", 0, 0, OPT_NO_TCP},
432 { "no-udp", 0, 0, OPT_NO_UDP},
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000433 { "norefersub", 0, 0, OPT_NOREFERSUB},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000434 { "proxy", 1, 0, OPT_PROXY},
435 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
436 { "registrar", 1, 0, OPT_REGISTRAR},
437 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000438 { "publish", 0, 0, OPT_PUBLISH},
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000439 { "use-100rel", 0, 0, OPT_100REL},
Benny Prijono48ab2b72007-11-08 09:24:30 +0000440 { "use-ims", 0, 0, OPT_USE_IMS},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000441 { "id", 1, 0, OPT_ID},
442 { "contact", 1, 0, OPT_CONTACT},
Benny Prijonofce28542007-12-09 15:41:10 +0000443 { "auto-update-nat", 1, 0, OPT_AUTO_UPDATE_NAT},
444 { "use-compact-form", 0, 0, OPT_USE_COMPACT_FORM},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000445 { "realm", 1, 0, OPT_REALM},
446 { "username", 1, 0, OPT_USERNAME},
447 { "password", 1, 0, OPT_PASSWORD},
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000448 { "nameserver", 1, 0, OPT_NAMESERVER},
Benny Prijonoebbf6892007-03-24 17:37:25 +0000449 { "stun-domain",1, 0, OPT_STUN_DOMAIN},
Benny Prijonoc97608e2007-03-23 16:34:20 +0000450 { "stun-srv", 1, 0, OPT_STUN_SRV},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000451 { "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-hangup",1, 0, OPT_AUTO_HANGUP},
456 { "auto-play", 0, 0, OPT_AUTO_PLAY},
Benny Prijono1ebd6142006-10-19 15:48:02 +0000457 { "auto-rec", 0, 0, OPT_AUTO_REC},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000458 { "auto-loop", 0, 0, OPT_AUTO_LOOP},
459 { "auto-conf", 0, 0, OPT_AUTO_CONF},
460 { "play-file", 1, 0, OPT_PLAY_FILE},
Benny Prijono4af234b2007-01-24 02:02:09 +0000461 { "play-tone", 1, 0, OPT_PLAY_TONE},
Benny Prijono1ebd6142006-10-19 15:48:02 +0000462 { "rec-file", 1, 0, OPT_REC_FILE},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000463 { "rtp-port", 1, 0, OPT_RTP_PORT},
Benny Prijonof76e1392008-06-06 14:51:48 +0000464
Benny Prijonoc97608e2007-03-23 16:34:20 +0000465 { "use-ice", 0, 0, OPT_USE_ICE},
Benny Prijonof76e1392008-06-06 14:51:48 +0000466 { "use-turn", 0, 0, OPT_USE_TURN},
467 { "ice-no-host",0, 0, OPT_ICE_NO_HOST},
468 { "turn-srv", 1, 0, OPT_TURN_SRV},
469 { "turn-tcp", 0, 0, OPT_TURN_TCP},
470 { "turn-user", 1, 0, OPT_TURN_USER},
471 { "turn-passwd",1, 0, OPT_TURN_PASSWD},
472
Benny Prijonod8179652008-01-23 20:39:07 +0000473#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
474 { "use-srtp", 1, 0, OPT_USE_SRTP},
Benny Prijonof6508982008-01-25 09:02:33 +0000475 { "srtp-secure",1, 0, OPT_SRTP_SECURE},
Benny Prijonod8179652008-01-23 20:39:07 +0000476#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000477 { "add-codec", 1, 0, OPT_ADD_CODEC},
Benny Prijonofce28542007-12-09 15:41:10 +0000478 { "dis-codec", 1, 0, OPT_DIS_CODEC},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000479 { "complexity", 1, 0, OPT_COMPLEXITY},
480 { "quality", 1, 0, OPT_QUALITY},
481 { "ptime", 1, 0, OPT_PTIME},
Benny Prijono0a12f002006-07-26 17:05:39 +0000482 { "no-vad", 0, 0, OPT_NO_VAD},
Benny Prijonod79f25c2006-08-02 19:41:37 +0000483 { "ec-tail", 1, 0, OPT_EC_TAIL},
Benny Prijono00cae612006-07-31 15:19:36 +0000484 { "ilbc-mode", 1, 0, OPT_ILBC_MODE},
485 { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT},
486 { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000487 { "next-account",0,0, OPT_NEXT_ACCOUNT},
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000488 { "next-cred", 0, 0, OPT_NEXT_CRED},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000489 { "max-calls", 1, 0, OPT_MAX_CALLS},
Benny Prijonof521eb02006-08-06 23:07:25 +0000490 { "duration", 1, 0, OPT_DURATION},
491 { "thread-cnt", 1, 0, OPT_THREAD_CNT},
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000492 { "use-tls", 0, 0, OPT_USE_TLS},
493 { "tls-ca-file",1, 0, OPT_TLS_CA_FILE},
Benny Prijonof3bbc132006-12-25 06:43:59 +0000494 { "tls-cert-file",1,0, OPT_TLS_CERT_FILE},
495 { "tls-privkey-file",1,0, OPT_TLS_PRIV_FILE},
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000496 { "tls-password",1,0, OPT_TLS_PASSWORD},
Benny Prijonof3bbc132006-12-25 06:43:59 +0000497 { "tls-verify-server", 0, 0, OPT_TLS_VERIFY_SERVER},
498 { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT},
499 { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT},
Benny Prijono4e5d5512007-03-06 18:11:30 +0000500 { "capture-dev", 1, 0, OPT_CAPTURE_DEV},
501 { "playback-dev", 1, 0, OPT_PLAYBACK_DEV},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000502 { NULL, 0, 0, 0}
503 };
504 pj_status_t status;
505 pjsua_acc_config *cur_acc;
506 char *config_file = NULL;
507 unsigned i;
508
509 /* Run pj_getopt once to see if user specifies config file to read. */
Benny Prijonof762ee72006-12-01 11:14:37 +0000510 pj_optind = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000511 while ((c=pj_getopt_long(argc, argv, "", long_options,
512 &option_index)) != -1)
513 {
514 switch (c) {
515 case OPT_CONFIG_FILE:
516 config_file = pj_optarg;
517 break;
518 }
519 if (config_file)
520 break;
521 }
522
523 if (config_file) {
524 status = read_config_file(app_config.pool, config_file, &argc, &argv);
525 if (status != 0)
526 return status;
527 }
528
529 cfg->acc_cnt = 0;
530 cur_acc = &cfg->acc_cfg[0];
531
532
533 /* Reinitialize and re-run pj_getopt again, possibly with new arguments
534 * read from config file.
535 */
536 pj_optind = 0;
537 while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000538 pj_str_t tmp;
539 long lval;
540
541 switch (c) {
542
Benny Prijono6f137482006-06-15 11:31:36 +0000543 case OPT_CONFIG_FILE:
544 /* Ignore as this has been processed before */
545 break;
546
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000547 case OPT_LOG_FILE:
548 cfg->log_cfg.log_filename = pj_str(pj_optarg);
549 break;
550
551 case OPT_LOG_LEVEL:
552 c = pj_strtoul(pj_cstr(&tmp, pj_optarg));
553 if (c < 0 || c > 6) {
554 PJ_LOG(1,(THIS_FILE,
555 "Error: expecting integer value 0-6 "
556 "for --log-level"));
557 return PJ_EINVAL;
558 }
559 cfg->log_cfg.level = c;
560 pj_log_set_level( c );
561 break;
562
563 case OPT_APP_LOG_LEVEL:
564 cfg->log_cfg.console_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
565 if (cfg->log_cfg.console_level < 0 || cfg->log_cfg.console_level > 6) {
566 PJ_LOG(1,(THIS_FILE,
567 "Error: expecting integer value 0-6 "
568 "for --app-log-level"));
569 return PJ_EINVAL;
570 }
571 break;
572
573 case OPT_HELP:
574 usage();
575 return PJ_EINVAL;
576
577 case OPT_VERSION: /* version */
578 pj_dump_config();
579 return PJ_EINVAL;
580
581 case OPT_NULL_AUDIO:
582 cfg->null_audio = PJ_TRUE;
583 break;
584
585 case OPT_CLOCK_RATE:
586 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
Benny Prijono5dfac8f2008-06-02 14:06:19 +0000587 if (lval < 8000 || lval > 192000) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000588 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
Benny Prijono5dfac8f2008-06-02 14:06:19 +0000589 "8000-192000 for conference clock rate"));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000590 return PJ_EINVAL;
591 }
592 cfg->media_cfg.clock_rate = lval;
593 break;
594
Benny Prijonof3758ee2008-02-26 15:32:16 +0000595 case OPT_SND_CLOCK_RATE:
596 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
Benny Prijono5dfac8f2008-06-02 14:06:19 +0000597 if (lval < 8000 || lval > 192000) {
Benny Prijonof3758ee2008-02-26 15:32:16 +0000598 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
Benny Prijono5dfac8f2008-06-02 14:06:19 +0000599 "8000-192000 for sound device clock rate"));
Benny Prijonof3758ee2008-02-26 15:32:16 +0000600 return PJ_EINVAL;
601 }
602 cfg->media_cfg.snd_clock_rate = lval;
603 break;
604
Benny Prijono7d60d052008-03-29 12:24:20 +0000605 case OPT_STEREO:
606 cfg->media_cfg.channel_count = 2;
607 break;
608
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000609 case OPT_LOCAL_PORT: /* local-port */
610 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
Benny Prijonoe347cb02007-02-14 14:36:13 +0000611 if (lval < 0 || lval > 65535) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000612 PJ_LOG(1,(THIS_FILE,
613 "Error: expecting integer value for "
614 "--local-port"));
615 return PJ_EINVAL;
616 }
617 cfg->udp_cfg.port = (pj_uint16_t)lval;
618 break;
619
Benny Prijono0a5cad82006-09-26 13:21:02 +0000620 case OPT_IP_ADDR: /* ip-addr */
621 cfg->udp_cfg.public_addr = pj_str(pj_optarg);
622 cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
623 break;
624
Benny Prijonoe93e2872006-06-28 16:46:49 +0000625 case OPT_NO_UDP: /* no-udp */
626 if (cfg->no_tcp) {
Benny Prijonob988d762007-12-05 04:30:04 +0000627 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
628 return PJ_EINVAL;
Benny Prijonoe93e2872006-06-28 16:46:49 +0000629 }
630
631 cfg->no_udp = PJ_TRUE;
632 break;
633
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000634 case OPT_NOREFERSUB: /* norefersub */
635 cfg->no_refersub = PJ_TRUE;
636 break;
637
Benny Prijonoe93e2872006-06-28 16:46:49 +0000638 case OPT_NO_TCP: /* no-tcp */
639 if (cfg->no_udp) {
Benny Prijonob988d762007-12-05 04:30:04 +0000640 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
641 return PJ_EINVAL;
Benny Prijonoe93e2872006-06-28 16:46:49 +0000642 }
643
644 cfg->no_tcp = PJ_TRUE;
645 break;
646
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000647 case OPT_PROXY: /* proxy */
648 if (pjsua_verify_sip_url(pj_optarg) != 0) {
649 PJ_LOG(1,(THIS_FILE,
650 "Error: invalid SIP URL '%s' "
651 "in proxy argument", pj_optarg));
652 return PJ_EINVAL;
653 }
654 cur_acc->proxy[cur_acc->proxy_cnt++] = pj_str(pj_optarg);
655 break;
656
657 case OPT_OUTBOUND_PROXY: /* outbound proxy */
658 if (pjsua_verify_sip_url(pj_optarg) != 0) {
659 PJ_LOG(1,(THIS_FILE,
660 "Error: invalid SIP URL '%s' "
661 "in outbound proxy argument", pj_optarg));
662 return PJ_EINVAL;
663 }
664 cfg->cfg.outbound_proxy[cfg->cfg.outbound_proxy_cnt++] = pj_str(pj_optarg);
665 break;
666
667 case OPT_REGISTRAR: /* registrar */
668 if (pjsua_verify_sip_url(pj_optarg) != 0) {
669 PJ_LOG(1,(THIS_FILE,
670 "Error: invalid SIP URL '%s' in "
671 "registrar argument", pj_optarg));
672 return PJ_EINVAL;
673 }
674 cur_acc->reg_uri = pj_str(pj_optarg);
675 break;
676
677 case OPT_REG_TIMEOUT: /* reg-timeout */
678 cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,pj_optarg));
679 if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
680 PJ_LOG(1,(THIS_FILE,
681 "Error: invalid value for --reg-timeout "
682 "(expecting 1-3600)"));
683 return PJ_EINVAL;
684 }
685 break;
686
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000687 case OPT_PUBLISH: /* publish */
688 cur_acc->publish_enabled = PJ_TRUE;
689 break;
690
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000691 case OPT_100REL: /** 100rel */
692 cur_acc->require_100rel = PJ_TRUE;
693 cfg->cfg.require_100rel = PJ_TRUE;
694 break;
695
Benny Prijono48ab2b72007-11-08 09:24:30 +0000696 case OPT_USE_IMS: /* Activate IMS settings */
697 cur_acc->auth_pref.initial_auth = PJ_TRUE;
Benny Prijono2a67ea42007-10-25 02:51:33 +0000698 break;
699
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000700 case OPT_ID: /* id */
701 if (pjsua_verify_sip_url(pj_optarg) != 0) {
702 PJ_LOG(1,(THIS_FILE,
703 "Error: invalid SIP URL '%s' "
704 "in local id argument", pj_optarg));
705 return PJ_EINVAL;
706 }
707 cur_acc->id = pj_str(pj_optarg);
708 break;
709
710 case OPT_CONTACT: /* contact */
711 if (pjsua_verify_sip_url(pj_optarg) != 0) {
712 PJ_LOG(1,(THIS_FILE,
713 "Error: invalid SIP URL '%s' "
714 "in contact argument", pj_optarg));
715 return PJ_EINVAL;
716 }
Benny Prijonob4a17c92006-07-10 14:40:21 +0000717 cur_acc->force_contact = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000718 break;
719
Benny Prijonofce28542007-12-09 15:41:10 +0000720 case OPT_AUTO_UPDATE_NAT: /* OPT_AUTO_UPDATE_NAT */
Benny Prijonoe8554ef2008-03-22 09:33:52 +0000721 cur_acc->allow_contact_rewrite = pj_strtoul(pj_cstr(&tmp, pj_optarg));
Benny Prijonofce28542007-12-09 15:41:10 +0000722 break;
723
724 case OPT_USE_COMPACT_FORM:
725 /* enable compact form - from Ticket #342 */
726 {
727 extern pj_bool_t pjsip_use_compact_form;
728 extern pj_bool_t pjsip_include_allow_hdr_in_dlg;
729 extern pj_bool_t pjmedia_add_rtpmap_for_static_pt;
730
731 pjsip_use_compact_form = PJ_TRUE;
732 /* do not transmit Allow header */
733 pjsip_include_allow_hdr_in_dlg = PJ_FALSE;
734 /* Do not include rtpmap for static payload types (<96) */
735 pjmedia_add_rtpmap_for_static_pt = PJ_FALSE;
736 }
737 break;
738
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000739 case OPT_NEXT_ACCOUNT: /* Add more account. */
740 cfg->acc_cnt++;
Benny Prijono56315612006-07-18 14:39:40 +0000741 cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000742 break;
743
744 case OPT_USERNAME: /* Default authentication user */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000745 cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000746 cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("Digest");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000747 break;
748
749 case OPT_REALM: /* Default authentication realm. */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000750 cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000751 break;
752
753 case OPT_PASSWORD: /* authentication password */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000754 cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
755 cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
Benny Prijono28f673a2007-10-15 07:04:59 +0000756#if PJSIP_HAS_DIGEST_AKA_AUTH
757 cur_acc->cred_info[cur_acc->cred_count].data_type |= PJSIP_CRED_DATA_EXT_AKA;
758 cur_acc->cred_info[cur_acc->cred_count].ext.aka.k = pj_str(pj_optarg);
759 cur_acc->cred_info[cur_acc->cred_count].ext.aka.cb = &pjsip_auth_create_aka_response;
760#endif
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000761 break;
762
763 case OPT_NEXT_CRED: /* next credential */
764 cur_acc->cred_count++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000765 break;
766
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000767 case OPT_NAMESERVER: /* nameserver */
768 cfg->cfg.nameserver[cfg->cfg.nameserver_count++] = pj_str(pj_optarg);
769 if (cfg->cfg.nameserver_count > PJ_ARRAY_SIZE(cfg->cfg.nameserver)) {
770 PJ_LOG(1,(THIS_FILE, "Error: too many nameservers"));
771 return PJ_ETOOMANY;
772 }
773 break;
774
Benny Prijonoebbf6892007-03-24 17:37:25 +0000775 case OPT_STUN_DOMAIN: /* STUN domain */
776 cfg->cfg.stun_domain = pj_str(pj_optarg);
777 break;
778
Benny Prijonoc97608e2007-03-23 16:34:20 +0000779 case OPT_STUN_SRV: /* STUN server */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000780 cfg->cfg.stun_host = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000781 break;
782
783 case OPT_ADD_BUDDY: /* Add to buddy list. */
784 if (pjsua_verify_sip_url(pj_optarg) != 0) {
785 PJ_LOG(1,(THIS_FILE,
786 "Error: invalid URL '%s' in "
787 "--add-buddy option", pj_optarg));
788 return -1;
789 }
790 if (cfg->buddy_cnt == PJ_ARRAY_SIZE(cfg->buddy_cfg)) {
791 PJ_LOG(1,(THIS_FILE,
792 "Error: too many buddies in buddy list."));
793 return -1;
794 }
795 cfg->buddy_cfg[cfg->buddy_cnt].uri = pj_str(pj_optarg);
796 cfg->buddy_cnt++;
797 break;
798
799 case OPT_AUTO_PLAY:
800 cfg->auto_play = 1;
801 break;
802
Benny Prijono1ebd6142006-10-19 15:48:02 +0000803 case OPT_AUTO_REC:
804 cfg->auto_rec = 1;
805 break;
806
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000807 case OPT_AUTO_LOOP:
808 cfg->auto_loop = 1;
809 break;
810
Benny Prijono7ca96da2006-08-07 12:11:40 +0000811 case OPT_AUTO_CONF:
812 cfg->auto_conf = 1;
813 break;
814
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000815 case OPT_PLAY_FILE:
Benny Prijono32e4f492007-01-24 00:44:26 +0000816 cfg->wav_files[cfg->wav_count++] = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000817 break;
818
Benny Prijono4af234b2007-01-24 02:02:09 +0000819 case OPT_PLAY_TONE:
820 {
821 int f1, f2, on, off;
822 int n;
823
824 n = sscanf(pj_optarg, "%d,%d,%d,%d", &f1, &f2, &on, &off);
825 if (n != 4) {
826 puts("Expecting f1,f2,on,off in --play-tone");
827 return -1;
828 }
829
830 cfg->tones[cfg->tone_count].freq1 = (short)f1;
831 cfg->tones[cfg->tone_count].freq2 = (short)f2;
832 cfg->tones[cfg->tone_count].on_msec = (short)on;
833 cfg->tones[cfg->tone_count].off_msec = (short)off;
834 ++cfg->tone_count;
835 }
836 break;
837
Benny Prijono1ebd6142006-10-19 15:48:02 +0000838 case OPT_REC_FILE:
839 cfg->rec_file = pj_str(pj_optarg);
840 break;
841
Benny Prijonoc97608e2007-03-23 16:34:20 +0000842 case OPT_USE_ICE:
843 cfg->media_cfg.enable_ice = PJ_TRUE;
844 break;
845
Benny Prijonof76e1392008-06-06 14:51:48 +0000846 case OPT_USE_TURN:
847 cfg->media_cfg.enable_turn = PJ_TRUE;
848 break;
849
850 case OPT_ICE_NO_HOST:
851 cfg->media_cfg.ice_no_host_cands = PJ_TRUE;
852 break;
853
854 case OPT_TURN_SRV:
855 cfg->media_cfg.turn_server = pj_str(pj_optarg);
856 break;
857
858 case OPT_TURN_TCP:
859 cfg->media_cfg.turn_conn_type = PJ_TURN_TP_TCP;
860 break;
861
862 case OPT_TURN_USER:
863 cfg->media_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
864 cfg->media_cfg.turn_auth_cred.data.static_cred.realm = pj_str("*");
865 cfg->media_cfg.turn_auth_cred.data.static_cred.username = pj_str(pj_optarg);
866 break;
867
868 case OPT_TURN_PASSWD:
869 cfg->media_cfg.turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
870 cfg->media_cfg.turn_auth_cred.data.static_cred.data = pj_str(pj_optarg);
871 break;
872
Benny Prijonod8179652008-01-23 20:39:07 +0000873#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
874 case OPT_USE_SRTP:
875 app_config.cfg.use_srtp = my_atoi(pj_optarg);
876 if (!pj_isdigit(*pj_optarg) || app_config.cfg.use_srtp > 2) {
877 PJ_LOG(1,(THIS_FILE, "Invalid value for --use-srtp option"));
878 return -1;
879 }
Benny Prijono3ec13c72008-01-29 11:52:58 +0000880 cur_acc->use_srtp = app_config.cfg.use_srtp;
Benny Prijonod8179652008-01-23 20:39:07 +0000881 break;
Benny Prijonof6508982008-01-25 09:02:33 +0000882 case OPT_SRTP_SECURE:
883 app_config.cfg.srtp_secure_signaling = my_atoi(pj_optarg);
884 if (!pj_isdigit(*pj_optarg) ||
885 app_config.cfg.srtp_secure_signaling > 2)
886 {
887 PJ_LOG(1,(THIS_FILE, "Invalid value for --srtp-secure option"));
888 return -1;
889 }
Benny Prijono3ec13c72008-01-29 11:52:58 +0000890 cur_acc->srtp_secure_signaling = app_config.cfg.srtp_secure_signaling;
Benny Prijonof6508982008-01-25 09:02:33 +0000891 break;
Benny Prijonod8179652008-01-23 20:39:07 +0000892#endif
893
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000894 case OPT_RTP_PORT:
895 cfg->rtp_cfg.port = my_atoi(pj_optarg);
Benny Prijono5583a802007-06-26 12:20:37 +0000896 if (cfg->rtp_cfg.port == 0) {
897 enum { START_PORT=4000 };
898 unsigned range;
899
900 range = (65535-START_PORT-PJSUA_MAX_CALLS*2);
901 cfg->rtp_cfg.port = START_PORT +
902 ((pj_rand() % range) & 0xFFFE);
903 }
904
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000905 if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) {
906 PJ_LOG(1,(THIS_FILE,
907 "Error: rtp-port argument value "
908 "(expecting 1-65535"));
909 return -1;
910 }
911 break;
912
Benny Prijonofce28542007-12-09 15:41:10 +0000913 case OPT_DIS_CODEC:
914 cfg->codec_dis[cfg->codec_dis_cnt++] = pj_str(pj_optarg);
915 break;
916
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000917 case OPT_ADD_CODEC:
918 cfg->codec_arg[cfg->codec_cnt++] = pj_str(pj_optarg);
919 break;
920
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000921 /* These options were no longer valid after new pjsua */
922 /*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000923 case OPT_COMPLEXITY:
924 cfg->complexity = my_atoi(pj_optarg);
925 if (cfg->complexity < 0 || cfg->complexity > 10) {
926 PJ_LOG(1,(THIS_FILE,
927 "Error: invalid --complexity (expecting 0-10"));
928 return -1;
929 }
930 break;
Benny Prijono804ff0a2006-09-14 11:17:48 +0000931 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000932
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000933 case OPT_DURATION:
934 cfg->duration = my_atoi(pj_optarg);
935 break;
936
Benny Prijonof521eb02006-08-06 23:07:25 +0000937 case OPT_THREAD_CNT:
938 cfg->cfg.thread_cnt = my_atoi(pj_optarg);
939 if (cfg->cfg.thread_cnt > 128) {
940 PJ_LOG(1,(THIS_FILE,
941 "Error: invalid --thread-cnt option"));
942 return -1;
943 }
944 break;
945
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000946 case OPT_PTIME:
Benny Prijono0a12f002006-07-26 17:05:39 +0000947 cfg->media_cfg.ptime = my_atoi(pj_optarg);
948 if (cfg->media_cfg.ptime < 10 || cfg->media_cfg.ptime > 1000) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000949 PJ_LOG(1,(THIS_FILE,
950 "Error: invalid --ptime option"));
951 return -1;
952 }
953 break;
954
Benny Prijono0a12f002006-07-26 17:05:39 +0000955 case OPT_NO_VAD:
956 cfg->media_cfg.no_vad = PJ_TRUE;
957 break;
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000958
Benny Prijonod79f25c2006-08-02 19:41:37 +0000959 case OPT_EC_TAIL:
960 cfg->media_cfg.ec_tail_len = my_atoi(pj_optarg);
961 if (cfg->media_cfg.ec_tail_len > 1000) {
962 PJ_LOG(1,(THIS_FILE, "I think the ec-tail length setting "
963 "is too big"));
964 return -1;
965 }
966 break;
967
Benny Prijono0498d902006-06-19 14:49:14 +0000968 case OPT_QUALITY:
969 cfg->media_cfg.quality = my_atoi(pj_optarg);
970 if (cfg->media_cfg.quality < 0 || cfg->media_cfg.quality > 10) {
971 PJ_LOG(1,(THIS_FILE,
972 "Error: invalid --quality (expecting 0-10"));
973 return -1;
974 }
975 break;
976
Benny Prijono00cae612006-07-31 15:19:36 +0000977 case OPT_ILBC_MODE:
978 cfg->media_cfg.ilbc_mode = my_atoi(pj_optarg);
979 if (cfg->media_cfg.ilbc_mode!=20 && cfg->media_cfg.ilbc_mode!=30) {
980 PJ_LOG(1,(THIS_FILE,
981 "Error: invalid --ilbc-mode (expecting 20 or 30"));
982 return -1;
983 }
984 break;
985
986 case OPT_RX_DROP_PCT:
987 cfg->media_cfg.rx_drop_pct = my_atoi(pj_optarg);
988 if (cfg->media_cfg.rx_drop_pct > 100) {
989 PJ_LOG(1,(THIS_FILE,
990 "Error: invalid --rx-drop-pct (expecting <= 100"));
991 return -1;
992 }
993 break;
994
995 case OPT_TX_DROP_PCT:
996 cfg->media_cfg.tx_drop_pct = my_atoi(pj_optarg);
997 if (cfg->media_cfg.tx_drop_pct > 100) {
998 PJ_LOG(1,(THIS_FILE,
999 "Error: invalid --tx-drop-pct (expecting <= 100"));
1000 return -1;
1001 }
1002 break;
1003
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001004 case OPT_AUTO_ANSWER:
1005 cfg->auto_answer = my_atoi(pj_optarg);
1006 if (cfg->auto_answer < 100 || cfg->auto_answer > 699) {
1007 PJ_LOG(1,(THIS_FILE,
1008 "Error: invalid code in --auto-answer "
1009 "(expecting 100-699"));
1010 return -1;
1011 }
1012 break;
1013
1014 case OPT_MAX_CALLS:
1015 cfg->cfg.max_calls = my_atoi(pj_optarg);
Benny Prijono48af79c2006-07-22 12:49:17 +00001016 if (cfg->cfg.max_calls < 1 || cfg->cfg.max_calls > PJSUA_MAX_CALLS) {
Benny Prijono804ff0a2006-09-14 11:17:48 +00001017 PJ_LOG(1,(THIS_FILE,"Error: maximum call setting exceeds "
1018 "compile time limit (PJSUA_MAX_CALLS=%d)",
Benny Prijono48af79c2006-07-22 12:49:17 +00001019 PJSUA_MAX_CALLS));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001020 return -1;
1021 }
1022 break;
1023
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001024 case OPT_USE_TLS:
1025 cfg->use_tls = PJ_TRUE;
Benny Prijonof3bbc132006-12-25 06:43:59 +00001026#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
1027 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
1028 return -1;
1029#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001030 break;
1031
1032 case OPT_TLS_CA_FILE:
Benny Prijonof3bbc132006-12-25 06:43:59 +00001033 cfg->udp_cfg.tls_setting.ca_list_file = pj_str(pj_optarg);
1034#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
1035 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
1036 return -1;
1037#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001038 break;
1039
Benny Prijonof3bbc132006-12-25 06:43:59 +00001040 case OPT_TLS_CERT_FILE:
1041 cfg->udp_cfg.tls_setting.cert_file = pj_str(pj_optarg);
1042#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
1043 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
1044 return -1;
1045#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001046 break;
1047
Benny Prijonof3bbc132006-12-25 06:43:59 +00001048 case OPT_TLS_PRIV_FILE:
1049 cfg->udp_cfg.tls_setting.privkey_file = pj_str(pj_optarg);
1050 break;
1051
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001052 case OPT_TLS_PASSWORD:
Benny Prijonof3bbc132006-12-25 06:43:59 +00001053 cfg->udp_cfg.tls_setting.password = pj_str(pj_optarg);
1054#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
1055 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
1056 return -1;
1057#endif
1058 break;
1059
1060 case OPT_TLS_VERIFY_SERVER:
1061 cfg->udp_cfg.tls_setting.verify_server = PJ_TRUE;
1062 break;
1063
1064 case OPT_TLS_VERIFY_CLIENT:
1065 cfg->udp_cfg.tls_setting.verify_client = PJ_TRUE;
1066 break;
1067
1068 case OPT_TLS_NEG_TIMEOUT:
1069 cfg->udp_cfg.tls_setting.timeout.sec = atoi(pj_optarg);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001070 break;
1071
Benny Prijono4e5d5512007-03-06 18:11:30 +00001072 case OPT_CAPTURE_DEV:
1073 cfg->capture_dev = atoi(pj_optarg);
1074 break;
1075
1076 case OPT_PLAYBACK_DEV:
1077 cfg->playback_dev = atoi(pj_optarg);
1078 break;
1079
Benny Prijono22a300a2006-06-14 20:04:55 +00001080 default:
Benny Prijono787b8692006-06-19 12:40:03 +00001081 PJ_LOG(1,(THIS_FILE,
Benny Prijonod6388ac2006-09-09 13:23:09 +00001082 "Argument \"%s\" is not valid. Use --help to see help",
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001083 argv[pj_optind-1]));
Benny Prijono22a300a2006-06-14 20:04:55 +00001084 return -1;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001085 }
1086 }
1087
1088 if (pj_optind != argc) {
1089 pj_str_t uri_arg;
1090
1091 if (pjsua_verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
1092 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
1093 return -1;
1094 }
1095 uri_arg = pj_str(argv[pj_optind]);
1096 if (uri_to_call)
1097 *uri_to_call = uri_arg;
1098 pj_optind++;
1099
1100 /* Add URI to call to buddy list if it's not already there */
1101 for (i=0; i<cfg->buddy_cnt; ++i) {
1102 if (pj_stricmp(&cfg->buddy_cfg[i].uri, &uri_arg)==0)
1103 break;
1104 }
1105 if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) {
1106 cfg->buddy_cfg[cfg->buddy_cnt++].uri = uri_arg;
1107 }
1108
1109 } else {
1110 if (uri_to_call)
1111 uri_to_call->slen = 0;
1112 }
1113
1114 if (pj_optind != argc) {
1115 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
1116 return PJ_EINVAL;
1117 }
1118
Benny Prijono56315612006-07-18 14:39:40 +00001119 if (cfg->acc_cfg[cfg->acc_cnt].id.slen)
1120 cfg->acc_cnt++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001121
1122 for (i=0; i<cfg->acc_cnt; ++i) {
Benny Prijono48ab2b72007-11-08 09:24:30 +00001123 pjsua_acc_config *acfg = &cfg->acc_cfg[i];
1124
1125 if (acfg->cred_info[acfg->cred_count].username.slen)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001126 {
Benny Prijono48ab2b72007-11-08 09:24:30 +00001127 acfg->cred_count++;
1128 }
1129
1130 /* When IMS mode is enabled for the account, verify that settings
1131 * are okay.
1132 */
1133 /* For now we check if IMS mode is activated by looking if
1134 * initial_auth is set.
1135 */
1136 if (acfg->auth_pref.initial_auth && acfg->cred_count) {
1137 /* Realm must point to the real domain */
1138 if (*acfg->cred_info[0].realm.ptr=='*') {
1139 PJ_LOG(1,(THIS_FILE,
1140 "Error: cannot use '*' as realm with IMS"));
1141 return PJ_EINVAL;
1142 }
1143
1144 /* Username for authentication must be in a@b format */
1145 if (strchr(acfg->cred_info[0].username.ptr, '@')==0) {
1146 PJ_LOG(1,(THIS_FILE,
1147 "Error: Username for authentication must "
1148 "be in user@domain format with IMS"));
1149 return PJ_EINVAL;
1150 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001151 }
1152 }
1153
1154
1155 return PJ_SUCCESS;
1156}
1157
1158
1159/*
1160 * Save account settings
1161 */
1162static void write_account_settings(int acc_index, pj_str_t *result)
1163{
1164 unsigned i;
1165 char line[128];
1166 pjsua_acc_config *acc_cfg = &app_config.acc_cfg[acc_index];
1167
1168
1169 pj_ansi_sprintf(line, "\n#\n# Account %d:\n#\n", acc_index);
1170 pj_strcat2(result, line);
1171
1172
1173 /* Identity */
1174 if (acc_cfg->id.slen) {
1175 pj_ansi_sprintf(line, "--id %.*s\n",
1176 (int)acc_cfg->id.slen,
1177 acc_cfg->id.ptr);
1178 pj_strcat2(result, line);
1179 }
1180
1181 /* Registrar server */
1182 if (acc_cfg->reg_uri.slen) {
1183 pj_ansi_sprintf(line, "--registrar %.*s\n",
1184 (int)acc_cfg->reg_uri.slen,
1185 acc_cfg->reg_uri.ptr);
1186 pj_strcat2(result, line);
1187
1188 pj_ansi_sprintf(line, "--reg-timeout %u\n",
1189 acc_cfg->reg_timeout);
1190 pj_strcat2(result, line);
1191 }
1192
1193 /* Contact */
Benny Prijonob4a17c92006-07-10 14:40:21 +00001194 if (acc_cfg->force_contact.slen) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001195 pj_ansi_sprintf(line, "--contact %.*s\n",
Benny Prijonob4a17c92006-07-10 14:40:21 +00001196 (int)acc_cfg->force_contact.slen,
1197 acc_cfg->force_contact.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001198 pj_strcat2(result, line);
1199 }
1200
Benny Prijonofce28542007-12-09 15:41:10 +00001201 /* */
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001202 if (acc_cfg->allow_contact_rewrite==0)
Benny Prijonofce28542007-12-09 15:41:10 +00001203 {
Benny Prijonoe8554ef2008-03-22 09:33:52 +00001204 pj_ansi_sprintf(line, "--contact-rewrite %i\n",
1205 (int)acc_cfg->allow_contact_rewrite);
Benny Prijonofce28542007-12-09 15:41:10 +00001206 pj_strcat2(result, line);
1207 }
1208
Benny Prijonod8179652008-01-23 20:39:07 +00001209#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
1210 /* SRTP */
1211 if (acc_cfg->use_srtp) {
1212 pj_ansi_sprintf(line, "--use-srtp %i\n",
1213 (int)acc_cfg->use_srtp);
1214 pj_strcat2(result, line);
1215 }
1216#endif
1217
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001218 /* Proxy */
1219 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
1220 pj_ansi_sprintf(line, "--proxy %.*s\n",
1221 (int)acc_cfg->proxy[i].slen,
1222 acc_cfg->proxy[i].ptr);
1223 pj_strcat2(result, line);
1224 }
1225
1226 /* Credentials */
1227 for (i=0; i<acc_cfg->cred_count; ++i) {
1228 if (acc_cfg->cred_info[i].realm.slen) {
1229 pj_ansi_sprintf(line, "--realm %.*s\n",
1230 (int)acc_cfg->cred_info[i].realm.slen,
1231 acc_cfg->cred_info[i].realm.ptr);
1232 pj_strcat2(result, line);
1233 }
1234
1235 if (acc_cfg->cred_info[i].username.slen) {
1236 pj_ansi_sprintf(line, "--username %.*s\n",
1237 (int)acc_cfg->cred_info[i].username.slen,
1238 acc_cfg->cred_info[i].username.ptr);
1239 pj_strcat2(result, line);
1240 }
1241
1242 if (acc_cfg->cred_info[i].data.slen) {
1243 pj_ansi_sprintf(line, "--password %.*s\n",
1244 (int)acc_cfg->cred_info[i].data.slen,
1245 acc_cfg->cred_info[i].data.ptr);
1246 pj_strcat2(result, line);
1247 }
Benny Prijonoeef4f8c2006-06-19 12:07:44 +00001248
1249 if (i != acc_cfg->cred_count - 1)
1250 pj_strcat2(result, "--next-cred\n");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001251 }
1252
1253}
1254
1255
1256/*
1257 * Write settings.
1258 */
1259static int write_settings(const struct app_config *config,
1260 char *buf, pj_size_t max)
1261{
1262 unsigned acc_index;
1263 unsigned i;
1264 pj_str_t cfg;
1265 char line[128];
Benny Prijonofce28542007-12-09 15:41:10 +00001266 extern pj_bool_t pjsip_use_compact_form;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001267
1268 PJ_UNUSED_ARG(max);
1269
1270 cfg.ptr = buf;
1271 cfg.slen = 0;
1272
1273 /* Logging. */
1274 pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
1275 pj_ansi_sprintf(line, "--log-level %d\n",
1276 config->log_cfg.level);
1277 pj_strcat2(&cfg, line);
1278
1279 pj_ansi_sprintf(line, "--app-log-level %d\n",
1280 config->log_cfg.console_level);
1281 pj_strcat2(&cfg, line);
1282
1283 if (config->log_cfg.log_filename.slen) {
1284 pj_ansi_sprintf(line, "--log-file %.*s\n",
1285 (int)config->log_cfg.log_filename.slen,
1286 config->log_cfg.log_filename.ptr);
1287 pj_strcat2(&cfg, line);
1288 }
1289
1290
1291 /* Save account settings. */
1292 for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) {
1293
1294 write_account_settings(acc_index, &cfg);
1295
1296 if (acc_index < config->acc_cnt-1)
1297 pj_strcat2(&cfg, "--next-account\n");
1298 }
1299
1300
1301 pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n");
1302
1303 /* Outbound proxy */
1304 for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) {
1305 pj_ansi_sprintf(line, "--outbound %.*s\n",
1306 (int)config->cfg.outbound_proxy[i].slen,
1307 config->cfg.outbound_proxy[i].ptr);
1308 pj_strcat2(&cfg, line);
1309 }
1310
1311
1312 /* UDP Transport. */
1313 pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port);
1314 pj_strcat2(&cfg, line);
1315
Benny Prijono0a5cad82006-09-26 13:21:02 +00001316 /* IP address, if any. */
1317 if (config->udp_cfg.public_addr.slen) {
1318 pj_ansi_sprintf(line, "--ip-addr %.*s\n",
1319 (int)config->udp_cfg.public_addr.slen,
1320 config->udp_cfg.public_addr.ptr);
1321 pj_strcat2(&cfg, line);
1322 }
1323
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001324 /* No TCP ? */
1325 if (config->no_tcp) {
1326 pj_strcat2(&cfg, "--no-tcp\n");
1327 }
1328
1329 /* No UDP ? */
1330 if (config->no_udp) {
1331 pj_strcat2(&cfg, "--no-udp\n");
1332 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001333
1334 /* STUN */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001335 if (config->cfg.stun_domain.slen) {
1336 pj_ansi_sprintf(line, "--stun-domain %.*s\n",
1337 (int)config->cfg.stun_domain.slen,
1338 config->cfg.stun_domain.ptr);
1339 pj_strcat2(&cfg, line);
1340 }
1341 if (config->cfg.stun_host.slen) {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001342 pj_ansi_sprintf(line, "--stun-srv %.*s\n",
Benny Prijonoebbf6892007-03-24 17:37:25 +00001343 (int)config->cfg.stun_host.slen,
1344 config->cfg.stun_host.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001345 pj_strcat2(&cfg, line);
1346 }
1347
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001348 /* TLS */
1349 if (config->use_tls)
1350 pj_strcat2(&cfg, "--use-tls\n");
Benny Prijonof3bbc132006-12-25 06:43:59 +00001351 if (config->udp_cfg.tls_setting.ca_list_file.slen) {
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001352 pj_ansi_sprintf(line, "--tls-ca-file %.*s\n",
Benny Prijonof3bbc132006-12-25 06:43:59 +00001353 (int)config->udp_cfg.tls_setting.ca_list_file.slen,
1354 config->udp_cfg.tls_setting.ca_list_file.ptr);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001355 pj_strcat2(&cfg, line);
1356 }
Benny Prijonof3bbc132006-12-25 06:43:59 +00001357 if (config->udp_cfg.tls_setting.cert_file.slen) {
1358 pj_ansi_sprintf(line, "--tls-cert-file %.*s\n",
1359 (int)config->udp_cfg.tls_setting.cert_file.slen,
1360 config->udp_cfg.tls_setting.cert_file.ptr);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001361 pj_strcat2(&cfg, line);
1362 }
Benny Prijonof3bbc132006-12-25 06:43:59 +00001363 if (config->udp_cfg.tls_setting.privkey_file.slen) {
1364 pj_ansi_sprintf(line, "--tls-privkey-file %.*s\n",
1365 (int)config->udp_cfg.tls_setting.privkey_file.slen,
1366 config->udp_cfg.tls_setting.privkey_file.ptr);
1367 pj_strcat2(&cfg, line);
1368 }
1369
1370 if (config->udp_cfg.tls_setting.password.slen) {
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001371 pj_ansi_sprintf(line, "--tls-password %.*s\n",
Benny Prijonof3bbc132006-12-25 06:43:59 +00001372 (int)config->udp_cfg.tls_setting.password.slen,
1373 config->udp_cfg.tls_setting.password.ptr);
1374 pj_strcat2(&cfg, line);
1375 }
1376
1377 if (config->udp_cfg.tls_setting.verify_server)
1378 pj_strcat2(&cfg, "--tls-verify-server\n");
1379
1380 if (config->udp_cfg.tls_setting.verify_client)
1381 pj_strcat2(&cfg, "--tls-verify-client\n");
1382
1383 if (config->udp_cfg.tls_setting.timeout.sec) {
1384 pj_ansi_sprintf(line, "--tls-neg-timeout %d\n",
Benny Prijonoe960bb52007-01-21 17:53:39 +00001385 (int)config->udp_cfg.tls_setting.timeout.sec);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001386 pj_strcat2(&cfg, line);
1387 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001388
1389 pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n");
1390
Benny Prijonof6508982008-01-25 09:02:33 +00001391 /* SRTP */
Benny Prijonofe5a6942008-02-18 12:16:23 +00001392#if PJMEDIA_HAS_SRTP
Benny Prijonof6508982008-01-25 09:02:33 +00001393 if (app_config.cfg.use_srtp != PJSUA_DEFAULT_USE_SRTP) {
1394 pj_ansi_sprintf(line, "--use-srtp %d\n",
1395 app_config.cfg.use_srtp);
1396 pj_strcat2(&cfg, line);
1397 }
1398 if (app_config.cfg.srtp_secure_signaling !=
1399 PJSUA_DEFAULT_SRTP_SECURE_SIGNALING)
1400 {
1401 pj_ansi_sprintf(line, "--srtp-secure %d\n",
1402 app_config.cfg.srtp_secure_signaling);
1403 pj_strcat2(&cfg, line);
1404 }
Benny Prijonofe5a6942008-02-18 12:16:23 +00001405#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001406
Benny Prijonof76e1392008-06-06 14:51:48 +00001407 /* Media Transport*/
Benny Prijonoc97608e2007-03-23 16:34:20 +00001408 if (config->media_cfg.enable_ice)
1409 pj_strcat2(&cfg, "--use-ice\n");
Benny Prijonof76e1392008-06-06 14:51:48 +00001410
1411 if (config->media_cfg.enable_turn)
1412 pj_strcat2(&cfg, "--use-turn\n");
1413
1414 if (config->media_cfg.ice_no_host_cands)
1415 pj_strcat2(&cfg, "--ice-no-host\n");
1416
1417 if (config->media_cfg.turn_server.slen) {
1418 pj_ansi_sprintf(line, "--turn-srv %.*s\n",
1419 (int)config->media_cfg.turn_server.slen,
1420 config->media_cfg.turn_server.ptr);
1421 pj_strcat2(&cfg, line);
1422 }
1423
1424 if (config->media_cfg.turn_conn_type == PJ_TURN_TP_TCP)
1425 pj_strcat2(&cfg, "--turn-tcp\n");
1426
1427 if (config->media_cfg.turn_auth_cred.data.static_cred.username.slen) {
1428 pj_ansi_sprintf(line, "--turn-user %.*s\n",
1429 (int)config->media_cfg.turn_auth_cred.data.static_cred.username.slen,
1430 config->media_cfg.turn_auth_cred.data.static_cred.username.ptr);
1431 pj_strcat2(&cfg, line);
1432 }
1433
1434 if (config->media_cfg.turn_auth_cred.data.static_cred.data.slen) {
1435 pj_ansi_sprintf(line, "--turn-passwd %.*s\n",
1436 (int)config->media_cfg.turn_auth_cred.data.static_cred.data.slen,
1437 config->media_cfg.turn_auth_cred.data.static_cred.data.ptr);
1438 pj_strcat2(&cfg, line);
1439 }
1440
1441 /* Media */
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001442 if (config->null_audio)
1443 pj_strcat2(&cfg, "--null-audio\n");
1444 if (config->auto_play)
1445 pj_strcat2(&cfg, "--auto-play\n");
1446 if (config->auto_loop)
1447 pj_strcat2(&cfg, "--auto-loop\n");
Benny Prijono7ca96da2006-08-07 12:11:40 +00001448 if (config->auto_conf)
1449 pj_strcat2(&cfg, "--auto-conf\n");
Benny Prijono32e4f492007-01-24 00:44:26 +00001450 for (i=0; i<config->wav_count; ++i) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001451 pj_ansi_sprintf(line, "--play-file %s\n",
Benny Prijono32e4f492007-01-24 00:44:26 +00001452 config->wav_files[i].ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001453 pj_strcat2(&cfg, line);
1454 }
Benny Prijono4af234b2007-01-24 02:02:09 +00001455 for (i=0; i<config->tone_count; ++i) {
1456 pj_ansi_sprintf(line, "--play-tone %d,%d,%d,%d\n",
1457 config->tones[i].freq1, config->tones[i].freq2,
1458 config->tones[i].on_msec, config->tones[i].off_msec);
1459 pj_strcat2(&cfg, line);
1460 }
Benny Prijono1ebd6142006-10-19 15:48:02 +00001461 if (config->rec_file.slen) {
1462 pj_ansi_sprintf(line, "--rec-file %s\n",
1463 config->rec_file.ptr);
1464 pj_strcat2(&cfg, line);
1465 }
1466 if (config->auto_rec)
1467 pj_strcat2(&cfg, "--auto-rec\n");
Benny Prijono4e5d5512007-03-06 18:11:30 +00001468 if (config->capture_dev != PJSUA_INVALID_ID) {
1469 pj_ansi_sprintf(line, "--capture-dev %d\n", config->capture_dev);
1470 pj_strcat2(&cfg, line);
1471 }
1472 if (config->playback_dev != PJSUA_INVALID_ID) {
1473 pj_ansi_sprintf(line, "--playback-dev %d\n", config->playback_dev);
1474 pj_strcat2(&cfg, line);
1475 }
Benny Prijono1ebd6142006-10-19 15:48:02 +00001476
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001477 /* Media clock rate. */
Benny Prijono70972992006-08-05 11:13:58 +00001478 if (config->media_cfg.clock_rate != PJSUA_DEFAULT_CLOCK_RATE) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001479 pj_ansi_sprintf(line, "--clock-rate %d\n",
Benny Prijono0498d902006-06-19 14:49:14 +00001480 config->media_cfg.clock_rate);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001481 pj_strcat2(&cfg, line);
Benny Prijono70972992006-08-05 11:13:58 +00001482 } else {
1483 pj_ansi_sprintf(line, "#using default --clock-rate %d\n",
1484 config->media_cfg.clock_rate);
1485 pj_strcat2(&cfg, line);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001486 }
Benny Prijono70972992006-08-05 11:13:58 +00001487
Benny Prijonoc59ca6e2008-04-10 11:04:49 +00001488 if (config->media_cfg.snd_clock_rate &&
1489 config->media_cfg.snd_clock_rate != config->media_cfg.clock_rate)
1490 {
Benny Prijonof3758ee2008-02-26 15:32:16 +00001491 pj_ansi_sprintf(line, "--snd-clock-rate %d\n",
1492 config->media_cfg.snd_clock_rate);
1493 pj_strcat2(&cfg, line);
Benny Prijonof3758ee2008-02-26 15:32:16 +00001494 }
1495
Benny Prijono7d60d052008-03-29 12:24:20 +00001496 /* Stereo mode. */
1497 if (config->media_cfg.channel_count == 2) {
1498 pj_ansi_sprintf(line, "--stereo\n");
1499 pj_strcat2(&cfg, line);
1500 }
1501
Benny Prijono70972992006-08-05 11:13:58 +00001502 /* quality */
1503 if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001504 pj_ansi_sprintf(line, "--quality %d\n",
Benny Prijono0498d902006-06-19 14:49:14 +00001505 config->media_cfg.quality);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001506 pj_strcat2(&cfg, line);
Benny Prijono70972992006-08-05 11:13:58 +00001507 } else {
1508 pj_ansi_sprintf(line, "#using default --quality %d\n",
1509 config->media_cfg.quality);
1510 pj_strcat2(&cfg, line);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001511 }
Benny Prijono0498d902006-06-19 14:49:14 +00001512
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001513
1514 /* ptime */
Benny Prijono2adfe292007-05-11 10:36:08 +00001515 if (config->media_cfg.ptime) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001516 pj_ansi_sprintf(line, "--ptime %d\n",
Benny Prijono2adfe292007-05-11 10:36:08 +00001517 config->media_cfg.ptime);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001518 pj_strcat2(&cfg, line);
1519 }
1520
Benny Prijono70972992006-08-05 11:13:58 +00001521 /* no-vad */
1522 if (config->media_cfg.no_vad) {
1523 pj_strcat2(&cfg, "--no-vad\n");
1524 }
1525
1526 /* ec-tail */
1527 if (config->media_cfg.ec_tail_len != PJSUA_DEFAULT_EC_TAIL_LEN) {
1528 pj_ansi_sprintf(line, "--ec-tail %d\n",
1529 config->media_cfg.ec_tail_len);
1530 pj_strcat2(&cfg, line);
1531 } else {
1532 pj_ansi_sprintf(line, "#using default --ec-tail %d\n",
1533 config->media_cfg.ec_tail_len);
1534 pj_strcat2(&cfg, line);
1535 }
1536
1537
1538 /* ilbc-mode */
1539 if (config->media_cfg.ilbc_mode != PJSUA_DEFAULT_ILBC_MODE) {
1540 pj_ansi_sprintf(line, "--ilbc-mode %d\n",
1541 config->media_cfg.ilbc_mode);
1542 pj_strcat2(&cfg, line);
1543 } else {
1544 pj_ansi_sprintf(line, "#using default --ilbc-mode %d\n",
1545 config->media_cfg.ilbc_mode);
1546 pj_strcat2(&cfg, line);
1547 }
1548
1549 /* RTP drop */
1550 if (config->media_cfg.tx_drop_pct) {
1551 pj_ansi_sprintf(line, "--tx-drop-pct %d\n",
1552 config->media_cfg.tx_drop_pct);
1553 pj_strcat2(&cfg, line);
1554
1555 }
1556 if (config->media_cfg.rx_drop_pct) {
1557 pj_ansi_sprintf(line, "--rx-drop-pct %d\n",
1558 config->media_cfg.rx_drop_pct);
1559 pj_strcat2(&cfg, line);
1560
1561 }
1562
1563
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001564 /* Start RTP port. */
1565 pj_ansi_sprintf(line, "--rtp-port %d\n",
1566 config->rtp_cfg.port);
1567 pj_strcat2(&cfg, line);
1568
1569 /* Add codec. */
1570 for (i=0; i<config->codec_cnt; ++i) {
1571 pj_ansi_sprintf(line, "--add-codec %s\n",
1572 config->codec_arg[i].ptr);
1573 pj_strcat2(&cfg, line);
1574 }
Benny Prijonofce28542007-12-09 15:41:10 +00001575 /* Disable codec */
1576 for (i=0; i<config->codec_dis_cnt; ++i) {
1577 pj_ansi_sprintf(line, "--dis-codec %s\n",
1578 config->codec_dis[i].ptr);
1579 pj_strcat2(&cfg, line);
1580 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001581
1582 pj_strcat2(&cfg, "\n#\n# User agent:\n#\n");
1583
1584 /* Auto-answer. */
1585 if (config->auto_answer != 0) {
1586 pj_ansi_sprintf(line, "--auto-answer %d\n",
1587 config->auto_answer);
1588 pj_strcat2(&cfg, line);
1589 }
1590
1591 /* Max calls. */
1592 pj_ansi_sprintf(line, "--max-calls %d\n",
1593 config->cfg.max_calls);
1594 pj_strcat2(&cfg, line);
1595
1596 /* Uas-duration. */
Benny Prijono804ff0a2006-09-14 11:17:48 +00001597 if (config->duration != NO_LIMIT) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001598 pj_ansi_sprintf(line, "--duration %d\n",
1599 config->duration);
1600 pj_strcat2(&cfg, line);
1601 }
1602
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001603 /* norefersub ? */
1604 if (config->no_refersub) {
1605 pj_strcat2(&cfg, "--norefersub\n");
1606 }
1607
Benny Prijonofce28542007-12-09 15:41:10 +00001608 if (pjsip_use_compact_form)
1609 {
1610 pj_strcat2(&cfg, "--use-compact-form\n");
1611 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001612
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001613 pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n");
1614
1615 /* Add buddies. */
1616 for (i=0; i<config->buddy_cnt; ++i) {
1617 pj_ansi_sprintf(line, "--add-buddy %.*s\n",
1618 (int)config->buddy_cfg[i].uri.slen,
1619 config->buddy_cfg[i].uri.ptr);
1620 pj_strcat2(&cfg, line);
1621 }
1622
1623
1624 *(cfg.ptr + cfg.slen) = '\0';
1625 return cfg.slen;
1626}
1627
1628
1629/*
1630 * Dump application states.
1631 */
1632static void app_dump(pj_bool_t detail)
1633{
Benny Prijonoda9785b2007-04-02 20:43:06 +00001634 pjsua_dump(detail);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001635}
1636
1637
1638/*****************************************************************************
1639 * Console application
1640 */
1641
1642/*
1643 * Find next call when current call is disconnected or when user
1644 * press ']'
1645 */
1646static pj_bool_t find_next_call(void)
1647{
1648 int i, max;
1649
1650 max = pjsua_call_get_max_count();
1651 for (i=current_call+1; i<max; ++i) {
1652 if (pjsua_call_is_active(i)) {
1653 current_call = i;
1654 return PJ_TRUE;
1655 }
1656 }
1657
1658 for (i=0; i<current_call; ++i) {
1659 if (pjsua_call_is_active(i)) {
1660 current_call = i;
1661 return PJ_TRUE;
1662 }
1663 }
1664
1665 current_call = PJSUA_INVALID_ID;
1666 return PJ_FALSE;
1667}
1668
1669
1670/*
1671 * Find previous call when user press '['
1672 */
1673static pj_bool_t find_prev_call(void)
1674{
1675 int i, max;
1676
1677 max = pjsua_call_get_max_count();
1678 for (i=current_call-1; i>=0; --i) {
1679 if (pjsua_call_is_active(i)) {
1680 current_call = i;
1681 return PJ_TRUE;
1682 }
1683 }
1684
1685 for (i=max-1; i>current_call; --i) {
1686 if (pjsua_call_is_active(i)) {
1687 current_call = i;
1688 return PJ_TRUE;
1689 }
1690 }
1691
1692 current_call = PJSUA_INVALID_ID;
1693 return PJ_FALSE;
1694}
1695
1696
Benny Prijono804ff0a2006-09-14 11:17:48 +00001697/* Callback from timer when the maximum call duration has been
1698 * exceeded.
1699 */
1700static void call_timeout_callback(pj_timer_heap_t *timer_heap,
1701 struct pj_timer_entry *entry)
1702{
1703 pjsua_call_id call_id = entry->id;
1704 pjsua_msg_data msg_data;
1705 pjsip_generic_string_hdr warn;
1706 pj_str_t hname = pj_str("Warning");
1707 pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\"");
1708
1709 PJ_UNUSED_ARG(timer_heap);
1710
Benny Prijono148c9dd2006-09-19 13:37:53 +00001711 if (call_id == PJSUA_INVALID_ID) {
1712 PJ_LOG(1,(THIS_FILE, "Invalid call ID in timer callback"));
1713 return;
1714 }
1715
Benny Prijono804ff0a2006-09-14 11:17:48 +00001716 /* Add warning header */
1717 pjsua_msg_data_init(&msg_data);
1718 pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
1719 pj_list_push_back(&msg_data.hdr_list, &warn);
1720
1721 /* Call duration has been exceeded; disconnect the call */
1722 PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
1723 "for call %d, disconnecting the call",
1724 app_config.duration, call_id));
1725 entry->id = PJSUA_INVALID_ID;
1726 pjsua_call_hangup(call_id, 200, NULL, &msg_data);
1727}
1728
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001729
1730/*
1731 * Handler when invite state has changed.
1732 */
1733static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
1734{
1735 pjsua_call_info call_info;
1736
1737 PJ_UNUSED_ARG(e);
1738
1739 pjsua_call_get_info(call_id, &call_info);
1740
1741 if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
1742
Benny Prijono804ff0a2006-09-14 11:17:48 +00001743 /* Cancel duration timer, if any */
1744 if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
1745 struct call_data *cd = &app_config.call_data[call_id];
1746 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
1747
1748 cd->timer.id = PJSUA_INVALID_ID;
1749 pjsip_endpt_cancel_timer(endpt, &cd->timer);
1750 }
1751
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001752 PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]",
1753 call_id,
1754 call_info.last_status,
1755 call_info.last_status_text.ptr));
1756
1757 if (call_id == current_call) {
1758 find_next_call();
1759 }
1760
Benny Prijono4be63b52006-11-25 14:50:25 +00001761 /* Dump media state upon disconnected */
1762 if (1) {
Benny Prijono6eddd872008-03-21 13:46:08 +00001763 pjsua_call_dump(call_id, PJ_TRUE, some_buf,
1764 sizeof(some_buf), " ");
Benny Prijono4be63b52006-11-25 14:50:25 +00001765 PJ_LOG(5,(THIS_FILE,
1766 "Call %d disconnected, dumping media stats\n%s",
Benny Prijono6eddd872008-03-21 13:46:08 +00001767 call_id, some_buf));
Benny Prijono4be63b52006-11-25 14:50:25 +00001768 }
1769
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001770 } else {
1771
Benny Prijono804ff0a2006-09-14 11:17:48 +00001772 if (app_config.duration!=NO_LIMIT &&
1773 call_info.state == PJSIP_INV_STATE_CONFIRMED)
1774 {
1775 /* Schedule timer to hangup call after the specified duration */
1776 struct call_data *cd = &app_config.call_data[call_id];
1777 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
1778 pj_time_val delay;
1779
1780 cd->timer.id = call_id;
1781 delay.sec = app_config.duration;
1782 delay.msec = 0;
1783 pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
1784 }
1785
Benny Prijono4be63b52006-11-25 14:50:25 +00001786 if (call_info.state == PJSIP_INV_STATE_EARLY) {
1787 int code;
1788 pj_str_t reason;
1789 pjsip_msg *msg;
1790
1791 /* This can only occur because of TX or RX message */
1792 pj_assert(e->type == PJSIP_EVENT_TSX_STATE);
1793
1794 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
1795 msg = e->body.tsx_state.src.rdata->msg_info.msg;
1796 } else {
1797 msg = e->body.tsx_state.src.tdata->msg;
1798 }
1799
1800 code = msg->line.status.code;
1801 reason = msg->line.status.reason;
1802
1803 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s (%d %.*s)",
1804 call_id, call_info.state_text.ptr,
1805 code, (int)reason.slen, reason.ptr));
1806 } else {
1807 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
1808 call_id,
1809 call_info.state_text.ptr));
1810 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001811
1812 if (current_call==PJSUA_INVALID_ID)
1813 current_call = call_id;
1814
1815 }
1816}
1817
1818
1819/**
1820 * Handler when there is incoming call.
1821 */
1822static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
1823 pjsip_rx_data *rdata)
1824{
1825 pjsua_call_info call_info;
1826
1827 PJ_UNUSED_ARG(acc_id);
1828 PJ_UNUSED_ARG(rdata);
1829
1830 pjsua_call_get_info(call_id, &call_info);
1831
1832 if (app_config.auto_answer > 0) {
1833 pjsua_call_answer(call_id, app_config.auto_answer, NULL, NULL);
1834 }
1835
1836 if (app_config.auto_answer < 200) {
1837 PJ_LOG(3,(THIS_FILE,
1838 "Incoming call for account %d!\n"
1839 "From: %s\n"
1840 "To: %s\n"
1841 "Press a to answer or h to reject call",
1842 acc_id,
1843 call_info.remote_info.ptr,
1844 call_info.local_info.ptr));
1845 }
1846}
1847
1848
1849/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001850 * Handler when a transaction within a call has changed state.
1851 */
1852static void on_call_tsx_state(pjsua_call_id call_id,
1853 pjsip_transaction *tsx,
1854 pjsip_event *e)
1855{
1856 const pjsip_method info_method =
1857 {
1858 PJSIP_OTHER_METHOD,
1859 { "INFO", 4 }
1860 };
1861
1862 if (pjsip_method_cmp(&tsx->method, &info_method)==0) {
1863 /*
1864 * Handle INFO method.
1865 */
1866 if (tsx->role == PJSIP_ROLE_UAC &&
1867 (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
Benny Prijonob071a782007-10-10 13:12:37 +00001868 (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
1869 e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED)))
Benny Prijonofeb69f42007-10-05 09:12:26 +00001870 {
1871 /* Status of outgoing INFO request */
1872 if (tsx->status_code >= 200 && tsx->status_code < 300) {
1873 PJ_LOG(4,(THIS_FILE,
1874 "Call %d: DTMF sent successfully with INFO",
1875 call_id));
1876 } else if (tsx->status_code >= 300) {
1877 PJ_LOG(4,(THIS_FILE,
1878 "Call %d: Failed to send DTMF with INFO: %d/%.*s",
1879 call_id,
1880 tsx->status_code,
1881 (int)tsx->status_text.slen,
1882 tsx->status_text.ptr));
1883 }
1884 } else if (tsx->role == PJSIP_ROLE_UAS &&
1885 tsx->state == PJSIP_TSX_STATE_TRYING)
1886 {
1887 /* Answer incoming INFO with 200/OK */
1888 pjsip_rx_data *rdata;
1889 pjsip_tx_data *tdata;
1890 pj_status_t status;
1891
1892 rdata = e->body.tsx_state.src.rdata;
1893
1894 if (rdata->msg_info.msg->body) {
1895 status = pjsip_endpt_create_response(tsx->endpt, rdata,
1896 200, NULL, &tdata);
1897 if (status == PJ_SUCCESS)
1898 status = pjsip_tsx_send_msg(tsx, tdata);
1899
1900 PJ_LOG(3,(THIS_FILE, "Call %d: incoming INFO:\n%.*s",
1901 call_id,
1902 (int)rdata->msg_info.msg->body->len,
1903 rdata->msg_info.msg->body->data));
1904 } else {
1905 status = pjsip_endpt_create_response(tsx->endpt, rdata,
1906 400, NULL, &tdata);
1907 if (status == PJ_SUCCESS)
1908 status = pjsip_tsx_send_msg(tsx, tdata);
1909 }
1910 }
1911 }
1912}
1913
1914
1915/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001916 * Callback on media state changed event.
1917 * The action may connect the call to sound device, to file, or
1918 * to loop the call.
1919 */
1920static void on_call_media_state(pjsua_call_id call_id)
1921{
1922 pjsua_call_info call_info;
1923
1924 pjsua_call_get_info(call_id, &call_info);
1925
1926 if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
1927 pj_bool_t connect_sound = PJ_TRUE;
1928
1929 /* Loopback sound, if desired */
1930 if (app_config.auto_loop) {
1931 pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot);
1932 connect_sound = PJ_FALSE;
Benny Prijono1ebd6142006-10-19 15:48:02 +00001933
1934 /* Automatically record conversation, if desired */
1935 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1936 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
1937 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001938 }
1939
1940 /* Stream a file, if desired */
1941 if (app_config.auto_play && app_config.wav_port != PJSUA_INVALID_ID) {
1942 pjsua_conf_connect(app_config.wav_port, call_info.conf_slot);
1943 connect_sound = PJ_FALSE;
1944 }
1945
Benny Prijono7ca96da2006-08-07 12:11:40 +00001946 /* Put call in conference with other calls, if desired */
1947 if (app_config.auto_conf) {
1948 pjsua_call_id call_ids[PJSUA_MAX_CALLS];
Benny Prijono10861bc2006-08-07 13:22:43 +00001949 unsigned call_cnt=PJ_ARRAY_SIZE(call_ids);
Benny Prijono7ca96da2006-08-07 12:11:40 +00001950 unsigned i;
1951
1952 /* Get all calls, and establish media connection between
1953 * this call and other calls.
1954 */
1955 pjsua_enum_calls(call_ids, &call_cnt);
Benny Prijono10861bc2006-08-07 13:22:43 +00001956
Benny Prijono7ca96da2006-08-07 12:11:40 +00001957 for (i=0; i<call_cnt; ++i) {
1958 if (call_ids[i] == call_id)
1959 continue;
1960
1961 if (!pjsua_call_has_media(call_ids[i]))
1962 continue;
1963
1964 pjsua_conf_connect(call_info.conf_slot,
1965 pjsua_call_get_conf_port(call_ids[i]));
1966 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
1967 call_info.conf_slot);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001968
1969 /* Automatically record conversation, if desired */
1970 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1971 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
1972 app_config.rec_port);
1973 }
1974
Benny Prijono7ca96da2006-08-07 12:11:40 +00001975 }
1976
1977 /* Also connect call to local sound device */
1978 connect_sound = PJ_TRUE;
1979 }
1980
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001981 /* Otherwise connect to sound device */
1982 if (connect_sound) {
1983 pjsua_conf_connect(call_info.conf_slot, 0);
1984 pjsua_conf_connect(0, call_info.conf_slot);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001985
1986 /* Automatically record conversation, if desired */
1987 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1988 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
1989 pjsua_conf_connect(0, app_config.rec_port);
1990 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001991 }
1992
1993 PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id));
1994
1995 } else if (call_info.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
1996 PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local",
1997 call_id));
1998 } else if (call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD) {
1999 PJ_LOG(3,(THIS_FILE,
2000 "Media for call %d is suspended (hold) by remote",
2001 call_id));
Benny Prijono096c56c2007-09-15 08:30:16 +00002002 } else if (call_info.media_status == PJSUA_CALL_MEDIA_ERROR) {
2003 pj_str_t reason = pj_str("ICE negotiation failed");
2004
2005 PJ_LOG(1,(THIS_FILE,
2006 "Media has reported error, disconnecting call"));
2007
2008 pjsua_call_hangup(call_id, 500, &reason, NULL);
2009
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002010 } else {
2011 PJ_LOG(3,(THIS_FILE,
2012 "Media for call %d is inactive",
2013 call_id));
2014 }
2015}
2016
Benny Prijono0875ae82006-12-26 00:11:48 +00002017/*
2018 * DTMF callback.
2019 */
2020static void call_on_dtmf_callback(pjsua_call_id call_id, int dtmf)
2021{
2022 PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c", call_id, dtmf));
2023}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002024
2025/*
2026 * Handler registration status has changed.
2027 */
2028static void on_reg_state(pjsua_acc_id acc_id)
2029{
2030 PJ_UNUSED_ARG(acc_id);
2031
2032 // Log already written.
2033}
2034
2035
2036/*
2037 * Handler on buddy state changed.
2038 */
2039static void on_buddy_state(pjsua_buddy_id buddy_id)
2040{
2041 pjsua_buddy_info info;
2042 pjsua_buddy_get_info(buddy_id, &info);
2043
2044 PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s",
2045 (int)info.uri.slen,
2046 info.uri.ptr,
2047 (int)info.status_text.slen,
2048 info.status_text.ptr));
2049}
2050
2051
2052/**
2053 * Incoming IM message (i.e. MESSAGE request)!
2054 */
2055static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
2056 const pj_str_t *to, const pj_str_t *contact,
2057 const pj_str_t *mime_type, const pj_str_t *text)
2058{
2059 /* Note: call index may be -1 */
2060 PJ_UNUSED_ARG(call_id);
2061 PJ_UNUSED_ARG(to);
2062 PJ_UNUSED_ARG(contact);
2063 PJ_UNUSED_ARG(mime_type);
2064
Benny Prijonof4b538d2007-05-14 16:45:20 +00002065 PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s (%.*s)",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002066 (int)from->slen, from->ptr,
Benny Prijonof4b538d2007-05-14 16:45:20 +00002067 (int)text->slen, text->ptr,
2068 (int)mime_type->slen, mime_type->ptr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002069}
2070
2071
2072/**
2073 * Received typing indication
2074 */
2075static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
2076 const pj_str_t *to, const pj_str_t *contact,
2077 pj_bool_t is_typing)
2078{
2079 PJ_UNUSED_ARG(call_id);
2080 PJ_UNUSED_ARG(to);
2081 PJ_UNUSED_ARG(contact);
2082
2083 PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
2084 (int)from->slen, from->ptr,
2085 (is_typing?"is typing..":"has stopped typing")));
2086}
2087
2088
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002089/**
2090 * Call transfer request status.
2091 */
2092static void on_call_transfer_status(pjsua_call_id call_id,
2093 int status_code,
2094 const pj_str_t *status_text,
2095 pj_bool_t final,
2096 pj_bool_t *p_cont)
2097{
2098 PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
2099 call_id, status_code,
2100 (int)status_text->slen, status_text->ptr,
2101 (final ? "[final]" : "")));
2102
2103 if (status_code/100 == 2) {
2104 PJ_LOG(3,(THIS_FILE,
2105 "Call %d: call transfered successfully, disconnecting call",
2106 call_id));
2107 pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
2108 *p_cont = PJ_FALSE;
2109 }
2110}
2111
2112
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002113/*
Benny Prijonof7b1c392006-11-11 16:46:34 +00002114 * Notification that call is being replaced.
2115 */
2116static void on_call_replaced(pjsua_call_id old_call_id,
2117 pjsua_call_id new_call_id)
2118{
2119 pjsua_call_info old_ci, new_ci;
2120
2121 pjsua_call_get_info(old_call_id, &old_ci);
2122 pjsua_call_get_info(new_call_id, &new_ci);
2123
2124 PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
2125 "call %d with %.*s",
2126 old_call_id,
2127 (int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
2128 new_call_id,
2129 (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
2130}
2131
2132
2133/*
Benny Prijono6ba8c542007-10-16 01:34:14 +00002134 * NAT type detection callback.
2135 */
2136static void on_nat_detect(const pj_stun_nat_detect_result *res)
2137{
2138 if (res->status != PJ_SUCCESS) {
2139 pjsua_perror(THIS_FILE, "NAT detection failed", res->status);
2140 } else {
2141 PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name));
2142 }
2143}
2144
2145
2146/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002147 * Print buddy list.
2148 */
2149static void print_buddy_list(void)
2150{
2151 pjsua_buddy_id ids[64];
2152 int i;
2153 unsigned count = PJ_ARRAY_SIZE(ids);
2154
2155 puts("Buddy list:");
2156
2157 pjsua_enum_buddies(ids, &count);
2158
2159 if (count == 0)
2160 puts(" -none-");
2161 else {
2162 for (i=0; i<(int)count; ++i) {
2163 pjsua_buddy_info info;
2164
2165 if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
2166 continue;
2167
Benny Prijono4461c7d2007-08-25 13:36:15 +00002168 printf(" [%2d] <%.*s> %.*s\n",
2169 ids[i]+1,
2170 (int)info.status_text.slen,
2171 info.status_text.ptr,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002172 (int)info.uri.slen,
2173 info.uri.ptr);
2174 }
2175 }
2176 puts("");
2177}
2178
2179
2180/*
2181 * Print account status.
2182 */
2183static void print_acc_status(int acc_id)
2184{
2185 char buf[80];
2186 pjsua_acc_info info;
2187
2188 pjsua_acc_get_info(acc_id, &info);
2189
2190 if (!info.has_registration) {
2191 pj_ansi_snprintf(buf, sizeof(buf), "%.*s",
2192 (int)info.status_text.slen,
2193 info.status_text.ptr);
2194
2195 } else {
2196 pj_ansi_snprintf(buf, sizeof(buf),
2197 "%d/%.*s (expires=%d)",
2198 info.status,
2199 (int)info.status_text.slen,
2200 info.status_text.ptr,
2201 info.expires);
2202
2203 }
2204
2205 printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
2206 acc_id, (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
Benny Prijono4461c7d2007-08-25 13:36:15 +00002207 printf(" Online status: %.*s\n",
2208 (int)info.online_status_text.slen,
2209 info.online_status_text.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002210}
2211
2212
2213/*
2214 * Show a bit of help.
2215 */
2216static void keystroke_help(void)
2217{
2218 pjsua_acc_id acc_ids[16];
2219 unsigned count = PJ_ARRAY_SIZE(acc_ids);
2220 int i;
2221
2222 printf(">>>>\n");
2223
2224 pjsua_enum_accs(acc_ids, &count);
2225
2226 printf("Account list:\n");
2227 for (i=0; i<(int)count; ++i)
2228 print_acc_status(acc_ids[i]);
2229
2230 print_buddy_list();
2231
2232 //puts("Commands:");
2233 puts("+=============================================================================+");
2234 puts("| Call Commands: | Buddy, IM & Presence: | Account: |");
2235 puts("| | | |");
2236 puts("| m Make new call | +b Add new buddy .| +a Add new accnt |");
2237 puts("| M Make multiple calls | -b Delete buddy | -a Delete accnt. |");
Benny Prijono4461c7d2007-08-25 13:36:15 +00002238 puts("| a Answer call | i Send IM | !a Modify accnt. |");
2239 puts("| h Hangup call (ha=all) | s Subscribe presence | rr (Re-)register |");
2240 puts("| H Hold call | u Unsubscribe presence | ru Unregister |");
2241 puts("| v re-inVite (release hold) | t ToGgle Online status | > Cycle next ac.|");
Benny Prijonoc08682e2007-10-04 06:17:58 +00002242 puts("| U send UPDATE | T Set online status | < Cycle prev ac.|");
2243 puts("| ],[ Select next/prev call +--------------------------+-------------------+");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002244 puts("| x Xfer call | Media Commands: | Status & Config: |");
Benny Prijonof7b1c392006-11-11 16:46:34 +00002245 puts("| X Xfer with Replaces | | |");
Benny Prijonoc08682e2007-10-04 06:17:58 +00002246 puts("| # Send RFC 2833 DTMF | cl List ports | d Dump status |");
2247 puts("| * Send DTMF with INFO | cc Connect port | dd Dump detailed |");
2248 puts("| dq Dump curr. call quality | cd Disconnect port | dc Dump config |");
2249 puts("| | V Adjust audio Volume | f Save config |");
2250 puts("| S Send arbitrary REQUEST | Cp Codec priorities | f Save config |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002251 puts("+------------------------------+--------------------------+-------------------+");
Benny Prijono438e65b2007-11-03 21:42:10 +00002252 puts("| q QUIT sleep N: console sleep for N ms n: detect NAT type |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002253 puts("+=============================================================================+");
Benny Prijono48af79c2006-07-22 12:49:17 +00002254
2255 i = pjsua_call_get_count();
2256 printf("You have %d active call%s\n", i, (i>1?"s":""));
Benny Prijonof7b1c392006-11-11 16:46:34 +00002257
2258 if (current_call != PJSUA_INVALID_ID) {
2259 pjsua_call_info ci;
2260 if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
2261 printf("Current call id=%d to %.*s [%.*s]\n", current_call,
2262 (int)ci.remote_info.slen, ci.remote_info.ptr,
2263 (int)ci.state_text.slen, ci.state_text.ptr);
2264 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002265}
2266
2267
2268/*
2269 * Input simple string
2270 */
2271static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
2272{
2273 char *p;
2274
2275 printf("%s (empty to cancel): ", title); fflush(stdout);
2276 fgets(buf, len, stdin);
2277
2278 /* Remove trailing newlines. */
2279 for (p=buf; ; ++p) {
2280 if (*p=='\r' || *p=='\n') *p='\0';
2281 else if (!*p) break;
2282 }
2283
2284 if (!*buf)
2285 return PJ_FALSE;
2286
2287 return PJ_TRUE;
2288}
2289
2290
2291#define NO_NB -2
2292struct input_result
2293{
2294 int nb_result;
2295 char *uri_result;
2296};
2297
2298
2299/*
2300 * Input URL.
2301 */
2302static void ui_input_url(const char *title, char *buf, int len,
2303 struct input_result *result)
2304{
2305 result->nb_result = NO_NB;
2306 result->uri_result = NULL;
2307
2308 print_buddy_list();
2309
2310 printf("Choices:\n"
2311 " 0 For current dialog.\n"
2312 " -1 All %d buddies in buddy list\n"
2313 " [1 -%2d] Select from buddy list\n"
2314 " URL An URL\n"
2315 " <Enter> Empty input (or 'q') to cancel\n"
2316 , pjsua_get_buddy_count(), pjsua_get_buddy_count());
2317 printf("%s: ", title);
2318
2319 fflush(stdout);
2320 fgets(buf, len, stdin);
2321 len = strlen(buf);
2322
2323 /* Left trim */
2324 while (pj_isspace(*buf)) {
2325 ++buf;
2326 --len;
2327 }
2328
2329 /* Remove trailing newlines */
2330 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
2331 buf[--len] = '\0';
2332
2333 if (len == 0 || buf[0]=='q')
2334 return;
2335
2336 if (pj_isdigit(*buf) || *buf=='-') {
2337
2338 int i;
2339
2340 if (*buf=='-')
2341 i = 1;
2342 else
2343 i = 0;
2344
2345 for (; i<len; ++i) {
2346 if (!pj_isdigit(buf[i])) {
2347 puts("Invalid input");
2348 return;
2349 }
2350 }
2351
2352 result->nb_result = my_atoi(buf);
2353
2354 if (result->nb_result >= 0 &&
2355 result->nb_result <= (int)pjsua_get_buddy_count())
2356 {
2357 return;
2358 }
2359 if (result->nb_result == -1)
2360 return;
2361
2362 puts("Invalid input");
2363 result->nb_result = NO_NB;
2364 return;
2365
2366 } else {
2367 pj_status_t status;
2368
2369 if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
2370 pjsua_perror(THIS_FILE, "Invalid URL", status);
2371 return;
2372 }
2373
2374 result->uri_result = buf;
2375 }
2376}
2377
2378/*
2379 * List the ports in conference bridge
2380 */
2381static void conf_list(void)
2382{
2383 unsigned i, count;
2384 pjsua_conf_port_id id[PJSUA_MAX_CALLS];
2385
2386 printf("Conference ports:\n");
2387
2388 count = PJ_ARRAY_SIZE(id);
2389 pjsua_enum_conf_ports(id, &count);
2390
2391 for (i=0; i<count; ++i) {
2392 char txlist[PJSUA_MAX_CALLS*4+10];
2393 unsigned j;
2394 pjsua_conf_port_info info;
2395
2396 pjsua_conf_get_port_info(id[i], &info);
2397
2398 txlist[0] = '\0';
2399 for (j=0; j<info.listener_cnt; ++j) {
2400 char s[10];
2401 pj_ansi_sprintf(s, "#%d ", info.listeners[j]);
2402 pj_ansi_strcat(txlist, s);
2403 }
Benny Prijono7d60d052008-03-29 12:24:20 +00002404 printf("Port #%02d[%2dKHz/%dms/%d] %20.*s transmitting to: %s\n",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002405 info.slot_id,
2406 info.clock_rate/1000,
2407 info.samples_per_frame * 1000 / info.clock_rate,
Benny Prijono7d60d052008-03-29 12:24:20 +00002408 info.channel_count,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002409 (int)info.name.slen,
2410 info.name.ptr,
2411 txlist);
2412
2413 }
2414 puts("");
2415}
2416
2417
2418/*
Benny Prijono56315612006-07-18 14:39:40 +00002419 * Send arbitrary request to remote host
2420 */
2421static void send_request(char *cstr_method, const pj_str_t *dst_uri)
2422{
2423 pj_str_t str_method;
2424 pjsip_method method;
2425 pjsip_tx_data *tdata;
Benny Prijono56315612006-07-18 14:39:40 +00002426 pjsip_endpoint *endpt;
2427 pj_status_t status;
2428
2429 endpt = pjsua_get_pjsip_endpt();
2430
2431 str_method = pj_str(cstr_method);
2432 pjsip_method_init_np(&method, &str_method);
2433
Benny Prijonofff245c2007-04-02 11:44:47 +00002434 status = pjsua_acc_create_request(current_acc, &method, dst_uri, &tdata);
Benny Prijono56315612006-07-18 14:39:40 +00002435
Benny Prijonob988d762007-12-05 04:30:04 +00002436 status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL);
Benny Prijono56315612006-07-18 14:39:40 +00002437 if (status != PJ_SUCCESS) {
Benny Prijonob988d762007-12-05 04:30:04 +00002438 pjsua_perror(THIS_FILE, "Unable to send request", status);
Benny Prijono56315612006-07-18 14:39:40 +00002439 return;
2440 }
2441}
2442
2443
2444/*
Benny Prijono4461c7d2007-08-25 13:36:15 +00002445 * Change extended online status.
2446 */
2447static void change_online_status(void)
2448{
2449 char menuin[32];
2450 pj_bool_t online_status;
2451 pjrpid_element elem;
2452 int i, choice;
2453
2454 enum {
2455 AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX
2456 };
2457
2458 struct opt {
2459 int id;
2460 char *name;
2461 } opts[] = {
2462 { AVAILABLE, "Available" },
2463 { BUSY, "Busy"},
2464 { OTP, "On the phone"},
2465 { IDLE, "Idle"},
2466 { AWAY, "Away"},
2467 { BRB, "Be right back"},
2468 { OFFLINE, "Offline"}
2469 };
2470
2471 printf("\n"
2472 "Choices:\n");
2473 for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) {
2474 printf(" %d %s\n", opts[i].id+1, opts[i].name);
2475 }
2476
2477 if (!simple_input("Select status", menuin, sizeof(menuin)))
2478 return;
2479
2480 choice = atoi(menuin) - 1;
2481 if (choice < 0 || choice >= OPT_MAX) {
2482 puts("Invalid selection");
2483 return;
2484 }
2485
2486 pj_bzero(&elem, sizeof(elem));
2487 elem.type = PJRPID_ELEMENT_TYPE_PERSON;
2488
2489 online_status = PJ_TRUE;
2490
2491 switch (choice) {
2492 case AVAILABLE:
2493 break;
2494 case BUSY:
2495 elem.activity = PJRPID_ACTIVITY_BUSY;
2496 elem.note = pj_str("Busy");
2497 break;
2498 case OTP:
2499 elem.activity = PJRPID_ACTIVITY_BUSY;
2500 elem.note = pj_str("On the phone");
2501 break;
2502 case IDLE:
2503 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
2504 elem.note = pj_str("Idle");
2505 break;
2506 case AWAY:
2507 elem.activity = PJRPID_ACTIVITY_AWAY;
2508 elem.note = pj_str("Away");
2509 break;
2510 case BRB:
2511 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
2512 elem.note = pj_str("Be right back");
2513 break;
2514 case OFFLINE:
2515 online_status = PJ_FALSE;
2516 break;
2517 }
2518
2519 pjsua_acc_set_online_status2(current_acc, online_status, &elem);
2520}
2521
2522
2523/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00002524 * Change codec priorities.
2525 */
2526static void manage_codec_prio(void)
2527{
2528 pjsua_codec_info c[32];
2529 unsigned i, count = PJ_ARRAY_SIZE(c);
2530 char input[32];
2531 char *codec, *prio;
2532 pj_str_t id;
2533 int new_prio;
2534 pj_status_t status;
2535
2536 printf("List of codecs:\n");
2537
2538 pjsua_enum_codecs(c, &count);
2539 for (i=0; i<count; ++i) {
2540 printf(" %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
2541 c[i].codec_id.ptr);
2542 }
2543
2544 puts("");
2545 puts("Enter codec name and its new priority (e.g. \"speex/16000 200\"), empty to cancel:");
2546
2547 printf("Codec name and priority: ");
2548 fgets(input, sizeof(input), stdin);
2549 if (input[0]=='\r' || input[0]=='\n') {
2550 puts("Done");
2551 return;
2552 }
2553
2554 codec = strtok(input, " \t\r\n");
2555 prio = strtok(NULL, " \r\n");
2556
2557 if (!codec || !prio) {
2558 puts("Invalid input");
2559 return;
2560 }
2561
2562 new_prio = atoi(prio);
2563 if (new_prio < 0)
2564 new_prio = 0;
2565 else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST)
2566 new_prio = PJMEDIA_CODEC_PRIO_HIGHEST;
2567
2568 status = pjsua_codec_set_priority(pj_cstr(&id, codec),
2569 (pj_uint8_t)new_prio);
2570 if (status != PJ_SUCCESS)
2571 pjsua_perror(THIS_FILE, "Error setting codec priority", status);
2572}
2573
2574
2575/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002576 * Main "user interface" loop.
2577 */
2578void console_app_main(const pj_str_t *uri_to_call)
2579{
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002580 char menuin[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002581 char buf[128];
2582 char text[128];
2583 int i, count;
2584 char *uri;
2585 pj_str_t tmp;
2586 struct input_result result;
2587 pjsua_call_info call_info;
2588 pjsua_acc_info acc_info;
2589
2590
2591 /* If user specifies URI to call, then call the URI */
2592 if (uri_to_call->slen) {
2593 pjsua_call_make_call( current_acc, uri_to_call, 0, NULL, NULL, NULL);
2594 }
2595
2596 keystroke_help();
2597
2598 for (;;) {
2599
2600 printf(">>> ");
2601 fflush(stdout);
2602
Benny Prijono990042e2007-01-21 19:36:00 +00002603 if (fgets(menuin, sizeof(menuin), stdin) == NULL) {
2604 /*
2605 * Be friendly to users who redirect commands into
2606 * program, when file ends, resume with kbd.
2607 * If exit is desired end script with q for quit
2608 */
2609 /* Reopen stdin/stdout/stderr to /dev/console */
2610#if defined(PJ_WIN32) && PJ_WIN32!=0
2611 if (freopen ("CONIN$", "r", stdin) == NULL) {
2612#else
2613 if (1) {
2614#endif
2615 puts("Cannot switch back to console from file redirection");
2616 menuin[0] = 'q';
2617 menuin[1] = '\0';
2618 } else {
2619 puts("Switched back to console from file redirection");
2620 continue;
2621 }
2622 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002623
2624 switch (menuin[0]) {
2625
2626 case 'm':
2627 /* Make call! : */
2628 printf("(You currently have %d calls)\n",
2629 pjsua_call_get_count());
2630
2631 uri = NULL;
2632 ui_input_url("Make call", buf, sizeof(buf), &result);
2633 if (result.nb_result != NO_NB) {
2634
2635 if (result.nb_result == -1 || result.nb_result == 0) {
2636 puts("You can't do that with make call!");
2637 continue;
2638 } else {
2639 pjsua_buddy_info binfo;
2640 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2641 uri = binfo.uri.ptr;
2642 }
2643
2644 } else if (result.uri_result) {
2645 uri = result.uri_result;
2646 }
2647
2648 tmp = pj_str(uri);
2649 pjsua_call_make_call( current_acc, &tmp, 0, NULL, NULL, NULL);
2650 break;
2651
2652 case 'M':
2653 /* Make multiple calls! : */
2654 printf("(You currently have %d calls)\n",
2655 pjsua_call_get_count());
2656
2657 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
2658 continue;
2659
2660 count = my_atoi(menuin);
2661 if (count < 1)
2662 continue;
2663
2664 ui_input_url("Make call", buf, sizeof(buf), &result);
2665 if (result.nb_result != NO_NB) {
2666 pjsua_buddy_info binfo;
2667 if (result.nb_result == -1 || result.nb_result == 0) {
2668 puts("You can't do that with make call!");
2669 continue;
2670 }
2671 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2672 uri = binfo.uri.ptr;
2673 } else {
2674 uri = result.uri_result;
2675 }
2676
2677 for (i=0; i<my_atoi(menuin); ++i) {
2678 pj_status_t status;
2679
2680 tmp = pj_str(uri);
2681 status = pjsua_call_make_call(current_acc, &tmp, 0, NULL,
2682 NULL, NULL);
2683 if (status != PJ_SUCCESS)
2684 break;
2685 }
2686 break;
2687
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002688 case 'n':
Benny Prijono438e65b2007-11-03 21:42:10 +00002689 i = pjsua_detect_nat_type();
2690 if (i != PJ_SUCCESS)
2691 pjsua_perror(THIS_FILE, "Error", i);
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002692 break;
2693
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002694 case 'i':
2695 /* Send instant messaeg */
2696
2697 /* i is for call index to send message, if any */
2698 i = -1;
2699
2700 /* Make compiler happy. */
2701 uri = NULL;
2702
2703 /* Input destination. */
2704 ui_input_url("Send IM to", buf, sizeof(buf), &result);
2705 if (result.nb_result != NO_NB) {
2706
2707 if (result.nb_result == -1) {
2708 puts("You can't send broadcast IM like that!");
2709 continue;
2710
2711 } else if (result.nb_result == 0) {
2712
2713 i = current_call;
2714
2715 } else {
2716 pjsua_buddy_info binfo;
2717 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2718 uri = binfo.uri.ptr;
2719 }
2720
2721 } else if (result.uri_result) {
2722 uri = result.uri_result;
2723 }
2724
2725
2726 /* Send typing indication. */
2727 if (i != -1)
2728 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
2729 else {
2730 pj_str_t tmp_uri = pj_str(uri);
2731 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
2732 }
2733
2734 /* Input the IM . */
2735 if (!simple_input("Message", text, sizeof(text))) {
2736 /*
2737 * Cancelled.
2738 * Send typing notification too, saying we're not typing.
2739 */
2740 if (i != -1)
2741 pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
2742 else {
2743 pj_str_t tmp_uri = pj_str(uri);
2744 pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
2745 }
2746 continue;
2747 }
2748
2749 tmp = pj_str(text);
2750
2751 /* Send the IM */
2752 if (i != -1)
2753 pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
2754 else {
2755 pj_str_t tmp_uri = pj_str(uri);
2756 pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
2757 }
2758
2759 break;
2760
2761 case 'a':
2762
2763 if (current_call != -1) {
2764 pjsua_call_get_info(current_call, &call_info);
2765 } else {
2766 /* Make compiler happy */
2767 call_info.role = PJSIP_ROLE_UAC;
2768 call_info.state = PJSIP_INV_STATE_DISCONNECTED;
2769 }
2770
2771 if (current_call == -1 ||
2772 call_info.role != PJSIP_ROLE_UAS ||
2773 call_info.state >= PJSIP_INV_STATE_CONNECTING)
2774 {
2775 puts("No pending incoming call");
2776 fflush(stdout);
2777 continue;
2778
2779 } else {
Benny Prijono20d36722007-02-22 14:52:24 +00002780 int st_code;
2781 char contact[120];
2782 pj_str_t hname = { "Contact", 7 };
2783 pj_str_t hvalue;
2784 pjsip_generic_string_hdr hcontact;
2785 pjsua_msg_data msg_data;
2786
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002787 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
2788 continue;
2789
Benny Prijono20d36722007-02-22 14:52:24 +00002790 st_code = my_atoi(buf);
2791 if (st_code < 100)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002792 continue;
2793
Benny Prijono20d36722007-02-22 14:52:24 +00002794 pjsua_msg_data_init(&msg_data);
2795
2796 if (st_code/100 == 3) {
2797 if (!simple_input("Enter URL to be put in Contact",
2798 contact, sizeof(contact)))
2799 continue;
2800 hvalue = pj_str(contact);
2801 pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue);
2802
2803 pj_list_push_back(&msg_data.hdr_list, &hcontact);
2804 }
2805
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002806 /*
2807 * Must check again!
2808 * Call may have been disconnected while we're waiting for
2809 * keyboard input.
2810 */
2811 if (current_call == -1) {
2812 puts("Call has been disconnected");
2813 fflush(stdout);
2814 continue;
2815 }
2816
Benny Prijono20d36722007-02-22 14:52:24 +00002817 pjsua_call_answer(current_call, st_code, NULL, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002818 }
2819
2820 break;
2821
2822
2823 case 'h':
2824
2825 if (current_call == -1) {
2826 puts("No current call");
2827 fflush(stdout);
2828 continue;
2829
2830 } else if (menuin[1] == 'a') {
2831
2832 /* Hangup all calls */
2833 pjsua_call_hangup_all();
2834
2835 } else {
2836
2837 /* Hangup current calls */
2838 pjsua_call_hangup(current_call, 0, NULL, NULL);
2839 }
2840 break;
2841
2842 case ']':
2843 case '[':
2844 /*
2845 * Cycle next/prev dialog.
2846 */
2847 if (menuin[0] == ']') {
2848 find_next_call();
2849
2850 } else {
2851 find_prev_call();
2852 }
2853
2854 if (current_call != -1) {
2855
2856 pjsua_call_get_info(current_call, &call_info);
2857 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s",
2858 (int)call_info.remote_info.slen,
2859 call_info.remote_info.ptr));
2860
2861 } else {
2862 PJ_LOG(3,(THIS_FILE,"No current dialog"));
2863 }
2864 break;
2865
2866
2867 case '>':
2868 case '<':
2869 if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
2870 break;
2871
2872 i = my_atoi(buf);
2873 if (pjsua_acc_is_valid(i)) {
Benny Prijono21b9ad92006-08-15 13:11:22 +00002874 pjsua_acc_set_default(i);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002875 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
2876 } else {
2877 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
2878 }
2879 break;
2880
2881
2882 case '+':
2883 if (menuin[1] == 'b') {
2884
2885 pjsua_buddy_config buddy_cfg;
2886 pjsua_buddy_id buddy_id;
2887 pj_status_t status;
2888
2889 if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
2890 break;
2891
2892 if (pjsua_verify_sip_url(buf) != PJ_SUCCESS) {
2893 printf("Invalid SIP URI '%s'\n", buf);
2894 break;
2895 }
2896
Benny Prijonoac623b32006-07-03 15:19:31 +00002897 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002898
2899 buddy_cfg.uri = pj_str(buf);
2900 buddy_cfg.subscribe = PJ_TRUE;
2901
2902 status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
2903 if (status == PJ_SUCCESS) {
2904 printf("New buddy '%s' added at index %d\n",
2905 buf, buddy_id+1);
2906 }
2907
2908 } else if (menuin[1] == 'a') {
2909
Benny Prijonofb2b3652007-06-28 07:15:03 +00002910 char id[80], registrar[80], realm[80], uname[80], passwd[30];
2911 pjsua_acc_config acc_cfg;
2912 pj_status_t status;
2913
2914 if (!simple_input("Your SIP URL:", id, sizeof(id)))
2915 break;
2916 if (!simple_input("URL of the registrar:", registrar, sizeof(registrar)))
2917 break;
2918 if (!simple_input("Auth Realm:", realm, sizeof(realm)))
2919 break;
2920 if (!simple_input("Auth Username:", uname, sizeof(uname)))
2921 break;
2922 if (!simple_input("Auth Password:", passwd, sizeof(passwd)))
2923 break;
2924
2925 pjsua_acc_config_default(&acc_cfg);
2926 acc_cfg.id = pj_str(id);
2927 acc_cfg.reg_uri = pj_str(registrar);
2928 acc_cfg.cred_count = 1;
Benny Prijono48ab2b72007-11-08 09:24:30 +00002929 acc_cfg.cred_info[0].scheme = pj_str("Digest");
Benny Prijonofb2b3652007-06-28 07:15:03 +00002930 acc_cfg.cred_info[0].realm = pj_str(realm);
2931 acc_cfg.cred_info[0].username = pj_str(uname);
2932 acc_cfg.cred_info[0].data_type = 0;
2933 acc_cfg.cred_info[0].data = pj_str(passwd);
2934
2935 status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
2936 if (status != PJ_SUCCESS) {
2937 pjsua_perror(THIS_FILE, "Error adding new account", status);
2938 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002939
2940 } else {
2941 printf("Invalid input %s\n", menuin);
2942 }
2943 break;
2944
2945 case '-':
2946 if (menuin[1] == 'b') {
2947 if (!simple_input("Enter buddy ID to delete",buf,sizeof(buf)))
2948 break;
2949
2950 i = my_atoi(buf) - 1;
2951
2952 if (!pjsua_buddy_is_valid(i)) {
2953 printf("Invalid buddy id %d\n", i);
2954 } else {
2955 pjsua_buddy_del(i);
2956 printf("Buddy %d deleted\n", i);
2957 }
2958
2959 } else if (menuin[1] == 'a') {
2960
2961 if (!simple_input("Enter account ID to delete",buf,sizeof(buf)))
2962 break;
2963
2964 i = my_atoi(buf);
2965
2966 if (!pjsua_acc_is_valid(i)) {
2967 printf("Invalid account id %d\n", i);
2968 } else {
2969 pjsua_acc_del(i);
2970 printf("Account %d deleted\n", i);
2971 }
2972
2973 } else {
2974 printf("Invalid input %s\n", menuin);
2975 }
2976 break;
2977
2978 case 'H':
2979 /*
2980 * Hold call.
2981 */
2982 if (current_call != -1) {
2983
2984 pjsua_call_set_hold(current_call, NULL);
2985
2986 } else {
2987 PJ_LOG(3,(THIS_FILE, "No current call"));
2988 }
2989 break;
2990
2991 case 'v':
2992 /*
2993 * Send re-INVITE (to release hold, etc).
2994 */
2995 if (current_call != -1) {
2996
2997 pjsua_call_reinvite(current_call, PJ_TRUE, NULL);
2998
2999 } else {
3000 PJ_LOG(3,(THIS_FILE, "No current call"));
3001 }
3002 break;
3003
Benny Prijonoc08682e2007-10-04 06:17:58 +00003004 case 'U':
3005 /*
3006 * Send UPDATE
3007 */
3008 if (current_call != -1) {
3009
3010 pjsua_call_update(current_call, 0, NULL);
3011
3012 } else {
3013 PJ_LOG(3,(THIS_FILE, "No current call"));
3014 }
3015 break;
3016
3017 case 'C':
3018 if (menuin[1] == 'p') {
3019 manage_codec_prio();
3020 }
3021 break;
3022
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003023 case 'x':
3024 /*
3025 * Transfer call.
3026 */
3027 if (current_call == -1) {
3028
3029 PJ_LOG(3,(THIS_FILE, "No current call"));
3030
3031 } else {
3032 int call = current_call;
Benny Prijonod524e822006-09-22 12:48:18 +00003033 pjsua_msg_data msg_data;
3034 pjsip_generic_string_hdr refer_sub;
3035 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
3036 pj_str_t STR_FALSE = { "false", 5 };
Benny Prijonof7b1c392006-11-11 16:46:34 +00003037 pjsua_call_info ci;
3038
3039 pjsua_call_get_info(current_call, &ci);
3040 printf("Transfering current call [%d] %.*s\n",
3041 current_call,
3042 (int)ci.remote_info.slen, ci.remote_info.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003043
3044 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
3045
3046 /* Check if call is still there. */
3047
3048 if (call != current_call) {
3049 puts("Call has been disconnected");
3050 continue;
3051 }
3052
Benny Prijonod524e822006-09-22 12:48:18 +00003053 pjsua_msg_data_init(&msg_data);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003054 if (app_config.no_refersub) {
3055 /* Add Refer-Sub: false in outgoing REFER request */
3056 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
3057 &STR_FALSE);
3058 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
3059 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003060 if (result.nb_result != NO_NB) {
3061 if (result.nb_result == -1 || result.nb_result == 0)
3062 puts("You can't do that with transfer call!");
3063 else {
3064 pjsua_buddy_info binfo;
3065 pjsua_buddy_get_info(result.nb_result-1, &binfo);
Benny Prijonod524e822006-09-22 12:48:18 +00003066 pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003067 }
3068
3069 } else if (result.uri_result) {
3070 pj_str_t tmp;
3071 tmp = pj_str(result.uri_result);
Benny Prijonod524e822006-09-22 12:48:18 +00003072 pjsua_call_xfer( current_call, &tmp, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003073 }
3074 }
3075 break;
3076
Benny Prijonof7b1c392006-11-11 16:46:34 +00003077 case 'X':
3078 /*
3079 * Transfer call with replaces.
3080 */
3081 if (current_call == -1) {
3082
3083 PJ_LOG(3,(THIS_FILE, "No current call"));
3084
3085 } else {
3086 int call = current_call;
3087 int dst_call;
3088 pjsua_msg_data msg_data;
3089 pjsip_generic_string_hdr refer_sub;
3090 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
3091 pj_str_t STR_FALSE = { "false", 5 };
3092 pjsua_call_id ids[PJSUA_MAX_CALLS];
3093 pjsua_call_info ci;
3094 unsigned i, count;
3095
3096 count = PJ_ARRAY_SIZE(ids);
3097 pjsua_enum_calls(ids, &count);
3098
3099 if (count <= 1) {
3100 puts("There are no other calls");
3101 continue;
3102 }
3103
3104 pjsua_call_get_info(current_call, &ci);
3105 printf("Transfer call [%d] %.*s to one of the following:\n",
3106 current_call,
3107 (int)ci.remote_info.slen, ci.remote_info.ptr);
3108
3109 for (i=0; i<count; ++i) {
3110 pjsua_call_info call_info;
3111
3112 if (ids[i] == call)
3113 continue;
3114
3115 pjsua_call_get_info(ids[i], &call_info);
3116 printf("%d %.*s [%.*s]\n",
3117 ids[i],
3118 (int)call_info.remote_info.slen,
3119 call_info.remote_info.ptr,
3120 (int)call_info.state_text.slen,
3121 call_info.state_text.ptr);
3122 }
3123
3124 if (!simple_input("Enter call number to be replaced",
3125 buf, sizeof(buf)))
3126 continue;
3127
3128 dst_call = my_atoi(buf);
3129
3130 /* Check if call is still there. */
3131
3132 if (call != current_call) {
3133 puts("Call has been disconnected");
3134 continue;
3135 }
3136
3137 /* Check that destination call is valid. */
3138 if (dst_call == call) {
3139 puts("Destination call number must not be the same "
3140 "as the call being transfered");
3141 continue;
3142 }
3143 if (dst_call >= PJSUA_MAX_CALLS) {
3144 puts("Invalid destination call number");
3145 continue;
3146 }
3147 if (!pjsua_call_is_active(dst_call)) {
3148 puts("Invalid destination call number");
3149 continue;
3150 }
3151
3152 pjsua_msg_data_init(&msg_data);
3153 if (app_config.no_refersub) {
3154 /* Add Refer-Sub: false in outgoing REFER request */
3155 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
3156 &STR_FALSE);
3157 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
3158 }
3159
3160 pjsua_call_xfer_replaces(call, dst_call, 0, &msg_data);
3161 }
3162 break;
3163
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003164 case '#':
3165 /*
3166 * Send DTMF strings.
3167 */
3168 if (current_call == -1) {
3169
3170 PJ_LOG(3,(THIS_FILE, "No current call"));
3171
3172 } else if (!pjsua_call_has_media(current_call)) {
3173
3174 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
3175
3176 } else {
3177 pj_str_t digits;
3178 int call = current_call;
3179 pj_status_t status;
3180
3181 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
3182 sizeof(buf)))
3183 {
3184 break;
3185 }
3186
3187 if (call != current_call) {
3188 puts("Call has been disconnected");
3189 continue;
3190 }
3191
3192 digits = pj_str(buf);
3193 status = pjsua_call_dial_dtmf(current_call, &digits);
3194 if (status != PJ_SUCCESS) {
3195 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
3196 } else {
3197 puts("DTMF digits enqueued for transmission");
3198 }
3199 }
3200 break;
3201
Benny Prijonofeb69f42007-10-05 09:12:26 +00003202 case '*':
3203 /* Send DTMF with INFO */
3204 if (current_call == -1) {
3205
3206 PJ_LOG(3,(THIS_FILE, "No current call"));
3207
3208 } else {
3209 const pj_str_t SIP_INFO = pj_str("INFO");
3210 pj_str_t digits;
3211 int call = current_call;
3212 int i;
3213 pj_status_t status;
3214
3215 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
3216 sizeof(buf)))
3217 {
3218 break;
3219 }
3220
3221 if (call != current_call) {
3222 puts("Call has been disconnected");
3223 continue;
3224 }
3225
3226 digits = pj_str(buf);
3227 for (i=0; i<digits.slen; ++i) {
3228 pjsua_msg_data msg_data;
3229 char body[80];
3230
3231 pjsua_msg_data_init(&msg_data);
3232 msg_data.content_type = pj_str("application/dtmf-relay");
3233
3234 pj_ansi_snprintf(body, sizeof(body),
3235 "Signal=%c\r\n"
3236 "Duration=160",
3237 buf[i]);
3238 msg_data.msg_body = pj_str(body);
3239
3240 status = pjsua_call_send_request(current_call, &SIP_INFO,
3241 &msg_data);
3242 if (status != PJ_SUCCESS) {
3243 break;
3244 }
3245 }
3246 }
3247 break;
3248
Benny Prijono56315612006-07-18 14:39:40 +00003249 case 'S':
3250 /*
3251 * Send arbitrary request
3252 */
3253 if (pjsua_acc_get_count() == 0) {
3254 puts("Sorry, need at least one account configured");
3255 break;
3256 }
3257
3258 puts("Send arbitrary request to remote host");
3259
3260 /* Input METHOD */
3261 if (!simple_input("Request method:",text,sizeof(text)))
3262 break;
3263
3264 /* Input destination URI */
3265 uri = NULL;
3266 ui_input_url("Destination URI", buf, sizeof(buf), &result);
3267 if (result.nb_result != NO_NB) {
3268
3269 if (result.nb_result == -1 || result.nb_result == 0) {
3270 puts("Sorry you can't do that!");
3271 continue;
3272 } else {
3273 pjsua_buddy_info binfo;
3274 pjsua_buddy_get_info(result.nb_result-1, &binfo);
3275 uri = binfo.uri.ptr;
3276 }
3277
3278 } else if (result.uri_result) {
3279 uri = result.uri_result;
3280 }
3281
3282 tmp = pj_str(uri);
3283
3284 send_request(text, &tmp);
3285 break;
3286
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003287 case 's':
Benny Prijono990042e2007-01-21 19:36:00 +00003288 if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
3289 pj_str_t tmp;
3290 int delay;
3291
3292 tmp.ptr = menuin+6;
3293 tmp.slen = pj_ansi_strlen(menuin)-7;
3294
3295 if (tmp.slen < 1) {
3296 puts("Usage: sleep MSEC");
3297 break;
3298 }
3299
3300 delay = pj_strtoul(&tmp);
3301 if (delay < 0) delay = 0;
3302 pj_thread_sleep(delay);
3303 break;
3304 }
3305 /* Continue below */
3306
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003307 case 'u':
3308 /*
3309 * Subscribe/unsubscribe presence.
3310 */
3311 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
3312 if (result.nb_result != NO_NB) {
3313 if (result.nb_result == -1) {
3314 int i, count;
3315 count = pjsua_get_buddy_count();
3316 for (i=0; i<count; ++i)
3317 pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
3318 } else if (result.nb_result == 0) {
3319 puts("Sorry, can only subscribe to buddy's presence, "
3320 "not from existing call");
3321 } else {
3322 pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
3323 }
3324
3325 } else if (result.uri_result) {
3326 puts("Sorry, can only subscribe to buddy's presence, "
3327 "not arbitrary URL (for now)");
3328 }
3329
3330 break;
3331
3332 case 'r':
3333 switch (menuin[1]) {
3334 case 'r':
3335 /*
3336 * Re-Register.
3337 */
3338 pjsua_acc_set_registration(current_acc, PJ_TRUE);
3339 break;
3340 case 'u':
3341 /*
3342 * Unregister
3343 */
3344 pjsua_acc_set_registration(current_acc, PJ_FALSE);
3345 break;
3346 }
3347 break;
3348
3349 case 't':
3350 pjsua_acc_get_info(current_acc, &acc_info);
3351 acc_info.online_status = !acc_info.online_status;
3352 pjsua_acc_set_online_status(current_acc, acc_info.online_status);
3353 printf("Setting %s online status to %s\n",
3354 acc_info.acc_uri.ptr,
3355 (acc_info.online_status?"online":"offline"));
3356 break;
3357
Benny Prijono4461c7d2007-08-25 13:36:15 +00003358 case 'T':
3359 change_online_status();
3360 break;
3361
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003362 case 'c':
3363 switch (menuin[1]) {
3364 case 'l':
3365 conf_list();
3366 break;
3367 case 'c':
3368 case 'd':
3369 {
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003370 char tmp[10], src_port[10], dst_port[10];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003371 pj_status_t status;
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003372 int cnt;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003373 const char *src_title, *dst_title;
3374
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003375 cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003376
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003377 if (cnt != 3) {
3378 conf_list();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003379
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003380 src_title = (menuin[1]=='c'?
3381 "Connect src port #":
3382 "Disconnect src port #");
3383 dst_title = (menuin[1]=='c'?
3384 "To dst port #":
3385 "From dst port #");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003386
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003387 if (!simple_input(src_title, src_port, sizeof(src_port)))
3388 break;
3389
3390 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
3391 break;
3392 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003393
3394 if (menuin[1]=='c') {
3395 status = pjsua_conf_connect(my_atoi(src_port),
3396 my_atoi(dst_port));
3397 } else {
3398 status = pjsua_conf_disconnect(my_atoi(src_port),
3399 my_atoi(dst_port));
3400 }
3401 if (status == PJ_SUCCESS) {
3402 puts("Success");
3403 } else {
3404 puts("ERROR!!");
3405 }
3406 }
3407 break;
3408 }
3409 break;
3410
Benny Prijono6dd967c2006-12-26 02:27:14 +00003411 case 'V':
3412 /* Adjust audio volume */
3413 sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level);
3414 if (simple_input(buf,text,sizeof(text))) {
3415 char *err;
3416 app_config.mic_level = (float)strtod(text, &err);
3417 pjsua_conf_adjust_rx_level(0, app_config.mic_level);
3418 }
3419 sprintf(buf, "Adjust speaker level: [%4.1fx] ",
3420 app_config.speaker_level);
3421 if (simple_input(buf,text,sizeof(text))) {
3422 char *err;
3423 app_config.speaker_level = (float)strtod(text, &err);
3424 pjsua_conf_adjust_tx_level(0, app_config.speaker_level);
3425 }
3426
3427 break;
3428
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003429 case 'd':
3430 if (menuin[1] == 'c') {
3431 char settings[2000];
3432 int len;
3433
3434 len = write_settings(&app_config, settings, sizeof(settings));
3435 if (len < 1)
3436 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
3437 else
3438 PJ_LOG(3,(THIS_FILE,
3439 "Dumping configuration (%d bytes):\n%s\n",
3440 len, settings));
Benny Prijono819506c2006-06-22 22:29:51 +00003441
3442 } else if (menuin[1] == 'q') {
3443
3444 if (current_call != PJSUA_INVALID_ID) {
Benny Prijono6eddd872008-03-21 13:46:08 +00003445 pjsua_call_dump(current_call, PJ_TRUE, some_buf,
3446 sizeof(some_buf), " ");
3447 PJ_LOG(3,(THIS_FILE, "\n%s", some_buf));
Benny Prijono819506c2006-06-22 22:29:51 +00003448 } else {
3449 PJ_LOG(3,(THIS_FILE, "No current call"));
3450 }
3451
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003452 } else {
3453 app_dump(menuin[1]=='d');
3454 }
3455 break;
3456
3457
3458 case 'f':
3459 if (simple_input("Enter output filename", buf, sizeof(buf))) {
3460 char settings[2000];
3461 int len;
3462
3463 len = write_settings(&app_config, settings, sizeof(settings));
3464 if (len < 1)
3465 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
3466 else {
3467 pj_oshandle_t fd;
3468 pj_status_t status;
3469
3470 status = pj_file_open(app_config.pool, buf,
3471 PJ_O_WRONLY, &fd);
3472 if (status != PJ_SUCCESS) {
3473 pjsua_perror(THIS_FILE, "Unable to open file", status);
3474 } else {
3475 pj_ssize_t size = len;
3476 pj_file_write(fd, settings, &size);
3477 pj_file_close(fd);
3478
3479 printf("Settings successfully written to '%s'\n", buf);
3480 }
3481 }
3482
3483 }
3484 break;
3485
3486
3487 case 'q':
3488 goto on_exit;
3489
3490
3491 default:
3492 if (menuin[0] != '\n' && menuin[0] != '\r') {
3493 printf("Invalid input %s", menuin);
3494 }
3495 keystroke_help();
3496 break;
3497 }
3498 }
3499
3500on_exit:
3501 ;
3502}
3503
3504
3505/*****************************************************************************
3506 * Public API
3507 */
3508
3509pj_status_t app_init(int argc, char *argv[])
3510{
Benny Prijonoe93e2872006-06-28 16:46:49 +00003511 pjsua_transport_id transport_id = -1;
Benny Prijonoe347cb02007-02-14 14:36:13 +00003512 pjsua_transport_config tcp_cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003513 unsigned i;
3514 pj_status_t status;
3515
3516 /* Create pjsua */
3517 status = pjsua_create();
3518 if (status != PJ_SUCCESS)
3519 return status;
3520
3521 /* Create pool for application */
Benny Prijonod5696da2007-07-17 16:25:45 +00003522 app_config.pool = pjsua_pool_create("pjsua", 1000, 1000);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003523
3524 /* Initialize default config */
3525 default_config(&app_config);
3526
3527 /* Parse the arguments */
3528 status = parse_args(argc, argv, &app_config, &uri_arg);
3529 if (status != PJ_SUCCESS)
3530 return status;
3531
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003532 /* Initialize application callbacks */
3533 app_config.cfg.cb.on_call_state = &on_call_state;
3534 app_config.cfg.cb.on_call_media_state = &on_call_media_state;
3535 app_config.cfg.cb.on_incoming_call = &on_incoming_call;
Benny Prijonofeb69f42007-10-05 09:12:26 +00003536 app_config.cfg.cb.on_call_tsx_state = &on_call_tsx_state;
Benny Prijono0875ae82006-12-26 00:11:48 +00003537 app_config.cfg.cb.on_dtmf_digit = &call_on_dtmf_callback;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003538 app_config.cfg.cb.on_reg_state = &on_reg_state;
3539 app_config.cfg.cb.on_buddy_state = &on_buddy_state;
3540 app_config.cfg.cb.on_pager = &on_pager;
3541 app_config.cfg.cb.on_typing = &on_typing;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003542 app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
Benny Prijonof7b1c392006-11-11 16:46:34 +00003543 app_config.cfg.cb.on_call_replaced = &on_call_replaced;
Benny Prijono6ba8c542007-10-16 01:34:14 +00003544 app_config.cfg.cb.on_nat_detect = &on_nat_detect;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003545
3546 /* Initialize pjsua */
3547 status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
3548 &app_config.media_cfg);
3549 if (status != PJ_SUCCESS)
3550 return status;
3551
Benny Prijonoe909eac2006-07-27 22:04:56 +00003552#ifdef STEREO_DEMO
3553 stereo_demo();
3554#endif
3555
Benny Prijono804ff0a2006-09-14 11:17:48 +00003556 /* Initialize calls data */
3557 for (i=0; i<PJ_ARRAY_SIZE(app_config.call_data); ++i) {
3558 app_config.call_data[i].timer.id = PJSUA_INVALID_ID;
3559 app_config.call_data[i].timer.cb = &call_timeout_callback;
3560 }
3561
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003562 /* Optionally registers WAV file */
Benny Prijono32e4f492007-01-24 00:44:26 +00003563 for (i=0; i<app_config.wav_count; ++i) {
3564 pjsua_player_id wav_id;
3565
3566 status = pjsua_player_create(&app_config.wav_files[i], 0,
3567 &wav_id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003568 if (status != PJ_SUCCESS)
3569 goto on_error;
Benny Prijono22a300a2006-06-14 20:04:55 +00003570
Benny Prijono72c04d22007-02-17 22:20:58 +00003571 if (app_config.wav_id == PJSUA_INVALID_ID) {
Benny Prijono32e4f492007-01-24 00:44:26 +00003572 app_config.wav_id = wav_id;
3573 app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
3574 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003575 }
3576
Benny Prijono4af234b2007-01-24 02:02:09 +00003577 /* Optionally registers tone players */
3578 for (i=0; i<app_config.tone_count; ++i) {
3579 pjmedia_port *tport;
3580 char name[80];
3581 pj_str_t label;
3582 pj_status_t status;
3583
3584 pj_ansi_snprintf(name, sizeof(name), "tone-%d,%d",
3585 app_config.tones[i].freq1,
3586 app_config.tones[i].freq2);
3587 label = pj_str(name);
3588 status = pjmedia_tonegen_create2(app_config.pool, &label,
3589 8000, 1, 160, 16,
3590 PJMEDIA_TONEGEN_LOOP, &tport);
3591 if (status != PJ_SUCCESS) {
3592 pjsua_perror(THIS_FILE, "Unable to create tone generator", status);
3593 goto on_error;
3594 }
3595
3596 status = pjsua_conf_add_port(app_config.pool, tport,
3597 &app_config.tone_slots[i]);
3598 pj_assert(status == PJ_SUCCESS);
3599
3600 status = pjmedia_tonegen_play(tport, 1, &app_config.tones[i], 0);
3601 pj_assert(status == PJ_SUCCESS);
3602 }
3603
Benny Prijono1ebd6142006-10-19 15:48:02 +00003604 /* Optionally create recorder file, if any. */
3605 if (app_config.rec_file.slen) {
3606 status = pjsua_recorder_create(&app_config.rec_file, 0, NULL, 0, 0,
3607 &app_config.rec_id);
3608 if (status != PJ_SUCCESS)
3609 goto on_error;
3610
3611 app_config.rec_port = pjsua_recorder_get_conf_port(app_config.rec_id);
3612 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003613
Benny Prijonoe347cb02007-02-14 14:36:13 +00003614 pj_memcpy(&tcp_cfg, &app_config.udp_cfg, sizeof(tcp_cfg));
3615
Benny Prijono87ef89a2007-01-14 00:39:45 +00003616 /* Add UDP transport unless it's disabled. */
3617 if (!app_config.no_udp) {
3618 pjsua_acc_id aid;
3619
3620 status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
3621 &app_config.udp_cfg,
3622 &transport_id);
3623 if (status != PJ_SUCCESS)
3624 goto on_error;
3625
3626 /* Add local account */
3627 pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
3628 //pjsua_acc_set_transport(aid, transport_id);
3629 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
Benny Prijonoe347cb02007-02-14 14:36:13 +00003630
3631 if (app_config.udp_cfg.port == 0) {
3632 pjsua_transport_info ti;
3633 pj_sockaddr_in *a;
3634
3635 pjsua_transport_get_info(transport_id, &ti);
3636 a = (pj_sockaddr_in*)&ti.local_addr;
3637
3638 tcp_cfg.port = pj_ntohs(a->sin_port);
3639 }
Benny Prijono87ef89a2007-01-14 00:39:45 +00003640 }
3641
Benny Prijonoe93e2872006-06-28 16:46:49 +00003642 /* Add TCP transport unless it's disabled */
3643 if (!app_config.no_tcp) {
3644 status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
Benny Prijonoe347cb02007-02-14 14:36:13 +00003645 &tcp_cfg,
Benny Prijonoe93e2872006-06-28 16:46:49 +00003646 &transport_id);
3647 if (status != PJ_SUCCESS)
3648 goto on_error;
3649
3650 /* Add local account */
Benny Prijono21b9ad92006-08-15 13:11:22 +00003651 pjsua_acc_add_local(transport_id, PJ_TRUE, NULL);
Benny Prijonoe93e2872006-06-28 16:46:49 +00003652 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
3653
3654 }
3655
Benny Prijonoe93e2872006-06-28 16:46:49 +00003656
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003657#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
3658 /* Add TLS transport when application wants one */
3659 if (app_config.use_tls) {
Benny Prijonof3bbc132006-12-25 06:43:59 +00003660
3661 pjsua_acc_id acc_id;
3662
3663 /* Set TLS port as TCP port+1 */
Benny Prijonoafc47be2007-02-14 14:44:55 +00003664 tcp_cfg.port++;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003665 status = pjsua_transport_create(PJSIP_TRANSPORT_TLS,
Benny Prijonoafc47be2007-02-14 14:44:55 +00003666 &tcp_cfg,
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003667 &transport_id);
Benny Prijonoafc47be2007-02-14 14:44:55 +00003668 tcp_cfg.port--;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003669 if (status != PJ_SUCCESS)
3670 goto on_error;
Benny Prijonof3bbc132006-12-25 06:43:59 +00003671
3672 /* Add local account */
3673 pjsua_acc_add_local(transport_id, PJ_FALSE, &acc_id);
3674 pjsua_acc_set_online_status(acc_id, PJ_TRUE);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003675 }
3676#endif
3677
Benny Prijonoe93e2872006-06-28 16:46:49 +00003678 if (transport_id == -1) {
3679 PJ_LOG(3,(THIS_FILE, "Error: no transport is configured"));
3680 status = -1;
3681 goto on_error;
3682 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003683
3684
3685 /* Add accounts */
3686 for (i=0; i<app_config.acc_cnt; ++i) {
Benny Prijono21b9ad92006-08-15 13:11:22 +00003687 status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003688 if (status != PJ_SUCCESS)
3689 goto on_error;
3690 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
3691 }
3692
3693 /* Add buddies */
3694 for (i=0; i<app_config.buddy_cnt; ++i) {
3695 status = pjsua_buddy_add(&app_config.buddy_cfg[i], NULL);
3696 if (status != PJ_SUCCESS)
3697 goto on_error;
3698 }
3699
3700 /* Optionally set codec orders */
3701 for (i=0; i<app_config.codec_cnt; ++i) {
3702 pjsua_codec_set_priority(&app_config.codec_arg[i],
3703 (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
3704 }
3705
Benny Prijonofce28542007-12-09 15:41:10 +00003706 /* Optionally disable some codec */
3707 for (i=0; i<app_config.codec_dis_cnt; ++i) {
3708 pjsua_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED);
3709 }
3710
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003711 /* Add RTP transports */
3712 status = pjsua_media_transports_create(&app_config.rtp_cfg);
3713 if (status != PJ_SUCCESS)
3714 goto on_error;
3715
Benny Prijono22a300a2006-06-14 20:04:55 +00003716 /* Use null sound device? */
Benny Prijonoe909eac2006-07-27 22:04:56 +00003717#ifndef STEREO_DEMO
Benny Prijono22a300a2006-06-14 20:04:55 +00003718 if (app_config.null_audio) {
3719 status = pjsua_set_null_snd_dev();
3720 if (status != PJ_SUCCESS)
3721 return status;
3722 }
Benny Prijonoe909eac2006-07-27 22:04:56 +00003723#endif
Benny Prijono22a300a2006-06-14 20:04:55 +00003724
Benny Prijono4e5d5512007-03-06 18:11:30 +00003725 if (app_config.capture_dev != PJSUA_INVALID_ID
3726 || app_config.playback_dev != PJSUA_INVALID_ID) {
3727 status
3728 = pjsua_set_snd_dev(app_config.capture_dev, app_config.playback_dev);
3729 if (status != PJ_SUCCESS)
3730 goto on_error;
3731 }
3732
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003733 return PJ_SUCCESS;
3734
3735on_error:
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00003736 app_destroy();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003737 return status;
3738}
3739
3740
3741pj_status_t app_main(void)
3742{
3743 pj_status_t status;
3744
3745 /* Start pjsua */
3746 status = pjsua_start();
3747 if (status != PJ_SUCCESS) {
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00003748 app_destroy();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003749 return status;
3750 }
3751
3752 console_app_main(&uri_arg);
3753
3754 return PJ_SUCCESS;
3755}
3756
3757pj_status_t app_destroy(void)
3758{
Benny Prijonof762ee72006-12-01 11:14:37 +00003759 pj_status_t status;
Benny Prijono4af234b2007-01-24 02:02:09 +00003760 unsigned i;
Benny Prijonof762ee72006-12-01 11:14:37 +00003761
Benny Prijonoe909eac2006-07-27 22:04:56 +00003762#ifdef STEREO_DEMO
3763 if (app_config.snd) {
3764 pjmedia_snd_port_destroy(app_config.snd);
3765 app_config.snd = NULL;
3766 }
3767#endif
3768
Benny Prijono4af234b2007-01-24 02:02:09 +00003769 /* Close tone generators */
3770 for (i=0; i<app_config.tone_count; ++i) {
3771 pjsua_conf_remove_port(app_config.tone_slots[i]);
3772 }
3773
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003774 if (app_config.pool) {
3775 pj_pool_release(app_config.pool);
3776 app_config.pool = NULL;
3777 }
3778
Benny Prijonof762ee72006-12-01 11:14:37 +00003779 status = pjsua_destroy();
3780
3781 pj_bzero(&app_config, sizeof(app_config));
3782
3783 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003784}
Benny Prijonoe909eac2006-07-27 22:04:56 +00003785
3786
3787#ifdef STEREO_DEMO
3788static void stereo_demo()
3789{
3790 pjmedia_port *conf, *splitter, *ch1;
Benny Prijonoe909eac2006-07-27 22:04:56 +00003791 pj_status_t status;
3792
3793 /* Disable existing sound device */
3794 conf = pjsua_set_no_snd_dev();
3795
Benny Prijonoe909eac2006-07-27 22:04:56 +00003796 /* Create stereo-mono splitter/combiner */
3797 status = pjmedia_splitcomb_create(app_config.pool,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003798 conf->info.clock_rate /* clock rate */,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003799 2 /* stereo */,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003800 2 * conf->info.samples_per_frame,
3801 conf->info.bits_per_sample,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003802 0 /* options */,
3803 &splitter);
3804 pj_assert(status == PJ_SUCCESS);
3805
3806 /* Connect channel0 (left channel?) to conference port slot0 */
3807 status = pjmedia_splitcomb_set_channel(splitter, 0 /* ch0 */,
3808 0 /*options*/,
3809 conf);
3810 pj_assert(status == PJ_SUCCESS);
3811
3812 /* Create reverse channel for channel1 (right channel?)... */
3813 status = pjmedia_splitcomb_create_rev_channel(app_config.pool,
3814 splitter,
3815 1 /* ch1 */,
3816 0 /* options */,
3817 &ch1);
3818 pj_assert(status == PJ_SUCCESS);
3819
3820 /* .. and register it to conference bridge (it would be slot1
3821 * if there's no other devices connected to the bridge)
3822 */
3823 status = pjsua_conf_add_port(app_config.pool, ch1, NULL);
3824 pj_assert(status == PJ_SUCCESS);
3825
3826 /* Create sound device */
3827 status = pjmedia_snd_port_create(app_config.pool, -1, -1,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003828 conf->info.clock_rate,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003829 2 /* stereo */,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003830 2 * conf->info.samples_per_frame,
3831 conf->info.bits_per_sample,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003832 0, &app_config.snd);
3833 pj_assert(status == PJ_SUCCESS);
3834
3835
3836 /* Connect the splitter to the sound device */
3837 status = pjmedia_snd_port_connect(app_config.snd, splitter);
3838 pj_assert(status == PJ_SUCCESS);
3839
3840}
3841#endif
3842