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