diff --git a/cockatrice/src/abstractclient.cpp b/cockatrice/src/abstractclient.cpp index e5bc36a1..5fb9b433 100644 --- a/cockatrice/src/abstractclient.cpp +++ b/cockatrice/src/abstractclient.cpp @@ -32,6 +32,7 @@ void AbstractClient::processProtocolItem(ProtocolItem *item) GenericEvent *genericEvent = qobject_cast(item); if (genericEvent) { switch (genericEvent->getItemId()) { + case ItemId_Event_ConnectionClosed: emit connectionClosedEventReceived(qobject_cast(item)); break; case ItemId_Event_AddToList: emit addToListEventReceived(qobject_cast(item)); break; case ItemId_Event_RemoveFromList: emit removeFromListEventReceived(qobject_cast(item)); break; case ItemId_Event_UserJoined: emit userJoinedEventReceived(qobject_cast(item)); break; @@ -63,7 +64,6 @@ void AbstractClient::processProtocolItem(ProtocolItem *item) } } - void AbstractClient::setStatus(const ClientStatus _status) { if (_status != status) { diff --git a/cockatrice/src/abstractclient.h b/cockatrice/src/abstractclient.h index 5e13bf9d..f43131b5 100644 --- a/cockatrice/src/abstractclient.h +++ b/cockatrice/src/abstractclient.h @@ -20,6 +20,7 @@ class Event_ServerMessage; class Event_ListRooms; class Event_GameJoined; class Event_Message; +class Event_ConnectionClosed; enum ClientStatus { StatusDisconnected, @@ -41,6 +42,7 @@ signals: // Game events void gameEventContainerReceived(GameEventContainer *event); // Generic events + void connectionClosedEventReceived(Event_ConnectionClosed *event); void addToListEventReceived(Event_AddToList *event); void removeFromListEventReceived(Event_RemoveFromList *event); void userJoinedEventReceived(Event_UserJoined *event); diff --git a/cockatrice/src/localserverinterface.h b/cockatrice/src/localserverinterface.h index 7adb5cb3..fc23b1b5 100644 --- a/cockatrice/src/localserverinterface.h +++ b/cockatrice/src/localserverinterface.h @@ -19,6 +19,7 @@ private: ResponseCode cmdDeckUpload(Command_DeckUpload * /*cmd*/, CommandContainer * /*cont*/) { return RespFunctionNotAllowed; } ResponseCode cmdDeckDownload(Command_DeckDownload * /*cmd*/, CommandContainer * /*cont*/) { return RespFunctionNotAllowed; } ResponseCode cmdUpdateServerMessage(Command_UpdateServerMessage * /*cmd*/, CommandContainer * /*cont*/) { return RespFunctionNotAllowed; } + ResponseCode cmdBanFromServer(Command_BanFromServer * /*cmd*/, CommandContainer * /*cont*/) { return RespFunctionNotAllowed; } public: LocalServerInterface(LocalServer *_server); ~LocalServerInterface(); diff --git a/cockatrice/src/main.h b/cockatrice/src/main.h index 0bc80917..b1f30bdb 100644 --- a/cockatrice/src/main.h +++ b/cockatrice/src/main.h @@ -8,7 +8,7 @@ extern CardDatabase *db; extern QTranslator *translator; const QString translationPrefix = "cockatrice"; -const QString versionString = "0.20110123"; +const QString versionString = "0.20110303"; void installNewTranslator(); diff --git a/cockatrice/src/remoteclient.cpp b/cockatrice/src/remoteclient.cpp index 6baa7f30..76f4854c 100644 --- a/cockatrice/src/remoteclient.cpp +++ b/cockatrice/src/remoteclient.cpp @@ -31,8 +31,9 @@ RemoteClient::~RemoteClient() void RemoteClient::slotSocketError(QAbstractSocket::SocketError /*error*/) { - emit socketError(socket->errorString()); + QString errorString = socket->errorString(); disconnectFromServer(); + emit socketError(errorString); } void RemoteClient::slotConnected() diff --git a/cockatrice/src/tab_admin.cpp b/cockatrice/src/tab_admin.cpp index 3b949ec6..4ea3a652 100644 --- a/cockatrice/src/tab_admin.cpp +++ b/cockatrice/src/tab_admin.cpp @@ -7,7 +7,7 @@ #include "protocol_items.h" TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, QWidget *parent) - : Tab(_tabSupervisor, parent), client(_client) + : Tab(_tabSupervisor, parent), locked(true), client(_client) { updateServerMessageButton = new QPushButton; connect(updateServerMessageButton, SIGNAL(clicked()), this, SLOT(actUpdateServerMessage())); @@ -55,6 +55,7 @@ void TabAdmin::actUnlock() adminGroupBox->setEnabled(true); lockButton->setEnabled(true); unlockButton->setEnabled(false); + locked = false; } } @@ -63,4 +64,5 @@ void TabAdmin::actLock() adminGroupBox->setEnabled(false); lockButton->setEnabled(false); unlockButton->setEnabled(true); + locked = true; } \ No newline at end of file diff --git a/cockatrice/src/tab_admin.h b/cockatrice/src/tab_admin.h index fc61eb95..8450d473 100644 --- a/cockatrice/src/tab_admin.h +++ b/cockatrice/src/tab_admin.h @@ -11,6 +11,7 @@ class QPushButton; class TabAdmin : public Tab { Q_OBJECT private: + bool locked; AbstractClient *client; QPushButton *updateServerMessageButton; QGroupBox *adminGroupBox; @@ -24,6 +25,7 @@ public: TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, QWidget *parent = 0); void retranslateUi(); QString getTabText() const { return tr("Administration"); } + bool getLocked() const { return locked; } }; -#endif +#endif \ No newline at end of file diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index 1585eb43..51c93616 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -132,7 +132,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, const Q gameTypes.insert(gameTypeList[i]->getGameTypeId(), gameTypeList[i]->getDescription()); gameSelector = new GameSelector(client, this); - userList = new UserList(tabSupervisor->getUserListsTab(), client, UserList::RoomList); + userList = new UserList(tabSupervisor, client, UserList::RoomList); connect(userList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool))); chatView = new ChatView(ownName); @@ -205,10 +205,18 @@ void TabRoom::sendMessage() if (sayEdit->text().isEmpty()) return; - client->sendCommand(new Command_RoomSay(roomId, sayEdit->text())); + Command_RoomSay *cmd = new Command_RoomSay(roomId, sayEdit->text()); + connect(cmd, SIGNAL(finished(ProtocolResponse *)), this, SLOT(sayFinished(ProtocolResponse *))); + client->sendCommand(cmd); sayEdit->clear(); } +void TabRoom::sayFinished(ProtocolResponse *response) +{ + if (response->getResponseCode() == RespChatFlood) + chatView->appendMessage(QString(), tr("You are flooding the chat. Please wait a couple of seconds.")); +} + void TabRoom::actLeaveRoom() { client->sendCommand(new Command_LeaveRoom(roomId)); diff --git a/cockatrice/src/tab_room.h b/cockatrice/src/tab_room.h index cc2913a9..1b013306 100644 --- a/cockatrice/src/tab_room.h +++ b/cockatrice/src/tab_room.h @@ -23,6 +23,7 @@ class Event_ListGames; class Event_JoinRoom; class Event_LeaveRoom; class Event_RoomSay; +class ProtocolResponse; class TabRoom; class GameSelector : public QGroupBox { @@ -73,6 +74,7 @@ signals: private slots: void sendMessage(); void actLeaveRoom(); + void sayFinished(ProtocolResponse *response); void processListGamesEvent(Event_ListGames *event); void processJoinRoomEvent(Event_JoinRoom *event); diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index fbb108ff..9ed8ba17 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -291,3 +291,10 @@ void TabSupervisor::updateCurrent(int index) } else emit setMenu(0); } + +bool TabSupervisor::getAdminLocked() const +{ + if (!tabAdmin) + return true; + return tabAdmin->getLocked(); +} diff --git a/cockatrice/src/tab_supervisor.h b/cockatrice/src/tab_supervisor.h index 75402672..61e5987d 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -45,6 +45,7 @@ public: void stop(); int getGameCount() const { return gameTabs.size(); } TabUserLists *getUserListsTab() const { return tabUserLists; } + bool getAdminLocked() const; signals: void setMenu(QMenu *menu); void localGameEnded(); diff --git a/cockatrice/src/tab_userlists.cpp b/cockatrice/src/tab_userlists.cpp index 5c2e5ca1..825c9c4b 100644 --- a/cockatrice/src/tab_userlists.cpp +++ b/cockatrice/src/tab_userlists.cpp @@ -10,9 +10,9 @@ TabUserLists::TabUserLists(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerInfo_User *userInfo, QWidget *parent) : Tab(_tabSupervisor, parent), client(_client) { - allUsersList = new UserList(this, client, UserList::AllUsersList); - buddyList = new UserList(this, client, UserList::BuddyList); - ignoreList = new UserList(this, client, UserList::IgnoreList); + allUsersList = new UserList(_tabSupervisor, client, UserList::AllUsersList); + buddyList = new UserList(_tabSupervisor, client, UserList::BuddyList); + ignoreList = new UserList(_tabSupervisor, client, UserList::IgnoreList); userInfoBox = new UserInfoBox(client, false); userInfoBox->updateInfo(userInfo); diff --git a/cockatrice/src/userlist.cpp b/cockatrice/src/userlist.cpp index 053acca7..c95c8f4a 100644 --- a/cockatrice/src/userlist.cpp +++ b/cockatrice/src/userlist.cpp @@ -1,5 +1,6 @@ #include "userlist.h" #include "tab_userlists.h" +#include "tab_supervisor.h" #include "abstractclient.h" #include "pixmapgenerator.h" #include "userinfobox.h" @@ -8,6 +9,7 @@ #include #include #include +#include UserListItemDelegate::UserListItemDelegate(QObject *const parent) : QStyledItemDelegate(parent) @@ -45,8 +47,8 @@ bool UserListTWI::operator<(const QTreeWidgetItem &other) const return data(2, Qt::UserRole).toString().toLower() < other.data(2, Qt::UserRole).toString().toLower(); } -UserList::UserList(TabUserLists *_tabUserLists, AbstractClient *_client, UserListType _type, QWidget *parent) - : QGroupBox(parent), tabUserLists(_tabUserLists), client(_client), type(_type), onlineCount(0) +UserList::UserList(TabSupervisor *_tabSupervisor, AbstractClient *_client, UserListType _type, QWidget *parent) + : QGroupBox(parent), tabSupervisor(_tabSupervisor), client(_client), type(_type), onlineCount(0) { itemDelegate = new UserListItemDelegate(this); @@ -173,6 +175,7 @@ void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index) QAction *aRemoveFromBuddyList = new QAction(tr("Remove from &buddy list"), this); QAction *aAddToIgnoreList = new QAction(tr("Add to &ignore list"), this); QAction *aRemoveFromIgnoreList = new QAction(tr("Remove from &ignore list"), this); + QAction *aBan = new QAction(tr("Ban from &server"), this); QMenu *menu = new QMenu(this); menu->addAction(aUserName); @@ -180,14 +183,18 @@ void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index) menu->addAction(aDetails); menu->addAction(aChat); menu->addSeparator(); - if (tabUserLists->getBuddyList()->userInList(userName)) + if (tabSupervisor->getUserListsTab()->getBuddyList()->userInList(userName)) menu->addAction(aRemoveFromBuddyList); else menu->addAction(aAddToBuddyList); - if (tabUserLists->getIgnoreList()->userInList(userName)) + if (tabSupervisor->getUserListsTab()->getIgnoreList()->userInList(userName)) menu->addAction(aRemoveFromIgnoreList); else menu->addAction(aAddToIgnoreList); + if (!tabSupervisor->getAdminLocked()) { + menu->addSeparator(); + menu->addAction(aBan); + } QAction *actionClicked = menu->exec(pos); if (actionClicked == aDetails) { @@ -204,6 +211,11 @@ void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index) client->sendCommand(new Command_AddToList("ignore", userName)); else if (actionClicked == aRemoveFromIgnoreList) client->sendCommand(new Command_RemoveFromList("ignore", userName)); + else if (actionClicked == aBan) { + bool ok; + int minutes = QInputDialog::getInt(this, tr("Duration"), tr("Please enter the duration of the ban (in minutes).\nEnter 0 for an indefinite ban."), 0, 0, 2147483647, 10, &ok); + client->sendCommand(new Command_BanFromServer(userName, minutes)); + } delete menu; delete aUserName; @@ -213,6 +225,7 @@ void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index) delete aRemoveFromBuddyList; delete aAddToIgnoreList; delete aRemoveFromIgnoreList; + delete aBan; } bool UserList::userInList(const QString &userName) const diff --git a/cockatrice/src/userlist.h b/cockatrice/src/userlist.h index cd883ff6..a611bf5d 100644 --- a/cockatrice/src/userlist.h +++ b/cockatrice/src/userlist.h @@ -8,7 +8,7 @@ class QTreeWidget; class ServerInfo_User; class AbstractClient; -class TabUserLists; +class TabSupervisor; class UserListItemDelegate : public QStyledItemDelegate { public: @@ -27,7 +27,7 @@ class UserList : public QGroupBox { public: enum UserListType { AllUsersList, RoomList, BuddyList, IgnoreList }; private: - TabUserLists *tabUserLists; + TabSupervisor *tabSupervisor; AbstractClient *client; UserListType type; QTreeWidget *userTree; @@ -45,7 +45,7 @@ signals: void addIgnore(const QString &userName); void removeIgnore(const QString &userName); public: - UserList(TabUserLists *_tabUserLists, AbstractClient *_client, UserListType _type, QWidget *parent = 0); + UserList(TabSupervisor *_tabSupervisor, AbstractClient *_client, UserListType _type, QWidget *parent = 0); void retranslateUi(); void processUserInfo(ServerInfo_User *user, bool online); bool deleteUser(const QString &userName); diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 3d39a785..06d6e3a6 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -47,6 +47,20 @@ void MainWindow::updateTabMenu(QMenu *menu) menuBar()->insertMenu(helpMenu->menuAction(), menu); } +void MainWindow::processConnectionClosedEvent(Event_ConnectionClosed *event) +{ + QString reason = event->getReason(); + client->disconnectFromServer(); + QString reasonStr; + if (reason == "too_many_connections") + reasonStr = tr("There are too many concurrent connections from your address."); + else if (reason == "banned") + reasonStr = tr("Banned by moderator."); + else + reasonStr = tr("Unknown reason."); + QMessageBox::critical(this, tr("Connection closed"), tr("The server has terminated your connection.\nReason: %1").arg(reasonStr)); +} + void MainWindow::statusChanged(ClientStatus _status) { setClientStatusTitle(); @@ -273,6 +287,7 @@ MainWindow::MainWindow(QWidget *parent) QPixmapCache::setCacheLimit(200000); client = new RemoteClient(this); + connect(client, SIGNAL(connectionClosedEventReceived(Event_ConnectionClosed *)), this, SLOT(processConnectionClosedEvent(Event_ConnectionClosed *))); connect(client, SIGNAL(serverError(ResponseCode)), this, SLOT(serverError(ResponseCode))); connect(client, SIGNAL(socketError(const QString &)), this, SLOT(socketError(const QString &))); connect(client, SIGNAL(serverTimeout()), this, SLOT(serverTimeout())); diff --git a/cockatrice/src/window_main.h b/cockatrice/src/window_main.h index e6bcb155..9ca62ba8 100644 --- a/cockatrice/src/window_main.h +++ b/cockatrice/src/window_main.h @@ -35,6 +35,7 @@ class MainWindow : public QMainWindow { private slots: void updateTabMenu(QMenu *menu); void statusChanged(ClientStatus _status); + void processConnectionClosedEvent(Event_ConnectionClosed *event); void serverTimeout(); void serverError(ResponseCode r); void socketError(const QString &errorStr); diff --git a/common/protocol_item_ids.h b/common/protocol_item_ids.h index f2907846..1b2378d4 100644 --- a/common/protocol_item_ids.h +++ b/common/protocol_item_ids.h @@ -63,16 +63,18 @@ ItemId_Event_DumpZone = 1061, ItemId_Event_StopDumpZone = 1062, ItemId_Event_RemoveFromList = 1063, ItemId_Event_ServerMessage = 1064, -ItemId_Event_Message = 1065, -ItemId_Event_GameJoined = 1066, -ItemId_Event_UserLeft = 1067, -ItemId_Event_LeaveRoom = 1068, -ItemId_Event_RoomSay = 1069, -ItemId_Context_ReadyStart = 1070, -ItemId_Context_Concede = 1071, -ItemId_Context_DeckSelect = 1072, -ItemId_Context_UndoDraw = 1073, -ItemId_Context_MoveCard = 1074, -ItemId_Command_UpdateServerMessage = 1075, -ItemId_Other = 1076 +ItemId_Event_ConnectionClosed = 1065, +ItemId_Event_Message = 1066, +ItemId_Event_GameJoined = 1067, +ItemId_Event_UserLeft = 1068, +ItemId_Event_LeaveRoom = 1069, +ItemId_Event_RoomSay = 1070, +ItemId_Context_ReadyStart = 1071, +ItemId_Context_Concede = 1072, +ItemId_Context_DeckSelect = 1073, +ItemId_Context_UndoDraw = 1074, +ItemId_Context_MoveCard = 1075, +ItemId_Command_UpdateServerMessage = 1076, +ItemId_Command_BanFromServer = 1077, +ItemId_Other = 1078 }; diff --git a/common/protocol_items.cpp b/common/protocol_items.cpp index ffedf384..5fde8b1f 100644 --- a/common/protocol_items.cpp +++ b/common/protocol_items.cpp @@ -389,6 +389,11 @@ Event_ServerMessage::Event_ServerMessage(const QString &_message) { insertItem(new SerializableItem_String("message", _message)); } +Event_ConnectionClosed::Event_ConnectionClosed(const QString &_reason) + : GenericEvent("connection_closed") +{ + insertItem(new SerializableItem_String("reason", _reason)); +} Event_Message::Event_Message(const QString &_senderName, const QString &_receiverName, const QString &_text) : GenericEvent("message") { @@ -448,6 +453,12 @@ Command_UpdateServerMessage::Command_UpdateServerMessage() : AdminCommand("update_server_message") { } +Command_BanFromServer::Command_BanFromServer(const QString &_userName, int _minutes) + : AdminCommand("ban_from_server") +{ + insertItem(new SerializableItem_String("user_name", _userName)); + insertItem(new SerializableItem_Int("minutes", _minutes)); +} void ProtocolItem::initializeHashAuto() { itemNameHash.insert("cmdping", Command_Ping::newItem); @@ -514,6 +525,7 @@ void ProtocolItem::initializeHashAuto() itemNameHash.insert("game_eventstop_dump_zone", Event_StopDumpZone::newItem); itemNameHash.insert("generic_eventremove_from_list", Event_RemoveFromList::newItem); itemNameHash.insert("generic_eventserver_message", Event_ServerMessage::newItem); + itemNameHash.insert("generic_eventconnection_closed", Event_ConnectionClosed::newItem); itemNameHash.insert("generic_eventmessage", Event_Message::newItem); itemNameHash.insert("generic_eventgame_joined", Event_GameJoined::newItem); itemNameHash.insert("generic_eventuser_left", Event_UserLeft::newItem); @@ -525,4 +537,5 @@ void ProtocolItem::initializeHashAuto() itemNameHash.insert("game_event_contextundo_draw", Context_UndoDraw::newItem); itemNameHash.insert("game_event_contextmove_card", Context_MoveCard::newItem); itemNameHash.insert("cmdupdate_server_message", Command_UpdateServerMessage::newItem); + itemNameHash.insert("cmdban_from_server", Command_BanFromServer::newItem); } diff --git a/common/protocol_items.dat b/common/protocol_items.dat index 64175935..8ca86b97 100644 --- a/common/protocol_items.dat +++ b/common/protocol_items.dat @@ -62,6 +62,7 @@ 3:stop_dump_zone:i,zone_owner_id:s,zone 4:remove_from_list:s,list:s,user_name 4:server_message:s,message +4:connection_closed:s,reason 4:message:s,sender_name:s,receiver_name:s,text 4:game_joined:i,game_id:s,game_description:i,player_id:b,spectator:b,spectators_can_talk:b,spectators_see_everything:b,resuming 4:user_left:s,user_name @@ -73,3 +74,4 @@ 6:undo_draw 6:move_card 7:update_server_message +7:ban_from_server:s,user_name:i,minutes \ No newline at end of file diff --git a/common/protocol_items.h b/common/protocol_items.h index 58264ff9..31495758 100644 --- a/common/protocol_items.h +++ b/common/protocol_items.h @@ -583,6 +583,14 @@ public: static SerializableItem *newItem() { return new Event_ServerMessage; } int getItemId() const { return ItemId_Event_ServerMessage; } }; +class Event_ConnectionClosed : public GenericEvent { + Q_OBJECT +public: + Event_ConnectionClosed(const QString &_reason = QString()); + QString getReason() const { return static_cast(itemMap.value("reason"))->getData(); }; + static SerializableItem *newItem() { return new Event_ConnectionClosed; } + int getItemId() const { return ItemId_Event_ConnectionClosed; } +}; class Event_Message : public GenericEvent { Q_OBJECT public: @@ -675,5 +683,14 @@ public: static SerializableItem *newItem() { return new Command_UpdateServerMessage; } int getItemId() const { return ItemId_Command_UpdateServerMessage; } }; +class Command_BanFromServer : public AdminCommand { + Q_OBJECT +public: + Command_BanFromServer(const QString &_userName = QString(), int _minutes = -1); + QString getUserName() const { return static_cast(itemMap.value("user_name"))->getData(); }; + int getMinutes() const { return static_cast(itemMap.value("minutes"))->getData(); }; + static SerializableItem *newItem() { return new Command_BanFromServer; } + int getItemId() const { return ItemId_Command_BanFromServer; } +}; #endif diff --git a/common/server.h b/common/server.h index f9ff7fad..552fd8ea 100644 --- a/common/server.h +++ b/common/server.h @@ -44,6 +44,7 @@ public: virtual QMap getBuddyList(const QString &name) = 0; virtual QMap getIgnoreList(const QString &name) = 0; + virtual bool getUserBanned(Server_ProtocolHandler * /*client*/, const QString & /*userName*/) const { return false; } protected: QMap games; QList clients; diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 9a9cc90e..108116ad 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -133,6 +133,7 @@ ResponseCode Server_ProtocolHandler::processCommandHelper(Command *command, Comm switch (command->getItemId()) { case ItemId_Command_UpdateServerMessage: return cmdUpdateServerMessage(static_cast(command), cont); + case ItemId_Command_BanFromServer: return cmdBanFromServer(static_cast(command), cont); default: return RespInvalidCommand; } } @@ -244,6 +245,8 @@ ResponseCode Server_ProtocolHandler::cmdLogin(Command_Login *cmd, CommandContain QString userName = cmd->getUsername().simplified(); if (userName.isEmpty() || (userInfo != 0)) return RespContextError; + if (server->getUserBanned(this, userName)) + return RespWrongPassword; authState = server->loginUser(this, userName, cmd->getPassword()); if (authState == PasswordWrong) return RespWrongPassword; diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index e47b6d2e..3cd60503 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -85,6 +85,7 @@ private: ResponseCode cmdStopDumpZone(Command_StopDumpZone *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player); ResponseCode cmdRevealCards(Command_RevealCards *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player); virtual ResponseCode cmdUpdateServerMessage(Command_UpdateServerMessage *cmd, CommandContainer *cont) = 0; + virtual ResponseCode cmdBanFromServer(Command_BanFromServer *cmd, CommandContainer *cont) = 0; ResponseCode processCommandHelper(Command *command, CommandContainer *cont); private slots: diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 3660432f..8558de40 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -33,6 +33,10 @@ Servatrice::Servatrice(QObject *parent) connect(pingClock, SIGNAL(timeout()), this, SIGNAL(pingClockTimeout())); pingClock->start(1000); + banTimeoutClock = new QTimer(this); + connect(banTimeoutClock, SIGNAL(timeout()), this, SLOT(updateBanTimer())); + banTimeoutClock->start(60000); + ProtocolItem::initializeHash(); settings = new QSettings("servatrice.ini", QSettings::IniFormat, this); @@ -85,6 +89,7 @@ Servatrice::Servatrice(QObject *parent) maxGameInactivityTime = settings->value("game/max_game_inactivity_time").toInt(); maxPlayerInactivityTime = settings->value("game/max_player_inactivity_time").toInt(); + maxUsersPerAddress = settings->value("security/max_users_per_address").toInt(); messageCountingInterval = settings->value("security/message_counting_interval").toInt(); maxMessageCountPerInterval = settings->value("security/max_message_count_per_interval").toInt(); maxMessageSizePerInterval = settings->value("security/max_message_size_per_interval").toInt(); @@ -232,6 +237,15 @@ ServerInfo_User *Servatrice::getUserData(const QString &name) return new ServerInfo_User(name, ServerInfo_User::IsUser); } +int Servatrice::getUsersWithAddress(const QHostAddress &address) const +{ + int result = 0; + for (int i = 0; i < clients.size(); ++i) + if (static_cast(clients[i])->getPeerAddress() == address) + ++result; + return result; +} + QMap Servatrice::getBuddyList(const QString &name) { QMap result; @@ -276,6 +290,32 @@ QMap Servatrice::getIgnoreList(const QString &name) return result; } +bool Servatrice::getUserBanned(Server_ProtocolHandler *client, const QString &userName) const +{ + QHostAddress address = static_cast(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() +{ + for (int i = 0; i < addressBanList.size(); ) + if (--(addressBanList[i].second) <= 0) + 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() { checkSql(); @@ -308,4 +348,4 @@ void Servatrice::statusUpdate() execSqlQuery(query); } -const QString Servatrice::versionString = "Servatrice 0.20110215"; +const QString Servatrice::versionString = "Servatrice 0.20110303"; diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index fb4476d2..c53b5338 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -34,6 +34,7 @@ class Servatrice : public Server private slots: void newConnection(); void statusUpdate(); + void updateBanTimer(); public: static const QString versionString; Servatrice(QObject *parent = 0); @@ -45,27 +46,33 @@ public: bool getGameShouldPing() const { return true; } int getMaxGameInactivityTime() const { return maxGameInactivityTime; } int getMaxPlayerInactivityTime() const { return maxPlayerInactivityTime; } + int getMaxUsersPerAddress() const { return maxUsersPerAddress; } int getMessageCountingInterval() const { return messageCountingInterval; } int getMaxMessageCountPerInterval() const { return maxMessageCountPerInterval; } int getMaxMessageSizePerInterval() const { return maxMessageSizePerInterval; } QString getDbPrefix() const { return dbPrefix; } void updateLoginMessage(); ServerInfo_User *getUserData(const QString &name); + int getUsersWithAddress(const QHostAddress &address) const; + QMap getBuddyList(const QString &name); + QMap getIgnoreList(const QString &name); + bool getUserBanned(Server_ProtocolHandler *client, const QString &userName) const; + void addAddressBan(const QHostAddress &address, int minutes) { addressBanList.append(QPair(address, minutes)); } + void addNameBan(const QString &name, int minutes) { nameBanList.append(QPair(name, minutes)); } protected: bool userExists(const QString &user); AuthenticationResult checkUserPassword(const QString &user, const QString &password); - QMap getBuddyList(const QString &name); - QMap getIgnoreList(const QString &name); private: - QTimer *pingClock, *statusUpdateClock; + QTimer *pingClock, *statusUpdateClock, *banTimeoutClock; QTcpServer *tcpServer; QString loginMessage; QString dbPrefix; QSettings *settings; int uptime; - int maxGameInactivityTime; - int maxPlayerInactivityTime; - int messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval; + QList > addressBanList; + QList > nameBanList; + int maxGameInactivityTime, maxPlayerInactivityTime; + int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval; ServerInfo_User *evalUserQueryResult(const QSqlQuery &query, bool complete); }; diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index daeb29c4..f4b13d02 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "serversocketinterface.h" #include "servatrice.h" @@ -45,13 +46,19 @@ ServerSocketInterface::ServerSocketInterface(Servatrice *_server, QTcpSocket *_s xmlWriter->writeStartElement("cockatrice_server_stream"); xmlWriter->writeAttribute("version", QString::number(ProtocolItem::protocolVersion)); - sendProtocolItem(new Event_ServerMessage(Servatrice::versionString)); + int maxUsers = _server->getMaxUsersPerAddress(); + if ((maxUsers > 0) && (_server->getUsersWithAddress(socket->peerAddress()) >= maxUsers)) { + sendProtocolItem(new Event_ConnectionClosed("too_many_connections")); + deleteLater(); + } else + sendProtocolItem(new Event_ServerMessage(Servatrice::versionString)); } ServerSocketInterface::~ServerSocketInterface() { qDebug("ServerSocketInterface destructor"); + socket->flush(); delete xmlWriter; delete xmlReader; delete socket; @@ -424,3 +431,35 @@ ResponseCode ServerSocketInterface::cmdUpdateServerMessage(Command_UpdateServerM servatrice->updateLoginMessage(); return RespOk; } + +ResponseCode ServerSocketInterface::cmdBanFromServer(Command_BanFromServer *cmd, CommandContainer *cont) +{ + QString userName = cmd->getUserName(); + if (!server->getUsers().contains(userName)) + return RespNameNotFound; + + int minutes = cmd->getMinutes(); + + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); + if (user->getUserInfo()->getUserLevel() & ServerInfo_User::IsRegistered) { + // Registered users can be banned by name. + if (minutes == 0) { + 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); + } else { + // Unregistered users must be banned by IP address. + // Indefinite address bans are not reasonable -> default to 30 minutes. + if (minutes == 0) + minutes = 30; + servatrice->addAddressBan(user->getPeerAddress(), minutes); + } + + user->sendProtocolItem(new Event_ConnectionClosed("banned")); + user->deleteLater(); + + return RespOk; +} diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index dc347d93..f55dfd12 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -21,6 +21,7 @@ #define SERVERSOCKETINTERFACE_H #include +#include #include "server_protocolhandler.h" class QTcpSocket; @@ -59,9 +60,11 @@ private: 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); public: ServerSocketInterface(Servatrice *_server, QTcpSocket *_socket, QObject *parent = 0); ~ServerSocketInterface(); + QHostAddress getPeerAddress() const { return socket->peerAddress(); } void sendProtocolItem(ProtocolItem *item, bool deleteItem = true); };