PageRenderTime 49ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/php/Net/POP3.php

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