You are here

mimemail.module in Mime Mail 5

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

Component module for sending Mime-encoded emails.

File

mimemail.module
View source
<?php

/* $Id$ */

/**
 * @file 
 * Component module for sending Mime-encoded emails.
 */

/**
 * Implementation of hook_menu()
 */
function mimemail_menu() {
  $items[] = array(
    'path' => 'admin/settings/mimemail',
    'title' => t('Mail'),
    'description' => t('HTML E-mail settings'),
    'callback' => 'drupal_get_form',
    'callback arguments' => 'mimemail_settings',
    'access' => user_access('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
  );
  $items[] = array(
    'path' => 'mimemail',
    'callback' => 'mimemail_post',
    'access' => variable_get('mimemail_incoming', FALSE),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Administration settings.
 *
 * @return
 *   The administration from.
 */
function mimemail_settings() {

  // override the smtp_library value if mimemail is chosen to handle all mail
  // this will cause drupal_mail to call mimemail()
  if (variable_get('mimemail_alter', 0)) {
    if (!strpos(variable_get('smtp_library', ''), 'mimemail')) {
      variable_set('smtp_library', drupal_get_filename('module', 'mimemail'));
    }
  }
  else {
    if (strpos(variable_get('smtp_library', ''), 'mimemail')) {
      db_query("DELETE FROM {variable} WHERE name = 'smtp_library'");
    }
  }
  $engines = mimemail_get_engines();
  $form = array();
  $form['site_mail'] = array(
    '#type' => 'textfield',
    '#title' => t('E-mail address'),
    '#default_value' => variable_get('site_mail', ini_get('sendmail_from')),
    '#size' => 60,
    '#maxlength' => 128,
    '#description' => t('A valid e-mail address for this website, used by the auto-mailer during registration, new password requests, notifications, etc.'),
  );
  $form['mimemail']['mimemail_alter'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use mime mail for all messages'),
    '#default_value' => variable_get('mimemail_alter', 0),
    '#description' => t('Use the mime mail module to deliver all site messages.  With this option, system emails will have styles and formatting'),
  );
  $filter_format = variable_get('mimemail_format', FILTER_FORMAT_DEFAULT);
  $form['mimemail']['mimemail_format'] = filter_form($filter_format, NULL, array(
    "mimemail_format",
  ));
  $form['mimemail']['mimemail_textonly'] = array(
    '#type' => 'checkbox',
    '#title' => t('Plaintext email only'),
    '#default_value' => variable_get('mimemail_textonly', 0),
    '#description' => t('This option disables the use of email messages with graphics and styles.  All messages will be converted to plain text.'),
  );
  $form['mimemail']['incoming'] = array(
    '#type' => 'fieldset',
    '#title' => t('Advanced Settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['mimemail']['incoming']['mimemail_incoming'] = array(
    '#type' => 'checkbox',
    '#title' => t('Process incoming messages posted to this site'),
    '#default_value' => variable_get('mimemail_incoming', 0),
    '#description' => t('This is an advanced setting that should not be enabled unless you know what you are doing'),
  );
  $form['mimemail']['incoming']['mimemail_key'] = array(
    '#type' => 'textfield',
    '#title' => t('Message validation string'),
    '#default_value' => variable_get('mimemail_key', md5(rand())),
    '#required' => TRUE,
    '#description' => t('This string will be used to validate incoming messages.  It can be anything, but must be used on both sides of the transfer'),
  );

  // hide the settings if only 1 engine is available
  if (count($engines) == 1) {
    variable_set('mimemail_engine', key($engines));
    $form['mimemail_engine'] = array(
      '#type' => 'hidden',
      '#title' => t('E-mail engine'),
      '#default_value' => variable_get('mimemail_engine', 'mail'),
      '#options' => $engines,
      '#description' => t('Choose an e-mail engine for sending mails from your site.'),
    );
  }
  else {
    $form['mimemail_engine'] = array(
      '#type' => 'select',
      '#title' => t('E-mail engine'),
      '#default_value' => variable_get('mimemail_engine', 'mail'),
      '#options' => $engines,
      '#description' => t('Choose an e-mail engine for sending mails from your site.'),
    );
  }
  if (variable_get('mimemail_engine', 0)) {
    $settings = module_invoke(variable_get('mimemail_engine', 'mail'), 'mailengine', 'settings');
    if ($settings) {
      $form['mimemail_engine_settings'] = array(
        '#type' => 'fieldset',
        '#title' => t('Engine specific settings'),
      );
      foreach ($settings as $name => $value) {
        $form['mimemail_engine_settings'][$name] = $value;
      }
    }
  }
  else {
    drupal_set_message(t('Please choose a mail engine.'), 'error');
  }
  return system_settings_form($form);
}

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

/**
 * Sends a mime-encoded e-mail.
 *
 * This function first determines the mail engine to use, then prepares the
 * message by calling the mail engine's prepare function, or
 * mimemail_prepare() if another one does not exist, then sends the message.
 *
 * @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. The internal
 *   array consists of two parts: the file's path and the file's MIME type.
 *   The array of arrays looks something like this:
 *   Array
 *   (
 *     [0] => Array
 *       (
 *         [filepath] => '/path/to/file.name'
 *         [filemime] => 'mime/type'
 *       )
 *   )
 * @param $mailkey
 *   An identifier for the message.
 * @return
 *   An array containing the MIME encoded message, including headers and body.
 */
function mimemail_prepare($sender, $recipient, $subject, $body, $plaintext = NULL, $headers = array(), $text = NULL, $attachments = array(), $mailkey = '') {
  require_once dirname(__FILE__) . '/mimemail.inc';
  if (is_null($sender)) {

    // use site default for sender
    $sender = array(
      'name' => variable_get('site_name', 'Drupal'),
      'mail' => variable_get('site_mail', ini_get('sendmail_from')),
    );
  }

  // try to determine recpient's text mail preference
  if (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;

        // might as well pass the user object to the address function
      }
    }
  }
  $subject = mime_header_encode($subject);
  $plaintext = $plaintext || variable_get('mimemail_textonly', 0);
  $sender = mimemail_address($sender);
  $mail = mimemail_html_body(theme('mimemail_message', $body, $mailkey), $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;
}
function mimemail($sender, $recipient, $subject, $body, $plaintext = NULL, $headers = array(), $text = NULL, $attachments = array(), $mailkey = '') {
  $engine = variable_get('mimemail_engine', 'mimemail') . '_mailengine';
  if (!function_exists($engine)) {
    return FALSE;
  }

  // Allow modules implementing hook_mail_alter() to function when all
  // mail is routed through mimemail.
  //  - doesn't support passing all the variables used here (e.g. attachements)
  //  - should also provide a hook_mimemail_alter for full mimemail support
  foreach (module_implements('mail_alter') as $module) {
    $function = $module . '_mail_alter';
    $function($mailkey, $recipient, $subject, $body, $sender, $headers);
  }
  $engine_prepare = variable_get('mimemail_engine', 'mimemail') . '_prepare';
  if (function_exists($engine_prepare)) {
    $message = $engine_prepare($sender, $recipient, $subject, $body, $plaintext, $headers, $text, $attachments, $mailkey);
  }
  else {
    $message = mimemail_prepare($sender, $recipient, $subject, $body, $plaintext, $headers, $text, $attachments, $mailkey);
  }
  return $engine('send', $message);
  return FALSE;
}

/**
 * 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()) {

  //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().");
    case 'settings':

      //not implemented
      return FALSE;
    case 'multiple':
    case 'single':
    case 'send':
      if (!is_array($message['address'])) {
        $message['address'] = array(
          $message['address'],
        );
      }
      $status = TRUE;
      foreach ($message['address'] as $a) {
        $status = mail($a, $message['subject'], $message['body'], mimemail_rfc_headers($message['headers'])) && $status;
      }
      return $status;
  }
  return FALSE;
}

/**
 * Overrides Drupal's default mail sending process.
 *
 * @param $mailkey
 *   An identifier for the message.
 * @param $to
 *   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 $from
 *   The email address or user object who is sending the message.
 * @param $headers
 *   Optional e-mail headers in a keyed array.
 * @return
 *   Returns the resultss of the call to mimemail().
 */
if (strpos(variable_get('smtp_library', ''), 'mimemail') && !function_exists('drupal_mail_wrapper')) {
  function drupal_mail_wrapper($mailkey, $to, $subject, $body, $from, $headers) {
    if ($format = variable_get('mimemail_format', FILTER_FORMAT_DEFAULT)) {
      $body = check_markup($body, $format, FALSE);
    }
    return mimemail($from, $to, $subject, $body, NULL, $headers, NULL, array(), $mailkey);
  }
}

/**
 * Receive messages POSTed from an external source.
 *
 * This function enables messages to be sent via POST or some other RFC822
 * source input (e.g. directly from a mail server).
 *
 * @return
 *   The POSTed message.
 */
function mimemail_post() {
  $message = $_POST['message'];
  $token = $_POST['token'];
  $hash = md5(variable_get('mimemail_key', '**') . $message);
  if ($hash != $token) {
    watchdog('access denied', t('Authentication error for POST e-mail'), WATCHDOG_WARNING);
    return drupal_access_denied();
  }
  return mimemail_incoming($message);
}

/**
 * Parses an externally received message.
 *
 * @param $message
 *   The message to parse.
 */
function mimemail_incoming($message) {
  require_once dirname(__FILE__) . '/mimemail.inc';
  $mail = mimemail_parse($message);
  foreach (module_implements('mimemail_incoming_alter') as $module) {
    call_user_func_array($module . '_mimemail_incoming_alter', $mail);
  }
  module_invoke_all('mimemail_incoming', $mail);
}

/**
 * Formats an address string.
 * TODO could use some enhancement and stress testing.
 *
 * @param $address
 *   A user object, a text email address or an array containing name, mail.
 * @return 
 *   A formatted address string or FALSE.
 */
function mimemail_address($address) {
  if (is_array($address)) {

    // it's an array containing 'mail' and/or 'name'
    if (isset($address['mail'])) {
      $output = '';
      if (empty($address['name'])) {
        return $address['mail'];
      }
      else {
        return '"' . addslashes(mime_header_encode($address['name'])) . '" <' . $address['mail'] . '>';
      }
    }

    // it's an array of address items
    $addresses = array();
    foreach ($address as $a) {
      $addresses[] = mimemail_address($a);
    }
    return $addresses;
  }

  // it's a user object
  if (is_object($address) && isset($address->mail)) {
    return '"' . addslashes(mime_header_encode($address->name)) . '" <' . $address->mail . '>';
  }

  // it's formatted or unformatted string
  // TODO shouldn't assume it's valid - should try to re-parse
  if (is_string($address)) {
    return $address;
  }

  // it's null.  return the site default address
  if (is_null($address)) {
    return array(
      'name' => mime_header_encode(variable_get('site_name', 'Drupal')),
      'mail' => variable_get('site_mail', ini_get('sendmail_from')),
    );
  }
  return FALSE;
}

/**
 * Themes the message body.
 *
 * @param $body
 *   The message body to theme.
 * @param $mailkey
 *   An identifier for the message.
 * @return
 *   The themed HTML message body.
 */
function theme_mimemail_message($body, $mailkey = NULL) {
  $output = '<html><head>';
  $output .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';

  // attempt to include a mail-specific version of the css.
  // if you want smaller mail messages, add a mail.css file to your theme
  $styles = path_to_theme() . '/mail.css';
  $output .= '<style type="text/css"><!--';
  if (!file_exists($styles)) {

    // embed a version of all style definitions
    $styles = preg_replace('|<style.*"' . base_path() . '([^"]*)".*|', '\\1', drupal_get_css());
  }
  foreach (explode("\n", $styles) as $style) {
    if (file_exists($style)) {
      $output .= file_get_contents($style);
    }
  }
  $output .= '--></style></head><body id="mimemail-body"><div id="center"><div id="main">' . $body . '</div></div></body></html>';

  // compress output
  return preg_replace('/\\s+|\\n|\\r|^\\s|\\s$/', ' ', $output);
}

Functions

Namesort descending Description
mimemail
mimemail_address Formats an address string. TODO could use some enhancement and stress testing.
mimemail_get_engines Retreives a list of all available mailer engines.
mimemail_incoming Parses an externally received message.
mimemail_mailengine The default mailengine.
mimemail_menu Implementation of hook_menu()
mimemail_post Receive messages POSTed from an external source.
mimemail_prepare Sends a mime-encoded e-mail.
mimemail_settings Administration settings.
mimemail_user Implementation of hook_user().
theme_mimemail_message Themes the message body.