Simpler forgot password functionality (#2393)

* Simpler forgot password functionality (Server/Client)
This commit is contained in:
woogerboy21 2017-02-15 17:41:40 -05:00 committed by Zach H
parent b64eab204c
commit 0cfa6863d5
36 changed files with 1190 additions and 173 deletions

View file

@ -77,12 +77,12 @@ endif()
# Define proper compilation flags # Define proper compilation flags
IF(MSVC) IF(MSVC)
# Visual Studio: # Visual Studio:
# Support from Windows XP # Support from Windows XP
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS,5.01") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS,5.01")
# Maximum optimization # Maximum optimization
set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD") set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD")
# Generate complete debugging information # Generate complete debugging information
#set(CMAKE_CXX_FLAGS_DEBUG "/Zi") #set(CMAKE_CXX_FLAGS_DEBUG "/Zi")
ELSEIF (CMAKE_COMPILER_IS_GNUCXX) ELSEIF (CMAKE_COMPILER_IS_GNUCXX)
# linux/gcc, bsd/gcc, windows/mingw # linux/gcc, bsd/gcc, windows/mingw
@ -176,9 +176,9 @@ if(UNIX)
# linux # linux
set(CPACK_GENERATOR DEB ${CPACK_GENERATOR}) set(CPACK_GENERATOR DEB ${CPACK_GENERATOR})
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}") set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_PACKAGE_SECTION "games") set(CPACK_DEBIAN_PACKAGE_SECTION "games")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5")
endif() endif()
elseif(WIN32) elseif(WIN32)

View file

@ -15,6 +15,9 @@ SET(cockatrice_SOURCES
src/dlg_edit_password.cpp src/dlg_edit_password.cpp
src/dlg_edit_tokens.cpp src/dlg_edit_tokens.cpp
src/dlg_edit_user.cpp src/dlg_edit_user.cpp
src/dlg_forgotpasswordrequest.cpp
src/dlg_forgotpasswordreset.cpp
src/dlg_forgotpasswordchallenge.cpp
src/dlg_register.cpp src/dlg_register.cpp
src/dlg_update.cpp src/dlg_update.cpp
src/dlg_viewlog.cpp src/dlg_viewlog.cpp

View file

@ -35,6 +35,9 @@ enum ClientStatus {
StatusActivating, StatusActivating,
StatusLoggingIn, StatusLoggingIn,
StatusLoggedIn, StatusLoggedIn,
StatusRequestingForgotPassword,
StatusSubmitForgotPasswordReset,
StatusSubmitForgotPasswordChallenge,
}; };
class AbstractClient : public QObject { class AbstractClient : public QObject {

View file

@ -11,6 +11,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <iostream> #include <iostream>
#include <QGroupBox> #include <QGroupBox>
#include <QPushButton>
#include "dlg_connect.h" #include "dlg_connect.h"
#include "settingscache.h" #include "settingscache.h"
@ -69,6 +70,17 @@ DlgConnect::DlgConnect(QWidget *parent)
connect(savePasswordCheckBox, SIGNAL(stateChanged(int)), this, SLOT(passwordSaved(int))); connect(savePasswordCheckBox, SIGNAL(stateChanged(int)), this, SLOT(passwordSaved(int)));
btnForgotPassword = new QPushButton(tr("Forgot password"));
connect(btnForgotPassword, SIGNAL(released()), this, SLOT(actForgotPassword()));
btnOk = new QPushButton(tr("Connect"));
btnOk->setFixedWidth(100);
connect(btnOk, SIGNAL(released()), this, SLOT(actOk()));
btnCancel = new QPushButton(tr("Cancel"));
btnCancel->setFixedWidth(100);
connect(btnCancel, SIGNAL(released()), this, SLOT(actCancel()));
QGridLayout *connectionLayout = new QGridLayout; QGridLayout *connectionLayout = new QGridLayout;
connectionLayout->addWidget(previousHostButton, 0, 1); connectionLayout->addWidget(previousHostButton, 0, 1);
connectionLayout->addWidget(previousHosts, 1, 1); connectionLayout->addWidget(previousHosts, 1, 1);
@ -79,6 +91,11 @@ DlgConnect::DlgConnect(QWidget *parent)
connectionLayout->addWidget(portEdit, 4, 1); connectionLayout->addWidget(portEdit, 4, 1);
connectionLayout->addWidget(autoConnectCheckBox, 5, 1); connectionLayout->addWidget(autoConnectCheckBox, 5, 1);
QGridLayout *buttons = new QGridLayout;
buttons->addWidget(btnOk, 0, 0);
buttons->addWidget(btnForgotPassword, 0, 1);
buttons->addWidget(btnCancel, 0, 2);
QGroupBox *restrictionsGroupBox = new QGroupBox(tr("Server")); QGroupBox *restrictionsGroupBox = new QGroupBox(tr("Server"));
restrictionsGroupBox->setLayout(connectionLayout); restrictionsGroupBox->setLayout(connectionLayout);
@ -92,17 +109,16 @@ DlgConnect::DlgConnect(QWidget *parent)
QGroupBox *loginGroupBox = new QGroupBox(tr("Login")); QGroupBox *loginGroupBox = new QGroupBox(tr("Login"));
loginGroupBox->setLayout(loginLayout); loginGroupBox->setLayout(loginLayout);
QGroupBox *btnGroupBox = new QGroupBox(tr(""));
btnGroupBox->setLayout(buttons);
QGridLayout *grid = new QGridLayout; QGridLayout *grid = new QGridLayout;
grid->addWidget(restrictionsGroupBox, 0, 0); grid->addWidget(restrictionsGroupBox, 0, 0);
grid->addWidget(loginGroupBox, 1, 0); grid->addWidget(loginGroupBox, 1, 0);
grid->addWidget(btnGroupBox, 2, 0);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(actCancel()));
QVBoxLayout *mainLayout = new QVBoxLayout; QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(grid); mainLayout->addLayout(grid);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout); setLayout(mainLayout);
setWindowTitle(tr("Connect to server")); setWindowTitle(tr("Connect to server"));
@ -204,3 +220,8 @@ bool DeleteHighlightedItemWhenShiftDelPressedEventFilter::eventFilter(QObject *o
} }
return QObject::eventFilter(obj, event); return QObject::eventFilter(obj, event);
} }
void DlgConnect::actForgotPassword()
{
emit sigStartForgotPasswordRequest();
}

View file

@ -20,6 +20,8 @@ protected:
class DlgConnect : public QDialog { class DlgConnect : public QDialog {
Q_OBJECT Q_OBJECT
signals :
void sigStartForgotPasswordRequest();
public: public:
DlgConnect(QWidget *parent = 0); DlgConnect(QWidget *parent = 0);
QString getHost() const; QString getHost() const;
@ -32,12 +34,14 @@ private slots:
void passwordSaved(int state); void passwordSaved(int state);
void previousHostSelected(bool state); void previousHostSelected(bool state);
void newHostSelected(bool state); void newHostSelected(bool state);
void actForgotPassword();
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; QComboBox *previousHosts;
QRadioButton *newHostButton, *previousHostButton; QRadioButton *newHostButton, *previousHostButton;
QPushButton *btnOk, *btnCancel, *btnForgotPassword;
}; };
#endif #endif

View file

@ -0,0 +1,100 @@
#include <QLabel>
#include <QCheckBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QDebug>
#include "dlg_forgotpasswordchallenge.h"
#include "settingscache.h"
DlgForgotPasswordChallenge::DlgForgotPasswordChallenge(QWidget *parent)
: QDialog(parent)
{
QString lastfphost; QString lastfpport; QString lastfpplayername;
lastfphost = settingsCache->servers().getHostname("cockatrice.woogerworks.com");
lastfpport = settingsCache->servers().getPort("4747");
lastfpplayername = settingsCache->servers().getPlayerName("Player");
if (!settingsCache->servers().getFPHostname().isEmpty() && !settingsCache->servers().getFPPort().isEmpty() && !settingsCache->servers().getFPPlayerName().isEmpty()) {
lastfphost = settingsCache->servers().getFPHostname();
lastfpport = settingsCache->servers().getFPPort();
lastfpplayername = settingsCache->servers().getFPPlayerName();
}
if (settingsCache->servers().getFPHostname().isEmpty() && settingsCache->servers().getFPPort().isEmpty() && settingsCache->servers().getFPPlayerName().isEmpty())
{
QMessageBox::warning(this, tr("Forgot Password Challenge Warning"), tr("Oops, looks like something has gone wrong. Please restart the forgot password process by using the forgot password button on the connection screen."));
actCancel();
}
hostLabel = new QLabel(tr("&Host:"));
hostEdit = new QLineEdit(lastfphost);
hostLabel->setBuddy(hostEdit);
portLabel = new QLabel(tr("&Port:"));
portEdit = new QLineEdit(lastfpport);
portLabel->setBuddy(portEdit);
playernameLabel = new QLabel(tr("Player &name:"));
playernameEdit = new QLineEdit(lastfpplayername);
playernameLabel->setBuddy(playernameEdit);
emailLabel = new QLabel(tr("Email:"));
emailEdit = new QLineEdit();
emailLabel->setBuddy(emailLabel);
if (!settingsCache->servers().getFPHostname().isEmpty() && !settingsCache->servers().getFPPort().isEmpty() && !settingsCache->servers().getFPPlayerName().isEmpty()) {
hostLabel->hide();
hostEdit->hide();
portLabel->hide();
portEdit->hide();
playernameLabel->hide();
playernameEdit->hide();
}
QGridLayout *grid = new QGridLayout;
grid->addWidget(hostLabel, 0, 0);
grid->addWidget(hostEdit, 0, 1);
grid->addWidget(portLabel, 1, 0);
grid->addWidget(portEdit, 1, 1);
grid->addWidget(playernameLabel, 2, 0);
grid->addWidget(playernameEdit, 2, 1);
grid->addWidget(emailLabel, 3, 0);
grid->addWidget(emailEdit, 3, 1);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(actCancel()));
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(grid);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setWindowTitle(tr("Forgot Password Challenge"));
setFixedHeight(sizeHint().height());
setMinimumWidth(300);
}
void DlgForgotPasswordChallenge::actOk()
{
if (emailEdit->text().isEmpty())
{
QMessageBox::critical(this, tr("Forgot Password Challenge Warning"), tr("The email address can't be empty."));
return;
}
settingsCache->servers().setFPHostName(hostEdit->text());
settingsCache->servers().setFPPort(portEdit->text());
settingsCache->servers().setFPPlayerName(playernameEdit->text());
accept();
}
void DlgForgotPasswordChallenge::actCancel()
{
reject();
}

View file

@ -0,0 +1,28 @@
#ifndef DLG_FORGOTPASSWORDCHALLENGE_H
#define DLG_FORGOTPASSWORDCHALLENGE_H
#include <QDialog>
#include <QLineEdit>
#include <QComboBox>
class QLabel;
class QPushButton;
class QCheckBox;
class DlgForgotPasswordChallenge : public QDialog {
Q_OBJECT
public:
DlgForgotPasswordChallenge(QWidget *parent = 0);
QString getHost() const { return hostEdit->text(); }
int getPort() const { return portEdit->text().toInt(); }
QString getPlayerName() const { return playernameEdit->text(); }
QString getEmail() const { return emailEdit->text(); }
private slots:
void actOk();
void actCancel();
private:
QLabel *hostLabel, *portLabel, *playernameLabel, *emailLabel;
QLineEdit *hostEdit, *portEdit, *playernameEdit, *emailEdit;
};
#endif

View file

@ -0,0 +1,79 @@
#include <QLabel>
#include <QCheckBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QDebug>
#include "dlg_forgotpasswordrequest.h"
#include "settingscache.h"
DlgForgotPasswordRequest::DlgForgotPasswordRequest(QWidget *parent)
: QDialog(parent)
{
QString lastfphost; QString lastfpport; QString lastfpplayername;
lastfphost = settingsCache->servers().getHostname("cockatrice.woogerworks.com");
lastfpport = settingsCache->servers().getPort("4747");
lastfpplayername = settingsCache->servers().getPlayerName("Player");
if (!settingsCache->servers().getFPHostname().isEmpty() && !settingsCache->servers().getFPPort().isEmpty() && !settingsCache->servers().getFPPlayerName().isEmpty()) {
lastfphost = settingsCache->servers().getFPHostname();
lastfpport = settingsCache->servers().getFPPort();
lastfpplayername = settingsCache->servers().getFPPlayerName();
}
hostLabel = new QLabel(tr("&Host:"));
hostEdit = new QLineEdit(lastfphost);
hostLabel->setBuddy(hostEdit);
portLabel = new QLabel(tr("&Port:"));
portEdit = new QLineEdit(lastfpport);
portLabel->setBuddy(portEdit);
playernameLabel = new QLabel(tr("Player &name:"));
playernameEdit = new QLineEdit(lastfpplayername);
playernameLabel->setBuddy(playernameEdit);
QGridLayout *grid = new QGridLayout;
grid->addWidget(hostLabel, 0, 0);
grid->addWidget(hostEdit, 0, 1);
grid->addWidget(portLabel, 1, 0);
grid->addWidget(portEdit, 1, 1);
grid->addWidget(playernameLabel, 2, 0);
grid->addWidget(playernameEdit, 2, 1);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(actCancel()));
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(grid);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setWindowTitle(tr("Forgot Password Request"));
setFixedHeight(sizeHint().height());
setMinimumWidth(300);
}
void DlgForgotPasswordRequest::actOk()
{
if(playernameEdit->text().isEmpty())
{
QMessageBox::critical(this, tr("Forgot Password Request Warning"), tr("The player name can't be empty."));
return;
}
settingsCache->servers().setFPHostName(hostEdit->text());
settingsCache->servers().setFPPort(portEdit->text());
settingsCache->servers().setFPPlayerName(playernameEdit->text());
accept();
}
void DlgForgotPasswordRequest::actCancel()
{
reject();
}

View file

@ -0,0 +1,27 @@
#ifndef DLG_FORGOTPASSWORDREQUEST_H
#define DLG_FORGOTPASSWORDREQUEST_H
#include <QDialog>
#include <QLineEdit>
#include <QComboBox>
class QLabel;
class QPushButton;
class QCheckBox;
class DlgForgotPasswordRequest : public QDialog {
Q_OBJECT
public:
DlgForgotPasswordRequest(QWidget *parent = 0);
QString getHost() const { return hostEdit->text(); }
int getPort() const { return portEdit->text().toInt(); }
QString getPlayerName() const { return playernameEdit->text(); }
private slots:
void actOk();
void actCancel();
private:
QLabel *hostLabel, *portLabel, *playernameLabel;
QLineEdit *hostEdit, *portEdit, *playernameEdit;
};
#endif

View file

@ -0,0 +1,132 @@
#include <QLabel>
#include <QCheckBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QDebug>
#include "dlg_forgotpasswordreset.h"
#include "settingscache.h"
DlgForgotPasswordReset::DlgForgotPasswordReset(QWidget *parent)
: QDialog(parent)
{
QString lastfphost; QString lastfpport; QString lastfpplayername;
lastfphost = settingsCache->servers().getHostname("cockatrice.woogerworks.com");
lastfpport = settingsCache->servers().getPort("4747");
lastfpplayername = settingsCache->servers().getPlayerName("Player");
if (!settingsCache->servers().getFPHostname().isEmpty() && !settingsCache->servers().getFPPort().isEmpty() && !settingsCache->servers().getFPPlayerName().isEmpty()) {
lastfphost = settingsCache->servers().getFPHostname();
lastfpport = settingsCache->servers().getFPPort();
lastfpplayername = settingsCache->servers().getFPPlayerName();
}
if (settingsCache->servers().getFPHostname().isEmpty() && settingsCache->servers().getFPPort().isEmpty() && settingsCache->servers().getFPPlayerName().isEmpty())
{
QMessageBox::warning(this, tr("Forgot Password Reset Warning"), tr("Opps, looks like something has gone wrong. Please re-start the forgot password process by using the forgot password button on the connection screen."));
actCancel();
}
hostLabel = new QLabel(tr("&Host:"));
hostEdit = new QLineEdit(lastfphost);
hostLabel->setBuddy(hostEdit);
portLabel = new QLabel(tr("&Port:"));
portEdit = new QLineEdit(lastfpport);
portLabel->setBuddy(portEdit);
playernameLabel = new QLabel(tr("Player &name:"));
playernameEdit = new QLineEdit(lastfpplayername);
playernameLabel->setBuddy(playernameEdit);
tokenLabel = new QLabel(tr("Token:"));
tokenEdit = new QLineEdit();
tokenLabel->setBuddy(tokenLabel);
newpasswordLabel = new QLabel(tr("New Password:"));
newpasswordEdit = new QLineEdit();
newpasswordLabel->setBuddy(newpasswordEdit);
newpasswordEdit->setEchoMode(QLineEdit::Password);
newpasswordverifyLabel = new QLabel(tr("New Password:"));
newpasswordverifyEdit = new QLineEdit();
newpasswordverifyLabel->setBuddy(newpasswordEdit);
newpasswordverifyEdit->setEchoMode(QLineEdit::Password);
if (!settingsCache->servers().getFPHostname().isEmpty() && !settingsCache->servers().getFPPort().isEmpty() && !settingsCache->servers().getFPPlayerName().isEmpty()) {
hostLabel->hide();
hostEdit->hide();
portLabel->hide();
portEdit->hide();
playernameLabel->hide();
playernameEdit->hide();
}
QGridLayout *grid = new QGridLayout;
grid->addWidget(hostLabel, 0, 0);
grid->addWidget(hostEdit, 0, 1);
grid->addWidget(portLabel, 1, 0);
grid->addWidget(portEdit, 1, 1);
grid->addWidget(playernameLabel, 2, 0);
grid->addWidget(playernameEdit, 2, 1);
grid->addWidget(tokenLabel, 3, 0);
grid->addWidget(tokenEdit, 3, 1);
grid->addWidget(newpasswordLabel, 4, 0);
grid->addWidget(newpasswordEdit, 4, 1);
grid->addWidget(newpasswordverifyLabel, 5, 0);
grid->addWidget(newpasswordverifyEdit, 5, 1);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(actCancel()));
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(grid);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setWindowTitle(tr("Forgot Password Reset"));
setFixedHeight(sizeHint().height());
setMinimumWidth(300);
}
void DlgForgotPasswordReset::actOk()
{
if(playernameEdit->text().isEmpty())
{
QMessageBox::critical(this, tr("Forgot Password Reset Warning"), tr("The player name can't be empty."));
return;
}
if (tokenEdit->text().isEmpty())
{
QMessageBox::critical(this, tr("Forgot Password Reset Warning"), tr("The token can't be empty."));
return;
}
if (newpasswordEdit->text().isEmpty())
{
QMessageBox::critical(this, tr("Forgot Password Reset Warning"), tr("The new password can't be empty."));
return;
}
if (newpasswordEdit->text() != newpasswordverifyEdit->text())
{
QMessageBox::critical(this, tr("Forgot Password Reset Warning"), tr("The passwords do not match."));
return;
}
settingsCache->servers().setFPHostName(hostEdit->text());
settingsCache->servers().setFPPort(portEdit->text());
settingsCache->servers().setFPPlayerName(playernameEdit->text());
accept();
}
void DlgForgotPasswordReset::actCancel()
{
reject();
}

View file

@ -0,0 +1,29 @@
#ifndef DLG_FORGOTPASSWORDRESET_H
#define DLG_FORGOTPASSWORDRESET_H
#include <QDialog>
#include <QLineEdit>
#include <QComboBox>
class QLabel;
class QPushButton;
class QCheckBox;
class DlgForgotPasswordReset : public QDialog {
Q_OBJECT
public:
DlgForgotPasswordReset(QWidget *parent = 0);
QString getHost() const { return hostEdit->text(); }
int getPort() const { return portEdit->text().toInt(); }
QString getPlayerName() const { return playernameEdit->text(); }
QString getToken() const { return tokenEdit->text(); }
QString getPassword() const { return newpasswordEdit->text(); }
private slots:
void actOk();
void actCancel();
private:
QLabel *hostLabel, *portLabel, *playernameLabel, *tokenLabel, *newpasswordLabel, *newpasswordverifyLabel;
QLineEdit *hostEdit, *portEdit, *playernameEdit, *tokenEdit, *newpasswordEdit, *newpasswordverifyEdit;
};
#endif

View file

@ -13,6 +13,7 @@
#include "pb/response_login.pb.h" #include "pb/response_login.pb.h"
#include "pb/response_register.pb.h" #include "pb/response_register.pb.h"
#include "pb/response_activate.pb.h" #include "pb/response_activate.pb.h"
#include "pb/response_forgotpasswordrequest.pb.h"
#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"
@ -43,6 +44,9 @@ RemoteClient::RemoteClient(QObject *parent)
connect(this, SIGNAL(sigDisconnectFromServer()), this, SLOT(doDisconnectFromServer())); connect(this, SIGNAL(sigDisconnectFromServer()), this, SLOT(doDisconnectFromServer()));
connect(this, SIGNAL(sigRegisterToServer(QString, unsigned int, QString, QString, QString, int, QString, QString)), this, SLOT(doRegisterToServer(QString, unsigned int, QString, QString, QString, int, QString, QString))); connect(this, SIGNAL(sigRegisterToServer(QString, unsigned int, QString, QString, QString, int, QString, QString)), this, SLOT(doRegisterToServer(QString, unsigned int, QString, QString, QString, int, QString, QString)));
connect(this, SIGNAL(sigActivateToServer(QString)), this, SLOT(doActivateToServer(QString))); connect(this, SIGNAL(sigActivateToServer(QString)), this, SLOT(doActivateToServer(QString)));
connect(this, SIGNAL(sigRequestForgotPasswordToServer(QString, unsigned int, QString)), this, SLOT(doRequestForgotPasswordToServer(QString, unsigned int, QString)));
connect(this, SIGNAL(sigSubmitForgotPasswordResetToServer(QString, unsigned int, QString, QString, QString)), this, SLOT(doSubmitForgotPasswordResetToServer(QString, unsigned int, QString, QString, QString)));
connect(this, SIGNAL(sigSubmitForgotPasswordChallengeToServer(QString, unsigned int, QString, QString)), this, SLOT(doSubmitForgotPasswordChallengeToServer(QString, unsigned int, QString, QString)));
} }
RemoteClient::~RemoteClient() RemoteClient::~RemoteClient()
@ -77,6 +81,42 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica
return; return;
} }
if (getStatus() == StatusRequestingForgotPassword)
{
Command_ForgotPasswordRequest cmdForgotPasswordRequest;
cmdForgotPasswordRequest.set_user_name(userName.toStdString());
cmdForgotPasswordRequest.set_clientid(getSrvClientID(lastHostname).toStdString());
PendingCommand *pend = prepareSessionCommand(cmdForgotPasswordRequest);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(requestForgotPasswordResponse(Response)));
sendCommand(pend);
return;
}
if (getStatus() == StatusSubmitForgotPasswordReset)
{
Command_ForgotPasswordReset cmdForgotPasswordReset;
cmdForgotPasswordReset.set_user_name(userName.toStdString());
cmdForgotPasswordReset.set_clientid(getSrvClientID(lastHostname).toStdString());
cmdForgotPasswordReset.set_token(token.toStdString());
cmdForgotPasswordReset.set_new_password(password.toStdString());
PendingCommand *pend = prepareSessionCommand(cmdForgotPasswordReset);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(submitForgotPasswordResetResponse(Response)));
sendCommand(pend);
return;
}
if (getStatus() == StatusSubmitForgotPasswordChallenge)
{
Command_ForgotPasswordChallenge cmdForgotPasswordChallenge;
cmdForgotPasswordChallenge.set_user_name(userName.toStdString());
cmdForgotPasswordChallenge.set_clientid(getSrvClientID(lastHostname).toStdString());
cmdForgotPasswordChallenge.set_email(email.toStdString());
PendingCommand *pend = prepareSessionCommand(cmdForgotPasswordChallenge);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(submitForgotPasswordChallengeResponse(Response)));
sendCommand(pend);
return;
}
if(getStatus() == StatusRegistering) if(getStatus() == StatusRegistering)
{ {
Command_Register cmdRegister; Command_Register cmdRegister;
@ -415,4 +455,97 @@ void RemoteClient::clearNewClientFeatures()
} }
} }
settingsCache->setKnownMissingFeatures(newKnownMissingFeatures); settingsCache->setKnownMissingFeatures(newKnownMissingFeatures);
}
void RemoteClient::requestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName)
{
emit sigRequestForgotPasswordToServer(hostname, port, _userName);
}
void RemoteClient::submitForgotPasswordResetToServer(const QString & hostname, unsigned int port, const QString & _userName, const QString & _token, const QString & _newpassword)
{
emit sigSubmitForgotPasswordResetToServer(hostname, port, _userName, _token, _newpassword);
}
void RemoteClient::doRequestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName)
{
doDisconnectFromServer();
userName = _userName;
lastHostname = hostname;
lastPort = port;
socket->connectToHost(lastHostname, lastPort);
setStatus(StatusRequestingForgotPassword);
}
void RemoteClient::requestForgotPasswordResponse(const Response &response)
{
const Response_ForgotPasswordRequest &resp = response.GetExtension(Response_ForgotPasswordRequest::ext);
if (response.response_code() == Response::RespOk)
{
if (resp.challenge_email()) {
emit sigPromptForForgotPasswordChallenge();
}
else
emit sigPromptForForgotPasswordReset();
}
else
emit sigForgotPasswordError();
doDisconnectFromServer();
}
void RemoteClient::doSubmitForgotPasswordResetToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_token, const QString &_newpassword)
{
doDisconnectFromServer();
userName = _userName;
lastHostname = hostname;
lastPort = port;
token = _token;
password = _newpassword;
socket->connectToHost(lastHostname, lastPort);
setStatus(StatusSubmitForgotPasswordReset);
}
void RemoteClient::submitForgotPasswordResetResponse(const Response &response)
{
if (response.response_code() == Response::RespOk) {
emit sigForgotPasswordSuccess();
} else
emit sigForgotPasswordError();
doDisconnectFromServer();
}
void RemoteClient::submitForgotPasswordChallengeToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_email)
{
emit sigSubmitForgotPasswordChallengeToServer(hostname, port, _userName, _email);
}
void RemoteClient::doSubmitForgotPasswordChallengeToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_email)
{
doDisconnectFromServer();
userName = _userName;
lastHostname = hostname;
lastPort = port;
email = _email;
socket->connectToHost(lastHostname, lastPort);
setStatus(StatusSubmitForgotPasswordChallenge);
}
void RemoteClient::submitForgotPasswordChallengeResponse(const Response &response)
{
if (response.response_code() == Response::RespOk) {
emit sigPromptForForgotPasswordReset();
}
else
emit sigForgotPasswordError();
doDisconnectFromServer();
} }

View file

@ -22,6 +22,13 @@ signals:
void sigActivateToServer(const QString &_token); void sigActivateToServer(const QString &_token);
void sigDisconnectFromServer(); void sigDisconnectFromServer();
void notifyUserAboutUpdate(); void notifyUserAboutUpdate();
void sigRequestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName);
void sigForgotPasswordSuccess();
void sigForgotPasswordError();
void sigPromptForForgotPasswordReset();
void sigSubmitForgotPasswordResetToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_token, const QString &_newpassword);
void sigPromptForForgotPasswordChallenge();
void sigSubmitForgotPasswordChallengeToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_email);
private slots: private slots:
void slotConnected(); void slotConnected();
void readData(); void readData();
@ -37,7 +44,12 @@ private slots:
void doLogin(); void doLogin();
void doDisconnectFromServer(); void doDisconnectFromServer();
void doActivateToServer(const QString &_token); void doActivateToServer(const QString &_token);
void doRequestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName);
void requestForgotPasswordResponse(const Response &response);
void doSubmitForgotPasswordResetToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_token, const QString &_newpassword);
void submitForgotPasswordResetResponse(const Response &response);
void doSubmitForgotPasswordChallengeToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_email);
void submitForgotPasswordChallengeResponse(const Response &response);
private: private:
static const int maxTimeout = 10; static const int maxTimeout = 10;
int timeRunning, lastDataReceived; int timeRunning, lastDataReceived;
@ -64,6 +76,9 @@ public:
void registerToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname); void registerToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname);
void activateToServer(const QString &_token); void activateToServer(const QString &_token);
void disconnectFromServer(); void disconnectFromServer();
void requestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName);
void submitForgotPasswordResetToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_token, const QString &_newpassword);
void submitForgotPasswordChallengeToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_email);
}; };
#endif #endif

View file

@ -100,3 +100,36 @@ int ServersSettings::getAutoConnect()
QVariant autoconnect = getValue("auto_connect", "server"); QVariant autoconnect = getValue("auto_connect", "server");
return autoconnect == QVariant() ? 0 : autoconnect.toInt(); return autoconnect == QVariant() ? 0 : autoconnect.toInt();
} }
void ServersSettings::setFPHostName(QString hostname)
{
setValue(hostname, "fphostname", "server");
}
QString ServersSettings::getFPHostname(QString defaultHost)
{
QVariant hostname = getValue("fphostname", "server");
return hostname == QVariant() ? defaultHost : hostname.toString();
}
void ServersSettings::setFPPort(QString port)
{
setValue(port, "fpport", "server");
}
QString ServersSettings::getFPPort(QString defaultPort)
{
QVariant port = getValue("fpport", "server");
return port == QVariant() ? defaultPort : port.toString();
}
void ServersSettings::setFPPlayerName(QString playerName)
{
setValue(playerName, "fpplayername", "server");
}
QString ServersSettings::getFPPlayerName(QString defaultName)
{
QVariant name = getValue("fpplayername", "server");
return name == QVariant() ? defaultName : name.toString();
}

View file

@ -16,6 +16,9 @@ public:
QString getHostname(QString defaultHost = ""); QString getHostname(QString defaultHost = "");
QString getPort(QString defaultPort = ""); QString getPort(QString defaultPort = "");
QString getPlayerName(QString defaultName = ""); QString getPlayerName(QString defaultName = "");
QString getFPHostname(QString defaultHost = "");
QString getFPPort(QString defaultPort = "");
QString getFPPlayerName(QString defaultName = "");
QString getPassword(); QString getPassword();
int getSavePassword(); int getSavePassword();
int getAutoConnect(); int getAutoConnect();
@ -29,6 +32,9 @@ public:
void setPassword(QString password); void setPassword(QString password);
void setSavePassword(int save); void setSavePassword(int save);
void setAutoConnect(int autoconnect); void setAutoConnect(int autoconnect);
void setFPHostName(QString hostname);
void setFPPort(QString port);
void setFPPlayerName(QString playerName);
signals: signals:
public slots: public slots:

View file

@ -63,6 +63,9 @@ void SettingsCache::translateLegacySettings()
servers().setPassword(legacySetting.value("password").toString()); servers().setPassword(legacySetting.value("password").toString());
servers().setSavePassword(legacySetting.value("save_password").toInt()); servers().setSavePassword(legacySetting.value("save_password").toInt());
servers().setAutoConnect(legacySetting.value("auto_connect").toInt()); servers().setAutoConnect(legacySetting.value("auto_connect").toInt());
servers().setFPHostName(legacySetting.value("fphostname").toString());
servers().setFPPort(legacySetting.value("fpport").toString());
servers().setFPPlayerName(legacySetting.value("fpplayername").toString());
usedKeys.append(legacySetting.allKeys()); usedKeys.append(legacySetting.allKeys());
QStringList allKeysServer = legacySetting.allKeys(); QStringList allKeysServer = legacySetting.allKeys();
for (int i = 0; i < allKeysServer.size(); ++i) { for (int i = 0; i < allKeysServer.size(); ++i) {

View file

@ -36,6 +36,9 @@
#include "main.h" #include "main.h"
#include "window_main.h" #include "window_main.h"
#include "dlg_connect.h" #include "dlg_connect.h"
#include "dlg_forgotpasswordrequest.h"
#include "dlg_forgotpasswordreset.h"
#include "dlg_forgotpasswordchallenge.h"
#include "dlg_register.h" #include "dlg_register.h"
#include "dlg_settings.h" #include "dlg_settings.h"
#include "dlg_update.h" #include "dlg_update.h"
@ -166,9 +169,10 @@ void MainWindow::activateAccepted()
void MainWindow::actConnect() void MainWindow::actConnect()
{ {
DlgConnect dlg(this); DlgConnect *dlg = new DlgConnect(this);
if (dlg.exec()) connect(dlg, SIGNAL(sigStartForgotPasswordRequest()), this, SLOT(actForgotPasswordRequest()));
client->connectToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName(), dlg.getPassword()); if (dlg->exec())
client->connectToServer(dlg->getHost(), dlg->getPort(), dlg->getPlayerName(), dlg->getPassword());
} }
void MainWindow::actRegister() void MainWindow::actRegister()
@ -506,6 +510,9 @@ void MainWindow::setClientStatusTitle()
case StatusDisconnected: setWindowTitle(appName + " - " + tr("Disconnected")); break; case StatusDisconnected: setWindowTitle(appName + " - " + tr("Disconnected")); break;
case StatusLoggingIn: setWindowTitle(appName + " - " + tr("Connected, logging in at %1").arg(client->peerName())); break; case StatusLoggingIn: setWindowTitle(appName + " - " + tr("Connected, logging in at %1").arg(client->peerName())); break;
case StatusLoggedIn: setWindowTitle(client->getUserName() + "@" + client->peerName()); break; case StatusLoggedIn: setWindowTitle(client->getUserName() + "@" + client->peerName()); break;
case StatusRequestingForgotPassword: setWindowTitle(appName + " - " + tr("Requesting forgot password to %1 as %2...").arg(client->peerName()).arg(client->getUserName())); break;
case StatusSubmitForgotPasswordChallenge: setWindowTitle(appName + " - " + tr("Requesting forgot password to %1 as %2...").arg(client->peerName()).arg(client->getUserName())); break;
case StatusSubmitForgotPasswordReset: setWindowTitle(appName + " - " + tr("Requesting forgot password to %1 as %2...").arg(client->peerName()).arg(client->getUserName())); break;
default: setWindowTitle(appName); default: setWindowTitle(appName);
} }
} }
@ -666,6 +673,10 @@ MainWindow::MainWindow(QWidget *parent)
connect(client, SIGNAL(registerError(Response::ResponseCode, QString, quint32)), this, SLOT(registerError(Response::ResponseCode, QString, quint32))); connect(client, SIGNAL(registerError(Response::ResponseCode, QString, quint32)), this, SLOT(registerError(Response::ResponseCode, QString, quint32)));
connect(client, SIGNAL(activateAccepted()), this, SLOT(activateAccepted())); connect(client, SIGNAL(activateAccepted()), this, SLOT(activateAccepted()));
connect(client, SIGNAL(activateError()), this, SLOT(activateError())); connect(client, SIGNAL(activateError()), this, SLOT(activateError()));
connect(client, SIGNAL(sigForgotPasswordSuccess()), this, SLOT(forgotPasswordSuccess()));
connect(client, SIGNAL(sigForgotPasswordError()), this, SLOT(forgotPasswordError()));
connect(client, SIGNAL(sigPromptForForgotPasswordReset()), this, SLOT(promptForgotPasswordReset()));
connect(client, SIGNAL(sigPromptForForgotPasswordChallenge()), this, SLOT(promptForgotPasswordChallenge()));
clientThread = new QThread(this); clientThread = new QThread(this);
client->moveToThread(clientThread); client->moveToThread(clientThread);
@ -734,6 +745,13 @@ void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason) {
} }
} }
void MainWindow::promptForgotPasswordChallenge()
{
DlgForgotPasswordChallenge dlg(this);
if (dlg.exec())
client->submitForgotPasswordChallengeToServer(dlg.getHost(),dlg.getPort(),dlg.getPlayerName(),dlg.getEmail());
}
void MainWindow::createTrayActions() { void MainWindow::createTrayActions() {
closeAction = new QAction(tr("&Exit"), this); closeAction = new QAction(tr("&Exit"), this);
@ -1054,3 +1072,34 @@ void MainWindow::actEditTokens()
dlg.exec(); dlg.exec();
db->saveCustomTokensToFile(); db->saveCustomTokensToFile();
} }
void MainWindow::actForgotPasswordRequest()
{
DlgForgotPasswordRequest dlg(this);
if (dlg.exec())
client->requestForgotPasswordToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName());
}
void MainWindow::forgotPasswordSuccess()
{
QMessageBox::information(this, tr("Forgot Password"), tr("Your password has been reset successfully, you now may log in using the new credentials."));
settingsCache->servers().setFPHostName("");
settingsCache->servers().setFPPort("");
settingsCache->servers().setFPPlayerName("");
}
void MainWindow::forgotPasswordError()
{
QMessageBox::warning(this, tr("Forgot Password"), tr("Failed to reset user account password, please contact the server operator to reset your password."));
settingsCache->servers().setFPHostName("");
settingsCache->servers().setFPPort("");
settingsCache->servers().setFPPlayerName("");
}
void MainWindow::promptForgotPasswordReset()
{
QMessageBox::information(this, tr("Forgot Password"), tr("Activation request received, please check your email for an activation token."));
DlgForgotPasswordReset dlg(this);
if (dlg.exec())
client->submitForgotPasswordResetToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName(), dlg.getToken(), dlg.getPassword());
}

View file

@ -67,13 +67,15 @@ private slots:
void actRegister(); void actRegister();
void actSettings(); void actSettings();
void actExit(); void actExit();
void actForgotPasswordRequest();
void actAbout(); void actAbout();
void actUpdate(); void actUpdate();
void actViewLog(); void actViewLog();
void forgotPasswordSuccess();
void forgotPasswordError();
void promptForgotPasswordReset();
void iconActivated(QSystemTrayIcon::ActivationReason reason); void iconActivated(QSystemTrayIcon::ActivationReason reason);
void promptForgotPasswordChallenge();
void showWindowIfHidden(); void showWindowIfHidden();
void actCheckCardUpdates(); void actCheckCardUpdates();

View file

@ -21,6 +21,7 @@ void FeatureSet::initalizeFeatureList(QMap<QString, bool> &featureList) {
featureList.insert("client_warnings", false); featureList.insert("client_warnings", false);
featureList.insert("mod_log_lookup", false); featureList.insert("mod_log_lookup", false);
featureList.insert("idle_client", false); featureList.insert("idle_client", false);
featureList.insert("forgot_password", false);
} }
void FeatureSet::enableRequiredFeature(QMap<QString, bool> &featureList, QString featureName){ void FeatureSet::enableRequiredFeature(QMap<QString, bool> &featureList, QString featureName){

View file

@ -119,6 +119,7 @@ SET(PROTO_FILES
response_deck_list.proto response_deck_list.proto
response_deck_upload.proto response_deck_upload.proto
response_dump_zone.proto response_dump_zone.proto
response_forgotpasswordrequest.proto
response_get_games_of_user.proto response_get_games_of_user.proto
response_get_user_info.proto response_get_user_info.proto
response_join_room.proto response_join_room.proto

View file

@ -58,6 +58,7 @@ message Response {
WARN_HISTORY = 1013; WARN_HISTORY = 1013;
WARN_LIST = 1014; WARN_LIST = 1014;
VIEW_LOG = 1015; VIEW_LOG = 1015;
FORGOT_PASSWORD_REQUEST = 1016;
REPLAY_LIST = 1100; REPLAY_LIST = 1100;
REPLAY_DOWNLOAD = 1101; REPLAY_DOWNLOAD = 1101;
} }

View file

@ -0,0 +1,9 @@
syntax = "proto2";
import "response.proto";
message Response_ForgotPasswordRequest {
extend Response {
optional Response_ForgotPasswordRequest ext = 1016;
}
optional bool challenge_email = 1;
}

View file

@ -24,6 +24,9 @@ message SessionCommand {
ACCOUNT_EDIT = 1018; ACCOUNT_EDIT = 1018;
ACCOUNT_IMAGE = 1019; ACCOUNT_IMAGE = 1019;
ACCOUNT_PASSWORD = 1020; ACCOUNT_PASSWORD = 1020;
FORGOT_PASSWORD_REQUEST = 1021;
FORGOT_PASSWORD_RESET = 1022;
FORGOT_PASSWORD_CHALLENGE = 1023;
REPLAY_LIST = 1100; REPLAY_LIST = 1100;
REPLAY_DOWNLOAD = 1101; REPLAY_DOWNLOAD = 1101;
REPLAY_MODIFY_MATCH = 1102; REPLAY_MODIFY_MATCH = 1102;
@ -160,3 +163,30 @@ message Command_AccountPassword {
optional string old_password = 1; optional string old_password = 1;
optional string new_password = 2; optional string new_password = 2;
} }
message Command_ForgotPasswordRequest {
extend SessionCommand {
optional Command_ForgotPasswordRequest ext = 1021;
}
required string user_name = 1;
optional string clientid = 2;
}
message Command_ForgotPasswordReset {
extend SessionCommand {
optional Command_ForgotPasswordReset ext = 1022;
}
required string user_name = 1;
optional string clientid = 2;
optional string token = 3;
optional string new_password = 4;
}
message Command_ForgotPasswordChallenge {
extend SessionCommand {
optional Command_ForgotPasswordChallenge ext = 1023;
}
required string user_name = 1;
optional string clientid = 2;
optional string email = 3;
}

View file

@ -21,7 +21,7 @@ public:
virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0; virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0;
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 bool removeForgotPassword(const QString & /* user */) { return false; }
virtual qint64 startSession(const QString & /* userName */, const QString & /* address */, const QString & /* clientId */, const QString & /* connectionType */) { return 0; } virtual qint64 startSession(const QString & /* userName */, const QString & /* address */, const QString & /* clientId */, const QString & /* connectionType */) { 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:
@ -46,7 +46,7 @@ public:
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 */) { };
bool checkUserIsBanned(Server_ProtocolHandler *session, QString &banReason, int &banSecondsRemaining); bool checkUserIsBanned(Server_ProtocolHandler *session, QString &banReason, int &banSecondsRemaining);
virtual int checkNumberOfUserAccounts(const QString & /* email */) { return 0; }; virtual int checkNumberOfUserAccounts(const QString & /* email */) { return 0; };
virtual bool changeUserPassword(const QString & /* user */, const QString & /* oldPassword */, const QString & /* newPassword */) { return true; }; virtual bool changeUserPassword(const QString & /* user */, const QString & /* oldPassword */, const QString & /* newPassword */, const bool & /* force */) { return false; };
virtual QChar getGenderChar(ServerInfo_User_Gender const & /* gender */) { return QChar('u'); }; virtual QChar getGenderChar(ServerInfo_User_Gender const & /* gender */) { return QChar('u'); };
}; };

View file

@ -507,7 +507,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
} }
joinPersistentGames(rc); joinPersistentGames(rc);
databaseInterface->removeForgotPassword(userName);
rc.setResponseExtension(re); rc.setResponseExtension(re);
return Response::RespOk; return Response::RespOk;
} }

View file

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS `cockatrice_forgot_password` (
`id` int(7) unsigned zerofill NOT NULL auto_increment,
`name` varchar(35) NOT NULL,
`requestDate` datetime NOT NULL default '0000-00-00 00:00:00',
`emailed` tinyint(1) NOT NULL default 0,
PRIMARY KEY (`id`),
KEY `user_name` (`name`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
UPDATE cockatrice_schema_version SET version=21 WHERE version=20;

View file

@ -142,7 +142,7 @@ disallowedregexp=""
; Set this number to the maximum number of accounts any one user can use to create new accounts ; Set this number to the maximum number of accounts any one user can use to create new accounts
; using the same email address. 0 = Unlimited number of accounts (default). ; using the same email address. 0 = Unlimited number of accounts (default).
;maxaccountsperemail=0 ;maxaccountsperemail=0
; You can prevent users from using certain mail domains for registration. This setting contains a ; You can prevent users from using certain mail domains for registration. This setting contains a
; comma-seperated list of email provider domains that you would like to prevent users from using ; comma-seperated list of email provider domains that you would like to prevent users from using
@ -151,6 +151,31 @@ disallowedregexp=""
; Example: "10minutemail.com,gmail.com" ; Example: "10minutemail.com,gmail.com"
;emailproviderblacklist="" ;emailproviderblacklist=""
[forgotpassword]
; Servatrice can process forgot password requests allowing users to reset their account
; passwords in the event they forget it. Should this feature be enabled? Default: false.
; enable=false
; Forgot password request should not be allowed to stay valid forever. This settings
; informs servatrice how long a players forgot password reset token is valid for (in minutes).
; Default: 60
; tokenlife=60
; Servatrice can challenge users that are making forgot password requests to answer
; questions in regards to their account to help validate they are the true owner of the account.
; Should this feature be enabled? Default: false
; enablechallenge=false
; Email subject for the forgot password emails
; subject="Cockatrice forgot password token"
; Forgot password email body. You can use these tags here: %username %token
; They will be substituted with the actual values in the email
;
; body="Hi %username, sorry to hear you forgot your password on our Cockatrice server\r\nHere's the token to use to reset your account password:\r\n\r\n%token\r\n\r\nHappy gaming!"
[smtp] [smtp]
; Enable the internal smtp client to send registration emails. If you would like to ; Enable the internal smtp client to send registration emails. If you would like to

View file

@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_schema_version` (
PRIMARY KEY (`version`) PRIMARY KEY (`version`)
) ENGINE=INNODB DEFAULT CHARSET=utf8; ) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO cockatrice_schema_version VALUES(20); INSERT INTO cockatrice_schema_version VALUES(21);
-- users and user data tables -- users and user data tables
CREATE TABLE IF NOT EXISTS `cockatrice_users` ( CREATE TABLE IF NOT EXISTS `cockatrice_users` (
@ -258,3 +258,12 @@ CREATE TABLE IF NOT EXISTS `cockatrice_donations` (
`pp_type` varchar(255) DEFAULT NULL, `pp_type` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `cockatrice_forgot_password` (
`id` int(7) unsigned zerofill NOT NULL auto_increment,
`name` varchar(35) NOT NULL,
`requestDate` datetime NOT NULL default '0000-00-00 00:00:00',
`emailed` tinyint(1) NOT NULL default 0,
PRIMARY KEY (`id`),
KEY `user_name` (`name`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

View file

@ -252,6 +252,12 @@ bool Servatrice::initServer()
} }
} }
qDebug() << "Forgot password enabled: " << getEnableForgotPassword();
if (getEnableForgotPassword()) {
qDebug() << "Forgot password token life (in minutes): " << getForgotPasswordTokenLife();
qDebug() << "Forgot password challenge on: " << getEnableForgotPasswordChallenge();
}
if (getDBTypeString() == "mysql") { if (getDBTypeString() == "mysql") {
databaseType = DatabaseMySql; databaseType = DatabaseMySql;
} else { } else {
@ -542,28 +548,51 @@ void Servatrice::statusUpdate()
query->bindValue(":rx", rx); query->bindValue(":rx", rx);
servatriceDatabaseInterface->execSqlQuery(query); servatriceDatabaseInterface->execSqlQuery(query);
// send activation emails if (getRegistrationEnabled() && getEnableInternalSMTPClient())
if (getRegistrationEnabled() && getRequireEmailActivationEnabled() && getEnableInternalSMTPClient())
{ {
QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select a.name, b.email, b.token from {prefix}_activation_emails a left join {prefix}_users b on a.name = b.name"); if (getRequireEmailActivationEnabled())
if (!servatriceDatabaseInterface->execSqlQuery(query)) {
return; QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select a.name, b.email, b.token from {prefix}_activation_emails a left join {prefix}_users b on a.name = b.name");
if (!servatriceDatabaseInterface->execSqlQuery(query))
return;
QSqlQuery *queryDelete = servatriceDatabaseInterface->prepareQuery("delete from {prefix}_activation_emails where name = :name"); QSqlQuery *queryDelete = servatriceDatabaseInterface->prepareQuery("delete from {prefix}_activation_emails where name = :name");
while (query->next()) { while (query->next()) {
const QString userName = query->value(0).toString(); const QString userName = query->value(0).toString();
const QString emailAddress = query->value(1).toString(); const QString emailAddress = query->value(1).toString();
const QString token = query->value(2).toString(); const QString token = query->value(2).toString();
if(smtpClient->enqueueActivationTokenMail(userName, emailAddress, token)) if (smtpClient->enqueueActivationTokenMail(userName, emailAddress, token))
{ {
queryDelete->bindValue(":name", userName); queryDelete->bindValue(":name", userName);
servatriceDatabaseInterface->execSqlQuery(queryDelete); servatriceDatabaseInterface->execSqlQuery(queryDelete);
} }
} }
}
smtpClient->sendAllEmails(); if (getEnableForgotPassword())
{
QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select a.name, b.email, b.token from {prefix}_forgot_password a left join {prefix}_users b on a.name = b.name where a.emailed = 0");
if (!servatriceDatabaseInterface->execSqlQuery(query))
return;
QSqlQuery *queryDelete = servatriceDatabaseInterface->prepareQuery("update {prefix}_forgot_password set emailed = 1 where name = :name");
while (query->next()) {
const QString userName = query->value(0).toString();
const QString emailAddress = query->value(1).toString();
const QString token = query->value(2).toString();
if (smtpClient->enqueueForgotPasswordTokenMail(userName, emailAddress, token))
{
queryDelete->bindValue(":name", userName);
servatriceDatabaseInterface->execSqlQuery(queryDelete);
}
}
}
smtpClient->sendAllEmails();
} }
} }
@ -847,6 +876,18 @@ bool Servatrice::getEnableInternalSMTPClient() const {
return settingsCache->value("smtp/enableinternalsmtpclient", true).toBool(); return settingsCache->value("smtp/enableinternalsmtpclient", true).toBool();
} }
bool Servatrice::getEnableForgotPassword() const {
return settingsCache->value("forgotpassword/enable", false).toBool();
}
int Servatrice::getForgotPasswordTokenLife() const {
return settingsCache->value("forgotpassword/tokenlife", 60).toInt();
}
bool Servatrice::getEnableForgotPasswordChallenge() const {
return settingsCache->value("forgotpassword/enablechallenge", false).toBool();
}
QString Servatrice::getEmailBlackList() const { QString Servatrice::getEmailBlackList() const {
return settingsCache->value("registration/emailproviderblacklist").toString(); return settingsCache->value("registration/emailproviderblacklist").toString();
} }

View file

@ -154,7 +154,6 @@ private:
QString getRoomsMethodString() const; QString getRoomsMethodString() const;
QString getISLNetworkSSLCertFile() const; QString getISLNetworkSSLCertFile() const;
QString getISLNetworkSSLKeyFile() const; QString getISLNetworkSSLKeyFile() const;
int getServerStatusUpdateTime() const; int getServerStatusUpdateTime() const;
int getNumberOfTCPPools() const; int getNumberOfTCPPools() const;
int getServerTCPPort() const; int getServerTCPPort() const;
@ -192,6 +191,8 @@ public:
bool getRequireEmailForRegistrationEnabled() const; bool getRequireEmailForRegistrationEnabled() const;
bool getRequireEmailActivationEnabled() const; bool getRequireEmailActivationEnabled() const;
bool getEnableLogQuery() const; bool getEnableLogQuery() const;
bool getEnableForgotPassword() const;
bool getEnableForgotPasswordChallenge() const;
int getIdleClientTimeout() const; int getIdleClientTimeout() const;
int getServerID() const; int getServerID() const;
int getMaxGameInactivityTime() const; int getMaxGameInactivityTime() const;
@ -209,6 +210,7 @@ public:
int getMaxWebSocketUserLimit() const; int getMaxWebSocketUserLimit() const;
int getUsersWithAddress(const QHostAddress &address) const; int getUsersWithAddress(const QHostAddress &address) const;
int getMaxAccountsPerEmail() const; int getMaxAccountsPerEmail() const;
int getForgotPasswordTokenLife() const;
QList<AbstractServerSocketInterface *> getUsersWithAddressAsList(const QHostAddress &address) const; QList<AbstractServerSocketInterface *> getUsersWithAddressAsList(const QHostAddress &address) const;
void incTxBytes(quint64 num); void incTxBytes(quint64 num);
void incRxBytes(quint64 num); void incRxBytes(quint64 num);

View file

@ -176,9 +176,9 @@ bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const Q
token = active ? QString() : PasswordHasher::generateActivationToken(); token = active ? QString() : PasswordHasher::generateActivationToken();
QSqlQuery *query = prepareQuery("insert into {prefix}_users " QSqlQuery *query = prepareQuery("insert into {prefix}_users "
"(name, realname, gender, password_sha512, email, country, registrationDate, active, token) " "(name, realname, gender, password_sha512, email, country, registrationDate, active, token, admin, avatar_bmp, clientid, privlevel, privlevelStartDate, privlevelEndDate) "
"values " "values "
"(:userName, :realName, :gender, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active, :token)"); "(:userName, :realName, :gender, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active, :token, 0, '', '', 'NONE', UTC_TIMESTAMP(), UTC_TIMESTAMP())");
query->bindValue(":userName", userName); query->bindValue(":userName", userName);
query->bindValue(":realName", realName); query->bindValue(":realName", realName);
query->bindValue(":gender", getGenderChar(gender)); query->bindValue(":gender", getGenderChar(gender));
@ -831,41 +831,43 @@ void Servatrice_DatabaseInterface::logMessage(const int senderId, const QString
execSqlQuery(query); execSqlQuery(query);
} }
bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword) bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword, const bool &force = false)
{ {
if(server->getAuthenticationMethod() != Servatrice::AuthenticationSql) if(server->getAuthenticationMethod() != Servatrice::AuthenticationSql)
return true; return false;
if (!checkSql()) if (!checkSql())
return true; return false;
QString error; QString error;
if (!usernameIsValid(user, error)) if (!usernameIsValid(user, error))
return true; return false;
QSqlQuery *passwordQuery = prepareQuery("select password_sha512 from {prefix}_users where name = :name"); QSqlQuery *passwordQuery = prepareQuery("select password_sha512 from {prefix}_users where name = :name");
passwordQuery->bindValue(":name", user); passwordQuery->bindValue(":name", user);
if (!execSqlQuery(passwordQuery)) {
qDebug("Change password denied: SQL error"); if (!force) {
return true; if (!execSqlQuery(passwordQuery)) {
qDebug("Change password denied: SQL error");
return false;
}
if (!passwordQuery->next())
return false;
const QString correctPassword = passwordQuery->value(0).toString();
if (correctPassword != PasswordHasher::computeHash(oldPassword, correctPassword.left(16)))
return false;
} }
if (!passwordQuery->next())
return true;
const QString correctPassword = passwordQuery->value(0).toString();
if (correctPassword != PasswordHasher::computeHash(oldPassword, correctPassword.left(16)))
return true;
QString passwordSha512 = PasswordHasher::computeHash(newPassword, PasswordHasher::generateRandomSalt()); QString passwordSha512 = PasswordHasher::computeHash(newPassword, PasswordHasher::generateRandomSalt());
passwordQuery = prepareQuery("update {prefix}_users set password_sha512=:password where name = :name"); passwordQuery = prepareQuery("update {prefix}_users set password_sha512=:password where name = :name");
passwordQuery->bindValue(":password", passwordSha512); passwordQuery->bindValue(":password", passwordSha512);
passwordQuery->bindValue(":name", user); passwordQuery->bindValue(":name", user);
if (!execSqlQuery(passwordQuery)) { if (execSqlQuery(passwordQuery))
qDebug("Change password denied: SQL error");
return true; return true;
}
return false; return false;
} }
@ -1128,3 +1130,91 @@ int Servatrice_DatabaseInterface::checkNumberOfUserAccounts(const QString &email
return 0; return 0;
} }
bool Servatrice_DatabaseInterface::addForgotPassword(const QString &user)
{
if (!checkSql())
return false;
if (!updateUserToken(PasswordHasher::generateActivationToken(), user))
return false;
QSqlQuery *query = prepareQuery("insert into {prefix}_forgot_password (name,requestDate) values (:username,NOW())");
query->bindValue(":username", user);
if (execSqlQuery(query))
return true;
return false;
}
bool Servatrice_DatabaseInterface::removeForgotPassword(const QString &user)
{
if (!checkSql())
return false;
QSqlQuery *query = prepareQuery("delete from {prefix}_forgot_password where name = :username");
query->bindValue(":username", user);
if (execSqlQuery(query))
return true;
return false;
}
bool Servatrice_DatabaseInterface::doesForgotPasswordExist(const QString &user)
{
if (!checkSql())
return false;
QSqlQuery *query = prepareQuery("select count(name) from {prefix}_forgot_password where name = :user_name AND requestDate > (now() - interval :minutes minute)");
query->bindValue(":user_name", user);
query->bindValue(":minutes", QString::number(server->getForgotPasswordTokenLife()));
if (!execSqlQuery(query))
return false;
if (query->next())
if (query->value("count(name)").toInt() > 0)
return true;
return false;
}
bool Servatrice_DatabaseInterface::updateUserToken(const QString & token, const QString &user)
{
if (!checkSql())
return false;
if (token.isEmpty() || user.isEmpty())
return false;
QSqlQuery *query = prepareQuery("update {prefix}_users set token = :token where name = :user_name");
query->bindValue(":user_name", user);
query->bindValue(":token", token);
if (execSqlQuery(query))
return true;
return false;
}
bool Servatrice_DatabaseInterface::validateTableColumnStringData(const QString &table, const QString &column, const QString &_user, const QString &_datatocheck)
{
if (!checkSql())
return false;
if (table.isEmpty() || column.isEmpty() ||_user.isEmpty() || _datatocheck.isEmpty())
return false;
QString formatedQuery = QString("select %1 from %2 where name = :user_name").arg(column).arg(table);
QSqlQuery *query = prepareQuery(formatedQuery);
query->bindValue(":user_name", _user);
if (!execSqlQuery(query))
return false;
if (query->next())
if (query->value(column).toString() == _datatocheck)
return true;
return false;
}

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 20 #define DATABASE_SCHEMA_VERSION 21
class Servatrice; class Servatrice;
@ -70,19 +70,22 @@ public:
bool usernameIsValid(const QString &user, QString & error); bool usernameIsValid(const QString &user, QString & error);
bool checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining); bool checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining);
int checkNumberOfUserAccounts(const QString &email); int checkNumberOfUserAccounts(const QString &email);
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 updateUsersLastLoginData(const QString &userName, const QString &clientVersion); void updateUsersLastLoginData(const QString &userName, const QString &clientVersion);
void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, 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, const bool &force);
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);
QList<ServerInfo_Ban> getUserBanHistory(const QString userName); QList<ServerInfo_Ban> getUserBanHistory(const QString userName);
bool addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID); bool addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID);
QList<ServerInfo_Warning> getUserWarnHistory(const QString userName); QList<ServerInfo_Warning> getUserWarnHistory(const QString userName);
QList<ServerInfo_ChatMessage> getMessageLogHistory(const QString &user, const QString &ipaddress, const QString &gamename, const QString &gameid, const QString &message, bool &chat, bool &game, bool &room, int &range, int &maxresults); QList<ServerInfo_ChatMessage> getMessageLogHistory(const QString &user, const QString &ipaddress, const QString &gamename, const QString &gameid, const QString &message, bool &chat, bool &game, bool &room, int &range, int &maxresults);
bool addForgotPassword(const QString &user);
bool removeForgotPassword(const QString &user);
bool doesForgotPasswordExist(const QString &user);
bool updateUserToken(const QString &token, const QString &user);
bool validateTableColumnStringData(const QString &table, const QString &column, const QString &_user, const QString &_datatocheck);
}; };
#endif #endif

View file

@ -62,6 +62,7 @@
#include "pb/response_warn_history.pb.h" #include "pb/response_warn_history.pb.h"
#include "pb/response_warn_list.pb.h" #include "pb/response_warn_list.pb.h"
#include "pb/response_viewlog_history.pb.h" #include "pb/response_viewlog_history.pb.h"
#include "pb/response_forgotpasswordrequest.pb.h"
#include "pb/serverinfo_replay.pb.h" #include "pb/serverinfo_replay.pb.h"
#include "pb/serverinfo_user.pb.h" #include "pb/serverinfo_user.pb.h"
#include "pb/serverinfo_deckstorage.pb.h" #include "pb/serverinfo_deckstorage.pb.h"
@ -150,7 +151,9 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedSessionComm
case SessionCommand::REPLAY_DELETE_MATCH: return cmdReplayDeleteMatch(cmd.GetExtension(Command_ReplayDeleteMatch::ext), rc); case SessionCommand::REPLAY_DELETE_MATCH: return cmdReplayDeleteMatch(cmd.GetExtension(Command_ReplayDeleteMatch::ext), rc);
case SessionCommand::REGISTER: return cmdRegisterAccount(cmd.GetExtension(Command_Register::ext), rc); break; case SessionCommand::REGISTER: return cmdRegisterAccount(cmd.GetExtension(Command_Register::ext), rc); break;
case SessionCommand::ACTIVATE: return cmdActivateAccount(cmd.GetExtension(Command_Activate::ext), rc); break; case SessionCommand::ACTIVATE: return cmdActivateAccount(cmd.GetExtension(Command_Activate::ext), rc); break;
case SessionCommand::FORGOT_PASSWORD_REQUEST: return cmdForgotPasswordRequest(cmd.GetExtension(Command_ForgotPasswordRequest::ext), rc); break;
case SessionCommand::FORGOT_PASSWORD_RESET: return cmdForgotPasswordReset(cmd.GetExtension(Command_ForgotPasswordReset::ext), rc); break;
case SessionCommand::FORGOT_PASSWORD_CHALLENGE: return cmdForgotPasswordChallenge(cmd.GetExtension(Command_ForgotPasswordChallenge::ext), rc); break;
case SessionCommand::ACCOUNT_EDIT: return cmdAccountEdit(cmd.GetExtension(Command_AccountEdit::ext), rc); case SessionCommand::ACCOUNT_EDIT: return cmdAccountEdit(cmd.GetExtension(Command_AccountEdit::ext), rc);
case SessionCommand::ACCOUNT_IMAGE: return cmdAccountImage(cmd.GetExtension(Command_AccountImage::ext), rc); case SessionCommand::ACCOUNT_IMAGE: return cmdAccountImage(cmd.GetExtension(Command_AccountImage::ext), rc);
case SessionCommand::ACCOUNT_PASSWORD: return cmdAccountPassword(cmd.GetExtension(Command_AccountPassword::ext), rc); case SessionCommand::ACCOUNT_PASSWORD: return cmdAccountPassword(cmd.GetExtension(Command_AccountPassword::ext), rc);
@ -1054,14 +1057,86 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountPassword(const C
QString userName = QString::fromStdString(userInfo->name()); QString userName = QString::fromStdString(userInfo->name());
bool changeFailed = databaseInterface->changeUserPassword(userName, oldPassword, newPassword); if(!databaseInterface->changeUserPassword(userName, oldPassword, newPassword, false))
if(changeFailed)
return Response::RespWrongPassword; return Response::RespWrongPassword;
return Response::RespOk; return Response::RespOk;
} }
Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordRequest(const Command_ForgotPasswordRequest &cmd, ResponseContainer &rc)
{
qDebug() << "Received forgot password request from user: " << QString::fromStdString(cmd.user_name());
if (!servatrice->getEnableForgotPassword())
return Response::RespFunctionNotAllowed;
if (!sqlInterface->userExists(QString::fromStdString(cmd.user_name())))
return Response::RespFunctionNotAllowed;
if (sqlInterface->doesForgotPasswordExist(QString::fromStdString(cmd.user_name()))) {
Response_ForgotPasswordRequest *re = new Response_ForgotPasswordRequest;
re->set_challenge_email(false);
rc.setResponseExtension(re);
return Response::RespOk;
}
QString banReason; int banTimeRemaining;
if (sqlInterface->checkUserIsBanned(this->getAddress(), QString::fromStdString(cmd.user_name()), QString::fromStdString(cmd.clientid()), banReason, banTimeRemaining))
return Response::RespFunctionNotAllowed;
if (servatrice->getEnableForgotPasswordChallenge()) {
Response_ForgotPasswordRequest *re = new Response_ForgotPasswordRequest;
re->set_challenge_email(true);
rc.setResponseExtension(re);
return Response::RespOk;
}
else {
if (sqlInterface->addForgotPassword(QString::fromStdString(cmd.user_name()))) {
Response_ForgotPasswordRequest *re = new Response_ForgotPasswordRequest;
re->set_challenge_email(false);
rc.setResponseExtension(re);
return Response::RespOk;
}
}
return Response::RespFunctionNotAllowed;
}
Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordReset(const Command_ForgotPasswordReset &cmd, ResponseContainer &rc)
{
Q_UNUSED(rc);
qDebug() << "Received forgot password reset from user: " << QString::fromStdString(cmd.user_name());
if (!sqlInterface->doesForgotPasswordExist(QString::fromStdString(cmd.user_name())))
return Response::RespFunctionNotAllowed;
if (!sqlInterface->validateTableColumnStringData("{prefix}_users","token", QString::fromStdString(cmd.user_name()),QString::fromStdString(cmd.token())))
return Response::RespFunctionNotAllowed;
if (sqlInterface->changeUserPassword(QString::fromStdString(cmd.user_name()), "", QString::fromStdString(cmd.new_password()), true)) {
sqlInterface->removeForgotPassword(QString::fromStdString(cmd.user_name()));
return Response::RespOk;
}
return Response::RespFunctionNotAllowed;
}
Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordChallenge(const Command_ForgotPasswordChallenge &cmd, ResponseContainer &rc)
{
Q_UNUSED(rc);
qDebug() << "Received forgot password challenge from user: " << QString::fromStdString(cmd.user_name());
if (sqlInterface->doesForgotPasswordExist(QString::fromStdString(cmd.user_name())))
return Response::RespOk;
if (sqlInterface->validateTableColumnStringData("{prefix}_users","email", QString::fromStdString(cmd.user_name()), QString::fromStdString(cmd.email())))
if (sqlInterface->addForgotPassword(QString::fromStdString(cmd.user_name())))
return Response::RespOk;
return Response::RespFunctionNotAllowed;
}
// ADMIN FUNCTIONS. // ADMIN FUNCTIONS.
// Permission is checked by the calling function. // Permission is checked by the calling function.

View file

@ -57,124 +57,126 @@ class Command_AccountPassword;
class AbstractServerSocketInterface : public Server_ProtocolHandler class AbstractServerSocketInterface : public Server_ProtocolHandler
{ {
Q_OBJECT Q_OBJECT
protected slots: protected slots:
void catchSocketError(QAbstractSocket::SocketError socketError); void catchSocketError(QAbstractSocket::SocketError socketError);
virtual void flushOutputQueue() = 0; virtual void flushOutputQueue() = 0;
signals: signals:
void outputQueueChanged(); void outputQueueChanged();
protected: protected:
void logDebugMessage(const QString &message); void logDebugMessage(const QString &message);
bool tooManyRegistrationAttempts(const QString &ipAddress); bool tooManyRegistrationAttempts(const QString &ipAddress);
virtual void writeToSocket(QByteArray & data) = 0; virtual void writeToSocket(QByteArray & data) = 0;
virtual void flushSocket() = 0; virtual void flushSocket() = 0;
Servatrice *servatrice; Servatrice *servatrice;
QList<ServerMessage> outputQueue; QList<ServerMessage> outputQueue;
QMutex outputQueueMutex; QMutex outputQueueMutex;
private: private:
Servatrice_DatabaseInterface *sqlInterface; Servatrice_DatabaseInterface *sqlInterface;
Response::ResponseCode cmdAddToList(const Command_AddToList &cmd, ResponseContainer &rc); Response::ResponseCode cmdAddToList(const Command_AddToList &cmd, ResponseContainer &rc);
Response::ResponseCode cmdRemoveFromList(const Command_RemoveFromList &cmd, ResponseContainer &rc); Response::ResponseCode cmdRemoveFromList(const Command_RemoveFromList &cmd, ResponseContainer &rc);
int getDeckPathId(int basePathId, QStringList path); int getDeckPathId(int basePathId, QStringList path);
int getDeckPathId(const QString &path); int getDeckPathId(const QString &path);
bool deckListHelper(int folderId, ServerInfo_DeckStorage_Folder *folder); bool deckListHelper(int folderId, ServerInfo_DeckStorage_Folder *folder);
Response::ResponseCode cmdDeckList(const Command_DeckList &cmd, ResponseContainer &rc); Response::ResponseCode cmdDeckList(const Command_DeckList &cmd, ResponseContainer &rc);
Response::ResponseCode cmdDeckNewDir(const Command_DeckNewDir &cmd, ResponseContainer &rc); Response::ResponseCode cmdDeckNewDir(const Command_DeckNewDir &cmd, ResponseContainer &rc);
void deckDelDirHelper(int basePathId); void deckDelDirHelper(int basePathId);
void sendServerMessage(const QString userName, const QString message); void sendServerMessage(const QString userName, const QString message);
Response::ResponseCode cmdDeckDelDir(const Command_DeckDelDir &cmd, ResponseContainer &rc); Response::ResponseCode cmdDeckDelDir(const Command_DeckDelDir &cmd, ResponseContainer &rc);
Response::ResponseCode cmdDeckDel(const Command_DeckDel &cmd, ResponseContainer &rc); Response::ResponseCode cmdDeckDel(const Command_DeckDel &cmd, ResponseContainer &rc);
Response::ResponseCode cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc); Response::ResponseCode cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc);
DeckList *getDeckFromDatabase(int deckId); DeckList *getDeckFromDatabase(int deckId);
Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc); Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc);
Response::ResponseCode cmdReplayList(const Command_ReplayList &cmd, ResponseContainer &rc); Response::ResponseCode cmdReplayList(const Command_ReplayList &cmd, ResponseContainer &rc);
Response::ResponseCode cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc); Response::ResponseCode cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc);
Response::ResponseCode cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer &rc); Response::ResponseCode cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer &rc);
Response::ResponseCode cmdReplayDeleteMatch(const Command_ReplayDeleteMatch &cmd, ResponseContainer &rc); Response::ResponseCode cmdReplayDeleteMatch(const Command_ReplayDeleteMatch &cmd, ResponseContainer &rc);
Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc); Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc);
Response::ResponseCode cmdWarnUser(const Command_WarnUser &cmd, ResponseContainer &rc); Response::ResponseCode cmdWarnUser(const Command_WarnUser &cmd, ResponseContainer &rc);
Response::ResponseCode cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc);
Response::ResponseCode cmdGetBanHistory(const Command_GetBanHistory &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetBanHistory(const Command_GetBanHistory &cmd, ResponseContainer &rc);
Response::ResponseCode cmdGetWarnList(const Command_GetWarnList &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetWarnList(const Command_GetWarnList &cmd, ResponseContainer &rc);
Response::ResponseCode cmdGetWarnHistory(const Command_GetWarnHistory &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetWarnHistory(const Command_GetWarnHistory &cmd, ResponseContainer &rc);
Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc); Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc);
Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc);
Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc); Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc);
Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */); Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */);
Response::ResponseCode cmdReloadConfig(const Command_ReloadConfig &/* cmd */, ResponseContainer & /*rc*/); Response::ResponseCode cmdReloadConfig(const Command_ReloadConfig &/* cmd */, ResponseContainer & /*rc*/);
Response::ResponseCode cmdAdjustMod(const Command_AdjustMod &cmd, ResponseContainer & /*rc*/); Response::ResponseCode cmdAdjustMod(const Command_AdjustMod &cmd, ResponseContainer & /*rc*/);
Response::ResponseCode cmdForgotPasswordRequest(const Command_ForgotPasswordRequest &cmd, ResponseContainer &rc);
Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc); Response::ResponseCode cmdForgotPasswordReset(const Command_ForgotPasswordReset &cmd, ResponseContainer &rc);
Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc); Response::ResponseCode cmdForgotPasswordChallenge(const Command_ForgotPasswordChallenge &cmd, ResponseContainer &rc);
Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc); Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc);
Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc);
Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc);
Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc); Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc);
Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc);
Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc); Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc);
public: public:
AbstractServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); AbstractServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0);
~AbstractServerSocketInterface() { }; ~AbstractServerSocketInterface() { };
bool initSession(); bool initSession();
virtual QHostAddress getPeerAddress() const = 0; virtual QHostAddress getPeerAddress() const = 0;
virtual QString getAddress() const = 0; virtual QString getAddress() const = 0;
void transmitProtocolItem(const ServerMessage &item); void transmitProtocolItem(const ServerMessage &item);
}; };
class TcpServerSocketInterface : public AbstractServerSocketInterface class TcpServerSocketInterface : public AbstractServerSocketInterface
{ {
Q_OBJECT Q_OBJECT
public: public:
TcpServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); TcpServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0);
~TcpServerSocketInterface(); ~TcpServerSocketInterface();
QHostAddress getPeerAddress() const { return socket->peerAddress(); } QHostAddress getPeerAddress() const { return socket->peerAddress(); }
QString getAddress() const { return socket->peerAddress().toString(); } QString getAddress() const { return socket->peerAddress().toString(); }
QString getConnectionType() const { return "tcp"; }; QString getConnectionType() const { return "tcp"; };
private: private:
QTcpSocket *socket; QTcpSocket *socket;
QByteArray inputBuffer; QByteArray inputBuffer;
bool messageInProgress; bool messageInProgress;
bool handshakeStarted; bool handshakeStarted;
int messageLength; int messageLength;
protected: protected:
void writeToSocket(QByteArray & data) { socket->write(data); }; void writeToSocket(QByteArray & data) { socket->write(data); };
void flushSocket() { socket->flush(); }; void flushSocket() { socket->flush(); };
void initSessionDeprecated(); void initSessionDeprecated();
bool initTcpSession(); bool initTcpSession();
protected slots: protected slots:
void readClient(); void readClient();
void flushOutputQueue(); void flushOutputQueue();
public slots: public slots:
void initConnection(int socketDescriptor); void initConnection(int socketDescriptor);
}; };
#if QT_VERSION > 0x050300 #if QT_VERSION > 0x050300
class WebsocketServerSocketInterface : public AbstractServerSocketInterface class WebsocketServerSocketInterface : public AbstractServerSocketInterface
{ {
Q_OBJECT Q_OBJECT
public: public:
WebsocketServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); WebsocketServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0);
~WebsocketServerSocketInterface(); ~WebsocketServerSocketInterface();
QHostAddress getPeerAddress() const { return socket->peerAddress(); } QHostAddress getPeerAddress() const { return socket->peerAddress(); }
QString getAddress() const { return socket->peerAddress().toString(); } QString getAddress() const { return socket->peerAddress().toString(); }
QString getConnectionType() const { return "websocket"; }; QString getConnectionType() const { return "websocket"; };
private: private:
QWebSocket *socket; QWebSocket *socket;
protected: protected:
void writeToSocket(QByteArray & data) { socket->sendBinaryMessage(data); }; void writeToSocket(QByteArray & data) { socket->sendBinaryMessage(data); };
void flushSocket() { socket->flush(); }; void flushSocket() { socket->flush(); };
bool initWebsocketSession(); bool initWebsocketSession();
protected slots: protected slots:
void binaryMessageReceived(const QByteArray & message); void binaryMessageReceived(const QByteArray & message);
void flushOutputQueue(); void flushOutputQueue();
public slots: public slots:
void initConnection(void * _socket); void initConnection(void * _socket);
}; };
#endif #endif

View file

@ -82,6 +82,54 @@ bool SmtpClient::enqueueActivationTokenMail(const QString &nickname, const QStri
return true; return true;
} }
bool SmtpClient::enqueueForgotPasswordTokenMail(const QString &nickname, const QString &recipient, const QString &token)
{
QString email = settingsCache->value("smtp/email", "").toString();
QString name = settingsCache->value("smtp/name", "").toString();
QString subject = settingsCache->value("forgotpassword/subject", "").toString();
QString body = settingsCache->value("forgotpassword/body", "").toString();
if (email.isEmpty())
{
qDebug() << "[MAIL] Missing sender email in configuration";
return false;
}
if (subject.isEmpty())
{
qDebug() << "[MAIL] Missing subject field in configuration";
return false;
}
if (body.isEmpty())
{
qDebug() << "[MAIL] Missing body field in configuration";
return false;
}
if (recipient.isEmpty())
{
qDebug() << "[MAIL] Missing recipient field for user " << nickname;
return false;
}
if (token.isEmpty())
{
qDebug() << "[MAIL] Missing token field for user " << nickname;
return false;
}
QxtMailMessage message;
message.setSender(name + " <" + email + ">");
message.addRecipient(recipient);
message.setSubject(subject);
message.setBody(body.replace("%username", nickname).replace("%token", token));
int id = smtp->send(message);
qDebug() << "[MAIL] Enqueued mail to" << recipient << "as" << id;
return true;
}
void SmtpClient::sendAllEmails() void SmtpClient::sendAllEmails()
{ {
// still connected from the previous round // still connected from the previous round

View file

@ -7,28 +7,29 @@ class QxtSmtp;
class QxtMailMessage; class QxtMailMessage;
class SmtpClient : public QObject { class SmtpClient : public QObject {
Q_OBJECT Q_OBJECT
public: public:
SmtpClient(QObject *parent = 0); SmtpClient(QObject *parent = 0);
~SmtpClient(); ~SmtpClient();
protected: protected:
QxtSmtp *smtp; QxtSmtp *smtp;
public slots: public slots:
bool enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token); bool enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token);
void sendAllEmails(); bool enqueueForgotPasswordTokenMail(const QString &nickname, const QString &recipient, const QString &token);
void sendAllEmails();
protected slots: protected slots:
void authenticated(); void authenticated();
void authenticationFailed(const QByteArray & msg); void authenticationFailed(const QByteArray & msg);
void connected(); void connected();
void connectionFailed(const QByteArray & msg); void connectionFailed(const QByteArray & msg);
void disconnected(); void disconnected();
void encrypted(); void encrypted();
void encryptionFailed(const QByteArray & msg); void encryptionFailed(const QByteArray & msg);
void finished(); void finished();
void mailFailed(int mailID, int errorCode, const QByteArray & msg); void mailFailed(int mailID, int errorCode, const QByteArray & msg);
void mailSent(int mailID); void mailSent(int mailID);
void recipientRejected(int mailID, const QString & address, const QByteArray & msg); void recipientRejected(int mailID, const QString & address, const QByteArray & msg);
void senderRejected(int mailID, const QString & address, const QByteArray & msg); void senderRejected(int mailID, const QString & address, const QByteArray & msg);
}; };
#endif #endif