From 2437309098844a83f9f290240c6dec85192ffb15 Mon Sep 17 00:00:00 2001 From: Moritz Strohm <strohm@data-quest.de> Date: Thu, 30 Nov 2023 14:48:57 +0100 Subject: [PATCH] fix for BIESt 3523, closes #3523 --- lib/models/MailQueueEntry.class.php | 18 +-- lib/models/MailQueueEntry.class.php.orig | 137 +++++++++++++++++++++++ 2 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 lib/models/MailQueueEntry.class.php.orig diff --git a/lib/models/MailQueueEntry.class.php b/lib/models/MailQueueEntry.class.php index 7fe63f67ea8..bc956e78f70 100644 --- a/lib/models/MailQueueEntry.class.php +++ b/lib/models/MailQueueEntry.class.php @@ -123,15 +123,19 @@ class MailQueueEntry extends SimpleORMap { $mail = new StudipMail($this->mail); - $success = $mail->send(); - if ($success) { - $this->delete(); + if ($mail->getRecipients()) { + $success = $mail->send(); + if ($success) { + $this->delete(); + } else { + $this['tries'] = $this['tries'] + 1; + $this['last_try'] = time(); + $this->store(); + } } else { - $this['tries'] = $this['tries'] + 1; - $this['last_try'] = time(); - $this->store(); + $success = false; + $this->delete(); } - return $success; } } diff --git a/lib/models/MailQueueEntry.class.php.orig b/lib/models/MailQueueEntry.class.php.orig new file mode 100644 index 00000000000..7fe63f67ea8 --- /dev/null +++ b/lib/models/MailQueueEntry.class.php.orig @@ -0,0 +1,137 @@ +<?php + +/* + * Copyright (c) 2013 Rasmus Fuhse <fuhse@data-quest.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +/** + * Class to handle entries in the mail-queue in Stud.IP. + * Use MailQueueEntry::add($mail, $message_id, $user_id) to add a mail to the queue + * and MailQueueEntry::sendAll() or MailQueueEntry::sendNew() to flush the queue + * and send the mails. + * + * @property string $id alias column for mail_queue_id + * @property string $mail_queue_id database column + * @property JSONArrayObject $mail database column + * @property string|null $message_id database column + * @property string|null $user_id database column + * @property int $tries database column + * @property int $last_try database column + * @property int $mkdate database column + * @property int $chdate database column + */ +class MailQueueEntry extends SimpleORMap +{ + protected static function configure($config = []) + { + $config['db_table'] = 'mail_queue_entries'; + + $config['serialized_fields']['mail'] = JSONArrayObject::class; + + parent::configure($config); + } + + /** + * Add an email to the queue. + * @param StudipMail $mail : the mailobject that should be added and sent later. + * @param string|null $message_id : the id of the Stud.IP internal message the + * mail is related to. Leave this null if it isn't related to any internal message. + * @param string|null $user_id : user_id of the receiver. Leave null if the + * receiver has no account in Stud.IP. + * @return MailQueueEntry : object in the mailqueue. + */ + public static function add(StudipMail $mail, $message_id = null, $user_id = null) + { + $queue_entry = new self(); + $queue_entry['mail'] = $mail->toArray(); + $queue_entry['message_id'] = $message_id; + $queue_entry['user_id'] = $user_id; + $queue_entry['tries'] = 0; + $queue_entry->store(); + + return $queue_entry; + } + + /** + * Sends all new mails in the mailqueue (which means they haven't been sent yet). + */ + public static function sendNew() + { + self::findEachBySQL(function ($m) { + $m->send(); + }, "tries = 0 ORDER BY mkdate"); + } + + /** + * Sends all mails in the mailqueue. Stud.IP will give each mail 24 tries to + * deliver it. If the mail could not be sent after 24 tries (which are 24 + * hours) it will stay in the mailqueue table but won't be sent anymore. + * Each mail will only be tried to deliver once per hour. So if it fails + * Stud.IP will try again next hour. + * + * @param int $limit The maximum amount of messages to be sent. + * @return array An empty array if no status messages are output + * or an array with status messages, one for each mail. + */ + public static function sendAll($limit = null) + { + //The status messages will be returned + $status_messages = []; + + self::findEachBySQL(function ($m) use (&$status_messages) { + // Reconstruct the StudipMail object + $mail = new StudipMail($m->mail); + $status_message = sprintf( + 'sending message %1$s (sender: %2$s, %3$u recipient(s))...', + $m->message_id, + $mail->getSenderEmail(), + count($mail->getRecipients()) + ); + + $was_sent = $m->send(); + $status_message .= $was_sent ? 'DONE' : 'FAILURE'; + + if ($m->tries > 0) { + // If sending the message has failed at least once + // we add the amount of tries to the status message. + $status_message .= "(t={$m->tries})"; + } + + $status_messages[] = $status_message; + }, "tries = 0 " . + "OR (last_try > (UNIX_TIMESTAMP() - 60 * 60) AND tries < 25) ORDER BY mkdate". + ($limit > 0 ? " LIMIT ". (int) $limit : "") + ); + + return $status_messages; + } + + /** + * Sends the object in the mailqueue. If this succeeds, the object will be + * deleted immediately. Otherwise the field "tries" in the mailqueue table + * will be incremented by one. + * + * @return bool True, if the mail in the mailqueue entry could be sent, + * false otherwise. + */ + public function send() + { + $mail = new StudipMail($this->mail); + + $success = $mail->send(); + if ($success) { + $this->delete(); + } else { + $this['tries'] = $this['tries'] + 1; + $this['last_try'] = time(); + $this->store(); + } + + return $success; + } +} -- GitLab