diff --git a/CMakeLists.txt b/CMakeLists.txt index d06907d6..4f3c59c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,12 +77,12 @@ endif() # Define proper compilation flags IF(MSVC) - # Visual Studio: + # Visual Studio: # Support from Windows XP SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS,5.01") - # Maximum optimization + # Maximum optimization set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD") - # Generate complete debugging information + # Generate complete debugging information #set(CMAKE_CXX_FLAGS_DEBUG "/Zi") ELSEIF (CMAKE_COMPILER_IS_GNUCXX) # linux/gcc, bsd/gcc, windows/mingw @@ -176,9 +176,9 @@ if(UNIX) # linux set(CPACK_GENERATOR DEB ${CPACK_GENERATOR}) set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}") - set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) - set(CPACK_DEBIAN_PACKAGE_SECTION "games") - set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice") + set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) + set(CPACK_DEBIAN_PACKAGE_SECTION "games") + set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5") endif() elseif(WIN32) diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 3a5d12a2..818ab0a5 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -15,6 +15,9 @@ SET(cockatrice_SOURCES src/dlg_edit_password.cpp src/dlg_edit_tokens.cpp src/dlg_edit_user.cpp + src/dlg_forgotpasswordrequest.cpp + src/dlg_forgotpasswordreset.cpp + src/dlg_forgotpasswordchallenge.cpp src/dlg_register.cpp src/dlg_update.cpp src/dlg_viewlog.cpp diff --git a/cockatrice/src/abstractclient.h b/cockatrice/src/abstractclient.h index 9c7606a2..6c8d987b 100644 --- a/cockatrice/src/abstractclient.h +++ b/cockatrice/src/abstractclient.h @@ -35,6 +35,9 @@ enum ClientStatus { StatusActivating, StatusLoggingIn, StatusLoggedIn, + StatusRequestingForgotPassword, + StatusSubmitForgotPasswordReset, + StatusSubmitForgotPasswordChallenge, }; class AbstractClient : public QObject { diff --git a/cockatrice/src/dlg_connect.cpp b/cockatrice/src/dlg_connect.cpp index e4232246..8a64041b 100644 --- a/cockatrice/src/dlg_connect.cpp +++ b/cockatrice/src/dlg_connect.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "dlg_connect.h" #include "settingscache.h" @@ -69,6 +70,17 @@ DlgConnect::DlgConnect(QWidget *parent) 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; connectionLayout->addWidget(previousHostButton, 0, 1); connectionLayout->addWidget(previousHosts, 1, 1); @@ -79,6 +91,11 @@ DlgConnect::DlgConnect(QWidget *parent) connectionLayout->addWidget(portEdit, 4, 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")); restrictionsGroupBox->setLayout(connectionLayout); @@ -92,17 +109,16 @@ DlgConnect::DlgConnect(QWidget *parent) QGroupBox *loginGroupBox = new QGroupBox(tr("Login")); loginGroupBox->setLayout(loginLayout); + QGroupBox *btnGroupBox = new QGroupBox(tr("")); + btnGroupBox->setLayout(buttons); + QGridLayout *grid = new QGridLayout; grid->addWidget(restrictionsGroupBox, 0, 0); grid->addWidget(loginGroupBox, 1, 0); - - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(actCancel())); + grid->addWidget(btnGroupBox, 2, 0); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addLayout(grid); - mainLayout->addWidget(buttonBox); setLayout(mainLayout); setWindowTitle(tr("Connect to server")); @@ -204,3 +220,8 @@ bool DeleteHighlightedItemWhenShiftDelPressedEventFilter::eventFilter(QObject *o } return QObject::eventFilter(obj, event); } + +void DlgConnect::actForgotPassword() +{ + emit sigStartForgotPasswordRequest(); +} \ No newline at end of file diff --git a/cockatrice/src/dlg_connect.h b/cockatrice/src/dlg_connect.h index 43cb319e..23b19c32 100644 --- a/cockatrice/src/dlg_connect.h +++ b/cockatrice/src/dlg_connect.h @@ -20,6 +20,8 @@ protected: class DlgConnect : public QDialog { Q_OBJECT +signals : + void sigStartForgotPasswordRequest(); public: DlgConnect(QWidget *parent = 0); QString getHost() const; @@ -32,12 +34,14 @@ private slots: void passwordSaved(int state); void previousHostSelected(bool state); void newHostSelected(bool state); + void actForgotPassword(); private: QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel; QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit; QCheckBox *savePasswordCheckBox, *autoConnectCheckBox; QComboBox *previousHosts; QRadioButton *newHostButton, *previousHostButton; + QPushButton *btnOk, *btnCancel, *btnForgotPassword; }; #endif diff --git a/cockatrice/src/dlg_forgotpasswordchallenge.cpp b/cockatrice/src/dlg_forgotpasswordchallenge.cpp new file mode 100644 index 00000000..9423f8e8 --- /dev/null +++ b/cockatrice/src/dlg_forgotpasswordchallenge.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include + +#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(); +} diff --git a/cockatrice/src/dlg_forgotpasswordchallenge.h b/cockatrice/src/dlg_forgotpasswordchallenge.h new file mode 100644 index 00000000..3f7ce9b4 --- /dev/null +++ b/cockatrice/src/dlg_forgotpasswordchallenge.h @@ -0,0 +1,28 @@ +#ifndef DLG_FORGOTPASSWORDCHALLENGE_H +#define DLG_FORGOTPASSWORDCHALLENGE_H + +#include +#include +#include + +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 diff --git a/cockatrice/src/dlg_forgotpasswordrequest.cpp b/cockatrice/src/dlg_forgotpasswordrequest.cpp new file mode 100644 index 00000000..e5965866 --- /dev/null +++ b/cockatrice/src/dlg_forgotpasswordrequest.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include + +#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(); +} diff --git a/cockatrice/src/dlg_forgotpasswordrequest.h b/cockatrice/src/dlg_forgotpasswordrequest.h new file mode 100644 index 00000000..9cdf2cc2 --- /dev/null +++ b/cockatrice/src/dlg_forgotpasswordrequest.h @@ -0,0 +1,27 @@ +#ifndef DLG_FORGOTPASSWORDREQUEST_H +#define DLG_FORGOTPASSWORDREQUEST_H + +#include +#include +#include + +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 diff --git a/cockatrice/src/dlg_forgotpasswordreset.cpp b/cockatrice/src/dlg_forgotpasswordreset.cpp new file mode 100644 index 00000000..1d3d4c7e --- /dev/null +++ b/cockatrice/src/dlg_forgotpasswordreset.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include +#include + +#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(); +} diff --git a/cockatrice/src/dlg_forgotpasswordreset.h b/cockatrice/src/dlg_forgotpasswordreset.h new file mode 100644 index 00000000..0832b15d --- /dev/null +++ b/cockatrice/src/dlg_forgotpasswordreset.h @@ -0,0 +1,29 @@ +#ifndef DLG_FORGOTPASSWORDRESET_H +#define DLG_FORGOTPASSWORDRESET_H + +#include +#include +#include + +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 diff --git a/cockatrice/src/remoteclient.cpp b/cockatrice/src/remoteclient.cpp index 0616f21b..bc47bb30 100644 --- a/cockatrice/src/remoteclient.cpp +++ b/cockatrice/src/remoteclient.cpp @@ -13,6 +13,7 @@ #include "pb/response_login.pb.h" #include "pb/response_register.pb.h" #include "pb/response_activate.pb.h" +#include "pb/response_forgotpasswordrequest.pb.h" #include "pb/server_message.pb.h" #include "pb/event_server_identification.pb.h" #include "settingscache.h" @@ -43,6 +44,9 @@ RemoteClient::RemoteClient(QObject *parent) 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(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() @@ -77,6 +81,42 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica 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) { Command_Register cmdRegister; @@ -415,4 +455,97 @@ void RemoteClient::clearNewClientFeatures() } } 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(); } \ No newline at end of file diff --git a/cockatrice/src/remoteclient.h b/cockatrice/src/remoteclient.h index b8c257d6..7c2e2778 100644 --- a/cockatrice/src/remoteclient.h +++ b/cockatrice/src/remoteclient.h @@ -22,6 +22,13 @@ signals: void sigActivateToServer(const QString &_token); void sigDisconnectFromServer(); 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: void slotConnected(); void readData(); @@ -37,7 +44,12 @@ private slots: void doLogin(); void doDisconnectFromServer(); 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: static const int maxTimeout = 10; 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 activateToServer(const QString &_token); 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 diff --git a/cockatrice/src/settings/serverssettings.cpp b/cockatrice/src/settings/serverssettings.cpp index e698e127..367c7702 100644 --- a/cockatrice/src/settings/serverssettings.cpp +++ b/cockatrice/src/settings/serverssettings.cpp @@ -100,3 +100,36 @@ int ServersSettings::getAutoConnect() QVariant autoconnect = getValue("auto_connect", "server"); 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(); +} diff --git a/cockatrice/src/settings/serverssettings.h b/cockatrice/src/settings/serverssettings.h index 9d3fad8c..c64cc727 100644 --- a/cockatrice/src/settings/serverssettings.h +++ b/cockatrice/src/settings/serverssettings.h @@ -16,6 +16,9 @@ public: QString getHostname(QString defaultHost = ""); QString getPort(QString defaultPort = ""); QString getPlayerName(QString defaultName = ""); + QString getFPHostname(QString defaultHost = ""); + QString getFPPort(QString defaultPort = ""); + QString getFPPlayerName(QString defaultName = ""); QString getPassword(); int getSavePassword(); int getAutoConnect(); @@ -29,6 +32,9 @@ public: void setPassword(QString password); void setSavePassword(int save); void setAutoConnect(int autoconnect); + void setFPHostName(QString hostname); + void setFPPort(QString port); + void setFPPlayerName(QString playerName); signals: public slots: diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 85b4e5a0..857f7094 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -63,6 +63,9 @@ void SettingsCache::translateLegacySettings() servers().setPassword(legacySetting.value("password").toString()); servers().setSavePassword(legacySetting.value("save_password").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()); QStringList allKeysServer = legacySetting.allKeys(); for (int i = 0; i < allKeysServer.size(); ++i) { diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 1933c8de..d87ad423 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -36,6 +36,9 @@ #include "main.h" #include "window_main.h" #include "dlg_connect.h" +#include "dlg_forgotpasswordrequest.h" +#include "dlg_forgotpasswordreset.h" +#include "dlg_forgotpasswordchallenge.h" #include "dlg_register.h" #include "dlg_settings.h" #include "dlg_update.h" @@ -166,9 +169,10 @@ void MainWindow::activateAccepted() void MainWindow::actConnect() { - DlgConnect dlg(this); - if (dlg.exec()) - client->connectToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName(), dlg.getPassword()); + DlgConnect *dlg = new DlgConnect(this); + connect(dlg, SIGNAL(sigStartForgotPasswordRequest()), this, SLOT(actForgotPasswordRequest())); + if (dlg->exec()) + client->connectToServer(dlg->getHost(), dlg->getPort(), dlg->getPlayerName(), dlg->getPassword()); } void MainWindow::actRegister() @@ -506,6 +510,9 @@ void MainWindow::setClientStatusTitle() case StatusDisconnected: setWindowTitle(appName + " - " + tr("Disconnected")); break; case StatusLoggingIn: setWindowTitle(appName + " - " + tr("Connected, logging in at %1").arg(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); } } @@ -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(activateAccepted()), this, SLOT(activateAccepted())); 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); 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() { closeAction = new QAction(tr("&Exit"), this); @@ -1054,3 +1072,34 @@ void MainWindow::actEditTokens() dlg.exec(); 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()); +} \ No newline at end of file diff --git a/cockatrice/src/window_main.h b/cockatrice/src/window_main.h index b5ed81d6..ff77a44d 100644 --- a/cockatrice/src/window_main.h +++ b/cockatrice/src/window_main.h @@ -67,13 +67,15 @@ private slots: void actRegister(); void actSettings(); void actExit(); - + void actForgotPasswordRequest(); void actAbout(); void actUpdate(); void actViewLog(); - + void forgotPasswordSuccess(); + void forgotPasswordError(); + void promptForgotPasswordReset(); void iconActivated(QSystemTrayIcon::ActivationReason reason); - + void promptForgotPasswordChallenge(); void showWindowIfHidden(); void actCheckCardUpdates(); diff --git a/common/featureset.cpp b/common/featureset.cpp index 39110540..2200ad6f 100644 --- a/common/featureset.cpp +++ b/common/featureset.cpp @@ -21,6 +21,7 @@ void FeatureSet::initalizeFeatureList(QMap &featureList) { featureList.insert("client_warnings", false); featureList.insert("mod_log_lookup", false); featureList.insert("idle_client", false); + featureList.insert("forgot_password", false); } void FeatureSet::enableRequiredFeature(QMap &featureList, QString featureName){ diff --git a/common/pb/CMakeLists.txt b/common/pb/CMakeLists.txt index 70c369fa..889bc769 100644 --- a/common/pb/CMakeLists.txt +++ b/common/pb/CMakeLists.txt @@ -119,6 +119,7 @@ SET(PROTO_FILES response_deck_list.proto response_deck_upload.proto response_dump_zone.proto + response_forgotpasswordrequest.proto response_get_games_of_user.proto response_get_user_info.proto response_join_room.proto diff --git a/common/pb/response.proto b/common/pb/response.proto index 0768c64f..75bca97a 100644 --- a/common/pb/response.proto +++ b/common/pb/response.proto @@ -58,6 +58,7 @@ message Response { WARN_HISTORY = 1013; WARN_LIST = 1014; VIEW_LOG = 1015; + FORGOT_PASSWORD_REQUEST = 1016; REPLAY_LIST = 1100; REPLAY_DOWNLOAD = 1101; } diff --git a/common/pb/response_forgotpasswordrequest.proto b/common/pb/response_forgotpasswordrequest.proto new file mode 100644 index 00000000..5205e0fd --- /dev/null +++ b/common/pb/response_forgotpasswordrequest.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; +import "response.proto"; + +message Response_ForgotPasswordRequest { + extend Response { + optional Response_ForgotPasswordRequest ext = 1016; + } + optional bool challenge_email = 1; +} diff --git a/common/pb/session_commands.proto b/common/pb/session_commands.proto index afd85a39..9494577a 100644 --- a/common/pb/session_commands.proto +++ b/common/pb/session_commands.proto @@ -24,6 +24,9 @@ message SessionCommand { ACCOUNT_EDIT = 1018; ACCOUNT_IMAGE = 1019; ACCOUNT_PASSWORD = 1020; + FORGOT_PASSWORD_REQUEST = 1021; + FORGOT_PASSWORD_RESET = 1022; + FORGOT_PASSWORD_CHALLENGE = 1023; REPLAY_LIST = 1100; REPLAY_DOWNLOAD = 1101; REPLAY_MODIFY_MATCH = 1102; @@ -160,3 +163,30 @@ message Command_AccountPassword { optional string old_password = 1; 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; +} diff --git a/common/server_database_interface.h b/common/server_database_interface.h index 836c843e..e36629f6 100644 --- a/common/server_database_interface.h +++ b/common/server_database_interface.h @@ -21,7 +21,7 @@ public: 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 & /* allPlayersEver */, const QSet & /* allSpectatorsEver */, const QList & /* replayList */) { } 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 bool usernameIsValid(const QString & /*userName */, QString & /* error */) { return true; }; 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 */) { }; bool checkUserIsBanned(Server_ProtocolHandler *session, QString &banReason, int &banSecondsRemaining); 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'); }; }; diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index cb9d7b55..e326a7ec 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -507,7 +507,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd } joinPersistentGames(rc); - + databaseInterface->removeForgotPassword(userName); rc.setResponseExtension(re); return Response::RespOk; } diff --git a/servatrice/migrations/servatrice_0020_to_0021.sql b/servatrice/migrations/servatrice_0020_to_0021.sql new file mode 100644 index 00000000..97372253 --- /dev/null +++ b/servatrice/migrations/servatrice_0020_to_0021.sql @@ -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; \ No newline at end of file diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index e2f7530c..f83c6ca7 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -142,7 +142,7 @@ disallowedregexp="" ; 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). -;maxaccountsperemail=0 +;maxaccountsperemail=0 ; 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 @@ -151,6 +151,31 @@ disallowedregexp="" ; Example: "10minutemail.com,gmail.com" ;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] ; Enable the internal smtp client to send registration emails. If you would like to diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index c1e84f53..9496da99 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_schema_version` ( PRIMARY KEY (`version`) ) ENGINE=INNODB DEFAULT CHARSET=utf8; -INSERT INTO cockatrice_schema_version VALUES(20); +INSERT INTO cockatrice_schema_version VALUES(21); -- users and user data tables CREATE TABLE IF NOT EXISTS `cockatrice_users` ( @@ -258,3 +258,12 @@ CREATE TABLE IF NOT EXISTS `cockatrice_donations` ( `pp_type` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) 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; diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 5895fdf0..18242cd1 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -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") { databaseType = DatabaseMySql; } else { @@ -542,28 +548,51 @@ void Servatrice::statusUpdate() query->bindValue(":rx", rx); servatriceDatabaseInterface->execSqlQuery(query); - // send activation emails - if (getRegistrationEnabled() && getRequireEmailActivationEnabled() && getEnableInternalSMTPClient()) + if (getRegistrationEnabled() && 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 (!servatriceDatabaseInterface->execSqlQuery(query)) - return; + if (getRequireEmailActivationEnabled()) + { + 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()) { - const QString userName = query->value(0).toString(); - const QString emailAddress = query->value(1).toString(); - const QString token = query->value(2).toString(); + 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->enqueueActivationTokenMail(userName, emailAddress, token)) - { - queryDelete->bindValue(":name", userName); - servatriceDatabaseInterface->execSqlQuery(queryDelete); - } - } + if (smtpClient->enqueueActivationTokenMail(userName, emailAddress, token)) + { + queryDelete->bindValue(":name", userName); + 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(); } +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 { return settingsCache->value("registration/emailproviderblacklist").toString(); } \ No newline at end of file diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index b7dbf827..5a6479d0 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -154,7 +154,6 @@ private: QString getRoomsMethodString() const; QString getISLNetworkSSLCertFile() const; QString getISLNetworkSSLKeyFile() const; - int getServerStatusUpdateTime() const; int getNumberOfTCPPools() const; int getServerTCPPort() const; @@ -192,6 +191,8 @@ public: bool getRequireEmailForRegistrationEnabled() const; bool getRequireEmailActivationEnabled() const; bool getEnableLogQuery() const; + bool getEnableForgotPassword() const; + bool getEnableForgotPasswordChallenge() const; int getIdleClientTimeout() const; int getServerID() const; int getMaxGameInactivityTime() const; @@ -209,6 +210,7 @@ public: int getMaxWebSocketUserLimit() const; int getUsersWithAddress(const QHostAddress &address) const; int getMaxAccountsPerEmail() const; + int getForgotPasswordTokenLife() const; QList getUsersWithAddressAsList(const QHostAddress &address) const; void incTxBytes(quint64 num); void incRxBytes(quint64 num); diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index dc1a2933..49ec04f1 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -176,9 +176,9 @@ bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const Q token = active ? QString() : PasswordHasher::generateActivationToken(); QSqlQuery *query = prepareQuery("insert into {prefix}_users " - "(name, realname, gender, password_sha512, email, country, registrationDate, active, token) " - "values " - "(:userName, :realName, :gender, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active, :token)"); + "(name, realname, gender, password_sha512, email, country, registrationDate, active, token, admin, avatar_bmp, clientid, privlevel, privlevelStartDate, privlevelEndDate) " + "values " + "(:userName, :realName, :gender, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active, :token, 0, '', '', 'NONE', UTC_TIMESTAMP(), UTC_TIMESTAMP())"); query->bindValue(":userName", userName); query->bindValue(":realName", realName); query->bindValue(":gender", getGenderChar(gender)); @@ -831,41 +831,43 @@ void Servatrice_DatabaseInterface::logMessage(const int senderId, const QString 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) - return true; + return false; if (!checkSql()) - return true; + return false; QString error; if (!usernameIsValid(user, error)) - return true; + return false; QSqlQuery *passwordQuery = prepareQuery("select password_sha512 from {prefix}_users where name = :name"); passwordQuery->bindValue(":name", user); - if (!execSqlQuery(passwordQuery)) { - qDebug("Change password denied: SQL error"); - return true; + + if (!force) { + 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()); passwordQuery = prepareQuery("update {prefix}_users set password_sha512=:password where name = :name"); passwordQuery->bindValue(":password", passwordSha512); passwordQuery->bindValue(":name", user); - if (!execSqlQuery(passwordQuery)) { - qDebug("Change password denied: SQL error"); + if (execSqlQuery(passwordQuery)) return true; - } + return false; } @@ -1128,3 +1130,91 @@ int Servatrice_DatabaseInterface::checkNumberOfUserAccounts(const QString &email 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; +} diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index e770738a..89ea3152 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -9,7 +9,7 @@ #include "server.h" #include "server_database_interface.h" -#define DATABASE_SCHEMA_VERSION 20 +#define DATABASE_SCHEMA_VERSION 21 class Servatrice; @@ -70,19 +70,22 @@ public: bool usernameIsValid(const QString &user, QString & error); bool checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining); int checkNumberOfUserAccounts(const QString &email); - 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); + 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); bool activateUser(const QString &userName, const QString &token); void updateUsersClientID(const QString &userName, const QString &userClientID); void updateUsersLastLoginData(const QString &userName, const QString &clientVersion); - void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, - LogMessage_TargetType targetType, const int targetId, const QString &targetName); - bool changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword); + void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, LogMessage_TargetType targetType, const int targetId, const QString &targetName); + bool changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword, const bool &force); QChar getGenderChar(ServerInfo_User_Gender const &gender); QList getUserBanHistory(const QString userName); bool addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID); QList getUserWarnHistory(const QString userName); QList 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 diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index ae130b5e..92f28896 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -62,6 +62,7 @@ #include "pb/response_warn_history.pb.h" #include "pb/response_warn_list.pb.h" #include "pb/response_viewlog_history.pb.h" +#include "pb/response_forgotpasswordrequest.pb.h" #include "pb/serverinfo_replay.pb.h" #include "pb/serverinfo_user.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::REGISTER: return cmdRegisterAccount(cmd.GetExtension(Command_Register::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_IMAGE: return cmdAccountImage(cmd.GetExtension(Command_AccountImage::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()); - bool changeFailed = databaseInterface->changeUserPassword(userName, oldPassword, newPassword); - - if(changeFailed) + if(!databaseInterface->changeUserPassword(userName, oldPassword, newPassword, false)) return Response::RespWrongPassword; 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. // Permission is checked by the calling function. diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index 9eaeb43d..908ea9d3 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -57,124 +57,126 @@ class Command_AccountPassword; class AbstractServerSocketInterface : public Server_ProtocolHandler { - Q_OBJECT + Q_OBJECT protected slots: - void catchSocketError(QAbstractSocket::SocketError socketError); - virtual void flushOutputQueue() = 0; + void catchSocketError(QAbstractSocket::SocketError socketError); + virtual void flushOutputQueue() = 0; signals: - void outputQueueChanged(); + void outputQueueChanged(); protected: - void logDebugMessage(const QString &message); - bool tooManyRegistrationAttempts(const QString &ipAddress); + void logDebugMessage(const QString &message); + bool tooManyRegistrationAttempts(const QString &ipAddress); - virtual void writeToSocket(QByteArray & data) = 0; - virtual void flushSocket() = 0; + virtual void writeToSocket(QByteArray & data) = 0; + virtual void flushSocket() = 0; - Servatrice *servatrice; - QList outputQueue; - QMutex outputQueueMutex; + Servatrice *servatrice; + QList outputQueue; + QMutex outputQueueMutex; private: - Servatrice_DatabaseInterface *sqlInterface; - - Response::ResponseCode cmdAddToList(const Command_AddToList &cmd, ResponseContainer &rc); - Response::ResponseCode cmdRemoveFromList(const Command_RemoveFromList &cmd, ResponseContainer &rc); - int getDeckPathId(int basePathId, QStringList path); - int getDeckPathId(const QString &path); - bool deckListHelper(int folderId, ServerInfo_DeckStorage_Folder *folder); - Response::ResponseCode cmdDeckList(const Command_DeckList &cmd, ResponseContainer &rc); - Response::ResponseCode cmdDeckNewDir(const Command_DeckNewDir &cmd, ResponseContainer &rc); - void deckDelDirHelper(int basePathId); - void sendServerMessage(const QString userName, const QString message); - Response::ResponseCode cmdDeckDelDir(const Command_DeckDelDir &cmd, ResponseContainer &rc); - Response::ResponseCode cmdDeckDel(const Command_DeckDel &cmd, ResponseContainer &rc); - Response::ResponseCode cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc); - DeckList *getDeckFromDatabase(int deckId); - Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReplayList(const Command_ReplayList &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReplayDeleteMatch(const Command_ReplayDeleteMatch &cmd, ResponseContainer &rc); - Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc); - Response::ResponseCode cmdWarnUser(const Command_WarnUser &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetBanHistory(const Command_GetBanHistory &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetWarnList(const Command_GetWarnList &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetWarnHistory(const Command_GetWarnHistory &cmd, ResponseContainer &rc); - Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc); - Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); + Servatrice_DatabaseInterface *sqlInterface; + + Response::ResponseCode cmdAddToList(const Command_AddToList &cmd, ResponseContainer &rc); + Response::ResponseCode cmdRemoveFromList(const Command_RemoveFromList &cmd, ResponseContainer &rc); + int getDeckPathId(int basePathId, QStringList path); + int getDeckPathId(const QString &path); + bool deckListHelper(int folderId, ServerInfo_DeckStorage_Folder *folder); + Response::ResponseCode cmdDeckList(const Command_DeckList &cmd, ResponseContainer &rc); + Response::ResponseCode cmdDeckNewDir(const Command_DeckNewDir &cmd, ResponseContainer &rc); + void deckDelDirHelper(int basePathId); + void sendServerMessage(const QString userName, const QString message); + Response::ResponseCode cmdDeckDelDir(const Command_DeckDelDir &cmd, ResponseContainer &rc); + Response::ResponseCode cmdDeckDel(const Command_DeckDel &cmd, ResponseContainer &rc); + Response::ResponseCode cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc); + DeckList *getDeckFromDatabase(int deckId); + Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc); + Response::ResponseCode cmdReplayList(const Command_ReplayList &cmd, ResponseContainer &rc); + Response::ResponseCode cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc); + Response::ResponseCode cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer &rc); + Response::ResponseCode cmdReplayDeleteMatch(const Command_ReplayDeleteMatch &cmd, ResponseContainer &rc); + Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc); + Response::ResponseCode cmdWarnUser(const Command_WarnUser &cmd, ResponseContainer &rc); + Response::ResponseCode cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc); + Response::ResponseCode cmdGetBanHistory(const Command_GetBanHistory &cmd, ResponseContainer &rc); + Response::ResponseCode cmdGetWarnList(const Command_GetWarnList &cmd, ResponseContainer &rc); + Response::ResponseCode cmdGetWarnHistory(const Command_GetWarnHistory &cmd, ResponseContainer &rc); + Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc); + Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc); Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */); Response::ResponseCode cmdReloadConfig(const Command_ReloadConfig &/* cmd */, ResponseContainer & /*rc*/); - Response::ResponseCode cmdAdjustMod(const Command_AdjustMod &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 cmdAdjustMod(const Command_AdjustMod &cmd, ResponseContainer & /*rc*/); + Response::ResponseCode cmdForgotPasswordRequest(const Command_ForgotPasswordRequest &cmd, ResponseContainer &rc); + Response::ResponseCode cmdForgotPasswordReset(const Command_ForgotPasswordReset &cmd, ResponseContainer &rc); + Response::ResponseCode cmdForgotPasswordChallenge(const Command_ForgotPasswordChallenge &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 cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); - Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc); + Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc); + Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); + Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc); public: - AbstractServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); - ~AbstractServerSocketInterface() { }; - bool initSession(); + AbstractServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); + ~AbstractServerSocketInterface() { }; + bool initSession(); - virtual QHostAddress getPeerAddress() const = 0; - virtual QString getAddress() const = 0; + virtual QHostAddress getPeerAddress() const = 0; + virtual QString getAddress() const = 0; - void transmitProtocolItem(const ServerMessage &item); + void transmitProtocolItem(const ServerMessage &item); }; class TcpServerSocketInterface : public AbstractServerSocketInterface { - Q_OBJECT + Q_OBJECT public: - TcpServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); - ~TcpServerSocketInterface(); + TcpServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); + ~TcpServerSocketInterface(); - QHostAddress getPeerAddress() const { return socket->peerAddress(); } - QString getAddress() const { return socket->peerAddress().toString(); } - QString getConnectionType() const { return "tcp"; }; + QHostAddress getPeerAddress() const { return socket->peerAddress(); } + QString getAddress() const { return socket->peerAddress().toString(); } + QString getConnectionType() const { return "tcp"; }; private: - QTcpSocket *socket; - QByteArray inputBuffer; - bool messageInProgress; - bool handshakeStarted; - int messageLength; + QTcpSocket *socket; + QByteArray inputBuffer; + bool messageInProgress; + bool handshakeStarted; + int messageLength; protected: - void writeToSocket(QByteArray & data) { socket->write(data); }; - void flushSocket() { socket->flush(); }; - void initSessionDeprecated(); - bool initTcpSession(); + void writeToSocket(QByteArray & data) { socket->write(data); }; + void flushSocket() { socket->flush(); }; + void initSessionDeprecated(); + bool initTcpSession(); protected slots: - void readClient(); - void flushOutputQueue(); + void readClient(); + void flushOutputQueue(); public slots: - void initConnection(int socketDescriptor); + void initConnection(int socketDescriptor); }; #if QT_VERSION > 0x050300 class WebsocketServerSocketInterface : public AbstractServerSocketInterface { - Q_OBJECT + Q_OBJECT public: - WebsocketServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); - ~WebsocketServerSocketInterface(); + WebsocketServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); + ~WebsocketServerSocketInterface(); - QHostAddress getPeerAddress() const { return socket->peerAddress(); } - QString getAddress() const { return socket->peerAddress().toString(); } - QString getConnectionType() const { return "websocket"; }; + QHostAddress getPeerAddress() const { return socket->peerAddress(); } + QString getAddress() const { return socket->peerAddress().toString(); } + QString getConnectionType() const { return "websocket"; }; private: - QWebSocket *socket; + QWebSocket *socket; protected: - void writeToSocket(QByteArray & data) { socket->sendBinaryMessage(data); }; - void flushSocket() { socket->flush(); }; - bool initWebsocketSession(); + void writeToSocket(QByteArray & data) { socket->sendBinaryMessage(data); }; + void flushSocket() { socket->flush(); }; + bool initWebsocketSession(); protected slots: - void binaryMessageReceived(const QByteArray & message); - void flushOutputQueue(); + void binaryMessageReceived(const QByteArray & message); + void flushOutputQueue(); public slots: - void initConnection(void * _socket); + void initConnection(void * _socket); }; #endif diff --git a/servatrice/src/smtpclient.cpp b/servatrice/src/smtpclient.cpp index aa2f9198..7e131d3f 100644 --- a/servatrice/src/smtpclient.cpp +++ b/servatrice/src/smtpclient.cpp @@ -82,6 +82,54 @@ bool SmtpClient::enqueueActivationTokenMail(const QString &nickname, const QStri 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() { // still connected from the previous round diff --git a/servatrice/src/smtpclient.h b/servatrice/src/smtpclient.h index b2b1ad00..4e07021f 100644 --- a/servatrice/src/smtpclient.h +++ b/servatrice/src/smtpclient.h @@ -7,28 +7,29 @@ class QxtSmtp; class QxtMailMessage; class SmtpClient : public QObject { - Q_OBJECT + Q_OBJECT public: - SmtpClient(QObject *parent = 0); - ~SmtpClient(); + SmtpClient(QObject *parent = 0); + ~SmtpClient(); protected: - QxtSmtp *smtp; + QxtSmtp *smtp; public slots: - bool enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token); - void sendAllEmails(); + bool enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token); + bool enqueueForgotPasswordTokenMail(const QString &nickname, const QString &recipient, const QString &token); + void sendAllEmails(); protected slots: - void authenticated(); - void authenticationFailed(const QByteArray & msg); - void connected(); - void connectionFailed(const QByteArray & msg); - void disconnected(); - void encrypted(); - void encryptionFailed(const QByteArray & msg); - void finished(); - void mailFailed(int mailID, int errorCode, const QByteArray & msg); - void mailSent(int mailID); - void recipientRejected(int mailID, const QString & address, const QByteArray & msg); - void senderRejected(int mailID, const QString & address, const QByteArray & msg); + void authenticated(); + void authenticationFailed(const QByteArray & msg); + void connected(); + void connectionFailed(const QByteArray & msg); + void disconnected(); + void encrypted(); + void encryptionFailed(const QByteArray & msg); + void finished(); + void mailFailed(int mailID, int errorCode, const QByteArray & msg); + void mailSent(int mailID); + void recipientRejected(int mailID, const QString & address, const QByteArray & msg); + void senderRejected(int mailID, const QString & address, const QByteArray & msg); }; #endif \ No newline at end of file