PageRenderTime 43ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/sources/subs/Mail.subs.php

https://github.com/Arantor/Elkarte
PHP | 702 lines | 415 code | 108 blank | 179 comment | 100 complexity | befa55ef08d50e6d3c1ee3c9ceaf89e6 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
  1. <?php
  2. /**
  3. * @name ElkArte Forum
  4. * @copyright ElkArte Forum contributors
  5. * @license BSD http://opensource.org/licenses/BSD-3-Clause
  6. *
  7. * This software is a derived product, based on:
  8. *
  9. * Simple Machines Forum (SMF)
  10. * copyright: 2011 Simple Machines (http://www.simplemachines.org)
  11. * license: BSD, See included LICENSE.TXT for terms and conditions.
  12. *
  13. * @version 1.0 Alpha
  14. *
  15. * This file handles tasks related to mail.
  16. * The functions in this file do NOT check permissions.
  17. * @todo should not check permissions.
  18. *
  19. */
  20. if (!defined('ELKARTE'))
  21. die('No access...');
  22. /**
  23. * This function sends an email to the specified recipient(s).
  24. * It uses the mail_type settings and webmaster_email variable.
  25. *
  26. * @param array $to - the email(s) to send to
  27. * @param string $subject - email subject, expected to have entities, and slashes, but not be parsed
  28. * @param string $message - email body, expected to have slashes, no htmlentities
  29. * @param string $from = null - the address to use for replies
  30. * @param string $message_id = null - if specified, it will be used as local part of the Message-ID header.
  31. * @param bool $send_html = false, whether or not the message is HTML vs. plain text
  32. * @param int $priority = 3
  33. * @param bool $hotmail_fix = null
  34. * @param $is_private
  35. * @return boolean, whether ot not the email was sent properly.
  36. */
  37. function sendmail($to, $subject, $message, $from = null, $message_id = null, $send_html = false, $priority = 3, $hotmail_fix = null, $is_private = false)
  38. {
  39. global $webmaster_email, $context, $modSettings, $txt, $scripturl;
  40. global $smcFunc;
  41. // Use sendmail if it's set or if no SMTP server is set.
  42. $use_sendmail = empty($modSettings['mail_type']) || $modSettings['smtp_host'] == '';
  43. // Line breaks need to be \r\n only in windows or for SMTP.
  44. $line_break = $context['server']['is_windows'] || !$use_sendmail ? "\r\n" : "\n";
  45. // So far so good.
  46. $mail_result = true;
  47. // If the recipient list isn't an array, make it one.
  48. $to_array = is_array($to) ? $to : array($to);
  49. // Once upon a time, Hotmail could not interpret non-ASCII mails.
  50. // In honour of those days, it's still called the 'hotmail fix'.
  51. if ($hotmail_fix === null)
  52. {
  53. $hotmail_to = array();
  54. foreach ($to_array as $i => $to_address)
  55. {
  56. if (preg_match('~@(att|comcast|bellsouth)\.[a-zA-Z\.]{2,6}$~i', $to_address) === 1)
  57. {
  58. $hotmail_to[] = $to_address;
  59. $to_array = array_diff($to_array, array($to_address));
  60. }
  61. }
  62. // Call this function recursively for the hotmail addresses.
  63. if (!empty($hotmail_to))
  64. $mail_result = sendmail($hotmail_to, $subject, $message, $from, $message_id, $send_html, $priority, true, $is_private);
  65. // The remaining addresses no longer need the fix.
  66. $hotmail_fix = false;
  67. // No other addresses left? Return instantly.
  68. if (empty($to_array))
  69. return $mail_result;
  70. }
  71. // Get rid of entities.
  72. $subject = un_htmlspecialchars($subject);
  73. // Make the message use the proper line breaks.
  74. $message = str_replace(array("\r", "\n"), array('', $line_break), $message);
  75. // Make sure hotmail mails are sent as HTML so that HTML entities work.
  76. if ($hotmail_fix && !$send_html)
  77. {
  78. $send_html = true;
  79. $message = strtr($message, array($line_break => '<br />' . $line_break));
  80. $message = preg_replace('~(' . preg_quote($scripturl, '~') . '(?:[?/][\w\-_%\.,\?&;=#]+)?)~', '<a href="$1">$1</a>', $message);
  81. }
  82. list (, $from_name) = mimespecialchars(addcslashes($from !== null ? $from : $context['forum_name'], '<>()\'\\"'), true, $hotmail_fix, $line_break);
  83. list (, $subject) = mimespecialchars($subject, true, $hotmail_fix, $line_break);
  84. // Construct the mail headers...
  85. $headers = 'From: "' . $from_name . '" <' . (empty($modSettings['mail_from']) ? $webmaster_email : $modSettings['mail_from']) . '>' . $line_break;
  86. $headers .= $from !== null ? 'Reply-To: <' . $from . '>' . $line_break : '';
  87. $headers .= 'Return-Path: ' . (empty($modSettings['mail_from']) ? $webmaster_email : $modSettings['mail_from']) . $line_break;
  88. $headers .= 'Date: ' . gmdate('D, d M Y H:i:s') . ' -0000' . $line_break;
  89. if ($message_id !== null && empty($modSettings['mail_no_message_id']))
  90. $headers .= 'Message-ID: <' . md5($scripturl . microtime()) . '-' . $message_id . strstr(empty($modSettings['mail_from']) ? $webmaster_email : $modSettings['mail_from'], '@') . '>' . $line_break;
  91. $headers .= 'X-Mailer: ELKARTE' . $line_break;
  92. // Pass this to the integration before we start modifying the output -- it'll make it easier later.
  93. if (in_array(false, call_integration_hook('integrate_outgoing_email', array(&$subject, &$message, &$headers)), true))
  94. return false;
  95. // Save the original message...
  96. $orig_message = $message;
  97. // The mime boundary separates the different alternative versions.
  98. $mime_boundary = 'ELKARTE-' . md5($message . time());
  99. // Using mime, as it allows to send a plain unencoded alternative.
  100. $headers .= 'Mime-Version: 1.0' . $line_break;
  101. $headers .= 'Content-Type: multipart/alternative; boundary="' . $mime_boundary . '"' . $line_break;
  102. $headers .= 'Content-Transfer-Encoding: 7bit' . $line_break;
  103. // Sending HTML? Let's plop in some basic stuff, then.
  104. if ($send_html)
  105. {
  106. $no_html_message = un_htmlspecialchars(strip_tags(strtr($orig_message, array('</title>' => $line_break))));
  107. // But, then, dump it and use a plain one for dinosaur clients.
  108. list(, $plain_message) = mimespecialchars($no_html_message, false, true, $line_break);
  109. $message = $plain_message . $line_break . '--' . $mime_boundary . $line_break;
  110. // This is the plain text version. Even if no one sees it, we need it for spam checkers.
  111. list($charset, $plain_charset_message, $encoding) = mimespecialchars($no_html_message, false, false, $line_break);
  112. $message .= 'Content-Type: text/plain; charset=' . $charset . $line_break;
  113. $message .= 'Content-Transfer-Encoding: ' . $encoding . $line_break . $line_break;
  114. $message .= $plain_charset_message . $line_break . '--' . $mime_boundary . $line_break;
  115. // This is the actual HTML message, prim and proper. If we wanted images, they could be inlined here (with multipart/related, etc.)
  116. list($charset, $html_message, $encoding) = mimespecialchars($orig_message, false, $hotmail_fix, $line_break);
  117. $message .= 'Content-Type: text/html; charset=' . $charset . $line_break;
  118. $message .= 'Content-Transfer-Encoding: ' . ($encoding == '' ? '7bit' : $encoding) . $line_break . $line_break;
  119. $message .= $html_message . $line_break . '--' . $mime_boundary . '--';
  120. }
  121. // Text is good too.
  122. else
  123. {
  124. // Send a plain message first, for the older web clients.
  125. list(, $plain_message) = mimespecialchars($orig_message, false, true, $line_break);
  126. $message = $plain_message . $line_break . '--' . $mime_boundary . $line_break;
  127. // Now add an encoded message using the forum's character set.
  128. list ($charset, $encoded_message, $encoding) = mimespecialchars($orig_message, false, false, $line_break);
  129. $message .= 'Content-Type: text/plain; charset=' . $charset . $line_break;
  130. $message .= 'Content-Transfer-Encoding: ' . $encoding . $line_break . $line_break;
  131. $message .= $encoded_message . $line_break . '--' . $mime_boundary . '--';
  132. }
  133. // Are we using the mail queue, if so this is where we butt in...
  134. if (!empty($modSettings['mail_queue']) && $priority != 0)
  135. return AddMailQueue(false, $to_array, $subject, $message, $headers, $send_html, $priority, $is_private);
  136. // If it's a priority mail, send it now - note though that this should NOT be used for sending many at once.
  137. elseif (!empty($modSettings['mail_queue']) && !empty($modSettings['mail_limit']))
  138. {
  139. list ($last_mail_time, $mails_this_minute) = @explode('|', $modSettings['mail_recent']);
  140. if (empty($mails_this_minute) || time() > $last_mail_time + 60)
  141. $new_queue_stat = time() . '|' . 1;
  142. else
  143. $new_queue_stat = $last_mail_time . '|' . ((int) $mails_this_minute + 1);
  144. updateSettings(array('mail_recent' => $new_queue_stat));
  145. }
  146. // SMTP or sendmail?
  147. if ($use_sendmail)
  148. {
  149. $subject = strtr($subject, array("\r" => '', "\n" => ''));
  150. if (!empty($modSettings['mail_strip_carriage']))
  151. {
  152. $message = strtr($message, array("\r" => ''));
  153. $headers = strtr($headers, array("\r" => ''));
  154. }
  155. foreach ($to_array as $to)
  156. {
  157. if (!mail(strtr($to, array("\r" => '', "\n" => '')), $subject, $message, $headers))
  158. {
  159. log_error(sprintf($txt['mail_send_unable'], $to));
  160. $mail_result = false;
  161. }
  162. // Wait, wait, I'm still sending here!
  163. @set_time_limit(300);
  164. if (function_exists('apache_reset_timeout'))
  165. @apache_reset_timeout();
  166. }
  167. }
  168. else
  169. $mail_result = $mail_result && smtp_mail($to_array, $subject, $message, $headers);
  170. // Everything go smoothly?
  171. return $mail_result;
  172. }
  173. /**
  174. * Add an email to the mail queue.
  175. *
  176. * @param bool $flush = false
  177. * @param array $to_array = array()
  178. * @param string $subject = ''
  179. * @param string $message = ''
  180. * @param string $headers = ''
  181. * @param bool $send_html = false
  182. * @param int $priority = 3
  183. * @param $is_private
  184. * @return boolean
  185. */
  186. function AddMailQueue($flush = false, $to_array = array(), $subject = '', $message = '', $headers = '', $send_html = false, $priority = 3, $is_private = false)
  187. {
  188. global $context, $modSettings, $smcFunc;
  189. static $cur_insert = array();
  190. static $cur_insert_len = 0;
  191. if ($cur_insert_len == 0)
  192. $cur_insert = array();
  193. // If we're flushing, make the final inserts - also if we're near the MySQL length limit!
  194. if (($flush || $cur_insert_len > 800000) && !empty($cur_insert))
  195. {
  196. // Only do these once.
  197. $cur_insert_len = 0;
  198. // Dump the data...
  199. $smcFunc['db_insert']('',
  200. '{db_prefix}mail_queue',
  201. array(
  202. 'time_sent' => 'int', 'recipient' => 'string-255', 'body' => 'string', 'subject' => 'string-255',
  203. 'headers' => 'string-65534', 'send_html' => 'int', 'priority' => 'int', 'private' => 'int',
  204. ),
  205. $cur_insert,
  206. array('id_mail')
  207. );
  208. $cur_insert = array();
  209. $context['flush_mail'] = false;
  210. }
  211. // If we're flushing we're done.
  212. if ($flush)
  213. {
  214. $nextSendTime = time() + 10;
  215. $smcFunc['db_query']('', '
  216. UPDATE {db_prefix}settings
  217. SET value = {string:nextSendTime}
  218. WHERE variable = {string:mail_next_send}
  219. AND value = {string:no_outstanding}',
  220. array(
  221. 'nextSendTime' => $nextSendTime,
  222. 'mail_next_send' => 'mail_next_send',
  223. 'no_outstanding' => '0',
  224. )
  225. );
  226. return true;
  227. }
  228. // Ensure we tell obExit to flush.
  229. $context['flush_mail'] = true;
  230. foreach ($to_array as $to)
  231. {
  232. // Will this insert go over MySQL's limit?
  233. $this_insert_len = strlen($to) + strlen($message) + strlen($headers) + 700;
  234. // Insert limit of 1M (just under the safety) is reached?
  235. if ($this_insert_len + $cur_insert_len > 1000000)
  236. {
  237. // Flush out what we have so far.
  238. $smcFunc['db_insert']('',
  239. '{db_prefix}mail_queue',
  240. array(
  241. 'time_sent' => 'int', 'recipient' => 'string-255', 'body' => 'string', 'subject' => 'string-255',
  242. 'headers' => 'string-65534', 'send_html' => 'int', 'priority' => 'int', 'private' => 'int',
  243. ),
  244. $cur_insert,
  245. array('id_mail')
  246. );
  247. // Clear this out.
  248. $cur_insert = array();
  249. $cur_insert_len = 0;
  250. }
  251. // Now add the current insert to the array...
  252. $cur_insert[] = array(time(), (string) $to, (string) $message, (string) $subject, (string) $headers, ($send_html ? 1 : 0), $priority, (int) $is_private);
  253. $cur_insert_len += $this_insert_len;
  254. }
  255. // If they are using SSI there is a good chance obExit will never be called. So lets be nice and flush it for them.
  256. if (ELKARTE === 'SSI')
  257. return AddMailQueue(true);
  258. return true;
  259. }
  260. /**
  261. * Prepare text strings for sending as email body or header.
  262. * In case there are higher ASCII characters in the given string, this
  263. * function will attempt the transport method 'quoted-printable'.
  264. * Otherwise the transport method '7bit' is used.
  265. *
  266. * @param string $string
  267. * @param bool $with_charset = true
  268. * @param bool $hotmail_fix = false, with hotmail_fix set all higher ASCII
  269. * characters are converted to HTML entities to assure proper display of the mail
  270. * @param $line_break
  271. * @param string $custom_charset = null, if set, it uses this character set
  272. * @return array an array containing the character set, the converted string and the transport method.
  273. */
  274. function mimespecialchars($string, $with_charset = true, $hotmail_fix = false, $line_break = "\r\n", $custom_charset = null)
  275. {
  276. global $context;
  277. $charset = $custom_charset !== null ? $custom_charset : 'UTF-8';
  278. // This is the fun part....
  279. if (preg_match_all('~&#(\d{3,8});~', $string, $matches) !== 0 && !$hotmail_fix)
  280. {
  281. // Let's, for now, assume there are only &#021;'ish characters.
  282. $simple = true;
  283. foreach ($matches[1] as $entity)
  284. if ($entity > 128)
  285. $simple = false;
  286. unset($matches);
  287. if ($simple)
  288. $string = preg_replace('~&#(\d{3,8});~e', 'chr(\'$1\')', $string);
  289. else
  290. {
  291. $string = preg_replace_callback('~&#(\d{3,8});~', 'fixchar__callback', $string);
  292. // Unicode, baby.
  293. $charset = 'UTF-8';
  294. }
  295. }
  296. // Convert all special characters to HTML entities...just for Hotmail :-\
  297. if ($hotmail_fix)
  298. {
  299. //@todo ... another replaceEntities ?
  300. $entityConvert = create_function('$c', '
  301. if (strlen($c) === 1 && ord($c[0]) <= 0x7F)
  302. return $c;
  303. elseif (strlen($c) === 2 && ord($c[0]) >= 0xC0 && ord($c[0]) <= 0xDF)
  304. return "&#" . (((ord($c[0]) ^ 0xC0) << 6) + (ord($c[1]) ^ 0x80)) . ";";
  305. elseif (strlen($c) === 3 && ord($c[0]) >= 0xE0 && ord($c[0]) <= 0xEF)
  306. return "&#" . (((ord($c[0]) ^ 0xE0) << 12) + ((ord($c[1]) ^ 0x80) << 6) + (ord($c[2]) ^ 0x80)) . ";";
  307. elseif (strlen($c) === 4 && ord($c[0]) >= 0xF0 && ord($c[0]) <= 0xF7)
  308. return "&#" . (((ord($c[0]) ^ 0xF0) << 18) + ((ord($c[1]) ^ 0x80) << 12) + ((ord($c[2]) ^ 0x80) << 6) + (ord($c[3]) ^ 0x80)) . ";";
  309. else
  310. return "";');
  311. // Convert all 'special' characters to HTML entities.
  312. return array($charset, preg_replace('~([\x80-\x{10FFFF}])~eu', '$entityConvert(\'\1\')', $string), '7bit');
  313. }
  314. // We don't need to mess with the subject line if no special characters were in it..
  315. elseif (!$hotmail_fix && preg_match('~([^\x09\x0A\x0D\x20-\x7F])~', $string) === 1)
  316. {
  317. // Base64 encode.
  318. $string = base64_encode($string);
  319. // Show the characterset and the transfer-encoding for header strings.
  320. if ($with_charset)
  321. $string = '=?' . $charset . '?B?' . $string . '?=';
  322. // Break it up in lines (mail body).
  323. else
  324. $string = chunk_split($string, 76, $line_break);
  325. return array($charset, $string, 'base64');
  326. }
  327. else
  328. return array($charset, $string, '7bit');
  329. }
  330. /**
  331. * Sends mail, like mail() but over SMTP.
  332. * It expects no slashes or entities.
  333. * @internal
  334. *
  335. * @param array $mail_to_array - array of strings (email addresses)
  336. * @param string $subject email subject
  337. * @param string $message email message
  338. * @param string $headers
  339. * @return boolean whether it sent or not.
  340. */
  341. function smtp_mail($mail_to_array, $subject, $message, $headers)
  342. {
  343. global $modSettings, $webmaster_email, $txt;
  344. $modSettings['smtp_host'] = trim($modSettings['smtp_host']);
  345. // Try POP3 before SMTP?
  346. // @todo There's no interface for this yet.
  347. if ($modSettings['mail_type'] == 2 && $modSettings['smtp_username'] != '' && $modSettings['smtp_password'] != '')
  348. {
  349. $socket = fsockopen($modSettings['smtp_host'], 110, $errno, $errstr, 2);
  350. if (!$socket && (substr($modSettings['smtp_host'], 0, 5) == 'smtp.' || substr($modSettings['smtp_host'], 0, 11) == 'ssl://smtp.'))
  351. $socket = fsockopen(strtr($modSettings['smtp_host'], array('smtp.' => 'pop.')), 110, $errno, $errstr, 2);
  352. if ($socket)
  353. {
  354. fgets($socket, 256);
  355. fputs($socket, 'USER ' . $modSettings['smtp_username'] . "\r\n");
  356. fgets($socket, 256);
  357. fputs($socket, 'PASS ' . base64_decode($modSettings['smtp_password']) . "\r\n");
  358. fgets($socket, 256);
  359. fputs($socket, 'QUIT' . "\r\n");
  360. fclose($socket);
  361. }
  362. }
  363. // Try to connect to the SMTP server... if it doesn't exist, only wait three seconds.
  364. if (!$socket = fsockopen($modSettings['smtp_host'], empty($modSettings['smtp_port']) ? 25 : $modSettings['smtp_port'], $errno, $errstr, 3))
  365. {
  366. // Maybe we can still save this? The port might be wrong.
  367. if (substr($modSettings['smtp_host'], 0, 4) == 'ssl:' && (empty($modSettings['smtp_port']) || $modSettings['smtp_port'] == 25))
  368. {
  369. if ($socket = fsockopen($modSettings['smtp_host'], 465, $errno, $errstr, 3))
  370. log_error($txt['smtp_port_ssl']);
  371. }
  372. // Unable to connect! Don't show any error message, but just log one and try to continue anyway.
  373. if (!$socket)
  374. {
  375. log_error($txt['smtp_no_connect'] . ': ' . $errno . ' : ' . $errstr);
  376. return false;
  377. }
  378. }
  379. // Wait for a response of 220, without "-" continuer.
  380. if (!server_parse(null, $socket, '220'))
  381. return false;
  382. if ($modSettings['mail_type'] == 1 && $modSettings['smtp_username'] != '' && $modSettings['smtp_password'] != '')
  383. {
  384. // @todo These should send the CURRENT server's name, not the mail server's!
  385. // EHLO could be understood to mean encrypted hello...
  386. if (server_parse('EHLO ' . $modSettings['smtp_host'], $socket, null) == '250')
  387. {
  388. if (!server_parse('AUTH LOGIN', $socket, '334'))
  389. return false;
  390. // Send the username and password, encoded.
  391. if (!server_parse(base64_encode($modSettings['smtp_username']), $socket, '334'))
  392. return false;
  393. // The password is already encoded ;)
  394. if (!server_parse($modSettings['smtp_password'], $socket, '235'))
  395. return false;
  396. }
  397. elseif (!server_parse('HELO ' . $modSettings['smtp_host'], $socket, '250'))
  398. return false;
  399. }
  400. else
  401. {
  402. // Just say "helo".
  403. if (!server_parse('HELO ' . $modSettings['smtp_host'], $socket, '250'))
  404. return false;
  405. }
  406. // Fix the message for any lines beginning with a period! (the first is ignored, you see.)
  407. $message = strtr($message, array("\r\n" . '.' => "\r\n" . '..'));
  408. // !! Theoretically, we should be able to just loop the RCPT TO.
  409. $mail_to_array = array_values($mail_to_array);
  410. foreach ($mail_to_array as $i => $mail_to)
  411. {
  412. // Reset the connection to send another email.
  413. if ($i != 0)
  414. {
  415. if (!server_parse('RSET', $socket, '250'))
  416. return false;
  417. }
  418. // From, to, and then start the data...
  419. if (!server_parse('MAIL FROM: <' . (empty($modSettings['mail_from']) ? $webmaster_email : $modSettings['mail_from']) . '>', $socket, '250'))
  420. return false;
  421. if (!server_parse('RCPT TO: <' . $mail_to . '>', $socket, '250'))
  422. return false;
  423. if (!server_parse('DATA', $socket, '354'))
  424. return false;
  425. fputs($socket, 'Subject: ' . $subject . "\r\n");
  426. if (strlen($mail_to) > 0)
  427. fputs($socket, 'To: <' . $mail_to . '>' . "\r\n");
  428. fputs($socket, $headers . "\r\n\r\n");
  429. fputs($socket, $message . "\r\n");
  430. // Send a ., or in other words "end of data".
  431. if (!server_parse('.', $socket, '250'))
  432. return false;
  433. // Almost done, almost done... don't stop me just yet!
  434. @set_time_limit(300);
  435. if (function_exists('apache_reset_timeout'))
  436. @apache_reset_timeout();
  437. }
  438. fputs($socket, 'QUIT' . "\r\n");
  439. fclose($socket);
  440. return true;
  441. }
  442. /**
  443. * Parse a message to the SMTP server.
  444. * Sends the specified message to the server, and checks for the
  445. * expected response.
  446. * @internal
  447. *
  448. * @param string $message - the message to send
  449. * @param resource $socket - socket to send on
  450. * @param string $response - the expected response code
  451. * @return whether it responded as such.
  452. */
  453. function server_parse($message, $socket, $response)
  454. {
  455. global $txt;
  456. if ($message !== null)
  457. fputs($socket, $message . "\r\n");
  458. // No response yet.
  459. $server_response = '';
  460. while (substr($server_response, 3, 1) != ' ')
  461. if (!($server_response = fgets($socket, 256)))
  462. {
  463. // @todo Change this message to reflect that it may mean bad user/password/server issues/etc.
  464. log_error($txt['smtp_bad_response']);
  465. return false;
  466. }
  467. if ($response === null)
  468. return substr($server_response, 0, 3);
  469. if (substr($server_response, 0, 3) != $response)
  470. {
  471. log_error($txt['smtp_error'] . $server_response);
  472. return false;
  473. }
  474. return true;
  475. }
  476. /**
  477. * Load a template from EmailTemplates language file.
  478. *
  479. * @param string $template
  480. * @param array $replacements = array()
  481. * @param string $lang = ''
  482. * @param bool $loadLang = true
  483. */
  484. function loadEmailTemplate($template, $replacements = array(), $lang = '', $loadLang = true)
  485. {
  486. global $txt, $mbname, $scripturl, $settings, $user_info;
  487. // First things first, load up the email templates language file, if we need to.
  488. if ($loadLang)
  489. loadLanguage('EmailTemplates', $lang);
  490. if (!isset($txt[$template . '_subject']) || !isset($txt[$template . '_body']))
  491. fatal_lang_error('email_no_template', 'template', array($template));
  492. $ret = array(
  493. 'subject' => $txt[$template . '_subject'],
  494. 'body' => $txt[$template . '_body'],
  495. );
  496. // Add in the default replacements.
  497. $replacements += array(
  498. 'FORUMNAME' => $mbname,
  499. 'SCRIPTURL' => $scripturl,
  500. 'THEMEURL' => $settings['theme_url'],
  501. 'IMAGESURL' => $settings['images_url'],
  502. 'DEFAULT_THEMEURL' => $settings['default_theme_url'],
  503. 'REGARDS' => $txt['regards_team'],
  504. );
  505. // Split the replacements up into two arrays, for use with str_replace
  506. $find = array();
  507. $replace = array();
  508. foreach ($replacements as $f => $r)
  509. {
  510. $find[] = '{' . $f . '}';
  511. $replace[] = $r;
  512. }
  513. // Do the variable replacements.
  514. $ret['subject'] = str_replace($find, $replace, $ret['subject']);
  515. $ret['body'] = str_replace($find, $replace, $ret['body']);
  516. // Now deal with the {USER.variable} items.
  517. $ret['subject'] = preg_replace_callback('~{USER.([^}]+)}~', 'user_info_callback', $ret['subject']);
  518. $ret['body'] = preg_replace_callback('~{USER.([^}]+)}~', 'user_info_callback', $ret['body']);
  519. // Finally return the email to the caller so they can send it out.
  520. return $ret;
  521. }
  522. /**
  523. * Prepare subject and message of an email for the preview box
  524. * Used in ComposeMailing and RetrievePreview (Xml.controller.php)
  525. */
  526. function prepareMailingForPreview()
  527. {
  528. global $context, $smcFunc, $modSettings, $scripturl, $user_info, $txt;
  529. loadLanguage('Errors');
  530. $processing = array('preview_subject' => 'subject', 'preview_message' => 'message');
  531. // Use the default time format.
  532. $user_info['time_format'] = $modSettings['time_format'];
  533. $variables = array(
  534. '{$board_url}',
  535. '{$current_time}',
  536. '{$latest_member.link}',
  537. '{$latest_member.id}',
  538. '{$latest_member.name}'
  539. );
  540. $html = $context['send_html'];
  541. // We might need this in a bit
  542. $cleanLatestMember = empty($context['send_html']) || $context['send_pm'] ? un_htmlspecialchars($modSettings['latestRealName']) : $modSettings['latestRealName'];
  543. foreach ($processing as $key => $post)
  544. {
  545. $context[$key] = !empty($_REQUEST[$post]) ? $_REQUEST[$post] : '';
  546. if (empty($context[$key]) && empty($_REQUEST['xml']))
  547. $context['post_error']['messages'][] = $txt['error_no_' . $post];
  548. elseif (!empty($_REQUEST['xml']))
  549. continue;
  550. preparsecode($context[$key]);
  551. if ($html)
  552. {
  553. $enablePostHTML = $modSettings['enablePostHTML'];
  554. $modSettings['enablePostHTML'] = $context['send_html'];
  555. $context[$key] = parse_bbc($context[$key]);
  556. $modSettings['enablePostHTML'] = $enablePostHTML;
  557. }
  558. // Replace in all the standard things.
  559. $context[$key] = str_replace($variables,
  560. array(
  561. !empty($context['send_html']) ? '<a href="' . $scripturl . '">' . $scripturl . '</a>' : $scripturl,
  562. timeformat(forum_time(), false),
  563. !empty($context['send_html']) ? '<a href="' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . '">' . $cleanLatestMember . '</a>' : ($context['send_pm'] ? '[url=' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . ']' . $cleanLatestMember . '[/url]' : $cleanLatestMember),
  564. $modSettings['latestMember'],
  565. $cleanLatestMember
  566. ), $context[$key]);
  567. }
  568. }
  569. /**
  570. * Callback function for loademaitemplate on subject and body
  571. * Uses capture group 1 in array
  572. *
  573. * @param type $matches
  574. * @return string
  575. */
  576. function user_info_callback($matches)
  577. {
  578. global $user_info;
  579. if (empty($matches[1]))
  580. return '';
  581. $use_ref = true;
  582. $ref = &$user_info;
  583. foreach (explode('.', $matches[1]) as $index)
  584. {
  585. if ($use_ref && isset($ref[$index]))
  586. $ref = &$ref[$index];
  587. else
  588. {
  589. $use_ref = false;
  590. break;
  591. }
  592. }
  593. return $use_ref ? $ref : $matches[0];
  594. }