blob: 1b0074b4d39a1c9386a736120e6fd6ccd9065e00 [file] [log] [blame]
Benny Prijono8df5b022006-03-01 19:31:18 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
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#include <stdlib.h> /* atoi */
21
22
23#define THIS_FILE "main.c"
24
25/* Current dialog */
26static int current_acc;
27static int current_call = -1;
28
29
30/*
31 * Find next call.
32 */
33static pj_bool_t find_next_call(void)
34{
35 int i;
36
37 for (i=current_call+1; i<(int)pjsua.max_calls; ++i) {
38 if (pjsua.calls[i].inv != NULL) {
39 current_call = i;
40 return PJ_TRUE;
41 }
42 }
43
44 for (i=0; i<current_call; ++i) {
45 if (pjsua.calls[i].inv != NULL) {
46 current_call = i;
47 return PJ_TRUE;
48 }
49 }
50
51 current_call = -1;
52 return PJ_FALSE;
53}
54
55
56/*
57 * Find previous call.
58 */
59static pj_bool_t find_prev_call(void)
60{
61 int i;
62
63 for (i=current_call-1; i>=0; --i) {
64 if (pjsua.calls[i].inv != NULL) {
65 current_call = i;
66 return PJ_TRUE;
67 }
68 }
69
70 for (i=pjsua.max_calls-1; i>current_call; --i) {
71 if (pjsua.calls[i].inv != NULL) {
72 current_call = i;
73 return PJ_TRUE;
74 }
75 }
76
77 current_call = -1;
78 return PJ_FALSE;
79}
80
81
82
83/*
84 * Notify UI when invite state has changed.
85 */
Benny Prijono3cbcca62006-03-02 21:19:55 +000086void pjsua_ui_on_call_state(int call_index, pjsip_event *e)
Benny Prijono8df5b022006-03-01 19:31:18 +000087{
88 pjsua_call *call = &pjsua.calls[call_index];
89
90 PJ_UNUSED_ARG(e);
91
92 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
93 call_index,
94 pjsua_inv_state_names[call->inv->state]));
95
96 if (call->inv->state == PJSIP_INV_STATE_DISCONNECTED) {
97 call->inv = NULL;
98 if ((int)call->index == current_call) {
99 find_next_call();
100 }
101
102 } else {
103
104 if (call && current_call==-1)
105 current_call = call->index;
106
107 }
108}
109
110/**
111 * Notify UI when registration status has changed.
112 */
Benny Prijono3cbcca62006-03-02 21:19:55 +0000113void pjsua_ui_on_reg_state(int acc_index)
Benny Prijono8df5b022006-03-01 19:31:18 +0000114{
Benny Prijono3cbcca62006-03-02 21:19:55 +0000115 PJ_UNUSED_ARG(acc_index);
Benny Prijono8df5b022006-03-01 19:31:18 +0000116
117 // Log already written.
118}
119
120
Benny Prijono3cbcca62006-03-02 21:19:55 +0000121/**
122 * Incoming IM message (i.e. MESSAGE request)!
123 */
124void pjsua_ui_on_pager(int call_index, const pj_str_t *from,
125 const pj_str_t *to, const pj_str_t *text)
126{
127 /* Note: call index may be -1 */
128 PJ_UNUSED_ARG(call_index);
129 PJ_UNUSED_ARG(to);
130
131 PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s",
132 (int)from->slen, from->ptr,
133 (int)text->slen, text->ptr));
134}
135
136
137/**
138 * Typing indication
139 */
140void pjsua_ui_on_typing(int call_index, const pj_str_t *from,
141 const pj_str_t *to, pj_bool_t is_typing)
142{
143 PJ_UNUSED_ARG(call_index);
144 PJ_UNUSED_ARG(to);
145
146 PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
147 (int)from->slen, from->ptr,
148 (is_typing?"is typing..":"has stopped typing")));
149}
150
151
Benny Prijono8df5b022006-03-01 19:31:18 +0000152/*
153 * Print buddy list.
154 */
155static void print_buddy_list(void)
156{
157 int i;
158
159 puts("Buddy list:");
160
161 if (pjsua.buddy_cnt == 0)
162 puts(" -none-");
163 else {
164 for (i=0; i<pjsua.buddy_cnt; ++i) {
165 const char *status;
166
167 if (pjsua.buddies[i].sub == NULL ||
168 pjsua.buddies[i].status.info_cnt==0)
169 {
170 status = " ? ";
171 }
172 else if (pjsua.buddies[i].status.info[0].basic_open)
173 status = " Online";
174 else
175 status = "Offline";
176
177 printf(" [%2d] <%s> %s\n",
178 i+1, status, pjsua.buddies[i].uri.ptr);
179 }
180 }
181 puts("");
182}
183
184
185/*
186 * Print account status.
187 */
188static void print_acc_status(int acc_index)
189{
190 char reg_status[128];
191
192 if (pjsua.acc[acc_index].regc == NULL) {
193 pj_ansi_strcpy(reg_status, " -not registered to server-");
194
195 } else if (pjsua.acc[acc_index].reg_last_err != PJ_SUCCESS) {
196 pj_strerror(pjsua.acc[acc_index].reg_last_err, reg_status, sizeof(reg_status));
197
198 } else if (pjsua.acc[acc_index].reg_last_code>=200 &&
199 pjsua.acc[acc_index].reg_last_code<=699) {
200
201 pjsip_regc_info info;
202
203 pjsip_regc_get_info(pjsua.acc[acc_index].regc, &info);
204
205 pj_snprintf(reg_status, sizeof(reg_status),
206 "%s (%.*s;expires=%d)",
207 pjsip_get_status_text(pjsua.acc[acc_index].reg_last_code)->ptr,
208 (int)info.client_uri.slen,
209 info.client_uri.ptr,
210 info.next_reg);
211
212 } else {
213 pj_sprintf(reg_status, "in progress (%d)",
214 pjsua.acc[acc_index].reg_last_code);
215 }
216
217 printf("[%2d] Registration status: %s\n", acc_index, reg_status);
218 printf(" Online status: %s\n",
219 (pjsua.acc[acc_index].online_status ? "Online" : "Invisible"));
220}
221
222/*
223 * Show a bit of help.
224 */
225static void keystroke_help(void)
226{
227 int i;
228
229 printf(">>>>\n");
230
231 for (i=0; i<pjsua.acc_cnt; ++i)
232 print_acc_status(i);
233
234 print_buddy_list();
235
236 //puts("Commands:");
237 puts("+=============================================================================+");
238 puts("| Call Commands: | IM & Presence: | Misc: |");
239 puts("| | | |");
240 puts("| m Make new call | i Send IM | o Send OPTIONS |");
241 puts("| M Make multiple calls | s Subscribe presence | rr (Re-)register |");
242 puts("| a Answer call | u Unsubscribe presence | ru Unregister |");
Benny Prijonof090a832006-03-01 20:47:16 +0000243 puts("| h Hangup call (ha=all) | t ToGgle Online status | |");
244 puts("| H Hold call | | |");
Benny Prijono8df5b022006-03-01 19:31:18 +0000245 puts("| v re-inVite (release hold) +--------------------------+-------------------+");
246 puts("| ] Select next dialog | Conference Command | |");
Benny Prijonof090a832006-03-01 20:47:16 +0000247 puts("| [ Select previous dialog | cl List ports | d Dump status |");
248 puts("| x Xfer call | cc Connect port | dd Dump detailed |");
249 puts("| # Send DTMF string | cd Disconnect port | dc Dump config |");
Benny Prijono8df5b022006-03-01 19:31:18 +0000250 puts("+------------------------------+--------------------------+-------------------+");
251 puts("| q QUIT |");
252 puts("+=============================================================================+");
253}
254
255
256/*
257 * Input simple string
258 */
259static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
260{
261 char *p;
262
263 printf("%s (empty to cancel): ", title); fflush(stdout);
264 fgets(buf, len, stdin);
265
266 /* Remove trailing newlines. */
267 for (p=buf; ; ++p) {
268 if (*p=='\r' || *p=='\n') *p='\0';
269 else if (!*p) break;
270 }
271
272 if (!*buf)
273 return PJ_FALSE;
274
275 return PJ_TRUE;
276}
277
278
279#define NO_NB -2
280struct input_result
281{
282 int nb_result;
283 char *uri_result;
284};
285
286
287/*
288 * Input URL.
289 */
290static void ui_input_url(const char *title, char *buf, int len,
291 struct input_result *result)
292{
293 result->nb_result = NO_NB;
294 result->uri_result = NULL;
295
296 print_buddy_list();
297
298 printf("Choices:\n"
299 " 0 For current dialog.\n"
300 " -1 All %d buddies in buddy list\n"
301 " [1 -%2d] Select from buddy list\n"
302 " URL An URL\n"
303 " <Enter> Empty input (or 'q') to cancel\n"
304 , pjsua.buddy_cnt, pjsua.buddy_cnt);
305 printf("%s: ", title);
306
307 fflush(stdout);
308 fgets(buf, len, stdin);
309 len = strlen(buf);
310
311 /* Left trim */
312 while (isspace(*buf)) {
313 ++buf;
314 --len;
315 }
316
317 /* Remove trailing newlines */
318 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
319 buf[--len] = '\0';
320
321 if (len == 0 || buf[0]=='q')
322 return;
323
324 if (isdigit(*buf) || *buf=='-') {
325
326 int i;
327
328 if (*buf=='-')
329 i = 1;
330 else
331 i = 0;
332
333 for (; i<len; ++i) {
334 if (!isdigit(buf[i])) {
335 puts("Invalid input");
336 return;
337 }
338 }
339
340 result->nb_result = atoi(buf);
341
Benny Prijono3cbcca62006-03-02 21:19:55 +0000342 if (result->nb_result >= 0 && result->nb_result <= (int)pjsua.buddy_cnt) {
343 result->nb_result;
Benny Prijono8df5b022006-03-01 19:31:18 +0000344 return;
345 }
346 if (result->nb_result == -1)
347 return;
348
349 puts("Invalid input");
350 result->nb_result = NO_NB;
351 return;
352
353 } else {
354 pj_status_t status;
355
356 if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
357 pjsua_perror(THIS_FILE, "Invalid URL", status);
358 return;
359 }
360
361 result->uri_result = buf;
362 }
363}
364
365static void conf_list(void)
366{
367 unsigned i, count;
368 pjmedia_conf_port_info info[PJSUA_MAX_CALLS];
369
370 printf("Conference ports:\n");
371
372 count = PJ_ARRAY_SIZE(info);
373 pjmedia_conf_get_ports_info(pjsua.mconf, &count, info);
374 for (i=0; i<count; ++i) {
375 char txlist[PJSUA_MAX_CALLS*4+10];
376 int j;
377 pjmedia_conf_port_info *port_info = &info[i];
378
379 txlist[0] = '\0';
380 for (j=0; j<pjsua.max_calls+PJSUA_CONF_MORE_PORTS; ++j) {
381 char s[10];
382 if (port_info->listener[j]) {
383 pj_sprintf(s, "#%d ", j);
384 pj_ansi_strcat(txlist, s);
385 }
386 }
Benny Prijono08e0d062006-03-04 14:52:44 +0000387 printf("Port #%02d[%2dKHz/%dms] %20.*s transmitting to: %s\n",
Benny Prijono8df5b022006-03-01 19:31:18 +0000388 port_info->slot,
Benny Prijono08e0d062006-03-04 14:52:44 +0000389 port_info->clock_rate/1000,
390 port_info->samples_per_frame * 1000 / port_info->clock_rate,
Benny Prijono8df5b022006-03-01 19:31:18 +0000391 (int)port_info->name.slen,
392 port_info->name.ptr,
393 txlist);
394
395 }
396 puts("");
397}
398
399
400static void ui_console_main(void)
401{
402 char menuin[10];
403 char buf[128];
Benny Prijono3cbcca62006-03-02 21:19:55 +0000404 char text[128];
Benny Prijono8df5b022006-03-01 19:31:18 +0000405 int i, count;
406 char *uri;
407 struct input_result result;
408
409 keystroke_help();
410
411 for (;;) {
412
413 printf(">>> ");
414 fflush(stdout);
415
416 fgets(menuin, sizeof(menuin), stdin);
417
418 switch (menuin[0]) {
419
420 case 'm':
421 /* Make call! : */
422 printf("(You currently have %d calls)\n", pjsua.call_cnt);
423
Benny Prijono3cbcca62006-03-02 21:19:55 +0000424 uri = NULL;
Benny Prijono8df5b022006-03-01 19:31:18 +0000425 ui_input_url("Make call", buf, sizeof(buf), &result);
426 if (result.nb_result != NO_NB) {
Benny Prijono3cbcca62006-03-02 21:19:55 +0000427
428 if (result.nb_result == -1 || result.nb_result == 0) {
Benny Prijono8df5b022006-03-01 19:31:18 +0000429 puts("You can't do that with make call!");
Benny Prijono3cbcca62006-03-02 21:19:55 +0000430 continue;
431 } else {
432 uri = pjsua.buddies[result.nb_result-1].uri.ptr;
433 }
434
435 } else if (result.uri_result) {
436 uri = result.uri_result;
437 }
Benny Prijono8df5b022006-03-01 19:31:18 +0000438
Benny Prijono3cbcca62006-03-02 21:19:55 +0000439 pjsua_make_call( current_acc, uri, NULL);
Benny Prijono8df5b022006-03-01 19:31:18 +0000440 break;
441
442 case 'M':
443 /* Make multiple calls! : */
444 printf("(You currently have %d calls)\n", pjsua.call_cnt);
445
Benny Prijonof090a832006-03-01 20:47:16 +0000446 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
447 continue;
448
449 count = atoi(menuin);
450 if (count < 1)
451 continue;
452
Benny Prijono8df5b022006-03-01 19:31:18 +0000453 ui_input_url("Make call", buf, sizeof(buf), &result);
454 if (result.nb_result != NO_NB) {
Benny Prijono3cbcca62006-03-02 21:19:55 +0000455 if (result.nb_result == -1 || result.nb_result == 0) {
Benny Prijono8df5b022006-03-01 19:31:18 +0000456 puts("You can't do that with make call!");
457 continue;
458 }
Benny Prijono3cbcca62006-03-02 21:19:55 +0000459 uri = pjsua.buddies[result.nb_result-1].uri.ptr;
Benny Prijono8df5b022006-03-01 19:31:18 +0000460 } else {
461 uri = result.uri_result;
462 }
463
Benny Prijono8df5b022006-03-01 19:31:18 +0000464 for (i=0; i<atoi(menuin); ++i) {
465 pj_status_t status;
466
467 status = pjsua_make_call(current_acc, uri, NULL);
468 if (status != PJ_SUCCESS)
469 break;
470 }
471 break;
472
Benny Prijono3cbcca62006-03-02 21:19:55 +0000473 case 'i':
474 /* Send instant messaeg */
475
476 /* i is for call index to send message, if any */
477 i = -1;
478
479 /* Make compiler happy. */
480 uri = NULL;
481
482 /* Input destination. */
483 ui_input_url("Send IM to", buf, sizeof(buf), &result);
484 if (result.nb_result != NO_NB) {
485
486 if (result.nb_result == -1) {
487 puts("You can't send broadcast IM like that!");
488 continue;
489
490 } else if (result.nb_result == 0) {
491
492 i = current_call;
493
494 } else {
495 uri = pjsua.buddies[result.nb_result-1].uri.ptr;
496 }
497
498 } else if (result.uri_result) {
499 uri = result.uri_result;
500 }
501
502
503 /* Send typing indication. */
504 if (i != -1)
505 pjsua_call_typing(i, PJ_TRUE);
506 else
507 pjsua_im_typing(current_acc, uri, PJ_TRUE);
508
509 /* Input the IM . */
510 if (!simple_input("Message", text, sizeof(text))) {
511 /*
512 * Cancelled.
513 * Send typing notification too, saying we're not typing.
514 */
515 if (i != -1)
516 pjsua_call_typing(i, PJ_FALSE);
517 else
518 pjsua_im_typing(current_acc, uri, PJ_FALSE);
519 continue;
520 }
521
522 /* Send the IM */
523 if (i != -1)
524 pjsua_call_send_im(i, text);
525 else
526 pjsua_im_send(current_acc, uri, text);
527
528 break;
529
Benny Prijono8df5b022006-03-01 19:31:18 +0000530 case 'a':
531
532 if (current_call == -1 ||
533 pjsua.calls[current_call].inv->role != PJSIP_ROLE_UAS ||
534 pjsua.calls[current_call].inv->state >= PJSIP_INV_STATE_CONNECTING)
535 {
536 puts("No pending incoming call");
537 fflush(stdout);
538 continue;
539
540 } else {
541 pj_status_t status;
542 pjsip_tx_data *tdata;
543
544 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
545 continue;
546
547 if (atoi(buf) < 100)
548 continue;
549
550 /*
551 * Must check again!
552 * Call may have been disconnected while we're waiting for
553 * keyboard input.
554 */
555 if (current_call == -1) {
556 puts("Call has been disconnected");
557 fflush(stdout);
558 continue;
559 }
560
561 status = pjsip_inv_answer(pjsua.calls[current_call].inv,
562 atoi(buf),
563 NULL, NULL, &tdata);
564 if (status == PJ_SUCCESS)
565 status = pjsip_inv_send_msg(pjsua.calls[current_call].inv,
566 tdata, NULL);
567
568 if (status != PJ_SUCCESS)
569 pjsua_perror(THIS_FILE, "Unable to create/send response",
570 status);
571 }
572
573 break;
574
575
576 case 'h':
577
578 if (current_call == -1) {
579 puts("No current call");
580 fflush(stdout);
581 continue;
582
Benny Prijonof090a832006-03-01 20:47:16 +0000583 } else if (menuin[1] == 'a') {
584
585 /* Hangup all calls */
586 pjsua_call_hangup_all();
587
Benny Prijono8df5b022006-03-01 19:31:18 +0000588 } else {
Benny Prijonof090a832006-03-01 20:47:16 +0000589
590 /* Hangup current calls */
Benny Prijono8df5b022006-03-01 19:31:18 +0000591 pjsua_call_hangup(current_call, PJSIP_SC_DECLINE);
592 }
593 break;
594
595 case ']':
596 case '[':
597 /*
598 * Cycle next/prev dialog.
599 */
600 if (menuin[0] == ']') {
601 find_next_call();
602
603 } else {
604 find_prev_call();
605 }
606
607 if (current_call != -1) {
608 char url[PJSIP_MAX_URL_SIZE];
609 int len;
610 const pjsip_uri *u;
611
612 u = pjsua.calls[current_call].inv->dlg->remote.info->uri;
613 len = pjsip_uri_print(0, u, url, sizeof(url)-1);
614 if (len < 1) {
615 pj_ansi_strcpy(url, "<uri is too long>");
616 } else {
617 url[len] = '\0';
618 }
619
620 PJ_LOG(3,(THIS_FILE,"Current dialog: %s", url));
621
622 } else {
623 PJ_LOG(3,(THIS_FILE,"No current dialog"));
624 }
625 break;
626
627 case 'H':
628 /*
629 * Hold call.
630 */
631 if (current_call != -1) {
632
633 pjsua_call_set_hold(current_call);
634
635 } else {
636 PJ_LOG(3,(THIS_FILE, "No current call"));
637 }
638 break;
639
640 case 'v':
641 /*
642 * Send re-INVITE (to release hold, etc).
643 */
644 if (current_call != -1) {
645
646 pjsua_call_reinvite(current_call);
647
648 } else {
649 PJ_LOG(3,(THIS_FILE, "No current call"));
650 }
651 break;
652
653 case 'x':
654 /*
655 * Transfer call.
656 */
657 if (current_call == -1) {
658
659 PJ_LOG(3,(THIS_FILE, "No current call"));
660
661 } else {
662 int call = current_call;
663
664 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
665
666 /* Check if call is still there. */
667
668 if (call != current_call) {
669 puts("Call has been disconnected");
670 continue;
671 }
672
673 if (result.nb_result != NO_NB) {
Benny Prijono3cbcca62006-03-02 21:19:55 +0000674 if (result.nb_result == -1 || result.nb_result == 0)
Benny Prijono8df5b022006-03-01 19:31:18 +0000675 puts("You can't do that with transfer call!");
676 else
677 pjsua_call_xfer( current_call,
Benny Prijono3cbcca62006-03-02 21:19:55 +0000678 pjsua.buddies[result.nb_result-1].uri.ptr);
Benny Prijono8df5b022006-03-01 19:31:18 +0000679
680 } else if (result.uri_result) {
681 pjsua_call_xfer( current_call, result.uri_result);
682 }
683 }
684 break;
685
686 case '#':
687 /*
688 * Send DTMF strings.
689 */
690 if (current_call == -1) {
691
692 PJ_LOG(3,(THIS_FILE, "No current call"));
693
694 } else if (pjsua.calls[current_call].session == NULL) {
695
696 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
697
698 } else {
699 pj_str_t digits;
700 int call = current_call;
701 pj_status_t status;
702
703 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
704 sizeof(buf)))
705 {
706 break;
707 }
708
709 if (call != current_call) {
710 puts("Call has been disconnected");
711 continue;
712 }
713
714 digits = pj_str(buf);
715 status = pjmedia_session_dial_dtmf(pjsua.calls[current_call].session, 0,
716 &digits);
717 if (status != PJ_SUCCESS) {
718 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
719 } else {
720 puts("DTMF digits enqueued for transmission");
721 }
722 }
723 break;
724
725 case 's':
726 case 'u':
727 /*
728 * Subscribe/unsubscribe presence.
729 */
730 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
731 if (result.nb_result != NO_NB) {
732 if (result.nb_result == -1) {
733 int i;
734 for (i=0; i<pjsua.buddy_cnt; ++i)
735 pjsua.buddies[i].monitor = (menuin[0]=='s');
Benny Prijono3cbcca62006-03-02 21:19:55 +0000736 } else if (result.nb_result == 0) {
737 puts("Sorry, can only subscribe to buddy's presence, "
738 "not from existing call");
Benny Prijono8df5b022006-03-01 19:31:18 +0000739 } else {
Benny Prijono3cbcca62006-03-02 21:19:55 +0000740 pjsua.buddies[result.nb_result-1].monitor = (menuin[0]=='s');
Benny Prijono8df5b022006-03-01 19:31:18 +0000741 }
742
743 pjsua_pres_refresh(current_acc);
744
745 } else if (result.uri_result) {
746 puts("Sorry, can only subscribe to buddy's presence, "
747 "not arbitrary URL (for now)");
748 }
749
750 break;
751
752 case 'r':
753 switch (menuin[1]) {
754 case 'r':
755 /*
756 * Re-Register.
757 */
758 pjsua_regc_update(current_acc, PJ_TRUE);
759 break;
760 case 'u':
761 /*
762 * Unregister
763 */
764 pjsua_regc_update(current_acc, PJ_FALSE);
765 break;
766 }
767 break;
768
769 case 't':
770 pjsua.acc[current_acc].online_status =
771 !pjsua.acc[current_acc].online_status;
Benny Prijono3cbcca62006-03-02 21:19:55 +0000772 printf("Setting %s online status to %s\n",
773 pjsua.acc[current_acc].local_uri.ptr,
774 (pjsua.acc[current_acc].online_status?"online":"offline"));
Benny Prijono8df5b022006-03-01 19:31:18 +0000775 pjsua_pres_refresh(current_acc);
776 break;
777
778 case 'c':
779 switch (menuin[1]) {
780 case 'l':
781 conf_list();
782 break;
783 case 'c':
784 case 'd':
785 {
786 char src_port[10], dst_port[10];
787 pj_status_t status;
788 const char *src_title, *dst_title;
789
790 conf_list();
791
792 src_title = (menuin[1]=='c'?
793 "Connect src port #":
794 "Disconnect src port #");
795 dst_title = (menuin[1]=='c'?
796 "To dst port #":
797 "From dst port #");
798
799 if (!simple_input(src_title, src_port, sizeof(src_port)))
800 break;
801
802 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
803 break;
804
805 if (menuin[1]=='c') {
806 status = pjmedia_conf_connect_port(pjsua.mconf,
807 atoi(src_port),
808 atoi(dst_port));
809 } else {
810 status = pjmedia_conf_disconnect_port(pjsua.mconf,
811 atoi(src_port),
812 atoi(dst_port));
813 }
814 if (status == PJ_SUCCESS) {
815 puts("Success");
816 } else {
817 puts("ERROR!!");
818 }
819 }
820 break;
821 }
822 break;
823
824 case 'd':
825 if (menuin[1] == 'c') {
826 char settings[2000];
827 int len;
828
829 len = pjsua_dump_settings(settings, sizeof(settings));
830 if (len < 1)
831 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
832 else
833 PJ_LOG(3,(THIS_FILE,
834 "Dumping configuration (%d bytes):\n%s\n",
835 len, settings));
836 } else {
Benny Prijonof090a832006-03-01 20:47:16 +0000837 pjsua_dump(menuin[1]=='d');
Benny Prijono8df5b022006-03-01 19:31:18 +0000838 }
839 break;
840
841 case 'q':
842 goto on_exit;
843
844 default:
Benny Prijono3cbcca62006-03-02 21:19:55 +0000845 if (menuin[0] != '\n' && menuin[0] != '\r') {
846 printf("Invalid input %s", menuin);
847 }
Benny Prijono8df5b022006-03-01 19:31:18 +0000848 keystroke_help();
849 break;
850 }
851 }
852
853on_exit:
854 ;
855}
856
857
858/*****************************************************************************
859 * This is a very simple PJSIP module, whose sole purpose is to display
860 * incoming and outgoing messages to log. This module will have priority
861 * higher than transport layer, which means:
862 *
863 * - incoming messages will come to this module first before reaching
864 * transaction layer.
865 *
866 * - outgoing messages will come to this module last, after the message
867 * has been 'printed' to contiguous buffer by transport layer and
868 * appropriate transport instance has been decided for this message.
869 *
870 */
871
872/* Notification on incoming messages */
873static pj_bool_t console_on_rx_msg(pjsip_rx_data *rdata)
874{
875 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
876 "%s\n"
877 "--end msg--",
878 rdata->msg_info.len,
879 pjsip_rx_data_get_info(rdata),
880 rdata->pkt_info.src_name,
881 rdata->pkt_info.src_port,
882 rdata->msg_info.msg_buf));
883
884 /* Always return false, otherwise messages will not get processed! */
885 return PJ_FALSE;
886}
887
888/* Notification on outgoing messages */
889static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata)
890{
891
892 /* Important note:
893 * tp_info field is only valid after outgoing messages has passed
894 * transport layer. So don't try to access tp_info when the module
895 * has lower priority than transport layer.
896 */
897
898 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
899 "%s\n"
900 "--end msg--",
901 (tdata->buf.cur - tdata->buf.start),
902 pjsip_tx_data_get_info(tdata),
903 tdata->tp_info.dst_name,
904 tdata->tp_info.dst_port,
905 tdata->buf.start));
906
907 /* Always return success, otherwise message will not get sent! */
908 return PJ_SUCCESS;
909}
910
911/* The module instance. */
912static pjsip_module console_msg_logger =
913{
914 NULL, NULL, /* prev, next. */
915 { "mod-pjsua-log", 13 }, /* Name. */
916 -1, /* Id */
917 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
918 NULL, /* load() */
919 NULL, /* start() */
920 NULL, /* stop() */
921 NULL, /* unload() */
922 &console_on_rx_msg, /* on_rx_request() */
923 &console_on_rx_msg, /* on_rx_response() */
924 &console_on_tx_msg, /* on_tx_request. */
925 &console_on_tx_msg, /* on_tx_response() */
926 NULL, /* on_tsx_state() */
927
928};
929
930
931
932/*****************************************************************************
933 * Console application custom logging:
934 */
935
936
937static FILE *log_file;
938
939
940static void app_log_writer(int level, const char *buffer, int len)
941{
942 /* Write to both stdout and file. */
943
944 if (level <= pjsua.app_log_level)
945 pj_log_write(level, buffer, len);
946
947 if (log_file) {
948 fwrite(buffer, len, 1, log_file);
949 fflush(log_file);
950 }
951}
952
953
Benny Prijono858b2fe2006-03-01 22:26:38 +0000954pj_status_t app_logging_init(void)
Benny Prijono8df5b022006-03-01 19:31:18 +0000955{
956 /* Redirect log function to ours */
957
958 pj_log_set_log_func( &app_log_writer );
959
960 /* If output log file is desired, create the file: */
961
Benny Prijono858b2fe2006-03-01 22:26:38 +0000962 if (pjsua.log_filename) {
Benny Prijono8df5b022006-03-01 19:31:18 +0000963 log_file = fopen(pjsua.log_filename, "wt");
Benny Prijono858b2fe2006-03-01 22:26:38 +0000964 if (log_file == NULL) {
965 PJ_LOG(1,(THIS_FILE, "Unable to open log file %s",
966 pjsua.log_filename));
967 return -1;
968 }
969 }
970
971 return PJ_SUCCESS;
Benny Prijono8df5b022006-03-01 19:31:18 +0000972}
973
974
975void app_logging_shutdown(void)
976{
977 /* Close logging file, if any: */
978
979 if (log_file) {
980 fclose(log_file);
981 log_file = NULL;
982 }
983}
984
985/*****************************************************************************
986 * Error display:
987 */
988
989/*
990 * Display error message for the specified error code.
991 */
992void pjsua_perror(const char *sender, const char *title,
993 pj_status_t status)
994{
995 char errmsg[PJ_ERR_MSG_SIZE];
996
997 pj_strerror(status, errmsg, sizeof(errmsg));
998
999 PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
1000}
1001
1002
1003
1004
1005/*****************************************************************************
1006 * main():
1007 */
1008int main(int argc, char *argv[])
1009{
1010
1011 /* Init default settings. */
1012 pjsua_default();
1013
1014
1015 /* Initialize pjsua (to create pool etc).
1016 */
1017 if (pjsua_init() != PJ_SUCCESS)
1018 return 1;
1019
1020
1021 /* Parse command line arguments: */
1022 if (pjsua_parse_args(argc, argv) != PJ_SUCCESS)
1023 return 1;
1024
1025
1026 /* Init logging: */
Benny Prijono858b2fe2006-03-01 22:26:38 +00001027 if (app_logging_init() != PJ_SUCCESS)
1028 return 1;
Benny Prijono8df5b022006-03-01 19:31:18 +00001029
1030
1031 /* Register message logger to print incoming and outgoing
1032 * messages.
1033 */
1034 pjsip_endpt_register_module(pjsua.endpt, &console_msg_logger);
1035
1036
1037 /* Start pjsua! */
1038 if (pjsua_start() != PJ_SUCCESS) {
1039
1040 pjsua_destroy();
1041 return 1;
1042 }
1043
1044
1045 /* Sleep for a while, let any messages get printed to console: */
1046 pj_thread_sleep(500);
1047
1048
1049 /* Start UI console main loop: */
1050 ui_console_main();
1051
1052
1053 /* Destroy pjsua: */
1054 pjsua_destroy();
1055
1056
1057 /* Close logging: */
1058 app_logging_shutdown();
1059
1060
1061 /* Exit... */
1062
1063 return 0;
1064}
1065