See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
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 #[\ReturnTypeWillChange] 137 public function offsetExists($offset) 138 { 139 return isset($this->_parsed[$offset]); 140 } 141 142 /** 143 */ 144 #[\ReturnTypeWillChange] 145 public function offsetGet($offset) 146 { 147 if (!isset($this->_parsed[$offset])) { 148 return null; 149 } 150 151 $p = $this->_parsed[$offset]; 152 $end = isset($this->_parsed[$offset + 1]) 153 ? $this->_parsed[$offset + 1]['start'] 154 : null; 155 $fd = fopen('php://temp', 'w+'); 156 157 fseek($this->_data, $p['start']); 158 while (!feof($this->_data)) { 159 $line = fgets($this->_data); 160 if ($end && (ftell($this->_data) >= $end)) { 161 break; 162 } 163 164 fwrite( 165 $fd, 166 (($p['date'] !== false) && substr($line, 0, 6) == '>From ') 167 ? substr($line, 1) 168 : $line 169 ); 170 } 171 172 $out = array( 173 'data' => $fd, 174 'date' => ($p['date'] === false) ? null : $p['date'], 175 'size' => intval(ftell($fd)) 176 ); 177 rewind($fd); 178 179 return $out; 180 } 181 182 /** 183 */ 184 #[\ReturnTypeWillChange] 185 public function offsetSet($offset, $value) 186 { 187 // NOOP 188 } 189 190 /** 191 */ 192 #[\ReturnTypeWillChange] 193 public function offsetUnset($offset) 194 { 195 // NOOP 196 } 197 198 /* Countable methods. */ 199 200 /** 201 * Index count. 202 * 203 * @return integer The number of messages. 204 */ 205 #[\ReturnTypeWillChange] 206 public function count() 207 { 208 return count($this->_parsed); 209 } 210 211 /* Magic methods. */ 212 213 /** 214 * String representation of the object. 215 * 216 * @return string String representation. 217 */ 218 public function __toString() 219 { 220 rewind($this->_data); 221 return stream_get_contents($this->_data); 222 } 223 224 /* Iterator methods. */ 225 226 #[\ReturnTypeWillChange] 227 public function current() 228 { 229 $key = $this->key(); 230 231 return is_null($key) 232 ? null 233 : $this[$key]; 234 } 235 236 #[\ReturnTypeWillChange] 237 public function key() 238 { 239 return key($this->_parsed); 240 } 241 242 #[\ReturnTypeWillChange] 243 public function next() 244 { 245 if ($this->valid()) { 246 next($this->_parsed); 247 } 248 } 249 250 #[\ReturnTypeWillChange] 251 public function rewind() 252 { 253 reset($this->_parsed); 254 } 255 256 #[\ReturnTypeWillChange] 257 public function valid() 258 { 259 return !is_null($this->key()); 260 } 261 262 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body