Verzeichnisstruktur phpBB-3.0.0


Veröffentlicht
12.12.2007

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:50 - Dateigröße: 35.57 KiB


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