source: mediastreamer2/linphone/console/linphonec.c @ 257:9ebb25281d87

Last change on this file since 257:9ebb25281d87 was 256:70ad9c490986, checked in by smorlat <smorlat@…>, 4 years ago

linphonecsh and linphonec use a unix socket by default, for security reasons

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

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