diff --git a/cockatrice/src/carditem.cpp b/cockatrice/src/carditem.cpp index 312350c1..61bd9a0f 100644 --- a/cockatrice/src/carditem.cpp +++ b/cockatrice/src/carditem.cpp @@ -2,8 +2,8 @@ #include #include #include -#include #include +#include "gamescene.h" #include "carditem.h" #include "carddragitem.h" #include "carddatabase.h" @@ -369,6 +369,8 @@ void CardItem::resetState() attachedCards.clear(); setTapped(false, false); setDoesntUntap(false); + if (scene()) + static_cast(scene())->unregisterAnimationItem(this); update(); } diff --git a/cockatrice/src/gamescene.cpp b/cockatrice/src/gamescene.cpp index d89bf0e8..1ba225e6 100644 --- a/cockatrice/src/gamescene.cpp +++ b/cockatrice/src/gamescene.cpp @@ -262,6 +262,13 @@ void GameScene::registerAnimationItem(AbstractCardItem *card) animationTimer->start(50, this); } +void GameScene::unregisterAnimationItem(AbstractCardItem *card) +{ + cardsToAnimate.remove(static_cast(card)); + if (cardsToAnimate.isEmpty()) + animationTimer->stop(); +} + void GameScene::startRubberBand(const QPointF &selectionOrigin) { emit sigStartRubberBand(selectionOrigin); diff --git a/cockatrice/src/gamescene.h b/cockatrice/src/gamescene.h index 2f3b5ca9..e46b6661 100644 --- a/cockatrice/src/gamescene.h +++ b/cockatrice/src/gamescene.h @@ -42,6 +42,7 @@ public: void stopRubberBand(); void registerAnimationItem(AbstractCardItem *item); + void unregisterAnimationItem(AbstractCardItem *card); public slots: void toggleZoneView(Player *player, const QString &zoneName, int numberCards); void addRevealedZoneView(Player *player, CardZone *zone, const QList &cardList); diff --git a/cockatrice/src/localserver.h b/cockatrice/src/localserver.h index cf24b438..36838cc6 100644 --- a/cockatrice/src/localserver.h +++ b/cockatrice/src/localserver.h @@ -20,6 +20,8 @@ public: LocalServerInterface *newConnection(); protected: + int startSession(const QString & /*userName*/, const QString & /*address*/) { return -1; } + void endSession(int /*sessionId*/) { } bool userExists(const QString & /*name*/) { return false; } ServerInfo_User *getUserData(const QString &name); QMap getBuddyList(const QString & /*name*/) { return QMap(); } diff --git a/common/server.cpp b/common/server.cpp index d9e83a06..4a37dadc 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -82,6 +82,9 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString users.insert(name, session); qDebug() << "Server::loginUser: name=" << name; + session->setSessionId(startSession(name, session->getAddress())); + qDebug() << "session id:" << session->getSessionId(); + Event_UserJoined *event = new Event_UserJoined(new ServerInfo_User(data, false)); for (int i = 0; i < clients.size(); ++i) if (clients[i]->getAcceptsUserListChanges()) @@ -111,6 +114,10 @@ void Server::removeClient(Server_ProtocolHandler *client) users.remove(data->getName()); qDebug() << "Server::removeClient: name=" << data->getName(); + + if (client->getSessionId() != -1) + endSession(client->getSessionId()); + qDebug() << "closed session id:" << client->getSessionId(); } qDebug() << "Server::removeClient:" << clients.size() << "clients; " << users.size() << "users left"; } diff --git a/common/server.h b/common/server.h index de8dd2a5..467642f6 100644 --- a/common/server.h +++ b/common/server.h @@ -50,6 +50,8 @@ protected: QMap users; QMap rooms; + virtual int startSession(const QString &userName, const QString &address) = 0; + virtual void endSession(int sessionId) = 0; virtual bool userExists(const QString &user) = 0; virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password) = 0; virtual ServerInfo_User *getUserData(const QString &name) = 0; diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 7bd1e99f..8a7336f8 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -14,7 +14,7 @@ #include Server_ProtocolHandler::Server_ProtocolHandler(Server *_server, QObject *parent) - : QObject(parent), server(_server), authState(PasswordWrong), acceptsUserListChanges(false), acceptsRoomListChanges(false), userInfo(0), timeRunning(0), lastDataReceived(0), gameListMutex(QMutex::Recursive) + : QObject(parent), server(_server), authState(PasswordWrong), acceptsUserListChanges(false), acceptsRoomListChanges(false), userInfo(0), sessionId(-1), timeRunning(0), lastDataReceived(0), gameListMutex(QMutex::Recursive) { connect(server, SIGNAL(pingClockTimeout()), this, SLOT(pingClockTimeout())); } @@ -147,6 +147,8 @@ ResponseCode Server_ProtocolHandler::processCommandHelper(Command *command, Comm ModeratorCommand *moderatorCommand = qobject_cast(command); if (moderatorCommand) { qDebug() << "received ModeratorCommand"; + if (!userInfo) + return RespLoginNeeded; if (!(userInfo->getUserLevel() & ServerInfo_User::IsModerator)) return RespLoginNeeded; @@ -158,6 +160,8 @@ ResponseCode Server_ProtocolHandler::processCommandHelper(Command *command, Comm AdminCommand *adminCommand = qobject_cast(command); if (adminCommand) { qDebug() << "received AdminCommand"; + if (!userInfo) + return RespLoginNeeded; if (!(userInfo->getUserLevel() & ServerInfo_User::IsAdmin)) return RespLoginNeeded; diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index c95f5094..05b07ce2 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -31,6 +31,7 @@ protected: void prepareDestroy(); virtual bool getCompressionSupport() const = 0; + int sessionId; private: QList itemQueue; QList messageSizeOverTime, messageCountOverTime; @@ -110,6 +111,8 @@ public: void setUserInfo(ServerInfo_User *_userInfo) { userInfo = _userInfo; } const QMap &getBuddyList() const { return buddyList; } const QMap &getIgnoreList() const { return ignoreList; } + int getSessionId() const { return sessionId; } + void setSessionId(int _sessionId) { sessionId = _sessionId; } int getLastCommandTime() const { return timeRunning - lastDataReceived; } void processCommandContainer(CommandContainer *cont); diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index 4bbd6c84..192cdef7 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -119,9 +119,11 @@ CREATE TABLE IF NOT EXISTS `cockatrice_users` ( CREATE TABLE `cockatrice_uptime` ( `id_server` tinyint(3) NOT NULL, `timest` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `uptime` int(11) DEFAULT NULL, - `users_count` int(11) DEFAULT NULL, - `games_count` int(11) DEFAULT NULL, + `uptime` int(11) NOT NULL, + `users_count` int(11) NOT NULL, + `games_count` int(11) NOT NULL, + `rx_bytes` int(11) NOT NULL, + `tx_bytes` int(11) NOT NULL, PRIMARY KEY (`timest`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; @@ -149,7 +151,7 @@ CREATE TABLE `cockatrice_buddylist` ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE `cockatrice_bans` ( - `user_name` varchar(255) unsigned zerofill NOT NULL, + `user_name` varchar(255) NOT NULL, `ip_address` varchar(255) NOT NULL, `id_admin` int(7) unsigned zerofill NOT NULL, `time_from` datetime NOT NULL, @@ -159,3 +161,12 @@ CREATE TABLE `cockatrice_bans` ( KEY `time_from` (`time_from`,`ip_address`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `cockatrice_sessions` ( + `id` int(9) NOT NULL AUTO_INCREMENT, + `user_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `ip_address` char(15) COLLATE utf8_unicode_ci NOT NULL, + `start_time` datetime NOT NULL, + `end_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `username` (`user_name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 890aedc4..c3c103b9 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -189,20 +189,32 @@ AuthenticationResult Servatrice::checkUserPassword(Server_ProtocolHandler *handl return PasswordWrong; } - QSqlQuery query; - query.prepare("select a.password_sha512, 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.user_name = a.name and b.time_from = (select max(c.time_from) from " + dbPrefix + "_bans c where c.user_name = a.name) where a.name = :name and a.active = 1"); - query.bindValue(":name", user); - if (!execSqlQuery(query)) { + QSqlQuery nameBanQuery; + nameBanQuery.prepare("select time_to_sec(timediff(now(), date_add(b.time_from, interval b.minutes minute))) < 0, b.minutes <=> 0 from " + dbPrefix + "_bans b where b.time_from = (select max(c.time_from) from " + dbPrefix + "_bans c where c.user_name = :name2) and b.user_name = :name1"); + nameBanQuery.bindValue(":name1", user); + nameBanQuery.bindValue(":name2", user); + if (!execSqlQuery(nameBanQuery)) { qDebug("Login denied: SQL error"); return PasswordWrong; } - if (query.next()) { - if (query.value(1).toInt() || query.value(2).toInt()) { + if (nameBanQuery.next()) + if (nameBanQuery.value(0).toInt() || nameBanQuery.value(1).toInt()) { qDebug("Login denied: banned by name"); return PasswordWrong; } - if (query.value(0).toString() == PasswordHasher::computeHash(password, query.value(0).toString().left(16))) { + + QSqlQuery passwordQuery; + passwordQuery.prepare("select password_sha512 from " + dbPrefix + "_users where name = :name and active = 1"); + passwordQuery.bindValue(":name", user); + if (!execSqlQuery(passwordQuery)) { + qDebug("Login denied: SQL error"); + return PasswordWrong; + } + + if (passwordQuery.next()) { + const QString correctPassword = passwordQuery.value(0).toString(); + if (correctPassword == PasswordHasher::computeHash(password, correctPassword.left(16))) { qDebug("Login accepted: password right"); return PasswordRight; } else { @@ -300,6 +312,31 @@ int Servatrice::getUsersWithAddress(const QHostAddress &address) const return result; } +int Servatrice::startSession(const QString &userName, const QString &address) +{ + QMutexLocker locker(&dbMutex); + checkSql(); + + QSqlQuery query; + query.prepare("insert into " + dbPrefix + "_sessions (user_name, ip_address, start_time) values(:user_name, :ip_address, NOW())"); + query.bindValue(":user_name", userName); + query.bindValue(":ip_address", address); + if (execSqlQuery(query)) + return query.lastInsertId().toInt(); + return -1; +} + +void Servatrice::endSession(int sessionId) +{ + QMutexLocker locker(&dbMutex); + checkSql(); + + QSqlQuery query; + query.prepare("update " + dbPrefix + "_sessions set end_time=NOW() where id = :id_session"); + query.bindValue(":id_session", sessionId); + execSqlQuery(query); +} + QMap Servatrice::getBuddyList(const QString &name) { QMutexLocker locker(&dbMutex); @@ -373,15 +410,26 @@ void Servatrice::statusUpdate() uptime += statusUpdateClock->interval() / 1000; + txBytesMutex.lock(); + quint64 tx = txBytes; + txBytes = 0; + txBytesMutex.unlock(); + rxBytesMutex.lock(); + quint64 rx = rxBytes; + rxBytes = 0; + rxBytesMutex.unlock(); + 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.prepare("insert into " + dbPrefix + "_uptime (id_server, timest, uptime, users_count, games_count, tx_bytes, rx_bytes) values(:id, NOW(), :uptime, :users_count, :games_count, :tx, :rx)"); query.bindValue(":id", serverId); query.bindValue(":uptime", uptime); query.bindValue(":users_count", uc); query.bindValue(":games_count", gc); + query.bindValue(":tx", tx); + query.bindValue(":rx", rx); execSqlQuery(query); } @@ -399,6 +447,20 @@ void Servatrice::scheduleShutdown(const QString &reason, int minutes) shutdownTimeout(); } +void Servatrice::incTxBytes(quint64 num) +{ + txBytesMutex.lock(); + txBytes += num; + txBytesMutex.unlock(); +} + +void Servatrice::incRxBytes(quint64 num) +{ + rxBytesMutex.lock(); + rxBytes += num; + rxBytesMutex.unlock(); +} + void Servatrice::shutdownTimeout() { QMutexLocker locker(&serverMutex); diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index 8b397d1b..e22aa01c 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -75,7 +75,11 @@ public: QMap getBuddyList(const QString &name); QMap getIgnoreList(const QString &name); void scheduleShutdown(const QString &reason, int minutes); + void incTxBytes(quint64 num); + void incRxBytes(quint64 num); 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); private: @@ -87,6 +91,8 @@ private: int serverId; bool threaded; int uptime; + QMutex txBytesMutex, rxBytesMutex; + quint64 txBytes, rxBytes; int maxGameInactivityTime, maxPlayerInactivityTime; int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser; ServerInfo_User *evalUserQueryResult(const QSqlQuery &query, bool complete); diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 25ac9be8..ecd1c000 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -86,6 +86,7 @@ void ServerSocketInterface::flushXmlBuffer() QMutexLocker locker(&xmlBufferMutex); if (xmlBuffer.isEmpty()) return; + servatrice->incTxBytes(xmlBuffer.size()); socket->write(xmlBuffer.toUtf8()); socket->flush(); xmlBuffer.clear(); @@ -94,6 +95,7 @@ void ServerSocketInterface::flushXmlBuffer() void ServerSocketInterface::readClient() { QByteArray data = socket->readAll(); + servatrice->incRxBytes(data.size()); if (!data.contains("logMessage(QString(data), this); xmlReader->addData(data);