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