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