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