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