theme('placeholder', $mail_id)))); } /** * Send queued e-mail. * * @param $mid * ID of queued e-mail * @return * Number of e-mails sent. */ function mail_queue_send() { $max_can_send = variable_get('mail_queue_throttle', 100); $last_sent = variable_get('mail_queue_last_sent', 0); $can_send = 0; if ($max_can_send > 0) { $period = time() - $last_sent; $can_send = floor($period / 3600 * $max_can_send); $can_send = ($can_send < 1) ? 1 : $can_send; } if ($can_send) { $result = db_query('SELECT q.*, a.* FROM {mail_queue} q INNER JOIN {mail_queue_addresses} a ON q.mid = a.mid WHERE a.sent = 0 ORDER BY q.timestamp ASC'); $mails_sent = array(); $sent = 0; while ($mail = db_fetch_array($result)) { if (system_mailengine('send as is', $mail)){ $sent++; db_query('UPDATE {mail_queue_addresses} SET sent = 1 WHERE mid = %d AND aid = %d', $mail['mid'], $mail['aid']); } else { // We stop sending mail after the first error. break; } if ($sent >= $can_send) { // We stop sending mail after reaching our quota. break; } } variable_set('mail_queue_last_sent', time()); watchdog('mail', format_plural($sent, 'One e-mail from queue sent.', '%count e-mails from queue sent.')); return $sent; } } /** * Clear sent messages from the queue. */ function mail_queue_clean() { // Checks to make sure all the addresses in a given messages' recipient list have been sent $result = db_query('SELECT mid, COUNT(*) FROM {mail_queue_addresses} GROUP BY mid HAVING SUM(sent) = COUNT(*)'); $mail_ids = array(); while ($queue = db_fetch_object($result)) { $mail_ids[] = $queue->mid; } if (count($mail_ids)) { db_query('DELETE FROM {mail_queue} WHERE mid IN (%s)', implode(',', $mail_ids)); db_query('DELETE FROM {mail_queue_addresses} WHERE mid IN (%s)', implode(',', $mail_ids)); } } /** * Gives usefull defaults for standard email headers. * * @param $headers An array of headers * @return header string. */ function mail_prepare_headers($headers) { // Note: This may work or not. The MTA might rewrite the Return-Path, and Errors-To is deprecated. // Depending on your MTA and its configuration you can make it work by specifying appropriate arguments in $additional_parameters $errors_to = variable_get('site_mail', ini_get('sendmail_from')); // allow a mail to overwrite standard headers. $all_headers = array_merge(array('Return-Path' => "<$errors_to>", 'Errors-To' => $errors_to, 'From' => variable_get('site_mail', ini_get('sendmail_from')), 'X-Mailer' => 'Drupal', 'MIME-Version' => '1.0', 'Content-Type' => 'text/plain; charset=utf-8; format=flowed', 'Content-Transfer-Encoding' => '8bit'), $headers); $headers = ''; foreach ($all_headers as $key => $value) { // We strip newlines to prevent spam attacks $value = strtr($value, "\r\n", ' '); if ($value != '') { $headers .= "$key: $value\n"; } } return $headers; } /** * Converts html to utf-8 encoded text. * * @param $txt html text that needs formatting. * @param $inline Optional. If TRUE put links in the text, * if FALSE put a footnote into the text and * a list of links below it. Default: FALSE * * @return formatted text encoded in utf-8 */ function mail_html_to_text($txt, $inline = FALSE) { $pattern = '@(]*>(.+?))@ei'; if ($inline) { $txt = preg_replace($pattern, "_mail_uri('\\2', '\\3')", $txt); } else { $txt = preg_replace($pattern, "'\\3 ['. _mail_urls('\\2') .']'", $txt); $urls = _mail_urls(); if (count($urls)) { $txt .= "\n"; $i = 0; for ($max = count($urls); $i < $max; $i++) { $txt .= '['. ($i + 1) .'] '. $urls[$i] ."\n"; } } _mail_urls(0, TRUE); } // some basic html to text conversion $txt = preg_replace(_mail_define_search(), _mail_define_replace(), $txt); $txt = preg_replace("/\n\s+\n/", "\n\n", $txt); $txt = strip_tags($txt); $txt = decode_entities($txt); return wordwrap($txt, 80); } /** * Extracts links to local images from html documents. * * @param $html html text * @param $name document name * * @return an array of arrays * array(array( * 'name' => document name * 'content' => html text, local image urls replaced by Content-IDs, * 'Content-Type' => 'text/html; charset=utf-8') * array( * 'name' => file name, * 'file' => reference to local file, * 'Content-ID' => generated Content-ID, * 'Content-Type' => derived using mime_content_type * if available, educated guess otherwise * ) * ) */ function mail_extract_files($html, $name) { $pattern = '@()@ei'; $html = preg_replace($pattern, '""', $html); $pattern = '|(@import\s+"(\S+)";\s*)|ei'; $html = preg_replace($pattern, '""', $html); $pattern = '|()|ei'; $html = preg_replace($pattern, '""', $html); $document = array('name' => $name, 'content' => $html, 'Content-Type' => 'text/html; charset=utf-8'); $files = _mail_file(); return array($document) + $files; } /** * Helper function to extract local files * * @param $file a link to a file * * @return an absolute : */ function _mail_file($file = NULL, $type = '') { static $files = array(); if ($file && !preg_match('@://@', $file) && file_exists($file)) { $content_id = md5($file) .'@'. variable_get('site_name', 'drupal'); if ($type) { $content_type = $type; } else if (function_exists('mime_content_type')) { $content_type = mime_content_type($file); } $new_file = array('name' => substr($file, strrpos($file, '/') + 1), 'file' => $file, 'Content-ID' => $content_id, ); if ($content_type) { $new_file['Content-Type'] = $content_type; } $files[] = $new_file; return 'cid:'. $content_id; } else if ($file) { return $file; } else { return $files; } } /** * * @param $parts * an array of parts to be included * each part is itself an array: * array( * 'name' => $name the name of the attachement * 'content' => $content textual content * 'file' => $file a file * 'Content-Type' => Content type of either file or content. * Mandatory for content, optional for file. * If not present, it will be derived from * file the file if mime_content_type is available. * If not, application/octet-stream is used. * 'Content-Disposition' => optional, inline is assumed * 'Content-Transfer-Encoding' => optional, * base64 is assumed for files * 8bit for other content. * 'Content-ID' => optional, for in-mail references to attachements. * ) * name is mandatory, one of content and file is required, * they are mutually exclusive. * * @param $content_type * Content-Type for the combined message, optional, default: multipart/mixed * * @return * an array containing the elements 'header' and 'body'. * 'body' is the mime encoded multipart body of a mail. * 'headers' is an array that includes some headers for the mail to be sent. */ function mail_multipart_body($parts, $content_type = 'multipart/mixed') { $boundary = md5(uniqid(time())); $headers['Content-Type'] = "$content_type;\n boundary=\"$boundary\"; type=\"$content_type\""; $body = "\r\nThis is a multi-part message in MIME format.\r\n\r\n"; foreach ($parts as $part) { $sub_boundary = md5($part['name'] . uniqid(time())); if (isset($part['Content-ID'])) { $subheader['Content-ID'] = '<'. $part['Content-ID'] .'>'; } if ($part['Content-Disposition']) { $subheader['Content-Disposition'] = $part['Content-Disposition']; } else { $subheader['Content-Disposition'] = 'inline'; } if ($part['content']) { $subheader['Content-Type'] = $part['Content-Type']; if ($part['Content-Transfer-Encoding']) { $subheader['Content-Transfer-Encoding'] = $part['Content-Transfer-Encoding']; } else { $subheader['Content-Transfer-Encoding'] = '8bit'; } $sub_header = "--$boundary\n"; if (isset($part['Content-ID'])) { $sub_header .= 'Content-ID: <'. $part['Content-ID'] .">\n"; } $sub_header .= "Content-Type: ". $subheader['Content-Type'] .";\n name=\"". $part['name'] ."\" boundary=\"$sub_boundary\"\nContent-Transfer-Encoding: ". $subheader['Content-Transfer-Encoding'] ."\nContent-Disposition: ". $subheader['Content-Disposition'] .";\n"; $body .= $sub_header ."\r\n"; $body .= $part['content'] ."\r\n"; } else { $file_content = file_get_contents($part['file']); if ($part['Content-Type']) { $subheader['Content-Type'] = $part['Content-Type']; } else if (function_exists('mime_content_type')) { $subheader['Content-Type'] = mime_content_type($part['file']); } else { $subheader['Content-Type'] = 'application/octet-stream'; } if ($part['Content-Transfer-Encoding']) { $subheader['Content-Transfer-Encoding'] = $part['Content-Transfer-Encoding']; } else { $subheader['Content-Transfer-Encoding'] = 'base64'; } $sub_header = "--$boundary\n"; if (isset($part['Content-ID'])) { $sub_header .= 'Content-ID: <'. $part['Content-ID'] .">\n"; } $sub_header .= "Content-Type: ". $subheader['Content-Type'] .";\n name=\"". $part['name'] ."\" boundary=\"$sub_boundary\"\nContent-Transfer-Encoding: ". $subheader['Content-Transfer-Encoding'] ."\nContent-Disposition: ". $subheader['Content-Disposition'] .";\n filename=\"". $part['name'] ."\"\n"; $body .= $sub_header ."\r\n"; $body .= chunk_split(base64_encode($file_content)) ."\r\n"; } } $body .= "--$boundary--\r\n"; return array('headers' => $headers, 'body' => $body); } /** * Helper function to format urls * */ function _mail_uri($href, $link) { $href = _mail_url($href); if ($href == $link) { $output = '['.$href.']'; } else { $output = $link.' ['.$href.']'; } return $output; } /** * Helper function to format urls * * @param $url an url * * @return an absolute url, sans mailto: */ function _mail_url($url) { if (preg_match('@://@', $url)) { return $url; } elseif (preg_match('!mailto:!i', $url)) { return str_replace('mailto:', '', $url); } else { return url($url, NULL, NULL, TRUE); } } /** * Helper function to store processed urls. * * @param $url Optional. * @param $refresh Optional. If TRUE refresh the cache. * * @return a count of stored if $url evaluates to false, the stored urls ortherwise. */ function _mail_urls($url = 0, $refresh = FALSE) { static $urls = array(); if($refresh) { $urls = array(); } if ($url) { $urls[] = _mail_url($url); return count($urls); } return $urls; } /** * List of preg* regular expression patterns to search for, * used in conjunction with $replace. * Based on / modified from html2txt.module */ function _mail_define_search() { $search = array( "/\r/", // Non-legal carriage return "/[\t]+/", // tabs '/]*>.*?<\/script>/i', //