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