mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-10 03:53:26 -08:00
[Server] Serve the e-mail queue through the database interface (#7858)
This commit is contained in:
parent
9087d0d3fe
commit
526d528e96
4 changed files with 131 additions and 40 deletions
|
|
@ -780,6 +780,42 @@ public:
|
|||
return {};
|
||||
}
|
||||
|
||||
/** @brief One queued activation e-mail, with the address exactly as stored. */
|
||||
struct PendingActivationMailRecord
|
||||
{
|
||||
QString userName;
|
||||
QString emailAddress;
|
||||
QString token;
|
||||
};
|
||||
/** @brief One queued password-reset e-mail, identified by its queue row. */
|
||||
struct PendingPasswordResetRecord
|
||||
{
|
||||
qint64 rowId = 0;
|
||||
QString userName;
|
||||
QString emailAddress;
|
||||
QString token;
|
||||
};
|
||||
/** @brief Fills the queued activation e-mails; false when the query fails. */
|
||||
virtual bool getPendingActivationMails(QList<PendingActivationMailRecord> & /* mails */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/** @brief Fills the unsent password-reset e-mails; false when the query fails. */
|
||||
virtual bool getPendingPasswordResets(QList<PendingPasswordResetRecord> & /* resets */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/** @brief Deletes a delivered activation e-mail queue row; false when the statement fails. */
|
||||
virtual bool deleteActivationEmail(const QString & /* userName */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/** @brief Marks a password-reset queue row as emailed, by row id when known; false on failure. */
|
||||
virtual bool markPasswordResetEmailDelivered(const QString & /* userName */, qint64 /* rowId */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void clearSessionTables()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@
|
|||
#include <QSet>
|
||||
#include <QSharedPointer>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
#include <QSsl>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
|
@ -870,10 +869,8 @@ void Servatrice::statusUpdate()
|
|||
|
||||
if (getRegistrationEnabled() && getEnableInternalSMTPClient()) {
|
||||
if (getRequireEmailActivationEnabled()) {
|
||||
auto servDbSelQuery = 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(servDbSelQuery)) {
|
||||
QList<Server_DatabaseInterface::PendingActivationMailRecord> activationMails;
|
||||
if (!servatriceDatabaseInterface->getPendingActivationMails(activationMails)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -881,32 +878,23 @@ void Servatrice::statusUpdate()
|
|||
// activation e-mail is the database row, which is only removed once delivery has been
|
||||
// confirmed by the SMTP client (see onEmailDelivered). This makes sending resilient to
|
||||
// transient SMTP failures and server restarts.
|
||||
while (servDbSelQuery->next()) {
|
||||
const QString userName = servDbSelQuery->value(0).toString();
|
||||
const auto emailAddress = EmailParser::getParsedEmailAddress(servDbSelQuery->value(1).toString());
|
||||
const QString token = servDbSelQuery->value(2).toString();
|
||||
|
||||
smtpClient->enqueueActivationTokenMail(userName, emailAddress, token);
|
||||
for (const auto &mail : activationMails) {
|
||||
smtpClient->enqueueActivationTokenMail(
|
||||
mail.userName, EmailParser::getParsedEmailAddress(mail.emailAddress), mail.token);
|
||||
}
|
||||
}
|
||||
|
||||
if (getEnableForgotPassword()) {
|
||||
auto *forgotPwQuery = servatriceDatabaseInterface->prepareQuery(
|
||||
"select a.id, 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(forgotPwQuery)) {
|
||||
QList<Server_DatabaseInterface::PendingPasswordResetRecord> passwordResets;
|
||||
if (!servatriceDatabaseInterface->getPendingPasswordResets(passwordResets)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rows are intentionally left with emailed = 0 here; they are marked emailed only once
|
||||
// delivery is confirmed by the SMTP client (see onEmailDelivered).
|
||||
while (forgotPwQuery->next()) {
|
||||
const qint64 rowId = forgotPwQuery->value(0).toLongLong();
|
||||
const QString userName = forgotPwQuery->value(1).toString();
|
||||
const auto emailAddress = EmailParser::getParsedEmailAddress(forgotPwQuery->value(2).toString());
|
||||
const QString token = forgotPwQuery->value(3).toString();
|
||||
|
||||
smtpClient->enqueueForgotPasswordTokenMail(userName, emailAddress, token, rowId);
|
||||
for (const auto &reset : passwordResets) {
|
||||
smtpClient->enqueueForgotPasswordTokenMail(
|
||||
reset.userName, EmailParser::getParsedEmailAddress(reset.emailAddress), reset.token, reset.rowId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -952,27 +940,13 @@ bool Servatrice::cleanupDatabaseForEmail(const QString &userName, EmailType type
|
|||
{
|
||||
Q_UNUSED(token);
|
||||
switch (type) {
|
||||
case EmailType::Activation: {
|
||||
QSqlQuery *query =
|
||||
servatriceDatabaseInterface->prepareQuery("delete from {prefix}_activation_emails where name = :name");
|
||||
query->bindValue(":name", userName);
|
||||
return servatriceDatabaseInterface->execSqlQuery(query);
|
||||
}
|
||||
case EmailType::ForgotPassword: {
|
||||
case EmailType::Activation:
|
||||
return servatriceDatabaseInterface->deleteActivationEmail(userName);
|
||||
case EmailType::ForgotPassword:
|
||||
// Scope the update to the row that was actually sent: the table
|
||||
// holds one row per request, and marking by name would also mark
|
||||
// rows for newer tokens that were never delivered.
|
||||
if (rowId >= 0) {
|
||||
QSqlQuery *query = servatriceDatabaseInterface->prepareQuery(
|
||||
"update {prefix}_forgot_password set emailed = 1 where id = :id");
|
||||
query->bindValue(":id", rowId);
|
||||
return servatriceDatabaseInterface->execSqlQuery(query);
|
||||
}
|
||||
QSqlQuery *query = servatriceDatabaseInterface->prepareQuery(
|
||||
"update {prefix}_forgot_password set emailed = 1 where name = :name");
|
||||
query->bindValue(":name", userName);
|
||||
return servatriceDatabaseInterface->execSqlQuery(query);
|
||||
}
|
||||
return servatriceDatabaseInterface->markPasswordResetEmailDelivered(userName, rowId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3421,6 +3421,83 @@ QList<Server_DatabaseInterface::BootstrapRoomRecord> Servatrice_DatabaseInterfac
|
|||
return rooms;
|
||||
}
|
||||
|
||||
bool Servatrice_DatabaseInterface::getPendingActivationMails(QList<PendingActivationMailRecord> &mails)
|
||||
{
|
||||
if (!checkSql()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QSqlQuery *query = prepareQuery("select a.name, b.email, b.token from "
|
||||
"{prefix}_activation_emails a left join "
|
||||
"{prefix}_users b on a.name = b.name");
|
||||
if (!execSqlQuery(query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (query->next()) {
|
||||
PendingActivationMailRecord mail;
|
||||
mail.userName = query->value(0).toString();
|
||||
mail.emailAddress = query->value(1).toString();
|
||||
mail.token = query->value(2).toString();
|
||||
mails.append(mail);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Servatrice_DatabaseInterface::getPendingPasswordResets(QList<PendingPasswordResetRecord> &resets)
|
||||
{
|
||||
if (!checkSql()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QSqlQuery *query = prepareQuery(
|
||||
"select a.id, 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 (!execSqlQuery(query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (query->next()) {
|
||||
PendingPasswordResetRecord reset;
|
||||
reset.rowId = query->value(0).toLongLong();
|
||||
reset.userName = query->value(1).toString();
|
||||
reset.emailAddress = query->value(2).toString();
|
||||
reset.token = query->value(3).toString();
|
||||
resets.append(reset);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Servatrice_DatabaseInterface::deleteActivationEmail(const QString &userName)
|
||||
{
|
||||
if (!checkSql()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QSqlQuery *query = prepareQuery("delete from {prefix}_activation_emails where name = :name");
|
||||
query->bindValue(":name", userName);
|
||||
return execSqlQuery(query);
|
||||
}
|
||||
|
||||
bool Servatrice_DatabaseInterface::markPasswordResetEmailDelivered(const QString &userName, qint64 rowId)
|
||||
{
|
||||
if (!checkSql()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rowId >= 0) {
|
||||
QSqlQuery *query = prepareQuery("update {prefix}_forgot_password set emailed = 1 where id = :id");
|
||||
query->bindValue(":id", rowId);
|
||||
return execSqlQuery(query);
|
||||
}
|
||||
|
||||
QSqlQuery *query = prepareQuery("update {prefix}_forgot_password set emailed = 1 where name = :name");
|
||||
query->bindValue(":name", userName);
|
||||
return execSqlQuery(query);
|
||||
}
|
||||
|
||||
bool Servatrice_DatabaseInterface::removeUserAvatar(const QString &userName)
|
||||
{
|
||||
if (!checkSql()) {
|
||||
|
|
|
|||
|
|
@ -282,6 +282,10 @@ public:
|
|||
QList<ServerListEntryRecord> getServerListEntries() override;
|
||||
void recordUptime(const UptimeRecord &record) override;
|
||||
QList<BootstrapRoomRecord> getBootstrapRooms(int serverId) override;
|
||||
bool getPendingActivationMails(QList<PendingActivationMailRecord> &mails) override;
|
||||
bool getPendingPasswordResets(QList<PendingPasswordResetRecord> &resets) override;
|
||||
bool deleteActivationEmail(const QString &userName) override;
|
||||
bool markPasswordResetEmailDelivered(const QString &userName, qint64 rowId) override;
|
||||
bool removeUserAvatar(const QString &userName) override;
|
||||
// Outcome of removeUserBanner(), so the caller can tell a missing user apart
|
||||
// from a user who simply has no banner card set.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue