Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

functions_messenger.php

Zuletzt modifiziert: 09.10.2024, 12:51 - Dateigröße: 43.09 KiB


 0001  <?php
 0002  /**
 0003  *
 0004  * This file is part of the phpBB Forum Software package.
 0005  *
 0006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
 0007  * @license GNU General Public License, version 2 (GPL-2.0)
 0008  *
 0009  * For full copyright and license information, please see
 0010  * the docs/CREDITS.txt file.
 0011  *
 0012  */
 0013   
 0014  /**
 0015  * @ignore
 0016  */
 0017  if (!defined('IN_PHPBB'))
 0018  {
 0019      exit;
 0020  }
 0021   
 0022  /**
 0023  * Messenger
 0024  */
 0025  class messenger
 0026  {
 0027      var $msg, $extra_headers, $replyto, $from, $subject;
 0028      var $addresses = array();
 0029   
 0030      var $mail_priority = MAIL_NORMAL_PRIORITY;
 0031      var $use_queue = true;
 0032   
 0033      /** @var \phpbb\template\template */
 0034      protected $template;
 0035   
 0036      var $eol = "\n";
 0037   
 0038      /**
 0039      * Constructor
 0040      */
 0041      function messenger($use_queue = true)
 0042      {
 0043          global $config;
 0044   
 0045          $this->use_queue = (!$config['email_package_size']) ? false : $use_queue;
 0046          $this->subject = '';
 0047   
 0048          // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac)
 0049          $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL;
 0050          $this->eol = (!$this->eol) ? "\n" : $this->eol;
 0051      }
 0052   
 0053      /**
 0054      * Resets all the data (address, template file, etc etc) to default
 0055      */
 0056      function reset()
 0057      {
 0058          $this->addresses = $this->extra_headers = array();
 0059          $this->msg = $this->replyto = $this->from = '';
 0060          $this->mail_priority = MAIL_NORMAL_PRIORITY;
 0061      }
 0062   
 0063      /**
 0064      * Set addresses for to/im as available
 0065      *
 0066      * @param array $user User row
 0067      */
 0068      function set_addresses($user)
 0069      {
 0070          if (isset($user['user_email']) && $user['user_email'])
 0071          {
 0072              $this->to($user['user_email'], (isset($user['username']) ? $user['username'] : ''));
 0073          }
 0074   
 0075          if (isset($user['user_jabber']) && $user['user_jabber'])
 0076          {
 0077              $this->im($user['user_jabber'], (isset($user['username']) ? $user['username'] : ''));
 0078          }
 0079      }
 0080   
 0081      /**
 0082      * Sets an email address to send to
 0083      */
 0084      function to($address, $realname = '')
 0085      {
 0086          global $config;
 0087   
 0088          if (!trim($address))
 0089          {
 0090              return;
 0091          }
 0092   
 0093          $pos = isset($this->addresses['to']) ? sizeof($this->addresses['to']) : 0;
 0094   
 0095          $this->addresses['to'][$pos]['email'] = trim($address);
 0096   
 0097          // If empty sendmail_path on windows, PHP changes the to line
 0098          if (!$config['smtp_delivery'] && DIRECTORY_SEPARATOR == '\\')
 0099          {
 0100              $this->addresses['to'][$pos]['name'] = '';
 0101          }
 0102          else
 0103          {
 0104              $this->addresses['to'][$pos]['name'] = trim($realname);
 0105          }
 0106      }
 0107   
 0108      /**
 0109      * Sets an cc address to send to
 0110      */
 0111      function cc($address, $realname = '')
 0112      {
 0113          if (!trim($address))
 0114          {
 0115              return;
 0116          }
 0117   
 0118          $pos = isset($this->addresses['cc']) ? sizeof($this->addresses['cc']) : 0;
 0119          $this->addresses['cc'][$pos]['email'] = trim($address);
 0120          $this->addresses['cc'][$pos]['name'] = trim($realname);
 0121      }
 0122   
 0123      /**
 0124      * Sets an bcc address to send to
 0125      */
 0126      function bcc($address, $realname = '')
 0127      {
 0128          if (!trim($address))
 0129          {
 0130              return;
 0131          }
 0132   
 0133          $pos = isset($this->addresses['bcc']) ? sizeof($this->addresses['bcc']) : 0;
 0134          $this->addresses['bcc'][$pos]['email'] = trim($address);
 0135          $this->addresses['bcc'][$pos]['name'] = trim($realname);
 0136      }
 0137   
 0138      /**
 0139      * Sets a im contact to send to
 0140      */
 0141      function im($address, $realname = '')
 0142      {
 0143          // IM-Addresses could be empty
 0144          if (!trim($address))
 0145          {
 0146              return;
 0147          }
 0148   
 0149          $pos = isset($this->addresses['im']) ? sizeof($this->addresses['im']) : 0;
 0150          $this->addresses['im'][$pos]['uid'] = trim($address);
 0151          $this->addresses['im'][$pos]['name'] = trim($realname);
 0152      }
 0153   
 0154      /**
 0155      * Set the reply to address
 0156      */
 0157      function replyto($address)
 0158      {
 0159          $this->replyto = trim($address);
 0160      }
 0161   
 0162      /**
 0163      * Set the from address
 0164      */
 0165      function from($address)
 0166      {
 0167          $this->from = trim($address);
 0168      }
 0169   
 0170      /**
 0171      * set up subject for mail
 0172      */
 0173      function subject($subject = '')
 0174      {
 0175          $this->subject = trim($subject);
 0176      }
 0177   
 0178      /**
 0179      * set up extra mail headers
 0180      */
 0181      function headers($headers)
 0182      {
 0183          $this->extra_headers[] = trim($headers);
 0184      }
 0185   
 0186      /**
 0187      * Adds X-AntiAbuse headers
 0188      *
 0189      * @param array $config        Configuration array
 0190      * @param user $user            A user object
 0191      *
 0192      * @return null
 0193      */
 0194      function anti_abuse_headers($config, $user)
 0195      {
 0196          $this->headers('X-AntiAbuse: Board servername - ' . mail_encode($config['server_name']));
 0197          $this->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']);
 0198          $this->headers('X-AntiAbuse: Username - ' . mail_encode($user->data['username']));
 0199          $this->headers('X-AntiAbuse: User IP - ' . $user->ip);
 0200      }
 0201   
 0202      /**
 0203      * Set the email priority
 0204      */
 0205      function set_mail_priority($priority = MAIL_NORMAL_PRIORITY)
 0206      {
 0207          $this->mail_priority = $priority;
 0208      }
 0209   
 0210      /**
 0211      * Set email template to use
 0212      */
 0213      function template($template_file, $template_lang = '', $template_path = '')
 0214      {
 0215          global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager;
 0216   
 0217          $this->setup_template();
 0218   
 0219          if (!trim($template_file))
 0220          {
 0221              trigger_error('No template file for emailing set.', E_USER_ERROR);
 0222          }
 0223   
 0224          if (!trim($template_lang))
 0225          {
 0226              // fall back to board default language if the user's language is
 0227              // missing $template_file.  If this does not exist either,
 0228              // $this->template->set_filenames will do a trigger_error
 0229              $template_lang = basename($config['default_lang']);
 0230          }
 0231   
 0232          if ($template_path)
 0233          {
 0234              $template_paths = array(
 0235                  $template_path,
 0236              );
 0237          }
 0238          else
 0239          {
 0240              $template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
 0241              $template_path .= $template_lang . '/email';
 0242   
 0243              $template_paths = array(
 0244                  $template_path,
 0245              );
 0246   
 0247              // we can only specify default language fallback when the path is not a custom one for which we
 0248              // do not know the default language alternative
 0249              if ($template_lang !== basename($config['default_lang']))
 0250              {
 0251                  $fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
 0252                  $fallback_template_path .= basename($config['default_lang']) . '/email';
 0253   
 0254                  $template_paths[] = $fallback_template_path;
 0255              }
 0256          }
 0257   
 0258          $this->set_template_paths(array(
 0259              array(
 0260                  'name'         => $template_lang . '_email',
 0261                  'ext_path'     => 'language/' . $template_lang . '/email'
 0262              ),
 0263          ), $template_paths);
 0264   
 0265          $this->template->set_filenames(array(
 0266              'body'        => $template_file . '.txt',
 0267          ));
 0268   
 0269          return true;
 0270      }
 0271   
 0272      /**
 0273      * assign variables to email template
 0274      */
 0275      function assign_vars($vars)
 0276      {
 0277          $this->setup_template();
 0278   
 0279          $this->template->assign_vars($vars);
 0280      }
 0281   
 0282      function assign_block_vars($blockname, $vars)
 0283      {
 0284          $this->setup_template();
 0285   
 0286          $this->template->assign_block_vars($blockname, $vars);
 0287      }
 0288   
 0289      /**
 0290      * Send the mail out to the recipients set previously in var $this->addresses
 0291      */
 0292      function send($method = NOTIFY_EMAIL, $break = false)
 0293      {
 0294          global $config, $user;
 0295   
 0296          // We add some standard variables we always use, no need to specify them always
 0297          $this->assign_vars(array(
 0298              'U_BOARD'    => generate_board_url(),
 0299              'EMAIL_SIG'    => str_replace('<br />', "\n", "-- \n" . htmlspecialchars_decode($config['board_email_sig'])),
 0300              'SITENAME'    => htmlspecialchars_decode($config['sitename']),
 0301          ));
 0302   
 0303          // Parse message through template
 0304          $this->msg = trim($this->template->assign_display('body'));
 0305   
 0306          // 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
 0307          $this->msg = str_replace("\r\n", "\n", $this->msg);
 0308   
 0309          // We now try and pull a subject from the email body ... if it exists,
 0310          // do this here because the subject may contain a variable
 0311          $drop_header = '';
 0312          $match = array();
 0313          if (preg_match('#^(Subject:(.*?))$#m', $this->msg, $match))
 0314          {
 0315              $this->subject = (trim($match[2]) != '') ? trim($match[2]) : (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']);
 0316              $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#');
 0317          }
 0318          else
 0319          {
 0320              $this->subject = (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']);
 0321          }
 0322   
 0323          if ($drop_header)
 0324          {
 0325              $this->msg = trim(preg_replace('#' . $drop_header . '#s', '', $this->msg));
 0326          }
 0327   
 0328          if ($break)
 0329          {
 0330              return true;
 0331          }
 0332   
 0333          switch ($method)
 0334          {
 0335              case NOTIFY_EMAIL:
 0336                  $result = $this->msg_email();
 0337              break;
 0338   
 0339              case NOTIFY_IM:
 0340                  $result = $this->msg_jabber();
 0341              break;
 0342   
 0343              case NOTIFY_BOTH:
 0344                  $result = $this->msg_email();
 0345                  $this->msg_jabber();
 0346              break;
 0347          }
 0348   
 0349          $this->reset();
 0350          return $result;
 0351      }
 0352   
 0353      /**
 0354      * Add error message to log
 0355      */
 0356      function error($type, $msg)
 0357      {
 0358          global $user, $phpEx, $phpbb_root_path, $config, $request;
 0359   
 0360          // Session doesn't exist, create it
 0361          if (!isset($user->session_id) || $user->session_id === '')
 0362          {
 0363              $user->session_begin();
 0364          }
 0365   
 0366          $calling_page = htmlspecialchars_decode($request->server('PHP_SELF'));
 0367   
 0368          $message = '';
 0369          switch ($type)
 0370          {
 0371              case 'EMAIL':
 0372                  $message = '<strong>EMAIL/' . (($config['smtp_delivery']) ? 'SMTP' : 'PHP/' . $config['email_function_name'] . '()') . '</strong>';
 0373              break;
 0374   
 0375              default:
 0376                  $message = "<strong>$type</strong>";
 0377              break;
 0378          }
 0379   
 0380          $message .= '<br /><em>' . htmlspecialchars($calling_page) . '</em><br /><br />' . $msg . '<br />';
 0381          add_log('critical', 'LOG_ERROR_' . $type, $message);
 0382      }
 0383   
 0384      /**
 0385      * Save to queue
 0386      */
 0387      function save_queue()
 0388      {
 0389          global $config;
 0390   
 0391          if ($config['email_package_size'] && $this->use_queue && !empty($this->queue))
 0392          {
 0393              $this->queue->save();
 0394              return;
 0395          }
 0396      }
 0397   
 0398      /**
 0399      * Generates a valid message id to be used in emails
 0400      *
 0401      * @return string message id
 0402      */
 0403      function generate_message_id()
 0404      {
 0405          global $config, $request;
 0406   
 0407          $domain = ($config['server_name']) ?: $request->server('SERVER_NAME', 'phpbb.generated');
 0408   
 0409          return md5(unique_id(time())) . '@' . $domain;
 0410      }
 0411   
 0412      /**
 0413      * Return email header
 0414      */
 0415      function build_header($to, $cc, $bcc)
 0416      {
 0417          global $config;
 0418   
 0419          // We could use keys here, but we won't do this for 3.0.x to retain backwards compatibility
 0420          $headers = array();
 0421   
 0422          $headers[] = 'From: ' . $this->from;
 0423   
 0424          if ($cc)
 0425          {
 0426              $headers[] = 'Cc: ' . $cc;
 0427          }
 0428   
 0429          if ($bcc)
 0430          {
 0431              $headers[] = 'Bcc: ' . $bcc;
 0432          }
 0433   
 0434          $headers[] = 'Reply-To: ' . $this->replyto;
 0435          $headers[] = 'Return-Path: <' . $config['board_email'] . '>';
 0436          $headers[] = 'Sender: <' . $config['board_email'] . '>';
 0437          $headers[] = 'MIME-Version: 1.0';
 0438          $headers[] = 'Message-ID: <' . $this->generate_message_id() . '>';
 0439          $headers[] = 'Date: ' . date('r', time());
 0440          $headers[] = 'Content-Type: text/plain; charset=UTF-8'; // format=flowed
 0441          $headers[] = 'Content-Transfer-Encoding: 8bit'; // 7bit
 0442   
 0443          $headers[] = 'X-Priority: ' . $this->mail_priority;
 0444          $headers[] = 'X-MSMail-Priority: ' . (($this->mail_priority == MAIL_LOW_PRIORITY) ? 'Low' : (($this->mail_priority == MAIL_NORMAL_PRIORITY) ? 'Normal' : 'High'));
 0445          $headers[] = 'X-Mailer: phpBB3';
 0446          $headers[] = 'X-MimeOLE: phpBB3';
 0447          $headers[] = 'X-phpBB-Origin: phpbb://' . str_replace(array('http://', 'https://'), array('', ''), generate_board_url());
 0448   
 0449          if (sizeof($this->extra_headers))
 0450          {
 0451              $headers = array_merge($headers, $this->extra_headers);
 0452          }
 0453   
 0454          return $headers;
 0455      }
 0456   
 0457      /**
 0458      * Send out emails
 0459      */
 0460      function msg_email()
 0461      {
 0462          global $config, $user;
 0463   
 0464          if (empty($config['email_enable']))
 0465          {
 0466              return false;
 0467          }
 0468   
 0469          // Addresses to send to?
 0470          if (empty($this->addresses) || (empty($this->addresses['to']) && empty($this->addresses['cc']) && empty($this->addresses['bcc'])))
 0471          {
 0472              // Send was successful. ;)
 0473              return true;
 0474          }
 0475   
 0476          $use_queue = false;
 0477          if ($config['email_package_size'] && $this->use_queue)
 0478          {
 0479              if (empty($this->queue))
 0480              {
 0481                  $this->queue = new queue();
 0482                  $this->queue->init('email', $config['email_package_size']);
 0483              }
 0484              $use_queue = true;
 0485          }
 0486   
 0487          $contact_name = htmlspecialchars_decode($config['board_contact_name']);
 0488          $board_contact = (($contact_name !== '') ? '"' . mail_encode($contact_name) . '" ' : '') . '<' . $config['board_contact'] . '>';
 0489   
 0490          if (empty($this->replyto))
 0491          {
 0492              $this->replyto = $board_contact;
 0493          }
 0494   
 0495          if (empty($this->from))
 0496          {
 0497              $this->from = $board_contact;
 0498          }
 0499   
 0500          $encode_eol = ($config['smtp_delivery']) ? "\r\n" : $this->eol;
 0501   
 0502          // Build to, cc and bcc strings
 0503          $to = $cc = $bcc = '';
 0504          foreach ($this->addresses as $type => $address_ary)
 0505          {
 0506              if ($type == 'im')
 0507              {
 0508                  continue;
 0509              }
 0510   
 0511              foreach ($address_ary as $which_ary)
 0512              {
 0513                  $$type .= (($$type != '') ? ', ' : '') . (($which_ary['name'] != '') ? mail_encode($which_ary['name'], $encode_eol) . ' <' . $which_ary['email'] . '>' : $which_ary['email']);
 0514              }
 0515          }
 0516   
 0517          // Build header
 0518          $headers = $this->build_header($to, $cc, $bcc);
 0519   
 0520          // Send message ...
 0521          if (!$use_queue)
 0522          {
 0523              $mail_to = ($to == '') ? 'undisclosed-recipients:;' : $to;
 0524              $err_msg = '';
 0525   
 0526              if ($config['smtp_delivery'])
 0527              {
 0528                  $result = smtpmail($this->addresses, mail_encode($this->subject), wordwrap(utf8_wordwrap($this->msg), 997, "\n", true), $err_msg, $headers);
 0529              }
 0530              else
 0531              {
 0532                  $result = phpbb_mail($mail_to, $this->subject, $this->msg, $headers, $this->eol, $err_msg);
 0533              }
 0534   
 0535              if (!$result)
 0536              {
 0537                  $this->error('EMAIL', $err_msg);
 0538                  return false;
 0539              }
 0540          }
 0541          else
 0542          {
 0543              $this->queue->put('email', array(
 0544                  'to'            => $to,
 0545                  'addresses'        => $this->addresses,
 0546                  'subject'        => $this->subject,
 0547                  'msg'            => $this->msg,
 0548                  'headers'        => $headers)
 0549              );
 0550          }
 0551   
 0552          return true;
 0553      }
 0554   
 0555      /**
 0556      * Send jabber message out
 0557      */
 0558      function msg_jabber()
 0559      {
 0560          global $config, $db, $user, $phpbb_root_path, $phpEx;
 0561   
 0562          if (empty($config['jab_enable']) || empty($config['jab_host']) || empty($config['jab_username']) || empty($config['jab_password']))
 0563          {
 0564              return false;
 0565          }
 0566   
 0567          if (empty($this->addresses['im']))
 0568          {
 0569              // Send was successful. ;)
 0570              return true;
 0571          }
 0572   
 0573          $use_queue = false;
 0574          if ($config['jab_package_size'] && $this->use_queue)
 0575          {
 0576              if (empty($this->queue))
 0577              {
 0578                  $this->queue = new queue();
 0579                  $this->queue->init('jabber', $config['jab_package_size']);
 0580              }
 0581              $use_queue = true;
 0582          }
 0583   
 0584          $addresses = array();
 0585          foreach ($this->addresses['im'] as $type => $uid_ary)
 0586          {
 0587              $addresses[] = $uid_ary['uid'];
 0588          }
 0589          $addresses = array_unique($addresses);
 0590   
 0591          if (!$use_queue)
 0592          {
 0593              include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx);
 0594              $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], htmlspecialchars_decode($config['jab_password']), $config['jab_use_ssl']);
 0595   
 0596              if (!$this->jabber->connect())
 0597              {
 0598                  $this->error('JABBER', $user->lang['ERR_JAB_CONNECT'] . '<br />' . $this->jabber->get_log());
 0599                  return false;
 0600              }
 0601   
 0602              if (!$this->jabber->login())
 0603              {
 0604                  $this->error('JABBER', $user->lang['ERR_JAB_AUTH'] . '<br />' . $this->jabber->get_log());
 0605                  return false;
 0606              }
 0607   
 0608              foreach ($addresses as $address)
 0609              {
 0610                  $this->jabber->send_message($address, $this->msg, $this->subject);
 0611              }
 0612   
 0613              $this->jabber->disconnect();
 0614          }
 0615          else
 0616          {
 0617              $this->queue->put('jabber', array(
 0618                  'addresses'        => $addresses,
 0619                  'subject'        => $this->subject,
 0620                  'msg'            => $this->msg)
 0621              );
 0622          }
 0623          unset($addresses);
 0624          return true;
 0625      }
 0626   
 0627      /**
 0628      * Setup template engine
 0629      */
 0630      protected function setup_template()
 0631      {
 0632          global $config, $phpbb_path_helper, $user, $phpbb_extension_manager;
 0633   
 0634          if ($this->template instanceof \phpbb\template\template)
 0635          {
 0636              return;
 0637          }
 0638   
 0639          $this->template = new \phpbb\template\twig\twig($phpbb_path_helper, $config, $user, new \phpbb\template\context(), $phpbb_extension_manager);
 0640      }
 0641   
 0642      /**
 0643      * Set template paths to load
 0644      */
 0645      protected function set_template_paths($path_name, $paths)
 0646      {
 0647          $this->setup_template();
 0648   
 0649          $this->template->set_custom_style($path_name, $paths);
 0650      }
 0651  }
 0652   
 0653  /**
 0654  * handling email and jabber queue
 0655  */
 0656  class queue
 0657  {
 0658      var $data = array();
 0659      var $queue_data = array();
 0660      var $package_size = 0;
 0661      var $cache_file = '';
 0662      var $eol = "\n";
 0663   
 0664      /**
 0665      * constructor
 0666      */
 0667      function queue()
 0668      {
 0669          global $phpEx, $phpbb_root_path;
 0670   
 0671          $this->data = array();
 0672          $this->cache_file = "{$phpbb_root_path}cache/queue.$phpEx";
 0673   
 0674          // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac)
 0675          $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL;
 0676          $this->eol = (!$this->eol) ? "\n" : $this->eol;
 0677      }
 0678   
 0679      /**
 0680      * Init a queue object
 0681      */
 0682      function init($object, $package_size)
 0683      {
 0684          $this->data[$object] = array();
 0685          $this->data[$object]['package_size'] = $package_size;
 0686          $this->data[$object]['data'] = array();
 0687      }
 0688   
 0689      /**
 0690      * Put object in queue
 0691      */
 0692      function put($object, $scope)
 0693      {
 0694          $this->data[$object]['data'][] = $scope;
 0695      }
 0696   
 0697      /**
 0698      * Process queue
 0699      * Using lock file
 0700      */
 0701      function process()
 0702      {
 0703          global $db, $config, $phpEx, $phpbb_root_path, $user;
 0704   
 0705          $lock = new \phpbb\lock\flock($this->cache_file);
 0706          $lock->acquire();
 0707   
 0708          // avoid races, check file existence once
 0709          $have_cache_file = file_exists($this->cache_file);
 0710          if (!$have_cache_file || $config['last_queue_run'] > time() - $config['queue_interval'])
 0711          {
 0712              if (!$have_cache_file)
 0713              {
 0714                  set_config('last_queue_run', time(), true);
 0715              }
 0716   
 0717              $lock->release();
 0718              return;
 0719          }
 0720   
 0721          set_config('last_queue_run', time(), true);
 0722   
 0723          include($this->cache_file);
 0724   
 0725          foreach ($this->queue_data as $object => $data_ary)
 0726          {
 0727              @set_time_limit(0);
 0728   
 0729              if (!isset($data_ary['package_size']))
 0730              {
 0731                  $data_ary['package_size'] = 0;
 0732              }
 0733   
 0734              $package_size = $data_ary['package_size'];
 0735              $num_items = (!$package_size || sizeof($data_ary['data']) < $package_size) ? sizeof($data_ary['data']) : $package_size;
 0736   
 0737              /*
 0738              * This code is commented out because it causes problems on some web hosts.
 0739              * The core problem is rather restrictive email sending limits.
 0740              * This code is nly useful if you have no such restrictions from the
 0741              * web host and the package size setting is wrong.
 0742   
 0743              // If the amount of emails to be sent is way more than package_size than we need to increase it to prevent backlogs...
 0744              if (sizeof($data_ary['data']) > $package_size * 2.5)
 0745              {
 0746                  $num_items = sizeof($data_ary['data']);
 0747              }
 0748              */
 0749   
 0750              switch ($object)
 0751              {
 0752                  case 'email':
 0753                      // Delete the email queued objects if mailing is disabled
 0754                      if (!$config['email_enable'])
 0755                      {
 0756                          unset($this->queue_data['email']);
 0757                          continue 2;
 0758                      }
 0759                  break;
 0760   
 0761                  case 'jabber':
 0762                      if (!$config['jab_enable'])
 0763                      {
 0764                          unset($this->queue_data['jabber']);
 0765                          continue 2;
 0766                      }
 0767   
 0768                      include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx);
 0769                      $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], htmlspecialchars_decode($config['jab_password']), $config['jab_use_ssl']);
 0770   
 0771                      if (!$this->jabber->connect())
 0772                      {
 0773                          $messenger = new messenger();
 0774                          $messenger->error('JABBER', $user->lang['ERR_JAB_CONNECT']);
 0775                          continue 2;
 0776                      }
 0777   
 0778                      if (!$this->jabber->login())
 0779                      {
 0780                          $messenger = new messenger();
 0781                          $messenger->error('JABBER', $user->lang['ERR_JAB_AUTH']);
 0782                          continue 2;
 0783                      }
 0784   
 0785                  break;
 0786   
 0787                  default:
 0788                      $lock->release();
 0789                      return;
 0790              }
 0791   
 0792              for ($i = 0; $i < $num_items; $i++)
 0793              {
 0794                  // Make variables available...
 0795                  extract(array_shift($this->queue_data[$object]['data']));
 0796   
 0797                  switch ($object)
 0798                  {
 0799                      case 'email':
 0800                          $err_msg = '';
 0801                          $to = (!$to) ? 'undisclosed-recipients:;' : $to;
 0802   
 0803                          if ($config['smtp_delivery'])
 0804                          {
 0805                              $result = smtpmail($addresses, mail_encode($subject), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $err_msg, $headers);
 0806                          }
 0807                          else
 0808                          {
 0809                              $result = phpbb_mail($to, $subject, $msg, $headers, $this->eol, $err_msg);
 0810                          }
 0811   
 0812                          if (!$result)
 0813                          {
 0814                              $messenger = new messenger();
 0815                              $messenger->error('EMAIL', $err_msg);
 0816                              continue 2;
 0817                          }
 0818                      break;
 0819   
 0820                      case 'jabber':
 0821                          foreach ($addresses as $address)
 0822                          {
 0823                              if ($this->jabber->send_message($address, $msg, $subject) === false)
 0824                              {
 0825                                  $messenger = new messenger();
 0826                                  $messenger->error('JABBER', $this->jabber->get_log());
 0827                                  continue 3;
 0828                              }
 0829                          }
 0830                      break;
 0831                  }
 0832              }
 0833   
 0834              // No more data for this object? Unset it
 0835              if (!sizeof($this->queue_data[$object]['data']))
 0836              {
 0837                  unset($this->queue_data[$object]);
 0838              }
 0839   
 0840              // Post-object processing
 0841              switch ($object)
 0842              {
 0843                  case 'jabber':
 0844                      // Hang about a couple of secs to ensure the messages are
 0845                      // handled, then disconnect
 0846                      $this->jabber->disconnect();
 0847                  break;
 0848              }
 0849          }
 0850   
 0851          if (!sizeof($this->queue_data))
 0852          {
 0853              @unlink($this->cache_file);
 0854          }
 0855          else
 0856          {
 0857              if ($fp = @fopen($this->cache_file, 'wb'))
 0858              {
 0859                  fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->queue_data), true) . ");\n\n?>");
 0860                  fclose($fp);
 0861   
 0862                  phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
 0863              }
 0864          }
 0865   
 0866          $lock->release();
 0867      }
 0868   
 0869      /**
 0870      * Save queue
 0871      */
 0872      function save()
 0873      {
 0874          if (!sizeof($this->data))
 0875          {
 0876              return;
 0877          }
 0878   
 0879          $lock = new \phpbb\lock\flock($this->cache_file);
 0880          $lock->acquire();
 0881   
 0882          if (file_exists($this->cache_file))
 0883          {
 0884              include($this->cache_file);
 0885   
 0886              foreach ($this->queue_data as $object => $data_ary)
 0887              {
 0888                  if (isset($this->data[$object]) && sizeof($this->data[$object]))
 0889                  {
 0890                      $this->data[$object]['data'] = array_merge($data_ary['data'], $this->data[$object]['data']);
 0891                  }
 0892                  else
 0893                  {
 0894                      $this->data[$object]['data'] = $data_ary['data'];
 0895                  }
 0896              }
 0897          }
 0898   
 0899          if ($fp = @fopen($this->cache_file, 'w'))
 0900          {
 0901              fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->data), true) . ");\n\n?>");
 0902              fclose($fp);
 0903   
 0904              phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
 0905          }
 0906   
 0907          $lock->release();
 0908      }
 0909  }
 0910   
 0911  /**
 0912  * Replacement or substitute for PHP's mail command
 0913  */
 0914  function smtpmail($addresses, $subject, $message, &$err_msg, $headers = false)
 0915  {
 0916      global $config, $user;
 0917   
 0918      // Fix any bare linefeeds in the message to make it RFC821 Compliant.
 0919      $message = preg_replace("#(?<!\r)\n#si", "\r\n", $message);
 0920   
 0921      if ($headers !== false)
 0922      {
 0923          if (!is_array($headers))
 0924          {
 0925              // Make sure there are no bare linefeeds in the headers
 0926              $headers = preg_replace('#(?<!\r)\n#si', "\n", $headers);
 0927              $headers = explode("\n", $headers);
 0928          }
 0929   
 0930          // Ok this is rather confusing all things considered,
 0931          // but we have to grab bcc and cc headers and treat them differently
 0932          // Something we really didn't take into consideration originally
 0933          $headers_used = array();
 0934   
 0935          foreach ($headers as $header)
 0936          {
 0937              if (strpos(strtolower($header), 'cc:') === 0 || strpos(strtolower($header), 'bcc:') === 0)
 0938              {
 0939                  continue;
 0940              }
 0941              $headers_used[] = trim($header);
 0942          }
 0943   
 0944          $headers = chop(implode("\r\n", $headers_used));
 0945      }
 0946   
 0947      if (trim($subject) == '')
 0948      {
 0949          $err_msg = (isset($user->lang['NO_EMAIL_SUBJECT'])) ? $user->lang['NO_EMAIL_SUBJECT'] : 'No email subject specified';
 0950          return false;
 0951      }
 0952   
 0953      if (trim($message) == '')
 0954      {
 0955          $err_msg = (isset($user->lang['NO_EMAIL_MESSAGE'])) ? $user->lang['NO_EMAIL_MESSAGE'] : 'Email message was blank';
 0956          return false;
 0957      }
 0958   
 0959      $mail_rcpt = $mail_to = $mail_cc = array();
 0960   
 0961      // Build correct addresses for RCPT TO command and the client side display (TO, CC)
 0962      if (isset($addresses['to']) && sizeof($addresses['to']))
 0963      {
 0964          foreach ($addresses['to'] as $which_ary)
 0965          {
 0966              $mail_to[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>';
 0967              $mail_rcpt['to'][] = '<' . trim($which_ary['email']) . '>';
 0968          }
 0969      }
 0970   
 0971      if (isset($addresses['bcc']) && sizeof($addresses['bcc']))
 0972      {
 0973          foreach ($addresses['bcc'] as $which_ary)
 0974          {
 0975              $mail_rcpt['bcc'][] = '<' . trim($which_ary['email']) . '>';
 0976          }
 0977      }
 0978   
 0979      if (isset($addresses['cc']) && sizeof($addresses['cc']))
 0980      {
 0981          foreach ($addresses['cc'] as $which_ary)
 0982          {
 0983              $mail_cc[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>';
 0984              $mail_rcpt['cc'][] = '<' . trim($which_ary['email']) . '>';
 0985          }
 0986      }
 0987   
 0988      $smtp = new smtp_class();
 0989   
 0990      $errno = 0;
 0991      $errstr = '';
 0992   
 0993      $smtp->add_backtrace('Connecting to ' . $config['smtp_host'] . ':' . $config['smtp_port']);
 0994   
 0995      // Ok we have error checked as much as we can to this point let's get on it already.
 0996      if (!class_exists('\phpbb\error_collector'))
 0997      {
 0998          global $phpbb_root_path, $phpEx;
 0999          include($phpbb_root_path . 'includes/error_collector.' . $phpEx);
 1000      }
 1001      $collector = new \phpbb\error_collector;
 1002      $collector->install();
 1003      $smtp->socket = fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 20);
 1004      $collector->uninstall();
 1005      $error_contents = $collector->format_errors();
 1006   
 1007      if (!$smtp->socket)
 1008      {
 1009          if ($errstr)
 1010          {
 1011              $errstr = utf8_convert_message($errstr);
 1012          }
 1013   
 1014          $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";
 1015          $err_msg .= ($error_contents) ? '<br /><br />' . htmlspecialchars($error_contents) : '';
 1016          return false;
 1017      }
 1018   
 1019      // Wait for reply
 1020      if ($err_msg = $smtp->server_parse('220', __LINE__))
 1021      {
 1022          $smtp->close_session($err_msg);
 1023          return false;
 1024      }
 1025   
 1026      // Let me in. This function handles the complete authentication process
 1027      if ($err_msg = $smtp->log_into_server($config['smtp_host'], $config['smtp_username'], htmlspecialchars_decode($config['smtp_password']), $config['smtp_auth_method']))
 1028      {
 1029          $smtp->close_session($err_msg);
 1030          return false;
 1031      }
 1032   
 1033      // From this point onward most server response codes should be 250
 1034      // Specify who the mail is from....
 1035      $smtp->server_send('MAIL FROM:<' . $config['board_email'] . '>');
 1036      if ($err_msg = $smtp->server_parse('250', __LINE__))
 1037      {
 1038          $smtp->close_session($err_msg);
 1039          return false;
 1040      }
 1041   
 1042      // Specify each user to send to and build to header.
 1043      $to_header = implode(', ', $mail_to);
 1044      $cc_header = implode(', ', $mail_cc);
 1045   
 1046      // Now tell the MTA to send the Message to the following people... [TO, BCC, CC]
 1047      $rcpt = false;
 1048      foreach ($mail_rcpt as $type => $mail_to_addresses)
 1049      {
 1050          foreach ($mail_to_addresses as $mail_to_address)
 1051          {
 1052              // Add an additional bit of error checking to the To field.
 1053              if (preg_match('#[^ ]+\@[^ ]+#', $mail_to_address))
 1054              {
 1055                  $smtp->server_send("RCPT TO:$mail_to_address");
 1056                  if ($err_msg = $smtp->server_parse('250', __LINE__))
 1057                  {
 1058                      // We continue... if users are not resolved we do not care
 1059                      if ($smtp->numeric_response_code != 550)
 1060                      {
 1061                          $smtp->close_session($err_msg);
 1062                          return false;
 1063                      }
 1064                  }
 1065                  else
 1066                  {
 1067                      $rcpt = true;
 1068                  }
 1069              }
 1070          }
 1071      }
 1072   
 1073      // 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.
 1074      if (!$rcpt)
 1075      {
 1076          $user->session_begin();
 1077          $err_msg .= '<br /><br />';
 1078          $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?';
 1079          $smtp->close_session($err_msg);
 1080          return false;
 1081      }
 1082   
 1083      // Ok now we tell the server we are ready to start sending data
 1084      $smtp->server_send('DATA');
 1085   
 1086      // This is the last response code we look for until the end of the message.
 1087      if ($err_msg = $smtp->server_parse('354', __LINE__))
 1088      {
 1089          $smtp->close_session($err_msg);
 1090          return false;
 1091      }
 1092   
 1093      // Send the Subject Line...
 1094      $smtp->server_send("Subject: $subject");
 1095   
 1096      // Now the To Header.
 1097      $to_header = ($to_header == '') ? 'undisclosed-recipients:;' : $to_header;
 1098      $smtp->server_send("To: $to_header");
 1099   
 1100      // Now the CC Header.
 1101      if ($cc_header != '')
 1102      {
 1103          $smtp->server_send("CC: $cc_header");
 1104      }
 1105   
 1106      // Now any custom headers....
 1107      if ($headers !== false)
 1108      {
 1109          $smtp->server_send("$headers\r\n");
 1110      }
 1111   
 1112      // Ok now we are ready for the message...
 1113      $smtp->server_send($message);
 1114   
 1115      // Ok the all the ingredients are mixed in let's cook this puppy...
 1116      $smtp->server_send('.');
 1117      if ($err_msg = $smtp->server_parse('250', __LINE__))
 1118      {
 1119          $smtp->close_session($err_msg);
 1120          return false;
 1121      }
 1122   
 1123      // Now tell the server we are done and close the socket...
 1124      $smtp->server_send('QUIT');
 1125      $smtp->close_session($err_msg);
 1126   
 1127      return true;
 1128  }
 1129   
 1130  /**
 1131  * SMTP Class
 1132  * Auth Mechanisms originally taken from the AUTH Modules found within the PHP Extension and Application Repository (PEAR)
 1133  * See docs/AUTHORS for more details
 1134  */
 1135  class smtp_class
 1136  {
 1137      var $server_response = '';
 1138      var $socket = 0;
 1139      protected $socket_tls = false;
 1140      var $responses = array();
 1141      var $commands = array();
 1142      var $numeric_response_code = 0;
 1143   
 1144      var $backtrace = false;
 1145      var $backtrace_log = array();
 1146   
 1147      function smtp_class()
 1148      {
 1149          // Always create a backtrace for admins to identify SMTP problems
 1150          $this->backtrace = true;
 1151          $this->backtrace_log = array();
 1152      }
 1153   
 1154      /**
 1155      * Add backtrace message for debugging
 1156      */
 1157      function add_backtrace($message)
 1158      {
 1159          if ($this->backtrace)
 1160          {
 1161              $this->backtrace_log[] = utf8_htmlspecialchars($message);
 1162          }
 1163      }
 1164   
 1165      /**
 1166      * Send command to smtp server
 1167      */
 1168      function server_send($command, $private_info = false)
 1169      {
 1170          fputs($this->socket, $command . "\r\n");
 1171   
 1172          (!$private_info) ? $this->add_backtrace("# $command") : $this->add_backtrace('# Omitting sensitive information');
 1173   
 1174          // We could put additional code here
 1175      }
 1176   
 1177      /**
 1178      * We use the line to give the support people an indication at which command the error occurred
 1179      */
 1180      function server_parse($response, $line)
 1181      {
 1182          global $user;
 1183   
 1184          $this->server_response = '';
 1185          $this->responses = array();
 1186          $this->numeric_response_code = 0;
 1187   
 1188          while (substr($this->server_response, 3, 1) != ' ')
 1189          {
 1190              if (!($this->server_response = fgets($this->socket, 256)))
 1191              {
 1192                  return (isset($user->lang['NO_EMAIL_RESPONSE_CODE'])) ? $user->lang['NO_EMAIL_RESPONSE_CODE'] : 'Could not get mail server response codes';
 1193              }
 1194              $this->responses[] = substr(rtrim($this->server_response), 4);
 1195              $this->numeric_response_code = (int) substr($this->server_response, 0, 3);
 1196   
 1197              $this->add_backtrace("LINE: $line <- {$this->server_response}");
 1198          }
 1199   
 1200          if (!(substr($this->server_response, 0, 3) == $response))
 1201          {
 1202              $this->numeric_response_code = (int) substr($this->server_response, 0, 3);
 1203              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";
 1204          }
 1205   
 1206          return 0;
 1207      }
 1208   
 1209      /**
 1210      * Close session
 1211      */
 1212      function close_session(&$err_msg)
 1213      {
 1214          fclose($this->socket);
 1215   
 1216          if ($this->backtrace)
 1217          {
 1218              $message = '<h1>Backtrace</h1><p>' . implode('<br />', $this->backtrace_log) . '</p>';
 1219              $err_msg .= $message;
 1220          }
 1221      }
 1222   
 1223      /**
 1224      * Log into server and get possible auth codes if neccessary
 1225      */
 1226      function log_into_server($hostname, $username, $password, $default_auth_method)
 1227      {
 1228          global $user;
 1229   
 1230          $err_msg = '';
 1231   
 1232          // Here we try to determine the *real* hostname (reverse DNS entry preferrably)
 1233          $local_host = $user->host;
 1234   
 1235          if (function_exists('php_uname'))
 1236          {
 1237              $local_host = php_uname('n');
 1238   
 1239              // Able to resolve name to IP
 1240              if (($addr = @gethostbyname($local_host)) !== $local_host)
 1241              {
 1242                  // Able to resolve IP back to name
 1243                  if (($name = @gethostbyaddr($addr)) !== $addr)
 1244                  {
 1245                      $local_host = $name;
 1246                  }
 1247              }
 1248          }
 1249   
 1250          // If we are authenticating through pop-before-smtp, we
 1251          // have to login ones before we get authenticated
 1252          // NOTE: on some configurations the time between an update of the auth database takes so
 1253          // long that the first email send does not work. This is not a biggie on a live board (only
 1254          // the install mail will most likely fail) - but on a dynamic ip connection this might produce
 1255          // severe problems and is not fixable!
 1256          if ($default_auth_method == 'POP-BEFORE-SMTP' && $username && $password)
 1257          {
 1258              global $config;
 1259   
 1260              $errno = 0;
 1261              $errstr = '';
 1262   
 1263              $this->server_send("QUIT");
 1264              fclose($this->socket);
 1265   
 1266              $result = $this->pop_before_smtp($hostname, $username, $password);
 1267              $username = $password = $default_auth_method = '';
 1268   
 1269              // We need to close the previous session, else the server is not
 1270              // able to get our ip for matching...
 1271              if (!$this->socket = @fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 10))
 1272              {
 1273                  if ($errstr)
 1274                  {
 1275                      $errstr = utf8_convert_message($errstr);
 1276                  }
 1277   
 1278                  $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";
 1279                  return $err_msg;
 1280              }
 1281   
 1282              // Wait for reply
 1283              if ($err_msg = $this->server_parse('220', __LINE__))
 1284              {
 1285                  $this->close_session($err_msg);
 1286                  return $err_msg;
 1287              }
 1288          }
 1289   
 1290          $hello_result = $this->hello($local_host);
 1291          if (!is_null($hello_result))
 1292          {
 1293              return $hello_result;
 1294          }
 1295   
 1296          // SMTP STARTTLS (RFC 3207)
 1297          if (!$this->socket_tls)
 1298          {
 1299              $this->socket_tls = $this->starttls();
 1300   
 1301              if ($this->socket_tls)
 1302              {
 1303                  // Switched to TLS
 1304                  // RFC 3207: "The client MUST discard any knowledge obtained from the server, [...]"
 1305                  // So say hello again
 1306                  $hello_result = $this->hello($local_host);
 1307   
 1308                  if (!is_null($hello_result))
 1309                  {
 1310                      return $hello_result;
 1311                  }
 1312              }
 1313          }
 1314   
 1315          // If we are not authenticated yet, something might be wrong if no username and passwd passed
 1316          if (!$username || !$password)
 1317          {
 1318              return false;
 1319          }
 1320   
 1321          if (!isset($this->commands['AUTH']))
 1322          {
 1323              return (isset($user->lang['SMTP_NO_AUTH_SUPPORT'])) ? $user->lang['SMTP_NO_AUTH_SUPPORT'] : 'SMTP server does not support authentication';
 1324          }
 1325   
 1326          // Get best authentication method
 1327          $available_methods = explode(' ', $this->commands['AUTH']);
 1328   
 1329          // Define the auth ordering if the default auth method was not found
 1330          $auth_methods = array('PLAIN', 'LOGIN', 'CRAM-MD5', 'DIGEST-MD5');
 1331          $method = '';
 1332   
 1333          if (in_array($default_auth_method, $available_methods))
 1334          {
 1335              $method = $default_auth_method;
 1336          }
 1337          else
 1338          {
 1339              foreach ($auth_methods as $_method)
 1340              {
 1341                  if (in_array($_method, $available_methods))
 1342                  {
 1343                      $method = $_method;
 1344                      break;
 1345                  }
 1346              }
 1347          }
 1348   
 1349          if (!$method)
 1350          {
 1351              return (isset($user->lang['NO_SUPPORTED_AUTH_METHODS'])) ? $user->lang['NO_SUPPORTED_AUTH_METHODS'] : 'No supported authentication methods';
 1352          }
 1353   
 1354          $method = strtolower(str_replace('-', '_', $method));
 1355          return $this->$method($username, $password);
 1356      }
 1357   
 1358      /**
 1359      * SMTP EHLO/HELO
 1360      *
 1361      * @return mixed        Null if the authentication process is supposed to continue
 1362      *                    False if already authenticated
 1363      *                    Error message (string) otherwise
 1364      */
 1365      protected function hello($hostname)
 1366      {
 1367          // Try EHLO first
 1368          $this->server_send("EHLO $hostname");
 1369          if ($err_msg = $this->server_parse('250', __LINE__))
 1370          {
 1371              // a 503 response code means that we're already authenticated
 1372              if ($this->numeric_response_code == 503)
 1373              {
 1374                  return false;
 1375              }
 1376   
 1377              // If EHLO fails, we try HELO
 1378              $this->server_send("HELO $hostname");
 1379              if ($err_msg = $this->server_parse('250', __LINE__))
 1380              {
 1381                  return ($this->numeric_response_code == 503) ? false : $err_msg;
 1382              }
 1383          }
 1384   
 1385          foreach ($this->responses as $response)
 1386          {
 1387              $response = explode(' ', $response);
 1388              $response_code = $response[0];
 1389              unset($response[0]);
 1390              $this->commands[$response_code] = implode(' ', $response);
 1391          }
 1392      }
 1393   
 1394      /**
 1395      * SMTP STARTTLS (RFC 3207)
 1396      *
 1397      * @return bool        Returns true if TLS was started
 1398      *                    Otherwise false
 1399      */
 1400      protected function starttls()
 1401      {
 1402          if (!function_exists('stream_socket_enable_crypto'))
 1403          {
 1404              return false;
 1405          }
 1406   
 1407          if (!isset($this->commands['STARTTLS']))
 1408          {
 1409              return false;
 1410          }
 1411   
 1412          $this->server_send('STARTTLS');
 1413   
 1414          if ($err_msg = $this->server_parse('220', __LINE__))
 1415          {
 1416              return false;
 1417          }
 1418   
 1419          $result = false;
 1420          $stream_meta = stream_get_meta_data($this->socket);
 1421   
 1422          if (socket_set_blocking($this->socket, 1))
 1423          {
 1424              $result = stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
 1425              socket_set_blocking($this->socket, (int) $stream_meta['blocked']);
 1426          }
 1427   
 1428          return $result;
 1429      }
 1430   
 1431      /**
 1432      * Pop before smtp authentication
 1433      */
 1434      function pop_before_smtp($hostname, $username, $password)
 1435      {
 1436          global $user;
 1437   
 1438          if (!$this->socket = @fsockopen($hostname, 110, $errno, $errstr, 10))
 1439          {
 1440              if ($errstr)
 1441              {
 1442                  $errstr = utf8_convert_message($errstr);
 1443              }
 1444   
 1445              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";
 1446          }
 1447   
 1448          $this->server_send("USER $username", true);
 1449          if ($err_msg = $this->server_parse('+OK', __LINE__))
 1450          {
 1451              return $err_msg;
 1452          }
 1453   
 1454          $this->server_send("PASS $password", true);
 1455          if ($err_msg = $this->server_parse('+OK', __LINE__))
 1456          {
 1457              return $err_msg;
 1458          }
 1459   
 1460          $this->server_send('QUIT');
 1461          fclose($this->socket);
 1462   
 1463          return false;
 1464      }
 1465   
 1466      /**
 1467      * Plain authentication method
 1468      */
 1469      function plain($username, $password)
 1470      {
 1471          $this->server_send('AUTH PLAIN');
 1472          if ($err_msg = $this->server_parse('334', __LINE__))
 1473          {
 1474              return ($this->numeric_response_code == 503) ? false : $err_msg;
 1475          }
 1476   
 1477          $base64_method_plain = base64_encode("\0" . $username . "\0" . $password);
 1478          $this->server_send($base64_method_plain, true);
 1479          if ($err_msg = $this->server_parse('235', __LINE__))
 1480          {
 1481              return $err_msg;
 1482          }
 1483   
 1484          return false;
 1485      }
 1486   
 1487      /**
 1488      * Login authentication method
 1489      */
 1490      function login($username, $password)
 1491      {
 1492          $this->server_send('AUTH LOGIN');
 1493          if ($err_msg = $this->server_parse('334', __LINE__))
 1494          {
 1495              return ($this->numeric_response_code == 503) ? false : $err_msg;
 1496          }
 1497   
 1498          $this->server_send(base64_encode($username), true);
 1499          if ($err_msg = $this->server_parse('334', __LINE__))
 1500          {
 1501              return $err_msg;
 1502          }
 1503   
 1504          $this->server_send(base64_encode($password), true);
 1505          if ($err_msg = $this->server_parse('235', __LINE__))
 1506          {
 1507              return $err_msg;
 1508          }
 1509   
 1510          return false;
 1511      }
 1512   
 1513      /**
 1514      * cram_md5 authentication method
 1515      */
 1516      function cram_md5($username, $password)
 1517      {
 1518          $this->server_send('AUTH CRAM-MD5');
 1519          if ($err_msg = $this->server_parse('334', __LINE__))
 1520          {
 1521              return ($this->numeric_response_code == 503) ? false : $err_msg;
 1522          }
 1523   
 1524          $md5_challenge = base64_decode($this->responses[0]);
 1525          $password = (strlen($password) > 64) ? pack('H32', md5($password)) : ((strlen($password) < 64) ? str_pad($password, 64, chr(0)) : $password);
 1526          $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))));
 1527   
 1528          $base64_method_cram_md5 = base64_encode($username . ' ' . $md5_digest);
 1529   
 1530          $this->server_send($base64_method_cram_md5, true);
 1531          if ($err_msg = $this->server_parse('235', __LINE__))
 1532          {
 1533              return $err_msg;
 1534          }
 1535   
 1536          return false;
 1537      }
 1538   
 1539      /**
 1540      * digest_md5 authentication method
 1541      * A real pain in the ***
 1542      */
 1543      function digest_md5($username, $password)
 1544      {
 1545          global $config, $user;
 1546   
 1547          $this->server_send('AUTH DIGEST-MD5');
 1548          if ($err_msg = $this->server_parse('334', __LINE__))
 1549          {
 1550              return ($this->numeric_response_code == 503) ? false : $err_msg;
 1551          }
 1552   
 1553          $md5_challenge = base64_decode($this->responses[0]);
 1554   
 1555          // Parse the md5 challenge - from AUTH_SASL (PEAR)
 1556          $tokens = array();
 1557          while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $md5_challenge, $matches))
 1558          {
 1559              // Ignore these as per rfc2831
 1560              if ($matches[1] == 'opaque' || $matches[1] == 'domain')
 1561              {
 1562                  $md5_challenge = substr($md5_challenge, strlen($matches[0]) + 1);
 1563                  continue;
 1564              }
 1565   
 1566              // Allowed multiple "realm" and "auth-param"
 1567              if (!empty($tokens[$matches[1]]) && ($matches[1] == 'realm' || $matches[1] == 'auth-param'))
 1568              {
 1569                  if (is_array($tokens[$matches[1]]))
 1570                  {
 1571                      $tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
 1572                  }
 1573                  else
 1574                  {
 1575                      $tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2]));
 1576                  }
 1577              }
 1578              else if (!empty($tokens[$matches[1]])) // Any other multiple instance = failure
 1579              {
 1580                  $tokens = array();
 1581                  break;
 1582              }
 1583              else
 1584              {
 1585                  $tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
 1586              }
 1587   
 1588              // Remove the just parsed directive from the challenge
 1589              $md5_challenge = substr($md5_challenge, strlen($matches[0]) + 1);
 1590          }
 1591   
 1592          // Realm
 1593          if (empty($tokens['realm']))
 1594          {
 1595              $tokens['realm'] = (function_exists('php_uname')) ? php_uname('n') : $user->host;
 1596          }
 1597   
 1598          // Maxbuf
 1599          if (empty($tokens['maxbuf']))
 1600          {
 1601              $tokens['maxbuf'] = 65536;
 1602          }
 1603   
 1604          // Required: nonce, algorithm
 1605          if (empty($tokens['nonce']) || empty($tokens['algorithm']))
 1606          {
 1607              $tokens = array();
 1608          }
 1609          $md5_challenge = $tokens;
 1610   
 1611          if (!empty($md5_challenge))
 1612          {
 1613              $str = '';
 1614              for ($i = 0; $i < 32; $i++)
 1615              {
 1616                  $str .= chr(mt_rand(0, 255));
 1617              }
 1618              $cnonce = base64_encode($str);
 1619   
 1620              $digest_uri = 'smtp/' . $config['smtp_host'];
 1621   
 1622              $auth_1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $username, $md5_challenge['realm'], $password))), $md5_challenge['nonce'], $cnonce);
 1623              $auth_2 = 'AUTHENTICATE:' . $digest_uri;
 1624              $response_value = md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($auth_1), $md5_challenge['nonce'], $cnonce, md5($auth_2)));
 1625   
 1626              $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']);
 1627          }
 1628          else
 1629          {
 1630              return (isset($user->lang['INVALID_DIGEST_CHALLENGE'])) ? $user->lang['INVALID_DIGEST_CHALLENGE'] : 'Invalid digest challenge';
 1631          }
 1632   
 1633          $base64_method_digest_md5 = base64_encode($input_string);
 1634          $this->server_send($base64_method_digest_md5, true);
 1635          if ($err_msg = $this->server_parse('334', __LINE__))
 1636          {
 1637              return $err_msg;
 1638          }
 1639   
 1640          $this->server_send(' ');
 1641          if ($err_msg = $this->server_parse('235', __LINE__))
 1642          {
 1643              return $err_msg;
 1644          }
 1645   
 1646          return false;
 1647      }
 1648  }
 1649   
 1650  /**
 1651  * Encodes the given string for proper display in UTF-8.
 1652  *
 1653  * This version is using base64 encoded data. The downside of this
 1654  * is if the mail client does not understand this encoding the user
 1655  * is basically doomed with an unreadable subject.
 1656  *
 1657  * Please note that this version fully supports RFC 2045 section 6.8.
 1658  *
 1659  * @param string $eol End of line we are using (optional to be backwards compatible)
 1660  */
 1661  function mail_encode($str, $eol = "\r\n")
 1662  {
 1663      // define start delimimter, end delimiter and spacer
 1664      $start = "=?UTF-8?B?";
 1665      $end = "?=";
 1666      $delimiter = "$eol ";
 1667   
 1668      // Maximum length is 75. $split_length *must* be a multiple of 4, but <= 75 - strlen($start . $delimiter . $end)!!!
 1669      $split_length = 60;
 1670      $encoded_str = base64_encode($str);
 1671   
 1672      // If encoded string meets the limits, we just return with the correct data.
 1673      if (strlen($encoded_str) <= $split_length)
 1674      {
 1675          return $start . $encoded_str . $end;
 1676      }
 1677   
 1678      // If there is only ASCII data, we just return what we want, correctly splitting the lines.
 1679      if (strlen($str) === utf8_strlen($str))
 1680      {
 1681          return $start . implode($end . $delimiter . $start, str_split($encoded_str, $split_length)) . $end;
 1682      }
 1683   
 1684      // UTF-8 data, compose encoded lines
 1685      $array = utf8_str_split($str);
 1686      $str = '';
 1687   
 1688      while (sizeof($array))
 1689      {
 1690          $text = '';
 1691   
 1692          while (sizeof($array) && intval((strlen($text . $array[0]) + 2) / 3) << 2 <= $split_length)
 1693          {
 1694              $text .= array_shift($array);
 1695          }
 1696   
 1697          $str .= $start . base64_encode($text) . $end . $delimiter;
 1698      }
 1699   
 1700      return substr($str, 0, -strlen($delimiter));
 1701  }
 1702   
 1703  /**
 1704  * Wrapper for sending out emails with the PHP's mail function
 1705  */
 1706  function phpbb_mail($to, $subject, $msg, $headers, $eol, &$err_msg)
 1707  {
 1708      global $config, $phpbb_root_path, $phpEx;
 1709   
 1710      // 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...
 1711      // Reference: http://bugs.php.net/bug.php?id=15841
 1712      $headers = implode($eol, $headers);
 1713   
 1714      if (!class_exists('\phpbb\error_collector'))
 1715      {
 1716          include($phpbb_root_path . 'includes/error_collector.' . $phpEx);
 1717      }
 1718   
 1719      $collector = new \phpbb\error_collector;
 1720      $collector->install();
 1721   
 1722      // On some PHP Versions mail() *may* fail if there are newlines within the subject.
 1723      // Newlines are used as a delimiter for lines in mail_encode() according to RFC 2045 section 6.8.
 1724      // 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)
 1725      $result = $config['email_function_name']($to, mail_encode($subject, ''), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $headers);
 1726   
 1727      $collector->uninstall();
 1728      $err_msg = $collector->format_errors();
 1729   
 1730      return $result;
 1731  }
 1732