mumble/src/murmur/DBus.cpp
Robert Adam 1d45d991aa CHANGE: Use Protobuf for UDP messages
Previously Mumble was using a custom binary format for transmitting data
via UDP (mainly audio). This has worked for a long time but besides
being inconvenient for 3rdParty implementors (they had to manually
re-implement encoding and decoding support for this format) this format
was not very flexible and changes to the data format were very hard.

In order to improve on this situation, this commit introduces changes
that allow to use Protobuf for the UDP messages as well (it's already
used for TCP). With that it should be relatively easy to extend/change
the UDP packet formats in the future and 3rdParty implementors can now
simply use Protobuf to handle decoding/encoding packets for them (much
less work and much less prone to errors).

Since the new Protobuf format is incompatible with the old UDP format,
this commit also includes support for dealing with older clients or
servers that don't recognize the new protocol yet. That way the new
protocol format is only used if both the client and the server are
recent enough to have it implemented (assumed to be the case >=1.5.0).

Note also that the server will make sure that clients using the old and
the new format can seamlessly communicate with one another.

Therefore, on the surface it should not be noticeable to the user which
protocol is currently used.

Note also that the new protocol format only supports Opus as an audio
codec. If one of the legacy codecs is to be used, the legacy packet
format has to be used as well. However, all codecs except for Opus will
be removed from Mumble in the future anyway.

Fixes #4350
2022-03-27 09:49:58 +02:00

949 lines
28 KiB
C++

// Copyright 2007-2022 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include <QtCore/QtGlobal>
#ifdef Q_OS_WIN
# include "win.h"
#endif
#include "DBus.h"
#include "Connection.h"
#include "QtUtils.h"
#include "Server.h"
#include "ServerDB.h"
#include "ServerUser.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QStack>
#include <QtDBus/QDBusConnectionInterface>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusMessage>
#include <QtDBus/QDBusMetaType>
#include <QtDBus/QDBusReply>
#ifdef Q_OS_WIN
# include <winsock2.h>
#else
# include <arpa/inet.h>
#endif
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 = nullptr;
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, int sessionId, 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, sessionId, nullptr, 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(nullptr, pUser, false, text);
}
void MurmurDBus::sendMessageChannel(int id, bool tree, const QString &text, const QDBusMessage &msg) {
CHANNEL_SETUP_VAR(id);
server->sendTextMessage(cChannel, nullptr, tree, text);
}
void MurmurDBus::addChannel(const QString &name, int chanparent, const QDBusMessage &msg, int &newid) {
CHANNEL_SETUP_VAR(chanparent);
Channel *nc;
{
QWriteLocker wl(&server->qrwlVoiceThread);
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;
}
QWriteLocker wl(&server->qrwlVoiceThread);
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 = nullptr;
}
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) : nullptr;
if (!g && !pg)
continue;
GroupInfo gi(g ? g : pg);
QSet< int > members;
if (pg)
members = pg->members();
if (g) {
gi.add = g->qsAdd.values();
gi.remove = g->qsRemove.values();
gi.inherited = false;
members += g->qsAdd;
members -= g->qsRemove;
} else {
gi.inherited = true;
}
gi.members = members.values();
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;
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
g->qsAdd = QSet< int >(gi.add.begin(), gi.add.end());
g->qsRemove = QSet< int >(gi.remove.begin(), gi.remove.end());
#else
// In Qt 5.14 QList::toSet() has been deprecated as there exists a dedicated constructor of QSet for this now
g->qsAdd = gi.add.toSet();
g->qsRemove = gi.remove.toSet();
#endif
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, 0, 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) : inherited(false) {
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::userTextMessage(const User *, const TextMessage &) {
// Not implemented for DBus.
}
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 {
if (key == "key" || key == "passphrase")
MurmurDBus::qdbc->send(
msg.createErrorReply("net.sourceforge.mumble.Error.writeonly", "Requested read of write-only field."));
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);
values.remove("key");
values.remove("passphrase");
}
}
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;
values.remove("key");
values.remove("passphrase");
}
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);
}
#undef PLAYER_SETUP_VAR
#undef PLAYER_SETUP
#undef CHANNEL_SETUP_VAR2
#undef CHANNEL_SETUP_VAR