PageRenderTime 17ms CodeModel.GetById 2ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 1ms

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

http://zoop.googlecode.com/
PHP | 644 lines | 272 code | 59 blank | 313 comment | 43 complexity | 4e1a0b7f9712f33d8abfe22e3325ef19 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: Imap.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_Protocol_Imap
 31 */
 32require_once 'Zend/Mail/Protocol/Imap.php';
 33
 34/**
 35 * @see Zend_Mail_Storage_Writable_Interface
 36 */
 37require_once 'Zend/Mail/Storage/Writable/Interface.php';
 38
 39/**
 40 * @see Zend_Mail_Storage_Folder_Interface
 41 */
 42require_once 'Zend/Mail/Storage/Folder/Interface.php';
 43
 44/**
 45 * @see Zend_Mail_Storage_Folder
 46 */
 47require_once 'Zend/Mail/Storage/Folder.php';
 48
 49/**
 50 * @see Zend_Mail_Message
 51 */
 52require_once 'Zend/Mail/Message.php';
 53
 54/**
 55 * @see Zend_Mail_Storage
 56 */
 57require_once 'Zend/Mail/Storage.php';
 58
 59/**
 60 * @category   Zend
 61 * @package    Zend_Mail
 62 * @subpackage Storage
 63 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 64 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 65 */
 66class Zend_Mail_Storage_Imap extends Zend_Mail_Storage_Abstract
 67                             implements Zend_Mail_Storage_Folder_Interface, Zend_Mail_Storage_Writable_Interface
 68{
 69    // TODO: with an internal cache we could optimize this class, or create an extra class with
 70    // such optimizations. Especially the various fetch calls could be combined to one cache call
 71
 72    /**
 73     * protocol handler
 74     * @var null|Zend_Mail_Protocol_Imap
 75     */
 76    protected $_protocol;
 77
 78    /**
 79     * name of current folder
 80     * @var string
 81     */
 82    protected $_currentFolder = '';
 83
 84    /**
 85     * imap flags to constants translation
 86     * @var array
 87     */
 88    protected static $_knownFlags = array('\Passed'   => Zend_Mail_Storage::FLAG_PASSED,
 89                                          '\Answered' => Zend_Mail_Storage::FLAG_ANSWERED,
 90                                          '\Seen'     => Zend_Mail_Storage::FLAG_SEEN,
 91                                          '\Deleted'  => Zend_Mail_Storage::FLAG_DELETED,
 92                                          '\Draft'    => Zend_Mail_Storage::FLAG_DRAFT,
 93                                          '\Flagged'  => Zend_Mail_Storage::FLAG_FLAGGED);
 94
 95    /**
 96     * map flags to search criterias
 97     * @var array
 98     */
 99    protected static $_searchFlags = array('\Recent'   => 'RECENT',
100                                           '\Answered' => 'ANSWERED',
101                                           '\Seen'     => 'SEEN',
102                                           '\Deleted'  => 'DELETED',
103                                           '\Draft'    => 'DRAFT',
104                                           '\Flagged'  => 'FLAGGED');
105
106    /**
107     * Count messages all messages in current box
108     *
109     * @return int number of messages
110     * @throws Zend_Mail_Storage_Exception
111     * @throws Zend_Mail_Protocol_Exception
112     */
113    public function countMessages($flags = null)
114    {
115        if (!$this->_currentFolder) {
116            /**
117             * @see Zend_Mail_Storage_Exception
118             */
119            require_once 'Zend/Mail/Storage/Exception.php';
120            throw new Zend_Mail_Storage_Exception('No selected folder to count');
121        }
122
123        if ($flags === null) {
124            return count($this->_protocol->search(array('ALL')));
125        }
126
127        $params = array();
128        foreach ((array)$flags as $flag) {
129            if (isset(self::$_searchFlags[$flag])) {
130                $params[] = self::$_searchFlags[$flag];
131            } else {
132                $params[] = 'KEYWORD';
133                $params[] = $this->_protocol->escapeString($flag);
134            }
135        }
136        return count($this->_protocol->search($params));
137    }
138
139    /**
140     * get a list of messages with number and size
141     *
142     * @param int $id number of message
143     * @return int|array size of given message of list with all messages as array(num => size)
144     * @throws Zend_Mail_Protocol_Exception
145     */
146    public function getSize($id = 0)
147    {
148        if ($id) {
149            return $this->_protocol->fetch('RFC822.SIZE', $id);
150        }
151        return $this->_protocol->fetch('RFC822.SIZE', 1, INF);
152    }
153
154    /**
155     * Fetch a message
156     *
157     * @param int $id number of message
158     * @return Zend_Mail_Message
159     * @throws Zend_Mail_Protocol_Exception
160     */
161    public function getMessage($id)
162    {
163        $data = $this->_protocol->fetch(array('FLAGS', 'RFC822.HEADER'), $id);
164        $header = $data['RFC822.HEADER'];
165
166        $flags = array();
167        foreach ($data['FLAGS'] as $flag) {
168            $flags[] = isset(self::$_knownFlags[$flag]) ? self::$_knownFlags[$flag] : $flag;
169        }
170
171        return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $header, 'flags' => $flags));
172    }
173
174    /*
175     * Get raw header of message or part
176     *
177     * @param  int               $id       number of message
178     * @param  null|array|string $part     path to part or null for messsage header
179     * @param  int               $topLines include this many lines with header (after an empty line)
180     * @param  int $topLines include this many lines with header (after an empty line)
181     * @return string raw header
182     * @throws Zend_Mail_Protocol_Exception
183     * @throws Zend_Mail_Storage_Exception
184     */
185    public function getRawHeader($id, $part = null, $topLines = 0)
186    {
187        if ($part !== null) {
188            // TODO: implement
189            /**
190             * @see Zend_Mail_Storage_Exception
191             */
192            require_once 'Zend/Mail/Storage/Exception.php';
193            throw new Zend_Mail_Storage_Exception('not implemented');
194        }
195
196        // TODO: toplines
197        return $this->_protocol->fetch('RFC822.HEADER', $id);
198    }
199
200    /*
201     * Get raw content of message or part
202     *
203     * @param  int               $id   number of message
204     * @param  null|array|string $part path to part or null for messsage content
205     * @return string raw content
206     * @throws Zend_Mail_Protocol_Exception
207     * @throws Zend_Mail_Storage_Exception
208     */
209    public function getRawContent($id, $part = null)
210    {
211        if ($part !== null) {
212            // TODO: implement
213            /**
214             * @see Zend_Mail_Storage_Exception
215             */
216            require_once 'Zend/Mail/Storage/Exception.php';
217            throw new Zend_Mail_Storage_Exception('not implemented');
218        }
219
220        return $this->_protocol->fetch('RFC822.TEXT', $id);
221    }
222
223    /**
224     * create instance with parameters
225     * Supported paramters are
226     *   - user username
227     *   - host hostname or ip address of IMAP server [optional, default = 'localhost']
228     *   - password password for user 'username' [optional, default = '']
229     *   - port port for IMAP server [optional, default = 110]
230     *   - ssl 'SSL' or 'TLS' for secure sockets
231     *   - folder select this folder [optional, default = 'INBOX']
232     *
233     * @param  array $params mail reader specific parameters
234     * @throws Zend_Mail_Storage_Exception
235     * @throws Zend_Mail_Protocol_Exception
236     */
237    public function __construct($params)
238    {
239        if (is_array($params)) {
240            $params = (object)$params;
241        }
242
243        $this->_has['flags'] = true;
244
245        if ($params instanceof Zend_Mail_Protocol_Imap) {
246            $this->_protocol = $params;
247            try {
248                $this->selectFolder('INBOX');
249            } catch(Zend_Mail_Storage_Exception $e) {
250                /**
251                 * @see Zend_Mail_Storage_Exception
252                 */
253                require_once 'Zend/Mail/Storage/Exception.php';
254                throw new Zend_Mail_Storage_Exception('cannot select INBOX, is this a valid transport?', 0, $e);
255            }
256            return;
257        }
258
259        if (!isset($params->user)) {
260            /**
261             * @see Zend_Mail_Storage_Exception
262             */
263            require_once 'Zend/Mail/Storage/Exception.php';
264            throw new Zend_Mail_Storage_Exception('need at least user in params');
265        }
266
267        $host     = isset($params->host)     ? $params->host     : 'localhost';
268        $password = isset($params->password) ? $params->password : '';
269        $port     = isset($params->port)     ? $params->port     : null;
270        $ssl      = isset($params->ssl)      ? $params->ssl      : false;
271
272        $this->_protocol = new Zend_Mail_Protocol_Imap();
273        $this->_protocol->connect($host, $port, $ssl);
274        if (!$this->_protocol->login($params->user, $password)) {
275            /**
276             * @see Zend_Mail_Storage_Exception
277             */
278            require_once 'Zend/Mail/Storage/Exception.php';
279            throw new Zend_Mail_Storage_Exception('cannot login, user or password wrong');
280        }
281        $this->selectFolder(isset($params->folder) ? $params->folder : 'INBOX');
282    }
283
284    /**
285     * Close resource for mail lib. If you need to control, when the resource
286     * is closed. Otherwise the destructor would call this.
287     *
288     * @return null
289     */
290    public function close()
291    {
292        $this->_currentFolder = '';
293        $this->_protocol->logout();
294    }
295
296    /**
297     * Keep the server busy.
298     *
299     * @return null
300     * @throws Zend_Mail_Storage_Exception
301     */
302    public function noop()
303    {
304        if (!$this->_protocol->noop()) {
305            /**
306             * @see Zend_Mail_Storage_Exception
307             */
308            require_once 'Zend/Mail/Storage/Exception.php';
309            throw new Zend_Mail_Storage_Exception('could not do nothing');
310        }
311    }
312
313    /**
314     * Remove a message from server. If you're doing that from a web enviroment
315     * you should be careful and use a uniqueid as parameter if possible to
316     * identify the message.
317     *
318     * @param   int $id number of message
319     * @return  null
320     * @throws  Zend_Mail_Storage_Exception
321     */
322    public function removeMessage($id)
323    {
324        if (!$this->_protocol->store(array(Zend_Mail_Storage::FLAG_DELETED), $id, null, '+')) {
325            /**
326             * @see Zend_Mail_Storage_Exception
327             */
328            require_once 'Zend/Mail/Storage/Exception.php';
329            throw new Zend_Mail_Storage_Exception('cannot set deleted flag');
330        }
331        // TODO: expunge here or at close? we can handle an error here better and are more fail safe
332        if (!$this->_protocol->expunge()) {
333            /**
334             * @see Zend_Mail_Storage_Exception
335             */
336            require_once 'Zend/Mail/Storage/Exception.php';
337            throw new Zend_Mail_Storage_Exception('message marked as deleted, but could not expunge');
338        }
339    }
340
341    /**
342     * get unique id for one or all messages
343     *
344     * if storage does not support unique ids it's the same as the message number
345     *
346     * @param int|null $id message number
347     * @return array|string message number for given message or all messages as array
348     * @throws Zend_Mail_Storage_Exception
349     */
350    public function getUniqueId($id = null)
351    {
352        if ($id) {
353            return $this->_protocol->fetch('UID', $id);
354        }
355
356        return $this->_protocol->fetch('UID', 1, INF);
357    }
358
359    /**
360     * get a message number from a unique id
361     *
362     * I.e. if you have a webmailer that supports deleting messages you should use unique ids
363     * as parameter and use this method to translate it to message number right before calling removeMessage()
364     *
365     * @param string $id unique id
366     * @return int message number
367     * @throws Zend_Mail_Storage_Exception
368     */
369    public function getNumberByUniqueId($id)
370    {
371        // TODO: use search to find number directly
372        $ids = $this->getUniqueId();
373        foreach ($ids as $k => $v) {
374            if ($v == $id) {
375                return $k;
376            }
377        }
378
379        /**
380         * @see Zend_Mail_Storage_Exception
381         */
382        require_once 'Zend/Mail/Storage/Exception.php';
383        throw new Zend_Mail_Storage_Exception('unique id not found');
384    }
385
386
387    /**
388     * get root folder or given folder
389     *
390     * @param  string $rootFolder get folder structure for given folder, else root
391     * @return Zend_Mail_Storage_Folder root or wanted folder
392     * @throws Zend_Mail_Storage_Exception
393     * @throws Zend_Mail_Protocol_Exception
394     */
395    public function getFolders($rootFolder = null)
396    {
397        $folders = $this->_protocol->listMailbox((string)$rootFolder);
398        if (!$folders) {
399            /**
400             * @see Zend_Mail_Storage_Exception
401             */
402            require_once 'Zend/Mail/Storage/Exception.php';
403            throw new Zend_Mail_Storage_Exception('folder not found');
404        }
405
406        ksort($folders, SORT_STRING);
407        $root = new Zend_Mail_Storage_Folder('/', '/', false);
408        $stack = array(null);
409        $folderStack = array(null);
410        $parentFolder = $root;
411        $parent = '';
412
413        foreach ($folders as $globalName => $data) {
414            do {
415                if (!$parent || strpos($globalName, $parent) === 0) {
416                    $pos = strrpos($globalName, $data['delim']);
417                    if ($pos === false) {
418                        $localName = $globalName;
419                    } else {
420                        $localName = substr($globalName, $pos + 1);
421                    }
422                    $selectable = !$data['flags'] || !in_array('\\Noselect', $data['flags']);
423
424                    array_push($stack, $parent);
425                    $parent = $globalName . $data['delim'];
426                    $folder = new Zend_Mail_Storage_Folder($localName, $globalName, $selectable);
427                    $parentFolder->$localName = $folder;
428                    array_push($folderStack, $parentFolder);
429                    $parentFolder = $folder;
430                    break;
431                } else if ($stack) {
432                    $parent = array_pop($stack);
433                    $parentFolder = array_pop($folderStack);
434                }
435            } while ($stack);
436            if (!$stack) {
437                /**
438                 * @see Zend_Mail_Storage_Exception
439                 */
440                require_once 'Zend/Mail/Storage/Exception.php';
441                throw new Zend_Mail_Storage_Exception('error while constructing folder tree');
442            }
443        }
444
445        return $root;
446    }
447
448    /**
449     * select given folder
450     *
451     * folder must be selectable!
452     *
453     * @param  Zend_Mail_Storage_Folder|string $globalName global name of folder or instance for subfolder
454     * @return null
455     * @throws Zend_Mail_Storage_Exception
456     * @throws Zend_Mail_Protocol_Exception
457     */
458    public function selectFolder($globalName)
459    {
460        $this->_currentFolder = $globalName;
461        if (!$this->_protocol->select($this->_currentFolder)) {
462            $this->_currentFolder = '';
463            /**
464             * @see Zend_Mail_Storage_Exception
465             */
466            require_once 'Zend/Mail/Storage/Exception.php';
467            throw new Zend_Mail_Storage_Exception('cannot change folder, maybe it does not exist');
468        }
469    }
470
471
472    /**
473     * get Zend_Mail_Storage_Folder instance for current folder
474     *
475     * @return Zend_Mail_Storage_Folder instance of current folder
476     * @throws Zend_Mail_Storage_Exception
477     */
478    public function getCurrentFolder()
479    {
480        return $this->_currentFolder;
481    }
482
483    /**
484     * create a new folder
485     *
486     * This method also creates parent folders if necessary. Some mail storages may restrict, which folder
487     * may be used as parent or which chars may be used in the folder name
488     *
489     * @param  string                          $name         global name of folder, local name if $parentFolder is set
490     * @param  string|Zend_Mail_Storage_Folder $parentFolder parent folder for new folder, else root folder is parent
491     * @return null
492     * @throws Zend_Mail_Storage_Exception
493     */
494    public function createFolder($name, $parentFolder = null)
495    {
496        // TODO: we assume / as the hierarchy delim - need to get that from the folder class!
497        if ($parentFolder instanceof Zend_Mail_Storage_Folder) {
498            $folder = $parentFolder->getGlobalName() . '/' . $name;
499        } else if ($parentFolder != null) {
500            $folder = $parentFolder . '/' . $name;
501        } else {
502            $folder = $name;
503        }
504
505        if (!$this->_protocol->create($folder)) {
506            /**
507             * @see Zend_Mail_Storage_Exception
508             */
509            require_once 'Zend/Mail/Storage/Exception.php';
510            throw new Zend_Mail_Storage_Exception('cannot create folder');
511        }
512    }
513
514    /**
515     * remove a folder
516     *
517     * @param  string|Zend_Mail_Storage_Folder $name      name or instance of folder
518     * @return null
519     * @throws Zend_Mail_Storage_Exception
520     */
521    public function removeFolder($name)
522    {
523        if ($name instanceof Zend_Mail_Storage_Folder) {
524            $name = $name->getGlobalName();
525        }
526
527        if (!$this->_protocol->delete($name)) {
528            /**
529             * @see Zend_Mail_Storage_Exception
530             */
531            require_once 'Zend/Mail/Storage/Exception.php';
532            throw new Zend_Mail_Storage_Exception('cannot delete folder');
533        }
534    }
535
536    /**
537     * rename and/or move folder
538     *
539     * The new name has the same restrictions as in createFolder()
540     *
541     * @param  string|Zend_Mail_Storage_Folder $oldName name or instance of folder
542     * @param  string                          $newName new global name of folder
543     * @return null
544     * @throws Zend_Mail_Storage_Exception
545     */
546    public function renameFolder($oldName, $newName)
547    {
548        if ($oldName instanceof Zend_Mail_Storage_Folder) {
549            $oldName = $oldName->getGlobalName();
550        }
551
552        if (!$this->_protocol->rename($oldName, $newName)) {
553            /**
554             * @see Zend_Mail_Storage_Exception
555             */
556            require_once 'Zend/Mail/Storage/Exception.php';
557            throw new Zend_Mail_Storage_Exception('cannot rename folder');
558        }
559    }
560
561    /**
562     * append a new message to mail storage
563     *
564     * @param  string                                     $message message as string or instance of message class
565     * @param  null|string|Zend_Mail_Storage_Folder       $folder  folder for new message, else current folder is taken
566     * @param  null|array                                 $flags   set flags for new message, else a default set is used
567     * @throws Zend_Mail_Storage_Exception
568     */
569     // not yet * @param string|Zend_Mail_Message|Zend_Mime_Message $message message as string or instance of message class
570    public function appendMessage($message, $folder = null, $flags = null)
571    {
572        if ($folder === null) {
573            $folder = $this->_currentFolder;
574        }
575
576        if ($flags === null) {
577            $flags = array(Zend_Mail_Storage::FLAG_SEEN);
578        }
579
580        // TODO: handle class instances for $message
581        if (!$this->_protocol->append($folder, $message, $flags)) {
582            /**
583             * @see Zend_Mail_Storage_Exception
584             */
585            require_once 'Zend/Mail/Storage/Exception.php';
586            throw new Zend_Mail_Storage_Exception('cannot create message, please check if the folder exists and your flags');
587        }
588    }
589
590    /**
591     * copy an existing message
592     *
593     * @param  int                             $id     number of message
594     * @param  string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
595     * @return null
596     * @throws Zend_Mail_Storage_Exception
597     */
598    public function copyMessage($id, $folder)
599    {
600        if (!$this->_protocol->copy($folder, $id)) {
601            /**
602             * @see Zend_Mail_Storage_Exception
603             */
604            require_once 'Zend/Mail/Storage/Exception.php';
605            throw new Zend_Mail_Storage_Exception('cannot copy message, does the folder exist?');
606        }
607    }
608
609    /**
610     * move an existing message
611     *
612     * NOTE: imap has no native move command, thus it's emulated with copy and delete
613     *
614     * @param  int                             $id     number of message
615     * @param  string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
616     * @return null
617     * @throws Zend_Mail_Storage_Exception
618     */
619    public function moveMessage($id, $folder) {
620        $this->copyMessage($id, $folder);
621        $this->removeMessage($id);
622    }
623
624    /**
625     * set flags for message
626     *
627     * NOTE: this method can't set the recent flag.
628     *
629     * @param  int   $id    number of message
630     * @param  array $flags new flags for message
631     * @throws Zend_Mail_Storage_Exception
632     */
633    public function setFlags($id, $flags)
634    {
635        if (!$this->_protocol->store($flags, $id)) {
636            /**
637             * @see Zend_Mail_Storage_Exception
638             */
639            require_once 'Zend/Mail/Storage/Exception.php';
640            throw new Zend_Mail_Storage_Exception('cannot set flags, have you tried to set the recent flag or special chars?');
641        }
642    }
643}
644