blob: 30725b5156bd495c11f0e02c20433e7ff825fb86 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <pjsua-lib/pjsua.h>
22#include "pjsua_app_common.h"
23
24#define THIS_FILE "pjsua_app_legacy.c"
25
26static pj_bool_t cmd_echo;
27
28/*
29 * Print buddy list.
30 */
31static void print_buddy_list()
32{
33 pjsua_buddy_id ids[64];
34 int i;
35 unsigned count = PJ_ARRAY_SIZE(ids);
36
37 puts("Buddy list:");
38
39 pjsua_enum_buddies(ids, &count);
40
41 if (count == 0)
42 puts(" -none-");
43 else {
44 for (i=0; i<(int)count; ++i) {
45 pjsua_buddy_info info;
46
47 if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
48 continue;
49
50 printf(" [%2d] <%.*s> %.*s\n",
51 ids[i]+1,
52 (int)info.status_text.slen,
53 info.status_text.ptr,
54 (int)info.uri.slen,
55 info.uri.ptr);
56 }
57 }
58 puts("");
59}
60
61/*
62 * Input URL.
63 */
64static void ui_input_url(const char *title, char *buf, pj_size_t len,
65 input_result *result)
66{
67 result->nb_result = PJSUA_APP_NO_NB;
68 result->uri_result = NULL;
69
70 print_buddy_list();
71
72 printf("Choices:\n"
73 " 0 For current dialog.\n"
74 " -1 All %d buddies in buddy list\n"
75 " [1 -%2d] Select from buddy list\n"
76 " URL An URL\n"
77 " <Enter> Empty input (or 'q') to cancel\n"
78 , pjsua_get_buddy_count(), pjsua_get_buddy_count());
79 printf("%s: ", title);
80
81 fflush(stdout);
82 if (fgets(buf, (int)len, stdin) == NULL)
83 return;
84 len = strlen(buf);
85
86 /* Left trim */
87 while (pj_isspace(*buf)) {
88 ++buf;
89 --len;
90 }
91
92 /* Remove trailing newlines */
93 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
94 buf[--len] = '\0';
95
96 if (len == 0 || buf[0]=='q')
97 return;
98
99 if (pj_isdigit(*buf) || *buf=='-') {
100
101 unsigned i;
102
103 if (*buf=='-')
104 i = 1;
105 else
106 i = 0;
107
108 for (; i<len; ++i) {
109 if (!pj_isdigit(buf[i])) {
110 puts("Invalid input");
111 return;
112 }
113 }
114
115 result->nb_result = my_atoi(buf);
116
117 if (result->nb_result >= 0 &&
118 result->nb_result <= (int)pjsua_get_buddy_count())
119 {
120 return;
121 }
122 if (result->nb_result == -1)
123 return;
124
125 puts("Invalid input");
126 result->nb_result = PJSUA_APP_NO_NB;
127 return;
128
129 } else {
130 pj_status_t status;
131
132 if ((status=pjsua_verify_url(buf)) != PJ_SUCCESS) {
133 pjsua_perror(THIS_FILE, "Invalid URL", status);
134 return;
135 }
136
137 result->uri_result = buf;
138 }
139}
140
141static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
142{
143 char *p;
144
145 printf("%s (empty to cancel): ", title); fflush(stdout);
146 if (fgets(buf, (int)len, stdin) == NULL)
147 return PJ_FALSE;
148
149 /* Remove trailing newlines. */
150 for (p=buf; ; ++p) {
151 if (*p=='\r' || *p=='\n') *p='\0';
152 else if (!*p) break;
153 }
154
155 if (!*buf)
156 return PJ_FALSE;
157
158 return PJ_TRUE;
159}
160
161/*
162 * Print account status.
163 */
164static void print_acc_status(int acc_id)
165{
166 char buf[80];
167 pjsua_acc_info info;
168
169 pjsua_acc_get_info(acc_id, &info);
170
171 if (!info.has_registration) {
172 pj_ansi_snprintf(buf, sizeof(buf), "%.*s",
173 (int)info.status_text.slen,
174 info.status_text.ptr);
175
176 } else {
177 pj_ansi_snprintf(buf, sizeof(buf),
178 "%d/%.*s (expires=%d)",
179 info.status,
180 (int)info.status_text.slen,
181 info.status_text.ptr,
182 info.expires);
183
184 }
185
186 printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
187 acc_id, (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
188 printf(" Online status: %.*s\n",
189 (int)info.online_status_text.slen,
190 info.online_status_text.ptr);
191}
192
193/*
194 * Show a bit of help.
195 */
196static void keystroke_help()
197{
198 pjsua_acc_id acc_ids[16];
199 unsigned count = PJ_ARRAY_SIZE(acc_ids);
200 int i;
201
202 printf(">>>>\n");
203
204 pjsua_enum_accs(acc_ids, &count);
205
206 printf("Account list:\n");
207 for (i=0; i<(int)count; ++i)
208 print_acc_status(acc_ids[i]);
209
210 print_buddy_list();
211
212 //puts("Commands:");
213 puts("+=============================================================================+");
214 puts("| Call Commands: | Buddy, IM & Presence: | Account: |");
215 puts("| | | |");
216 puts("| m Make new call | +b Add new buddy .| +a Add new accnt |");
217 puts("| M Make multiple calls | -b Delete buddy | -a Delete accnt. |");
218 puts("| a Answer call | i Send IM | !a Modify accnt. |");
219 puts("| h Hangup call (ha=all) | s Subscribe presence | rr (Re-)register |");
220 puts("| H Hold call | u Unsubscribe presence | ru Unregister |");
221 puts("| v re-inVite (release hold) | t ToGgle Online status | > Cycle next ac.|");
222 puts("| U send UPDATE | T Set online status | < Cycle prev ac.|");
223 puts("| ],[ Select next/prev call +--------------------------+-------------------+");
224 puts("| x Xfer call | Media Commands: | Status & Config: |");
225 puts("| X Xfer with Replaces | | |");
226 puts("| # Send RFC 2833 DTMF | cl List ports | d Dump status |");
227 puts("| * Send DTMF with INFO | cc Connect port | dd Dump detailed |");
228 puts("| dq Dump curr. call quality | cd Disconnect port | dc Dump config |");
229 puts("| | V Adjust audio Volume | f Save config |");
230 puts("| S Send arbitrary REQUEST | Cp Codec priorities | |");
231 puts("+-----------------------------------------------------------------------------+");
232#if PJSUA_HAS_VIDEO
233 puts("| Video: \"vid help\" for more info |");
234 puts("+-----------------------------------------------------------------------------+");
235#endif
236 puts("| q QUIT L ReLoad sleep MS echo [0|1|txt] n: detect NAT type |");
237 puts("+=============================================================================+");
238
239 i = pjsua_call_get_count();
240 printf("You have %d active call%s\n", i, (i>1?"s":""));
241
242 if (current_call != PJSUA_INVALID_ID) {
243 pjsua_call_info ci;
244 if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
245 printf("Current call id=%d to %.*s [%.*s]\n", current_call,
246 (int)ci.remote_info.slen, ci.remote_info.ptr,
247 (int)ci.state_text.slen, ci.state_text.ptr);
248 }
249}
250
251/* Help screen for video */
252#if PJSUA_HAS_VIDEO
253static void vid_show_help()
254{
255 pj_bool_t vid_enabled = (app_config.vid.vid_cnt > 0);
256
257 puts("+=============================================================================+");
258 puts("| Video commands: |");
259 puts("| |");
260 puts("| vid help Show this help screen |");
261 puts("| vid enable|disable Enable or disable video in next offer/answer |");
262 puts("| vid acc show Show current account video settings |");
263 puts("| vid acc autorx on|off Automatically show incoming video on/off |");
264 puts("| vid acc autotx on|off Automatically offer video on/off |");
265 puts("| vid acc cap ID Set default capture device for current acc |");
266 puts("| vid acc rend ID Set default renderer device for current acc |");
267 puts("| vid call rx on|off N Enable/disable video RX for stream N in curr call |");
268 puts("| vid call tx on|off N Enable/disable video TX for stream N in curr call |");
269 puts("| vid call add Add video stream for current call |");
270 puts("| vid call enable|disable N Enable/disable stream #N in current call |");
271 puts("| vid call cap N ID Set capture dev ID for stream #N in current call |");
272 puts("| vid dev list List all video devices |");
273 puts("| vid dev refresh Refresh video device list |");
274 puts("| vid dev prev on|off ID Enable/disable preview for specified device ID |");
275 puts("| vid codec list List video codecs |");
276 puts("| vid codec prio ID PRIO Set codec ID priority to PRIO |");
277 puts("| vid codec fps ID NUM DEN Set codec ID framerate to (NUM/DEN) fps |");
278 puts("| vid codec bw ID AVG MAX Set codec ID bitrate to AVG & MAX kbps |");
279 puts("| vid codec size ID W H Set codec ID size/resolution to W x H |");
280 puts("| vid win list List all active video windows |");
281 puts("| vid win arrange Auto arrange windows |");
282 puts("| vid win show|hide ID Show/hide the specified video window ID |");
283 puts("| vid win move ID X Y Move window ID to position X,Y |");
284 puts("| vid win resize ID w h Resize window ID to the specified width, height |");
285 puts("+=============================================================================+");
286 printf("| Video will be %s in the next offer/answer %s |\n",
287 (vid_enabled? "enabled" : "disabled"), (vid_enabled? " " : ""));
288 puts("+=============================================================================+");
289}
290
291static void vid_handle_menu(char *menuin)
292{
293 char *argv[8];
294 int argc = 0;
295
296 /* Tokenize */
297 argv[argc] = strtok(menuin, " \t\r\n");
298 while (argv[argc] && *argv[argc]) {
299 argc++;
300 argv[argc] = strtok(NULL, " \t\r\n");
301 }
302
303 if (argc == 1 || strcmp(argv[1], "help")==0) {
304 vid_show_help();
305 } else if (argc == 2 && (strcmp(argv[1], "enable")==0 ||
306 strcmp(argv[1], "disable")==0))
307 {
308 pj_bool_t enabled = (strcmp(argv[1], "enable")==0);
309 app_config.vid.vid_cnt = (enabled ? 1 : 0);
310 PJ_LOG(3,(THIS_FILE, "Video will be %s in next offer/answer",
311 (enabled?"enabled":"disabled")));
312 } else if (strcmp(argv[1], "acc")==0) {
313 pjsua_acc_config acc_cfg;
314 pj_bool_t changed = PJ_FALSE;
315 pj_pool_t *tmp_pool = pjsua_pool_create("tmp-pjsua", 1000, 1000);
316
317 pjsua_acc_get_config(current_acc, tmp_pool, &acc_cfg);
318
319 if (argc == 3 && strcmp(argv[2], "show")==0) {
320 app_config_show_video(current_acc, &acc_cfg);
321 } else if (argc == 4 && strcmp(argv[2], "autorx")==0) {
322 int on = (strcmp(argv[3], "on")==0);
323 acc_cfg.vid_in_auto_show = on;
324 changed = PJ_TRUE;
325 } else if (argc == 4 && strcmp(argv[2], "autotx")==0) {
326 int on = (strcmp(argv[3], "on")==0);
327 acc_cfg.vid_out_auto_transmit = on;
328 changed = PJ_TRUE;
329 } else if (argc == 4 && strcmp(argv[2], "cap")==0) {
330 int dev = atoi(argv[3]);
331 acc_cfg.vid_cap_dev = dev;
332 changed = PJ_TRUE;
333 } else if (argc == 4 && strcmp(argv[2], "rend")==0) {
334 int dev = atoi(argv[3]);
335 acc_cfg.vid_rend_dev = dev;
336 changed = PJ_TRUE;
337 } else {
338 pj_pool_release(tmp_pool);
339 goto on_error;
340 }
341
342 if (changed) {
343 pj_status_t status = pjsua_acc_modify(current_acc, &acc_cfg);
344 if (status != PJ_SUCCESS)
345 PJ_PERROR(1,(THIS_FILE, status, "Error modifying account %d",
346 current_acc));
347 }
348 pj_pool_release(tmp_pool);
349
350 } else if (strcmp(argv[1], "call")==0) {
351 pjsua_call_vid_strm_op_param param;
352 pj_status_t status = PJ_SUCCESS;
353
354 pjsua_call_vid_strm_op_param_default(&param);
355
356 if (argc == 5 && strcmp(argv[2], "rx")==0) {
357 pjsua_stream_info si;
358 pj_bool_t on = (strcmp(argv[3], "on") == 0);
359
360 param.med_idx = atoi(argv[4]);
361 if (pjsua_call_get_stream_info(current_call, param.med_idx, &si) ||
362 si.type != PJMEDIA_TYPE_VIDEO)
363 {
364 PJ_PERROR(1,(THIS_FILE, PJ_EINVAL, "Invalid stream"));
365 return;
366 }
367
368 if (on) param.dir = (si.info.vid.dir | PJMEDIA_DIR_DECODING);
369 else param.dir = (si.info.vid.dir & PJMEDIA_DIR_ENCODING);
370
371 status = pjsua_call_set_vid_strm(current_call,
372 PJSUA_CALL_VID_STRM_CHANGE_DIR,
373 &param);
374 }
375 else if (argc == 5 && strcmp(argv[2], "tx")==0) {
376 pj_bool_t on = (strcmp(argv[3], "on") == 0);
377 pjsua_call_vid_strm_op op = on? PJSUA_CALL_VID_STRM_START_TRANSMIT :
378 PJSUA_CALL_VID_STRM_STOP_TRANSMIT;
379
380 param.med_idx = atoi(argv[4]);
381
382 status = pjsua_call_set_vid_strm(current_call, op, &param);
383 }
384 else if (argc == 3 && strcmp(argv[2], "add")==0) {
385 status = pjsua_call_set_vid_strm(current_call,
386 PJSUA_CALL_VID_STRM_ADD, NULL);
387 }
388 else if (argc >= 3 &&
389 (strcmp(argv[2], "disable")==0 || strcmp(argv[2], "enable")==0))
390 {
391 pj_bool_t enable = (strcmp(argv[2], "enable") == 0);
392 pjsua_call_vid_strm_op op = enable? PJSUA_CALL_VID_STRM_CHANGE_DIR :
393 PJSUA_CALL_VID_STRM_REMOVE;
394
395 param.med_idx = argc >= 4? atoi(argv[3]) : -1;
396 param.dir = PJMEDIA_DIR_ENCODING_DECODING;
397 status = pjsua_call_set_vid_strm(current_call, op, &param);
398 }
399 else if (argc >= 3 && strcmp(argv[2], "cap")==0) {
400 param.med_idx = argc >= 4? atoi(argv[3]) : -1;
401 param.cap_dev = argc >= 5? atoi(argv[4]) : PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
402 status = pjsua_call_set_vid_strm(current_call,
403 PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV,
404 &param);
405 } else
406 goto on_error;
407
408 if (status != PJ_SUCCESS) {
409 PJ_PERROR(1,(THIS_FILE, status, "Error modifying video stream"));
410 }
411
412 } else if (argc >= 3 && strcmp(argv[1], "dev")==0) {
413 if (strcmp(argv[2], "list")==0) {
414 vid_list_devs();
415 } else if (strcmp(argv[2], "refresh")==0) {
416 pjmedia_vid_dev_refresh();
417 } else if (strcmp(argv[2], "prev")==0) {
418 if (argc != 5) {
419 goto on_error;
420 } else {
421 pj_bool_t on = (strcmp(argv[3], "on") == 0);
422 int dev_id = atoi(argv[4]);
423 if (on) {
424 pjsua_vid_preview_param param;
425
426 pjsua_vid_preview_param_default(&param);
427 param.wnd_flags = PJMEDIA_VID_DEV_WND_BORDER |
428 PJMEDIA_VID_DEV_WND_RESIZABLE;
429 pjsua_vid_preview_start(dev_id, &param);
430 arrange_window(pjsua_vid_preview_get_win(dev_id));
431 } else {
432 pjsua_vid_win_id wid;
433 wid = pjsua_vid_preview_get_win(dev_id);
434 if (wid != PJSUA_INVALID_ID) {
435 /* Preview window hiding once it is stopped is
436 * responsibility of app */
437 pjsua_vid_win_set_show(wid, PJ_FALSE);
438 pjsua_vid_preview_stop(dev_id);
439 }
440 }
441 }
442 } else
443 goto on_error;
444 } else if (strcmp(argv[1], "win")==0) {
445 pj_status_t status = PJ_SUCCESS;
446
447 if (argc==3 && strcmp(argv[2], "list")==0) {
448 pjsua_vid_win_id wids[PJSUA_MAX_VID_WINS];
449 unsigned i, cnt = PJ_ARRAY_SIZE(wids);
450
451 pjsua_vid_enum_wins(wids, &cnt);
452
453 PJ_LOG(3,(THIS_FILE, "Found %d video windows:", cnt));
454 PJ_LOG(3,(THIS_FILE, "WID show pos size"));
455 PJ_LOG(3,(THIS_FILE, "------------------------------"));
456 for (i = 0; i < cnt; ++i) {
457 pjsua_vid_win_info wi;
458 pjsua_vid_win_get_info(wids[i], &wi);
459 PJ_LOG(3,(THIS_FILE, "%3d %c (%d,%d) %dx%d",
460 wids[i], (wi.show?'Y':'N'), wi.pos.x, wi.pos.y,
461 wi.size.w, wi.size.h));
462 }
463 } else if (argc==4 && (strcmp(argv[2], "show")==0 ||
464 strcmp(argv[2], "hide")==0))
465 {
466 pj_bool_t show = (strcmp(argv[2], "show")==0);
467 pjsua_vid_win_id wid = atoi(argv[3]);
468 status = pjsua_vid_win_set_show(wid, show);
469 } else if (argc==6 && strcmp(argv[2], "move")==0) {
470 pjsua_vid_win_id wid = atoi(argv[3]);
471 pjmedia_coord pos;
472
473 pos.x = atoi(argv[4]);
474 pos.y = atoi(argv[5]);
475 status = pjsua_vid_win_set_pos(wid, &pos);
476 } else if (argc==6 && strcmp(argv[2], "resize")==0) {
477 pjsua_vid_win_id wid = atoi(argv[3]);
478 pjmedia_rect_size size;
479
480 size.w = atoi(argv[4]);
481 size.h = atoi(argv[5]);
482 status = pjsua_vid_win_set_size(wid, &size);
483 } else if (argc==3 && strcmp(argv[2], "arrange")==0) {
484 arrange_window(PJSUA_INVALID_ID);
485 } else
486 goto on_error;
487
488 if (status != PJ_SUCCESS) {
489 PJ_PERROR(1,(THIS_FILE, status, "Window operation error"));
490 }
491
492 } else if (strcmp(argv[1], "codec")==0) {
493 pjsua_codec_info ci[PJMEDIA_CODEC_MGR_MAX_CODECS];
494 unsigned count = PJ_ARRAY_SIZE(ci);
495 pj_status_t status;
496
497 if (argc==3 && strcmp(argv[2], "list")==0) {
498 status = pjsua_vid_enum_codecs(ci, &count);
499 if (status != PJ_SUCCESS) {
500 PJ_PERROR(1,(THIS_FILE, status, "Error enumerating codecs"));
501 } else {
502 unsigned i;
503 PJ_LOG(3,(THIS_FILE, "Found %d video codecs:", count));
504 PJ_LOG(3,(THIS_FILE, "codec id prio fps bw(kbps) size"));
505 PJ_LOG(3,(THIS_FILE, "------------------------------------------"));
506 for (i=0; i<count; ++i) {
507 pjmedia_vid_codec_param cp;
508 pjmedia_video_format_detail *vfd;
509
510 status = pjsua_vid_codec_get_param(&ci[i].codec_id, &cp);
511 if (status != PJ_SUCCESS)
512 continue;
513
514 vfd = pjmedia_format_get_video_format_detail(&cp.enc_fmt,
515 PJ_TRUE);
516 PJ_LOG(3,(THIS_FILE, "%.*s%.*s %3d %7.2f %4d/%4d %dx%d",
517 (int)ci[i].codec_id.slen, ci[i].codec_id.ptr,
518 13-(int)ci[i].codec_id.slen, " ",
519 ci[i].priority,
520 (vfd->fps.num*1.0/vfd->fps.denum),
521 vfd->avg_bps/1000, vfd->max_bps/1000,
522 vfd->size.w, vfd->size.h));
523 }
524 }
525 } else if (argc==5 && strcmp(argv[2], "prio")==0) {
526 pj_str_t cid;
527 int prio;
528 cid = pj_str(argv[3]);
529 prio = atoi(argv[4]);
530 status = pjsua_vid_codec_set_priority(&cid, (pj_uint8_t)prio);
531 if (status != PJ_SUCCESS)
532 PJ_PERROR(1,(THIS_FILE, status, "Set codec priority error"));
533 } else if (argc==6 && strcmp(argv[2], "fps")==0) {
534 pjmedia_vid_codec_param cp;
535 pj_str_t cid;
536 int M, N;
537 cid = pj_str(argv[3]);
538 M = atoi(argv[4]);
539 N = atoi(argv[5]);
540 status = pjsua_vid_codec_get_param(&cid, &cp);
541 if (status == PJ_SUCCESS) {
542 cp.enc_fmt.det.vid.fps.num = M;
543 cp.enc_fmt.det.vid.fps.denum = N;
544 status = pjsua_vid_codec_set_param(&cid, &cp);
545 }
546 if (status != PJ_SUCCESS)
547 PJ_PERROR(1,(THIS_FILE, status, "Set codec framerate error"));
548 } else if (argc==6 && strcmp(argv[2], "bw")==0) {
549 pjmedia_vid_codec_param cp;
550 pj_str_t cid;
551 int M, N;
552 cid = pj_str(argv[3]);
553 M = atoi(argv[4]);
554 N = atoi(argv[5]);
555 status = pjsua_vid_codec_get_param(&cid, &cp);
556 if (status == PJ_SUCCESS) {
557 cp.enc_fmt.det.vid.avg_bps = M * 1000;
558 cp.enc_fmt.det.vid.max_bps = N * 1000;
559 status = pjsua_vid_codec_set_param(&cid, &cp);
560 }
561 if (status != PJ_SUCCESS)
562 PJ_PERROR(1,(THIS_FILE, status, "Set codec bitrate error"));
563 } else if (argc==6 && strcmp(argv[2], "size")==0) {
564 pjmedia_vid_codec_param cp;
565 pj_str_t cid;
566 int M, N;
567 cid = pj_str(argv[3]);
568 M = atoi(argv[4]);
569 N = atoi(argv[5]);
570 status = pjsua_vid_codec_get_param(&cid, &cp);
571 if (status == PJ_SUCCESS) {
572 cp.enc_fmt.det.vid.size.w = M;
573 cp.enc_fmt.det.vid.size.h = N;
574 status = pjsua_vid_codec_set_param(&cid, &cp);
575 }
576 if (status != PJ_SUCCESS)
577 PJ_PERROR(1,(THIS_FILE, status, "Set codec size error"));
578 } else
579 goto on_error;
580 } else
581 goto on_error;
582
583 return;
584
585on_error:
586 PJ_LOG(1,(THIS_FILE, "Invalid command, use 'vid help'"));
587}
588
589#endif /* PJSUA_HAS_VIDEO */
590
591/** UI Command **/
592static void ui_make_new_call()
593{
594 char buf[128];
595 pjsua_msg_data msg_data;
596 input_result result;
597 pj_str_t tmp;
598
599 printf("(You currently have %d calls)\n", pjsua_call_get_count());
600
601 ui_input_url("Make call", buf, sizeof(buf), &result);
602 if (result.nb_result != PJSUA_APP_NO_NB) {
603
604 if (result.nb_result == -1 || result.nb_result == 0) {
605 puts("You can't do that with make call!");
606 return;
607 } else {
608 pjsua_buddy_info binfo;
609 pjsua_buddy_get_info(result.nb_result-1, &binfo);
610 tmp.ptr = buf;
611 pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
612 }
613
614 } else if (result.uri_result) {
615 tmp = pj_str(result.uri_result);
616 } else {
617 tmp.slen = 0;
618 }
619
620 pjsua_msg_data_init(&msg_data);
621 TEST_MULTIPART(&msg_data);
622 pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL,
623 &msg_data, &current_call);
624}
625
626static void ui_make_multi_call()
627{
628 char menuin[32];
629 int count;
630 char buf[128];
631 input_result result;
632 pj_str_t tmp;
633 int i;
634
635 printf("(You currently have %d calls)\n", pjsua_call_get_count());
636
637 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
638 return;
639
640 count = my_atoi(menuin);
641 if (count < 1)
642 return;
643
644 ui_input_url("Make call", buf, sizeof(buf), &result);
645 if (result.nb_result != PJSUA_APP_NO_NB) {
646 pjsua_buddy_info binfo;
647 if (result.nb_result == -1 || result.nb_result == 0) {
648 puts("You can't do that with make call!");
649 return;
650 }
651 pjsua_buddy_get_info(result.nb_result-1, &binfo);
652 tmp.ptr = buf;
653 pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
654 } else {
655 tmp = pj_str(result.uri_result);
656 }
657
658 for (i=0; i<my_atoi(menuin); ++i) {
659 pj_status_t status;
660
661 status = pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL,
662 NULL, NULL);
663 if (status != PJ_SUCCESS)
664 break;
665 }
666}
667
668static void ui_detect_nat_type()
669{
670 int i = pjsua_detect_nat_type();
671 if (i != PJ_SUCCESS)
672 pjsua_perror(THIS_FILE, "Error", i);
673}
674
675static void ui_send_instant_message()
676{
677 char *uri = NULL;
678 /* i is for call index to send message, if any */
679 int i = -1;
680 input_result result;
681 char buf[128];
682 char text[128];
683 pj_str_t tmp;
684
685 /* Input destination. */
686 ui_input_url("Send IM to", buf, sizeof(buf), &result);
687 if (result.nb_result != PJSUA_APP_NO_NB) {
688
689 if (result.nb_result == -1) {
690 puts("You can't send broadcast IM like that!");
691 return;
692
693 } else if (result.nb_result == 0) {
694 i = current_call;
695 } else {
696 pjsua_buddy_info binfo;
697 pjsua_buddy_get_info(result.nb_result-1, &binfo);
698 tmp.ptr = buf;
699 pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
700 uri = buf;
701 }
702
703 } else if (result.uri_result) {
704 uri = result.uri_result;
705 }
706
707
708 /* Send typing indication. */
709 if (i != -1)
710 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
711 else {
712 pj_str_t tmp_uri = pj_str(uri);
713 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
714 }
715
716 /* Input the IM . */
717 if (!simple_input("Message", text, sizeof(text))) {
718 /*
719 * Cancelled.
720 * Send typing notification too, saying we're not typing.
721 */
722 if (i != -1)
723 pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
724 else {
725 pj_str_t tmp_uri = pj_str(uri);
726 pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
727 }
728 return;
729 }
730
731 tmp = pj_str(text);
732
733 /* Send the IM */
734 if (i != -1)
735 pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
736 else {
737 pj_str_t tmp_uri = pj_str(uri);
738 pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
739 }
740}
741
742static void ui_answer_call()
743{
744 pjsua_call_info call_info;
745 char buf[128];
746 pjsua_msg_data msg_data;
747
748 if (current_call != -1) {
749 pjsua_call_get_info(current_call, &call_info);
750 } else {
751 /* Make compiler happy */
752 call_info.role = PJSIP_ROLE_UAC;
753 call_info.state = PJSIP_INV_STATE_DISCONNECTED;
754 }
755
756 if (current_call == -1 ||
757 call_info.role != PJSIP_ROLE_UAS ||
758 call_info.state >= PJSIP_INV_STATE_CONNECTING)
759 {
760 puts("No pending incoming call");
761 fflush(stdout);
762 return;
763
764 } else {
765 int st_code;
766 char contact[120];
767 pj_str_t hname = { "Contact", 7 };
768 pj_str_t hvalue;
769 pjsip_generic_string_hdr hcontact;
770
771 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
772 return;
773
774 st_code = my_atoi(buf);
775 if (st_code < 100)
776 return;
777
778 pjsua_msg_data_init(&msg_data);
779
780 if (st_code/100 == 3) {
781 if (!simple_input("Enter URL to be put in Contact",
782 contact, sizeof(contact)))
783 return;
784 hvalue = pj_str(contact);
785 pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue);
786
787 pj_list_push_back(&msg_data.hdr_list, &hcontact);
788 }
789
790 /*
791 * Must check again!
792 * Call may have been disconnected while we're waiting for
793 * keyboard input.
794 */
795 if (current_call == -1) {
796 puts("Call has been disconnected");
797 fflush(stdout);
798 return;
799 }
800
801 pjsua_call_answer2(current_call, &call_opt, st_code, NULL, &msg_data);
802 }
803}
804
805static void ui_hangup_call(char menuin[])
806{
807 if (current_call == -1) {
808 puts("No current call");
809 fflush(stdout);
810 return;
811
812 } else if (menuin[1] == 'a') {
813 /* Hangup all calls */
814 pjsua_call_hangup_all();
815 } else {
816 /* Hangup current calls */
817 pjsua_call_hangup(current_call, 0, NULL, NULL);
818 }
819}
820
821static void ui_cycle_dialog(char menuin[])
822{
823 if (menuin[0] == ']') {
824 find_next_call();
825
826 } else {
827 find_prev_call();
828 }
829
830 if (current_call != -1) {
831 pjsua_call_info call_info;
832
833 pjsua_call_get_info(current_call, &call_info);
834 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s",
835 (int)call_info.remote_info.slen,
836 call_info.remote_info.ptr));
837
838 } else {
839 PJ_LOG(3,(THIS_FILE,"No current dialog"));
840 }
841}
842
843static void ui_cycle_account()
844{
845 int i;
846 char buf[128];
847
848 if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
849 return;
850
851 i = my_atoi(buf);
852 if (pjsua_acc_is_valid(i)) {
853 pjsua_acc_set_default(i);
854 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
855 } else {
856 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
857 }
858}
859
860static void ui_add_buddy()
861{
862 char buf[128];
863 pjsua_buddy_config buddy_cfg;
864 pjsua_buddy_id buddy_id;
865 pj_status_t status;
866
867 if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
868 return;
869
870 if (pjsua_verify_url(buf) != PJ_SUCCESS) {
871 printf("Invalid URI '%s'\n", buf);
872 return;
873 }
874
875 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
876
877 buddy_cfg.uri = pj_str(buf);
878 buddy_cfg.subscribe = PJ_TRUE;
879
880 status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
881 if (status == PJ_SUCCESS) {
882 printf("New buddy '%s' added at index %d\n",
883 buf, buddy_id+1);
884 }
885}
886
887static void ui_add_account(pjsua_transport_config *rtp_cfg)
888{
889 char id[80], registrar[80], realm[80], uname[80], passwd[30];
890 pjsua_acc_config acc_cfg;
891 pj_status_t status;
892
893 if (!simple_input("Your SIP URL:", id, sizeof(id)))
894 return;
895 if (!simple_input("URL of the registrar:", registrar, sizeof(registrar)))
896 return;
897 if (!simple_input("Auth Realm:", realm, sizeof(realm)))
898 return;
899 if (!simple_input("Auth Username:", uname, sizeof(uname)))
900 return;
901 if (!simple_input("Auth Password:", passwd, sizeof(passwd)))
902 return;
903
904 pjsua_acc_config_default(&acc_cfg);
905 acc_cfg.id = pj_str(id);
906 acc_cfg.reg_uri = pj_str(registrar);
907 acc_cfg.cred_count = 1;
908 acc_cfg.cred_info[0].scheme = pj_str("Digest");
909 acc_cfg.cred_info[0].realm = pj_str(realm);
910 acc_cfg.cred_info[0].username = pj_str(uname);
911 acc_cfg.cred_info[0].data_type = 0;
912 acc_cfg.cred_info[0].data = pj_str(passwd);
913
914 acc_cfg.rtp_cfg = *rtp_cfg;
915 app_config_init_video(&acc_cfg);
916
917 status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
918 if (status != PJ_SUCCESS) {
919 pjsua_perror(THIS_FILE, "Error adding new account", status);
920 }
921}
922
923static void ui_delete_buddy()
924{
925 char buf[128];
926 int i;
927
928 if (!simple_input("Enter buddy ID to delete", buf, sizeof(buf)))
929 return;
930
931 i = my_atoi(buf) - 1;
932
933 if (!pjsua_buddy_is_valid(i)) {
934 printf("Invalid buddy id %d\n", i);
935 } else {
936 pjsua_buddy_del(i);
937 printf("Buddy %d deleted\n", i);
938 }
939}
940
941static void ui_delete_account()
942{
943 char buf[128];
944 int i;
945
946 if (!simple_input("Enter account ID to delete", buf, sizeof(buf)))
947 return;
948
949 i = my_atoi(buf);
950
951 if (!pjsua_acc_is_valid(i)) {
952 printf("Invalid account id %d\n", i);
953 } else {
954 pjsua_acc_del(i);
955 printf("Account %d deleted\n", i);
956 }
957}
958
959static void ui_call_hold()
960{
961 if (current_call != -1) {
962 pjsua_call_set_hold(current_call, NULL);
963 } else {
964 PJ_LOG(3,(THIS_FILE, "No current call"));
965 }
966}
967
968static void ui_call_reinvite()
969{
970 call_opt.flag |= PJSUA_CALL_UNHOLD;
971 pjsua_call_reinvite2(current_call, &call_opt, NULL);
972}
973
974static void ui_send_update()
975{
976 if (current_call != -1) {
977 pjsua_call_update2(current_call, &call_opt, NULL);
978 } else {
979 PJ_LOG(3,(THIS_FILE, "No current call"));
980 }
981}
982
983/*
984 * Change codec priorities.
985 */
986static void ui_manage_codec_prio()
987{
988 pjsua_codec_info c[32];
989 unsigned i, count = PJ_ARRAY_SIZE(c);
990 char input[32];
991 char *codec, *prio;
992 pj_str_t id;
993 int new_prio;
994 pj_status_t status;
995
996 printf("List of audio codecs:\n");
997 pjsua_enum_codecs(c, &count);
998 for (i=0; i<count; ++i) {
999 printf(" %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
1000 c[i].codec_id.ptr);
1001 }
1002
1003#if PJSUA_HAS_VIDEO
1004 puts("");
1005 printf("List of video codecs:\n");
1006 pjsua_vid_enum_codecs(c, &count);
1007 for (i=0; i<count; ++i) {
1008 printf(" %d\t%.*s%s%.*s\n", c[i].priority,
1009 (int)c[i].codec_id.slen,
1010 c[i].codec_id.ptr,
1011 c[i].desc.slen? " - ":"",
1012 (int)c[i].desc.slen,
1013 c[i].desc.ptr);
1014 }
1015#endif
1016
1017 puts("");
1018 puts("Enter codec id and its new priority (e.g. \"speex/16000 200\", "
1019 """\"H263 200\"),");
1020 puts("or empty to cancel.");
1021
1022 printf("Codec name (\"*\" for all) and priority: ");
1023 if (fgets(input, sizeof(input), stdin) == NULL)
1024 return;
1025 if (input[0]=='\r' || input[0]=='\n') {
1026 puts("Done");
1027 return;
1028 }
1029
1030 codec = strtok(input, " \t\r\n");
1031 prio = strtok(NULL, " \r\n");
1032
1033 if (!codec || !prio) {
1034 puts("Invalid input");
1035 return;
1036 }
1037
1038 new_prio = atoi(prio);
1039 if (new_prio < 0)
1040 new_prio = 0;
1041 else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST)
1042 new_prio = PJMEDIA_CODEC_PRIO_HIGHEST;
1043
1044 status = pjsua_codec_set_priority(pj_cstr(&id, codec),
1045 (pj_uint8_t)new_prio);
1046#if PJSUA_HAS_VIDEO
1047 if (status != PJ_SUCCESS) {
1048 status = pjsua_vid_codec_set_priority(pj_cstr(&id, codec),
1049 (pj_uint8_t)new_prio);
1050 }
1051#endif
1052 if (status != PJ_SUCCESS)
1053 pjsua_perror(THIS_FILE, "Error setting codec priority", status);
1054}
1055
1056static void ui_call_transfer(pj_bool_t no_refersub)
1057{
1058 if (current_call == -1) {
1059 PJ_LOG(3,(THIS_FILE, "No current call"));
1060 } else {
1061 int call = current_call;
1062 char buf[128];
1063 pjsip_generic_string_hdr refer_sub;
1064 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
1065 pj_str_t STR_FALSE = { "false", 5 };
1066 pjsua_call_info ci;
1067 input_result result;
1068 pjsua_msg_data msg_data;
1069
1070 pjsua_call_get_info(current_call, &ci);
1071 printf("Transfering current call [%d] %.*s\n", current_call,
1072 (int)ci.remote_info.slen, ci.remote_info.ptr);
1073
1074 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
1075
1076 /* Check if call is still there. */
1077
1078 if (call != current_call) {
1079 puts("Call has been disconnected");
1080 return;
1081 }
1082
1083 pjsua_msg_data_init(&msg_data);
1084 if (no_refersub) {
1085 /* Add Refer-Sub: false in outgoing REFER request */
1086 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
1087 &STR_FALSE);
1088 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
1089 }
1090 if (result.nb_result != PJSUA_APP_NO_NB) {
1091 if (result.nb_result == -1 || result.nb_result == 0)
1092 puts("You can't do that with transfer call!");
1093 else {
1094 pjsua_buddy_info binfo;
1095 pjsua_buddy_get_info(result.nb_result-1, &binfo);
1096 pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
1097 }
1098
1099 } else if (result.uri_result) {
1100 pj_str_t tmp;
1101 tmp = pj_str(result.uri_result);
1102 pjsua_call_xfer( current_call, &tmp, &msg_data);
1103 }
1104 }
1105}
1106
1107static void ui_call_transfer_replaces(pj_bool_t no_refersub)
1108{
1109 if (current_call == -1) {
1110 PJ_LOG(3,(THIS_FILE, "No current call"));
1111 } else {
1112 int call = current_call;
1113 int dst_call;
1114 pjsip_generic_string_hdr refer_sub;
1115 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
1116 pj_str_t STR_FALSE = { "false", 5 };
1117 pjsua_call_id ids[PJSUA_MAX_CALLS];
1118 pjsua_call_info ci;
1119 pjsua_msg_data msg_data;
1120 char buf[128];
1121 unsigned i, count;
1122
1123 count = PJ_ARRAY_SIZE(ids);
1124 pjsua_enum_calls(ids, &count);
1125
1126 if (count <= 1) {
1127 puts("There are no other calls");
1128 return;
1129 }
1130
1131 pjsua_call_get_info(current_call, &ci);
1132 printf("Transfer call [%d] %.*s to one of the following:\n",
1133 current_call,
1134 (int)ci.remote_info.slen, ci.remote_info.ptr);
1135
1136 for (i=0; i<count; ++i) {
1137 pjsua_call_info call_info;
1138
1139 if (ids[i] == call)
1140 return;
1141
1142 pjsua_call_get_info(ids[i], &call_info);
1143 printf("%d %.*s [%.*s]\n",
1144 ids[i],
1145 (int)call_info.remote_info.slen,
1146 call_info.remote_info.ptr,
1147 (int)call_info.state_text.slen,
1148 call_info.state_text.ptr);
1149 }
1150
1151 if (!simple_input("Enter call number to be replaced", buf, sizeof(buf)))
1152 return;
1153
1154 dst_call = my_atoi(buf);
1155
1156 /* Check if call is still there. */
1157
1158 if (call != current_call) {
1159 puts("Call has been disconnected");
1160 return;
1161 }
1162
1163 /* Check that destination call is valid. */
1164 if (dst_call == call) {
1165 puts("Destination call number must not be the same "
1166 "as the call being transfered");
1167 return;
1168 }
1169 if (dst_call >= PJSUA_MAX_CALLS) {
1170 puts("Invalid destination call number");
1171 return;
1172 }
1173 if (!pjsua_call_is_active(dst_call)) {
1174 puts("Invalid destination call number");
1175 return;
1176 }
1177
1178 pjsua_msg_data_init(&msg_data);
1179 if (no_refersub) {
1180 /* Add Refer-Sub: false in outgoing REFER request */
1181 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
1182 &STR_FALSE);
1183 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
1184 }
1185
1186 pjsua_call_xfer_replaces(call, dst_call,
1187 PJSUA_XFER_NO_REQUIRE_REPLACES,
1188 &msg_data);
1189 }
1190}
1191
1192static void ui_send_dtmf_2833()
1193{
1194 if (current_call == -1) {
1195 PJ_LOG(3,(THIS_FILE, "No current call"));
1196 } else if (!pjsua_call_has_media(current_call)) {
1197 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
1198 } else {
1199 pj_str_t digits;
1200 int call = current_call;
1201 pj_status_t status;
1202 char buf[128];
1203
1204 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
1205 sizeof(buf)))
1206 {
1207 return;
1208 }
1209
1210 if (call != current_call) {
1211 puts("Call has been disconnected");
1212 return;
1213 }
1214
1215 digits = pj_str(buf);
1216 status = pjsua_call_dial_dtmf(current_call, &digits);
1217 if (status != PJ_SUCCESS) {
1218 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
1219 } else {
1220 puts("DTMF digits enqueued for transmission");
1221 }
1222 }
1223}
1224
1225static void ui_send_dtmf_info()
1226{
1227 if (current_call == -1) {
1228 PJ_LOG(3,(THIS_FILE, "No current call"));
1229 } else {
1230 const pj_str_t SIP_INFO = pj_str("INFO");
1231 pj_str_t digits;
1232 int call = current_call;
1233 int i;
1234 pj_status_t status;
1235 char buf[128];
1236
1237 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
1238 sizeof(buf)))
1239 {
1240 return;
1241 }
1242
1243 if (call != current_call) {
1244 puts("Call has been disconnected");
1245 return;
1246 }
1247
1248 digits = pj_str(buf);
1249 for (i=0; i<digits.slen; ++i) {
1250 char body[80];
1251 pjsua_msg_data msg_data;
1252
1253 pjsua_msg_data_init(&msg_data);
1254 msg_data.content_type = pj_str("application/dtmf-relay");
1255
1256 pj_ansi_snprintf(body, sizeof(body),
1257 "Signal=%c\r\n"
1258 "Duration=160",
1259 buf[i]);
1260 msg_data.msg_body = pj_str(body);
1261
1262 status = pjsua_call_send_request(current_call, &SIP_INFO,
1263 &msg_data);
1264 if (status != PJ_SUCCESS) {
1265 return;
1266 }
1267 }
1268 }
1269}
1270
1271static void ui_send_arbitrary_request()
1272{
1273 char text[128];
1274 char buf[128];
1275 char *uri;
1276 input_result result;
1277 pj_str_t tmp;
1278
1279 if (pjsua_acc_get_count() == 0) {
1280 puts("Sorry, need at least one account configured");
1281 return;
1282 }
1283
1284 puts("Send arbitrary request to remote host");
1285
1286 /* Input METHOD */
1287 if (!simple_input("Request method:",text,sizeof(text)))
1288 return;
1289
1290 /* Input destination URI */
1291 uri = NULL;
1292 ui_input_url("Destination URI", buf, sizeof(buf), &result);
1293 if (result.nb_result != PJSUA_APP_NO_NB) {
1294
1295 if (result.nb_result == -1) {
1296 puts("Sorry you can't do that!");
1297 return;
1298 } else if (result.nb_result == 0) {
1299 uri = NULL;
1300 if (current_call == PJSUA_INVALID_ID) {
1301 puts("No current call");
1302 return;
1303 }
1304 } else {
1305 pjsua_buddy_info binfo;
1306 pjsua_buddy_get_info(result.nb_result-1, &binfo);
1307 tmp.ptr = buf;
1308 pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
1309 uri = buf;
1310 }
1311
1312 } else if (result.uri_result) {
1313 uri = result.uri_result;
1314 } else {
1315 return;
1316 }
1317
1318 if (uri) {
1319 tmp = pj_str(uri);
1320 send_request(text, &tmp);
1321 } else {
1322 /* If you send call control request using this method
1323 * (such requests includes BYE, CANCEL, etc.), it will
1324 * not go well with the call state, so don't do it
1325 * unless it's for testing.
1326 */
1327 pj_str_t method = pj_str(text);
1328 pjsua_call_send_request(current_call, &method, NULL);
1329 }
1330}
1331
1332static void ui_echo(char menuin[])
1333{
1334 if (pj_ansi_strnicmp(menuin, "echo", 4)==0) {
1335 pj_str_t tmp;
1336
1337 tmp.ptr = menuin+5;
1338 tmp.slen = pj_ansi_strlen(menuin)-6;
1339
1340 if (tmp.slen < 1) {
1341 puts("Usage: echo [0|1]");
1342 return;
1343 }
1344 cmd_echo = *tmp.ptr != '0' || tmp.slen!=1;
1345 }
1346}
1347
1348static void ui_sleep(char menuin[])
1349{
1350 if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
1351 pj_str_t tmp;
1352 int delay;
1353
1354 tmp.ptr = menuin+6;
1355 tmp.slen = pj_ansi_strlen(menuin)-7;
1356
1357 if (tmp.slen < 1) {
1358 puts("Usage: sleep MSEC");
1359 return;
1360 }
1361
1362 delay = pj_strtoul(&tmp);
1363 if (delay < 0) delay = 0;
1364 pj_thread_sleep(delay);
1365 }
1366}
1367
1368static void ui_subscribe(char menuin[])
1369{
1370 char buf[128];
1371 input_result result;
1372
1373 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
1374 if (result.nb_result != PJSUA_APP_NO_NB) {
1375 if (result.nb_result == -1) {
1376 int i, count;
1377 count = pjsua_get_buddy_count();
1378 for (i=0; i<count; ++i)
1379 pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
1380 } else if (result.nb_result == 0) {
1381 puts("Sorry, can only subscribe to buddy's presence, "
1382 "not from existing call");
1383 } else {
1384 pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
1385 }
1386
1387 } else if (result.uri_result) {
1388 puts("Sorry, can only subscribe to buddy's presence, "
1389 "not arbitrary URL (for now)");
1390 }
1391}
1392
1393static void ui_register(char menuin[])
1394{
1395 switch (menuin[1]) {
1396 case 'r':
1397 /*
1398 * Re-Register.
1399 */
1400 pjsua_acc_set_registration(current_acc, PJ_TRUE);
1401 break;
1402 case 'u':
1403 /*
1404 * Unregister
1405 */
1406 pjsua_acc_set_registration(current_acc, PJ_FALSE);
1407 break;
1408 }
1409}
1410
1411static void ui_toggle_state()
1412{
1413 pjsua_acc_info acc_info;
1414
1415 pjsua_acc_get_info(current_acc, &acc_info);
1416 acc_info.online_status = !acc_info.online_status;
1417 pjsua_acc_set_online_status(current_acc, acc_info.online_status);
1418 printf("Setting %s online status to %s\n",
1419 acc_info.acc_uri.ptr,
1420 (acc_info.online_status?"online":"offline"));
1421}
1422
1423/*
1424 * Change extended online status.
1425 */
1426static void ui_change_online_status()
1427{
1428 char menuin[32];
1429 pj_bool_t online_status;
1430 pjrpid_element elem;
1431 int i, choice;
1432
1433 enum {
1434 AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX
1435 };
1436
1437 struct opt {
1438 int id;
1439 char *name;
1440 } opts[] = {
1441 { AVAILABLE, "Available" },
1442 { BUSY, "Busy"},
1443 { OTP, "On the phone"},
1444 { IDLE, "Idle"},
1445 { AWAY, "Away"},
1446 { BRB, "Be right back"},
1447 { OFFLINE, "Offline"}
1448 };
1449
1450 printf("\n"
1451 "Choices:\n");
1452 for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) {
1453 printf(" %d %s\n", opts[i].id+1, opts[i].name);
1454 }
1455
1456 if (!simple_input("Select status", menuin, sizeof(menuin)))
1457 return;
1458
1459 choice = atoi(menuin) - 1;
1460 if (choice < 0 || choice >= OPT_MAX) {
1461 puts("Invalid selection");
1462 return;
1463 }
1464
1465 pj_bzero(&elem, sizeof(elem));
1466 elem.type = PJRPID_ELEMENT_TYPE_PERSON;
1467
1468 online_status = PJ_TRUE;
1469
1470 switch (choice) {
1471 case AVAILABLE:
1472 break;
1473 case BUSY:
1474 elem.activity = PJRPID_ACTIVITY_BUSY;
1475 elem.note = pj_str("Busy");
1476 break;
1477 case OTP:
1478 elem.activity = PJRPID_ACTIVITY_BUSY;
1479 elem.note = pj_str("On the phone");
1480 break;
1481 case IDLE:
1482 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
1483 elem.note = pj_str("Idle");
1484 break;
1485 case AWAY:
1486 elem.activity = PJRPID_ACTIVITY_AWAY;
1487 elem.note = pj_str("Away");
1488 break;
1489 case BRB:
1490 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
1491 elem.note = pj_str("Be right back");
1492 break;
1493 case OFFLINE:
1494 online_status = PJ_FALSE;
1495 break;
1496 }
1497
1498 pjsua_acc_set_online_status2(current_acc, online_status, &elem);
1499}
1500
1501/*
1502 * List the ports in conference bridge
1503 */
1504static void ui_conf_list()
1505{
1506 unsigned i, count;
1507 pjsua_conf_port_id id[PJSUA_MAX_CALLS];
1508
1509 printf("Conference ports:\n");
1510
1511 count = PJ_ARRAY_SIZE(id);
1512 pjsua_enum_conf_ports(id, &count);
1513
1514 for (i=0; i<count; ++i) {
1515 char txlist[PJSUA_MAX_CALLS*4+10];
1516 unsigned j;
1517 pjsua_conf_port_info info;
1518
1519 pjsua_conf_get_port_info(id[i], &info);
1520
1521 txlist[0] = '\0';
1522 for (j=0; j<info.listener_cnt; ++j) {
1523 char s[10];
1524 pj_ansi_snprintf(s, sizeof(s), "#%d ", info.listeners[j]);
1525 pj_ansi_strcat(txlist, s);
1526 }
1527 printf("Port #%02d[%2dKHz/%dms/%d] %20.*s transmitting to: %s\n",
1528 info.slot_id,
1529 info.clock_rate/1000,
1530 info.samples_per_frame*1000/info.channel_count/info.clock_rate,
1531 info.channel_count,
1532 (int)info.name.slen,
1533 info.name.ptr,
1534 txlist);
1535
1536 }
1537 puts("");
1538}
1539
1540static void ui_conf_connect(char menuin[])
1541{
1542 char tmp[10], src_port[10], dst_port[10];
1543 pj_status_t status;
1544 int cnt;
1545 const char *src_title, *dst_title;
1546
1547 cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
1548
1549 if (cnt != 3) {
1550 ui_conf_list();
1551
1552 src_title = (menuin[1]=='c'? "Connect src port #":
1553 "Disconnect src port #");
1554 dst_title = (menuin[1]=='c'? "To dst port #":"From dst port #");
1555
1556 if (!simple_input(src_title, src_port, sizeof(src_port)))
1557 return;
1558
1559 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
1560 return;
1561 }
1562
1563 if (menuin[1]=='c') {
1564 status = pjsua_conf_connect(my_atoi(src_port), my_atoi(dst_port));
1565 } else {
1566 status = pjsua_conf_disconnect(my_atoi(src_port), my_atoi(dst_port));
1567 }
1568 if (status == PJ_SUCCESS) {
1569 puts("Success");
1570 } else {
1571 puts("ERROR!!");
1572 }
1573}
1574
1575static void ui_adjust_volume()
1576{
1577 char buf[128];
1578 char text[128];
1579 sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level);
1580 if (simple_input(buf,text,sizeof(text))) {
1581 char *err;
1582 app_config.mic_level = (float)strtod(text, &err);
1583 pjsua_conf_adjust_rx_level(0, app_config.mic_level);
1584 }
1585 sprintf(buf, "Adjust speaker level: [%4.1fx] ", app_config.speaker_level);
1586 if (simple_input(buf,text,sizeof(text))) {
1587 char *err;
1588 app_config.speaker_level = (float)strtod(text, &err);
1589 pjsua_conf_adjust_tx_level(0, app_config.speaker_level);
1590 }
1591}
1592
1593static void ui_dump_call_quality()
1594{
1595 if (current_call != PJSUA_INVALID_ID) {
1596 log_call_dump(current_call);
1597 } else {
1598 PJ_LOG(3,(THIS_FILE, "No current call"));
1599 }
1600}
1601
1602static void ui_dump_configuration()
1603{
1604 char settings[2000];
1605 int len;
1606
1607 len = write_settings(&app_config, settings, sizeof(settings));
1608 if (len < 1)
1609 PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
1610 else
1611 PJ_LOG(3,(THIS_FILE, "Dumping configuration (%d bytes):\n%s\n",
1612 len, settings));
1613}
1614
1615static void ui_write_settings()
1616{
1617 char settings[2000];
1618 int len;
1619 char buf[128];
1620
1621 len = write_settings(&app_config, settings, sizeof(settings));
1622 if (len < 1)
1623 PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
1624 else {
1625 pj_oshandle_t fd;
1626 pj_status_t status;
1627
1628 status = pj_file_open(app_config.pool, buf, PJ_O_WRONLY, &fd);
1629 if (status != PJ_SUCCESS) {
1630 pjsua_perror(THIS_FILE, "Unable to open file", status);
1631 } else {
1632 pj_ssize_t size = len;
1633 pj_file_write(fd, settings, &size);
1634 pj_file_close(fd);
1635
1636 printf("Settings successfully written to '%s'\n", buf);
1637 }
1638 }
1639}
1640
1641/*
1642 * Dump application states.
1643 */
1644static void ui_app_dump(pj_bool_t detail)
1645{
1646 pjsua_dump(detail);
1647}
1648
1649static void ui_call_redirect(char menuin[])
1650{
1651 if (current_call == PJSUA_INVALID_ID) {
1652 PJ_LOG(3,(THIS_FILE, "No current call"));
1653 } else {
1654 if (!pjsua_call_is_active(current_call)) {
1655 PJ_LOG(1,(THIS_FILE, "Call %d has gone", current_call));
1656 } else if (menuin[1] == 'a') {
1657 pjsua_call_process_redirect(current_call,
1658 PJSIP_REDIRECT_ACCEPT_REPLACE);
1659 } else if (menuin[1] == 'A') {
1660 pjsua_call_process_redirect(current_call,
1661 PJSIP_REDIRECT_ACCEPT);
1662 } else if (menuin[1] == 'r') {
1663 pjsua_call_process_redirect(current_call,
1664 PJSIP_REDIRECT_REJECT);
1665 } else {
1666 pjsua_call_process_redirect(current_call,
1667 PJSIP_REDIRECT_STOP);
1668 }
1669 }
1670}
1671
1672/*
1673 * Main "user interface" loop.
1674 */
1675void legacy_main()
1676{
1677 char menuin[80];
1678 char buf[128];
1679
1680 keystroke_help();
1681
1682 for (;;) {
1683
1684 printf(">>> ");
1685 fflush(stdout);
1686
1687 if (fgets(menuin, sizeof(menuin), stdin) == NULL) {
1688 /*
1689 * Be friendly to users who redirect commands into
1690 * program, when file ends, resume with kbd.
1691 * If exit is desired end script with q for quit
1692 */
1693 /* Reopen stdin/stdout/stderr to /dev/console */
1694#if ((defined(PJ_WIN32) && PJ_WIN32!=0) || \
1695 (defined(PJ_WIN64) && PJ_WIN64!=0)) && \
1696 (!defined(PJ_WIN32_WINCE) || PJ_WIN32_WINCE==0)
1697 if (freopen ("CONIN$", "r", stdin) == NULL) {
1698#else
1699 if (1) {
1700#endif
1701 puts("Cannot switch back to console from file redirection");
1702 menuin[0] = 'q';
1703 menuin[1] = '\0';
1704 } else {
1705 puts("Switched back to console from file redirection");
1706 continue;
1707 }
1708 }
1709
1710 if (cmd_echo) {
1711 printf("%s", menuin);
1712 }
1713
1714 /* Update call setting */
1715 pjsua_call_setting_default(&call_opt);
1716 call_opt.aud_cnt = app_config.aud_cnt;
1717 call_opt.vid_cnt = app_config.vid.vid_cnt;
1718
1719 switch (menuin[0]) {
1720
1721 case 'm':
1722 /* Make call! : */
1723 ui_make_new_call();
1724 break;
1725
1726 case 'M':
1727 /* Make multiple calls! : */
1728 ui_make_multi_call();
1729 break;
1730
1731 case 'n':
1732 ui_detect_nat_type();
1733 break;
1734
1735 case 'i':
1736 /* Send instant messaeg */
1737 ui_send_instant_message();
1738 break;
1739
1740 case 'a':
1741 ui_answer_call();
1742 break;
1743
1744 case 'h':
1745 ui_hangup_call(menuin);
1746 break;
1747
1748 case ']':
1749 case '[':
1750 /*
1751 * Cycle next/prev dialog.
1752 */
1753 ui_cycle_dialog(menuin);
1754 break;
1755
1756 case '>':
1757 case '<':
1758 ui_cycle_account();
1759 break;
1760
1761 case '+':
1762 if (menuin[1] == 'b') {
1763 ui_add_buddy();
1764 } else if (menuin[1] == 'a') {
1765 ui_add_account(&app_config.rtp_cfg);
1766 } else {
1767 printf("Invalid input %s\n", menuin);
1768 }
1769 break;
1770
1771 case '-':
1772 if (menuin[1] == 'b') {
1773 ui_delete_buddy();
1774 } else if (menuin[1] == 'a') {
1775 ui_delete_account();
1776 } else {
1777 printf("Invalid input %s\n", menuin);
1778 }
1779 break;
1780
1781 case 'H':
1782 /*
1783 * Hold call.
1784 */
1785 ui_call_hold();
1786 break;
1787
1788 case 'v':
1789#if PJSUA_HAS_VIDEO
1790 if (menuin[1]=='i' && menuin[2]=='d' && menuin[3]==' ') {
1791 vid_handle_menu(menuin);
1792 } else
1793#endif
1794 if (current_call != -1) {
1795 /*
1796 * re-INVITE
1797 */
1798 ui_call_reinvite();
1799 } else {
1800 PJ_LOG(3,(THIS_FILE, "No current call"));
1801 }
1802 break;
1803
1804 case 'U':
1805 /*
1806 * Send UPDATE
1807 */
1808 ui_send_update();
1809 break;
1810
1811 case 'C':
1812 if (menuin[1] == 'p') {
1813 ui_manage_codec_prio();
1814 }
1815 break;
1816
1817 case 'x':
1818 /*
1819 * Transfer call.
1820 */
1821 ui_call_transfer(app_config.no_refersub);
1822 break;
1823
1824 case 'X':
1825 /*
1826 * Transfer call with replaces.
1827 */
1828 ui_call_transfer_replaces(app_config.no_refersub);
1829 break;
1830
1831 case '#':
1832 /*
1833 * Send DTMF strings.
1834 */
1835 ui_send_dtmf_2833();
1836 break;
1837
1838 case '*':
1839 /* Send DTMF with INFO */
1840 ui_send_dtmf_info();
1841 break;
1842
1843 case 'S':
1844 /*
1845 * Send arbitrary request
1846 */
1847 ui_send_arbitrary_request();
1848 break;
1849
1850 case 'e':
1851 ui_echo(menuin);
1852 break;
1853
1854 case 's':
1855 if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
1856 ui_sleep(menuin);
1857 break;
1858 }
1859 /* Continue below */
1860
1861 case 'u':
1862 /*
1863 * Subscribe/unsubscribe presence.
1864 */
1865 ui_subscribe(menuin);
1866 break;
1867
1868 case 'r':
1869 ui_register(menuin);
1870 break;
1871
1872 case 't':
1873 ui_toggle_state();
1874 break;
1875
1876 case 'T':
1877 ui_change_online_status();
1878 break;
1879
1880 case 'c':
1881 switch (menuin[1]) {
1882 case 'l':
1883 ui_conf_list();
1884 break;
1885 case 'c':
1886 case 'd':
1887 ui_conf_connect(menuin);
1888 break;
1889 }
1890 break;
1891
1892 case 'V':
1893 /* Adjust audio volume */
1894 ui_adjust_volume();
1895 break;
1896
1897 case 'd':
1898 if (menuin[1] == 'c') {
1899 ui_dump_configuration();
1900 } else if (menuin[1] == 'q') {
1901 ui_dump_call_quality();
1902 } else {
1903 ui_app_dump(menuin[1]=='d');
1904 }
1905 break;
1906
1907 case 'f':
1908 if (simple_input("Enter output filename", buf, sizeof(buf))) {
1909 ui_write_settings();
1910 }
1911 break;
1912
1913 case 'L': /* Restart */
1914 case 'q':
1915 legacy_on_stopped(menuin[0]=='L');
1916 goto on_exit;
1917
1918 case 'R':
1919 ui_call_redirect(menuin);
1920 break;
1921
1922 default:
1923 if (menuin[0] != '\n' && menuin[0] != '\r') {
1924 printf("Invalid input %s", menuin);
1925 }
1926 keystroke_help();
1927 break;
1928 }
1929 }
1930
1931on_exit:
1932 ;
1933}