fix calendar css, take 2. (#213)
[openemr.git] / interface / modules / zend_modules / library / Zend / Mail / Storage / Imap.php
blob142f8624d35cf80944aa31291ceb74c317336f31
1 <?php
2 /**
3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
8 */
10 namespace Zend\Mail\Storage;
12 use Zend\Mail;
13 use Zend\Mail\Protocol;
15 class Imap extends AbstractStorage implements Folder\FolderInterface, Writable\WritableInterface
17 // TODO: with an internal cache we could optimize this class, or create an extra class with
18 // such optimizations. Especially the various fetch calls could be combined to one cache call
20 /**
21 * protocol handler
22 * @var null|\Zend\Mail\Protocol\Imap
24 protected $protocol;
26 /**
27 * name of current folder
28 * @var string
30 protected $currentFolder = '';
32 /**
33 * IMAP flags to constants translation
34 * @var array
36 protected static $knownFlags = array('\Passed' => Mail\Storage::FLAG_PASSED,
37 '\Answered' => Mail\Storage::FLAG_ANSWERED,
38 '\Seen' => Mail\Storage::FLAG_SEEN,
39 '\Unseen' => Mail\Storage::FLAG_UNSEEN,
40 '\Deleted' => Mail\Storage::FLAG_DELETED,
41 '\Draft' => Mail\Storage::FLAG_DRAFT,
42 '\Flagged' => Mail\Storage::FLAG_FLAGGED);
44 /**
45 * IMAP flags to search criteria
46 * @var array
48 protected static $searchFlags = array('\Recent' => 'RECENT',
49 '\Answered' => 'ANSWERED',
50 '\Seen' => 'SEEN',
51 '\Unseen' => 'UNSEEN',
52 '\Deleted' => 'DELETED',
53 '\Draft' => 'DRAFT',
54 '\Flagged' => 'FLAGGED');
56 /**
57 * Count messages all messages in current box
59 * @param null $flags
60 * @throws Exception\RuntimeException
61 * @throws \Zend\Mail\Protocol\Exception\RuntimeException
62 * @return int number of messages
64 public function countMessages($flags = null)
66 if (!$this->currentFolder) {
67 throw new Exception\RuntimeException('No selected folder to count');
70 if ($flags === null) {
71 return count($this->protocol->search(array('ALL')));
74 $params = array();
75 foreach ((array) $flags as $flag) {
76 if (isset(static::$searchFlags[$flag])) {
77 $params[] = static::$searchFlags[$flag];
78 } else {
79 $params[] = 'KEYWORD';
80 $params[] = $this->protocol->escapeString($flag);
83 return count($this->protocol->search($params));
86 /**
87 * get a list of messages with number and size
89 * @param int $id number of message
90 * @return int|array size of given message of list with all messages as array(num => size)
91 * @throws \Zend\Mail\Protocol\Exception\RuntimeException
93 public function getSize($id = 0)
95 if ($id) {
96 return $this->protocol->fetch('RFC822.SIZE', $id);
98 return $this->protocol->fetch('RFC822.SIZE', 1, INF);
102 * Fetch a message
104 * @param int $id number of message
105 * @return \Zend\Mail\Storage\Message
106 * @throws \Zend\Mail\Protocol\Exception\RuntimeException
108 public function getMessage($id)
110 $data = $this->protocol->fetch(array('FLAGS', 'RFC822.HEADER'), $id);
111 $header = $data['RFC822.HEADER'];
113 $flags = array();
114 foreach ($data['FLAGS'] as $flag) {
115 $flags[] = isset(static::$knownFlags[$flag]) ? static::$knownFlags[$flag] : $flag;
118 return new $this->messageClass(array('handler' => $this, 'id' => $id, 'headers' => $header, 'flags' => $flags));
122 * Get raw header of message or part
124 * @param int $id number of message
125 * @param null|array|string $part path to part or null for message header
126 * @param int $topLines include this many lines with header (after an empty line)
127 * @param int $topLines include this many lines with header (after an empty line)
128 * @return string raw header
129 * @throws Exception\RuntimeException
130 * @throws \Zend\Mail\Protocol\Exception\RuntimeException
132 public function getRawHeader($id, $part = null, $topLines = 0)
134 if ($part !== null) {
135 // TODO: implement
136 throw new Exception\RuntimeException('not implemented');
139 // TODO: toplines
140 return $this->protocol->fetch('RFC822.HEADER', $id);
144 * Get raw content of message or part
146 * @param int $id number of message
147 * @param null|array|string $part path to part or null for message content
148 * @return string raw content
149 * @throws \Zend\Mail\Protocol\Exception\RuntimeException
150 * @throws Exception\RuntimeException
152 public function getRawContent($id, $part = null)
154 if ($part !== null) {
155 // TODO: implement
156 throw new Exception\RuntimeException('not implemented');
159 return $this->protocol->fetch('RFC822.TEXT', $id);
163 * create instance with parameters
164 * Supported parameters are
165 * - user username
166 * - host hostname or ip address of IMAP server [optional, default = 'localhost']
167 * - password password for user 'username' [optional, default = '']
168 * - port port for IMAP server [optional, default = 110]
169 * - ssl 'SSL' or 'TLS' for secure sockets
170 * - folder select this folder [optional, default = 'INBOX']
172 * @param array $params mail reader specific parameters
173 * @throws Exception\RuntimeException
174 * @throws Exception\InvalidArgumentException
175 * @throws \Zend\Mail\Protocol\Exception\RuntimeException
177 public function __construct($params)
179 if (is_array($params)) {
180 $params = (object) $params;
183 $this->has['flags'] = true;
185 if ($params instanceof Protocol\Imap) {
186 $this->protocol = $params;
187 try {
188 $this->selectFolder('INBOX');
189 } catch (Exception\ExceptionInterface $e) {
190 throw new Exception\RuntimeException('cannot select INBOX, is this a valid transport?', 0, $e);
192 return;
195 if (!isset($params->user)) {
196 throw new Exception\InvalidArgumentException('need at least user in params');
199 $host = isset($params->host) ? $params->host : 'localhost';
200 $password = isset($params->password) ? $params->password : '';
201 $port = isset($params->port) ? $params->port : null;
202 $ssl = isset($params->ssl) ? $params->ssl : false;
204 $this->protocol = new Protocol\Imap();
205 $this->protocol->connect($host, $port, $ssl);
206 if (!$this->protocol->login($params->user, $password)) {
207 throw new Exception\RuntimeException('cannot login, user or password wrong');
209 $this->selectFolder(isset($params->folder) ? $params->folder : 'INBOX');
213 * Close resource for mail lib. If you need to control, when the resource
214 * is closed. Otherwise the destructor would call this.
216 public function close()
218 $this->currentFolder = '';
219 $this->protocol->logout();
223 * Keep the server busy.
225 * @throws Exception\RuntimeException
227 public function noop()
229 if (!$this->protocol->noop()) {
230 throw new Exception\RuntimeException('could not do nothing');
235 * Remove a message from server. If you're doing that from a web environment
236 * you should be careful and use a uniqueid as parameter if possible to
237 * identify the message.
239 * @param int $id number of message
240 * @throws Exception\RuntimeException
242 public function removeMessage($id)
244 if (!$this->protocol->store(array(Mail\Storage::FLAG_DELETED), $id, null, '+')) {
245 throw new Exception\RuntimeException('cannot set deleted flag');
247 // TODO: expunge here or at close? we can handle an error here better and are more fail safe
248 if (!$this->protocol->expunge()) {
249 throw new Exception\RuntimeException('message marked as deleted, but could not expunge');
254 * get unique id for one or all messages
256 * if storage does not support unique ids it's the same as the message number
258 * @param int|null $id message number
259 * @return array|string message number for given message or all messages as array
260 * @throws \Zend\Mail\Protocol\Exception\RuntimeException
262 public function getUniqueId($id = null)
264 if ($id) {
265 return $this->protocol->fetch('UID', $id);
268 return $this->protocol->fetch('UID', 1, INF);
272 * get a message number from a unique id
274 * I.e. if you have a webmailer that supports deleting messages you should use unique ids
275 * as parameter and use this method to translate it to message number right before calling removeMessage()
277 * @param string $id unique id
278 * @throws Exception\InvalidArgumentException
279 * @return int message number
281 public function getNumberByUniqueId($id)
283 // TODO: use search to find number directly
284 $ids = $this->getUniqueId();
285 foreach ($ids as $k => $v) {
286 if ($v == $id) {
287 return $k;
291 throw new Exception\InvalidArgumentException('unique id not found');
295 * get root folder or given folder
297 * @param string $rootFolder get folder structure for given folder, else root
298 * @throws Exception\RuntimeException
299 * @throws Exception\InvalidArgumentException
300 * @throws \Zend\Mail\Protocol\Exception\RuntimeException
301 * @return \Zend\Mail\Storage\Folder root or wanted folder
303 public function getFolders($rootFolder = null)
305 $folders = $this->protocol->listMailbox((string) $rootFolder);
306 if (!$folders) {
307 throw new Exception\InvalidArgumentException('folder not found');
310 ksort($folders, SORT_STRING);
311 $root = new Folder('/', '/', false);
312 $stack = array(null);
313 $folderStack = array(null);
314 $parentFolder = $root;
315 $parent = '';
317 foreach ($folders as $globalName => $data) {
318 do {
319 if (!$parent || strpos($globalName, $parent) === 0) {
320 $pos = strrpos($globalName, $data['delim']);
321 if ($pos === false) {
322 $localName = $globalName;
323 } else {
324 $localName = substr($globalName, $pos + 1);
326 $selectable = !$data['flags'] || !in_array('\\Noselect', $data['flags']);
328 array_push($stack, $parent);
329 $parent = $globalName . $data['delim'];
330 $folder = new Folder($localName, $globalName, $selectable);
331 $parentFolder->$localName = $folder;
332 array_push($folderStack, $parentFolder);
333 $parentFolder = $folder;
334 break;
335 } elseif ($stack) {
336 $parent = array_pop($stack);
337 $parentFolder = array_pop($folderStack);
339 } while ($stack);
340 if (!$stack) {
341 throw new Exception\RuntimeException('error while constructing folder tree');
345 return $root;
349 * select given folder
351 * folder must be selectable!
353 * @param \Zend\Mail\Storage\Folder|string $globalName global name of folder or instance for subfolder
354 * @throws Exception\RuntimeException
355 * @throws \Zend\Mail\Protocol\Exception\RuntimeException
357 public function selectFolder($globalName)
359 $this->currentFolder = $globalName;
360 if (!$this->protocol->select($this->currentFolder)) {
361 $this->currentFolder = '';
362 throw new Exception\RuntimeException('cannot change folder, maybe it does not exist');
367 * get \Zend\Mail\Storage\Folder instance for current folder
369 * @return \Zend\Mail\Storage\Folder instance of current folder
371 public function getCurrentFolder()
373 return $this->currentFolder;
377 * create a new folder
379 * This method also creates parent folders if necessary. Some mail storages may restrict, which folder
380 * may be used as parent or which chars may be used in the folder name
382 * @param string $name global name of folder, local name if $parentFolder is set
383 * @param string|\Zend\Mail\Storage\Folder $parentFolder parent folder for new folder, else root folder is parent
384 * @throws Exception\RuntimeException
386 public function createFolder($name, $parentFolder = null)
388 // TODO: we assume / as the hierarchy delim - need to get that from the folder class!
389 if ($parentFolder instanceof Folder) {
390 $folder = $parentFolder->getGlobalName() . '/' . $name;
391 } elseif ($parentFolder !== null) {
392 $folder = $parentFolder . '/' . $name;
393 } else {
394 $folder = $name;
397 if (!$this->protocol->create($folder)) {
398 throw new Exception\RuntimeException('cannot create folder');
403 * remove a folder
405 * @param string|\Zend\Mail\Storage\Folder $name name or instance of folder
406 * @throws Exception\RuntimeException
408 public function removeFolder($name)
410 if ($name instanceof Folder) {
411 $name = $name->getGlobalName();
414 if (!$this->protocol->delete($name)) {
415 throw new Exception\RuntimeException('cannot delete folder');
420 * rename and/or move folder
422 * The new name has the same restrictions as in createFolder()
424 * @param string|\Zend\Mail\Storage\Folder $oldName name or instance of folder
425 * @param string $newName new global name of folder
426 * @throws Exception\RuntimeException
428 public function renameFolder($oldName, $newName)
430 if ($oldName instanceof Folder) {
431 $oldName = $oldName->getGlobalName();
434 if (!$this->protocol->rename($oldName, $newName)) {
435 throw new Exception\RuntimeException('cannot rename folder');
440 * append a new message to mail storage
442 * @param string $message message as string or instance of message class
443 * @param null|string|\Zend\Mail\Storage\Folder $folder folder for new message, else current folder is taken
444 * @param null|array $flags set flags for new message, else a default set is used
445 * @throws Exception\RuntimeException
447 public function appendMessage($message, $folder = null, $flags = null)
449 if ($folder === null) {
450 $folder = $this->currentFolder;
453 if ($flags === null) {
454 $flags = array(Mail\Storage::FLAG_SEEN);
457 // TODO: handle class instances for $message
458 if (!$this->protocol->append($folder, $message, $flags)) {
459 throw new Exception\RuntimeException('cannot create message, please check if the folder exists and your flags');
464 * copy an existing message
466 * @param int $id number of message
467 * @param string|\Zend\Mail\Storage\Folder $folder name or instance of target folder
468 * @throws Exception\RuntimeException
470 public function copyMessage($id, $folder)
472 if (!$this->protocol->copy($folder, $id)) {
473 throw new Exception\RuntimeException('cannot copy message, does the folder exist?');
478 * move an existing message
480 * NOTE: IMAP has no native move command, thus it's emulated with copy and delete
482 * @param int $id number of message
483 * @param string|\Zend\Mail\Storage\Folder $folder name or instance of target folder
484 * @throws Exception\RuntimeException
486 public function moveMessage($id, $folder)
488 $this->copyMessage($id, $folder);
489 $this->removeMessage($id);
493 * set flags for message
495 * NOTE: this method can't set the recent flag.
497 * @param int $id number of message
498 * @param array $flags new flags for message
499 * @throws Exception\RuntimeException
501 public function setFlags($id, $flags)
503 if (!$this->protocol->store($flags, $id)) {
504 throw new Exception\RuntimeException('cannot set flags, have you tried to set the recent flag or special chars?');