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