PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/forum/includes/functions_messenger.php

https://bitbucket.org/itoxable/chiron-gaming
PHP | 1627 lines | 1148 code | 244 blank | 235 comment | 196 complexity | e6d39049590b5c906f1f123b13e52e33 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0
  1. <?php
  2. /**
  3. *
  4. * @package phpBB3
  5. * @version $Id$
  6. * @copyright (c) 2005 phpBB Group
  7. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  8. *
  9. */
  10. /**
  11. * @ignore
  12. */
  13. if (!defined('IN_PHPBB'))
  14. {
  15. exit;
  16. }
  17. /**
  18. * Messenger
  19. * @package phpBB3
  20. */
  21. class messenger
  22. {
  23. var $vars, $msg, $extra_headers, $replyto, $from, $subject;
  24. var $addresses = array();
  25. var $mail_priority = MAIL_NORMAL_PRIORITY;
  26. var $use_queue = true;
  27. var $tpl_obj = NULL;
  28. var $tpl_msg = array();
  29. var $eol = "\n";
  30. /**
  31. * Constructor
  32. */
  33. function messenger($use_queue = true)
  34. {
  35. global $config;
  36. $this->use_queue = (!$config['email_package_size']) ? false : $use_queue;
  37. $this->subject = '';
  38. // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac)
  39. $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL;
  40. $this->eol = (!$this->eol) ? "\n" : $this->eol;
  41. }
  42. /**
  43. * Resets all the data (address, template file, etc etc) to default
  44. */
  45. function reset()
  46. {
  47. $this->addresses = $this->extra_headers = array();
  48. $this->vars = $this->msg = $this->replyto = $this->from = '';
  49. $this->mail_priority = MAIL_NORMAL_PRIORITY;
  50. }
  51. /**
  52. * Sets an email address to send to
  53. */
  54. function to($address, $realname = '')
  55. {
  56. global $config;
  57. if (!trim($address))
  58. {
  59. return;
  60. }
  61. $pos = isset($this->addresses['to']) ? sizeof($this->addresses['to']) : 0;
  62. $this->addresses['to'][$pos]['email'] = trim($address);
  63. // If empty sendmail_path on windows, PHP changes the to line
  64. if (!$config['smtp_delivery'] && DIRECTORY_SEPARATOR == '\\')
  65. {
  66. $this->addresses['to'][$pos]['name'] = '';
  67. }
  68. else
  69. {
  70. $this->addresses['to'][$pos]['name'] = trim($realname);
  71. }
  72. }
  73. /**
  74. * Sets an cc address to send to
  75. */
  76. function cc($address, $realname = '')
  77. {
  78. if (!trim($address))
  79. {
  80. return;
  81. }
  82. $pos = isset($this->addresses['cc']) ? sizeof($this->addresses['cc']) : 0;
  83. $this->addresses['cc'][$pos]['email'] = trim($address);
  84. $this->addresses['cc'][$pos]['name'] = trim($realname);
  85. }
  86. /**
  87. * Sets an bcc address to send to
  88. */
  89. function bcc($address, $realname = '')
  90. {
  91. if (!trim($address))
  92. {
  93. return;
  94. }
  95. $pos = isset($this->addresses['bcc']) ? sizeof($this->addresses['bcc']) : 0;
  96. $this->addresses['bcc'][$pos]['email'] = trim($address);
  97. $this->addresses['bcc'][$pos]['name'] = trim($realname);
  98. }
  99. /**
  100. * Sets a im contact to send to
  101. */
  102. function im($address, $realname = '')
  103. {
  104. // IM-Addresses could be empty
  105. if (!trim($address))
  106. {
  107. return;
  108. }
  109. $pos = isset($this->addresses['im']) ? sizeof($this->addresses['im']) : 0;
  110. $this->addresses['im'][$pos]['uid'] = trim($address);
  111. $this->addresses['im'][$pos]['name'] = trim($realname);
  112. }
  113. /**
  114. * Set the reply to address
  115. */
  116. function replyto($address)
  117. {
  118. $this->replyto = trim($address);
  119. }
  120. /**
  121. * Set the from address
  122. */
  123. function from($address)
  124. {
  125. $this->from = trim($address);
  126. }
  127. /**
  128. * set up subject for mail
  129. */
  130. function subject($subject = '')
  131. {
  132. $this->subject = trim($subject);
  133. }
  134. /**
  135. * set up extra mail headers
  136. */
  137. function headers($headers)
  138. {
  139. $this->extra_headers[] = trim($headers);
  140. }
  141. /**
  142. * Set the email priority
  143. */
  144. function set_mail_priority($priority = MAIL_NORMAL_PRIORITY)
  145. {
  146. $this->mail_priority = $priority;
  147. }
  148. /**
  149. * Set email template to use
  150. */
  151. function template($template_file, $template_lang = '', $template_path = '')
  152. {
  153. global $config, $phpbb_root_path, $user;
  154. if (!trim($template_file))
  155. {
  156. trigger_error('No template file for emailing set.', E_USER_ERROR);
  157. }
  158. if (!trim($template_lang))
  159. {
  160. // fall back to board default language if the user's language is
  161. // missing $template_file. If this does not exist either,
  162. // $tpl->set_custom_template will do a trigger_error
  163. $template_lang = basename($config['default_lang']);
  164. }
  165. // tpl_msg now holds a template object we can use to parse the template file
  166. if (!isset($this->tpl_msg[$template_lang . $template_file]))
  167. {
  168. $this->tpl_msg[$template_lang . $template_file] = new template();
  169. $tpl = &$this->tpl_msg[$template_lang . $template_file];
  170. $fallback_template_path = false;
  171. if (!$template_path)
  172. {
  173. $template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
  174. $template_path .= $template_lang . '/email';
  175. // we can only specify default language fallback when the path is not a custom one for which we
  176. // do not know the default language alternative
  177. if ($template_lang !== basename($config['default_lang']))
  178. {
  179. $fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
  180. $fallback_template_path .= basename($config['default_lang']) . '/email';
  181. }
  182. }
  183. $tpl->set_custom_template($template_path, $template_lang . '_email', $fallback_template_path);
  184. $tpl->set_filenames(array(
  185. 'body' => $template_file . '.txt',
  186. ));
  187. }
  188. $this->tpl_obj = &$this->tpl_msg[$template_lang . $template_file];
  189. $this->vars = &$this->tpl_obj->_rootref;
  190. $this->tpl_msg = '';
  191. return true;
  192. }
  193. /**
  194. * assign variables to email template
  195. */
  196. function assign_vars($vars)
  197. {
  198. if (!is_object($this->tpl_obj))
  199. {
  200. return;
  201. }
  202. $this->tpl_obj->assign_vars($vars);
  203. }
  204. function assign_block_vars($blockname, $vars)
  205. {
  206. if (!is_object($this->tpl_obj))
  207. {
  208. return;
  209. }
  210. $this->tpl_obj->assign_block_vars($blockname, $vars);
  211. }
  212. /**
  213. * Send the mail out to the recipients set previously in var $this->addresses
  214. */
  215. function send($method = NOTIFY_EMAIL, $break = false)
  216. {
  217. global $config, $user;
  218. // We add some standard variables we always use, no need to specify them always
  219. if (!isset($this->vars['U_BOARD']))
  220. {
  221. $this->assign_vars(array(
  222. 'U_BOARD' => generate_board_url(),
  223. ));
  224. }
  225. if (!isset($this->vars['EMAIL_SIG']))
  226. {
  227. $this->assign_vars(array(
  228. 'EMAIL_SIG' => str_replace('<br />', "\n", "-- \n" . htmlspecialchars_decode($config['board_email_sig'])),
  229. ));
  230. }
  231. if (!isset($this->vars['SITENAME']))
  232. {
  233. $this->assign_vars(array(
  234. 'SITENAME' => htmlspecialchars_decode($config['sitename']),
  235. ));
  236. }
  237. // Parse message through template
  238. $this->msg = trim($this->tpl_obj->assign_display('body'));
  239. // 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
  240. $this->msg = str_replace("\r\n", "\n", $this->msg);
  241. // We now try and pull a subject from the email body ... if it exists,
  242. // do this here because the subject may contain a variable
  243. $drop_header = '';
  244. $match = array();
  245. if (preg_match('#^(Subject:(.*?))$#m', $this->msg, $match))
  246. {
  247. $this->subject = (trim($match[2]) != '') ? trim($match[2]) : (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']);
  248. $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#');
  249. }
  250. else
  251. {
  252. $this->subject = (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']);
  253. }
  254. if ($drop_header)
  255. {
  256. $this->msg = trim(preg_replace('#' . $drop_header . '#s', '', $this->msg));
  257. }
  258. if ($break)
  259. {
  260. return true;
  261. }
  262. switch ($method)
  263. {
  264. case NOTIFY_EMAIL:
  265. $result = $this->msg_email();
  266. break;
  267. case NOTIFY_IM:
  268. $result = $this->msg_jabber();
  269. break;
  270. case NOTIFY_BOTH:
  271. $result = $this->msg_email();
  272. $this->msg_jabber();
  273. break;
  274. }
  275. $this->reset();
  276. return $result;
  277. }
  278. /**
  279. * Add error message to log
  280. */
  281. function error($type, $msg)
  282. {
  283. global $user, $phpEx, $phpbb_root_path, $config;
  284. // Session doesn't exist, create it
  285. if (!isset($user->session_id) || $user->session_id === '')
  286. {
  287. $user->session_begin();
  288. }
  289. $calling_page = (!empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
  290. $message = '';
  291. switch ($type)
  292. {
  293. case 'EMAIL':
  294. $message = '<strong>EMAIL/' . (($config['smtp_delivery']) ? 'SMTP' : 'PHP/' . $config['email_function_name'] . '()') . '</strong>';
  295. break;
  296. default:
  297. $message = "<strong>$type</strong>";
  298. break;
  299. }
  300. $message .= '<br /><em>' . htmlspecialchars($calling_page) . '</em><br /><br />' . $msg . '<br />';
  301. add_log('critical', 'LOG_ERROR_' . $type, $message);
  302. }
  303. /**
  304. * Save to queue
  305. */
  306. function save_queue()
  307. {
  308. global $config;
  309. if ($config['email_package_size'] && $this->use_queue && !empty($this->queue))
  310. {
  311. $this->queue->save();
  312. return;
  313. }
  314. }
  315. /**
  316. * Return email header
  317. */
  318. function build_header($to, $cc, $bcc)
  319. {
  320. global $config;
  321. // We could use keys here, but we won't do this for 3.0.x to retain backwards compatibility
  322. $headers = array();
  323. $headers[] = 'From: ' . $this->from;
  324. if ($cc)
  325. {
  326. $headers[] = 'Cc: ' . $cc;
  327. }
  328. if ($bcc)
  329. {
  330. $headers[] = 'Bcc: ' . $bcc;
  331. }
  332. $headers[] = 'Reply-To: ' . $this->replyto;
  333. $headers[] = 'Return-Path: <' . $config['board_email'] . '>';
  334. $headers[] = 'Sender: <' . $config['board_email'] . '>';
  335. $headers[] = 'MIME-Version: 1.0';
  336. $headers[] = 'Message-ID: <' . md5(unique_id(time())) . '@' . $config['server_name'] . '>';
  337. $headers[] = 'Date: ' . date('r', time());
  338. $headers[] = 'Content-Type: text/plain; charset=UTF-8'; // format=flowed
  339. $headers[] = 'Content-Transfer-Encoding: 8bit'; // 7bit
  340. $headers[] = 'X-Priority: ' . $this->mail_priority;
  341. $headers[] = 'X-MSMail-Priority: ' . (($this->mail_priority == MAIL_LOW_PRIORITY) ? 'Low' : (($this->mail_priority == MAIL_NORMAL_PRIORITY) ? 'Normal' : 'High'));
  342. $headers[] = 'X-Mailer: phpBB3';
  343. $headers[] = 'X-MimeOLE: phpBB3';
  344. $headers[] = 'X-phpBB-Origin: phpbb://' . str_replace(array('http://', 'https://'), array('', ''), generate_board_url());
  345. if (sizeof($this->extra_headers))
  346. {
  347. $headers = array_merge($headers, $this->extra_headers);
  348. }
  349. return $headers;
  350. }
  351. /**
  352. * Send out emails
  353. */
  354. function msg_email()
  355. {
  356. global $config, $user;
  357. if (empty($config['email_enable']))
  358. {
  359. return false;
  360. }
  361. // Addresses to send to?
  362. if (empty($this->addresses) || (empty($this->addresses['to']) && empty($this->addresses['cc']) && empty($this->addresses['bcc'])))
  363. {
  364. // Send was successful. ;)
  365. return true;
  366. }
  367. $use_queue = false;
  368. if ($config['email_package_size'] && $this->use_queue)
  369. {
  370. if (empty($this->queue))
  371. {
  372. $this->queue = new queue();
  373. $this->queue->init('email', $config['email_package_size']);
  374. }
  375. $use_queue = true;
  376. }
  377. if (empty($this->replyto))
  378. {
  379. $this->replyto = '<' . $config['board_contact'] . '>';
  380. }
  381. if (empty($this->from))
  382. {
  383. $this->from = '<' . $config['board_contact'] . '>';
  384. }
  385. $encode_eol = ($config['smtp_delivery']) ? "\r\n" : $this->eol;
  386. // Build to, cc and bcc strings
  387. $to = $cc = $bcc = '';
  388. foreach ($this->addresses as $type => $address_ary)
  389. {
  390. if ($type == 'im')
  391. {
  392. continue;
  393. }
  394. foreach ($address_ary as $which_ary)
  395. {
  396. $$type .= (($$type != '') ? ', ' : '') . (($which_ary['name'] != '') ? mail_encode($which_ary['name'], $encode_eol) . ' <' . $which_ary['email'] . '>' : $which_ary['email']);
  397. }
  398. }
  399. // Build header
  400. $headers = $this->build_header($to, $cc, $bcc);
  401. // Send message ...
  402. if (!$use_queue)
  403. {
  404. $mail_to = ($to == '') ? 'undisclosed-recipients:;' : $to;
  405. $err_msg = '';
  406. if ($config['smtp_delivery'])
  407. {
  408. $result = smtpmail($this->addresses, mail_encode($this->subject), wordwrap(utf8_wordwrap($this->msg), 997, "\n", true), $err_msg, $headers);
  409. }
  410. else
  411. {
  412. $result = phpbb_mail($mail_to, $this->subject, $this->msg, $headers, $this->eol, $err_msg);
  413. }
  414. if (!$result)
  415. {
  416. $this->error('EMAIL', $err_msg);
  417. return false;
  418. }
  419. }
  420. else
  421. {
  422. $this->queue->put('email', array(
  423. 'to' => $to,
  424. 'addresses' => $this->addresses,
  425. 'subject' => $this->subject,
  426. 'msg' => $this->msg,
  427. 'headers' => $headers)
  428. );
  429. }
  430. return true;
  431. }
  432. /**
  433. * Send jabber message out
  434. */
  435. function msg_jabber()
  436. {
  437. global $config, $db, $user, $phpbb_root_path, $phpEx;
  438. if (empty($config['jab_enable']) || empty($config['jab_host']) || empty($config['jab_username']) || empty($config['jab_password']))
  439. {
  440. return false;
  441. }
  442. if (empty($this->addresses['im']))
  443. {
  444. // Send was successful. ;)
  445. return true;
  446. }
  447. $use_queue = false;
  448. if ($config['jab_package_size'] && $this->use_queue)
  449. {
  450. if (empty($this->queue))
  451. {
  452. $this->queue = new queue();
  453. $this->queue->init('jabber', $config['jab_package_size']);
  454. }
  455. $use_queue = true;
  456. }
  457. $addresses = array();
  458. foreach ($this->addresses['im'] as $type => $uid_ary)
  459. {
  460. $addresses[] = $uid_ary['uid'];
  461. }
  462. $addresses = array_unique($addresses);
  463. if (!$use_queue)
  464. {
  465. include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx);
  466. $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], $config['jab_password'], $config['jab_use_ssl']);
  467. if (!$this->jabber->connect())
  468. {
  469. $this->error('JABBER', $user->lang['ERR_JAB_CONNECT'] . '<br />' . $this->jabber->get_log());
  470. return false;
  471. }
  472. if (!$this->jabber->login())
  473. {
  474. $this->error('JABBER', $user->lang['ERR_JAB_AUTH'] . '<br />' . $this->jabber->get_log());
  475. return false;
  476. }
  477. foreach ($addresses as $address)
  478. {
  479. $this->jabber->send_message($address, $this->msg, $this->subject);
  480. }
  481. $this->jabber->disconnect();
  482. }
  483. else
  484. {
  485. $this->queue->put('jabber', array(
  486. 'addresses' => $addresses,
  487. 'subject' => $this->subject,
  488. 'msg' => $this->msg)
  489. );
  490. }
  491. unset($addresses);
  492. return true;
  493. }
  494. }
  495. /**
  496. * handling email and jabber queue
  497. * @package phpBB3
  498. */
  499. class queue
  500. {
  501. var $data = array();
  502. var $queue_data = array();
  503. var $package_size = 0;
  504. var $cache_file = '';
  505. var $eol = "\n";
  506. /**
  507. * constructor
  508. */
  509. function queue()
  510. {
  511. global $phpEx, $phpbb_root_path;
  512. $this->data = array();
  513. $this->cache_file = "{$phpbb_root_path}cache/queue.$phpEx";
  514. // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac)
  515. $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL;
  516. $this->eol = (!$this->eol) ? "\n" : $this->eol;
  517. }
  518. /**
  519. * Init a queue object
  520. */
  521. function init($object, $package_size)
  522. {
  523. $this->data[$object] = array();
  524. $this->data[$object]['package_size'] = $package_size;
  525. $this->data[$object]['data'] = array();
  526. }
  527. /**
  528. * Put object in queue
  529. */
  530. function put($object, $scope)
  531. {
  532. $this->data[$object]['data'][] = $scope;
  533. }
  534. /**
  535. * Obtains exclusive lock on queue cache file.
  536. * Returns resource representing the lock
  537. */
  538. function lock()
  539. {
  540. // For systems that can't have two processes opening
  541. // one file for writing simultaneously
  542. if (file_exists($this->cache_file . '.lock'))
  543. {
  544. $mode = 'rb';
  545. }
  546. else
  547. {
  548. $mode = 'wb';
  549. }
  550. $lock_fp = @fopen($this->cache_file . '.lock', $mode);
  551. if ($mode == 'wb')
  552. {
  553. if (!$lock_fp)
  554. {
  555. // Two processes may attempt to create lock file at the same time.
  556. // Have the losing process try opening the lock file again for reading
  557. // on the assumption that the winning process created it
  558. $mode = 'rb';
  559. $lock_fp = @fopen($this->cache_file . '.lock', $mode);
  560. }
  561. else
  562. {
  563. // Only need to set mode when the lock file is written
  564. @chmod($this->cache_file . '.lock', 0666);
  565. }
  566. }
  567. if ($lock_fp)
  568. {
  569. @flock($lock_fp, LOCK_EX);
  570. }
  571. return $lock_fp;
  572. }
  573. /**
  574. * Releases lock on queue cache file, using resource obtained from lock()
  575. */
  576. function unlock($lock_fp)
  577. {
  578. // lock() will return null if opening lock file, and thus locking, failed.
  579. // Accept null values here so that client code does not need to check them
  580. if ($lock_fp)
  581. {
  582. @flock($lock_fp, LOCK_UN);
  583. fclose($lock_fp);
  584. }
  585. }
  586. /**
  587. * Process queue
  588. * Using lock file
  589. */
  590. function process()
  591. {
  592. global $db, $config, $phpEx, $phpbb_root_path, $user;
  593. $lock_fp = $this->lock();
  594. set_config('last_queue_run', time(), true);
  595. if (!file_exists($this->cache_file) || filemtime($this->cache_file) > time() - $config['queue_interval'])
  596. {
  597. $this->unlock($lock_fp);
  598. return;
  599. }
  600. include($this->cache_file);
  601. foreach ($this->queue_data as $object => $data_ary)
  602. {
  603. @set_time_limit(0);
  604. if (!isset($data_ary['package_size']))
  605. {
  606. $data_ary['package_size'] = 0;
  607. }
  608. $package_size = $data_ary['package_size'];
  609. $num_items = (!$package_size || sizeof($data_ary['data']) < $package_size) ? sizeof($data_ary['data']) : $package_size;
  610. /*
  611. * This code is commented out because it causes problems on some web hosts.
  612. * The core problem is rather restrictive email sending limits.
  613. * This code is nly useful if you have no such restrictions from the
  614. * web host and the package size setting is wrong.
  615. // If the amount of emails to be sent is way more than package_size than we need to increase it to prevent backlogs...
  616. if (sizeof($data_ary['data']) > $package_size * 2.5)
  617. {
  618. $num_items = sizeof($data_ary['data']);
  619. }
  620. */
  621. switch ($object)
  622. {
  623. case 'email':
  624. // Delete the email queued objects if mailing is disabled
  625. if (!$config['email_enable'])
  626. {
  627. unset($this->queue_data['email']);
  628. continue 2;
  629. }
  630. break;
  631. case 'jabber':
  632. if (!$config['jab_enable'])
  633. {
  634. unset($this->queue_data['jabber']);
  635. continue 2;
  636. }
  637. include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx);
  638. $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], $config['jab_password'], $config['jab_use_ssl']);
  639. if (!$this->jabber->connect())
  640. {
  641. messenger::error('JABBER', $user->lang['ERR_JAB_CONNECT']);
  642. continue 2;
  643. }
  644. if (!$this->jabber->login())
  645. {
  646. messenger::error('JABBER', $user->lang['ERR_JAB_AUTH']);
  647. continue 2;
  648. }
  649. break;
  650. default:
  651. $this->unlock($lock_fp);
  652. return;
  653. }
  654. for ($i = 0; $i < $num_items; $i++)
  655. {
  656. // Make variables available...
  657. extract(array_shift($this->queue_data[$object]['data']));
  658. switch ($object)
  659. {
  660. case 'email':
  661. $err_msg = '';
  662. $to = (!$to) ? 'undisclosed-recipients:;' : $to;
  663. if ($config['smtp_delivery'])
  664. {
  665. $result = smtpmail($addresses, mail_encode($subject), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $err_msg, $headers);
  666. }
  667. else
  668. {
  669. $result = phpbb_mail($to, $subject, $msg, $headers, $this->eol, $err_msg);
  670. }
  671. if (!$result)
  672. {
  673. messenger::error('EMAIL', $err_msg);
  674. continue 2;
  675. }
  676. break;
  677. case 'jabber':
  678. foreach ($addresses as $address)
  679. {
  680. if ($this->jabber->send_message($address, $msg, $subject) === false)
  681. {
  682. messenger::error('JABBER', $this->jabber->get_log());
  683. continue 3;
  684. }
  685. }
  686. break;
  687. }
  688. }
  689. // No more data for this object? Unset it
  690. if (!sizeof($this->queue_data[$object]['data']))
  691. {
  692. unset($this->queue_data[$object]);
  693. }
  694. // Post-object processing
  695. switch ($object)
  696. {
  697. case 'jabber':
  698. // Hang about a couple of secs to ensure the messages are
  699. // handled, then disconnect
  700. $this->jabber->disconnect();
  701. break;
  702. }
  703. }
  704. if (!sizeof($this->queue_data))
  705. {
  706. @unlink($this->cache_file);
  707. }
  708. else
  709. {
  710. if ($fp = @fopen($this->cache_file, 'wb'))
  711. {
  712. fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->queue_data), true) . ");\n\n?>");
  713. fclose($fp);
  714. phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
  715. }
  716. }
  717. $this->unlock($lock_fp);
  718. }
  719. /**
  720. * Save queue
  721. */
  722. function save()
  723. {
  724. if (!sizeof($this->data))
  725. {
  726. return;
  727. }
  728. $lock_fp = $this->lock();
  729. if (file_exists($this->cache_file))
  730. {
  731. include($this->cache_file);
  732. foreach ($this->queue_data as $object => $data_ary)
  733. {
  734. if (isset($this->data[$object]) && sizeof($this->data[$object]))
  735. {
  736. $this->data[$object]['data'] = array_merge($data_ary['data'], $this->data[$object]['data']);
  737. }
  738. else
  739. {
  740. $this->data[$object]['data'] = $data_ary['data'];
  741. }
  742. }
  743. }
  744. if ($fp = @fopen($this->cache_file, 'w'))
  745. {
  746. fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->data), true) . ");\n\n?>");
  747. fclose($fp);
  748. phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
  749. }
  750. $this->unlock($lock_fp);
  751. }
  752. }
  753. /**
  754. * Replacement or substitute for PHP's mail command
  755. */
  756. function smtpmail($addresses, $subject, $message, &$err_msg, $headers = false)
  757. {
  758. global $config, $user;
  759. // Fix any bare linefeeds in the message to make it RFC821 Compliant.
  760. $message = preg_replace("#(?<!\r)\n#si", "\r\n", $message);
  761. if ($headers !== false)
  762. {
  763. if (!is_array($headers))
  764. {
  765. // Make sure there are no bare linefeeds in the headers
  766. $headers = preg_replace('#(?<!\r)\n#si', "\n", $headers);
  767. $headers = explode("\n", $headers);
  768. }
  769. // Ok this is rather confusing all things considered,
  770. // but we have to grab bcc and cc headers and treat them differently
  771. // Something we really didn't take into consideration originally
  772. $headers_used = array();
  773. foreach ($headers as $header)
  774. {
  775. if (strpos(strtolower($header), 'cc:') === 0 || strpos(strtolower($header), 'bcc:') === 0)
  776. {
  777. continue;
  778. }
  779. $headers_used[] = trim($header);
  780. }
  781. $headers = chop(implode("\r\n", $headers_used));
  782. }
  783. if (trim($subject) == '')
  784. {
  785. $err_msg = (isset($user->lang['NO_EMAIL_SUBJECT'])) ? $user->lang['NO_EMAIL_SUBJECT'] : 'No email subject specified';
  786. return false;
  787. }
  788. if (trim($message) == '')
  789. {
  790. $err_msg = (isset($user->lang['NO_EMAIL_MESSAGE'])) ? $user->lang['NO_EMAIL_MESSAGE'] : 'Email message was blank';
  791. return false;
  792. }
  793. $mail_rcpt = $mail_to = $mail_cc = array();
  794. // Build correct addresses for RCPT TO command and the client side display (TO, CC)
  795. if (isset($addresses['to']) && sizeof($addresses['to']))
  796. {
  797. foreach ($addresses['to'] as $which_ary)
  798. {
  799. $mail_to[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>';
  800. $mail_rcpt['to'][] = '<' . trim($which_ary['email']) . '>';
  801. }
  802. }
  803. if (isset($addresses['bcc']) && sizeof($addresses['bcc']))
  804. {
  805. foreach ($addresses['bcc'] as $which_ary)
  806. {
  807. $mail_rcpt['bcc'][] = '<' . trim($which_ary['email']) . '>';
  808. }
  809. }
  810. if (isset($addresses['cc']) && sizeof($addresses['cc']))
  811. {
  812. foreach ($addresses['cc'] as $which_ary)
  813. {
  814. $mail_cc[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>';
  815. $mail_rcpt['cc'][] = '<' . trim($which_ary['email']) . '>';
  816. }
  817. }
  818. $smtp = new smtp_class();
  819. $errno = 0;
  820. $errstr = '';
  821. $smtp->add_backtrace('Connecting to ' . $config['smtp_host'] . ':' . $config['smtp_port']);
  822. // Ok we have error checked as much as we can to this point let's get on it already.
  823. ob_start();
  824. $smtp->socket = fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 20);
  825. $error_contents = ob_get_clean();
  826. if (!$smtp->socket)
  827. {
  828. if ($errstr)
  829. {
  830. $errstr = utf8_convert_message($errstr);
  831. }
  832. $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";
  833. $err_msg .= ($error_contents) ? '<br /><br />' . htmlspecialchars($error_contents) : '';
  834. return false;
  835. }
  836. // Wait for reply
  837. if ($err_msg = $smtp->server_parse('220', __LINE__))
  838. {
  839. $smtp->close_session($err_msg);
  840. return false;
  841. }
  842. // Let me in. This function handles the complete authentication process
  843. if ($err_msg = $smtp->log_into_server($config['smtp_host'], $config['smtp_username'], $config['smtp_password'], $config['smtp_auth_method']))
  844. {
  845. $smtp->close_session($err_msg);
  846. return false;
  847. }
  848. // From this point onward most server response codes should be 250
  849. // Specify who the mail is from....
  850. $smtp->server_send('MAIL FROM:<' . $config['board_email'] . '>');
  851. if ($err_msg = $smtp->server_parse('250', __LINE__))
  852. {
  853. $smtp->close_session($err_msg);
  854. return false;
  855. }
  856. // Specify each user to send to and build to header.
  857. $to_header = implode(', ', $mail_to);
  858. $cc_header = implode(', ', $mail_cc);
  859. // Now tell the MTA to send the Message to the following people... [TO, BCC, CC]
  860. $rcpt = false;
  861. foreach ($mail_rcpt as $type => $mail_to_addresses)
  862. {
  863. foreach ($mail_to_addresses as $mail_to_address)
  864. {
  865. // Add an additional bit of error checking to the To field.
  866. if (preg_match('#[^ ]+\@[^ ]+#', $mail_to_address))
  867. {
  868. $smtp->server_send("RCPT TO:$mail_to_address");
  869. if ($err_msg = $smtp->server_parse('250', __LINE__))
  870. {
  871. // We continue... if users are not resolved we do not care
  872. if ($smtp->numeric_response_code != 550)
  873. {
  874. $smtp->close_session($err_msg);
  875. return false;
  876. }
  877. }
  878. else
  879. {
  880. $rcpt = true;
  881. }
  882. }
  883. }
  884. }
  885. // 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.
  886. if (!$rcpt)
  887. {
  888. $user->session_begin();
  889. $err_msg .= '<br /><br />';
  890. $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?';
  891. $smtp->close_session($err_msg);
  892. return false;
  893. }
  894. // Ok now we tell the server we are ready to start sending data
  895. $smtp->server_send('DATA');
  896. // This is the last response code we look for until the end of the message.
  897. if ($err_msg = $smtp->server_parse('354', __LINE__))
  898. {
  899. $smtp->close_session($err_msg);
  900. return false;
  901. }
  902. // Send the Subject Line...
  903. $smtp->server_send("Subject: $subject");
  904. // Now the To Header.
  905. $to_header = ($to_header == '') ? 'undisclosed-recipients:;' : $to_header;
  906. $smtp->server_send("To: $to_header");
  907. // Now the CC Header.
  908. if ($cc_header != '')
  909. {
  910. $smtp->server_send("CC: $cc_header");
  911. }
  912. // Now any custom headers....
  913. if ($headers !== false)
  914. {
  915. $smtp->server_send("$headers\r\n");
  916. }
  917. // Ok now we are ready for the message...
  918. $smtp->server_send($message);
  919. // Ok the all the ingredients are mixed in let's cook this puppy...
  920. $smtp->server_send('.');
  921. if ($err_msg = $smtp->server_parse('250', __LINE__))
  922. {
  923. $smtp->close_session($err_msg);
  924. return false;
  925. }
  926. // Now tell the server we are done and close the socket...
  927. $smtp->server_send('QUIT');
  928. $smtp->close_session($err_msg);
  929. return true;
  930. }
  931. /**
  932. * SMTP Class
  933. * Auth Mechanisms originally taken from the AUTH Modules found within the PHP Extension and Application Repository (PEAR)
  934. * See docs/AUTHORS for more details
  935. * @package phpBB3
  936. */
  937. class smtp_class
  938. {
  939. var $server_response = '';
  940. var $socket = 0;
  941. var $responses = array();
  942. var $commands = array();
  943. var $numeric_response_code = 0;
  944. var $backtrace = false;
  945. var $backtrace_log = array();
  946. function smtp_class()
  947. {
  948. // Always create a backtrace for admins to identify SMTP problems
  949. $this->backtrace = true;
  950. $this->backtrace_log = array();
  951. }
  952. /**
  953. * Add backtrace message for debugging
  954. */
  955. function add_backtrace($message)
  956. {
  957. if ($this->backtrace)
  958. {
  959. $this->backtrace_log[] = utf8_htmlspecialchars($message);
  960. }
  961. }
  962. /**
  963. * Send command to smtp server
  964. */
  965. function server_send($command, $private_info = false)
  966. {
  967. fputs($this->socket, $command . "\r\n");
  968. (!$private_info) ? $this->add_backtrace("# $command") : $this->add_backtrace('# Omitting sensitive information');
  969. // We could put additional code here
  970. }
  971. /**
  972. * We use the line to give the support people an indication at which command the error occurred
  973. */
  974. function server_parse($response, $line)
  975. {
  976. global $user;
  977. $this->server_response = '';
  978. $this->responses = array();
  979. $this->numeric_response_code = 0;
  980. while (substr($this->server_response, 3, 1) != ' ')
  981. {
  982. if (!($this->server_response = fgets($this->socket, 256)))
  983. {
  984. return (isset($user->lang['NO_EMAIL_RESPONSE_CODE'])) ? $user->lang['NO_EMAIL_RESPONSE_CODE'] : 'Could not get mail server response codes';
  985. }
  986. $this->responses[] = substr(rtrim($this->server_response), 4);
  987. $this->numeric_response_code = (int) substr($this->server_response, 0, 3);
  988. $this->add_backtrace("LINE: $line <- {$this->server_response}");
  989. }
  990. if (!(substr($this->server_response, 0, 3) == $response))
  991. {
  992. $this->numeric_response_code = (int) substr($this->server_response, 0, 3);
  993. 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";
  994. }
  995. return 0;
  996. }
  997. /**
  998. * Close session
  999. */
  1000. function close_session(&$err_msg)
  1001. {
  1002. fclose($this->socket);
  1003. if ($this->backtrace)
  1004. {
  1005. $message = '<h1>Backtrace</h1><p>' . implode('<br />', $this->backtrace_log) . '</p>';
  1006. $err_msg .= $message;
  1007. }
  1008. }
  1009. /**
  1010. * Log into server and get possible auth codes if neccessary
  1011. */
  1012. function log_into_server($hostname, $username, $password, $default_auth_method)
  1013. {
  1014. global $user;
  1015. $err_msg = '';
  1016. // Here we try to determine the *real* hostname (reverse DNS entry preferrably)
  1017. $local_host = $user->host;
  1018. if (function_exists('php_uname'))
  1019. {
  1020. $local_host = php_uname('n');
  1021. // Able to resolve name to IP
  1022. if (($addr = @gethostbyname($local_host)) !== $local_host)
  1023. {
  1024. // Able to resolve IP back to name
  1025. if (($name = @gethostbyaddr($addr)) !== $addr)
  1026. {
  1027. $local_host = $name;
  1028. }
  1029. }
  1030. }
  1031. // If we are authenticating through pop-before-smtp, we
  1032. // have to login ones before we get authenticated
  1033. // NOTE: on some configurations the time between an update of the auth database takes so
  1034. // long that the first email send does not work. This is not a biggie on a live board (only
  1035. // the install mail will most likely fail) - but on a dynamic ip connection this might produce
  1036. // severe problems and is not fixable!
  1037. if ($default_auth_method == 'POP-BEFORE-SMTP' && $username && $password)
  1038. {
  1039. global $config;
  1040. $errno = 0;
  1041. $errstr = '';
  1042. $this->server_send("QUIT");
  1043. fclose($this->socket);
  1044. $result = $this->pop_before_smtp($hostname, $username, $password);
  1045. $username = $password = $default_auth_method = '';
  1046. // We need to close the previous session, else the server is not
  1047. // able to get our ip for matching...
  1048. if (!$this->socket = @fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 10))
  1049. {
  1050. if ($errstr)
  1051. {
  1052. $errstr = utf8_convert_message($errstr);
  1053. }
  1054. $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";
  1055. return $err_msg;
  1056. }
  1057. // Wait for reply
  1058. if ($err_msg = $this->server_parse('220', __LINE__))
  1059. {
  1060. $this->close_session($err_msg);
  1061. return $err_msg;
  1062. }
  1063. }
  1064. // Try EHLO first
  1065. $this->server_send("EHLO {$local_host}");
  1066. if ($err_msg = $this->server_parse('250', __LINE__))
  1067. {
  1068. // a 503 response code means that we're already authenticated
  1069. if ($this->numeric_response_code == 503)
  1070. {
  1071. return false;
  1072. }
  1073. // If EHLO fails, we try HELO
  1074. $this->server_send("HELO {$local_host}");
  1075. if ($err_msg = $this->server_parse('250', __LINE__))
  1076. {
  1077. return ($this->numeric_response_code == 503) ? false : $err_msg;
  1078. }
  1079. }
  1080. foreach ($this->responses as $response)
  1081. {
  1082. $response = explode(' ', $response);
  1083. $response_code = $response[0];
  1084. unset($response[0]);
  1085. $this->commands[$response_code] = implode(' ', $response);
  1086. }
  1087. // If we are not authenticated yet, something might be wrong if no username and passwd passed
  1088. if (!$username || !$password)
  1089. {
  1090. return false;
  1091. }
  1092. if (!isset($this->commands['AUTH']))
  1093. {
  1094. return (isset($user->lang['SMTP_NO_AUTH_SUPPORT'])) ? $user->lang['SMTP_NO_AUTH_SUPPORT'] : 'SMTP server does not support authentication';
  1095. }
  1096. // Get best authentication method
  1097. $available_methods = explode(' ', $this->commands['AUTH']);
  1098. // Define the auth ordering if the default auth method was not found
  1099. $auth_methods = array('PLAIN', 'LOGIN', 'CRAM-MD5', 'DIGEST-MD5');
  1100. $method = '';
  1101. if (in_array($default_auth_method, $available_methods))
  1102. {
  1103. $method = $default_auth_method;
  1104. }
  1105. else
  1106. {
  1107. foreach ($auth_methods as $_method)
  1108. {
  1109. if (in_array($_method, $available_methods))
  1110. {
  1111. $method = $_method;
  1112. break;
  1113. }
  1114. }
  1115. }
  1116. if (!$method)
  1117. {
  1118. return (isset($user->lang['NO_SUPPORTED_AUTH_METHODS'])) ? $user->lang['NO_SUPPORTED_AUTH_METHODS'] : 'No supported authentication methods';
  1119. }
  1120. $method = strtolower(str_replace('-', '_', $method));
  1121. return $this->$method($username, $password);
  1122. }
  1123. /**
  1124. * Pop before smtp authentication
  1125. */
  1126. function pop_before_smtp($hostname, $username, $password)
  1127. {
  1128. global $user;
  1129. if (!$this->socket = @fsockopen($hostname, 110, $errno, $errstr, 10))
  1130. {
  1131. if ($errstr)
  1132. {
  1133. $errstr = utf8_convert_message($errstr);
  1134. }
  1135. 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";
  1136. }
  1137. $this->server_send("USER $username", true);
  1138. if ($err_msg = $this->server_parse('+OK', __LINE__))
  1139. {
  1140. return $err_msg;
  1141. }
  1142. $this->server_send("PASS $password", true);
  1143. if ($err_msg = $this->server_parse('+OK', __LINE__))
  1144. {
  1145. return $err_msg;
  1146. }
  1147. $this->server_send('QUIT');
  1148. fclose($this->socket);
  1149. return false;
  1150. }
  1151. /**
  1152. * Plain authentication method
  1153. */
  1154. function plain($username, $password)
  1155. {
  1156. $this->server_send('AUTH PLAIN');
  1157. if ($err_msg = $this->server_parse('334', __LINE__))
  1158. {
  1159. return ($this->numeric_response_code == 503) ? false : $err_msg;
  1160. }
  1161. $base64_method_plain = base64_encode("\0" . $username . "\0" . $password);
  1162. $this->server_send($base64_method_plain, true);
  1163. if ($err_msg = $this->server_parse('235', __LINE__))
  1164. {
  1165. return $err_msg;
  1166. }
  1167. return false;
  1168. }
  1169. /**
  1170. * Login authentication method
  1171. */
  1172. function login($username, $password)
  1173. {
  1174. $this->server_send('AUTH LOGIN');
  1175. if ($err_msg = $this->server_parse('334', __LINE__))
  1176. {
  1177. return ($this->numeric_response_code == 503) ? false : $err_msg;
  1178. }
  1179. $this->server_send(base64_encode($username), true);
  1180. if ($err_msg = $this->server_parse('334', __LINE__))
  1181. {
  1182. return $err_msg;
  1183. }
  1184. $this->server_send(base64_encode($password), true);
  1185. if ($err_msg = $this->server_parse('235', __LINE__))
  1186. {
  1187. return $err_msg;
  1188. }
  1189. return false;
  1190. }
  1191. /**
  1192. * cram_md5 authentication method
  1193. */
  1194. function cram_md5($username, $password)
  1195. {
  1196. $this->server_send('AUTH CRAM-MD5');
  1197. if ($err_msg = $this->server_parse('334', __LINE__))
  1198. {
  1199. return ($this->numeric_response_code == 503) ? false : $err_msg;
  1200. }
  1201. $md5_challenge = base64_decode($this->responses[0]);
  1202. $password = (strlen($password) > 64) ? pack('H32', md5($password)) : ((strlen($password) < 64) ? str_pad($password, 64, chr(0)) : $password);
  1203. $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))));
  1204. $base64_method_cram_md5 = base64_encode($username . ' ' . $md5_digest);
  1205. $this->server_send($base64_method_cram_md5, true);
  1206. if ($err_msg = $this->server_parse('235', __LINE__))
  1207. {
  1208. return $err_msg;
  1209. }
  1210. return false;
  1211. }
  1212. /**
  1213. * digest_md5 authentication method
  1214. * A real pain in the ***
  1215. */
  1216. function digest_md5($username, $password)
  1217. {
  1218. global $config, $user;
  1219. $this->server_send('AUTH DIGEST-MD5');
  1220. if ($err_msg = $this->server_parse('334', __LINE__))
  1221. {
  1222. return ($this->numeric_response_code == 503) ? false : $err_msg;
  1223. }
  1224. $md5_challenge = base64_decode($this->responses[0]);
  1225. // Parse the md5 challenge - from AUTH_SASL (PEAR)
  1226. $tokens = array();
  1227. while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $md5_challenge, $matches))
  1228. {
  1229. // Ignore these as per rfc2831
  1230. if ($matches[1] == 'opaque' || $matches[1] == 'domain')
  1231. {
  1232. $md5_challenge = substr($md5_challenge, strlen($matches[0]) + 1);
  1233. continue;
  1234. }
  1235. // Allowed multiple "realm" and "auth-param"
  1236. if (!empty($tokens[$matches[1]]) && ($matches[1] == 'realm' || $matches[1] == 'auth-param'))
  1237. {
  1238. if (is_array($tokens[$matches[1]]))
  1239. {
  1240. $tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
  1241. }
  1242. else
  1243. {
  1244. $tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2]));
  1245. }
  1246. }
  1247. else if (!empty($tokens[$matches[1]])) // Any other multiple instance = failure
  1248. {
  1249. $tokens = array();
  1250. break;
  1251. }
  1252. else
  1253. {
  1254. $tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
  1255. }
  1256. // Remove the just parsed directive from the challenge
  1257. $md5_challenge = substr($md5_challenge, strlen($matches[0]) + 1);
  1258. }
  1259. // Realm
  1260. if (empty($tokens['realm']))
  1261. {
  1262. $tokens['realm'] = (function_exists('php_uname')) ? php_uname('n') : $user->host;
  1263. }
  1264. // Maxbuf
  1265. if (empty($tokens['maxbuf']))
  1266. {
  1267. $tokens['maxbuf'] = 65536;
  1268. }
  1269. // Required: nonce, algorithm
  1270. if (empty($tokens['nonce']) || empty($tokens['algorithm']))
  1271. {
  1272. $tokens = array();
  1273. }
  1274. $md5_challenge = $tokens;
  1275. if (!empty($md5_challenge))
  1276. {
  1277. $str = '';
  1278. for ($i = 0; $i < 32; $i++)
  1279. {
  1280. $str .= chr(mt_rand(0, 255));
  1281. }
  1282. $cnonce = base64_encode($str);
  1283. $digest_uri = 'smtp/' . $config['smtp_host'];
  1284. $auth_1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $username, $md5_challenge['realm'], $password))), $md5_challenge['nonce'], $cnonce);
  1285. $auth_2 = 'AUTHENTICATE:' . $digest_uri;
  1286. $response_value = md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($auth_1), $md5_challenge['nonce'], $cnonce, md5($auth_2)));
  1287. $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']);
  1288. }
  1289. else
  1290. {
  1291. return (isset($user->lang['INVALID_DIGEST_CHALLENGE'])) ? $user->lang['INVALID_DIGEST_CHALLENGE'] : 'Invalid digest challenge';
  1292. }
  1293. $base64_method_digest_md5 = base64_encode($input_string);
  1294. $this->server_send($base64_method_digest_md5, true);
  1295. if ($err_msg = $this->server_parse('334', __LINE__))
  1296. {
  1297. return $err_msg;
  1298. }
  1299. $this->server_send(' ');
  1300. if ($err_msg = $this->server_parse('235', __LINE__))
  1301. {
  1302. return $err_msg;
  1303. }
  1304. return false;
  1305. }
  1306. }
  1307. /**
  1308. * Encodes the given string for proper display in UTF-8.
  1309. *
  1310. * This version is using base64 encoded data. The downside of this
  1311. * is if the mail client does not understand this encoding the user
  1312. * is basically doomed with an unreadable subject.
  1313. *
  1314. * Please note that this version fully supports RFC 2045 section 6.8.
  1315. *
  1316. * @param string $eol End of line we are using (optional to be backwards compatible)
  1317. */
  1318. function mail_encode($str, $eol = "\r\n")
  1319. {
  1320. // define start delimimter, end delimiter and spacer
  1321. $start = "=?UTF-8?B?";
  1322. $end = "?=";
  1323. $delimiter = "$eol ";
  1324. // Maximum length is 75. $split_length *must* be a multiple of 4, but <= 75 - strlen($start . $delimiter . $end)!!!
  1325. $split_length = 60;
  1326. $encoded_str = base64_encode($str);
  1327. // If encoded string meets the limits, we just return with the correct data.
  1328. if (strlen($encoded_str) <= $split_length)
  1329. {
  1330. return $start . $encoded_str . $end;
  1331. }
  1332. // If there is only ASCII data, we just return what we want, correctly splitting the lines.
  1333. if (strlen($str) === utf8_strlen($str))
  1334. {
  1335. return $start . implode($end . $delimiter . $start, str_split($encoded_str, $split_length)) . $end;
  1336. }
  1337. // UTF-8 data, compose encoded lines
  1338. $array = utf8_str_split($str);
  1339. $str = '';
  1340. while (sizeof($array))
  1341. {
  1342. $text = '';
  1343. while (sizeof($array) && intval((strlen($text . $array[0]) + 2) / 3) << 2 <= $split_length)
  1344. {
  1345. $text .= array_shift($array);
  1346. }
  1347. $str .= $start . base64_encode($text) . $end . $delimiter;
  1348. }
  1349. return substr($str, 0, -strlen($delimiter));
  1350. }
  1351. /**
  1352. * Wrapper for sending out emails with the PHP's mail function
  1353. */
  1354. function phpbb_mail($to, $subject, $msg, $headers, $eol, &$err_msg)
  1355. {
  1356. global $config;
  1357. // 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...
  1358. // Reference: http://bugs.php.net/bug.php?id=15841
  1359. $headers = implode($eol, $headers);
  1360. ob_start();
  1361. // On some PHP Versions mail() *may* fail if there are newlines within the subject.
  1362. // Newlines are used as a delimiter for lines in mail_encode() according to RFC 2045 section 6.8.
  1363. // 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)
  1364. $result = $config['email_function_name']($to, mail_encode($subject, ''), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $headers);
  1365. $err_msg = ob_get_clean();
  1366. return $result;
  1367. }
  1368. ?>