daemon.cc 28.7 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
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
#include "commands/ptime.h"
#include "commands/register.h"
73
#include "commands/register-info.h"
74 75 76 77
#include "commands/register-status.h"
#include "commands/terminate.h"
#include "commands/unregister.h"
#include "commands/quit.h"
78
#include "commands/configcommand.h"
79
#include "commands/netsim.h"
Simon Morlat's avatar
Simon Morlat committed
80
#include "commands/cn.h"
Ghislain MARY's avatar
Ghislain MARY committed
81
#include "commands/version.h"
Yann Diorcet's avatar
Yann Diorcet committed
82

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

Ghislain MARY's avatar
Ghislain MARY committed
86 87 88
#define INT_TO_VOIDPTR(i) ((void*)(intptr_t)(i))
#define VOIDPTR_TO_INT(p) ((int)(intptr_t)(p))

89 90 91 92 93 94 95 96 97 98 99 100 101 102
#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

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

112 113 114 115 116 117 118 119 120 121 122
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
123
EventResponse::EventResponse(Daemon *daemon, LinphoneCall *call, LinphoneCallState state) {
Yann Diorcet's avatar
Yann Diorcet committed
124
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
125 126 127
	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
128
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
129 130 131 132
	setBody(ostr.str().c_str());
	ms_free(remote);
}

Yann Diorcet's avatar
Yann Diorcet committed
133
DtmfResponse::DtmfResponse(Daemon *daemon, LinphoneCall *call, int dtmf) {
Yann Diorcet's avatar
Yann Diorcet committed
134 135 136 137
	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
138
	ostr << "Id: " << daemon->updateCallId(call) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
139 140 141 142
	setBody(ostr.str().c_str());
	ms_free(remote);
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
143
static ostream &printCallStatsHelper(ostream &ostr, const LinphoneCallStats *stats, const string &prefix) {
144 145 146
	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
147 148 149
//	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";
150
	ostr << prefix << "JitterBufferSizeMs: " << linphone_call_stats_get_jitter_buffer_size_ms(stats) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
151

152 153
	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
154

155 156
	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
157 158 159 160 161 162 163 164 165 166 167 168
	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: ";
169
		if (linphone_call_stats_get_type(stats) == LINPHONE_CALL_STATS_AUDIO) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
170 171 172 173 174 175
			ostr << "Audio";
		} else {
			ostr << "Video";
		}
		ostr << "\n";
	} else {
176
		prefix = ((linphone_call_stats_get_type(stats) == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
Guillaume Beraudo's avatar
Guillaume Beraudo committed
177 178 179
	}

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

181
	if (linphone_call_stats_get_type(stats) == LINPHONE_CALL_STATS_AUDIO) {
Yann Diorcet's avatar
Yann Diorcet committed
182 183 184 185 186
		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
187 188 189 190 191
	}

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

Guillaume Beraudo's avatar
Guillaume Beraudo committed
192 193 194 195

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

Guillaume Beraudo's avatar
Guillaume Beraudo committed
197 198 199 200 201
	ostringstream ostr;
	if (event) {
		ostr << "Event-type: audio-stream-stats\n";
		ostr << "Id: " << daemon->updateAudioStreamId(stream) << "\n";
		ostr << "Type: ";
202
		if (linphone_call_stats_get_type(stats) == LINPHONE_CALL_STATS_AUDIO) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
203 204 205 206 207 208
			ostr << "Audio";
		} else {
			ostr << "Video";
		}
		ostr << "\n";
	} else {
209
		prefix = ((linphone_call_stats_get_type(stats) == LINPHONE_CALL_STATS_AUDIO) ? "Audio-" : "Video-");
Guillaume Beraudo's avatar
Guillaume Beraudo committed
210
	}
211

Guillaume Beraudo's avatar
Guillaume Beraudo committed
212
	printCallStatsHelper(ostr, stats, prefix);
213

Guillaume Beraudo's avatar
Guillaume Beraudo committed
214 215 216
	setBody(ostr.str().c_str());
}

Yann Diorcet's avatar
Yann Diorcet committed
217
PayloadTypeResponse::PayloadTypeResponse(LinphoneCore *core, const PayloadType *payloadType, int index, const string &prefix, bool enabled_status) {
Yann Diorcet's avatar
Yann Diorcet committed
218
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
219 220 221
	if (payloadType != NULL) {
		if (index >= 0)
			ostr << prefix << "Index: " << index << "\n";
222
		ostr << prefix << "Payload-type-number: " << payload_type_get_number(payloadType) << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
223 224 225 226 227 228 229 230 231 232
		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
233 234
}

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

263
DaemonCommandExample::DaemonCommandExample(const string& command, const string& output)
Ghislain MARY's avatar
Ghislain MARY committed
264 265
	: mCommand(command), mOutput(output) {}

266
DaemonCommand::DaemonCommand(const string& name, const string& proto, const string& description) :
Ghislain MARY's avatar
Ghislain MARY committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
		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
287 288
}

289 290
bool DaemonCommand::matches(const string& name) const {
	return mName.compare(name) == 0;
Yann Diorcet's avatar
Yann Diorcet committed
291 292
}

293
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) :
294
		mLSD(0), mLogFile(NULL), mAutoVideo(0), mCallIds(0), mProxyIds(0), mAudioStreamIds(0) {
295
	ms_mutex_init(&mMutex, NULL);
296 297
	mServerFd = (ortp_pipe_t)-1;
	mChildFd = (ortp_pipe_t)-1;
298
	if (pipe_name == NULL) {
299 300
#ifdef HAVE_READLINE
		const char *homedir = getenv("HOME");
jehan's avatar
jehan committed
301
		rl_readline_name = (char*)"daemon";
302 303 304 305 306 307
		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
308
	} else {
309
		mServerFd = ortp_server_pipe_create(pipe_name);
310
#ifndef _WIN32
Yann Diorcet's avatar
Yann Diorcet committed
311
		listen(mServerFd, 2);
312 313
		fprintf(stdout, "Server unix socket created, name=%s fd=%i\n", pipe_name, (int)mServerFd);
#else
Ghislain MARY's avatar
Ghislain MARY committed
314
		fprintf(stdout, "Named pipe  created, name=%s fd=%p\n", pipe_name, mServerFd);
315
#endif
Yann Diorcet's avatar
Yann Diorcet committed
316 317
	}

318 319 320 321 322 323
	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
324

325 326
	LinphoneCoreVTable vtable;
	memset(&vtable, 0, sizeof(vtable));
Yann Diorcet's avatar
Yann Diorcet committed
327
	vtable.call_state_changed = callStateChanged;
Yann Diorcet's avatar
Yann Diorcet committed
328
	vtable.call_stats_updated = callStatsUpdated;
Yann Diorcet's avatar
Yann Diorcet committed
329
	vtable.dtmf_received = dtmfReceived;
330
	mLc = linphone_core_new(&vtable, config_path, factory_config_path, this);
Yann Diorcet's avatar
Yann Diorcet committed
331
	linphone_core_set_user_data(mLc, this);
332 333
	linphone_core_enable_video_capture(mLc,capture_video);
	linphone_core_enable_video_display(mLc,display_video);
Ronan's avatar
Ronan committed
334

335 336 337
	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
338

Yann Diorcet's avatar
Yann Diorcet committed
339
	initCommands();
Simon Morlat's avatar
Simon Morlat committed
340
	mUseStatsEvents=true;
Yann Diorcet's avatar
Yann Diorcet committed
341 342
}

Yann Diorcet's avatar
Yann Diorcet committed
343
const list<DaemonCommand*> &Daemon::getCommandList() const {
Yann Diorcet's avatar
Yann Diorcet committed
344 345 346
	return mCommands;
}

Yann Diorcet's avatar
Yann Diorcet committed
347
LinphoneCore *Daemon::getCore() {
Yann Diorcet's avatar
Yann Diorcet committed
348 349 350
	return mLc;
}

Ghislain MARY's avatar
Ghislain MARY committed
351 352 353 354
LinphoneSoundDaemon *Daemon::getLSD() {
	return mLSD;
}

Yann Diorcet's avatar
Yann Diorcet committed
355
int Daemon::updateCallId(LinphoneCall *call) {
356
	int val = VOIDPTR_TO_INT(linphone_call_get_user_data(call));
Yann Diorcet's avatar
Yann Diorcet committed
357
	if (val == 0) {
358
		linphone_call_set_user_data(call, INT_TO_VOIDPTR(++mCallIds));
359
		return mCallIds;
Yann Diorcet's avatar
Yann Diorcet committed
360 361
	}
	return val;
Yann Diorcet's avatar
Yann Diorcet committed
362 363
}

Yann Diorcet's avatar
Yann Diorcet committed
364
LinphoneCall *Daemon::findCall(int id) {
365
	const bctbx_list_t *elem = linphone_core_get_calls(mLc);
Yann Diorcet's avatar
Yann Diorcet committed
366 367
	for (; elem != NULL; elem = elem->next) {
		LinphoneCall *call = (LinphoneCall *) elem->data;
368
		if (VOIDPTR_TO_INT(linphone_call_get_user_data(call)) == id)
Yann Diorcet's avatar
Yann Diorcet committed
369 370 371 372 373
			return call;
	}
	return NULL;
}

Yann Diorcet's avatar
Yann Diorcet committed
374
int Daemon::updateProxyId(LinphoneProxyConfig *cfg) {
Ghislain MARY's avatar
Ghislain MARY committed
375
	int val = VOIDPTR_TO_INT(linphone_proxy_config_get_user_data(cfg));
Yann Diorcet's avatar
Yann Diorcet committed
376
	if (val == 0) {
Ghislain MARY's avatar
Ghislain MARY committed
377
		linphone_proxy_config_set_user_data(cfg, INT_TO_VOIDPTR(++mProxyIds));
378
		return mProxyIds;
Yann Diorcet's avatar
Yann Diorcet committed
379 380
	}
	return val;
Yann Diorcet's avatar
Yann Diorcet committed
381 382
}

Yann Diorcet's avatar
Yann Diorcet committed
383
LinphoneProxyConfig *Daemon::findProxy(int id) {
384
	const bctbx_list_t *elem = linphone_core_get_proxy_config_list(mLc);
Yann Diorcet's avatar
Yann Diorcet committed
385 386
	for (; elem != NULL; elem = elem->next) {
		LinphoneProxyConfig *proxy = (LinphoneProxyConfig *) elem->data;
Ghislain MARY's avatar
Ghislain MARY committed
387
		if (VOIDPTR_TO_INT(linphone_proxy_config_get_user_data(proxy)) == id)
Yann Diorcet's avatar
Yann Diorcet committed
388 389 390 391 392
			return proxy;
	}
	return NULL;
}

393
LinphoneAuthInfo *Daemon::findAuthInfo(int id)  {
394 395
	const bctbx_list_t *elem = linphone_core_get_auth_info_list(mLc);
	if (elem == NULL || id < 1 || (unsigned int)id > bctbx_list_size(elem)) {
396 397 398 399 400 401 402 403 404
		return NULL;
	}
	while (id > 1) {
		elem = elem->next;
		--id;
	}
	return (LinphoneAuthInfo *) elem->data;
}

Yann Diorcet's avatar
Yann Diorcet committed
405
int Daemon::updateAudioStreamId(AudioStream *audio_stream) {
Ronan's avatar
Ronan committed
406
	for (map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
407
		if (it->second->stream == audio_stream)
Yann Diorcet's avatar
Yann Diorcet committed
408 409 410
			return it->first;
	}

411 412 413
	++mAudioStreamIds;
	mAudioStreams.insert(make_pair(mAudioStreamIds, new AudioStreamAndOther(audio_stream)));
	return mAudioStreamIds;
Yann Diorcet's avatar
Yann Diorcet committed
414 415
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
416
AudioStreamAndOther *Daemon::findAudioStreamAndOther(int id) {
Ronan's avatar
Ronan committed
417
	map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
Yann Diorcet's avatar
Yann Diorcet committed
418 419 420 421 422
	if (it != mAudioStreams.end())
		return it->second;
	return NULL;
}

Guillaume Beraudo's avatar
Guillaume Beraudo committed
423
AudioStream *Daemon::findAudioStream(int id) {
Ronan's avatar
Ronan committed
424
	map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
425
	if (it != mAudioStreams.end())
Guillaume Beraudo's avatar
Guillaume Beraudo committed
426 427 428 429 430
		return it->second->stream;
	return NULL;
}

void Daemon::removeAudioStream(int id) {
Ronan's avatar
Ronan committed
431
	map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.find(id);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
432
	if (it != mAudioStreams.end()) {
433
		mAudioStreams.erase(it);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
434 435
		delete(it->second);
	}
436 437
}

438 439 440 441
static bool compareCommands(const DaemonCommand *command1, const DaemonCommand *command2) {
	return (command1->getProto() < command2->getProto());
}

Yann Diorcet's avatar
Yann Diorcet committed
442
void Daemon::initCommands() {
Yann Diorcet's avatar
Yann Diorcet committed
443
	mCommands.push_back(new RegisterCommand());
444
	mCommands.push_back(new ContactCommand());
Yann Diorcet's avatar
Yann Diorcet committed
445
	mCommands.push_back(new RegisterStatusCommand());
446
	mCommands.push_back(new RegisterInfoCommand());
Yann Diorcet's avatar
Yann Diorcet committed
447
	mCommands.push_back(new UnregisterCommand());
448
	mCommands.push_back(new AuthInfosClearCommand());
Yann Diorcet's avatar
Yann Diorcet committed
449 450
	mCommands.push_back(new CallCommand());
	mCommands.push_back(new TerminateCommand());
451
	mCommands.push_back(new DtmfCommand());
Ghislain MARY's avatar
Ghislain MARY committed
452
	mCommands.push_back(new PlayWavCommand());
Yann Diorcet's avatar
Yann Diorcet committed
453 454
	mCommands.push_back(new PopEventCommand());
	mCommands.push_back(new AnswerCommand());
Yann Diorcet's avatar
Yann Diorcet committed
455
	mCommands.push_back(new CallStatusCommand());
Yann Diorcet's avatar
Yann Diorcet committed
456
	mCommands.push_back(new CallStatsCommand());
457 458 459 460
	mCommands.push_back(new CallPauseCommand());
	mCommands.push_back(new CallMuteCommand());
	mCommands.push_back(new CallResumeCommand());
	mCommands.push_back(new CallTransferCommand());
461 462 463
	mCommands.push_back(new Video());
	mCommands.push_back(new VideoSource());
	mCommands.push_back(new AutoVideo());
464
	mCommands.push_back(new ConferenceCommand());
Yann Diorcet's avatar
Yann Diorcet committed
465 466 467 468
	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
469
	mCommands.push_back(new AudioCodecSetCommand());
Yann Diorcet's avatar
Yann Diorcet committed
470 471
	mCommands.push_back(new AudioStreamStartCommand());
	mCommands.push_back(new AudioStreamStopCommand());
Guillaume Beraudo's avatar
Guillaume Beraudo committed
472
	mCommands.push_back(new AudioStreamStatsCommand());
Yann Diorcet's avatar
Yann Diorcet committed
473
	mCommands.push_back(new MSFilterAddFmtpCommand());
Yann Diorcet's avatar
Yann Diorcet committed
474
	mCommands.push_back(new PtimeCommand());
Ghislain MARY's avatar
Ghislain MARY committed
475
	mCommands.push_back(new IPv6Command());
Ghislain MARY's avatar
Ghislain MARY committed
476
	mCommands.push_back(new FirewallPolicyCommand());
Ghislain MARY's avatar
Ghislain MARY committed
477
	mCommands.push_back(new MediaEncryptionCommand());
Ghislain MARY's avatar
Ghislain MARY committed
478
	mCommands.push_back(new PortCommand());
479
	mCommands.push_back(new AdaptiveBufferCompensationCommand());
Simon Morlat's avatar
Simon Morlat committed
480 481
	mCommands.push_back(new JitterBufferCommand());
	mCommands.push_back(new JitterBufferResetCommand());
Ghislain MARY's avatar
Ghislain MARY committed
482
	mCommands.push_back(new VersionCommand());
Yann Diorcet's avatar
Yann Diorcet committed
483 484
	mCommands.push_back(new QuitCommand());
	mCommands.push_back(new HelpCommand());
485 486
	mCommands.push_back(new ConfigGetCommand());
	mCommands.push_back(new ConfigSetCommand());
487
	mCommands.push_back(new NetsimCommand());
Simon Morlat's avatar
Simon Morlat committed
488
	mCommands.push_back(new CNCommand());
489
	mCommands.sort(compareCommands);
Yann Diorcet's avatar
Yann Diorcet committed
490 491 492 493 494 495 496
}

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

Yann Diorcet's avatar
Yann Diorcet committed
499 500
bool Daemon::pullEvent() {
	bool status = false;
Yann Diorcet's avatar
Yann Diorcet committed
501
	ostringstream ostr;
Yann Diorcet's avatar
Yann Diorcet committed
502 503
	if (!mEventQueue.empty()) {
		Response *r = mEventQueue.front();
Yann Diorcet's avatar
Yann Diorcet committed
504
		mEventQueue.pop();
Yann Diorcet's avatar
Yann Diorcet committed
505
		ostr << r->getBody() << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
506
		delete r;
Yann Diorcet's avatar
Yann Diorcet committed
507
		status = true;
Yann Diorcet's avatar
Yann Diorcet committed
508
	}
Yann Diorcet's avatar
Yann Diorcet committed
509
	ostr << "Size: " << mEventQueue.size() << "\n";
Yann Diorcet's avatar
Yann Diorcet committed
510 511
	sendResponse(Response(ostr.str().c_str(), Response::Ok));
	return status;
Yann Diorcet's avatar
Yann Diorcet committed
512 513
}

Yann Diorcet's avatar
Yann Diorcet committed
514
void Daemon::callStateChanged(LinphoneCall *call, LinphoneCallState state, const char *msg) {
515
	mEventQueue.push(new EventResponse(this, call, state));
Yann Diorcet's avatar
Yann Diorcet committed
516 517
}

Yann Diorcet's avatar
Yann Diorcet committed
518
void Daemon::callStatsUpdated(LinphoneCall *call, const LinphoneCallStats *stats) {
519 520
	if (mUseStatsEvents) {
		/* don't queue periodical updates (3 per seconds for just bandwidth updates) */
521
		if (!(_linphone_call_stats_get_updated(stats) & LINPHONE_CALL_STATS_PERIODICAL_UPDATE)){
522 523 524
			mEventQueue.push(new CallStatsResponse(this, call, stats, true));
		}
	}
Yann Diorcet's avatar
Yann Diorcet committed
525 526
}

Yann Diorcet's avatar
Yann Diorcet committed
527
void Daemon::dtmfReceived(LinphoneCall *call, int dtmf) {
Yann Diorcet's avatar
Yann Diorcet committed
528
	mEventQueue.push(new DtmfResponse(this, call, dtmf));
Yann Diorcet's avatar
Yann Diorcet committed
529 530
}

Yann Diorcet's avatar
Yann Diorcet committed
531 532 533
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
534
}
Yann Diorcet's avatar
Yann Diorcet committed
535 536 537 538
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
539 540 541 542
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
543

Guillaume Beraudo's avatar
Guillaume Beraudo committed
544
void Daemon::iterateStreamStats() {
Ronan's avatar
Ronan committed
545
	for (map<int, AudioStreamAndOther*>::iterator it = mAudioStreams.begin(); it != mAudioStreams.end(); ++it) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
546 547 548 549
		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) {
550
				linphone_call_stats_fill(it->second->stats, &it->second->stream->ms, ev);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
551
				if (mUseStatsEvents) mEventQueue.push(new AudioStreamStatsResponse(this,
552
					it->second->stream, it->second->stats, true));
Guillaume Beraudo's avatar
Guillaume Beraudo committed
553 554 555 556 557 558
			}
			ortp_event_destroy(ev);
		}
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
559
void Daemon::iterate() {
Yann Diorcet's avatar
Yann Diorcet committed
560
	linphone_core_iterate(mLc);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
561
	iterateStreamStats();
562
	if (mChildFd == (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
563 564
		if (!mEventQueue.empty()) {
			Response *r = mEventQueue.front();
Yann Diorcet's avatar
Yann Diorcet committed
565
			mEventQueue.pop();
Yann Diorcet's avatar
Yann Diorcet committed
566
			fprintf(stdout, "%s\n", r->getBody().c_str());
Yann Diorcet's avatar
Yann Diorcet committed
567 568 569 570 571 572
			fflush(stdout);
			delete r;
		}
	}
}

573 574 575 576 577 578 579 580
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
581 582
	list<DaemonCommand*>::iterator it = find_if(mCommands.begin(), mCommands.end(), bind2nd(mem_fun(&DaemonCommand::matches), name));
	if (it != mCommands.end()) {
583
		ms_mutex_lock(&mMutex);
Yann Diorcet's avatar
Yann Diorcet committed
584
		(*it)->exec(this, args);
585
		ms_mutex_unlock(&mMutex);
Yann Diorcet's avatar
Yann Diorcet committed
586
	} else {
Yann Diorcet's avatar
Yann Diorcet committed
587 588 589 590
		sendResponse(Response("Unknown command."));
	}
}

Yann Diorcet's avatar
Yann Diorcet committed
591
void Daemon::sendResponse(const Response &resp) {
592
	string buf = resp.toBuf();
593
	if (mChildFd != (ortp_pipe_t)-1) {
Ghislain MARY's avatar
Ghislain MARY committed
594
		if (ortp_pipe_write(mChildFd, (uint8_t *)buf.c_str(), (int)buf.size()) == -1) {
Yann Diorcet's avatar
Yann Diorcet committed
595
			ms_error("Fail to write to pipe: %s", strerror(errno));
Yann Diorcet's avatar
Yann Diorcet committed
596
		}
Yann Diorcet's avatar
Yann Diorcet committed
597
	} else {
598
		cout << buf << flush;
Yann Diorcet's avatar
Yann Diorcet committed
599 600 601
	}
}

602 603 604
string Daemon::readPipe() {
	char buffer[32768];
	memset(buffer, '\0', sizeof(buffer));
605 606 607 608 609 610
#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) {
611
		int ret = ortp_pipe_read(mChildFd, (uint8_t *)buffer, sizeof(buffer));
612 613 614 615 616 617 618
		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;
619
				return "";
620
			}
621
			buffer[ret] = '\0';
622 623 624 625
			return buffer;
		}
	}
#else
626
	struct pollfd pfd[2];
Yann Diorcet's avatar
Yann Diorcet committed
627
	int nfds = 1;
628
	memset(&pfd[0], 0, sizeof(pfd));
629
	if (mServerFd != (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
630 631
		pfd[0].events = POLLIN;
		pfd[0].fd = mServerFd;
Yann Diorcet's avatar
Yann Diorcet committed
632
	}
633
	if (mChildFd != (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
634 635
		pfd[1].events = POLLIN;
		pfd[1].fd = mChildFd;
Yann Diorcet's avatar
Yann Diorcet committed
636 637
		nfds++;
	}
638
	int err = poll(pfd, (nfds_t)nfds, 50);
Yann Diorcet's avatar
Yann Diorcet committed
639
	if (err > 0) {
640
		if (mServerFd != (ortp_pipe_t)-1 && (pfd[0].revents & POLLIN)) {
Yann Diorcet's avatar
Yann Diorcet committed
641
			struct sockaddr_storage addr;
Yann Diorcet's avatar
Yann Diorcet committed
642 643 644
			socklen_t addrlen = sizeof(addr);
			int childfd = accept(mServerFd, (struct sockaddr*) &addr, &addrlen);
			if (childfd != -1) {
645
				if (mChildFd != (ortp_pipe_t)-1) {
Yann Diorcet's avatar
Yann Diorcet committed
646 647
					ms_error("Cannot accept two client at the same time");
					close(childfd);
Yann Diorcet's avatar
Yann Diorcet committed
648
				} else {
649
					mChildFd = (ortp_pipe_t)childfd;
650
					return "";
Yann Diorcet's avatar
Yann Diorcet committed
651 652 653
				}
			}
		}
654
		if (mChildFd != (ortp_pipe_t)-1 && (pfd[1].revents & POLLIN)) {
Yann Diorcet's avatar
Yann Diorcet committed
655
			int ret;
656
			if ((ret = ortp_pipe_read(mChildFd, (uint8_t *)buffer, sizeof(buffer))) == -1) {
Yann Diorcet's avatar
Yann Diorcet committed
657
				ms_error("Fail to read from pipe: %s", strerror(errno));
Yann Diorcet's avatar
Yann Diorcet committed
658 659
			} else {
				if (ret == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
660
					ms_message("Client disconnected");
661 662
					ortp_server_pipe_close_client(mChildFd);
					mChildFd = (ortp_pipe_t)-1;
663
					return "";
Yann Diorcet's avatar
Yann Diorcet committed
664
				}
665
				buffer[ret] = '\0';
Yann Diorcet's avatar
Yann Diorcet committed
666 667 668 669
				return buffer;
			}
		}
	}
670
#endif
671
	return "";
Yann Diorcet's avatar
Yann Diorcet committed
672 673
}

Ghislain MARY's avatar
Ghislain MARY committed
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
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
693 694 695
static string htmlEscape(const string &orig){
	string ret=orig;
	size_t pos;
Ghislain MARY's avatar
Ghislain MARY committed
696

Simon Morlat's avatar
Simon Morlat committed
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
	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
733
		const list<const DaemonCommandExample*> &examples=(*it)->getExamples();
Simon Morlat's avatar
Simon Morlat committed
734 735 736 737 738 739 740 741
		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
742

Simon Morlat's avatar
Simon Morlat committed
743 744 745
	cout << "</body></html>"<<endl;
}

Ghislain MARY's avatar
Ghislain MARY committed
746

Yann Diorcet's avatar
Yann Diorcet committed
747
static void printHelp() {
748
	cout << "daemon-linphone [<options>]" << endl <<
749
#if defined(LICENCE_GPL) || defined(LICENCE_COMMERCIAL)
750
		"Licence: "
751
#ifdef LICENCE_GPL
752
		"GPL"
753 754
#endif
#ifdef LICENCE_COMMERCIAL
755
		"Commercial"
756
#endif
757
		<< endl <<
758 759
#endif

760 761 762 763 764 765 766 767 768 769 770 771
		"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
772 773
}

774 775 776 777
void Daemon::startThread() {
	ms_thread_create(&this->mThread, NULL, Daemon::iterateThread, this);
}

778 779 780 781 782
#ifdef max
#undef max
#endif

string Daemon::readLine(const string& prompt, bool *eof) {
783
	*eof=false;
784
#ifdef HAVE_READLINE
785
	return readline(prompt.c_str());
786
#else
787 788
	if (cin.eof()) {
		*eof=true;
789
		return "";
790
	}
791
	cout << prompt;
792 793 794
	stringbuf outbuf;
	cin.get(outbuf);
	cin.clear();
Ronan's avatar
Ronan committed
795
	cin.ignore(numeric_limits<streamsize>::max(), '\n');
796
	return outbuf.str();
797 798 799
#endif
}

Yann Diorcet's avatar
Yann Diorcet committed
800
int Daemon::run() {
801
	const string prompt("daemon-linphone>");
Yann Diorcet's avatar
Yann Diorcet committed
802
	mRunning = true;
803
	startThread();
Yann Diorcet's avatar
Yann Diorcet committed
804
	while (mRunning) {
805
		string line;
806
		bool eof=false;
807
		if (mServerFd == (ortp_pipe_t)-1) {
808 809
			line = readLine(prompt, &eof);
			if (!line.empty()) {
810
#ifdef HAVE_READLINE
811
				add_history(line.c_str());
812
#endif
Yann Diorcet's avatar
Yann Diorcet committed
813
			}
Yann Diorcet's avatar
Yann Diorcet committed
814
		} else {
815
			line = readPipe();
Yann Diorcet's avatar
Yann Diorcet committed
816
		}
817 818
		if (!line.empty()) {
			execCommand(line);
Yann Diorcet's avatar
Yann Diorcet committed
819
		}
820
		if (eof && mRunning) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed