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 }