text: /****************************************************************************
   *
   *  $Id: linphonec.c,v 1.57 2007/11/14 13:40:27 smorlat Exp $
   *
   *  Copyright (C) 2006  Sandro Santilli <strk@keybit.net>
   *  Copyright (C) 2002  Florian Winterstein <flox@gmx.net>
   *  Copyright (C) 2000  Simon MORLAT <simon.morlat@free.fr>
   *
  ****************************************************************************
   *
   * 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 <string.h>
  #ifndef _WIN32_WCE
  #include <sys/types.h>
  #include <errno.h>
  #include <signal.h>
  #endif /*_WIN32_WCE*/
  #include <limits.h>
  #include <ctype.h>
  #include <stdlib.h>
  
  #include <linphone/core.h>
  
  #include "linphonec.h"
  #include <bctoolbox/vfs.h>
  
  #ifdef _WIN32
  #include <ws2tcpip.h>
  #include <ctype.h>
  #ifndef _WIN32_WCE
  #include <conio.h>
  #endif /*_WIN32_WCE*/
  #else
  #include <sys/socket.h>
  #include <sys/time.h>
  #include <netdb.h>
  #include <sys/un.h>
  #include <sys/stat.h>
  #include <unistd.h>
  #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 <X11/Xlib.h>
  #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_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_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_data (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_data(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_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_data(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_data(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_data(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_data(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_data(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<argc;++i){
  		wcstombs(argtmp,wargv[i],sizeof(argtmp));
  		result[i]=strdup(argtmp);
  	}
  	return result;
  }
  
  int _tmain(int argc, _TCHAR* wargv[]) {
  	char **argv=convert_args_to_ascii(argc,wargv);
  	trace_level=6;
  
  #else
  int
  main (int argc, char *argv[]) {
  #endif
  	linphonec_vtable.call_state_changed=linphonec_call_state_changed;
  	linphonec_vtable.notify_presence_received = linphonec_notify_presence_received;
  	linphonec_vtable.new_subscription_requested = linphonec_new_unknown_subscriber;
  	linphonec_vtable.auth_info_requested = linphonec_prompt_for_auth;
  	linphonec_vtable.text_received=linphonec_text_received;
  	linphonec_vtable.dtmf_received=linphonec_dtmf_received;
  	linphonec_vtable.refer_received=linphonec_display_refer;
  	linphonec_vtable.transfer_state_changed=linphonec_transfer_state_changed;
  	linphonec_vtable.call_encryption_changed=linphonec_call_encryption_changed;
  
  	if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE);
  
  	linphonec_main_loop (linphonec);
  
  	linphonec_finish(EXIT_SUCCESS);
  
  	exit(EXIT_SUCCESS); /* should never reach here */
  }
  
  #ifdef _MSC_VER
  int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  	return main(__argc, __argv);
  }
  #endif
  
  /*
   * Initialize linphonec
   */
  static int
  linphonec_init(int argc, char **argv)
  {
  
  	//g_mem_set_vtable(&dbgtable);
  
  	/*
  	 * Set initial values for global variables
  	 */
  	mylogfile = NULL;
  
  
  #ifndef _WIN32
  	snprintf(configfile_name, PATH_MAX, "%s/.linphonerc",
  			getenv("HOME"));
  	snprintf(zrtpsecrets, PATH_MAX, "%s/.linphone-zidcache",
  			getenv("HOME"));
  	snprintf(usr_certificates_path, PATH_MAX, "%s/.linphone-usr-crt",
  			getenv("HOME"));
  #elif defined(_WIN32_WCE)
  	strncpy(configfile_name,PACKAGE_DIR "\\linphonerc",PATH_MAX);
  	mylogfile=fopen(PACKAGE_DIR "\\" "linphonec.log","w");
  	printf("Logs are redirected in" PACKAGE_DIR "\\linphonec.log");
  #else
  	snprintf(configfile_name, PATH_MAX, "%s/Linphone/linphonerc",
  			getenv("APPDATA"));
  	snprintf(zrtpsecrets, PATH_MAX, "%s/Linphone/linphone-zidcache",
  			getenv("APPDATA"));
  	snprintf(usr_certificates_path, PATH_MAX, "%s/Linphone/linphone-usr-crt",
  			getenv("APPDATA"));
  #endif
  	/* Handle configuration filename changes */
  	switch (handle_configfile_migration())
  	{
  		case -1: /* error during file copies */
  			fprintf(stderr,
  				"Error in configuration file migration\n");
  			break;
  
  		case 0: /* nothing done */
  		case 1: /* migrated */
  		default:
  			break;
  	}
  
  	linphonec_parse_cmdline(argc, argv);
  
  	if (trace_level > 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: ",
  		linphone_auth_info_get_username(pending_auth), linphone_auth_info_get_realm(pending_auth));
  
  	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, linphone_core_get_current_call(opm));
  		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).
   *
   *
   ****************************************************************************/
  
  