[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/includes/ -> functions_messenger.php (source)

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


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1