1 /****************************************************************************
3 * $Id: linphonec.c,v 1.57 2007/11/14 13:40:27 smorlat Exp $
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>
9 ****************************************************************************
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.
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.
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.
25 ****************************************************************************/
29 #include <sys/types.h>
33 #include "private.h" /*coreapi/private.h, needed for LINPHONE_VERSION */
39 #include <linphonecore.h>
41 #include "linphonec.h"
50 #include <sys/socket.h>
56 #if defined(_WIN32_WCE)
58 #if !defined(PATH_MAX)
63 #define strdup _strdup
71 #define _(String) gettext(String)
74 #define _(something) (something)
78 #define PACKAGE_DIR ""
81 #ifdef HAVE_X11_XLIB_H
85 /***************************************************************************
89 ***************************************************************************/
92 LinphoneAuthInfo *elem[MAX_PENDING_AUTH];
96 /***************************************************************************
98 * Forward declarations
100 ***************************************************************************/
102 char *lpc_strip_blanks(char *input);
104 static int handle_configfile_migration(void);
105 #if !defined(_WIN32_WCE)
106 static int copy_file(const char *from, const char *to);
107 #endif /*_WIN32_WCE*/
108 static int linphonec_parse_cmdline(int argc, char **argv);
109 static int linphonec_init(int argc, char **argv);
110 static int linphonec_main_loop (LinphoneCore * opm);
111 static int linphonec_idle_call (void);
113 static int linphonec_initialize_readline(void);
114 static int linphonec_finish_readline();
115 static char **linephonec_readline_completion(const char *text,
119 /* These are callback for linphone core */
120 static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm,
121 const char *username);
122 static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to);
123 static void linphonec_display_something (LinphoneCore * lc, const char *something);
124 static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url);
125 static void linphonec_display_warning (LinphoneCore * lc, const char *something);
126 static void linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event);
128 static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid);
129 static void linphonec_new_unknown_subscriber(LinphoneCore *lc,
130 LinphoneFriend *lf, const char *url);
132 static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr,
133 const LinphoneAddress *from, const char *msg);
134 static void linphonec_display_status (LinphoneCore * lc, const char *something);
135 static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf);
136 static void print_prompt(LinphoneCore *opm);
137 void linphonec_out(const char *fmt,...);
138 /***************************************************************************
142 ***************************************************************************/
144 LinphoneCore *linphonec;
147 static char *histfile_name=NULL;
148 static char last_in_history[256];
150 //auto answer (-a) option
151 static bool_t auto_answer=FALSE;
152 static bool_t answer_call=FALSE;
153 static bool_t vcap_enabled=FALSE;
154 static bool_t display_enabled=FALSE;
155 static bool_t preview_enabled=FALSE;
156 static bool_t show_general_state=FALSE;
157 static bool_t unix_socket=FALSE;
158 static bool_t linphonec_running=TRUE;
159 LPC_AUTH_STACK auth_stack;
160 static int trace_level = 0;
161 static char *logfile_name = NULL;
162 static char configfile_name[PATH_MAX];
163 static char zrtpsecrets[PATH_MAX];
164 static const char *factory_configfile_name=NULL;
165 static char *sip_addr_to_call = NULL; /* for autocall */
166 static int window_id = 0; /* 0=standalone window, or window id for embedding video */
167 #if !defined(_WIN32_WCE)
168 static ortp_pipe_t client_sock=ORTP_PIPE_INVALID;
169 #endif /*_WIN32_WCE*/
170 char prompt[PROMPT_MAX_LEN];
171 #if !defined(_WIN32_WCE)
172 static ortp_thread_t pipe_reader_th;
173 static bool_t pipe_reader_run=FALSE;
174 #endif /*_WIN32_WCE*/
175 #if !defined(_WIN32_WCE)
176 static ortp_pipe_t server_sock;
177 #endif /*_WIN32_WCE*/
179 bool_t linphonec_camera_enabled=TRUE;
183 void linphonec_call_identify(LinphoneCall* call){
184 static long callid=1;
185 linphone_call_set_user_pointer (call,(void*)callid);
189 LinphoneCall *linphonec_get_call(long id){
190 const MSList *elem=linphone_core_get_calls(linphonec);
191 for (;elem!=NULL;elem=elem->next){
192 LinphoneCall *call=(LinphoneCall*)elem->data;
193 if (linphone_call_get_user_pointer (call)==(void*)id){
197 linphonec_out("Sorry, no call with id %i exists at this time.\n",id);
201 /***************************************************************************
203 * Linphone core callbacks
205 ***************************************************************************/
208 * Linphone core callback
211 linphonec_display_refer (LinphoneCore * lc, const char *refer_to)
213 linphonec_out("Receiving out of call refer to %s\n", refer_to);
217 * Linphone core callback
220 linphonec_display_something (LinphoneCore * lc, const char *something)
222 fprintf (stdout, "%s\n%s", something,prompt);
227 * Linphone core callback
230 linphonec_display_status (LinphoneCore * lc, const char *something)
232 fprintf (stdout, "%s\n%s", something,prompt);
237 * Linphone core callback
240 linphonec_display_warning (LinphoneCore * lc, const char *something)
242 fprintf (stdout, "Warning: %s\n%s", something,prompt);
247 * Linphone core callback
250 linphonec_display_url (LinphoneCore * lc, const char *something, const char *url)
252 fprintf (stdout, "%s : %s\n", something, url);
256 * Linphone core callback
259 linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username)
261 /* no prompt possible when using pipes or tcp mode*/
263 linphone_core_abort_authentication(lc,NULL);
265 LinphoneAuthInfo *pending_auth;
267 if ( auth_stack.nitems+1 > MAX_PENDING_AUTH )
270 "Can't accept another authentication request.\n"
271 "Consider incrementing MAX_PENDING_AUTH macro.\n");
275 pending_auth=linphone_auth_info_new(username,NULL,NULL,NULL,realm);
276 auth_stack.elem[auth_stack.nitems++]=pending_auth;
281 * Linphone core callback
284 linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event)
286 if(!strcmp(event,"refer"))
288 linphonec_out("The distand endpoint %s of call %li has been transfered, you can safely close the call.\n",
289 from,(long)linphone_call_get_user_pointer (call));
295 * Linphone core callback
298 linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid)
300 char *tmp=linphone_address_as_string(linphone_friend_get_address(fid));
301 printf("Friend %s is %s\n", tmp, linphone_online_status_to_string(linphone_friend_get_status(fid)));
303 // todo: update Friend list state (unimplemented)
307 * Linphone core callback
310 linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf,
313 printf("Friend %s requested subscription "
314 "(accept/deny is not implemented yet)\n", url);
315 // This means that this person wishes to be notified
316 // of your presence information (online, busy, away...).
320 static void linphonec_call_updated(LinphoneCall *call){
321 const LinphoneCallParams *cp=linphone_call_get_current_params(call);
322 if (!linphone_call_camera_enabled (call) && linphone_call_params_video_enabled (cp)){
323 linphonec_out("Far end requests to share video.\nType 'camera on' if you agree.\n");
327 static void linphonec_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t encrypted, const char *auth_token) {
328 long id=(long)linphone_call_get_user_pointer (call);
330 linphonec_out("Call %i is not fully encrypted and auth token is %s.\n", id,
331 (auth_token != NULL) ? auth_token : "absent");
333 linphonec_out("Call %i is fully encrypted and auth token is %s.\n", id,
334 (auth_token != NULL) ? auth_token : "absent");
338 static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState st, const char *msg){
339 char *from=linphone_call_get_remote_address_as_string(call);
340 long id=(long)linphone_call_get_user_pointer (call);
342 case LinphoneCallEnd:
343 linphonec_out("Call %i with %s ended (%s).\n", id, from, linphone_reason_to_string(linphone_call_get_reason(call)));
345 case LinphoneCallResuming:
346 linphonec_out("Resuming call %i with %s.\n", id, from);
348 case LinphoneCallStreamsRunning:
349 linphonec_out("Media streams established with %s for call %i (%s).\n", from,id,( linphone_call_params_video_enabled( linphone_call_get_current_params(call)) ? "video":"audio"));
351 case LinphoneCallPausing:
352 linphonec_out("Pausing call %i with %s.\n", id, from);
354 case LinphoneCallPaused:
355 linphonec_out("Call %i with %s is now paused.\n", id, from);
357 case LinphoneCallPausedByRemote:
358 linphonec_out("Call %i has been paused by %s.\n",id,from);
360 case LinphoneCallIncomingReceived:
361 linphonec_call_identify(call);
362 linphone_call_enable_camera (call,linphonec_camera_enabled);
363 id=(long)linphone_call_get_user_pointer (call);
364 linphonec_set_caller(from);
368 linphonec_out("Receiving new incoming call from %s, assigned id %i\n", from,id);
370 case LinphoneCallOutgoingInit:
371 linphonec_call_identify(call);
372 id=(long)linphone_call_get_user_pointer (call);
373 linphonec_out("Establishing call id to %s, assigned id %i\n", from,id);
375 case LinphoneCallUpdatedByRemote:
376 linphonec_call_updated(call);
378 case LinphoneCallOutgoingProgress:
379 linphonec_out("Call %i to %s in progress.\n", id, from);
381 case LinphoneCallOutgoingRinging:
382 linphonec_out("Call %i to %s ringing.\n", id, from);
384 case LinphoneCallConnected:
385 linphonec_out("Call %i with %s connected.\n", id, from);
387 case LinphoneCallOutgoingEarlyMedia:
388 linphonec_out("Call %i with %s early media.\n", id, from);
390 case LinphoneCallError:
391 linphonec_out("Call %i with %s error.\n", id, from);
400 * Linphone core callback
403 linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr,
404 const LinphoneAddress *from, const char *msg)
406 linphonec_out("Message received from %s: %s\n", linphone_address_as_string(from), msg);
407 // TODO: provide mechanism for answering.. ('say' command?)
411 static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf){
412 char *from=linphone_call_get_remote_address_as_string(call);
413 fprintf(stdout,"Receiving tone %c from %s\n",dtmf,from);
418 static char received_prompt[PROMPT_MAX_LEN];
419 static ms_mutex_t prompt_mutex;
420 static bool_t have_prompt=FALSE;
422 static void *prompt_reader_thread(void *arg){
424 char tmp[PROMPT_MAX_LEN];
425 while ((ret=fgets(tmp,sizeof(tmp),stdin))!=NULL){
426 ms_mutex_lock(&prompt_mutex);
427 strcpy(received_prompt,ret);
429 ms_mutex_unlock(&prompt_mutex);
434 static void start_prompt_reader(void){
436 ms_mutex_init(&prompt_mutex,NULL);
437 ortp_thread_create(&th,NULL,prompt_reader_thread,NULL);
439 #if !defined(_WIN32_WCE)
440 static ortp_pipe_t create_server_socket(void){
443 snprintf(path,sizeof(path)-1,"linphonec-%i",getuid());
447 DWORD size=sizeof(username)-1;
448 GetUserName(username,&size);
449 snprintf(path,sizeof(path)-1,"linphonec-%s",username);
452 return ortp_server_pipe_create(path);
456 static void *pipe_thread(void*p){
458 server_sock=create_server_socket();
459 if (server_sock==ORTP_PIPE_INVALID) return NULL;
460 while(pipe_reader_run){
461 while(client_sock!=ORTP_PIPE_INVALID){ /*sleep until the last command is finished*/
468 client_sock=ortp_server_pipe_accept_client(server_sock);
469 if (client_sock!=ORTP_PIPE_INVALID){
471 /*now read from the client */
472 if ((len=ortp_pipe_read(client_sock,(uint8_t*)tmp,sizeof(tmp)-1))>0){
473 ortp_mutex_lock(&prompt_mutex);
475 strcpy(received_prompt,tmp);
476 printf("Receiving command '%s'\n",received_prompt);fflush(stdout);
478 ortp_mutex_unlock(&prompt_mutex);
480 printf("read nothing\n");fflush(stdout);
481 ortp_server_pipe_close_client(client_sock);
482 client_sock=ORTP_PIPE_INVALID;
486 if (pipe_reader_run) fprintf(stderr,"accept() failed: %s\n",strerror(errno));
489 ms_message("Exiting pipe_reader_thread.");
494 static void start_pipe_reader(void){
495 ms_mutex_init(&prompt_mutex,NULL);
496 pipe_reader_run=TRUE;
497 ortp_thread_create(&pipe_reader_th,NULL,pipe_thread,NULL);
500 static void stop_pipe_reader(void){
501 pipe_reader_run=FALSE;
502 linphonec_command_finished();
503 ortp_server_pipe_close(server_sock);
504 ortp_thread_join(pipe_reader_th,NULL);
506 #endif /*_WIN32_WCE*/
509 #define BOOL_HAVE_READLINE 1
511 #define BOOL_HAVE_READLINE 0
514 char *linphonec_readline(char *prompt){
515 if (unix_socket || !BOOL_HAVE_READLINE ){
516 static bool_t prompt_reader_started=FALSE;
517 static bool_t pipe_reader_started=FALSE;
518 if (!prompt_reader_started){
519 start_prompt_reader();
520 prompt_reader_started=TRUE;
522 if (unix_socket && !pipe_reader_started){
523 #if !defined(_WIN32_WCE)
525 pipe_reader_started=TRUE;
526 #endif /*_WIN32_WCE*/
528 fprintf(stdout,"%s",prompt);
531 ms_mutex_lock(&prompt_mutex);
533 char *ret=strdup(received_prompt);
535 ms_mutex_unlock(&prompt_mutex);
538 ms_mutex_unlock(&prompt_mutex);
539 linphonec_idle_call();
542 /* Following is to get the video window going as it
543 should. Maybe should we only have this on when the option -V
547 if (PeekMessage(&msg, NULL, 0, 0,1)) {
548 TranslateMessage(&msg);
549 DispatchMessage(&msg);
557 char* ret=readline(prompt);
563 void linphonec_out(const char *fmt,...){
566 va_start (args, fmt);
567 res=ortp_strdup_vprintf(fmt,args);
571 #if !defined(_WIN32_WCE)
572 if (client_sock!=ORTP_PIPE_INVALID){
573 if (ortp_pipe_write(client_sock,(uint8_t*)res,strlen(res))==-1){
574 fprintf(stderr,"Fail to send output via pipe: %s",strerror(errno));
577 #endif /*_WIN32_WCE*/
581 void linphonec_command_finished(void){
582 #if !defined(_WIN32_WCE)
583 if (client_sock!=ORTP_PIPE_INVALID){
584 ortp_server_pipe_close_client(client_sock);
585 client_sock=ORTP_PIPE_INVALID;
587 #endif /*_WIN32_WCE*/
590 void linphonec_set_autoanswer(bool_t enabled){
594 bool_t linphonec_get_autoanswer(){
598 LinphoneCoreVTable linphonec_vtable={0};
600 /***************************************************************************/
606 * - char *histfile_name
609 #if defined (_WIN32_WCE)
611 char **convert_args_to_ascii(int argc, _TCHAR **wargv){
613 char **result=malloc(argc*sizeof(char*));
616 wcstombs(argtmp,wargv[i],sizeof(argtmp));
617 result[i]=strdup(argtmp);
622 int _tmain(int argc, _TCHAR* wargv[]) {
623 char **argv=convert_args_to_ascii(argc,wargv);
628 main (int argc, char *argv[]) {
630 linphonec_vtable.call_state_changed=linphonec_call_state_changed;
631 linphonec_vtable.notify_presence_recv = linphonec_notify_presence_received;
632 linphonec_vtable.new_subscription_request = linphonec_new_unknown_subscriber;
633 linphonec_vtable.auth_info_requested = linphonec_prompt_for_auth;
634 linphonec_vtable.display_status = linphonec_display_status;
635 linphonec_vtable.display_message=linphonec_display_something;
636 linphonec_vtable.display_warning=linphonec_display_warning;
637 linphonec_vtable.display_url=linphonec_display_url;
638 linphonec_vtable.text_received=linphonec_text_received;
639 linphonec_vtable.dtmf_received=linphonec_dtmf_received;
640 linphonec_vtable.refer_received=linphonec_display_refer;
641 linphonec_vtable.notify_recv=linphonec_notify_received;
642 linphonec_vtable.call_encryption_changed=linphonec_call_encryption_changed;
644 if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE);
646 linphonec_main_loop (linphonec);
648 linphonec_finish(EXIT_SUCCESS);
650 exit(EXIT_SUCCESS); /* should never reach here */
654 * Initialize linphonec
657 linphonec_init(int argc, char **argv)
660 //g_mem_set_vtable(&dbgtable);
663 * Set initial values for global variables
669 snprintf(configfile_name, PATH_MAX, "%s/.linphonerc",
671 snprintf(zrtpsecrets, PATH_MAX, "%s/.linphone-zidcache",
673 #elif defined(_WIN32_WCE)
674 strncpy(configfile_name,PACKAGE_DIR "\\linphonerc",PATH_MAX);
675 mylogfile=fopen(PACKAGE_DIR "\\" "linphonec.log","w");
676 printf("Logs are redirected in" PACKAGE_DIR "\\linphonec.log");
678 snprintf(configfile_name, PATH_MAX, "%s/Linphone/linphonerc",
680 snprintf(zrtpsecrets, PATH_MAX, "%s/Linphone/linphone-zidcache",
683 /* Handle configuration filename changes */
684 switch (handle_configfile_migration())
686 case -1: /* error during file copies */
688 "Error in configuration file migration\n");
691 case 0: /* nothing done */
692 case 1: /* migrated */
698 if (NULL == bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR))
699 perror ("bindtextdomain failed");
701 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
703 textdomain (GETTEXT_PACKAGE);
705 printf ("NLS disabled.\n");
708 linphonec_parse_cmdline(argc, argv);
712 if (logfile_name != NULL)
713 mylogfile = fopen (logfile_name, "w+");
715 if (mylogfile == NULL)
719 "INFO: no logfile, logging to stdout\n");
721 linphone_core_enable_logs(mylogfile);
725 linphone_core_disable_logs();
728 * Initialize auth stack
733 * Initialize linphone core
735 linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL);
736 linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets);
737 linphone_core_enable_video(linphonec,vcap_enabled,display_enabled);
738 if (display_enabled && window_id != 0)
740 printf ("Setting window_id: 0x%x\n", window_id);
741 linphone_core_set_native_video_window_id(linphonec,window_id);
744 linphone_core_enable_video_preview(linphonec,preview_enabled);
745 if (!(vcap_enabled || display_enabled)) printf("Warning: video is disabled in linphonec, use -V or -C or -D to enable.\n");
748 * Initialize readline
750 linphonec_initialize_readline();
752 #if !defined(_WIN32_WCE)
754 * Initialize signal handlers
756 signal(SIGTERM, linphonec_finish);
757 signal(SIGINT, linphonec_finish);
758 #endif /*_WIN32_WCE*/
763 void linphonec_main_loop_exit(void){
764 linphonec_running=FALSE;
768 * Close linphonec, cleanly terminating
772 linphonec_finish(int exit_status)
774 // Do not allow concurrent destroying to prevent glibc errors
775 static bool_t terminating=FALSE;
776 if (terminating) return;
778 linphonec_out("Terminating...\n");
780 /* Terminate any pending call */
781 linphone_core_terminate_all_calls(linphonec);
783 linphonec_finish_readline();
785 #if !defined(_WIN32_WCE)
788 #endif /*_WIN32_WCE*/
790 linphone_core_destroy (linphonec);
792 if (mylogfile != NULL && mylogfile != stdout)
802 * This is called from idle_call() whenever
803 * pending_auth != NULL.
805 * It prompts user for a password.
806 * Hitting ^D (EOF) would make this function
808 * Any other input would try to set linphone core
809 * auth_password for the pending_auth, add the auth_info
813 linphonec_prompt_for_auth_final(LinphoneCore *lc)
815 static int reentrancy=0;
817 char auth_prompt[256];
819 rl_hook_func_t *old_event_hook;
822 if (reentrancy!=0) return 0;
826 LinphoneAuthInfo *pending_auth=auth_stack.elem[auth_stack.nitems-1];
828 snprintf(auth_prompt, 256, "Password for %s on %s: ",
829 pending_auth->username, pending_auth->realm);
834 * Disable event hook to avoid entering an
835 * infinite loop. This would prevent idle_call
836 * from being called during authentication reads.
837 * Note that it might be undesiderable...
839 old_event_hook=rl_event_hook;
845 input=linphonec_readline(auth_prompt);
848 * If EOF (^D) is sent you probably don't want
849 * to provide an auth password... should give up
850 * the operation, but there's no mechanism to
851 * send this info back to caller currently...
855 printf("Cancel requested, but not implemented.\n");
860 iptr=lpc_strip_blanks(input);
863 * Only blanks, continue asking
871 /* Something typed, let's try */
876 * No check is done here to ensure password is correct.
877 * I guess password will be asked again later.
879 linphone_auth_info_set_passwd(pending_auth, input);
880 linphone_core_add_auth_info(lc, pending_auth);
881 linphone_auth_info_destroy(pending_auth);
882 auth_stack.elem[auth_stack.nitems-1]=0;
883 --(auth_stack.nitems);
886 * Reset line_buffer, to avoid the password
887 * to be used again from outer readline
889 rl_line_buffer[0]='\0';
890 rl_event_hook=old_event_hook;
896 print_usage (int exit_status)
898 fprintf (stdout, "\n"
899 "usage: linphonec [-c file] [-s sipaddr] [-a] [-V] [-d level ] [-l logfile]\n"
902 " -b file specify path of readonly factory configuration file.\n"
903 " -c file specify path of configuration file.\n"
904 " -d level be verbose. 0 is no output. 6 is all output\n"
905 " -l logfile specify the log file for your SIP phone\n"
906 " -s sipaddress specify the sip call to do at startup\n"
907 " -a enable auto answering for incoming calls\n"
908 " -V enable video features globally (disabled by default)\n"
909 " -C enable video capture only (disabled by default)\n"
910 " -D enable video display only (disabled by default)\n"
911 " -S show general state messages (disabled by default)\n"
912 " --wid windowid force embedding of video window into provided windowid (disabled by default)\n"
913 " -v or --version display version and exits.\n");
920 #ifdef HAVE_X11_XLIB_H
921 static void x11_apply_video_params(VideoParams *params, Window window){
923 unsigned int flags=0;
924 static Display *display = NULL;
925 const char *dname=getenv("DISPLAY");
927 if (display==NULL && dname!=NULL){
929 display=XOpenDisplay(dname);
933 ms_error("Could not open display %s",dname);
936 memset(&wc,0,sizeof(wc));
945 flags|=CWWidth|CWHeight;
947 /*printf("XConfigureWindow x=%i,y=%i,w=%i,h=%i\n",
948 wc.x, wc.y ,wc.width, wc.height);*/
949 XConfigureWindow(display,window,flags,&wc);
951 XMapWindow(display,window);
953 XUnmapWindow(display,window);
954 XSync(display,FALSE);
959 static void lpc_apply_video_params(){
960 static unsigned long old_wid=0,old_pwid=0;
961 unsigned long wid=linphone_core_get_native_video_window_id (linphonec);
962 unsigned long pwid=linphone_core_get_native_preview_window_id (linphonec);
964 if (wid!=0 && (lpc_video_params.refresh || old_wid!=wid)){
965 lpc_video_params.refresh=FALSE;
966 #ifdef HAVE_X11_XLIB_H
967 if (lpc_video_params.wid==0){ // do not manage window if embedded
968 x11_apply_video_params(&lpc_video_params,wid);
970 linphone_core_show_video(linphonec, lpc_video_params.show);
975 if (pwid!=0 && (lpc_preview_params.refresh || old_pwid!=pwid)){
976 lpc_preview_params.refresh=FALSE;
977 #ifdef HAVE_X11_XLIB_H
978 /*printf("wid=%lu pwid=%lu\n",wid,pwid);*/
979 if (lpc_preview_params.wid==0){ // do not manage window if embedded
980 printf("Refreshing\n");
981 x11_apply_video_params(&lpc_preview_params,pwid);
993 * Called every second from main read loop.
995 * Will use the following globals:
997 * - LinphoneCore linphonec
998 * - LPC_AUTH_STACK auth_stack;
1002 linphonec_idle_call ()
1004 LinphoneCore *opm=linphonec;
1006 /* Uncomment the following to verify being called */
1007 /* printf(".\n"); */
1009 linphone_core_iterate(opm);
1011 fprintf (stdout, "-------auto answering to call-------\n" );
1012 linphone_core_accept_call(opm,NULL);
1015 /* auto call handling */
1016 if (sip_addr_to_call != NULL )
1019 snprintf (buf, sizeof(buf),"call %s", sip_addr_to_call);
1020 sip_addr_to_call=NULL;
1021 linphonec_parse_command_line(linphonec, buf);
1024 if ( auth_stack.nitems )
1027 * Inhibit command completion
1028 * during password prompts
1030 #ifdef HAVE_READLINE
1031 rl_inhibit_completion=1;
1033 linphonec_prompt_for_auth_final(opm);
1034 #ifdef HAVE_READLINE
1035 rl_inhibit_completion=0;
1039 #ifdef VIDEO_ENABLED
1040 lpc_apply_video_params();
1046 #ifdef HAVE_READLINE
1050 * - char *histfile_name (also sets this)
1051 * - char *last_in_history (allocates it)
1054 linphonec_initialize_readline()
1056 /*rl_bind_key('\t', rl_insert);*/
1058 /* Allow conditional parsing of ~/.inputrc */
1059 rl_readline_name = "linphonec";
1061 /* Call idle_call() every second */
1062 rl_set_keyboard_input_timeout(LPC_READLINE_TIMEOUT);
1063 rl_event_hook=linphonec_idle_call;
1065 /* Set history file and read it */
1066 histfile_name = ms_strdup_printf ("%s/.linphonec_history",
1068 read_history(histfile_name);
1070 /* Initialized last_in_history cache*/
1071 last_in_history[0] = '\0';
1073 /* Register a completion function */
1074 rl_attempted_completion_function = linephonec_readline_completion;
1076 /* printf("Readline initialized.\n"); */
1084 * - char *histfile_name (writes history to file and frees it)
1085 * - char *last_in_history (frees it)
1089 linphonec_finish_readline()
1092 stifle_history(HISTSIZE);
1093 write_history(histfile_name);
1094 free(histfile_name);
1101 static void print_prompt(LinphoneCore *opm){
1102 #ifdef IDENTITY_AS_PROMPT
1103 snprintf(prompt, PROMPT_MAX_LEN, "%s> ",
1104 linphone_core_get_primary_contact(opm));
1106 snprintf(prompt, PROMPT_MAX_LEN, "linphonec> ");
1111 linphonec_main_loop (LinphoneCore * opm)
1118 while (linphonec_running && (input=linphonec_readline(prompt)))
1120 char *iptr; /* input and input pointer */
1124 iptr=lpc_strip_blanks(input);
1126 input_len = strlen(iptr);
1129 * Do nothing but release memory
1130 * if only blanks are read
1138 #ifdef HAVE_READLINE
1140 * Only add to history if not already
1141 * last item in it, and only if the command
1142 * doesn't start with a space (to allow for
1145 if ( iptr == input && strcmp(last_in_history, iptr) )
1147 strncpy(last_in_history,iptr,sizeof(last_in_history));
1148 last_in_history[sizeof(last_in_history)-1]='\0';
1153 linphonec_parse_command_line(linphonec, iptr);
1154 linphonec_command_finished();
1162 * Parse command line switches
1167 * - char *logfile_name
1168 * - char *configfile_name
1172 linphonec_parse_cmdline(int argc, char **argv)
1176 while (arg_num < argc)
1178 int old_arg_num = arg_num;
1179 if (strncmp ("-d", argv[arg_num], 2) == 0)
1183 trace_level = atoi (argv[arg_num]);
1187 else if (strncmp ("-l", argv[arg_num], 2) == 0)
1191 logfile_name = argv[arg_num];
1193 else if (strncmp ("-c", argv[arg_num], 2) == 0)
1195 if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE);
1196 #if !defined(_WIN32_WCE)
1197 if (access(argv[arg_num],F_OK)!=0 )
1200 "Cannot open config file %s.\n",
1204 #endif /*_WIN32_WCE*/
1205 snprintf(configfile_name, PATH_MAX, "%s", argv[arg_num]);
1207 else if (strncmp ("-b", argv[arg_num], 2) == 0)
1209 if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE);
1210 #if !defined(_WIN32_WCE)
1211 if (access(argv[arg_num],F_OK)!=0 )
1214 "Cannot open config file %s.\n",
1218 #endif /*_WIN32_WCE*/
1219 factory_configfile_name = argv[arg_num];
1221 else if (strncmp ("-s", argv[arg_num], 2) == 0)
1225 sip_addr_to_call = argv[arg_num];
1227 else if (strncmp ("-a", argv[arg_num], 2) == 0)
1231 else if (strncmp ("-C", argv[arg_num], 2) == 0)
1233 vcap_enabled = TRUE;
1235 else if (strncmp ("-D", argv[arg_num], 2) == 0)
1237 display_enabled = TRUE;
1239 else if (strncmp ("-V", argv[arg_num], 2) == 0)
1241 display_enabled = TRUE;
1242 vcap_enabled = TRUE;
1243 preview_enabled=TRUE;
1245 else if ((strncmp ("-v", argv[arg_num], 2) == 0)
1248 ("--version", argv[arg_num],
1249 strlen ("--version")) == 0))
1251 #if !defined(_WIN32_WCE)
1252 printf ("version: " LINPHONE_VERSION "\n");
1254 exit (EXIT_SUCCESS);
1256 else if (strncmp ("-S", argv[arg_num], 2) == 0)
1258 show_general_state = TRUE;
1260 else if (strncmp ("--pipe", argv[arg_num], 6) == 0)
1264 else if (strncmp ("--wid", argv[arg_num], 5) == 0)
1267 if (arg_num < argc) {
1269 window_id = strtol( argv[arg_num], &tmp, 0 );
1270 lpc_video_params.wid = window_id;
1273 else if (old_arg_num == arg_num)
1275 fprintf (stderr, "ERROR: bad arguments\n");
1276 print_usage (EXIT_FAILURE);
1285 * Up to version 1.2.1 linphone used ~/.linphonec for
1286 * CLI and ~/.gnome2/linphone for GUI as configuration file.
1287 * In newer version both interfaces will use ~/.linphonerc.
1289 * This function helps transparently migrating from one
1290 * to the other layout using the following heuristic:
1292 * IF new_config EXISTS => do nothing
1293 * ELSE IF old_cli_config EXISTS => copy to new_config
1294 * ELSE IF old_gui_config EXISTS => copy to new_config
1297 * 0 if it did nothing
1298 * 1 if it migrated successfully
1302 handle_configfile_migration()
1304 #if !defined(_WIN32_WCE)
1308 #if !defined(_WIN32_WCE)
1309 const char *home = getenv("HOME");
1311 const char *home = ".";
1312 #endif /*_WIN32_WCE*/
1313 new_cfg = ms_strdup_printf("%s/.linphonerc", home);
1316 * If the *NEW* configuration already exists
1319 if (access(new_cfg,F_OK)==0)
1325 old_cfg_cli = ms_strdup_printf("%s/.linphonec", home);
1328 * If the *OLD* CLI configurations exist copy it to
1329 * the new file and make it a symlink.
1331 if (access(old_cfg_cli, F_OK)==0)
1333 if ( ! copy_file(old_cfg_cli, new_cfg) )
1339 printf("%s copied to %s\n", old_cfg_cli, new_cfg);
1346 old_cfg_gui = ms_strdup_printf("%s/.gnome2/linphone", home);
1349 * If the *OLD* GUI configurations exist copy it to
1350 * the new file and make it a symlink.
1352 if (access(old_cfg_gui, F_OK)==0)
1354 if ( ! copy_file(old_cfg_gui, new_cfg) )
1361 printf("%s copied to %s\n", old_cfg_gui, new_cfg);
1369 #endif /*_WIN32_WCE*/
1372 #if !defined(_WIN32_WCE)
1374 * Copy file "from" to file "to".
1375 * Destination file is truncated if existing.
1376 * Return 1 on success, 0 on error (printing an error).
1379 copy_file(const char *from, const char *to)
1386 /* Open "from" file for reading */
1387 in=fopen(from, "r");
1390 snprintf(message, 255, "Can't open %s for reading: %s\n",
1391 from, strerror(errno));
1392 fprintf(stderr, "%s", message);
1396 /* Open "to" file for writing (will truncate existing files) */
1400 snprintf(message, 255, "Can't open %s for writing: %s\n",
1401 to, strerror(errno));
1402 fprintf(stderr, "%s", message);
1407 /* Copy data from "in" to "out" */
1408 while ( (n=fread(buf, 1, sizeof buf, in)) > 0 )
1410 if ( ! fwrite(buf, 1, n, out) )
1423 #endif /*_WIN32_WCE*/
1425 #ifdef HAVE_READLINE
1427 linephonec_readline_completion(const char *text, int start, int end)
1429 char **matches = NULL;
1432 * Prevent readline from falling
1433 * back to filename-completion
1435 rl_attempted_completion_over=1;
1438 * If this is the start of line we complete with commands
1442 return rl_completion_matches(text, linphonec_command_generator);
1446 * Otherwise, we should peek at command name
1447 * or context to implement a smart completion.
1448 * For example: "call .." could return
1449 * friends' sip-uri as matches
1458 * Strip blanks from a string.
1459 * Return a pointer into the provided string.
1460 * Modifies input adding a NULL at first
1461 * of trailing blanks.
1464 lpc_strip_blanks(char *input)
1468 /* Find first non-blank */
1469 while(*input && isspace(*input)) ++input;
1471 /* Find last non-blank */
1472 iptr=input+strlen(input);
1474 while(isspace(*--iptr));
1481 /****************************************************************************
1483 * $Log: linphonec.c,v $
1484 * Revision 1.57 2007/11/14 13:40:27 smorlat
1485 * fix --disable-video build.
1487 * Revision 1.56 2007/09/26 14:07:27 fixkowalski
1488 * - ANSI/C++ compilation issues with non-GCC compilers
1489 * - Faster epm-based packaging
1490 * - Ability to build & run on FC6's eXosip/osip
1492 * Revision 1.55 2007/09/24 16:01:58 smorlat
1495 * Revision 1.54 2007/08/22 14:06:11 smorlat
1496 * authentication bugs fixed.
1498 * Revision 1.53 2007/02/13 21:31:01 smorlat
1499 * added patch for general state.
1500 * new doxygen for oRTP
1503 * Revision 1.52 2007/01/10 14:11:24 smorlat
1504 * add --video to linphonec.
1506 * Revision 1.51 2006/08/21 12:49:59 smorlat
1507 * merged several little patches.
1509 * Revision 1.50 2006/07/26 08:17:28 smorlat
1512 * Revision 1.49 2006/07/17 18:45:00 smorlat
1513 * support for several event queues in ortp.
1514 * glib dependency removed from coreapi/ and console/
1516 * Revision 1.48 2006/04/09 12:45:32 smorlat
1517 * linphonec improvements.
1519 * Revision 1.47 2006/04/04 08:04:34 smorlat
1520 * switched to mediastreamer2, most bugs fixed.
1522 * Revision 1.46 2006/03/16 17:17:40 smorlat
1525 * Revision 1.45 2006/03/12 21:48:31 smorlat
1526 * gcc-2.95 compile error fixed.
1527 * mediastreamer2 in progress
1529 * Revision 1.44 2006/03/04 11:17:10 smorlat
1530 * mediastreamer2 in progress.
1532 * Revision 1.43 2006/02/13 09:50:50 strk
1533 * fixed unused variable warning.
1535 * Revision 1.42 2006/02/02 15:39:18 strk
1536 * - Added 'friend list' and 'friend call' commands
1537 * - Allowed for multiple DTFM send in a single line
1538 * - Added status-specific callback (bare version)
1540 * Revision 1.41 2006/02/02 13:30:05 strk
1541 * - Padded vtable with missing callbacks
1542 * (fixing a segfault on friends subscription)
1543 * - Handled friends notify (bare version)
1544 * - Handled text messages receive (bare version)
1545 * - Printed message on subscription request (bare version)
1547 * Revision 1.40 2006/01/26 09:48:05 strk
1548 * Added limits.h include
1550 * Revision 1.39 2006/01/26 02:11:01 strk
1551 * Removed unused variables, fixed copyright date
1553 * Revision 1.38 2006/01/25 18:33:02 strk
1554 * Removed the -t swich, terminate_on_close made the default behaviour
1556 * Revision 1.37 2006/01/20 14:12:34 strk
1557 * Added linphonec_init() and linphonec_finish() functions.
1558 * Handled SIGINT and SIGTERM to invoke linphonec_finish().
1559 * Handling of auto-termination (-t) moved to linphonec_finish().
1560 * Reworked main (input read) loop to not rely on 'terminate'
1561 * and 'run' variable (dropped). configfile_name allocated on stack
1562 * using PATH_MAX limit. Changed print_usage signature to allow
1563 * for an exit_status specification.
1565 * Revision 1.36 2006/01/18 09:25:32 strk
1566 * Command completion inhibited in proxy addition and auth request prompts.
1567 * Avoided use of readline's internal filename completion.
1569 * Revision 1.35 2006/01/14 13:29:32 strk
1570 * Reworked commands interface to use a table structure,
1571 * used by command line parser and help function.
1572 * Implemented first level of completion (commands).
1573 * Added notification of invalid "answer" and "terminate"
1574 * commands (no incoming call, no active call).
1575 * Forbidden "call" intialization when a call is already active.
1576 * Cleaned up all commands, adding more feedback and error checks.
1578 * Revision 1.34 2006/01/13 13:00:29 strk
1579 * Added linphonec.h. Code layout change (added comments, forward decl,
1580 * globals on top, copyright notices and Logs). Handled out-of-memory
1581 * condition on history management. Removed assumption on sizeof(char).
1582 * Fixed bug in authentication prompt (introduced by readline).
1583 * Added support for multiple authentication requests (up to MAX_PENDING_AUTH).
1586 ****************************************************************************/