Differences Between: [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 /** 3 * Copyright 2012-2017 Horde LLC (http://www.horde.org/) 4 * 5 * See the enclosed file LICENSE for license information (LGPL). If you 6 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 7 * 8 * @category Horde 9 * @copyright 2012-2017 Horde LLC 10 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 11 * @package Imap_Client 12 */ 13 14 /** 15 * Tokenization of an IMAP data stream. 16 * 17 * NOTE: This class is NOT intended to be accessed outside of this package. 18 * There is NO guarantees that the API of this class will not change across 19 * versions. 20 * 21 * @author Michael Slusarz <slusarz@horde.org> 22 * @category Horde 23 * @copyright 2012-2017 Horde LLC 24 * @internal 25 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 26 * @package Imap_Client 27 * 28 * @property-read boolean $eos Has the end of the stream been reached? 29 */ 30 class Horde_Imap_Client_Tokenize implements Iterator 31 { 32 /** 33 * Current data. 34 * 35 * @var mixed 36 */ 37 protected $_current = false; 38 39 /** 40 * Current key. 41 * 42 * @var integer 43 */ 44 protected $_key = false; 45 46 /** 47 * Sublevel. 48 * 49 * @var integer 50 */ 51 protected $_level = false; 52 53 /** 54 * Array of literal stream objects. 55 * 56 * @var array 57 */ 58 protected $_literals = array(); 59 60 /** 61 * Return Horde_Stream object for literal tokens? 62 * 63 * @var boolean 64 */ 65 protected $_literalStream = false; 66 67 /** 68 * next() modifiers. 69 * 70 * @var array 71 */ 72 protected $_nextModify = array(); 73 74 /** 75 * Data stream. 76 * 77 * @var Horde_Stream 78 */ 79 protected $_stream; 80 81 /** 82 * Constructor. 83 * 84 * @param mixed $data Data to add (string, resource, or Horde_Stream 85 * object). 86 */ 87 public function __construct($data = null) 88 { 89 $this->_stream = new Horde_Stream_Temp(); 90 91 if (!is_null($data)) { 92 $this->add($data); 93 } 94 } 95 96 /** 97 */ 98 public function __clone() 99 { 100 throw new LogicException('Object can not be cloned.'); 101 } 102 103 /** 104 */ 105 public function __get($name) 106 { 107 switch ($name) { 108 case 'eos': 109 return $this->_stream->eof(); 110 } 111 } 112 113 /** 114 */ 115 public function __sleep() 116 { 117 throw new LogicException('Object can not be serialized.'); 118 } 119 120 /** 121 */ 122 public function __toString() 123 { 124 $pos = $this->_stream->pos(); 125 $out = $this->_current . ' ' . $this->_stream->getString(); 126 $this->_stream->seek($pos, false); 127 return $out; 128 } 129 130 /** 131 * Add data to buffer. 132 * 133 * @param mixed $data Data to add (string, resource, or Horde_Stream 134 * object). 135 */ 136 public function add($data) 137 { 138 $this->_stream->add($data); 139 } 140 141 /** 142 * Add data to literal stream at the current position. 143 * 144 * @param mixed $data Data to add (string, resource, or Horde_Stream 145 * object). 146 */ 147 public function addLiteralStream($data) 148 { 149 $pos = $this->_stream->pos(); 150 if (!isset($this->_literals[$pos])) { 151 $this->_literals[$pos] = new Horde_Stream_Temp(); 152 } 153 $this->_literals[$pos]->add($data); 154 } 155 156 /** 157 * Flush the remaining entries left in the iterator. 158 * 159 * @param boolean $return If true, return entries. Only returns entries 160 * on the current level. 161 * @param boolean $sublevel Only flush items in current sublevel? 162 * 163 * @return array The entries if $return is true. 164 */ 165 public function flushIterator($return = true, $sublevel = true) 166 { 167 $out = array(); 168 169 if ($return) { 170 $this->_nextModify = array( 171 'level' => $sublevel ? $this->_level : 0, 172 'out' => array() 173 ); 174 $this->next(); 175 $out = $this->_nextModify['out']; 176 $this->_nextModify = array(); 177 } elseif ($sublevel && $this->_level) { 178 $this->_nextModify = array( 179 'level' => $this->_level 180 ); 181 $this->next(); 182 $this->_nextModify = array(); 183 } else { 184 $this->_stream->end(); 185 $this->_stream->getChar(); 186 $this->_current = $this->_key = $this->_level = false; 187 } 188 189 return $out; 190 } 191 192 /** 193 * Return literal length data located at the end of the stream. 194 * 195 * @return mixed Null if no literal data found, or an array with these 196 * keys: 197 * - binary: (boolean) True if this is a literal8. 198 * - length: (integer) Length of the literal. 199 */ 200 public function getLiteralLength() 201 { 202 if ($this->_stream->substring(-1, 1) === '}') { 203 $literal_data = $this->_stream->getString( 204 $this->_stream->search('{', true) - 1 205 ); 206 $literal_len = substr($literal_data, 2, -1); 207 208 if (is_numeric($literal_len)) { 209 return array( 210 'binary' => ($literal_data[0] === '~'), 211 'length' => intval($literal_len) 212 ); 213 } 214 } 215 216 return null; 217 } 218 219 /* Iterator methods. */ 220 221 /** 222 */ 223 public function current() 224 { 225 return $this->_current; 226 } 227 228 /** 229 */ 230 public function key() 231 { 232 return $this->_key; 233 } 234 235 /** 236 * @return mixed Either a string, boolean (true for open paren, false for 237 * close paren/EOS), Horde_Stream object, or null. 238 */ 239 public function next() 240 { 241 $level = isset($this->_nextModify['level']) 242 ? $this->_nextModify['level'] 243 : null; 244 /* Directly access stream here to drastically reduce the number of 245 * getChar() calls we would have to make. */ 246 $stream = $this->_stream->stream; 247 248 do { 249 $check_len = true; 250 $in_quote = $text = $binary = false; 251 252 while (($c = fgetc($stream)) !== false) { 253 switch ($c) { 254 case '\\': 255 $text .= $in_quote 256 ? fgetc($stream) 257 : $c; 258 break; 259 260 case '"': 261 if ($in_quote) { 262 $check_len = false; 263 break 2; 264 } 265 $in_quote = true; 266 /* Set $text to non-false (could be empty string). */ 267 $text = ''; 268 break; 269 270 default: 271 if ($in_quote) { 272 $text .= $c; 273 break; 274 } 275 276 switch ($c) { 277 case '(': 278 ++$this->_level; 279 $check_len = false; 280 $text = true; 281 break 3; 282 283 case ')': 284 if ($text === false) { 285 --$this->_level; 286 $check_len = $text = false; 287 } else { 288 $this->_stream->seek(-1); 289 } 290 break 3; 291 292 case '~': 293 // Ignore binary string identifier. PHP strings are 294 // binary-safe. But keep it if it is not used as string 295 // identifier. 296 $binary = true; 297 $text .= $c; 298 continue 3; 299 300 case '{': 301 if ($binary) { 302 $text = substr($text, 0, -1); 303 } 304 $literal_len = intval($this->_stream->getToChar('}')); 305 $pos = $this->_stream->pos(); 306 if (isset($this->_literals[$pos])) { 307 $text = $this->_literals[$pos]; 308 if (!$this->_literalStream) { 309 $text = strval($text); 310 } 311 } elseif ($this->_literalStream) { 312 $text = new Horde_Stream_Temp(); 313 while (($literal_len > 0) && !feof($stream)) { 314 $part = $this->_stream->substring( 315 0, 316 min($literal_len, 8192) 317 ); 318 $text->add($part); 319 $literal_len -= strlen($part); 320 } 321 } else { 322 $text = $this->_stream->substring(0, $literal_len); 323 } 324 $check_len = false; 325 break 3; 326 327 case ' ': 328 if ($text !== false) { 329 break 3; 330 } 331 break; 332 333 default: 334 $text .= $c; 335 break; 336 } 337 break; 338 } 339 $binary = false; 340 } 341 342 if ($check_len) { 343 switch (strlen($text)) { 344 case 0: 345 $text = false; 346 break; 347 348 case 3: 349 if (strcasecmp($text, 'NIL') === 0) { 350 $text = null; 351 } 352 break; 353 } 354 } 355 356 if (($text === false) && feof($stream)) { 357 $this->_key = $this->_level = false; 358 break; 359 } 360 361 ++$this->_key; 362 363 if (is_null($level) || ($level > $this->_level)) { 364 break; 365 } 366 367 if (($level === $this->_level) && !is_bool($text)) { 368 $this->_nextModify['out'][] = $text; 369 } 370 } while (true); 371 372 $this->_current = $text; 373 374 return $text; 375 } 376 377 /** 378 * Force return of literal data as stream, if next token. 379 * 380 * @see next() 381 */ 382 public function nextStream() 383 { 384 $changed = $this->_literalStream; 385 $this->_literalStream = true; 386 387 $out = $this->next(); 388 389 if ($changed) { 390 $this->_literalStream = false; 391 } 392 393 return $out; 394 } 395 396 /** 397 */ 398 public function rewind() 399 { 400 $this->_stream->rewind(); 401 $this->_current = false; 402 $this->_key = -1; 403 $this->_level = 0; 404 } 405 406 /** 407 */ 408 public function valid() 409 { 410 return ($this->_level !== false); 411 } 412 413 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body