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