]> sjero.net Git - linphone/blob - console/linphonec.c
Merge branch 'master' into dev_multicall
[linphone] / console / linphonec.c
1 /****************************************************************************
2  *
3  *  $Id: linphonec.c,v 1.57 2007/11/14 13:40:27 smorlat Exp $
4  *
5  *  Copyright (C) 2006  Sandro Santilli <strk@keybit.net>
6  *  Copyright (C) 2002  Florian Winterstein <flox@gmx.net>
7  *  Copyright (C) 2000  Simon MORLAT <simon.morlat@free.fr>
8  *
9 ****************************************************************************
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  *
25  ****************************************************************************/
26 #include <string.h>
27 #ifndef _WIN32_WCE
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <signal.h>
33 #include "private.h" /*coreapi/private.h, needed for LINPHONE_VERSION */
34 #endif /*_WIN32_WCE*/
35 #include <limits.h>
36 #include <ctype.h>
37
38 #include <linphonecore.h>
39
40 #include "linphonec.h"
41
42 #ifdef WIN32
43 #include <ws2tcpip.h>
44 #include <ctype.h>
45 #ifndef _WIN32_WCE
46 #include <conio.h>
47 #endif /*_WIN32_WCE*/
48 #else
49 #include <sys/socket.h>
50 #include <netdb.h>
51 #include <sys/un.h>
52 #include <sys/stat.h>
53 #endif
54
55 #if defined(_WIN32_WCE)
56
57 #if !defined(PATH_MAX)
58 #define PATH_MAX 256
59 #endif /*PATH_MAX*/
60
61 #if !defined(strdup)
62 #define strdup _strdup
63 #endif /*strdup*/
64
65 #endif /*_WIN32_WCE*/
66
67 #ifdef HAVE_GETTEXT
68 #include <libintl.h>
69 #ifndef _
70 #define _(String) gettext(String)
71 #endif
72 #else
73 #define _(something)    (something)
74 #endif
75
76 #ifndef PACKAGE_DIR
77 #define PACKAGE_DIR ""
78 #endif
79
80 /***************************************************************************
81  *
82  *  Types
83  *
84  ***************************************************************************/
85
86 typedef struct {
87         LinphoneAuthInfo *elem[MAX_PENDING_AUTH];
88         int nitems;
89 } LPC_AUTH_STACK;
90
91 /***************************************************************************
92  *
93  *  Forward declarations
94  *
95  ***************************************************************************/
96
97 char *lpc_strip_blanks(char *input);
98
99 static int handle_configfile_migration(void);
100 #if !defined(_WIN32_WCE)
101 static int copy_file(const char *from, const char *to);
102 #endif /*_WIN32_WCE*/
103 static int linphonec_parse_cmdline(int argc, char **argv);
104 static int linphonec_init(int argc, char **argv);
105 static int linphonec_main_loop (LinphoneCore * opm, char * sipAddr);
106 static int linphonec_idle_call (void);
107 #ifdef HAVE_READLINE
108 static int linphonec_initialize_readline(void);
109 static int linphonec_finish_readline();
110 static char **linephonec_readline_completion(const char *text,
111         int start, int end);
112 #endif
113
114 /* These are callback for linphone core */
115 static void linphonec_call_received(LinphoneCore *lc, LinphoneCall *call);
116 static void linphonec_paused_received(LinphoneCore *lc, LinphoneCall *call);
117 static void linphonec_resumed_received(LinphoneCore *lc, LinphoneCall *call);
118 static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm,
119         const char *username);
120 static void linphonec_display_refer (LinphoneCore * lc,LinphoneCall *call, const char *refer_to);
121 static void linphonec_display_something (LinphoneCore * lc, const char *something);
122 static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url);
123 static void linphonec_display_warning (LinphoneCore * lc, const char *something);
124 static void stub () {}
125 static void linphonec_notify_received(LinphoneCore *lc,const char *from,const char *msg);
126 static void linphonec_ack_paused_received(LinphoneCore *lc, LinphoneCall *call);
127 static void linphonec_ack_resumed_received(LinphoneCore *lc, LinphoneCall *call);
128 static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid);
129 static void linphonec_new_unknown_subscriber(LinphoneCore *lc,
130                 LinphoneFriend *lf, const char *url);
131 static void linphonec_bye_received(LinphoneCore *lc, LinphoneCall *call);
132 static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr,
133                 const char *from, const char *msg);
134 static void linphonec_display_status (LinphoneCore * lc, const char *something);
135 static void linphonec_general_state (LinphoneCore * lc, LinphoneGeneralState *gstate);
136 static void linphonec_dtmf_received(LinphoneCore *lc, int dtmf);
137 static void print_prompt(LinphoneCore *opm);
138 void linphonec_out(const char *fmt,...);
139 /***************************************************************************
140  *
141  * Global variables
142  *
143  ***************************************************************************/
144
145 LinphoneCore *linphonec;
146 FILE *mylogfile;
147 #ifdef HAVE_READLINE
148 static char *histfile_name=NULL;
149 static char last_in_history[256];
150 #endif
151 //auto answer (-a) option
152 static bool_t auto_answer=FALSE;
153 static bool_t answer_call=FALSE;
154 static bool_t vcap_enabled=FALSE;
155 static bool_t display_enabled=FALSE;
156 static bool_t preview_enabled=FALSE;
157 static bool_t show_general_state=FALSE;
158 static bool_t unix_socket=FALSE;
159 static bool_t linphonec_running=TRUE;
160 LPC_AUTH_STACK auth_stack;
161 static int trace_level = 0;
162 static char *logfile_name = NULL;
163 static char configfile_name[PATH_MAX];
164 static char *sipAddr = NULL; /* for autocall */
165 #if !defined(_WIN32_WCE)
166 static ortp_pipe_t client_sock=ORTP_PIPE_INVALID;
167 #endif /*_WIN32_WCE*/
168 char prompt[PROMPT_MAX_LEN];
169 #if !defined(_WIN32_WCE)
170 static ortp_thread_t pipe_reader_th;
171 static bool_t pipe_reader_run=FALSE;
172 #endif /*_WIN32_WCE*/
173 #if !defined(_WIN32_WCE)
174 static ortp_pipe_t server_sock;
175 #endif /*_WIN32_WCE*/
176
177
178 LinphoneCoreVTable linphonec_vtable
179 #if !defined (_MSC_VER)
180 = {
181         .show =(ShowInterfaceCb) stub,
182         .inv_recv = linphonec_call_received,
183         .bye_recv = linphonec_bye_received,
184         .ringing_recv = (RingingReceivedCb) stub,
185         .connected_recv = (ConnectedReceivedCb) stub,
186         .failure_recv = (FailureReceivedCb) stub,
187         .paused_recv = linphonec_paused_received,
188         .resumed_recv = linphonec_resumed_received,
189         .notify_recv = linphonec_notify_received,
190         .ack_paused_recv = linphonec_ack_paused_received,
191         .ack_resumed_recv = linphonec_ack_resumed_received,
192         .notify_presence_recv = linphonec_notify_presence_received,
193         .new_unknown_subscriber = linphonec_new_unknown_subscriber,
194         .auth_info_requested = linphonec_prompt_for_auth,
195         .display_status = linphonec_display_status,
196         .display_message=linphonec_display_something,
197 #ifdef VINCENT_MAURY_RSVP
198         /* the yes/no dialog box */
199         .display_yes_no= (DisplayMessageCb) stub,
200 #endif
201         .display_warning=linphonec_display_warning,
202         .display_url=linphonec_display_url,
203         .display_question=(DisplayQuestionCb)stub,
204         .text_received=linphonec_text_received,
205         .general_state=linphonec_general_state,
206         .dtmf_received=linphonec_dtmf_received,
207         .refer_received=linphonec_display_refer
208 }
209 #endif /*_WIN32_WCE*/
210 ;
211
212
213
214 /***************************************************************************
215  *
216  * Linphone core callbacks
217  *
218  ***************************************************************************/
219
220 /*
221  * Linphone core callback
222  */
223 static void
224 linphonec_display_refer (LinphoneCore * lc,LinphoneCall *call, const char *refer_to)
225 {
226         fprintf (stdout, "The distant end point asked to transfer the call to %s,don't forget to terminate the call if not\n%s", refer_to,prompt);
227         fflush(stdout);
228 }
229
230 /*
231  * Linphone core callback
232  */
233 static void
234 linphonec_display_something (LinphoneCore * lc, const char *something)
235 {
236         fprintf (stdout, "%s\n%s", something,prompt);
237         fflush(stdout);
238 }
239
240 /*
241  * Linphone core callback
242  */
243 static void
244 linphonec_display_status (LinphoneCore * lc, const char *something)
245 {
246         fprintf (stdout, "%s\n%s", something,prompt);
247         fflush(stdout);
248 }
249
250 /*
251  * Linphone core callback
252  */
253 static void
254 linphonec_display_warning (LinphoneCore * lc, const char *something)
255 {
256         fprintf (stdout, "Warning: %s\n%s", something,prompt);
257         fflush(stdout);
258 }
259
260 /*
261  * Linphone core callback
262  */
263 static void
264 linphonec_display_url (LinphoneCore * lc, const char *something, const char *url)
265 {
266         fprintf (stdout, "%s : %s\n", something, url);
267 }
268
269
270 /*
271  * Linphone core callback
272  */
273 static void
274 linphonec_call_received(LinphoneCore *lc, LinphoneCall *call)
275 {
276         char *from=linphone_call_get_remote_address_as_string(call);
277         linphonec_set_caller(from);
278         ms_free(from);
279         if ( auto_answer)  {
280                 answer_call=TRUE;
281         }
282 }
283 /*
284  * Linphone core callback
285  */
286 static void
287 linphonec_paused_received(LinphoneCore *lc, LinphoneCall *call)
288 {
289         char *from=linphone_call_get_remote_address_as_string(call);
290         if(from)
291         {
292                 linphonec_out("the call from %s have been paused\n",from);
293                 ms_free(from);
294         }
295 }
296 /*
297  * Linphone core callback
298  */
299 static void
300 linphonec_resumed_received(LinphoneCore *lc, LinphoneCall *call)
301 {
302         char *from=linphone_call_get_remote_address_as_string(call);
303         if(from)
304         {
305                 linphonec_out("the call from %s have been resumed\n",from);
306                 ms_free(from);
307         }
308 }
309
310
311
312 /*
313  * Linphone core callback
314  */
315 static void
316 linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username)
317 {
318         /* no prompt possible when using pipes or tcp mode*/
319         if (unix_socket){
320                 linphone_core_abort_authentication(lc,NULL);
321         }else{
322                 LinphoneAuthInfo *pending_auth;
323
324                 if ( auth_stack.nitems+1 > MAX_PENDING_AUTH )
325                 {
326                         fprintf(stderr,
327                                 "Can't accept another authentication request.\n"
328                                 "Consider incrementing MAX_PENDING_AUTH macro.\n");
329                         return;
330                 }
331
332                 pending_auth=linphone_auth_info_new(username,NULL,NULL,NULL,realm);
333                 auth_stack.elem[auth_stack.nitems++]=pending_auth;
334         }
335 }
336
337 /*
338  * Linphone core callback
339  */
340 static void
341 linphonec_notify_received(LinphoneCore *lc,const char *from,const char *msg)
342 {
343         printf("Notify type %s from %s\n", msg, from);
344         if(!strcmp(msg,"refer"))
345         {
346                 printf("The distant SIP end point get the refer we can close the call\n");
347                 linphonec_parse_command_line(linphonec, "terminate");
348         }
349 }
350
351 /*
352  * Linphone core callback
353  */
354 static void
355 linphonec_ack_paused_received(LinphoneCore *lc, LinphoneCall *call)
356 {
357         char *from=linphone_call_get_remote_address_as_string(call);
358         if(from)
359         {
360                 linphonec_out("the previous pause sent to %s has been acknowledged\n",from);
361                 ms_free(from);
362         }
363 }
364
365 /*
366  * Linphone core callback
367  */
368 static void
369 linphonec_ack_resumed_received(LinphoneCore *lc, LinphoneCall *call)
370 {
371         char *from=linphone_call_get_remote_address_as_string(call);
372         if(from)
373         {
374                 linphonec_out("the previous resume sent to %s has been acknowledged\n",from);
375                 ms_free(from);
376         }
377 }
378
379 /*
380  * Linphone core callback
381  */
382 static void
383 linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid)
384 {
385         char *tmp=linphone_address_as_string(linphone_friend_get_address(fid));
386         printf("Friend %s is %s\n", tmp, linphone_online_status_to_string(linphone_friend_get_status(fid)));
387         ms_free(tmp);
388         // todo: update Friend list state (unimplemented)
389 }
390
391 /*
392  * Linphone core callback
393  */
394 static void
395 linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf,
396                 const char *url)
397 {
398         printf("Friend %s requested subscription "
399                 "(accept/deny is not implemented yet)\n", url);
400         // This means that this person wishes to be notified
401         // of your presence information (online, busy, away...).
402
403 }
404
405 /*
406  * Linphone core callback
407  */
408 static void
409 linphonec_bye_received(LinphoneCore *lc, LinphoneCall *call)
410 {
411         // Should change prompt back to original maybe
412
413         // printing this is unneeded as we'd get a "Communication ended"
414         // message trough display_status callback anyway
415         char *from=linphone_call_get_remote_address_as_string(call);
416         printf("Bye received from %s\n", from);
417         ms_free(from);
418 }
419
420 /*
421  * Linphone core callback
422  */
423 static void
424 linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr,
425                 const char *from, const char *msg)
426 {
427         printf("%s: %s\n", from, msg);
428         // TODO: provide mechanism for answering.. ('say' command?)
429 }
430
431
432 static void linphonec_dtmf_received(LinphoneCore *lc, int dtmf){
433         fprintf(stdout,"Receiving tone %c\n",dtmf);
434         fflush(stdout);
435 }
436
437 static void
438 linphonec_general_state (LinphoneCore * lc, LinphoneGeneralState *gstate)
439 {
440         if (show_general_state) {
441           switch(gstate->new_state) {
442            case GSTATE_POWER_OFF:
443              printf("GSTATE_POWER_OFF");
444              break;
445            case GSTATE_POWER_STARTUP:
446              printf("GSTATE_POWER_STARTUP");
447              break;
448            case GSTATE_POWER_ON:
449              printf("GSTATE_POWER_ON");
450              break;
451            case GSTATE_POWER_SHUTDOWN:
452              printf("GSTATE_POWER_SHUTDOWN");
453              break;
454            case GSTATE_REG_NONE:
455              printf("GSTATE_REG_NONE");
456              break;
457            case GSTATE_REG_OK:
458              printf("GSTATE_REG_OK");
459              break;
460            case GSTATE_REG_FAILED:
461              printf("GSTATE_REG_FAILED");
462              break;
463            case GSTATE_CALL_IDLE:
464              printf("GSTATE_CALL_IDLE");
465              break;
466            case GSTATE_CALL_OUT_INVITE:
467              printf("GSTATE_CALL_OUT_INVITE");
468              break;
469            case GSTATE_CALL_OUT_CONNECTED:
470              printf("GSTATE_CALL_OUT_CONNECTED");
471              break;
472            case GSTATE_CALL_IN_INVITE:
473              printf("GSTATE_CALL_IN_INVITE");
474              break;
475            case GSTATE_CALL_IN_CONNECTED:
476              printf("GSTATE_CALL_IN_CONNECTED");
477              break;
478            case GSTATE_CALL_END:
479              printf("GSTATE_CALL_END");
480              break;
481            case GSTATE_CALL_ERROR:
482              printf("GSTATE_CALL_ERROR");
483              break;
484            default:
485               printf("GSTATE_UNKNOWN_%d",gstate->new_state);
486           }
487           if (gstate->message) printf(" %s", gstate->message);
488           printf("\n");
489         }
490 }
491
492 static char received_prompt[PROMPT_MAX_LEN];
493 static ms_mutex_t prompt_mutex;
494 static bool_t have_prompt=FALSE;
495
496 static void *prompt_reader_thread(void *arg){
497         char *ret;
498         char tmp[PROMPT_MAX_LEN];
499         while ((ret=fgets(tmp,sizeof(tmp),stdin))!=NULL){
500                 ms_mutex_lock(&prompt_mutex);
501                 strcpy(received_prompt,ret);
502                 have_prompt=TRUE;
503                 ms_mutex_unlock(&prompt_mutex);
504         }
505         return NULL;
506 }
507
508 static void start_prompt_reader(void){
509         ortp_thread_t th;
510         ms_mutex_init(&prompt_mutex,NULL);
511         ortp_thread_create(&th,NULL,prompt_reader_thread,NULL);
512 }
513 #if !defined(_WIN32_WCE)
514 static ortp_pipe_t create_server_socket(void){
515         char path[128];
516 #ifndef WIN32
517         snprintf(path,sizeof(path)-1,"linphonec-%i",getuid());
518 #else
519         {
520                 TCHAR username[128];
521                 DWORD size=sizeof(username)-1;
522                 GetUserName(username,&size);
523                 snprintf(path,sizeof(path)-1,"linphonec-%s",username);
524         }
525 #endif
526         return ortp_server_pipe_create(path);
527 }
528
529
530 static void *pipe_thread(void*p){
531         char tmp[250];
532         server_sock=create_server_socket();
533         if (server_sock==ORTP_PIPE_INVALID) return NULL;
534         while(pipe_reader_run){
535                 while(client_sock!=ORTP_PIPE_INVALID){ /*sleep until the last command is finished*/
536 #ifndef WIN32
537                         usleep(20000);
538 #else
539                         Sleep(20);
540 #endif
541                 }
542                 client_sock=ortp_server_pipe_accept_client(server_sock);
543                 if (client_sock!=ORTP_PIPE_INVALID){
544                         int len;
545                         /*now read from the client */
546                         if ((len=ortp_pipe_read(client_sock,(uint8_t*)tmp,sizeof(tmp)-1))>0){
547                                 ortp_mutex_lock(&prompt_mutex);
548                                 tmp[len]='\0';
549                                 strcpy(received_prompt,tmp);
550                                 printf("Receiving command '%s'\n",received_prompt);fflush(stdout);
551                                 have_prompt=TRUE;
552                                 ortp_mutex_unlock(&prompt_mutex);
553                         }else{
554                                 printf("read nothing\n");fflush(stdout);
555                                 ortp_server_pipe_close_client(client_sock);
556                                 client_sock=ORTP_PIPE_INVALID;
557                         }
558
559                 }else{
560                         if (pipe_reader_run) fprintf(stderr,"accept() failed: %s\n",strerror(errno));
561                 }
562         }
563         ms_message("Exiting pipe_reader_thread.");
564         fflush(stdout);
565         return NULL;
566 }
567
568 static void start_pipe_reader(void){
569         ms_mutex_init(&prompt_mutex,NULL);
570         pipe_reader_run=TRUE;
571         ortp_thread_create(&pipe_reader_th,NULL,pipe_thread,NULL);
572 }
573
574 static void stop_pipe_reader(void){
575         pipe_reader_run=FALSE;
576         linphonec_command_finished();
577         ortp_server_pipe_close(server_sock);
578         ortp_thread_join(pipe_reader_th,NULL);
579 }
580 #endif /*_WIN32_WCE*/
581
582 #ifdef HAVE_READLINE
583 #define BOOL_HAVE_READLINE 1
584 #else
585 #define BOOL_HAVE_READLINE 0
586 #endif
587
588 char *linphonec_readline(char *prompt){
589         if (unix_socket || !BOOL_HAVE_READLINE ){
590                 static bool_t prompt_reader_started=FALSE;
591                 static bool_t pipe_reader_started=FALSE;
592                 if (!prompt_reader_started){
593                         start_prompt_reader();
594                         prompt_reader_started=TRUE;
595                 }
596                 if (unix_socket && !pipe_reader_started){
597 #if !defined(_WIN32_WCE)
598                         start_pipe_reader();
599                         pipe_reader_started=TRUE;
600 #endif /*_WIN32_WCE*/
601                 }
602                 fprintf(stdout,"%s",prompt);
603                 fflush(stdout);
604                 while(1){
605                         ms_mutex_lock(&prompt_mutex);
606                         if (have_prompt){
607                                 char *ret=strdup(received_prompt);
608                                 have_prompt=FALSE;
609                                 ms_mutex_unlock(&prompt_mutex);
610                                 return ret;
611                         }
612                         ms_mutex_unlock(&prompt_mutex);
613                         linphonec_idle_call();
614 #ifdef WIN32
615                         Sleep(20);
616                         /* Following is to get the video window going as it
617                                  should. Maybe should we only have this on when the option -V
618                                  or -D is on? */
619                         MSG msg;
620         
621                         if (PeekMessage(&msg, NULL, 0, 0,1)) {
622                                 TranslateMessage(&msg);
623                                 DispatchMessage(&msg);
624                         }
625 #else
626                         usleep(20000);
627 #endif
628                 }
629         }else{
630 #ifdef HAVE_READLINE
631                 return readline(prompt);
632 #endif
633         }
634 }
635
636 void linphonec_out(const char *fmt,...){
637         char *res;
638         va_list args;
639         va_start (args, fmt);
640         res=ortp_strdup_vprintf(fmt,args);
641         va_end (args);
642         printf("%s",res);
643         fflush(stdout);
644 #if !defined(_WIN32_WCE)
645         if (client_sock!=ORTP_PIPE_INVALID){
646                 if (ortp_pipe_write(client_sock,(uint8_t*)res,strlen(res))==-1){
647                         fprintf(stderr,"Fail to send output via pipe: %s",strerror(errno));
648                 }
649         }
650 #endif /*_WIN32_WCE*/
651         ortp_free(res);
652 }
653
654 void linphonec_command_finished(void){
655 #if !defined(_WIN32_WCE)
656         if (client_sock!=ORTP_PIPE_INVALID){
657                 ortp_server_pipe_close_client(client_sock);
658                 client_sock=ORTP_PIPE_INVALID;
659         }
660 #endif /*_WIN32_WCE*/
661 }
662
663 void linphonec_set_autoanswer(bool_t enabled){
664         auto_answer=enabled;
665 }
666
667 bool_t linphonec_get_autoanswer(){
668         return auto_answer;
669 }
670
671 /***************************************************************************/
672 /*
673  * Main
674  *
675  * Use globals:
676  *
677  *      - char *histfile_name
678  *      - FILE *mylogfile
679  */
680 #if defined (_WIN32_WCE)
681
682 char **convert_args_to_ascii(int argc, _TCHAR **wargv){
683         int i;
684         char **result=malloc(argc*sizeof(char*));
685         char argtmp[128];
686         for(i=0;i<argc;++i){
687                 wcstombs(argtmp,wargv[i],sizeof(argtmp));
688                 result[i]=strdup(argtmp);
689         }
690         return result;
691 }
692
693 int _tmain(int argc, _TCHAR* wargv[]) {
694         char **argv=convert_args_to_ascii(argc,wargv);
695         trace_level=6;
696         linphonec_vtable.show =(ShowInterfaceCb) stub;
697         linphonec_vtable.inv_recv = linphonec_call_received;
698         linphonec_vtable.bye_recv = linphonec_bye_received;
699         linphonec_vtable.notify_presence_recv = linphonec_notify_received;
700         linphonec_vtable.new_unknown_subscriber = linphonec_new_unknown_subscriber;
701         linphonec_vtable.auth_info_requested = linphonec_prompt_for_auth;
702         linphonec_vtable.display_status = linphonec_display_status;
703         linphonec_vtable.display_message=linphonec_display_something;
704 #ifdef VINCENT_MAURY_RSVP
705         /* the yes/no dialog box */
706         linphonec_vtable.display_yes_no= (DisplayMessageCb) stub;
707 #endif
708         linphonec_vtable.display_warning=linphonec_display_warning;
709         linphonec_vtable.display_url=linphonec_display_url;
710         linphonec_vtable.display_question=(DisplayQuestionCb)stub;
711         linphonec_vtable.text_received=linphonec_text_received;
712         linphonec_vtable.general_state=linphonec_general_state;
713         linphonec_vtable.dtmf_received=linphonec_dtmf_received;
714
715 #else
716 int
717 main (int argc, char *argv[]) {
718 #endif
719
720
721         if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE);
722
723         linphonec_main_loop (linphonec, sipAddr);
724
725         linphonec_finish(EXIT_SUCCESS);
726
727         exit(EXIT_SUCCESS); /* should never reach here */
728 }
729
730 /*
731  * Initialize linphonec
732  */
733 static int
734 linphonec_init(int argc, char **argv)
735 {
736
737         //g_mem_set_vtable(&dbgtable);
738
739         /*
740          * Set initial values for global variables
741          */
742         mylogfile = NULL;
743         
744         
745 #ifndef _WIN32
746         snprintf(configfile_name, PATH_MAX, "%s/.linphonerc",
747                         getenv("HOME"));
748 #elif defined(_WIN32_WCE)
749         strncpy(configfile_name,PACKAGE_DIR "\\linphonerc",PATH_MAX);
750         mylogfile=fopen(PACKAGE_DIR "\\" "linphonec.log","w");
751         printf("Logs are redirected in" PACKAGE_DIR "\\linphonec.log");
752 #else
753         snprintf(configfile_name, PATH_MAX, "%s/Linphone/linphonerc",
754                         getenv("APPDATA"));
755 #endif
756         /* Handle configuration filename changes */
757         switch (handle_configfile_migration())
758         {
759                 case -1: /* error during file copies */
760                         fprintf(stderr,
761                                 "Error in configuration file migration\n");
762                         break;
763
764                 case 0: /* nothing done */
765                 case 1: /* migrated */
766                 default:
767                         break;
768         }
769
770 #ifdef ENABLE_NLS
771         if (NULL == bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR))
772                 perror ("bindtextdomain failed");
773 #ifndef __ARM__
774         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
775 #endif
776         textdomain (GETTEXT_PACKAGE);
777 #else
778         printf ("NLS disabled.\n");
779 #endif
780
781         linphonec_parse_cmdline(argc, argv);
782
783         if (trace_level > 0)
784         {
785                 if (logfile_name != NULL)
786                         mylogfile = fopen (logfile_name, "w+");
787
788                 if (mylogfile == NULL)
789                 {
790                         mylogfile = stdout;
791                         fprintf (stderr,
792                                  "INFO: no logfile, logging to stdout\n");
793                 }
794                 linphone_core_enable_logs(mylogfile);
795         }
796         else
797         {
798                 linphone_core_disable_logs();
799         }
800         /*
801          * Initialize auth stack
802          */
803         auth_stack.nitems=0;
804
805         /*
806          * Initialize linphone core
807          */
808         linphonec=linphone_core_new (&linphonec_vtable, configfile_name, NULL,
809                             NULL);
810         linphone_core_enable_video(linphonec,vcap_enabled,display_enabled);
811         linphone_core_enable_video_preview(linphonec,preview_enabled);
812         if (!(vcap_enabled || display_enabled)) printf("Warning: video is disabled in linphonec, use -V or -C or -D to enable.\n");
813 #ifdef HAVE_READLINE
814         /*
815          * Initialize readline
816          */
817         linphonec_initialize_readline();
818 #endif
819 #if !defined(_WIN32_WCE)
820         /*
821          * Initialize signal handlers
822          */
823         signal(SIGTERM, linphonec_finish);
824         signal(SIGINT, linphonec_finish);
825 #endif /*_WIN32_WCE*/
826         return 1;
827 }
828
829
830 void linphonec_main_loop_exit(void){
831         linphonec_running=FALSE;
832 }
833
834 /*
835  * Close linphonec, cleanly terminating
836  * any pending call
837  */
838 void
839 linphonec_finish(int exit_status)
840 {
841         printf("Terminating...\n");
842
843         /* Terminate any pending call */
844         linphonec_parse_command_line(linphonec, "terminate");
845         linphonec_command_finished();
846 #ifdef HAVE_READLINE
847         linphonec_finish_readline();
848 #endif
849 #if !defined(_WIN32_WCE)
850         if (pipe_reader_run)
851                 stop_pipe_reader();
852 #endif /*_WIN32_WCE*/
853
854         linphone_core_destroy (linphonec);
855
856         if (mylogfile != NULL && mylogfile != stdout)
857         {
858                 fclose (mylogfile);
859         }
860
861         exit(exit_status);
862
863 }
864
865 /*
866  * This is called from idle_call() whenever
867  * pending_auth != NULL.
868  *
869  * It prompts user for a password.
870  * Hitting ^D (EOF) would make this function
871  * return 0 (Cancel).
872  * Any other input would try to set linphone core
873  * auth_password for the pending_auth, add the auth_info
874  * and return 1.
875  */
876 int
877 linphonec_prompt_for_auth_final(LinphoneCore *lc)
878 {
879         char *input, *iptr;
880         char auth_prompt[256];
881 #ifdef HAVE_READLINE
882         rl_hook_func_t *old_event_hook;
883 #endif
884         LinphoneAuthInfo *pending_auth=auth_stack.elem[auth_stack.nitems-1];
885
886         snprintf(auth_prompt, 256, "Password for %s on %s: ",
887                 pending_auth->username, pending_auth->realm);
888
889         printf("\n");
890 #ifdef HAVE_READLINE
891         /*
892          * Disable event hook to avoid entering an
893          * infinite loop. This would prevent idle_call
894          * from being called during authentication reads.
895          * Note that it might be undesiderable...
896          */
897         old_event_hook=rl_event_hook;
898         rl_event_hook=NULL;
899 #endif
900
901         while (1)
902         {
903                 input=linphonec_readline(auth_prompt);
904
905                 /*
906                  * If EOF (^D) is sent you probably don't want
907                  * to provide an auth password... should give up
908                  * the operation, but there's no mechanism to
909                  * send this info back to caller currently...
910                  */
911                 if ( ! input )
912                 {
913                         printf("Cancel requested, but not implemented.\n");
914                         continue;
915                 }
916
917                 /* Strip blanks */
918                 iptr=lpc_strip_blanks(input);
919
920                 /*
921                  * Only blanks, continue asking
922                  */
923                 if ( ! *iptr )
924                 {
925                         free(input);
926                         continue;
927                 }
928
929                 /* Something typed, let's try */
930                 break;
931         }
932
933         /*
934          * No check is done here to ensure password is correct.
935          * I guess password will be asked again later.
936          */
937         linphone_auth_info_set_passwd(pending_auth, input);
938         linphone_core_add_auth_info(lc, pending_auth);
939         linphone_auth_info_destroy(pending_auth);
940         auth_stack.elem[auth_stack.nitems-1]=0;
941         --(auth_stack.nitems);
942 #ifdef HAVE_READLINE
943         /*
944          * Reset line_buffer, to avoid the password
945          * to be used again from outer readline
946          */
947         rl_line_buffer[0]='\0';
948         rl_event_hook=old_event_hook;
949 #endif
950         return 1;
951 }
952
953 void
954 print_usage (int exit_status)
955 {
956         fprintf (stdout, "\n\
957 usage: linphonec [-c file] [-s sipaddr] [-a] [-V] [-d level ] [-l logfile]\n\
958        linphonec -v\n\
959 \n\
960   -c  file             specify path of configuration file.\n\
961   -d  level            be verbose. 0 is no output. 6 is all output\n\
962   -l  logfile          specify the log file for your SIP phone\n\
963   -s  sipaddress       specify the sip call to do at startup\n\
964   -a                   enable auto answering for incoming calls\n\
965   -V                   enable video features globally (disabled by default)\n\
966   -C                   enable video capture only (disabled by default)\n\
967   -D                   enable video display only (disabled by default)\n\
968   -S                   show general state messages (disabled by default)\n\
969   -v or --version      display version and exits.\n");
970
971         exit(exit_status);
972 }
973
974
975 /*
976  *
977  * Called every second from main read loop.
978  *
979  * Will use the following globals:
980  *
981  *  - LinphoneCore linphonec
982  *  - LPC_AUTH_STACK auth_stack;
983  *
984  */
985 static int
986 linphonec_idle_call ()
987 {
988         LinphoneCore *opm=linphonec;
989
990         /* Uncomment the following to verify being called */
991         /* printf(".\n"); */
992
993         linphone_core_iterate(opm);
994         if (answer_call){
995                 fprintf (stdout, "-------auto answering to call-------\n" );
996                 linphone_core_accept_call(opm,NULL);
997                 answer_call=FALSE;
998         }
999
1000         if ( auth_stack.nitems )
1001         {
1002                 /*
1003                  * Inhibit command completion
1004                  * during password prompts
1005                  */
1006 #ifdef HAVE_READLINE
1007                 rl_inhibit_completion=1;
1008 #endif
1009                 linphonec_prompt_for_auth_final(opm);
1010 #ifdef HAVE_READLINE
1011                 rl_inhibit_completion=0;
1012 #endif
1013         }
1014
1015         return 0;
1016 }
1017
1018 #ifdef HAVE_READLINE
1019 /*
1020  * Use globals:
1021  *
1022  *      - char *histfile_name (also sets this)
1023  *      - char *last_in_history (allocates it)
1024  */
1025 static int
1026 linphonec_initialize_readline()
1027 {
1028         /*rl_bind_key('\t', rl_insert);*/
1029
1030         /* Allow conditional parsing of ~/.inputrc */
1031         rl_readline_name = "linphonec";
1032
1033         /* Call idle_call() every second */
1034         rl_set_keyboard_input_timeout(LPC_READLINE_TIMEOUT);
1035         rl_event_hook=linphonec_idle_call;
1036
1037         /* Set history file and read it */
1038         histfile_name = ms_strdup_printf ("%s/.linphonec_history",
1039                 getenv("HOME"));
1040         read_history(histfile_name);
1041
1042         /* Initialized last_in_history cache*/
1043         last_in_history[0] = '\0';
1044
1045         /* Register a completion function */
1046         rl_attempted_completion_function = linephonec_readline_completion;
1047
1048         /* printf("Readline initialized.\n"); */
1049         setlinebuf(stdout);
1050         return 0;
1051 }
1052
1053 /*
1054  * Uses globals:
1055  *
1056  *      - char *histfile_name (writes history to file and frees it)
1057  *      - char *last_in_history (frees it)
1058  *
1059  */
1060 static int
1061 linphonec_finish_readline()
1062 {
1063
1064         stifle_history(HISTSIZE);
1065         write_history(histfile_name);
1066         free(histfile_name);
1067         histfile_name=NULL;
1068         return 0;
1069 }
1070
1071 #endif
1072
1073 static void print_prompt(LinphoneCore *opm){
1074 #ifdef IDENTITY_AS_PROMPT
1075         snprintf(prompt, PROMPT_MAX_LEN, "%s> ",
1076                 linphone_core_get_primary_contact(opm));
1077 #else
1078         snprintf(prompt, PROMPT_MAX_LEN, "linphonec> ");
1079 #endif
1080 }
1081
1082 static int
1083 linphonec_main_loop (LinphoneCore * opm, char * sipAddr)
1084 {
1085         char buf[LINE_MAX_LEN]; /* auto call handling */
1086         char *input;
1087
1088         print_prompt(opm);
1089
1090
1091         /* auto call handling */
1092         if (sipAddr != NULL )
1093         {
1094                 snprintf (buf, sizeof(buf),"call %s", sipAddr);
1095                 linphonec_parse_command_line(linphonec, buf);
1096         }
1097
1098         while (linphonec_running && (input=linphonec_readline(prompt)))
1099         {
1100                 char *iptr; /* input and input pointer */
1101                 size_t input_len;
1102
1103                 /* Strip blanks */
1104                 iptr=lpc_strip_blanks(input);
1105
1106                 input_len = strlen(iptr);
1107
1108                 /*
1109                  * Do nothing but release memory
1110                  * if only blanks are read
1111                  */
1112                 if ( ! input_len )
1113                 {
1114                         free(input);
1115                         continue;
1116                 }
1117
1118 #ifdef HAVE_READLINE
1119                 /*
1120                  * Only add to history if not already
1121                  * last item in it, and only if the command
1122                  * doesn't start with a space (to allow for
1123                  * hiding passwords)
1124                  */
1125                 if ( iptr == input && strcmp(last_in_history, iptr) )
1126                 {
1127                         strncpy(last_in_history,iptr,sizeof(last_in_history));
1128                         last_in_history[sizeof(last_in_history)-1]='\0';
1129                         add_history(iptr);
1130                 }
1131 #endif
1132
1133                 linphonec_parse_command_line(linphonec, iptr);
1134                 linphonec_command_finished();
1135                 free(input);
1136         }
1137
1138         return 0;
1139 }
1140
1141 /*
1142  *  Parse command line switches
1143  *
1144  *  Use globals:
1145  *
1146  *      - int trace_level
1147  *      - char *logfile_name
1148  *      - char *configfile_name
1149  *      - char *sipAddr
1150  */
1151 static int
1152 linphonec_parse_cmdline(int argc, char **argv)
1153 {
1154         int arg_num=1;
1155
1156         while (arg_num < argc)
1157         {
1158                 int old_arg_num = arg_num;
1159                 if (strncmp ("-d", argv[arg_num], 2) == 0)
1160                 {
1161                         arg_num++;
1162                         if (arg_num < argc)
1163                                 trace_level = atoi (argv[arg_num]);
1164                         else
1165                                 trace_level = 1;
1166                 }
1167                 else if (strncmp ("-l", argv[arg_num], 2) == 0)
1168                 {
1169                         arg_num++;
1170                         if (arg_num < argc)
1171                                 logfile_name = argv[arg_num];
1172                 }
1173                 else if (strncmp ("-c", argv[arg_num], 2) == 0)
1174                 {
1175                         if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE);
1176 #if !defined(_WIN32_WCE)
1177                         if (access(argv[arg_num],F_OK)!=0 )
1178                         {
1179                                 fprintf (stderr,
1180                                         "Cannot open config file %s.\n",
1181                                          argv[arg_num]);
1182                                 exit(EXIT_FAILURE);
1183                         }
1184 #endif /*_WIN32_WCE*/
1185                         snprintf(configfile_name, PATH_MAX, "%s", argv[arg_num]);
1186                 }
1187                 else if (strncmp ("-s", argv[arg_num], 2) == 0)
1188                 {
1189                         arg_num++;
1190                         if (arg_num < argc)
1191                                 sipAddr = argv[arg_num];
1192                 }
1193                 else if (strncmp ("-a", argv[arg_num], 2) == 0)
1194                 {
1195                         auto_answer = TRUE;
1196                 }
1197                 else if (strncmp ("-C", argv[arg_num], 2) == 0)
1198                 {
1199                         vcap_enabled = TRUE;
1200                 }
1201                 else if (strncmp ("-D", argv[arg_num], 2) == 0)
1202                 {
1203                         display_enabled = TRUE;
1204                 }
1205                 else if (strncmp ("-V", argv[arg_num], 2) == 0)
1206                 {
1207                         display_enabled = TRUE;
1208                         vcap_enabled = TRUE;
1209                         preview_enabled=TRUE;
1210                 }
1211                 else if ((strncmp ("-v", argv[arg_num], 2) == 0)
1212                          ||
1213                          (strncmp
1214                           ("--version", argv[arg_num],
1215                            strlen ("--version")) == 0))
1216                 {
1217 #if !defined(_WIN32_WCE)
1218                         printf ("version: " LINPHONE_VERSION "\n");
1219 #endif
1220                         exit (EXIT_SUCCESS);
1221                 }
1222                 else if (strncmp ("-S", argv[arg_num], 2) == 0)
1223                 {
1224                         show_general_state = TRUE;
1225                 }
1226                 else if (strncmp ("--pipe", argv[arg_num], 6) == 0)
1227                 {
1228                         unix_socket=1;
1229                 }
1230                 else if (old_arg_num == arg_num)
1231                 {
1232                         fprintf (stderr, "ERROR: bad arguments\n");
1233                         print_usage (EXIT_FAILURE);
1234                 }
1235                 arg_num++;
1236         }
1237
1238         return 1;
1239 }
1240
1241 /*
1242  * Up to version 1.2.1 linphone used ~/.linphonec for
1243  * CLI and ~/.gnome2/linphone for GUI as configuration file.
1244  * In newer version both interfaces will use ~/.linphonerc.
1245  *
1246  * This function helps transparently migrating from one
1247  * to the other layout using the following heuristic:
1248  *
1249  *      IF new_config EXISTS => do nothing
1250  *      ELSE IF old_cli_config EXISTS => copy to new_config
1251  *      ELSE IF old_gui_config EXISTS => copy to new_config
1252  *
1253  * Returns:
1254  *       0 if it did nothing
1255  *       1 if it migrated successfully
1256  *      -1 on error
1257  */
1258 static int
1259 handle_configfile_migration()
1260 {
1261 #if !defined(_WIN32_WCE)
1262         char *old_cfg_gui;
1263         char *old_cfg_cli;
1264         char *new_cfg;
1265 #if !defined(_WIN32_WCE)
1266         const char *home = getenv("HOME");
1267 #else
1268         const char *home = ".";
1269 #endif /*_WIN32_WCE*/
1270         new_cfg = ms_strdup_printf("%s/.linphonerc", home);
1271
1272         /*
1273          * If the *NEW* configuration already exists
1274          * do nothing.
1275          */
1276         if (access(new_cfg,F_OK)==0)
1277         {
1278                 free(new_cfg);
1279                 return 0;
1280         }
1281
1282         old_cfg_cli = ms_strdup_printf("%s/.linphonec", home);
1283
1284         /*
1285          * If the *OLD* CLI configurations exist copy it to
1286          * the new file and make it a symlink.
1287          */
1288         if (access(old_cfg_cli, F_OK)==0)
1289         {
1290                 if ( ! copy_file(old_cfg_cli, new_cfg) )
1291                 {
1292                         free(old_cfg_cli);
1293                         free(new_cfg);
1294                         return -1;
1295                 }
1296                 printf("%s copied to %s\n", old_cfg_cli, new_cfg);
1297                 free(old_cfg_cli);
1298                 free(new_cfg);
1299                 return 1;
1300         }
1301
1302         free(old_cfg_cli);
1303         old_cfg_gui = ms_strdup_printf("%s/.gnome2/linphone", home);
1304
1305         /*
1306          * If the *OLD* GUI configurations exist copy it to
1307          * the new file and make it a symlink.
1308          */
1309         if (access(old_cfg_gui, F_OK)==0)
1310         {
1311                 if ( ! copy_file(old_cfg_gui, new_cfg) )
1312                 {
1313                         exit(EXIT_FAILURE);
1314                         free(old_cfg_gui);
1315                         free(new_cfg);
1316                         return -1;
1317                 }
1318                 printf("%s copied to %s\n", old_cfg_gui, new_cfg);
1319                 free(old_cfg_gui);
1320                 free(new_cfg);
1321                 return 1;
1322         }
1323
1324         free(old_cfg_gui);
1325         free(new_cfg);
1326 #endif /*_WIN32_WCE*/
1327         return 0;
1328 }
1329 #if !defined(_WIN32_WCE)
1330 /*
1331  * Copy file "from" to file "to".
1332  * Destination file is truncated if existing.
1333  * Return 1 on success, 0 on error (printing an error).
1334  */
1335 static int
1336 copy_file(const char *from, const char *to)
1337 {
1338         char message[256];
1339         FILE *in, *out;
1340         char buf[256];
1341         size_t n;
1342
1343         /* Open "from" file for reading */
1344         in=fopen(from, "r");
1345         if ( in == NULL )
1346         {
1347                 snprintf(message, 255, "Can't open %s for reading: %s\n",
1348                         from, strerror(errno));
1349                 fprintf(stderr, "%s", message);
1350                 return 0;
1351         }
1352
1353         /* Open "to" file for writing (will truncate existing files) */
1354         out=fopen(to, "w");
1355         if ( out == NULL )
1356         {
1357                 snprintf(message, 255, "Can't open %s for writing: %s\n",
1358                         to, strerror(errno));
1359                 fprintf(stderr, "%s", message);
1360                 return 0;
1361         }
1362
1363         /* Copy data from "in" to "out" */
1364         while ( (n=fread(buf, 1, sizeof buf, in)) > 0 )
1365         {
1366                 if ( ! fwrite(buf, 1, n, out) )
1367                 {
1368                         return 0;
1369                 }
1370         }
1371
1372         fclose(in);
1373         fclose(out);
1374
1375         return 1;
1376 }
1377 #endif /*_WIN32_WCE*/
1378
1379 #ifdef HAVE_READLINE
1380 static char **
1381 linephonec_readline_completion(const char *text, int start, int end)
1382 {
1383         char **matches = NULL;
1384
1385         /*
1386          * Prevent readline from falling
1387          * back to filename-completion
1388          */
1389         rl_attempted_completion_over=1;
1390
1391         /*
1392          * If this is the start of line we complete with commands
1393          */
1394         if ( ! start )
1395         {
1396                 return rl_completion_matches(text, linphonec_command_generator);
1397         }
1398
1399         /*
1400          * Otherwise, we should peek at command name
1401          * or context to implement a smart completion.
1402          * For example: "call .." could return
1403          * friends' sip-uri as matches
1404          */
1405
1406         return matches;
1407 }
1408
1409 #endif
1410
1411 /*
1412  * Strip blanks from a string.
1413  * Return a pointer into the provided string.
1414  * Modifies input adding a NULL at first
1415  * of trailing blanks.
1416  */
1417 char *
1418 lpc_strip_blanks(char *input)
1419 {
1420         char *iptr;
1421
1422         /* Find first non-blank */
1423         while(*input && isspace(*input)) ++input;
1424
1425         /* Find last non-blank */
1426         iptr=input+strlen(input);
1427         if (iptr > input) {
1428                 while(isspace(*--iptr));
1429                 *(iptr+1)='\0';
1430         }
1431
1432         return input;
1433 }
1434
1435 /****************************************************************************
1436  *
1437  * $Log: linphonec.c,v $
1438  * Revision 1.57  2007/11/14 13:40:27  smorlat
1439  * fix --disable-video build.
1440  *
1441  * Revision 1.56  2007/09/26 14:07:27  fixkowalski
1442  * - ANSI/C++ compilation issues with non-GCC compilers
1443  * - Faster epm-based packaging
1444  * - Ability to build & run on FC6's eXosip/osip
1445  *
1446  * Revision 1.55  2007/09/24 16:01:58  smorlat
1447  * fix bugs.
1448  *
1449  * Revision 1.54  2007/08/22 14:06:11  smorlat
1450  * authentication bugs fixed.
1451  *
1452  * Revision 1.53  2007/02/13 21:31:01  smorlat
1453  * added patch for general state.
1454  * new doxygen for oRTP
1455  * gtk-doc removed.
1456  *
1457  * Revision 1.52  2007/01/10 14:11:24  smorlat
1458  * add --video to linphonec.
1459  *
1460  * Revision 1.51  2006/08/21 12:49:59  smorlat
1461  * merged several little patches.
1462  *
1463  * Revision 1.50  2006/07/26 08:17:28  smorlat
1464  * fix bugs.
1465  *
1466  * Revision 1.49  2006/07/17 18:45:00  smorlat
1467  * support for several event queues in ortp.
1468  * glib dependency removed from coreapi/ and console/
1469  *
1470  * Revision 1.48  2006/04/09 12:45:32  smorlat
1471  * linphonec improvements.
1472  *
1473  * Revision 1.47  2006/04/04 08:04:34  smorlat
1474  * switched to mediastreamer2, most bugs fixed.
1475  *
1476  * Revision 1.46  2006/03/16 17:17:40  smorlat
1477  * fix various bugs.
1478  *
1479  * Revision 1.45  2006/03/12 21:48:31  smorlat
1480  * gcc-2.95 compile error fixed.
1481  * mediastreamer2 in progress
1482  *
1483  * Revision 1.44  2006/03/04 11:17:10  smorlat
1484  * mediastreamer2 in progress.
1485  *
1486  * Revision 1.43  2006/02/13 09:50:50  strk
1487  * fixed unused variable warning.
1488  *
1489  * Revision 1.42  2006/02/02 15:39:18  strk
1490  * - Added 'friend list' and 'friend call' commands
1491  * - Allowed for multiple DTFM send in a single line
1492  * - Added status-specific callback (bare version)
1493  *
1494  * Revision 1.41  2006/02/02 13:30:05  strk
1495  * - Padded vtable with missing callbacks
1496  *   (fixing a segfault on friends subscription)
1497  * - Handled friends notify (bare version)
1498  * - Handled text messages receive (bare version)
1499  * - Printed message on subscription request (bare version)
1500  *
1501  * Revision 1.40  2006/01/26 09:48:05  strk
1502  * Added limits.h include
1503  *
1504  * Revision 1.39  2006/01/26 02:11:01  strk
1505  * Removed unused variables, fixed copyright date
1506  *
1507  * Revision 1.38  2006/01/25 18:33:02  strk
1508  * Removed the -t swich, terminate_on_close made the default behaviour
1509  *
1510  * Revision 1.37  2006/01/20 14:12:34  strk
1511  * Added linphonec_init() and linphonec_finish() functions.
1512  * Handled SIGINT and SIGTERM to invoke linphonec_finish().
1513  * Handling of auto-termination (-t) moved to linphonec_finish().
1514  * Reworked main (input read) loop to not rely on 'terminate'
1515  * and 'run' variable (dropped). configfile_name allocated on stack
1516  * using PATH_MAX limit. Changed print_usage signature to allow
1517  * for an exit_status specification.
1518  *
1519  * Revision 1.36  2006/01/18 09:25:32  strk
1520  * Command completion inhibited in proxy addition and auth request prompts.
1521  * Avoided use of readline's internal filename completion.
1522  *
1523  * Revision 1.35  2006/01/14 13:29:32  strk
1524  * Reworked commands interface to use a table structure,
1525  * used by command line parser and help function.
1526  * Implemented first level of completion (commands).
1527  * Added notification of invalid "answer" and "terminate"
1528  * commands (no incoming call, no active call).
1529  * Forbidden "call" intialization when a call is already active.
1530  * Cleaned up all commands, adding more feedback and error checks.
1531  *
1532  * Revision 1.34  2006/01/13 13:00:29  strk
1533  * Added linphonec.h. Code layout change (added comments, forward decl,
1534  * globals on top, copyright notices and Logs). Handled out-of-memory
1535  * condition on history management. Removed assumption on sizeof(char).
1536  * Fixed bug in authentication prompt (introduced by readline).
1537  * Added support for multiple authentication requests (up to MAX_PENDING_AUTH).
1538  *
1539  *
1540  ****************************************************************************/
1541