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 = '|()|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
'/