2323 */
2424class Mail
2525{
26+ /*****************
27+ * Class constants
28+ *****************/
29+
30+ /**
31+ * Maximum number of tries to send a email.
32+ *
33+ * @var int
34+ */
35+ private const MAX_TRIES = 15 ;
36+
37+ /**
38+ * Multiplier for delaying emails that fail to send.
39+ * See calculateNextTry() for implementation
40+ * @var int
41+ */
42+ private const DELAY_MULTIPLIER = 15 ;
43+
2644 /***********************
2745 * Public static methods
2846 ***********************/
@@ -409,11 +427,13 @@ public static function reduceQueue(bool|int $number = false, bool $override_limi
409427 $ emails = [];
410428
411429 $ request = Db::$ db ->query (
412- 'SELECT id_mail, recipient, body, subject, headers, send_html, time_sent, private, priority
430+ 'SELECT id_mail, recipient, body, subject, headers, send_html, time_sent, private, priority, next_try, tries, extra
413431 FROM {db_prefix}mail_queue
414- ORDER BY priority ASC, id_mail ASC
432+ WHERE next_try <= {int:current_time}
433+ ORDER BY priority ASC, next_try ASC, tries ASC, id_mail ASC
415434 LIMIT {int:limit} ' ,
416435 [
436+ 'current_time ' => time (),
417437 'limit ' => $ number ,
418438 ],
419439 );
@@ -431,6 +451,9 @@ public static function reduceQueue(bool|int $number = false, bool $override_limi
431451 'time_sent ' => $ row ['time_sent ' ],
432452 'private ' => $ row ['private ' ],
433453 'priority ' => $ row ['priority ' ],
454+ 'next_try ' => $ row ['next_try ' ],
455+ 'tries ' => $ row ['tries ' ],
456+ 'extra ' => $ row ['extra ' ],
434457 ];
435458 }
436459 Db::$ db ->free_result ($ request );
@@ -467,27 +490,8 @@ public static function reduceQueue(bool|int $number = false, bool $override_limi
467490
468491 // Send each email, yea!
469492 $ failed_emails = [];
470- $ max_priority = 127 ;
471- $ smtp_expire = 259200 ;
472- $ priority_offset = 4 ;
473493
474494 foreach ($ emails as $ email ) {
475- // This seems odd, but check the priority if we should try again so soon. Do this so we don't DOS some poor mail server.
476- if ($ email ['priority ' ] > $ priority_offset && (time () - $ email ['time_sent ' ]) % $ priority_offset != rand (0 , $ priority_offset )) {
477- $ failed_emails [] = [
478- $ email ['to ' ],
479- $ email ['body ' ],
480- $ email ['subject ' ],
481- $ email ['headers ' ],
482- $ email ['send_html ' ],
483- $ email ['time_sent ' ],
484- $ email ['private ' ],
485- $ email ['priority ' ],
486- ];
487-
488- continue ;
489- }
490-
491495 if (empty (Config::$ modSettings ['mail_type ' ]) || Config::$ modSettings ['smtp_host ' ] == '' ) {
492496 $ email ['subject ' ] = strtr ($ email ['subject ' ], ["\r" => '' , "\n" => '' ]);
493497
@@ -507,16 +511,25 @@ public static function reduceQueue(bool|int $number = false, bool $override_limi
507511 }
508512
509513 // Old emails should expire
510- if (!$ result && $ email ['priority ' ] >= $ max_priority ) {
514+ if (!$ result && $ email ['tries ' ] >= self :: MAX_TRIES ) {
511515 $ result = true ;
512516 }
513517
514518 // Hopefully it sent?
515519 if (!$ result ) {
516- // Determine the "priority" as a way to keep track of SMTP failures.
517- $ email ['priority ' ] = max ($ priority_offset , $ email ['priority ' ], min (ceil ((time () - $ email ['time_sent ' ]) / $ smtp_expire * ($ max_priority - $ priority_offset )) + $ priority_offset , $ max_priority ));
518-
519- $ failed_emails [] = [$ email ['to ' ], $ email ['body ' ], $ email ['subject ' ], $ email ['headers ' ], $ email ['send_html ' ], $ email ['time_sent ' ], $ email ['private ' ], $ email ['priority ' ]];
520+ $ failed_emails [] = [
521+ $ email ['to ' ],
522+ $ email ['body ' ],
523+ $ email ['subject ' ],
524+ $ email ['headers ' ],
525+ $ email ['send_html ' ],
526+ $ email ['time_sent ' ],
527+ $ email ['private ' ],
528+ $ email ['priority ' ],
529+ self ::calculateNextTry ($ email ['tries ' ]),
530+ ++$ email ['tries ' ],
531+ $ email ['extra ' ],
532+ ];
520533 }
521534 }
522535
@@ -565,7 +578,10 @@ public static function reduceQueue(bool|int $number = false, bool $override_limi
565578 'send_html ' => 'string ' ,
566579 'time_sent ' => 'string ' ,
567580 'private ' => 'int ' ,
568- 'priority ' => 'int ' ,
581+ 'next_try ' => 'int ' ,
582+ 'tries ' => 'int ' ,
583+ 'extra ' => 'string ' ,
584+
569585 ],
570586 $ failed_emails ,
571587 ['id_mail ' ],
@@ -1120,4 +1136,21 @@ protected static function userInfoCallback(array $matches): string
11201136
11211137 return $ use_ref ? $ ref : $ matches [0 ];
11221138 }
1139+
1140+ /**
1141+ * Based on the number of tries, increase the time we delay the next sending.
1142+ *
1143+ * @param int $tries
1144+ * @return int Next time we should try to send.
1145+ */
1146+ private static function calculateNextTry (int $ tries )
1147+ {
1148+ $ next = time ();
1149+
1150+ for ($ i = 0 ; $ i < ($ tries + 1 ); $ i ++) {
1151+ $ next_send_time += $ i * self ::DELAY_MULTIPLIER ;
1152+ }
1153+
1154+ return $ next ;
1155+ }
11231156}
0 commit comments