You are here

mimemail.module in Mime Mail 6

Same filename and directory in other branches
  1. 8 mimemail.module
  2. 5 mimemail.module
  3. 7 mimemail.module

Component module for sending MIME-encoded e-mails.

File

mimemail.module
View source
<?php

/**
 * @file
 * Component module for sending MIME-encoded e-mails.
 */

/**
 * Implements hook_menu().
 */
function mimemail_menu() {
  $path = drupal_get_path('module', 'mimemail') . '/includes';
  $items['admin/settings/mimemail'] = array(
    'title' => 'Mime Mail',
    'description' => 'HTML E-mail settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'mimemail_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'mimemail.admin.inc',
    'file path' => $path,
  );
  $items['mimemail'] = array(
    'page callback' => 'mimemail_post',
    'access callback' => 'mimemail_incoming_access',
    'type' => MENU_CALLBACK,
    'file' => 'mimemail.incoming.inc',
    'file path' => $path,
  );
  return $items;
}

/**
 * Access callback to process incoming messages.
 */
function mimemail_incoming_access() {
  return variable_get('mimemail_incoming', FALSE);
}

/**
 * Implements hook_perm().
 */
function mimemail_perm() {
  return array(
    'send arbitrary files - Warning: has security implications!',
    'edit mimemail user settings',
  );
}

/**
 * Implements hook_user().
 */
function mimemail_user($op, &$edit, &$account, $category = '') {
  if ($op == 'form' && $category == 'account') {
    $form = array();
    $form['mimemail'] = array(
      '#type' => 'fieldset',
      '#title' => t('Email settings'),
      '#weight' => 5,
      '#collapsible' => TRUE,
      '#access' => user_access('edit mimemail user settings'),
    );
    $form['mimemail']['mimemail_textonly'] = array(
      '#type' => 'checkbox',
      '#title' => t('Plaintext email only'),
      '#default_value' => isset($account->mimemail_textonly) ? $account->mimemail_textonly : 0,
      '#description' => t('Check this option if you do not wish to receive email messages with graphics and styles'),
    );
    return $form;
  }
  return;
}

/**
 * Implements hook_theme().
 */
function mimemail_theme() {
  module_load_include('theme.inc', 'mimemail', 'theme/mimemail');
  return mimemail_theme_theme();
}

/**
 * Implements hook_mail().
 *
 * Set's the message subject and body as configured in the $settings of the action.
 */
function mimemail_mail($key, &$message, $params) {
  $account = $params['account'];
  $variables = array(
    '%site_name' => variable_get('site_name', 'Drupal'),
    '%username' => $account->name,
  );

  // Replace variables.
  if (isset($params['node']) && is_object($params['node'])) {
    $node = $params['node'];
    $variables += array(
      '%author' => $account->mail,
      '%uid' => $node->uid,
      '%node_url' => url('node/' . $node->nid, array(
        'absolute' => TRUE,
      )),
      '%node_type' => $node->type,
      '%title' => $node->title,
      '%teaser' => $node->teaser,
      '%body' => $node->body,
    );
  }

  // Prepare the array of the attachments.
  $attachments = array();
  $attachments_string = trim($params['attachments']);
  if (!empty($attachments_string)) {
    $attachment_lines = array_filter(explode("\n", trim($attachments_string)));
    foreach ($attachment_lines as $key => $attachment_line) {
      $attachment = explode(':', trim($attachment_line), 2);
      $attachments[] = array(
        'filepath' => $attachment[1],
        'filemime' => $attachment[0],
      );
    }
  }

  // We handle different address headers if set.
  $address_headers = array(
    'cc' => 'Cc',
    'bcc' => 'Bcc',
    'reply_to' => 'Reply-to',
  );
  foreach ($address_headers as $param_key => $address_header) {
    $params[$param_key] = empty($params[$param_key]) ? array() : explode(',', $params[$param_key]);
    if (!empty($params[$param_key])) {
      foreach ($params[$param_key] as $key => $address) {
        $params[$param_key][$key] = strtr(str_replace(array(
          "\r",
          "\n",
        ), '', trim($address)), $variables);
      }
      $message['headers'][$address_header] = implode(',', $params[$param_key]);
    }
  }
  $message['to'] = strtr($message['to'], $variables);
  $message['subject'] = strtr($params['subject'], $variables);
  $message['body'][] = check_markup(strtr($params['message_html'], $variables), $params['message_html_filter'], FALSE);
  $message['params']['plaintext'] = strtr($params['message_plaintext'], $variables);
  $message['params']['attachments'] = $attachments;
}

/**
 * Default engine's prepare function.
 *
 * @param
 *   Same parameters as mimemail() except $send.
 *
 * @return
 *   The $message array structure containing all details of the message.
 */
function mimemail_prepare_message($sender, $recipient, $subject, $body, $plaintext = NULL, $headers = array(), $text = NULL, $attachments = array(), $mailkey = '') {
  module_load_include('inc', 'mimemail');
  $site_name = variable_get('site_name', 'Drupal');
  $site_mail = variable_get('site_mail', ini_get('sendmail_from'));

  // Override site mails default sender when using default engine.
  if (variable_get('mimemail_engine', 'mimemail') == 'mimemail') {
    if (empty($sender) || $sender == $site_mail) {
      $mimemail_name = variable_get('mimemail_name', $site_name);
      $mimemail_mail = variable_get('mimemail_mail', $site_mail);
      $sender = array(
        'name' => !empty($mimemail_name) ? $mimemail_name : $site_name,
        'mail' => !empty($mimemail_mail) ? $mimemail_mail : $site_mail,
      );
    }
    elseif (is_array($sender)) {
      if (empty($sender['name'])) {
        $sender['name'] = variable_get('mimemail_name', $site_name);
      }
      if (empty($sender['mail'])) {
        $sender['mail'] = variable_get('mimemail_mail', $site_mail);
      }
    }
  }

  // Body is empty, this is a plaintext message.
  if (empty($body)) {
    $plaintext = 1;
  }
  elseif (is_null($plaintext)) {
    if (is_object($recipient)) {
      if (isset($recipient->mimemail_textonly)) {
        $plaintext = $recipient->mimemail_textonly;
      }
    }
    elseif (is_string($recipient) && valid_email_address($recipient)) {
      if (is_object($r = user_load(array(
        'mail' => $recipient,
      ))) && isset($r->mimemail_textonly)) {
        $plaintext = $r->mimemail_textonly;
        $recipient = $r;

        // Pass the user object to the address function.
      }
    }
  }
  $subject = str_replace(array(
    " \n",
    "\n",
  ), '', trim(drupal_html_to_text($subject)));
  $body = theme(array(
    'mimemail_message__' . $mailkey,
    'mimemail_message',
  ), $subject, $body, $mailkey, $recipient);
  foreach (module_implements('mail_post_process') as $module) {
    $function = $module . '_mail_post_process';
    $function($body, $mailkey);
  }
  $plaintext = $plaintext || variable_get('mimemail_textonly', 0);
  $sender = mimemail_address($sender);
  $subject = mime_header_encode($subject);
  $mail = mimemail_html_body($body, $subject, $plaintext, $text, $attachments);
  $headers = array_merge($headers, $mail['headers']);
  $message = array(
    'address' => mimemail_address($recipient),
    'subject' => $subject,
    'body' => $mail['body'],
    'sender' => $sender,
    'headers' => mimemail_headers($headers, $sender),
  );
  return $message;
}

/**
 * Send a MIME-encoded e-mail message.
 *
 * @param $message
 *   The message array.
 *
 * @return
 *   TRUE if the mail was successfully accepted for delivery, FALSE otherwise.
 */
function mimemail_send_message($message) {
  $engine = variable_get('mimemail_engine', 'mimemail');
  $mailengine = $engine . '_mailengine';
  if (!$engine || !function_exists($mailengine)) {
    return FALSE;
  }
  return $mailengine('send', $message);
}

/**
 * Compose and optionally send a MIME-encoded e-mail message.
 *
 * Determines the mail engine to use, then prepares the message by calling
 * it's prepare function. Uses the default engine, If another one does not exist.
 *
 * @param $sender
 *   The email address or user object who is sending the message.
 * @param $recipient
 *   An email address or user object who is receiving the message.
 * @param $subject
 *   A subject line string.
 * @param $body
 *   The message body in HTML format.
 * @param $plaintext
 *   Whether to send the message as plaintext only or HTML. If set to 1, Yes
 *   or TRUE, then the message will be sent as plaintext.
 * @param $headers
 *   Optional e-mail headers in a keyed array.
 * @param $text
 *   Optional plaintext portion of a multipart e-mail.
 * @param $attachments
 *   An array of arrays which describe one or more attachments.
 *   Existing files can be added by path, dinamically-generated files
 *   can be added by content. The internal array consists of the following parts:
 *   - filepath: Relative Drupal path to an existing file (filecontent is NULL).
 *   - filecontent: The actual content of the file (filepath is NULL).
 *   - filename: The filename of the file.
 *   - filemime: The MIME type of the file.
 *   The array of arrays looks something like this:
 *   Array
 *   (
 *     [0] => Array
 *       (
 *         [filepath] => '/sites/default/files/attachment.txt'
 *         [filecontent] => 'My attachment.'
 *         [filename] => 'attachment.txt'
 *         [filemime] => 'text/plain'
 *       )
 *   )
 * @param $mailkey
 *   An identifier for the message.
 * @param $send
 *   Send the message directly.
 *
 * @return
 *   An array containing the MIME-encoded message, including headers and body.
 */
function mimemail($sender, $recipient, $subject, $body, $plaintext = NULL, $headers = array(), $text = NULL, $attachments = array(), $mailkey = '', $send = TRUE) {
  $engine = variable_get('mimemail_engine', 'mimemail');
  $mailengine = $engine . '_mailengine';
  $engine_prepare_message = $engine . '_prepare_message';
  if (!$engine || !function_exists($mailengine)) {
    return FALSE;
  }
  if (function_exists($engine_prepare_message)) {
    $message = $engine_prepare_message($sender, $recipient, $subject, $body, $plaintext, $headers, $text, $attachments, $mailkey);
  }
  else {
    $message = mimemail_prepare_message($sender, $recipient, $subject, $body, $plaintext, $headers, $text, $attachments, $mailkey);
  }

  // Optionally send e-mail.
  if ($send) {
    $message['result'] = mimemail_send_message($message);
  }
  return $message;
}

/**
 * Retreives a list of all available mailer engines.
 *
 * @return
 *   An array of mailer engine names.
 */
function mimemail_get_engines() {
  $engines = array();
  foreach (module_implements('mailengine') as $module) {
    $function = $module . '_mailengine';
    if (function_exists($function)) {
      $engines[$module] = $function('name') . ' - ' . $function('description');
    }
  }
  return $engines;
}

/**
 * The default mailengine.
 *
 * @param $op
 *   The operation to perform on the message.
 * @param $message
 *   The message to be sent.
 *
 * @return
 *   Returns TRUE if the operation was successful or FALSE if it was not.
 */
function mimemail_mailengine($op, $message = array()) {
  module_load_include('inc', 'mimemail');

  // Default values.
  $message = array_merge(array(
    'address' => '',
    'subject' => '',
    'body' => '',
    'sender' => '',
    'headers' => '',
  ), $message);
  switch ($op) {
    case 'name':
      return t('Mime Mail');
    case 'description':
      return t("Default mailing engine using drupal_mail().");

    // Not implemented.
    case 'settings':
      return FALSE;
    case 'multiple':
    case 'single':
    case 'send':
      if (!is_array($message['address'])) {
        $message['address'] = array(
          $message['address'],
        );
      }

      // If 'Return-Path' isn't already set in php.ini, we pass it separately
      // as an additional parameter instead of in the header.
      // However, if PHP's 'safe_mode' is on, this is not allowed.
      if (isset($message['headers']['Return-Path']) && !ini_get('safe_mode')) {
        $return_path_set = strpos(ini_get('sendmail_path'), ' -f');
        if (!$return_path_set) {
          $return_path = trim($message['headers']['Return-Path'], '<,>');
          unset($message['headers']['Return-Path']);
        }
      }
      $status = TRUE;
      foreach ($message['address'] as $to) {
        $subject = $message['subject'];
        $body = $message['body'];
        $headers = mimemail_rfc_headers($message['headers']);

        // We validate the return path, unless it is equal to the site mail, which
        // we assume to be safe.
        if (isset($return_path) && !empty($return_path) && (variable_get('site_mail', ini_get('sendmail_from')) === $return_path || mimemail_isshellsafe($return_path))) {
          if (isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER_SOFTWARE'], 'Win32') !== FALSE) {

            // On Windows, PHP will use the value of sendmail_from for the
            // Return-Path header.
            $old_from = ini_get('sendmail_from');
            ini_set('sendmail_from', $return_path);
            $status = @mail($to, $subject, $body, $headers) && $status;
            ini_set('sendmail_from', $old_from);
          }
          else {

            // On most non-Windows systems, the "-f" option to the sendmail command
            // is used to set the Return-Path.
            $status = @mail($to, $subject, $body, $headers, '-f' . $return_path) && $status;
          }
        }
        else {

          // The optional $additional_parameters argument to mail() is not allowed
          // if safe_mode is enabled. Passing any value throws a PHP warning and
          // makes mail() return FALSE.
          $status = @mail($to, $subject, $body, $headers) && $status;
        }
      }
      return $status;
  }
  return FALSE;
}

/**
 * Overrides Drupal's default mail sending process.
 */
if (strpos(variable_get('smtp_library', ''), 'mimemail') !== FALSE && !function_exists('drupal_mail_wrapper')) {
  function drupal_mail_wrapper(&$message) {
    $from = $message['from'];
    $to = $message['to'];
    $subject = $message['subject'];
    if (preg_match('/plain/', $message['headers']['Content-Type'])) {
      $format = variable_get('mimemail_format', FILTER_FORMAT_DEFAULT);
      $body = check_markup($message['body'], $format, FALSE);
    }
    else {
      $body = $message['body'];
    }
    $plaintext = isset($message['plaintext']) ? $message['plaintext'] : NULL;
    $headers = isset($message['headers']) ? $message['headers'] : array();
    $text = isset($message['text']) ? $message['text'] : NULL;
    $attachments = isset($message['attachments']) ? $message['attachments'] : array();
    $mailkey = isset($message['id']) ? $message['id'] : '';
    $message = mimemail($from, $to, $subject, $body, $plaintext, $headers, $text, $attachments, $mailkey);
    return $message['result'];
  }
}

/**
 * Disallows potentially unsafe shell characters.
 *
 * @param string $string
 *   The string to be validated.
 *
 * @return bool
 *   True if the string is shell-safe.
 */
function mimemail_isshellsafe($string) {
  if (escapeshellcmd($string) !== $string || !in_array(escapeshellarg($string), array(
    "'{$string}'",
    "\"{$string}\"",
  ))) {
    return FALSE;
  }
  if (preg_match('/[^a-zA-Z0-9@_\\-.]/', $string) !== 0) {
    return FALSE;
  }
  return TRUE;
}

Functions

Namesort descending Description
mimemail Compose and optionally send a MIME-encoded e-mail message.
mimemail_get_engines Retreives a list of all available mailer engines.
mimemail_incoming_access Access callback to process incoming messages.
mimemail_isshellsafe Disallows potentially unsafe shell characters.
mimemail_mail Implements hook_mail().
mimemail_mailengine The default mailengine.
mimemail_menu Implements hook_menu().
mimemail_perm Implements hook_perm().
mimemail_prepare_message Default engine's prepare function.
mimemail_send_message Send a MIME-encoded e-mail message.
mimemail_theme Implements hook_theme().
mimemail_user Implements hook_user().