diff --git a/cockatrice/src/abstractcarditem.cpp b/cockatrice/src/abstractcarditem.cpp index e264f867..18653399 100644 --- a/cockatrice/src/abstractcarditem.cpp +++ b/cockatrice/src/abstractcarditem.cpp @@ -84,7 +84,6 @@ void AbstractCardItem::transformPainter(QPainter *painter, const QSizeF &transla void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedSize, int angle) { - QRectF totalBoundingRect = painter->combinedTransform().mapRect(boundingRect()); qreal scaleFactor = translatedSize.width() / boundingRect().width(); QPixmap *translatedPixmap = info->getPixmap(translatedSize.toSize()); diff --git a/cockatrice/src/decklistmodel.cpp b/cockatrice/src/decklistmodel.cpp index 8c7ede32..c46ebe2e 100644 --- a/cockatrice/src/decklistmodel.cpp +++ b/cockatrice/src/decklistmodel.cpp @@ -257,6 +257,7 @@ QModelIndex DeckListModel::addCard(const QString &cardName, const QString &zoneN cardNode->setNumber(cardNode->getNumber() + 1); QModelIndex ind = nodeToIndex(cardNode); emitRecursiveUpdates(ind); + deckList->updateDeckHash(); return ind; } } diff --git a/cockatrice/src/deckview.cpp b/cockatrice/src/deckview.cpp index e3ffe863..83f93893 100644 --- a/cockatrice/src/deckview.cpp +++ b/cockatrice/src/deckview.cpp @@ -45,7 +45,6 @@ void DeckViewCardDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { setCursor(Qt::OpenHandCursor); DeckViewScene *sc = static_cast(scene()); - QPointF sp = pos(); sc->removeItem(this); if (currentZone) { diff --git a/cockatrice/src/localserverinterface.h b/cockatrice/src/localserverinterface.h index 1db97412..ca5ba9ce 100644 --- a/cockatrice/src/localserverinterface.h +++ b/cockatrice/src/localserverinterface.h @@ -27,6 +27,7 @@ public: LocalServerInterface(LocalServer *_server); ~LocalServerInterface(); + QString getAddress() const { return QString(); } void sendProtocolItem(ProtocolItem *item, bool deleteItem = true); signals: void itemToClient(ProtocolItem *item); diff --git a/cockatrice/src/main.h b/cockatrice/src/main.h index 19a3ad52..c9d6ad04 100644 --- a/cockatrice/src/main.h +++ b/cockatrice/src/main.h @@ -9,7 +9,7 @@ extern CardDatabase *db; extern QTranslator *translator; const QString translationPrefix = "cockatrice"; -const QString versionString = "0.20110921"; +const QString versionString = "0.20111101"; void installNewTranslator(); diff --git a/cockatrice/src/tab_supervisor.h b/cockatrice/src/tab_supervisor.h index 0a38d5f8..a88e7384 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -59,6 +59,7 @@ public: void stop(); int getGameCount() const { return gameTabs.size(); } TabUserLists *getUserListsTab() const { return tabUserLists; } + ServerInfo_User *getUserInfo() const { return userInfo; } bool getAdminLocked() const; int getUserLevel() const; signals: diff --git a/cockatrice/src/userlist.cpp b/cockatrice/src/userlist.cpp index 0128b98d..6a2a28c3 100644 --- a/cockatrice/src/userlist.cpp +++ b/cockatrice/src/userlist.cpp @@ -13,24 +13,72 @@ #include #include #include +#include #include #include #include +#include +#include -BanDialog::BanDialog(QWidget *parent) +BanDialog::BanDialog(ServerInfo_User *info, QWidget *parent) : QDialog(parent) { - QLabel *durationLabel = new QLabel(tr("Please enter the duration of the ban (in minutes).\nEnter 0 for an indefinite ban.")); - durationEdit = new QSpinBox; - durationEdit->setMinimum(0); - durationEdit->setValue(5); - durationEdit->setMaximum(99999999); + setAttribute(Qt::WA_DeleteOnClose); + + nameBanCheckBox = new QCheckBox(tr("ban &user name")); + nameBanCheckBox->setChecked(true); + nameBanEdit = new QLineEdit(info->getName()); + ipBanCheckBox = new QCheckBox(tr("ban &IP address")); + ipBanCheckBox->setChecked(true); + ipBanEdit = new QLineEdit(info->getAddress()); + QGridLayout *banTypeGrid = new QGridLayout; + banTypeGrid->addWidget(nameBanCheckBox, 0, 0); + banTypeGrid->addWidget(nameBanEdit, 0, 1); + banTypeGrid->addWidget(ipBanCheckBox, 1, 0); + banTypeGrid->addWidget(ipBanEdit, 1, 1); + QGroupBox *banTypeGroupBox = new QGroupBox(tr("Ban type")); + banTypeGroupBox->setLayout(banTypeGrid); + + permanentRadio = new QRadioButton(tr("&permanent ban")); + temporaryRadio = new QRadioButton(tr("&temporary ban")); + temporaryRadio->setChecked(true); + connect(temporaryRadio, SIGNAL(toggled(bool)), this, SLOT(enableTemporaryEdits(bool))); + daysLabel = new QLabel(tr("&Days:")); + daysEdit = new QSpinBox; + daysEdit->setMinimum(0); + daysEdit->setValue(0); + daysEdit->setMaximum(10000); + daysLabel->setBuddy(daysEdit); + hoursLabel = new QLabel(tr("&Hours:")); + hoursEdit = new QSpinBox; + hoursEdit->setMinimum(0); + hoursEdit->setValue(0); + hoursEdit->setMaximum(24); + hoursLabel->setBuddy(hoursEdit); + minutesLabel = new QLabel(tr("&Minutes:")); + minutesEdit = new QSpinBox; + minutesEdit->setMinimum(0); + minutesEdit->setValue(5); + minutesEdit->setMaximum(60); + minutesLabel->setBuddy(minutesEdit); + QGridLayout *durationLayout = new QGridLayout; + durationLayout->addWidget(permanentRadio, 0, 0, 1, 6); + durationLayout->addWidget(temporaryRadio, 1, 0, 1, 6); + durationLayout->addWidget(daysLabel, 2, 0); + durationLayout->addWidget(daysEdit, 2, 1); + durationLayout->addWidget(hoursLabel, 2, 2); + durationLayout->addWidget(hoursEdit, 2, 3); + durationLayout->addWidget(minutesLabel, 2, 4); + durationLayout->addWidget(minutesEdit, 2, 5); + QGroupBox *durationGroupBox = new QGroupBox(tr("Duration of the ban")); + durationGroupBox->setLayout(durationLayout); + QLabel *reasonLabel = new QLabel(tr("Please enter the reason for the ban.\nThis is only saved for moderators and cannot be seen by the banned person.")); reasonEdit = new QPlainTextEdit; QPushButton *okButton = new QPushButton(tr("&OK")); okButton->setAutoDefault(true); - connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(okButton, SIGNAL(clicked()), this, SLOT(okClicked())); QPushButton *cancelButton = new QPushButton(tr("&Cancel")); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); @@ -40,8 +88,8 @@ BanDialog::BanDialog(QWidget *parent) buttonLayout->addWidget(cancelButton); QVBoxLayout *vbox = new QVBoxLayout; - vbox->addWidget(durationLabel); - vbox->addWidget(durationEdit); + vbox->addWidget(banTypeGroupBox); + vbox->addWidget(durationGroupBox); vbox->addWidget(reasonLabel); vbox->addWidget(reasonEdit); vbox->addLayout(buttonLayout); @@ -50,9 +98,38 @@ BanDialog::BanDialog(QWidget *parent) setWindowTitle(tr("Ban user from server")); } +void BanDialog::okClicked() +{ + if (!nameBanCheckBox->isChecked() && !ipBanCheckBox->isChecked()) { + QMessageBox::critical(this, tr("Error"), tr("You have to select a name-based or IP-based ban, or both.")); + return; + } + accept(); +} + +void BanDialog::enableTemporaryEdits(bool enabled) +{ + daysLabel->setEnabled(enabled); + daysEdit->setEnabled(enabled); + hoursLabel->setEnabled(enabled); + hoursEdit->setEnabled(enabled); + minutesLabel->setEnabled(enabled); + minutesEdit->setEnabled(enabled); +} + +QString BanDialog::getBanName() const +{ + return nameBanCheckBox->isChecked() ? nameBanEdit->text() : QString(); +} + +QString BanDialog::getBanIP() const +{ + return ipBanCheckBox->isChecked() ? ipBanEdit->text() : QString(); +} + int BanDialog::getMinutes() const { - return durationEdit->value(); + return permanentRadio->isChecked() ? 0 : (daysEdit->value() * 24 * 60 + hoursEdit->value() * 60 + minutesEdit->value()); } QString BanDialog::getReason() const @@ -242,6 +319,24 @@ void UserList::gamesOfUserReceived(ProtocolResponse *resp) selector->show(); } +void UserList::banUser_processUserInfoResponse(ProtocolResponse *r) +{ + Response_GetUserInfo *response = qobject_cast(r); + if (!response) + return; + + // The dialog needs to be non-modal in order to not block the event queue of the client. + BanDialog *dlg = new BanDialog(response->getUserInfo(), this); + connect(dlg, SIGNAL(accepted()), this, SLOT(banUser_dialogFinished())); + dlg->show(); +} + +void UserList::banUser_dialogFinished() +{ + BanDialog *dlg = static_cast(sender()); + client->sendCommand(new Command_BanFromServer(dlg->getBanName(), dlg->getBanIP(), dlg->getMinutes(), dlg->getReason())); +} + void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index) { const QString &userName = index.sibling(index.row(), 2).data(Qt::UserRole).toString(); @@ -279,6 +374,14 @@ void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index) menu->addSeparator(); menu->addAction(aBan); } + if (userName == tabSupervisor->getUserInfo()->getName()) { + aChat->setEnabled(false); + aAddToBuddyList->setEnabled(false); + aRemoveFromBuddyList->setEnabled(false); + aAddToIgnoreList->setEnabled(false); + aRemoveFromIgnoreList->setEnabled(false); + aBan->setEnabled(false); + } QAction *actionClicked = menu->exec(pos); if (actionClicked == aDetails) { @@ -300,9 +403,9 @@ void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index) else if (actionClicked == aRemoveFromIgnoreList) client->sendCommand(new Command_RemoveFromList("ignore", userName)); else if (actionClicked == aBan) { - BanDialog dlg(this); - if (dlg.exec()) - client->sendCommand(new Command_BanFromServer(userName, dlg.getMinutes(), dlg.getReason())); + Command_GetUserInfo *command = new Command_GetUserInfo(userName); + connect(command, SIGNAL(finished(ProtocolResponse *)), this, SLOT(banUser_processUserInfoResponse(ProtocolResponse *))); + client->sendCommand(command); } delete menu; diff --git a/cockatrice/src/userlist.h b/cockatrice/src/userlist.h index c71ddc31..45d9eb5f 100644 --- a/cockatrice/src/userlist.h +++ b/cockatrice/src/userlist.h @@ -10,17 +10,29 @@ class QTreeWidget; class ServerInfo_User; class AbstractClient; class TabSupervisor; +class QLabel; +class QCheckBox; class QSpinBox; +class QRadioButton; class QPlainTextEdit; class ProtocolResponse; class BanDialog : public QDialog { Q_OBJECT private: - QSpinBox *durationEdit; + QLabel *daysLabel, *hoursLabel, *minutesLabel; + QCheckBox *nameBanCheckBox, *ipBanCheckBox; + QLineEdit *nameBanEdit, *ipBanEdit; + QSpinBox *daysEdit, *hoursEdit, *minutesEdit; + QRadioButton *permanentRadio, *temporaryRadio; QPlainTextEdit *reasonEdit; +private slots: + void okClicked(); + void enableTemporaryEdits(bool enabled); public: - BanDialog(QWidget *parent = 0); + BanDialog(ServerInfo_User *info, QWidget *parent = 0); + QString getBanName() const; + QString getBanIP() const; int getMinutes() const; QString getReason() const; }; @@ -53,6 +65,8 @@ private: void setUserOnline(QTreeWidgetItem *user, bool online); private slots: void userClicked(QTreeWidgetItem *item, int column); + void banUser_processUserInfoResponse(ProtocolResponse *resp); + void banUser_dialogFinished(); void gamesOfUserReceived(ProtocolResponse *resp); signals: void openMessageDialog(const QString &userName, bool focus); diff --git a/common/decklist.cpp b/common/decklist.cpp index d0e0c29c..c4eefff7 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -565,7 +565,13 @@ void DeckList::updateDeckHash() } } cardList.sort(); - deckHash = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1).toBase64().left(10); + QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1); + quint64 number = (((quint64) (unsigned char) deckHashArray[0]) << 32) + + (((quint64) (unsigned char) deckHashArray[1]) << 24) + + (((quint64) (unsigned char) deckHashArray[2] << 16)) + + (((quint64) (unsigned char) deckHashArray[3]) << 8) + + (quint64) (unsigned char) deckHashArray[4]; + deckHash = QString::number(number, 32).rightJustified(8, '0'); emit deckHashChanged(); } diff --git a/common/protocol_datastructures.cpp b/common/protocol_datastructures.cpp index 5d3362f0..62f52182 100644 --- a/common/protocol_datastructures.cpp +++ b/common/protocol_datastructures.cpp @@ -12,22 +12,24 @@ CardToMove::CardToMove(int _cardId, bool _faceDown, const QString &_pt, bool _ta insertItem(new SerializableItem_Bool("tapped", _tapped)); } -ServerInfo_User::ServerInfo_User(const QString &_name, int _userLevel, const QString &_realName, Gender _gender, const QString &_country, const QByteArray &_avatarBmp) +ServerInfo_User::ServerInfo_User(const QString &_name, int _userLevel, const QString &_address, const QString &_realName, Gender _gender, const QString &_country, const QByteArray &_avatarBmp) : SerializableItem_Map("user") { insertItem(new SerializableItem_String("name", _name)); insertItem(new SerializableItem_Int("userlevel", _userLevel)); + insertItem(new SerializableItem_String("address", _address)); insertItem(new SerializableItem_String("real_name", _realName)); insertItem(new SerializableItem_Int("gender", _gender)); insertItem(new SerializableItem_String("country", _country)); insertItem(new SerializableItem_ByteArray("avatar_bmp", _avatarBmp)); } -ServerInfo_User::ServerInfo_User(const ServerInfo_User *other, bool complete) +ServerInfo_User::ServerInfo_User(const ServerInfo_User *other, bool complete, bool moderatorInfo) : SerializableItem_Map("user") { insertItem(new SerializableItem_String("name", other->getName())); insertItem(new SerializableItem_Int("userlevel", other->getUserLevel())); + insertItem(new SerializableItem_String("address", moderatorInfo ? other->getAddress() : QString())); insertItem(new SerializableItem_String("real_name", other->getRealName())); insertItem(new SerializableItem_Int("gender", other->getGender())); insertItem(new SerializableItem_String("country", other->getCountry())); diff --git a/common/protocol_datastructures.h b/common/protocol_datastructures.h index c2ff26ef..70522706 100644 --- a/common/protocol_datastructures.h +++ b/common/protocol_datastructures.h @@ -50,13 +50,15 @@ public: Male = 0, Female = 1 }; - ServerInfo_User(const QString &_name = QString(), int _userLevel = IsNothing, const QString &_realName = QString(), Gender _gender = GenderUnknown, const QString &_country = QString(), const QByteArray &_avatarBmp = QByteArray()); - ServerInfo_User(const ServerInfo_User *other, bool complete = true); + ServerInfo_User(const QString &_name = QString(), int _userLevel = IsNothing, const QString &_address = QString(), const QString &_realName = QString(), Gender _gender = GenderUnknown, const QString &_country = QString(), const QByteArray &_avatarBmp = QByteArray()); + ServerInfo_User(const ServerInfo_User *other, bool complete = true, bool moderatorInfo = false); static SerializableItem *newItem() { return new ServerInfo_User; } QString getName() const { return static_cast(itemMap.value("name"))->getData(); } void setName(const QString &_name) { static_cast(itemMap.value("name"))->setData(_name); } int getUserLevel() const { return static_cast(itemMap.value("userlevel"))->getData(); } void setUserLevel(int _userLevel) { static_cast(itemMap.value("userlevel"))->setData(_userLevel); } + QString getAddress() const { return static_cast(itemMap.value("address"))->getData(); } + void setAddress(const QString &_address) { static_cast(itemMap.value("address"))->setData(_address); } QString getRealName() const { return static_cast(itemMap.value("real_name"))->getData(); } Gender getGender() const { return static_cast(static_cast(itemMap.value("gender"))->getData()); } QString getCountry() const { return static_cast(itemMap.value("country"))->getData(); } diff --git a/common/protocol_items.cpp b/common/protocol_items.cpp index 0ea3e9b5..20edd389 100644 --- a/common/protocol_items.cpp +++ b/common/protocol_items.cpp @@ -489,10 +489,11 @@ Command_ShutdownServer::Command_ShutdownServer(const QString &_reason, int _minu insertItem(new SerializableItem_String("reason", _reason)); insertItem(new SerializableItem_Int("minutes", _minutes)); } -Command_BanFromServer::Command_BanFromServer(const QString &_userName, int _minutes, const QString &_reason) +Command_BanFromServer::Command_BanFromServer(const QString &_userName, const QString &_address, int _minutes, const QString &_reason) : ModeratorCommand("ban_from_server") { insertItem(new SerializableItem_String("user_name", _userName)); + insertItem(new SerializableItem_String("address", _address)); insertItem(new SerializableItem_Int("minutes", _minutes)); insertItem(new SerializableItem_String("reason", _reason)); } diff --git a/common/protocol_items.dat b/common/protocol_items.dat index 588097be..6c41af26 100644 --- a/common/protocol_items.dat +++ b/common/protocol_items.dat @@ -81,4 +81,4 @@ 6:mulligan:i,number 7:update_server_message 7:shutdown_server:s,reason:i,minutes -8:ban_from_server:s,user_name:i,minutes:s,reason \ No newline at end of file +8:ban_from_server:s,user_name:s,address:i,minutes:s,reason \ No newline at end of file diff --git a/common/protocol_items.h b/common/protocol_items.h index cf87d2fe..773e44bd 100644 --- a/common/protocol_items.h +++ b/common/protocol_items.h @@ -743,8 +743,9 @@ public: class Command_BanFromServer : public ModeratorCommand { Q_OBJECT public: - Command_BanFromServer(const QString &_userName = QString(), int _minutes = -1, const QString &_reason = QString()); + Command_BanFromServer(const QString &_userName = QString(), const QString &_address = QString(), int _minutes = -1, const QString &_reason = QString()); QString getUserName() const { return static_cast(itemMap.value("user_name"))->getData(); }; + QString getAddress() const { return static_cast(itemMap.value("address"))->getData(); }; int getMinutes() const { return static_cast(itemMap.value("minutes"))->getData(); }; QString getReason() const { return static_cast(itemMap.value("reason"))->getData(); }; static SerializableItem *newItem() { return new Command_BanFromServer; } diff --git a/common/server.cpp b/common/server.cpp index c09a38d6..d9e83a06 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -57,6 +57,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString return authState; ServerInfo_User *data = getUserData(name); + data->setAddress(session->getAddress()); name = data->getName(); // Compensate for case indifference if (authState == PasswordRight) { diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 44d4a4a2..759df264 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -402,7 +402,7 @@ ResponseCode Server_ProtocolHandler::cmdGetUserInfo(Command_GetUserInfo *cmd, Co Server_ProtocolHandler *handler = server->getUsers().value(cmd->getUserName()); if (!handler) return RespNameNotFound; - result = new ServerInfo_User(handler->getUserInfo()); + result = new ServerInfo_User(handler->getUserInfo(), true, userInfo->getUserLevel() & ServerInfo_User::IsModerator); } cont->setResponse(new Response_GetUserInfo(cont->getCmdId(), RespOk, result)); diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index 1902f1d8..c95f5094 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -106,6 +106,7 @@ public: bool getAcceptsUserListChanges() const { return acceptsUserListChanges; } bool getAcceptsRoomListChanges() const { return acceptsRoomListChanges; } ServerInfo_User *getUserInfo() const { return userInfo; } + virtual QString getAddress() const = 0; void setUserInfo(ServerInfo_User *_userInfo) { userInfo = _userInfo; } const QMap &getBuddyList() const { return buddyList; } const QMap &getIgnoreList() const { return ignoreList; } diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index 7bd64c58..acb0a7aa 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -105,7 +105,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_users` ( `name` varchar(35) NOT NULL, `realname` varchar(255) NOT NULL, `gender` char(1) NOT NULL, - `password` varchar(255) NOT NULL, + `password_sha512` char(120) NOT NULL, `email` varchar(255) NOT NULL, `country` char(2) NOT NULL, `avatar_bmp` blob NOT NULL, @@ -149,7 +149,8 @@ CREATE TABLE `cockatrice_buddylist` ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE `cockatrice_bans` ( - `id_user` int(7) unsigned zerofill NOT NULL, + `user_name` varchar(255) unsigned zerofill NOT NULL, + `ip_address` varchar(255) NOT NULL, `id_admin` int(7) unsigned zerofill NOT NULL, `time_from` datetime NOT NULL, `minutes` int(6) NOT NULL, diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 49dbf3fa..e29dd0bc 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -50,10 +50,6 @@ Servatrice::Servatrice(QSettings *_settings, 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(); serverId = settings->value("server/id", 0).toInt(); @@ -171,13 +167,6 @@ bool Servatrice::execSqlQuery(QSqlQuery &query) AuthenticationResult Servatrice::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password) { - serverMutex.lock(); - QHostAddress address = static_cast(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") @@ -185,8 +174,23 @@ AuthenticationResult Servatrice::checkUserPassword(Server_ProtocolHandler *handl else if (method == "sql") { checkSql(); + QSqlQuery ipBanQuery; + ipBanQuery.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.ip_address = :address) and b.ip_address = :address2"); + ipBanQuery.bindValue(":address", static_cast(handler)->getPeerAddress().toString()); + ipBanQuery.bindValue(":address2", static_cast(handler)->getPeerAddress().toString()); + if (!execSqlQuery(ipBanQuery)) { + qDebug("Login denied: SQL error"); + return PasswordWrong; + } + + if (ipBanQuery.next()) + if (ipBanQuery.value(0).toInt() || ipBanQuery.value(1).toInt()) { + qDebug("Login denied: banned by address"); + 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.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.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)) { qDebug("Login denied: SQL error"); @@ -195,7 +199,7 @@ AuthenticationResult Servatrice::checkUserPassword(Server_ProtocolHandler *handl if (query.next()) { if (query.value(1).toInt() || query.value(2).toInt()) { - qDebug("Login denied: banned"); + qDebug("Login denied: banned by name"); return PasswordWrong; } if (query.value(0).toString() == PasswordHasher::computeHash(password, query.value(0).toString().left(16))) { @@ -257,6 +261,7 @@ ServerInfo_User *Servatrice::evalUserQueryResult(const QSqlQuery &query, bool co return new ServerInfo_User( name, userLevel, + QString(), realName, gender, country, @@ -341,16 +346,6 @@ QMap Servatrice::getIgnoreList(const QString &name) return result; } -void Servatrice::updateBanTimer() -{ - QMutexLocker locker(&serverMutex); - for (int i = 0; i < addressBanList.size(); ) - if (--(addressBanList[i].second) <= 0) - addressBanList.removeAt(i); - else - ++i; -} - void Servatrice::updateLoginMessage() { QMutexLocker locker(&dbMutex); @@ -424,4 +419,4 @@ void Servatrice::shutdownTimeout() deleteLater(); } -const QString Servatrice::versionString = "Servatrice 0.20111004"; +const QString Servatrice::versionString = "Servatrice 0.20111101"; diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index 8dae2c91..8b397d1b 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -49,7 +49,6 @@ class Servatrice : public Server Q_OBJECT private slots: void statusUpdate(); - void updateBanTimer(); void shutdownTimeout(); public: QMutex dbMutex; @@ -75,13 +74,12 @@ public: int getUsersWithAddress(const QHostAddress &address) const; QMap getBuddyList(const QString &name); QMap getIgnoreList(const QString &name); - void addAddressBan(const QHostAddress &address, int minutes) { addressBanList.append(QPair(address, minutes)); } void scheduleShutdown(const QString &reason, int minutes); protected: bool userExists(const QString &user); AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password); private: - QTimer *pingClock, *statusUpdateClock, *banTimeoutClock; + QTimer *pingClock, *statusUpdateClock; QTcpServer *tcpServer; QString loginMessage; QString dbPrefix; @@ -89,7 +87,6 @@ private: int serverId; bool threaded; int uptime; - QList > addressBanList; 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 1f793fcd..25ac9be8 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -464,50 +464,43 @@ ResponseCode ServerSocketInterface::cmdDeckDownload(Command_DeckDownload *cmd, C // MODERATOR FUNCTIONS. // May be called by admins and moderators. Permission is checked by the calling function. -ResponseCode ServerSocketInterface::cmdUpdateServerMessage(Command_UpdateServerMessage * /*cmd*/, CommandContainer * /*cont*/) +ResponseCode ServerSocketInterface::cmdBanFromServer(Command_BanFromServer *cmd, CommandContainer * /*cont*/) { - servatrice->updateLoginMessage(); + QString userName = cmd->getUserName(); + QString address = cmd->getAddress(); + int minutes = cmd->getMinutes(); + + servatrice->dbMutex.lock(); + QSqlQuery query; + query.prepare("insert into " + servatrice->getDbPrefix() + "_bans (user_name, ip_address, id_admin, time_from, minutes, reason) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason)"); + query.bindValue(":user_name", userName); + query.bindValue(":ip_address", address); + query.bindValue(":id_admin", getUserIdInDB(userInfo->getName())); + query.bindValue(":minutes", minutes); + query.bindValue(":reason", cmd->getReason() + "\n"); + servatrice->execSqlQuery(query); + servatrice->dbMutex.unlock(); + + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); + if (user) { + user->sendProtocolItem(new Event_ConnectionClosed("banned")); + user->deleteLater(); + } + return RespOk; } // ADMIN FUNCTIONS. // Permission is checked by the calling function. +ResponseCode ServerSocketInterface::cmdUpdateServerMessage(Command_UpdateServerMessage * /*cmd*/, CommandContainer * /*cont*/) +{ + servatrice->updateLoginMessage(); + return RespOk; +} + 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(); - 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. - 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. - 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 14cf7030..fad934ea 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -76,6 +76,7 @@ public: ServerSocketInterface(Servatrice *_server, QTcpSocket *_socket, QObject *parent = 0); ~ServerSocketInterface(); QHostAddress getPeerAddress() const { return socket->peerAddress(); } + QString getAddress() const { return socket->peerAddress().toString(); } void sendProtocolItem(ProtocolItem *item, bool deleteItem = true); };