Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  /**
   3   * Copyright 2008-2017 Horde LLC (http://www.horde.org/)
   4   *
   5   * getBaseSubject() code adapted from imap-base-subject.c (Dovecot 1.2)
   6   *   Original code released under the LGPL-2.1
   7   *   Copyright (c) 2002-2008 Timo Sirainen <tss@iki.fi>
   8   *
   9   * See the enclosed file LICENSE for license information (LGPL). If you
  10   * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  11   *
  12   * @category  Horde
  13   * @copyright 2002-2008 Timo Sirainen
  14   * @copyright 2008-2017 Horde LLC
  15   * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
  16   * @package   Imap_Client
  17   */
  18  
  19  /**
  20   * Determines the "base subject" of a string (RFC 5256 [2.1]).
  21   *
  22   * @author    Timo Sirainen <tss@iki.fi>
  23   * @author    Michael Slusarz <slusarz@horde.org>
  24   * @category  Horde
  25   * @copyright 2002-2008 Timo Sirainen
  26   * @copyright 2011-2017 Horde LLC
  27   * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
  28   * @package   Imap_Client
  29   */
  30  class Horde_Imap_Client_Data_BaseSubject
  31  {
  32      /**
  33       * The base subject.
  34       *
  35       * @var string
  36       */
  37      protected $_subject;
  38  
  39      /**
  40       * Constructor.
  41       *
  42       * @param string $str  The subject string.
  43       * @param array $opts  Additional options:
  44       *   - keepblob: (boolean) Don't remove any "blob" information (i.e. text
  45       *               leading text between square brackets) from string.
  46       *
  47       * @return string  The cleaned up subject string.
  48       */
  49      public function __construct($str, array $opts = array())
  50      {
  51          // Rule 1a: MIME decode.
  52          $str = Horde_Mime::decode($str);
  53  
  54          // Rule 1b: Remove superfluous whitespace.
  55          $str = preg_replace("/[\t\r\n ]+/", ' ', $str);
  56  
  57          do {
  58              /* (2) Remove all trailing text of the subject that matches the
  59               * the subj-trailer ABNF, repeat until no more matches are
  60               * possible. */
  61              $str = preg_replace("/(?:\s*\(fwd\)\s*)+$/i", '', $str);
  62  
  63              do {
  64                  /* (3) Remove all prefix text of the subject that matches the
  65                   * subj-leader ABNF. */
  66                  $found = $this->_removeSubjLeader($str, !empty($opts['keepblob']));
  67  
  68                  /* (4) If there is prefix text of the subject that matches
  69                   * the subj-blob ABNF, and removing that prefix leaves a
  70                   * non-empty subj-base, then remove the prefix text. */
  71                  $found = (empty($opts['keepblob']) && $this->_removeBlobWhenNonempty($str)) || $found;
  72  
  73                  /* (5) Repeat (3) and (4) until no matches remain. */
  74              } while ($found);
  75  
  76              /* (6) If the resulting text begins with the subj-fwd-hdr ABNF and
  77               * ends with the subj-fwd-trl ABNF, remove the subj-fwd-hdr and
  78               * subj-fwd-trl and repeat from step (2). */
  79          } while ($this->_removeSubjFwdHdr($str));
  80  
  81          $this->_subject = strval($str);
  82      }
  83  
  84      /**
  85       * Return the "base subject" defined in RFC 5256 [2.1].
  86       *
  87       * @return string  The base subject.
  88       */
  89      public function __toString()
  90      {
  91          return $this->_subject;
  92      }
  93  
  94      /**
  95       * Remove all prefix text of the subject that matches the subj-leader
  96       * ABNF.
  97       *
  98       * @param string &$str       The subject string.
  99       * @param boolean $keepblob  Remove blob information?
 100       *
 101       * @return boolean  True if string was altered.
 102       */
 103      protected function _removeSubjLeader(&$str, $keepblob = false)
 104      {
 105          $ret = false;
 106  
 107          if (!strlen($str)) {
 108              return $ret;
 109          }
 110  
 111          if ($len = strspn($str, " \t")) {
 112              $str = substr($str, $len);
 113              $ret = true;
 114          }
 115  
 116          $i = 0;
 117  
 118          if (!$keepblob) {
 119              while (isset($str[$i]) && ($str[$i] === '[')) {
 120                  if (($i = $this->_removeBlob($str, $i)) === false) {
 121                      return $ret;
 122                  }
 123              }
 124          }
 125  
 126          if (stripos($str, 're', $i) === 0) {
 127              $i += 2;
 128          } elseif (stripos($str, 'fw', $i) === 0) {
 129              $i += (stripos($str, 'fwd', $i) === 0) ? 3 : 2;
 130          } else {
 131              return $ret;
 132          }
 133  
 134          $i += strspn($str, " \t", $i);
 135  
 136          if (!$keepblob) {
 137              while (isset($str[$i]) && ($str[$i] === '[')) {
 138                  if (($i = $this->_removeBlob($str, $i)) === false) {
 139                      return $ret;
 140                  }
 141              }
 142          }
 143  
 144          if (!isset($str[$i]) || ($str[$i] !== ':')) {
 145              return $ret;
 146          }
 147  
 148          $str = substr($str, ++$i);
 149  
 150          return true;
 151      }
 152  
 153      /**
 154       * Remove "[...]" text.
 155       *
 156       * @param string $str  The subject string.
 157       * @param integer $i   Current position.
 158       *
 159       * @return boolean|integer  False if blob was not found, otherwise the
 160       *                          string position of the first non-blob char.
 161       */
 162      protected function _removeBlob($str, $i)
 163      {
 164          if ($str[$i] !== '[') {
 165              return false;
 166          }
 167  
 168          ++$i;
 169  
 170          for ($cnt = strlen($str); $i < $cnt; ++$i) {
 171              if ($str[$i] === ']') {
 172                  break;
 173              }
 174  
 175              if ($str[$i] === '[') {
 176                  return false;
 177              }
 178          }
 179  
 180          if ($i === ($cnt - 1)) {
 181              return false;
 182          }
 183  
 184          ++$i;
 185  
 186          if ($str[$i] === ' ') {
 187              ++$i;
 188          }
 189  
 190          return $i;
 191      }
 192  
 193      /**
 194       * Remove "[...]" text if it doesn't result in the subject becoming
 195       * empty.
 196       *
 197       * @param string &$str  The subject string.
 198       *
 199       * @return boolean  True if string was altered.
 200       */
 201      protected function _removeBlobWhenNonempty(&$str)
 202      {
 203          if ($str &&
 204              ($str[0] === '[') &&
 205              (($i = $this->_removeBlob($str, 0)) !== false) &&
 206              ($i !== strlen($str))) {
 207              $str = substr($str, $i);
 208              return true;
 209          }
 210  
 211          return false;
 212      }
 213  
 214      /**
 215       * Remove a "[fwd: ... ]" string.
 216       *
 217       * @param string &$str  The subject string.
 218       *
 219       * @return boolean  True if string was altered.
 220       */
 221      protected function _removeSubjFwdHdr(&$str)
 222      {
 223          if ((stripos($str, '[fwd:') !== 0) || (substr($str, -1) !== ']')) {
 224              return false;
 225          }
 226  
 227          $str = substr($str, 5, -1);
 228          return true;
 229      }
 230  
 231  }