1 : <?php
2 :
3 :
4 :
5 :
6 :
7 :
8 :
9 :
10 :
11 :
12 :
13 :
14 :
15 :
16 :
17 :
18 :
19 :
20 :
21 :
22 :
23 : require_once 'Zend/Mail/Transport/Exception.php';
24 :
25 :
26 :
27 :
28 :
29 :
30 : class Zend_Mail_Transport_Imap
31 : {
32 :
33 :
34 :
35 : private $_socket;
36 :
37 :
38 :
39 :
40 :
41 :
42 :
43 :
44 :
45 : function __construct($host = '', $port = null, $ssl = false)
46 : {
47 28 : if($host) {
48 1 : $this->connect($host, $port, $ssl);
49 1 : }
50 28 : }
51 :
52 :
53 :
54 :
55 : public function __destruct()
56 : {
57 28 : $this->logout();
58 28 : }
59 :
60 :
61 :
62 :
63 :
64 :
65 :
66 :
67 :
68 :
69 : public function connect($host, $port = null, $ssl = false)
70 : {
71 28 : if ($ssl == 'SSL') {
72 1 : $host = 'ssl://' . $host;
73 1 : }
74 :
75 28 : if($port === null) {
76 26 : $port = $ssl === 'SSL' ? 993 : 143;
77 26 : }
78 :
79 28 : $this->_socket = @fsockopen($host, $port);
80 28 : if(!$this->_socket) {
81 2 : throw new Zend_Mail_Transport_Exception('cannot connect to host');
82 : }
83 :
84 26 : if(!$this->_assumedNextLine('* OK')) {
85 1 : throw new Zend_Mail_Transport_Exception('host doesn\'t allow connection');
86 : }
87 :
88 25 : if($ssl === 'TLS') {
89 1 : $result = $this->requestAndResponse('STARTTLS');
90 1 : $result = $result && stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
91 1 : if(!$result) {
92 0 : throw new Zend_Mail_Transport_Exception('cannot enable TLS');
93 : }
94 1 : }
95 25 : }
96 :
97 :
98 :
99 :
100 :
101 :
102 :
103 : private function _nextLine()
104 : {
105 26 : $line = fgets($this->_socket);
106 26 : if($line === false) {
107 1 : throw new Zend_Mail_Transport_Exception('cannot read - connection closed?');
108 : }
109 :
110 26 : return $line;
111 : }
112 :
113 :
114 :
115 :
116 :
117 :
118 :
119 :
120 :
121 : private function _assumedNextLine($start)
122 : {
123 26 : $line = $this->_nextLine();
124 26 : return strpos($line, $start) === 0;
125 : }
126 :
127 :
128 :
129 :
130 :
131 :
132 :
133 :
134 : private function _nextTaggedLine(&$tag)
135 : {
136 26 : $line = $this->_nextLine();
137 :
138 :
139 26 : list($tag, $line) = explode(' ', $line, 2);
140 :
141 26 : return $line;
142 : }
143 :
144 :
145 :
146 :
147 :
148 :
149 :
150 : private function _decodeLine($line)
151 : {
152 24 : $tokens = array();
153 24 : $stack = array();
154 :
155 :
156 :
157 :
158 :
159 :
160 :
161 :
162 :
163 :
164 :
165 :
166 :
167 :
168 24 : $line = rtrim($line) . ' ';
169 24 : while(($pos = strpos($line, ' ')) !== false) {
170 24 : $token = substr($line, 0, $pos);
171 24 : while($token[0] == '(') {
172 22 : array_push($stack, $tokens);
173 22 : $tokens = array();
174 22 : $token = substr($token, 1);
175 22 : }
176 24 : if($token[0] == '"') {
177 4 : if(preg_match('%^"((.|\\\\|\\")*?)"%', $line, $matches)) {
178 4 : $tokens[] = $matches[1];
179 4 : $line = substr($line, strlen($matches[0]) + 1);
180 4 : continue;
181 : }
182 0 : }
183 24 : if($token[0] == '{') {
184 4 : $endPos = strpos($token, '}');
185 4 : $chars = substr($token, 1, $endPos - 1);
186 4 : if(is_numeric($chars)) {
187 4 : $token = '';
188 4 : while(strlen($token) < $chars) {
189 4 : $token .= $this->_nextLine();
190 4 : }
191 4 : $line = '';
192 4 : if(strlen($token) > $chars) {
193 0 : $line = substr($token, $chars);
194 0 : $token = substr($token, 0, $chars);
195 0 : } else {
196 4 : $line .= $this->_nextLine();
197 : }
198 4 : $tokens[] = $token;
199 4 : $line = trim($line) . ' ';
200 4 : continue;
201 : }
202 0 : }
203 24 : if($stack && $token[strlen($token) - 1] == ')') {
204 :
205 22 : $braces = strlen($token);
206 22 : $token = rtrim($token, ')');
207 :
208 22 : $braces -= strlen($token) + 1;
209 :
210 22 : if($token) {
211 22 : $tokens[] = $token;
212 22 : }
213 22 : $token = $tokens;
214 22 : $tokens = array_pop($stack);
215 :
216 22 : while($braces-- > 0) {
217 0 : $tokens[] = $token;
218 0 : $token = $tokens;
219 0 : $tokens = array_pop($stack);
220 0 : }
221 22 : }
222 24 : $tokens[] = $token;
223 24 : $line = substr($line, $pos + 1);
224 24 : }
225 :
226 :
227 24 : while($stack) {
228 22 : $child = $tokens;
229 22 : $tokens = array_pop($stack);
230 22 : $tokens[] = $child;
231 22 : }
232 :
233 24 : return $tokens;
234 : }
235 :
236 :
237 :
238 :
239 :
240 :
241 :
242 :
243 :
244 :
245 :
246 :
247 : public function readLine(&$tokens = array(), $wantedTag = '*', $dontParse = false)
248 : {
249 26 : $line = $this->_nextTaggedLine($tag);
250 26 : if(!$dontParse) {
251 24 : $tokens = $this->_decodeLine($line);
252 24 : } else {
253 26 : $tokens = $line;
254 : }
255 :
256 :
257 26 : return $tag == $wantedTag;
258 : }
259 :
260 :
261 :
262 :
263 :
264 :
265 :
266 :
267 :
268 :
269 :
270 : public function readResponse($tag, $dontParse = false)
271 : {
272 26 : $lines = array();
273 26 : while(!$this->readLine($tokens, $tag, $dontParse)) {
274 26 : $lines[] = $tokens;
275 26 : }
276 25 : if($dontParse) {
277 :
278 25 : $tokens = array(substr($tokens, 0, 2));
279 25 : }
280 :
281 :
282 25 : if($tokens[0] == 'OK') {
283 25 : return $lines ? $lines : true;
284 1 : } else if($tokens[0] == 'NO'){
285 1 : return false;
286 : }
287 0 : return null;
288 : }
289 :
290 :
291 :
292 :
293 :
294 :
295 :
296 :
297 :
298 :
299 : public function sendRequest($command, $tokens = array(), &$tag = null)
300 : {
301 26 : if(!$tag) {
302 26 : $tag = 'TAG' . rand(100, 999);
303 26 : }
304 :
305 26 : fputs($this->_socket, $tag . ' ' . $command);
306 :
307 26 : foreach($tokens as $token) {
308 25 : if(is_array($token)) {
309 1 : fputs($this->_socket, ' ' . $token[0] . "\r\n");
310 1 : if(!$this->_assumedNextLine('+ OK')) {
311 0 : throw new Zend_Mail_Transport_Exception('cannot send literal string');
312 : }
313 1 : fputs($this->_socket, $token[1]);
314 1 : } else {
315 25 : fputs($this->_socket, ' ' . $token);
316 : }
317 25 : }
318 :
319 26 : fputs($this->_socket, "\r\n");
320 26 : }
321 :
322 :
323 :
324 :
325 :
326 :
327 :
328 :
329 :
330 : public function requestAndResponse($command, $tokens = array(), $dontParse = false)
331 : {
332 26 : $this->sendRequest($command, $tokens, $tag);
333 26 : $response = $this->readResponse($tag, $dontParse);
334 :
335 25 : return $response;
336 : }
337 :
338 :
339 :
340 :
341 :
342 :
343 :
344 :
345 : public function escapeString($string)
346 : {
347 25 : if(func_num_args() < 2) {
348 25 : if(strpos($string, "\n") !== false) {
349 1 : return array('{' . strlen($string) . '}', $string);
350 : } else {
351 25 : return '"' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $string) . '"';
352 : }
353 : }
354 25 : $result = array();
355 25 : foreach(func_get_args() as $string) {
356 25 : $result[] = $this->escapeString($string);
357 25 : }
358 25 : return $result;
359 : }
360 :
361 :
362 :
363 :
364 :
365 :
366 :
367 : public function escapeList($list)
368 : {
369 7 : $result = array();
370 7 : foreach($list as $k => $v) {
371 7 : if(!is_array($v)) {
372 :
373 7 : $result[] = $v;
374 7 : continue;
375 : }
376 0 : $result[] = $this->escapeList($v);
377 0 : }
378 7 : return '(' . implode(' ', $result) . ')';
379 : }
380 :
381 :
382 :
383 :
384 :
385 :
386 :
387 :
388 : public function login($user, $password)
389 : {
390 25 : return $this->requestAndResponse('LOGIN', $this->escapeString($user, $password), true);
391 : }
392 :
393 :
394 :
395 :
396 :
397 :
398 : public function logout()
399 : {
400 28 : $result = false;
401 28 : if($this->_socket) {
402 : try {
403 26 : $result = $this->requestAndResponse('LOGOUT', array(), true);
404 26 : } catch (Zend_Mail_Transport_Exception $e) {
405 :
406 : }
407 26 : fclose($this->_socket);
408 26 : $this->_socket = null;
409 26 : }
410 28 : return $result;
411 : }
412 :
413 :
414 :
415 :
416 :
417 :
418 :
419 : public function capability()
420 : {
421 0 : $response = $this->requestAndResponse('CAPABILITY');
422 :
423 0 : if(!$response) {
424 0 : return $response;
425 : }
426 :
427 0 : $capabilities = array();
428 0 : foreach($response as $line) {
429 0 : $capabilities = array_merge($capabilities, $line);
430 0 : }
431 0 : return $capabilities;
432 : }
433 :
434 :
435 :
436 :
437 :
438 :
439 :
440 :
441 :
442 :
443 :
444 : public function examineOrSelect($command = 'EXAMINE', $box = 'INBOX')
445 : {
446 24 : $this->sendRequest($command, (array)$this->escapeString($box), $tag);
447 :
448 24 : $result = array();
449 24 : while(!$this->readLine($tokens, $tag)) {
450 22 : if($tokens[0] == 'FLAGS') {
451 22 : array_shift($tokens);
452 22 : $result['flags'] = $tokens;
453 22 : continue;
454 : }
455 22 : switch($tokens[1]) {
456 22 : case 'EXISTS':
457 22 : case 'RECENT':
458 22 : $result[strtolower($tokens[1])] = $tokens[0];
459 22 : break;
460 22 : case '[UIDVALIDITY':
461 22 : $result['uidvalidity'] = (int)$tokens[2];
462 22 : break;
463 22 : default:
464 :
465 22 : }
466 22 : }
467 :
468 24 : if($tokens[0] != 'OK') {
469 3 : return false;
470 : }
471 22 : return $result;
472 : }
473 :
474 :
475 :
476 :
477 :
478 :
479 :
480 : public function select($box = 'INBOX')
481 : {
482 24 : return $this->examineOrSelect('SELECT', $box);
483 : }
484 :
485 :
486 :
487 :
488 :
489 :
490 :
491 : public function examine($box = 'INBOX')
492 : {
493 2 : return $this->examineOrSelect('EXAMINE', $box);
494 : }
495 :
496 :
497 :
498 :
499 :
500 :
501 :
502 :
503 :
504 :
505 :
506 :
507 :
508 :
509 : public function fetch($items, $from, $to = null)
510 : {
511 7 : if($to === null) {
512 5 : $set = (int)$from;
513 7 : } else if(is_array($from)) {
514 0 : $set = implode(',', $from);
515 2 : } else if($to === INF) {
516 2 : $set = (int)$from . ':*';
517 2 : } else {
518 0 : $set = (int)$from . ':' . (int)$to;
519 : }
520 :
521 7 : $items = (array)$items;
522 7 : $itemList = $this->escapeList($items);
523 :
524 7 : $this->sendRequest('FETCH', array($set, $itemList), $tag);
525 :
526 7 : $result = array();
527 7 : while(!$this->readLine($tokens, $tag)) {
528 7 : if($tokens[1] != 'FETCH') {
529 1 : continue;
530 : }
531 7 : if($to === null && $tokens[0] != $from) {
532 0 : continue;
533 : }
534 7 : if(count($items) == 1) {
535 7 : $data = next($tokens[2]);
536 7 : } else {
537 0 : $data = array();
538 0 : while(key($tokens[2]) !== null) {
539 0 : $data[current($tokens[2])] = next($tokens[2]);
540 0 : next($tokens[2]);
541 0 : }
542 : }
543 7 : if($to === null && $tokens[0] == $from) {
544 5 : return $data;
545 : }
546 2 : $result[$tokens[0]] = $data;
547 2 : }
548 :
549 2 : if($to === null) {
550 0 : throw new Zend_Mail_Transport_Exception('the single id was not found in response');
551 : }
552 :
553 2 : return $result;
554 : }
555 :
556 :
557 :
558 :
559 :
560 :
561 :
562 :
563 :
564 :
565 : public function listMailbox($reference = '', $mailbox = '*')
566 : {
567 4 : $result = array();
568 4 : $list = $this->requestAndResponse('LIST', $this->escapeString($reference, $mailbox));
569 4 : if(!$list) {
570 0 : return $result;
571 : }
572 :
573 4 : foreach($list as $item) {
574 4 : if(count($item) != 4 || $item[0] != 'LIST') {
575 0 : continue;
576 : }
577 4 : $result[$item[3]] = array('delim' => $item[2], 'flags' => $item[1]);
578 4 : }
579 :
580 4 : return $result;
581 : }
582 : }
583 :
|