linphonec.c 41 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8
/****************************************************************************
 *
 *  $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>
 *
9
****************************************************************************
aymeric's avatar
aymeric committed
10
 *
11 12 13 14
 * 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.
aymeric's avatar
aymeric committed
15
 *
16 17 18 19
 * 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.
aymeric's avatar
aymeric committed
20
 *
21 22 23
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
aymeric's avatar
aymeric committed
24 25
 *
 ****************************************************************************/
Jehan Monnier's avatar
Jehan Monnier committed
26 27
#include <string.h>
#ifndef _WIN32_WCE
aymeric's avatar
aymeric committed
28
#include <sys/types.h>
Jehan Monnier's avatar
Jehan Monnier committed
29
#include <errno.h>
aymeric's avatar
aymeric committed
30
#include <signal.h>
Jehan Monnier's avatar
Jehan Monnier committed
31 32
#include "private.h" /*coreapi/private.h, needed for LINPHONE_VERSION */
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
33
#include <limits.h>
34
#include <ctype.h>
35
#include <stdlib.h>
36

aymeric's avatar
aymeric committed
37
#include <linphonecore.h>
Jehan Monnier's avatar
Jehan Monnier committed
38

aymeric's avatar
aymeric committed
39 40
#include "linphonec.h"

41 42 43
#ifdef WIN32
#include <ws2tcpip.h>
#include <ctype.h>
Jehan Monnier's avatar
Jehan Monnier committed
44
#ifndef _WIN32_WCE
45
#include <conio.h>
Jehan Monnier's avatar
Jehan Monnier committed
46
#endif /*_WIN32_WCE*/
47 48
#else
#include <sys/socket.h>
49
#include <sys/time.h>
50
#include <netdb.h>
51 52
#include <sys/un.h>
#include <sys/stat.h>
53
#include <unistd.h>
54 55
#endif

Jehan Monnier's avatar
Jehan Monnier committed
56 57 58 59
#if !defined(PATH_MAX)
#define PATH_MAX 256
#endif /*PATH_MAX*/

60 61
#if defined(_WIN32_WCE)

Jehan Monnier's avatar
Jehan Monnier committed
62 63 64 65 66
#if !defined(strdup)
#define strdup _strdup
#endif /*strdup*/

#endif /*_WIN32_WCE*/
67

68 69 70 71
#ifndef PACKAGE_DIR
#define PACKAGE_DIR ""
#endif

72 73 74 75
#ifdef HAVE_X11_XLIB_H
#include <X11/Xlib.h>
#endif

aymeric's avatar
aymeric committed
76 77 78 79 80 81 82 83 84 85 86 87 88
/***************************************************************************
 *
 *  Types
 *
 ***************************************************************************/

typedef struct {
	LinphoneAuthInfo *elem[MAX_PENDING_AUTH];
	int nitems;
} LPC_AUTH_STACK;

/***************************************************************************
 *
Jehan Monnier's avatar
Jehan Monnier committed
89
 *  Forward declarations
aymeric's avatar
aymeric committed
90 91 92 93 94 95
 *
 ***************************************************************************/

char *lpc_strip_blanks(char *input);

static int handle_configfile_migration(void);
Jehan Monnier's avatar
Jehan Monnier committed
96
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
97
static int copy_file(const char *from, const char *to);
Jehan Monnier's avatar
Jehan Monnier committed
98
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
99 100
static int linphonec_parse_cmdline(int argc, char **argv);
static int linphonec_init(int argc, char **argv);
101
static int linphonec_main_loop (LinphoneCore * opm);
aymeric's avatar
aymeric committed
102
static int linphonec_idle_call (void);
103 104 105
#ifdef HAVE_READLINE
static int linphonec_initialize_readline(void);
static int linphonec_finish_readline();
aymeric's avatar
aymeric committed
106 107
static char **linephonec_readline_completion(const char *text,
	int start, int end);
108
#endif
aymeric's avatar
aymeric committed
109 110

/* These are callback for linphone core */
111
static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username, const char *domain);
Simon Morlat's avatar
Simon Morlat committed
112
static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to);
aymeric's avatar
aymeric committed
113 114 115
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);
116
static void linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state);
Simon Morlat's avatar
Simon Morlat committed
117

118
static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid);
aymeric's avatar
aymeric committed
119 120
static void linphonec_new_unknown_subscriber(LinphoneCore *lc,
		LinphoneFriend *lf, const char *url);
Simon Morlat's avatar
Simon Morlat committed
121

aymeric's avatar
aymeric committed
122
static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr,
jehan's avatar
jehan committed
123
		const LinphoneAddress *from, const char *msg);
aymeric's avatar
aymeric committed
124
static void linphonec_display_status (LinphoneCore * lc, const char *something);
125
static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf);
aymeric's avatar
aymeric committed
126
static void print_prompt(LinphoneCore *opm);
127
void linphonec_out(const char *fmt,...);
aymeric's avatar
aymeric committed
128 129
/***************************************************************************
 *
Jehan Monnier's avatar
Jehan Monnier committed
130
 * Global variables
aymeric's avatar
aymeric committed
131 132 133
 *
 ***************************************************************************/

134
LinphoneCore *linphonec;
aymeric's avatar
aymeric committed
135
FILE *mylogfile;
136
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
137 138
static char *histfile_name=NULL;
static char last_in_history[256];
139
#endif
aymeric's avatar
aymeric committed
140 141
//auto answer (-a) option
static bool_t auto_answer=FALSE;
142
static bool_t real_early_media_sending=FALSE;
aymeric's avatar
aymeric committed
143 144 145
static bool_t answer_call=FALSE;
static bool_t vcap_enabled=FALSE;
static bool_t display_enabled=FALSE;
146
static bool_t preview_enabled=FALSE;
aymeric's avatar
aymeric committed
147
static bool_t show_general_state=FALSE;
148
static bool_t unix_socket=FALSE;
149
static bool_t linphonec_running=TRUE;
aymeric's avatar
aymeric committed
150 151 152 153
LPC_AUTH_STACK auth_stack;
static int trace_level = 0;
static char *logfile_name = NULL;
static char configfile_name[PATH_MAX];
Simon Morlat's avatar
Simon Morlat committed
154
static char zrtpsecrets[PATH_MAX];
155
static char usr_certificates_path[PATH_MAX];
156
static const char *factory_configfile_name=NULL;
157
static char *sip_addr_to_call = NULL; /* for autocall */
158
static void *window_id = NULL; /* NULL=standalone window, or window id for embedding video */
Jehan Monnier's avatar
Jehan Monnier committed
159
#if !defined(_WIN32_WCE)
160
static ortp_pipe_t client_sock=ORTP_PIPE_INVALID;
Jehan Monnier's avatar
Jehan Monnier committed
161
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
162
char prompt[PROMPT_MAX_LEN];
Jehan Monnier's avatar
Jehan Monnier committed
163
#if !defined(_WIN32_WCE)
164 165
static ortp_thread_t pipe_reader_th;
static bool_t pipe_reader_run=FALSE;
Jehan Monnier's avatar
Jehan Monnier committed
166 167
#endif /*_WIN32_WCE*/
#if !defined(_WIN32_WCE)
168
static ortp_pipe_t server_sock;
Jehan Monnier's avatar
Jehan Monnier committed
169
#endif /*_WIN32_WCE*/
170

171 172
bool_t linphonec_camera_enabled=TRUE;

aymeric's avatar
aymeric committed
173

174

175 176 177 178 179 180 181 182 183 184 185 186 187 188
void linphonec_call_identify(LinphoneCall* call){
	static long callid=1;
	linphone_call_set_user_pointer (call,(void*)callid);
	callid++;
}

LinphoneCall *linphonec_get_call(long id){
	const MSList *elem=linphone_core_get_calls(linphonec);
	for (;elem!=NULL;elem=elem->next){
		LinphoneCall *call=(LinphoneCall*)elem->data;
		if (linphone_call_get_user_pointer (call)==(void*)id){
			return call;
		}
	}
Simon Morlat's avatar
Simon Morlat committed
189
	linphonec_out("Sorry, no call with id %i exists at this time.\n",id);
190 191 192
	return NULL;
}

aymeric's avatar
aymeric committed
193 194 195 196 197 198
/***************************************************************************
 *
 * Linphone core callbacks
 *
 ***************************************************************************/

199 200 201 202
/*
 * Linphone core callback
 */
static void
Simon Morlat's avatar
Simon Morlat committed
203
linphonec_display_refer (LinphoneCore * lc, const char *refer_to)
204
{
Simon Morlat's avatar
Simon Morlat committed
205
	linphonec_out("Receiving out of call refer to %s\n", refer_to);
206 207
}

aymeric's avatar
aymeric committed
208
/*
Jehan Monnier's avatar
Jehan Monnier committed
209
 * Linphone core callback
aymeric's avatar
aymeric committed
210 211 212 213 214 215 216 217 218
 */
static void
linphonec_display_something (LinphoneCore * lc, const char *something)
{
	fprintf (stdout, "%s\n%s", something,prompt);
	fflush(stdout);
}

/*
Jehan Monnier's avatar
Jehan Monnier committed
219
 * Linphone core callback
aymeric's avatar
aymeric committed
220 221 222 223 224 225 226 227 228
 */
static void
linphonec_display_status (LinphoneCore * lc, const char *something)
{
	fprintf (stdout, "%s\n%s", something,prompt);
	fflush(stdout);
}

/*
Jehan Monnier's avatar
Jehan Monnier committed
229
 * Linphone core callback
aymeric's avatar
aymeric committed
230 231 232 233 234 235 236 237 238
 */
static void
linphonec_display_warning (LinphoneCore * lc, const char *something)
{
	fprintf (stdout, "Warning: %s\n%s", something,prompt);
	fflush(stdout);
}

/*
Jehan Monnier's avatar
Jehan Monnier committed
239
 * Linphone core callback
aymeric's avatar
aymeric committed
240 241 242 243 244 245 246 247
 */
static void
linphonec_display_url (LinphoneCore * lc, const char *something, const char *url)
{
	fprintf (stdout, "%s : %s\n", something, url);
}

/*
Jehan Monnier's avatar
Jehan Monnier committed
248
 * Linphone core callback
aymeric's avatar
aymeric committed
249 250
 */
static void
251
linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username, const char *domain)
aymeric's avatar
aymeric committed
252
{
253
	/* no prompt possible when using pipes or tcp mode*/
254
	if (unix_socket){
255 256 257
		linphone_core_abort_authentication(lc,NULL);
	}else{
		LinphoneAuthInfo *pending_auth;
Jehan Monnier's avatar
Jehan Monnier committed
258

259 260 261 262 263 264
		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;
Jehan Monnier's avatar
Jehan Monnier committed
265 266
		}

267
		pending_auth=linphone_auth_info_new(username,NULL,NULL,NULL,realm,domain);
268 269
		auth_stack.elem[auth_stack.nitems++]=pending_auth;
	}
aymeric's avatar
aymeric committed
270 271 272 273 274 275
}

/*
 * Linphone core callback
 */
static void
276
linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state)
277
{
278 279 280 281
	char *remote=linphone_call_get_remote_address_as_string(call);
	if (new_call_state==LinphoneCallConnected){
		linphonec_out("The distant endpoint %s of call %li has been transfered, you can safely close the call.\n",
		              remote,(long)linphone_call_get_user_pointer (call));
282
	}
283
	ms_free(remote);
284 285
}

Simon Morlat's avatar
Simon Morlat committed
286

287 288 289 290 291
/*
 * Linphone core callback
 */
static void
linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid)
aymeric's avatar
aymeric committed
292
{
293 294 295
	char *tmp=linphone_address_as_string(linphone_friend_get_address(fid));
	printf("Friend %s is %s\n", tmp, linphone_online_status_to_string(linphone_friend_get_status(fid)));
	ms_free(tmp);
aymeric's avatar
aymeric committed
296 297 298 299 300 301 302 303 304 305 306
	// 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 "
Jehan Monnier's avatar
Jehan Monnier committed
307 308
		"(accept/deny is not implemented yet)\n", url);
	// This means that this person wishes to be notified
aymeric's avatar
aymeric committed
309 310 311 312
	// of your presence information (online, busy, away...).

}

313 314 315
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)){
Simon Morlat's avatar
Simon Morlat committed
316
		linphonec_out("Far end requests to share video.\nType 'camera on' if you agree.\n");
317 318 319
	}
}

320 321 322
static void linphonec_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t encrypted, const char *auth_token) {
	long id=(long)linphone_call_get_user_pointer (call);
	if (!encrypted) {
323 324
		linphonec_out("Call %i is not fully encrypted and auth token is %s.\n", id,
				(auth_token != NULL) ? auth_token : "absent");
325
	} else {
326
		linphonec_out("Call %i is fully encrypted and auth token is %s.\n", id,
327 328 329 330
				(auth_token != NULL) ? auth_token : "absent");
	}
}

Simon Morlat's avatar
Simon Morlat committed
331
static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState st, const char *msg){
332
	char *from=linphone_call_get_remote_address_as_string(call);
333
	long id=(long)linphone_call_get_user_pointer (call);
Simon Morlat's avatar
Simon Morlat committed
334 335
	switch(st){
		case LinphoneCallEnd:
336
			linphonec_out("Call %i with %s ended (%s).\n", id, from, linphone_reason_to_string(linphone_call_get_reason(call)));
Simon Morlat's avatar
Simon Morlat committed
337 338
		break;
		case LinphoneCallResuming:
339
			linphonec_out("Resuming call %i with %s.\n", id, from);
Simon Morlat's avatar
Simon Morlat committed
340 341
		break;
		case LinphoneCallStreamsRunning:
342
			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"));
Simon Morlat's avatar
Simon Morlat committed
343 344
		break;
		case LinphoneCallPausing:
345
			linphonec_out("Pausing call %i with %s.\n", id, from);
Simon Morlat's avatar
Simon Morlat committed
346 347
		break;
		case LinphoneCallPaused:
348
			linphonec_out("Call %i with %s is now paused.\n", id, from);
Simon Morlat's avatar
Simon Morlat committed
349
		break;
350 351 352
		case LinphoneCallPausedByRemote:
			linphonec_out("Call %i has been paused by %s.\n",id,from);
		break;
Simon Morlat's avatar
Simon Morlat committed
353
		case LinphoneCallIncomingReceived:
354
			linphonec_call_identify(call);
355
			linphone_call_enable_camera (call,linphonec_camera_enabled);
356
			id=(long)linphone_call_get_user_pointer (call);
Simon Morlat's avatar
Simon Morlat committed
357
			linphonec_set_caller(from);
358
			linphonec_out("Receiving new incoming call from %s, assigned id %i\n", from,id);
Simon Morlat's avatar
Simon Morlat committed
359 360
			if ( auto_answer)  {
				answer_call=TRUE;
361
			} else if (real_early_media_sending) {
362
				LinphoneCallParams* callparams = linphone_core_create_call_params(lc, call);
363
				linphonec_out("Sending early media using real hardware\n");
364 365 366 367
				linphone_call_params_enable_early_media_sending(callparams, TRUE);
				if (vcap_enabled) linphone_call_params_enable_video(callparams, TRUE);
				linphone_core_accept_early_media_with_params(lc, call, callparams);
				linphone_call_params_destroy(callparams);
Simon Morlat's avatar
Simon Morlat committed
368
			}
369 370 371
		break;
		case LinphoneCallOutgoingInit:
			linphonec_call_identify(call);
372 373
			id=(long)linphone_call_get_user_pointer (call);
			linphonec_out("Establishing call id to %s, assigned id %i\n", from,id);
Simon Morlat's avatar
Simon Morlat committed
374
		break;
375 376 377
		case LinphoneCallUpdatedByRemote:
			linphonec_call_updated(call);
		break;
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
		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;
Simon Morlat's avatar
Simon Morlat committed
393 394 395
		default:
		break;
	}
396
	ms_free(from);
aymeric's avatar
aymeric committed
397 398 399 400 401 402 403
}

/*
 * Linphone core callback
 */
static void
linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr,
jehan's avatar
jehan committed
404
		const LinphoneAddress *from, const char *msg)
aymeric's avatar
aymeric committed
405
{
406
	linphonec_out("Message received from %s: %s\n", linphone_address_as_string(from), msg);
aymeric's avatar
aymeric committed
407 408 409 410
	// TODO: provide mechanism for answering.. ('say' command?)
}


411 412 413
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);
414
	fflush(stdout);
415
	ms_free(from);
416 417
}

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
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);
}
Jehan Monnier's avatar
Jehan Monnier committed
439
#if !defined(_WIN32_WCE)
440 441
static ortp_pipe_t create_server_socket(void){
	char path[128];
442
#ifndef WIN32
443
	snprintf(path,sizeof(path)-1,"linphonec-%i",getuid());
444
#else
445
	{
Jehan Monnier's avatar
Jehan Monnier committed
446
		TCHAR username[128];
447 448 449
		DWORD size=sizeof(username)-1;
		GetUserName(username,&size);
		snprintf(path,sizeof(path)-1,"linphonec-%s",username);
450
	}
451 452
#endif
	return ortp_server_pipe_create(path);
453 454
}

Jehan Monnier's avatar
Jehan Monnier committed
455

456
static void *pipe_thread(void*p){
457
	char tmp[250];
458 459 460 461
	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*/
462 463 464 465 466 467
#ifndef WIN32
			usleep(20000);
#else
			Sleep(20);
#endif
		}
468 469
		client_sock=ortp_server_pipe_accept_client(server_sock);
		if (client_sock!=ORTP_PIPE_INVALID){
470 471
			int len;
			/*now read from the client */
smorlat's avatar
smorlat committed
472
			if ((len=ortp_pipe_read(client_sock,(uint8_t*)tmp,sizeof(tmp)-1))>0){
473 474 475 476 477 478 479 480
				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);
481 482
				ortp_server_pipe_close_client(client_sock);
				client_sock=ORTP_PIPE_INVALID;
483
			}
Jehan Monnier's avatar
Jehan Monnier committed
484

485
		}else{
486
			if (pipe_reader_run) fprintf(stderr,"accept() failed: %s\n",strerror(errno));
487 488
		}
	}
489
	ms_message("Exiting pipe_reader_thread.");
490
	fflush(stdout);
491 492 493
	return NULL;
}

494 495 496
static void start_pipe_reader(void){
	pipe_reader_run=TRUE;
	ortp_thread_create(&pipe_reader_th,NULL,pipe_thread,NULL);
497 498
}

499 500
static void stop_pipe_reader(void){
	pipe_reader_run=FALSE;
501
	linphonec_command_finished();
502 503
	ortp_server_pipe_close(server_sock);
	ortp_thread_join(pipe_reader_th,NULL);
504
}
Jehan Monnier's avatar
Jehan Monnier committed
505
#endif /*_WIN32_WCE*/
506

507 508 509 510
#ifdef HAVE_READLINE
#define BOOL_HAVE_READLINE 1
#else
#define BOOL_HAVE_READLINE 0
511 512 513
#endif

char *linphonec_readline(char *prompt){
514
	if (unix_socket || !BOOL_HAVE_READLINE ){
515
		static bool_t prompt_reader_started=FALSE;
516
		static bool_t pipe_reader_started=FALSE;
517 518 519
		if (!prompt_reader_started){
			start_prompt_reader();
			prompt_reader_started=TRUE;
520
		}
521
		if (unix_socket && !pipe_reader_started){
Jehan Monnier's avatar
Jehan Monnier committed
522
#if !defined(_WIN32_WCE)
523 524
			start_pipe_reader();
			pipe_reader_started=TRUE;
Jehan Monnier's avatar
Jehan Monnier committed
525
#endif /*_WIN32_WCE*/
526 527 528 529
		}
		fprintf(stdout,"%s",prompt);
		fflush(stdout);
		while(1){
530

531 532 533 534 535 536 537 538 539
			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();
540
#ifdef WIN32
541 542 543 544 545 546 547 548 549 550 551
			{
				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);
				}
552
			}
553
#else
554
			usleep(20000);
555
#endif
556 557 558
		}
	}else{
#ifdef HAVE_READLINE
559 560
		char* ret=readline(prompt);
		return ret;
561
#endif
562
	}
563
}
aymeric's avatar
aymeric committed
564

565 566 567 568 569 570
void linphonec_out(const char *fmt,...){
	char *res;
	va_list args;
	va_start (args, fmt);
	res=ortp_strdup_vprintf(fmt,args);
	va_end (args);
571 572
	printf("%s",res);
	fflush(stdout);
Jehan Monnier's avatar
Jehan Monnier committed
573
#if !defined(_WIN32_WCE)
574
	if (client_sock!=ORTP_PIPE_INVALID){
smorlat's avatar
smorlat committed
575
		if (ortp_pipe_write(client_sock,(uint8_t*)res,strlen(res))==-1){
576
			fprintf(stderr,"Fail to send output via pipe: %s",strerror(errno));
577 578
		}
	}
Jehan Monnier's avatar
Jehan Monnier committed
579
#endif /*_WIN32_WCE*/
580 581 582 583
	ortp_free(res);
}

void linphonec_command_finished(void){
Jehan Monnier's avatar
Jehan Monnier committed
584
#if !defined(_WIN32_WCE)
585 586 587
	if (client_sock!=ORTP_PIPE_INVALID){
		ortp_server_pipe_close_client(client_sock);
		client_sock=ORTP_PIPE_INVALID;
588
	}
Jehan Monnier's avatar
Jehan Monnier committed
589
#endif /*_WIN32_WCE*/
590 591
}

smorlat's avatar
smorlat committed
592 593 594
void linphonec_set_autoanswer(bool_t enabled){
	auto_answer=enabled;
}
595

596
bool_t linphonec_get_autoanswer(void){
smorlat's avatar
smorlat committed
597 598
	return auto_answer;
}
599

Simon Morlat's avatar
Simon Morlat committed
600 601
LinphoneCoreVTable linphonec_vtable={0};

aymeric's avatar
aymeric committed
602 603 604 605 606 607 608 609 610
/***************************************************************************/
/*
 * Main
 *
 * Use globals:
 *
 *	- char *histfile_name
 *	- FILE *mylogfile
 */
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
#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;
Simon Morlat's avatar
Simon Morlat committed
627 628 629 630 631 632

#else
int
main (int argc, char *argv[]) {
#endif
	linphonec_vtable.call_state_changed=linphonec_call_state_changed;
633 634
	linphonec_vtable.notify_presence_received = linphonec_notify_presence_received;
	linphonec_vtable.new_subscription_requested = linphonec_new_unknown_subscriber;
Jehan Monnier's avatar
Jehan Monnier committed
635 636 637 638 639 640 641
	linphonec_vtable.auth_info_requested = linphonec_prompt_for_auth;
	linphonec_vtable.display_status = linphonec_display_status;
	linphonec_vtable.display_message=linphonec_display_something;
	linphonec_vtable.display_warning=linphonec_display_warning;
	linphonec_vtable.display_url=linphonec_display_url;
	linphonec_vtable.text_received=linphonec_text_received;
	linphonec_vtable.dtmf_received=linphonec_dtmf_received;
Simon Morlat's avatar
Simon Morlat committed
642
	linphonec_vtable.refer_received=linphonec_display_refer;
643
	linphonec_vtable.transfer_state_changed=linphonec_transfer_state_changed;
644
	linphonec_vtable.call_encryption_changed=linphonec_call_encryption_changed;
645

aymeric's avatar
aymeric committed
646 647
	if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE);

648
	linphonec_main_loop (linphonec);
aymeric's avatar
aymeric committed
649 650 651 652 653 654

	linphonec_finish(EXIT_SUCCESS);

	exit(EXIT_SUCCESS); /* should never reach here */
}

655 656 657 658 659 660
#ifdef _MSC_VER
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	return main(__argc, __argv);
}
#endif

aymeric's avatar
aymeric committed
661 662 663 664 665 666 667 668 669 670 671 672 673
/*
 * Initialize linphonec
 */
static int
linphonec_init(int argc, char **argv)
{

	//g_mem_set_vtable(&dbgtable);

	/*
	 * Set initial values for global variables
	 */
	mylogfile = NULL;
674 675


676
#ifndef _WIN32
Jehan Monnier's avatar
Jehan Monnier committed
677 678
	snprintf(configfile_name, PATH_MAX, "%s/.linphonerc",
			getenv("HOME"));
Simon Morlat's avatar
Simon Morlat committed
679 680
	snprintf(zrtpsecrets, PATH_MAX, "%s/.linphone-zidcache",
			getenv("HOME"));
681 682
	snprintf(usr_certificates_path, PATH_MAX, "%s/.linphone-usr-crt",
			getenv("HOME"));
683 684 685 686
#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");
Jehan Monnier's avatar
Jehan Monnier committed
687
#else
688 689
	snprintf(configfile_name, PATH_MAX, "%s/Linphone/linphonerc",
			getenv("APPDATA"));
Simon Morlat's avatar
Simon Morlat committed
690 691
	snprintf(zrtpsecrets, PATH_MAX, "%s/Linphone/linphone-zidcache",
			getenv("APPDATA"));
692 693
	snprintf(usr_certificates_path, PATH_MAX, "%s/Linphone/linphone-usr-crt",
			getenv("APPDATA"));
694
#endif
aymeric's avatar
aymeric committed
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
	/* 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
	 */
736
	linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL);
737 738

	linphone_core_set_user_agent(linphonec,"Linphonec", LINPHONE_VERSION);
Simon Morlat's avatar
Simon Morlat committed
739
	linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets);
740
	linphone_core_set_user_certificates_path(linphonec,usr_certificates_path);
741 742
	linphone_core_enable_video_capture(linphonec, vcap_enabled);
	linphone_core_enable_video_display(linphonec, display_enabled);
743
	if (display_enabled && (window_id != NULL))
744
	{
745
		printf("Setting window_id: 0x%p\n", window_id);
746 747 748
		linphone_core_set_native_video_window_id(linphonec,window_id);
	}

749
	linphone_core_enable_video_preview(linphonec,preview_enabled);
750
	if (!(vcap_enabled || display_enabled)) printf("Warning: video is disabled in linphonec, use -V or -C or -D to enable.\n");
751
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
752 753 754 755
	/*
	 * Initialize readline
	 */
	linphonec_initialize_readline();
756
#endif
Jehan Monnier's avatar
Jehan Monnier committed
757
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
758 759 760
	/*
	 * Initialize signal handlers
	 */
Jehan Monnier's avatar
Jehan Monnier committed
761 762 763
	signal(SIGTERM, linphonec_finish);
	signal(SIGINT, linphonec_finish);
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
764 765 766 767
	return 1;
}


768 769 770 771
void linphonec_main_loop_exit(void){
	linphonec_running=FALSE;
}

aymeric's avatar
aymeric committed
772 773 774 775 776 777 778
/*
 * Close linphonec, cleanly terminating
 * any pending call
 */
void
linphonec_finish(int exit_status)
{
Simon Morlat's avatar
Simon Morlat committed
779 780
	// Do not allow concurrent destroying to prevent glibc errors
	static bool_t terminating=FALSE;
781
	if (terminating) return;
Simon Morlat's avatar
Simon Morlat committed
782
	terminating=TRUE;
783
	linphonec_out("Terminating...\n");
Jehan Monnier's avatar
Jehan Monnier committed
784

aymeric's avatar
aymeric committed
785
	/* Terminate any pending call */
786
	linphone_core_terminate_all_calls(linphonec);
787
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
788
	linphonec_finish_readline();
789
#endif
Jehan Monnier's avatar
Jehan Monnier committed
790
#if !defined(_WIN32_WCE)
791 792
	if (pipe_reader_run)
		stop_pipe_reader();
Jehan Monnier's avatar
Jehan Monnier committed
793
#endif /*_WIN32_WCE*/
strk's avatar
strk committed
794

795
	linphone_core_destroy (linphonec);
aymeric's avatar
aymeric committed
796 797 798 799

	if (mylogfile != NULL && mylogfile != stdout)
	{
		fclose (mylogfile);
800
		mylogfile=stdout;
aymeric's avatar
aymeric committed
801
	}
802
	printf("\n");
aymeric's avatar
aymeric committed
803 804 805 806 807 808 809 810 811
	exit(exit_status);

}

/*
 * This is called from idle_call() whenever
 * pending_auth != NULL.
 *
 * It prompts user for a password.
Jehan Monnier's avatar
Jehan Monnier committed
812
 * Hitting ^D (EOF) would make this function
aymeric's avatar
aymeric committed
813 814 815 816 817 818 819 820
 * 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)
{
821
	static int reentrancy=0;
aymeric's avatar
aymeric committed
822 823
	char *input, *iptr;
	char auth_prompt[256];
824
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
825
	rl_hook_func_t *old_event_hook;
826
#endif
827
	LinphoneAuthInfo *pending_auth;
828 829

	if (reentrancy!=0) return 0;
830

831
	reentrancy++;
832

833
	pending_auth=auth_stack.elem[auth_stack.nitems-1];
aymeric's avatar
aymeric committed
834 835 836 837 838

	snprintf(auth_prompt, 256, "Password for %s on %s: ",
		pending_auth->username, pending_auth->realm);

	printf("\n");
839
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
840 841 842 843 844 845 846 847
	/*
	 * 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;
848
#endif
aymeric's avatar
aymeric committed
849 850 851

	while (1)
	{
852
		input=linphonec_readline(auth_prompt);
aymeric's avatar
aymeric committed
853 854 855 856 857 858 859 860 861

		/*
		 * 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 )
		{
Jehan Monnier's avatar
Jehan Monnier committed
862
			printf("Cancel requested, but not implemented.\n");
aymeric's avatar
aymeric committed
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
			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);
888 889
	linphone_auth_info_destroy(pending_auth);
	auth_stack.elem[auth_stack.nitems-1]=0;
aymeric's avatar
aymeric committed
890
	--(auth_stack.nitems);
891
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
892 893 894 895 896 897
	/*
	 * 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;
898
#endif
aymeric's avatar
aymeric committed
899 900 901 902 903 904
	return 1;
}

void
print_usage (int exit_status)
{
905 906 907 908 909 910 911 912 913 914
	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"
915
"  --real-early-media   enable sending early media using real audio/video (beware of privacy issue)\n"
916 917 918 919 920
"  -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"
921 922
"  -v or --version      display version and exits.\n"
);
aymeric's avatar
aymeric committed
923 924 925 926

  	exit(exit_status);
}

927 928 929
#ifdef VIDEO_ENABLED

#ifdef HAVE_X11_XLIB_H
930 931 932 933 934 935 936
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){
937
		XInitThreads();
938
		display=XOpenDisplay(dname);
939
	}
940 941 942 943

	if (display==NULL){
		ms_error("Could not open display %s",dname);
		return;
944
	}
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
	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);
964 965 966 967
}
#endif


968
static void lpc_apply_video_params(void){
969 970 971 972
	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);
Simon Morlat's avatar
Simon Morlat committed
973

974
	if (wid!=NULL && (lpc_video_params.refresh || old_wid!=wid)){
Simon Morlat's avatar
Simon Morlat committed
975
		lpc_video_params.refresh=FALSE;
976
#ifdef HAVE_X11_XLIB_H
Simon Morlat's avatar
Simon Morlat committed
977
		if (lpc_video_params.wid==0){  // do not manage window if embedded
978
			x11_apply_video_params(&lpc_video_params,(Window)wid);
979 980
		} else {
		        linphone_core_show_video(linphonec, lpc_video_params.show);
981 982 983 984
		}
#endif
	}
	old_wid=wid;
985
	if (pwid!=NULL && (lpc_preview_params.refresh || old_pwid!=pwid)){
986 987
		lpc_preview_params.refresh=FALSE;
#ifdef HAVE_X11_XLIB_H
988 989
		/*printf("wid=%p pwid=%p\n",wid,pwid);*/
		if (lpc_preview_params.wid==NULL){  // do not manage window if embedded
990
			printf("Refreshing\n");
991
			x11_apply_video_params(&lpc_preview_params,(Window)pwid);
Simon Morlat's avatar
Simon Morlat committed
992
		}
993 994
#endif
	}
995
	old_pwid=pwid;
996 997 998 999
}

#endif

aymeric's avatar
aymeric committed
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013

/*
 *
 * Called every second from main read loop.
 *
 * Will use the following globals:
 *
 *  - LinphoneCore linphonec
 *  - LPC_AUTH_STACK auth_stack;
 *
 */
static int
linphonec_idle_call ()
{
1014
	LinphoneCore *opm=linphonec;
aymeric's avatar
aymeric committed
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024

	/* 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;
	}
1025 1026 1027 1028 1029 1030 1031 1032
	/* 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);
	}
aymeric's avatar
aymeric committed
1033 1034 1035 1036 1037 1038 1039

	if ( auth_stack.nitems )
	{
		/*
		 * Inhibit command completion
		 * during password prompts
		 */
1040
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
1041
		rl_inhibit_completion=1;
1042
#endif
aymeric's avatar
aymeric committed
1043
		linphonec_prompt_for_auth_final(opm);
1044
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
1045
		rl_inhibit_completion=0;
1046
#endif
aymeric's avatar
aymeric committed
1047 1048
	}

1049 1050 1051 1052
#ifdef VIDEO_ENABLED
	lpc_apply_video_params();
#endif

aymeric's avatar
aymeric committed
1053 1054 1055
	return 0;
}

1056
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
/*
 * 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 */
Jehan Monnier's avatar
Jehan Monnier committed
1072
	rl_set_keyboard_input_timeout(LPC_READLINE_TIMEOUT);
aymeric's avatar
aymeric committed
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086
	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"); */
1087
	setlinebuf(stdout);
aymeric's avatar
aymeric committed
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
	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;
}

1109 1110
#endif

aymeric's avatar
aymeric committed
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
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
1121
linphonec_main_loop (LinphoneCore * opm)
aymeric's avatar
aymeric committed
1122 1123 1124 1125 1126 1127
{
	char *input;

	print_prompt(opm);


1128
	while (linphonec_running && (input=linphonec_readline(prompt)))
aymeric's avatar
aymeric committed
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147
	{
		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;
		}

1148
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
1149 1150
		/*
		 * Only add to history if not already
1151 1152 1153
		 * last item in it, and only if the command
		 * doesn't start with a space (to allow for
		 * hiding passwords)
aymeric's avatar
aymeric committed
1154
		 */
1155
		if ( iptr == input && strcmp(last_in_history, iptr) )
aymeric's avatar
aymeric committed
1156 1157 1158 1159 1160
		{
			strncpy(last_in_history,iptr,sizeof(last_in_history));
			last_in_history[sizeof(last_in_history)-1]='\0';
			add_history(iptr);
		}
1161
#endif
1162
		linphonec_parse_command_line(linphonec, iptr);
1163
		linphonec_command_finished();
aymeric's avatar
aymeric committed
1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
		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);
1205 1206 1207
#ifdef _MSC_VER
			if (strcmp(argv[arg_num], "NUL") != 0) {
#endif
Jehan Monnier's avatar
Jehan Monnier committed
1208
#if !defined(_WIN32_WCE)
1209 1210 1211 1212 1213 1214 1215
				if (access(argv[arg_num], F_OK) != 0)
				{
					fprintf(stderr,
						"Cannot open config file %s.\n",
						argv[arg_num]);
					exit(EXIT_FAILURE);
				}
Jehan Monnier's avatar
Jehan Monnier committed
1216
#endif /*_WIN32_WCE*/
1217 1218 1219
#ifdef _MSC_VER
			}
#endif
1220
			snprintf(configfile_name, PATH_MAX, "%s", argv[arg_num]);
aymeric's avatar
aymeric committed
1221
		}
1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
		else if (strncmp ("-b", argv[arg_num], 2) == 0)
		{
			if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE);
#if !defined(_WIN32_WCE)
			if (access(argv[arg_num],F_OK)!=0 )
			{
				fprintf (stderr,
					"Cannot open config file %s.\n",
					 argv[arg_num]);
				exit(EXIT_FAILURE);
			}
#endif /*_WIN32_WCE*/
			factory_configfile_name = argv[arg_num];
		}
aymeric's avatar
aymeric committed
1236 1237 1238 1239
		else if (strncmp ("-s", argv[arg_num], 2) == 0)
		{
			arg_num++;
			if (arg_num < argc)
1240
				sip_addr_to_call = argv[arg_num];
aymeric's avatar
aymeric committed
1241 1242 1243 1244 1245
		}
                else if (strncmp ("-a", argv[arg_num], 2) == 0)
                {
                        auto_answer = TRUE;
                }
1246
                else if (strncmp ("--real-early-media", argv[arg_num], strlen("--real-early-media")) == 0)
1247 1248 1249
                {
                        real_early_media_sending = TRUE;
                }
aymeric's avatar
aymeric committed
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
		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;
1262
			preview_enabled=TRUE;
aymeric's avatar
aymeric committed
1263 1264 1265 1266 1267 1268 1269
                }
		else if ((strncmp ("-v", argv[arg_num], 2) == 0)
			 ||
			 (strncmp
			  ("--version", argv[arg_num],
			   strlen ("--version")) == 0))
		{
Jehan Monnier's avatar
Jehan Monnier committed
1270
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
1271
			printf ("version: " LINPHONE_VERSION "\n");
Jehan Monnier's avatar
Jehan Monnier committed
1272
#endif
aymeric's avatar
aymeric committed
1273 1274 1275 1276 1277 1278
			exit (EXIT_SUCCESS);
		}
		else if (strncmp ("-S", argv[arg_num], 2) == 0)
		{
			show_general_state = TRUE;
		}
1279 1280 1281 1282
		else if (strncmp ("--pipe", argv[arg_num], 6) == 0)
		{
			unix_socket=1;
		}
1283 1284 1285 1286 1287
		else if (strncmp ("--wid", argv[arg_num], 5) == 0)
		{
			arg_num++;
			if (arg_num < argc) {
				char *tmp;
1288
				window_id = (void *)strtol( argv[arg_num], &tmp, 0 );
1289
				lpc_video_params.wid = window_id;
1290 1291
			}
		}
aymeric's avatar
aymeric committed
1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317
		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
Jehan Monnier's avatar
Jehan Monnier committed
1318
 *	-1 on error
aymeric's avatar
aymeric committed
1319 1320 1321 1322
 */
static int
handle_configfile_migration()
{
Jehan Monnier's avatar
Jehan Monnier committed
1323
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
1324
	char *old_cfg_gui;
Jehan Monnier's avatar
Jehan Monnier committed
1325
	char *old_cfg_cli;
aymeric's avatar
aymeric committed
1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383
	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 (access(new_cfg,F_OK)==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 (access(old_cfg_cli, F_OK)==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 (access(old_cfg_gui, F_OK)==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);
Jehan Monnier's avatar
Jehan Monnier committed
1384
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
1385 1386
	return 0;
}
Jehan Monnier's avatar
Jehan Monnier committed
1387
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
/*
 * 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));
1407
		fprintf(stderr, "%s", message);
aymeric's avatar
aymeric committed
1408 1409 1410 1411 1412 1413 1414 1415 1416
		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));
1417
		fprintf(stderr, "%s", message);
Margaux Clerc's avatar
Margaux Clerc committed
1418
		fclose(in);
aymeric's avatar
aymeric committed
1419 1420 1421 1422 1423 1424 1425 1426
		return 0;
	}

	/* Copy data from "in" to "out" */
	while ( (n=fread(buf, 1, sizeof buf, in)) > 0 )
	{
		if ( ! fwrite(buf, 1, n, out) )
		{
Margaux Clerc's avatar
Margaux Clerc committed
1427 1428
			fclose(in);
			fclose(out);
aymeric's avatar
aymeric committed
1429 1430
			return 0;
		}
Jehan Monnier's avatar
Jehan Monnier committed
1431
	}
aymeric's avatar
aymeric committed
1432 1433 1434 1435 1436 1437

	fclose(in);
	fclose(out);

	return 1;
}
Jehan Monnier's avatar
Jehan Monnier committed
1438
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
1439

1440
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
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;
}

1470 1471
#endif

aymeric's avatar
aymeric committed
1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504