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

Simon Morlat's avatar
Simon Morlat committed
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;
smorlat's avatar
smorlat committed
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];
johan's avatar
johan committed
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
 *
 ***************************************************************************/

Simon Morlat's avatar
Simon Morlat committed
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)
Simon Morlat's avatar
Simon Morlat committed
204
{
Simon Morlat's avatar
Simon Morlat committed
205
	linphonec_out("Receiving out of call refer to %s\n", refer_to);
Simon Morlat's avatar
Simon Morlat committed
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:
Simon Morlat's avatar
Simon Morlat committed
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;
Simon Morlat's avatar
Simon Morlat committed
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
		}
	}
smorlat's avatar
smorlat committed
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;
smorlat's avatar
smorlat committed
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

smorlat's avatar
smorlat committed
596 597 598
bool_t linphonec_get_autoanswer(){
	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
 */
Simon Morlat's avatar
Simon Morlat committed
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


Simon Morlat's avatar
Simon Morlat committed
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"));
johan's avatar
johan committed
681 682
	snprintf(usr_certificates_path, PATH_MAX, "%s/.linphone-usr-crt",
			getenv("HOME"));
Simon Morlat's avatar
Simon Morlat committed
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
Simon Morlat's avatar
Simon Morlat committed
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"));
johan's avatar
johan committed
692 693
	snprintf(usr_certificates_path, PATH_MAX, "%s/Linphone/linphone-usr-crt",
			getenv("APPDATA"));
Simon Morlat's avatar
Simon Morlat committed
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);
johan's avatar
johan committed
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;
}


smorlat's avatar
smorlat committed
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)
{
Ghislain MARY's avatar
Ghislain MARY committed
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"
Ghislain MARY's avatar
Ghislain MARY committed
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 968
}
#endif


static void lpc_apply_video_params(){
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