phpmailer.module 8.17 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
smk-ka's avatar
smk-ka committed
5
 * Integrates the PHPMailer library for SMTP e-mail delivery.
6 7 8 9 10
 */

/**
 * Implementation of hook_perm().
 */
11 12 13 14 15 16 17
function phpmailer_permission() {
  return array(
    'administer phpmailer settings' => array(
      'title' => t('Administer PHPMailer settings'),
      'restrict access' => TRUE,
    ),
  );
18 19 20 21 22
}

/**
 * Implementation of hook_menu().
 */
smk-ka's avatar
smk-ka committed
23
function phpmailer_menu() {
24
  $items['admin/config/system/phpmailer'] = array(
25
    'title' => 'PHPMailer',
smk-ka's avatar
smk-ka committed
26 27
    'description' => 'Configure PHPMailer settings.',
    'page callback' => 'drupal_get_form',
28
    'page arguments' => array('phpmailer_settings_form'),
29
    'access arguments' => array('administer phpmailer settings'),
smk-ka's avatar
smk-ka committed
30 31
    'file' => 'phpmailer.admin.inc',
  );
32
  // @todo Move to Mime Mail project.
33 34 35 36
  $items['phpmailer/preview'] = array(
    'title' => 'Mail preview',
    'page callback' => 'phpmailer_preview',
    'access callback' => 'phpmailer_preview_access',
37
    'file' => 'phpmailer.admin.inc',
38 39
    'type' => MENU_CALLBACK,
  );
40 41 42
  return $items;
}

43
/**
44
 * Implementation of hook_form_FORM_ID_alter().
45
 */
46
function phpmailer_form_mimemail_admin_settings_alter(&$form, &$form_state) {
smk-ka's avatar
smk-ka committed
47 48
  // Hide the Mime Mail global enabler setting if phpmailer is used to deliver
  // e-mails (they can't be both active).
49
  if (phpmailer_enabled()) {
50 51 52
    $mimemail_alter = &$form['mimemail']['mimemail_alter'];
    $mimemail_alter['#disabled'] = TRUE;
    $mimemail_alter['#default_value'] = 0;
53
    $mimemail_alter['#description'] = t('PHPMailer has been set to deliver all site messages. To let Mime Mail apply styles and formatting to system e-mails but still use PHPMailer for mail transport, uncheck <em>Use PHPMailer to send e-mails</em> first on the <a href="@url">PHPMailer settings page</a>. Then activate this setting and choose PHPMailer from the list of e-mail engines below.', array('@url' => url('admin/config/system/phpmailer')));
54
  }
55 56 57 58 59 60 61
  // @todo Move to MimeMail project.
  $form['preview'] = array(
    '#type' => 'item',
    '#title' => t('Preview'),
    '#value' => t('See a <a href="@url" target="_blank">preview of a styled e-mail</a> using the current message template (<code>mimemail-message.tpl.php</code>).', array('@url' => url('phpmailer/preview'))),
  );
  $form['buttons']['#weight'] = 10;
62 63
}

64 65 66 67
/**
 * Determine if PHPMailer is used to deliver e-mails.
 */
function phpmailer_enabled() {
68 69 70
  // We need to rely on our 'smtp_on' variable, since PHPMailer may not be
  // configured as the default mail system.
  return (bool) variable_get('smtp_on', 0);
71 72 73 74 75 76
}

/**
 * Implementation of hook_mailengine().
 */
function phpmailer_mailengine($op, $message = array()) {
77 78 79 80
  if (!phpmailer_library_exists()) {
    return;
  }

81
  switch ($op) {
82 83 84 85 86
    case 'list':
      return array(
        'name' => t('PHPMailer'),
        'description' => t('Mailing engine using the PHPMailer library.'),
      );
87 88

    case 'settings':
89
      $form['info']['#markup'] = t('To configure your mail server settings, visit the <a href="@url">PHPMailer settings page</a>.', array('@url' => url('admin/config/system/phpmailer')));
90
      return $form;
91 92 93 94

    case 'multiple':
    case 'single':
    case 'send':
smk-ka's avatar
smk-ka committed
95
      module_load_include('inc', 'phpmailer', 'includes/phpmailer.mimemail');
96 97 98
      // Mimemail API does not load mimemail.inc for other mailengines; we rely
      // on mimemail_rfc_headers(), so ensure that it is loaded.
      module_load_include('inc', 'mimemail');
smk-ka's avatar
smk-ka committed
99
      return mimemail_phpmailer_send($message);
100 101 102
  }
}

smk-ka's avatar
smk-ka committed
103 104 105
/**
 * Extract address and optional display name of an e-mail address.
 *
106
 * @param $string
smk-ka's avatar
smk-ka committed
107 108
 *   A string containing one or more valid e-mail address(es) separated with
 *   commas.
109
 *
smk-ka's avatar
smk-ka committed
110 111 112 113 114
 * @return
 *   An array containing all found e-mail addresses split into mail and name.
 *
 * @see http://tools.ietf.org/html/rfc5322#section-3.4
 */
115
function phpmailer_parse_address($string) {
smk-ka's avatar
smk-ka committed
116 117
  $parsed = array();

118 119 120
  // The display name may contain commas (3.4). Extract all quoted strings
  // (3.2.4) to a stack and replace them with a placeholder to prevent
  // splitting at wrong places.
121
  $string = preg_replace_callback('(".*?(?<!\\\\)")', '_phpmailer_stack', $string);
122 123 124 125 126 127 128 129

  // Build a regex that matches a name-addr (3.4).
  // @see valid_email_address()
  $user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+';
  $domain = '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+';
  $ipv4 = '[0-9]{1,3}(?:\.[0-9]{1,3}){3}';
  $ipv6 = '[0-9a-fA-F]{1,4}(?:\:[0-9a-fA-F]{1,4}){7}';
  $address = "$user@(?:$domain|(?:\[(?:$ipv4|$ipv6)\]))";
130
  $adr_rx = "/^(?P<name>.*)\s<(?P<address>$address)>$/";
131 132 133 134

  // Split string into multiple parts and process each.
  foreach (explode(',', $string) as $email) {
    // Re-inject stripped placeholders.
135
    $email = preg_replace_callback('(\x01)', '_phpmailer_stack', trim($email));
136 137
    // Check if it's a name-addr or a plain address (3.4).
    if (preg_match($adr_rx, $email, $matches)) {
138 139
      // PHPMailer expects an unencoded display name.
      $parsed[] = array('mail' => $matches['address'], 'name' => mime_header_decode(stripslashes($matches['name'])));
smk-ka's avatar
smk-ka committed
140 141
    }
    else {
142
      $parsed[] = array('mail' => trim($email, '<>'), 'name' => '');
smk-ka's avatar
smk-ka committed
143
    }
144
  }
smk-ka's avatar
smk-ka committed
145
  return $parsed;
146 147
}

148 149 150
/**
 * Implements a FIFO stack to store extracted quoted strings.
 */
151 152
function _phpmailer_stack($matches = NULL) {
  $string = $matches[0];
153 154
  static $stack = array();

155
  if ($string == "\x01") {
156 157 158 159 160 161 162 163 164 165
    // Unescape quoted characters (3.2.4) to prevent double escaping.
    return str_replace(array('\"', '\\\\'), array('"', '\\'), array_shift($stack));
  }
  // Remove surrounding quotes and push on stack.
  array_push($stack, substr($string, 1, -1));
  // Return placeholder substitution. 0x01 may never appear outside a quoted
  // string (3.2.3).
  return "\x01";
}

166 167 168 169
/**
 * Menu access callback; Determine access for HTML mail preview page.
 */
function phpmailer_preview_access() {
170
  if (module_exists('mimemail')) {
171 172 173 174 175
    return user_access('administer phpmailer settings');
  }
  return FALSE;
}

176 177 178 179 180 181
/**
 * Implementation of hook_enable().
 */
function phpmailer_enable() {
  if (!phpmailer_enabled() && !(module_exists('mimemail') && variable_get('mimemail_engine', 'mimemail') == 'phpmailer')) {
    $t = get_t();
182
    drupal_set_message($t('PHPMailer has been installed, but is currently disabled. <a href="@settings-url">Configure it now</a>.', array('@settings-url' => url('admin/config/system/phpmailer'))));
183
  }
184 185 186 187

  if (module_exists('mailsystem')) {
    mailsystem_set(array('phpmailer' => 'DrupalPHPMailer'));
  }
188 189
}

190 191 192 193 194
/**
 * Implementation of hook_disable().
 */
function phpmailer_disable() {
  if (phpmailer_enabled()) {
195 196 197 198 199 200 201 202 203 204 205 206 207 208
    // Remove PHPMailer from all mail keys it is configured for.
    $mail_system = variable_get('mail_system', array('default-system' => 'DefaultMailSystem'));
    foreach ($mail_system as $key => $class) {
      if ($class == 'DrupalPHPMailer') {
        if ($key != 'default-system') {
          unset($mail_system[$key]);
        }
        else {
          $mail_system[$key] = 'DefaultMailSystem';
        }
      }
    }
    variable_set('mail_system', $mail_system);

209
    variable_del('smtp_on');
210 211 212 213
    drupal_set_message(t('PHPMailer has been disabled.'));
  }
  if (module_exists('mimemail') && variable_get('mimemail_engine', 'mimemail') == 'phpmailer') {
    variable_del('mimemail_engine');
214
    drupal_set_message(t('MimeMail e-mail engine has been reset to default.'), 'warning');
215
  }
216 217 218 219

  if (module_exists('mailsystem')) {
    mailsystem_clear(array('phpmailer' => 'DrupalPHPMailer'));
  }
220 221
}

222
/**
223 224 225
 * Implements hook_registry_files_alter().
 *
 * @todo Consider to move this into Libraries API.
226
 */
227 228 229 230
function phpmailer_registry_files_alter(&$files, $modules) {
  $library_path = libraries_get_path('phpmailer');
  if (!$library_path) {
    return;
231
  }
232
  foreach (array('class.phpmailer.php', 'class.smtp.php') as $filename) {
233 234 235 236
    $files[$library_path . '/' . $filename] = array(
      'module' => 'phpmailer',
      'weight' => 0,
    );
237 238
  }
}
239 240

/**
241
 * Verify that PHPMailer libraries exist.
242
 */
243
function phpmailer_library_exists() {
244
  $library_path = libraries_get_path('phpmailer');
245 246 247 248 249 250
  if (!$library_path) {
    return FALSE;
  }
  foreach (array('class.phpmailer.php', 'class.smtp.php') as $filename) {
    if (!file_exists(DRUPAL_ROOT . '/' . $library_path . '/' . $filename)) {
      return FALSE;
251 252
    }
  }
253
  return TRUE;
254
}