123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- <?php
- /**
- * @file
- * Mail processing.
- */
- class MailjetSmtpMailSystem implements MailSystemInterface {
- protected $AllowHtml;
- /**
- * Concatenate and wrap the e-mail body for either
- * plain-text or HTML emails.
- *
- * @param array $message
- * A message array, as described in hook_mail_alter().
- *
- * @return string
- * The formatted $message.
- */
- public function format(array $message) {
- $this->AllowHtml = variable_get('mailjet_allowhtml', TRUE);
- // Join the body array into one string.
- $message['body'] = implode("\n\n", $message['body']);
- if (! $this->AllowHtml) {
- // 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 array $message
- * A message array, as described in hook_mail_alter().
- *
- * @return bool
- * TRUE if the mail was successfully accepted, otherwise FALSE.
- */
- public function mail(array $message) {
- $id = $message['id'];
- $to = $message['to'];
- $from = $message['from'];
- $subject = $message['subject'];
- $body = $message['body'];
- $headers = $message['headers'];
- if (isset($message['params']['subject'])) {
- $subject = $message['params']['subject'];
- }
- if (isset($message['params']['body'][0])) {
- $body = $message['params']['body'][0];
- }
- $path = libraries_get_path('phpmailer');
- if (file_exists($path . '/class.phpmailer.php')) {
- require_once $path . '/class.phpmailer.php';
- }
- else {
- drupal_set_message(t('Unable to send mail : PHPMailer library does not exist.'), 'error');
- return FALSE;
- }
- // Create a new PHPMailer object - autoloaded from registry.
- $mailer = new PHPMailer();
- $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('mailjet_from', '')) == '') {
- // If smtp_from config option is blank, use site_email.
- if (($from = variable_get('site_email', '')) == '') {
- drupal_set_message(t('There is no submitted from address.'), 'error');
- if (variable_get('mailjet_debug')) {
- watchdog('mailjet', '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);
- $from = preg_replace("/(.*)\<(.*)\>/i", '$2', $from);
- }
- elseif (!valid_email_address($from)) {
- drupal_set_message(t('The submitted from address (@from) is not valid.', array('@from' => $from)), 'error');
- if (variable_get('mailjet_debug')) {
- watchdog('mailjet', 'The submitted from address (@from) is not valid.', array('@from' => $from), WATCHDOG_ERROR);
- }
- return FALSE;
- }
- // Defines the From value $from_nameto what we expect.
- $mailer->SetFrom($from, $from_name);
- // 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) {
- 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 = drupal_strtolower(drupal_substr($var, $cut + 1));
- $new_key = drupal_substr($var, 0, $cut);
- unset($vars[$i]);
- $vars[$new_key] = $new_var;
- }
- }
- // Set the charset based on the provided value, if there is one.
- $mailer->CharSet = isset($vars['charset']) ? $vars['charset'] : 'utf-8';
- 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->getSubstrings($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->getSubstrings($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->getSubstrings($value, 'boundary', '"', '"');
- break;
- default:
- // Everything else is unsuppored by PHPMailer.
- drupal_set_message(t('The Content-Type of your message is not supported by PHPMailer and will be sent as text/plain instead.'), 'error');
- if (variable_get('mailjet_debug')) {
- watchdog('mailjet', 'The Content-Type of your message is not supported by PHPMailer and will be sent as text/plain instead.', array(), 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) {
- $reply_to_parts = explode('<', $value);
- $reply_to_name = trim($reply_to_parts[0]);
- $reply_to_name = trim($reply_to_name, '"');
- $reply_to_addr = rtrim($reply_to_parts[1], '>');
- $mailer->AddReplyTo($reply_to_addr, $reply_to_name);
- }
- else {
- $mailer->AddReplyTo($value);
- }
- }
- break;
- case 'content-transfer-encoding':
- $mailer->Encoding = $value;
- break;
- case 'return-path':
- case 'mime-version':
- case 'x-mailer':
- 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->AddBCC($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);
- }
- }
- $mailer->AddCustomHeader('X-Mailer:Mailjet-for-Drupal/1.0');
- $mailer->Subject = $subject;
- // Processes the message's body.
- switch ($content_type) {
- case 'multipart/related':
- $mailer->Body = $body;
- break;
- case 'multipart/alternative':
- // Split the body based on the boundary ID.
- $body_parts = $this->boundarySplit($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->removeHeaders(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->removeHeaders(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->boundarySplit($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')) {
- // Clean up the text.
- $body_part = trim($this->removeHeaders(trim($body_part)));
- // Get boundary ID from the Content-Type header.
- $boundary2 = $this->getSubstrings($body_part, 'boundary', '"', '"');
- // Split the body based on the boundary ID.
- $body_parts2 = $this->boundarySplit($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->removeHeaders(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->removeHeaders(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->removeHeaders(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->removeHeaders(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->getSubstrings($body_part, 'filename=', '"', '"');
- $file_name = $this->getSubstrings($body_part, ' name=', '"', '"');
- $file_encoding = $this->getSubstrings($body_part, 'Content-Transfer-Encoding', ' ', "\n");
- $file_type = $this->getSubstrings($body_part, 'Content-Type', ' ', ';');
- if (file_exists($file_path)) {
- if (!$mailer->AddAttachment($file_path, $file_name, $file_encoding, $filetype)) {
- drupal_set_message(t('Attahment could not be found or accessed.'));
- }
- }
- else {
- // Clean up the text.
- $body_part = trim($this->removeHeaders(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 = tempnam(realpath(file_directory_temp()), 'smtp');
- $file_path = file_save_data($attachment, $attachment_new_filename, FILE_EXISTS_RENAME);
- if (!$mailer->AddAttachment($file_path, $file_name)) {
- drupal_set_message(t('Attachment could not be found or accessed.'));
- }
- }
- }
- }
- break;
- default:
- $mailer->Body = $body;
- break;
- }
- // Set the authentication settings.
- $username = variable_get('mailjet_username', '');
- $password = variable_get('mailjet_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('mailjet_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('mailjet_host', 'in.mailjet.com');
- $mailer->Port = variable_get('mailjet_port', '25');
- $mailer->Mailer = 'smtp';
- if (variable_get('mailjet_debug')) {
- watchdog('mailjet', 'Sending mail to: @to', array('@to' => $to));
- }
- // Try to send e-mail. If it fails, set watchdog entry.
- if (!$mailer->Send()) {
- if (variable_get('mailjet_debug')) {
- watchdog('mailjet',
- '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 string $input
- * A string containing the body text to parse.
- * @param string $boundary
- * A string with the boundary string to parse on.
- *
- * @return array
- * An array containing the resulting mime parts
- */
- protected function boundarySplit($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;
- }
- /**
- * Strips the headers from the body part.
- *
- * @param string $input
- * A string containing the body part to strip.
- *
- * @return string
- * A string with the stripped body part.
- */
- protected function removeHeaders($input) {
- $part_array = explode("\n", $input);
- if (strpos($part_array[0], 'Content') !== FALSE) {
- if (strpos($part_array[1], 'Content') !== FALSE) {
- if (strpos($part_array[2], 'Content') !== FALSE) {
- array_shift($part_array);
- array_shift($part_array);
- array_shift($part_array);
- }
- else {
- array_shift($part_array);
- array_shift($part_array);
- }
- }
- else {
- array_shift($part_array);
- }
- }
- $output = implode("\n", $part_array);
- return $output;
- }
- /**
- * 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 string $source
- * A string containing the text to look through.
- * @param string $target
- * A string containing the text in $source to start looking from.
- * @param string $beginning_character
- * A string containing the character just before the sought after text.
- * @param string $ending_character
- * A string containing the character just after the sought after text.
- *
- * @return string
- * A string with the text found between the $beginning_character and the
- * $ending_character.
- */
- protected function getSubstrings($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;
- }
- }
|