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