smtp.mail.inc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. <?php
  2. /**
  3. * @file
  4. * The code processing mail in the smtp module.
  5. *
  6. */
  7. /**
  8. * Modify the drupal mail system to use smtp when sending emails.
  9. * Include the option to choose between plain text or HTML
  10. */
  11. class SmtpMailSystem implements MailSystemInterface {
  12. protected $AllowHtml;
  13. /**
  14. * Concatenate and wrap the e-mail body for either
  15. * plain-text or HTML emails.
  16. *
  17. * @param $message
  18. * A message array, as described in hook_mail_alter().
  19. *
  20. * @return
  21. * The formatted $message.
  22. */
  23. public function format(array $message) {
  24. $this->AllowHtml = variable_get('smtp_allowhtml', 0);
  25. // Join the body array into one string.
  26. $message['body'] = implode("\n\n", $message['body']);
  27. if ($this->AllowHtml == 0) {
  28. // Convert any HTML to plain-text.
  29. $message['body'] = drupal_html_to_text($message['body']);
  30. // Wrap the mail body for sending.
  31. $message['body'] = drupal_wrap_mail($message['body']);
  32. }
  33. return $message;
  34. }
  35. /**
  36. * Send the e-mail message.
  37. *
  38. * @see drupal_mail()
  39. *
  40. * @param $message
  41. * A message array, as described in hook_mail_alter().
  42. * @return
  43. * TRUE if the mail was successfully accepted, otherwise FALSE.
  44. */
  45. public function mail(array $message) {
  46. if (variable_get('smtp_queue', FALSE)
  47. && (!isset($message['params']['skip_queue']) || !$message['params']['skip_queue'])) {
  48. smtp_send_queue($message);
  49. if (variable_get('smtp_debugging', SMTP_LOGGING_ERRORS) == SMTP_LOGGING_ALL) {
  50. watchdog('smtp', 'Queue sending mail to: @to', array('@to' => $message['to']));
  51. }
  52. return TRUE;
  53. }
  54. else {
  55. return $this->mailWithoutQueue($message);
  56. }
  57. }
  58. public function mailWithoutQueue(array $message) {
  59. $to = $message['to'];
  60. $from = $message['from'];
  61. $body = $message['body'];
  62. $headers = $message['headers'];
  63. $subject = $message['subject'];
  64. // Optionally reroute all emails to a single address.
  65. $reroute_address = variable_get('smtp_reroute_address', '');
  66. if (!empty($reroute_address)) {
  67. $to = $reroute_address;
  68. // Remove any CC and BCC headers that might have been set.
  69. unset($headers['cc']);
  70. unset($headers['bcc']);
  71. }
  72. // Create a new PHPMailer object - autoloaded from registry.
  73. $mailer = new PHPMailer();
  74. $logging = variable_get('smtp_debugging', SMTP_LOGGING_ERRORS);
  75. // Turn on debugging, if requested.
  76. if ($logging == SMTP_LOGGING_ALL && user_access('administer smtp module')) {
  77. $mailer->SMTPDebug = TRUE;
  78. }
  79. // Set the from name. First we try to get the name from i18n, in the case
  80. // that it has been translated. The name is set according to the language
  81. // of the email being sent.
  82. $from_name = FALSE;
  83. if (function_exists('i18n_variable_get')) {
  84. // The 'language' value may be stored as an object.
  85. $langcode = $message['language'];
  86. if (is_object($langcode)) {
  87. $langcode = $langcode->language;
  88. }
  89. if (i18n_variable_get('smtp_fromname', $langcode, '') != '') {
  90. $from_name = i18n_variable_get('smtp_fromname', $langcode, '');
  91. }
  92. else {
  93. // If value is not defined in settings, use site_name.
  94. $from_name = i18n_variable_get('site_name', $langcode, '');
  95. }
  96. }
  97. if (variable_get('smtp_client_hostname', '') != '') {
  98. $mailer->Hostname = variable_get('smtp_client_hostname', '');
  99. }
  100. if (variable_get('smtp_client_helo', '') != '') {
  101. $mailer->Helo = variable_get('smtp_client_helo', '');
  102. }
  103. // If i18n is not enabled, we get the From Name through normal variables
  104. if (!$from_name) {
  105. if (variable_get('smtp_fromname', '') != '') {
  106. $from_name = variable_get('smtp_fromname', '');
  107. }
  108. else {
  109. // If value is not defined in settings, use site_name.
  110. $from_name = variable_get('site_name', '');
  111. }
  112. }
  113. //Hack to fix reply-to issue.
  114. if (!isset($headers['Reply-To']) || empty($headers['Reply-To'])) {
  115. if (strpos($from, '<')) {
  116. $reply = preg_replace('/>.*/', '', preg_replace('/.*</', '', $from));
  117. }
  118. else {
  119. $reply = $from;
  120. }
  121. $headers['Reply-To'] = $reply;
  122. }
  123. $properfrom = variable_get('smtp_from', '');
  124. if (!empty($properfrom)) {
  125. $headers['From'] = $properfrom;
  126. $from = $properfrom;
  127. }
  128. // Blank value will let the e-mail address appear.
  129. if ($from == NULL || $from == '') {
  130. // If from e-mail address is blank, use smtp_from config option.
  131. if (($from = variable_get('smtp_from', '')) == '') {
  132. // If smtp_from config option is blank, use site_email.
  133. if (($from = variable_get('site_mail', '')) == '') {
  134. drupal_set_message(t('There is no submitted from address.'), 'error');
  135. if ($logging) {
  136. watchdog('smtp', 'There is no submitted from address.', array(), WATCHDOG_ERROR);
  137. }
  138. return FALSE;
  139. }
  140. }
  141. }
  142. $from_comp = $this->_get_components($from);
  143. if (!valid_email_address($from_comp['email'])) {
  144. drupal_set_message(t('The submitted from address (@from) is not valid.', array('@from' => $from_comp['email'])), 'error');
  145. if ($logging) {
  146. watchdog('smtp', 'The submitted from address (@from) is not valid.', array('@from' => $from_comp['email']), WATCHDOG_ERROR);
  147. }
  148. return FALSE;
  149. }
  150. // Defines the From value to what we expect.
  151. $mailer->From = $from_comp['email'];
  152. $mailer->FromName = empty($from_comp['name']) ? $from_name : $from_comp['name'];
  153. $mailer->Sender = $from_comp['email'];
  154. // Create the list of 'To:' recipients.
  155. $torecipients = explode(',', $to);
  156. foreach ($torecipients as $torecipient) {
  157. $to_comp = $this->_get_components($torecipient);
  158. $mailer->AddAddress($to_comp['email'], $to_comp['name']);
  159. }
  160. // Parse the headers of the message and set the PHPMailer object's settings
  161. // accordingly.
  162. foreach ($headers as $key => $value) {
  163. //watchdog('error', 'Key: ' . $key . ' Value: ' . $value);
  164. switch (drupal_strtolower($key)) {
  165. case 'from':
  166. if ($from == NULL or $from == '') {
  167. // If a from value was already given, then set based on header.
  168. // Should be the most common situation since drupal_mail moves the
  169. // from to headers.
  170. $from = $value;
  171. $mailer->From = $value;
  172. // then from can be out of sync with from_name !
  173. $mailer->FromName = '';
  174. $mailer->Sender = $value;
  175. }
  176. break;
  177. case 'content-type':
  178. // Parse several values on the Content-type header, storing them in an array like
  179. // key=value -> $vars['key']='value'
  180. $vars = explode(';', $value);
  181. foreach ($vars as $i => $var) {
  182. if ($cut = strpos($var, '=')) {
  183. $new_var = trim(drupal_strtolower(drupal_substr($var, $cut + 1)));
  184. $new_key = trim(drupal_substr($var, 0, $cut));
  185. unset($vars[$i]);
  186. $vars[$new_key] = $new_var;
  187. }
  188. }
  189. // Set the charset based on the provided value, otherwise set it to UTF-8 (which is Drupals internal default).
  190. $mailer->CharSet = isset($vars['charset']) ? $vars['charset'] : 'UTF-8';
  191. // If $vars is empty then set an empty value at index 0 to avoid a PHP warning in the next statement
  192. $vars[0] = isset($vars[0])?$vars[0]:'';
  193. switch ($vars[0]) {
  194. case 'text/plain':
  195. // The message includes only a plain text part.
  196. $mailer->IsHTML(FALSE);
  197. $content_type = 'text/plain';
  198. break;
  199. case 'text/html':
  200. // The message includes only an HTML part.
  201. $mailer->IsHTML(TRUE);
  202. $content_type = 'text/html';
  203. break;
  204. case 'multipart/related':
  205. // Get the boundary ID from the Content-Type header.
  206. $boundary = $this->_get_substring($value, 'boundary', '"', '"');
  207. // The message includes an HTML part w/inline attachments.
  208. $mailer->ContentType = $content_type = 'multipart/related; boundary="' . $boundary . '"';
  209. break;
  210. case 'multipart/alternative':
  211. // The message includes both a plain text and an HTML part.
  212. $mailer->ContentType = $content_type = 'multipart/alternative';
  213. // Get the boundary ID from the Content-Type header.
  214. $boundary = $this->_get_substring($value, 'boundary', '"', '"');
  215. break;
  216. case 'multipart/mixed':
  217. // The message includes one or more attachments.
  218. $mailer->ContentType = $content_type = 'multipart/mixed';
  219. // Get the boundary ID from the Content-Type header.
  220. $boundary = $this->_get_substring($value, 'boundary', '"', '"');
  221. break;
  222. default:
  223. // Everything else is unsuppored by PHPMailer.
  224. 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');
  225. if ($logging) {
  226. 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);
  227. }
  228. // Force the Content-Type to be text/plain.
  229. $mailer->IsHTML(FALSE);
  230. $content_type = 'text/plain';
  231. }
  232. break;
  233. case 'reply-to':
  234. // Only add a "reply-to" if it's not the same as "return-path".
  235. if ($value != $headers['Return-Path']) {
  236. $replyto_comp = $this->_get_components($value);
  237. $mailer->AddReplyTo($replyto_comp['email'], $replyto_comp['name']);
  238. }
  239. break;
  240. case 'content-transfer-encoding':
  241. $mailer->Encoding = $value;
  242. break;
  243. case 'return-path':
  244. $returnpath_comp = $this->_get_components($value);
  245. $mailer->Sender = $returnpath_comp['email'];
  246. break;
  247. case 'mime-version':
  248. case 'x-mailer':
  249. // Let PHPMailer specify these.
  250. break;
  251. case 'errors-to':
  252. $mailer->AddCustomHeader('Errors-To: ' . $value);
  253. break;
  254. case 'cc':
  255. $ccrecipients = explode(',', $value);
  256. foreach ($ccrecipients as $ccrecipient) {
  257. $cc_comp = $this->_get_components($ccrecipient);
  258. $mailer->AddCC($cc_comp['email'], $cc_comp['name']);
  259. }
  260. break;
  261. case 'bcc':
  262. $bccrecipients = explode(',', $value);
  263. foreach ($bccrecipients as $bccrecipient) {
  264. $bcc_comp = $this->_get_components($bccrecipient);
  265. $mailer->AddBCC($bcc_comp['email'], $bcc_comp['name']);
  266. }
  267. break;
  268. case 'message-id':
  269. $mailer->MessageID = $value;
  270. break;
  271. default:
  272. // The header key is not special - add it as is.
  273. $mailer->AddCustomHeader($key . ': ' . $value);
  274. }
  275. }
  276. /**
  277. * TODO
  278. * Need to figure out the following.
  279. *
  280. * Add one last header item, but not if it has already been added.
  281. * $errors_to = FALSE;
  282. * foreach ($mailer->CustomHeader as $custom_header) {
  283. * if ($custom_header[0] = '') {
  284. * $errors_to = TRUE;
  285. * }
  286. * }
  287. * if ($errors_to) {
  288. * $mailer->AddCustomHeader('Errors-To: '. $from);
  289. * }
  290. */
  291. // Add the message's subject.
  292. $mailer->Subject = $subject;
  293. // Processes the message's body.
  294. switch ($content_type) {
  295. case 'multipart/related':
  296. $mailer->Body = $body;
  297. // TODO: Figure out if there is anything more to handling this type.
  298. break;
  299. case 'multipart/alternative':
  300. // Split the body based on the boundary ID.
  301. $body_parts = $this->_boundary_split($body, $boundary);
  302. foreach ($body_parts as $body_part) {
  303. // If plain/text within the body part, add it to $mailer->AltBody.
  304. if (strpos($body_part, 'text/plain')) {
  305. // Clean up the text.
  306. $body_part = trim($this->_remove_headers(trim($body_part)));
  307. // Include it as part of the mail object.
  308. $mailer->AltBody = $body_part;
  309. }
  310. // If plain/html within the body part, add it to $mailer->Body.
  311. elseif (strpos($body_part, 'text/html')) {
  312. // Clean up the text.
  313. $body_part = trim($this->_remove_headers(trim($body_part)));
  314. // Include it as part of the mail object.
  315. $mailer->Body = $body_part;
  316. }
  317. }
  318. break;
  319. case 'multipart/mixed':
  320. // Split the body based on the boundary ID.
  321. $body_parts = $this->_boundary_split($body, $boundary);
  322. // Determine if there is an HTML part for when adding the plain text part.
  323. $text_plain = FALSE;
  324. $text_html = FALSE;
  325. foreach ($body_parts as $body_part) {
  326. if (strpos($body_part, 'text/plain')) {
  327. $text_plain = TRUE;
  328. }
  329. if (strpos($body_part, 'text/html')) {
  330. $text_html = TRUE;
  331. }
  332. }
  333. foreach ($body_parts as $body_part) {
  334. // If test/plain within the body part, add it to either
  335. // $mailer->AltBody or $mailer->Body, depending on whether there is
  336. // also a text/html part ot not.
  337. if (strpos($body_part, 'multipart/alternative')) {
  338. // Get boundary ID from the Content-Type header.
  339. $boundary2 = $this->_get_substring($body_part, 'boundary', '"', '"');
  340. // Clean up the text.
  341. $body_part = trim($this->_remove_headers(trim($body_part)));
  342. // Split the body based on the boundary ID.
  343. $body_parts2 = $this->_boundary_split($body_part, $boundary2);
  344. foreach ($body_parts2 as $body_part2) {
  345. // If plain/text within the body part, add it to $mailer->AltBody.
  346. if (strpos($body_part2, 'text/plain')) {
  347. // Clean up the text.
  348. $body_part2 = trim($this->_remove_headers(trim($body_part2)));
  349. // Include it as part of the mail object.
  350. $mailer->AltBody = $body_part2;
  351. $mailer->ContentType = 'multipart/mixed';
  352. }
  353. // If plain/html within the body part, add it to $mailer->Body.
  354. elseif (strpos($body_part2, 'text/html')) {
  355. // Get the encoding.
  356. $body_part2_encoding = trim($this->_get_substring($body_part2, 'Content-Transfer-Encoding', ':', "\n"));
  357. // Clean up the text.
  358. $body_part2 = trim($this->_remove_headers(trim($body_part2)));
  359. // Check whether the encoding is base64, and if so, decode it.
  360. if (drupal_strtolower($body_part2_encoding) == 'base64') {
  361. // Include it as part of the mail object.
  362. $mailer->Body = base64_decode($body_part2);
  363. // Ensure the whole message is recoded in the base64 format.
  364. $mailer->Encoding = 'base64';
  365. }
  366. else {
  367. // Include it as part of the mail object.
  368. $mailer->Body = $body_part2;
  369. }
  370. $mailer->ContentType = 'multipart/mixed';
  371. }
  372. }
  373. }
  374. // If text/plain within the body part, add it to $mailer->Body.
  375. elseif (strpos($body_part, 'text/plain')) {
  376. // Clean up the text.
  377. $body_part = trim($this->_remove_headers(trim($body_part)));
  378. if ($text_html) {
  379. $mailer->AltBody = $body_part;
  380. $mailer->IsHTML(TRUE);
  381. $mailer->ContentType = 'multipart/mixed';
  382. }
  383. else {
  384. $mailer->Body = $body_part;
  385. $mailer->IsHTML(FALSE);
  386. $mailer->ContentType = 'multipart/mixed';
  387. }
  388. }
  389. // If text/html within the body part, add it to $mailer->Body.
  390. elseif (strpos($body_part, 'text/html')) {
  391. // Clean up the text.
  392. $body_part = trim($this->_remove_headers(trim($body_part)));
  393. // Include it as part of the mail object.
  394. $mailer->Body = $body_part;
  395. $mailer->IsHTML(TRUE);
  396. $mailer->ContentType = 'multipart/mixed';
  397. }
  398. // Add the attachment.
  399. elseif (strpos($body_part, 'Content-Disposition: attachment;') && !isset($message['params']['attachments'])) {
  400. $file_path = $this->_get_substring($body_part, 'filename=', '"', '"');
  401. $file_name = $this->_get_substring($body_part, ' name=', '"', '"');
  402. $file_encoding = $this->_get_substring($body_part, 'Content-Transfer-Encoding', ' ', "\n");
  403. $file_type = $this->_get_substring($body_part, 'Content-Type', ' ', ';');
  404. if (file_exists($file_path)) {
  405. if (!$mailer->AddAttachment($file_path, $file_name, $file_encoding, $file_type)) {
  406. drupal_set_message(t('Attahment could not be found or accessed.'));
  407. }
  408. }
  409. else {
  410. // Clean up the text.
  411. $body_part = trim($this->_remove_headers(trim($body_part)));
  412. if (drupal_strtolower($file_encoding) == 'base64') {
  413. $attachment = base64_decode($body_part);
  414. }
  415. elseif (drupal_strtolower($file_encoding) == 'quoted-printable') {
  416. $attachment = quoted_printable_decode($body_part);
  417. }
  418. else {
  419. $attachment = $body_part;
  420. }
  421. $attachment_new_filename = drupal_tempnam('temporary://', 'smtp');
  422. $file_path = file_save_data($attachment, $attachment_new_filename, FILE_EXISTS_REPLACE);
  423. $real_path = drupal_realpath($file_path->uri);
  424. if (!$mailer->AddAttachment($real_path, $file_name)) {
  425. drupal_set_message(t('Attachment could not be found or accessed.'));
  426. }
  427. }
  428. }
  429. }
  430. break;
  431. default:
  432. $mailer->Body = $body;
  433. break;
  434. }
  435. // Process mimemail attachments, which are prepared in mimemail_mail().
  436. if (isset($message['params']['attachments'])) {
  437. foreach ($message['params']['attachments'] as $attachment) {
  438. if (isset($attachment['filecontent'])) {
  439. $mailer->AddStringAttachment($attachment['filecontent'], $attachment['filename'], 'base64', $attachment['filemime']);
  440. }
  441. if (isset($attachment['filepath'])) {
  442. $filename = isset($attachment['filename']) ? $attachment['filename'] : basename($attachment['filepath']);
  443. $filemime = isset($attachment['filemime']) ? $attachment['filemime'] : file_get_mimetype($attachment['filepath']);
  444. $mailer->AddAttachment($attachment['filepath'], $filename, 'base64', $filemime);
  445. }
  446. }
  447. }
  448. // Set the authentication settings.
  449. $username = variable_get('smtp_username', '');
  450. $password = variable_get('smtp_password', '');
  451. // If username and password are given, use SMTP authentication.
  452. if ($username != '' && $password != '') {
  453. $mailer->SMTPAuth = TRUE;
  454. $mailer->Username = $username;
  455. $mailer->Password = $password;
  456. }
  457. // Set the protocol prefix for the smtp host.
  458. switch (variable_get('smtp_protocol', 'standard')) {
  459. case 'ssl':
  460. $mailer->SMTPSecure = 'ssl';
  461. break;
  462. case 'tls':
  463. $mailer->SMTPSecure = 'tls';
  464. break;
  465. default:
  466. $mailer->SMTPSecure = '';
  467. }
  468. // Set other connection settings.
  469. $mailer->Host = variable_get('smtp_host', '') . ';' . variable_get('smtp_hostbackup', '');
  470. $mailer->Port = variable_get('smtp_port', '25');
  471. $mailer->Mailer = 'smtp';
  472. // Integration with the Maillog module.
  473. if (module_exists('maillog')) {
  474. if (variable_get('maillog_log', TRUE)) {
  475. $record = new stdClass;
  476. // In case the subject/from/to is already encoded, decode with
  477. // mime_header_decode.
  478. $record->header_message_id = isset($mailer->MessageID) ? $mailer->MessageID : NULL;
  479. $record->subject = drupal_substr(mime_header_decode($mailer->Subject), 0, 255);
  480. $record->header_from = $from;
  481. $record->header_to = $to;
  482. $record->header_reply_to = isset($headers['Reply-To']) ? $headers['Reply-To'] : '';
  483. $record->header_all = serialize($headers);
  484. $record->sent_date = REQUEST_TIME;
  485. // Used to separate different portions of the body string.
  486. $divider = str_repeat('-', 60) . "\n";
  487. // Load the attachments.
  488. $attachments = $mailer->GetAttachments();
  489. $record->body = '';
  490. // If there's more than one item to display, add a divider.
  491. if (!empty($mailer->AltBody) || !empty($attachments)) {
  492. $record->body .= t('Body') . ":\n";
  493. $record->body .= $divider;
  494. }
  495. // Add the body field.
  496. if (isset($mailer->Body)) {
  497. $record->body .= $mailer->Body;
  498. }
  499. else {
  500. $record->body .= t('*No message body*') . ":\n";
  501. }
  502. // The AltBody value is optional.
  503. if (!empty($mailer->AltBody)) {
  504. $record->body .= "\n";
  505. $record->body .= $divider;
  506. $record->body .= t('Alternative body') . ":\n";
  507. $record->body .= $divider;
  508. $record->body .= $mailer->AltBody;
  509. }
  510. // List the attachments.
  511. if (!empty($attachments)) {
  512. $record->body .= "\n";
  513. $record->body .= $divider;
  514. $record->body .= t('Attachments') . ":\n";
  515. $record->body .= $divider;
  516. foreach ($attachments as $file) {
  517. $record->body .= t('Filename') . ':' . $file[1] . "\n";
  518. $record->body .= t('Name') . ':' . $file[2] . "\n";
  519. $record->body .= t('Encoding') . ':' . $file[3] . "\n";
  520. $record->body .= t('Type') . ':' . $file[4] . "\n";
  521. $record->body .= "\n";
  522. }
  523. }
  524. drupal_write_record('maillog', $record);
  525. }
  526. // Display the e-mail using Devel module.
  527. if (variable_get('maillog_devel', TRUE) && function_exists('dpm')) {
  528. $devel_msg = array();
  529. $devel_msg[t('Subject')] = $mailer->Subject;
  530. $devel_msg[t('From')] = $from;
  531. $devel_msg[t('To')] = $to;
  532. $devel_msg[t('Reply-To')] = isset($headers['Reply-To']) ? $headers['Reply-To'] : NULL;
  533. $devel_msg[t('Headers')] = $headers;
  534. $devel_msg[t('Body')] = $mailer->Body;
  535. $devel_msg[t('Alternative body')] = $mailer->AltBody;
  536. $devel_msg[t('Attachments')] = $mailer->GetAttachments();
  537. dpm($devel_msg, 'maillog');
  538. }
  539. }
  540. $error = FALSE;
  541. // Email delivery was disabled.
  542. if (!variable_get('smtp_deliver', TRUE)) {
  543. if ($logging) {
  544. $params = array(
  545. '@from' => $from,
  546. '@to' => $to,
  547. );
  548. watchdog('smtp', 'Email delivery is disabled, did not send email from @from to @to.', $params);
  549. }
  550. }
  551. else {
  552. if (!$mailer->send()) {
  553. $params = array(
  554. '@from' => $from,
  555. '@to' => $to,
  556. '!error_message' => $mailer->ErrorInfo
  557. );
  558. if (variable_get('smtp_queue_fail', FALSE)) {
  559. if ($logging) {
  560. watchdog('smtp', 'Error sending e-mail from @from to @to, will retry on cron run : !error_message.', $params, WATCHDOG_ERROR);
  561. }
  562. smtp_failed_messages($message);
  563. }
  564. elseif ($logging) {
  565. $error = TRUE;
  566. watchdog('smtp', 'Error sending e-mail from @from to @to : !error_message', $params, WATCHDOG_ERROR);
  567. }
  568. }
  569. elseif (variable_get('smtp_debugging', SMTP_LOGGING_ERRORS) == SMTP_LOGGING_ALL) {
  570. watchdog('smtp', 'Sent mail to: @to', array('@to' => $to));
  571. }
  572. }
  573. $mailer->SmtpClose();
  574. return !$error;
  575. }
  576. /**
  577. * Splits the input into parts based on the given boundary.
  578. *
  579. * Swiped from Mail::MimeDecode, with modifications based on Drupal's coding
  580. * standards and this bug report: http://pear.php.net/bugs/bug.php?id=6495
  581. *
  582. * @param input
  583. * A string containing the body text to parse.
  584. * @param boundary
  585. * A string with the boundary string to parse on.
  586. * @return
  587. * An array containing the resulting mime parts
  588. */
  589. protected function _boundary_split($input, $boundary) {
  590. $parts = array();
  591. $bs_possible = drupal_substr($boundary, 2, -2);
  592. $bs_check = '\"' . $bs_possible . '\"';
  593. if ($boundary == $bs_check) {
  594. $boundary = $bs_possible;
  595. }
  596. $tmp = explode('--' . $boundary, $input);
  597. for ($i = 1; $i < count($tmp); $i++) {
  598. if (trim($tmp[$i])) {
  599. $parts[] = $tmp[$i];
  600. }
  601. }
  602. return $parts;
  603. } // End of _smtp_boundary_split().
  604. /**
  605. * Strips the headers from the body part.
  606. *
  607. * @param input
  608. * A string containing the body part to strip.
  609. * @return
  610. * A string with the stripped body part.
  611. */
  612. protected function _remove_headers($input) {
  613. $part_array = explode("\n", $input);
  614. // will strip these headers according to RFC2045
  615. $headers_to_strip = array( 'Content-Type', 'Content-Transfer-Encoding', 'Content-ID', 'Content-Disposition');
  616. $pattern = '/^(' . implode('|', $headers_to_strip) . '):/';
  617. while (count($part_array) > 0) {
  618. // ignore trailing spaces/newlines
  619. $line = rtrim($part_array[0]);
  620. // if the line starts with a known header string
  621. if (preg_match($pattern, $line)) {
  622. $line = rtrim(array_shift($part_array));
  623. // remove line containing matched header.
  624. // if line ends in a ';' and the next line starts with four spaces, it's a continuation
  625. // of the header split onto the next line. Continue removing lines while we have this condition.
  626. while (substr($line, -1) == ';' && count($part_array) > 0 && substr($part_array[0], 0, 4) == ' ') {
  627. $line = rtrim(array_shift($part_array));
  628. }
  629. }
  630. else {
  631. // no match header, must be past headers; stop searching.
  632. break;
  633. }
  634. }
  635. $output = implode("\n", $part_array);
  636. return $output;
  637. } // End of _smtp_remove_headers().
  638. /**
  639. * Returns a string that is contained within another string.
  640. *
  641. * Returns the string from within $source that is some where after $target
  642. * and is between $beginning_character and $ending_character.
  643. *
  644. * @param $source
  645. * A string containing the text to look through.
  646. * @param $target
  647. * A string containing the text in $source to start looking from.
  648. * @param $beginning_character
  649. * A string containing the character just before the sought after text.
  650. * @param $ending_character
  651. * A string containing the character just after the sought after text.
  652. * @return
  653. * A string with the text found between the $beginning_character and the
  654. * $ending_character.
  655. */
  656. protected function _get_substring($source, $target, $beginning_character, $ending_character) {
  657. $search_start = strpos($source, $target) + 1;
  658. $first_character = strpos($source, $beginning_character, $search_start) + 1;
  659. $second_character = strpos($source, $ending_character, $first_character) + 1;
  660. $substring = drupal_substr($source, $first_character, $second_character - $first_character);
  661. $string_length = drupal_strlen($substring) - 1;
  662. if ($substring[$string_length] == $ending_character) {
  663. $substring = drupal_substr($substring, 0, $string_length);
  664. }
  665. return trim($substring);
  666. } // End of _smtp_get_substring().
  667. /**
  668. * Returns an array of name and email address from a string.
  669. *
  670. * @param $input
  671. * A string that contains different possible combinations of names and
  672. * email address.
  673. * @return
  674. * An array containing a name and an email address.
  675. */
  676. protected function _get_components($input) {
  677. $components = array(
  678. 'input' => $input,
  679. 'name' => '',
  680. 'email' => '',
  681. );
  682. // If the input is a valid email address in its entirety, then there is
  683. // nothing to do, just return that.
  684. if (valid_email_address($input)) {
  685. $components['email'] = trim($input);
  686. return $components;
  687. }
  688. // Check if $input has one of the following formats, extract what we can:
  689. // some name <address@example.com>
  690. // "another name" <address@example.com>
  691. // <address@example.com>
  692. if (preg_match('/^"?([^"\t\n]*)"?\s*<([^>\t\n]*)>$/', $input, $matches)) {
  693. $components['name'] = trim($matches[1]);
  694. $components['email'] = trim($matches[2]);
  695. }
  696. return $components;
  697. }
  698. }