source: mediastreamer2/linphone/console/linphonec.c @ 260:11a6030a888c

Last change on this file since 260:11a6030a888c was 260:11a6030a888c, checked in by smorlat <smorlat@…>, 4 years ago

add linphonec under windows

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