Merge branch 'experimental' of git://cockatrice.git.sourceforge.net/gitroot/cockatrice/cockatrice

This commit is contained in:
Max-Wilhelm Bruker 2011-07-09 12:14:24 +02:00
commit 07317efd46
116 changed files with 22300 additions and 8904 deletions

View file

@ -129,15 +129,20 @@ int main(int argc, char *argv[])
if (testRandom)
testRNG();
Servatrice server(settings);
Servatrice *server = new Servatrice(settings);
QObject::connect(server, SIGNAL(destroyed()), &app, SLOT(quit()), Qt::QueuedConnection);
std::cerr << "-------------------------" << std::endl;
std::cerr << "Server initialized." << std::endl;
int retval = app.exec();
std::cerr << "Server quit." << std::endl;
std::cerr << "-------------------------" << std::endl;
delete rng;
delete settings;
delete loggerThread;
return retval;
}

View file

@ -44,7 +44,7 @@ void Servatrice_TcpServer::incomingConnection(int socketDescriptor)
}
Servatrice::Servatrice(QSettings *_settings, QObject *parent)
: Server(parent), dbMutex(QMutex::Recursive), settings(_settings), uptime(0)
: Server(parent), dbMutex(QMutex::Recursive), settings(_settings), uptime(0), shutdownTimer(0)
{
pingClock = new QTimer(this);
connect(pingClock, SIGNAL(timeout()), this, SIGNAL(pingClockTimeout()));
@ -65,8 +65,8 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent)
statusUpdateClock->start(statusUpdateTime);
}
bool threaded = settings->value("server/threaded", false).toInt();
tcpServer = new Servatrice_TcpServer(this, threaded);
threaded = settings->value("server/threaded", false).toInt();
tcpServer = new Servatrice_TcpServer(this, threaded, this);
int port = settings->value("server/port", 4747).toInt();
qDebug() << "Starting server on port" << port;
if (tcpServer->listen(QHostAddress::Any, port))
@ -119,6 +119,7 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent)
Servatrice::~Servatrice()
{
prepareDestroy();
QSqlDatabase::database().close();
}
bool Servatrice::openDatabase()
@ -168,25 +169,32 @@ bool Servatrice::execSqlQuery(QSqlQuery &query)
return false;
}
AuthenticationResult Servatrice::checkUserPassword(const QString &user, const QString &password)
AuthenticationResult Servatrice::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password)
{
serverMutex.lock();
QHostAddress address = static_cast<ServerSocketInterface *>(handler)->getPeerAddress();
for (int i = 0; i < addressBanList.size(); ++i)
if (address == addressBanList[i].first)
return PasswordWrong;
serverMutex.unlock();
QMutexLocker locker(&dbMutex);
const QString method = settings->value("authentication/method").toString();
if (method == "none")
return UnknownUser;
else if (method == "sql") {
checkSql();
QSqlQuery query;
query.prepare("select banned, password from " + dbPrefix + "_users where name = :name and active = 1");
query.prepare("select a.password, time_to_sec(timediff(now(), date_add(b.time_from, interval b.minutes minute))) < 0, b.minutes <=> 0 from " + dbPrefix + "_users a left join " + dbPrefix + "_bans b on b.id_user = a.id and b.time_from = (select max(c.time_from) from " + dbPrefix + "_bans c where c.id_user = a.id) where a.name = :name and a.active = 1");
query.bindValue(":name", user);
if (!execSqlQuery(query))
return PasswordWrong;
if (query.next()) {
if (query.value(0).toInt())
if (query.value(1).toInt() || query.value(2).toInt())
return PasswordWrong;
if (query.value(1).toString() == password)
if (query.value(0).toString() == password)
return PasswordRight;
else
return PasswordWrong;
@ -233,7 +241,7 @@ ServerInfo_User *Servatrice::evalUserQueryResult(const QSqlQuery &query, bool co
int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered;
if (is_admin == 1)
userLevel |= ServerInfo_User::IsAdmin;
userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator;
else if (is_admin == 2)
userLevel |= ServerInfo_User::IsModerator;
@ -324,19 +332,6 @@ QMap<QString, ServerInfo_User *> Servatrice::getIgnoreList(const QString &name)
return result;
}
bool Servatrice::getUserBanned(Server_ProtocolHandler *client, const QString &userName) const
{
QMutexLocker locker(&serverMutex);
QHostAddress address = static_cast<ServerSocketInterface *>(client)->getPeerAddress();
for (int i = 0; i < addressBanList.size(); ++i)
if (address == addressBanList[i].first)
return true;
for (int i = 0; i < nameBanList.size(); ++i)
if (userName == nameBanList[i].first)
return true;
return false;
}
void Servatrice::updateBanTimer()
{
QMutexLocker locker(&serverMutex);
@ -345,11 +340,6 @@ void Servatrice::updateBanTimer()
addressBanList.removeAt(i);
else
++i;
for (int i = 0; i < nameBanList.size(); )
if (--(nameBanList[i].second) <= 0)
nameBanList.removeAt(i);
else
++i;
}
void Servatrice::updateLoginMessage()
@ -374,18 +364,55 @@ void Servatrice::updateLoginMessage()
void Servatrice::statusUpdate()
{
QMutexLocker locker(&dbMutex);
const int uc = getUsersCount(); // for correct mutex locking order
const int gc = getGamesCount();
uptime += statusUpdateClock->interval() / 1000;
QMutexLocker locker(&dbMutex);
checkSql();
QSqlQuery query;
query.prepare("insert into " + dbPrefix + "_uptime (id_server, timest, uptime, users_count, games_count) values(:id, NOW(), :uptime, :users_count, :games_count)");
query.bindValue(":id", serverId);
query.bindValue(":uptime", uptime);
query.bindValue(":users_count", getUsersCount());
query.bindValue(":games_count", getGamesCount());
query.bindValue(":users_count", uc);
query.bindValue(":games_count", gc);
execSqlQuery(query);
}
const QString Servatrice::versionString = "Servatrice 0.20110527";
void Servatrice::scheduleShutdown(const QString &reason, int minutes)
{
QMutexLocker locker(&serverMutex);
shutdownReason = reason;
shutdownMinutes = minutes + 1;
if (minutes > 0) {
shutdownTimer = new QTimer;
connect(shutdownTimer, SIGNAL(timeout()), this, SLOT(shutdownTimeout()));
shutdownTimer->start(60000);
}
shutdownTimeout();
}
void Servatrice::shutdownTimeout()
{
QMutexLocker locker(&serverMutex);
--shutdownMinutes;
GenericEvent *event;
if (shutdownMinutes)
event = new Event_ServerShutdown(shutdownReason, shutdownMinutes);
else
event = new Event_ConnectionClosed("server_shutdown");
for (int i = 0; i < clients.size(); ++i)
clients[i]->sendProtocolItem(event, false);
delete event;
if (!shutdownMinutes)
deleteLater();
}
const QString Servatrice::versionString = "Servatrice 0.20110625";

View file

@ -50,6 +50,7 @@ class Servatrice : public Server
private slots:
void statusUpdate();
void updateBanTimer();
void shutdownTimeout();
public:
QMutex dbMutex;
static const QString versionString;
@ -67,18 +68,18 @@ public:
int getMaxMessageCountPerInterval() const { return maxMessageCountPerInterval; }
int getMaxMessageSizePerInterval() const { return maxMessageSizePerInterval; }
int getMaxGamesPerUser() const { return maxGamesPerUser; }
bool getThreaded() const { return threaded; }
QString getDbPrefix() const { return dbPrefix; }
void updateLoginMessage();
ServerInfo_User *getUserData(const QString &name);
int getUsersWithAddress(const QHostAddress &address) const;
QMap<QString, ServerInfo_User *> getBuddyList(const QString &name);
QMap<QString, ServerInfo_User *> getIgnoreList(const QString &name);
bool getUserBanned(Server_ProtocolHandler *client, const QString &userName) const;
void addAddressBan(const QHostAddress &address, int minutes) { addressBanList.append(QPair<QHostAddress, int>(address, minutes)); }
void addNameBan(const QString &name, int minutes) { nameBanList.append(QPair<QString, int>(name, minutes)); }
void scheduleShutdown(const QString &reason, int minutes);
protected:
bool userExists(const QString &user);
AuthenticationResult checkUserPassword(const QString &user, const QString &password);
AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password);
private:
QTimer *pingClock, *statusUpdateClock, *banTimeoutClock;
QTcpServer *tcpServer;
@ -86,12 +87,16 @@ private:
QString dbPrefix;
QSettings *settings;
int serverId;
bool threaded;
int uptime;
QList<QPair<QHostAddress, int> > addressBanList;
QList<QPair<QString, int> > nameBanList;
int maxGameInactivityTime, maxPlayerInactivityTime;
int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser;
ServerInfo_User *evalUserQueryResult(const QSqlQuery &query, bool complete);
QString shutdownReason;
int shutdownMinutes;
QTimer *shutdownTimer;
};
#endif

View file

@ -33,7 +33,7 @@
#include "server_logger.h"
ServerSocketInterface::ServerSocketInterface(Servatrice *_server, QTcpSocket *_socket, QObject *parent)
: Server_ProtocolHandler(_server, parent), servatrice(_server), socket(_socket), topLevelItem(0)
: Server_ProtocolHandler(_server, parent), servatrice(_server), socket(_socket), topLevelItem(0), compressionSupport(false)
{
xmlWriter = new QXmlStreamWriter(&xmlBuffer);
xmlReader = new QXmlStreamReader;
@ -69,6 +69,7 @@ ServerSocketInterface::~ServerSocketInterface()
delete xmlReader;
delete socket;
socket = 0;
delete topLevelItem;
}
void ServerSocketInterface::processProtocolItem(ProtocolItem *item)
@ -102,6 +103,8 @@ void ServerSocketInterface::readClient()
if (topLevelItem)
topLevelItem->readElement(xmlReader);
else if (xmlReader->isStartElement() && (xmlReader->name().toString() == "cockatrice_client_stream")) {
if (xmlReader->attributes().value("comp").toString().toInt() == 1)
compressionSupport = true;
topLevelItem = new TopLevelProtocolItem;
connect(topLevelItem, SIGNAL(protocolItemReceived(ProtocolItem *)), this, SLOT(processProtocolItem(ProtocolItem *)));
}
@ -296,7 +299,10 @@ ResponseCode ServerSocketInterface::cmdDeckList(Command_DeckList * /*cmd*/, Comm
if (!deckListHelper(root))
return RespContextError;
cont->setResponse(new Response_DeckList(cont->getCmdId(), RespOk, root));
ProtocolResponse *resp = new Response_DeckList(cont->getCmdId(), RespOk, root);
if (getCompressionSupport())
resp->setCompressed(true);
cont->setResponse(resp);
return RespNothing;
}
@ -455,8 +461,8 @@ ResponseCode ServerSocketInterface::cmdDeckDownload(Command_DeckDownload *cmd, C
return RespNothing;
}
// ADMIN FUNCTIONS.
// Permission is checked by the calling function.
// MODERATOR FUNCTIONS.
// May be called by admins and moderators. Permission is checked by the calling function.
ResponseCode ServerSocketInterface::cmdUpdateServerMessage(Command_UpdateServerMessage * /*cmd*/, CommandContainer * /*cont*/)
{
@ -464,6 +470,15 @@ ResponseCode ServerSocketInterface::cmdUpdateServerMessage(Command_UpdateServerM
return RespOk;
}
// ADMIN FUNCTIONS.
// Permission is checked by the calling function.
ResponseCode ServerSocketInterface::cmdShutdownServer(Command_ShutdownServer *cmd, CommandContainer * /*cont*/)
{
servatrice->scheduleShutdown(cmd->getReason(), cmd->getMinutes());
return RespOk;
}
ResponseCode ServerSocketInterface::cmdBanFromServer(Command_BanFromServer *cmd, CommandContainer * /*cont*/)
{
QString userName = cmd->getUserName();
@ -475,14 +490,14 @@ ResponseCode ServerSocketInterface::cmdBanFromServer(Command_BanFromServer *cmd,
ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName));
if (user->getUserInfo()->getUserLevel() & ServerInfo_User::IsRegistered) {
// Registered users can be banned by name.
if (minutes == 0) {
QMutexLocker locker(&servatrice->dbMutex);
QSqlQuery query;
query.prepare("update " + servatrice->getDbPrefix() + "_users set banned=1 where name = :name");
query.bindValue(":name", userName);
servatrice->execSqlQuery(query);
} else
servatrice->addNameBan(userName, minutes);
QMutexLocker locker(&servatrice->dbMutex);
QSqlQuery query;
query.prepare("insert into " + servatrice->getDbPrefix() + "_bans (id_user, id_admin, time_from, minutes, reason) values(:id_user, :id_admin, NOW(), :minutes, :reason)");
query.bindValue(":id_user", getUserIdInDB(userName));
query.bindValue(":id_admin", getUserIdInDB(userInfo->getName()));
query.bindValue(":minutes", minutes);
query.bindValue(":reason", cmd->getReason() + "\n");
servatrice->execSqlQuery(query);
} else {
// Unregistered users must be banned by IP address.
// Indefinite address bans are not reasonable -> default to 30 minutes.

View file

@ -51,6 +51,7 @@ private:
QXmlStreamReader *xmlReader;
QString xmlBuffer;
TopLevelProtocolItem *topLevelItem;
bool compressionSupport;
int getUserIdInDB(const QString &name) const;
ResponseCode cmdAddToList(Command_AddToList *cmd, CommandContainer *cont);
@ -66,8 +67,11 @@ private:
ResponseCode cmdDeckUpload(Command_DeckUpload *cmd, CommandContainer *cont);
DeckList *getDeckFromDatabase(int deckId);
ResponseCode cmdDeckDownload(Command_DeckDownload *cmd, CommandContainer *cont);
ResponseCode cmdUpdateServerMessage(Command_UpdateServerMessage *cmd, CommandContainer *cont);
ResponseCode cmdBanFromServer(Command_BanFromServer *cmd, CommandContainer *cont);
ResponseCode cmdShutdownServer(Command_ShutdownServer *cmd, CommandContainer *cont);
ResponseCode cmdUpdateServerMessage(Command_UpdateServerMessage *cmd, CommandContainer *cont);
protected:
bool getCompressionSupport() const { return compressionSupport; }
public:
ServerSocketInterface(Servatrice *_server, QTcpSocket *_socket, QObject *parent = 0);
~ServerSocketInterface();