blob: 08f0a380fe26826d6c69e3bdad166ad3d8f7b882 [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 }
387 printf("Port #%02d %20.*s transmitting to: %s\n",
388 port_info->slot,
389 (int)port_info->name.slen,
390 port_info->name.ptr,
391 txlist);
392
393 }
394 puts("");
395}
396
397
398static void ui_console_main(void)
399{
400 char menuin[10];
401 char buf[128];
Benny Prijono3cbcca62006-03-02 21:19:55 +0000402 char text[128];
Benny Prijono8df5b022006-03-01 19:31:18 +0000403 int i, count;
404 char *uri;
405 struct input_result result;
406
407 keystroke_help();
408
409 for (;;) {
410
411 printf(">>> ");
412 fflush(stdout);
413
414 fgets(menuin, sizeof(menuin), stdin);
415
416 switch (menuin[0]) {
417
418 case 'm':
419 /* Make call! : */
420 printf("(You currently have %d calls)\n", pjsua.call_cnt);
421
Benny Prijono3cbcca62006-03-02 21:19:55 +0000422 uri = NULL;
Benny Prijono8df5b022006-03-01 19:31:18 +0000423 ui_input_url("Make call", buf, sizeof(buf), &result);
424 if (result.nb_result != NO_NB) {
Benny Prijono3cbcca62006-03-02 21:19:55 +0000425
426 if (result.nb_result == -1 || result.nb_result == 0) {
Benny Prijono8df5b022006-03-01 19:31:18 +0000427 puts("You can't do that with make call!");
Benny Prijono3cbcca62006-03-02 21:19:55 +0000428 continue;
429 } else {
430 uri = pjsua.buddies[result.nb_result-1].uri.ptr;
431 }
432
433 } else if (result.uri_result) {
434 uri = result.uri_result;
435 }
Benny Prijono8df5b022006-03-01 19:31:18 +0000436
Benny Prijono3cbcca62006-03-02 21:19:55 +0000437 pjsua_make_call( current_acc, uri, NULL);
Benny Prijono8df5b022006-03-01 19:31:18 +0000438 break;
439
440 case 'M':
441 /* Make multiple calls! : */
442 printf("(You currently have %d calls)\n", pjsua.call_cnt);
443
Benny Prijonof090a832006-03-01 20:47:16 +0000444 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
445 continue;
446
447 count = atoi(menuin);
448 if (count < 1)
449 continue;
450
Benny Prijono8df5b022006-03-01 19:31:18 +0000451 ui_input_url("Make call", buf, sizeof(buf), &result);
452 if (result.nb_result != NO_NB) {
Benny Prijono3cbcca62006-03-02 21:19:55 +0000453 if (result.nb_result == -1 || result.nb_result == 0) {
Benny Prijono8df5b022006-03-01 19:31:18 +0000454 puts("You can't do that with make call!");
455 continue;
456 }
Benny Prijono3cbcca62006-03-02 21:19:55 +0000457 uri = pjsua.buddies[result.nb_result-1].uri.ptr;
Benny Prijono8df5b022006-03-01 19:31:18 +0000458 } else {
459 uri = result.uri_result;
460 }
461
Benny Prijono8df5b022006-03-01 19:31:18 +0000462 for (i=0; i<atoi(menuin); ++i) {
463 pj_status_t status;
464
465 status = pjsua_make_call(current_acc, uri, NULL);
466 if (status != PJ_SUCCESS)
467 break;
468 }
469 break;
470
Benny Prijono3cbcca62006-03-02 21:19:55 +0000471 case 'i':
472 /* Send instant messaeg */
473
474 /* i is for call index to send message, if any */
475 i = -1;
476
477 /* Make compiler happy. */
478 uri = NULL;
479
480 /* Input destination. */
481 ui_input_url("Send IM to", buf, sizeof(buf), &result);
482 if (result.nb_result != NO_NB) {
483
484 if (result.nb_result == -1) {
485 puts("You can't send broadcast IM like that!");
486 continue;
487
488 } else if (result.nb_result == 0) {
489
490 i = current_call;
491
492 } else {
493 uri = pjsua.buddies[result.nb_result-1].uri.ptr;
494 }
495
496 } else if (result.uri_result) {
497 uri = result.uri_result;
498 }
499
500
501 /* Send typing indication. */
502 if (i != -1)
503 pjsua_call_typing(i, PJ_TRUE);
504 else
505 pjsua_im_typing(current_acc, uri, PJ_TRUE);
506
507 /* Input the IM . */
508 if (!simple_input("Message", text, sizeof(text))) {
509 /*
510 * Cancelled.
511 * Send typing notification too, saying we're not typing.
512 */
513 if (i != -1)
514 pjsua_call_typing(i, PJ_FALSE);
515 else
516 pjsua_im_typing(current_acc, uri, PJ_FALSE);
517 continue;
518 }
519
520 /* Send the IM */
521 if (i != -1)
522 pjsua_call_send_im(i, text);
523 else
524 pjsua_im_send(current_acc, uri, text);
525
526 break;
527
Benny Prijono8df5b022006-03-01 19:31:18 +0000528 case 'a':
529
530 if (current_call == -1 ||
531 pjsua.calls[current_call].inv->role != PJSIP_ROLE_UAS ||
532 pjsua.calls[current_call].inv->state >= PJSIP_INV_STATE_CONNECTING)
533 {
534 puts("No pending incoming call");
535 fflush(stdout);
536 continue;
537
538 } else {
539 pj_status_t status;
540 pjsip_tx_data *tdata;
541
542 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
543 continue;
544
545 if (atoi(buf) < 100)
546 continue;
547
548 /*
549 * Must check again!
550 * Call may have been disconnected while we're waiting for
551 * keyboard input.
552 */
553 if (current_call == -1) {
554 puts("Call has been disconnected");
555 fflush(stdout);
556 continue;
557 }
558
559 status = pjsip_inv_answer(pjsua.calls[current_call].inv,
560 atoi(buf),
561 NULL, NULL, &tdata);
562 if (status == PJ_SUCCESS)
563 status = pjsip_inv_send_msg(pjsua.calls[current_call].inv,
564 tdata, NULL);
565
566 if (status != PJ_SUCCESS)
567 pjsua_perror(THIS_FILE, "Unable to create/send response",
568 status);
569 }
570
571 break;
572
573
574 case 'h':
575
576 if (current_call == -1) {
577 puts("No current call");
578 fflush(stdout);
579 continue;
580
Benny Prijonof090a832006-03-01 20:47:16 +0000581 } else if (menuin[1] == 'a') {
582
583 /* Hangup all calls */
584 pjsua_call_hangup_all();
585
Benny Prijono8df5b022006-03-01 19:31:18 +0000586 } else {
Benny Prijonof090a832006-03-01 20:47:16 +0000587
588 /* Hangup current calls */
Benny Prijono8df5b022006-03-01 19:31:18 +0000589 pjsua_call_hangup(current_call, PJSIP_SC_DECLINE);
590 }
591 break;
592
593 case ']':
594 case '[':
595 /*
596 * Cycle next/prev dialog.
597 */
598 if (menuin[0] == ']') {
599 find_next_call();
600
601 } else {
602 find_prev_call();
603 }
604
605 if (current_call != -1) {
606 char url[PJSIP_MAX_URL_SIZE];
607 int len;
608 const pjsip_uri *u;
609
610 u = pjsua.calls[current_call].inv->dlg->remote.info->uri;
611 len = pjsip_uri_print(0, u, url, sizeof(url)-1);
612 if (len < 1) {
613 pj_ansi_strcpy(url, "<uri is too long>");
614 } else {
615 url[len] = '\0';
616 }
617
618 PJ_LOG(3,(THIS_FILE,"Current dialog: %s", url));
619
620 } else {
621 PJ_LOG(3,(THIS_FILE,"No current dialog"));
622 }
623 break;
624
625 case 'H':
626 /*
627 * Hold call.
628 */
629 if (current_call != -1) {
630
631 pjsua_call_set_hold(current_call);
632
633 } else {
634 PJ_LOG(3,(THIS_FILE, "No current call"));
635 }
636 break;
637
638 case 'v':
639 /*
640 * Send re-INVITE (to release hold, etc).
641 */
642 if (current_call != -1) {
643
644 pjsua_call_reinvite(current_call);
645
646 } else {
647 PJ_LOG(3,(THIS_FILE, "No current call"));
648 }
649 break;
650
651 case 'x':
652 /*
653 * Transfer call.
654 */
655 if (current_call == -1) {
656
657 PJ_LOG(3,(THIS_FILE, "No current call"));
658
659 } else {
660 int call = current_call;
661
662 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
663
664 /* Check if call is still there. */
665
666 if (call != current_call) {
667 puts("Call has been disconnected");
668 continue;
669 }
670
671 if (result.nb_result != NO_NB) {
Benny Prijono3cbcca62006-03-02 21:19:55 +0000672 if (result.nb_result == -1 || result.nb_result == 0)
Benny Prijono8df5b022006-03-01 19:31:18 +0000673 puts("You can't do that with transfer call!");
674 else
675 pjsua_call_xfer( current_call,
Benny Prijono3cbcca62006-03-02 21:19:55 +0000676 pjsua.buddies[result.nb_result-1].uri.ptr);
Benny Prijono8df5b022006-03-01 19:31:18 +0000677
678 } else if (result.uri_result) {
679 pjsua_call_xfer( current_call, result.uri_result);
680 }
681 }
682 break;
683
684 case '#':
685 /*
686 * Send DTMF strings.
687 */
688 if (current_call == -1) {
689
690 PJ_LOG(3,(THIS_FILE, "No current call"));
691
692 } else if (pjsua.calls[current_call].session == NULL) {
693
694 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
695
696 } else {
697 pj_str_t digits;
698 int call = current_call;
699 pj_status_t status;
700
701 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
702 sizeof(buf)))
703 {
704 break;
705 }
706
707 if (call != current_call) {
708 puts("Call has been disconnected");
709 continue;
710 }
711
712 digits = pj_str(buf);
713 status = pjmedia_session_dial_dtmf(pjsua.calls[current_call].session, 0,
714 &digits);
715 if (status != PJ_SUCCESS) {
716 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
717 } else {
718 puts("DTMF digits enqueued for transmission");
719 }
720 }
721 break;
722
723 case 's':
724 case 'u':
725 /*
726 * Subscribe/unsubscribe presence.
727 */
728 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
729 if (result.nb_result != NO_NB) {
730 if (result.nb_result == -1) {
731 int i;
732 for (i=0; i<pjsua.buddy_cnt; ++i)
733 pjsua.buddies[i].monitor = (menuin[0]=='s');
Benny Prijono3cbcca62006-03-02 21:19:55 +0000734 } else if (result.nb_result == 0) {
735 puts("Sorry, can only subscribe to buddy's presence, "
736 "not from existing call");
Benny Prijono8df5b022006-03-01 19:31:18 +0000737 } else {
Benny Prijono3cbcca62006-03-02 21:19:55 +0000738 pjsua.buddies[result.nb_result-1].monitor = (menuin[0]=='s');
Benny Prijono8df5b022006-03-01 19:31:18 +0000739 }
740
741 pjsua_pres_refresh(current_acc);
742
743 } else if (result.uri_result) {
744 puts("Sorry, can only subscribe to buddy's presence, "
745 "not arbitrary URL (for now)");
746 }
747
748 break;
749
750 case 'r':
751 switch (menuin[1]) {
752 case 'r':
753 /*
754 * Re-Register.
755 */
756 pjsua_regc_update(current_acc, PJ_TRUE);
757 break;
758 case 'u':
759 /*
760 * Unregister
761 */
762 pjsua_regc_update(current_acc, PJ_FALSE);
763 break;
764 }
765 break;
766
767 case 't':
768 pjsua.acc[current_acc].online_status =
769 !pjsua.acc[current_acc].online_status;
Benny Prijono3cbcca62006-03-02 21:19:55 +0000770 printf("Setting %s online status to %s\n",
771 pjsua.acc[current_acc].local_uri.ptr,
772 (pjsua.acc[current_acc].online_status?"online":"offline"));
Benny Prijono8df5b022006-03-01 19:31:18 +0000773 pjsua_pres_refresh(current_acc);
774 break;
775
776 case 'c':
777 switch (menuin[1]) {
778 case 'l':
779 conf_list();
780 break;
781 case 'c':
782 case 'd':
783 {
784 char src_port[10], dst_port[10];
785 pj_status_t status;
786 const char *src_title, *dst_title;
787
788 conf_list();
789
790 src_title = (menuin[1]=='c'?
791 "Connect src port #":
792 "Disconnect src port #");
793 dst_title = (menuin[1]=='c'?
794 "To dst port #":
795 "From dst port #");
796
797 if (!simple_input(src_title, src_port, sizeof(src_port)))
798 break;
799
800 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
801 break;
802
803 if (menuin[1]=='c') {
804 status = pjmedia_conf_connect_port(pjsua.mconf,
805 atoi(src_port),
806 atoi(dst_port));
807 } else {
808 status = pjmedia_conf_disconnect_port(pjsua.mconf,
809 atoi(src_port),
810 atoi(dst_port));
811 }
812 if (status == PJ_SUCCESS) {
813 puts("Success");
814 } else {
815 puts("ERROR!!");
816 }
817 }
818 break;
819 }
820 break;
821
822 case 'd':
823 if (menuin[1] == 'c') {
824 char settings[2000];
825 int len;
826
827 len = pjsua_dump_settings(settings, sizeof(settings));
828 if (len < 1)
829 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
830 else
831 PJ_LOG(3,(THIS_FILE,
832 "Dumping configuration (%d bytes):\n%s\n",
833 len, settings));
834 } else {
Benny Prijonof090a832006-03-01 20:47:16 +0000835 pjsua_dump(menuin[1]=='d');
Benny Prijono8df5b022006-03-01 19:31:18 +0000836 }
837 break;
838
839 case 'q':
840 goto on_exit;
841
842 default:
Benny Prijono3cbcca62006-03-02 21:19:55 +0000843 if (menuin[0] != '\n' && menuin[0] != '\r') {
844 printf("Invalid input %s", menuin);
845 }
Benny Prijono8df5b022006-03-01 19:31:18 +0000846 keystroke_help();
847 break;
848 }
849 }
850
851on_exit:
852 ;
853}
854
855
856/*****************************************************************************
857 * This is a very simple PJSIP module, whose sole purpose is to display
858 * incoming and outgoing messages to log. This module will have priority
859 * higher than transport layer, which means:
860 *
861 * - incoming messages will come to this module first before reaching
862 * transaction layer.
863 *
864 * - outgoing messages will come to this module last, after the message
865 * has been 'printed' to contiguous buffer by transport layer and
866 * appropriate transport instance has been decided for this message.
867 *
868 */
869
870/* Notification on incoming messages */
871static pj_bool_t console_on_rx_msg(pjsip_rx_data *rdata)
872{
873 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
874 "%s\n"
875 "--end msg--",
876 rdata->msg_info.len,
877 pjsip_rx_data_get_info(rdata),
878 rdata->pkt_info.src_name,
879 rdata->pkt_info.src_port,
880 rdata->msg_info.msg_buf));
881
882 /* Always return false, otherwise messages will not get processed! */
883 return PJ_FALSE;
884}
885
886/* Notification on outgoing messages */
887static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata)
888{
889
890 /* Important note:
891 * tp_info field is only valid after outgoing messages has passed
892 * transport layer. So don't try to access tp_info when the module
893 * has lower priority than transport layer.
894 */
895
896 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
897 "%s\n"
898 "--end msg--",
899 (tdata->buf.cur - tdata->buf.start),
900 pjsip_tx_data_get_info(tdata),
901 tdata->tp_info.dst_name,
902 tdata->tp_info.dst_port,
903 tdata->buf.start));
904
905 /* Always return success, otherwise message will not get sent! */
906 return PJ_SUCCESS;
907}
908
909/* The module instance. */
910static pjsip_module console_msg_logger =
911{
912 NULL, NULL, /* prev, next. */
913 { "mod-pjsua-log", 13 }, /* Name. */
914 -1, /* Id */
915 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
916 NULL, /* load() */
917 NULL, /* start() */
918 NULL, /* stop() */
919 NULL, /* unload() */
920 &console_on_rx_msg, /* on_rx_request() */
921 &console_on_rx_msg, /* on_rx_response() */
922 &console_on_tx_msg, /* on_tx_request. */
923 &console_on_tx_msg, /* on_tx_response() */
924 NULL, /* on_tsx_state() */
925
926};
927
928
929
930/*****************************************************************************
931 * Console application custom logging:
932 */
933
934
935static FILE *log_file;
936
937
938static void app_log_writer(int level, const char *buffer, int len)
939{
940 /* Write to both stdout and file. */
941
942 if (level <= pjsua.app_log_level)
943 pj_log_write(level, buffer, len);
944
945 if (log_file) {
946 fwrite(buffer, len, 1, log_file);
947 fflush(log_file);
948 }
949}
950
951
Benny Prijono858b2fe2006-03-01 22:26:38 +0000952pj_status_t app_logging_init(void)
Benny Prijono8df5b022006-03-01 19:31:18 +0000953{
954 /* Redirect log function to ours */
955
956 pj_log_set_log_func( &app_log_writer );
957
958 /* If output log file is desired, create the file: */
959
Benny Prijono858b2fe2006-03-01 22:26:38 +0000960 if (pjsua.log_filename) {
Benny Prijono8df5b022006-03-01 19:31:18 +0000961 log_file = fopen(pjsua.log_filename, "wt");
Benny Prijono858b2fe2006-03-01 22:26:38 +0000962 if (log_file == NULL) {
963 PJ_LOG(1,(THIS_FILE, "Unable to open log file %s",
964 pjsua.log_filename));
965 return -1;
966 }
967 }
968
969 return PJ_SUCCESS;
Benny Prijono8df5b022006-03-01 19:31:18 +0000970}
971
972
973void app_logging_shutdown(void)
974{
975 /* Close logging file, if any: */
976
977 if (log_file) {
978 fclose(log_file);
979 log_file = NULL;
980 }
981}
982
983/*****************************************************************************
984 * Error display:
985 */
986
987/*
988 * Display error message for the specified error code.
989 */
990void pjsua_perror(const char *sender, const char *title,
991 pj_status_t status)
992{
993 char errmsg[PJ_ERR_MSG_SIZE];
994
995 pj_strerror(status, errmsg, sizeof(errmsg));
996
997 PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
998}
999
1000
1001
1002
1003/*****************************************************************************
1004 * main():
1005 */
1006int main(int argc, char *argv[])
1007{
1008
1009 /* Init default settings. */
1010 pjsua_default();
1011
1012
1013 /* Initialize pjsua (to create pool etc).
1014 */
1015 if (pjsua_init() != PJ_SUCCESS)
1016 return 1;
1017
1018
1019 /* Parse command line arguments: */
1020 if (pjsua_parse_args(argc, argv) != PJ_SUCCESS)
1021 return 1;
1022
1023
1024 /* Init logging: */
Benny Prijono858b2fe2006-03-01 22:26:38 +00001025 if (app_logging_init() != PJ_SUCCESS)
1026 return 1;
Benny Prijono8df5b022006-03-01 19:31:18 +00001027
1028
1029 /* Register message logger to print incoming and outgoing
1030 * messages.
1031 */
1032 pjsip_endpt_register_module(pjsua.endpt, &console_msg_logger);
1033
1034
1035 /* Start pjsua! */
1036 if (pjsua_start() != PJ_SUCCESS) {
1037
1038 pjsua_destroy();
1039 return 1;
1040 }
1041
1042
1043 /* Sleep for a while, let any messages get printed to console: */
1044 pj_thread_sleep(500);
1045
1046
1047 /* Start UI console main loop: */
1048 ui_console_main();
1049
1050
1051 /* Destroy pjsua: */
1052 pjsua_destroy();
1053
1054
1055 /* Close logging: */
1056 app_logging_shutdown();
1057
1058
1059 /* Exit... */
1060
1061 return 0;
1062}
1063