Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   1  <?php
   2  /**
   3   * Copyright 2011-2017 Horde LLC (http://www.horde.org/)
   4   *
   5   * See the enclosed file LICENSE for license information (BSD). If you
   6   * did not receive this file, see http://www.horde.org/licenses/bsd.
   7   *
   8   * @category  Horde
   9   * @copyright 2011-2017 Horde LLC
  10   * @license   http://www.horde.org/licenses/bsd New BSD License
  11   * @package   Mail
  12   */
  13  
  14  /**
  15   * This object allows easy access to parsing mbox data (RFC 4155).
  16   *
  17   * See:
  18   * http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/mail-mbox-formats
  19   *
  20   * @author    Michael Slusarz <slusarz@horde.org>
  21   * @category  Horde
  22   * @copyright 2011-2017 Horde LLC
  23   * @license   http://www.horde.org/licenses/bsd New BSD License
  24   * @package   Mail
  25   * @since     2.5.0
  26   */
  27  class Horde_Mail_Mbox_Parse
  28  implements ArrayAccess, Countable, Iterator
  29  {
  30      /**
  31       * Data stream.
  32       *
  33       * @var resource
  34       */
  35      protected $_data;
  36  
  37      /**
  38       * Parsed data. Each entry is an array containing 3 keys:
  39       *   - date: (mixed) Date information, in DateTime object. Null if date
  40       *           cannot be parsed. False if message is not MBOX data.
  41       *   - start: (integer) Start boundary.
  42       *
  43       * @var array
  44       */
  45      protected $_parsed = array();
  46  
  47      /**
  48       * Constructor.
  49       *
  50       * @param mixed $data     The mbox data. Either a resource or a filename
  51       *                        as interpreted by fopen() (string).
  52       * @param integer $limit  Limit to this many messages; additional messages
  53       *                        will throw an exception.
  54       *
  55       * @throws Horde_Mail_Parse_Exception
  56       */
  57      public function __construct($data, $limit = null)
  58      {
  59          $this->_data = is_resource($data)
  60              ? $data
  61              : @fopen($data, 'r');
  62  
  63          if ($this->_data === false) {
  64              throw new Horde_Mail_Exception(
  65                  Horde_Mail_Translation::t("Could not parse mailbox data.")
  66              );
  67          }
  68  
  69          rewind($this->_data);
  70  
  71          $i = 0;
  72          $last_line = null;
  73          /* Is this a MBOX format file? */
  74          $mbox = false;
  75  
  76          while (!feof($this->_data)) {
  77              if (is_null($last_line)) {
  78                  $start = ftell($this->_data);
  79              }
  80  
  81              $line = fgets($this->_data);
  82  
  83              if (is_null($last_line)) {
  84                  ltrim($line);
  85              }
  86  
  87              if (substr($line, 0, 5) == 'From ') {
  88                  if (is_null($last_line)) {
  89                      /* This file is in MBOX format. */
  90                      $mbox = true;
  91                  } elseif (!$mbox || (trim($last_line) !== '')) {
  92                      continue;
  93                  }
  94  
  95                  if ($limit && ($i++ > $limit)) {
  96                      throw new Horde_Mail_Exception(
  97                          sprintf(
  98                              Horde_Mail_Translation::t("Imported mailbox contains more than enforced limit of %u messages."),
  99                              $limit
 100                          )
 101                      );
 102                  }
 103  
 104                  $from_line = explode(' ', $line, 3);
 105                  try {
 106                      $date = new DateTime($from_line[2]);
 107                  } catch (Exception $e) {
 108                      $date = null;
 109                  }
 110  
 111                  $this->_parsed[] = array(
 112                      'date' => $date,
 113                      'start' => ftell($this->_data)
 114                  );
 115              }
 116  
 117              /* Strip all empty lines before first data. */
 118              if (!is_null($last_line) || (trim($line) !== '')) {
 119                  $last_line = $line;
 120              }
 121          }
 122  
 123          /* This was a single message, not a MBOX file. */
 124          if (empty($this->_parsed)) {
 125              $this->_parsed[] = array(
 126                  'date' => false,
 127                  'start' => $start
 128              );
 129          }
 130      }
 131  
 132      /* ArrayAccess methods. */
 133  
 134      /**
 135       */
 136      public function offsetExists($offset)
 137      {
 138          return isset($this->_parsed[$offset]);
 139      }
 140  
 141      /**
 142       */
 143      public function offsetGet($offset)
 144      {
 145          if (!isset($this->_parsed[$offset])) {
 146              return null;
 147          }
 148  
 149          $p = $this->_parsed[$offset];
 150          $end = isset($this->_parsed[$offset + 1])
 151              ? $this->_parsed[$offset + 1]['start']
 152              : null;
 153          $fd = fopen('php://temp', 'w+');
 154  
 155          fseek($this->_data, $p['start']);
 156          while (!feof($this->_data)) {
 157              $line = fgets($this->_data);
 158              if ($end && (ftell($this->_data) >= $end)) {
 159                  break;
 160              }
 161  
 162              fwrite(
 163                  $fd,
 164                  (($p['date'] !== false) && substr($line, 0, 6) == '>From ')
 165                      ? substr($line, 1)
 166                      : $line
 167              );
 168          }
 169  
 170          $out = array(
 171              'data' => $fd,
 172              'date' => ($p['date'] === false) ? null : $p['date'],
 173              'size' => intval(ftell($fd))
 174          );
 175          rewind($fd);
 176  
 177          return $out;
 178      }
 179  
 180      /**
 181       */
 182      public function offsetSet($offset, $value)
 183      {
 184          // NOOP
 185      }
 186  
 187      /**
 188       */
 189      public function offsetUnset($offset)
 190      {
 191          // NOOP
 192      }
 193  
 194      /* Countable methods. */
 195  
 196      /**
 197       * Index count.
 198       *
 199       * @return integer  The number of messages.
 200       */
 201      public function count()
 202      {
 203          return count($this->_parsed);
 204      }
 205  
 206      /* Magic methods. */
 207  
 208      /**
 209       * String representation of the object.
 210       *
 211       * @return string  String representation.
 212       */
 213      public function __toString()
 214      {
 215          rewind($this->_data);
 216          return stream_get_contents($this->_data);
 217      }
 218  
 219      /* Iterator methods. */
 220  
 221      public function current()
 222      {
 223          $key = $this->key();
 224  
 225          return is_null($key)
 226              ? null
 227              : $this[$key];
 228      }
 229  
 230      public function key()
 231      {
 232          return key($this->_parsed);
 233      }
 234  
 235      public function next()
 236      {
 237          if ($this->valid()) {
 238              next($this->_parsed);
 239          }
 240      }
 241  
 242      public function rewind()
 243      {
 244          reset($this->_parsed);
 245      }
 246  
 247      public function valid()
 248      {
 249          return !is_null($this->key());
 250      }
 251  
 252  }