source: mediastreamer2/linphone/console/linphonec.c @ 259:1a6d8af99540

Last change on this file since 259:1a6d8af99540 was 258:d5b37ea1e679, checked in by strk <strk@…>, 4 years ago

Fix HAVE_READLINE define setting

git-svn-id: svn+ssh://svn.savannah.nongnu.org/linphone/trunk@261 3f6dc0c8-ddfe-455d-9043-3cd528dc4637

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