/lib/horde/framework/Horde/Imap/Client/Tokenize.php
https://gitlab.com/unofficial-mirrors/moodle · PHP · 413 lines · 234 code · 50 blank · 129 comment · 33 complexity · f270422e6d485b5c5e2a65adeca83737 MD5 · raw file
- <?php
- /**
- * Copyright 2012-2017 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category Horde
- * @copyright 2012-2017 Horde LLC
- * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package Imap_Client
- */
- /**
- * Tokenization of an IMAP data stream.
- *
- * NOTE: This class is NOT intended to be accessed outside of this package.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @copyright 2012-2017 Horde LLC
- * @internal
- * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package Imap_Client
- *
- * @property-read boolean $eos Has the end of the stream been reached?
- */
- class Horde_Imap_Client_Tokenize implements Iterator
- {
- /**
- * Current data.
- *
- * @var mixed
- */
- protected $_current = false;
- /**
- * Current key.
- *
- * @var integer
- */
- protected $_key = false;
- /**
- * Sublevel.
- *
- * @var integer
- */
- protected $_level = false;
- /**
- * Array of literal stream objects.
- *
- * @var array
- */
- protected $_literals = array();
- /**
- * Return Horde_Stream object for literal tokens?
- *
- * @var boolean
- */
- protected $_literalStream = false;
- /**
- * next() modifiers.
- *
- * @var array
- */
- protected $_nextModify = array();
- /**
- * Data stream.
- *
- * @var Horde_Stream
- */
- protected $_stream;
- /**
- * Constructor.
- *
- * @param mixed $data Data to add (string, resource, or Horde_Stream
- * object).
- */
- public function __construct($data = null)
- {
- $this->_stream = new Horde_Stream_Temp();
- if (!is_null($data)) {
- $this->add($data);
- }
- }
- /**
- */
- public function __clone()
- {
- throw new LogicException('Object can not be cloned.');
- }
- /**
- */
- public function __get($name)
- {
- switch ($name) {
- case 'eos':
- return $this->_stream->eof();
- }
- }
- /**
- */
- public function __sleep()
- {
- throw new LogicException('Object can not be serialized.');
- }
- /**
- */
- public function __toString()
- {
- $pos = $this->_stream->pos();
- $out = $this->_current . ' ' . $this->_stream->getString();
- $this->_stream->seek($pos, false);
- return $out;
- }
- /**
- * Add data to buffer.
- *
- * @param mixed $data Data to add (string, resource, or Horde_Stream
- * object).
- */
- public function add($data)
- {
- $this->_stream->add($data);
- }
- /**
- * Add data to literal stream at the current position.
- *
- * @param mixed $data Data to add (string, resource, or Horde_Stream
- * object).
- */
- public function addLiteralStream($data)
- {
- $pos = $this->_stream->pos();
- if (!isset($this->_literals[$pos])) {
- $this->_literals[$pos] = new Horde_Stream_Temp();
- }
- $this->_literals[$pos]->add($data);
- }
- /**
- * Flush the remaining entries left in the iterator.
- *
- * @param boolean $return If true, return entries. Only returns entries
- * on the current level.
- * @param boolean $sublevel Only flush items in current sublevel?
- *
- * @return array The entries if $return is true.
- */
- public function flushIterator($return = true, $sublevel = true)
- {
- $out = array();
- if ($return) {
- $this->_nextModify = array(
- 'level' => $sublevel ? $this->_level : 0,
- 'out' => array()
- );
- $this->next();
- $out = $this->_nextModify['out'];
- $this->_nextModify = array();
- } elseif ($sublevel && $this->_level) {
- $this->_nextModify = array(
- 'level' => $this->_level
- );
- $this->next();
- $this->_nextModify = array();
- } else {
- $this->_stream->end();
- $this->_stream->getChar();
- $this->_current = $this->_key = $this->_level = false;
- }
- return $out;
- }
- /**
- * Return literal length data located at the end of the stream.
- *
- * @return mixed Null if no literal data found, or an array with these
- * keys:
- * - binary: (boolean) True if this is a literal8.
- * - length: (integer) Length of the literal.
- */
- public function getLiteralLength()
- {
- if ($this->_stream->substring(-1, 1) === '}') {
- $literal_data = $this->_stream->getString(
- $this->_stream->search('{', true) - 1
- );
- $literal_len = substr($literal_data, 2, -1);
- if (is_numeric($literal_len)) {
- return array(
- 'binary' => ($literal_data[0] === '~'),
- 'length' => intval($literal_len)
- );
- }
- }
- return null;
- }
- /* Iterator methods. */
- /**
- */
- public function current()
- {
- return $this->_current;
- }
- /**
- */
- public function key()
- {
- return $this->_key;
- }
- /**
- * @return mixed Either a string, boolean (true for open paren, false for
- * close paren/EOS), Horde_Stream object, or null.
- */
- public function next()
- {
- $level = isset($this->_nextModify['level'])
- ? $this->_nextModify['level']
- : null;
- /* Directly access stream here to drastically reduce the number of
- * getChar() calls we would have to make. */
- $stream = $this->_stream->stream;
- do {
- $check_len = true;
- $in_quote = $text = $binary = false;
- while (($c = fgetc($stream)) !== false) {
- switch ($c) {
- case '\\':
- $text .= $in_quote
- ? fgetc($stream)
- : $c;
- break;
- case '"':
- if ($in_quote) {
- $check_len = false;
- break 2;
- }
- $in_quote = true;
- /* Set $text to non-false (could be empty string). */
- $text = '';
- break;
- default:
- if ($in_quote) {
- $text .= $c;
- break;
- }
- switch ($c) {
- case '(':
- ++$this->_level;
- $check_len = false;
- $text = true;
- break 3;
- case ')':
- if ($text === false) {
- --$this->_level;
- $check_len = $text = false;
- } else {
- $this->_stream->seek(-1);
- }
- break 3;
- case '~':
- // Ignore binary string identifier. PHP strings are
- // binary-safe. But keep it if it is not used as string
- // identifier.
- $binary = true;
- $text .= $c;
- continue;
- case '{':
- if ($binary) {
- $text = substr($text, 0, -1);
- }
- $literal_len = intval($this->_stream->getToChar('}'));
- $pos = $this->_stream->pos();
- if (isset($this->_literals[$pos])) {
- $text = $this->_literals[$pos];
- if (!$this->_literalStream) {
- $text = strval($text);
- }
- } elseif ($this->_literalStream) {
- $text = new Horde_Stream_Temp();
- while (($literal_len > 0) && !feof($stream)) {
- $part = $this->_stream->substring(
- 0,
- min($literal_len, 8192)
- );
- $text->add($part);
- $literal_len -= strlen($part);
- }
- } else {
- $text = $this->_stream->substring(0, $literal_len);
- }
- $check_len = false;
- break 3;
- case ' ':
- if ($text !== false) {
- break 3;
- }
- break;
- default:
- $text .= $c;
- break;
- }
- break;
- }
- $binary = false;
- }
- if ($check_len) {
- switch (strlen($text)) {
- case 0:
- $text = false;
- break;
- case 3:
- if (strcasecmp($text, 'NIL') === 0) {
- $text = null;
- }
- break;
- }
- }
- if (($text === false) && feof($stream)) {
- $this->_key = $this->_level = false;
- break;
- }
- ++$this->_key;
- if (is_null($level) || ($level > $this->_level)) {
- break;
- }
- if (($level === $this->_level) && !is_bool($text)) {
- $this->_nextModify['out'][] = $text;
- }
- } while (true);
- $this->_current = $text;
- return $text;
- }
- /**
- * Force return of literal data as stream, if next token.
- *
- * @see next()
- */
- public function nextStream()
- {
- $changed = $this->_literalStream;
- $this->_literalStream = true;
- $out = $this->next();
- if ($changed) {
- $this->_literalStream = false;
- }
- return $out;
- }
- /**
- */
- public function rewind()
- {
- $this->_stream->rewind();
- $this->_current = false;
- $this->_key = -1;
- $this->_level = 0;
- }
- /**
- */
- public function valid()
- {
- return ($this->_level !== false);
- }
- }