diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 3a0d4044..8515d64b 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -30,6 +30,10 @@ #include #include #include +#if QT_VERSION < 0x050000 + // for Qt::escape() + #include +#endif #include "main.h" #include "window_main.h" @@ -79,7 +83,7 @@ void MainWindow::processConnectionClosedEvent(const Event_ConnectionClosed &even break; } case Event_ConnectionClosed::SERVER_SHUTDOWN: reasonStr = tr("Scheduled server shutdown."); break; - case Event_ConnectionClosed::USERNAMEINVALID: reasonStr = tr("Invalid username.\nYou may only use A-Z, a-z, 0-9, _, ., and - in your username."); break; + case Event_ConnectionClosed::USERNAMEINVALID: reasonStr = tr("Invalid username."); break; default: reasonStr = QString::fromStdString(event.reason_str()); } QMessageBox::critical(this, tr("Connection closed"), tr("The server has terminated your connection.\nReason: %1").arg(reasonStr)); @@ -302,9 +306,10 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32 QMessageBox::critical(this, tr("Error"), bannedStr); break; } - case Response::RespUsernameInvalid: - QMessageBox::critical(this, tr("Error"), tr("Invalid username.\nYou may only use A-Z, a-z, 0-9, _, ., and - in your username.")); + case Response::RespUsernameInvalid: { + QMessageBox::critical(this, tr("Error"), extractInvalidUsernameMessage(reasonStr)); break; + } case Response::RespRegistrationRequired: if (QMessageBox::question(this, tr("Error"), tr("This server requires user registration. Do you want to register now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { actRegister(); @@ -328,6 +333,41 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32 actConnect(); } +QString MainWindow::extractInvalidUsernameMessage(QString & in) +{ + QString out = tr("Invalid username.") + "
"; + QStringList rules = in.split(QChar('|')); + if (rules.size() == 7) + { + out += tr("Your username must respect these rules:") + "
    "; + + out += "
  • " + tr("is %1 - %2 characters long").arg(rules.at(0)).arg(rules.at(1)) + "
  • "; + out += "
  • " + tr("can %1 contain lowercase characters").arg((rules.at(2).toInt() > 0) ? "" : tr("NOT")) + "
  • "; + out += "
  • " + tr("can %1 contain uppercase characters").arg((rules.at(3).toInt() > 0) ? "" : tr("NOT")) + "
  • "; + out += "
  • " + tr("can %1 contain numeric characters").arg((rules.at(4).toInt() > 0) ? "" : tr("NOT")) + "
  • "; + + if (rules.at(6).size() > 0) + { + out += "
  • " + tr("can contain the following punctuation: %1").arg( + #if QT_VERSION < 0x050000 + Qt::escape(rules.at(6)) + #else + rules.at(6).toHtmlEscaped() + #endif + ) + "
  • "; + } + + out += "
  • " + tr("first character can %1 be a punctuation mark").arg((rules.at(5).toInt() > 0) ? "" : tr("NOT")) + "
  • "; + out += "
"; + } + else + { + out += tr("You may only use A-Z, a-z, 0-9, _, ., and - in your username."); + } + + return out; +} + void MainWindow::registerError(Response::ResponseCode r, QString reasonStr, quint32 endTime) { switch (r) { @@ -358,9 +398,10 @@ void MainWindow::registerError(Response::ResponseCode r, QString reasonStr, quin QMessageBox::critical(this, tr("Error"), bannedStr); break; } - case Response::RespUsernameInvalid: - QMessageBox::critical(this, tr("Error"), tr("Invalid username.\nYou may only use A-Z, a-z, 0-9, _, ., and - in your username.")); + case Response::RespUsernameInvalid: { + QMessageBox::critical(this, tr("Error"), extractInvalidUsernameMessage(reasonStr)); break; + } case Response::RespRegistrationFailed: QMessageBox::critical(this, tr("Error"), tr("Registration failed for a technical problem on the server.")); break; diff --git a/cockatrice/src/window_main.h b/cockatrice/src/window_main.h index 57b9e780..5c319318 100644 --- a/cockatrice/src/window_main.h +++ b/cockatrice/src/window_main.h @@ -109,6 +109,7 @@ public: protected: void closeEvent(QCloseEvent *event); void changeEvent(QEvent *event); + QString extractInvalidUsernameMessage(QString & in); }; #endif diff --git a/common/server_database_interface.h b/common/server_database_interface.h index d809b1ff..60d6c056 100644 --- a/common/server_database_interface.h +++ b/common/server_database_interface.h @@ -25,7 +25,7 @@ public: virtual DeckList *getDeckFromDatabase(int /* deckId */, int /* userId */) { return 0; } virtual qint64 startSession(const QString & /* userName */, const QString & /* address */) { return 0; } - virtual bool usernameIsValid(const QString & /*userName */) { return true; }; + virtual bool usernameIsValid(const QString & /*userName */, QString & /* error */) { return true; }; public slots: virtual void endSession(qint64 /* sessionId */ ) { } public: diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 46e2bb83..7df87189 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -388,7 +388,12 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd } case NotLoggedIn: return Response::RespWrongPassword; case WouldOverwriteOldSession: return Response::RespWouldOverwriteOldSession; - case UsernameInvalid: return Response::RespUsernameInvalid; + case UsernameInvalid: { + Response_Login *re = new Response_Login; + re->set_denied_reason_str(reasonStr.toStdString()); + rc.setResponseExtension(re); + return Response::RespUsernameInvalid; + } case RegistrationRequired: return Response::RespRegistrationRequired; case UserIsInactive: return Response::RespAccountNotActivated; default: authState = res; diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index 3a81c9ba..b48fb738 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -118,24 +118,29 @@ bool Servatrice_DatabaseInterface::execSqlQuery(QSqlQuery *query) return false; } -bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user) +bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString & error) { - int maxNameLength = settingsCache->value("users/maxnamelength", 12).toInt(); int minNameLength = settingsCache->value("users/minnamelength", 6).toInt(); + int maxNameLength = settingsCache->value("users/maxnamelength", 12).toInt(); + bool allowLowercase = settingsCache->value("users/allowlowercase", true).toBool(); + bool allowUppercase = settingsCache->value("users/allowuppercase", true).toBool(); + bool allowNumerics = settingsCache->value("users/allownumerics", true).toBool(); + bool allowPunctuationPrefix = settingsCache->value("users/allowpunctuationprefix", false).toBool(); + QString allowedPunctuation = settingsCache->value("users/allowedpunctuation", "_").toString(); + error = QString("%1|%2|%3|%4|%5|%6|%7").arg(minNameLength).arg(maxNameLength).arg(allowLowercase).arg(allowUppercase).arg(allowNumerics).arg(allowPunctuationPrefix).arg(allowedPunctuation); + if (user.length() < minNameLength || user.length() > maxNameLength) return false; - bool allowPunctuationPrefix = settingsCache->value("users/allowpunctuationprefix", false).toBool(); - QString allowedPunctuation = settingsCache->value("users/allowedpunctuation", "_").toString(); if (!allowPunctuationPrefix && allowedPunctuation.contains(user.at(0))) return false; QString regEx("["); - if (settingsCache->value("users/allowlowercase", true).toBool()) + if (allowLowercase) regEx.append("a-z"); - if (settingsCache->value("users/allowuppercase", true).toBool()) + if (allowUppercase) regEx.append("A-Z"); - if(settingsCache->value("users/allownumerics", true).toBool()) + if(allowNumerics) regEx.append("0-9"); regEx.append(QRegExp::escape(allowedPunctuation)); regEx.append("]+"); @@ -242,7 +247,7 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot if (!checkSql()) return UnknownUser; - if (!usernameIsValid(user)) + if (!usernameIsValid(user, reasonStr)) return UsernameInvalid; if (checkUserIsBanned(handler->getAddress(), user, reasonStr, banSecondsLeft)) diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index 10770691..e5aae344 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -62,7 +62,7 @@ public: void lockSessionTables(); void unlockSessionTables(); bool userSessionExists(const QString &userName); - bool usernameIsValid(const QString &user); + bool usernameIsValid(const QString &user, QString & error); bool checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining); bool getRequireRegistration(); diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index d4d4bdc4..d781e3e4 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -789,8 +789,14 @@ Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_R } // TODO: Move this method outside of the db interface - if (!sqlInterface->usernameIsValid(userName)) + QString errorString; + if (!sqlInterface->usernameIsValid(userName, errorString)) + { + Response_Register *re = new Response_Register; + re->set_denied_reason_str(errorString.toStdString()); + rc.setResponseExtension(re); return Response::RespUsernameInvalid; + } if(sqlInterface->userExists(userName)) return Response::RespUserAlreadyExists;