Mercurial > hg > rr-repo
diff sites/all/modules/smtp/smtp.mail.inc @ 9:830c812b520f
added smtp module
author | root <root@paio.local> |
---|---|
date | Mon, 28 Oct 2013 15:34:27 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sites/all/modules/smtp/smtp.mail.inc Mon Oct 28 15:34:27 2013 +0000 @@ -0,0 +1,608 @@ +<?php +/** + * @file + * The code processing mail in the smtp module. + * + */ + +/** +* Modify the drupal mail system to use smtp when sending emails. +* Include the option to choose between plain text or HTML +*/ +class SmtpMailSystem implements MailSystemInterface { + + protected $AllowHtml; + /** + * Concatenate and wrap the e-mail body for either + * plain-text or HTML emails. + * + * @param $message + * A message array, as described in hook_mail_alter(). + * + * @return + * The formatted $message. + */ + public function format(array $message) { + $this->AllowHtml = variable_get('smtp_allowhtml', 0); + // Join the body array into one string. + $message['body'] = implode("\n\n", $message['body']); + if ($this->AllowHtml == 0) { + // Convert any HTML to plain-text. + $message['body'] = drupal_html_to_text($message['body']); + // Wrap the mail body for sending. + $message['body'] = drupal_wrap_mail($message['body']); + } + return $message; + } + + /** + * Send the e-mail message. + * + * @see drupal_mail() + * + * @param $message + * A message array, as described in hook_mail_alter(). + * @return + * TRUE if the mail was successfully accepted, otherwise FALSE. + */ + public function mail(array $message) { + $id = $message['id']; + $to = $message['to']; + $from = $message['from']; + $body = $message['body']; + $headers = $message['headers']; + $subject = $message['subject']; + + // Create a new PHPMailer object - autoloaded from registry. + $mailer = new PHPMailer(); + + // Turn on debugging, if requested. + if (variable_get('smtp_debugging', 0) == 1) { + $mailer->SMTPDebug = TRUE; + } + + // Set the from name. + if (variable_get('smtp_fromname', '') != '') { + $from_name = variable_get('smtp_fromname', ''); + } + else { + // If value is not defined in settings, use site_name. + $from_name = variable_get('site_name', ''); + } + + //Hack to fix reply-to issue. + $properfrom = variable_get('site_mail', ''); + if (!empty($properfrom)) { + $headers['From'] = $properfrom; + } + if (!isset($headers['Reply-To']) || empty($headers['Reply-To'])) { + if (strpos($from, '<')) { + $reply = preg_replace('/>.*/', '', preg_replace('/.*</', '', $from)); + } + else { + $reply = $from; + } + $headers['Reply-To'] = $reply; + } + + // Blank value will let the e-mail address appear. + + if ($from == NULL || $from == '') { + // If from e-mail address is blank, use smtp_from config option. + if (($from = variable_get('smtp_from', '')) == '') { + // If smtp_from config option is blank, use site_email. + if (($from = variable_get('site_mail', '')) == '') { + drupal_set_message(t('There is no submitted from address.'), 'error'); + watchdog('smtp', 'There is no submitted from address.', array(), WATCHDOG_ERROR); + return FALSE; + } + } + } + if (preg_match('/^"?.*"?\s*<.*>$/', $from)) { + // . == Matches any single character except line break characters \r and \n. + // * == Repeats the previous item zero or more times. + $from_name = preg_replace('/"?([^("\t\n)]*)"?.*$/', '$1', $from); // It gives: Name + $from = preg_replace("/(.*)\<(.*)\>/i", '$2', $from); // It gives: name@domain.tld + } + elseif (!valid_email_address($from)) { + drupal_set_message(t('The submitted from address (@from) is not valid.', array('@from' => $from)), 'error'); + watchdog('smtp', 'The submitted from address (@from) is not valid.', array('@from' => $from), WATCHDOG_ERROR); + return FALSE; + } + + // Defines the From value to what we expect. + $mailer->From = $from; + $mailer->FromName = $from_name; + $mailer->Sender = $from; + + + // Create the list of 'To:' recipients. + $torecipients = explode(',', $to); + foreach ($torecipients as $torecipient) { + if (strpos($torecipient, '<') !== FALSE) { + $toparts = explode(' <', $torecipient); + $toname = $toparts[0]; + $toaddr = rtrim($toparts[1], '>'); + } + else { + $toname = ''; + $toaddr = $torecipient; + } + $mailer->AddAddress($toaddr, $toname); + } + + + // Parse the headers of the message and set the PHPMailer object's settings + // accordingly. + foreach ($headers as $key => $value) { + //watchdog('error', 'Key: ' . $key . ' Value: ' . $value); + switch (drupal_strtolower($key)) { + case 'from': + if ($from == NULL or $from == '') { + // If a from value was already given, then set based on header. + // Should be the most common situation since drupal_mail moves the + // from to headers. + $from = $value; + $mailer->From = $value; + // then from can be out of sync with from_name ! + $mailer->FromName = ''; + $mailer->Sender = $value; + } + break; + case 'content-type': + // Parse several values on the Content-type header, storing them in an array like + // key=value -> $vars['key']='value' + $vars = explode(';', $value); + foreach ($vars as $i => $var) { + if ($cut = strpos($var, '=')) { + $new_var = trim(drupal_strtolower(drupal_substr($var, $cut + 1))); + $new_key = trim(drupal_substr($var, 0, $cut)); + unset($vars[$i]); + $vars[$new_key] = $new_var; + } + } + // Set the charset based on the provided value, otherwise set it to UTF-8 (which is Drupals internal default). + $mailer->CharSet = isset($vars['charset']) ? $vars['charset'] : 'UTF-8'; + // If $vars is empty then set an empty value at index 0 to avoid a PHP warning in the next statement + $vars[0] = isset($vars[0])?$vars[0]:''; + + switch ($vars[0]) { + case 'text/plain': + // The message includes only a plain text part. + $mailer->IsHTML(FALSE); + $content_type = 'text/plain'; + break; + case 'text/html': + // The message includes only an HTML part. + $mailer->IsHTML(TRUE); + $content_type = 'text/html'; + break; + case 'multipart/related': + // Get the boundary ID from the Content-Type header. + $boundary = $this->_get_substring($value, 'boundary', '"', '"'); + + // The message includes an HTML part w/inline attachments. + $mailer->ContentType = $content_type = 'multipart/related; boundary="' . $boundary . '"'; + break; + case 'multipart/alternative': + // The message includes both a plain text and an HTML part. + $mailer->ContentType = $content_type = 'multipart/alternative'; + + // Get the boundary ID from the Content-Type header. + $boundary = $this->_get_substring($value, 'boundary', '"', '"'); + break; + case 'multipart/mixed': + // The message includes one or more attachments. + $mailer->ContentType = $content_type = 'multipart/mixed'; + + // Get the boundary ID from the Content-Type header. + $boundary = $this->_get_substring($value, 'boundary', '"', '"'); + break; + default: + // Everything else is unsuppored by PHPMailer. + drupal_set_message(t('The %header of your message is not supported by PHPMailer and will be sent as text/plain instead.', array('%header' => "Content-Type: $value")), 'error'); + watchdog('smtp', 'The %header of your message is not supported by PHPMailer and will be sent as text/plain instead.', array('%header' => "Content-Type: $value"), WATCHDOG_ERROR); + + // Force the Content-Type to be text/plain. + $mailer->IsHTML(FALSE); + $content_type = 'text/plain'; + } + break; + + case 'reply-to': + // Only add a "reply-to" if it's not the same as "return-path". + if ($value != $headers['Return-Path']) { + if (strpos($value, '<') !== FALSE) { + $replyToParts = explode('<', $value); + $replyToName = trim($replyToParts[0]); + $replyToName = trim($replyToName, '"'); + $replyToAddr = rtrim($replyToParts[1], '>'); + $mailer->AddReplyTo($replyToAddr, $replyToName); + } + else { + $mailer->AddReplyTo($value); + } + } + break; + + case 'content-transfer-encoding': + $mailer->Encoding = $value; + break; + + case 'return-path': + case 'mime-version': + case 'x-mailer': + // Let PHPMailer specify these. + break; + + case 'errors-to': + $mailer->AddCustomHeader('Errors-To: ' . $value); + break; + + case 'cc': + $ccrecipients = explode(',', $value); + foreach ($ccrecipients as $ccrecipient) { + if (strpos($ccrecipient, '<') !== FALSE) { + $ccparts = explode(' <', $ccrecipient); + $ccname = $ccparts[0]; + $ccaddr = rtrim($ccparts[1], '>'); + } + else { + $ccname = ''; + $ccaddr = $ccrecipient; + } + $mailer->AddCC($ccaddr, $ccname); + } + break; + + case 'bcc': + $bccrecipients = explode(',', $value); + foreach ($bccrecipients as $bccrecipient) { + if (strpos($bccrecipient, '<') !== FALSE) { + $bccparts = explode(' <', $bccrecipient); + $bccname = $bccparts[0]; + $bccaddr = rtrim($bccparts[1], '>'); + } + else { + $bccname = ''; + $bccaddr = $bccrecipient; + } + $mailer->AddBCC($bccaddr, $bccname); + } + break; + + default: + // The header key is not special - add it as is. + $mailer->AddCustomHeader($key . ': ' . $value); + } + } + +/** + * TODO + * Need to figure out the following. + * + * Add one last header item, but not if it has already been added. + * $errors_to = FALSE; + * foreach ($mailer->CustomHeader as $custom_header) { + * if ($custom_header[0] = '') { + * $errors_to = TRUE; + * } + * } + * if ($errors_to) { + * $mailer->AddCustomHeader('Errors-To: '. $from); + * } + */ + // Add the message's subject. + $mailer->Subject = $subject; + + // Processes the message's body. + switch ($content_type) { + case 'multipart/related': + $mailer->Body = $body; + + /** + * TODO + * Firgure out if there is anything more to handling this type. + */ + + break; + + case 'multipart/alternative': + // Split the body based on the boundary ID. + $body_parts = $this->_boundary_split($body, $boundary); + foreach ($body_parts as $body_part) { + // If plain/text within the body part, add it to $mailer->AltBody. + if (strpos($body_part, 'text/plain')) { + // Clean up the text. + $body_part = trim($this->_remove_headers(trim($body_part))); + // Include it as part of the mail object. + $mailer->AltBody = $body_part; + } + // If plain/html within the body part, add it to $mailer->Body. + elseif (strpos($body_part, 'text/html')) { + // Clean up the text. + $body_part = trim($this->_remove_headers(trim($body_part))); + // Include it as part of the mail object. + $mailer->Body = $body_part; + } + } + break; + + case 'multipart/mixed': + // Split the body based on the boundary ID. + $body_parts = $this->_boundary_split($body, $boundary); + + // Determine if there is an HTML part for when adding the plain text part. + $text_plain = FALSE; + $text_html = FALSE; + foreach ($body_parts as $body_part) { + if (strpos($body_part, 'text/plain')) { + $text_plain = TRUE; + } + if (strpos($body_part, 'text/html')) { + $text_html = TRUE; + } + } + + foreach ($body_parts as $body_part) { + // If test/plain within the body part, add it to either + // $mailer->AltBody or $mailer->Body, depending on whether there is + // also a text/html part ot not. + if (strpos($body_part, 'multipart/alternative')) { + // Get boundary ID from the Content-Type header. + $boundary2 = $this->_get_substring($body_part, 'boundary', '"', '"'); + // Clean up the text. + $body_part = trim($this->_remove_headers(trim($body_part))); + // Split the body based on the boundary ID. + $body_parts2 = $this->_boundary_split($body_part, $boundary2); + + foreach ($body_parts2 as $body_part2) { + // If plain/text within the body part, add it to $mailer->AltBody. + if (strpos($body_part2, 'text/plain')) { + // Clean up the text. + $body_part2 = trim($this->_remove_headers(trim($body_part2))); + // Include it as part of the mail object. + $mailer->AltBody = $body_part2; + $mailer->ContentType = 'multipart/mixed'; + } + // If plain/html within the body part, add it to $mailer->Body. + elseif (strpos($body_part2, 'text/html')) { + // Clean up the text. + $body_part2 = trim($this->_remove_headers(trim($body_part2))); + // Include it as part of the mail object. + $mailer->Body = $body_part2; + $mailer->ContentType = 'multipart/mixed'; + } + } + } + // If text/plain within the body part, add it to $mailer->Body. + elseif (strpos($body_part, 'text/plain')) { + // Clean up the text. + $body_part = trim($this->_remove_headers(trim($body_part))); + + if ($text_html) { + $mailer->AltBody = $body_part; + $mailer->IsHTML(TRUE); + $mailer->ContentType = 'multipart/mixed'; + } + else { + $mailer->Body = $body_part; + $mailer->IsHTML(FALSE); + $mailer->ContentType = 'multipart/mixed'; + } + } + // If text/html within the body part, add it to $mailer->Body. + elseif (strpos($body_part, 'text/html')) { + // Clean up the text. + $body_part = trim($this->_remove_headers(trim($body_part))); + // Include it as part of the mail object. + $mailer->Body = $body_part; + $mailer->IsHTML(TRUE); + $mailer->ContentType = 'multipart/mixed'; + } + // Add the attachment. + elseif (strpos($body_part, 'Content-Disposition: attachment;')) { + $file_path = $this->_get_substring($body_part, 'filename=', '"', '"'); + $file_name = $this->_get_substring($body_part, ' name=', '"', '"'); + $file_encoding = $this->_get_substring($body_part, 'Content-Transfer-Encoding', ' ', "\n"); + $file_type = $this->_get_substring($body_part, 'Content-Type', ' ', ';'); + + if (file_exists($file_path)) { + if (!$mailer->AddAttachment($file_path, $file_name, $file_encoding, $file_type)) { + drupal_set_message(t('Attahment could not be found or accessed.')); + } + } + else { + // Clean up the text. + $body_part = trim($this->_remove_headers(trim($body_part))); + + if (drupal_strtolower($file_encoding) == 'base64') { + $attachment = base64_decode($body_part); + } + elseif (drupal_strtolower($file_encoding) == 'quoted-printable') { + $attachment = quoted_printable_decode($body_part); + } + else { + $attachment = $body_part; + } + + $attachment_new_filename = drupal_tempnam('temporary://', 'smtp'); + $file_path = file_save_data($attachment, $attachment_new_filename, FILE_EXISTS_REPLACE); + $real_path = drupal_realpath($file_path->uri); + + if (!$mailer->AddAttachment($real_path, $file_name)) { + drupal_set_message(t('Attachment could not be found or accessed.')); + } + } + } + } + break; + + default: + $mailer->Body = $body; + break; + } + + // Process mimemail attachments + if (isset($message['params']['attachments'])) { + foreach ($message['params']['attachments'] as $attachment) { + if (isset($attachment['filecontent'])) { + $mailer->AddStringAttachment($attachment['filecontent'], $attachment['filename'], 'base64', $attachment['filemime']); + } + if (isset($attachment['filepath'])) { + $mailer->AddAttachment($attachment['filepath'], $attachment['filename'], 'base64', $attachment['filemime']); + } + } + } + + // Set the authentication settings. + $username = variable_get('smtp_username', ''); + $password = variable_get('smtp_password', ''); + + // If username and password are given, use SMTP authentication. + if ($username != '' && $password != '') { + $mailer->SMTPAuth = TRUE; + $mailer->Username = $username; + $mailer->Password = $password; + } + + + // Set the protocol prefix for the smtp host. + switch (variable_get('smtp_protocol', 'standard')) { + case 'ssl': + $mailer->SMTPSecure = 'ssl'; + break; + + case 'tls': + $mailer->SMTPSecure = 'tls'; + break; + + default: + $mailer->SMTPSecure = ''; + } + + + // Set other connection settings. + $mailer->Host = variable_get('smtp_host', '') . ';' . variable_get('smtp_hostbackup', ''); + $mailer->Port = variable_get('smtp_port', '25'); + $mailer->Mailer = 'smtp'; + + // Let the people know what is going on. + watchdog('smtp', 'Sending mail to: @to', array('@to' => $to)); + + // Try to send e-mail. If it fails, set watchdog entry. + if (!$mailer->Send()) { + watchdog('smtp', 'Error sending e-mail from @from to @to : !error_message', array('@from' => $from, '@to' => $to, '!error_message' => $mailer->ErrorInfo), WATCHDOG_ERROR); + return FALSE; + } + + $mailer->SmtpClose(); + return TRUE; + } + + /** + * Splits the input into parts based on the given boundary. + * + * Swiped from Mail::MimeDecode, with modifications based on Drupal's coding + * standards and this bug report: http://pear.php.net/bugs/bug.php?id=6495 + * + * @param input + * A string containing the body text to parse. + * @param boundary + * A string with the boundary string to parse on. + * @return + * An array containing the resulting mime parts + */ + protected function _boundary_split($input, $boundary) { + $parts = array(); + $bs_possible = drupal_substr($boundary, 2, -2); + $bs_check = '\"' . $bs_possible . '\"'; + + if ($boundary == $bs_check) { + $boundary = $bs_possible; + } + + $tmp = explode('--' . $boundary, $input); + + for ($i = 1; $i < count($tmp); $i++) { + if (trim($tmp[$i])) { + $parts[] = $tmp[$i]; + } + } + + return $parts; + } // End of _smtp_boundary_split(). + + /** + * Strips the headers from the body part. + * + * @param input + * A string containing the body part to strip. + * @return + * A string with the stripped body part. + */ + protected function _remove_headers($input) { + $part_array = explode("\n", $input); + + // will strip these headers according to RFC2045 + $headers_to_strip = array( 'Content-Type', 'Content-Transfer-Encoding', 'Content-ID', 'Content-Disposition'); + $pattern = '/^(' . implode('|', $headers_to_strip) . '):/'; + + while (count($part_array) > 0) { + + // ignore trailing spaces/newlines + $line = rtrim($part_array[0]); + + // if the line starts with a known header string + if (preg_match($pattern, $line)) { + $line = rtrim(array_shift($part_array)); + // remove line containing matched header. + + // if line ends in a ';' and the next line starts with four spaces, it's a continuation + // of the header split onto the next line. Continue removing lines while we have this condition. + while (substr($line, -1) == ';' && count($part_array) > 0 && substr($part_array[0], 0, 4) == ' ') { + $line = rtrim(array_shift($part_array)); + } + } + else { + // no match header, must be past headers; stop searching. + break; + } + } + + $output = implode("\n", $part_array); + return $output; + } // End of _smtp_remove_headers(). + + /** + * Returns a string that is contained within another string. + * + * Returns the string from within $source that is some where after $target + * and is between $beginning_character and $ending_character. + * + * @param $source + * A string containing the text to look through. + * @param $target + * A string containing the text in $source to start looking from. + * @param $beginning_character + * A string containing the character just before the sought after text. + * @param $ending_character + * A string containing the character just after the sought after text. + * @return + * A string with the text found between the $beginning_character and the + * $ending_character. + */ + protected function _get_substring($source, $target, $beginning_character, $ending_character) { + $search_start = strpos($source, $target) + 1; + $first_character = strpos($source, $beginning_character, $search_start) + 1; + $second_character = strpos($source, $ending_character, $first_character) + 1; + $substring = drupal_substr($source, $first_character, $second_character - $first_character); + $string_length = drupal_strlen($substring) - 1; + + if ($substring[$string_length] == $ending_character) { + $substring = drupal_substr($substring, 0, $string_length); + } + + return $substring; + } // End of _smtp_get_substring(). +}