daemon.cc 30.1 KB
Newer Older
Sandrine Avakian's avatar
Sandrine Avakian committed
1 2
/*
daemon.cc
Ronan's avatar
Ronan committed
3
Copyright (C) 2016 Belledonne Communications, Grenoble, France
Sandrine Avakian's avatar
Sandrine Avakian committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or (at
your option) any later version.

This library 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 Lesser General Public
License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

Ghislain MARY's avatar
Ghislain MARY committed
20
#include <cstdio>
21
#ifndef _WIN32
Ghislain MARY's avatar
Ghislain MARY committed
22
#include <sys/ioctl.h>
23
#endif
24 25 26

#include <signal.h>

Yann Diorcet's avatar
Yann Diorcet committed
27
#include <iostream>
Ghislain MARY's avatar
Ghislain MARY committed
28
#include <iomanip>
Yann Diorcet's avatar
Yann Diorcet committed
29 30
#include <sstream>
#include <algorithm>
31
#include <functional>
Ghislain MARY's avatar
Ghislain MARY committed
32
#include <limits>
Yann Diorcet's avatar
Yann Diorcet committed
33

34
#ifdef HAVE_READLINE
35 36
#include <readline/readline.h>
#include <readline/history.h>
37
#endif
38

39
#ifndef _WIN32
40
#include <poll.h>
41
#endif
Yann Diorcet's avatar
Yann Diorcet committed
42

43
#include "daemon.h"
44
#include "commands/adaptive-jitter-compensation.h"
Simon Morlat's avatar
Simon Morlat committed
45
#include "commands/jitterbuffer.h"
46 47 48
#include "commands/answer.h"
#include "commands/audio-codec-get.h"
#include "commands/audio-codec-move.h"
Yann Diorcet's avatar
Yann Diorcet committed
49
#include "commands/audio-codec-set.h"
50
#include "commands/audio-codec-toggle.h"
51 52
#include "commands/audio-stream-start.h"
#include "commands/audio-stream-stop.h"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
53
#include "commands/audio-stream-stats.h"
54
#include "commands/auth-infos-clear.h"
55 56 57
#include "commands/call.h"
#include "commands/call-stats.h"
#include "commands/call-status.h"
58 59 60
#include "commands/call-pause.h"
#include "commands/call-mute.h"
#include "commands/call-resume.h"
61
#include "commands/video.h"
62 63
#include "commands/call-transfer.h"
#include "commands/conference.h"
64
#include "commands/contact.h"
65
#include "commands/dtmf.h"
66
#include "commands/firewall-policy.h"
67
#include "commands/help.h"
Ghislain MARY's avatar
Ghislain MARY committed
68
#include "commands/ipv6.h"
69
#include "commands/media-encryption.h"
70
#include "commands/msfilter-add-fmtp.h"
71
#include "commands/play-wav.h"
72
#include "commands/pop-event.h"
Ghislain MARY's avatar
Ghislain MARY committed
73
#include "commands/port.h"
74 75
#include "commands/ptime.h"
#include "commands/register.h"
76
#include "commands/register-info.h"
77 78 79 80
#include "commands/register-status.h"
#include "commands/terminate.h"
#include "commands/unregister.h"
#include "commands/quit.h"
81
#include "commands/configcommand.h"
82
#include "commands/netsim.h"
Simon Morlat's avatar
Simon Morlat committed
83
#include "commands/cn.h"
Ghislain MARY's avatar
Ghislain MARY committed
84
#include "commands/version.h"
85
#include "commands/play.h"
86
#include "commands/message.h"
87

Guillaume Beraudo's avatar
Guillaume Beraudo committed
88
#include "private.h"
89

90
using namespace std;
Yann Diorcet's avatar
Yann Diorcet committed
91

Ghislain MARY's avatar
Ghislain MARY committed
92 93 94
#define INT_TO_VOIDPTR(i) ((void*)(intptr_t)(i))
#define VOIDPTR_TO_INT(p) ((int)(intptr_t)(p))

95 96 97 98 99 100 101 102 103 104 105 106 107 108
#ifndef WIN32
#else
#include <windows.h>
void usleep(int waitTime) {
	Sleep(waitTime/1000);
}
#endif

#ifdef HAVE_READLINE
#define LICENCE_GPL
#else
#define LICENCE_COMMERCIAL
#endif

109 110
const char * const ice_state_str[] = {
	"Not activated",	/* LinphoneIceStateNotActivated */
Ghislain MARY's avatar
Ghislain MARY committed
111
	"Failed",	/* LinphoneIceStateFailed */
112 113 114 115 116 117
	"In progress",	/* LinphoneIceStateInProgress */
	"Host connection",	/* LinphoneIceStateHostConnection */
	"Reflexive connection",	/* LinphoneIceStateReflexiveConnection */
	"Relayed connection"	/* LinphoneIceStateRelayConnection */
};

118 119 120 121 122 123 124 125 126 127 128
void *Daemon::iterateThread(void *arg) {
	Daemon *daemon = (Daemon *) arg;
	while (daemon->mRunning) {
		ms_mutex_lock(&daemon->mMutex);
		daemon->iterate();
		ms_mutex_unlock(&daemon->mMutex);
		usleep(20000);
	}
	return 0;
}

129
CallEvent::CallEvent(Daemon *daemon, LinphoneCall *call, LinphoneCallState state) : Event("call-state-changed") {
130
	LinphoneCallLog *callLog = linphone_call_get_call_log(call);
Simon Morlat's avatar
Simon Morlat committed
131
	const LinphoneAddress *fromAddr = linphone_call_log_get_from_address(callLog);
132 133
	char *fromStr = linphone_address_as_string(fromAddr);

Yann Diorcet's avatar
Yann Diorcet committed
134
	ostringstream ostr;
135 136
	ostr << "Event: " << linphone_call_state_to_string(state) << "\n";
	ostr << "From: " << fromStr << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
137
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
138
	setBody(ostr.str());
139 140

	bctbx_free(fromStr);
141
	
Yann Diorcet's avatar
Yann Diorcet committed
142 143
}

144
DtmfEvent::DtmfEvent(Daemon *daemon, LinphoneCall *call, int dtmf) : Event("receiving-tone"){
Yann Diorcet's avatar
Yann Diorcet committed
145 146
	ostringstream ostr;
	char *remote = linphone_call_get_remote_address_as_string(call);
147
	ostr << "Tone: " << (char) dtmf << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
148
	ostr << "From: " << remote << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
149
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
150
	setBody(ostr.str());
Yann Diorcet's avatar
Yann Diorcet committed
151 152 153
	ms_free(remote);
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
154
static ostream &printCallStatsHelper(ostream &ostr, const LinphoneCallStats *stats, const string &prefix) {
155 156 157
	ostr << prefix << "ICE state: " << ice_state_str[linphone_call_stats_get_ice_state(stats)] << "\n";
	ostr << prefix << "RoundTripDelay: " << linphone_call_stats_get_round_trip_delay(stats) << "\n";
//	ostr << prefix << "Jitter: " << stats->jitter_stats.jitter << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
158 159 160
//	ostr << prefix << "MaxJitter: " << stats->jitter_stats.max_jitter << "\n";
//	ostr << prefix << "SumJitter: " << stats->jitter_stats.sum_jitter << "\n";
//	ostr << prefix << "MaxJitterTs: " << stats->jitter_stats.max_jitter_ts << "\n";
161
	ostr << prefix << "JitterBufferSizeMs: " << linphone_call_stats_get_jitter_buffer_size_ms(stats) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
162

163 164
	ostr << prefix << "Received-InterarrivalJitter: " << linphone_call_stats_get_receiver_interarrival_jitter(stats) << "\n";
	ostr << prefix << "Received-FractionLost: " << linphone_call_stats_get_receiver_loss_rate(stats) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
165

166 167
	ostr << prefix << "Sent-InterarrivalJitter: " << linphone_call_stats_get_sender_interarrival_jitter(stats) << "\n";
	ostr << prefix << "Sent-FractionLost: " << linphone_call_stats_get_sender_loss_rate(stats) << "\n";
Guillaume Beraudo's avatar
Guillaume Beraudo committed
168 169 170
	return ostr;
}

171
CallStatsEvent::CallStatsEvent(Daemon *daemon, LinphoneCall *call, const LinphoneCallStats *stats) : Event("call-stats"){
Guillaume Beraudo's avatar
Guillaume Beraudo committed
172 173 174 175
	const LinphoneCallParams *callParams = linphone_call_get_current_params(call);
	const char *prefix = "";

	ostringstream ostr;
176 177 178 179
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
	ostr << "Type: ";
	if (linphone_call_stats_get_type(stats) == LINPHONE_CALL_STATS_AUDIO) {
		ostr << "Audio";
Guillaume Beraudo's avatar
Guillaume Beraudo committed
180
	} else {
181
		ostr << "Video";
Guillaume Beraudo's avatar
Guillaume Beraudo committed
182
	}
183 184
	ostr << "\n";

Guillaume Beraudo's avatar
Guillaume Beraudo committed
185 186

	printCallStatsHelper(ostr, stats, prefix);
Yann Diorcet's avatar
Yann Diorcet committed
187

188
	if (linphone_call_stats_get_type(stats) == LINPHONE_CALL_STATS_AUDIO) {
Yann Diorcet's avatar
Yann Diorcet committed
189 190 191 192 193
		const PayloadType *audioCodec = linphone_call_params_get_used_audio_codec(callParams);
		ostr << PayloadTypeResponse(linphone_call_get_core(call), audioCodec, -1, prefix, false).getBody() << "\n";
	} else {
		const PayloadType *videoCodec = linphone_call_params_get_used_video_codec(callParams);
		ostr << PayloadTypeResponse(linphone_call_get_core(call), videoCodec, -1, prefix, false).getBody() << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
194 195
	}

196
	setBody(ostr.str());
Yann Diorcet's avatar
Yann Diorcet committed
197 198
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
199

200 201
AudioStreamStatsEvent::AudioStreamStatsEvent(Daemon* daemon, AudioStream* stream,
		const LinphoneCallStats *stats) : Event("audio-stream-stats"){
Guillaume Beraudo's avatar
Guillaume Beraudo committed
202
	const char *prefix = "";
203

Guillaume Beraudo's avatar
Guillaume Beraudo committed
204
	ostringstream ostr;
205 206 207 208
	ostr << "Id: " << daemon->updateAudioStreamId(stream) << "\n";
	ostr << "Type: ";
	if (linphone_call_stats_get_type(stats) == LINPHONE_CALL_STATS_AUDIO) {
		ostr << "Audio";
Guillaume Beraudo's avatar
Guillaume Beraudo committed
209
	} else {
210
		ostr << "Video";
Guillaume Beraudo's avatar
Guillaume Beraudo committed
211
	}
212
	ostr << "\n";
213

Guillaume Beraudo's avatar
Guillaume Beraudo committed
214
	printCallStatsHelper(ostr, stats, prefix);
215

216
	setBody(ostr.str());
Guillaume Beraudo's avatar
Guillaume Beraudo committed
217 218
}

219
CallPlayingStatsEvent::CallPlayingStatsEvent(Daemon* daemon, int id) : Event("call-playing-complete"){
220 221 222 223
	ostringstream ostr;
 
	ostr << "Id: " << id << "\n";

224
	setBody(ostr.str());
225 226
}

Yann Diorcet's avatar
Yann Diorcet committed
227
PayloadTypeResponse::PayloadTypeResponse(LinphoneCore *core, const PayloadType *payloadType, int index, const string &prefix, bool enabled_status) {
Yann Diorcet's avatar
Yann Diorcet committed
228
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
229 230 231
	if (payloadType != NULL) {
		if (index >= 0)
			ostr << prefix << "Index: " << index << "\n";
232
		ostr << prefix << "Payload-type-number: " << payload_type_get_number(payloadType) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
233 234 235 236 237 238 239 240 241 242
		ostr << prefix << "Clock-rate: " << payloadType->clock_rate << "\n";
		ostr << prefix << "Bitrate: " << payloadType->normal_bitrate << "\n";
		ostr << prefix << "Mime: " << payloadType->mime_type << "\n";
		ostr << prefix << "Channels: " << payloadType->channels << "\n";
		ostr << prefix << "Recv-fmtp: " << ((payloadType->recv_fmtp) ? payloadType->recv_fmtp : "") << "\n";
		ostr << prefix << "Send-fmtp: " << ((payloadType->send_fmtp) ? payloadType->send_fmtp : "") << "\n";
		if (enabled_status)
			ostr << prefix << "Enabled: " << (linphone_core_payload_type_enabled(core, payloadType) == TRUE ? "true" : "false") << "\n";
		setBody(ostr.str().c_str());
	}
Yann Diorcet's avatar
Yann Diorcet committed
243 244
}

245 246
PayloadTypeParser::PayloadTypeParser(LinphoneCore *core, const string &mime_type, bool accept_all) : mAll(false), mSuccesful(true), mPayloadType(NULL),mPosition(-1){
	int number=-1;
247 248 249 250
	if (accept_all && (mime_type.compare("ALL") == 0)) {
		mAll = true;
		return;
	}
251
	istringstream ist(mime_type);
252
	ist >> number;
253
	if (ist.fail()) {
254
		char type[64]={0};
255
		int rate, channels;
256
		if (sscanf(mime_type.c_str(), "%63[^/]/%u/%u", type, &rate, &channels) != 3) {
257 258 259
			mSuccesful = false;
			return;
		}
260
		mPayloadType = linphone_core_find_payload_type(core, type, rate, channels);
261
		if (mPayloadType) mPosition=bctbx_list_index(linphone_core_get_audio_codecs(core), mPayloadType);
262
	}else if (number!=-1){
263
		const bctbx_list_t *elem;
264
		for(elem=linphone_core_get_audio_codecs(core);elem!=NULL;elem=elem->next){
265
			if (number==payload_type_get_number((PayloadType*)elem->data)){
266 267 268
				mPayloadType=(PayloadType*)elem->data;
				break;
			}
269 270 271 272
		}
	}
}

273
DaemonCommandExample::DaemonCommandExample(const string& command, const string& output)
Ghislain MARY's avatar
Ghislain MARY committed
274 275
	: mCommand(command), mOutput(output) {}

276
DaemonCommand::DaemonCommand(const string& name, const string& proto, const string& description) :
Ghislain MARY's avatar
Ghislain MARY committed
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
		mName(name), mProto(proto), mDescription(description) {
}

void DaemonCommand::addExample(const DaemonCommandExample *example) {
	mExamples.push_back(example);
}

const string DaemonCommand::getHelp() const {
	ostringstream ost;
	ost << getProto() << endl << endl;
	ost << "Description:" << endl << getDescription() << endl << endl;
	list<const DaemonCommandExample*> examples = getExamples();
	int c = 1;
	for (list<const DaemonCommandExample*>::iterator it = examples.begin(); it != examples.end(); ++it, ++c) {
		ost << "Example " << c << ":" << endl;
		ost << ">" << (*it)->getCommand() << endl;
		ost << (*it)->getOutput() << endl;
		ost << endl;
	}
	return ost.str();
Yann Diorcet's avatar
Yann Diorcet committed
297 298
}

299 300
bool DaemonCommand::matches(const string& name) const {
	return mName.compare(name) == 0;
Yann Diorcet's avatar
Yann Diorcet committed
301 302
}

303
Daemon::Daemon(const char *config_path, const char *factory_config_path, const char *log_file, const char *pipe_name, bool display_video, bool capture_video) :
304
		mLSD(0), mLogFile(NULL), mAutoVideo(0), mCallIds(0), mProxyIds(0), mAudioStreamIds(0) {
305
	ms_mutex_init(&mMutex, NULL);
306 307
	mServerFd = (ortp_pipe_t)-1;
	mChildFd = (ortp_pipe_t)-1;
308
	if (pipe_name == NULL) {
309 310
#ifdef HAVE_READLINE
		const char *homedir = getenv("HOME");
jehan's avatar
jehan committed
311
		rl_readline_name = (char*)"daemon";
312 313 314 315 316 317
		if (homedir == NULL)
			homedir = ".";
		mHistfile = string(homedir) + string("/.linphone_history");
		read_history(mHistfile.c_str());
		setlinebuf(stdout);
#endif
Yann Diorcet's avatar
Yann Diorcet committed
318
	} else {
319
		mServerFd = ortp_server_pipe_create(pipe_name);
320
#ifndef _WIN32
Yann Diorcet's avatar
Yann Diorcet committed
321
		listen(mServerFd, 2);
322 323
		fprintf(stdout, "Server unix socket created, name=%s fd=%i\n", pipe_name, (int)mServerFd);
#else
Ghislain MARY's avatar
Ghislain MARY committed
324
		fprintf(stdout, "Named pipe  created, name=%s fd=%p\n", pipe_name, mServerFd);
325
#endif
Yann Diorcet's avatar
Yann Diorcet committed
326 327
	}

328 329 330 331 332 333
	if (log_file != NULL) {
		mLogFile = fopen(log_file, "a+");
		linphone_core_enable_logs(mLogFile);
	} else {
		linphone_core_disable_logs();
	}
Yann Diorcet's avatar
Yann Diorcet committed
334

335 336
	LinphoneCoreVTable vtable;
	memset(&vtable, 0, sizeof(vtable));
Yann Diorcet's avatar
Yann Diorcet committed
337
	vtable.call_state_changed = callStateChanged;
Yann Diorcet's avatar
Yann Diorcet committed
338
	vtable.call_stats_updated = callStatsUpdated;
Yann Diorcet's avatar
Yann Diorcet committed
339
	vtable.dtmf_received = dtmfReceived;
340
	vtable.message_received = messageReceived;
341
	mLc = linphone_core_new(&vtable, config_path, factory_config_path, this);
Yann Diorcet's avatar
Yann Diorcet committed
342
	linphone_core_set_user_data(mLc, this);
343 344
	linphone_core_enable_video_capture(mLc,capture_video);
	linphone_core_enable_video_display(mLc,display_video);
Ronan's avatar
Ronan committed
345

346 347 348
	for(const bctbx_list_t *proxy = linphone_core_get_proxy_config_list(mLc); proxy != NULL; proxy = bctbx_list_next(proxy)) {
		updateProxyId((LinphoneProxyConfig *)bctbx_list_get_data(proxy));
	}
Ronan's avatar
Ronan committed
349

Yann Diorcet's avatar
Yann Diorcet committed
350
	initCommands();
Simon Morlat's avatar
Simon Morlat committed
351
	mUseStatsEvents=true;
Yann Diorcet's avatar
Yann Diorcet committed
352 353
}

Yann Diorcet's avatar
Yann Diorcet committed
354
const list<DaemonCommand*> &Daemon::getCommandList() const {
Yann Diorcet's avatar
Yann Diorcet committed
355 356 357
	return mCommands;
}

358
LinphoneCore *Daemon::getCore() {
Yann Diorcet's avatar
Yann Diorcet committed
359 360 361
	return mLc;
}

362 363 364 365
LinphoneSoundDaemon *Daemon::getLSD() {
	return mLSD;
}

Yann Diorcet's avatar
Yann Diorcet committed
366
int Daemon::updateCallId(LinphoneCall *call) {
367
	int val = VOIDPTR_TO_INT(linphone_call_get_user_data(call));
Yann Diorcet's avatar
Yann Diorcet committed
368
	if (val == 0) {
369
		linphone_call_set_user_data(call, INT_TO_VOIDPTR(++mCallIds));
370
		return mCallIds;
Yann Diorcet's avatar
Yann Diorcet committed
371 372
	}
	return val;
Yann Diorcet's avatar
Yann Diorcet committed
373 374
}

375
LinphoneCall *Daemon::findCall(int id) {
376
	const bctbx_list_t *elem = linphone_core_get_calls(mLc);
Yann Diorcet's avatar
Yann Diorcet committed
377 378
	for (; elem != NULL; elem = elem->next) {
		LinphoneCall *call = (LinphoneCall *) elem->data;
379
		if (VOIDPTR_TO_INT(linphone_call_get_user_data(call)) == id)
Yann Diorcet's avatar
Yann Diorcet committed
380 381 382 383 384
			return call;
	}
	return NULL;
}

Yann Diorcet's avatar
Yann Diorcet committed
385
int Daemon::updateProxyId(LinphoneProxyConfig *cfg) {
Ghislain MARY's avatar
Ghislain MARY committed
386
	int val = VOIDPTR_TO_INT(linphone_proxy_config_get_user_data(cfg));
Yann Diorcet's avatar
Yann Diorcet committed
387
	if (val == 0) {
Ghislain MARY's avatar
Ghislain MARY committed
388
		linphone_proxy_config_set_user_data(cfg, INT_TO_VOIDPTR(++mProxyIds));
389
		return mProxyIds;
Yann Diorcet's avatar
Yann Diorcet committed
390 391
	}
	return val;
392 393
}

394
LinphoneProxyConfig *Daemon::findProxy(int id) {
395
	const bctbx_list_t *elem = linphone_core_get_proxy_config_list(mLc);
Yann Diorcet's avatar
Yann Diorcet committed
396 397
	for (; elem != NULL; elem = elem->next) {
		LinphoneProxyConfig *proxy = (LinphoneProxyConfig *) elem->data;
Ghislain MARY's avatar
Ghislain MARY committed
398
		if (VOIDPTR_TO_INT(linphone_proxy_config_get_user_data(proxy)) == id)
399 400 401 402 403
			return proxy;
	}
	return NULL;
}

404
LinphoneAuthInfo *Daemon::findAuthInfo(int id)  {
405 406
	const bctbx_list_t *elem = linphone_core_get_auth_info_list(mLc);
	if (elem == NULL || id < 1 || (unsigned int)id > bctbx_list_size(elem)) {
407 408 409 410 411 412 413 414 415
		return NULL;
	}
	while (id > 1) {
		elem = elem->next;
		--id;
	}
	return (LinphoneAuthInfo *) elem->data;
}

Yann Diorcet's avatar
Yann Diorcet committed
416
int Daemon::updateAudioStreamId(AudioStream *audio_stream) {
Ronan's avatar
Ronan committed
417
	for (map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
418
		if (it->second->stream == audio_stream)
Yann Diorcet's avatar
Yann Diorcet committed
419 420 421
			return it->first;
	}

422 423 424
	++mAudioStreamIds;
	mAudioStreams.insert(make_pair(mAudioStreamIds, new AudioStreamAndOther(audio_stream)));
	return mAudioStreamIds;
425 426
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
427
AudioStreamAndOther *Daemon::findAudioStreamAndOther(int id) {
Ronan's avatar
Ronan committed
428
	map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
429 430 431 432 433
	if (it != mAudioStreams.end())
		return it->second;
	return NULL;
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
434
AudioStream *Daemon::findAudioStream(int id) {
Ronan's avatar
Ronan committed
435
	map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
436
	if (it != mAudioStreams.end())
Guillaume Beraudo's avatar
Guillaume Beraudo committed
437 438 439 440 441
		return it->second->stream;
	return NULL;
}

void Daemon::removeAudioStream(int id) {
Ronan's avatar
Ronan committed
442
	map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
443
	if (it != mAudioStreams.end()) {
444
		mAudioStreams.erase(it);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
445 446
		delete(it->second);
	}
447 448
}

449 450 451 452
static bool compareCommands(const DaemonCommand *command1, const DaemonCommand *command2) {
	return (command1->getProto() < command2->getProto());
}

Yann Diorcet's avatar
Yann Diorcet committed
453
void Daemon::initCommands() {
Yann Diorcet's avatar
Yann Diorcet committed
454
	mCommands.push_back(new RegisterCommand());
455
	mCommands.push_back(new ContactCommand());
Yann Diorcet's avatar
Yann Diorcet committed
456
	mCommands.push_back(new RegisterStatusCommand());
457
	mCommands.push_back(new RegisterInfoCommand());
458
	mCommands.push_back(new UnregisterCommand());
459
	mCommands.push_back(new AuthInfosClearCommand());
Yann Diorcet's avatar
Yann Diorcet committed
460 461
	mCommands.push_back(new CallCommand());
	mCommands.push_back(new TerminateCommand());
462
	mCommands.push_back(new DtmfCommand());
463
	mCommands.push_back(new PlayWavCommand());
Yann Diorcet's avatar
Yann Diorcet committed
464 465
	mCommands.push_back(new PopEventCommand());
	mCommands.push_back(new AnswerCommand());
Yann Diorcet's avatar
Yann Diorcet committed
466
	mCommands.push_back(new CallStatusCommand());
Yann Diorcet's avatar
Yann Diorcet committed
467
	mCommands.push_back(new CallStatsCommand());
468 469 470 471
	mCommands.push_back(new CallPauseCommand());
	mCommands.push_back(new CallMuteCommand());
	mCommands.push_back(new CallResumeCommand());
	mCommands.push_back(new CallTransferCommand());
472 473 474
	mCommands.push_back(new Video());
	mCommands.push_back(new VideoSource());
	mCommands.push_back(new AutoVideo());
475
	mCommands.push_back(new ConferenceCommand());
Yann Diorcet's avatar
Yann Diorcet committed
476 477 478 479
	mCommands.push_back(new AudioCodecGetCommand());
	mCommands.push_back(new AudioCodecEnableCommand());
	mCommands.push_back(new AudioCodecDisableCommand());
	mCommands.push_back(new AudioCodecMoveCommand());
Yann Diorcet's avatar
Yann Diorcet committed
480
	mCommands.push_back(new AudioCodecSetCommand());
481 482
	mCommands.push_back(new AudioStreamStartCommand());
	mCommands.push_back(new AudioStreamStopCommand());
Guillaume Beraudo's avatar
Guillaume Beraudo committed
483
	mCommands.push_back(new AudioStreamStatsCommand());
484
	mCommands.push_back(new MSFilterAddFmtpCommand());
Yann Diorcet's avatar
Yann Diorcet committed
485
	mCommands.push_back(new PtimeCommand());
Ghislain MARY's avatar
Ghislain MARY committed
486
	mCommands.push_back(new IPv6Command());
487
	mCommands.push_back(new FirewallPolicyCommand());
488
	mCommands.push_back(new MediaEncryptionCommand());
Ghislain MARY's avatar
Ghislain MARY committed
489
	mCommands.push_back(new PortCommand());
490
	mCommands.push_back(new AdaptiveBufferCompensationCommand());
Simon Morlat's avatar
Simon Morlat committed
491 492
	mCommands.push_back(new JitterBufferCommand());
	mCommands.push_back(new JitterBufferResetCommand());
Ghislain MARY's avatar
Ghislain MARY committed
493
	mCommands.push_back(new VersionCommand());
Yann Diorcet's avatar
Yann Diorcet committed
494 495
	mCommands.push_back(new QuitCommand());
	mCommands.push_back(new HelpCommand());
496 497
	mCommands.push_back(new ConfigGetCommand());
	mCommands.push_back(new ConfigSetCommand());
498
	mCommands.push_back(new NetsimCommand());
Simon Morlat's avatar
Simon Morlat committed
499
	mCommands.push_back(new CNCommand());
500 501 502 503
	mCommands.push_back(new IncallPlayerStartCommand());
	mCommands.push_back(new IncallPlayerStopCommand());
	mCommands.push_back(new IncallPlayerPauseCommand());
	mCommands.push_back(new IncallPlayerResumeCommand());
504
	mCommands.push_back(new MessageCommand());
505
	mCommands.sort(compareCommands);
Yann Diorcet's avatar
Yann Diorcet committed
506 507 508 509 510 511 512
}

void Daemon::uninitCommands() {
	while (!mCommands.empty()) {
		delete mCommands.front();
		mCommands.pop_front();
	}
Yann Diorcet's avatar
Yann Diorcet committed
513 514
}

Yann Diorcet's avatar
Yann Diorcet committed
515 516
bool Daemon::pullEvent() {
	bool status = false;
517
	ostringstream ostr;
518
	size_t size = mEventQueue.size();
519 520 521 522 523
	
	if (size != 0) size--;
	
	ostr << "Size: " << size << "\n"; //size is the number items remaining in the queue after popping the event.
	
Yann Diorcet's avatar
Yann Diorcet committed
524
	if (!mEventQueue.empty()) {
525
		Event *e = mEventQueue.front();
Yann Diorcet's avatar
Yann Diorcet committed
526
		mEventQueue.pop();
527 528
		ostr << e->toBuf() << "\n";
		delete e;
Yann Diorcet's avatar
Yann Diorcet committed
529
		status = true;
Yann Diorcet's avatar
Yann Diorcet committed
530
	}
531
	
Yann Diorcet's avatar
Yann Diorcet committed
532 533
	sendResponse(Response(ostr.str().c_str(), Response::Ok));
	return status;
Yann Diorcet's avatar
Yann Diorcet committed
534 535
}

Yann Diorcet's avatar
Yann Diorcet committed
536
void Daemon::callStateChanged(LinphoneCall *call, LinphoneCallState state, const char *msg) {
537
	queueEvent(new CallEvent(this, call, state));
538 539 540 541
	
	if (state == LinphoneCallIncomingReceived && mAutoAnswer){
		linphone_call_accept(call);
	}
Yann Diorcet's avatar
Yann Diorcet committed
542 543
}

544 545 546 547
void Daemon::messageReceived(LinphoneChatRoom *cr, LinphoneChatMessage *msg){
	queueEvent(new IncomingMessageEvent(msg));
}

Yann Diorcet's avatar
Yann Diorcet committed
548
void Daemon::callStatsUpdated(LinphoneCall *call, const LinphoneCallStats *stats) {
549 550
	if (mUseStatsEvents) {
		/* don't queue periodical updates (3 per seconds for just bandwidth updates) */
551
		if (!(_linphone_call_stats_get_updated(stats) & LINPHONE_CALL_STATS_PERIODICAL_UPDATE)){
552
			queueEvent(new CallStatsEvent(this, call, stats));
553 554
		}
	}
Yann Diorcet's avatar
Yann Diorcet committed
555 556
}

557
void Daemon::callPlayingComplete(int id) {
558
	queueEvent(new CallPlayingStatsEvent(this, id));
559 560
}

Yann Diorcet's avatar
Yann Diorcet committed
561
void Daemon::dtmfReceived(LinphoneCall *call, int dtmf) {
562
	queueEvent(new DtmfEvent(this, call, dtmf));
Yann Diorcet's avatar
Yann Diorcet committed
563 564
}

Yann Diorcet's avatar
Yann Diorcet committed
565 566 567
void Daemon::callStateChanged(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState state, const char *msg) {
	Daemon *app = (Daemon*) linphone_core_get_user_data(lc);
	app->callStateChanged(call, state, msg);
Yann Diorcet's avatar
Yann Diorcet committed
568
}
Yann Diorcet's avatar
Yann Diorcet committed
569 570 571 572
void Daemon::callStatsUpdated(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallStats *stats) {
	Daemon *app = (Daemon*) linphone_core_get_user_data(lc);
	app->callStatsUpdated(call, stats);
}
Yann Diorcet's avatar
Yann Diorcet committed
573 574 575 576
void Daemon::dtmfReceived(LinphoneCore *lc, LinphoneCall *call, int dtmf) {
	Daemon *app = (Daemon*) linphone_core_get_user_data(lc);
	app->dtmfReceived(call, dtmf);
}
Yann Diorcet's avatar
Yann Diorcet committed
577

578 579 580 581 582
void Daemon::messageReceived(LinphoneCore *lc, LinphoneChatRoom *cr, LinphoneChatMessage *msg){
	Daemon *app = (Daemon*) linphone_core_get_user_data(lc);
	app->messageReceived(cr, msg);
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
583
void Daemon::iterateStreamStats() {
Ronan's avatar
Ronan committed
584
	for (map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
585 586 587 588
		OrtpEvent *ev;
		while (it->second->queue && (NULL != (ev=ortp_ev_queue_get(it->second->queue)))){
			OrtpEventType evt=ortp_event_get_type(ev);
			if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED || evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
589
				linphone_call_stats_fill(it->second->stats, &it->second->stream->ms, ev);
590 591
				if (mUseStatsEvents) mEventQueue.push(new AudioStreamStatsEvent(this,
					it->second->stream, it->second->stats));
Guillaume Beraudo's avatar
Guillaume Beraudo committed
592 593 594 595 596 597
			}
			ortp_event_destroy(ev);
		}
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
598
void Daemon::iterate() {
Yann Diorcet's avatar
Yann Diorcet committed
599
	linphone_core_iterate(mLc);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
600
	iterateStreamStats();
601
	if (mChildFd == (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
602
		if (!mEventQueue.empty()) {
603
			Event *r = mEventQueue.front();
Yann Diorcet's avatar
Yann Diorcet committed
604
			mEventQueue.pop();
605
			fprintf(stdout, "\n%s\n", r->toBuf().c_str());
Yann Diorcet's avatar
Yann Diorcet committed
606 607 608 609 610 611
			fflush(stdout);
			delete r;
		}
	}
}

612 613 614 615 616 617 618 619
void Daemon::execCommand(const string &command) {
	istringstream ist(command);
	string name;
	ist >> name;
	stringbuf argsbuf;
	ist.get(argsbuf);
	string args = argsbuf.str();
	if (!args.empty() && (args[0] == ' ')) args.erase(0, 1);
Yann Diorcet's avatar
Yann Diorcet committed
620 621
	list<DaemonCommand*>::iterator it = find_if(mCommands.begin(), mCommands.end(), bind2nd(mem_fun(&DaemonCommand::matches), name));
	if (it != mCommands.end()) {
622
		ms_mutex_lock(&mMutex);
Yann Diorcet's avatar
Yann Diorcet committed
623
		(*it)->exec(this, args);
624
		ms_mutex_unlock(&mMutex);
Yann Diorcet's avatar
Yann Diorcet committed
625
	} else {
Yann Diorcet's avatar
Yann Diorcet committed
626 627 628 629
		sendResponse(Response("Unknown command."));
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
630
void Daemon::sendResponse(const Response &resp) {
631
	string buf = resp.toBuf();
632
	if (mChildFd != (ortp_pipe_t)-1) {
Ghislain MARY's avatar
Ghislain MARY committed
633
		if (ortp_pipe_write(mChildFd, (uint8_t *)buf.c_str(), (int)buf.size()) == -1) {
Yann Diorcet's avatar
Yann Diorcet committed
634
			ms_error("Fail to write to pipe: %s", strerror(errno));
Yann Diorcet's avatar
Yann Diorcet committed
635
		}
Yann Diorcet's avatar
Yann Diorcet committed
636
	} else {
637
		cout << buf << flush;
Yann Diorcet's avatar
Yann Diorcet committed
638 639 640
	}
}

641 642 643 644
void Daemon::queueEvent(Event *ev){
	mEventQueue.push(ev);
}

645 646 647
string Daemon::readPipe() {
	char buffer[32768];
	memset(buffer, '\0', sizeof(buffer));
648 649 650 651 652 653
#ifdef _WIN32
	if (mChildFd == (ortp_pipe_t)-1) {
		mChildFd = ortp_server_pipe_accept_client(mServerFd);
		ms_message("Client accepted");
	}
	if (mChildFd != (ortp_pipe_t)-1) {
654
		int ret = ortp_pipe_read(mChildFd, (uint8_t *)buffer, sizeof(buffer));
655 656 657 658 659 660 661
		if (ret == -1) {
			ms_error("Fail to read from pipe: %s", strerror(errno));
			mChildFd = (ortp_pipe_t)-1;
		} else {
			if (ret == 0) {
				ms_message("Client disconnected");
				mChildFd = (ortp_pipe_t)-1;
662
				return "";
663
			}
664
			buffer[ret] = '\0';
665 666 667 668
			return buffer;
		}
	}
#else
669
	struct pollfd pfd[2];
Yann Diorcet's avatar
Yann Diorcet committed
670
	int nfds = 1;
671
	memset(&pfd[0], 0, sizeof(pfd));
672
	if (mServerFd != (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
673 674
		pfd[0].events = POLLIN;
		pfd[0].fd = mServerFd;
Yann Diorcet's avatar
Yann Diorcet committed
675
	}
676
	if (mChildFd != (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
677 678
		pfd[1].events = POLLIN;
		pfd[1].fd = mChildFd;
Yann Diorcet's avatar
Yann Diorcet committed
679 680
		nfds++;
	}
681
	int err = poll(pfd, (nfds_t)nfds, 50);
Yann Diorcet's avatar
Yann Diorcet committed
682
	if (err > 0) {
683
		if (mServerFd != (ortp_pipe_t)-1 && (pfd[0].revents & POLLIN)) {
Yann Diorcet's avatar
Yann Diorcet committed
684
			struct sockaddr_storage addr;
Yann Diorcet's avatar
Yann Diorcet committed
685 686 687
			socklen_t addrlen = sizeof(addr);
			int childfd = accept(mServerFd, (struct sockaddr*) &addr, &addrlen);
			if (childfd != -1) {
688
				if (mChildFd != (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
689 690
					ms_error("Cannot accept two client at the same time");
					close(childfd);
Yann Diorcet's avatar
Yann Diorcet committed
691
				} else {
692
					mChildFd = (ortp_pipe_t)childfd;
693
					return "";
Yann Diorcet's avatar
Yann Diorcet committed
694 695 696
				}
			}
		}
697
		if (mChildFd != (ortp_pipe_t)-1 && (pfd[1].revents & POLLIN)) {
Yann Diorcet's avatar
Yann Diorcet committed
698
			int ret;
699
			if ((ret = ortp_pipe_read(mChildFd, (uint8_t *)buffer, sizeof(buffer))) == -1) {
Yann Diorcet's avatar
Yann Diorcet committed
700
				ms_error("Fail to read from pipe: %s", strerror(errno));
Yann Diorcet's avatar
Yann Diorcet committed
701 702
			} else {
				if (ret == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
703
					ms_message("Client disconnected");
704 705
					ortp_server_pipe_close_client(mChildFd);
					mChildFd = (ortp_pipe_t)-1;
706
					return "";
Yann Diorcet's avatar
Yann Diorcet committed
707
				}
708
				buffer[ret] = '\0';
Yann Diorcet's avatar
Yann Diorcet committed
709 710 711 712
				return buffer;
			}
		}
	}
713
#endif
714
	return "";
Yann Diorcet's avatar
Yann Diorcet committed
715 716
}

Ghislain MARY's avatar
Ghislain MARY committed
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
void Daemon::dumpCommandsHelp() {
	int cols = 80;
#ifdef TIOCGSIZE
	struct ttysize ts;
	ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
	cols = ts.ts_cols;
#elif defined(TIOCGWINSZ)
	struct winsize ts;
	ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
	cols = ts.ws_col;
#endif

	cout << endl;
	for (list<DaemonCommand*>::iterator it = mCommands.begin(); it != mCommands.end(); ++it) {
		cout << setfill('-') << setw(cols) << "-" << endl << endl;
		cout << (*it)->getHelp();
	}
}

736 737 738
static string htmlEscape(const string &orig){
	string ret=orig;
	size_t pos;
Ghislain MARY's avatar
Ghislain MARY committed
739

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
	while(1){
		pos=ret.find('<');
		if (pos!=string::npos){
			ret.erase(pos,1);
			ret.insert(pos,"&lt");
			continue;
		}
		pos=ret.find('>');
		if (pos!=string::npos){
			ret.erase(pos,1);
			ret.insert(pos,"&gt");
			continue;
		}
		break;
	}
	while(1){
		pos=ret.find('\n');
		if (pos!=string::npos){
			ret.erase(pos,1);
			ret.insert(pos,"<br>");
			continue;
		}
		break;
	}
	return ret;
}

void Daemon::dumpCommandsHelpHtml(){
	cout << endl;
	cout << "<!DOCTYPE html><html><body>"<<endl;
	cout << "<h1>List of linphone-daemon commands.</h1>"<<endl;
	for (list<DaemonCommand*>::iterator it = mCommands.begin(); it != mCommands.end(); ++it) {
		cout<<"<h2>"<<htmlEscape((*it)->getProto())<<"</h2>"<<endl;
		cout<<"<h3>"<<"Description"<<"</h3>"<<endl;
		cout<<"<p>"<<htmlEscape((*it)->getDescription())<<"</p>"<<endl;
		cout<<"<h3>"<<"Examples"<<"</h3>"<<endl;
Ronan's avatar
Ronan committed
776
		const list<const DaemonCommandExample*> &examples=(*it)->getExamples();
777 778 779 780 781 782 783 784
		cout<<"<p><i>";
		for(list<const DaemonCommandExample*>::const_iterator ex_it=examples.begin();ex_it!=examples.end();++ex_it){
			cout<<"<b>"<<htmlEscape("Linphone-daemon>")<<htmlEscape((*ex_it)->getCommand())<<"</b><br>"<<endl;
			cout<<htmlEscape((*ex_it)->getOutput())<<"<br>"<<endl;
			cout<<"<br><br>";
		}
		cout<<"</i></p>"<<endl;
	}
Ghislain MARY's avatar
Ghislain MARY committed
785

786 787 788
	cout << "</body></html>"<<endl;
}

Ghislain MARY's avatar
Ghislain MARY committed
789

Yann Diorcet's avatar
Yann Diorcet committed
790
static void printHelp() {
791
	cout << "daemon-linphone [<options>]" << endl <<
792
#if defined(LICENCE_GPL) || defined(LICENCE_COMMERCIAL)
793
		"Licence: "
794
#ifdef LICENCE_GPL
795
		"GPL"
796 797
#endif
#ifdef LICENCE_COMMERCIAL
798
		"Commercial"
799
#endif
800
		<< endl <<
801 802
#endif

803 804 805 806
		"where options are :" << endl <<
		"\t--help                     Print this notice." << endl <<
		"\t--dump-commands-help       Dump the help of every available commands." << endl <<
		"\t--dump-commands-html-help  Dump the help of every available commands." << endl <<
807
		"\t--pipe <pipename>          Create an unix server socket in /tmp to receive commands from." << endl <<
808 809 810 811 812 813
		"\t--log <path>               Supply a file where the log will be saved." << endl <<
		"\t--factory-config <path>    Supply a readonly linphonerc style config file to start with." << endl <<
		"\t--config <path>            Supply a linphonerc style config file to start with." << endl <<
		"\t--disable-stats-events     Do not automatically raise RTP statistics events." << endl <<
		"\t--enable-lsd               Use the linphone sound daemon." << endl <<
		"\t-C                         Enable video capture." << endl <<
814 815
		"\t-D                         Enable video display." << endl <<
		"\t--auto-answer              Automatically answer incoming calls."<<endl;
Yann Diorcet's avatar
Yann Diorcet committed
816 817
}

818 819 820 821
void Daemon::startThread() {
	ms_thread_create(&this->mThread, NULL, Daemon::iterateThread, this);
}

822 823 824 825 826
#ifdef max
#undef max
#endif

string Daemon::readLine(const string& prompt, bool *eof) {
827
	*eof=false;
828
#ifdef HAVE_READLINE
829
	return readline(prompt.c_str());
830
#else
831 832
	if (cin.eof()) {
		*eof=true;
833
		return "";
834
	}
835
	cout << prompt;
836 837 838
	stringbuf outbuf;
	cin.get(outbuf);
	cin.clear();
Ronan's avatar
Ronan committed
839
	cin.ignore(numeric_limits<streamsize>::max(), '\n');
840
	return outbuf.str();
841 842 843
#endif
}

Yann Diorcet's avatar
Yann Diorcet committed
844
int Daemon::run() {
845
	const string prompt("daemon-linphone>");
Yann Diorcet's avatar
Yann Diorcet committed
846
	mRunning = true;
847
	startThread();
Yann Diorcet's avatar
Yann Diorcet committed
848
	while (mRunning) {
849
		string line;
850
		bool eof=false;
851
		if (mServerFd == (ortp_pipe_t)-1) {
852 853
			line = readLine(prompt, &eof);
			if (!line.empty()) {
854
#ifdef HAVE_READLINE
855
				add_history(line.c_str());
856
#endif
Yann Diorcet's avatar
Yann Diorcet committed
857
			}
Yann Diorcet's avatar
Yann Diorcet committed
858
		} else {
859
			line = readPipe();
Yann Diorcet's avatar
Yann Diorcet committed
860
		}
861 862
		if (!line.empty()) {
			execCommand(line);
Yann Diorcet's avatar
Yann Diorcet committed
863
		}
864
		if (eof && mRunning) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
865 866 867
			mRunning = false; // ctrl+d
			cout << "Quitting..." << endl;
		}
Yann Diorcet's avatar
Yann Diorcet committed
868
	}
869
	stopThread();
Yann Diorcet's avatar
Yann Diorcet committed
870 871 872
	return 0;
}

873 874 875 876 877
void Daemon::stopThread() {
	void *ret;
	ms_thread_join(mThread, &ret);
}

Yann Diorcet's avatar
Yann Diorcet committed
878 879
void Daemon::quit() {
	mRunning = false;
Yann Diorcet's avatar
Yann Diorcet committed
880 881
}

Simon Morlat's avatar
Simon Morlat committed
882 883 884 885
void Daemon::enableStatsEvents(bool enabled){
	mUseStatsEvents=enabled;
}

886 887 888 889
void Daemon::enableAutoAnswer(bool enabled){
	mAutoAnswer = enabled;
}

890 891 892 893
void Daemon::enableLSD(bool enabled) {
	if (mLSD) linphone_sound_daemon_destroy(mLSD);
	linphone_core_use_sound_daemon(mLc, NULL);
	if (enabled) {
894
		mLSD = linphone_sound_daemon_new(mLc->factory,NULL, 44100, 1);
895 896 897 898
		linphone_core_use_sound_daemon(mLc, mLSD);
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
899 900
Daemon::~Daemon() {
	uninitCommands();
901

Ronan's avatar
Ronan committed
902
	for (map<int, AudioStreamAndOther *>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
903
		audio_stream_stop(it->second->stream);
904 905
	}

906
	enableLSD(false);
Simon Morlat's avatar
Simon Morlat committed
907
	linphone_core_unref(mLc);
908 909
	if (mChildFd != (ortp_pipe_t)-1) {
		ortp_server_pipe_close_client(mChildFd);
Yann Diorcet's avatar
Yann Diorcet committed
910
	}
911
	if (mServerFd != (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
912 913
		ortp_server_pipe_close(mServerFd);
	}
914 915 916 917 918
	if (mLogFile != NULL) {
		linphone_core_enable_logs(NULL);
		fclose(mLogFile);
	}

919 920 921
	ms_mutex_destroy(&mMutex);

#ifdef HAVE_READLINE
Yann Diorcet's avatar
Yann Diorcet committed
922 923
	stifle_history(30);
	write_history(mHistfile.c_str());
924
#endif
Yann Diorcet's avatar
Yann Diorcet committed
925 926
}

927 928 929 930 931 932 933 934 935
static Daemon *the_app = NULL;

static void sighandler(int signum){
	if (the_app){
		the_app->quit();
		the_app = NULL;
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
936 937
int main(int argc, char *argv[]) {
	const char *config_path = NULL;
938
	const char *factory_config_path = NULL;
939
	const char *pipe_name = NULL;
940
	const char *log_file = NULL;
Yann Diorcet's avatar
Yann Diorcet committed
941 942
	bool capture_video = false;
	bool display_video = false;
Ghislain MARY's avatar
Ghislain MARY committed
943
	bool stats_enabled = true;
944
	bool lsd_enabled = false;
945
	bool auto_answer = false;
Yann Diorcet's avatar
Yann Diorcet committed
946 947
	int i;

Yann Diorcet's avatar
Yann Diorcet committed
948 949
	for (i = 1; i < argc; ++i) {
		if (strcmp(argv[i], "--help") == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
950 951
			printHelp();
			return 0;
Ghislain MARY's avatar
Ghislain MARY committed
952 953 954 955
		} else if (strcmp(argv[i], "--dump-commands-help") == 0) {
			Daemon app(NULL, NULL, NULL, NULL, false, false);
			app.dumpCommandsHelp();
			return 0;
956 957 958 959
		}else if (strcmp(argv[i], "--dump-commands-html-help") == 0) {
			Daemon app(NULL, NULL, NULL, NULL, false, false);
			app.dumpCommandsHelpHtml();
			return 0;
Yann Diorcet's avatar
Yann Diorcet committed
960
		} else if (strcmp(argv[i], "--pipe") == 0) {
961
			if (i + 1 >= argc) {
962
				fprintf(stderr, "no pipe name specify after --pipe\n");
963 964 965
				return -1;
			}
			pipe_name = argv[++i];
966
			stats_enabled = false;
967 968
		} else if (strcmp(argv[i], "--factory-config") == 0) {
			if (i + 1 >= argc) {
969
				fprintf(stderr, "no file specify after --factory-config\n");
970 971 972 973
				return -1;
			}
			factory_config_path = argv[i + 1];
			i++;
Yann Diorcet's avatar
Yann Diorcet committed
974
		} else if (strcmp(argv[i], "--config") == 0) {
975
			if (i + 1 >= argc) {
976
				fprintf(stderr, "no file specify after --config\n");
977 978
				return -1;
			}
Yann Diorcet's avatar
Yann Diorcet committed
979
			config_path = argv[i + 1];
980 981 982
			i++;
		} else if (strcmp(argv[i], "--log") == 0) {
			if (i + 1 >= argc) {
983
				fprintf(stderr, "no file specify after --log\n");
984 985 986 987
				return -1;
			}
			log_file = argv[i + 1];
			i++;
Yann Diorcet's avatar
Yann Diorcet committed
988 989 990 991
		} else if (strcmp(argv[i], "-C") == 0) {
			capture_video = true;
		} else if (strcmp(argv[i], "-D") == 0) {
			display_video = true;
Simon Morlat's avatar
Simon Morlat committed
992
		}else if (strcmp(argv[i],"--disable-stats-events")==0){
Ghislain MARY's avatar
Ghislain MARY committed
993
			stats_enabled = false;
994
		}else if (strcmp(argv[i], "--enable-lsd") == 0) {
995
			lsd_enabled = true;
996 997
		}else if (strcmp(argv[i], "--auto-answer") == 0) {
			auto_answer = true;
Yann Diorcet's avatar
Yann Diorcet committed
998
		}
999 1000 1001
		else{
			fprintf(stderr, "Unrecognized option : %s", argv[i]);
		}
Yann Diorcet's avatar
Yann Diorcet committed
1002
	}
1003
	Daemon app(config_path, factory_config_path, log_file, pipe_name, display_video, capture_video);
1004 1005 1006
	
	the_app = &app;
	signal(SIGINT, sighandler);
Ghislain MARY's avatar
Ghislain MARY committed
1007
	app.enableStatsEvents(stats_enabled);
1008
	app.enableLSD(lsd_enabled);
1009
	app.enableAutoAnswer(auto_answer);
Yann Diorcet's avatar
Yann Diorcet committed
1010
	return app.run();
Yann Diorcet's avatar
Yann Diorcet committed
1011
}