/** * Display the mail queue... * * @uses ManageMail template */ public function action_browse() { global $scripturl, $context, $txt; require_once SUBSDIR . '/Mail.subs.php'; loadTemplate('ManageMail'); // First, are we deleting something from the queue? if (isset($_REQUEST['delete'])) { checkSession('post'); deleteMailQueueItems($_REQUEST['delete']); } // Fetch the number of items in the current queue $status = list_MailQueueStatus(); $context['oldest_mail'] = empty($status['mailOldest']) ? $txt['mailqueue_oldest_not_available'] : time_since(time() - $status['mailOldest']); $context['mail_queue_size'] = comma_format($status['mailQueueSize']); // Build our display list $listOptions = array('id' => 'mail_queue', 'title' => $txt['mailqueue_browse'], 'items_per_page' => 20, 'base_href' => $scripturl . '?action=admin;area=mailqueue', 'default_sort_col' => 'age', 'no_items_label' => $txt['mailqueue_no_items'], 'get_items' => array('function' => 'list_getMailQueue'), 'get_count' => array('function' => 'list_getMailQueueSize'), 'columns' => array('subject' => array('header' => array('value' => $txt['mailqueue_subject']), 'data' => array('function' => create_function('$rowData', ' return Util::shorten_text(Util::htmlspecialchars($rowData[\'subject\'], 50)); ')), 'sort' => array('default' => 'subject', 'reverse' => 'subject DESC')), 'recipient' => array('header' => array('value' => $txt['mailqueue_recipient']), 'data' => array('sprintf' => array('format' => '<a href="mailto:%1$s">%1$s</a>', 'params' => array('recipient' => true))), 'sort' => array('default' => 'recipient', 'reverse' => 'recipient DESC')), 'priority' => array('header' => array('value' => $txt['mailqueue_priority'], 'class' => 'centertext'), 'data' => array('function' => create_function('$rowData', ' global $txt; // We probably have a text label with your priority. $txtKey = sprintf(\'mq_mpriority_%1$s\', $rowData[\'priority\']); // But if not, revert to priority 0. return isset($txt[$txtKey]) ? $txt[$txtKey] : $txt[\'mq_mpriority_1\']; '), 'class' => 'centertext'), 'sort' => array('default' => 'priority', 'reverse' => 'priority DESC')), 'age' => array('header' => array('value' => $txt['mailqueue_age']), 'data' => array('function' => create_function('$rowData', ' return time_since(time() - $rowData[\'time_sent\']); ')), 'sort' => array('default' => 'time_sent', 'reverse' => 'time_sent DESC')), 'check' => array('header' => array('value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />'), 'data' => array('function' => create_function('$rowData', ' return \'<input type="checkbox" name="delete[]" value="\' . $rowData[\'id_mail\'] . \'" class="input_check" />\'; ')))), 'form' => array('href' => $scripturl . '?action=admin;area=mailqueue', 'include_start' => true, 'include_sort' => true), 'additional_rows' => array(array('position' => 'bottom_of_list', 'class' => 'submitbutton', 'value' => ' <input type="submit" name="delete_redirects" value="' . $txt['quickmod_delete_selected'] . '" onclick="return confirm(\'' . $txt['quickmod_confirm'] . '\');" class="right_submit" /> <a class="linkbutton" href="' . $scripturl . '?action=admin;area=mailqueue;sa=clear;' . $context['session_var'] . '=' . $context['session_id'] . '" onclick="return confirm(\'' . $txt['mailqueue_clear_list_warning'] . '\');">' . $txt['mailqueue_clear_list'] . '</a> '))); require_once SUBSDIR . '/GenericList.class.php'; createList($listOptions); }
/** * Sends a group of emails from the mail queue. * * - Allows a batch of emails to be released every 5 to 10 seconds (based on per period limits) * - If batch size is not set, will determine a size such that it sends in 1/2 the period (buffer) * * @package Mail * @param int|false $batch_size = false the number to send each loop * @param boolean $override_limit = false bypassing our limit flaf * @param boolean $force_send = false * @return boolean */ function reduceMailQueue($batch_size = false, $override_limit = false, $force_send = false) { global $modSettings, $context, $webmaster_email, $scripturl; // Do we have another script to send out the queue? if (!empty($modSettings['mail_queue_use_cron']) && empty($force_send)) { return false; } // How many emails can we send each time we are called in a period if (!$batch_size) { // Batch size has been set in the ACP, use it if (!empty($modSettings['mail_batch_size'])) { $batch_size = $modSettings['mail_batch_size']; } elseif (empty($modSettings['mail_period_limit'])) { $batch_size = 5; } else { // Based on the number of times we will potentially be called each minute $delay = !empty($modSettings['mail_queue_delay']) ? $modSettings['mail_queue_delay'] : (!empty($modSettings['mail_period_limit']) && $modSettings['mail_period_limit'] <= 5 ? 10 : 5); $batch_size = ceil($modSettings['mail_period_limit'] / ceil(60 / $delay)); $batch_size = $batch_size == 1 && $modSettings['mail_period_limit'] > 1 ? 2 : $batch_size; } } // If we came with a timestamp, and that doesn't match the next event, then someone else has beaten us. if (isset($_GET['ts']) && $_GET['ts'] != $modSettings['mail_next_send'] && empty($force_send)) { return false; } // Prepare to send each email, and log that for future proof. require_once SUBSDIR . '/Maillist.subs.php'; // Set the delay for the next sending if (!$override_limit) { // Update next send time for our mail queue, if there was something to update. Otherwise bail out :P $delay = updateNextSendTime(); if ($delay === false) { return false; } $modSettings['mail_next_send'] = time() + $delay; } // If we're not overriding, do we have quota left in this mail period limit? if (!$override_limit && !empty($modSettings['mail_period_limit'])) { // See if we have quota left to send another batch_size this minute or if we have to wait list($mail_time, $mail_number) = isset($modSettings['mail_recent']) ? explode('|', $modSettings['mail_recent']) : array(0, 0); // Nothing worth noting... if (empty($mail_number) || $mail_time < time() - 60) { $mail_time = time(); $mail_number = $batch_size; } elseif ($mail_number < $modSettings['mail_period_limit']) { // If this is likely one of the last cycles for this period, then send any remaining quota if ($mail_time - (time() - 60) < $delay * 2) { $batch_size = $modSettings['mail_period_limit'] - $mail_number; } elseif ($mail_number + $batch_size > $modSettings['mail_period_limit']) { $batch_size = $modSettings['mail_period_limit'] - $mail_number; } $mail_number += $batch_size; } else { return false; } // Reflect that we're about to send some, do it now to be safe. updateSettings(array('mail_recent' => $mail_time . '|' . $mail_number)); } // Now we know how many we're sending, let's send them. list($ids, $emails) = emailsInfo($batch_size); // Delete, delete, delete!!! if (!empty($ids)) { deleteMailQueueItems($ids); } // Don't believe we have any left after this batch? if (count($ids) < $batch_size) { resetNextSendTime(); } if (empty($ids)) { return false; } // We have some to send, lets send them! $sent = array(); $failed_emails = array(); // Use sendmail or SMTP $use_sendmail = empty($modSettings['mail_type']) || $modSettings['smtp_host'] == ''; // Line breaks need to be \r\n only in windows or for SMTP. $line_break = !empty($context['server']['is_windows']) || !$use_sendmail ? "\r\n" : "\n"; foreach ($emails as $key => $email) { // Use the right mail resource if ($use_sendmail) { $email['subject'] = strtr($email['subject'], array("\r" => '', "\n" => '')); if (!empty($modSettings['mail_strip_carriage'])) { $email['body'] = strtr($email['body'], array("\r" => '')); $email['headers'] = strtr($email['headers'], array("\r" => '')); } $need_break = substr($email['headers'], -1) === "\n" || substr($email['headers'], -1) === "\r" ? false : true; // Create our unique reply to email header if this message needs one $unq_id = ''; $unq_head = ''; if (!empty($modSettings['maillist_enabled']) && $email['message_id'] !== null && strpos($email['headers'], 'List-Id: <') !== false) { $unq_head = md5($scripturl . microtime() . rand()) . '-' . $email['message_id']; $encoded_unq_head = base64_encode($line_break . $line_break . '[' . $unq_head . ']' . $line_break); $unq_id = ($need_break ? $line_break : '') . 'Message-ID: <' . $unq_head . strstr(empty($modSettings['maillist_mail_from']) ? $webmaster_email : $modSettings['maillist_mail_from'], '@') . '>'; $email['body_fail'] = $email['body']; $email['body'] = mail_insert_key($email['body'], $unq_head, $encoded_unq_head, $line_break); } elseif ($email['message_id'] !== null && empty($modSettings['mail_no_message_id'])) { $unq_id = ($need_break ? $line_break : '') . 'Message-ID: <' . md5($scripturl . microtime()) . '-' . $email['message_id'] . strstr(empty($modSettings['maillist_mail_from']) ? $webmaster_email : $modSettings['maillist_mail_from'], '@') . '>'; } // No point logging a specific error here, as we have no language. PHP error is helpful anyway... $result = mail(strtr($email['to'], array("\r" => '', "\n" => '')), $email['subject'], $email['body'], $email['headers'] . $unq_id); // If it sent, keep a record so we can save it in our allowed to reply log if (!empty($unq_head) && $result) { $sent[] = array($unq_head, time(), $email['to']); } // Track total emails sent if ($result && !empty($modSettings['trackStats'])) { trackStats(array('email' => '+')); } // Try to stop a timeout, this would be bad... @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } } else { $result = smtp_mail(array($email['to']), $email['subject'], $email['body'], $email['send_html'] ? $email['headers'] : 'Mime-Version: 1.0' . "\r\n" . $email['headers'], $email['priority'], $email['message_id']); } // Hopefully it sent? if (!$result) { $failed_emails[] = array(time(), $email['to'], $email['body_fail'], $email['subject'], $email['headers'], $email['send_html'], $email['priority'], $email['private'], $email['message_id']); } } // Clear out the stat cache. trackStats(); // Log each of the sent emails. if (!empty($sent)) { log_email($sent); } // Any emails that didn't send? if (!empty($failed_emails)) { // If it failed, add it back to the queue updateFailedQueue($failed_emails); return false; } elseif (!empty($modSettings['mail_failed_attempts'])) { updateSuccessQueue(); } // Had something to send... return true; }