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