/**************************************************************************** * * $Id: linphonec.c,v 1.57 2007/11/14 13:40:27 smorlat Exp $ * * Copyright (C) 2006 Sandro Santilli * Copyright (C) 2002 Florian Winterstein * Copyright (C) 2000 Simon MORLAT * **************************************************************************** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ****************************************************************************/ #include #ifndef _WIN32_WCE #include #include #include #include "private.h" /*coreapi/private.h, needed for LINPHONE_VERSION */ #endif /*_WIN32_WCE*/ #include #include #include #include #include "linphonec.h" #include #ifdef _WIN32 #include #include #ifndef _WIN32_WCE #include #endif /*_WIN32_WCE*/ #else #include #include #include #include #include #include #endif #if !defined(PATH_MAX) #define PATH_MAX 256 #endif /*PATH_MAX*/ #if defined(_WIN32_WCE) #if !defined(strdup) #define strdup _strdup #endif /*strdup*/ #endif /*_WIN32_WCE*/ #ifndef PACKAGE_DIR #define PACKAGE_DIR "" #endif #ifdef HAVE_X11_XLIB_H #include #endif /*************************************************************************** * * Types * ***************************************************************************/ typedef struct { LinphoneAuthInfo *elem[MAX_PENDING_AUTH]; int nitems; } LPC_AUTH_STACK; /*************************************************************************** * * Forward declarations * ***************************************************************************/ char *lpc_strip_blanks(char *input); static int handle_configfile_migration(void); #if !defined(_WIN32_WCE) static int copy_file(const char *from, const char *to); #endif /*_WIN32_WCE*/ static int linphonec_parse_cmdline(int argc, char **argv); static int linphonec_init(int argc, char **argv); static int linphonec_main_loop (LinphoneCore * opm); static int linphonec_idle_call (void); #ifdef HAVE_READLINE static int linphonec_initialize_readline(void); static int linphonec_finish_readline(void); static char **linephonec_readline_completion(const char *text, int start, int end); #endif /* These are callback for linphone core */ static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username, const char *domain); static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to); static void linphonec_display_something (LinphoneCore * lc, const char *something); static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url); static void linphonec_display_warning (LinphoneCore * lc, const char *something); static void linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state); static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid); static void linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url); static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, const LinphoneAddress *from, const char *msg); static void linphonec_display_status (LinphoneCore * lc, const char *something); static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf); static void print_prompt(LinphoneCore *opm); void linphonec_out(const char *fmt,...); /*************************************************************************** * * Global variables * ***************************************************************************/ LinphoneCore *linphonec; FILE *mylogfile; #ifdef HAVE_READLINE static char *histfile_name=NULL; static char last_in_history[256]; #endif //auto answer (-a) option static bool_t auto_answer=FALSE; static bool_t real_early_media_sending=FALSE; static bool_t answer_call=FALSE; static bool_t vcap_enabled=FALSE; static bool_t display_enabled=FALSE; static bool_t preview_enabled=FALSE; static bool_t show_general_state=FALSE; static bool_t unix_socket=FALSE; static bool_t linphonec_running=TRUE; LPC_AUTH_STACK auth_stack; static int trace_level = 0; static char *logfile_name = NULL; static char configfile_name[PATH_MAX]; static char zrtpsecrets[PATH_MAX]; static char usr_certificates_path[PATH_MAX]; static const char *factory_configfile_name=NULL; static char *sip_addr_to_call = NULL; /* for autocall */ static void *window_id = NULL; /* NULL=standalone window, or window id for embedding video */ #if !defined(_WIN32_WCE) static ortp_pipe_t client_sock=ORTP_PIPE_INVALID; #endif /*_WIN32_WCE*/ char prompt[PROMPT_MAX_LEN]; #if !defined(_WIN32_WCE) static ortp_thread_t pipe_reader_th; static bool_t pipe_reader_run=FALSE; #endif /*_WIN32_WCE*/ #if !defined(_WIN32_WCE) static ortp_pipe_t server_sock; #endif /*_WIN32_WCE*/ bool_t linphonec_camera_enabled=TRUE; void linphonec_call_identify(LinphoneCall* call){ static int callid=1; linphone_call_set_user_pointer (call,INT_TO_VOIDPTR(callid)); callid++; } LinphoneCall *linphonec_get_call(int id){ const MSList *elem=linphone_core_get_calls(linphonec); for (;elem!=NULL;elem=elem->next){ LinphoneCall *call=(LinphoneCall*)elem->data; if (VOIDPTR_TO_INT(linphone_call_get_user_pointer(call))==id){ return call; } } linphonec_out("Sorry, no call with id %i exists at this time.\n",id); return NULL; } /*************************************************************************** * * Linphone core callbacks * ***************************************************************************/ /* * Linphone core callback */ static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to) { linphonec_out("Receiving out of call refer to %s\n", refer_to); } /* * Linphone core callback */ static void linphonec_display_something (LinphoneCore * lc, const char *something) { fprintf (stdout, "%s\n%s", something,prompt); fflush(stdout); } /* * Linphone core callback */ static void linphonec_display_status (LinphoneCore * lc, const char *something) { fprintf (stdout, "%s\n%s", something,prompt); fflush(stdout); } /* * Linphone core callback */ static void linphonec_display_warning (LinphoneCore * lc, const char *something) { fprintf (stdout, "Warning: %s\n%s", something,prompt); fflush(stdout); } /* * Linphone core callback */ static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url) { fprintf (stdout, "%s : %s\n", something, url); } /* * Linphone core callback */ static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username, const char *domain) { /* no prompt possible when using pipes or tcp mode*/ if (unix_socket){ linphone_core_abort_authentication(lc,NULL); }else{ LinphoneAuthInfo *pending_auth; if ( auth_stack.nitems+1 > MAX_PENDING_AUTH ) { fprintf(stderr, "Can't accept another authentication request.\n" "Consider incrementing MAX_PENDING_AUTH macro.\n"); return; } pending_auth=linphone_auth_info_new(username,NULL,NULL,NULL,realm,domain); auth_stack.elem[auth_stack.nitems++]=pending_auth; } } /* * Linphone core callback */ static void linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state) { char *remote=linphone_call_get_remote_address_as_string(call); if (new_call_state==LinphoneCallConnected){ linphonec_out("The distant endpoint %s of call %i has been transfered, you can safely close the call.\n", remote,VOIDPTR_TO_INT(linphone_call_get_user_pointer(call))); } ms_free(remote); } /* * Linphone core callback */ static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid) { const LinphoneAddress *addr = linphone_friend_get_address(fid); if (addr) { char *tmp=linphone_address_as_string(addr); printf("Friend %s is %s\n", tmp, linphone_online_status_to_string(linphone_friend_get_status(fid))); ms_free(tmp); } // todo: update Friend list state (unimplemented) } /* * Linphone core callback */ static void linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url) { printf("Friend %s requested subscription " "(accept/deny is not implemented yet)\n", url); // This means that this person wishes to be notified // of your presence information (online, busy, away...). } static void linphonec_call_updated(LinphoneCall *call){ const LinphoneCallParams *cp=linphone_call_get_current_params(call); if (!linphone_call_camera_enabled (call) && linphone_call_params_video_enabled (cp)){ linphonec_out("Far end requests to share video.\nType 'camera on' if you agree.\n"); } } static void linphonec_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t encrypted, const char *auth_token) { int id=VOIDPTR_TO_INT(linphone_call_get_user_pointer(call)); if (!encrypted) { linphonec_out("Call %i is not fully encrypted and auth token is %s.\n", id, (auth_token != NULL) ? auth_token : "absent"); } else { linphonec_out("Call %i is fully encrypted and auth token is %s.\n", id, (auth_token != NULL) ? auth_token : "absent"); } } static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState st, const char *msg){ char *from=linphone_call_get_remote_address_as_string(call); int id=VOIDPTR_TO_INT(linphone_call_get_user_pointer(call)); switch(st){ case LinphoneCallEnd: linphonec_out("Call %i with %s ended (%s).\n", id, from, linphone_reason_to_string(linphone_call_get_reason(call))); break; case LinphoneCallResuming: linphonec_out("Resuming call %i with %s.\n", id, from); break; case LinphoneCallStreamsRunning: linphonec_out("Media streams established with %s for call %i (%s).\n", from,id,( linphone_call_params_video_enabled( linphone_call_get_current_params(call)) ? "video":"audio")); break; case LinphoneCallPausing: linphonec_out("Pausing call %i with %s.\n", id, from); break; case LinphoneCallPaused: linphonec_out("Call %i with %s is now paused.\n", id, from); break; case LinphoneCallPausedByRemote: linphonec_out("Call %i has been paused by %s.\n",id,from); break; case LinphoneCallIncomingReceived: linphonec_call_identify(call); linphone_call_enable_camera (call,linphonec_camera_enabled); id=VOIDPTR_TO_INT(linphone_call_get_user_pointer(call)); linphonec_set_caller(from); linphonec_out("Receiving new incoming call from %s, assigned id %i\n", from,id); if ( auto_answer) { answer_call=TRUE; } else if (real_early_media_sending) { LinphoneCallParams* callparams = linphone_core_create_call_params(lc, call); linphonec_out("Sending early media using real hardware\n"); linphone_call_params_enable_early_media_sending(callparams, TRUE); if (vcap_enabled) linphone_call_params_enable_video(callparams, TRUE); linphone_call_accept_early_media_with_params(call, callparams); linphone_call_params_unref(callparams); } break; case LinphoneCallOutgoingInit: linphonec_call_identify(call); id=VOIDPTR_TO_INT(linphone_call_get_user_pointer(call)); linphonec_out("Establishing call id to %s, assigned id %i\n", from,id); break; case LinphoneCallUpdatedByRemote: linphonec_call_updated(call); break; case LinphoneCallOutgoingProgress: linphonec_out("Call %i to %s in progress.\n", id, from); break; case LinphoneCallOutgoingRinging: linphonec_out("Call %i to %s ringing.\n", id, from); break; case LinphoneCallConnected: linphonec_out("Call %i with %s connected.\n", id, from); break; case LinphoneCallOutgoingEarlyMedia: linphonec_out("Call %i with %s early media.\n", id, from); break; case LinphoneCallError: linphonec_out("Call %i with %s error.\n", id, from); break; default: break; } ms_free(from); } /* * Linphone core callback */ static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, const LinphoneAddress *from, const char *msg) { linphonec_out("Message received from %s: %s\n", linphone_address_as_string(from), msg); // TODO: provide mechanism for answering.. ('say' command?) } static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf){ char *from=linphone_call_get_remote_address_as_string(call); fprintf(stdout,"Receiving tone %c from %s\n",dtmf,from); fflush(stdout); ms_free(from); } static char received_prompt[PROMPT_MAX_LEN]; static ms_mutex_t prompt_mutex; static bool_t have_prompt=FALSE; static void *prompt_reader_thread(void *arg){ char *ret; char tmp[PROMPT_MAX_LEN]; while ((ret=fgets(tmp,sizeof(tmp),stdin))!=NULL){ ms_mutex_lock(&prompt_mutex); strcpy(received_prompt,ret); have_prompt=TRUE; ms_mutex_unlock(&prompt_mutex); } return NULL; } static void start_prompt_reader(void){ ortp_thread_t th; ms_mutex_init(&prompt_mutex,NULL); ortp_thread_create(&th,NULL,prompt_reader_thread,NULL); } #if !defined(_WIN32_WCE) static ortp_pipe_t create_server_socket(void){ char path[128]; #ifndef _WIN32 snprintf(path,sizeof(path)-1,"linphonec-%i",getuid()); #else { TCHAR username[128]; DWORD size=sizeof(username)-1; GetUserName(username,&size); snprintf(path,sizeof(path)-1,"linphonec-%s",username); } #endif return ortp_server_pipe_create(path); } static void *pipe_thread(void*p){ char tmp[250]; server_sock=create_server_socket(); if (server_sock==ORTP_PIPE_INVALID) return NULL; while(pipe_reader_run){ while(client_sock!=ORTP_PIPE_INVALID){ /*sleep until the last command is finished*/ #ifndef _WIN32 usleep(20000); #else Sleep(20); #endif } client_sock=ortp_server_pipe_accept_client(server_sock); if (client_sock!=ORTP_PIPE_INVALID){ int len; /*now read from the client */ if ((len=ortp_pipe_read(client_sock,(uint8_t*)tmp,sizeof(tmp)-1))>0){ ortp_mutex_lock(&prompt_mutex); tmp[len]='\0'; strcpy(received_prompt,tmp); printf("Receiving command '%s'\n",received_prompt);fflush(stdout); have_prompt=TRUE; ortp_mutex_unlock(&prompt_mutex); }else{ printf("read nothing\n");fflush(stdout); ortp_server_pipe_close_client(client_sock); client_sock=ORTP_PIPE_INVALID; } }else{ if (pipe_reader_run) fprintf(stderr,"accept() failed: %s\n",strerror(errno)); } } ms_message("Exiting pipe_reader_thread."); fflush(stdout); return NULL; } static void start_pipe_reader(void){ pipe_reader_run=TRUE; ortp_thread_create(&pipe_reader_th,NULL,pipe_thread,NULL); } static void stop_pipe_reader(void){ pipe_reader_run=FALSE; linphonec_command_finished(); ortp_server_pipe_close(server_sock); ortp_thread_join(pipe_reader_th,NULL); } #endif /*_WIN32_WCE*/ #ifdef HAVE_READLINE #define BOOL_HAVE_READLINE 1 #else #define BOOL_HAVE_READLINE 0 #endif char *linphonec_readline(char *prompt){ if (unix_socket || !BOOL_HAVE_READLINE ){ static bool_t prompt_reader_started=FALSE; static bool_t pipe_reader_started=FALSE; if (!prompt_reader_started){ start_prompt_reader(); prompt_reader_started=TRUE; } if (unix_socket && !pipe_reader_started){ #if !defined(_WIN32_WCE) start_pipe_reader(); pipe_reader_started=TRUE; #endif /*_WIN32_WCE*/ } fprintf(stdout,"%s",prompt); fflush(stdout); while(1){ ms_mutex_lock(&prompt_mutex); if (have_prompt){ char *ret=strdup(received_prompt); have_prompt=FALSE; ms_mutex_unlock(&prompt_mutex); return ret; } ms_mutex_unlock(&prompt_mutex); linphonec_idle_call(); #ifdef _WIN32 { MSG msg; Sleep(20); /* Following is to get the video window going as it should. Maybe should we only have this on when the option -V or -D is on? */ if (PeekMessage(&msg, NULL, 0, 0,1)) { TranslateMessage(&msg); DispatchMessage(&msg); } } #else usleep(20000); #endif } }else{ #ifdef HAVE_READLINE char* ret=readline(prompt); return ret; #endif } } void linphonec_out(const char *fmt,...){ char *res; va_list args; va_start (args, fmt); res=ortp_strdup_vprintf(fmt,args); va_end (args); printf("%s",res); fflush(stdout); #if !defined(_WIN32_WCE) if (client_sock!=ORTP_PIPE_INVALID){ if (ortp_pipe_write(client_sock,(uint8_t*)res,(int)strlen(res))==-1){ fprintf(stderr,"Fail to send output via pipe: %s",strerror(errno)); } } #endif /*_WIN32_WCE*/ ortp_free(res); } void linphonec_command_finished(void){ #if !defined(_WIN32_WCE) if (client_sock!=ORTP_PIPE_INVALID){ ortp_server_pipe_close_client(client_sock); client_sock=ORTP_PIPE_INVALID; } #endif /*_WIN32_WCE*/ } void linphonec_set_autoanswer(bool_t enabled){ auto_answer=enabled; } bool_t linphonec_get_autoanswer(void){ return auto_answer; } LinphoneCoreVTable linphonec_vtable={0}; /***************************************************************************/ /* * Main * * Use globals: * * - char *histfile_name * - FILE *mylogfile */ #if defined (_WIN32_WCE) char **convert_args_to_ascii(int argc, _TCHAR **wargv){ int i; char **result=malloc(argc*sizeof(char*)); char argtmp[128]; for(i=0;i 0) { if (logfile_name != NULL) mylogfile = fopen (logfile_name, "w+"); if (mylogfile == NULL) { mylogfile = stdout; fprintf (stderr, "INFO: no logfile, logging to stdout\n"); } linphone_core_enable_logs(mylogfile); } else { linphone_core_disable_logs(); } /* * Initialize auth stack */ auth_stack.nitems=0; /* * Initialize linphone core */ linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL); linphone_core_set_user_agent(linphonec,"Linphonec", LINPHONE_VERSION); linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets); linphone_core_set_user_certificates_path(linphonec,usr_certificates_path); linphone_core_enable_video_capture(linphonec, vcap_enabled); linphone_core_enable_video_display(linphonec, display_enabled); if (display_enabled && (window_id != NULL)) { printf("Setting window_id: 0x%p\n", window_id); linphone_core_set_native_video_window_id(linphonec,window_id); } linphone_core_enable_video_preview(linphonec,preview_enabled); if (!(vcap_enabled || display_enabled)) printf("Warning: video is disabled in linphonec, use -V or -C or -D to enable.\n"); #ifdef HAVE_READLINE /* * Initialize readline */ linphonec_initialize_readline(); #endif #if !defined(_WIN32_WCE) /* * Initialize signal handlers */ signal(SIGTERM, linphonec_finish); signal(SIGINT, linphonec_finish); #endif /*_WIN32_WCE*/ return 1; } void linphonec_main_loop_exit(void){ linphonec_running=FALSE; } /* * Close linphonec, cleanly terminating * any pending call */ void linphonec_finish(int exit_status) { // Do not allow concurrent destroying to prevent glibc errors static bool_t terminating=FALSE; if (terminating) return; terminating=TRUE; linphonec_out("Terminating...\n"); /* Terminate any pending call */ linphone_core_terminate_all_calls(linphonec); #ifdef HAVE_READLINE linphonec_finish_readline(); #endif #if !defined(_WIN32_WCE) if (pipe_reader_run) stop_pipe_reader(); #endif /*_WIN32_WCE*/ linphone_core_unref (linphonec); if (mylogfile != NULL && mylogfile != stdout) { fclose (mylogfile); mylogfile=stdout; } printf("\n"); exit(exit_status); } /* * This is called from idle_call() whenever * pending_auth != NULL. * * It prompts user for a password. * Hitting ^D (EOF) would make this function * return 0 (Cancel). * Any other input would try to set linphone core * auth_password for the pending_auth, add the auth_info * and return 1. */ int linphonec_prompt_for_auth_final(LinphoneCore *lc) { static int reentrancy=0; char *input, *iptr; char auth_prompt[256]; #ifdef HAVE_READLINE rl_hook_func_t *old_event_hook; #endif LinphoneAuthInfo *pending_auth; if (reentrancy!=0) return 0; reentrancy++; pending_auth=auth_stack.elem[auth_stack.nitems-1]; snprintf(auth_prompt, 256, "Password for %s on %s: ", pending_auth->username, pending_auth->realm); printf("\n"); #ifdef HAVE_READLINE /* * Disable event hook to avoid entering an * infinite loop. This would prevent idle_call * from being called during authentication reads. * Note that it might be undesiderable... */ old_event_hook=rl_event_hook; rl_event_hook=NULL; #endif while (1) { input=linphonec_readline(auth_prompt); /* * If EOF (^D) is sent you probably don't want * to provide an auth password... should give up * the operation, but there's no mechanism to * send this info back to caller currently... */ if ( ! input ) { printf("Cancel requested, but not implemented.\n"); continue; } /* Strip blanks */ iptr=lpc_strip_blanks(input); /* * Only blanks, continue asking */ if ( ! *iptr ) { free(input); continue; } /* Something typed, let's try */ break; } /* * No check is done here to ensure password is correct. * I guess password will be asked again later. */ linphone_auth_info_set_passwd(pending_auth, input); linphone_core_add_auth_info(lc, pending_auth); linphone_auth_info_unref(pending_auth); auth_stack.elem[auth_stack.nitems-1]=0; --(auth_stack.nitems); #ifdef HAVE_READLINE /* * Reset line_buffer, to avoid the password * to be used again from outer readline */ rl_line_buffer[0]='\0'; rl_event_hook=old_event_hook; #endif return 1; } void print_usage (int exit_status) { fprintf (stdout, "\n" "usage: linphonec [-c file] [-s sipaddr] [-a] [-V] [-d level ] [-l logfile]\n" "linphonec -v\n" "\n" " -b file specify path of readonly factory configuration file.\n" " -c file specify path of configuration file.\n" " -d level be verbose. 0 is no output. 6 is all output\n" " -l logfile specify the log file for your SIP phone\n" " -s sipaddress specify the sip call to do at startup\n" " -a enable auto answering for incoming calls\n" " --real-early-media enable sending early media using real audio/video (beware of privacy issue)\n" " -V enable video features globally (disabled by default)\n" " -C enable video capture only (disabled by default)\n" " -D enable video display only (disabled by default)\n" " -S show general state messages (disabled by default)\n" " --wid windowid force embedding of video window into provided windowid (disabled by default)\n" " -v or --version display version and exits.\n" ); exit(exit_status); } #ifdef VIDEO_ENABLED #ifdef HAVE_X11_XLIB_H static void x11_apply_video_params(VideoParams *params, Window window){ XWindowChanges wc; unsigned int flags=0; static Display *display = NULL; const char *dname=getenv("DISPLAY"); if (display==NULL && dname!=NULL){ XInitThreads(); display=XOpenDisplay(dname); } if (display==NULL){ ms_error("Could not open display %s",dname); return; } memset(&wc,0,sizeof(wc)); wc.x=params->x; wc.y=params->y; wc.width=params->w; wc.height=params->h; if (params->x!=-1 ){ flags|=CWX|CWY; } if (params->w!=-1){ flags|=CWWidth|CWHeight; } /*printf("XConfigureWindow x=%i,y=%i,w=%i,h=%i\n", wc.x, wc.y ,wc.width, wc.height);*/ XConfigureWindow(display,window,flags,&wc); if (params->show) XMapWindow(display,window); else XUnmapWindow(display,window); XSync(display,FALSE); } #endif static void lpc_apply_video_params(void){ static void *old_wid=NULL; static void *old_pwid=NULL; void *wid=linphone_core_get_native_video_window_id(linphonec); void *pwid=linphone_core_get_native_preview_window_id(linphonec); if (wid!=NULL && (lpc_video_params.refresh || old_wid!=wid)){ lpc_video_params.refresh=FALSE; #ifdef HAVE_X11_XLIB_H if (lpc_video_params.wid==0){ // do not manage window if embedded x11_apply_video_params(&lpc_video_params,(Window)wid); } else { linphone_core_show_video(linphonec, lpc_video_params.show); } #endif } old_wid=wid; if (pwid!=NULL && (lpc_preview_params.refresh || old_pwid!=pwid)){ lpc_preview_params.refresh=FALSE; #ifdef HAVE_X11_XLIB_H /*printf("wid=%p pwid=%p\n",wid,pwid);*/ if (lpc_preview_params.wid==NULL){ // do not manage window if embedded printf("Refreshing\n"); x11_apply_video_params(&lpc_preview_params,(Window)pwid); } #endif } old_pwid=pwid; } #endif /* * * Called every second from main read loop. * * Will use the following globals: * * - LinphoneCore linphonec * - LPC_AUTH_STACK auth_stack; * */ static int linphonec_idle_call () { LinphoneCore *opm=linphonec; /* Uncomment the following to verify being called */ /* printf(".\n"); */ linphone_core_iterate(opm); if (answer_call){ fprintf (stdout, "-------auto answering to call-------\n" ); linphone_core_accept_call(opm,NULL); answer_call=FALSE; } /* auto call handling */ if (sip_addr_to_call != NULL ) { char buf[512]; snprintf (buf, sizeof(buf),"call %s", sip_addr_to_call); sip_addr_to_call=NULL; linphonec_parse_command_line(linphonec, buf); } if ( auth_stack.nitems ) { /* * Inhibit command completion * during password prompts */ #ifdef HAVE_READLINE rl_inhibit_completion=1; #endif linphonec_prompt_for_auth_final(opm); #ifdef HAVE_READLINE rl_inhibit_completion=0; #endif } #ifdef VIDEO_ENABLED lpc_apply_video_params(); #endif return 0; } #ifdef HAVE_READLINE /* * Use globals: * * - char *histfile_name (also sets this) * - char *last_in_history (allocates it) */ static int linphonec_initialize_readline() { /*rl_bind_key('\t', rl_insert);*/ /* Allow conditional parsing of ~/.inputrc */ rl_readline_name = "linphonec"; /* Call idle_call() every second */ rl_set_keyboard_input_timeout(LPC_READLINE_TIMEOUT); rl_event_hook=linphonec_idle_call; /* Set history file and read it */ histfile_name = ms_strdup_printf ("%s/.linphonec_history", getenv("HOME")); read_history(histfile_name); /* Initialized last_in_history cache*/ last_in_history[0] = '\0'; /* Register a completion function */ rl_attempted_completion_function = linephonec_readline_completion; /* printf("Readline initialized.\n"); */ setlinebuf(stdout); return 0; } /* * Uses globals: * * - char *histfile_name (writes history to file and frees it) * - char *last_in_history (frees it) * */ static int linphonec_finish_readline() { stifle_history(HISTSIZE); write_history(histfile_name); free(histfile_name); histfile_name=NULL; return 0; } #endif static void print_prompt(LinphoneCore *opm){ #ifdef IDENTITY_AS_PROMPT snprintf(prompt, PROMPT_MAX_LEN, "%s> ", linphone_core_get_primary_contact(opm)); #else snprintf(prompt, PROMPT_MAX_LEN, "linphonec> "); #endif } static int linphonec_main_loop (LinphoneCore * opm) { char *input; print_prompt(opm); while (linphonec_running && (input=linphonec_readline(prompt))) { char *iptr; /* input and input pointer */ size_t input_len; /* Strip blanks */ iptr=lpc_strip_blanks(input); input_len = strlen(iptr); /* * Do nothing but release memory * if only blanks are read */ if ( ! input_len ) { free(input); continue; } #ifdef HAVE_READLINE /* * Only add to history if not already * last item in it, and only if the command * doesn't start with a space (to allow for * hiding passwords) */ if ( iptr == input && strcmp(last_in_history, iptr) ) { strncpy(last_in_history,iptr,sizeof(last_in_history)); last_in_history[sizeof(last_in_history)-1]='\0'; add_history(iptr); } #endif linphonec_parse_command_line(linphonec, iptr); linphonec_command_finished(); free(input); } return 0; } /* * Parse command line switches * * Use globals: * * - int trace_level * - char *logfile_name * - char *configfile_name * - char *sipAddr */ static int linphonec_parse_cmdline(int argc, char **argv) { int arg_num=1; while (arg_num < argc) { int old_arg_num = arg_num; if (strncmp ("-d", argv[arg_num], 2) == 0) { arg_num++; if (arg_num < argc) trace_level = atoi (argv[arg_num]); else trace_level = 1; } else if (strncmp ("-l", argv[arg_num], 2) == 0) { arg_num++; if (arg_num < argc) logfile_name = argv[arg_num]; } else if (strncmp ("-c", argv[arg_num], 2) == 0) { if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE); #ifdef _MSC_VER if (strcmp(argv[arg_num], "NUL") != 0) { #endif #if !defined(_WIN32_WCE) if (bctbx_file_exist(argv[arg_num]) != 0) { fprintf(stderr, "Cannot open config file %s.\n", argv[arg_num]); exit(EXIT_FAILURE); } #endif /*_WIN32_WCE*/ #ifdef _MSC_VER } #endif snprintf(configfile_name, PATH_MAX, "%s", argv[arg_num]); } else if (strncmp ("-b", argv[arg_num], 2) == 0) { if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE); #if !defined(_WIN32_WCE) if (bctbx_file_exist(argv[arg_num])!=0 ) { fprintf (stderr, "Cannot open config file %s.\n", argv[arg_num]); exit(EXIT_FAILURE); } #endif /*_WIN32_WCE*/ factory_configfile_name = argv[arg_num]; } else if (strncmp ("-s", argv[arg_num], 2) == 0) { arg_num++; if (arg_num < argc) sip_addr_to_call = argv[arg_num]; } else if (strncmp ("-a", argv[arg_num], 2) == 0) { auto_answer = TRUE; } else if (strncmp ("--real-early-media", argv[arg_num], strlen("--real-early-media")) == 0) { real_early_media_sending = TRUE; } else if (strncmp ("-C", argv[arg_num], 2) == 0) { vcap_enabled = TRUE; } else if (strncmp ("-D", argv[arg_num], 2) == 0) { display_enabled = TRUE; } else if (strncmp ("-V", argv[arg_num], 2) == 0) { display_enabled = TRUE; vcap_enabled = TRUE; preview_enabled=TRUE; } else if ((strncmp ("-v", argv[arg_num], 2) == 0) || (strncmp ("--version", argv[arg_num], strlen ("--version")) == 0)) { #if !defined(_WIN32_WCE) printf ("version: " LINPHONE_VERSION "\n"); #endif exit (EXIT_SUCCESS); } else if (strncmp ("-S", argv[arg_num], 2) == 0) { show_general_state = TRUE; } else if (strncmp ("--pipe", argv[arg_num], 6) == 0) { unix_socket=1; } else if (strncmp ("--wid", argv[arg_num], 5) == 0) { arg_num++; if (arg_num < argc) { char *tmp; window_id = INT_TO_VOIDPTR((int)strtol( argv[arg_num], &tmp, 0 )); lpc_video_params.wid = window_id; } } else if (old_arg_num == arg_num) { fprintf (stderr, "ERROR: bad arguments\n"); print_usage (EXIT_FAILURE); } arg_num++; } return 1; } /* * Up to version 1.2.1 linphone used ~/.linphonec for * CLI and ~/.gnome2/linphone for GUI as configuration file. * In newer version both interfaces will use ~/.linphonerc. * * This function helps transparently migrating from one * to the other layout using the following heuristic: * * IF new_config EXISTS => do nothing * ELSE IF old_cli_config EXISTS => copy to new_config * ELSE IF old_gui_config EXISTS => copy to new_config * * Returns: * 0 if it did nothing * 1 if it migrated successfully * -1 on error */ static int handle_configfile_migration() { #if !defined(_WIN32_WCE) char *old_cfg_gui; char *old_cfg_cli; char *new_cfg; const char *home = getenv("HOME"); new_cfg = ms_strdup_printf("%s/.linphonerc", home); /* * If the *NEW* configuration already exists * do nothing. */ if (bctbx_file_exist(new_cfg)==0) { free(new_cfg); return 0; } old_cfg_cli = ms_strdup_printf("%s/.linphonec", home); /* * If the *OLD* CLI configurations exist copy it to * the new file and make it a symlink. */ if (bctbx_file_exist(old_cfg_cli)==0) { if ( ! copy_file(old_cfg_cli, new_cfg) ) { free(old_cfg_cli); free(new_cfg); return -1; } printf("%s copied to %s\n", old_cfg_cli, new_cfg); free(old_cfg_cli); free(new_cfg); return 1; } free(old_cfg_cli); old_cfg_gui = ms_strdup_printf("%s/.gnome2/linphone", home); /* * If the *OLD* GUI configurations exist copy it to * the new file and make it a symlink. */ if (bctbx_file_exist(old_cfg_gui)==0) { if ( ! copy_file(old_cfg_gui, new_cfg) ) { exit(EXIT_FAILURE); free(old_cfg_gui); free(new_cfg); return -1; } printf("%s copied to %s\n", old_cfg_gui, new_cfg); free(old_cfg_gui); free(new_cfg); return 1; } free(old_cfg_gui); free(new_cfg); #endif /*_WIN32_WCE*/ return 0; } #if !defined(_WIN32_WCE) /* * Copy file "from" to file "to". * Destination file is truncated if existing. * Return 1 on success, 0 on error (printing an error). */ static int copy_file(const char *from, const char *to) { char message[256]; FILE *in, *out; char buf[256]; size_t n; /* Open "from" file for reading */ in=fopen(from, "r"); if ( in == NULL ) { snprintf(message, 255, "Can't open %s for reading: %s\n", from, strerror(errno)); fprintf(stderr, "%s", message); return 0; } /* Open "to" file for writing (will truncate existing files) */ out=fopen(to, "w"); if ( out == NULL ) { snprintf(message, 255, "Can't open %s for writing: %s\n", to, strerror(errno)); fprintf(stderr, "%s", message); fclose(in); return 0; } /* Copy data from "in" to "out" */ while ( (n=fread(buf, 1, sizeof buf, in)) > 0 ) { if ( ! fwrite(buf, 1, n, out) ) { fclose(in); fclose(out); return 0; } } fclose(in); fclose(out); return 1; } #endif /*_WIN32_WCE*/ #ifdef HAVE_READLINE static char ** linephonec_readline_completion(const char *text, int start, int end) { char **matches = NULL; /* * Prevent readline from falling * back to filename-completion */ rl_attempted_completion_over=1; /* * If this is the start of line we complete with commands */ if ( ! start ) { return rl_completion_matches(text, linphonec_command_generator); } /* * Otherwise, we should peek at command name * or context to implement a smart completion. * For example: "call .." could return * friends' sip-uri as matches */ return matches; } #endif /* * Strip blanks from a string. * Return a pointer into the provided string. * Modifies input adding a NULL at first * of trailing blanks. */ char * lpc_strip_blanks(char *input) { char *iptr; /* Find first non-blank */ while(*input && isspace(*input)) ++input; /* Find last non-blank */ iptr=input+strlen(input); if (iptr > input) { while(isspace(*--iptr)); *(iptr+1)='\0'; } return input; } /**************************************************************************** * * $Log: linphonec.c,v $ * Revision 1.57 2007/11/14 13:40:27 smorlat * fix --disable-video build. * * Revision 1.56 2007/09/26 14:07:27 fixkowalski * - ANSI/C++ compilation issues with non-GCC compilers * - Faster epm-based packaging * - Ability to build & run on FC6's eXosip/osip * * Revision 1.55 2007/09/24 16:01:58 smorlat * fix bugs. * * Revision 1.54 2007/08/22 14:06:11 smorlat * authentication bugs fixed. * * Revision 1.53 2007/02/13 21:31:01 smorlat * added patch for general state. * new doxygen for oRTP * gtk-doc removed. * * Revision 1.52 2007/01/10 14:11:24 smorlat * add --video to linphonec. * * Revision 1.51 2006/08/21 12:49:59 smorlat * merged several little patches. * * Revision 1.50 2006/07/26 08:17:28 smorlat * fix bugs. * * Revision 1.49 2006/07/17 18:45:00 smorlat * support for several event queues in ortp. * glib dependency removed from coreapi/ and console/ * * Revision 1.48 2006/04/09 12:45:32 smorlat * linphonec improvements. * * Revision 1.47 2006/04/04 08:04:34 smorlat * switched to mediastreamer2, most bugs fixed. * * Revision 1.46 2006/03/16 17:17:40 smorlat * fix various bugs. * * Revision 1.45 2006/03/12 21:48:31 smorlat * gcc-2.95 compile error fixed. * mediastreamer2 in progress * * Revision 1.44 2006/03/04 11:17:10 smorlat * mediastreamer2 in progress. * * Revision 1.43 2006/02/13 09:50:50 strk * fixed unused variable warning. * * Revision 1.42 2006/02/02 15:39:18 strk * - Added 'friend list' and 'friend call' commands * - Allowed for multiple DTFM send in a single line * - Added status-specific callback (bare version) * * Revision 1.41 2006/02/02 13:30:05 strk * - Padded vtable with missing callbacks * (fixing a segfault on friends subscription) * - Handled friends notify (bare version) * - Handled text messages receive (bare version) * - Printed message on subscription request (bare version) * * Revision 1.40 2006/01/26 09:48:05 strk * Added limits.h include * * Revision 1.39 2006/01/26 02:11:01 strk * Removed unused variables, fixed copyright date * * Revision 1.38 2006/01/25 18:33:02 strk * Removed the -t swich, terminate_on_close made the default behaviour * * Revision 1.37 2006/01/20 14:12:34 strk * Added linphonec_init() and linphonec_finish() functions. * Handled SIGINT and SIGTERM to invoke linphonec_finish(). * Handling of auto-termination (-t) moved to linphonec_finish(). * Reworked main (input read) loop to not rely on 'terminate' * and 'run' variable (dropped). configfile_name allocated on stack * using PATH_MAX limit. Changed print_usage signature to allow * for an exit_status specification. * * Revision 1.36 2006/01/18 09:25:32 strk * Command completion inhibited in proxy addition and auth request prompts. * Avoided use of readline's internal filename completion. * * Revision 1.35 2006/01/14 13:29:32 strk * Reworked commands interface to use a table structure, * used by command line parser and help function. * Implemented first level of completion (commands). * Added notification of invalid "answer" and "terminate" * commands (no incoming call, no active call). * Forbidden "call" intialization when a call is already active. * Cleaned up all commands, adding more feedback and error checks. * * Revision 1.34 2006/01/13 13:00:29 strk * Added linphonec.h. Code layout change (added comments, forward decl, * globals on top, copyright notices and Logs). Handled out-of-memory * condition on history management. Removed assumption on sizeof(char). * Fixed bug in authentication prompt (introduced by readline). * Added support for multiple authentication requests (up to MAX_PENDING_AUTH). * * ****************************************************************************/