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