/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>

   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   - Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright notice,
     this list of conditions and the following disclaimer in the documentation
     and/or other materials provided with the distribution.
   - Neither the name of the Mumble Developers nor the names of its
     contributors may be used to endorse or promote products derived from this
     software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "Server.h"
#include "ServerUser.h"
#include "ServerDB.h"
#include "Connection.h"
#include "Message.h"
#include "DBus.h"

QDBusArgument &operator<<(QDBusArgument &a, const PlayerInfo &s) {
	a.beginStructure();
	a << s.session << s.mute << s.deaf << s.suppressed << s.selfMute << s.selfDeaf << s.channel;
	a.endStructure();
	return a;
}

const QDBusArgument & operator >>(const QDBusArgument &a, PlayerInfo &s) {
	a.beginStructure();
	a >> s.session >> s.mute >> s.deaf >> s.suppressed >> s.selfMute >> s.selfDeaf >> s.channel;
	a.endStructure();
	return a;
}

QDBusArgument &operator<<(QDBusArgument &a, const PlayerInfoExtended &s) {
	a.beginStructure();
	a << s.session << s.mute << s.deaf << s.suppressed << s.selfMute << s.selfDeaf << s.channel << s.id << s.name << s.onlinesecs << s.bytespersec;
	a.endStructure();
	return a;
}

const QDBusArgument & operator >>(const QDBusArgument &a, PlayerInfoExtended &s) {
	a.beginStructure();
	a >> s.session >> s.mute >> s.deaf >> s.suppressed >> s.selfMute >> s.selfDeaf >> s.channel >> s.id >> s.name >> s.onlinesecs >> s.bytespersec;
	a.endStructure();
	return a;
}

QDBusArgument &operator<<(QDBusArgument &a, const ChannelInfo &s) {
	a.beginStructure();
	a << s.id << s.name << s.parent << s.links;
	a.endStructure();
	return a;
}

const QDBusArgument & operator >>(const QDBusArgument &a, ChannelInfo &s) {
	a.beginStructure();
	a >> s.id >> s.name >> s.parent >> s.links;
	a.endStructure();
	return a;
}

QDBusArgument &operator<<(QDBusArgument &a, const GroupInfo &s) {
	a.beginStructure();
	a << s.name << s.inherited << s.inherit << s.inheritable;
	a << s.add << s.remove << s.members;
	a.endStructure();
	return a;
}

const QDBusArgument & operator >>(const QDBusArgument &a, GroupInfo &s) {
	a.beginStructure();
	a >> s.name >> s.inherited >> s.inherit >> s.inheritable;
	a >> s.add >> s.remove >> s.members;
	a.endStructure();
	return a;
}

QDBusArgument &operator<<(QDBusArgument &a, const ACLInfo &s) {
	a.beginStructure();
	a << s.applyHere << s.applySubs << s.inherited;
	a << s.playerid << s.group;
	a << s.allow << s.deny;
	a.endStructure();
	return a;
}

const QDBusArgument & operator >>(const QDBusArgument &a, ACLInfo &s) {
	a.beginStructure();
	a >> s.applyHere >> s.applySubs >> s.inherited;
	a >> s.playerid >> s.group;
	a >> s.allow >> s.deny;
	a.endStructure();
	return a;
}

QDBusArgument &operator<<(QDBusArgument &a, const BanInfo &s) {
	a.beginStructure();
	a << s.address << s.bits;
	a.endStructure();
	return a;
}

const QDBusArgument & operator >>(const QDBusArgument &a, BanInfo &s) {
	a.beginStructure();
	a >> s.address >> s.bits;
	a.endStructure();
	return a;
}

QDBusArgument &operator<<(QDBusArgument &a, const RegisteredPlayer &s) {
	a.beginStructure();
	a << s.id << s.name << s.email << s.pw;
	a.endStructure();
	return a;
}

const QDBusArgument & operator >>(const QDBusArgument &a, RegisteredPlayer &s) {
	a.beginStructure();
	a >> s.id >> s.name >> s.email >> s.pw;
	a.endStructure();
	return a;
}


QDBusArgument &operator<<(QDBusArgument &a, const LogEntry &s) {
	a.beginStructure();
	a << s.timestamp << s.txt;
	a.endStructure();
	return a;
}

const QDBusArgument & operator >>(const QDBusArgument &a, LogEntry &s) {
	a.beginStructure();
	a >> s.timestamp >> s.txt;
	a.endStructure();
	return a;
}

void MurmurDBus::registerTypes() {
	qDBusRegisterMetaType<PlayerInfo>();
	qDBusRegisterMetaType<PlayerInfoExtended>();
	qDBusRegisterMetaType<QList<PlayerInfoExtended> >();
	qDBusRegisterMetaType<ChannelInfo>();
	qDBusRegisterMetaType<QList<ChannelInfo> >();
	qDBusRegisterMetaType<GroupInfo>();
	qDBusRegisterMetaType<QList<GroupInfo> >();
	qDBusRegisterMetaType<ACLInfo>();
	qDBusRegisterMetaType<QList<ACLInfo> >();
	qDBusRegisterMetaType<BanInfo>();
	qDBusRegisterMetaType<QList<BanInfo> >();
	qDBusRegisterMetaType<RegisteredPlayer>();
	qDBusRegisterMetaType<QList<RegisteredPlayer> >();
	qDBusRegisterMetaType<ConfigMap>();
	qDBusRegisterMetaType<LogEntry>();
	qDBusRegisterMetaType<QList<LogEntry> >();
}

QDBusConnection MurmurDBus::qdbc(QLatin1String("mainbus"));

MurmurDBus::MurmurDBus(Server *srv) : QDBusAbstractAdaptor(srv) {
	server = srv;
	srv->connectListener(this);
}

void MurmurDBus::removeAuthenticator() {
	server->disconnectAuthenticator(this);
	qsAuthPath = QString();
	qsAuthService = QString();
}


void MurmurDBus::idToNameSlot(QString &name, int id) {
	QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
	QDBusReply<QString> reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "getUserName",id);
	if (reply.isValid())
		name = reply.value();
	else {
		server->log(QString("DBus Authenticator failed getUserName for %1: %2").arg(QString::number(id), QString(reply.error().message())));
		removeAuthenticator();
	}
}

void MurmurDBus::idToTextureSlot(QByteArray &qba, int id) {
	QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
	QDBusReply<QByteArray> reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "getUserTexture",id);
	if (reply.isValid()) {
		qba = reply.value();
	}
}

void MurmurDBus::nameToIdSlot(int &id, const QString &name) {
	QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
	QDBusReply<int> reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "getUserId",name);
	if (reply.isValid())
		id = reply.value();
	else {
		server->log(QString("DBus Authenticator failed getUserId for %1: %2").arg(name).arg(reply.error().message()));
		removeAuthenticator();
	}
}

void MurmurDBus::registerUserSlot(int &res, const QMap<int, QString> &info) {
	QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
	QDBusReply<int> reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "registerPlayer", info.value(ServerDB::User_Name));
	if (reply.isValid()) {
		res = reply.value();
		if ((info.count() > 1) && (res > 0))
			setInfoSlot(res, res, info);
	}
}

void MurmurDBus::unregisterUserSlot(int &res, int id) {
	QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
	QDBusReply<int> reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "unregisterPlayer", id);
	if (reply.isValid())
		res = reply.value();
}

void MurmurDBus::getRegistrationSlot(int &res, int id, QMap<int, QString> &info) {
	QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
	QDBusReply<RegisteredPlayer> reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "getRegistration", id);
	if (reply.isValid()) {
		const RegisteredPlayer &r = reply.value();
		info.insert(ServerDB::User_Name, r.name);
		if (! r.email.isEmpty())
			info.insert(ServerDB::User_Email, r.email);
		res = 1;
	}
}

void  MurmurDBus::getRegisteredUsersSlot(const QString &filter, QMap<int, QString > &m) {
	QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
	QDBusReply<QList<RegisteredPlayer> > reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "getRegisteredPlayers", filter);
	if (reply.isValid()) {
		const QList<RegisteredPlayer> &r = reply.value();
		foreach(const RegisteredPlayer &p, r) {
			m.insert(p.id, p.name);
		}
	}
}

void MurmurDBus::setInfoSlot(int &res, int id, const QMap<int, QString> &info) {
	if (info.contains(ServerDB::User_Name)) {
		QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
		QDBusReply<int> reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "setName",id, info.value(ServerDB::User_Name));
		if (reply.isValid())
			res = reply.value();
	}
	if (info.contains(ServerDB::User_Password)) {
		QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
		QDBusReply<int> reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "setPW",id, info.value(ServerDB::User_Password));
		if (reply.isValid())
			res = reply.value();
	}
	if (info.contains(ServerDB::User_Email)) {
		QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
		QDBusReply<int> reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "setEmail",id, info.value(ServerDB::User_Email));
		if (reply.isValid())
			res = reply.value();
	}
}

void MurmurDBus::setTextureSlot(int &res, int id, const QByteArray &texture) {
	QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
	QDBusReply<int> reply = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "setTexture",id, texture);
	if (reply.isValid())
		res = reply.value();
}

void MurmurDBus::authenticateSlot(int &res, QString &uname, const QList<QSslCertificate> &, const QString &, bool, const QString &pw) {
	QDBusInterface remoteApp(qsAuthService,qsAuthPath,QString(),qdbc);
	QDBusMessage msg = remoteApp.call(bReentrant ? QDBus::BlockWithGui : QDBus::Block, "authenticate",uname,pw);
	QDBusError err = msg;
	if (! err.isValid()) {
		QString newname;
		int uid = -2;
		bool ok = true;
		if (msg.arguments().count() >= 1) {
			uid = msg.arguments().at(0).toInt(&ok);
		}
		if (ok && (msg.arguments().count() >= 2)) {
			newname = msg.arguments().at(1).toString();
			if (! newname.isEmpty()) {
				uname = newname;
			}
		}
		if (ok && (msg.arguments().count() >= 3)) {
			server->setTempGroups(uid, NULL, msg.arguments().at(2).toStringList());
		}
		if (ok) {
			server->log(QString("DBus Authenticate success for %1: %2").arg(uname).arg(uid));
			res = uid;
		} else {
			server->log(QString("DBus Autenticator failed authenticate for %1: Invalid return type").arg(uname));
		}
	} else {
		server->log(QString("DBus Authenticator failed authenticate for %1: %2").arg(uname).arg(err.message()));
		removeAuthenticator();
	}
}

#define PLAYER_SETUP_VAR(var) \
  ServerUser *pUser = server->qhUsers.value(var); \
  if (! pUser) { \
    qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.session", "Invalid session id")); \
    return; \
  }

#define PLAYER_SETUP PLAYER_SETUP_VAR(session)

#define CHANNEL_SETUP_VAR2(dst,var) \
  Channel *dst = server->qhChannels.value(var); \
  if (! dst) { \
    qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.channel", "Invalid channel id")); \
    return; \
  }

#define CHANNEL_SETUP_VAR(var) CHANNEL_SETUP_VAR2(cChannel,var)

void MurmurDBus::getPlayers(QList<PlayerInfoExtended> &a) {
	a.clear();
	foreach(ServerUser *p, server->qhUsers) {
		if (static_cast<ServerUser *>(p)->sState == ServerUser::Authenticated)
			a << PlayerInfoExtended(p);
	}
}

void MurmurDBus::getChannels(QList<ChannelInfo> &a) {
	a.clear();
	QQueue<Channel *> q;
	q << server->qhChannels.value(0);
	while (! q.isEmpty()) {
		Channel *c = q.dequeue();
		a << ChannelInfo(c);
		foreach(c, c->qlChannels)
			q.enqueue(c);
	}
}

void MurmurDBus::kickPlayer(unsigned int session, const QString &reason, const QDBusMessage &msg) {
	PLAYER_SETUP;
	Connection *c = server->qhUsers.value(session);
	if (!c)
		return;

	MumbleProto::UserRemove mpur;
	mpur.set_session(session);
	mpur.set_reason(u8(reason));
	server->sendAll(mpur);
	c->disconnectSocket();
}

void MurmurDBus::getPlayerState(unsigned int session, const QDBusMessage &msg, PlayerInfo &pi) {
	PLAYER_SETUP;
	pi = PlayerInfo(pUser);
}

void MurmurDBus::setPlayerState(const PlayerInfo &npi, const QDBusMessage &msg) {
	PLAYER_SETUP_VAR(npi.session);
	CHANNEL_SETUP_VAR(npi.channel);
	PlayerInfo pi(pUser);

	server->setUserState(pUser, cChannel, npi.mute, npi.deaf, pUser->bPrioritySpeaker, npi.suppressed);
}

void MurmurDBus::sendMessage(unsigned int session, const QString &text, const QDBusMessage &msg) {
	PLAYER_SETUP;

	server->sendTextMessage(NULL, pUser, false, text);
}

void MurmurDBus::sendMessageChannel(int id, bool tree, const QString &text, const QDBusMessage &msg) {
	CHANNEL_SETUP_VAR(id);

	server->sendTextMessage(cChannel, NULL, tree, text);
}

void MurmurDBus::addChannel(const QString &name, int chanparent, const QDBusMessage &msg, int &newid) {
	CHANNEL_SETUP_VAR(chanparent);

	Channel *nc = server->addChannel(cChannel, name);
	server->updateChannel(nc);
	newid = nc->iId;

	MumbleProto::ChannelState mpcs;
	mpcs.set_channel_id(nc->iId);
	mpcs.set_parent(cChannel->iId);
	mpcs.set_name(u8(name));
	server->sendAll(mpcs);
}

void MurmurDBus::removeChannel(int id, const QDBusMessage &msg) {
	CHANNEL_SETUP_VAR(id);
	if (!cChannel->cParent) {
		qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.channel", "Invalid channel id"));
		return;
	}
	server->removeChannel(cChannel);
}

void MurmurDBus::getChannelState(int id, const QDBusMessage &msg, ChannelInfo &state) {
	CHANNEL_SETUP_VAR(id);
	state = ChannelInfo(cChannel);
}

void MurmurDBus::setChannelState(const ChannelInfo &nci, const QDBusMessage &msg) {
	CHANNEL_SETUP_VAR(nci.id);
	CHANNEL_SETUP_VAR2(cParent, nci.parent);

	QSet<Channel *> newset;
	foreach(int id, nci.links) {
		CHANNEL_SETUP_VAR2(cLink, id);
		newset << cLink;
	}

	if (! server->setChannelState(cChannel, cParent, nci.name, newset)) {
		qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.channel", "Moving channel to subchannel"));
		return;
	}
}

void MurmurDBus::getACL(int id, const QDBusMessage &msg, QList<ACLInfo> &acls, QList<GroupInfo> &groups, bool &inherit) {
	CHANNEL_SETUP_VAR(id);

	QStack<Channel *> chans;
	Channel *p;
	ChanACL *acl;

	p = cChannel;
	while (p) {
		chans.push(p);
		if ((p == cChannel) || (p->bInheritACL))
			p = p->cParent;
		else
			p =NULL;
	}

	inherit = cChannel->bInheritACL;

	while (! chans.isEmpty()) {
		p = chans.pop();
		foreach(acl, p->qlACL) {
			if ((p==cChannel) || (acl->bApplySubs)) {
				ACLInfo ai(acl);
				if (p != cChannel)
					ai.inherited = true;
				acls << ai;
			}
		}
	}

	p = cChannel->cParent;
	QSet<QString> allnames = Group::groupNames(cChannel);
	QString name;
	foreach(name, allnames) {
		Group *g = cChannel->qhGroups.value(name);
		Group *pg = p ? Group::getGroup(p, name) : NULL;
		if (!g && ! pg)
			continue;
		GroupInfo gi(g ? g : pg);
		QSet<int> members;
		if (pg)
			members = pg->members();
		if (g) {
			gi.add = g->qsAdd.toList();
			gi.remove = g->qsRemove.toList();
			gi.inherited = false;
			members+=g->qsAdd;
			members-=g->qsRemove;
		} else {
			gi.inherited = true;
		}
		gi.members = members.toList();
		groups << gi;
	}
}

void MurmurDBus::setACL(int id, const QList<ACLInfo> &acls, const QList<GroupInfo> &groups, bool inherit, const QDBusMessage &msg) {
	CHANNEL_SETUP_VAR(id);

	Group *g;
	ChanACL *a;
	ACLInfo ai;
	GroupInfo gi;

	QHash<QString, QSet<int> > hOldTemp;

	foreach(g, cChannel->qhGroups) {
		hOldTemp.insert(g->qsName, g->qsTemporary);
		delete g;
	}
	foreach(a, cChannel->qlACL)
		delete a;
	cChannel->qhGroups.clear();
	cChannel->qlACL.clear();

	cChannel->bInheritACL = inherit;

	foreach(gi, groups) {
		g = new Group(cChannel, gi.name);
		g->bInherit=gi.inherit;
		g->bInheritable=gi.inheritable;
		g->qsAdd = gi.add.toSet();
		g->qsRemove = gi.remove.toSet();
		g->qsTemporary = hOldTemp.value(gi.name);
	}

	foreach(ai, acls) {
		a = new ChanACL(cChannel);
		a->bApplyHere = ai.applyHere;
		a->bApplySubs = ai.applySubs;
		a->iUserId = ai.playerid;
		a->qsGroup = ai.group;
		a->pDeny = static_cast<ChanACL::Permissions>(ai.deny) & ChanACL::All;
		a->pAllow = static_cast<ChanACL::Permissions>(ai.allow) & ChanACL::All;
	}

	server->clearACLCache();
	server->updateChannel(cChannel);
}

void MurmurDBus::getBans(QList<BanInfo> &bi) {
	bi.clear();
	foreach(const Ban &b, server->qlBans) {
		if (! b.haAddress.isV6())
			bi << BanInfo(b);
	}
}

void MurmurDBus::setBans(const QList<BanInfo> &, const QDBusMessage &) {
}

void MurmurDBus::getPlayerNames(const QList<int> &ids, const QDBusMessage &, QStringList &names) {
	names.clear();
	foreach(int id, ids) {
		names << server->getUserName(id);
	}
}

void MurmurDBus::getPlayerIds(const QStringList &names, const QDBusMessage &, QList<int> &ids) {
	ids.clear();
	foreach(QString name, names) {
		ids << server->getUserID(name);
	}
}

void MurmurDBus::registerPlayer(const QString &name, const QDBusMessage &msg, int &id) {
	QMap<int, QString> info;
	info.insert(ServerDB::User_Name, name);
	id = server->registerUser(info);
	if (id < 0) {
		qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.playername", "Illegal player name"));
		return;
	}
}

void MurmurDBus::unregisterPlayer(int id, const QDBusMessage &msg) {
	if (! server->unregisterUser(id)) {
		qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.playerid", "Invalid player id"));
		return;
	}
}

void MurmurDBus::getRegistration(int id, const QDBusMessage &msg, RegisteredPlayer &user) {
	QMap<int, QString> info = server->getRegistration(id);
	if (info.isEmpty()) {
		qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.playerid", "Invalid player id"));
		return;
	}

	user.id = id;
	user.name = info.value(ServerDB::User_Name);
	user.email = info.value(ServerDB::User_Email);
}

void MurmurDBus::setRegistration(int id, const QString &name, const QString &email, const QString &pw, const QDBusMessage &msg) {
	RegisteredPlayer user;
	user.id = id;
	user.name = name;
	user.email = email;
	user.pw = pw;
	updateRegistration(user, msg);
}

void MurmurDBus::updateRegistration(const RegisteredPlayer &user, const QDBusMessage &msg) {
	QMap<int, QString> info;

	if (! user.name.isEmpty())
		info.insert(ServerDB::User_Name, user.name);

	if (! user.email.isEmpty())
		info.insert(ServerDB::User_Email, user.email);

	if (! user.pw.isEmpty())
		info.insert(ServerDB::User_Password, user.pw);

	if (info.isEmpty() || !server->setInfo(user.id, info)) {
		qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.playerid", "Invalid player id"));
		return;
	}
}

void MurmurDBus::getTexture(int id, const QDBusMessage &msg, QByteArray &texture) {
	if (! server->isUserId(id)) {
		qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.playerid", "Invalid player id"));
		return;
	}
	texture = server->getUserTexture(id);
}

void MurmurDBus::setTexture(int id, const QByteArray &texture, const QDBusMessage &msg) {
	if (! server->isUserId(id)) {
		qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.playerid", "Invalid player id"));
		return;
	}
	if (! server->setTexture(id, texture)) {
		qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.texture", "Invalid texture"));
		return;
	}
}

void MurmurDBus::getRegisteredPlayers(const QString &filter, QList<RegisteredPlayer> &users) {
	users.clear();
	QMap<int, QString > l = server->getRegisteredUsers(filter);
	QMap<int, QString >::const_iterator i;
	for (i = l.constBegin(); i != l.constEnd(); ++i) {
		RegisteredPlayer r;
		r.id = i.key();
		r.name = i.value();
		users << r;
	}
}

void MurmurDBus::verifyPassword(int id, const QString &pw, const QDBusMessage &msg, bool &ok) {
	QList<int> ids;
	ids << id;
	QStringList names;
	getPlayerNames(ids, msg, names);

	if ((names.count() != 1) || (names.at(0).isEmpty())) {
		qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.playerid", "Invalid player id"));
		return;
	}

	QString name = names.at(0);
	int nid = server->authenticate(name, pw);
	if (nid == id)
		ok = true;
	else
		ok = false;
}

#ifdef Q_OS_WIN
#undef interface
#endif

void MurmurDBus::setAuthenticator(const QDBusObjectPath &path, bool reentrant, const QDBusMessage &msg) {
	if (! qsAuthPath.isNull() || ! qsAuthService.isNull())
		removeAuthenticator();
	server->connectAuthenticator(this);
	qsAuthPath = path.path();
	qsAuthService = msg.service();

	QDBusConnectionInterface *interface = qdbc.interface();
	QDBusReply<QStringList> names = interface->registeredServiceNames();
	if (names.isValid()) {
		foreach(const QString &name, names.value()) {
			QDBusReply<QString> owner = interface->serviceOwner(name);
			if (owner.isValid() && (owner.value() != name) && (owner.value() == qsAuthService)) {
				qsAuthService = name;
				break;
			}
		}
	}

	bReentrant = reentrant;
	server->log(QString("DBus Authenticator set to %1 %2 (%3)").arg(qsAuthService, qsAuthPath, QString::number(reentrant)));
}

void MurmurDBus::setTemporaryGroups(int channel, int userid, const QStringList &groups, const QDBusMessage &msg) {
	CHANNEL_SETUP_VAR(channel);

	server->setTempGroups(userid, cChannel, groups);
}

PlayerInfo::PlayerInfo(const User *p) {
	session = p->uiSession;
	mute = p->bMute;
	deaf = p->bDeaf;
	suppressed = p->bSuppress;
	selfMute = p->bSelfMute;
	selfDeaf = p->bSelfDeaf;
	channel = p->cChannel->iId;
}

PlayerInfoExtended::PlayerInfoExtended(const User *p) : PlayerInfo(p) {
	id = p->iId;
	name = p->qsName;

	const ServerUser *u = static_cast<const ServerUser *>(p);
	onlinesecs = u->bwr.onlineSeconds();
	bytespersec = u->bwr.bandwidth();
}

ChannelInfo::ChannelInfo(const Channel *c) {
	id = c->iId;
	name = c->qsName;
	parent = c->cParent ? c->cParent->iId : -1;
	foreach(Channel *chn, c->qsPermLinks)
		links << chn->iId;
}

ACLInfo::ACLInfo(const ChanACL *acl) {
	applyHere = acl->bApplyHere;
	applySubs = acl->bApplySubs;
	inherited = false;
	playerid = acl->iUserId;
	group = acl->qsGroup;
	allow = acl->pAllow;
	deny = acl->pDeny;
}

GroupInfo::GroupInfo(const Group *g) {
	name = g->qsName;
	inherit = g->bInherit;
	inheritable = g->bInheritable;
	members.clear();
}

BanInfo::BanInfo(const Ban &b) {
	address = ntohl(b.haAddress.hash[3]);
	bits = b.iMask;
}

RegisteredPlayer::RegisteredPlayer() {
	id = -1;
}

LogEntry::LogEntry() {
	timestamp = 0;
}

LogEntry::LogEntry(const ServerDB::LogRecord &r) {
	timestamp = r.first;
	txt = r.second;
}

void MurmurDBus::userStateChanged(const User *p) {
	emit playerStateChanged(PlayerInfo(p));
}

void MurmurDBus::userConnected(const User *p) {
	emit playerConnected(PlayerInfo(p));
}

void MurmurDBus::userDisconnected(const User *p) {
	emit playerDisconnected(PlayerInfo(p));
}

void MurmurDBus::channelStateChanged(const Channel *c) {
	emit channelStateChanged(ChannelInfo(c));
}

void MurmurDBus::channelCreated(const Channel *c) {
	emit channelCreated(ChannelInfo(c));
}

void MurmurDBus::channelRemoved(const Channel *c) {
	emit channelRemoved(ChannelInfo(c));
}


MetaDBus::MetaDBus(Meta *m) : QDBusAbstractAdaptor(m) {
	meta = m;
	m->connectListener(this);
}

void MetaDBus::started(Server *s) {
	new MurmurDBus(s);
	if (MurmurDBus::qdbc.isConnected())
		MurmurDBus::qdbc.registerObject(QString::fromLatin1("/%1").arg(s->iServerNum), s);

	emit started(s->iServerNum);
}

void MetaDBus::stopped(Server *s) {
	emit stopped(s->iServerNum);
}

void MetaDBus::start(int server_id, const QDBusMessage &msg) {
	if (meta->qhServers.contains(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.booted", "Server already booted"));
	} else if (! ServerDB::serverExists(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.server", "Invalid server id"));
	} else if (! meta->boot(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.bootfail", "Booting server failed"));
	}
}

void MetaDBus::stop(int server_id, const QDBusMessage &msg) {
	if (! meta->qhServers.contains(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.booted", "Server not booted"));
	} else {
		meta->kill(server_id);
	}
}

void MetaDBus::newServer(int &server_id) {
	server_id = ServerDB::addServer();
}

void MetaDBus::deleteServer(int server_id, const QDBusMessage &msg) {
	if (meta->qhServers.contains(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.booted", "Server is running"));
	} else if (! ServerDB::serverExists(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.server", "Invalid server id"));
	} else {
		ServerDB::deleteServer(server_id);
	}
}

void MetaDBus::getBootedServers(QList<int> &server_list) {
	server_list = meta->qhServers.keys();
}

void MetaDBus::getAllServers(QList<int> &server_list) {
	server_list = ServerDB::getAllServers();
}

void MetaDBus::isBooted(int server_id, bool &booted) {
	booted = meta->qhServers.contains(server_id);
}

void MetaDBus::getConf(int server_id, const QString &key, const QDBusMessage &msg, QString &value) {
	if (! ServerDB::serverExists(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.server", "Invalid server id"));
	} else {
		value = ServerDB::getConf(server_id, key).toString();
	}
}

void MetaDBus::setConf(int server_id, const QString &key, const QString &value, const QDBusMessage &msg) {
	if (! ServerDB::serverExists(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.server", "Invalid server id"));
	} else {
		ServerDB::setConf(server_id, key, value);
		Server *s = meta->qhServers.value(server_id);
		if (s)
			s->setLiveConf(key, value);
	}
}

void MetaDBus::getAllConf(int server_id, const QDBusMessage &msg, ConfigMap &values) {
	if (! ServerDB::serverExists(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.server", "Invalid server id"));
	} else {
		values = ServerDB::getAllConf(server_id);
	}
}

void MetaDBus::getLog(int server_id, int min_offset, int max_offset, const QDBusMessage &msg, QList<LogEntry> &entries) {
	if (! ServerDB::serverExists(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.server", "Invalid server id"));
	} else {
		entries.clear();
		QList<ServerDB::LogRecord> dblog = ServerDB::getLog(server_id, min_offset, max_offset);
		foreach(const ServerDB::LogRecord &e, dblog) {
			entries << LogEntry(e);
		}
	}
}

void MetaDBus::getDefaultConf(ConfigMap &values) {
	values = Meta::mp.qmConfig;
}

void MetaDBus::setSuperUserPassword(int server_id, const QString &pw, const QDBusMessage &msg) {
	if (! ServerDB::serverExists(server_id)) {
		MurmurDBus::qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.server", "Invalid server id"));
	} else {
		ServerDB::setSUPW(server_id, pw);
	}
}

void MetaDBus::quit() {
	qCritical("Quit requested from D-Bus");
	QCoreApplication::instance()->quit();
}

void MetaDBus::getVersion(int &major, int &minor, int &patch, QString &text) {
	Meta::getVersion(major, minor, patch, text);
}
