PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.1
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.1
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / back / Libraries / Pear / Pop3.php

Pop3.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.1, at back/Libraries/Pear/Pop3.php

1,226 lines 33.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\Libraries\Pear;
4
5 acym_cmsLoaded();
6
7 // +-----------------------------------------------------------------------+
8 // | Copyright (c) 2002, Richard Heyes |
9 // | All rights reserved. |
10 // | |
11 // | Redistribution and use in source and binary forms, with or without |
12 // | modification, are permitted provided that the following conditions |
13 // | are met: |
14 // | |
15 // | o Redistributions of source code must retain the above copyright |
16 // | notice, this list of conditions and the following disclaimer. |
17 // | o Redistributions in binary form must reproduce the above copyright |
18 // | notice, this list of conditions and the following disclaimer in the |
19 // | documentation and/or other materials provided with the distribution.|
20 // | o The names of the authors may not be used to endorse or promote |
21 // | products derived from this software without specific prior written |
22 // | permission. |
23 // | |
24 // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
25 // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
26 // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
27 // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
28 // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
29 // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
30 // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
31 // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
32 // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
33 // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
34 // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
35 // | |
36 // +-----------------------------------------------------------------------+
37 // | Author: Richard Heyes <[email protected]> |
38 // | Co-Author: Damian Fernandez Sosa <[email protected]> |
39 // +-----------------------------------------------------------------------+
40 //
41 // $Id: POP3.php 295243 2010-02-18 22:05:20Z clockwerx $
42
43 /**
44 * +----------------------------- IMPORTANT ------------------------------+
45 * | Usage of this class compared to native php extensions such as IMAP |
46 * | is slow and may be feature deficient. If available you are STRONGLY |
47 * | recommended to use the php extensions. |
48 * +----------------------------------------------------------------------+
49 *
50 * POP3 Access Class
51 *
52 * For usage see the example script
53 */
54
55 define('NET_POP3_STATE_DISCONNECTED', 1);
56 define('NET_POP3_STATE_AUTHORISATION', 2);
57 define('NET_POP3_STATE_TRANSACTION', 4);
58
59 class Pop3
60 {
61
62 /**
63 * Some basic information about the mail drop
64 * garnered from the STAT command
65 *
66 * @var array
67 */
68 var $_maildrop;
69
70 /**
71 * Used for APOP to store the timestamp
72 *
73 * @var string
74 */
75 var $_timestamp;
76
77 /**
78 * Timeout that is passed to the socket object
79 *
80 * @var integer
81 */
82 var $_timeout;
83
84 /**
85 * Socket object
86 *
87 * @var object
88 */
89 var $_socket;
90
91 /**
92 * Current state of the connection. Used with the
93 * constants defined above.
94 *
95 * @var integer
96 */
97 var $_state;
98
99 /**
100 * Hostname to connect to
101 *
102 * @var string
103 */
104 var $_host;
105
106 /**
107 * Port to connect to
108 *
109 * @var integer
110 */
111 var $_port;
112
113 /**
114 * To allow class debuging
115 * @var boolean
116 */
117 var $_debug = false;
118
119
120 /**
121 * The auth methods this class support
122 * @var array
123 */
124 //var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
125 //Disabling DIGEST-MD5 for now
126 var $supportedAuthMethods = ['CRAM-MD5', 'APOP', 'PLAIN', 'LOGIN', 'USER'];
127 //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN');
128 //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN');
129
130
131 /**
132 * The auth methods this class support
133 * @var array
134 */
135 var $supportedSASLAuthMethods = ['DIGEST-MD5', 'CRAM-MD5'];
136
137
138 /**
139 * The capability response
140 * @var array
141 */
142 var $_capability;
143
144
145 /**
146 * Constructor. Sets up the object variables, and instantiates
147 * the socket object.
148 *
149 */
150 function __construct()
151 {
152 $this->_timestamp = ''; // Used for APOP
153 $this->_maildrop = [];
154 $this->_timeout = 3;
155 $this->_state = NET_POP3_STATE_DISCONNECTED;
156 $this->_socket = new Socket();
157 /*
158 * Include the Auth_SASL package. If the package is not available,
159 * we disable the authentication methods that depend upon it.
160 */
161 if (!class_exists('Auth_SASL')) {
162 if ($this->_debug) {
163 echo "AUTH_SASL NOT PRESENT!\n";
164 }
165 foreach ($this->supportedSASLAuthMethods as $SASLMethod) {
166 $pos = array_search($SASLMethod, $this->supportedAuthMethods);
167 if ($this->_debug) {
168 echo "DISABLING METHOD $SASLMethod\n";
169 }
170 unset($this->supportedAuthMethods[$pos]);
171 }
172 }
173 }
174
175
176 /**
177 * Handles the errors the class can find
178 * on the server
179 *
180 * @access private
181 * @return Error
182 */
183 function _raiseError($msg, $code = -1)
184 {
185 return Pear::raiseError($msg, $code);
186 }
187
188
189 /**
190 * Connects to the given host on the given port.
191 * Also looks for the timestamp in the greeting
192 * needed for APOP authentication
193 *
194 * @param string $host Hostname/IP address to connect to
195 * @param string $port Port to use to connect to on host
196 *
197 * @return bool Success/Failure
198 */
199 function connect($host = 'localhost', $port = 110)
200 {
201 $this->_host = $host;
202 $this->_port = $port;
203
204 $result = $this->_socket->connect($host, $port, false, $this->_timeout);
205 if ($result === true) {
206 $data = $this->_recvLn();
207
208 if ($this->_checkResponse($data)) {
209 // if the response begins with '+OK' ...
210 // if (@substr(strtoupper($data), 0, 3) == '+OK') {
211 // Check for string matching apop timestamp
212 if (preg_match('/<.+@.+>/U', $data, $matches)) {
213 $this->_timestamp = $matches[0];
214 }
215 $this->_maildrop = [];
216 $this->_state = NET_POP3_STATE_AUTHORISATION;
217
218 return true;
219 }
220 }
221
222 $this->_socket->disconnect();
223
224 return false;
225 }
226
227
228 /**
229 * Disconnect function. Sends the QUIT command
230 * and closes the socket.
231 *
232 * @return bool Success/Failure
233 */
234 function disconnect()
235 {
236 return $this->_cmdQuit();
237 }
238
239
240 /**
241 * Performs the login procedure. If there is a timestamp
242 * stored, APOP will be tried first, then basic USER/PASS.
243 *
244 * @param string $user Username to use
245 * @param string $pass Password to use
246 * @param mixed $apop Whether to try APOP first, if used as string you can select the auth methd to use ( $pop3->login('validlogin', 'validpass', "CRAM-MD5");
247 * Valid methods are: 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER'
248 *
249 * @return mixed true on Success/ Error on error
250 */
251 function login($user, $pass, $apop = true)
252 {
253 if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
254
255 if (Pear::isError($ret = $this->_cmdAuthenticate($user, $pass, $apop))) {
256 return $ret;
257 }
258 if (!Pear::isError($ret)) {
259 $this->_state = NET_POP3_STATE_TRANSACTION;
260
261 return true;
262 }
263 }
264
265 return $this->_raiseError('Generic login error', 1);
266 }
267
268
269 /**
270 * Parses the response from the capability command. Stores
271 * the result in $this->_capability
272 *
273 * @access private
274 */
275 function _parseCapability()
276 {
277
278 if (!Pear::isError($data = $this->_sendCmd('CAPA'))) {
279 $data = $this->_getMultiline();
280 } else {
281 // CAPA command not supported, reset data var
282 // to avoid Notice errors of preg_split on an object
283 $data = '';
284 }
285 $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY);
286
287 for ($i = 0 ; $i < count($data) ; $i++) {
288
289 $capa = '';
290 if (preg_match('/^([a-z,\-]+)( ((.*))|$)$/i', $data[$i], $matches)) {
291
292 $capa = strtolower($matches[1]);
293 switch ($capa) {
294 case 'implementation':
295 $this->_capability['implementation'] = $matches[3];
296 break;
297 case 'sasl':
298 $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]);
299 break;
300 default :
301 $this->_capability[$capa] = $matches[2];
302 break;
303 }
304 }
305 }
306 }
307
308
309 /**
310 * Returns the name of the best authentication method that the server
311 * has advertised.
312 *
313 * @param string if !=null,authenticate with this method ($userMethod).
314 *
315 * @return mixed Returns a string containing the name of the best
316 * supported authentication method or a Error object
317 * if a failure condition is encountered.
318 * @access private
319 * @since 1.0
320 */
321 function _getBestAuthMethod($userMethod = null)
322 {
323 /*
324 return 'USER';
325 return 'APOP';
326 return 'DIGEST-MD5';
327 return 'CRAM-MD5';
328 */
329
330 $this->_parseCapability();
331
332 //unset($this->_capability['sasl']);
333
334 if (isset($this->_capability['sasl'])) {
335 $serverMethods = $this->_capability['sasl'];
336 } else {
337 $serverMethods = ['USER'];
338 // Check for timestamp before attempting APOP
339 if ($this->_timestamp != null) {
340 $serverMethods[] = 'APOP';
341 }
342 }
343
344 if ($userMethod !== null && $userMethod !== true) {
345 $methods = [];
346 $methods[] = $userMethod;
347
348 return $userMethod;
349 } else {
350 $methods = $this->supportedAuthMethods;
351 }
352
353 if (($methods != null) && ($serverMethods != null)) {
354
355 foreach ($methods as $method) {
356
357 if (in_array($method, $serverMethods)) {
358 return $method;
359 }
360 }
361 $serverMethods = implode(',', $serverMethods);
362 $myMethods = implode(',', $this->supportedAuthMethods);
363
364 return $this->_raiseError(
365 "$method NOT supported authentication method!. This server ".
366 "supports these methods: $serverMethods, but I support $myMethods"
367 );
368 } else {
369 return $this->_raiseError("This server don't support any Auth methods");
370 }
371 }
372
373
374 /**
375 * Handles the authentication using any known method
376 *
377 * @param string The userid to authenticate as.
378 * @param string The password to authenticate with.
379 * @param string The method to use ( if $usermethod == '' then the class chooses the best method (the stronger is the best ) )
380 *
381 * @return mixed string or Error
382 * @access private
383 * @since 1.0
384 */
385 function _cmdAuthenticate($uid, $pwd, $userMethod = null)
386 {
387
388 if (Pear::isError($method = $this->_getBestAuthMethod($userMethod))) {
389 return $method;
390 }
391
392 if (!in_array($userMethod, $this->supportedAuthMethods)) {
393 return $this->_raiseError(
394 'Authentication method "'.$userMethod.'" is not supported.'
395 );
396 }
397
398 switch ($method) {
399 case 'DIGEST-MD5':
400 $result = $this->_authDigest_MD5($uid, $pwd);
401 break;
402 case 'CRAM-MD5':
403 $result = $this->_authCRAM_MD5($uid, $pwd);
404 break;
405 case 'LOGIN':
406 $result = $this->_authLOGIN($uid, $pwd);
407 break;
408 case 'PLAIN':
409 $result = $this->_authPLAIN($uid, $pwd);
410 break;
411 case 'APOP':
412 $result = $this->_cmdApop($uid, $pwd);
413 // if APOP fails fallback to USER auth
414 if (Pear::isError($result)) {
415 //echo "APOP FAILED!!!\n";
416 $result = $this->_authUSER($uid, $pwd);
417 }
418 break;
419 case 'USER':
420 $result = $this->_authUSER($uid, $pwd);
421 break;
422
423
424 default :
425 $result = $this->_raiseError("$method is not a supported authentication method");
426 break;
427 }
428
429 return $result;
430 }
431
432
433 /**
434 * Authenticates the user using the USER-PASS method.
435 *
436 * @param string The userid to authenticate as.
437 * @param string The password to authenticate with.
438 *
439 * @return mixed true on success or Error on failure
440 *
441 * @access private
442 * @since 1.0
443 */
444 function _authUSER($user, $pass)
445 {
446 if (Pear::isError($ret = $this->_cmdUser($user))) {
447 return $ret;
448 }
449 if (Pear::isError($ret = $this->_cmdPass($pass))) {
450 return $ret;
451 }
452
453 return true;
454 }
455
456
457 /**
458 * Authenticates the user using the PLAIN method.
459 *
460 * @param string The userid to authenticate as.
461 * @param string The password to authenticate with.
462 *
463 * @return array Returns an array containing the response
464 *
465 * @access private
466 * @since 1.0
467 */
468 function _authPLAIN($user, $pass)
469 {
470 $cmd = sprintf('AUTH PLAIN %s', base64_encode(chr(0).$user.chr(0).$pass));
471
472 if (Pear::isError($ret = $this->_send($cmd))) {
473 return $ret;
474 }
475 if (Pear::isError($challenge = $this->_recvLn())) {
476 return $challenge;
477 }
478 if (Pear::isError($ret = $this->_checkResponse($challenge))) {
479 return $ret;
480 }
481
482 return true;
483 }
484
485
486 /**
487 * Authenticates the user using the PLAIN method.
488 *
489 * @param string The userid to authenticate as.
490 * @param string The password to authenticate with.
491 *
492 * @return array Returns an array containing the response
493 *
494 * @access private
495 * @since 1.0
496 */
497 function _authLOGIN($user, $pass)
498 {
499 $this->_send('AUTH LOGIN');
500
501 if (Pear::isError($challenge = $this->_recvLn())) {
502 return $challenge;
503 }
504 if (Pear::isError($ret = $this->_checkResponse($challenge))) {
505 return $ret;
506 }
507
508
509 if (Pear::isError($ret = $this->_send(sprintf('%s', base64_encode($user))))) {
510 return $ret;
511 }
512
513 if (Pear::isError($challenge = $this->_recvLn())) {
514 return $challenge;
515 }
516 if (Pear::isError($ret = $this->_checkResponse($challenge))) {
517 return $ret;
518 }
519
520 if (Pear::isError($ret = $this->_send(sprintf('%s', base64_encode($pass))))) {
521 return $ret;
522 }
523
524 if (Pear::isError($challenge = $this->_recvLn())) {
525 return $challenge;
526 }
527
528 return $this->_checkResponse($challenge);
529 }
530
531
532 /**
533 * Authenticates the user using the CRAM-MD5 method.
534 *
535 * @param string The userid to authenticate as.
536 * @param string The password to authenticate with.
537 *
538 * @return array Returns an array containing the response
539 *
540 * @access private
541 * @since 1.0
542 */
543 function _authCRAM_MD5($uid, $pwd)
544 {
545 if (Pear::isError($ret = $this->_send('AUTH CRAM-MD5'))) {
546 return $ret;
547 }
548
549 if (Pear::isError($challenge = $this->_recvLn())) {
550 return $challenge;
551 }
552 if (Pear::isError($ret = $this->_checkResponse($challenge))) {
553 return $ret;
554 }
555
556 // remove '+ '
557
558 $challenge = substr($challenge, 2);
559
560 $challenge = base64_decode($challenge);
561
562 $cram = &Auth_SASL::factory('crammd5');
563 $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge));
564
565
566 if (Pear::isError($error = $this->_send($auth_str))) {
567 return $error;
568 }
569 if (Pear::isError($ret = $this->_recvLn())) {
570 return $ret;
571 }
572
573 //echo "RET:$ret\n";
574 return $this->_checkResponse($ret);
575 }
576
577
578 /**
579 * Authenticates the user using the DIGEST-MD5 method.
580 *
581 * @param string The userid to authenticate as.
582 * @param string The password to authenticate with.
583 * @param string The efective user
584 *
585 * @return array Returns an array containing the response
586 *
587 * @access private
588 * @since 1.0
589 */
590 function _authDigest_MD5($uid, $pwd)
591 {
592 if (Pear::isError($ret = $this->_send('AUTH DIGEST-MD5'))) {
593 return $ret;
594 }
595
596 if (Pear::isError($challenge = $this->_recvLn())) {
597 return $challenge;
598 }
599 if (Pear::isError($ret = $this->_checkResponse($challenge))) {
600 return $ret;
601 }
602
603 // remove '+ '
604 $challenge = substr($challenge, 2);
605
606 $challenge = base64_decode($challenge);
607 $digest = &Auth_SASL::factory('digestmd5');
608 $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, "localhost", "pop3"));
609
610 if (Pear::isError($error = $this->_send($auth_str))) {
611 return $error;
612 }
613
614 if (Pear::isError($challenge = $this->_recvLn())) {
615 return $challenge;
616 }
617 if (Pear::isError($ret = $this->_checkResponse($challenge))) {
618 return $ret;
619 }
620 /*
621 * We don't use the protocol's third step because POP3 doesn't allow
622 * subsequent authentication, so we just silently ignore it.
623 */
624
625 if (Pear::isError($challenge = $this->_send("\r\n"))) {
626 return $challenge;
627 }
628
629 if (Pear::isError($challenge = $this->_recvLn())) {
630 return $challenge;
631 }
632
633 return $this->_checkResponse($challenge);
634 }
635
636
637 /**
638 * Sends the APOP command
639 *
640 * @param $user Username to send
641 * @param $pass Password to send
642 *
643 * @return bool Success/Failure
644 */
645 function _cmdApop($user, $pass)
646 {
647 if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
648
649 if (!empty($this->_timestamp)) {
650 if (Pear::isError($data = $this->_sendCmd('APOP '.$user.' '.md5($this->_timestamp.$pass)))) {
651 return $data;
652 }
653 $this->_state = NET_POP3_STATE_TRANSACTION;
654
655 return true;
656 }
657 }
658
659 return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State1');
660 }
661
662
663 /**
664 * Returns the raw headers of the specified message.
665 *
666 * @param integer $msg_id Message number
667 *
668 * @return mixed Either raw headers or false on error
669 */
670 function getRawHeaders($msg_id)
671 {
672 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
673 return $this->_cmdTop($msg_id, 0);
674 }
675
676 return false;
677 }
678
679 /**
680 * Returns the headers of the specified message in an
681 * associative array. Array keys are the header names, array
682 * values are the header values. In the case of multiple headers
683 * having the same names, eg Received:, the array value will be
684 * an indexed array of all the header values.
685 *
686 * @param integer $msg_id Message number
687 *
688 * @return mixed Either array of headers or false on error
689 */
690 function getParsedHeaders($msg_id)
691 {
692 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
693
694 $raw_headers = rtrim($this->getRawHeaders($msg_id));
695
696 $raw_headers = preg_replace("/\r\n[ \t]+/", ' ', $raw_headers); // Unfold headers
697 $raw_headers = explode("\r\n", $raw_headers);
698 foreach ($raw_headers as $value) {
699 $name = substr($value, 0, $pos = strpos($value, ':'));
700 $value = ltrim(substr($value, $pos + 1));
701 if (isset($headers[$name]) and is_array($headers[$name])) {
702 $headers[$name][] = $value;
703 } elseif (isset($headers[$name])) {
704 $headers[$name] = [$headers[$name], $value];
705 } else {
706 $headers[$name] = $value;
707 }
708 }
709
710 return $headers;
711 }
712
713 return false;
714 }
715
716 /**
717 * Returns the body of the message with given message number.
718 *
719 * @param integer $msg_id Message number
720 *
721 * @return mixed Either message body or false on error
722 */
723 function getBody($msg_id)
724 {
725 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
726 $msg = $this->_cmdRetr($msg_id);
727
728 return substr($msg, strpos($msg, "\r\n\r\n") + 4);
729 }
730
731 return false;
732 }
733
734 /**
735 * Returns the entire message with given message number.
736 *
737 * @param integer $msg_id Message number
738 *
739 * @return mixed Either entire message or false on error
740 */
741 function getMsg($msg_id)
742 {
743 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
744 return $this->_cmdRetr($msg_id);
745 }
746
747 return false;
748 }
749
750 /**
751 * Returns the size of the maildrop
752 *
753 * @return mixed Either size of maildrop or false on error
754 */
755 function getSize()
756 {
757 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
758 if (isset($this->_maildrop['size'])) {
759 return $this->_maildrop['size'];
760 } else {
761 [, $size] = $this->_cmdStat();
762
763 return $size;
764 }
765 }
766
767 return false;
768 }
769
770 /**
771 * Returns number of messages in this maildrop
772 *
773 * @return mixed Either number of messages or false on error
774 */
775 function numMsg()
776 {
777 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
778 if (isset($this->_maildrop['num_msg'])) {
779 return $this->_maildrop['num_msg'];
780 } else {
781 [$num_msg,] = $this->_cmdStat();
782
783 return $num_msg;
784 }
785 }
786
787 return false;
788 }
789
790 /**
791 * Marks a message for deletion. Only will be deleted if the
792 * disconnect() method is called.
793 *
794 * @param integer $msg_id Message to delete
795 *
796 * @return bool Success/Failure
797 */
798 function deleteMsg($msg_id)
799 {
800 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
801 return $this->_cmdDele($msg_id);
802 }
803
804 return false;
805 }
806
807 /**
808 * Combination of LIST/UIDL commands, returns an array
809 * of data
810 *
811 * @param integer $msg_id Optional message number
812 *
813 * @return mixed Array of data or false on error
814 */
815 function getListing($msg_id = null)
816 {
817
818 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
819 if (!isset($msg_id)) {
820
821 $list = [];
822 if ($list = $this->_cmdList()) {
823 if ($uidl = $this->_cmdUidl()) {
824 foreach ($uidl as $i => $value) {
825 $list[$i]['uidl'] = $value['uidl'];
826 }
827 }
828
829 return $list;
830 } else {
831 return [];
832 }
833 } else {
834 if ($list = $this->_cmdList($msg_id) and $uidl = $this->_cmdUidl($msg_id)) {
835 return array_merge($list, $uidl);
836 }
837 }
838 }
839
840 return false;
841 }
842
843
844 /**
845 * Sends the USER command
846 *
847 * @param string $user Username to send
848 *
849 * @return bool Success/Failure
850 */
851 function _cmdUser($user)
852 {
853 if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
854 return $this->_sendCmd('USER '.$user);
855 }
856
857 return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
858 }
859
860
861 /**
862 * Sends the PASS command
863 *
864 * @param string $pass Password to send
865 *
866 * @return bool Success/Failure
867 */
868 function _cmdPass($pass)
869 {
870 if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
871 return $this->_sendCmd('PASS '.$pass);
872 }
873
874 return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
875 }
876
877
878 /**
879 * Sends the STAT command
880 *
881 * @return mixed Indexed array of number of messages and
882 * maildrop size, or false on error.
883 */
884 function _cmdStat()
885 {
886 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
887 if (!Pear::isError($data = $this->_sendCmd('STAT'))) {
888 sscanf($data, '+OK %d %d', $msg_num, $size);
889 $this->_maildrop['num_msg'] = $msg_num;
890 $this->_maildrop['size'] = $size;
891
892 return [$msg_num, $size];
893 }
894 }
895
896 return false;
897 }
898
899
900 /**
901 * Sends the LIST command
902 *
903 * @param integer $msg_id Optional message number
904 *
905 * @return mixed Indexed array of msg_id/msg size or
906 * false on error
907 */
908 function _cmdList($msg_id = null)
909 {
910 $return = [];
911 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
912 if (!isset($msg_id)) {
913 if (!Pear::isError($data = $this->_sendCmd('LIST'))) {
914 $data = $this->_getMultiline();
915 $data = explode("\r\n", $data);
916 foreach ($data as $line) {
917 if ($line != '') {
918 sscanf($line, '%s %s', $msg_id, $size);
919 $return[] = ['msg_id' => $msg_id, 'size' => $size];
920 }
921 }
922
923 return $return;
924 }
925 } else {
926 if (!Pear::isError($data = $this->_sendCmd('LIST '.$msg_id))) {
927 if ($data != '') {
928 sscanf($data, '+OK %d %d', $msg_id, $size);
929
930 return ['msg_id' => $msg_id, 'size' => $size];
931 }
932
933 return [];
934 }
935 }
936 }
937
938
939 return false;
940 }
941
942
943 /**
944 * Sends the RETR command
945 *
946 * @param integer $msg_id The message number to retrieve
947 *
948 * @return mixed The message or false on error
949 */
950 function _cmdRetr($msg_id)
951 {
952 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
953 if (!Pear::isError($data = $this->_sendCmd('RETR '.$msg_id))) {
954 $data = $this->_getMultiline();
955
956 return $data;
957 }
958 }
959
960 return false;
961 }
962
963
964 /**
965 * Sends the DELE command
966 *
967 * @param integer $msg_id Message number to mark as deleted
968 *
969 * @return bool Success/Failure
970 */
971 function _cmdDele($msg_id)
972 {
973 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
974 return $this->_sendCmd('DELE '.$msg_id);
975 }
976
977 return false;
978 }
979
980
981 /**
982 * Sends the NOOP command
983 *
984 * @return bool Success/Failure
985 */
986 function _cmdNoop()
987 {
988 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
989 if (!Pear::isError($data = $this->_sendCmd('NOOP'))) {
990 return true;
991 }
992 }
993
994 return false;
995 }
996
997
998 /**
999 * Sends the RSET command
1000 *
1001 * @return bool Success/Failure
1002 */
1003 function _cmdRset()
1004 {
1005 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1006 if (!Pear::isError($data = $this->_sendCmd('RSET'))) {
1007 return true;
1008 }
1009 }
1010
1011 return false;
1012 }
1013
1014
1015 /**
1016 * Sends the QUIT command
1017 *
1018 * @return bool Success/Failure
1019 */
1020 function _cmdQuit()
1021 {
1022 $data = $this->_sendCmd('QUIT');
1023 $this->_state = NET_POP3_STATE_DISCONNECTED;
1024 $this->_socket->disconnect();
1025
1026 return (bool)$data;
1027 }
1028
1029
1030 /**
1031 * Sends the TOP command
1032 *
1033 * @param integer $msg_id Message number
1034 * @param integer $num_lines Number of lines to retrieve
1035 *
1036 * @return mixed Message data or false on error
1037 */
1038 function _cmdTop($msg_id, $num_lines)
1039 {
1040 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1041
1042 if (!Pear::isError($data = $this->_sendCmd('TOP '.$msg_id.' '.$num_lines))) {
1043 return $this->_getMultiline();
1044 }
1045 }
1046
1047 return false;
1048 }
1049
1050
1051 /**
1052 * Sends the UIDL command
1053 *
1054 * @param integer $msg_id Message number
1055 *
1056 * @return mixed indexed array of msg_id/uidl or false on error
1057 */
1058 function _cmdUidl($msg_id = null)
1059 {
1060 if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1061
1062 if (!isset($msg_id)) {
1063 if (!Pear::isError($data = $this->_sendCmd('UIDL'))) {
1064 $data = $this->_getMultiline();
1065 $data = explode("\r\n", $data);
1066 foreach ($data as $line) {
1067 sscanf($line, '%d %s', $msg_id, $uidl);
1068 $return[] = ['msg_id' => $msg_id, 'uidl' => $uidl];
1069 }
1070
1071 return $return;
1072 }
1073 } else {
1074
1075 $data = $this->_sendCmd('UIDL '.$msg_id);
1076 sscanf($data, '+OK %d %s', $msg_id, $uidl);
1077
1078 return ['msg_id' => $msg_id, 'uidl' => $uidl];
1079 }
1080 }
1081
1082 return false;
1083 }
1084
1085
1086 /**
1087 * Sends a command, checks the reponse, and
1088 * if good returns the reponse, other wise
1089 * returns false.
1090 *
1091 * @param string $cmd Command to send (\r\n will be appended)
1092 *
1093 * @return mixed First line of response if successful, otherwise false
1094 */
1095 function _sendCmd($cmd)
1096 {
1097 if (Pear::isError($result = $this->_send($cmd))) {
1098 return $result;
1099 }
1100
1101 if (Pear::isError($data = $this->_recvLn())) {
1102 return $data;
1103 }
1104
1105 if (strtoupper(substr($data, 0, 3)) == '+OK') {
1106 return $data;
1107 }
1108
1109
1110 return $this->_raiseError($data);
1111 }
1112
1113
1114 /**
1115 * Reads a multiline reponse and returns the data
1116 *
1117 * @return string The reponse.
1118 */
1119 function _getMultiline()
1120 {
1121 $data = '';
1122 while (!Pear::isError($tmp = $this->_recvLn())) {
1123 if ($tmp == '.' || $tmp == "\n.") {
1124 return substr($data, 0, -2);
1125 }
1126 if (substr($tmp, 0, 2) == '..') {
1127 $tmp = substr($tmp, 1);
1128 }
1129 $data .= $tmp."\r\n";
1130 }
1131
1132 return substr($data, 0, -2);
1133 }
1134
1135
1136 /**
1137 * Sets the bebug state
1138 *
1139 * @param bool $debug
1140 *
1141 * @access public
1142 * @return void
1143 */
1144 function setDebug($debug = true)
1145 {
1146 $this->_debug = $debug;
1147 }
1148
1149
1150 /**
1151 * Send the given string of data to the server.
1152 *
1153 * @param string $data The string of data to send.
1154 *
1155 * @return mixed True on success or a Error object on failure.
1156 *
1157 * @access private
1158 * @since 1.0
1159 */
1160 function _send($data)
1161 {
1162 if ($this->_debug) {
1163 echo "C: $data\n";
1164 }
1165
1166 if (Pear::isError($error = $this->_socket->writeLine($data))) {
1167 return $this->_raiseError('Failed to write to socket: '.$error->getMessage());
1168 }
1169
1170 return true;
1171 }
1172
1173
1174 /**
1175 * Receive the given string of data from the server.
1176 *
1177 * @return mixed a line of response on success or a Error object on failure.
1178 *
1179 * @access private
1180 * @since 1.0
1181 */
1182 function _recvLn()
1183 {
1184 if (Pear::isError($lastline = $this->_socket->readLine(8192))) {
1185 return $this->_raiseError('Failed to read from socket: '.$this->lastline->getMessage());
1186 }
1187 if ($this->_debug) {
1188 // S: means this data was sent by the POP3 Server
1189 echo "S:$lastline\n";
1190 }
1191
1192 return $lastline;
1193 }
1194
1195
1196 /**
1197 * Checks de server Response
1198 *
1199 * @param string $response the response
1200 *
1201 * @return mixed true on success or a Error object on failure.
1202 *
1203 * @access private
1204 * @since 1.3.3
1205 */
1206 function _checkResponse($response)
1207 {
1208 if (@substr(strtoupper($response), 0, 3) == '+OK') {
1209 return true;
1210 } else {
1211 if (@substr(strtoupper($response), 0, 4) == '-ERR') {
1212 return $this->_raiseError($response);
1213 } else {
1214 if (@substr(strtoupper($response), 0, 2) == '+ ') {
1215 return true;
1216 }
1217 }
1218 }
1219
1220 return $this->_raiseError("Unknown Response ($response)");
1221 }
1222
1223 }
1224
1225 ?>
1226