Merge branch 'master' into Customizable-shortcuts

This commit is contained in:
marco 2015-08-16 13:52:34 +02:00
commit be82472f35
42 changed files with 708 additions and 220 deletions

View file

@ -1,33 +1,29 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update --> <p align='center'><img src=https://cloud.githubusercontent.com/assets/9874850/7516775/b00b8e36-f4d1-11e4-8da4-3df294d01f86.png></p>
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Cockatrice](#cockatrice) ---
- [Get Involved [![Gitter chat](https://badges.gitter.im/Cockatrice/Cockatrice.png)](https://gitter.im/Cockatrice/Cockatrice)](#get-involved-)
- [Community Resources](#community-resources)
- [Translation Status [![Cockatrice on Transiflex](https://ds0k0en9abmn1.cloudfront.net/static/charts/images/tx-logo-micro.646b0065fce6.png)](https://www.transifex.com/projects/p/cockatrice/)](#translation-status-)
- [Building [![Build Status](https://travis-ci.org/Cockatrice/Cockatrice.svg?branch=master)](https://travis-ci.org/Cockatrice/Cockatrice)](#building-)
- [Building servatrice Docker container](#building-servatrice-docker-container)
- [Running](#running)
- [License](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update --> **Table of Contents** &nbsp;&nbsp; [Cockatrice](#cockatrice) | [Get Involved] (#get-involved-) | [Community](#community-resources) | [Translation](#translation-status-) | [Building](#building-) | [Running](#running) | [License](#license)
---
# Cockatrice # Cockatrice
Cockatrice is an open-source multiplatform software for playing card games, Cockatrice is an open-source multiplatform software for playing card games,
such as Magic: The Gathering, over a network. It is fully client-server based such as Magic: The Gathering, over a network. It is fully client-server based
to prevent any kind of cheating, though it supports single-player games without to prevent any kind of cheating, though it supports single-player games without
a network interface as well. Both client and server are written in Qt, supporting both Qt4 and Qt5. a network interface as well. Both client and server are written in Qt, supporting both Qt4 and Qt5.<br>
# Get Involved [![Gitter chat](https://badges.gitter.im/Cockatrice/Cockatrice.png)](https://gitter.im/Cockatrice/Cockatrice) # Get Involved [![Gitter chat](https://badges.gitter.im/Cockatrice/Cockatrice.png)](https://gitter.im/Cockatrice/Cockatrice)
Chat with the Cockatrice developers on Gitter. Come here to talk about the application, features, or just to hang out. For support regarding specific servers, please contact that server's admin or forum for support rather than asking here. Chat with the Cockatrice developers on Gitter. Come here to talk about the application, features, or just to hang out. For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.<br>
# Community Resources # Community Resources
- [reddit r/Cockatrice](http://reddit.com/r/cockatrice)
- [Woogerworks Server & Forums](http://www.woogerworks.com)
- [Cockatrice Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki) - [Cockatrice Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
- [reddit r/Cockatrice](http://reddit.com/r/cockatrice)
- [Woogerworks](http://www.woogerworks.com) / [Chickatrice] (http://www.chickatrice.net/) / [Poixen](http://www.poixen.com/) (incomplete Serverlist)<br>
# Translation Status [![Cockatrice on Transiflex](https://ds0k0en9abmn1.cloudfront.net/static/charts/images/tx-logo-micro.646b0065fce6.png)](https://www.transifex.com/projects/p/cockatrice/) # Translation Status [![Cockatrice on Transiflex](https://ds0k0en9abmn1.cloudfront.net/static/charts/images/tx-logo-micro.646b0065fce6.png)](https://www.transifex.com/projects/p/cockatrice/)
@ -38,26 +34,25 @@ Language statistics for `Cockatrice` *(on the left)* and `Oracle` *(on the right
[![Cockatrice translations](https://www.transifex.com/projects/p/cockatrice/resource/cockatrice/chart/image_png)](https://www.transifex.com/projects/p/cockatrice/resource/cockatrice/)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[![Oracle translations](https://www.transifex.com/projects/p/cockatrice/resource/oracle/chart/image_png)](https://www.transifex.com/projects/p/cockatrice/resource/oracle/) [![Cockatrice translations](https://www.transifex.com/projects/p/cockatrice/resource/cockatrice/chart/image_png)](https://www.transifex.com/projects/p/cockatrice/resource/cockatrice/)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[![Oracle translations](https://www.transifex.com/projects/p/cockatrice/resource/oracle/chart/image_png)](https://www.transifex.com/projects/p/cockatrice/resource/oracle/)
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information! Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information!<br>
# Building [![Build Status](https://travis-ci.org/Cockatrice/Cockatrice.svg?branch=master)](https://travis-ci.org/Cockatrice/Cockatrice) # Building [![Build Status](https://travis-ci.org/Cockatrice/Cockatrice.svg?branch=master)](https://travis-ci.org/Cockatrice/Cockatrice)
**Detailed compiling instructions are on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)** **Detailed compiling instructions are on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)**
Dependencies: Dependencies:
- [Qt](http://qt-project.org/) - [Qt](http://qt-project.org/)
- [protobuf](http://code.google.com/p/protobuf/) - [protobuf](http://code.google.com/p/protobuf/)
- [CMake](http://www.cmake.org/) - [CMake](http://www.cmake.org/)
Oracle can optionally use zlib to load zipped files: Oracle can optionally use zlib to load zipped files:
- [zlib](http://www.zlib.net/) - [zlib](http://www.zlib.net/)
The server requires an additional dependency when compiled under Qt4: The server requires an additional dependency when compiled under Qt4:
- [libgcrypt](http://www.gnu.org/software/libgcrypt/) - [libgcrypt](http://www.gnu.org/software/libgcrypt/)
To compile: To compile:
mkdir build mkdir build
@ -75,15 +70,18 @@ The following flags can be passed to `cmake`:
- `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings. - `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings.
- `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files. - `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files.
# Building servatrice Docker container #### Building servatrice Docker container
`docker build -t servatrice .` `docker build -t servatrice .`<br>
# Running # Running
`oracle` fetches card data `oracle` fetches card data
`cockatrice` is the game client `cockatrice` is the game client
`servatrice` is the server `servatrice` is the server<br>
# License # License
Cockatrice is free software, licensed under the GPLv2; see COPYING for details. Cockatrice is free software, licensed under the GPLv2; see COPYING for details.<br>

View file

@ -1,10 +1,14 @@
#include <QSettings> #include <QSettings>
#include <QLabel> #include <QLabel>
#include <QCheckBox> #include <QCheckBox>
#include <QComboBox>
#include <QRadioButton>
#include <QGridLayout> #include <QGridLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QDebug> #include <QDebug>
#include <QEvent>
#include <QKeyEvent>
#include <iostream> #include <iostream>
#include "dlg_connect.h" #include "dlg_connect.h"
@ -14,8 +18,24 @@ DlgConnect::DlgConnect(QWidget *parent)
QSettings settings; QSettings settings;
settings.beginGroup("server"); settings.beginGroup("server");
previousHostButton = new QRadioButton(tr("Previous Host"), this);
previousHosts = new QComboBox(this);
previousHosts->installEventFilter(new DeleteHighlightedItemWhenShiftDelPressedEventFilter);
QStringList previousHostList = settings.value("previoushosts").toStringList();
if (previousHostList.isEmpty()) {
previousHostList << "cockatrice.woogerworks.com";
previousHostList << "vps.poixen.com";
previousHostList << "chickatrice.net";
}
previousHosts->addItems(previousHostList);
previousHosts->setCurrentIndex(settings.value("previoushostindex").toInt());
newHostButton = new QRadioButton(tr("New Host"), this);
hostLabel = new QLabel(tr("&Host:")); hostLabel = new QLabel(tr("&Host:"));
hostEdit = new QLineEdit(settings.value("hostname", "cockatrice.woogerworks.com").toString()); hostEdit = new QLineEdit();
hostEdit->setPlaceholderText(tr("Enter host name"));
hostLabel->setBuddy(hostEdit); hostLabel->setBuddy(hostEdit);
portLabel = new QLabel(tr("&Port:")); portLabel = new QLabel(tr("&Port:"));
@ -48,16 +68,19 @@ DlgConnect::DlgConnect(QWidget *parent)
connect(savePasswordCheckBox, SIGNAL(stateChanged(int)), this, SLOT(passwordSaved(int))); connect(savePasswordCheckBox, SIGNAL(stateChanged(int)), this, SLOT(passwordSaved(int)));
QGridLayout *grid = new QGridLayout; QGridLayout *grid = new QGridLayout;
grid->addWidget(hostLabel, 0, 0); grid->addWidget(previousHostButton, 0, 1);
grid->addWidget(hostEdit, 0, 1); grid->addWidget(previousHosts, 1, 1);
grid->addWidget(portLabel, 1, 0); grid->addWidget(newHostButton, 2, 1);
grid->addWidget(portEdit, 1, 1); grid->addWidget(hostLabel, 3, 0);
grid->addWidget(playernameLabel, 2, 0); grid->addWidget(hostEdit, 3, 1);
grid->addWidget(playernameEdit, 2, 1); grid->addWidget(portLabel, 4, 0);
grid->addWidget(passwordLabel, 3, 0); grid->addWidget(portEdit, 4, 1);
grid->addWidget(passwordEdit, 3, 1); grid->addWidget(playernameLabel, 5, 0);
grid->addWidget(savePasswordCheckBox, 4, 0, 1, 2); grid->addWidget(playernameEdit, 5, 1);
grid->addWidget(autoConnectCheckBox, 5, 0, 1, 2); grid->addWidget(passwordLabel, 6, 0);
grid->addWidget(passwordEdit, 6, 1);
grid->addWidget(savePasswordCheckBox, 7, 0, 1, 2);
grid->addWidget(autoConnectCheckBox, 8, 0, 1, 2);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk()));
@ -71,8 +94,32 @@ DlgConnect::DlgConnect(QWidget *parent)
setWindowTitle(tr("Connect to server")); setWindowTitle(tr("Connect to server"));
setFixedHeight(sizeHint().height()); setFixedHeight(sizeHint().height());
setMinimumWidth(300); setMinimumWidth(300);
connect(previousHostButton, SIGNAL(toggled(bool)), this, SLOT(previousHostSelected(bool)));
connect(newHostButton, SIGNAL(toggled(bool)), this, SLOT(newHostSelected(bool)));
if (settings.value("previoushostlogin", 1).toInt())
previousHostButton->setChecked(true);
else
newHostButton->setChecked(true);
} }
void DlgConnect::previousHostSelected(bool state) {
if (state) {
hostEdit->setDisabled(true);
previousHosts->setDisabled(false);
}
}
void DlgConnect::newHostSelected(bool state) {
if (state) {
hostEdit->setDisabled(false);
previousHosts->setDisabled(true);
}
}
void DlgConnect::passwordSaved(int state) void DlgConnect::passwordSaved(int state)
{ {
Q_UNUSED(state); Q_UNUSED(state);
@ -88,17 +135,34 @@ void DlgConnect::actOk()
{ {
QSettings settings; QSettings settings;
settings.beginGroup("server"); settings.beginGroup("server");
settings.setValue("hostname", hostEdit->text());
settings.setValue("port", portEdit->text()); settings.setValue("port", portEdit->text());
settings.setValue("playername", playernameEdit->text()); settings.setValue("playername", playernameEdit->text());
settings.setValue("password", savePasswordCheckBox->isChecked() ? passwordEdit->text() : QString()); settings.setValue("password", savePasswordCheckBox->isChecked() ? passwordEdit->text() : QString());
settings.setValue("save_password", savePasswordCheckBox->isChecked() ? 1 : 0); settings.setValue("save_password", savePasswordCheckBox->isChecked() ? 1 : 0);
settings.setValue("auto_connect", autoConnectCheckBox->isChecked() ? 1 : 0); settings.setValue("auto_connect", autoConnectCheckBox->isChecked() ? 1 : 0);
settings.setValue("previoushostlogin", previousHostButton->isChecked() ? 1 : 0);
QStringList hostList;
if (newHostButton->isChecked())
if (!hostEdit->text().trimmed().isEmpty())
hostList << hostEdit->text();
for (int i = 0; i < previousHosts->count(); i++)
if(!previousHosts->itemText(i).trimmed().isEmpty())
hostList << previousHosts->itemText(i);
settings.setValue("previoushosts", hostList);
settings.setValue("previoushostindex", previousHosts->currentIndex());
settings.endGroup(); settings.endGroup();
accept(); accept();
} }
QString DlgConnect::getHost() const {
return previousHostButton->isChecked() ? previousHosts->currentText() : hostEdit->text();
}
void DlgConnect::actCancel() void DlgConnect::actCancel()
{ {
QSettings settings; QSettings settings;
@ -109,3 +173,17 @@ void DlgConnect::actCancel()
reject(); reject();
} }
bool DeleteHighlightedItemWhenShiftDelPressedEventFilter::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Delete) {
QComboBox* combobox = reinterpret_cast<QComboBox *>(obj);
combobox->removeItem(combobox->currentIndex());
return true;
}
}
return QObject::eventFilter(obj, event);
}

View file

@ -7,12 +7,22 @@
class QLabel; class QLabel;
class QPushButton; class QPushButton;
class QCheckBox; class QCheckBox;
class QComboBox;
class QRadioButton;
class DeleteHighlightedItemWhenShiftDelPressedEventFilter : public QObject
{
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
class DlgConnect : public QDialog { class DlgConnect : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
DlgConnect(QWidget *parent = 0); DlgConnect(QWidget *parent = 0);
QString getHost() const { return hostEdit->text(); } QString getHost() const;
int getPort() const { return portEdit->text().toInt(); } int getPort() const { return portEdit->text().toInt(); }
QString getPlayerName() const { return playernameEdit->text(); } QString getPlayerName() const { return playernameEdit->text(); }
QString getPassword() const { return passwordEdit->text(); } QString getPassword() const { return passwordEdit->text(); }
@ -20,10 +30,14 @@ private slots:
void actOk(); void actOk();
void actCancel(); void actCancel();
void passwordSaved(int state); void passwordSaved(int state);
void previousHostSelected(bool state);
void newHostSelected(bool state);
private: private:
QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel; QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel;
QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit; QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit;
QCheckBox *savePasswordCheckBox, *autoConnectCheckBox; QCheckBox *savePasswordCheckBox, *autoConnectCheckBox;
QComboBox *previousHosts;
QRadioButton *newHostButton, *previousHostButton;
}; };
#endif #endif

View file

@ -574,6 +574,9 @@ MessagesSettingsPage::MessagesSettingsPage()
{ {
chatMentionCheckBox.setChecked(settingsCache->getChatMention()); chatMentionCheckBox.setChecked(settingsCache->getChatMention());
connect(&chatMentionCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setChatMention(int))); connect(&chatMentionCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setChatMention(int)));
chatMentionCompleterCheckbox.setChecked(settingsCache->getChatMentionCompleter());
connect(&chatMentionCompleterCheckbox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setChatMentionCompleter(int)));
ignoreUnregUsersMainChat.setChecked(settingsCache->getIgnoreUnregisteredUsers()); ignoreUnregUsersMainChat.setChecked(settingsCache->getIgnoreUnregisteredUsers());
ignoreUnregUserMessages.setChecked(settingsCache->getIgnoreUnregisteredUserMessages()); ignoreUnregUserMessages.setChecked(settingsCache->getIgnoreUnregisteredUserMessages());
@ -606,11 +609,12 @@ MessagesSettingsPage::MessagesSettingsPage()
chatGrid->addWidget(&chatMentionCheckBox, 0, 0); chatGrid->addWidget(&chatMentionCheckBox, 0, 0);
chatGrid->addWidget(&invertMentionForeground, 0, 1); chatGrid->addWidget(&invertMentionForeground, 0, 1);
chatGrid->addWidget(mentionColor, 0, 2); chatGrid->addWidget(mentionColor, 0, 2);
chatGrid->addWidget(&ignoreUnregUsersMainChat, 1, 0); chatGrid->addWidget(&chatMentionCompleterCheckbox, 1, 0);
chatGrid->addWidget(&ignoreUnregUsersMainChat, 2, 0);
chatGrid->addWidget(&hexLabel, 1, 2); chatGrid->addWidget(&hexLabel, 1, 2);
chatGrid->addWidget(&ignoreUnregUserMessages, 2, 0); chatGrid->addWidget(&ignoreUnregUserMessages, 3, 0);
chatGrid->addWidget(&messagePopups, 3, 0); chatGrid->addWidget(&messagePopups, 4, 0);
chatGrid->addWidget(&mentionPopups, 4, 0); chatGrid->addWidget(&mentionPopups, 5, 0);
chatGroupBox = new QGroupBox; chatGroupBox = new QGroupBox;
chatGroupBox->setLayout(chatGrid); chatGroupBox->setLayout(chatGrid);
@ -735,6 +739,7 @@ void MessagesSettingsPage::retranslateUi()
chatGroupBox->setTitle(tr("Chat settings")); chatGroupBox->setTitle(tr("Chat settings"));
highlightGroupBox->setTitle(tr("Custom alert words")); highlightGroupBox->setTitle(tr("Custom alert words"));
chatMentionCheckBox.setText(tr("Enable chat mentions")); chatMentionCheckBox.setText(tr("Enable chat mentions"));
chatMentionCompleterCheckbox.setText(tr("Enable mention completer"));
messageShortcuts->setTitle(tr("In-game message macros")); messageShortcuts->setTitle(tr("In-game message macros"));
ignoreUnregUsersMainChat.setText(tr("Ignore chat room messages sent by unregistered users")); ignoreUnregUsersMainChat.setText(tr("Ignore chat room messages sent by unregistered users"));
ignoreUnregUserMessages.setText(tr("Ignore private messages sent by unregistered users")); ignoreUnregUserMessages.setText(tr("Ignore private messages sent by unregistered users"));

View file

@ -168,6 +168,7 @@ private:
QAction *aAdd; QAction *aAdd;
QAction *aRemove; QAction *aRemove;
QCheckBox chatMentionCheckBox; QCheckBox chatMentionCheckBox;
QCheckBox chatMentionCompleterCheckbox;
QCheckBox invertMentionForeground; QCheckBox invertMentionForeground;
QCheckBox invertHighlightForeground; QCheckBox invertHighlightForeground;
QCheckBox ignoreUnregUsersMainChat; QCheckBox ignoreUnregUsersMainChat;

View file

@ -33,7 +33,7 @@ ServerInfo_User LocalServer_DatabaseInterface::getUserData(const QString &name,
return result; return result;
} }
AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler * /* handler */, const QString & /* user */, const QString & /* password */, QString & /* reasonStr */, int & /* secondsLeft */) AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler * /* handler */, const QString & /* user */, const QString & /* password */, const QString & /* clientId */, QString & /* reasonStr */, int & /* secondsLeft */)
{ {
return UnknownUser; return UnknownUser;
} }

View file

@ -24,7 +24,7 @@ protected:
ServerInfo_User getUserData(const QString &name, bool withId = false); ServerInfo_User getUserData(const QString &name, bool withId = false);
public: public:
LocalServer_DatabaseInterface(LocalServer *_localServer); LocalServer_DatabaseInterface(LocalServer *_localServer);
AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft); AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &secondsLeft);
int getNextGameId() { return localServer->getNextLocalGameId(); } int getNextGameId() { return localServer->getNextLocalGameId(); }
int getNextReplayId() { return -1; } int getNextReplayId() { return -1; }
int getActiveUserCount() { return 0; } int getActiveUserCount() { return 0; }

View file

@ -222,8 +222,8 @@ int main(int argc, char *argv[])
settingsCache->setClientID(generateClientID()); settingsCache->setClientID(generateClientID());
qDebug() << "ClientID In Cache: " << settingsCache->getClientID(); qDebug() << "ClientID In Cache: " << settingsCache->getClientID();
ui.showMaximized(); ui.show();
qDebug("main(): ui.showMaximized() finished"); qDebug("main(): ui.show() finished");
app.exec(); app.exec();
} }

View file

@ -11,6 +11,7 @@
#include "pb/server_message.pb.h" #include "pb/server_message.pb.h"
#include "pb/event_server_identification.pb.h" #include "pb/event_server_identification.pb.h"
#include "settingscache.h" #include "settingscache.h"
#include "main.h"
static const unsigned int protocolVersion = 14; static const unsigned int protocolVersion = 14;
@ -78,6 +79,7 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica
cmdRegister.set_gender((ServerInfo_User_Gender) gender); cmdRegister.set_gender((ServerInfo_User_Gender) gender);
cmdRegister.set_country(country.toStdString()); cmdRegister.set_country(country.toStdString());
cmdRegister.set_real_name(realName.toStdString()); cmdRegister.set_real_name(realName.toStdString());
cmdRegister.set_clientid(settingsCache->getClientID().toStdString());
PendingCommand *pend = prepareSessionCommand(cmdRegister); PendingCommand *pend = prepareSessionCommand(cmdRegister);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(registerResponse(Response))); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(registerResponse(Response)));

View file

@ -78,6 +78,7 @@ SettingsCache::SettingsCache()
minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 5).toInt(); minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 5).toInt();
tapAnimation = settings->value("cards/tapanimation", true).toBool(); tapAnimation = settings->value("cards/tapanimation", true).toBool();
chatMention = settings->value("chat/mention", true).toBool(); chatMention = settings->value("chat/mention", true).toBool();
chatMentionCompleter = settings->value("chat/mentioncompleter", true).toBool();
chatMentionForeground = settings->value("chat/mentionforeground", true).toBool(); chatMentionForeground = settings->value("chat/mentionforeground", true).toBool();
chatHighlightForeground = settings->value("chat/highlightforeground", true).toBool(); chatHighlightForeground = settings->value("chat/highlightforeground", true).toBool();
chatMentionColor = settings->value("chat/mentioncolor", "A6120D").toString(); chatMentionColor = settings->value("chat/mentioncolor", "A6120D").toString();
@ -361,6 +362,13 @@ void SettingsCache::setChatMention(int _chatMention) {
settings->setValue("chat/mention", chatMention); settings->setValue("chat/mention", chatMention);
} }
void SettingsCache::setChatMentionCompleter(const int _enableMentionCompleter)
{
chatMentionCompleter = _enableMentionCompleter;
settings->setValue("chat/mentioncompleter", chatMentionCompleter);
emit chatMentionCompleterChanged();
}
void SettingsCache::setChatMentionForeground(int _chatMentionForeground) { void SettingsCache::setChatMentionForeground(int _chatMentionForeground) {
chatMentionForeground = _chatMentionForeground; chatMentionForeground = _chatMentionForeground;
settings->setValue("chat/mentionforeground", chatMentionForeground); settings->setValue("chat/mentionforeground", chatMentionForeground);

View file

@ -44,6 +44,7 @@ signals:
void ignoreUnregisteredUserMessagesChanged(); void ignoreUnregisteredUserMessagesChanged();
void pixmapCacheSizeChanged(int newSizeInMBs); void pixmapCacheSizeChanged(int newSizeInMBs);
void masterVolumeChanged(int value); void masterVolumeChanged(int value);
void chatMentionCompleterChanged();
private: private:
QSettings *settings; QSettings *settings;
ShortcutsSettings *shortcutsSettings; ShortcutsSettings *shortcutsSettings;
@ -66,6 +67,7 @@ private:
int minPlayersForMultiColumnLayout; int minPlayersForMultiColumnLayout;
bool tapAnimation; bool tapAnimation;
bool chatMention; bool chatMention;
bool chatMentionCompleter;
QString chatMentionColor; QString chatMentionColor;
QString chatHighlightColor; QString chatHighlightColor;
bool chatMentionForeground; bool chatMentionForeground;
@ -137,6 +139,7 @@ public:
int getMinPlayersForMultiColumnLayout() const { return minPlayersForMultiColumnLayout; } int getMinPlayersForMultiColumnLayout() const { return minPlayersForMultiColumnLayout; }
bool getTapAnimation() const { return tapAnimation; } bool getTapAnimation() const { return tapAnimation; }
bool getChatMention() const { return chatMention; } bool getChatMention() const { return chatMention; }
bool getChatMentionCompleter() const { return chatMentionCompleter; }
bool getChatMentionForeground() const { return chatMentionForeground; } bool getChatMentionForeground() const { return chatMentionForeground; }
bool getChatHighlightForeground() const { return chatHighlightForeground; } bool getChatHighlightForeground() const { return chatHighlightForeground; }
bool getZoneViewSortByName() const { return zoneViewSortByName; } bool getZoneViewSortByName() const { return zoneViewSortByName; }
@ -220,6 +223,7 @@ public slots:
void setMinPlayersForMultiColumnLayout(int _minPlayersForMultiColumnLayout); void setMinPlayersForMultiColumnLayout(int _minPlayersForMultiColumnLayout);
void setTapAnimation(int _tapAnimation); void setTapAnimation(int _tapAnimation);
void setChatMention(int _chatMention); void setChatMention(int _chatMention);
void setChatMentionCompleter(int _chatMentionCompleter);
void setChatMentionForeground(int _chatMentionForeground); void setChatMentionForeground(int _chatMentionForeground);
void setChatHighlightForeground(int _chatHighlightForeground); void setChatHighlightForeground(int _chatHighlightForeground);
void setZoneViewSortByName(int _zoneViewSortByName); void setZoneViewSortByName(int _zoneViewSortByName);

View file

@ -1,5 +1,6 @@
#include "shortcutssettings.h" #include "shortcutssettings.h"
#include <QFile> #include <QFile>
#include <QStringList>
ShortcutsSettings::ShortcutsSettings(QString settingsPath, QObject *parent) : QObject(parent) ShortcutsSettings::ShortcutsSettings(QString settingsPath, QObject *parent) : QObject(parent)
{ {

View file

@ -21,6 +21,8 @@
#include <QTimer> #include <QTimer>
#include <QDockWidget> #include <QDockWidget>
#include <QPushButton> #include <QPushButton>
#include <QDir>
#include <QDesktopServices>
#include "tab_deck_editor.h" #include "tab_deck_editor.h"
#include "window_sets.h" #include "window_sets.h"
#include "carddatabase.h" #include "carddatabase.h"
@ -363,6 +365,7 @@ void TabDeckEditor::createMenus()
#if defined(Q_OS_WIN) || defined(Q_OS_MAC) #if defined(Q_OS_WIN) || defined(Q_OS_MAC)
dbMenu->addSeparator(); dbMenu->addSeparator();
dbMenu->addAction(aOpenCustomFolder); dbMenu->addAction(aOpenCustomFolder);
dbMenu->addAction(aOpenCustomsetsFolder);
#endif #endif
addTabMenu(dbMenu); addTabMenu(dbMenu);
} }
@ -573,6 +576,7 @@ void TabDeckEditor::retranslateUi()
aPrintDeck->setText(tr("&Print deck...")); aPrintDeck->setText(tr("&Print deck..."));
aAnalyzeDeck->setText(tr("&Analyze deck on deckstats.net")); aAnalyzeDeck->setText(tr("&Analyze deck on deckstats.net"));
aOpenCustomFolder->setText(tr("Open custom image folder")); aOpenCustomFolder->setText(tr("Open custom image folder"));
aOpenCustomsetsFolder->setText(tr("Open custom sets folder"));
aClose->setText(tr("&Close")); aClose->setText(tr("&Close"));
aClose->setShortcuts(settingsCache->shortcuts().getShortcut( aClose->setShortcuts(settingsCache->shortcuts().getShortcut(
"TabDeckEditor/aClose", "TabDeckEditor/aClose",
@ -825,6 +829,33 @@ void TabDeckEditor::actOpenCustomFolder() {
} }
void TabDeckEditor::actOpenCustomsetsFolder() {
#if QT_VERSION < 0x050000
QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
#else
QString dataDir = QStandardPaths::standardLocations(QStandardPaths::DataLocation).first();
#endif
#if defined(Q_OS_MAC)
QStringList scriptArgs;
scriptArgs << QLatin1String("-e");
scriptArgs << QString::fromLatin1("tell application \"Finder\" to open POSIX file \"%1\"").arg(dataDir + "/customsets/");
scriptArgs << QLatin1String("-e");
scriptArgs << QLatin1String("tell application \"Finder\" to activate");
QProcess::execute("/usr/bin/osascript", scriptArgs);
#endif
#if defined(Q_OS_WIN)
QStringList args;
dataDir.append("/customsets");
args << QDir::toNativeSeparators(dataDir);
aOpenCustomsetsFolder->setText(dataDir);
QProcess::startDetached("explorer", args);
#endif
}
void TabDeckEditor::actEditSets() void TabDeckEditor::actEditSets()
{ {
WndSets *w = new WndSets; WndSets *w = new WndSets;

View file

@ -54,6 +54,7 @@ class TabDeckEditor : public Tab {
void actPrintDeck(); void actPrintDeck();
void actAnalyzeDeck(); void actAnalyzeDeck();
void actOpenCustomFolder(); void actOpenCustomFolder();
void actOpenCustomsetsFolder();
void actEditSets(); void actEditSets();
void actEditTokens(); void actEditTokens();
@ -112,7 +113,7 @@ private:
QWidget *filterBox; QWidget *filterBox;
QMenu *deckMenu, *dbMenu; QMenu *deckMenu, *dbMenu;
QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aAnalyzeDeck, *aClose, *aOpenCustomFolder; QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aAnalyzeDeck, *aClose, *aOpenCustomFolder, *aOpenCustomsetsFolder;
QAction *aEditSets, *aEditTokens, *aClearFilterAll, *aClearFilterOne; QAction *aEditSets, *aEditTokens, *aClearFilterAll, *aClearFilterOne;
QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement;// *aUpdatePrices; QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement;// *aUpdatePrices;
QAction *aResetLayout; QAction *aResetLayout;

View file

@ -11,6 +11,14 @@
#include <QSplitter> #include <QSplitter>
#include <QApplication> #include <QApplication>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include <QCompleter>
#include <QWidget>
#include <QTextCursor>
#include <QAbstractItemView>
#include <QScrollBar>
#include <QStringListModel>
#include <QKeyEvent>
#include <QFocusEvent>
#include "tab_supervisor.h" #include "tab_supervisor.h"
#include "tab_room.h" #include "tab_room.h"
#include "tab_userlists.h" #include "tab_userlists.h"
@ -51,8 +59,9 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI
connect(chatView, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString))); connect(chatView, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(chatView, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString))); connect(chatView, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
connect(chatView, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString))); connect(chatView, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString)));
connect(settingsCache, SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged()));
sayLabel = new QLabel; sayLabel = new QLabel;
sayEdit = new QLineEdit; sayEdit = new CustomLineEdit;
sayLabel->setBuddy(sayEdit); sayLabel->setBuddy(sayEdit);
connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage())); connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage()));
@ -103,13 +112,26 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI
setLayout(hbox); setLayout(hbox);
const int userListSize = info.user_list_size(); const int userListSize = info.user_list_size();
for (int i = 0; i < userListSize; ++i) for (int i = 0; i < userListSize; ++i){
userList->processUserInfo(info.user_list(i), true); userList->processUserInfo(info.user_list(i), true);
autocompleteUserList.append("@" + QString::fromStdString(info.user_list(i).name()));
}
userList->sortItems(); userList->sortItems();
const int gameListSize = info.game_list_size(); const int gameListSize = info.game_list_size();
for (int i = 0; i < gameListSize; ++i) for (int i = 0; i < gameListSize; ++i)
gameSelector->processGameInfo(info.game_list(i)); gameSelector->processGameInfo(info.game_list(i));
completer = new QCompleter(autocompleteUserList, sayEdit);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setMaxVisibleItems(5);
#if QT_VERSION >= 0x050000
completer->setFilterMode(Qt::MatchStartsWith);
#endif
sayEdit->setCompleter(completer);
actCompleterChanged();
} }
TabRoom::~TabRoom() TabRoom::~TabRoom()
@ -119,7 +141,7 @@ TabRoom::~TabRoom()
void TabRoom::retranslateUi() void TabRoom::retranslateUi()
{ {
gameSelector->retranslateUi(); gameSelector->retranslateUi();
chatView->retranslateUi(); chatView->retranslateUi();
userList->retranslateUi(); userList->retranslateUi();
sayLabel->setText(tr("&Say:")); sayLabel->setText(tr("&Say:"));
@ -166,16 +188,20 @@ QString TabRoom::sanitizeHtml(QString dirty) const
void TabRoom::sendMessage() void TabRoom::sendMessage()
{ {
if (sayEdit->text().isEmpty()) if (sayEdit->text().isEmpty()){
return; return;
}else if (completer->popup()->isVisible()){
completer->popup()->hide();
return;
}else{
Command_RoomSay cmd;
cmd.set_message(sayEdit->text().toStdString());
Command_RoomSay cmd; PendingCommand *pend = prepareRoomCommand(cmd);
cmd.set_message(sayEdit->text().toStdString()); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(sayFinished(const Response &)));
sendRoomCommand(pend);
PendingCommand *pend = prepareRoomCommand(cmd); sayEdit->clear();
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(sayFinished(const Response &))); }
sendRoomCommand(pend);
sayEdit->clear();
} }
void TabRoom::sayFinished(const Response &response) void TabRoom::sayFinished(const Response &response)
@ -200,6 +226,11 @@ void TabRoom::actOpenChatSettings() {
settings.exec(); settings.exec();
} }
void TabRoom::actCompleterChanged()
{
settingsCache->getChatMentionCompleter() ? completer->setCompletionRole(2) : completer->setCompletionRole(1);
}
void TabRoom::processRoomEvent(const RoomEvent &event) void TabRoom::processRoomEvent(const RoomEvent &event)
{ {
switch (static_cast<RoomEvent::RoomEventType>(getPbExtension(event))) { switch (static_cast<RoomEvent::RoomEventType>(getPbExtension(event))) {
@ -222,11 +253,17 @@ void TabRoom::processJoinRoomEvent(const Event_JoinRoom &event)
{ {
userList->processUserInfo(event.user_info(), true); userList->processUserInfo(event.user_info(), true);
userList->sortItems(); userList->sortItems();
if (!autocompleteUserList.contains("@" + QString::fromStdString(event.user_info().name()))){
autocompleteUserList << "@" + QString::fromStdString(event.user_info().name());
sayEdit->updateCompleterModel(autocompleteUserList);
}
} }
void TabRoom::processLeaveRoomEvent(const Event_LeaveRoom &event) void TabRoom::processLeaveRoomEvent(const Event_LeaveRoom &event)
{ {
userList->deleteUser(QString::fromStdString(event.name())); userList->deleteUser(QString::fromStdString(event.name()));
autocompleteUserList.removeOne("@" + QString::fromStdString(event.name()));
sayEdit->updateCompleterModel(autocompleteUserList);
} }
void TabRoom::processRoomSayEvent(const Event_RoomSay &event) void TabRoom::processRoomSayEvent(const Event_RoomSay &event)
@ -259,3 +296,124 @@ void TabRoom::sendRoomCommand(PendingCommand *pend)
{ {
client->sendCommand(pend); client->sendCommand(pend);
} }
CustomLineEdit::CustomLineEdit(QWidget *parent)
: QLineEdit(parent)
{
}
void CustomLineEdit::focusOutEvent(QFocusEvent * e){
QLineEdit::focusOutEvent(e);
if (c->popup()->isVisible()){
//Remove Popup
c->popup()->hide();
//Truncate the line to last space or whole string
QString textValue = text();
int lastIndex = textValue.length();
int lastWordStartIndex = textValue.lastIndexOf(" ") + 1;
int leftShift = qMin(lastIndex, lastWordStartIndex);
setText(textValue.left(leftShift));
//Insert highlighted line from popup
insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " ");
//Set focus back to the textbox since tab was pressed
setFocus();
}
}
void CustomLineEdit::keyPressEvent(QKeyEvent * event)
{
switch (event->key()){
case Qt::Key_Return:
case Qt::Key_Enter:
case Qt::Key_Escape:
if (c->popup()->isVisible()){
event->ignore();
//Remove Popup
c->popup()->hide();
//Truncate the line to last space or whole string
QString textValue = text();
int lastIndexof = textValue.lastIndexOf(" ");
QString finalString = textValue.left(lastIndexof);
//Add a space if there's a word
if (finalString != "")
finalString += " ";
setText(finalString);
return;
}
break;
case Qt::Key_Space:
if (c->popup()->isVisible()){
event->ignore();
//Remove Popup
c->popup()->hide();
//Truncate the line to last space or whole string
QString textValue = text();
int lastIndex = textValue.length();
int lastWordStartIndex = textValue.lastIndexOf(" ") + 1;
int leftShift = qMin(lastIndex, lastWordStartIndex);
setText(textValue.left(leftShift));
//Insert highlighted line from popup
insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " ");
return;
}
break;
default:
break;
}
QLineEdit::keyPressEvent(event);
//Wait until the first character after @
if (!c || text().right(1).contains("@"))
return;
//Set new completion prefix
c->setCompletionPrefix(cursorWord(text()));
if (c->completionPrefix().length() < 1){
c->popup()->hide();
return;
}
//Draw completion box
QRect cr = cursorRect();
cr.setWidth(c->popup()->sizeHintForColumn(0) + c->popup()->verticalScrollBar()->sizeHint().width());
c->complete(cr);
//Select first item in the completion popup
QItemSelectionModel* sm = new QItemSelectionModel(c->completionModel());
c->popup()->setSelectionModel(sm);
sm->select(c->completionModel()->index(0, 0), QItemSelectionModel::ClearAndSelect);
sm->setCurrentIndex(c->completionModel()->index(0, 0), QItemSelectionModel::NoUpdate);
}
QString CustomLineEdit::cursorWord(const QString &line) const
{
return line.mid(line.left(cursorPosition()).lastIndexOf(" ") + 1,
cursorPosition() - line.left(cursorPosition()).lastIndexOf(" ") - 1);
}
void CustomLineEdit::insertCompletion(QString arg)
{
QString s_arg = arg + " ";
setText(text().replace(text().left(cursorPosition()).lastIndexOf(" ") + 1,
cursorPosition() - text().left(cursorPosition()).lastIndexOf(" ") - 1, s_arg));
}
void CustomLineEdit::setCompleter(QCompleter* completer)
{
c = completer;
c->setWidget(this);
connect(c, SIGNAL(activated(QString)),this, SLOT(insertCompletion(QString)));
}
void CustomLineEdit::updateCompleterModel(QStringList completionList)
{
if (!c || c->popup()->isVisible())
return;
QStringListModel *model;
model = (QStringListModel*)(c->model());
if (model == NULL)
model = new QStringListModel();
QStringList updatedList = completionList;
model->setStringList(updatedList);
}

View file

@ -4,6 +4,9 @@
#include "tab.h" #include "tab.h"
#include <QGroupBox> #include <QGroupBox>
#include <QMap> #include <QMap>
#include <QLineEdit>
#include <QKeyEvent>
#include <QFocusEvent>
namespace google { namespace protobuf { class Message; } } namespace google { namespace protobuf { class Message; } }
class AbstractClient; class AbstractClient;
@ -13,6 +16,7 @@ class ChatView;
class QLineEdit; class QLineEdit;
class QPushButton; class QPushButton;
class QTextTable; class QTextTable;
class QCompleter;
class RoomEvent; class RoomEvent;
class ServerInfo_Room; class ServerInfo_Room;
class ServerInfo_Game; class ServerInfo_Game;
@ -24,6 +28,7 @@ class GameSelector;
class Response; class Response;
class PendingCommand; class PendingCommand;
class ServerInfo_User; class ServerInfo_User;
class CustomLineEdit;
class TabRoom : public Tab { class TabRoom : public Tab {
Q_OBJECT Q_OBJECT
@ -38,14 +43,17 @@ private:
UserList *userList; UserList *userList;
ChatView *chatView; ChatView *chatView;
QLabel *sayLabel; QLabel *sayLabel;
QLineEdit *sayEdit; CustomLineEdit *sayEdit;
QGroupBox *chatGroupBox; QGroupBox *chatGroupBox;
QMenu *roomMenu; QMenu *roomMenu;
QAction *aLeaveRoom; QAction *aLeaveRoom;
QAction *aOpenChatSettings; QAction *aOpenChatSettings;
QAction * aClearChat; QAction *aClearChat;
QString sanitizeHtml(QString dirty) const; QString sanitizeHtml(QString dirty) const;
QStringList autocompleteUserList;
QCompleter *completer;
signals: signals:
void roomClosing(TabRoom *tab); void roomClosing(TabRoom *tab);
void openMessageDialog(const QString &userName, bool focus); void openMessageDialog(const QString &userName, bool focus);
@ -59,7 +67,8 @@ private slots:
void addMentionTag(QString mentionTag); void addMentionTag(QString mentionTag);
void focusTab(); void focusTab();
void actShowMentionPopup(QString &sender); void actShowMentionPopup(QString &sender);
void actCompleterChanged();
void processListGamesEvent(const Event_ListGames &event); void processListGamesEvent(const Event_ListGames &event);
void processJoinRoomEvent(const Event_JoinRoom &event); void processJoinRoomEvent(const Event_JoinRoom &event);
void processLeaveRoomEvent(const Event_LeaveRoom &event); void processLeaveRoomEvent(const Event_LeaveRoom &event);
@ -81,4 +90,21 @@ public:
void sendRoomCommand(PendingCommand *pend); void sendRoomCommand(PendingCommand *pend);
}; };
class CustomLineEdit : public QLineEdit
{
Q_OBJECT
private:
QString cursorWord(const QString& line) const;
QCompleter* c;
private slots:
void insertCompletion(QString);
protected:
void keyPressEvent(QKeyEvent * event);
void focusOutEvent(QFocusEvent * e);
public:
explicit CustomLineEdit(QWidget *parent = 0);
void setCompleter(QCompleter*);
void updateCompleterModel(QStringList);
};
#endif #endif

View file

@ -99,6 +99,7 @@ void UserContextMenu::banUser_dialogFinished()
cmd.set_minutes(dlg->getMinutes()); cmd.set_minutes(dlg->getMinutes());
cmd.set_reason(dlg->getReason().toStdString()); cmd.set_reason(dlg->getReason().toStdString());
cmd.set_visible_reason(dlg->getVisibleReason().toStdString()); cmd.set_visible_reason(dlg->getVisibleReason().toStdString());
cmd.set_clientid(dlg->getBanId().toStdString());
client->sendCommand(client->prepareModeratorCommand(cmd)); client->sendCommand(client->prepareModeratorCommand(cmd));
} }

View file

@ -36,11 +36,19 @@ BanDialog::BanDialog(const ServerInfo_User &info, QWidget *parent)
ipBanCheckBox = new QCheckBox(tr("ban &IP address")); ipBanCheckBox = new QCheckBox(tr("ban &IP address"));
ipBanCheckBox->setChecked(true); ipBanCheckBox->setChecked(true);
ipBanEdit = new QLineEdit(QString::fromStdString(info.address())); ipBanEdit = new QLineEdit(QString::fromStdString(info.address()));
idBanCheckBox = new QCheckBox(tr("ban client I&D"));
idBanCheckBox->setChecked(true);
idBanEdit = new QLineEdit(QString::fromStdString(info.clientid()));
if (QString::fromStdString(info.clientid()).isEmpty())
idBanCheckBox->setChecked(false);
QGridLayout *banTypeGrid = new QGridLayout; QGridLayout *banTypeGrid = new QGridLayout;
banTypeGrid->addWidget(nameBanCheckBox, 0, 0); banTypeGrid->addWidget(nameBanCheckBox, 0, 0);
banTypeGrid->addWidget(nameBanEdit, 0, 1); banTypeGrid->addWidget(nameBanEdit, 0, 1);
banTypeGrid->addWidget(ipBanCheckBox, 1, 0); banTypeGrid->addWidget(ipBanCheckBox, 1, 0);
banTypeGrid->addWidget(ipBanEdit, 1, 1); banTypeGrid->addWidget(ipBanEdit, 1, 1);
banTypeGrid->addWidget(idBanCheckBox, 2, 0);
banTypeGrid->addWidget(idBanEdit, 2, 1);
QGroupBox *banTypeGroupBox = new QGroupBox(tr("Ban type")); QGroupBox *banTypeGroupBox = new QGroupBox(tr("Ban type"));
banTypeGroupBox->setLayout(banTypeGrid); banTypeGroupBox->setLayout(banTypeGrid);
@ -110,8 +118,8 @@ BanDialog::BanDialog(const ServerInfo_User &info, QWidget *parent)
void BanDialog::okClicked() void BanDialog::okClicked()
{ {
if (!nameBanCheckBox->isChecked() && !ipBanCheckBox->isChecked()) { if (!nameBanCheckBox->isChecked() && !ipBanCheckBox->isChecked() && !idBanCheckBox->isChecked()) {
QMessageBox::critical(this, tr("Error"), tr("You have to select a name-based or IP-based ban, or both.")); QMessageBox::critical(this, tr("Error"), tr("You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban."));
return; return;
} }
accept(); accept();
@ -127,6 +135,11 @@ void BanDialog::enableTemporaryEdits(bool enabled)
minutesEdit->setEnabled(enabled); minutesEdit->setEnabled(enabled);
} }
QString BanDialog::getBanId() const
{
return idBanCheckBox->isChecked() ? idBanEdit->text() : QString();
}
QString BanDialog::getBanName() const QString BanDialog::getBanName() const
{ {
return nameBanCheckBox->isChecked() ? nameBanEdit->text() : QString(); return nameBanCheckBox->isChecked() ? nameBanEdit->text() : QString();

View file

@ -24,8 +24,8 @@ class BanDialog : public QDialog {
Q_OBJECT Q_OBJECT
private: private:
QLabel *daysLabel, *hoursLabel, *minutesLabel; QLabel *daysLabel, *hoursLabel, *minutesLabel;
QCheckBox *nameBanCheckBox, *ipBanCheckBox; QCheckBox *nameBanCheckBox, *ipBanCheckBox, *idBanCheckBox;
QLineEdit *nameBanEdit, *ipBanEdit; QLineEdit *nameBanEdit, *ipBanEdit, *idBanEdit;
QSpinBox *daysEdit, *hoursEdit, *minutesEdit; QSpinBox *daysEdit, *hoursEdit, *minutesEdit;
QRadioButton *permanentRadio, *temporaryRadio; QRadioButton *permanentRadio, *temporaryRadio;
QPlainTextEdit *reasonEdit, *visibleReasonEdit; QPlainTextEdit *reasonEdit, *visibleReasonEdit;
@ -36,6 +36,7 @@ public:
BanDialog(const ServerInfo_User &info, QWidget *parent = 0); BanDialog(const ServerInfo_User &info, QWidget *parent = 0);
QString getBanName() const; QString getBanName() const;
QString getBanIP() const; QString getBanIP() const;
QString getBanId() const;
int getMinutes() const; int getMinutes() const;
QString getReason() const; QString getReason() const;
QString getVisibleReason() const; QString getVisibleReason() const;

View file

@ -319,6 +319,9 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32
case Response::RespClientIdRequired: case Response::RespClientIdRequired:
QMessageBox::critical(this, tr("Error"), tr("This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client.\nPlease close and reopen your client to try again.")); QMessageBox::critical(this, tr("Error"), tr("This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client.\nPlease close and reopen your client to try again."));
break; break;
case Response::RespContextError:
QMessageBox::critical(this, tr("Error"), tr("An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider."));
break;
case Response::RespAccountNotActivated: { case Response::RespAccountNotActivated: {
bool ok = false; bool ok = false;
QString token = QInputDialog::getText(this, tr("Account activation"), tr("Your account has not been activated yet.\nYou need to provide the activation token received in the activation email"), QLineEdit::Normal, QString(), &ok); QString token = QInputDialog::getText(this, tr("Account activation"), tr("Your account has not been activated yet.\nYou need to provide the activation token received in the activation email"), QLineEdit::Normal, QString(), &ok);

View file

@ -14,4 +14,5 @@ message Command_BanFromServer {
optional uint32 minutes = 3; optional uint32 minutes = 3;
optional string reason = 4; optional string reason = 4;
optional string visible_reason = 5; optional string visible_reason = 5;
optional string clientid = 6;
} }

View file

@ -23,4 +23,5 @@ message ServerInfo_User {
optional uint64 session_id = 10; optional uint64 session_id = 10;
optional uint64 accountage_secs = 11; optional uint64 accountage_secs = 11;
optional string email = 12; optional string email = 12;
optional string clientid = 13;
} }

View file

@ -119,6 +119,7 @@ message Command_Register {
// Country code of the user. 2 letter ISO format // Country code of the user. 2 letter ISO format
optional string country = 5; optional string country = 5;
optional string real_name = 6; optional string real_name = 6;
optional string clientid = 7;
} }
// User wants to activate an account // User wants to activate an account

View file

@ -46,7 +46,7 @@ Server::Server(bool _threaded, QObject *parent)
qRegisterMetaType<GameEventContainer>("GameEventContainer"); qRegisterMetaType<GameEventContainer>("GameEventContainer");
qRegisterMetaType<IslMessage>("IslMessage"); qRegisterMetaType<IslMessage>("IslMessage");
qRegisterMetaType<Command_JoinGame>("Command_JoinGame"); qRegisterMetaType<Command_JoinGame>("Command_JoinGame");
connect(this, SIGNAL(sigSendIslMessage(IslMessage, int)), this, SLOT(doSendIslMessage(IslMessage, int)), Qt::QueuedConnection); connect(this, SIGNAL(sigSendIslMessage(IslMessage, int)), this, SLOT(doSendIslMessage(IslMessage, int)), Qt::QueuedConnection);
} }
@ -62,9 +62,9 @@ void Server::prepareDestroy()
for (int i = 0; i < clients.size(); ++i) for (int i = 0; i < clients.size(); ++i)
QMetaObject::invokeMethod(clients.at(i), "prepareDestroy", Qt::QueuedConnection); QMetaObject::invokeMethod(clients.at(i), "prepareDestroy", Qt::QueuedConnection);
clientsLock.unlock(); clientsLock.unlock();
bool done = false; bool done = false;
class SleeperThread : public QThread class SleeperThread : public QThread
{ {
public: public:
@ -83,7 +83,7 @@ void Server::prepareDestroy()
while (!clients.isEmpty()) while (!clients.isEmpty())
clients.first()->prepareDestroy(); clients.first()->prepareDestroy();
} }
roomsLock.lockForWrite(); roomsLock.lockForWrite();
QMapIterator<int, Server_Room *> roomIterator(rooms); QMapIterator<int, Server_Room *> roomIterator(rooms);
while (roomIterator.hasNext()) while (roomIterator.hasNext())
@ -107,21 +107,21 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
{ {
if (name.size() > 35) if (name.size() > 35)
name = name.left(35); name = name.left(35);
Server_DatabaseInterface *databaseInterface = getDatabaseInterface(); Server_DatabaseInterface *databaseInterface = getDatabaseInterface();
QWriteLocker locker(&clientsLock); QWriteLocker locker(&clientsLock);
AuthenticationResult authState = databaseInterface->checkUserPassword(session, name, password, reasonStr, secondsLeft); AuthenticationResult authState = databaseInterface->checkUserPassword(session, name, password, clientid, reasonStr, secondsLeft);
if (authState == NotLoggedIn || authState == UserIsBanned || authState == UsernameInvalid || authState == UserIsInactive) if (authState == NotLoggedIn || authState == UserIsBanned || authState == UsernameInvalid || authState == UserIsInactive)
return authState; return authState;
ServerInfo_User data = databaseInterface->getUserData(name, true); ServerInfo_User data = databaseInterface->getUserData(name, true);
data.set_address(session->getAddress().toStdString()); data.set_address(session->getAddress().toStdString());
name = QString::fromStdString(data.name()); // Compensate for case indifference name = QString::fromStdString(data.name()); // Compensate for case indifference
databaseInterface->lockSessionTables(); databaseInterface->lockSessionTables();
if (authState == PasswordRight) { if (authState == PasswordRight) {
// verify that new session would not cause problems with older existing session // verify that new session would not cause problems with older existing session
@ -133,8 +133,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
} else if (authState == UnknownUser) { } else if (authState == UnknownUser) {
// Change user name so that no two users have the same names, // Change user name so that no two users have the same names,
// don't interfere with registered user names though. // don't interfere with registered user names though.
bool requireReg = databaseInterface->getRequireRegistration(); if (getRegOnlyServer()) {
if (requireReg) {
qDebug("Login denied: registration required"); qDebug("Login denied: registration required");
databaseInterface->unlockSessionTables(); databaseInterface->unlockSessionTables();
return RegistrationRequired; return RegistrationRequired;
@ -147,18 +146,18 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
name = tempName; name = tempName;
data.set_name(name.toStdString()); data.set_name(name.toStdString());
} }
users.insert(name, session); users.insert(name, session);
qDebug() << "Server::loginUser:" << session << "name=" << name; qDebug() << "Server::loginUser:" << session << "name=" << name;
data.set_session_id(databaseInterface->startSession(name, session->getAddress())); data.set_session_id(databaseInterface->startSession(name, session->getAddress(), clientid));
databaseInterface->unlockSessionTables(); databaseInterface->unlockSessionTables();
usersBySessionId.insert(data.session_id(), session); usersBySessionId.insert(data.session_id(), session);
qDebug() << "session id:" << data.session_id(); qDebug() << "session id:" << data.session_id();
session->setUserInfo(data); session->setUserInfo(data);
Event_UserJoined event; Event_UserJoined event;
event.mutable_user_info()->CopyFrom(session->copyUserInfo(false)); event.mutable_user_info()->CopyFrom(session->copyUserInfo(false));
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
@ -166,10 +165,10 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
if (clients[i]->getAcceptsUserListChanges()) if (clients[i]->getAcceptsUserListChanges())
clients[i]->sendProtocolItem(*se); clients[i]->sendProtocolItem(*se);
delete se; delete se;
event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true, true)); event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true, true));
locker.unlock(); locker.unlock();
if (clientid.isEmpty()){ if (clientid.isEmpty()){
// client id is empty, either out dated client or client has been modified // client id is empty, either out dated client or client has been modified
if (getClientIdRequired()) if (getClientIdRequired())
@ -180,10 +179,11 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
databaseInterface->updateUsersClientID(name, clientid); databaseInterface->updateUsersClientID(name, clientid);
} }
databaseInterface->updateUsersLastLoginTime(name);
se = Server_ProtocolHandler::prepareSessionEvent(event); se = Server_ProtocolHandler::prepareSessionEvent(event);
sendIsl_SessionEvent(*se); sendIsl_SessionEvent(*se);
delete se; delete se;
return authState; return authState;
} }
@ -208,7 +208,7 @@ QList<PlayerReference> Server::getPersistentPlayerReferences(const QString &user
Server_AbstractUserInterface *Server::findUser(const QString &userName) const Server_AbstractUserInterface *Server::findUser(const QString &userName) const
{ {
// Call this only with clientsLock set. // Call this only with clientsLock set.
Server_AbstractUserInterface *userHandler = users.value(userName); Server_AbstractUserInterface *userHandler = users.value(userName);
if (userHandler) if (userHandler)
return userHandler; return userHandler;
@ -236,10 +236,10 @@ void Server::removeClient(Server_ProtocolHandler *client)
clients[i]->sendProtocolItem(*se); clients[i]->sendProtocolItem(*se);
sendIsl_SessionEvent(*se); sendIsl_SessionEvent(*se);
delete se; delete se;
users.remove(QString::fromStdString(data->name())); users.remove(QString::fromStdString(data->name()));
qDebug() << "Server::removeClient: name=" << QString::fromStdString(data->name()); qDebug() << "Server::removeClient: name=" << QString::fromStdString(data->name());
if (data->has_session_id()) { if (data->has_session_id()) {
const qint64 sessionId = data->session_id(); const qint64 sessionId = data->session_id();
usersBySessionId.remove(sessionId); usersBySessionId.remove(sessionId);
@ -254,21 +254,21 @@ void Server::externalUserJoined(const ServerInfo_User &userInfo)
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
clientsLock.lockForWrite(); clientsLock.lockForWrite();
Server_RemoteUserInterface *newUser = new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo)); Server_RemoteUserInterface *newUser = new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo));
externalUsers.insert(QString::fromStdString(userInfo.name()), newUser); externalUsers.insert(QString::fromStdString(userInfo.name()), newUser);
externalUsersBySessionId.insert(userInfo.session_id(), newUser); externalUsersBySessionId.insert(userInfo.session_id(), newUser);
Event_UserJoined event; Event_UserJoined event;
event.mutable_user_info()->CopyFrom(userInfo); event.mutable_user_info()->CopyFrom(userInfo);
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
for (int i = 0; i < clients.size(); ++i) for (int i = 0; i < clients.size(); ++i)
if (clients[i]->getAcceptsUserListChanges()) if (clients[i]->getAcceptsUserListChanges())
clients[i]->sendProtocolItem(*se); clients[i]->sendProtocolItem(*se);
delete se; delete se;
clientsLock.unlock(); clientsLock.unlock();
ResponseContainer rc(-1); ResponseContainer rc(-1);
newUser->joinPersistentGames(rc); newUser->joinPersistentGames(rc);
newUser->sendResponseContainer(rc, Response::RespNothing); newUser->sendResponseContainer(rc, Response::RespNothing);
@ -277,12 +277,12 @@ void Server::externalUserJoined(const ServerInfo_User &userInfo)
void Server::externalUserLeft(const QString &userName) void Server::externalUserLeft(const QString &userName)
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
clientsLock.lockForWrite(); clientsLock.lockForWrite();
Server_AbstractUserInterface *user = externalUsers.take(userName); Server_AbstractUserInterface *user = externalUsers.take(userName);
externalUsersBySessionId.remove(user->getUserInfo()->session_id()); externalUsersBySessionId.remove(user->getUserInfo()->session_id());
clientsLock.unlock(); clientsLock.unlock();
QMap<int, QPair<int, int> > userGames(user->getGames()); QMap<int, QPair<int, int> > userGames(user->getGames());
QMapIterator<int, QPair<int, int> > userGamesIterator(userGames); QMapIterator<int, QPair<int, int> > userGamesIterator(userGames);
roomsLock.lockForRead(); roomsLock.lockForRead();
@ -291,26 +291,26 @@ void Server::externalUserLeft(const QString &userName)
Server_Room *room = rooms.value(userGamesIterator.value().first); Server_Room *room = rooms.value(userGamesIterator.value().first);
if (!room) if (!room)
continue; continue;
QReadLocker roomGamesLocker(&room->gamesLock); QReadLocker roomGamesLocker(&room->gamesLock);
Server_Game *game = room->getGames().value(userGamesIterator.key()); Server_Game *game = room->getGames().value(userGamesIterator.key());
if (!game) if (!game)
continue; continue;
QMutexLocker gameLocker(&game->gameMutex); QMutexLocker gameLocker(&game->gameMutex);
Server_Player *player = game->getPlayers().value(userGamesIterator.value().second); Server_Player *player = game->getPlayers().value(userGamesIterator.value().second);
if (!player) if (!player)
continue; continue;
player->disconnectClient(); player->disconnectClient();
} }
roomsLock.unlock(); roomsLock.unlock();
delete user; delete user;
Event_UserLeft event; Event_UserLeft event;
event.set_name(userName.toStdString()); event.set_name(userName.toStdString());
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
clientsLock.lockForRead(); clientsLock.lockForRead();
for (int i = 0; i < clients.size(); ++i) for (int i = 0; i < clients.size(); ++i)
@ -324,7 +324,7 @@ void Server::externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo)
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
QReadLocker locker(&roomsLock); QReadLocker locker(&roomsLock);
Server_Room *room = rooms.value(roomId); Server_Room *room = rooms.value(roomId);
if (!room) { if (!room) {
qDebug() << "externalRoomUserJoined: room id=" << roomId << "not found"; qDebug() << "externalRoomUserJoined: room id=" << roomId << "not found";
@ -337,7 +337,7 @@ void Server::externalRoomUserLeft(int roomId, const QString &userName)
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
QReadLocker locker(&roomsLock); QReadLocker locker(&roomsLock);
Server_Room *room = rooms.value(roomId); Server_Room *room = rooms.value(roomId);
if (!room) { if (!room) {
qDebug() << "externalRoomUserLeft: room id=" << roomId << "not found"; qDebug() << "externalRoomUserLeft: room id=" << roomId << "not found";
@ -350,7 +350,7 @@ void Server::externalRoomSay(int roomId, const QString &userName, const QString
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
QReadLocker locker(&roomsLock); QReadLocker locker(&roomsLock);
Server_Room *room = rooms.value(roomId); Server_Room *room = rooms.value(roomId);
if (!room) { if (!room) {
qDebug() << "externalRoomSay: room id=" << roomId << "not found"; qDebug() << "externalRoomSay: room id=" << roomId << "not found";
@ -365,7 +365,7 @@ void Server::externalRoomGameListChanged(int roomId, const ServerInfo_Game &game
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
QReadLocker locker(&roomsLock); QReadLocker locker(&roomsLock);
Server_Room *room = rooms.value(roomId); Server_Room *room = rooms.value(roomId);
if (!room) { if (!room) {
qDebug() << "externalRoomGameListChanged: room id=" << roomId << "not found"; qDebug() << "externalRoomGameListChanged: room id=" << roomId << "not found";
@ -377,11 +377,11 @@ void Server::externalRoomGameListChanged(int roomId, const ServerInfo_Game &game
void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cmdId, int roomId, int serverId, qint64 sessionId) void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cmdId, int roomId, int serverId, qint64 sessionId)
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
try { try {
QReadLocker roomsLocker(&roomsLock); QReadLocker roomsLocker(&roomsLock);
QReadLocker clientsLocker(&clientsLock); QReadLocker clientsLocker(&clientsLock);
Server_Room *room = rooms.value(roomId); Server_Room *room = rooms.value(roomId);
if (!room) { if (!room) {
qDebug() << "externalJoinGameCommandReceived: room id=" << roomId << "not found"; qDebug() << "externalJoinGameCommandReceived: room id=" << roomId << "not found";
@ -392,7 +392,7 @@ void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cm
qDebug() << "externalJoinGameCommandReceived: session id=" << sessionId << "not found"; qDebug() << "externalJoinGameCommandReceived: session id=" << sessionId << "not found";
throw Response::RespNotInRoom; throw Response::RespNotInRoom;
} }
ResponseContainer responseContainer(cmdId); ResponseContainer responseContainer(cmdId);
Response::ResponseCode responseCode = room->processJoinGameCommand(cmd, responseContainer, userInterface); Response::ResponseCode responseCode = room->processJoinGameCommand(cmd, responseContainer, userInterface);
userInterface->sendResponseContainer(responseContainer, responseCode); userInterface->sendResponseContainer(responseContainer, responseCode);
@ -400,7 +400,7 @@ void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cm
Response response; Response response;
response.set_cmd_id(cmdId); response.set_cmd_id(cmdId);
response.set_response_code(code); response.set_response_code(code);
sendIsl_Response(response, serverId, sessionId); sendIsl_Response(response, serverId, sessionId);
} }
} }
@ -408,44 +408,44 @@ void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cm
void Server::externalGameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId) void Server::externalGameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId)
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
try { try {
ResponseContainer responseContainer(cont.cmd_id()); ResponseContainer responseContainer(cont.cmd_id());
Response::ResponseCode finalResponseCode = Response::RespOk; Response::ResponseCode finalResponseCode = Response::RespOk;
QReadLocker roomsLocker(&roomsLock); QReadLocker roomsLocker(&roomsLock);
Server_Room *room = rooms.value(cont.room_id()); Server_Room *room = rooms.value(cont.room_id());
if (!room) { if (!room) {
qDebug() << "externalGameCommandContainerReceived: room id=" << cont.room_id() << "not found"; qDebug() << "externalGameCommandContainerReceived: room id=" << cont.room_id() << "not found";
throw Response::RespNotInRoom; throw Response::RespNotInRoom;
} }
QReadLocker roomGamesLocker(&room->gamesLock); QReadLocker roomGamesLocker(&room->gamesLock);
Server_Game *game = room->getGames().value(cont.game_id()); Server_Game *game = room->getGames().value(cont.game_id());
if (!game) { if (!game) {
qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found"; qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found";
throw Response::RespNotInRoom; throw Response::RespNotInRoom;
} }
QMutexLocker gameLocker(&game->gameMutex); QMutexLocker gameLocker(&game->gameMutex);
Server_Player *player = game->getPlayers().value(playerId); Server_Player *player = game->getPlayers().value(playerId);
if (!player) { if (!player) {
qDebug() << "externalGameCommandContainerReceived: player id=" << playerId << "not found"; qDebug() << "externalGameCommandContainerReceived: player id=" << playerId << "not found";
throw Response::RespNotInRoom; throw Response::RespNotInRoom;
} }
GameEventStorage ges; GameEventStorage ges;
for (int i = cont.game_command_size() - 1; i >= 0; --i) { for (int i = cont.game_command_size() - 1; i >= 0; --i) {
const GameCommand &sc = cont.game_command(i); const GameCommand &sc = cont.game_command(i);
qDebug() << "[ISL]" << QString::fromStdString(sc.ShortDebugString()); qDebug() << "[ISL]" << QString::fromStdString(sc.ShortDebugString());
Response::ResponseCode resp = player->processGameCommand(sc, responseContainer, ges); Response::ResponseCode resp = player->processGameCommand(sc, responseContainer, ges);
if (resp != Response::RespOk) if (resp != Response::RespOk)
finalResponseCode = resp; finalResponseCode = resp;
} }
ges.sendToGame(game); ges.sendToGame(game);
if (finalResponseCode != Response::RespNothing) { if (finalResponseCode != Response::RespNothing) {
player->playerMutex.lock(); player->playerMutex.lock();
player->getUserInterface()->sendResponseContainer(responseContainer, finalResponseCode); player->getUserInterface()->sendResponseContainer(responseContainer, finalResponseCode);
@ -455,7 +455,7 @@ void Server::externalGameCommandContainerReceived(const CommandContainer &cont,
Response response; Response response;
response.set_cmd_id(cont.cmd_id()); response.set_cmd_id(cont.cmd_id());
response.set_response_code(code); response.set_response_code(code);
sendIsl_Response(response, serverId, sessionId); sendIsl_Response(response, serverId, sessionId);
} }
} }
@ -463,9 +463,9 @@ void Server::externalGameCommandContainerReceived(const CommandContainer &cont,
void Server::externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId) void Server::externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId)
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
QReadLocker usersLocker(&clientsLock); QReadLocker usersLocker(&clientsLock);
Server_ProtocolHandler *client = usersBySessionId.value(sessionId); Server_ProtocolHandler *client = usersBySessionId.value(sessionId);
if (!client) { if (!client) {
qDebug() << "externalGameEventContainerReceived: session" << sessionId << "not found"; qDebug() << "externalGameEventContainerReceived: session" << sessionId << "not found";
@ -477,9 +477,9 @@ void Server::externalGameEventContainerReceived(const GameEventContainer &cont,
void Server::externalResponseReceived(const Response &resp, qint64 sessionId) void Server::externalResponseReceived(const Response &resp, qint64 sessionId)
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
QReadLocker usersLocker(&clientsLock); QReadLocker usersLocker(&clientsLock);
Server_ProtocolHandler *client = usersBySessionId.value(sessionId); Server_ProtocolHandler *client = usersBySessionId.value(sessionId);
if (!client) { if (!client) {
qDebug() << "externalResponseReceived: session" << sessionId << "not found"; qDebug() << "externalResponseReceived: session" << sessionId << "not found";
@ -491,10 +491,10 @@ void Server::externalResponseReceived(const Response &resp, qint64 sessionId)
void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl) void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl)
{ {
// This function is always called from the main thread via signal/slot. // This function is always called from the main thread via signal/slot.
Event_ListRooms event; Event_ListRooms event;
event.add_room_list()->CopyFrom(roomInfo); event.add_room_list()->CopyFrom(roomInfo);
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
clientsLock.lockForRead(); clientsLock.lockForRead();
@ -502,10 +502,10 @@ void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl
if (clients[i]->getAcceptsRoomListChanges()) if (clients[i]->getAcceptsRoomListChanges())
clients[i]->sendProtocolItem(*se); clients[i]->sendProtocolItem(*se);
clientsLock.unlock(); clientsLock.unlock();
if (sendToIsl) if (sendToIsl)
sendIsl_SessionEvent(*se); sendIsl_SessionEvent(*se);
delete se; delete se;
} }
@ -543,7 +543,7 @@ void Server::sendIsl_Response(const Response &item, int serverId, qint64 session
if (sessionId != -1) if (sessionId != -1)
msg.set_session_id(sessionId); msg.set_session_id(sessionId);
msg.mutable_response()->CopyFrom(item); msg.mutable_response()->CopyFrom(item);
emit sigSendIslMessage(msg, serverId); emit sigSendIslMessage(msg, serverId);
} }
@ -554,7 +554,7 @@ void Server::sendIsl_SessionEvent(const SessionEvent &item, int serverId, qint64
if (sessionId != -1) if (sessionId != -1)
msg.set_session_id(sessionId); msg.set_session_id(sessionId);
msg.mutable_session_event()->CopyFrom(item); msg.mutable_session_event()->CopyFrom(item);
emit sigSendIslMessage(msg, serverId); emit sigSendIslMessage(msg, serverId);
} }
@ -565,7 +565,7 @@ void Server::sendIsl_GameEventContainer(const GameEventContainer &item, int serv
if (sessionId != -1) if (sessionId != -1)
msg.set_session_id(sessionId); msg.set_session_id(sessionId);
msg.mutable_game_event_container()->CopyFrom(item); msg.mutable_game_event_container()->CopyFrom(item);
emit sigSendIslMessage(msg, serverId); emit sigSendIslMessage(msg, serverId);
} }
@ -576,7 +576,7 @@ void Server::sendIsl_RoomEvent(const RoomEvent &item, int serverId, qint64 sessi
if (sessionId != -1) if (sessionId != -1)
msg.set_session_id(sessionId); msg.set_session_id(sessionId);
msg.mutable_room_event()->CopyFrom(item); msg.mutable_room_event()->CopyFrom(item);
emit sigSendIslMessage(msg, serverId); emit sigSendIslMessage(msg, serverId);
} }
@ -586,11 +586,11 @@ void Server::sendIsl_GameCommand(const CommandContainer &item, int serverId, qin
msg.set_message_type(IslMessage::GAME_COMMAND_CONTAINER); msg.set_message_type(IslMessage::GAME_COMMAND_CONTAINER);
msg.set_session_id(sessionId); msg.set_session_id(sessionId);
msg.set_player_id(playerId); msg.set_player_id(playerId);
CommandContainer *cont = msg.mutable_game_command(); CommandContainer *cont = msg.mutable_game_command();
cont->CopyFrom(item); cont->CopyFrom(item);
cont->set_room_id(roomId); cont->set_room_id(roomId);
emit sigSendIslMessage(msg, serverId); emit sigSendIslMessage(msg, serverId);
} }
@ -599,10 +599,10 @@ void Server::sendIsl_RoomCommand(const CommandContainer &item, int serverId, qin
IslMessage msg; IslMessage msg;
msg.set_message_type(IslMessage::ROOM_COMMAND_CONTAINER); msg.set_message_type(IslMessage::ROOM_COMMAND_CONTAINER);
msg.set_session_id(sessionId); msg.set_session_id(sessionId);
CommandContainer *cont = msg.mutable_room_command(); CommandContainer *cont = msg.mutable_room_command();
cont->CopyFrom(item); cont->CopyFrom(item);
cont->set_room_id(roomId); cont->set_room_id(roomId);
emit sigSendIslMessage(msg, serverId); emit sigSendIslMessage(msg, serverId);
} }

View file

@ -57,6 +57,7 @@ public:
virtual bool getGameShouldPing() const { return false; } virtual bool getGameShouldPing() const { return false; }
virtual bool getClientIdRequired() const { return false; } virtual bool getClientIdRequired() const { return false; }
virtual bool getRegOnlyServer() const { return false; }
virtual int getPingClockInterval() const { return 0; } virtual int getPingClockInterval() const { return 0; }
virtual int getMaxGameInactivityTime() const { return 9999999; } virtual int getMaxGameInactivityTime() const { return 9999999; }
virtual int getMaxPlayerInactivityTime() const { return 9999999; } virtual int getMaxPlayerInactivityTime() const { return 9999999; }

View file

@ -12,8 +12,8 @@ public:
Server_DatabaseInterface(QObject *parent = 0) Server_DatabaseInterface(QObject *parent = 0)
: QObject(parent) { } : QObject(parent) { }
virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) = 0; virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &secondsLeft) = 0;
virtual bool checkUserIsBanned(const QString & /* ipAddress */, const QString & /* userName */, QString & /* banReason */, int & /* banSecondsRemaining */) { return false; } virtual bool checkUserIsBanned(const QString & /* ipAddress */, const QString & /* userName */, const QString & /* clientId */, QString & /* banReason */, int & /* banSecondsRemaining */) { return false; }
virtual bool activeUserExists(const QString & /* user */) { return false; } virtual bool activeUserExists(const QString & /* user */) { return false; }
virtual bool userExists(const QString & /* user */) { return false; } virtual bool userExists(const QString & /* user */) { return false; }
virtual QMap<QString, ServerInfo_User> getBuddyList(const QString & /* name */) { return QMap<QString, ServerInfo_User>(); } virtual QMap<QString, ServerInfo_User> getBuddyList(const QString & /* name */) { return QMap<QString, ServerInfo_User>(); }
@ -24,7 +24,7 @@ public:
virtual void storeGameInformation(const QString & /* roomName */, const QStringList & /* roomGameTypes */, const ServerInfo_Game & /* gameInfo */, const QSet<QString> & /* allPlayersEver */, const QSet<QString> & /* allSpectatorsEver */, const QList<GameReplay *> & /* replayList */) { } virtual void storeGameInformation(const QString & /* roomName */, const QStringList & /* roomGameTypes */, const ServerInfo_Game & /* gameInfo */, const QSet<QString> & /* allPlayersEver */, const QSet<QString> & /* allSpectatorsEver */, const QList<GameReplay *> & /* replayList */) { }
virtual DeckList *getDeckFromDatabase(int /* deckId */, int /* userId */) { return 0; } virtual DeckList *getDeckFromDatabase(int /* deckId */, int /* userId */) { return 0; }
virtual qint64 startSession(const QString & /* userName */, const QString & /* address */) { return 0; } virtual qint64 startSession(const QString & /* userName */, const QString & /* address */, const QString & /* clientId */) { return 0; }
virtual bool usernameIsValid(const QString & /*userName */, QString & /* error */) { return true; }; virtual bool usernameIsValid(const QString & /*userName */, QString & /* error */) { return true; };
public slots: public slots:
virtual void endSession(qint64 /* sessionId */ ) { } virtual void endSession(qint64 /* sessionId */ ) { }
@ -42,6 +42,7 @@ public:
virtual bool registerUser(const QString & /* userName */, const QString & /* realName */, ServerInfo_User_Gender const & /* gender */, const QString & /* password */, const QString & /* emailAddress */, const QString & /* country */, bool /* active = false */) { return false; } virtual bool registerUser(const QString & /* userName */, const QString & /* realName */, ServerInfo_User_Gender const & /* gender */, const QString & /* password */, const QString & /* emailAddress */, const QString & /* country */, bool /* active = false */) { return false; }
virtual bool activateUser(const QString & /* userName */, const QString & /* token */) { return false; } virtual bool activateUser(const QString & /* userName */, const QString & /* token */) { return false; }
virtual void updateUsersClientID(const QString & /* userName */, const QString & /* userClientID */) { } virtual void updateUsersClientID(const QString & /* userName */, const QString & /* userClientID */) { }
virtual void updateUsersLastLoginTime(const QString & /* userName */) { }
enum LogMessage_TargetType { MessageTargetRoom, MessageTargetGame, MessageTargetChat, MessageTargetIslRoom }; enum LogMessage_TargetType { MessageTargetRoom, MessageTargetGame, MessageTargetChat, MessageTargetIslRoom };
virtual void logMessage(const int /* senderId */, const QString & /* senderName */, const QString & /* senderIp */, const QString & /* logMessage */, LogMessage_TargetType /* targetType */, const int /* targetId */, const QString & /* targetName */) { }; virtual void logMessage(const int /* senderId */, const QString & /* senderName */, const QString & /* senderIp */, const QString & /* logMessage */, LogMessage_TargetType /* targetType */, const int /* targetId */, const QString & /* targetName */) { };

View file

@ -385,8 +385,6 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
if (userName.isEmpty() || (userInfo != 0)) if (userName.isEmpty() || (userInfo != 0))
return Response::RespContextError; return Response::RespContextError;
if (clientId.isEmpty())
return Response::RespContextError;
QString reasonStr; QString reasonStr;
int banSecondsLeft = 0; int banSecondsLeft = 0;

View file

@ -36,6 +36,7 @@ ServerInfo_User &ServerInfo_User_Container::copyUserInfo(ServerInfo_User &result
if (!sessionInfo) { if (!sessionInfo) {
result.clear_session_id(); result.clear_session_id();
result.clear_address(); result.clear_address();
result.clear_clientid();
} }
if (!internalInfo) if (!internalInfo)
{ {

View file

@ -0,0 +1,5 @@
-- Servatrice db migration from version 2 to version 3
alter table cockatrice_users add clientid varchar(15) not null;
UPDATE cockatrice_schema_version SET version=3 WHERE version=2;

View file

@ -0,0 +1,5 @@
-- Servatrice db migration from version 3 to version 4
alter table cockatrice_sessions add clientid varchar(15) not null;
UPDATE cockatrice_schema_version SET version=4 WHERE version=3;

View file

@ -0,0 +1,5 @@
-- Servatrice db migration from version 4 to version 5
alter table cockatrice_bans add clientid varchar(15) not null;
UPDATE cockatrice_schema_version SET version=5 WHERE version=4;

View file

@ -0,0 +1,5 @@
-- Servatrice db migration from version 5 to version 6
alter table cockatrice_users add last_login datetime not null;
UPDATE cockatrice_schema_version SET version=6 WHERE version=5;

View file

@ -0,0 +1,36 @@
#!/bin/bash
# THIS SCRIPT EXPECTS TO BE EXECUTED FROM THE GITHUB SOURCE FOLDER PATH STRUCTURE
# OTHERWISE, UPDATE THE 'COUNTRYCODEIMAGEPATH' TO POINT TO THE FOLDER CONTAINING THE COUNTRY CODE IMAGES
# USE THIS SCRIPT TO COMPARE EXISTING USER ACCOUNTS TO VALID COUNTRY CODES AND CLEAR INVALID COUNTRY CODE DATA
MODE="report" #set this to correct to fix invalid country codes, otherwise it only reports
DBNAME="servatrice" #set this to the database name used
TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _)
SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file
COUNTRYCODEIMAGEPATH='../../../cockatrice/resources/countries'
VALIDCOUNT=0
INVALIDCOUNT=0
for i in `mysql --defaults-file=$SQLCONFFILE -h localhost -e "select distinct(country) from ""$DBNAME"".""$TABLEPREFIX""_users;"`
do
if [ "$i" != "country" ]; then
if [ -f "$COUNTRYCODEIMAGEPATH/$i.svg" ]; then
((VALIDCOUNT++))
else
((INVALIDCOUNT++))
if [ "$MODE" == "correct" ]; then
echo "$i COUNTRY CODE INVALID, ATTEMPTING TO CORRECT"
mysql --defaults-file=$SQLCONFFILE -h localhost -e "update ""$DBNAME"".""$TABLEPREFIX""_users set country = '' where country = '$i';"
fi
fi
fi
done
if [ "$MODE" == "correct" ]; then
mysql --defaults-file=$SQLCONFFILE -h localhost -e "update ""$DBNAME"".""$TABLEPREFIX""_users set country = lower(country);"
fi
echo "INVALID: $INVALIDCOUNT"
echo "VALID: $VALIDCOUNT"

View file

@ -62,8 +62,8 @@ method=none
; if the chosen authentication method is password, here you can define the password your users will use to log in ; if the chosen authentication method is password, here you can define the password your users will use to log in
password=123456 password=123456
; Accept only registered users? default is 0 (accept unregistered users) ; Accept only registered users? default is false (accept unregistered users)
regonly=0 regonly=false
[users] [users]
@ -193,6 +193,11 @@ roomlist\1\game_types\3\name="GameType3"
; default is 120 ; default is 120
max_game_inactivity_time=120 max_game_inactivity_time=120
; All actions during a game are recorded and stored in the database as a replay that all participants of
; the game can go back to and review after the game is closed. This can require a fairly large amount of
; storage to save all the information. Disable this option to prevent the storing of replay data in
; the database. Default value is true.
store_replays=true
[security] [security]
; You may want to restrict the number of users that can connect to your server at any given time. ; You may want to restrict the number of users that can connect to your server at any given time.

View file

@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_schema_version` (
PRIMARY KEY (`version`) PRIMARY KEY (`version`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO cockatrice_schema_version VALUES(3); INSERT INTO cockatrice_schema_version VALUES(6);
CREATE TABLE IF NOT EXISTS `cockatrice_decklist_files` ( CREATE TABLE IF NOT EXISTS `cockatrice_decklist_files` (
`id` int(7) unsigned zerofill NOT NULL auto_increment, `id` int(7) unsigned zerofill NOT NULL auto_increment,
@ -84,6 +84,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_users` (
`active` tinyint(1) NOT NULL, `active` tinyint(1) NOT NULL,
`token` binary(16) NOT NULL, `token` binary(16) NOT NULL,
`clientid` varchar(15) NOT NULL, `clientid` varchar(15) NOT NULL,
`last_login` datetime NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`), UNIQUE KEY `name` (`name`),
KEY `token` (`token`), KEY `token` (`token`),
@ -132,6 +133,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_bans` (
`minutes` int(6) NOT NULL, `minutes` int(6) NOT NULL,
`reason` text NOT NULL, `reason` text NOT NULL,
`visible_reason` text NOT NULL, `visible_reason` text NOT NULL,
`clientid` varchar(15) NOT NULL,
PRIMARY KEY (`user_name`,`time_from`), PRIMARY KEY (`user_name`,`time_from`),
KEY `time_from` (`time_from`,`ip_address`), KEY `time_from` (`time_from`,`ip_address`),
KEY `ip_address` (`ip_address`) KEY `ip_address` (`ip_address`)
@ -144,6 +146,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_sessions` (
`ip_address` char(15) COLLATE utf8_unicode_ci NOT NULL, `ip_address` char(15) COLLATE utf8_unicode_ci NOT NULL,
`start_time` datetime NOT NULL, `start_time` datetime NOT NULL,
`end_time` datetime DEFAULT NULL, `end_time` datetime DEFAULT NULL,
`clientid` varchar(15) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `username` (`user_name`) KEY `username` (`user_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

View file

@ -143,7 +143,7 @@ bool Servatrice::initServer()
serverName = settingsCache->value("server/name", "My Cockatrice server").toString(); serverName = settingsCache->value("server/name", "My Cockatrice server").toString();
serverId = settingsCache->value("server/id", 0).toInt(); serverId = settingsCache->value("server/id", 0).toInt();
clientIdRequired = settingsCache->value("server/requireclientid",0).toBool(); clientIdRequired = settingsCache->value("server/requireclientid",0).toBool();
bool regServerOnly = settingsCache->value("authentication/regonly", 0).toBool(); regServerOnly = settingsCache->value("authentication/regonly", 0).toBool();
const QString authenticationMethodStr = settingsCache->value("authentication/method").toString(); const QString authenticationMethodStr = settingsCache->value("authentication/method").toString();
if (authenticationMethodStr == "sql") { if (authenticationMethodStr == "sql") {
@ -161,7 +161,8 @@ bool Servatrice::initServer()
qDebug() << "Authenticating method: none"; qDebug() << "Authenticating method: none";
authenticationMethod = AuthenticationNone; authenticationMethod = AuthenticationNone;
} }
qDebug() << "Store Replays: " << settingsCache->value("game/store_replays", true).toBool();
qDebug() << "Client ID Required: " << clientIdRequired; qDebug() << "Client ID Required: " << clientIdRequired;
bool maxUserLimitEnabled = settingsCache->value("security/enable_max_user_limit", false).toBool(); bool maxUserLimitEnabled = settingsCache->value("security/enable_max_user_limit", false).toBool();
qDebug() << "Maximum user limit enabled: " << maxUserLimitEnabled; qDebug() << "Maximum user limit enabled: " << maxUserLimitEnabled;
@ -174,7 +175,7 @@ bool Servatrice::initServer()
bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool();
bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool();
qDebug() << "Registration enabled: " << registrationEnabled; qDebug() << "Registration enabled: " << regServerOnly;
if (registrationEnabled) if (registrationEnabled)
qDebug() << "Require email address to register: " << requireEmailForRegistration; qDebug() << "Require email address to register: " << requireEmailForRegistration;

View file

@ -120,7 +120,7 @@ private:
QString shutdownReason; QString shutdownReason;
int shutdownMinutes; int shutdownMinutes;
QTimer *shutdownTimer; QTimer *shutdownTimer;
bool isFirstShutdownMessage, clientIdRequired; bool isFirstShutdownMessage, clientIdRequired, regServerOnly;
mutable QMutex serverListMutex; mutable QMutex serverListMutex;
QList<ServerProperties> serverList; QList<ServerProperties> serverList;
@ -138,6 +138,7 @@ public:
QString getLoginMessage() const { QMutexLocker locker(&loginMessageMutex); return loginMessage; } QString getLoginMessage() const { QMutexLocker locker(&loginMessageMutex); return loginMessage; }
bool getGameShouldPing() const { return true; } bool getGameShouldPing() const { return true; }
bool getClientIdRequired() const { return clientIdRequired; } bool getClientIdRequired() const { return clientIdRequired; }
bool getRegOnlyServer() const { return regServerOnly; }
int getPingClockInterval() const { return pingClockInterval; } int getPingClockInterval() const { return pingClockInterval; }
int getMaxGameInactivityTime() const { return maxGameInactivityTime; } int getMaxGameInactivityTime() const { return maxGameInactivityTime; }
int getMaxPlayerInactivityTime() const { return maxPlayerInactivityTime; } int getMaxPlayerInactivityTime() const { return maxPlayerInactivityTime; }

View file

@ -44,7 +44,7 @@ bool Servatrice_DatabaseInterface::initDatabase(const QString &type, const QStri
sqlDatabase.setDatabaseName(databaseName); sqlDatabase.setDatabaseName(databaseName);
sqlDatabase.setUserName(userName); sqlDatabase.setUserName(userName);
sqlDatabase.setPassword(password); sqlDatabase.setPassword(password);
return openDatabase(); return openDatabase();
} }
@ -52,7 +52,7 @@ bool Servatrice_DatabaseInterface::openDatabase()
{ {
if (sqlDatabase.isOpen()) if (sqlDatabase.isOpen())
sqlDatabase.close(); sqlDatabase.close();
const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId); const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId);
qDebug() << QString("[%1] Opening database...").arg(poolStr); qDebug() << QString("[%1] Opening database...").arg(poolStr);
if (!sqlDatabase.open()) { if (!sqlDatabase.open()) {
@ -92,7 +92,7 @@ bool Servatrice_DatabaseInterface::checkSql()
{ {
if (!sqlDatabase.isValid()) if (!sqlDatabase.isValid())
return false; return false;
if (!sqlDatabase.exec("select 1").isActive()) if (!sqlDatabase.exec("select 1").isActive())
return openDatabase(); return openDatabase();
return true; return true;
@ -152,12 +152,6 @@ bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString
return re.exactMatch(user); return re.exactMatch(user);
} }
// TODO move this to Server
bool Servatrice_DatabaseInterface::getRequireRegistration()
{
return settingsCache->value("authentication/regonly", 0).toBool();
}
bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active) bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active)
{ {
if (!checkSql()) if (!checkSql())
@ -235,7 +229,7 @@ QChar Servatrice_DatabaseInterface::getGenderChar(ServerInfo_User_Gender const &
} }
} }
AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &banSecondsLeft) AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &banSecondsLeft)
{ {
switch (server->getAuthenticationMethod()) { switch (server->getAuthenticationMethod()) {
case Servatrice::AuthenticationNone: return UnknownUser; case Servatrice::AuthenticationNone: return UnknownUser;
@ -252,23 +246,23 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
if (!usernameIsValid(user, reasonStr)) if (!usernameIsValid(user, reasonStr))
return UsernameInvalid; return UsernameInvalid;
if (checkUserIsBanned(handler->getAddress(), user, reasonStr, banSecondsLeft)) if (checkUserIsBanned(handler->getAddress(), user, clientId, reasonStr, banSecondsLeft))
return UserIsBanned; return UserIsBanned;
QSqlQuery *passwordQuery = prepareQuery("select password_sha512, active from {prefix}_users where name = :name"); QSqlQuery *passwordQuery = prepareQuery("select password_sha512, active from {prefix}_users where name = :name");
passwordQuery->bindValue(":name", user); passwordQuery->bindValue(":name", user);
if (!execSqlQuery(passwordQuery)) { if (!execSqlQuery(passwordQuery)) {
qDebug("Login denied: SQL error"); qDebug("Login denied: SQL error");
return NotLoggedIn; return NotLoggedIn;
} }
if (passwordQuery->next()) { if (passwordQuery->next()) {
const QString correctPassword = passwordQuery->value(0).toString(); const QString correctPassword = passwordQuery->value(0).toString();
const bool userIsActive = passwordQuery->value(1).toBool(); const bool userIsActive = passwordQuery->value(1).toBool();
if(!userIsActive) { if(!userIsActive) {
qDebug("Login denied: user not active"); qDebug("Login denied: user not active");
return UserIsInactive; return UserIsInactive;
} }
if (correctPassword == PasswordHasher::computeHash(password, correctPassword.left(16))) { if (correctPassword == PasswordHasher::computeHash(password, correctPassword.left(16))) {
qDebug("Login accepted: password right"); qDebug("Login accepted: password right");
@ -286,7 +280,7 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
return UnknownUser; return UnknownUser;
} }
bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining) bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining)
{ {
if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql) if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql)
return false; return false;
@ -297,11 +291,48 @@ bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, c
} }
return return
checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining) checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining) || checkUserIsNameBanned(userName, banReason, banSecondsRemaining) || checkUserIsIdBanned(clientId, banReason, banSecondsRemaining);
|| checkUserIsNameBanned(userName, banReason, banSecondsRemaining);
} }
bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId, QString &banReason, int &banSecondsRemaining)
{
if (clientId.isEmpty())
return false;
QSqlQuery *idBanQuery = prepareQuery(
"select"
" timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)),"
" b.minutes <=> 0,"
" b.visible_reason"
" from {prefix}_bans b"
" where"
" b.time_from = (select max(c.time_from)"
" from {prefix}_bans c"
" where c.clientid = :id)"
" and b.clientid = :id2");
idBanQuery->bindValue(":id", clientId);
idBanQuery->bindValue(":id2", clientId);
if (!execSqlQuery(idBanQuery)) {
qDebug() << "Id ban check failed: SQL error." << idBanQuery->lastError();
return false;
}
if (idBanQuery->next()) {
const int secondsLeft = idBanQuery->value(0).toInt();
const bool permanentBan = idBanQuery->value(1).toInt();
if ((secondsLeft > 0) || permanentBan) {
banReason = idBanQuery->value(2).toString();
banSecondsRemaining = permanentBan ? 0 : secondsLeft;
qDebug() << "User is banned by client id" << clientId;
return true;
}
}
return false;
}
bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName, QString &banReason, int &banSecondsRemaining) bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName, QString &banReason, int &banSecondsRemaining)
{ {
QSqlQuery *nameBanQuery = prepareQuery("select timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)), b.minutes <=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from {prefix}_bans c where c.user_name = :name2) and b.user_name = :name1"); QSqlQuery *nameBanQuery = prepareQuery("select timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)), b.minutes <=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from {prefix}_bans c where c.user_name = :name2) and b.user_name = :name1");
@ -363,7 +394,7 @@ bool Servatrice_DatabaseInterface::activeUserExists(const QString &user)
{ {
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
checkSql(); checkSql();
QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name and active = 1"); QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name and active = 1");
query->bindValue(":name", user); query->bindValue(":name", user);
if (!execSqlQuery(query)) if (!execSqlQuery(query))
@ -377,7 +408,7 @@ bool Servatrice_DatabaseInterface::userExists(const QString &user)
{ {
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
checkSql(); checkSql();
QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name"); QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name");
query->bindValue(":name", user); query->bindValue(":name", user);
if (!execSqlQuery(query)) if (!execSqlQuery(query))
@ -405,13 +436,13 @@ bool Servatrice_DatabaseInterface::isInBuddyList(const QString &whoseList, const
{ {
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
return false; return false;
if (!checkSql()) if (!checkSql())
return false; return false;
int id1 = getUserIdInDB(whoseList); int id1 = getUserIdInDB(whoseList);
int id2 = getUserIdInDB(who); int id2 = getUserIdInDB(who);
QSqlQuery *query = prepareQuery("select 1 from {prefix}_buddylist where id_user1 = :id_user1 and id_user2 = :id_user2"); QSqlQuery *query = prepareQuery("select 1 from {prefix}_buddylist where id_user1 = :id_user1 and id_user2 = :id_user2");
query->bindValue(":id_user1", id1); query->bindValue(":id_user1", id1);
query->bindValue(":id_user2", id2); query->bindValue(":id_user2", id2);
@ -424,13 +455,13 @@ bool Servatrice_DatabaseInterface::isInIgnoreList(const QString &whoseList, cons
{ {
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
return false; return false;
if (!checkSql()) if (!checkSql())
return false; return false;
int id1 = getUserIdInDB(whoseList); int id1 = getUserIdInDB(whoseList);
int id2 = getUserIdInDB(who); int id2 = getUserIdInDB(who);
QSqlQuery *query = prepareQuery("select 1 from {prefix}_ignorelist where id_user1 = :id_user1 and id_user2 = :id_user2"); QSqlQuery *query = prepareQuery("select 1 from {prefix}_ignorelist where id_user1 = :id_user1 and id_user2 = :id_user2");
query->bindValue(":id_user1", id1); query->bindValue(":id_user1", id1);
query->bindValue(":id_user2", id2); query->bindValue(":id_user2", id2);
@ -442,11 +473,11 @@ bool Servatrice_DatabaseInterface::isInIgnoreList(const QString &whoseList, cons
ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId) ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId)
{ {
ServerInfo_User result; ServerInfo_User result;
if (withId) if (withId)
result.set_id(query->value(0).toInt()); result.set_id(query->value(0).toInt());
result.set_name(query->value(1).toString().toStdString()); result.set_name(query->value(1).toString().toStdString());
const int is_admin = query->value(2).toInt(); const int is_admin = query->value(2).toInt();
int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered; int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered;
if (is_admin == 1) if (is_admin == 1)
@ -483,6 +514,10 @@ ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuer
const QString email = query->value(8).toString(); const QString email = query->value(8).toString();
if (!email.isEmpty()) if (!email.isEmpty())
result.set_email(email.toStdString()); result.set_email(email.toStdString());
const QString clientid = query->value(9).toString();
if (!clientid.isEmpty())
result.set_clientid(clientid.toStdString());
} }
return result; return result;
} }
@ -492,16 +527,16 @@ ServerInfo_User Servatrice_DatabaseInterface::getUserData(const QString &name, b
ServerInfo_User result; ServerInfo_User result;
result.set_name(name.toStdString()); result.set_name(name.toStdString());
result.set_user_level(ServerInfo_User::IsUser); result.set_user_level(ServerInfo_User::IsUser);
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
if (!checkSql()) if (!checkSql())
return result; return result;
QSqlQuery *query = prepareQuery("select id, name, admin, country, gender, realname, avatar_bmp, registrationDate, email from {prefix}_users where name = :name and active = 1"); QSqlQuery *query = prepareQuery("select id, name, admin, country, gender, realname, avatar_bmp, registrationDate, email, clientid from {prefix}_users where name = :name and active = 1");
query->bindValue(":name", name); query->bindValue(":name", name);
if (!execSqlQuery(query)) if (!execSqlQuery(query))
return result; return result;
if (query->next()) if (query->next())
return evalUserQueryResult(query, true, withId); return evalUserQueryResult(query, true, withId);
else else
@ -534,7 +569,7 @@ void Servatrice_DatabaseInterface::unlockSessionTables()
bool Servatrice_DatabaseInterface::userSessionExists(const QString &userName) bool Servatrice_DatabaseInterface::userSessionExists(const QString &userName)
{ {
// Call only after lockSessionTables(). // Call only after lockSessionTables().
QSqlQuery *query = prepareQuery("select 1 from {prefix}_sessions where user_name = :user_name and id_server = :id_server and end_time is null"); QSqlQuery *query = prepareQuery("select 1 from {prefix}_sessions where user_name = :user_name and id_server = :id_server and end_time is null");
query->bindValue(":id_server", server->getServerId()); query->bindValue(":id_server", server->getServerId());
query->bindValue(":user_name", userName); query->bindValue(":user_name", userName);
@ -542,18 +577,19 @@ bool Servatrice_DatabaseInterface::userSessionExists(const QString &userName)
return query->next(); return query->next();
} }
qint64 Servatrice_DatabaseInterface::startSession(const QString &userName, const QString &address) qint64 Servatrice_DatabaseInterface::startSession(const QString &userName, const QString &address, const QString &clientId)
{ {
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
return -1; return -1;
if (!checkSql()) if (!checkSql())
return -1; return -1;
QSqlQuery *query = prepareQuery("insert into {prefix}_sessions (user_name, id_server, ip_address, start_time) values(:user_name, :id_server, :ip_address, NOW())"); QSqlQuery *query = prepareQuery("insert into {prefix}_sessions (user_name, id_server, ip_address, start_time, clientid) values(:user_name, :id_server, :ip_address, NOW(), :client_id)");
query->bindValue(":user_name", userName); query->bindValue(":user_name", userName);
query->bindValue(":id_server", server->getServerId()); query->bindValue(":id_server", server->getServerId());
query->bindValue(":ip_address", address); query->bindValue(":ip_address", address);
query->bindValue(":client_id", clientId);
if (execSqlQuery(query)) if (execSqlQuery(query))
return query->lastInsertId().toInt(); return query->lastInsertId().toInt();
return -1; return -1;
@ -563,13 +599,13 @@ void Servatrice_DatabaseInterface::endSession(qint64 sessionId)
{ {
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
return; return;
if (!checkSql()) if (!checkSql())
return; return;
QSqlQuery *query = prepareQuery("lock tables {prefix}_sessions write"); QSqlQuery *query = prepareQuery("lock tables {prefix}_sessions write");
execSqlQuery(query); execSqlQuery(query);
query = prepareQuery("update {prefix}_sessions set end_time=NOW() where id = :id_session"); query = prepareQuery("update {prefix}_sessions set end_time=NOW() where id = :id_session");
query->bindValue(":id_session", sessionId); query->bindValue(":id_session", sessionId);
execSqlQuery(query); execSqlQuery(query);
@ -581,7 +617,7 @@ void Servatrice_DatabaseInterface::endSession(qint64 sessionId)
QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getBuddyList(const QString &name) QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getBuddyList(const QString &name)
{ {
QMap<QString, ServerInfo_User> result; QMap<QString, ServerInfo_User> result;
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
checkSql(); checkSql();
@ -589,7 +625,7 @@ QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getBuddyList(const
query->bindValue(":name", name); query->bindValue(":name", name);
if (!execSqlQuery(query)) if (!execSqlQuery(query))
return result; return result;
while (query->next()) { while (query->next()) {
const ServerInfo_User &temp = evalUserQueryResult(query, false); const ServerInfo_User &temp = evalUserQueryResult(query, false);
result.insert(QString::fromStdString(temp.name()), temp); result.insert(QString::fromStdString(temp.name()), temp);
@ -601,7 +637,7 @@ QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getBuddyList(const
QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getIgnoreList(const QString &name) QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getIgnoreList(const QString &name)
{ {
QMap<QString, ServerInfo_User> result; QMap<QString, ServerInfo_User> result;
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
checkSql(); checkSql();
@ -609,7 +645,7 @@ QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getIgnoreList(const
query->bindValue(":name", name); query->bindValue(":name", name);
if (!execSqlQuery(query)) if (!execSqlQuery(query))
return result; return result;
while (query->next()) { while (query->next()) {
ServerInfo_User temp = evalUserQueryResult(query, false); ServerInfo_User temp = evalUserQueryResult(query, false);
result.insert(QString::fromStdString(temp.name()), temp); result.insert(QString::fromStdString(temp.name()), temp);
@ -622,13 +658,13 @@ int Servatrice_DatabaseInterface::getNextGameId()
{ {
if (!sqlDatabase.isValid()) if (!sqlDatabase.isValid())
return server->getNextLocalGameId(); return server->getNextLocalGameId();
if (!checkSql()) if (!checkSql())
return -1; return -1;
QSqlQuery *query = prepareQuery("insert into {prefix}_games (time_started) values (now())"); QSqlQuery *query = prepareQuery("insert into {prefix}_games (time_started) values (now())");
execSqlQuery(query); execSqlQuery(query);
return query->lastInsertId().toInt(); return query->lastInsertId().toInt();
} }
@ -636,10 +672,10 @@ int Servatrice_DatabaseInterface::getNextReplayId()
{ {
if (!checkSql()) if (!checkSql())
return -1; return -1;
QSqlQuery *query = prepareQuery("insert into {prefix}_replays () values ()"); QSqlQuery *query = prepareQuery("insert into {prefix}_replays () values ()");
execSqlQuery(query); execSqlQuery(query);
return query->lastInsertId().toInt(); return query->lastInsertId().toInt();
} }
@ -647,7 +683,10 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName,
{ {
if (!checkSql()) if (!checkSql())
return; return;
if (!settingsCache->value("game/store_replays", 1).toBool() )
return;
QVariantList gameIds1, playerNames, gameIds2, userIds, replayNames; QVariantList gameIds1, playerNames, gameIds2, userIds, replayNames;
QSetIterator<QString> playerIterator(allPlayersEver); QSetIterator<QString> playerIterator(allPlayersEver);
while (playerIterator.hasNext()) { while (playerIterator.hasNext()) {
@ -665,20 +704,20 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName,
userIds.append(id); userIds.append(id);
replayNames.append(QString::fromStdString(gameInfo.description())); replayNames.append(QString::fromStdString(gameInfo.description()));
} }
QVariantList replayIds, replayGameIds, replayDurations, replayBlobs; QVariantList replayIds, replayGameIds, replayDurations, replayBlobs;
for (int i = 0; i < replayList.size(); ++i) { for (int i = 0; i < replayList.size(); ++i) {
QByteArray blob; QByteArray blob;
const unsigned int size = replayList[i]->ByteSize(); const unsigned int size = replayList[i]->ByteSize();
blob.resize(size); blob.resize(size);
replayList[i]->SerializeToArray(blob.data(), size); replayList[i]->SerializeToArray(blob.data(), size);
replayIds.append(QVariant((qulonglong) replayList[i]->replay_id())); replayIds.append(QVariant((qulonglong) replayList[i]->replay_id()));
replayGameIds.append(gameInfo.game_id()); replayGameIds.append(gameInfo.game_id());
replayDurations.append(replayList[i]->duration_seconds()); replayDurations.append(replayList[i]->duration_seconds());
replayBlobs.append(blob); replayBlobs.append(blob);
} }
{ {
QSqlQuery *query = prepareQuery("update {prefix}_games set room_name=:room_name, descr=:descr, creator_name=:creator_name, password=:password, game_types=:game_types, player_count=:player_count, time_finished=now() where id=:id_game"); QSqlQuery *query = prepareQuery("update {prefix}_games set room_name=:room_name, descr=:descr, creator_name=:creator_name, password=:password, game_types=:game_types, player_count=:player_count, time_finished=now() where id=:id_game");
query->bindValue(":room_name", roomName); query->bindValue(":room_name", roomName);
@ -717,17 +756,17 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName,
DeckList *Servatrice_DatabaseInterface::getDeckFromDatabase(int deckId, int userId) DeckList *Servatrice_DatabaseInterface::getDeckFromDatabase(int deckId, int userId)
{ {
checkSql(); checkSql();
QSqlQuery *query = prepareQuery("select content from {prefix}_decklist_files where id = :id and id_user = :id_user"); QSqlQuery *query = prepareQuery("select content from {prefix}_decklist_files where id = :id and id_user = :id_user");
query->bindValue(":id", deckId); query->bindValue(":id", deckId);
query->bindValue(":id_user", userId); query->bindValue(":id_user", userId);
execSqlQuery(query); execSqlQuery(query);
if (!query->next()) if (!query->next())
throw Response::RespNameNotFound; throw Response::RespNameNotFound;
DeckList *deck = new DeckList; DeckList *deck = new DeckList;
deck->loadFromString_Native(query->value(0).toString()); deck->loadFromString_Native(query->value(0).toString());
return deck; return deck;
} }
@ -789,7 +828,7 @@ bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, const
qDebug("Change password denied: SQL error"); qDebug("Change password denied: SQL error");
return true; return true;
} }
if (!passwordQuery->next()) if (!passwordQuery->next())
return true; return true;
@ -831,7 +870,7 @@ int Servatrice_DatabaseInterface::getActiveUserCount()
void Servatrice_DatabaseInterface::updateUsersClientID(const QString &userName, const QString &userClientID) void Servatrice_DatabaseInterface::updateUsersClientID(const QString &userName, const QString &userClientID)
{ {
if (!checkSql()) if (!checkSql())
return; return;
@ -839,5 +878,16 @@ void Servatrice_DatabaseInterface::updateUsersClientID(const QString &userName,
query->bindValue(":clientid", userClientID); query->bindValue(":clientid", userClientID);
query->bindValue(":username", userName); query->bindValue(":username", userName);
execSqlQuery(query); execSqlQuery(query);
}
void Servatrice_DatabaseInterface::updateUsersLastLoginTime(const QString &userName)
{
if (!checkSql())
return;
QSqlQuery *query = prepareQuery("update {prefix}_users set last_login = NOW() where name = :user_name");
query->bindValue(":user_name", userName);
execSqlQuery(query);
} }

View file

@ -9,7 +9,7 @@
#include "server.h" #include "server.h"
#include "server_database_interface.h" #include "server_database_interface.h"
#define DATABASE_SCHEMA_VERSION 3 #define DATABASE_SCHEMA_VERSION 6
class Servatrice; class Servatrice;
@ -22,13 +22,14 @@ private:
Servatrice *server; Servatrice *server;
ServerInfo_User evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId = false); ServerInfo_User evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId = false);
/** Must be called after checkSql and server is known to be in auth mode. */ /** Must be called after checkSql and server is known to be in auth mode. */
bool checkUserIsIdBanned(const QString &clientId, QString &banReason, int &banSecondsRemaining);
/** Must be called after checkSql and server is known to be in auth mode. */
bool checkUserIsIpBanned(const QString &ipAddress, QString &banReason, int &banSecondsRemaining); bool checkUserIsIpBanned(const QString &ipAddress, QString &banReason, int &banSecondsRemaining);
/** Must be called after checkSql and server is known to be in auth mode. */ /** Must be called after checkSql and server is known to be in auth mode. */
bool checkUserIsNameBanned(QString const &userName, QString &banReason, int &banSecondsRemaining); bool checkUserIsNameBanned(QString const &userName, QString &banReason, int &banSecondsRemaining);
protected: protected:
AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &secondsLeft);
const QString &password, QString &reasonStr, int &secondsLeft);
public slots: public slots:
void initDatabase(const QSqlDatabase &_sqlDatabase); void initDatabase(const QSqlDatabase &_sqlDatabase);
@ -36,7 +37,7 @@ public slots:
public: public:
Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server); Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server);
~Servatrice_DatabaseInterface(); ~Servatrice_DatabaseInterface();
bool initDatabase(const QString &type, const QString &hostName, const QString &databaseName, bool initDatabase(const QString &type, const QString &hostName, const QString &databaseName,
const QString &userName, const QString &password); const QString &userName, const QString &password);
bool openDatabase(); bool openDatabase();
bool checkSql(); bool checkSql();
@ -52,28 +53,28 @@ public:
bool isInBuddyList(const QString &whoseList, const QString &who); bool isInBuddyList(const QString &whoseList, const QString &who);
bool isInIgnoreList(const QString &whoseList, const QString &who); bool isInIgnoreList(const QString &whoseList, const QString &who);
ServerInfo_User getUserData(const QString &name, bool withId = false); ServerInfo_User getUserData(const QString &name, bool withId = false);
void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo,
const QSet<QString> &allPlayersEver, const QSet<QString>&allSpectatorsEver, const QList<GameReplay *> &replayList); const QSet<QString> &allPlayersEver, const QSet<QString>&allSpectatorsEver, const QList<GameReplay *> &replayList);
DeckList *getDeckFromDatabase(int deckId, int userId); DeckList *getDeckFromDatabase(int deckId, int userId);
int getNextGameId(); int getNextGameId();
int getNextReplayId(); int getNextReplayId();
int getActiveUserCount(); int getActiveUserCount();
qint64 startSession(const QString &userName, const QString &address); qint64 startSession(const QString &userName, const QString &address, const QString &clientId);
void endSession(qint64 sessionId); void endSession(qint64 sessionId);
void clearSessionTables(); void clearSessionTables();
void lockSessionTables(); void lockSessionTables();
void unlockSessionTables(); void unlockSessionTables();
bool userSessionExists(const QString &userName); bool userSessionExists(const QString &userName);
bool usernameIsValid(const QString &user, QString & error); bool usernameIsValid(const QString &user, QString & error);
bool checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining); bool checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining);
bool getRequireRegistration(); bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender,
bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender,
const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active = false); const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active = false);
bool activateUser(const QString &userName, const QString &token); bool activateUser(const QString &userName, const QString &token);
void updateUsersClientID(const QString &userName, const QString &userClientID); void updateUsersClientID(const QString &userName, const QString &userClientID);
void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, void updateUsersLastLoginTime(const QString &userName);
void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage,
LogMessage_TargetType targetType, const int targetId, const QString &targetName); LogMessage_TargetType targetType, const int targetId, const QString &targetName);
bool changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword); bool changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword);
QChar getGenderChar(ServerInfo_User_Gender const &gender); QChar getGenderChar(ServerInfo_User_Gender const &gender);

View file

@ -756,20 +756,40 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban
if (trustedSources.contains(address,Qt::CaseInsensitive)) if (trustedSources.contains(address,Qt::CaseInsensitive))
address = ""; address = "";
QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_bans (user_name, ip_address, id_admin, time_from, minutes, reason, visible_reason) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason, :visible_reason)"); QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_bans (user_name, ip_address, id_admin, time_from, minutes, reason, visible_reason, clientid) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason, :visible_reason, :client_id)");
query->bindValue(":user_name", userName); query->bindValue(":user_name", userName);
query->bindValue(":ip_address", address); query->bindValue(":ip_address", address);
query->bindValue(":id_admin", userInfo->id()); query->bindValue(":id_admin", userInfo->id());
query->bindValue(":minutes", minutes); query->bindValue(":minutes", minutes);
query->bindValue(":reason", QString::fromStdString(cmd.reason())); query->bindValue(":reason", QString::fromStdString(cmd.reason()));
query->bindValue(":visible_reason", QString::fromStdString(cmd.visible_reason())); query->bindValue(":visible_reason", QString::fromStdString(cmd.visible_reason()));
query->bindValue(":client_id", QString::fromStdString(cmd.clientid()));
sqlInterface->execSqlQuery(query); sqlInterface->execSqlQuery(query);
servatrice->clientsLock.lockForRead(); servatrice->clientsLock.lockForRead();
QList<ServerSocketInterface *> userList = servatrice->getUsersWithAddressAsList(QHostAddress(address)); QList<ServerSocketInterface *> userList = servatrice->getUsersWithAddressAsList(QHostAddress(address));
ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName));
if (user && !userList.contains(user)) if (!userName.isEmpty()) {
ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName));
userList.append(user); userList.append(user);
}
if (userName.isEmpty() && address.isEmpty()) {
QSqlQuery *query = sqlInterface->prepareQuery("select name from {prefix}_users where clientid = :client_id");
query->bindValue(":client_id", QString::fromStdString(cmd.clientid()));
sqlInterface->execSqlQuery(query);
if (!sqlInterface->execSqlQuery(query)){
qDebug("ClientID username ban lookup failed: SQL Error");
} else {
while (query->next()) {
userName = query->value(0).toString();
ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName));
if (user && !userList.contains(user))
userList.append(user);
}
}
}
if (!userList.isEmpty()) { if (!userList.isEmpty()) {
Event_ConnectionClosed event; Event_ConnectionClosed event;
event.set_reason(Event_ConnectionClosed::BANNED); event.set_reason(Event_ConnectionClosed::BANNED);
@ -792,6 +812,7 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban
Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc) Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc)
{ {
QString userName = QString::fromStdString(cmd.user_name()); QString userName = QString::fromStdString(cmd.user_name());
QString clientId = QString::fromStdString(cmd.clientid());
qDebug() << "Got register command: " << userName; qDebug() << "Got register command: " << userName;
bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool();
@ -822,7 +843,7 @@ Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_R
QString banReason; QString banReason;
int banSecondsRemaining; int banSecondsRemaining;
if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, banReason, banSecondsRemaining)) if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, clientId, banReason, banSecondsRemaining))
{ {
Response_Register *re = new Response_Register; Response_Register *re = new Response_Register;
re->set_denied_reason_str(banReason.toStdString()); re->set_denied_reason_str(banReason.toStdString());

View file

@ -30,6 +30,7 @@
#define QXT_VERSION 0x000602 #define QXT_VERSION 0x000602
#define QXT_VERSION_STR "0.6.2" #define QXT_VERSION_STR "0.6.2"
#define QXT_STATIC
//--------------------------global macros------------------------------ //--------------------------global macros------------------------------

View file

@ -34,7 +34,7 @@
#include <QSharedDataPointer> #include <QSharedDataPointer>
#include <QIODevice> #include <QIODevice>
class QxtMailAttachmentPrivate; struct QxtMailAttachmentPrivate;
class QXT_NETWORK_EXPORT QxtMailAttachment class QXT_NETWORK_EXPORT QxtMailAttachment
{ {
public: public: