File indexing completed on 2024-12-22 05:36:51
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_Mail_Storage_Abstract 0026 */ 0027 // require_once 'Zend/Mail/Storage/Abstract.php'; 0028 0029 /** 0030 * @see Zend_Mail_Message_File 0031 */ 0032 // require_once 'Zend/Mail/Message/File.php'; 0033 0034 /** 0035 * @see Zend_Mail_Storage 0036 */ 0037 // require_once 'Zend/Mail/Storage.php'; 0038 0039 0040 /** 0041 * @category Zend 0042 * @package Zend_Mail 0043 * @subpackage Storage 0044 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0045 * @license http://framework.zend.com/license/new-bsd New BSD License 0046 */ 0047 class Zend_Mail_Storage_Maildir extends Zend_Mail_Storage_Abstract 0048 { 0049 /** 0050 * used message class, change it in an extened class to extend the returned message class 0051 * @var string 0052 */ 0053 protected $_messageClass = 'Zend_Mail_Message_File'; 0054 0055 /** 0056 * data of found message files in maildir dir 0057 * @var array 0058 */ 0059 protected $_files = array(); 0060 0061 /** 0062 * known flag chars in filenames 0063 * 0064 * This list has to be in alphabetical order for setFlags() 0065 * 0066 * @var array 0067 */ 0068 protected static $_knownFlags = array('D' => Zend_Mail_Storage::FLAG_DRAFT, 0069 'F' => Zend_Mail_Storage::FLAG_FLAGGED, 0070 'P' => Zend_Mail_Storage::FLAG_PASSED, 0071 'R' => Zend_Mail_Storage::FLAG_ANSWERED, 0072 'S' => Zend_Mail_Storage::FLAG_SEEN, 0073 'T' => Zend_Mail_Storage::FLAG_DELETED); 0074 0075 // TODO: getFlags($id) for fast access if headers are not needed (i.e. just setting flags)? 0076 0077 /** 0078 * Count messages all messages in current box 0079 * 0080 * @return int number of messages 0081 * @throws Zend_Mail_Storage_Exception 0082 */ 0083 public function countMessages($flags = null) 0084 { 0085 if ($flags === null) { 0086 return count($this->_files); 0087 } 0088 0089 $count = 0; 0090 if (!is_array($flags)) { 0091 foreach ($this->_files as $file) { 0092 if (isset($file['flaglookup'][$flags])) { 0093 ++$count; 0094 } 0095 } 0096 return $count; 0097 } 0098 0099 $flags = array_flip($flags); 0100 foreach ($this->_files as $file) { 0101 foreach ($flags as $flag => $v) { 0102 if (!isset($file['flaglookup'][$flag])) { 0103 continue 2; 0104 } 0105 } 0106 ++$count; 0107 } 0108 return $count; 0109 } 0110 0111 /** 0112 * Get one or all fields from file structure. Also checks if message is valid 0113 * 0114 * @param int $id message number 0115 * @param string|null $field wanted field 0116 * @return string|array wanted field or all fields as array 0117 * @throws Zend_Mail_Storage_Exception 0118 */ 0119 protected function _getFileData($id, $field = null) 0120 { 0121 if (!isset($this->_files[$id - 1])) { 0122 /** 0123 * @see Zend_Mail_Storage_Exception 0124 */ 0125 // require_once 'Zend/Mail/Storage/Exception.php'; 0126 throw new Zend_Mail_Storage_Exception('id does not exist'); 0127 } 0128 0129 if (!$field) { 0130 return $this->_files[$id - 1]; 0131 } 0132 0133 if (!isset($this->_files[$id - 1][$field])) { 0134 /** 0135 * @see Zend_Mail_Storage_Exception 0136 */ 0137 // require_once 'Zend/Mail/Storage/Exception.php'; 0138 throw new Zend_Mail_Storage_Exception('field does not exist'); 0139 } 0140 0141 return $this->_files[$id - 1][$field]; 0142 } 0143 0144 /** 0145 * Get a list of messages with number and size 0146 * 0147 * @param int|null $id number of message or null for all messages 0148 * @return int|array size of given message of list with all messages as array(num => size) 0149 * @throws Zend_Mail_Storage_Exception 0150 */ 0151 public function getSize($id = null) 0152 { 0153 if ($id !== null) { 0154 $filedata = $this->_getFileData($id); 0155 return isset($filedata['size']) ? $filedata['size'] : filesize($filedata['filename']); 0156 } 0157 0158 $result = array(); 0159 foreach ($this->_files as $num => $data) { 0160 $result[$num + 1] = isset($data['size']) ? $data['size'] : filesize($data['filename']); 0161 } 0162 0163 return $result; 0164 } 0165 0166 0167 0168 /** 0169 * Fetch a message 0170 * 0171 * @param int $id number of message 0172 * @return Zend_Mail_Message_File 0173 * @throws Zend_Mail_Storage_Exception 0174 */ 0175 public function getMessage($id) 0176 { 0177 // TODO that's ugly, would be better to let the message class decide 0178 if (strtolower($this->_messageClass) == 'zend_mail_message_file' || is_subclass_of($this->_messageClass, 'zend_mail_message_file')) { 0179 return new $this->_messageClass(array('file' => $this->_getFileData($id, 'filename'), 0180 'flags' => $this->_getFileData($id, 'flags'))); 0181 } 0182 0183 return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $this->getRawHeader($id), 0184 'flags' => $this->_getFileData($id, 'flags'))); 0185 } 0186 0187 /* 0188 * Get raw header of message or part 0189 * 0190 * @param int $id number of message 0191 * @param null|array|string $part path to part or null for messsage header 0192 * @param int $topLines include this many lines with header (after an empty line) 0193 * @return string raw header 0194 * @throws Zend_Mail_Storage_Exception 0195 */ 0196 public function getRawHeader($id, $part = null, $topLines = 0) 0197 { 0198 if ($part !== null) { 0199 // TODO: implement 0200 /** 0201 * @see Zend_Mail_Storage_Exception 0202 */ 0203 // require_once 'Zend/Mail/Storage/Exception.php'; 0204 throw new Zend_Mail_Storage_Exception('not implemented'); 0205 } 0206 0207 $fh = fopen($this->_getFileData($id, 'filename'), 'r'); 0208 0209 $content = ''; 0210 while (!feof($fh)) { 0211 $line = fgets($fh); 0212 if (!trim($line)) { 0213 break; 0214 } 0215 $content .= $line; 0216 } 0217 0218 fclose($fh); 0219 return $content; 0220 } 0221 0222 /* 0223 * Get raw content of message or part 0224 * 0225 * @param int $id number of message 0226 * @param null|array|string $part path to part or null for messsage content 0227 * @return string raw content 0228 * @throws Zend_Mail_Storage_Exception 0229 */ 0230 public function getRawContent($id, $part = null) 0231 { 0232 if ($part !== null) { 0233 // TODO: implement 0234 /** 0235 * @see Zend_Mail_Storage_Exception 0236 */ 0237 // require_once 'Zend/Mail/Storage/Exception.php'; 0238 throw new Zend_Mail_Storage_Exception('not implemented'); 0239 } 0240 0241 $fh = fopen($this->_getFileData($id, 'filename'), 'r'); 0242 0243 while (!feof($fh)) { 0244 $line = fgets($fh); 0245 if (!trim($line)) { 0246 break; 0247 } 0248 } 0249 0250 $content = stream_get_contents($fh); 0251 fclose($fh); 0252 return $content; 0253 } 0254 0255 /** 0256 * Create instance with parameters 0257 * Supported parameters are: 0258 * - dirname dirname of mbox file 0259 * 0260 * @param array $params mail reader specific parameters 0261 * @throws Zend_Mail_Storage_Exception 0262 */ 0263 public function __construct($params) 0264 { 0265 if (is_array($params)) { 0266 $params = (object)$params; 0267 } 0268 0269 if (!isset($params->dirname) || !is_dir($params->dirname)) { 0270 /** 0271 * @see Zend_Mail_Storage_Exception 0272 */ 0273 // require_once 'Zend/Mail/Storage/Exception.php'; 0274 throw new Zend_Mail_Storage_Exception('no valid dirname given in params'); 0275 } 0276 0277 if (!$this->_isMaildir($params->dirname)) { 0278 /** 0279 * @see Zend_Mail_Storage_Exception 0280 */ 0281 // require_once 'Zend/Mail/Storage/Exception.php'; 0282 throw new Zend_Mail_Storage_Exception('invalid maildir given'); 0283 } 0284 0285 $this->_has['top'] = true; 0286 $this->_has['flags'] = true; 0287 $this->_openMaildir($params->dirname); 0288 } 0289 0290 /** 0291 * check if a given dir is a valid maildir 0292 * 0293 * @param string $dirname name of dir 0294 * @return bool dir is valid maildir 0295 */ 0296 protected function _isMaildir($dirname) 0297 { 0298 if (file_exists($dirname . '/new') && !is_dir($dirname . '/new')) { 0299 return false; 0300 } 0301 if (file_exists($dirname . '/tmp') && !is_dir($dirname . '/tmp')) { 0302 return false; 0303 } 0304 return is_dir($dirname . '/cur'); 0305 } 0306 0307 /** 0308 * open given dir as current maildir 0309 * 0310 * @param string $dirname name of maildir 0311 * @return null 0312 * @throws Zend_Mail_Storage_Exception 0313 */ 0314 protected function _openMaildir($dirname) 0315 { 0316 if ($this->_files) { 0317 $this->close(); 0318 } 0319 0320 $dh = @opendir($dirname . '/cur/'); 0321 if (!$dh) { 0322 /** 0323 * @see Zend_Mail_Storage_Exception 0324 */ 0325 // require_once 'Zend/Mail/Storage/Exception.php'; 0326 throw new Zend_Mail_Storage_Exception('cannot open maildir'); 0327 } 0328 $this->_getMaildirFiles($dh, $dirname . '/cur/'); 0329 closedir($dh); 0330 0331 $dh = @opendir($dirname . '/new/'); 0332 if ($dh) { 0333 $this->_getMaildirFiles($dh, $dirname . '/new/', array(Zend_Mail_Storage::FLAG_RECENT)); 0334 closedir($dh); 0335 } else if (file_exists($dirname . '/new/')) { 0336 /** 0337 * @see Zend_Mail_Storage_Exception 0338 */ 0339 // require_once 'Zend/Mail/Storage/Exception.php'; 0340 throw new Zend_Mail_Storage_Exception('cannot read recent mails in maildir'); 0341 } 0342 } 0343 0344 /** 0345 * find all files in opened dir handle and add to maildir files 0346 * 0347 * @param resource $dh dir handle used for search 0348 * @param string $dirname dirname of dir in $dh 0349 * @param array $default_flags default flags for given dir 0350 * @return null 0351 */ 0352 protected function _getMaildirFiles($dh, $dirname, $default_flags = array()) 0353 { 0354 while (($entry = readdir($dh)) !== false) { 0355 if ($entry[0] == '.' || !is_file($dirname . $entry)) { 0356 continue; 0357 } 0358 0359 @list($uniq, $info) = explode(':', $entry, 2); 0360 @list(,$size) = explode(',', $uniq, 2); 0361 if ($size && $size[0] == 'S' && $size[1] == '=') { 0362 $size = substr($size, 2); 0363 } 0364 if (!ctype_digit($size)) { 0365 $size = null; 0366 } 0367 @list($version, $flags) = explode(',', $info, 2); 0368 if ($version != 2) { 0369 $flags = ''; 0370 } 0371 0372 $named_flags = $default_flags; 0373 $length = strlen($flags); 0374 for ($i = 0; $i < $length; ++$i) { 0375 $flag = $flags[$i]; 0376 $named_flags[$flag] = isset(self::$_knownFlags[$flag]) ? self::$_knownFlags[$flag] : $flag; 0377 } 0378 0379 $data = array('uniq' => $uniq, 0380 'flags' => $named_flags, 0381 'flaglookup' => array_flip($named_flags), 0382 'filename' => $dirname . $entry); 0383 if ($size !== null) { 0384 $data['size'] = (int)$size; 0385 } 0386 $this->_files[] = $data; 0387 } 0388 } 0389 0390 0391 /** 0392 * Close resource for mail lib. If you need to control, when the resource 0393 * is closed. Otherwise the destructor would call this. 0394 * 0395 * @return void 0396 */ 0397 public function close() 0398 { 0399 $this->_files = array(); 0400 } 0401 0402 0403 /** 0404 * Waste some CPU cycles doing nothing. 0405 * 0406 * @return void 0407 */ 0408 public function noop() 0409 { 0410 return true; 0411 } 0412 0413 0414 /** 0415 * stub for not supported message deletion 0416 * 0417 * @return null 0418 * @throws Zend_Mail_Storage_Exception 0419 */ 0420 public function removeMessage($id) 0421 { 0422 /** 0423 * @see Zend_Mail_Storage_Exception 0424 */ 0425 // require_once 'Zend/Mail/Storage/Exception.php'; 0426 throw new Zend_Mail_Storage_Exception('maildir is (currently) read-only'); 0427 } 0428 0429 /** 0430 * get unique id for one or all messages 0431 * 0432 * if storage does not support unique ids it's the same as the message number 0433 * 0434 * @param int|null $id message number 0435 * @return array|string message number for given message or all messages as array 0436 * @throws Zend_Mail_Storage_Exception 0437 */ 0438 public function getUniqueId($id = null) 0439 { 0440 if ($id) { 0441 return $this->_getFileData($id, 'uniq'); 0442 } 0443 0444 $ids = array(); 0445 foreach ($this->_files as $num => $file) { 0446 $ids[$num + 1] = $file['uniq']; 0447 } 0448 return $ids; 0449 } 0450 0451 /** 0452 * get a message number from a unique id 0453 * 0454 * I.e. if you have a webmailer that supports deleting messages you should use unique ids 0455 * as parameter and use this method to translate it to message number right before calling removeMessage() 0456 * 0457 * @param string $id unique id 0458 * @return int message number 0459 * @throws Zend_Mail_Storage_Exception 0460 */ 0461 public function getNumberByUniqueId($id) 0462 { 0463 foreach ($this->_files as $num => $file) { 0464 if ($file['uniq'] == $id) { 0465 return $num + 1; 0466 } 0467 } 0468 0469 /** 0470 * @see Zend_Mail_Storage_Exception 0471 */ 0472 // require_once 'Zend/Mail/Storage/Exception.php'; 0473 throw new Zend_Mail_Storage_Exception('unique id not found'); 0474 } 0475 }