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 {};
|
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()
|
virtual void clearSessionTables()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
|
||||||
#include <QSsl>
|
#include <QSsl>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
@ -870,10 +869,8 @@ void Servatrice::statusUpdate()
|
||||||
|
|
||||||
if (getRegistrationEnabled() && getEnableInternalSMTPClient()) {
|
if (getRegistrationEnabled() && getEnableInternalSMTPClient()) {
|
||||||
if (getRequireEmailActivationEnabled()) {
|
if (getRequireEmailActivationEnabled()) {
|
||||||
auto servDbSelQuery = servatriceDatabaseInterface->prepareQuery("select a.name, b.email, b.token from "
|
QList<Server_DatabaseInterface::PendingActivationMailRecord> activationMails;
|
||||||
"{prefix}_activation_emails a left join "
|
if (!servatriceDatabaseInterface->getPendingActivationMails(activationMails)) {
|
||||||
"{prefix}_users b on a.name = b.name");
|
|
||||||
if (!servatriceDatabaseInterface->execSqlQuery(servDbSelQuery)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -881,32 +878,23 @@ void Servatrice::statusUpdate()
|
||||||
// activation e-mail is the database row, which is only removed once delivery has been
|
// 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
|
// confirmed by the SMTP client (see onEmailDelivered). This makes sending resilient to
|
||||||
// transient SMTP failures and server restarts.
|
// transient SMTP failures and server restarts.
|
||||||
while (servDbSelQuery->next()) {
|
for (const auto &mail : activationMails) {
|
||||||
const QString userName = servDbSelQuery->value(0).toString();
|
smtpClient->enqueueActivationTokenMail(
|
||||||
const auto emailAddress = EmailParser::getParsedEmailAddress(servDbSelQuery->value(1).toString());
|
mail.userName, EmailParser::getParsedEmailAddress(mail.emailAddress), mail.token);
|
||||||
const QString token = servDbSelQuery->value(2).toString();
|
|
||||||
|
|
||||||
smtpClient->enqueueActivationTokenMail(userName, emailAddress, token);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getEnableForgotPassword()) {
|
if (getEnableForgotPassword()) {
|
||||||
auto *forgotPwQuery = servatriceDatabaseInterface->prepareQuery(
|
QList<Server_DatabaseInterface::PendingPasswordResetRecord> passwordResets;
|
||||||
"select a.id, a.name, b.email, b.token from {prefix}_forgot_password a left join {prefix}_users b on "
|
if (!servatriceDatabaseInterface->getPendingPasswordResets(passwordResets)) {
|
||||||
"a.name = b.name where a.emailed = 0");
|
|
||||||
if (!servatriceDatabaseInterface->execSqlQuery(forgotPwQuery)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rows are intentionally left with emailed = 0 here; they are marked emailed only once
|
// Rows are intentionally left with emailed = 0 here; they are marked emailed only once
|
||||||
// delivery is confirmed by the SMTP client (see onEmailDelivered).
|
// delivery is confirmed by the SMTP client (see onEmailDelivered).
|
||||||
while (forgotPwQuery->next()) {
|
for (const auto &reset : passwordResets) {
|
||||||
const qint64 rowId = forgotPwQuery->value(0).toLongLong();
|
smtpClient->enqueueForgotPasswordTokenMail(
|
||||||
const QString userName = forgotPwQuery->value(1).toString();
|
reset.userName, EmailParser::getParsedEmailAddress(reset.emailAddress), reset.token, reset.rowId);
|
||||||
const auto emailAddress = EmailParser::getParsedEmailAddress(forgotPwQuery->value(2).toString());
|
|
||||||
const QString token = forgotPwQuery->value(3).toString();
|
|
||||||
|
|
||||||
smtpClient->enqueueForgotPasswordTokenMail(userName, emailAddress, token, rowId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -952,27 +940,13 @@ bool Servatrice::cleanupDatabaseForEmail(const QString &userName, EmailType type
|
||||||
{
|
{
|
||||||
Q_UNUSED(token);
|
Q_UNUSED(token);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case EmailType::Activation: {
|
case EmailType::Activation:
|
||||||
QSqlQuery *query =
|
return servatriceDatabaseInterface->deleteActivationEmail(userName);
|
||||||
servatriceDatabaseInterface->prepareQuery("delete from {prefix}_activation_emails where name = :name");
|
case EmailType::ForgotPassword:
|
||||||
query->bindValue(":name", userName);
|
|
||||||
return servatriceDatabaseInterface->execSqlQuery(query);
|
|
||||||
}
|
|
||||||
case EmailType::ForgotPassword: {
|
|
||||||
// Scope the update to the row that was actually sent: the table
|
// Scope the update to the row that was actually sent: the table
|
||||||
// holds one row per request, and marking by name would also mark
|
// holds one row per request, and marking by name would also mark
|
||||||
// rows for newer tokens that were never delivered.
|
// rows for newer tokens that were never delivered.
|
||||||
if (rowId >= 0) {
|
return servatriceDatabaseInterface->markPasswordResetEmailDelivered(userName, rowId);
|
||||||
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 false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3421,6 +3421,83 @@ QList<Server_DatabaseInterface::BootstrapRoomRecord> Servatrice_DatabaseInterfac
|
||||||
return rooms;
|
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)
|
bool Servatrice_DatabaseInterface::removeUserAvatar(const QString &userName)
|
||||||
{
|
{
|
||||||
if (!checkSql()) {
|
if (!checkSql()) {
|
||||||
|
|
|
||||||
|
|
@ -282,6 +282,10 @@ public:
|
||||||
QList<ServerListEntryRecord> getServerListEntries() override;
|
QList<ServerListEntryRecord> getServerListEntries() override;
|
||||||
void recordUptime(const UptimeRecord &record) override;
|
void recordUptime(const UptimeRecord &record) override;
|
||||||
QList<BootstrapRoomRecord> getBootstrapRooms(int serverId) 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;
|
bool removeUserAvatar(const QString &userName) override;
|
||||||
// Outcome of removeUserBanner(), so the caller can tell a missing user apart
|
// Outcome of removeUserBanner(), so the caller can tell a missing user apart
|
||||||
// from a user who simply has no banner card set.
|
// from a user who simply has no banner card set.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue