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