linphonec.c 39.7 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 29 30
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
Jehan Monnier's avatar
Jehan Monnier committed
31
#include <errno.h>
aymeric's avatar
aymeric committed
32
#include <signal.h>
Jehan Monnier's avatar
Jehan Monnier committed
33 34
#include "private.h" /*coreapi/private.h, needed for LINPHONE_VERSION */
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
35
#include <limits.h>
36
#include <ctype.h>
37
#include <stdlib.h>
38

aymeric's avatar
aymeric committed
39
#include <linphonecore.h>
Jehan Monnier's avatar
Jehan Monnier committed
40

aymeric's avatar
aymeric committed
41 42
#include "linphonec.h"

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

Jehan Monnier's avatar
Jehan Monnier committed
56 57 58 59 60 61 62 63 64 65 66
#if defined(_WIN32_WCE)

#if !defined(PATH_MAX)
#define PATH_MAX 256
#endif /*PATH_MAX*/

#if !defined(strdup)
#define strdup _strdup
#endif /*strdup*/

#endif /*_WIN32_WCE*/
67

aymeric's avatar
aymeric committed
68 69 70 71 72 73 74 75 76
#ifdef HAVE_GETTEXT
#include <libintl.h>
#ifndef _
#define _(String) gettext(String)
#endif
#else
#define _(something)	(something)
#endif

Simon Morlat's avatar
Simon Morlat committed
77 78 79 80
#ifndef PACKAGE_DIR
#define PACKAGE_DIR ""
#endif

81 82 83 84
#ifdef HAVE_X11_XLIB_H
#include <X11/Xlib.h>
#endif

aymeric's avatar
aymeric committed
85 86 87 88 89 90 91 92 93 94 95 96 97
/***************************************************************************
 *
 *  Types
 *
 ***************************************************************************/

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

/***************************************************************************
 *
Jehan Monnier's avatar
Jehan Monnier committed
98
 *  Forward declarations
aymeric's avatar
aymeric committed
99 100 101 102 103 104
 *
 ***************************************************************************/

char *lpc_strip_blanks(char *input);

static int handle_configfile_migration(void);
Jehan Monnier's avatar
Jehan Monnier committed
105
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
106
static int copy_file(const char *from, const char *to);
Jehan Monnier's avatar
Jehan Monnier committed
107
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
108 109
static int linphonec_parse_cmdline(int argc, char **argv);
static int linphonec_init(int argc, char **argv);
110
static int linphonec_main_loop (LinphoneCore * opm);
aymeric's avatar
aymeric committed
111
static int linphonec_idle_call (void);
112 113 114
#ifdef HAVE_READLINE
static int linphonec_initialize_readline(void);
static int linphonec_finish_readline();
aymeric's avatar
aymeric committed
115 116
static char **linephonec_readline_completion(const char *text,
	int start, int end);
117
#endif
aymeric's avatar
aymeric committed
118 119 120 121

/* These are callback for linphone core */
static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm,
	const char *username);
Simon Morlat's avatar
Simon Morlat committed
122
static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to);
aymeric's avatar
aymeric committed
123 124 125
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);
126
static void linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event);
Simon Morlat's avatar
Simon Morlat committed
127

128
static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid);
aymeric's avatar
aymeric committed
129 130
static void linphonec_new_unknown_subscriber(LinphoneCore *lc,
		LinphoneFriend *lf, const char *url);
Simon Morlat's avatar
Simon Morlat committed
131

aymeric's avatar
aymeric committed
132
static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr,
jehan's avatar
jehan committed
133
		const LinphoneAddress *from, const char *msg);
aymeric's avatar
aymeric committed
134
static void linphonec_display_status (LinphoneCore * lc, const char *something);
135
static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf);
aymeric's avatar
aymeric committed
136
static void print_prompt(LinphoneCore *opm);
137
void linphonec_out(const char *fmt,...);
aymeric's avatar
aymeric committed
138 139
/***************************************************************************
 *
Jehan Monnier's avatar
Jehan Monnier committed
140
 * Global variables
aymeric's avatar
aymeric committed
141 142 143
 *
 ***************************************************************************/

144
LinphoneCore *linphonec;
aymeric's avatar
aymeric committed
145
FILE *mylogfile;
146
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
147 148
static char *histfile_name=NULL;
static char last_in_history[256];
149
#endif
aymeric's avatar
aymeric committed
150 151 152 153 154
//auto answer (-a) option
static bool_t auto_answer=FALSE;
static bool_t answer_call=FALSE;
static bool_t vcap_enabled=FALSE;
static bool_t display_enabled=FALSE;
155
static bool_t preview_enabled=FALSE;
aymeric's avatar
aymeric committed
156
static bool_t show_general_state=FALSE;
157
static bool_t unix_socket=FALSE;
smorlat's avatar
smorlat committed
158
static bool_t linphonec_running=TRUE;
aymeric's avatar
aymeric committed
159 160 161 162
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
163
static char zrtpsecrets[PATH_MAX];
164
static const char *factory_configfile_name=NULL;
165
static char *sip_addr_to_call = NULL; /* for autocall */
166
static int window_id = 0; /* 0=standalone window, or window id for embedding video */
Jehan Monnier's avatar
Jehan Monnier committed
167
#if !defined(_WIN32_WCE)
168
static ortp_pipe_t client_sock=ORTP_PIPE_INVALID;
Jehan Monnier's avatar
Jehan Monnier committed
169
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
170
char prompt[PROMPT_MAX_LEN];
Jehan Monnier's avatar
Jehan Monnier committed
171
#if !defined(_WIN32_WCE)
172 173
static ortp_thread_t pipe_reader_th;
static bool_t pipe_reader_run=FALSE;
Jehan Monnier's avatar
Jehan Monnier committed
174 175
#endif /*_WIN32_WCE*/
#if !defined(_WIN32_WCE)
176
static ortp_pipe_t server_sock;
Jehan Monnier's avatar
Jehan Monnier committed
177
#endif /*_WIN32_WCE*/
178

179 180
bool_t linphonec_camera_enabled=TRUE;

aymeric's avatar
aymeric committed
181

182

183 184 185 186 187 188 189 190 191 192 193 194 195 196
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
197
	linphonec_out("Sorry, no call with id %i exists at this time.\n",id);
198 199 200
	return NULL;
}

aymeric's avatar
aymeric committed
201 202 203 204 205 206
/***************************************************************************
 *
 * Linphone core callbacks
 *
 ***************************************************************************/

Simon Morlat's avatar
Simon Morlat committed
207 208 209 210
/*
 * Linphone core callback
 */
static void
Simon Morlat's avatar
Simon Morlat committed
211
linphonec_display_refer (LinphoneCore * lc, const char *refer_to)
Simon Morlat's avatar
Simon Morlat committed
212
{
Simon Morlat's avatar
Simon Morlat committed
213
	linphonec_out("Receiving out of call refer to %s\n", refer_to);
Simon Morlat's avatar
Simon Morlat committed
214 215
}

aymeric's avatar
aymeric committed
216
/*
Jehan Monnier's avatar
Jehan Monnier committed
217
 * Linphone core callback
aymeric's avatar
aymeric committed
218 219 220 221 222 223 224 225 226
 */
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
227
 * Linphone core callback
aymeric's avatar
aymeric committed
228 229 230 231 232 233 234 235 236
 */
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
237
 * Linphone core callback
aymeric's avatar
aymeric committed
238 239 240 241 242 243 244 245 246
 */
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
247
 * Linphone core callback
aymeric's avatar
aymeric committed
248 249 250 251 252 253 254 255
 */
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
256
 * Linphone core callback
aymeric's avatar
aymeric committed
257 258 259 260
 */
static void
linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username)
{
261
	/* no prompt possible when using pipes or tcp mode*/
262
	if (unix_socket){
263 264 265
		linphone_core_abort_authentication(lc,NULL);
	}else{
		LinphoneAuthInfo *pending_auth;
Jehan Monnier's avatar
Jehan Monnier committed
266

267 268 269 270 271 272
		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
273 274
		}

275 276 277
		pending_auth=linphone_auth_info_new(username,NULL,NULL,NULL,realm);
		auth_stack.elem[auth_stack.nitems++]=pending_auth;
	}
aymeric's avatar
aymeric committed
278 279 280 281 282 283
}

/*
 * Linphone core callback
 */
static void
284
linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event)
285
{
286
	if(!strcmp(event,"refer"))
287
	{
288 289
		linphonec_out("The distand endpoint %s of call %li has been transfered, you can safely close the call.\n",
		              from,(long)linphone_call_get_user_pointer (call));
290 291 292
	}
}

Simon Morlat's avatar
Simon Morlat committed
293

294 295 296 297 298
/*
 * Linphone core callback
 */
static void
linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid)
aymeric's avatar
aymeric committed
299
{
300 301 302
	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
303 304 305 306 307 308 309 310 311 312 313
	// 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
314 315
		"(accept/deny is not implemented yet)\n", url);
	// This means that this person wishes to be notified
aymeric's avatar
aymeric committed
316 317 318 319
	// of your presence information (online, busy, away...).

}

320 321 322
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
323
		linphonec_out("Far end requests to share video.\nType 'camera on' if you agree.\n");
324 325 326
	}
}

327 328 329
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) {
330 331
		linphonec_out("Call %i is not fully encrypted and auth token is %s.\n", id,
				(auth_token != NULL) ? auth_token : "absent");
332
	} else {
333
		linphonec_out("Call %i is fully encrypted and auth token is %s.\n", id,
334 335 336 337
				(auth_token != NULL) ? auth_token : "absent");
	}
}

Simon Morlat's avatar
Simon Morlat committed
338
static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState st, const char *msg){
339
	char *from=linphone_call_get_remote_address_as_string(call);
340
	long id=(long)linphone_call_get_user_pointer (call);
Simon Morlat's avatar
Simon Morlat committed
341 342
	switch(st){
		case LinphoneCallEnd:
Simon Morlat's avatar
Simon Morlat committed
343
			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
344 345
		break;
		case LinphoneCallResuming:
346
			linphonec_out("Resuming call %i with %s.\n", id, from);
Simon Morlat's avatar
Simon Morlat committed
347 348
		break;
		case LinphoneCallStreamsRunning:
349
			linphonec_out("Media streams established with %s for call %i.\n", from,id);
Simon Morlat's avatar
Simon Morlat committed
350 351
		break;
		case LinphoneCallPausing:
352
			linphonec_out("Pausing call %i with %s.\n", id, from);
Simon Morlat's avatar
Simon Morlat committed
353 354
		break;
		case LinphoneCallPaused:
355
			linphonec_out("Call %i with %s is now paused.\n", id, from);
Simon Morlat's avatar
Simon Morlat committed
356
		break;
Simon Morlat's avatar
Simon Morlat committed
357 358 359
		case LinphoneCallPausedByRemote:
			linphonec_out("Call %i has been paused by %s.\n",id,from);
		break;
Simon Morlat's avatar
Simon Morlat committed
360
		case LinphoneCallIncomingReceived:
361
			linphonec_call_identify(call);
362
			linphone_call_enable_camera (call,linphonec_camera_enabled);
363
			id=(long)linphone_call_get_user_pointer (call);
Simon Morlat's avatar
Simon Morlat committed
364 365 366 367
			linphonec_set_caller(from);
			if ( auto_answer)  {
				answer_call=TRUE;
			}
Simon Morlat's avatar
Simon Morlat committed
368
			linphonec_out("Receiving new incoming call from %s, assigned id %i\n", from,id);
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
static void start_pipe_reader(void){
495
	ms_mutex_init(&prompt_mutex,NULL);
496 497
	pipe_reader_run=TRUE;
	ortp_thread_create(&pipe_reader_th,NULL,pipe_thread,NULL);
498 499
}

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

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

char *linphonec_readline(char *prompt){
515
	if (unix_socket || !BOOL_HAVE_READLINE ){
516
		static bool_t prompt_reader_started=FALSE;
517
		static bool_t pipe_reader_started=FALSE;
518 519 520
		if (!prompt_reader_started){
			start_prompt_reader();
			prompt_reader_started=TRUE;
521
		}
522
		if (unix_socket && !pipe_reader_started){
Jehan Monnier's avatar
Jehan Monnier committed
523
#if !defined(_WIN32_WCE)
524 525
			start_pipe_reader();
			pipe_reader_started=TRUE;
Jehan Monnier's avatar
Jehan Monnier committed
526
#endif /*_WIN32_WCE*/
527 528 529 530 531 532 533 534 535 536 537 538 539
		}
		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();
540
#ifdef WIN32
541
			Sleep(20);
542 543 544 545 546 547 548 549 550
			/* 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? */
			MSG msg;
	
			if (PeekMessage(&msg, NULL, 0, 0,1)) {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
551
#else
552
			usleep(20000);
553
#endif
554 555 556
		}
	}else{
#ifdef HAVE_READLINE
557 558
		char* ret=readline(prompt);
		return ret;
559
#endif
560
	}
561
}
aymeric's avatar
aymeric committed
562

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

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

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

smorlat's avatar
smorlat committed
594 595 596
bool_t linphonec_get_autoanswer(){
	return auto_answer;
}
597

Simon Morlat's avatar
Simon Morlat committed
598 599
LinphoneCoreVTable linphonec_vtable={0};

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

#else
int
main (int argc, char *argv[]) {
#endif
	linphonec_vtable.call_state_changed=linphonec_call_state_changed;
	linphonec_vtable.notify_presence_recv = linphonec_notify_presence_received;
jehan's avatar
jehan committed
632
	linphonec_vtable.new_subscription_request = linphonec_new_unknown_subscriber;
Jehan Monnier's avatar
Jehan Monnier committed
633 634 635 636 637 638 639
	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
640 641
	linphonec_vtable.refer_received=linphonec_display_refer;
	linphonec_vtable.notify_recv=linphonec_notify_received;
642
	linphonec_vtable.call_encryption_changed=linphonec_call_encryption_changed;
Simon Morlat's avatar
Simon Morlat committed
643
	
aymeric's avatar
aymeric committed
644 645
	if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE);

646
	linphonec_main_loop (linphonec);
aymeric's avatar
aymeric committed
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665

	linphonec_finish(EXIT_SUCCESS);

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

/*
 * Initialize linphonec
 */
static int
linphonec_init(int argc, char **argv)
{

	//g_mem_set_vtable(&dbgtable);

	/*
	 * Set initial values for global variables
	 */
	mylogfile = NULL;
Simon Morlat's avatar
Simon Morlat committed
666 667 668
	
	
#ifndef _WIN32
Jehan Monnier's avatar
Jehan Monnier committed
669 670
	snprintf(configfile_name, PATH_MAX, "%s/.linphonerc",
			getenv("HOME"));
Simon Morlat's avatar
Simon Morlat committed
671 672
	snprintf(zrtpsecrets, PATH_MAX, "%s/.linphone-zidcache",
			getenv("HOME"));
Simon Morlat's avatar
Simon Morlat committed
673 674 675 676
#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
677
#else
Simon Morlat's avatar
Simon Morlat committed
678 679
	snprintf(configfile_name, PATH_MAX, "%s/Linphone/linphonerc",
			getenv("APPDATA"));
Simon Morlat's avatar
Simon Morlat committed
680 681
	snprintf(zrtpsecrets, PATH_MAX, "%s/Linphone/linphone-zidcache",
			getenv("APPDATA"));
Simon Morlat's avatar
Simon Morlat committed
682
#endif
aymeric's avatar
aymeric committed
683 684 685 686 687 688 689 690 691 692 693 694 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
	/* 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;
	}

#ifdef ENABLE_NLS
	if (NULL == bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR))
		perror ("bindtextdomain failed");
#ifndef __ARM__
	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif
	textdomain (GETTEXT_PACKAGE);
#else
	printf ("NLS disabled.\n");
#endif

	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
	 */
735
	linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL);
Simon Morlat's avatar
Simon Morlat committed
736
	linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets);
737
	linphone_core_enable_video(linphonec,vcap_enabled,display_enabled);
738 739 740 741 742 743
	if (display_enabled && window_id != 0) 
	{
		printf ("Setting window_id: 0x%x\n", window_id);
		linphone_core_set_native_video_window_id(linphonec,window_id);
	}

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


smorlat's avatar
smorlat committed
763 764 765 766
void linphonec_main_loop_exit(void){
	linphonec_running=FALSE;
}

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

aymeric's avatar
aymeric committed
780
	/* Terminate any pending call */
781
	linphone_core_terminate_all_calls(linphonec);
782
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
783
	linphonec_finish_readline();
784
#endif
Jehan Monnier's avatar
Jehan Monnier committed
785
#if !defined(_WIN32_WCE)
786 787
	if (pipe_reader_run)
		stop_pipe_reader();
Jehan Monnier's avatar
Jehan Monnier committed
788
#endif /*_WIN32_WCE*/
strk's avatar
strk committed
789

790
	linphone_core_destroy (linphonec);
aymeric's avatar
aymeric committed
791 792 793 794 795

	if (mylogfile != NULL && mylogfile != stdout)
	{
		fclose (mylogfile);
	}
796
	printf("\n");
aymeric's avatar
aymeric committed
797 798 799 800 801 802 803 804 805
	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
806
 * Hitting ^D (EOF) would make this function
aymeric's avatar
aymeric committed
807 808 809 810 811 812 813 814
 * 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)
{
815
	static int reentrancy=0;
aymeric's avatar
aymeric committed
816 817
	char *input, *iptr;
	char auth_prompt[256];
818
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
819
	rl_hook_func_t *old_event_hook;
820
#endif
821 822 823 824 825

	if (reentrancy!=0) return 0;
	
	reentrancy++;
	
aymeric's avatar
aymeric committed
826 827 828 829 830 831
	LinphoneAuthInfo *pending_auth=auth_stack.elem[auth_stack.nitems-1];

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

	printf("\n");
832
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
833 834 835 836 837 838 839 840
	/*
	 * 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;
841
#endif
aymeric's avatar
aymeric committed
842 843 844

	while (1)
	{
845
		input=linphonec_readline(auth_prompt);
aymeric's avatar
aymeric committed
846 847 848 849 850 851 852 853 854

		/*
		 * 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
855
			printf("Cancel requested, but not implemented.\n");
aymeric's avatar
aymeric committed
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
			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);
881 882
	linphone_auth_info_destroy(pending_auth);
	auth_stack.elem[auth_stack.nitems-1]=0;
aymeric's avatar
aymeric committed
883
	--(auth_stack.nitems);
884
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
885 886 887 888 889 890
	/*
	 * 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;
891
#endif
aymeric's avatar
aymeric committed
892 893 894 895 896 897 898 899 900 901
	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\
902
  -b  file             specify path of readonly factory configuration file.\n\
aymeric's avatar
aymeric committed
903 904 905 906 907 908 909 910 911
  -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\
  -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\
912
  --wid  windowid      force embedding of video window into provided windowid (disabled by default)\n\
aymeric's avatar
aymeric committed
913 914 915 916 917
  -v or --version      display version and exits.\n");

  	exit(exit_status);
}

918 919 920
#ifdef VIDEO_ENABLED

#ifdef HAVE_X11_XLIB_H
921 922 923 924 925 926 927
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){
928
		XInitThreads();
929
		display=XOpenDisplay(dname);
930
	}
931 932 933 934

	if (display==NULL){
		ms_error("Could not open display %s",dname);
		return;
935
	}
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
	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);
955 956 957 958 959
}
#endif


static void lpc_apply_video_params(){
960
	static unsigned long old_wid=0,old_pwid=0;
Simon Morlat's avatar
Simon Morlat committed
961
	unsigned long wid=linphone_core_get_native_video_window_id (linphonec);
962
	unsigned long pwid=linphone_core_get_native_preview_window_id (linphonec);
Simon Morlat's avatar
Simon Morlat committed
963

964
	if (wid!=0 && (lpc_video_params.refresh || old_wid!=wid)){
Simon Morlat's avatar
Simon Morlat committed
965
		lpc_video_params.refresh=FALSE;
966
#ifdef HAVE_X11_XLIB_H
Simon Morlat's avatar
Simon Morlat committed
967
		if (lpc_video_params.wid==0){  // do not manage window if embedded
968
			x11_apply_video_params(&lpc_video_params,wid);
969 970
		} else {
		        linphone_core_show_video(linphonec, lpc_video_params.show);
971 972 973 974 975 976 977
		}
#endif
	}
	old_wid=wid;
	if (pwid!=0 && (lpc_preview_params.refresh || old_pwid!=pwid)){
		lpc_preview_params.refresh=FALSE;
#ifdef HAVE_X11_XLIB_H
Simon Morlat's avatar
Simon Morlat committed
978
		/*printf("wid=%lu pwid=%lu\n",wid,pwid);*/
979 980 981
		if (lpc_preview_params.wid==0){  // do not manage window if embedded
			printf("Refreshing\n");
			x11_apply_video_params(&lpc_preview_params,pwid);
Simon Morlat's avatar
Simon Morlat committed
982
		}
983 984
#endif
	}
985
	old_pwid=pwid;
986 987 988 989
}

#endif

aymeric's avatar
aymeric committed
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003

/*
 *
 * Called every second from main read loop.
 *
 * Will use the following globals:
 *
 *  - LinphoneCore linphonec
 *  - LPC_AUTH_STACK auth_stack;
 *
 */
static int
linphonec_idle_call ()
{
1004
	LinphoneCore *opm=linphonec;
aymeric's avatar
aymeric committed
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014

	/* 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;
	}
1015 1016 1017 1018 1019 1020 1021 1022
	/* 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
1023 1024 1025 1026 1027 1028 1029

	if ( auth_stack.nitems )
	{
		/*
		 * Inhibit command completion
		 * during password prompts
		 */
1030
#ifdef HAVE_READLINE
aymeric's avatar
aymeric committed
1031
		rl_inhibit_completion=1;
1032
#endif
aymeric's avatar
aymeric committed
1033
		linphonec_prompt_for_auth_final(opm);
1034
#ifdef HAVE_READLINE