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