File indexing completed on 2025-03-02 05:29:34
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Mail 0017 * @subpackage Storage 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 0024 /** 0025 * @see Zend_Loader 0026 * May be used in constructor, but commented out for now 0027 */ 0028 // // require_once 'Zend/Loader.php'; 0029 0030 /** 0031 * @see Zend_Mail_Storage_Abstract 0032 */ 0033 // require_once 'Zend/Mail/Storage/Abstract.php'; 0034 0035 /** 0036 * @see Zend_Mail_Message_File 0037 */ 0038 // require_once 'Zend/Mail/Message/File.php'; 0039 0040 0041 /** 0042 * @category Zend 0043 * @package Zend_Mail 0044 * @subpackage Storage 0045 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0046 * @license http://framework.zend.com/license/new-bsd New BSD License 0047 */ 0048 class Zend_Mail_Storage_Mbox extends Zend_Mail_Storage_Abstract 0049 { 0050 /** 0051 * file handle to mbox file 0052 * @var null|resource 0053 */ 0054 protected $_fh; 0055 0056 /** 0057 * filename of mbox file for __wakeup 0058 * @var string 0059 */ 0060 protected $_filename; 0061 0062 /** 0063 * modification date of mbox file for __wakeup 0064 * @var int 0065 */ 0066 protected $_filemtime; 0067 0068 /** 0069 * start and end position of messages as array('start' => start, 'seperator' => headersep, 'end' => end) 0070 * @var array 0071 */ 0072 protected $_positions; 0073 0074 /** 0075 * used message class, change it in an extened class to extend the returned message class 0076 * @var string 0077 */ 0078 protected $_messageClass = 'Zend_Mail_Message_File'; 0079 0080 /** 0081 * Count messages all messages in current box 0082 * 0083 * @return int number of messages 0084 * @throws Zend_Mail_Storage_Exception 0085 */ 0086 public function countMessages() 0087 { 0088 return count($this->_positions); 0089 } 0090 0091 0092 /** 0093 * Get a list of messages with number and size 0094 * 0095 * @param int|null $id number of message or null for all messages 0096 * @return int|array size of given message of list with all messages as array(num => size) 0097 */ 0098 public function getSize($id = 0) 0099 { 0100 if ($id) { 0101 $pos = $this->_positions[$id - 1]; 0102 return $pos['end'] - $pos['start']; 0103 } 0104 0105 $result = array(); 0106 foreach ($this->_positions as $num => $pos) { 0107 $result[$num + 1] = $pos['end'] - $pos['start']; 0108 } 0109 0110 return $result; 0111 } 0112 0113 0114 /** 0115 * Get positions for mail message or throw exeption if id is invalid 0116 * 0117 * @param int $id number of message 0118 * @return array positions as in _positions 0119 * @throws Zend_Mail_Storage_Exception 0120 */ 0121 protected function _getPos($id) 0122 { 0123 if (!isset($this->_positions[$id - 1])) { 0124 /** 0125 * @see Zend_Mail_Storage_Exception 0126 */ 0127 // require_once 'Zend/Mail/Storage/Exception.php'; 0128 throw new Zend_Mail_Storage_Exception('id does not exist'); 0129 } 0130 0131 return $this->_positions[$id - 1]; 0132 } 0133 0134 0135 /** 0136 * Fetch a message 0137 * 0138 * @param int $id number of message 0139 * @return Zend_Mail_Message_File 0140 * @throws Zend_Mail_Storage_Exception 0141 */ 0142 public function getMessage($id) 0143 { 0144 // TODO that's ugly, would be better to let the message class decide 0145 if (strtolower($this->_messageClass) == 'zend_mail_message_file' || is_subclass_of($this->_messageClass, 'zend_mail_message_file')) { 0146 // TODO top/body lines 0147 $messagePos = $this->_getPos($id); 0148 return new $this->_messageClass(array('file' => $this->_fh, 'startPos' => $messagePos['start'], 0149 'endPos' => $messagePos['end'])); 0150 } 0151 0152 $bodyLines = 0; // TODO: need a way to change that 0153 0154 $message = $this->getRawHeader($id); 0155 // file pointer is after headers now 0156 if ($bodyLines) { 0157 $message .= "\n"; 0158 while ($bodyLines-- && ftell($this->_fh) < $this->_positions[$id - 1]['end']) { 0159 $message .= fgets($this->_fh); 0160 } 0161 } 0162 0163 return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $message)); 0164 } 0165 0166 /* 0167 * Get raw header of message or part 0168 * 0169 * @param int $id number of message 0170 * @param null|array|string $part path to part or null for messsage header 0171 * @param int $topLines include this many lines with header (after an empty line) 0172 * @return string raw header 0173 * @throws Zend_Mail_Protocol_Exception 0174 * @throws Zend_Mail_Storage_Exception 0175 */ 0176 public function getRawHeader($id, $part = null, $topLines = 0) 0177 { 0178 if ($part !== null) { 0179 // TODO: implement 0180 /** 0181 * @see Zend_Mail_Storage_Exception 0182 */ 0183 // require_once 'Zend/Mail/Storage/Exception.php'; 0184 throw new Zend_Mail_Storage_Exception('not implemented'); 0185 } 0186 $messagePos = $this->_getPos($id); 0187 // TODO: toplines 0188 return stream_get_contents($this->_fh, $messagePos['separator'] - $messagePos['start'], $messagePos['start']); 0189 } 0190 0191 /* 0192 * Get raw content of message or part 0193 * 0194 * @param int $id number of message 0195 * @param null|array|string $part path to part or null for messsage content 0196 * @return string raw content 0197 * @throws Zend_Mail_Protocol_Exception 0198 * @throws Zend_Mail_Storage_Exception 0199 */ 0200 public function getRawContent($id, $part = null) 0201 { 0202 if ($part !== null) { 0203 // TODO: implement 0204 /** 0205 * @see Zend_Mail_Storage_Exception 0206 */ 0207 // require_once 'Zend/Mail/Storage/Exception.php'; 0208 throw new Zend_Mail_Storage_Exception('not implemented'); 0209 } 0210 $messagePos = $this->_getPos($id); 0211 return stream_get_contents($this->_fh, $messagePos['end'] - $messagePos['separator'], $messagePos['separator']); 0212 } 0213 0214 /** 0215 * Create instance with parameters 0216 * Supported parameters are: 0217 * - filename filename of mbox file 0218 * 0219 * @param array $params mail reader specific parameters 0220 * @throws Zend_Mail_Storage_Exception 0221 */ 0222 public function __construct($params) 0223 { 0224 if (is_array($params)) { 0225 $params = (object)$params; 0226 } 0227 0228 if (!isset($params->filename) /* || Zend_Loader::isReadable($params['filename']) */) { 0229 /** 0230 * @see Zend_Mail_Storage_Exception 0231 */ 0232 // require_once 'Zend/Mail/Storage/Exception.php'; 0233 throw new Zend_Mail_Storage_Exception('no valid filename given in params'); 0234 } 0235 0236 $this->_openMboxFile($params->filename); 0237 $this->_has['top'] = true; 0238 $this->_has['uniqueid'] = false; 0239 } 0240 0241 /** 0242 * check if given file is a mbox file 0243 * 0244 * if $file is a resource its file pointer is moved after the first line 0245 * 0246 * @param resource|string $file stream resource of name of file 0247 * @param bool $fileIsString file is string or resource 0248 * @return bool file is mbox file 0249 */ 0250 protected function _isMboxFile($file, $fileIsString = true) 0251 { 0252 if ($fileIsString) { 0253 $file = @fopen($file, 'r'); 0254 if (!$file) { 0255 return false; 0256 } 0257 } else { 0258 fseek($file, 0); 0259 } 0260 0261 $result = false; 0262 0263 $line = fgets($file); 0264 if (strpos($line, 'From ') === 0) { 0265 $result = true; 0266 } 0267 0268 if ($fileIsString) { 0269 @fclose($file); 0270 } 0271 0272 return $result; 0273 } 0274 0275 /** 0276 * open given file as current mbox file 0277 * 0278 * @param string $filename filename of mbox file 0279 * @return null 0280 * @throws Zend_Mail_Storage_Exception 0281 */ 0282 protected function _openMboxFile($filename) 0283 { 0284 if ($this->_fh) { 0285 $this->close(); 0286 } 0287 0288 $this->_fh = @fopen($filename, 'r'); 0289 if (!$this->_fh) { 0290 /** 0291 * @see Zend_Mail_Storage_Exception 0292 */ 0293 // require_once 'Zend/Mail/Storage/Exception.php'; 0294 throw new Zend_Mail_Storage_Exception('cannot open mbox file'); 0295 } 0296 $this->_filename = $filename; 0297 $this->_filemtime = filemtime($this->_filename); 0298 0299 if (!$this->_isMboxFile($this->_fh, false)) { 0300 @fclose($this->_fh); 0301 /** 0302 * @see Zend_Mail_Storage_Exception 0303 */ 0304 // require_once 'Zend/Mail/Storage/Exception.php'; 0305 throw new Zend_Mail_Storage_Exception('file is not a valid mbox format'); 0306 } 0307 0308 $messagePos = array('start' => ftell($this->_fh), 'separator' => 0, 'end' => 0); 0309 while (($line = fgets($this->_fh)) !== false) { 0310 if (strpos($line, 'From ') === 0) { 0311 $messagePos['end'] = ftell($this->_fh) - strlen($line) - 2; // + newline 0312 if (!$messagePos['separator']) { 0313 $messagePos['separator'] = $messagePos['end']; 0314 } 0315 $this->_positions[] = $messagePos; 0316 $messagePos = array('start' => ftell($this->_fh), 'separator' => 0, 'end' => 0); 0317 } 0318 if (!$messagePos['separator'] && !trim($line)) { 0319 $messagePos['separator'] = ftell($this->_fh); 0320 } 0321 } 0322 0323 $messagePos['end'] = ftell($this->_fh); 0324 if (!$messagePos['separator']) { 0325 $messagePos['separator'] = $messagePos['end']; 0326 } 0327 $this->_positions[] = $messagePos; 0328 } 0329 0330 /** 0331 * Close resource for mail lib. If you need to control, when the resource 0332 * is closed. Otherwise the destructor would call this. 0333 * 0334 * @return void 0335 */ 0336 public function close() 0337 { 0338 @fclose($this->_fh); 0339 $this->_positions = array(); 0340 } 0341 0342 0343 /** 0344 * Waste some CPU cycles doing nothing. 0345 * 0346 * @return void 0347 */ 0348 public function noop() 0349 { 0350 return true; 0351 } 0352 0353 0354 /** 0355 * stub for not supported message deletion 0356 * 0357 * @return null 0358 * @throws Zend_Mail_Storage_Exception 0359 */ 0360 public function removeMessage($id) 0361 { 0362 /** 0363 * @see Zend_Mail_Storage_Exception 0364 */ 0365 // require_once 'Zend/Mail/Storage/Exception.php'; 0366 throw new Zend_Mail_Storage_Exception('mbox is read-only'); 0367 } 0368 0369 /** 0370 * get unique id for one or all messages 0371 * 0372 * Mbox does not support unique ids (yet) - it's always the same as the message number. 0373 * That shouldn't be a problem, because we can't change mbox files. Therefor the message 0374 * number is save enough. 0375 * 0376 * @param int|null $id message number 0377 * @return array|string message number for given message or all messages as array 0378 * @throws Zend_Mail_Storage_Exception 0379 */ 0380 public function getUniqueId($id = null) 0381 { 0382 if ($id) { 0383 // check if id exists 0384 $this->_getPos($id); 0385 return $id; 0386 } 0387 0388 $range = range(1, $this->countMessages()); 0389 return array_combine($range, $range); 0390 } 0391 0392 /** 0393 * get a message number from a unique id 0394 * 0395 * I.e. if you have a webmailer that supports deleting messages you should use unique ids 0396 * as parameter and use this method to translate it to message number right before calling removeMessage() 0397 * 0398 * @param string $id unique id 0399 * @return int message number 0400 * @throws Zend_Mail_Storage_Exception 0401 */ 0402 public function getNumberByUniqueId($id) 0403 { 0404 // check if id exists 0405 $this->_getPos($id); 0406 return $id; 0407 } 0408 0409 /** 0410 * magic method for serialize() 0411 * 0412 * with this method you can cache the mbox class 0413 * 0414 * @return array name of variables 0415 */ 0416 public function __sleep() 0417 { 0418 return array('_filename', '_positions', '_filemtime'); 0419 } 0420 0421 /** 0422 * magic method for unserialize() 0423 * 0424 * with this method you can cache the mbox class 0425 * for cache validation the mtime of the mbox file is used 0426 * 0427 * @return null 0428 * @throws Zend_Mail_Storage_Exception 0429 */ 0430 public function __wakeup() 0431 { 0432 if ($this->_filemtime != @filemtime($this->_filename)) { 0433 $this->close(); 0434 $this->_openMboxFile($this->_filename); 0435 } else { 0436 $this->_fh = @fopen($this->_filename, 'r'); 0437 if (!$this->_fh) { 0438 /** 0439 * @see Zend_Mail_Storage_Exception 0440 */ 0441 // require_once 'Zend/Mail/Storage/Exception.php'; 0442 throw new Zend_Mail_Storage_Exception('cannot open mbox file'); 0443 } 0444 } 0445 } 0446 0447 }