diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index fc0fef2c4..afdd8588a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -34,8 +34,6 @@ SET(common_HEADERS FIND_PACKAGE(Qt4 REQUIRED) FIND_PACKAGE(Protobuf REQUIRED) -set(CMAKE_BUILD_TYPE Release) - QT4_WRAP_CPP(common_HEADERS_MOC ${common_HEADERS}) INCLUDE(${QT_USE_FILE}) INCLUDE_DIRECTORIES(pb) diff --git a/common/pb/CMakeLists.txt b/common/pb/CMakeLists.txt index f10e55e8f..e1f171ef4 100644 --- a/common/pb/CMakeLists.txt +++ b/common/pb/CMakeLists.txt @@ -83,6 +83,7 @@ SET(PROTO_FILES event_reveal_cards.proto event_roll_die.proto event_room_say.proto + event_server_complete_list.proto event_server_identification.proto event_server_message.proto event_server_shutdown.proto @@ -132,6 +133,7 @@ SET(PROTO_FILES serverinfo_room.proto serverinfo_user.proto serverinfo_zone.proto + servernetwork_message.proto server_message.proto session_commands.proto session_event.proto diff --git a/common/pb/event_server_complete_list.proto b/common/pb/event_server_complete_list.proto new file mode 100644 index 000000000..4e402e9be --- /dev/null +++ b/common/pb/event_server_complete_list.proto @@ -0,0 +1,12 @@ +import "session_event.proto"; +import "serverinfo_user.proto"; +import "serverinfo_room.proto"; + +message Event_ServerCompleteList { + extend SessionEvent { + optional Event_ServerCompleteList ext = 600; + } + optional uint32 server_id = 1; + repeated ServerInfo_User user_list = 2; + repeated ServerInfo_Room room_list = 3; +} diff --git a/common/pb/servernetwork_message.proto b/common/pb/servernetwork_message.proto new file mode 100644 index 000000000..880a0a1d9 --- /dev/null +++ b/common/pb/servernetwork_message.proto @@ -0,0 +1,24 @@ +import "response.proto"; +import "session_event.proto"; +import "commands.proto"; +import "game_event_container.proto"; +import "room_event.proto"; + +message ServerNetworkMessage { + enum MessageType { + RESPONSE = 0; + SESSION_EVENT = 1; + GAME_COMMAND_CONTAINER = 2; + GAME_EVENT_CONTAINER = 3; + ROOM_EVENT = 4; + } + optional MessageType message_type = 1; + + optional sint32 game_id = 10; + + optional Response response = 100; + optional SessionEvent session_event = 101; + optional CommandContainer game_command = 102; + optional GameEventContainer game_event_container = 103; + optional RoomEvent room_event = 104; +} diff --git a/common/pb/session_event.proto b/common/pb/session_event.proto index a0b6254d3..a7b14d00c 100644 --- a/common/pb/session_event.proto +++ b/common/pb/session_event.proto @@ -1,6 +1,7 @@ message SessionEvent { enum SessionEventType { SERVER_IDENTIFICATION = 500; + SERVER_COMPLETE_LIST = 600; SERVER_MESSAGE = 1000; SERVER_SHUTDOWN = 1001; CONNECTION_CLOSED = 1002; diff --git a/common/server.cpp b/common/server.cpp index 75633dc6b..8d8fcf518 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -54,6 +54,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString QMutexLocker locker(&serverMutex); if (name.size() > 35) name = name.left(35); + AuthenticationResult authState = checkUserPassword(session, name, password, reasonStr); if ((authState == NotLoggedIn) || (authState == UserIsBanned)) return authState; @@ -62,8 +63,10 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString data.set_address(session->getAddress().toStdString()); name = QString::fromStdString(data.name()); // Compensate for case indifference + lockSessionTables(); + if (authState == PasswordRight) { - if (users.contains(name)) { + if (users.contains(name) || userSessionExists(name)) { qDebug("Login denied: would overwrite old session"); return WouldOverwriteOldSession; } @@ -72,7 +75,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString // don't interfere with registered user names though. QString tempName = name; int i = 0; - while (users.contains(tempName) || userExists(tempName)) + while (users.contains(tempName) || userExists(tempName) || userSessionExists(tempName)) tempName = name + "_" + QString::number(++i); name = tempName; data.set_name(name.toStdString()); @@ -83,7 +86,9 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString users.insert(name, session); qDebug() << "Server::loginUser: name=" << name; - session->setSessionId(startSession(name, session->getAddress())); + session->setSessionId(startSession(name, session->getAddress())); + unlockSessionTables(); + qDebug() << "session id:" << session->getSessionId(); Event_UserJoined event; diff --git a/common/server.h b/common/server.h index e05a67f22..dd5da5675 100644 --- a/common/server.h +++ b/common/server.h @@ -65,6 +65,11 @@ protected: int getGamesCount() const; int nextGameId, nextReplayId; void addRoom(Server_Room *newRoom); + + virtual void clearSessionTables() { } + virtual void lockSessionTables() { } + virtual void unlockSessionTables() { } + virtual bool userSessionExists(const QString &userName) { return false; } }; #endif diff --git a/common/server_game.cpp b/common/server_game.cpp index ef2d152d2..b7c9c399e 100644 --- a/common/server_game.cpp +++ b/common/server_game.cpp @@ -270,6 +270,7 @@ void Server_Game::doStartGameIfReady() activePlayer = -1; nextTurn(); + locker.unlock(); room->broadcastGameListUpdate(this); } diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 427bc6fe1..0c9507862 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -577,6 +577,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message if (authState == NotLoggedIn) return Response::RespLoginNeeded; + server->serverMutex.lock(); QString receiver = QString::fromStdString(cmd.user_name()); Server_ProtocolHandler *userHandler = server->getUsers().value(receiver); if (!userHandler) @@ -592,6 +593,8 @@ Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message SessionEvent *se = prepareSessionEvent(event); userHandler->sendProtocolItem(*se); rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, se); + server->serverMutex.unlock(); + return Response::RespOk; } @@ -631,10 +634,12 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetUserInfo(const Command_GetU if (userName.isEmpty()) re->mutable_user_info()->CopyFrom(*userInfo); else { + server->serverMutex.lock(); Server_ProtocolHandler *handler = server->getUsers().value(userName); if (!handler) return Response::RespNameNotFound; re->mutable_user_info()->CopyFrom(handler->copyUserInfo(true, userInfo->user_level() & ServerInfo_User::IsModerator)); + server->serverMutex.unlock(); } rc.setResponseExtension(re); @@ -690,9 +695,11 @@ Response::ResponseCode Server_ProtocolHandler::cmdListUsers(const Command_ListUs return Response::RespLoginNeeded; Response_ListUsers *re = new Response_ListUsers; + server->serverMutex.lock(); QMapIterator userIterator = server->getUsers(); while (userIterator.hasNext()) re->add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false)); + server->serverMutex.unlock(); acceptsUserListChanges = true; diff --git a/common/server_room.cpp b/common/server_room.cpp index 89ba02109..05d831185 100644 --- a/common/server_room.cpp +++ b/common/server_room.cpp @@ -24,7 +24,7 @@ Server_Room::~Server_Room() delete gameList[i]; games.clear(); - clear(); + userList.clear(); } Server *Server_Room::getServer() const @@ -39,7 +39,7 @@ ServerInfo_Room Server_Room::getInfo(bool complete, bool showGameTypes, bool upd ServerInfo_Room result; result.set_room_id(id); result.set_game_count(games.size()); - result.set_player_count(size()); + result.set_player_count(userList.size()); if (!updating) { result.set_name(name.toStdString()); @@ -52,8 +52,8 @@ ServerInfo_Room Server_Room::getInfo(bool complete, bool showGameTypes, bool upd while (gameIterator.hasNext()) result.add_game_list()->CopyFrom(gameIterator.next().value()->getInfo()); - for (int i = 0; i < size(); ++i) - result.add_user_list()->CopyFrom(at(i)->copyUserInfo(false)); + for (int i = 0; i < userList.size(); ++i) + result.add_user_list()->CopyFrom(userList[i]->copyUserInfo(false)); } if (complete || showGameTypes) for (int i = 0; i < gameTypes.size(); ++i) { @@ -75,21 +75,22 @@ RoomEvent *Server_Room::prepareRoomEvent(const ::google::protobuf::Message &room void Server_Room::addClient(Server_ProtocolHandler *client) { - QMutexLocker locker(&roomMutex); - Event_JoinRoom event; event.mutable_user_info()->CopyFrom(client->copyUserInfo(false)); sendRoomEvent(prepareRoomEvent(event)); - append(client); + roomMutex.lock(); + userList.append(client); + roomMutex.unlock(); + emit roomInfoChanged(); } void Server_Room::removeClient(Server_ProtocolHandler *client) { - QMutexLocker locker(&roomMutex); - - removeAt(indexOf(client)); + roomMutex.lock(); + userList.removeAt(userList.indexOf(client)); + roomMutex.unlock(); Event_LeaveRoom event; event.set_name(client->getUserInfo()->name()); @@ -110,8 +111,8 @@ void Server_Room::sendRoomEvent(RoomEvent *event) { QMutexLocker locker(&roomMutex); - for (int i = 0; i < size(); ++i) - at(i)->sendProtocolItem(*event); + for (int i = 0; i < userList.size(); ++i) + userList[i]->sendProtocolItem(*event); delete event; } diff --git a/common/server_room.h b/common/server_room.h index 04fe40480..9ded049c5 100644 --- a/common/server_room.h +++ b/common/server_room.h @@ -16,7 +16,7 @@ class ServerInfo_Game; class Server_Game; class Server; -class Server_Room : public QObject, public QList { +class Server_Room : public QObject { Q_OBJECT signals: void roomInfoChanged(); @@ -28,6 +28,7 @@ private: QString joinMessage; QStringList gameTypes; QMap games; + QList userList; public: mutable QMutex roomMutex; Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent); diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index 05a099be6..68e6b6689 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -9,6 +9,8 @@ SET(servatrice_SOURCES src/server_logger.cpp src/serversocketinterface.cpp src/serversocketthread.cpp + src/networkserverinterface.cpp + src/networkserverthread.cpp ${CMAKE_CURRENT_BINARY_DIR}/version_string.cpp ) SET(servatrice_HEADERS @@ -16,6 +18,8 @@ SET(servatrice_HEADERS src/server_logger.h src/serversocketinterface.h src/serversocketthread.h + src/networkserverinterface.h + src/networkserverthread.h ) SET(QT_DONTUSE_QTGUI) diff --git a/servatrice/src/networkserverinterface.cpp b/servatrice/src/networkserverinterface.cpp new file mode 100644 index 000000000..7a17568b9 --- /dev/null +++ b/servatrice/src/networkserverinterface.cpp @@ -0,0 +1,122 @@ +#include "networkserverinterface.h" +#include +#include "server_logger.h" +#include "main.h" +#include "server_protocolhandler.h" +#include "server_room.h" + +#include "pb/servernetwork_message.pb.h" +#include "pb/event_server_complete_list.pb.h" +#include + +NetworkServerInterface::NetworkServerInterface(Servatrice *_server, QSslSocket *_socket) + : QObject(), server(_server), socket(_socket), messageInProgress(false) +{ + connect(socket, SIGNAL(readyRead()), this, SLOT(readClient())); + connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater())); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(catchSocketError(QAbstractSocket::SocketError))); + connect(this, SIGNAL(outputBufferChanged()), this, SLOT(flushOutputBuffer()), Qt::QueuedConnection); + + Event_ServerCompleteList event; + event.set_server_id(server->getServerId()); + + server->serverMutex.lock(); + QMapIterator userIterator(server->getUsers()); + while (userIterator.hasNext()) + event.add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(true, true)); + + QMapIterator roomIterator(server->getRooms()); + while (roomIterator.hasNext()) { + Server_Room *room = roomIterator.next().value(); + room->roomMutex.lock(); + event.add_room_list()->CopyFrom(room->getInfo(true, true, false)); + } + + ServerNetworkMessage message; + message.set_message_type(ServerNetworkMessage::SESSION_EVENT); + SessionEvent *sessionEvent = message.mutable_session_event(); + sessionEvent->GetReflection()->MutableMessage(sessionEvent, event.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(event); + transmitMessage(message); + + server->addNetworkServerInterface(this); + + roomIterator.toFront(); + while (roomIterator.hasNext()) + roomIterator.next().value()->roomMutex.unlock(); + server->serverMutex.unlock(); +} + +NetworkServerInterface::~NetworkServerInterface() +{ + logger->logMessage("[SN] session ended", this); + + flushOutputBuffer(); +} + +void NetworkServerInterface::flushOutputBuffer() +{ + QMutexLocker locker(&outputBufferMutex); + if (outputBuffer.isEmpty()) + return; + server->incTxBytes(outputBuffer.size()); + socket->write(outputBuffer); + socket->flush(); + outputBuffer.clear(); +} + +void NetworkServerInterface::readClient() +{ + QByteArray data = socket->readAll(); + server->incRxBytes(data.size()); + inputBuffer.append(data); + + do { + if (!messageInProgress) { + if (inputBuffer.size() >= 4) { + messageLength = (((quint32) (unsigned char) inputBuffer[0]) << 24) + + (((quint32) (unsigned char) inputBuffer[1]) << 16) + + (((quint32) (unsigned char) inputBuffer[2]) << 8) + + ((quint32) (unsigned char) inputBuffer[3]); + inputBuffer.remove(0, 4); + messageInProgress = true; + } else + return; + } + if (inputBuffer.size() < messageLength) + return; + + ServerNetworkMessage newMessage; + newMessage.ParseFromArray(inputBuffer.data(), messageLength); + inputBuffer.remove(0, messageLength); + messageInProgress = false; + + processMessage(newMessage); + } while (!inputBuffer.isEmpty()); +} + +void NetworkServerInterface::catchSocketError(QAbstractSocket::SocketError socketError) +{ + qDebug() << "Socket error:" << socketError; + + deleteLater(); +} + +void NetworkServerInterface::transmitMessage(const ServerNetworkMessage &item) +{ + QByteArray buf; + unsigned int size = item.ByteSize(); + buf.resize(size + 4); + item.SerializeToArray(buf.data() + 4, size); + buf.data()[3] = (unsigned char) size; + buf.data()[2] = (unsigned char) (size >> 8); + buf.data()[1] = (unsigned char) (size >> 16); + buf.data()[0] = (unsigned char) (size >> 24); + + QMutexLocker locker(&outputBufferMutex); + outputBuffer.append(buf); + emit outputBufferChanged(); +} + +void NetworkServerInterface::processMessage(const ServerNetworkMessage &item) +{ +} diff --git a/servatrice/src/networkserverinterface.h b/servatrice/src/networkserverinterface.h new file mode 100644 index 000000000..d713a0d24 --- /dev/null +++ b/servatrice/src/networkserverinterface.h @@ -0,0 +1,35 @@ +#ifndef NETWORKSERVERINTERFACE_H +#define NETWORKSERVERINTERFACE_H + +#include "servatrice.h" + +class Servatrice; +class QSslSocket; +class ServerNetworkMessage; + +class NetworkServerInterface : public QObject { + Q_OBJECT +private slots: + void readClient(); + void catchSocketError(QAbstractSocket::SocketError socketError); + void flushOutputBuffer(); +signals: + void outputBufferChanged(); +private: + QMutex outputBufferMutex; + Servatrice *server; + QSslSocket *socket; + + QByteArray inputBuffer, outputBuffer; + bool messageInProgress; + int messageLength; + + void processMessage(const ServerNetworkMessage &item); +public: + NetworkServerInterface(Servatrice *_server, QSslSocket *_socket); + ~NetworkServerInterface(); + + void transmitMessage(const ServerNetworkMessage &item); +}; + +#endif diff --git a/servatrice/src/networkserverthread.cpp b/servatrice/src/networkserverthread.cpp new file mode 100644 index 000000000..05287e871 --- /dev/null +++ b/servatrice/src/networkserverthread.cpp @@ -0,0 +1,60 @@ +#include "networkserverthread.h" +#include "networkserverinterface.h" +#include "server_logger.h" +#include "servatrice.h" +#include "main.h" +#include + +NetworkServerThread::NetworkServerThread(int _socketDescriptor, Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent) + : QThread(parent), server(_server), socketDescriptor(_socketDescriptor), cert(_cert), privateKey(_privateKey) +{ + connect(this, SIGNAL(finished()), this, SLOT(deleteLater())); +} + +NetworkServerThread::~NetworkServerThread() +{ + quit(); + wait(); + + delete socket; +} + +void NetworkServerThread::run() +{ + socket = new QSslSocket; + socket->setSocketDescriptor(socketDescriptor); + socket->setLocalCertificate(cert); + socket->setPrivateKey(privateKey); + + logger->logMessage(QString("[SN] incoming connection: %1").arg(socket->peerAddress().toString())); + + QList serverList = server->getServerList(); + int listIndex = -1; + for (int i = 0; i < serverList.size(); ++i) + if (serverList[i].address == socket->peerAddress()) { + listIndex = i; + break; + } + if (listIndex == -1) { + logger->logMessage(QString("[SN] address %1 unknown, terminating connection").arg(socket->peerAddress().toString())); + return; + } + + socket->startServerEncryption(); + if (!socket->waitForEncrypted(5000)) { + logger->logMessage(QString("[SN] SSL handshake timeout, terminating connection")); + return; + } + + if (serverList[listIndex].cert == socket->peerCertificate()) + logger->logMessage(QString("[SN] Peer authenticated as " + serverList[listIndex].hostname)); + else { + logger->logMessage(QString("[SN] Authentication failed, terminating connection")); + return; + } + + interface = new NetworkServerInterface(server, socket); + connect(interface, SIGNAL(destroyed()), this, SLOT(deleteLater())); + + exec(); +} diff --git a/servatrice/src/networkserverthread.h b/servatrice/src/networkserverthread.h new file mode 100644 index 000000000..e9cef2659 --- /dev/null +++ b/servatrice/src/networkserverthread.h @@ -0,0 +1,28 @@ +#ifndef NETWORKSERVERTHREAD_H +#define NETWORKSERVERTHREAD_H + +#include +#include +#include + +class Servatrice; +class NetworkServerInterface; +class QSslSocket; + +class NetworkServerThread : public QThread { + Q_OBJECT +private: + Servatrice *server; + NetworkServerInterface *interface; + QSslCertificate cert; + QSslKey privateKey; + int socketDescriptor; + QSslSocket *socket; +public: + NetworkServerThread(int _socketDescriptor, Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent = 0); + ~NetworkServerThread(); +protected: + void run(); +}; + +#endif diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 98a5da889..cd73f7a26 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -21,11 +21,11 @@ #include #include #include -#include #include "servatrice.h" #include "server_room.h" #include "serversocketinterface.h" #include "serversocketthread.h" +#include "networkserverthread.h" #include "server_logger.h" #include "main.h" #include "passwordhasher.h" @@ -50,13 +50,8 @@ void Servatrice_GameServer::incomingConnection(int socketDescriptor) void Servatrice_NetworkServer::incomingConnection(int socketDescriptor) { - QSslSocket *socket = new QSslSocket; - socket->setLocalCertificate(cert); - socket->setPrivateKey(privateKey); - socket->setSocketDescriptor(socketDescriptor); - socket->startServerEncryption(); -// SocketInterface *ssi = new ServerSocketInterface(server, socket); -// logger->logMessage(QString("Incoming server network connection: %1").arg(socket->peerAddress().toString()), ssi); + NetworkServerThread *thread = new NetworkServerThread(socketDescriptor, server, cert, privateKey); + thread->start(); } Servatrice::Servatrice(QSettings *_settings, QObject *parent) @@ -100,11 +95,13 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent) if (databaseType != DatabaseNone) openDatabase(); + updateServerList(); + clearSessionTables(); + try { if (settings->value("servernetwork/active", 0).toInt()) { qDebug() << "Connecting to server network."; const QString certFileName = settings->value("servernetwork/ssl_cert").toString(); const QString keyFileName = settings->value("servernetwork/ssl_key").toString(); - const QString passphrase = settings->value("servernetwork/ssl_passphrase").toString(); qDebug() << "Loading certificate..."; QFile certFile(certFileName); if (!certFile.open(QIODevice::ReadOnly)) @@ -116,7 +113,7 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent) QFile keyFile(keyFileName); if (!keyFile.open(QIODevice::ReadOnly)) throw QString("Error opening private key file: %1").arg(keyFileName); - QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, passphrase.toAscii()); + QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); if (key.isNull()) throw QString("Invalid private key."); @@ -235,6 +232,34 @@ bool Servatrice::execSqlQuery(QSqlQuery &query) return false; } +void Servatrice::updateServerList() +{ + qDebug() << "Updating server list..."; + + serverListMutex.lock(); + serverList.clear(); + + QSqlQuery query; + query.prepare("select id, ssl_cert, hostname, address, game_port, control_port from " + dbPrefix + "_servers order by id asc"); + execSqlQuery(query); + while (query.next()) { + ServerProperties prop(query.value(0).toInt(), QSslCertificate(query.value(1).toString().toAscii()), query.value(2).toString(), QHostAddress(query.value(3).toString()), query.value(4).toInt(), query.value(5).toInt()); + serverList.append(prop); + qDebug() << QString("#%1 CERT=%2 NAME=%3 IP=%4:%5 CPORT=%6").arg(prop.id).arg(QString(prop.cert.digest().toHex())).arg(prop.hostname).arg(prop.address.toString()).arg(prop.gamePort).arg(prop.controlPort); + } + + serverListMutex.unlock(); +} + +QList Servatrice::getServerList() const +{ + serverListMutex.lock(); + QList result = serverList; + serverListMutex.unlock(); + + return result; +} + AuthenticationResult Servatrice::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr) { QMutexLocker locker(&dbMutex); @@ -461,6 +486,37 @@ QList Servatrice::getUsersWithAddressAsList(const QHost return result; } +void Servatrice::clearSessionTables() +{ + lockSessionTables(); + QSqlQuery query; + query.prepare("update " + dbPrefix + "_sessions set end_time=now() where end_time is null and id_server = :id_server"); + query.bindValue(":id_server", serverId); + query.exec(); + unlockSessionTables(); +} + +void Servatrice::lockSessionTables() +{ + QSqlQuery("lock tables " + dbPrefix + "_sessions write, " + dbPrefix + "_users read").exec(); +} + +void Servatrice::unlockSessionTables() +{ + QSqlQuery("unlock tables").exec(); +} + +bool Servatrice::userSessionExists(const QString &userName) +{ + // Call only after lockSessionTables(). + + QSqlQuery query; + query.prepare("select 1 from " + dbPrefix + "_sessions where user_name = :user_name and end_time is null"); + query.bindValue(":user_name", userName); + query.exec(); + return query.next(); +} + int Servatrice::startSession(const QString &userName, const QString &address) { if (authenticationMethod == AuthenticationNone) @@ -489,9 +545,11 @@ void Servatrice::endSession(int sessionId) return; QSqlQuery query; + query.exec("lock tables " + dbPrefix + "_sessions read"); query.prepare("update " + dbPrefix + "_sessions set end_time=NOW() where id = :id_session"); query.bindValue(":id_session", sessionId); execSqlQuery(query); + query.exec("unlock tables"); } QMap Servatrice::getBuddyList(const QString &name) @@ -751,3 +809,14 @@ void Servatrice::shutdownTimeout() if (!shutdownMinutes) deleteLater(); } + +void Servatrice::addNetworkServerInterface(NetworkServerInterface *interface) +{ + networkServerInterfaces.append(interface); +} + +void Servatrice::removeNetworkServerInterface(NetworkServerInterface *interface) +{ + // XXX we probably need to delete everything that belonged to it... + networkServerInterfaces.removeAt(networkServerInterfaces.indexOf(interface)); +} diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index c91c8f89c..e247fc1dd 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "server.h" class QSqlDatabase; @@ -34,6 +35,7 @@ class QTimer; class GameReplay; class Servatrice; class ServerSocketInterface; +class NetworkServerInterface; class Servatrice_GameServer : public QTcpServer { Q_OBJECT @@ -60,6 +62,19 @@ protected: void incomingConnection(int socketDescriptor); }; +class ServerProperties { +public: + int id; + QSslCertificate cert; + QString hostname; + QHostAddress address; + int gamePort; + int controlPort; + + ServerProperties(int _id, const QSslCertificate &_cert, const QString &_hostname, const QHostAddress &_address, int _gamePort, int _controlPort) + : id(_id), cert(_cert), hostname(_hostname), address(_address), gamePort(_gamePort), controlPort(_controlPort) { } +}; + class Servatrice : public Server { Q_OBJECT @@ -85,6 +100,7 @@ public: int getMaxGamesPerUser() const { return maxGamesPerUser; } bool getThreaded() const { return threaded; } QString getDbPrefix() const { return dbPrefix; } + int getServerId() const { return serverId; } void updateLoginMessage(); ServerInfo_User getUserData(const QString &name, bool withId = false); int getUsersWithAddress(const QHostAddress &address) const; @@ -98,11 +114,21 @@ public: void incRxBytes(quint64 num); int getUserIdInDB(const QString &name); void storeGameInformation(int secondsElapsed, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replays); + + void addNetworkServerInterface(NetworkServerInterface *interface); + void removeNetworkServerInterface(NetworkServerInterface *interface); + + QList getServerList() const; protected: int startSession(const QString &userName, const QString &address); void endSession(int sessionId); bool userExists(const QString &user); AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr); + + void clearSessionTables(); + void lockSessionTables(); + void unlockSessionTables(); + bool userSessionExists(const QString &userName); private: enum AuthenticationMethod { AuthenticationNone, AuthenticationSql }; enum DatabaseType { DatabaseNone, DatabaseMySql }; @@ -127,6 +153,12 @@ private: QString shutdownReason; int shutdownMinutes; QTimer *shutdownTimer; + + mutable QMutex serverListMutex; + QList serverList; + void updateServerList(); + + QList networkServerInterfaces; }; #endif diff --git a/servatrice/src/server_logger.cpp b/servatrice/src/server_logger.cpp index ff75d062e..e77eca54f 100644 --- a/servatrice/src/server_logger.cpp +++ b/servatrice/src/server_logger.cpp @@ -31,16 +31,16 @@ ServerLogger::~ServerLogger() flushBuffer(); } -void ServerLogger::logMessage(QString message, Server_ProtocolHandler *ssi) +void ServerLogger::logMessage(QString message, void *caller) { if (!logFile) return; bufferMutex.lock(); - QString ssiString; - if (ssi) - ssiString = QString::number((qulonglong) ssi, 16) + " "; - buffer.append(QDateTime::currentDateTime().toString() + " " + QString::number((qulonglong) QThread::currentThread(), 16) + " " + ssiString + message); + QString callerString; + if (caller) + callerString = QString::number((qulonglong) caller, 16) + " "; + buffer.append(QDateTime::currentDateTime().toString() + " " + QString::number((qulonglong) QThread::currentThread(), 16) + " " + callerString + message); bufferMutex.unlock(); emit sigFlushBuffer(); diff --git a/servatrice/src/server_logger.h b/servatrice/src/server_logger.h index f14c29806..038e60028 100644 --- a/servatrice/src/server_logger.h +++ b/servatrice/src/server_logger.h @@ -18,7 +18,7 @@ public: ~ServerLogger(); static void hupSignalHandler(int unused); public slots: - void logMessage(QString message, Server_ProtocolHandler *ssi = 0); + void logMessage(QString message, void *caller = 0); private slots: void handleSigHup(); void flushBuffer(); diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 44612c8a0..64d6aaa8a 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -64,7 +64,6 @@ ServerSocketInterface::ServerSocketInterface(Servatrice *_server, QTcpSocket *_s connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater())); connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(catchSocketError(QAbstractSocket::SocketError))); connect(this, SIGNAL(outputBufferChanged()), this, SLOT(flushOutputBuffer()), Qt::QueuedConnection); - connect(this, SIGNAL(logDebugMessage(const QString &, Server_ProtocolHandler *)), logger, SLOT(logMessage(QString, Server_ProtocolHandler *))); Event_ServerIdentification identEvent; identEvent.set_server_name(servatrice->getServerName().toStdString());