Guest User

Untitled

a guest
Feb 20th, 2017
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 45.28 KB | None | 0 0
  1. <?php
  2. /**
  3. *
  4. * This file is part of the phpBB Forum Software package.
  5. *
  6. * @copyright (c) phpBB Limited <https://www.phpbb.com>
  7. * @license GNU General Public License, version 2 (GPL-2.0)
  8. *
  9. * For full copyright and license information, please see
  10. * the docs/CREDITS.txt file.
  11. *
  12. */
  13.  
  14. /**
  15. * @ignore
  16. */
  17. if (!defined('IN_PHPBB'))
  18. {
  19. exit;
  20. }
  21.  
  22. /**
  23. * Messenger
  24. */
  25. class messenger
  26. {
  27. var $msg, $extra_headers, $replyto, $from, $subject;
  28. var $addresses = array();
  29.  
  30. var $mail_priority = MAIL_NORMAL_PRIORITY;
  31. var $use_queue = true;
  32.  
  33. /** @var \phpbb\template\template */
  34. protected $template;
  35.  
  36. var $eol = "\n";
  37.  
  38. /**
  39. * Constructor
  40. */
  41. function messenger($use_queue = true)
  42. {
  43. global $config;
  44.  
  45. $this->use_queue = (!$config['email_package_size']) ? false : $use_queue;
  46. $this->subject = '';
  47.  
  48. // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac)
  49. $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL;
  50. $this->eol = (!$this->eol) ? "\n" : $this->eol;
  51. }
  52.  
  53. /**
  54. * Resets all the data (address, template file, etc etc) to default
  55. */
  56. function reset()
  57. {
  58. $this->addresses = $this->extra_headers = array();
  59. $this->msg = $this->replyto = $this->from = '';
  60. $this->mail_priority = MAIL_NORMAL_PRIORITY;
  61. }
  62.  
  63. /**
  64. * Set addresses for to/im as available
  65. *
  66. * @param array $user User row
  67. */
  68. function set_addresses($user)
  69. {
  70. if (isset($user['user_email']) && $user['user_email'])
  71. {
  72. $this->to($user['user_email'], (isset($user['username']) ? $user['username'] : ''));
  73. }
  74.  
  75. if (isset($user['user_jabber']) && $user['user_jabber'])
  76. {
  77. $this->im($user['user_jabber'], (isset($user['username']) ? $user['username'] : ''));
  78. }
  79. }
  80.  
  81. /**
  82. * Sets an email address to send to
  83. */
  84. function to($address, $realname = '')
  85. {
  86. global $config;
  87.  
  88. if (!trim($address))
  89. {
  90. return;
  91. }
  92.  
  93. $pos = isset($this->addresses['to']) ? sizeof($this->addresses['to']) : 0;
  94.  
  95. $this->addresses['to'][$pos]['email'] = trim($address);
  96.  
  97. // If empty sendmail_path on windows, PHP changes the to line
  98. if (!$config['smtp_delivery'] && DIRECTORY_SEPARATOR == '\\')
  99. {
  100. $this->addresses['to'][$pos]['name'] = '';
  101. }
  102. else
  103. {
  104. $this->addresses['to'][$pos]['name'] = trim($realname);
  105. }
  106. }
  107.  
  108. /**
  109. * Sets an cc address to send to
  110. */
  111. function cc($address, $realname = '')
  112. {
  113. if (!trim($address))
  114. {
  115. return;
  116. }
  117.  
  118. $pos = isset($this->addresses['cc']) ? sizeof($this->addresses['cc']) : 0;
  119. $this->addresses['cc'][$pos]['email'] = trim($address);
  120. $this->addresses['cc'][$pos]['name'] = trim($realname);
  121. }
  122.  
  123. /**
  124. * Sets an bcc address to send to
  125. */
  126. function bcc($address, $realname = '')
  127. {
  128. if (!trim($address))
  129. {
  130. return;
  131. }
  132.  
  133. $pos = isset($this->addresses['bcc']) ? sizeof($this->addresses['bcc']) : 0;
  134. $this->addresses['bcc'][$pos]['email'] = trim($address);
  135. $this->addresses['bcc'][$pos]['name'] = trim($realname);
  136. }
  137.  
  138. /**
  139. * Sets a im contact to send to
  140. */
  141. function im($address, $realname = '')
  142. {
  143. // IM-Addresses could be empty
  144. if (!trim($address))
  145. {
  146. return;
  147. }
  148.  
  149. $pos = isset($this->addresses['im']) ? sizeof($this->addresses['im']) : 0;
  150. $this->addresses['im'][$pos]['uid'] = trim($address);
  151. $this->addresses['im'][$pos]['name'] = trim($realname);
  152. }
  153.  
  154. /**
  155. * Set the reply to address
  156. */
  157. function replyto($address)
  158. {
  159. $this->replyto = trim($address);
  160. }
  161.  
  162. /**
  163. * Set the from address
  164. */
  165. function from($address)
  166. {
  167. $this->from = trim($address);
  168. }
  169.  
  170. /**
  171. * set up subject for mail
  172. */
  173. function subject($subject = '')
  174. {
  175. $this->subject = trim($subject);
  176. }
  177.  
  178. /**
  179. * set up extra mail headers
  180. */
  181. function headers($headers)
  182. {
  183. $this->extra_headers[] = trim($headers);
  184. }
  185.  
  186. /**
  187. * Adds X-AntiAbuse headers
  188. *
  189. * @param array $config Configuration array
  190. * @param user $user A user object
  191. *
  192. * @return null
  193. */
  194. function anti_abuse_headers($config, $user)
  195. {
  196. $this->headers('X-AntiAbuse: Board servername - ' . mail_encode($config['server_name']));
  197. $this->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']);
  198. $this->headers('X-AntiAbuse: Username - ' . mail_encode($user->data['username']));
  199. $this->headers('X-AntiAbuse: User IP - ' . $user->ip);
  200. }
  201.  
  202. /**
  203. * Set the email priority
  204. */
  205. function set_mail_priority($priority = MAIL_NORMAL_PRIORITY)
  206. {
  207. $this->mail_priority = $priority;
  208. }
  209.  
  210. /**
  211. * Set email template to use
  212. */
  213. function template($template_file, $template_lang = '', $template_path = '', $template_dir_prefix = '')
  214. {
  215. global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager;
  216.  
  217. $template_dir_prefix = (!$template_dir_prefix || $template_dir_prefix[0] === '/') ? $template_dir_prefix : '/' . $template_dir_prefix;
  218.  
  219. $this->setup_template();
  220.  
  221. if (!trim($template_file))
  222. {
  223. trigger_error('No template file for emailing set.', E_USER_ERROR);
  224. }
  225.  
  226. if (!trim($template_lang))
  227. {
  228. // fall back to board default language if the user's language is
  229. // missing $template_file. If this does not exist either,
  230. // $this->template->set_filenames will do a trigger_error
  231. $template_lang = basename($config['default_lang']);
  232. }
  233.  
  234. if ($template_path)
  235. {
  236. $template_paths = array(
  237. $template_path . $template_dir_prefix,
  238. );
  239. }
  240. else
  241. {
  242. $template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
  243. $template_path .= $template_lang . '/email';
  244.  
  245. $template_paths = array(
  246. $template_path . $template_dir_prefix,
  247. );
  248.  
  249. // we can only specify default language fallback when the path is not a custom one for which we
  250. // do not know the default language alternative
  251. if ($template_lang !== basename($config['default_lang']))
  252. {
  253. $fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
  254. $fallback_template_path .= basename($config['default_lang']) . '/email';
  255.  
  256. $template_paths[] = $fallback_template_path . $template_dir_prefix;
  257. }
  258. }
  259.  
  260. $this->set_template_paths(array(
  261. array(
  262. 'name' => $template_lang . '_email',
  263. 'ext_path' => 'language/' . $template_lang . '/email' . $template_dir_prefix,
  264. ),
  265. ), $template_paths);
  266.  
  267. $this->template->set_filenames(array(
  268. 'body' => $template_file . '.txt',
  269. ));
  270.  
  271. return true;
  272. }
  273.  
  274. /**
  275. * assign variables to email template
  276. */
  277. function assign_vars($vars)
  278. {
  279. $this->setup_template();
  280.  
  281. $this->template->assign_vars($vars);
  282. }
  283.  
  284. function assign_block_vars($blockname, $vars)
  285. {
  286. $this->setup_template();
  287.  
  288. $this->template->assign_block_vars($blockname, $vars);
  289. }
  290.  
  291. /**
  292. * Send the mail out to the recipients set previously in var $this->addresses
  293. */
  294. function send($method = NOTIFY_EMAIL, $break = false)
  295. {
  296. global $config, $user;
  297.  
  298. // We add some standard variables we always use, no need to specify them always
  299. $this->assign_vars(array(
  300. 'U_BOARD' => generate_board_url(),
  301. 'EMAIL_SIG' => str_replace('<br />', "\n", "-- \n" . htmlspecialchars_decode($config['board_email_sig'])),
  302. 'SITENAME' => htmlspecialchars_decode($config['sitename']),
  303. ));
  304.  
  305. // Parse message through template
  306. $this->msg = trim($this->template->assign_display('body'));
  307.  
  308. // Because we use \n for newlines in the body message we need to fix line encoding errors for those admins who uploaded email template files in the wrong encoding
  309. $this->msg = str_replace("\r\n", "\n", $this->msg);
  310.  
  311. // We now try and pull a subject from the email body ... if it exists,
  312. // do this here because the subject may contain a variable
  313. $drop_header = '';
  314. $match = array();
  315. if (preg_match('#^(Subject:(.*?))$#m', $this->msg, $match))
  316. {
  317. $this->subject = (trim($match[2]) != '') ? trim($match[2]) : (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']);
  318. $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#');
  319. }
  320. else
  321. {
  322. $this->subject = (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']);
  323. }
  324.  
  325. if ($drop_header)
  326. {
  327. $this->msg = trim(preg_replace('#' . $drop_header . '#s', '', $this->msg));
  328. }
  329.  
  330. if ($break)
  331. {
  332. return true;
  333. }
  334.  
  335. switch ($method)
  336. {
  337. case NOTIFY_EMAIL:
  338. $result = $this->msg_email();
  339. break;
  340.  
  341. case NOTIFY_IM:
  342. $result = $this->msg_jabber();
  343. break;
  344.  
  345. case NOTIFY_BOTH:
  346. $result = $this->msg_email();
  347. $this->msg_jabber();
  348. break;
  349. }
  350.  
  351. $this->reset();
  352. return $result;
  353. }
  354.  
  355. /**
  356. * Add error message to log
  357. */
  358. function error($type, $msg)
  359. {
  360. global $user, $phpEx, $phpbb_root_path, $config, $request;
  361.  
  362. // Session doesn't exist, create it
  363. if (!isset($user->session_id) || $user->session_id === '')
  364. {
  365. $user->session_begin();
  366. }
  367.  
  368. $calling_page = htmlspecialchars_decode($request->server('PHP_SELF'));
  369.  
  370. $message = '';
  371. switch ($type)
  372. {
  373. case 'EMAIL':
  374. $message = '<strong>EMAIL/' . (($config['smtp_delivery']) ? 'SMTP' : 'PHP/' . $config['email_function_name'] . '()') . '</strong>';
  375. break;
  376.  
  377. default:
  378. $message = "<strong>$type</strong>";
  379. break;
  380. }
  381.  
  382. $message .= '<br /><em>' . htmlspecialchars($calling_page) . '</em><br /><br />' . $msg . '<br />';
  383. add_log('critical', 'LOG_ERROR_' . $type, $message);
  384. }
  385.  
  386. /**
  387. * Save to queue
  388. */
  389. function save_queue()
  390. {
  391. global $config;
  392.  
  393. if ($config['email_package_size'] && $this->use_queue && !empty($this->queue))
  394. {
  395. $this->queue->save();
  396. return;
  397. }
  398. }
  399.  
  400. /**
  401. * Generates a valid message id to be used in emails
  402. *
  403. * @return string message id
  404. */
  405. function generate_message_id()
  406. {
  407. global $config, $request;
  408.  
  409. $domain = ($config['server_name']) ?: $request->server('SERVER_NAME', 'phpbb.generated');
  410.  
  411. return md5(unique_id(time())) . '@' . $domain;
  412. }
  413.  
  414. /**
  415. * Return email header
  416. */
  417. function build_header($to, $cc, $bcc)
  418. {
  419. global $config;
  420.  
  421. // We could use keys here, but we won't do this for 3.0.x to retain backwards compatibility
  422. $headers = array();
  423.  
  424. $headers[] = 'From: ' . $this->from;
  425.  
  426. if ($cc)
  427. {
  428. $headers[] = 'Cc: ' . $cc;
  429. }
  430.  
  431. if ($bcc)
  432. {
  433. $headers[] = 'Bcc: ' . $bcc;
  434. }
  435.  
  436. $headers[] = 'Reply-To: ' . $this->replyto;
  437. $headers[] = 'Return-Path: <' . $config['board_email'] . '>';
  438. $headers[] = 'Sender: <' . $config['board_email'] . '>';
  439. $headers[] = 'MIME-Version: 1.0';
  440. $headers[] = 'Message-ID: <' . $this->generate_message_id() . '>';
  441. $headers[] = 'Date: ' . date('r', time());
  442. $headers[] = 'Content-Type: text/plain; charset=UTF-8'; // format=flowed
  443. $headers[] = 'Content-Transfer-Encoding: 8bit'; // 7bit
  444.  
  445. $headers[] = 'X-Priority: ' . $this->mail_priority;
  446. $headers[] = 'X-MSMail-Priority: ' . (($this->mail_priority == MAIL_LOW_PRIORITY) ? 'Low' : (($this->mail_priority == MAIL_NORMAL_PRIORITY) ? 'Normal' : 'High'));
  447. $headers[] = 'X-Mailer: phpBB3';
  448. $headers[] = 'X-MimeOLE: phpBB3';
  449. $headers[] = 'X-phpBB-Origin: phpbb://' . str_replace(array('http://', 'https://'), array('', ''), generate_board_url());
  450.  
  451. if (sizeof($this->extra_headers))
  452. {
  453. $headers = array_merge($headers, $this->extra_headers);
  454. }
  455.  
  456. return $headers;
  457. }
  458.  
  459. /**
  460. * Send out emails
  461. */
  462. function msg_email()
  463. {
  464. global $config, $user;
  465.  
  466. if (empty($config['email_enable']))
  467. {
  468. return false;
  469. }
  470.  
  471. // Addresses to send to?
  472. if (empty($this->addresses) || (empty($this->addresses['to']) && empty($this->addresses['cc']) && empty($this->addresses['bcc'])))
  473. {
  474. // Send was successful. ;)
  475. return true;
  476. }
  477.  
  478. $use_queue = false;
  479. if ($config['email_package_size'] && $this->use_queue)
  480. {
  481. if (empty($this->queue))
  482. {
  483. $this->queue = new queue();
  484. $this->queue->init('email', $config['email_package_size']);
  485. }
  486. $use_queue = true;
  487. }
  488.  
  489. $contact_name = htmlspecialchars_decode($config['board_contact_name']);
  490. $board_contact = (($contact_name !== '') ? '"' . mail_encode($contact_name) . '" ' : '') . '<' . $config['board_contact'] . '>';
  491.  
  492. if (empty($this->replyto))
  493. {
  494. $this->replyto = $board_contact;
  495. }
  496.  
  497. if (empty($this->from))
  498. {
  499. $this->from = $board_contact;
  500. }
  501.  
  502. $encode_eol = ($config['smtp_delivery']) ? "\r\n" : $this->eol;
  503.  
  504. // Build to, cc and bcc strings
  505. $to = $cc = $bcc = '';
  506. foreach ($this->addresses as $type => $address_ary)
  507. {
  508. if ($type == 'im')
  509. {
  510. continue;
  511. }
  512.  
  513. foreach ($address_ary as $which_ary)
  514. {
  515. ${$type} .= ((${$type} != '') ? ', ' : '') . (($which_ary['name'] != '') ? mail_encode($which_ary['name'], $encode_eol) . ' <' . $which_ary['email'] . '>' : $which_ary['email']);
  516. }
  517. }
  518.  
  519. // Build header
  520. $headers = $this->build_header($to, $cc, $bcc);
  521.  
  522. // Send message ...
  523. if (!$use_queue)
  524. {
  525. $mail_to = ($to == '') ? 'undisclosed-recipients:;' : $to;
  526. $err_msg = '';
  527.  
  528. if ($config['smtp_delivery'])
  529. {
  530. $result = smtpmail($this->addresses, mail_encode($this->subject), wordwrap(utf8_wordwrap($this->msg), 997, "\n", true), $err_msg, $headers);
  531. }
  532. else
  533. {
  534. $result = phpbb_mail($mail_to, $this->subject, $this->msg, $headers, $this->eol, $err_msg);
  535. }
  536.  
  537. if (!$result)
  538. {
  539. $this->error('EMAIL', $err_msg);
  540. return false;
  541. }
  542. }
  543. else
  544. {
  545. $this->queue->put('email', array(
  546. 'to' => $to,
  547. 'addresses' => $this->addresses,
  548. 'subject' => $this->subject,
  549. 'msg' => $this->msg,
  550. 'headers' => $headers)
  551. );
  552. }
  553.  
  554. return true;
  555. }
  556.  
  557. /**
  558. * Send jabber message out
  559. */
  560. function msg_jabber()
  561. {
  562. global $config, $db, $user, $phpbb_root_path, $phpEx;
  563.  
  564. if (empty($config['jab_enable']) || empty($config['jab_host']) || empty($config['jab_username']) || empty($config['jab_password']))
  565. {
  566. return false;
  567. }
  568.  
  569. if (empty($this->addresses['im']))
  570. {
  571. // Send was successful. ;)
  572. return true;
  573. }
  574.  
  575. $use_queue = false;
  576. if ($config['jab_package_size'] && $this->use_queue)
  577. {
  578. if (empty($this->queue))
  579. {
  580. $this->queue = new queue();
  581. $this->queue->init('jabber', $config['jab_package_size']);
  582. }
  583. $use_queue = true;
  584. }
  585.  
  586. $addresses = array();
  587. foreach ($this->addresses['im'] as $type => $uid_ary)
  588. {
  589. $addresses[] = $uid_ary['uid'];
  590. }
  591. $addresses = array_unique($addresses);
  592.  
  593. if (!$use_queue)
  594. {
  595. include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx);
  596. $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], htmlspecialchars_decode($config['jab_password']), $config['jab_use_ssl']);
  597.  
  598. if (!$this->jabber->connect())
  599. {
  600. $this->error('JABBER', $user->lang['ERR_JAB_CONNECT'] . '<br />' . $this->jabber->get_log());
  601. return false;
  602. }
  603.  
  604. if (!$this->jabber->login())
  605. {
  606. $this->error('JABBER', $user->lang['ERR_JAB_AUTH'] . '<br />' . $this->jabber->get_log());
  607. return false;
  608. }
  609.  
  610. foreach ($addresses as $address)
  611. {
  612. $this->jabber->send_message($address, $this->msg, $this->subject);
  613. }
  614.  
  615. $this->jabber->disconnect();
  616. }
  617. else
  618. {
  619. $this->queue->put('jabber', array(
  620. 'addresses' => $addresses,
  621. 'subject' => $this->subject,
  622. 'msg' => $this->msg)
  623. );
  624. }
  625. unset($addresses);
  626. return true;
  627. }
  628.  
  629. /**
  630. * Setup template engine
  631. */
  632. protected function setup_template()
  633. {
  634. global $config, $phpbb_path_helper, $user, $phpbb_extension_manager;
  635.  
  636. if ($this->template instanceof \phpbb\template\template)
  637. {
  638. return;
  639. }
  640.  
  641. $this->template = new \phpbb\template\twig\twig($phpbb_path_helper, $config, $user, new \phpbb\template\context(), $phpbb_extension_manager);
  642. }
  643.  
  644. /**
  645. * Set template paths to load
  646. */
  647. protected function set_template_paths($path_name, $paths)
  648. {
  649. $this->setup_template();
  650.  
  651. $this->template->set_custom_style($path_name, $paths);
  652. }
  653. }
  654.  
  655. /**
  656. * handling email and jabber queue
  657. */
  658. class queue
  659. {
  660. var $data = array();
  661. var $queue_data = array();
  662. var $package_size = 0;
  663. var $cache_file = '';
  664. var $eol = "\n";
  665.  
  666. /**
  667. * constructor
  668. */
  669. function queue()
  670. {
  671. global $phpEx, $phpbb_root_path;
  672.  
  673. $this->data = array();
  674. $this->cache_file = "{$phpbb_root_path}cache/queue.$phpEx";
  675.  
  676. // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac)
  677. $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL;
  678. $this->eol = (!$this->eol) ? "\n" : $this->eol;
  679. }
  680.  
  681. /**
  682. * Init a queue object
  683. */
  684. function init($object, $package_size)
  685. {
  686. $this->data[$object] = array();
  687. $this->data[$object]['package_size'] = $package_size;
  688. $this->data[$object]['data'] = array();
  689. }
  690.  
  691. /**
  692. * Put object in queue
  693. */
  694. function put($object, $scope)
  695. {
  696. $this->data[$object]['data'][] = $scope;
  697. }
  698.  
  699. /**
  700. * Process queue
  701. * Using lock file
  702. */
  703. function process()
  704. {
  705. global $db, $config, $phpEx, $phpbb_root_path, $user;
  706.  
  707. $lock = new \phpbb\lock\flock($this->cache_file);
  708. $lock->acquire();
  709.  
  710. // avoid races, check file existence once
  711. $have_cache_file = file_exists($this->cache_file);
  712. if (!$have_cache_file || $config['last_queue_run'] > time() - $config['queue_interval'])
  713. {
  714. if (!$have_cache_file)
  715. {
  716. set_config('last_queue_run', time(), true);
  717. }
  718.  
  719. $lock->release();
  720. return;
  721. }
  722.  
  723. set_config('last_queue_run', time(), true);
  724.  
  725. include($this->cache_file);
  726.  
  727. foreach ($this->queue_data as $object => $data_ary)
  728. {
  729. @set_time_limit(0);
  730.  
  731. if (!isset($data_ary['package_size']))
  732. {
  733. $data_ary['package_size'] = 0;
  734. }
  735.  
  736. $package_size = $data_ary['package_size'];
  737. $num_items = (!$package_size || sizeof($data_ary['data']) < $package_size) ? sizeof($data_ary['data']) : $package_size;
  738.  
  739. /*
  740. * This code is commented out because it causes problems on some web hosts.
  741. * The core problem is rather restrictive email sending limits.
  742. * This code is nly useful if you have no such restrictions from the
  743. * web host and the package size setting is wrong.
  744.  
  745. // If the amount of emails to be sent is way more than package_size than we need to increase it to prevent backlogs...
  746. if (sizeof($data_ary['data']) > $package_size * 2.5)
  747. {
  748. $num_items = sizeof($data_ary['data']);
  749. }
  750. */
  751.  
  752. switch ($object)
  753. {
  754. case 'email':
  755. // Delete the email queued objects if mailing is disabled
  756. if (!$config['email_enable'])
  757. {
  758. unset($this->queue_data['email']);
  759. continue 2;
  760. }
  761. break;
  762.  
  763. case 'jabber':
  764. if (!$config['jab_enable'])
  765. {
  766. unset($this->queue_data['jabber']);
  767. continue 2;
  768. }
  769.  
  770. include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx);
  771. $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], htmlspecialchars_decode($config['jab_password']), $config['jab_use_ssl']);
  772.  
  773. if (!$this->jabber->connect())
  774. {
  775. $messenger = new messenger();
  776. $messenger->error('JABBER', $user->lang['ERR_JAB_CONNECT']);
  777. continue 2;
  778. }
  779.  
  780. if (!$this->jabber->login())
  781. {
  782. $messenger = new messenger();
  783. $messenger->error('JABBER', $user->lang['ERR_JAB_AUTH']);
  784. continue 2;
  785. }
  786.  
  787. break;
  788.  
  789. default:
  790. $lock->release();
  791. return;
  792. }
  793.  
  794. for ($i = 0; $i < $num_items; $i++)
  795. {
  796. // Make variables available...
  797. extract(array_shift($this->queue_data[$object]['data']));
  798.  
  799. switch ($object)
  800. {
  801. case 'email':
  802. $err_msg = '';
  803. $to = (!$to) ? 'undisclosed-recipients:;' : $to;
  804.  
  805. if ($config['smtp_delivery'])
  806. {
  807. $result = smtpmail($addresses, mail_encode($subject), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $err_msg, $headers);
  808. }
  809. else
  810. {
  811. $result = phpbb_mail($to, $subject, $msg, $headers, $this->eol, $err_msg);
  812. }
  813.  
  814. if (!$result)
  815. {
  816. $messenger = new messenger();
  817. $messenger->error('EMAIL', $err_msg);
  818. continue 2;
  819. }
  820. break;
  821.  
  822. case 'jabber':
  823. foreach ($addresses as $address)
  824. {
  825. if ($this->jabber->send_message($address, $msg, $subject) === false)
  826. {
  827. $messenger = new messenger();
  828. $messenger->error('JABBER', $this->jabber->get_log());
  829. continue 3;
  830. }
  831. }
  832. break;
  833. }
  834. }
  835.  
  836. // No more data for this object? Unset it
  837. if (!sizeof($this->queue_data[$object]['data']))
  838. {
  839. unset($this->queue_data[$object]);
  840. }
  841.  
  842. // Post-object processing
  843. switch ($object)
  844. {
  845. case 'jabber':
  846. // Hang about a couple of secs to ensure the messages are
  847. // handled, then disconnect
  848. $this->jabber->disconnect();
  849. break;
  850. }
  851. }
  852.  
  853. if (!sizeof($this->queue_data))
  854. {
  855. @unlink($this->cache_file);
  856. }
  857. else
  858. {
  859. if ($fp = @fopen($this->cache_file, 'wb'))
  860. {
  861. fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->queue_data), true) . ");\n\n?>");
  862. fclose($fp);
  863.  
  864. if (function_exists('opcache_invalidate'))
  865. {
  866. @opcache_invalidate($this->cache_file);
  867. }
  868.  
  869. phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
  870. }
  871. }
  872.  
  873. $lock->release();
  874. }
  875.  
  876. /**
  877. * Save queue
  878. */
  879. function save()
  880. {
  881. if (!sizeof($this->data))
  882. {
  883. return;
  884. }
  885.  
  886. $lock = new \phpbb\lock\flock($this->cache_file);
  887. $lock->acquire();
  888.  
  889. if (file_exists($this->cache_file))
  890. {
  891. include($this->cache_file);
  892.  
  893. foreach ($this->queue_data as $object => $data_ary)
  894. {
  895. if (isset($this->data[$object]) && sizeof($this->data[$object]))
  896. {
  897. $this->data[$object]['data'] = array_merge($data_ary['data'], $this->data[$object]['data']);
  898. }
  899. else
  900. {
  901. $this->data[$object]['data'] = $data_ary['data'];
  902. }
  903. }
  904. }
  905.  
  906. if ($fp = @fopen($this->cache_file, 'w'))
  907. {
  908. fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->data), true) . ");\n\n?>");
  909. fclose($fp);
  910.  
  911. if (function_exists('opcache_invalidate'))
  912. {
  913. @opcache_invalidate($this->cache_file);
  914. }
  915.  
  916. phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
  917.  
  918. $this->data = array();
  919. }
  920.  
  921. $lock->release();
  922. }
  923. }
  924.  
  925. /**
  926. * Replacement or substitute for PHP's mail command
  927. */
  928. function smtpmail($addresses, $subject, $message, &$err_msg, $headers = false)
  929. {
  930. global $config, $user;
  931.  
  932. // Fix any bare linefeeds in the message to make it RFC821 Compliant.
  933. $message = preg_replace("#(?<!\r)\n#si", "\r\n", $message);
  934.  
  935. if ($headers !== false)
  936. {
  937. if (!is_array($headers))
  938. {
  939. // Make sure there are no bare linefeeds in the headers
  940. $headers = preg_replace('#(?<!\r)\n#si', "\n", $headers);
  941. $headers = explode("\n", $headers);
  942. }
  943.  
  944. // Ok this is rather confusing all things considered,
  945. // but we have to grab bcc and cc headers and treat them differently
  946. // Something we really didn't take into consideration originally
  947. $headers_used = array();
  948.  
  949. foreach ($headers as $header)
  950. {
  951. if (strpos(strtolower($header), 'cc:') === 0 || strpos(strtolower($header), 'bcc:') === 0)
  952. {
  953. continue;
  954. }
  955. $headers_used[] = trim($header);
  956. }
  957.  
  958. $headers = chop(implode("\r\n", $headers_used));
  959. }
  960.  
  961. if (trim($subject) == '')
  962. {
  963. $err_msg = (isset($user->lang['NO_EMAIL_SUBJECT'])) ? $user->lang['NO_EMAIL_SUBJECT'] : 'No email subject specified';
  964. return false;
  965. }
  966.  
  967. if (trim($message) == '')
  968. {
  969. $err_msg = (isset($user->lang['NO_EMAIL_MESSAGE'])) ? $user->lang['NO_EMAIL_MESSAGE'] : 'Email message was blank';
  970. return false;
  971. }
  972.  
  973. $mail_rcpt = $mail_to = $mail_cc = array();
  974.  
  975. // Build correct addresses for RCPT TO command and the client side display (TO, CC)
  976. if (isset($addresses['to']) && sizeof($addresses['to']))
  977. {
  978. foreach ($addresses['to'] as $which_ary)
  979. {
  980. $mail_to[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>';
  981. $mail_rcpt['to'][] = '<' . trim($which_ary['email']) . '>';
  982. }
  983. }
  984.  
  985. if (isset($addresses['bcc']) && sizeof($addresses['bcc']))
  986. {
  987. foreach ($addresses['bcc'] as $which_ary)
  988. {
  989. $mail_rcpt['bcc'][] = '<' . trim($which_ary['email']) . '>';
  990. }
  991. }
  992.  
  993. if (isset($addresses['cc']) && sizeof($addresses['cc']))
  994. {
  995. foreach ($addresses['cc'] as $which_ary)
  996. {
  997. $mail_cc[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>';
  998. $mail_rcpt['cc'][] = '<' . trim($which_ary['email']) . '>';
  999. }
  1000. }
  1001.  
  1002. $smtp = new smtp_class();
  1003.  
  1004. $errno = 0;
  1005. $errstr = '';
  1006.  
  1007. $smtp->add_backtrace('Connecting to ' . $config['smtp_host'] . ':' . $config['smtp_port']);
  1008.  
  1009. // Ok we have error checked as much as we can to this point let's get on it already.
  1010. if (!class_exists('\phpbb\error_collector'))
  1011. {
  1012. global $phpbb_root_path, $phpEx;
  1013. include($phpbb_root_path . 'includes/error_collector.' . $phpEx);
  1014. }
  1015. $collector = new \phpbb\error_collector;
  1016. $collector->install();
  1017. $smtp->socket = fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 20);
  1018. $collector->uninstall();
  1019. $error_contents = $collector->format_errors();
  1020.  
  1021. if (!$smtp->socket)
  1022. {
  1023. if ($errstr)
  1024. {
  1025. $errstr = utf8_convert_message($errstr);
  1026. }
  1027.  
  1028. $err_msg = (isset($user->lang['NO_CONNECT_TO_SMTP_HOST'])) ? sprintf($user->lang['NO_CONNECT_TO_SMTP_HOST'], $errno, $errstr) : "Could not connect to smtp host : $errno : $errstr";
  1029. $err_msg .= ($error_contents) ? '<br /><br />' . htmlspecialchars($error_contents) : '';
  1030. return false;
  1031. }
  1032.  
  1033. // Wait for reply
  1034. if ($err_msg = $smtp->server_parse('220', __LINE__))
  1035. {
  1036. $smtp->close_session($err_msg);
  1037. return false;
  1038. }
  1039.  
  1040. // Let me in. This function handles the complete authentication process
  1041. if ($err_msg = $smtp->log_into_server($config['smtp_host'], $config['smtp_username'], htmlspecialchars_decode($config['smtp_password']), $config['smtp_auth_method']))
  1042. {
  1043. $smtp->close_session($err_msg);
  1044. return false;
  1045. }
  1046.  
  1047. // From this point onward most server response codes should be 250
  1048. // Specify who the mail is from....
  1049. $smtp->server_send('MAIL FROM:<' . $config['board_email'] . '>');
  1050. if ($err_msg = $smtp->server_parse('250', __LINE__))
  1051. {
  1052. $smtp->close_session($err_msg);
  1053. return false;
  1054. }
  1055.  
  1056. // Specify each user to send to and build to header.
  1057. $to_header = implode(', ', $mail_to);
  1058. $cc_header = implode(', ', $mail_cc);
  1059.  
  1060. // Now tell the MTA to send the Message to the following people... [TO, BCC, CC]
  1061. $rcpt = false;
  1062. foreach ($mail_rcpt as $type => $mail_to_addresses)
  1063. {
  1064. foreach ($mail_to_addresses as $mail_to_address)
  1065. {
  1066. // Add an additional bit of error checking to the To field.
  1067. if (preg_match('#[^ ]+\@[^ ]+#', $mail_to_address))
  1068. {
  1069. $smtp->server_send("RCPT TO:$mail_to_address");
  1070. if ($err_msg = $smtp->server_parse('250', __LINE__))
  1071. {
  1072. // We continue... if users are not resolved we do not care
  1073. if ($smtp->numeric_response_code != 550)
  1074. {
  1075. $smtp->close_session($err_msg);
  1076. return false;
  1077. }
  1078. }
  1079. else
  1080. {
  1081. $rcpt = true;
  1082. }
  1083. }
  1084. }
  1085. }
  1086.  
  1087. // We try to send messages even if a few people do not seem to have valid email addresses, but if no one has, we have to exit here.
  1088. if (!$rcpt)
  1089. {
  1090. $user->session_begin();
  1091. $err_msg .= '<br /><br />';
  1092. $err_msg .= (isset($user->lang['INVALID_EMAIL_LOG'])) ? sprintf($user->lang['INVALID_EMAIL_LOG'], htmlspecialchars($mail_to_address)) : '<strong>' . htmlspecialchars($mail_to_address) . '</strong> possibly an invalid email address?';
  1093. $smtp->close_session($err_msg);
  1094. return false;
  1095. }
  1096.  
  1097. // Ok now we tell the server we are ready to start sending data
  1098. $smtp->server_send('DATA');
  1099.  
  1100. // This is the last response code we look for until the end of the message.
  1101. if ($err_msg = $smtp->server_parse('354', __LINE__))
  1102. {
  1103. $smtp->close_session($err_msg);
  1104. return false;
  1105. }
  1106.  
  1107. // Send the Subject Line...
  1108. $smtp->server_send("Subject: $subject");
  1109.  
  1110. // Now the To Header.
  1111. $to_header = ($to_header == '') ? 'undisclosed-recipients:;' : $to_header;
  1112. $smtp->server_send("To: $to_header");
  1113.  
  1114. // Now the CC Header.
  1115. if ($cc_header != '')
  1116. {
  1117. $smtp->server_send("CC: $cc_header");
  1118. }
  1119.  
  1120. // Now any custom headers....
  1121. if ($headers !== false)
  1122. {
  1123. $smtp->server_send("$headers\r\n");
  1124. }
  1125.  
  1126. // Ok now we are ready for the message...
  1127. $smtp->server_send($message);
  1128.  
  1129. // Ok the all the ingredients are mixed in let's cook this puppy...
  1130. $smtp->server_send('.');
  1131. if ($err_msg = $smtp->server_parse('250', __LINE__))
  1132. {
  1133. $smtp->close_session($err_msg);
  1134. return false;
  1135. }
  1136.  
  1137. // Now tell the server we are done and close the socket...
  1138. $smtp->server_send('QUIT');
  1139. $smtp->close_session($err_msg);
  1140.  
  1141. return true;
  1142. }
  1143.  
  1144. /**
  1145. * SMTP Class
  1146. * Auth Mechanisms originally taken from the AUTH Modules found within the PHP Extension and Application Repository (PEAR)
  1147. * See docs/AUTHORS for more details
  1148. */
  1149. class smtp_class
  1150. {
  1151. var $server_response = '';
  1152. var $socket = 0;
  1153. protected $socket_tls = false;
  1154. var $responses = array();
  1155. var $commands = array();
  1156. var $numeric_response_code = 0;
  1157.  
  1158. var $backtrace = false;
  1159. var $backtrace_log = array();
  1160.  
  1161. function smtp_class()
  1162. {
  1163. // Always create a backtrace for admins to identify SMTP problems
  1164. $this->backtrace = true;
  1165. $this->backtrace_log = array();
  1166. }
  1167.  
  1168. /**
  1169. * Add backtrace message for debugging
  1170. */
  1171. function add_backtrace($message)
  1172. {
  1173. if ($this->backtrace)
  1174. {
  1175. $this->backtrace_log[] = utf8_htmlspecialchars($message);
  1176. }
  1177. }
  1178.  
  1179. /**
  1180. * Send command to smtp server
  1181. */
  1182. function server_send($command, $private_info = false)
  1183. {
  1184. fputs($this->socket, $command . "\r\n");
  1185.  
  1186. (!$private_info) ? $this->add_backtrace("# $command") : $this->add_backtrace('# Omitting sensitive information');
  1187.  
  1188. // We could put additional code here
  1189. }
  1190.  
  1191. /**
  1192. * We use the line to give the support people an indication at which command the error occurred
  1193. */
  1194. function server_parse($response, $line)
  1195. {
  1196. global $user;
  1197.  
  1198. $this->server_response = '';
  1199. $this->responses = array();
  1200. $this->numeric_response_code = 0;
  1201.  
  1202. while (substr($this->server_response, 3, 1) != ' ')
  1203. {
  1204. if (!($this->server_response = fgets($this->socket, 256)))
  1205. {
  1206. return (isset($user->lang['NO_EMAIL_RESPONSE_CODE'])) ? $user->lang['NO_EMAIL_RESPONSE_CODE'] : 'Could not get mail server response codes';
  1207. }
  1208. $this->responses[] = substr(rtrim($this->server_response), 4);
  1209. $this->numeric_response_code = (int) substr($this->server_response, 0, 3);
  1210.  
  1211. $this->add_backtrace("LINE: $line <- {$this->server_response}");
  1212. }
  1213.  
  1214. if (!(substr($this->server_response, 0, 3) == $response))
  1215. {
  1216. $this->numeric_response_code = (int) substr($this->server_response, 0, 3);
  1217. return (isset($user->lang['EMAIL_SMTP_ERROR_RESPONSE'])) ? sprintf($user->lang['EMAIL_SMTP_ERROR_RESPONSE'], $line, $this->server_response) : "Ran into problems sending Mail at <strong>Line $line</strong>. Response: $this->server_response";
  1218. }
  1219.  
  1220. return 0;
  1221. }
  1222.  
  1223. /**
  1224. * Close session
  1225. */
  1226. function close_session(&$err_msg)
  1227. {
  1228. fclose($this->socket);
  1229.  
  1230. if ($this->backtrace)
  1231. {
  1232. $message = '<h1>Backtrace</h1><p>' . implode('<br />', $this->backtrace_log) . '</p>';
  1233. $err_msg .= $message;
  1234. }
  1235. }
  1236.  
  1237. /**
  1238. * Log into server and get possible auth codes if neccessary
  1239. */
  1240. function log_into_server($hostname, $username, $password, $default_auth_method)
  1241. {
  1242. global $user;
  1243.  
  1244. $err_msg = '';
  1245.  
  1246. // Here we try to determine the *real* hostname (reverse DNS entry preferrably)
  1247. $local_host = $user->host;
  1248.  
  1249. if (function_exists('php_uname'))
  1250. {
  1251. $local_host = php_uname('n');
  1252.  
  1253. // Able to resolve name to IP
  1254. if (($addr = @gethostbyname($local_host)) !== $local_host)
  1255. {
  1256. // Able to resolve IP back to name
  1257. if (($name = @gethostbyaddr($addr)) !== $addr)
  1258. {
  1259. $local_host = $name;
  1260. }
  1261. }
  1262. }
  1263.  
  1264. // If we are authenticating through pop-before-smtp, we
  1265. // have to login ones before we get authenticated
  1266. // NOTE: on some configurations the time between an update of the auth database takes so
  1267. // long that the first email send does not work. This is not a biggie on a live board (only
  1268. // the install mail will most likely fail) - but on a dynamic ip connection this might produce
  1269. // severe problems and is not fixable!
  1270. if ($default_auth_method == 'POP-BEFORE-SMTP' && $username && $password)
  1271. {
  1272. global $config;
  1273.  
  1274. $errno = 0;
  1275. $errstr = '';
  1276.  
  1277. $this->server_send("QUIT");
  1278. fclose($this->socket);
  1279.  
  1280. $result = $this->pop_before_smtp($hostname, $username, $password);
  1281. $username = $password = $default_auth_method = '';
  1282.  
  1283. // We need to close the previous session, else the server is not
  1284. // able to get our ip for matching...
  1285. if (!$this->socket = @fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 10))
  1286. {
  1287. if ($errstr)
  1288. {
  1289. $errstr = utf8_convert_message($errstr);
  1290. }
  1291.  
  1292. $err_msg = (isset($user->lang['NO_CONNECT_TO_SMTP_HOST'])) ? sprintf($user->lang['NO_CONNECT_TO_SMTP_HOST'], $errno, $errstr) : "Could not connect to smtp host : $errno : $errstr";
  1293. return $err_msg;
  1294. }
  1295.  
  1296. // Wait for reply
  1297. if ($err_msg = $this->server_parse('220', __LINE__))
  1298. {
  1299. $this->close_session($err_msg);
  1300. return $err_msg;
  1301. }
  1302. }
  1303.  
  1304. $hello_result = $this->hello($local_host);
  1305. if (!is_null($hello_result))
  1306. {
  1307. return $hello_result;
  1308. }
  1309.  
  1310. // SMTP STARTTLS (RFC 3207)
  1311. if (!$this->socket_tls)
  1312. {
  1313. $this->socket_tls = $this->starttls();
  1314.  
  1315. if ($this->socket_tls)
  1316. {
  1317. // Switched to TLS
  1318. // RFC 3207: "The client MUST discard any knowledge obtained from the server, [...]"
  1319. // So say hello again
  1320. $hello_result = $this->hello($local_host);
  1321.  
  1322. if (!is_null($hello_result))
  1323. {
  1324. return $hello_result;
  1325. }
  1326. }
  1327. }
  1328.  
  1329. // If we are not authenticated yet, something might be wrong if no username and passwd passed
  1330. if (!$username || !$password)
  1331. {
  1332. return false;
  1333. }
  1334.  
  1335. if (!isset($this->commands['AUTH']))
  1336. {
  1337. return (isset($user->lang['SMTP_NO_AUTH_SUPPORT'])) ? $user->lang['SMTP_NO_AUTH_SUPPORT'] : 'SMTP server does not support authentication';
  1338. }
  1339.  
  1340. // Get best authentication method
  1341. $available_methods = explode(' ', $this->commands['AUTH']);
  1342.  
  1343. // Define the auth ordering if the default auth method was not found
  1344. $auth_methods = array('PLAIN', 'LOGIN', 'CRAM-MD5', 'DIGEST-MD5');
  1345. $method = '';
  1346.  
  1347. if (in_array($default_auth_method, $available_methods))
  1348. {
  1349. $method = $default_auth_method;
  1350. }
  1351. else
  1352. {
  1353. foreach ($auth_methods as $_method)
  1354. {
  1355. if (in_array($_method, $available_methods))
  1356. {
  1357. $method = $_method;
  1358. break;
  1359. }
  1360. }
  1361. }
  1362.  
  1363. if (!$method)
  1364. {
  1365. return (isset($user->lang['NO_SUPPORTED_AUTH_METHODS'])) ? $user->lang['NO_SUPPORTED_AUTH_METHODS'] : 'No supported authentication methods';
  1366. }
  1367.  
  1368. $method = strtolower(str_replace('-', '_', $method));
  1369. return $this->$method($username, $password);
  1370. }
  1371.  
  1372. /**
  1373. * SMTP EHLO/HELO
  1374. *
  1375. * @return mixed Null if the authentication process is supposed to continue
  1376. * False if already authenticated
  1377. * Error message (string) otherwise
  1378. */
  1379. protected function hello($hostname)
  1380. {
  1381. // Try EHLO first
  1382. $this->server_send("EHLO $hostname");
  1383. if ($err_msg = $this->server_parse('250', __LINE__))
  1384. {
  1385. // a 503 response code means that we're already authenticated
  1386. if ($this->numeric_response_code == 503)
  1387. {
  1388. return false;
  1389. }
  1390.  
  1391. // If EHLO fails, we try HELO
  1392. $this->server_send("HELO $hostname");
  1393. if ($err_msg = $this->server_parse('250', __LINE__))
  1394. {
  1395. return ($this->numeric_response_code == 503) ? false : $err_msg;
  1396. }
  1397. }
  1398.  
  1399. foreach ($this->responses as $response)
  1400. {
  1401. $response = explode(' ', $response);
  1402. $response_code = $response[0];
  1403. unset($response[0]);
  1404. $this->commands[$response_code] = implode(' ', $response);
  1405. }
  1406. }
  1407.  
  1408. /**
  1409. * SMTP STARTTLS (RFC 3207)
  1410. *
  1411. * @return bool Returns true if TLS was started
  1412. * Otherwise false
  1413. */
  1414. protected function starttls()
  1415. {
  1416. if (!function_exists('stream_socket_enable_crypto'))
  1417. {
  1418. return false;
  1419. }
  1420.  
  1421. if (!isset($this->commands['STARTTLS']))
  1422. {
  1423. return false;
  1424. }
  1425.  
  1426. $this->server_send('STARTTLS');
  1427.  
  1428. if ($err_msg = $this->server_parse('220', __LINE__))
  1429. {
  1430. return false;
  1431. }
  1432.  
  1433. $result = false;
  1434. $stream_meta = stream_get_meta_data($this->socket);
  1435.  
  1436. if (socket_set_blocking($this->socket, 1))
  1437. {
  1438. $result = stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
  1439. socket_set_blocking($this->socket, (int) $stream_meta['blocked']);
  1440. }
  1441.  
  1442. return $result;
  1443. }
  1444.  
  1445. /**
  1446. * Pop before smtp authentication
  1447. */
  1448. function pop_before_smtp($hostname, $username, $password)
  1449. {
  1450. global $user;
  1451.  
  1452. if (!$this->socket = @fsockopen($hostname, 110, $errno, $errstr, 10))
  1453. {
  1454. if ($errstr)
  1455. {
  1456. $errstr = utf8_convert_message($errstr);
  1457. }
  1458.  
  1459. return (isset($user->lang['NO_CONNECT_TO_SMTP_HOST'])) ? sprintf($user->lang['NO_CONNECT_TO_SMTP_HOST'], $errno, $errstr) : "Could not connect to smtp host : $errno : $errstr";
  1460. }
  1461.  
  1462. $this->server_send("USER $username", true);
  1463. if ($err_msg = $this->server_parse('+OK', __LINE__))
  1464. {
  1465. return $err_msg;
  1466. }
  1467.  
  1468. $this->server_send("PASS $password", true);
  1469. if ($err_msg = $this->server_parse('+OK', __LINE__))
  1470. {
  1471. return $err_msg;
  1472. }
  1473.  
  1474. $this->server_send('QUIT');
  1475. fclose($this->socket);
  1476.  
  1477. return false;
  1478. }
  1479.  
  1480. /**
  1481. * Plain authentication method
  1482. */
  1483. function plain($username, $password)
  1484. {
  1485. $this->server_send('AUTH PLAIN');
  1486. if ($err_msg = $this->server_parse('334', __LINE__))
  1487. {
  1488. return ($this->numeric_response_code == 503) ? false : $err_msg;
  1489. }
  1490.  
  1491. $base64_method_plain = base64_encode("\0" . $username . "\0" . $password);
  1492. $this->server_send($base64_method_plain, true);
  1493. if ($err_msg = $this->server_parse('235', __LINE__))
  1494. {
  1495. return $err_msg;
  1496. }
  1497.  
  1498. return false;
  1499. }
  1500.  
  1501. /**
  1502. * Login authentication method
  1503. */
  1504. function login($username, $password)
  1505. {
  1506. $this->server_send('AUTH LOGIN');
  1507. if ($err_msg = $this->server_parse('334', __LINE__))
  1508. {
  1509. return ($this->numeric_response_code == 503) ? false : $err_msg;
  1510. }
  1511.  
  1512. $this->server_send(base64_encode($username), true);
  1513. if ($err_msg = $this->server_parse('334', __LINE__))
  1514. {
  1515. return $err_msg;
  1516. }
  1517.  
  1518. $this->server_send(base64_encode($password), true);
  1519. if ($err_msg = $this->server_parse('235', __LINE__))
  1520. {
  1521. return $err_msg;
  1522. }
  1523.  
  1524. return false;
  1525. }
  1526.  
  1527. /**
  1528. * cram_md5 authentication method
  1529. */
  1530. function cram_md5($username, $password)
  1531. {
  1532. $this->server_send('AUTH CRAM-MD5');
  1533. if ($err_msg = $this->server_parse('334', __LINE__))
  1534. {
  1535. return ($this->numeric_response_code == 503) ? false : $err_msg;
  1536. }
  1537.  
  1538. $md5_challenge = base64_decode($this->responses[0]);
  1539. $password = (strlen($password) > 64) ? pack('H32', md5($password)) : ((strlen($password) < 64) ? str_pad($password, 64, chr(0)) : $password);
  1540. $md5_digest = md5((substr($password, 0, 64) ^ str_repeat(chr(0x5C), 64)) . (pack('H32', md5((substr($password, 0, 64) ^ str_repeat(chr(0x36), 64)) . $md5_challenge))));
  1541.  
  1542. $base64_method_cram_md5 = base64_encode($username . ' ' . $md5_digest);
  1543.  
  1544. $this->server_send($base64_method_cram_md5, true);
  1545. if ($err_msg = $this->server_parse('235', __LINE__))
  1546. {
  1547. return $err_msg;
  1548. }
  1549.  
  1550. return false;
  1551. }
  1552.  
  1553. /**
  1554. * digest_md5 authentication method
  1555. * A real pain in the ***
  1556. */
  1557. function digest_md5($username, $password)
  1558. {
  1559. global $config, $user;
  1560.  
  1561. $this->server_send('AUTH DIGEST-MD5');
  1562. if ($err_msg = $this->server_parse('334', __LINE__))
  1563. {
  1564. return ($this->numeric_response_code == 503) ? false : $err_msg;
  1565. }
  1566.  
  1567. $md5_challenge = base64_decode($this->responses[0]);
  1568.  
  1569. // Parse the md5 challenge - from AUTH_SASL (PEAR)
  1570. $tokens = array();
  1571. while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $md5_challenge, $matches))
  1572. {
  1573. // Ignore these as per rfc2831
  1574. if ($matches[1] == 'opaque' || $matches[1] == 'domain')
  1575. {
  1576. $md5_challenge = substr($md5_challenge, strlen($matches[0]) + 1);
  1577. continue;
  1578. }
  1579.  
  1580. // Allowed multiple "realm" and "auth-param"
  1581. if (!empty($tokens[$matches[1]]) && ($matches[1] == 'realm' || $matches[1] == 'auth-param'))
  1582. {
  1583. if (is_array($tokens[$matches[1]]))
  1584. {
  1585. $tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
  1586. }
  1587. else
  1588. {
  1589. $tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2]));
  1590. }
  1591. }
  1592. else if (!empty($tokens[$matches[1]])) // Any other multiple instance = failure
  1593. {
  1594. $tokens = array();
  1595. break;
  1596. }
  1597. else
  1598. {
  1599. $tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
  1600. }
  1601.  
  1602. // Remove the just parsed directive from the challenge
  1603. $md5_challenge = substr($md5_challenge, strlen($matches[0]) + 1);
  1604. }
  1605.  
  1606. // Realm
  1607. if (empty($tokens['realm']))
  1608. {
  1609. $tokens['realm'] = (function_exists('php_uname')) ? php_uname('n') : $user->host;
  1610. }
  1611.  
  1612. // Maxbuf
  1613. if (empty($tokens['maxbuf']))
  1614. {
  1615. $tokens['maxbuf'] = 65536;
  1616. }
  1617.  
  1618. // Required: nonce, algorithm
  1619. if (empty($tokens['nonce']) || empty($tokens['algorithm']))
  1620. {
  1621. $tokens = array();
  1622. }
  1623. $md5_challenge = $tokens;
  1624.  
  1625. if (!empty($md5_challenge))
  1626. {
  1627. $str = '';
  1628. for ($i = 0; $i < 32; $i++)
  1629. {
  1630. $str .= chr(mt_rand(0, 255));
  1631. }
  1632. $cnonce = base64_encode($str);
  1633.  
  1634. $digest_uri = 'smtp/' . $config['smtp_host'];
  1635.  
  1636. $auth_1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $username, $md5_challenge['realm'], $password))), $md5_challenge['nonce'], $cnonce);
  1637. $auth_2 = 'AUTHENTICATE:' . $digest_uri;
  1638. $response_value = md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($auth_1), $md5_challenge['nonce'], $cnonce, md5($auth_2)));
  1639.  
  1640. $input_string = sprintf('username="%s",realm="%s",nonce="%s",cnonce="%s",nc="00000001",qop=auth,digest-uri="%s",response=%s,%d', $username, $md5_challenge['realm'], $md5_challenge['nonce'], $cnonce, $digest_uri, $response_value, $md5_challenge['maxbuf']);
  1641. }
  1642. else
  1643. {
  1644. return (isset($user->lang['INVALID_DIGEST_CHALLENGE'])) ? $user->lang['INVALID_DIGEST_CHALLENGE'] : 'Invalid digest challenge';
  1645. }
  1646.  
  1647. $base64_method_digest_md5 = base64_encode($input_string);
  1648. $this->server_send($base64_method_digest_md5, true);
  1649. if ($err_msg = $this->server_parse('334', __LINE__))
  1650. {
  1651. return $err_msg;
  1652. }
  1653.  
  1654. $this->server_send(' ');
  1655. if ($err_msg = $this->server_parse('235', __LINE__))
  1656. {
  1657. return $err_msg;
  1658. }
  1659.  
  1660. return false;
  1661. }
  1662. }
  1663.  
  1664. /**
  1665. * Encodes the given string for proper display in UTF-8.
  1666. *
  1667. * This version is using base64 encoded data. The downside of this
  1668. * is if the mail client does not understand this encoding the user
  1669. * is basically doomed with an unreadable subject.
  1670. *
  1671. * Please note that this version fully supports RFC 2045 section 6.8.
  1672. *
  1673. * @param string $eol End of line we are using (optional to be backwards compatible)
  1674. */
  1675. function mail_encode($str, $eol = "\r\n")
  1676. {
  1677. // define start delimimter, end delimiter and spacer
  1678. $start = "=?UTF-8?B?";
  1679. $end = "?=";
  1680. $delimiter = "$eol ";
  1681.  
  1682. // Maximum length is 75. $split_length *must* be a multiple of 4, but <= 75 - strlen($start . $delimiter . $end)!!!
  1683. $split_length = 60;
  1684. $encoded_str = base64_encode($str);
  1685.  
  1686. // If encoded string meets the limits, we just return with the correct data.
  1687. if (strlen($encoded_str) <= $split_length)
  1688. {
  1689. return $start . $encoded_str . $end;
  1690. }
  1691.  
  1692. // If there is only ASCII data, we just return what we want, correctly splitting the lines.
  1693. if (strlen($str) === utf8_strlen($str))
  1694. {
  1695. return $start . implode($end . $delimiter . $start, str_split($encoded_str, $split_length)) . $end;
  1696. }
  1697.  
  1698. // UTF-8 data, compose encoded lines
  1699. $array = utf8_str_split($str);
  1700. $str = '';
  1701.  
  1702. while (sizeof($array))
  1703. {
  1704. $text = '';
  1705.  
  1706. while (sizeof($array) && intval((strlen($text . $array[0]) + 2) / 3) << 2 <= $split_length)
  1707. {
  1708. $text .= array_shift($array);
  1709. }
  1710.  
  1711. $str .= $start . base64_encode($text) . $end . $delimiter;
  1712. }
  1713.  
  1714. return substr($str, 0, -strlen($delimiter));
  1715. }
  1716.  
  1717. /**
  1718. * Wrapper for sending out emails with the PHP's mail function
  1719. */
  1720. function phpbb_mail($to, $subject, $msg, $headers, $eol, &$err_msg)
  1721. {
  1722. global $config, $phpbb_root_path, $phpEx;
  1723.  
  1724. // We use the EOL character for the OS here because the PHP mail function does not correctly transform line endings. On Windows SMTP is used (SMTP is \r\n), on UNIX a command is used...
  1725. // Reference: http://bugs.php.net/bug.php?id=15841
  1726. $headers = implode($eol, $headers);
  1727.  
  1728. if (!class_exists('\phpbb\error_collector'))
  1729. {
  1730. include($phpbb_root_path . 'includes/error_collector.' . $phpEx);
  1731. }
  1732.  
  1733. $collector = new \phpbb\error_collector;
  1734. $collector->install();
  1735.  
  1736. // On some PHP Versions mail() *may* fail if there are newlines within the subject.
  1737. // Newlines are used as a delimiter for lines in mail_encode() according to RFC 2045 section 6.8.
  1738. // Because PHP can't decide what is wanted we revert back to the non-RFC-compliant way of separating by one space (Use '' as parameter to mail_encode() results in SPACE used)
  1739. $result = $config['email_function_name']($to, mail_encode($subject, ''), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $headers);
  1740.  
  1741. $collector->uninstall();
  1742. $err_msg = $collector->format_errors();
  1743.  
  1744. return $result;
  1745. }
Add Comment
Please, Sign In to add comment