Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.
<?php
/**
 * Copyright 2011-2017 Horde LLC (http://www.horde.org/)
 *
 * See the enclosed file LICENSE for license information (BSD). If you
 * did not receive this file, see http://www.horde.org/licenses/bsd.
 *
 * @category  Horde
 * @copyright 2011-2017 Horde LLC
 * @license   http://www.horde.org/licenses/bsd New BSD License
 * @package   Mail
 */

/**
 * This object allows easy access to parsing mbox data (RFC 4155).
 *
 * See:
 * http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/mail-mbox-formats
 *
 * @author    Michael Slusarz <slusarz@horde.org>
 * @category  Horde
 * @copyright 2011-2017 Horde LLC
 * @license   http://www.horde.org/licenses/bsd New BSD License
 * @package   Mail
 * @since     2.5.0
 */
class Horde_Mail_Mbox_Parse
implements ArrayAccess, Countable, Iterator
{
    /**
     * Data stream.
     *
     * @var resource
     */
    protected $_data;

    /**
     * Parsed data. Each entry is an array containing 3 keys:
     *   - date: (mixed) Date information, in DateTime object. Null if date
     *           cannot be parsed. False if message is not MBOX data.
     *   - start: (integer) Start boundary.
     *
     * @var array
     */
    protected $_parsed = array();

    /**
     * Constructor.
     *
     * @param mixed $data     The mbox data. Either a resource or a filename
     *                        as interpreted by fopen() (string).
     * @param integer $limit  Limit to this many messages; additional messages
     *                        will throw an exception.
     *
     * @throws Horde_Mail_Parse_Exception
     */
    public function __construct($data, $limit = null)
    {
        $this->_data = is_resource($data)
            ? $data
            : @fopen($data, 'r');

        if ($this->_data === false) {
            throw new Horde_Mail_Exception(
                Horde_Mail_Translation::t("Could not parse mailbox data.")
            );
        }

        rewind($this->_data);

        $i = 0;
        $last_line = null;
        /* Is this a MBOX format file? */
        $mbox = false;

        while (!feof($this->_data)) {
            if (is_null($last_line)) {
                $start = ftell($this->_data);
            }

            $line = fgets($this->_data);

            if (is_null($last_line)) {
                ltrim($line);
            }

            if (substr($line, 0, 5) == 'From ') {
                if (is_null($last_line)) {
                    /* This file is in MBOX format. */
                    $mbox = true;
                } elseif (!$mbox || (trim($last_line) !== '')) {
                    continue;
                }

                if ($limit && ($i++ > $limit)) {
                    throw new Horde_Mail_Exception(
                        sprintf(
                            Horde_Mail_Translation::t("Imported mailbox contains more than enforced limit of %u messages."),
                            $limit
                        )
                    );
                }

                $from_line = explode(' ', $line, 3);
                try {
                    $date = new DateTime($from_line[2]);
                } catch (Exception $e) {
                    $date = null;
                }

                $this->_parsed[] = array(
                    'date' => $date,
                    'start' => ftell($this->_data)
                );
            }

            /* Strip all empty lines before first data. */
            if (!is_null($last_line) || (trim($line) !== '')) {
                $last_line = $line;
            }
        }

        /* This was a single message, not a MBOX file. */
        if (empty($this->_parsed)) {
            $this->_parsed[] = array(
                'date' => false,
                'start' => $start
            );
        }
    }

    /* ArrayAccess methods. */

    /**
     */
> #[\ReturnTypeWillChange]
public function offsetExists($offset) { return isset($this->_parsed[$offset]); } /** */
> #[\ReturnTypeWillChange]
public function offsetGet($offset) { if (!isset($this->_parsed[$offset])) { return null; } $p = $this->_parsed[$offset]; $end = isset($this->_parsed[$offset + 1]) ? $this->_parsed[$offset + 1]['start'] : null; $fd = fopen('php://temp', 'w+'); fseek($this->_data, $p['start']); while (!feof($this->_data)) { $line = fgets($this->_data); if ($end && (ftell($this->_data) >= $end)) { break; } fwrite( $fd, (($p['date'] !== false) && substr($line, 0, 6) == '>From ') ? substr($line, 1) : $line ); } $out = array( 'data' => $fd, 'date' => ($p['date'] === false) ? null : $p['date'], 'size' => intval(ftell($fd)) ); rewind($fd); return $out; } /** */
> #[\ReturnTypeWillChange]
public function offsetSet($offset, $value) { // NOOP } /** */
> #[\ReturnTypeWillChange]
public function offsetUnset($offset) { // NOOP } /* Countable methods. */ /** * Index count. * * @return integer The number of messages. */
> #[\ReturnTypeWillChange]
public function count() { return count($this->_parsed); } /* Magic methods. */ /** * String representation of the object. * * @return string String representation. */ public function __toString() { rewind($this->_data); return stream_get_contents($this->_data); } /* Iterator methods. */
> #[\ReturnTypeWillChange]
public function current() { $key = $this->key(); return is_null($key) ? null : $this[$key]; }
> #[\ReturnTypeWillChange]
public function key() { return key($this->_parsed); }
> #[\ReturnTypeWillChange]
public function next() { if ($this->valid()) { next($this->_parsed); } }
> #[\ReturnTypeWillChange]
public function rewind() { reset($this->_parsed); }
> #[\ReturnTypeWillChange]
public function valid() { return !is_null($this->key()); } }