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

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
Yann Diorcet's avatar
Yann Diorcet committed
24
#include <iostream>
Ghislain MARY's avatar
Ghislain MARY committed
25
#include <iomanip>
Yann Diorcet's avatar
Yann Diorcet committed
26 27
#include <sstream>
#include <algorithm>
28
#include <functional>
Ghislain MARY's avatar
Ghislain MARY committed
29
#include <limits>
Yann Diorcet's avatar
Yann Diorcet committed
30

31
#ifdef HAVE_READLINE
32 33
#include <readline/readline.h>
#include <readline/history.h>
34
#endif
Yann Diorcet's avatar
Yann Diorcet committed
35

36
#ifndef _WIN32
37
#include <poll.h>
38
#endif
Yann Diorcet's avatar
Yann Diorcet committed
39

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

Guillaume Beraudo's avatar
Guillaume Beraudo committed
82
#include "private.h"
83
using namespace std;
Yann Diorcet's avatar
Yann Diorcet committed
84

85 86 87 88 89 90 91 92 93 94 95 96 97 98
#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

99 100
const char * const ice_state_str[] = {
	"Not activated",	/* LinphoneIceStateNotActivated */
Ghislain MARY's avatar
Ghislain MARY committed
101
	"Failed",	/* LinphoneIceStateFailed */
102 103 104 105 106 107
	"In progress",	/* LinphoneIceStateInProgress */
	"Host connection",	/* LinphoneIceStateHostConnection */
	"Reflexive connection",	/* LinphoneIceStateReflexiveConnection */
	"Relayed connection"	/* LinphoneIceStateRelayConnection */
};

108 109 110 111 112 113 114 115 116 117 118
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;
}

Yann Diorcet's avatar
Yann Diorcet committed
119
EventResponse::EventResponse(Daemon *daemon, LinphoneCall *call, LinphoneCallState state) {
Yann Diorcet's avatar
Yann Diorcet committed
120
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
121 122 123
	char *remote = linphone_call_get_remote_address_as_string(call);
	ostr << "Event-type: call-state-changed\nEvent: " << linphone_call_state_to_string(state) << "\n";
	ostr << "From: " << remote << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
124
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
125 126 127 128
	setBody(ostr.str().c_str());
	ms_free(remote);
}

Yann Diorcet's avatar
Yann Diorcet committed
129
DtmfResponse::DtmfResponse(Daemon *daemon, LinphoneCall *call, int dtmf) {
Yann Diorcet's avatar
Yann Diorcet committed
130 131 132 133
	ostringstream ostr;
	char *remote = linphone_call_get_remote_address_as_string(call);
	ostr << "Event-type: receiving-tone\nTone: " << (char) dtmf << "\n";
	ostr << "From: " << remote << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
134
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
135 136 137 138
	setBody(ostr.str().c_str());
	ms_free(remote);
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
139
static ostream &printCallStatsHelper(ostream &ostr, const LinphoneCallStats *stats, const string &prefix) {
140
	ostr << prefix << "ICE state: " << ice_state_str[stats->ice_state] << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
	ostr << prefix << "RoundTripDelay: " << stats->round_trip_delay << "\n";
	ostr << prefix << "Jitter: " << stats->jitter_stats.jitter << "\n";
//	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";
	ostr << prefix << "JitterBufferSizeMs: " << stats->jitter_stats.jitter_buffer_size_ms << "\n";

	const report_block_t *rrb = NULL;
	if (stats->received_rtcp != NULL) {
		if (stats->received_rtcp->b_cont != NULL)
			msgpullup(stats->received_rtcp, -1);
		if (rtcp_is_SR(stats->received_rtcp)) {
			rrb = rtcp_SR_get_report_block(stats->received_rtcp, 0);
		} else if (rtcp_is_RR(stats->received_rtcp)) {
			rrb = rtcp_RR_get_report_block(stats->received_rtcp, 0);
		}
	}
	if (rrb) {
		unsigned int ij;
		float flost;
		ij = report_block_get_interarrival_jitter(rrb);
		flost = (float) (100.0 * report_block_get_fraction_lost(rrb) / 256.0);
		ostr << prefix << "Received-InterarrivalJitter: " << ij << "\n";
		ostr << prefix << "Received-FractionLost: " << flost << "\n";
	}

	const report_block_t *srb = NULL;
	if (stats->sent_rtcp != NULL) {
		if (stats->sent_rtcp->b_cont != NULL)
			msgpullup(stats->sent_rtcp, -1);
		if (rtcp_is_SR(stats->sent_rtcp)) {
			srb = rtcp_SR_get_report_block(stats->sent_rtcp, 0);
		} else if (rtcp_is_RR(stats->sent_rtcp)) {
			srb = rtcp_RR_get_report_block(stats->sent_rtcp, 0);
		}
	}
	if (srb) {
		unsigned int ij;
		float flost;
		ij = report_block_get_interarrival_jitter(srb);
		flost = (float) (100.0 * report_block_get_fraction_lost(srb) / 256.0);
		ostr << prefix << "Sent-InterarrivalJitter: " << ij << "\n";
		ostr << prefix << "Sent-FractionLost: " << flost << "\n";
	}
Guillaume Beraudo's avatar
Guillaume Beraudo committed
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
	return ostr;
}

CallStatsResponse::CallStatsResponse(Daemon *daemon, LinphoneCall *call, const LinphoneCallStats *stats, bool event) {
	const LinphoneCallParams *callParams = linphone_call_get_current_params(call);
	const char *prefix = "";

	ostringstream ostr;
	if (event) {
		ostr << "Event-type: call-stats\n";
		ostr << "Id: " << daemon->updateCallId(call) << "\n";
		ostr << "Type: ";
		if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
			ostr << "Audio";
		} else {
			ostr << "Video";
		}
		ostr << "\n";
	} else {
		prefix = ((stats->type == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
	}

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

Yann Diorcet's avatar
Yann Diorcet committed
209 210 211 212 213 214
	if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
		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
215 216 217 218 219
	}

	setBody(ostr.str().c_str());
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
220 221 222 223

AudioStreamStatsResponse::AudioStreamStatsResponse(Daemon* daemon, AudioStream* stream,
		const LinphoneCallStats *stats, bool event) {
	const char *prefix = "";
224

Guillaume Beraudo's avatar
Guillaume Beraudo committed
225 226 227 228 229 230 231 232 233 234 235 236 237 238
	ostringstream ostr;
	if (event) {
		ostr << "Event-type: audio-stream-stats\n";
		ostr << "Id: " << daemon->updateAudioStreamId(stream) << "\n";
		ostr << "Type: ";
		if (stats->type == LINPHONE_CALL_STATS_AUDIO) {
			ostr << "Audio";
		} else {
			ostr << "Video";
		}
		ostr << "\n";
	} else {
		prefix = ((stats->type == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
	}
239

Guillaume Beraudo's avatar
Guillaume Beraudo committed
240
	printCallStatsHelper(ostr, stats, prefix);
241

Guillaume Beraudo's avatar
Guillaume Beraudo committed
242 243 244
	setBody(ostr.str().c_str());
}

Yann Diorcet's avatar
Yann Diorcet committed
245
PayloadTypeResponse::PayloadTypeResponse(LinphoneCore *core, const PayloadType *payloadType, int index, const string &prefix, bool enabled_status) {
Yann Diorcet's avatar
Yann Diorcet committed
246
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260
	if (payloadType != NULL) {
		if (index >= 0)
			ostr << prefix << "Index: " << index << "\n";
		ostr << prefix << "Payload-type-number: " << linphone_core_get_payload_type_number(core, payloadType) << "\n";
		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
261 262
}

263 264
PayloadTypeParser::PayloadTypeParser(LinphoneCore *core, const string &mime_type, bool accept_all) : mAll(false), mSuccesful(true), mPayloadType(NULL),mPosition(-1){
	int number=-1;
265 266 267 268
	if (accept_all && (mime_type.compare("ALL") == 0)) {
		mAll = true;
		return;
	}
269
	istringstream ist(mime_type);
270
	ist >> number;
271
	if (ist.fail()) {
272
		char type[64]={0};
273
		int rate, channels;
274
		if (sscanf(mime_type.c_str(), "%63[^/]/%u/%u", type, &rate, &channels) != 3) {
275 276 277
			mSuccesful = false;
			return;
		}
278
		mPayloadType = linphone_core_find_payload_type(core, type, rate, channels);
279
		if (mPayloadType) mPosition=bctbx_list_index(linphone_core_get_audio_codecs(core), mPayloadType);
280
	}else if (number!=-1){
281
		const bctbx_list_t *elem;
282 283 284 285 286
		for(elem=linphone_core_get_audio_codecs(core);elem!=NULL;elem=elem->next){
			if (number==linphone_core_get_payload_type_number(core,(PayloadType*)elem->data)){
				mPayloadType=(PayloadType*)elem->data;
				break;
			}
287 288 289 290
		}
	}
}

291
DaemonCommandExample::DaemonCommandExample(const string& command, const string& output)
Ghislain MARY's avatar
Ghislain MARY committed
292 293
	: mCommand(command), mOutput(output) {}

294
DaemonCommand::DaemonCommand(const string& name, const string& proto, const string& description) :
Ghislain MARY's avatar
Ghislain MARY committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
		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
315 316
}

317 318
bool DaemonCommand::matches(const string& name) const {
	return mName.compare(name) == 0;
Yann Diorcet's avatar
Yann Diorcet committed
319 320
}

321
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) :
322
		mLSD(0), mLogFile(NULL), mAutoVideo(0), mCallIds(0), mProxyIds(0), mAudioStreamIds(0) {
323
	ms_mutex_init(&mMutex, NULL);
324 325
	mServerFd = (ortp_pipe_t)-1;
	mChildFd = (ortp_pipe_t)-1;
326
	if (pipe_name == NULL) {
327 328
#ifdef HAVE_READLINE
		const char *homedir = getenv("HOME");
jehan's avatar
jehan committed
329
		rl_readline_name = (char*)"daemon";
330 331 332 333 334 335
		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
336
	} else {
337
		mServerFd = ortp_server_pipe_create(pipe_name);
338
#ifndef _WIN32
Yann Diorcet's avatar
Yann Diorcet committed
339
		listen(mServerFd, 2);
340 341 342 343
		fprintf(stdout, "Server unix socket created, name=%s fd=%i\n", pipe_name, (int)mServerFd);
#else
		fprintf(stdout, "Named pipe  created, name=%s fd=%i\n", pipe_name, (int)mServerFd);
#endif
Yann Diorcet's avatar
Yann Diorcet committed
344 345
	}

346 347 348 349 350 351
	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
352

353 354
	LinphoneCoreVTable vtable;
	memset(&vtable, 0, sizeof(vtable));
Yann Diorcet's avatar
Yann Diorcet committed
355
	vtable.call_state_changed = callStateChanged;
Yann Diorcet's avatar
Yann Diorcet committed
356
	vtable.call_stats_updated = callStatsUpdated;
Yann Diorcet's avatar
Yann Diorcet committed
357
	vtable.dtmf_received = dtmfReceived;
358
	mLc = linphone_core_new(&vtable, config_path, factory_config_path, this);
Yann Diorcet's avatar
Yann Diorcet committed
359
	linphone_core_set_user_data(mLc, this);
360 361
	linphone_core_enable_video_capture(mLc,capture_video);
	linphone_core_enable_video_display(mLc,display_video);
Yann Diorcet's avatar
Yann Diorcet committed
362
	initCommands();
Simon Morlat's avatar
Simon Morlat committed
363
	mUseStatsEvents=true;
Yann Diorcet's avatar
Yann Diorcet committed
364 365
}

Yann Diorcet's avatar
Yann Diorcet committed
366
const list<DaemonCommand*> &Daemon::getCommandList() const {
Yann Diorcet's avatar
Yann Diorcet committed
367 368 369
	return mCommands;
}

Yann Diorcet's avatar
Yann Diorcet committed
370
LinphoneCore *Daemon::getCore() {
Yann Diorcet's avatar
Yann Diorcet committed
371 372 373
	return mLc;
}

Ghislain MARY's avatar
Ghislain MARY committed
374 375 376 377
LinphoneSoundDaemon *Daemon::getLSD() {
	return mLSD;
}

Yann Diorcet's avatar
Yann Diorcet committed
378 379 380
int Daemon::updateCallId(LinphoneCall *call) {
	int val = (int) (long) linphone_call_get_user_pointer(call);
	if (val == 0) {
381 382
		linphone_call_set_user_pointer(call, (void*) (long) ++mCallIds);
		return mCallIds;
Yann Diorcet's avatar
Yann Diorcet committed
383 384
	}
	return val;
Yann Diorcet's avatar
Yann Diorcet committed
385 386
}

Yann Diorcet's avatar
Yann Diorcet committed
387
LinphoneCall *Daemon::findCall(int id) {
388
	const bctbx_list_t *elem = linphone_core_get_calls(mLc);
Yann Diorcet's avatar
Yann Diorcet committed
389 390 391
	for (; elem != NULL; elem = elem->next) {
		LinphoneCall *call = (LinphoneCall *) elem->data;
		if (linphone_call_get_user_pointer(call) == (void*) (long) id)
Yann Diorcet's avatar
Yann Diorcet committed
392 393 394 395 396
			return call;
	}
	return NULL;
}

Yann Diorcet's avatar
Yann Diorcet committed
397 398 399
int Daemon::updateProxyId(LinphoneProxyConfig *cfg) {
	int val = (int) (long) linphone_proxy_config_get_user_data(cfg);
	if (val == 0) {
400 401
		linphone_proxy_config_set_user_data(cfg, (void*) (long) ++mProxyIds);
		return mProxyIds;
Yann Diorcet's avatar
Yann Diorcet committed
402 403
	}
	return val;
Yann Diorcet's avatar
Yann Diorcet committed
404 405
}

Yann Diorcet's avatar
Yann Diorcet committed
406
LinphoneProxyConfig *Daemon::findProxy(int id) {
407
	const bctbx_list_t *elem = linphone_core_get_proxy_config_list(mLc);
Yann Diorcet's avatar
Yann Diorcet committed
408 409 410
	for (; elem != NULL; elem = elem->next) {
		LinphoneProxyConfig *proxy = (LinphoneProxyConfig *) elem->data;
		if (linphone_proxy_config_get_user_data(proxy) == (void*) (long) id)
Yann Diorcet's avatar
Yann Diorcet committed
411 412 413 414 415
			return proxy;
	}
	return NULL;
}

416
LinphoneAuthInfo *Daemon::findAuthInfo(int id)  {
417 418
	const bctbx_list_t *elem = linphone_core_get_auth_info_list(mLc);
	if (elem == NULL || id < 1 || (unsigned int)id > bctbx_list_size(elem)) {
419 420 421 422 423 424 425 426 427
		return NULL;
	}
	while (id > 1) {
		elem = elem->next;
		--id;
	}
	return (LinphoneAuthInfo *) elem->data;
}

Yann Diorcet's avatar
Yann Diorcet committed
428
int Daemon::updateAudioStreamId(AudioStream *audio_stream) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
429 430
	for (std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
		if (it->second->stream == audio_stream)
Yann Diorcet's avatar
Yann Diorcet committed
431 432 433
			return it->first;
	}

434 435 436
	++mAudioStreamIds;
	mAudioStreams.insert(make_pair(mAudioStreamIds, new AudioStreamAndOther(audio_stream)));
	return mAudioStreamIds;
Yann Diorcet's avatar
Yann Diorcet committed
437 438
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
439 440
AudioStreamAndOther *Daemon::findAudioStreamAndOther(int id) {
	std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
Yann Diorcet's avatar
Yann Diorcet committed
441 442 443 444 445
	if (it != mAudioStreams.end())
		return it->second;
	return NULL;
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
446 447
AudioStream *Daemon::findAudioStream(int id) {
	std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
448
	if (it != mAudioStreams.end())
Guillaume Beraudo's avatar
Guillaume Beraudo committed
449 450 451 452 453 454 455
		return it->second->stream;
	return NULL;
}

void Daemon::removeAudioStream(int id) {
	std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
	if (it != mAudioStreams.end()) {
456
		mAudioStreams.erase(it);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
457 458
		delete(it->second);
	}
459 460
}

461 462 463 464
static bool compareCommands(const DaemonCommand *command1, const DaemonCommand *command2) {
	return (command1->getProto() < command2->getProto());
}

Yann Diorcet's avatar
Yann Diorcet committed
465
void Daemon::initCommands() {
Yann Diorcet's avatar
Yann Diorcet committed
466
	mCommands.push_back(new RegisterCommand());
467
	mCommands.push_back(new ContactCommand());
Yann Diorcet's avatar
Yann Diorcet committed
468
	mCommands.push_back(new RegisterStatusCommand());
Yann Diorcet's avatar
Yann Diorcet committed
469
	mCommands.push_back(new UnregisterCommand());
470
	mCommands.push_back(new AuthInfosClearCommand());
Yann Diorcet's avatar
Yann Diorcet committed
471 472
	mCommands.push_back(new CallCommand());
	mCommands.push_back(new TerminateCommand());
473
	mCommands.push_back(new DtmfCommand());
Ghislain MARY's avatar
Ghislain MARY committed
474
	mCommands.push_back(new PlayWavCommand());
Yann Diorcet's avatar
Yann Diorcet committed
475 476
	mCommands.push_back(new PopEventCommand());
	mCommands.push_back(new AnswerCommand());
Yann Diorcet's avatar
Yann Diorcet committed
477
	mCommands.push_back(new CallStatusCommand());
Yann Diorcet's avatar
Yann Diorcet committed
478
	mCommands.push_back(new CallStatsCommand());
479 480 481 482
	mCommands.push_back(new CallPauseCommand());
	mCommands.push_back(new CallMuteCommand());
	mCommands.push_back(new CallResumeCommand());
	mCommands.push_back(new CallTransferCommand());
483 484 485
	mCommands.push_back(new Video());
	mCommands.push_back(new VideoSource());
	mCommands.push_back(new AutoVideo());
486
	mCommands.push_back(new ConferenceCommand());
Yann Diorcet's avatar
Yann Diorcet committed
487 488 489 490
	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
491
	mCommands.push_back(new AudioCodecSetCommand());
Yann Diorcet's avatar
Yann Diorcet committed
492 493
	mCommands.push_back(new AudioStreamStartCommand());
	mCommands.push_back(new AudioStreamStopCommand());
Guillaume Beraudo's avatar
Guillaume Beraudo committed
494
	mCommands.push_back(new AudioStreamStatsCommand());
Yann Diorcet's avatar
Yann Diorcet committed
495
	mCommands.push_back(new MSFilterAddFmtpCommand());
Yann Diorcet's avatar
Yann Diorcet committed
496
	mCommands.push_back(new PtimeCommand());
Ghislain MARY's avatar
Ghislain MARY committed
497
	mCommands.push_back(new IPv6Command());
Ghislain MARY's avatar
Ghislain MARY committed
498
	mCommands.push_back(new FirewallPolicyCommand());
Ghislain MARY's avatar
Ghislain MARY committed
499
	mCommands.push_back(new MediaEncryptionCommand());
Ghislain MARY's avatar
Ghislain MARY committed
500
	mCommands.push_back(new PortCommand());
501
	mCommands.push_back(new AdaptiveBufferCompensationCommand());
Simon Morlat's avatar
Simon Morlat committed
502 503
	mCommands.push_back(new JitterBufferCommand());
	mCommands.push_back(new JitterBufferResetCommand());
Ghislain MARY's avatar
Ghislain MARY committed
504
	mCommands.push_back(new VersionCommand());
Yann Diorcet's avatar
Yann Diorcet committed
505 506
	mCommands.push_back(new QuitCommand());
	mCommands.push_back(new HelpCommand());
507 508
	mCommands.push_back(new ConfigGetCommand());
	mCommands.push_back(new ConfigSetCommand());
509
	mCommands.push_back(new NetsimCommand());
Simon Morlat's avatar
Simon Morlat committed
510
	mCommands.push_back(new CNCommand());
511
	mCommands.sort(compareCommands);
Yann Diorcet's avatar
Yann Diorcet committed
512 513 514 515 516 517 518
}

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

Yann Diorcet's avatar
Yann Diorcet committed
521 522
bool Daemon::pullEvent() {
	bool status = false;
Yann Diorcet's avatar
Yann Diorcet committed
523
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
524 525
	if (!mEventQueue.empty()) {
		Response *r = mEventQueue.front();
Yann Diorcet's avatar
Yann Diorcet committed
526
		mEventQueue.pop();
Yann Diorcet's avatar
Yann Diorcet committed
527
		ostr << r->getBody() << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
528
		delete r;
Yann Diorcet's avatar
Yann Diorcet committed
529
		status = true;
Yann Diorcet's avatar
Yann Diorcet committed
530
	}
Yann Diorcet's avatar
Yann Diorcet committed
531
	ostr << "Size: " << mEventQueue.size() << "\n";
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
	mEventQueue.push(new EventResponse(this, call, state));
Yann Diorcet's avatar
Yann Diorcet committed
538 539
}

Yann Diorcet's avatar
Yann Diorcet committed
540
void Daemon::callStatsUpdated(LinphoneCall *call, const LinphoneCallStats *stats) {
541 542 543 544 545 546
	if (mUseStatsEvents) {
		/* don't queue periodical updates (3 per seconds for just bandwidth updates) */
		if (!(stats->updated & LINPHONE_CALL_STATS_PERIODICAL_UPDATE)){
			mEventQueue.push(new CallStatsResponse(this, call, stats, true));
		}
	}
Yann Diorcet's avatar
Yann Diorcet committed
547 548
}

Yann Diorcet's avatar
Yann Diorcet committed
549
void Daemon::dtmfReceived(LinphoneCall *call, int dtmf) {
Yann Diorcet's avatar
Yann Diorcet committed
550
	mEventQueue.push(new DtmfResponse(this, call, dtmf));
Yann Diorcet's avatar
Yann Diorcet committed
551 552
}

Yann Diorcet's avatar
Yann Diorcet committed
553 554 555
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
556
}
Yann Diorcet's avatar
Yann Diorcet committed
557 558 559 560
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
561 562 563 564
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
565

Guillaume Beraudo's avatar
Guillaume Beraudo committed
566 567 568 569 570 571
void Daemon::iterateStreamStats() {
	for (std::map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
		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) {
Simon Morlat's avatar
Simon Morlat committed
572
				linphone_call_stats_fill(&it->second->stats, &it->second->stream->ms, ev);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
573 574 575 576 577 578 579 580
				if (mUseStatsEvents) mEventQueue.push(new AudioStreamStatsResponse(this,
					it->second->stream, &it->second->stats, true));
			}
			ortp_event_destroy(ev);
		}
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
581
void Daemon::iterate() {
Yann Diorcet's avatar
Yann Diorcet committed
582
	linphone_core_iterate(mLc);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
583
	iterateStreamStats();
584
	if (mChildFd == (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
585 586
		if (!mEventQueue.empty()) {
			Response *r = mEventQueue.front();
Yann Diorcet's avatar
Yann Diorcet committed
587
			mEventQueue.pop();
Yann Diorcet's avatar
Yann Diorcet committed
588
			fprintf(stdout, "%s\n", r->getBody().c_str());
Yann Diorcet's avatar
Yann Diorcet committed
589 590 591 592 593 594
			fflush(stdout);
			delete r;
		}
	}
}

595 596 597 598 599 600 601 602
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
603 604
	list<DaemonCommand*>::iterator it = find_if(mCommands.begin(), mCommands.end(), bind2nd(mem_fun(&DaemonCommand::matches), name));
	if (it != mCommands.end()) {
605
		ms_mutex_lock(&mMutex);
Yann Diorcet's avatar
Yann Diorcet committed
606
		(*it)->exec(this, args);
607
		ms_mutex_unlock(&mMutex);
Yann Diorcet's avatar
Yann Diorcet committed
608
	} else {
Yann Diorcet's avatar
Yann Diorcet committed
609 610 611 612
		sendResponse(Response("Unknown command."));
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
613
void Daemon::sendResponse(const Response &resp) {
614
	string buf = resp.toBuf();
615
	if (mChildFd != (ortp_pipe_t)-1) {
616
		if (ortp_pipe_write(mChildFd, (uint8_t *)buf.c_str(), buf.size()) == -1) {
Yann Diorcet's avatar
Yann Diorcet committed
617
			ms_error("Fail to write to pipe: %s", strerror(errno));
Yann Diorcet's avatar
Yann Diorcet committed
618
		}
Yann Diorcet's avatar
Yann Diorcet committed
619
	} else {
620
		cout << buf << flush;
Yann Diorcet's avatar
Yann Diorcet committed
621 622 623
	}
}

624 625 626
string Daemon::readPipe() {
	char buffer[32768];
	memset(buffer, '\0', sizeof(buffer));
627 628 629 630 631 632
#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) {
633
		int ret = ortp_pipe_read(mChildFd, (uint8_t *)buffer, sizeof(buffer));
634 635 636 637 638 639 640
		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;
641
				return "";
642
			}
643
			buffer[ret] = '\0';
644 645 646 647
			return buffer;
		}
	}
#else
648
	struct pollfd pfd[2];
Yann Diorcet's avatar
Yann Diorcet committed
649
	int nfds = 1;
650
	memset(&pfd[0], 0, sizeof(pfd));
651
	if (mServerFd != (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
652 653
		pfd[0].events = POLLIN;
		pfd[0].fd = mServerFd;
Yann Diorcet's avatar
Yann Diorcet committed
654
	}
655
	if (mChildFd != (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
656 657
		pfd[1].events = POLLIN;
		pfd[1].fd = mChildFd;
Yann Diorcet's avatar
Yann Diorcet committed
658 659
		nfds++;
	}
Yann Diorcet's avatar
Yann Diorcet committed
660 661
	int err = poll(pfd, nfds, 50);
	if (err > 0) {
662
		if (mServerFd != (ortp_pipe_t)-1 && (pfd[0].revents & POLLIN)) {
Yann Diorcet's avatar
Yann Diorcet committed
663
			struct sockaddr_storage addr;
Yann Diorcet's avatar
Yann Diorcet committed
664 665 666
			socklen_t addrlen = sizeof(addr);
			int childfd = accept(mServerFd, (struct sockaddr*) &addr, &addrlen);
			if (childfd != -1) {
667
				if (mChildFd != (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
668 669
					ms_error("Cannot accept two client at the same time");
					close(childfd);
Yann Diorcet's avatar
Yann Diorcet committed
670
				} else {
671
					mChildFd = (ortp_pipe_t)childfd;
Yann Diorcet's avatar
Yann Diorcet committed
672 673 674 675
					return NULL;
				}
			}
		}
676
		if (mChildFd != (ortp_pipe_t)-1 && (pfd[1].revents & POLLIN)) {
Yann Diorcet's avatar
Yann Diorcet committed
677
			int ret;
678
			if ((ret = ortp_pipe_read(mChildFd, (uint8_t *)buffer, sizeof(buffer))) == -1) {
Yann Diorcet's avatar
Yann Diorcet committed
679
				ms_error("Fail to read from pipe: %s", strerror(errno));
Yann Diorcet's avatar
Yann Diorcet committed
680 681
			} else {
				if (ret == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
682
					ms_message("Client disconnected");
683 684
					ortp_server_pipe_close_client(mChildFd);
					mChildFd = (ortp_pipe_t)-1;
Yann Diorcet's avatar
Yann Diorcet committed
685 686
					return NULL;
				}
687
				buffer[ret] = '\0';
Yann Diorcet's avatar
Yann Diorcet committed
688 689 690 691
				return buffer;
			}
		}
	}
692
#endif
Yann Diorcet's avatar
Yann Diorcet committed
693 694 695
	return NULL;
}

Ghislain MARY's avatar
Ghislain MARY committed
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
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();
	}
}

Simon Morlat's avatar
Simon Morlat committed
715 716 717
static string htmlEscape(const string &orig){
	string ret=orig;
	size_t pos;
Ghislain MARY's avatar
Ghislain MARY committed
718

Simon Morlat's avatar
Simon Morlat committed
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 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
	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;
		const std::list<const DaemonCommandExample*> &examples=(*it)->getExamples();
		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
764

Simon Morlat's avatar
Simon Morlat committed
765 766 767
	cout << "</body></html>"<<endl;
}

Ghislain MARY's avatar
Ghislain MARY committed
768

Yann Diorcet's avatar
Yann Diorcet committed
769
static void printHelp() {
770
	cout << "daemon-linphone [<options>]" << endl <<
771
#if defined(LICENCE_GPL) || defined(LICENCE_COMMERCIAL)
772
		"Licence: "
773
#ifdef LICENCE_GPL
774
		"GPL"
775 776
#endif
#ifdef LICENCE_COMMERCIAL
777
		"Commercial"
778
#endif
779
		<< endl <<
780 781
#endif

782 783 784 785 786 787 788 789 790 791 792 793
		"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 <<
		"\t--pipe <pipename>          Create an unix server socket to receive commands." << endl <<
		"\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 <<
		"\t-D                         Enable video display." << endl;
Yann Diorcet's avatar
Yann Diorcet committed
794 795
}

796 797 798 799
void Daemon::startThread() {
	ms_thread_create(&this->mThread, NULL, Daemon::iterateThread, this);
}

800 801 802 803 804
#ifdef max
#undef max
#endif

string Daemon::readLine(const string& prompt, bool *eof) {
805
	*eof=false;
806
#ifdef HAVE_READLINE
807
	return readline(prompt.c_str());
808
#else
809 810
	if (cin.eof()) {
		*eof=true;
811
		return "";
812
	}
813
	cout << prompt;
814 815 816 817 818
	stringbuf outbuf;
	cin.get(outbuf);
	cin.clear();
	cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
	return outbuf.str();
819 820 821
#endif
}

Yann Diorcet's avatar
Yann Diorcet committed
822
int Daemon::run() {
823
	const string prompt("daemon-linphone>");
Yann Diorcet's avatar
Yann Diorcet committed
824
	mRunning = true;
825
	startThread();
Yann Diorcet's avatar
Yann Diorcet committed
826
	while (mRunning) {
827
		string line;
828
		bool eof=false;
829
		if (mServerFd == (ortp_pipe_t)-1) {
830 831
			line = readLine(prompt, &eof);
			if (!line.empty()) {
832
#ifdef HAVE_READLINE
833
				add_history(line.c_str());
834
#endif
Yann Diorcet's avatar
Yann Diorcet committed
835
			}
Yann Diorcet's avatar
Yann Diorcet committed
836
		} else {
837
			line = readPipe();
Yann Diorcet's avatar
Yann Diorcet committed
838
		}
839 840
		if (!line.empty()) {
			execCommand(line);
Yann Diorcet's avatar
Yann Diorcet committed
841
		}
842
		if (eof && mRunning) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
843 844 845
			mRunning = false; // ctrl+d
			cout << "Quitting..." << endl;
		}
Yann Diorcet's avatar
Yann Diorcet committed
846
	}
847
	stopThread();
Yann Diorcet's avatar
Yann Diorcet committed
848 849 850
	return 0;
}

851 852 853 854 855
void Daemon::stopThread() {
	void *ret;
	ms_thread_join(mThread, &ret);
}

Yann Diorcet's avatar
Yann Diorcet committed
856 857
void Daemon::quit() {
	mRunning = false;
Yann Diorcet's avatar
Yann Diorcet committed
858 859
}

Simon Morlat's avatar
Simon Morlat committed
860 861 862 863
void Daemon::enableStatsEvents(bool enabled){
	mUseStatsEvents=enabled;
}

Ghislain MARY's avatar
Ghislain MARY committed
864 865 866 867
void Daemon::enableLSD(bool enabled) {
	if (mLSD) linphone_sound_daemon_destroy(mLSD);
	linphone_core_use_sound_daemon(mLc, NULL);
	if (enabled) {
868
		mLSD = linphone_sound_daemon_new(mLc->factory,NULL, 44100, 1);
Ghislain MARY's avatar
Ghislain MARY committed
869 870 871 872
		linphone_core_use_sound_daemon(mLc, mLSD);
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
873 874
Daemon::~Daemon() {
	uninitCommands();
Yann Diorcet's avatar
Yann Diorcet committed
875

Guillaume Beraudo's avatar
Guillaume Beraudo committed
876 877
	for (std::map<int, AudioStreamAndOther *>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
		audio_stream_stop(it->second->stream);
Yann Diorcet's avatar
Yann Diorcet committed
878 879
	}