|
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 Protocol |
|
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 * @category Zend |
|
26 * @package Zend_Mail |
|
27 * @subpackage Protocol |
|
28 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
29 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
30 */ |
|
31 class Zend_Mail_Protocol_Imap |
|
32 { |
|
33 /** |
|
34 * Default timeout in seconds for initiating session |
|
35 */ |
|
36 const TIMEOUT_CONNECTION = 30; |
|
37 |
|
38 /** |
|
39 * socket to imap server |
|
40 * @var resource|null |
|
41 */ |
|
42 protected $_socket; |
|
43 |
|
44 /** |
|
45 * counter for request tag |
|
46 * @var int |
|
47 */ |
|
48 protected $_tagCount = 0; |
|
49 |
|
50 /** |
|
51 * Public constructor |
|
52 * |
|
53 * @param string $host hostname or IP address of IMAP server, if given connect() is called |
|
54 * @param int|null $port port of IMAP server, null for default (143 or 993 for ssl) |
|
55 * @param bool $ssl use ssl? 'SSL', 'TLS' or false |
|
56 * @throws Zend_Mail_Protocol_Exception |
|
57 */ |
|
58 function __construct($host = '', $port = null, $ssl = false) |
|
59 { |
|
60 if ($host) { |
|
61 $this->connect($host, $port, $ssl); |
|
62 } |
|
63 } |
|
64 |
|
65 /** |
|
66 * Public destructor |
|
67 */ |
|
68 public function __destruct() |
|
69 { |
|
70 $this->logout(); |
|
71 } |
|
72 |
|
73 /** |
|
74 * Open connection to IMAP server |
|
75 * |
|
76 * @param string $host hostname or IP address of IMAP server |
|
77 * @param int|null $port of IMAP server, default is 143 (993 for ssl) |
|
78 * @param string|bool $ssl use 'SSL', 'TLS' or false |
|
79 * @return string welcome message |
|
80 * @throws Zend_Mail_Protocol_Exception |
|
81 */ |
|
82 public function connect($host, $port = null, $ssl = false) |
|
83 { |
|
84 if ($ssl == 'SSL') { |
|
85 $host = 'ssl://' . $host; |
|
86 } |
|
87 |
|
88 if ($port === null) { |
|
89 $port = $ssl === 'SSL' ? 993 : 143; |
|
90 } |
|
91 |
|
92 $errno = 0; |
|
93 $errstr = ''; |
|
94 $this->_socket = @fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION); |
|
95 if (!$this->_socket) { |
|
96 /** |
|
97 * @see Zend_Mail_Protocol_Exception |
|
98 */ |
|
99 require_once 'Zend/Mail/Protocol/Exception.php'; |
|
100 throw new Zend_Mail_Protocol_Exception('cannot connect to host; error = ' . $errstr . |
|
101 ' (errno = ' . $errno . ' )'); |
|
102 } |
|
103 |
|
104 if (!$this->_assumedNextLine('* OK')) { |
|
105 /** |
|
106 * @see Zend_Mail_Protocol_Exception |
|
107 */ |
|
108 require_once 'Zend/Mail/Protocol/Exception.php'; |
|
109 throw new Zend_Mail_Protocol_Exception('host doesn\'t allow connection'); |
|
110 } |
|
111 |
|
112 if ($ssl === 'TLS') { |
|
113 $result = $this->requestAndResponse('STARTTLS'); |
|
114 $result = $result && stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); |
|
115 if (!$result) { |
|
116 /** |
|
117 * @see Zend_Mail_Protocol_Exception |
|
118 */ |
|
119 require_once 'Zend/Mail/Protocol/Exception.php'; |
|
120 throw new Zend_Mail_Protocol_Exception('cannot enable TLS'); |
|
121 } |
|
122 } |
|
123 } |
|
124 |
|
125 /** |
|
126 * get the next line from socket with error checking, but nothing else |
|
127 * |
|
128 * @return string next line |
|
129 * @throws Zend_Mail_Protocol_Exception |
|
130 */ |
|
131 protected function _nextLine() |
|
132 { |
|
133 $line = @fgets($this->_socket); |
|
134 if ($line === false) { |
|
135 /** |
|
136 * @see Zend_Mail_Protocol_Exception |
|
137 */ |
|
138 require_once 'Zend/Mail/Protocol/Exception.php'; |
|
139 throw new Zend_Mail_Protocol_Exception('cannot read - connection closed?'); |
|
140 } |
|
141 |
|
142 return $line; |
|
143 } |
|
144 |
|
145 /** |
|
146 * get next line and assume it starts with $start. some requests give a simple |
|
147 * feedback so we can quickly check if we can go on. |
|
148 * |
|
149 * @param string $start the first bytes we assume to be in the next line |
|
150 * @return bool line starts with $start |
|
151 * @throws Zend_Mail_Protocol_Exception |
|
152 */ |
|
153 protected function _assumedNextLine($start) |
|
154 { |
|
155 $line = $this->_nextLine(); |
|
156 return strpos($line, $start) === 0; |
|
157 } |
|
158 |
|
159 /** |
|
160 * get next line and split the tag. that's the normal case for a response line |
|
161 * |
|
162 * @param string $tag tag of line is returned by reference |
|
163 * @return string next line |
|
164 * @throws Zend_Mail_Protocol_Exception |
|
165 */ |
|
166 protected function _nextTaggedLine(&$tag) |
|
167 { |
|
168 $line = $this->_nextLine(); |
|
169 |
|
170 // seperate tag from line |
|
171 list($tag, $line) = explode(' ', $line, 2); |
|
172 |
|
173 return $line; |
|
174 } |
|
175 |
|
176 /** |
|
177 * split a given line in tokens. a token is literal of any form or a list |
|
178 * |
|
179 * @param string $line line to decode |
|
180 * @return array tokens, literals are returned as string, lists as array |
|
181 * @throws Zend_Mail_Protocol_Exception |
|
182 */ |
|
183 protected function _decodeLine($line) |
|
184 { |
|
185 $tokens = array(); |
|
186 $stack = array(); |
|
187 |
|
188 /* |
|
189 We start to decode the response here. The unterstood tokens are: |
|
190 literal |
|
191 "literal" or also "lit\\er\"al" |
|
192 {bytes}<NL>literal |
|
193 (literals*) |
|
194 All tokens are returned in an array. Literals in braces (the last unterstood |
|
195 token in the list) are returned as an array of tokens. I.e. the following response: |
|
196 "foo" baz {3}<NL>bar ("f\\\"oo" bar) |
|
197 would be returned as: |
|
198 array('foo', 'baz', 'bar', array('f\\\"oo', 'bar')); |
|
199 |
|
200 // TODO: add handling of '[' and ']' to parser for easier handling of response text |
|
201 */ |
|
202 // replace any trailling <NL> including spaces with a single space |
|
203 $line = rtrim($line) . ' '; |
|
204 while (($pos = strpos($line, ' ')) !== false) { |
|
205 $token = substr($line, 0, $pos); |
|
206 while ($token[0] == '(') { |
|
207 array_push($stack, $tokens); |
|
208 $tokens = array(); |
|
209 $token = substr($token, 1); |
|
210 } |
|
211 if ($token[0] == '"') { |
|
212 if (preg_match('%^\(*"((.|\\\\|\\")*?)" *%', $line, $matches)) { |
|
213 $tokens[] = $matches[1]; |
|
214 $line = substr($line, strlen($matches[0])); |
|
215 continue; |
|
216 } |
|
217 } |
|
218 if ($token[0] == '{') { |
|
219 $endPos = strpos($token, '}'); |
|
220 $chars = substr($token, 1, $endPos - 1); |
|
221 if (is_numeric($chars)) { |
|
222 $token = ''; |
|
223 while (strlen($token) < $chars) { |
|
224 $token .= $this->_nextLine(); |
|
225 } |
|
226 $line = ''; |
|
227 if (strlen($token) > $chars) { |
|
228 $line = substr($token, $chars); |
|
229 $token = substr($token, 0, $chars); |
|
230 } else { |
|
231 $line .= $this->_nextLine(); |
|
232 } |
|
233 $tokens[] = $token; |
|
234 $line = trim($line) . ' '; |
|
235 continue; |
|
236 } |
|
237 } |
|
238 if ($stack && $token[strlen($token) - 1] == ')') { |
|
239 // closing braces are not seperated by spaces, so we need to count them |
|
240 $braces = strlen($token); |
|
241 $token = rtrim($token, ')'); |
|
242 // only count braces if more than one |
|
243 $braces -= strlen($token) + 1; |
|
244 // only add if token had more than just closing braces |
|
245 if (rtrim($token) != '') { |
|
246 $tokens[] = rtrim($token); |
|
247 } |
|
248 $token = $tokens; |
|
249 $tokens = array_pop($stack); |
|
250 // special handline if more than one closing brace |
|
251 while ($braces-- > 0) { |
|
252 $tokens[] = $token; |
|
253 $token = $tokens; |
|
254 $tokens = array_pop($stack); |
|
255 } |
|
256 } |
|
257 $tokens[] = $token; |
|
258 $line = substr($line, $pos + 1); |
|
259 } |
|
260 |
|
261 // maybe the server forgot to send some closing braces |
|
262 while ($stack) { |
|
263 $child = $tokens; |
|
264 $tokens = array_pop($stack); |
|
265 $tokens[] = $child; |
|
266 } |
|
267 |
|
268 return $tokens; |
|
269 } |
|
270 |
|
271 /** |
|
272 * read a response "line" (could also be more than one real line if response has {..}<NL>) |
|
273 * and do a simple decode |
|
274 * |
|
275 * @param array|string $tokens decoded tokens are returned by reference, if $dontParse |
|
276 * is true the unparsed line is returned here |
|
277 * @param string $wantedTag check for this tag for response code. Default '*' is |
|
278 * continuation tag. |
|
279 * @param bool $dontParse if true only the unparsed line is returned $tokens |
|
280 * @return bool if returned tag matches wanted tag |
|
281 * @throws Zend_Mail_Protocol_Exception |
|
282 */ |
|
283 public function readLine(&$tokens = array(), $wantedTag = '*', $dontParse = false) |
|
284 { |
|
285 $line = $this->_nextTaggedLine($tag); |
|
286 if (!$dontParse) { |
|
287 $tokens = $this->_decodeLine($line); |
|
288 } else { |
|
289 $tokens = $line; |
|
290 } |
|
291 |
|
292 // if tag is wanted tag we might be at the end of a multiline response |
|
293 return $tag == $wantedTag; |
|
294 } |
|
295 |
|
296 /** |
|
297 * read all lines of response until given tag is found (last line of response) |
|
298 * |
|
299 * @param string $tag the tag of your request |
|
300 * @param string|array $filter you can filter the response so you get only the |
|
301 * given response lines |
|
302 * @param bool $dontParse if true every line is returned unparsed instead of |
|
303 * the decoded tokens |
|
304 * @return null|bool|array tokens if success, false if error, null if bad request |
|
305 * @throws Zend_Mail_Protocol_Exception |
|
306 */ |
|
307 public function readResponse($tag, $dontParse = false) |
|
308 { |
|
309 $lines = array(); |
|
310 while (!$this->readLine($tokens, $tag, $dontParse)) { |
|
311 $lines[] = $tokens; |
|
312 } |
|
313 |
|
314 if ($dontParse) { |
|
315 // last to chars are still needed for response code |
|
316 $tokens = array(substr($tokens, 0, 2)); |
|
317 } |
|
318 // last line has response code |
|
319 if ($tokens[0] == 'OK') { |
|
320 return $lines ? $lines : true; |
|
321 } else if ($tokens[0] == 'NO'){ |
|
322 return false; |
|
323 } |
|
324 return null; |
|
325 } |
|
326 |
|
327 /** |
|
328 * send a request |
|
329 * |
|
330 * @param string $command your request command |
|
331 * @param array $tokens additional parameters to command, use escapeString() to prepare |
|
332 * @param string $tag provide a tag otherwise an autogenerated is returned |
|
333 * @return null |
|
334 * @throws Zend_Mail_Protocol_Exception |
|
335 */ |
|
336 public function sendRequest($command, $tokens = array(), &$tag = null) |
|
337 { |
|
338 if (!$tag) { |
|
339 ++$this->_tagCount; |
|
340 $tag = 'TAG' . $this->_tagCount; |
|
341 } |
|
342 |
|
343 $line = $tag . ' ' . $command; |
|
344 |
|
345 foreach ($tokens as $token) { |
|
346 if (is_array($token)) { |
|
347 if (@fputs($this->_socket, $line . ' ' . $token[0] . "\r\n") === false) { |
|
348 /** |
|
349 * @see Zend_Mail_Protocol_Exception |
|
350 */ |
|
351 require_once 'Zend/Mail/Protocol/Exception.php'; |
|
352 throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?'); |
|
353 } |
|
354 if (!$this->_assumedNextLine('+ ')) { |
|
355 /** |
|
356 * @see Zend_Mail_Protocol_Exception |
|
357 */ |
|
358 require_once 'Zend/Mail/Protocol/Exception.php'; |
|
359 throw new Zend_Mail_Protocol_Exception('cannot send literal string'); |
|
360 } |
|
361 $line = $token[1]; |
|
362 } else { |
|
363 $line .= ' ' . $token; |
|
364 } |
|
365 } |
|
366 |
|
367 if (@fputs($this->_socket, $line . "\r\n") === false) { |
|
368 /** |
|
369 * @see Zend_Mail_Protocol_Exception |
|
370 */ |
|
371 require_once 'Zend/Mail/Protocol/Exception.php'; |
|
372 throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?'); |
|
373 } |
|
374 } |
|
375 |
|
376 /** |
|
377 * send a request and get response at once |
|
378 * |
|
379 * @param string $command command as in sendRequest() |
|
380 * @param array $tokens parameters as in sendRequest() |
|
381 * @param bool $dontParse if true unparsed lines are returned instead of tokens |
|
382 * @return mixed response as in readResponse() |
|
383 * @throws Zend_Mail_Protocol_Exception |
|
384 */ |
|
385 public function requestAndResponse($command, $tokens = array(), $dontParse = false) |
|
386 { |
|
387 $this->sendRequest($command, $tokens, $tag); |
|
388 $response = $this->readResponse($tag, $dontParse); |
|
389 |
|
390 return $response; |
|
391 } |
|
392 |
|
393 /** |
|
394 * escape one or more literals i.e. for sendRequest |
|
395 * |
|
396 * @param string|array $string the literal/-s |
|
397 * @return string|array escape literals, literals with newline ar returned |
|
398 * as array('{size}', 'string'); |
|
399 */ |
|
400 public function escapeString($string) |
|
401 { |
|
402 if (func_num_args() < 2) { |
|
403 if (strpos($string, "\n") !== false) { |
|
404 return array('{' . strlen($string) . '}', $string); |
|
405 } else { |
|
406 return '"' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $string) . '"'; |
|
407 } |
|
408 } |
|
409 $result = array(); |
|
410 foreach (func_get_args() as $string) { |
|
411 $result[] = $this->escapeString($string); |
|
412 } |
|
413 return $result; |
|
414 } |
|
415 |
|
416 /** |
|
417 * escape a list with literals or lists |
|
418 * |
|
419 * @param array $list list with literals or lists as PHP array |
|
420 * @return string escaped list for imap |
|
421 */ |
|
422 public function escapeList($list) |
|
423 { |
|
424 $result = array(); |
|
425 foreach ($list as $k => $v) { |
|
426 if (!is_array($v)) { |
|
427 // $result[] = $this->escapeString($v); |
|
428 $result[] = $v; |
|
429 continue; |
|
430 } |
|
431 $result[] = $this->escapeList($v); |
|
432 } |
|
433 return '(' . implode(' ', $result) . ')'; |
|
434 } |
|
435 |
|
436 /** |
|
437 * Login to IMAP server. |
|
438 * |
|
439 * @param string $user username |
|
440 * @param string $password password |
|
441 * @return bool success |
|
442 * @throws Zend_Mail_Protocol_Exception |
|
443 */ |
|
444 public function login($user, $password) |
|
445 { |
|
446 return $this->requestAndResponse('LOGIN', $this->escapeString($user, $password), true); |
|
447 } |
|
448 |
|
449 /** |
|
450 * logout of imap server |
|
451 * |
|
452 * @return bool success |
|
453 */ |
|
454 public function logout() |
|
455 { |
|
456 $result = false; |
|
457 if ($this->_socket) { |
|
458 try { |
|
459 $result = $this->requestAndResponse('LOGOUT', array(), true); |
|
460 } catch (Zend_Mail_Protocol_Exception $e) { |
|
461 // ignoring exception |
|
462 } |
|
463 fclose($this->_socket); |
|
464 $this->_socket = null; |
|
465 } |
|
466 return $result; |
|
467 } |
|
468 |
|
469 |
|
470 /** |
|
471 * Get capabilities from IMAP server |
|
472 * |
|
473 * @return array list of capabilities |
|
474 * @throws Zend_Mail_Protocol_Exception |
|
475 */ |
|
476 public function capability() |
|
477 { |
|
478 $response = $this->requestAndResponse('CAPABILITY'); |
|
479 |
|
480 if (!$response) { |
|
481 return $response; |
|
482 } |
|
483 |
|
484 $capabilities = array(); |
|
485 foreach ($response as $line) { |
|
486 $capabilities = array_merge($capabilities, $line); |
|
487 } |
|
488 return $capabilities; |
|
489 } |
|
490 |
|
491 /** |
|
492 * Examine and select have the same response. The common code for both |
|
493 * is in this method |
|
494 * |
|
495 * @param string $command can be 'EXAMINE' or 'SELECT' and this is used as command |
|
496 * @param string $box which folder to change to or examine |
|
497 * @return bool|array false if error, array with returned information |
|
498 * otherwise (flags, exists, recent, uidvalidity) |
|
499 * @throws Zend_Mail_Protocol_Exception |
|
500 */ |
|
501 public function examineOrSelect($command = 'EXAMINE', $box = 'INBOX') |
|
502 { |
|
503 $this->sendRequest($command, array($this->escapeString($box)), $tag); |
|
504 |
|
505 $result = array(); |
|
506 while (!$this->readLine($tokens, $tag)) { |
|
507 if ($tokens[0] == 'FLAGS') { |
|
508 array_shift($tokens); |
|
509 $result['flags'] = $tokens; |
|
510 continue; |
|
511 } |
|
512 switch ($tokens[1]) { |
|
513 case 'EXISTS': |
|
514 case 'RECENT': |
|
515 $result[strtolower($tokens[1])] = $tokens[0]; |
|
516 break; |
|
517 case '[UIDVALIDITY': |
|
518 $result['uidvalidity'] = (int)$tokens[2]; |
|
519 break; |
|
520 default: |
|
521 // ignore |
|
522 } |
|
523 } |
|
524 |
|
525 if ($tokens[0] != 'OK') { |
|
526 return false; |
|
527 } |
|
528 return $result; |
|
529 } |
|
530 |
|
531 /** |
|
532 * change folder |
|
533 * |
|
534 * @param string $box change to this folder |
|
535 * @return bool|array see examineOrselect() |
|
536 * @throws Zend_Mail_Protocol_Exception |
|
537 */ |
|
538 public function select($box = 'INBOX') |
|
539 { |
|
540 return $this->examineOrSelect('SELECT', $box); |
|
541 } |
|
542 |
|
543 /** |
|
544 * examine folder |
|
545 * |
|
546 * @param string $box examine this folder |
|
547 * @return bool|array see examineOrselect() |
|
548 * @throws Zend_Mail_Protocol_Exception |
|
549 */ |
|
550 public function examine($box = 'INBOX') |
|
551 { |
|
552 return $this->examineOrSelect('EXAMINE', $box); |
|
553 } |
|
554 |
|
555 /** |
|
556 * fetch one or more items of one or more messages |
|
557 * |
|
558 * @param string|array $items items to fetch from message(s) as string (if only one item) |
|
559 * or array of strings |
|
560 * @param int $from message for items or start message if $to !== null |
|
561 * @param int|null $to if null only one message ($from) is fetched, else it's the |
|
562 * last message, INF means last message avaible |
|
563 * @return string|array if only one item of one message is fetched it's returned as string |
|
564 * if items of one message are fetched it's returned as (name => value) |
|
565 * if one items of messages are fetched it's returned as (msgno => value) |
|
566 * if items of messages are fetchted it's returned as (msgno => (name => value)) |
|
567 * @throws Zend_Mail_Protocol_Exception |
|
568 */ |
|
569 public function fetch($items, $from, $to = null) |
|
570 { |
|
571 if (is_array($from)) { |
|
572 $set = implode(',', $from); |
|
573 } else if ($to === null) { |
|
574 $set = (int)$from; |
|
575 } else if ($to === INF) { |
|
576 $set = (int)$from . ':*'; |
|
577 } else { |
|
578 $set = (int)$from . ':' . (int)$to; |
|
579 } |
|
580 |
|
581 $items = (array)$items; |
|
582 $itemList = $this->escapeList($items); |
|
583 |
|
584 $this->sendRequest('FETCH', array($set, $itemList), $tag); |
|
585 |
|
586 $result = array(); |
|
587 while (!$this->readLine($tokens, $tag)) { |
|
588 // ignore other responses |
|
589 if ($tokens[1] != 'FETCH') { |
|
590 continue; |
|
591 } |
|
592 // ignore other messages |
|
593 if ($to === null && !is_array($from) && $tokens[0] != $from) { |
|
594 continue; |
|
595 } |
|
596 // if we only want one item we return that one directly |
|
597 if (count($items) == 1) { |
|
598 if ($tokens[2][0] == $items[0]) { |
|
599 $data = $tokens[2][1]; |
|
600 } else { |
|
601 // maybe the server send an other field we didn't wanted |
|
602 $count = count($tokens[2]); |
|
603 // we start with 2, because 0 was already checked |
|
604 for ($i = 2; $i < $count; $i += 2) { |
|
605 if ($tokens[2][$i] != $items[0]) { |
|
606 continue; |
|
607 } |
|
608 $data = $tokens[2][$i + 1]; |
|
609 break; |
|
610 } |
|
611 } |
|
612 } else { |
|
613 $data = array(); |
|
614 while (key($tokens[2]) !== null) { |
|
615 $data[current($tokens[2])] = next($tokens[2]); |
|
616 next($tokens[2]); |
|
617 } |
|
618 } |
|
619 // if we want only one message we can ignore everything else and just return |
|
620 if ($to === null && !is_array($from) && $tokens[0] == $from) { |
|
621 // we still need to read all lines |
|
622 while (!$this->readLine($tokens, $tag)); |
|
623 return $data; |
|
624 } |
|
625 $result[$tokens[0]] = $data; |
|
626 } |
|
627 |
|
628 if ($to === null && !is_array($from)) { |
|
629 /** |
|
630 * @see Zend_Mail_Protocol_Exception |
|
631 */ |
|
632 require_once 'Zend/Mail/Protocol/Exception.php'; |
|
633 throw new Zend_Mail_Protocol_Exception('the single id was not found in response'); |
|
634 } |
|
635 |
|
636 return $result; |
|
637 } |
|
638 |
|
639 /** |
|
640 * get mailbox list |
|
641 * |
|
642 * this method can't be named after the IMAP command 'LIST', as list is a reserved keyword |
|
643 * |
|
644 * @param string $reference mailbox reference for list |
|
645 * @param string $mailbox mailbox name match with wildcards |
|
646 * @return array mailboxes that matched $mailbox as array(globalName => array('delim' => .., 'flags' => ..)) |
|
647 * @throws Zend_Mail_Protocol_Exception |
|
648 */ |
|
649 public function listMailbox($reference = '', $mailbox = '*') |
|
650 { |
|
651 $result = array(); |
|
652 $list = $this->requestAndResponse('LIST', $this->escapeString($reference, $mailbox)); |
|
653 if (!$list || $list === true) { |
|
654 return $result; |
|
655 } |
|
656 |
|
657 foreach ($list as $item) { |
|
658 if (count($item) != 4 || $item[0] != 'LIST') { |
|
659 continue; |
|
660 } |
|
661 $result[$item[3]] = array('delim' => $item[2], 'flags' => $item[1]); |
|
662 } |
|
663 |
|
664 return $result; |
|
665 } |
|
666 |
|
667 /** |
|
668 * set flags |
|
669 * |
|
670 * @param array $flags flags to set, add or remove - see $mode |
|
671 * @param int $from message for items or start message if $to !== null |
|
672 * @param int|null $to if null only one message ($from) is fetched, else it's the |
|
673 * last message, INF means last message avaible |
|
674 * @param string|null $mode '+' to add flags, '-' to remove flags, everything else sets the flags as given |
|
675 * @param bool $silent if false the return values are the new flags for the wanted messages |
|
676 * @return bool|array new flags if $silent is false, else true or false depending on success |
|
677 * @throws Zend_Mail_Protocol_Exception |
|
678 */ |
|
679 public function store(array $flags, $from, $to = null, $mode = null, $silent = true) |
|
680 { |
|
681 $item = 'FLAGS'; |
|
682 if ($mode == '+' || $mode == '-') { |
|
683 $item = $mode . $item; |
|
684 } |
|
685 if ($silent) { |
|
686 $item .= '.SILENT'; |
|
687 } |
|
688 |
|
689 $flags = $this->escapeList($flags); |
|
690 $set = (int)$from; |
|
691 if ($to != null) { |
|
692 $set .= ':' . ($to == INF ? '*' : (int)$to); |
|
693 } |
|
694 |
|
695 $result = $this->requestAndResponse('STORE', array($set, $item, $flags), $silent); |
|
696 |
|
697 if ($silent) { |
|
698 return $result ? true : false; |
|
699 } |
|
700 |
|
701 $tokens = $result; |
|
702 $result = array(); |
|
703 foreach ($tokens as $token) { |
|
704 if ($token[1] != 'FETCH' || $token[2][0] != 'FLAGS') { |
|
705 continue; |
|
706 } |
|
707 $result[$token[0]] = $token[2][1]; |
|
708 } |
|
709 |
|
710 return $result; |
|
711 } |
|
712 |
|
713 /** |
|
714 * append a new message to given folder |
|
715 * |
|
716 * @param string $folder name of target folder |
|
717 * @param string $message full message content |
|
718 * @param array $flags flags for new message |
|
719 * @param string $date date for new message |
|
720 * @return bool success |
|
721 * @throws Zend_Mail_Protocol_Exception |
|
722 */ |
|
723 public function append($folder, $message, $flags = null, $date = null) |
|
724 { |
|
725 $tokens = array(); |
|
726 $tokens[] = $this->escapeString($folder); |
|
727 if ($flags !== null) { |
|
728 $tokens[] = $this->escapeList($flags); |
|
729 } |
|
730 if ($date !== null) { |
|
731 $tokens[] = $this->escapeString($date); |
|
732 } |
|
733 $tokens[] = $this->escapeString($message); |
|
734 |
|
735 return $this->requestAndResponse('APPEND', $tokens, true); |
|
736 } |
|
737 |
|
738 /** |
|
739 * copy message set from current folder to other folder |
|
740 * |
|
741 * @param string $folder destination folder |
|
742 * @param int|null $to if null only one message ($from) is fetched, else it's the |
|
743 * last message, INF means last message avaible |
|
744 * @return bool success |
|
745 * @throws Zend_Mail_Protocol_Exception |
|
746 */ |
|
747 public function copy($folder, $from, $to = null) |
|
748 { |
|
749 $set = (int)$from; |
|
750 if ($to != null) { |
|
751 $set .= ':' . ($to == INF ? '*' : (int)$to); |
|
752 } |
|
753 |
|
754 return $this->requestAndResponse('COPY', array($set, $this->escapeString($folder)), true); |
|
755 } |
|
756 |
|
757 /** |
|
758 * create a new folder (and parent folders if needed) |
|
759 * |
|
760 * @param string $folder folder name |
|
761 * @return bool success |
|
762 */ |
|
763 public function create($folder) |
|
764 { |
|
765 return $this->requestAndResponse('CREATE', array($this->escapeString($folder)), true); |
|
766 } |
|
767 |
|
768 /** |
|
769 * rename an existing folder |
|
770 * |
|
771 * @param string $old old name |
|
772 * @param string $new new name |
|
773 * @return bool success |
|
774 */ |
|
775 public function rename($old, $new) |
|
776 { |
|
777 return $this->requestAndResponse('RENAME', $this->escapeString($old, $new), true); |
|
778 } |
|
779 |
|
780 /** |
|
781 * remove a folder |
|
782 * |
|
783 * @param string $folder folder name |
|
784 * @return bool success |
|
785 */ |
|
786 public function delete($folder) |
|
787 { |
|
788 return $this->requestAndResponse('DELETE', array($this->escapeString($folder)), true); |
|
789 } |
|
790 |
|
791 /** |
|
792 * permanently remove messages |
|
793 * |
|
794 * @return bool success |
|
795 */ |
|
796 public function expunge() |
|
797 { |
|
798 // TODO: parse response? |
|
799 return $this->requestAndResponse('EXPUNGE'); |
|
800 } |
|
801 |
|
802 /** |
|
803 * send noop |
|
804 * |
|
805 * @return bool success |
|
806 */ |
|
807 public function noop() |
|
808 { |
|
809 // TODO: parse response |
|
810 return $this->requestAndResponse('NOOP'); |
|
811 } |
|
812 |
|
813 /** |
|
814 * do a search request |
|
815 * |
|
816 * This method is currently marked as internal as the API might change and is not |
|
817 * safe if you don't take precautions. |
|
818 * |
|
819 * @internal |
|
820 * @return array message ids |
|
821 */ |
|
822 public function search(array $params) |
|
823 { |
|
824 $response = $this->requestAndResponse('SEARCH', $params); |
|
825 if (!$response) { |
|
826 return $response; |
|
827 } |
|
828 |
|
829 foreach ($response as $ids) { |
|
830 if ($ids[0] == 'SEARCH') { |
|
831 array_shift($ids); |
|
832 return $ids; |
|
833 } |
|
834 } |
|
835 return array(); |
|
836 } |
|
837 |
|
838 } |