Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

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