PageRenderTime 57ms CodeModel.GetById 37ms app.highlight 15ms RepoModel.GetById 0ms app.codeStats 1ms

/framework/vendor/zend/Zend/Mail/Storage/Maildir.php

http://zoop.googlecode.com/
PHP | 475 lines | 227 code | 53 blank | 195 comment | 47 complexity | 333869328cdf1eb44e264db3843ca3a7 MD5 | raw file
  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 */
 27require_once 'Zend/Mail/Storage/Abstract.php';
 28
 29/**
 30 * @see Zend_Mail_Message_File
 31 */
 32require_once 'Zend/Mail/Message/File.php';
 33
 34/**
 35 * @see Zend_Mail_Storage
 36 */
 37require_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 */
 47class 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}