web/Zend/Mail/Storage/Maildir.php
changeset 0 4eba9c11703f
equal deleted inserted replaced
-1:000000000000 0:4eba9c11703f
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Mail
       
    17  * @subpackage Storage
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: Maildir.php 20096 2010-01-06 02:05:09Z bkarwin $
       
    21  */
       
    22 
       
    23 
       
    24 /**
       
    25  * @see Zend_Mail_Storage_Abstract
       
    26  */
       
    27 require_once 'Zend/Mail/Storage/Abstract.php';
       
    28 
       
    29 /**
       
    30  * @see Zend_Mail_Message_File
       
    31  */
       
    32 require_once 'Zend/Mail/Message/File.php';
       
    33 
       
    34 /**
       
    35  * @see Zend_Mail_Storage
       
    36  */
       
    37 require_once 'Zend/Mail/Storage.php';
       
    38 
       
    39 
       
    40 /**
       
    41  * @category   Zend
       
    42  * @package    Zend_Mail
       
    43  * @subpackage Storage
       
    44  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    45  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    46  */
       
    47 class Zend_Mail_Storage_Maildir extends Zend_Mail_Storage_Abstract
       
    48 {
       
    49     /**
       
    50      * used message class, change it in an extened class to extend the returned message class
       
    51      * @var string
       
    52      */
       
    53     protected $_messageClass = 'Zend_Mail_Message_File';
       
    54 
       
    55     /**
       
    56      * data of found message files in maildir dir
       
    57      * @var array
       
    58      */
       
    59     protected $_files = array();
       
    60 
       
    61     /**
       
    62      * known flag chars in filenames
       
    63      *
       
    64      * This list has to be in alphabetical order for setFlags()
       
    65      *
       
    66      * @var array
       
    67      */
       
    68     protected static $_knownFlags = array('D' => Zend_Mail_Storage::FLAG_DRAFT,
       
    69                                           'F' => Zend_Mail_Storage::FLAG_FLAGGED,
       
    70                                           'P' => Zend_Mail_Storage::FLAG_PASSED,
       
    71                                           'R' => Zend_Mail_Storage::FLAG_ANSWERED,
       
    72                                           'S' => Zend_Mail_Storage::FLAG_SEEN,
       
    73                                           'T' => Zend_Mail_Storage::FLAG_DELETED);
       
    74 
       
    75     // TODO: getFlags($id) for fast access if headers are not needed (i.e. just setting flags)?
       
    76 
       
    77     /**
       
    78      * Count messages all messages in current box
       
    79      *
       
    80      * @return int number of messages
       
    81      * @throws Zend_Mail_Storage_Exception
       
    82      */
       
    83     public function countMessages($flags = null)
       
    84     {
       
    85         if ($flags === null) {
       
    86             return count($this->_files);
       
    87         }
       
    88 
       
    89         $count = 0;
       
    90         if (!is_array($flags)) {
       
    91             foreach ($this->_files as $file) {
       
    92                 if (isset($file['flaglookup'][$flags])) {
       
    93                     ++$count;
       
    94                 }
       
    95             }
       
    96             return $count;
       
    97         }
       
    98 
       
    99         $flags = array_flip($flags);
       
   100            foreach ($this->_files as $file) {
       
   101                foreach ($flags as $flag => $v) {
       
   102                    if (!isset($file['flaglookup'][$flag])) {
       
   103                        continue 2;
       
   104                    }
       
   105                }
       
   106                ++$count;
       
   107            }
       
   108            return $count;
       
   109     }
       
   110 
       
   111     /**
       
   112      * Get one or all fields from file structure. Also checks if message is valid
       
   113      *
       
   114      * @param  int         $id    message number
       
   115      * @param  string|null $field wanted field
       
   116      * @return string|array wanted field or all fields as array
       
   117      * @throws Zend_Mail_Storage_Exception
       
   118      */
       
   119     protected function _getFileData($id, $field = null)
       
   120     {
       
   121         if (!isset($this->_files[$id - 1])) {
       
   122             /**
       
   123              * @see Zend_Mail_Storage_Exception
       
   124              */
       
   125             require_once 'Zend/Mail/Storage/Exception.php';
       
   126             throw new Zend_Mail_Storage_Exception('id does not exist');
       
   127         }
       
   128 
       
   129         if (!$field) {
       
   130             return $this->_files[$id - 1];
       
   131         }
       
   132 
       
   133         if (!isset($this->_files[$id - 1][$field])) {
       
   134             /**
       
   135              * @see Zend_Mail_Storage_Exception
       
   136              */
       
   137             require_once 'Zend/Mail/Storage/Exception.php';
       
   138             throw new Zend_Mail_Storage_Exception('field does not exist');
       
   139         }
       
   140 
       
   141         return $this->_files[$id - 1][$field];
       
   142     }
       
   143 
       
   144     /**
       
   145      * Get a list of messages with number and size
       
   146      *
       
   147      * @param  int|null $id number of message or null for all messages
       
   148      * @return int|array size of given message of list with all messages as array(num => size)
       
   149      * @throws Zend_Mail_Storage_Exception
       
   150      */
       
   151     public function getSize($id = null)
       
   152     {
       
   153         if ($id !== null) {
       
   154             $filedata = $this->_getFileData($id);
       
   155             return isset($filedata['size']) ? $filedata['size'] : filesize($filedata['filename']);
       
   156         }
       
   157 
       
   158         $result = array();
       
   159         foreach ($this->_files as $num => $data) {
       
   160             $result[$num + 1] = isset($data['size']) ? $data['size'] : filesize($data['filename']);
       
   161         }
       
   162 
       
   163         return $result;
       
   164     }
       
   165 
       
   166 
       
   167 
       
   168     /**
       
   169      * Fetch a message
       
   170      *
       
   171      * @param  int $id number of message
       
   172      * @return Zend_Mail_Message_File
       
   173      * @throws Zend_Mail_Storage_Exception
       
   174      */
       
   175     public function getMessage($id)
       
   176     {
       
   177         // TODO that's ugly, would be better to let the message class decide
       
   178         if (strtolower($this->_messageClass) == 'zend_mail_message_file' || is_subclass_of($this->_messageClass, 'zend_mail_message_file')) {
       
   179             return new $this->_messageClass(array('file'  => $this->_getFileData($id, 'filename'),
       
   180                                                   'flags' => $this->_getFileData($id, 'flags')));
       
   181         }
       
   182 
       
   183         return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $this->getRawHeader($id),
       
   184                                               'flags'   => $this->_getFileData($id, 'flags')));
       
   185     }
       
   186 
       
   187     /*
       
   188      * Get raw header of message or part
       
   189      *
       
   190      * @param  int               $id       number of message
       
   191      * @param  null|array|string $part     path to part or null for messsage header
       
   192      * @param  int               $topLines include this many lines with header (after an empty line)
       
   193      * @return string raw header
       
   194      * @throws Zend_Mail_Storage_Exception
       
   195      */
       
   196     public function getRawHeader($id, $part = null, $topLines = 0)
       
   197     {
       
   198         if ($part !== null) {
       
   199             // TODO: implement
       
   200             /**
       
   201              * @see Zend_Mail_Storage_Exception
       
   202              */
       
   203             require_once 'Zend/Mail/Storage/Exception.php';
       
   204             throw new Zend_Mail_Storage_Exception('not implemented');
       
   205         }
       
   206 
       
   207         $fh = fopen($this->_getFileData($id, 'filename'), 'r');
       
   208 
       
   209         $content = '';
       
   210         while (!feof($fh)) {
       
   211             $line = fgets($fh);
       
   212             if (!trim($line)) {
       
   213                 break;
       
   214             }
       
   215             $content .= $line;
       
   216         }
       
   217 
       
   218         fclose($fh);
       
   219         return $content;
       
   220     }
       
   221 
       
   222     /*
       
   223      * Get raw content of message or part
       
   224      *
       
   225      * @param  int               $id   number of message
       
   226      * @param  null|array|string $part path to part or null for messsage content
       
   227      * @return string raw content
       
   228      * @throws Zend_Mail_Storage_Exception
       
   229      */
       
   230     public function getRawContent($id, $part = null)
       
   231     {
       
   232         if ($part !== null) {
       
   233             // TODO: implement
       
   234             /**
       
   235              * @see Zend_Mail_Storage_Exception
       
   236              */
       
   237             require_once 'Zend/Mail/Storage/Exception.php';
       
   238             throw new Zend_Mail_Storage_Exception('not implemented');
       
   239         }
       
   240 
       
   241         $fh = fopen($this->_getFileData($id, 'filename'), 'r');
       
   242 
       
   243         while (!feof($fh)) {
       
   244             $line = fgets($fh);
       
   245             if (!trim($line)) {
       
   246                 break;
       
   247             }
       
   248         }
       
   249 
       
   250         $content = stream_get_contents($fh);
       
   251         fclose($fh);
       
   252         return $content;
       
   253     }
       
   254 
       
   255     /**
       
   256      * Create instance with parameters
       
   257      * Supported parameters are:
       
   258      *   - dirname dirname of mbox file
       
   259      *
       
   260      * @param  $params array mail reader specific parameters
       
   261      * @throws Zend_Mail_Storage_Exception
       
   262      */
       
   263     public function __construct($params)
       
   264     {
       
   265         if (is_array($params)) {
       
   266             $params = (object)$params;
       
   267         }
       
   268 
       
   269         if (!isset($params->dirname) || !is_dir($params->dirname)) {
       
   270             /**
       
   271              * @see Zend_Mail_Storage_Exception
       
   272              */
       
   273             require_once 'Zend/Mail/Storage/Exception.php';
       
   274             throw new Zend_Mail_Storage_Exception('no valid dirname given in params');
       
   275         }
       
   276 
       
   277         if (!$this->_isMaildir($params->dirname)) {
       
   278             /**
       
   279              * @see Zend_Mail_Storage_Exception
       
   280              */
       
   281             require_once 'Zend/Mail/Storage/Exception.php';
       
   282             throw new Zend_Mail_Storage_Exception('invalid maildir given');
       
   283         }
       
   284 
       
   285         $this->_has['top'] = true;
       
   286         $this->_has['flags'] = true;
       
   287         $this->_openMaildir($params->dirname);
       
   288     }
       
   289 
       
   290     /**
       
   291      * check if a given dir is a valid maildir
       
   292      *
       
   293      * @param string $dirname name of dir
       
   294      * @return bool dir is valid maildir
       
   295      */
       
   296     protected function _isMaildir($dirname)
       
   297     {
       
   298         if (file_exists($dirname . '/new') && !is_dir($dirname . '/new')) {
       
   299             return false;
       
   300         }
       
   301         if (file_exists($dirname . '/tmp') && !is_dir($dirname . '/tmp')) {
       
   302             return false;
       
   303         }
       
   304         return is_dir($dirname . '/cur');
       
   305     }
       
   306 
       
   307     /**
       
   308      * open given dir as current maildir
       
   309      *
       
   310      * @param string $dirname name of maildir
       
   311      * @return null
       
   312      * @throws Zend_Mail_Storage_Exception
       
   313      */
       
   314     protected function _openMaildir($dirname)
       
   315     {
       
   316         if ($this->_files) {
       
   317             $this->close();
       
   318         }
       
   319 
       
   320         $dh = @opendir($dirname . '/cur/');
       
   321         if (!$dh) {
       
   322             /**
       
   323              * @see Zend_Mail_Storage_Exception
       
   324              */
       
   325             require_once 'Zend/Mail/Storage/Exception.php';
       
   326             throw new Zend_Mail_Storage_Exception('cannot open maildir');
       
   327         }
       
   328         $this->_getMaildirFiles($dh, $dirname . '/cur/');
       
   329         closedir($dh);
       
   330 
       
   331         $dh = @opendir($dirname . '/new/');
       
   332         if ($dh) {
       
   333             $this->_getMaildirFiles($dh, $dirname . '/new/', array(Zend_Mail_Storage::FLAG_RECENT));
       
   334             closedir($dh);
       
   335         } else if (file_exists($dirname . '/new/')) {
       
   336             /**
       
   337              * @see Zend_Mail_Storage_Exception
       
   338              */
       
   339             require_once 'Zend/Mail/Storage/Exception.php';
       
   340             throw new Zend_Mail_Storage_Exception('cannot read recent mails in maildir');
       
   341         }
       
   342     }
       
   343 
       
   344     /**
       
   345      * find all files in opened dir handle and add to maildir files
       
   346      *
       
   347      * @param resource $dh            dir handle used for search
       
   348      * @param string   $dirname       dirname of dir in $dh
       
   349      * @param array    $default_flags default flags for given dir
       
   350      * @return null
       
   351      */
       
   352     protected function _getMaildirFiles($dh, $dirname, $default_flags = array())
       
   353     {
       
   354         while (($entry = readdir($dh)) !== false) {
       
   355             if ($entry[0] == '.' || !is_file($dirname . $entry)) {
       
   356                 continue;
       
   357             }
       
   358 
       
   359             @list($uniq, $info) = explode(':', $entry, 2);
       
   360             @list(,$size) = explode(',', $uniq, 2);
       
   361             if ($size && $size[0] == 'S' && $size[1] == '=') {
       
   362                 $size = substr($size, 2);
       
   363             }
       
   364             if (!ctype_digit($size)) {
       
   365                 $size = null;
       
   366             }
       
   367             @list($version, $flags) = explode(',', $info, 2);
       
   368             if ($version != 2) {
       
   369                 $flags = '';
       
   370             }
       
   371 
       
   372             $named_flags = $default_flags;
       
   373             $length = strlen($flags);
       
   374             for ($i = 0; $i < $length; ++$i) {
       
   375                 $flag = $flags[$i];
       
   376                 $named_flags[$flag] = isset(self::$_knownFlags[$flag]) ? self::$_knownFlags[$flag] : $flag;
       
   377             }
       
   378 
       
   379             $data = array('uniq'       => $uniq,
       
   380                           'flags'      => $named_flags,
       
   381                           'flaglookup' => array_flip($named_flags),
       
   382                           'filename'   => $dirname . $entry);
       
   383             if ($size !== null) {
       
   384                 $data['size'] = (int)$size;
       
   385             }
       
   386             $this->_files[] = $data;
       
   387         }
       
   388     }
       
   389 
       
   390 
       
   391     /**
       
   392      * Close resource for mail lib. If you need to control, when the resource
       
   393      * is closed. Otherwise the destructor would call this.
       
   394      *
       
   395      * @return void
       
   396      */
       
   397     public function close()
       
   398     {
       
   399         $this->_files = array();
       
   400     }
       
   401 
       
   402 
       
   403     /**
       
   404      * Waste some CPU cycles doing nothing.
       
   405      *
       
   406      * @return void
       
   407      */
       
   408     public function noop()
       
   409     {
       
   410         return true;
       
   411     }
       
   412 
       
   413 
       
   414     /**
       
   415      * stub for not supported message deletion
       
   416      *
       
   417      * @return null
       
   418      * @throws Zend_Mail_Storage_Exception
       
   419      */
       
   420     public function removeMessage($id)
       
   421     {
       
   422         /**
       
   423          * @see Zend_Mail_Storage_Exception
       
   424          */
       
   425         require_once 'Zend/Mail/Storage/Exception.php';
       
   426         throw new Zend_Mail_Storage_Exception('maildir is (currently) read-only');
       
   427     }
       
   428 
       
   429     /**
       
   430      * get unique id for one or all messages
       
   431      *
       
   432      * if storage does not support unique ids it's the same as the message number
       
   433      *
       
   434      * @param int|null $id message number
       
   435      * @return array|string message number for given message or all messages as array
       
   436      * @throws Zend_Mail_Storage_Exception
       
   437      */
       
   438     public function getUniqueId($id = null)
       
   439     {
       
   440         if ($id) {
       
   441             return $this->_getFileData($id, 'uniq');
       
   442         }
       
   443 
       
   444         $ids = array();
       
   445         foreach ($this->_files as $num => $file) {
       
   446             $ids[$num + 1] = $file['uniq'];
       
   447         }
       
   448         return $ids;
       
   449     }
       
   450 
       
   451     /**
       
   452      * get a message number from a unique id
       
   453      *
       
   454      * I.e. if you have a webmailer that supports deleting messages you should use unique ids
       
   455      * as parameter and use this method to translate it to message number right before calling removeMessage()
       
   456      *
       
   457      * @param string $id unique id
       
   458      * @return int message number
       
   459      * @throws Zend_Mail_Storage_Exception
       
   460      */
       
   461     public function getNumberByUniqueId($id)
       
   462     {
       
   463         foreach ($this->_files as $num => $file) {
       
   464             if ($file['uniq'] == $id) {
       
   465                 return $num + 1;
       
   466             }
       
   467         }
       
   468 
       
   469         /**
       
   470          * @see Zend_Mail_Storage_Exception
       
   471          */
       
   472         require_once 'Zend/Mail/Storage/Exception.php';
       
   473         throw new Zend_Mail_Storage_Exception('unique id not found');
       
   474     }
       
   475 }