home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / POP3.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  29.4 KB  |  1,153 lines

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