Net_NNTP
[ class tree: Net_NNTP ] [ index: Net_NNTP ] [ all elements ]

Source for file Client.php

Documentation is available at Client.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
  3. // +-----------------------------------------------------------------------+
  4. // |                                                                       |
  5. // |                  http://www.heino.gehlsen.dk/software/license         |
  6. // |                                                                       |
  7. // +-----------------------------------------------------------------------+
  8. // |                                                                       |
  9. // | This work (including software, documents, or other related items) is  |
  10. // | being provided by the copyright holders under the following license.  |
  11. // | By obtaining, using and/or copying this work, you (the licensee)      |
  12. // | agree that you have read, understood, and will comply with the        |
  13. // | following terms and conditions:                                       |
  14. // |                                                                       |
  15. // | Permission to use, copy, modify, and distribute this software and     |
  16. // | its documentation, with or without modification, for any purpose and  |
  17. // | without fee or royalty is hereby granted, provided that you include   |
  18. // | the following on ALL copies of the software and documentation or      |
  19. // | portions thereof, including modifications, that you make:             |
  20. // |                                                                       |
  21. // | 1. The full text of this NOTICE in a location viewable to users of    |
  22. // |    the redistributed or derivative work.                              |
  23. // |                                                                       |
  24. // | 2. Any pre-existing intellectual property disclaimers, notices, or    |
  25. // |    terms and conditions. If none exist, a short notice of the         |
  26. // |    following form (hypertext is preferred, text is permitted) should  |
  27. // |    be used within the body of any redistributed or derivative code:   |
  28. // |     http://www.heino.gehlsen.dk/software/license"                     |
  29. // |                                                                       |
  30. // | 3. Notice of any changes or modifications to the files, including     |
  31. // |    the date changes were made. (We recommend you provide URIs to      |
  32. // |    the location from which the code is derived.)                      |
  33. // |                                                                       |
  34. // | THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT    |
  35. // | HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED,    |
  36. // | INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR        |
  37. // | FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE    |
  38. // | OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS,           |
  39. // | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.                               |
  40. // |                                                                       |
  41. // | COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT,        |
  42. // | SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE        |
  43. // | SOFTWARE OR DOCUMENTATION.                                            |
  44. // |                                                                       |
  45. // | The name and trademarks of copyright holders may NOT be used in       |
  46. // | advertising or publicity pertaining to the software without specific, |
  47. // | written prior permission. Title to copyright in this software and any |
  48. // | associated documentation will at all times remain with copyright      |
  49. // | holders.                                                              |
  50. // |                                                                       |
  51. // +-----------------------------------------------------------------------+
  52. // |                                                                       |
  53. // | except for the references to the copyright holder, which has either   |
  54. // | been changes or removed.                                              |
  55. // |                                                                       |
  56. // +-----------------------------------------------------------------------+
  57. // $Id: Client.php,v 1.3.4.20 2005/11/27 22:47:16 heino Exp $
  58.  
  59. require_once 'PEAR.php';
  60. require_once 'Net/Socket.php';
  61. require_once 'Net/NNTP/Protocol/Responsecode.php';
  62.  
  63. // {{{ constants
  64.  
  65. /**
  66.  * Default host
  67.  *
  68.  * @access     public
  69.  */
  70. define('NET_NNTP_PROTOCOL_CLIENT_DEFAULT_HOST''localhost');
  71.  
  72. /**
  73.  * Default port
  74.  *
  75.  * @access     public
  76.  */
  77. define('NET_NNTP_PROTOCOL_CLIENT_DEFAULT_PORT''119');
  78.  
  79. // }}}
  80. // {{{ Net_NNTP_Protocol_Client
  81.  
  82. /**
  83.  * Low level NNTP Client
  84.  *
  85.  * Implements the client part of the NNTP standard acording to:
  86.  *  - RFC 977,
  87.  *  - RFC 2980,
  88.  *  - RFC 850/1036, and
  89.  *  - RFC 822/2822
  90.  *
  91.  * Each NNTP command is represented by a method: cmd*()
  92.  *
  93.  * WARNING: The Net_NNTP_Protocol_Client class is considered an internal class
  94.  *          (and should therefore currently not be extended directly outside of
  95.  *          the Net_NNTP package). Therefore its API is NOT required to be fully
  96.  *          stable, for as long as such changes doesn't affect the public API of
  97.  *          the Net_NNTP_Client class, which is considered stable.
  98.  *
  99.  * TODO:    cmdListActive()
  100.  *          cmdListActiveTimes()
  101.  *          cmdDistribPats()
  102.  *          cmdHeaders()
  103.  *
  104.  * @category   Net
  105.  * @package    Net_NNTP
  106.  * @author     Heino H. Gehlsen <heino@gehlsen.dk>
  107.  * @version    $Id: Client.php,v 1.3.4.20 2005/11/27 22:47:16 heino Exp $
  108.  * @access     private
  109.  * @see        Net_NNTP_Client
  110.  * @since      Class available since Release 0.11.0
  111.  */
  112. class Net_NNTP_Protocol_Client
  113. {
  114.     // {{{ properties
  115.  
  116.     /**
  117.      * The socket resource being used to connect to the NNTP server.
  118.      *
  119.      * @var resource 
  120.      * @access private
  121.      */
  122.     var $_socket = null;
  123.  
  124.     /**
  125.      * Contains the last recieved status response code and text
  126.      *
  127.      * @var array 
  128.      * @access private
  129.      */
  130.     var $_currentStatusResponse = null;
  131.  
  132.     /**
  133.      * Whether to enable internal debug messages.
  134.      *
  135.      * @var     bool 
  136.      * @access  private
  137.      */
  138.     var $_debug = false;
  139.  
  140.     // }}}
  141.     // {{{ constructor
  142.         
  143.     /**
  144.      * Constructor
  145.      *
  146.      * @access public
  147.      */
  148.     function Net_NNTP_Protocol_Client({
  149.         $this->_socket = new Net_Socket();
  150.     }
  151.  
  152.     // }}}
  153.     // {{{ Connect()
  154.  
  155.     /**
  156.      * Connect to a NNTP server
  157.      *
  158.      * @param optional string $host The address of the NNTP-server to connect to, defaults to 'localhost'.
  159.      * @param optional int $port The port number to connect to, defaults to 119.
  160.      *
  161.      * @return mixed (bool) on success (true when posting allowed, otherwise false) or (object) pear_error on failure
  162.      * @access protected
  163.      */
  164.     function connect($host = NET_NNTP_PROTOCOL_CLIENT_DEFAULT_HOST$port = NET_NNTP_PROTOCOL_CLIENT_DEFAULT_PORT)
  165.     {
  166.         if ($this->isConnected() ) {
  167.             return PEAR::throwError('Already connected, disconnect first!'null);
  168.         }
  169.  
  170.         // Open Connection
  171.         $R @$this->_socket->connect($host$portfalse15);
  172.         if (PEAR::isError($R)) {
  173.             return PEAR::throwError('Could not connect to the server'null$R->getMessage());
  174.         }
  175.  
  176.         // Retrive the server's initial response.
  177.         $response $this->_getStatusResponse();
  178.         if (PEAR::isError($response)) {
  179.             return $response;
  180.         }
  181.  
  182.         switch ($response{
  183.             case NET_NNTP_PROTOCOL_RESPONSECODE_READY_POSTING_ALLOWED// 200, Posting allowed
  184.                 // TODO: Set some variable
  185.                 return true;
  186.                 break;
  187.             case NET_NNTP_PROTOCOL_RESPONSECODE_READY_POSTING_PROHIBITED// 201, Posting NOT allowed
  188.                 // TODO: Set some variable
  189.                 return false;
  190.                 break;
  191.             case 400:
  192.                 return PEAR::throwError('Server refused connection'$response$this->_currentStatusResponse());
  193.                 break;
  194.             case NET_NNTP_PROTOCOL_RESPONSECODE_NOT_PERMITTED// 502, 'access restriction or permission denied' / service permanently unavailable
  195.                 return PEAR::throwError('Server refused connection'$response$this->_currentStatusResponse());
  196.                 break;
  197.             default:
  198.                 return $this->_handleUnexpectedResponse($response);
  199.         }
  200.     }
  201.  
  202.     // }}}
  203.     // {{{ disconnect()
  204.  
  205.     /**
  206.      * alias for cmdQuit()
  207.      *
  208.      * @access protected
  209.      */
  210.     function disconnect()
  211.     {
  212.         return $this->cmdQuit();
  213.     }
  214.  
  215.     // }}}
  216.     // {{{ cmdQuit()
  217.  
  218.     /**
  219.      * Disconnect from the NNTP server
  220.      *
  221.      * @return mixed (bool) true on success or (object) pear_error on failure
  222.      * @access protected
  223.      */
  224.     function cmdQuit()
  225.     {
  226.         // Tell the server to close the connection
  227.         $response $this->_sendCommand('QUIT');
  228.         if (PEAR::isError($response)) {
  229.             return $response;
  230.         }
  231.     
  232.         switch ($response{
  233.             case 205: // RFC977: 'closing connection - goodbye!'
  234.                 // If socket is still open, close it.
  235.                 if ($this->isConnected()) {
  236.                     $this->_socket->disconnect();
  237.                 }
  238.                 return true;
  239.                 break;
  240.             default:
  241.                 return $this->_handleUnexpectedResponse($response);
  242.         }
  243.     }
  244.  
  245.     // }}}
  246.  
  247.     /**
  248.      * The authentication process i not yet standarized but used any way
  249.      * (http://www.mibsoftware.com/userkt/nntpext/index.html).
  250.      */
  251.      
  252.     // {{{ cmdAuthinfo()
  253.  
  254.     /**
  255.      * Authenticate using 'original' method
  256.      *
  257.      * @param string $user The username to authenticate as.
  258.      * @param string $pass The password to authenticate with.
  259.      *
  260.      * @return mixed (bool) true on success or (object) pear_error on failure
  261.      * @access protected
  262.      */
  263.     function cmdAuthinfo($user$pass)
  264.     {
  265.         // Send the username
  266.         $response $this->_sendCommand('AUTHINFO user '.$user);
  267.         if (PEAR::isError($response)) {
  268.             return $response;
  269.         }
  270.  
  271.         // Send the password, if the server asks
  272.         if (($response == 381&& ($pass !== null)) {
  273.             // Send the password
  274.             $response $this->_sendCommand('AUTHINFO pass '.$pass);
  275.             if (PEAR::isError($response)) {
  276.                 return $response;
  277.             }
  278.         }
  279.  
  280.         switch ($response{
  281.             case 281: // RFC2980: 'Authentication accepted'
  282.                 return true;
  283.                 break;
  284.             case 381: // RFC2980: 'More authentication information required'
  285.                 return PEAR::throwError('Authentication uncompleted'$response$this->_currentStatusResponse());
  286.                 break;
  287.             case 482: // RFC2980: 'Authentication rejected'
  288.                 return PEAR::throwError('Authentication rejected'$response$this->_currentStatusResponse());
  289.                 break;
  290.             case 502: // RFC2980: 'No permission'
  291.                 return PEAR::throwError('Authentication rejected'$response$this->_currentStatusResponse());
  292.                 break;
  293. //            case 500:
  294. //            case 501:
  295. //                return PEAR::throwError('Authentication failed', $response, $this->_currentStatusResponse());
  296. //                break;
  297.             default:
  298.                 return $this->_handleUnexpectedResponse($response);
  299.         }
  300.     }
  301.     
  302.     // }}}
  303.     // {{{ cmdAuthinfoSimple()
  304.  
  305.     /**
  306.      * Authenticate using 'simple' method
  307.      *
  308.      * @param string $user The username to authenticate as.
  309.      * @param string $pass The password to authenticate with.
  310.      *
  311.      * @return mixed (bool) true on success or (object) pear_error on failure
  312.      * @access protected
  313.      */
  314.     function cmdAuthinfoSimple($user$pass)
  315.     {
  316.         return PEAR::throwError("The auth mode: 'simple' is has not been implemented yet"null);
  317.     }
  318.     
  319.     // }}}
  320.     // {{{ cmdAuthinfoGeneric()
  321.  
  322.     /**
  323.      * Authenticate using 'generic' method
  324.      *
  325.      * @param string $user The username to authenticate as.
  326.      * @param string $pass The password to authenticate with.
  327.      *
  328.      * @return mixed (bool) true on success or (object) pear_error on failure
  329.      * @access protected
  330.      */
  331.     function cmdAuthinfoGeneric($user$pass)
  332.     {
  333.         return PEAR::throwError("The auth mode: 'generic' is has not been implemented yet"null);
  334.     }
  335.     
  336.     // }}}
  337.     // {{{ cmdHelp()
  338.  
  339.     /**
  340.      * Returns the server's help text
  341.      *
  342.      * @return mixed (array) help text on success or (object) pear_error on failure
  343.      * @access protected
  344.      */
  345.     function cmdHelp()
  346.     {
  347.         // tell the newsserver we want an article
  348.         $response $this->_sendCommand('HELP');
  349.         if (PEAR::isError($response)) {
  350.             return $response;
  351.         }
  352.     
  353.         switch ($response{
  354.             case NET_NNTP_PROTOCOL_RESPONSECODE_HELP_FOLLOWS// 100
  355.                 $data $this->_getTextResponse();
  356.                 if (PEAR::isError($data)) {
  357.                     return $data;
  358.                 }
  359.                 return $data;
  360.                 break;
  361.             default:
  362.                 return $this->_handleUnexpectedResponse($response);
  363.         }
  364.     }
  365.  
  366.     // }}}
  367.     // {{{ cmdCapabilities()
  368.  
  369.     /**
  370.      * Returns servers capabilities
  371.      *
  372.      * @return mixed (array) list of capabilities on success or (object) pear_error on failure
  373.      * @access protected
  374.      */
  375.     function cmdCapabilities()
  376.     {
  377.         // tell the newsserver we want an article
  378.         $response $this->_sendCommand('CAPABILITIES');
  379.         if (PEAR::isError($response)) {
  380.             return $response;
  381.         }
  382.     
  383.         switch ($response{
  384.             case NET_NNTP_PROTOCOL_RESPONSECODE_CAPABILITIES_FOLLOW// 101, Draft: 'Capability list follows'
  385.                 $data $this->_getTextResponse();
  386.                 if (PEAR::isError($data)) {
  387.                     return $data;
  388.                 }
  389.                 return $data;
  390.                 break;
  391.             default:
  392.                 return $this->_handleUnexpectedResponse($response);
  393.         }
  394.     }
  395.  
  396.     // }}}
  397.     // {{{ cmdModeReader()
  398.  
  399.     /**
  400.      * 
  401.      *
  402.      * @return mixed (bool) true when posting allowed, false when postind disallowed or (object) pear_error on failure
  403.      * @access protected
  404.      */
  405.     function cmdModeReader()
  406.     {
  407.         // tell the newsserver we want an article
  408.         $response $this->_sendCommand('MODE READER');
  409.         if (PEAR::isError($response)) {
  410.             return $response;
  411.         }
  412.     
  413.         switch ($response{
  414.             case NET_NNTP_PROTOCOL_RESPONSECODE_READY_POSTING_ALLOWED// 200, RFC2980: 'Hello, you can post'
  415.                 return true;
  416.                 break;
  417.             case NET_NNTP_PROTOCOL_RESPONSECODE_READY_POSTING_PROHIBITED// 201, RFC2980: 'Hello, you can't post'
  418.                 return false;
  419.                 break;
  420.             case NET_NNTP_PROTOCOL_RESPONSECODE_NOT_PERMITTED// 502, 'access restriction or permission denied' / service permanently unavailable
  421.                 return PEAR::throwError('Connection being closed, since service so permanently unavailable'$response$this->_currentStatusResponse());
  422.                 break;
  423.             default:
  424.                 return $this->_handleUnexpectedResponse($response);
  425.         }
  426.     }
  427.  
  428.     // }}}
  429.     // {{{ cmdNext()
  430.  
  431.     /**
  432.      * 
  433.      *
  434.      * @return mixed (array) or (string) or (int) or (object) pear_error on failure
  435.      * @access protected
  436.      */
  437.     function cmdNext($ret = -1)
  438.     {
  439.         // 
  440.         $response $this->_sendCommand('NEXT');
  441.         if (PEAR::isError($response)) {
  442.             return $response;
  443.         }
  444.  
  445.         switch ($response{
  446.             case NET_NNTP_PROTOCOL_RESPONSECODE_ARTICLE_SELECTED// 223, RFC977: 'n a article retrieved - request text separately (n = article number, a = unique article id)'
  447.                 $response_arr split(' 'trim($this->_currentStatusResponse()));
  448.  
  449.                 switch ($ret{
  450.                     case -1:
  451.                         return array('number' => (int) $response_arr[0]'id' =>  (string) $response_arr[1]);
  452.                         break;
  453.                     case 0:
  454.                         return (int) $response_arr[0];
  455.                         break;
  456.                     case 1:
  457.                         return (string) $response_arr[1];
  458.                         break;
  459.                 }
  460.                 break;
  461.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC977: 'no newsgroup selected'
  462.                 return PEAR::throwError('No newsgroup has been selected'$response$this->_currentStatusResponse());
  463.                 break;
  464.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED// 420, RFC977: 'no current article has been selected'
  465.                 return PEAR::throwError('No current article has been selected'$response$this->_currentStatusResponse());
  466.                 break;
  467.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_NEXT_ARTICLE// 421, RFC977: 'no next article in this group'
  468.                 return PEAR::throwError('No next article in this group'$response$this->_currentStatusResponse());
  469.                 break;
  470.             default:
  471.                 return $this->_handleUnexpectedResponse($response);
  472.         }
  473.     }
  474.  
  475.     // }}}
  476.     // {{{ cmdLast()
  477.  
  478.     /**
  479.      * 
  480.      *
  481.      * @return mixed (array) or (string) or (int) or (object) pear_error on failure
  482.      * @access protected
  483.      */
  484.     function cmdLast($ret = -1)
  485.     {
  486.         // 
  487.         $response $this->_sendCommand('LAST');
  488.         if (PEAR::isError($response)) {
  489.             return $response;
  490.         }
  491.  
  492.         switch ($response{
  493.             case NET_NNTP_PROTOCOL_RESPONSECODE_ARTICLE_SELECTED// 223, RFC977: 'n a article retrieved - request text separately (n = article number, a = unique article id)'
  494.                 $response_arr split(' 'trim($this->_currentStatusResponse()));
  495.  
  496.                 switch ($ret{
  497.                     case -1:
  498.                         return array('number' => (int) $response_arr[0]'id' =>  (string) $response_arr[1]);
  499.                         break;
  500.                     case 0:
  501.                         return (int) $response_arr[0];
  502.                         break;
  503.                     case 1:
  504.                         return (string) $response_arr[1];
  505.                         break;
  506.                 }
  507.                 break;
  508.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC977: 'no newsgroup selected'
  509.                 return PEAR::throwError('No newsgroup has been selected'$response$this->_currentStatusResponse());
  510.                 break;
  511.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED// 420, RFC977: 'no current article has been selected'
  512.                 return PEAR::throwError('No current article has been selected'$response$this->_currentStatusResponse());
  513.                 break;
  514.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_PREVIOUS_ARTICLE// 422, RFC977: 'no previous article in this group'
  515.                 return PEAR::throwError('No previous article in this group'$response$this->_currentStatusResponse());
  516.                 break;
  517.             default:
  518.                 return $this->_handleUnexpectedResponse($response);
  519.         }
  520.     }
  521.  
  522.     // }}}
  523.     // {{{ cmdStat
  524.  
  525.     /**
  526.      * 
  527.      *
  528.      * @param mixed $article 
  529.      *
  530.      * @return mixed (array) or (string) or (int) or (object) pear_error on failure
  531.      * @access protected
  532.      */
  533.     function cmdStat($article$ret = -1)
  534.     {
  535.         // tell the newsserver we want an article
  536.         $response $this->_sendCommand('STAT '.$article);
  537.         if (PEAR::isError($response)) {
  538.             return $response;
  539.         }
  540.  
  541.         switch ($response{
  542.             case NET_NNTP_PROTOCOL_RESPONSECODE_ARTICLE_SELECTED// 223, RFC977: 'n <a> article retrieved - request text separately' (actually not documented, but copied from the ARTICLE command)
  543.                 $response_arr split(' 'trim($this->_currentStatusResponse()));
  544.  
  545.                 switch ($ret{
  546.                     case -1:
  547.                         return array('number' => (int) $response_arr[0]'id' =>  (string) $response_arr[1]);
  548.                         break;
  549.                     case 0:
  550.                         return (int) $response_arr[0];
  551.                         break;
  552.                     case 1:
  553.                         return (string) $response_arr[1];
  554.                         break;
  555.                 }
  556.         
  557.                 break;
  558.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC977: 'no newsgroup has been selected' (actually not documented, but copied from the ARTICLE command)
  559.                 return PEAR::throwError('No newsgroup has been selected'$response$this->_currentStatusResponse());
  560.                 break;
  561.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_NUMBER// 423, RFC977: 'no such article number in this group' (actually not documented, but copied from the ARTICLE command)
  562.                 return PEAR::throwError('No such article number in this group'$response$this->_currentStatusResponse());
  563.                 break;
  564.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_ID// 430, RFC977: 'no such article found' (actually not documented, but copied from the ARTICLE command)
  565.                 return PEAR::throwError('No such article found'$response$this->_currentStatusResponse());
  566.                 break;
  567.             default:
  568.                 return $this->_handleUnexpectedResponse($response);
  569.         }
  570.     }
  571.  
  572.     // }}}
  573.     // {{{ cmdArticle()
  574.  
  575.     /**
  576.      * Get an article from the currently open connection.
  577.      *
  578.      * @param mixed $article Either a message-id or a message-number of the article to fetch. If null or '', then use current article.
  579.      *
  580.      * @return mixed (array) article on success or (object) pear_error on failure
  581.      * @access protected
  582.      */
  583.     function cmdArticle($article)
  584.     {
  585.         // tell the newsserver we want an article
  586.         $response $this->_sendCommand('ARTICLE '.$article);
  587.         if (PEAR::isError($response)) {
  588.             return $response;
  589.         }
  590.     
  591.         switch ($response{
  592.             case NET_NNTP_PROTOCOL_RESPONSECODE_ARTICLE_FOLLOWS:  // 220, RFC977: 'n <a> article retrieved - head and body follow (n = article number, <a> = message-id)'
  593.                 $data $this->_getTextResponse();
  594.                 if (PEAR::isError($data)) {
  595.                     return $data;
  596.                 }
  597.                 return $data;
  598.                 break;
  599.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC977: 'no newsgroup has been selected'
  600.                 return PEAR::throwError('No newsgroup has been selected'$response$this->_currentStatusResponse());
  601.                 break;
  602.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED// 420, RFC977: 'no current article has been selected'
  603.                 return PEAR::throwError('No current article has been selected'$response$this->_currentStatusResponse());
  604.                 break;
  605.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_NUMBER// 423, RFC977: 'no such article number in this group'
  606.                 return PEAR::throwError('No such article number in this group'$response$this->_currentStatusResponse());
  607.                 break;
  608.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_ID// 430, RFC977: 'no such article found'
  609.                 return PEAR::throwError('No such article found'$response$this->_currentStatusResponse());
  610.                 break;
  611.             default:
  612.                 return $this->_handleUnexpectedResponse($response);
  613.         }
  614.     }
  615.  
  616.     // }}}
  617.     // {{{ cmdHead()
  618.  
  619.     /**
  620.      * Get the headers of an article from the currently open connection.
  621.      *
  622.      * @param mixed $article Either a message-id or a message-number of the article to fetch the headers from. If null or '', then use current article.
  623.      *
  624.      * @return mixed (array) headers on success or (object) pear_error on failure
  625.      * @access protected
  626.      */
  627.     function cmdHead($article)
  628.     {
  629.         // tell the newsserver we want the header of an article
  630.         $response $this->_sendCommand('HEAD '.$article);
  631.         if (PEAR::isError($response)) {
  632.             return $response;
  633.         }
  634.  
  635.         switch ($response{
  636.             case NET_NNTP_PROTOCOL_RESPONSECODE_HEAD_FOLLOWS:     // 221, RFC977: 'n <a> article retrieved - head follows'
  637.                 $data $this->_getTextResponse();
  638.                 if (PEAR::isError($data)) {
  639.                     return $data;
  640.             }
  641.                 return $data;
  642.                 break;
  643.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC977: 'no newsgroup has been selected'
  644.                 return PEAR::throwError('No newsgroup has been selected'$response$this->_currentStatusResponse());
  645.                 break;
  646.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED// 420, RFC977: 'no current article has been selected'
  647.                 return PEAR::throwError('No current article has been selected'$response$this->_currentStatusResponse());
  648.                 break;
  649.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_NUMBER// 423, RFC977: 'no such article number in this group'
  650.                 return PEAR::throwError('No such article number in this group'$response$this->_currentStatusResponse());
  651.                 break;
  652.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_ID// 430, RFC977: 'no such article found'
  653.                 return PEAR::throwError('No such article found'$response$this->_currentStatusResponse());
  654.                 break;
  655.             default:
  656.                 return $this->_handleUnexpectedResponse($response);
  657.         }
  658.     }
  659.  
  660.     // }}}
  661.     // {{{ cmdBody()
  662.  
  663.     /**
  664.      * Get the body of an article from the currently open connection.
  665.      *
  666.      * @param mixed $article Either a message-id or a message-number of the article to fetch the body from. If null or '', then use current article.
  667.      *
  668.      * @return mixed (array) body on success or (object) pear_error on failure
  669.      * @access protected
  670.      */
  671.     function cmdBody($article)
  672.     {
  673.         // tell the newsserver we want the body of an article
  674.         $response $this->_sendCommand('BODY '.$article);
  675.         if (PEAR::isError($response)) {
  676.             return $response;
  677.         }
  678.  
  679.         switch ($response{
  680.             case NET_NNTP_PROTOCOL_RESPONSECODE_BODY_FOLLOWS:     // 222, RFC977: 'n <a> article retrieved - body follows'
  681.                 $data $this->_getTextResponse();
  682.                 if (PEAR::isError($data)) {
  683.                     return $data;
  684.                 }
  685.                 return $data;
  686.                 break;
  687.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC977: 'no newsgroup has been selected'
  688.                 return PEAR::throwError('No newsgroup has been selected'$response$this->_currentStatusResponse());
  689.                 break;
  690.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED// 420, RFC977: 'no current article has been selected'
  691.                 return PEAR::throwError('No current article has been selected'$response$this->_currentStatusResponse());
  692.                 break;
  693.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_NUMBER// 423, RFC977: 'no such article number in this group'
  694.                 return PEAR::throwError('No such article number in this group'$response$this->_currentStatusResponse());
  695.                 break;
  696.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_ID// 430, RFC977: 'no such article found'
  697.                 return PEAR::throwError('No such article found'$response$this->_currentStatusResponse());
  698.                 break;
  699.             default:
  700.                 return $this->_handleUnexpectedResponse($response);
  701.         }
  702.     }
  703.  
  704.     // }}}
  705.     // {{{ cmdPost()
  706.  
  707.     /**
  708.      * Post an article to a newsgroup.
  709.      *
  710.      * Among the aditional headers you might think of adding could be:
  711.      * "NNTP-Posting-Host: <ip-of-author>", which should contain the IP-adress
  712.      * of the author of the post, so the message can be traced back to him.
  713.      * "Organization: <org>" which contain the name of the organization
  714.      * the post originates from.
  715.      *
  716.      * @param string $newsgroup The newsgroup to post to.
  717.      * @param string $subject The subject of the post.
  718.      * @param string $body The body of the post itself.
  719.      * @param string $from Name + email-adress of sender.
  720.      * @param optional mixed $headers Aditional headers to send.
  721.      *
  722.      * @return mixed (bool) true on success or (object) pear_error on failure
  723.      * @access protected
  724.      */
  725.     function cmdPost($newsgroup$subject$body$from$headers = null)
  726.     {
  727.     // Only accept $headers is null, array or string
  728.         if (!is_null($headers&& !is_array($headers&& !is_string($headers)) {
  729.             return PEAR::throwError('Ups'null0);
  730.     }
  731.  
  732.         // tell the newsserver we want to post an article
  733.         $response $this->_sendCommand('POST');
  734.         if (PEAR::isError($response)) {
  735.             return $response;
  736.         }
  737.  
  738.         switch ($response{
  739.             case NET_NNTP_PROTOCOL_RESPONSECODE_POSTING_SEND// 340, RFC977: 'send article to be posted. End with <CR-LF>.<CR-LF>'
  740.                 // continue...
  741.                 break;
  742.             case NET_NNTP_PROTOCOL_RESPONSECODE_POSTING_PROHIBITED// 440, RFC977: 'posting not allowed'
  743.                 return PEAR::throwError('Posting not allowed'$response$this->_currentStatusResponse());
  744.                 break;
  745.             default:
  746.                 return $this->_handleUnexpectedResponse($response);
  747.         }
  748.  
  749.         /* should be presented in the format specified by RFC850 */
  750.         
  751.         // Send standard headers and x-poster header
  752.         $this->_socket->write("Newsgroups: $newsgroup\r\nSubject: $subject\r\nFrom: $from\r\n");
  753.         $this->_socket->write("X-poster: PEAR::Net_NNTP\r\n");
  754.  
  755.         // Send additional headers, if any
  756.         switch (true{
  757.             case is_null($headers):
  758.         break;
  759.             case is_array($headers):
  760.             foreach ($headers as $header=>$value{
  761.                     switch (true{
  762.                     case is_string($header):
  763.                             echo $this->_socket->write("$header$value\r\n");
  764.                     break;
  765.                     case is_int($header&& strpos($value':'1):
  766.                             echo $this->_socket->write("$value\r\n");
  767.                             break;
  768.                     default:
  769.                             // Ignore header...
  770.                     }
  771.         }
  772.                 break;
  773.             case is_string($headers):
  774.                 $this->_socket->write("$headers\r\n");
  775.                 break;
  776.     }
  777.     
  778.         // Send body
  779.         $this->_socket->write("\r\n");
  780.         $this->_socket->write($body);
  781.         $this->_socket->write("\r\n.\r\n");
  782.  
  783.         // Retrive server's response.
  784.         $response $this->_getStatusResponse();
  785.         if (PEAR::isError($response)) {
  786.             return $response;
  787.         }
  788.  
  789.         switch ($response{
  790.             case NET_NNTP_PROTOCOL_RESPONSECODE_POSTING_SUCCESS// 240, RFC977: 'article posted ok'
  791.                 return true;
  792.                 break;
  793.             case NET_NNTP_PROTOCOL_RESPONSECODE_POSTING_FAILURE// 441, RFC977: 'posting failed'
  794.                 return PEAR::throwError('Posting failed'$response$this->_currentStatusResponse());
  795.                 break;
  796.             default:
  797.                 return $this->_handleUnexpectedResponse($response);
  798.         }
  799.     }
  800.  
  801.     // }}}
  802.     // {{{ cmdIhave()
  803.  
  804.     /**
  805.      *
  806.      *
  807.      * @param string $id 
  808.      * @param mixed $message (string/array)
  809.      *
  810.      * @return mixed (bool) true on success or (object) pear_error on failure
  811.      * @access protected
  812.      */
  813.     function cmdIhave($id$message)
  814.     {
  815.         // tell the newsserver we want to post an article
  816.         $response $this->_sendCommand('IHAVE ' $id);
  817.         if (PEAR::isError($response)) {
  818.             return $response;
  819.         }
  820.  
  821.         switch ($response{
  822.             case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_SEND// 335
  823.                 // continue...
  824.                 break;
  825.             case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_UNWANTED// 435
  826.                 return PEAR::throwError('Article not wanted'$response$this->_currentStatusResponse());
  827.                 break;
  828.             case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_FAILURE// 436
  829.                 return PEAR::throwError('Transfer not possible; try again later'$response$this->_currentStatusResponse());
  830.                 break;
  831.             default:
  832.                 return $this->_handleUnexpectedResponse($response);
  833.         }
  834.  
  835.         /* should be presented in the format specified by RFC850 */
  836.         
  837.         // Send standard headers and x-poster header
  838.         $this->_socket->write($message);
  839.         $this->_socket->write("\r\n.\r\n");
  840.  
  841.         // Retrive server's response.
  842.         $response $this->_getStatusResponse();
  843.         if (PEAR::isError($response)) {
  844.             return $response;
  845.         }
  846.  
  847.         switch ($response{
  848.             case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_SUCCESS// 235
  849.                 return true;
  850.                 break;
  851.             case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_FAILURE// 436
  852.                 return PEAR::throwError('Transfer not possible; try again later'$response$this->_currentStatusResponse());
  853.                 break;
  854.             case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_REJECTED// 437
  855.                 return PEAR::throwError('Transfer rejected; do not retry'$response$this->_currentStatusResponse());
  856.                 break;
  857.             default:
  858.                 return $this->_handleUnexpectedResponse($response);
  859.         }
  860.     }
  861.  
  862.     // }}}
  863.     // {{{ cmdGroup()
  864.  
  865.     /**
  866.      * Selects a news group (issue a GROUP command to the server)
  867.      *
  868.      * @param string $newsgroup The newsgroup name
  869.      *
  870.      * @return mixed (array) groupinfo on success or (object) pear_error on failure
  871.      * @access protected
  872.      */
  873.     function cmdGroup($newsgroup)
  874.     {
  875.         $response $this->_sendCommand('GROUP '.$newsgroup);
  876.         if (PEAR::isError($response)) {
  877.             return $response;
  878.         }
  879.  
  880.         switch ($response{
  881.             case NET_NNTP_PROTOCOL_RESPONSECODE_GROUP_SELECTED// 211, RFC977: 'n f l s group selected'
  882.                 $response_arr split(' 'trim($this->_currentStatusResponse()));
  883.  
  884.                 return array('count' => $response_arr[0],
  885.                              'first' => $response_arr[1],
  886.                              'last'  => $response_arr[2],
  887.                              'group' => $response_arr[3]);
  888.                 break;
  889.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_GROUP// 411, RFC977: 'no such news group'
  890.                 return PEAR::throwError('No such news group'$response$this->_currentStatusResponse());
  891.                 break;
  892.             default:
  893.                 return $this->_handleUnexpectedResponse($response);
  894.         }
  895.     }
  896.  
  897.     // }}}
  898.     // {{{ cmdList()
  899.  
  900.     /**
  901.      * Fetches a list of all avaible newsgroups
  902.      *
  903.      * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure
  904.      * @access protected
  905.      */
  906.     function cmdList()
  907.     {
  908.         $response $this->_sendCommand('LIST');
  909.         if (PEAR::isError($response)){
  910.             return $response;
  911.         }
  912.  
  913.         switch ($response{
  914.             case NET_NNTP_PROTOCOL_RESPONSECODE_GROUPS_FOLLOW// 215, RFC977: 'list of newsgroups follows'
  915.                 $data $this->_getTextResponse();
  916.                 if (PEAR::isError($data)) {
  917.                     return $data;
  918.                 }
  919.  
  920.                 $groups = array();
  921.                 foreach($data as $line{
  922.                     $arr explode(' 'trim($line));
  923.  
  924.                     $group = array('group'   => $arr[0],
  925.                                    'last'    => $arr[1],
  926.                                    'first'   => $arr[2],
  927.                                    'posting' => $arr[3]);
  928.  
  929.                     $groups[$group['group']] $group;
  930.                 }
  931.                 return $groups;
  932.                 break;
  933.             default:
  934.                 return $this->_handleUnexpectedResponse($response);
  935.         }
  936.     }
  937.  
  938.     // }}}
  939.     // {{{ cmdListNewsgroups()
  940.  
  941.     /**
  942.      * Fetches a list of (all) avaible newsgroup descriptions.
  943.      *
  944.      * @param string $wildmat Wildmat of the groups, that is to be listed, defaults to null;
  945.      *
  946.      * @return mixed (array) nested array with description of existing newsgroups on success or (object) pear_error on failure
  947.      * @access protected
  948.      */
  949.     function cmdListNewsgroups($wildmat = null)
  950.     {
  951.         if (is_null($wildmat)) {
  952.             $command 'LIST NEWSGROUPS';
  953.         else {
  954.             $command 'LIST NEWSGROUPS ' $wildmat;
  955.         }
  956.  
  957.         $response $this->_sendCommand($command);
  958.         if (PEAR::isError($response)){
  959.             return $response;
  960.         }
  961.  
  962.         switch ($response{
  963.             case NET_NNTP_PROTOCOL_RESPONSECODE_GROUPS_FOLLOW// 215, RFC2980: 'information follows'
  964.                 $data $this->_getTextResponse();
  965.                 if (PEAR::isError($data)) {
  966.                     return $data;
  967.                 }
  968.  
  969.                 $groups = array();
  970.  
  971.                 foreach($data as $line{
  972.                     preg_match("/^(.*?)\s(.*?$)/"trim($line)$matches);
  973.                     $groups[$matches[1]] = (string) $matches[2];
  974.                 }
  975.  
  976.                 return $groups;
  977.             break;
  978.             case 503: // RFC2980: 'program error, function not performed'
  979.                 return PEAR::throwError('Internal server error, function not performed'$response$this->_currentStatusResponse());
  980.                 break;
  981.             default:
  982.                 return $this->_handleUnexpectedResponse($response);
  983.         }
  984.     }
  985.  
  986.     // }}}
  987.     /**
  988.      * Fetches a list of (all) avaible newsgroup descriptions.
  989.      * Depresated as of RFC2980.
  990.      *
  991.      * @param string $wildmat Wildmat of the groups, that is to be listed, defaults to '*';
  992.      *
  993.      * @return mixed (array) nested array with description of existing newsgroups on success or (object) pear_error on failure
  994.      * @access protected
  995.      */
  996.     function cmdXGTitle($wildmat '*')
  997.     {
  998.         $response $this->_sendCommand('XGTITLE '.$wildmat);
  999.         if (PEAR::isError($response)){
  1000.             return $response;
  1001.         }
  1002.  
  1003.         switch ($response{
  1004.             case 282: // RFC2980: 'list of groups and descriptions follows'
  1005.                 $data $this->_getTextResponse();
  1006.                 if (PEAR::isError($data)) {
  1007.                     return $data;
  1008.                 }
  1009.  
  1010.                 $groups = array();
  1011.  
  1012.                 foreach($data as $line{
  1013.                     preg_match("/^(.*?)\s(.*?$)/"trim($line)$matches);
  1014.                     $groups[$matches[1]] = (string) $matches[2];
  1015.                 }
  1016.  
  1017.                 return $groups;
  1018.                 break;
  1019.           
  1020.             case 481: // RFC2980: 'Groups and descriptions unavailable'
  1021.                 return PEAR::throwError('Groups and descriptions unavailable'$response$this->_currentStatusResponse());
  1022.                 break;
  1023.             default:
  1024.                 return $this->_handleUnexpectedResponse($response);
  1025.         }
  1026.     }
  1027.  
  1028.     // }}}
  1029.     // {{{ cmdNewgroups()
  1030.  
  1031.     /**
  1032.      * Fetches a list of all newsgroups created since a specified date.
  1033.      *
  1034.      * @param int $time Last time you checked for groups (timestamp).
  1035.      * @param optional string $distributions (deprecaded in rfc draft)
  1036.      *
  1037.      * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure
  1038.      * @access protected
  1039.      */
  1040.     function cmdNewgroups($time$distributions = null)
  1041.     {
  1042.     $date date('ymd His'$time);
  1043.  
  1044.         if (is_null($distributions)) {
  1045.             $command 'NEWGROUPS ' $date ' GMT';
  1046.         else {
  1047.             $command 'NEWGROUPS ' $date ' GMT <' $distributions '>';
  1048.         }
  1049.  
  1050.         $response $this->_sendCommand($command);
  1051.         if (PEAR::isError($response)){
  1052.             return $response;
  1053.         }
  1054.  
  1055.         switch ($response{
  1056.             case NET_NNTP_PROTOCOL_RESPONSECODE_NEW_GROUPS_FOLLOW// 231, REF977: 'list of new newsgroups follows'
  1057.                 $groups = array();
  1058.                 foreach($this->_getTextResponse(as $line{
  1059.                     $arr explode(' '$line);
  1060.                     $groups[$arr[0]] = array('group'   => $arr[0],
  1061.                                              'last'    => $arr[1],
  1062.                                              'first'   => $arr[2],
  1063.                                              'posting' => $arr[3]);
  1064.  
  1065.                 }
  1066.                 return $groups;
  1067.                 break;
  1068.             default:
  1069.                 return $this->_handleUnexpectedResponse($response);
  1070.         }
  1071.     }
  1072.  
  1073.     // }}}
  1074.     // {{{ cmdListOverviewFmt()
  1075.  
  1076.     /**
  1077.      * Returns a list of avaible headers which are send from newsserver to client for every news message
  1078.      *
  1079.      * @return mixed (array) of header names on success or (object) pear_error on failure
  1080.      * @access protected
  1081.      */
  1082.     function cmdListOverviewFmt()
  1083.     {
  1084.         $response $this->_sendCommand('LIST OVERVIEW.FMT');
  1085.         if (PEAR::isError($response)){
  1086.             return $response;
  1087.         }
  1088.  
  1089.         switch ($response{
  1090.             case NET_NNTP_PROTOCOL_RESPONSECODE_GROUPS_FOLLOW// 215, RFC2980: 'information follows'
  1091.                 $data $this->_getTextResponse();
  1092.                 if (PEAR::isError($data)) {
  1093.                     return $data;
  1094.                 }
  1095.  
  1096.                 $format = array();
  1097.  
  1098.                 foreach ($data as $line{
  1099.  
  1100.             // Check if postfixed by ':full' (case-insensitive)
  1101.             if (0 == strcasecmp(substr($line-55)':full')) {
  1102.                     // ':full' is _not_ included in tag, but value set to true
  1103.                     $format[substr($line0-5)= true;
  1104.             else {
  1105.                     // ':' is _not_ included in tag; value set to false
  1106.                     $format[substr($line0-1)= false;
  1107.                     }
  1108.                 }
  1109.  
  1110.                 return $format;
  1111.                 break;
  1112.             case 503: // RFC2980: 'program error, function not performed'
  1113.                 return PEAR::throwError('Internal server error, function not performed'$response$this->_currentStatusResponse());
  1114.                 break;
  1115.             default:
  1116.                 return $this->_handleUnexpectedResponse($response);
  1117.         }
  1118.     }
  1119.  
  1120.     // }}}
  1121.     // {{{ cmdOver()
  1122.  
  1123.     /**
  1124.      * Fetch message header from message number $first until $last
  1125.      *
  1126.      * The format of the returned array is:
  1127.      * $messages[][header_name]
  1128.      *
  1129.      * @param optional string $range articles to fetch
  1130.      *
  1131.      * @return mixed (array) nested array of message and there headers on success or (object) pear_error on failure
  1132.      * @access protected
  1133.      */
  1134.     function cmdOver($range = null)
  1135.     {
  1136.         if (is_null($range)) {
  1137.         $command 'OVER';
  1138.         else {
  1139.             $command 'OVER ' $range;
  1140.         }
  1141.  
  1142.         $response $this->_sendCommand($command);
  1143.         if (PEAR::isError($response)){
  1144.             return $response;
  1145.         }
  1146.  
  1147.         switch ($response{
  1148.             case NET_NNTP_PROTOCOL_RESPONSECODE_OVERVIEW_FOLLOWS// 224, RFC2980: 'Overview information follows'
  1149.                 $data $this->_getTextResponse();
  1150.                 if (PEAR::isError($data)) {
  1151.                     return $data;
  1152.                 }
  1153.  
  1154.                 $format $this->cmdListOverviewFmt();
  1155.                 if (PEAR::isError($format)){
  1156.                     return $format;
  1157.                 }
  1158.                 array_splice($format07);
  1159.  
  1160.                 $messages = array();
  1161.                 foreach($data as $line{
  1162.                     $fields explode("\t"trim($line));
  1163.  
  1164.                     $message = array('Number'     => $fields[0],
  1165.                              'Subject'    => $fields[1],
  1166.                              'From'       => $fields[2],
  1167.                              'Date'       => $fields[3],
  1168.                              'Message-ID' => $fields[4],
  1169.                              'References' => $fields[5],
  1170.                              ':bytes'     => $fields[6],
  1171.                              ':lines'     => $fields[7]);
  1172.  
  1173.                     $i = 7;
  1174.                     foreach ($format as $tag => $full{
  1175.                         if ($full === true{
  1176.                             $field explode(':'$fields[++$i]2);
  1177.                             $message[$tagltrim($field[1]" \t");
  1178.                         else {
  1179.                             $message[$tag$fields[++$i];
  1180.                         }
  1181.                     }
  1182.  
  1183.                     $messages[$message;
  1184.                 }
  1185.                 return $messages;
  1186.                 break;
  1187.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC2980: 'No news group current selected'
  1188.                 return PEAR::throwError('No news group current selected'$response$this->_currentStatusResponse());
  1189.                 break;
  1190.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED// 420, RFC2980: 'No article(s) selected'
  1191.                 return PEAR::throwError('No article(s) selected'$response$this->_currentStatusResponse());
  1192.                 break;
  1193.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_NUMBER// 423:, Draft27: 'No articles in that range'
  1194.                 return PEAR::throwError('No articles in that range'$response$this->_currentStatusResponse());
  1195.                 break;
  1196.             case 502: // RFC2980: 'no permission'
  1197.                 return PEAR::throwError('No permission'$response$this->_currentStatusResponse());
  1198.                 break;
  1199.             default:
  1200.                 return $this->_handleUnexpectedResponse($response);
  1201.         }
  1202.     }
  1203.     
  1204.     // }}}
  1205.     // {{{ cmdXOver()
  1206.  
  1207.     /**
  1208.      * Fetch message header from message number $first until $last
  1209.      *
  1210.      * The format of the returned array is:
  1211.      * $messages[message_id][header_name]
  1212.      *
  1213.      * @param optional string $range articles to fetch
  1214.      *
  1215.      * @return mixed (array) nested array of message and there headers on success or (object) pear_error on failure
  1216.      * @access protected
  1217.      */
  1218.     function cmdXOver($range = null)
  1219.     {
  1220.     // deprecated API (the code _is_ still in alpha state)
  1221.         if (func_num_args(> 1 {
  1222.             die('The second parameter in cmdXOver() has been deprecated! Use x-y instead...');
  1223.         }
  1224.  
  1225.         $format $this->cmdListOverviewFmt();
  1226.         if (PEAR::isError($format)){
  1227.             return $format;
  1228.         }
  1229.  
  1230.         if (is_null($range)) {
  1231.         $command 'XOVER';
  1232.         else {
  1233.             $command 'XOVER ' $range;
  1234.         }
  1235.  
  1236.         $response $this->_sendCommand($command);
  1237.         if (PEAR::isError($response)){
  1238.             return $response;
  1239.         }
  1240.  
  1241.         switch ($response{
  1242.             case NET_NNTP_PROTOCOL_RESPONSECODE_OVERVIEW_FOLLOWS// 224, RFC2980: 'Overview information follows'
  1243.                 $data $this->_getTextResponse();
  1244.                 if (PEAR::isError($data)) {
  1245.                     return $data;
  1246.                 }
  1247.  
  1248.                 $messages = array();
  1249.                 foreach($data as $line{
  1250.                     $fields explode("\t"trim($line));
  1251.  
  1252.                     $message = array();
  1253.  
  1254. //                    $message['number'] = $fields[0];
  1255.                     $message['Number'$fields[0];
  1256.  
  1257.                     $i = 0;
  1258.                     foreach ($format as $tag => $full{
  1259.                         if ($full === true{
  1260.                             $field explode(':'$fields[++$i]2);
  1261.                             $message[$tagltrim($field[1]" \t");
  1262.                         else {
  1263.                             $message[$tag$fields[++$i];
  1264.                         }
  1265.                     }
  1266.  
  1267. //                    $messages[$fields[4]] = $message;
  1268.                     $messages[$message;
  1269.                 }
  1270.                 return $messages;
  1271.                 break;
  1272.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC2980: 'No news group current selected'
  1273.                 return PEAR::throwError('No news group current selected'$response$this->_currentStatusResponse());
  1274.                 break;
  1275.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED// 420, RFC2980: 'No article(s) selected'
  1276.                 return PEAR::throwError('No article(s) selected'$response$this->_currentStatusResponse());
  1277.                 break;
  1278.             case 502: // RFC2980: 'no permission'
  1279.                 return PEAR::throwError('No permission'$response$this->_currentStatusResponse());
  1280.                 break;
  1281.             default:
  1282.                 return $this->_handleUnexpectedResponse($response);
  1283.         }
  1284.     }
  1285.     
  1286.     // }}}
  1287.     // {{{ cmdXROver()
  1288.  
  1289.     /**
  1290.      * Fetch message references from message number $first to $last
  1291.      *
  1292.      * @param optional string $range articles to fetch
  1293.      *
  1294.      * @return mixed (array) assoc. array of message references on success or (object) pear_error on failure
  1295.      * @access protected
  1296.      */
  1297.     function cmdXROver($range = null)
  1298.     {
  1299.     // Warn about deprecated API (the code _is_ still in alpha state)
  1300.         if (func_num_args(> 1 {
  1301.             die('The second parameter in cmdXROver() has been deprecated! Use x-y instead...');
  1302.         }
  1303.  
  1304.         if (is_null($range)) {
  1305.             $command 'XROVER';
  1306.         else {
  1307.             $command 'XROVER ' $range;
  1308.         }
  1309.  
  1310.         $response $this->_sendCommand($command);
  1311.         if (PEAR::isError($response)){
  1312.             return $response;
  1313.         }
  1314.  
  1315.         switch ($response{
  1316.             case NET_NNTP_PROTOCOL_RESPONSECODE_OVERVIEW_FOLLOWS// 224, RFC2980: 'Overview information follows'
  1317.                 $data $this->_getTextResponse();
  1318.                 if (PEAR::isError($data)) {
  1319.                     return $data;
  1320.                 }
  1321.  
  1322.                 $return = array();
  1323.                 foreach($data as $line{
  1324.                     $line explode(' 'trim($line)2);
  1325.                     $return[$line[0]] $line[1];
  1326.                 }
  1327.                 return $return;
  1328.                 break;
  1329.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC2980: 'No news group current selected'
  1330.                 return PEAR::throwError('No news group current selected'$response$this->_currentStatusResponse());
  1331.                 break;
  1332.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED// 420, RFC2980: 'No article(s) selected'
  1333.                 return PEAR::throwError('No article(s) selected'$response$this->_currentStatusResponse());
  1334.                 break;
  1335.             case 502: // RFC2980: 'no permission'
  1336.                 return PEAR::throwError('No permission'$response$this->_currentStatusResponse());
  1337.                 break;
  1338.             default:
  1339.                 return $this->_handleUnexpectedResponse($response);
  1340.         }
  1341.     }
  1342.  
  1343.     // }}}
  1344.     // {{{ cmdXHdr()
  1345.  
  1346.     /**
  1347.      * 
  1348.      *
  1349.      * The format of the returned array is:
  1350.      * $messages[message_id]
  1351.      *
  1352.      * @param optional string $range articles to fetch
  1353.      *
  1354.      * @return mixed (array) nested array of message and there headers on success or (object) pear_error on failure
  1355.      * @access protected
  1356.      */
  1357.     function cmdXHdr($field$range = null)
  1358.     {
  1359.         if (is_null($range)) {
  1360.         $command 'XHDR ' $field;
  1361.         else {
  1362.             $command 'XHDR ' $field ' ' $range;
  1363.         }
  1364.  
  1365.         $response $this->_sendCommand($command);
  1366.         if (PEAR::isError($response)){
  1367.             return $response;
  1368.         }
  1369.  
  1370.         switch ($response{
  1371.             case 221: // 221, RFC2980: 'Header follows'
  1372.                 $data $this->_getTextResponse();
  1373.                 if (PEAR::isError($data)) {
  1374.                     return $data;
  1375.                 }
  1376.  
  1377.                 $return = array();
  1378.                 foreach($data as $line{
  1379.                     $line explode(' 'trim($line)2);
  1380.                     $return[$line[0]] $line[1];
  1381.                 }
  1382.  
  1383.                 return $return;
  1384.                 break;
  1385.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC2980: 'No news group current selected'
  1386.                 return PEAR::throwError('No news group current selected'$response$this->_currentStatusResponse());
  1387.                 break;
  1388.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED// 420, RFC2980: 'No current article selected'
  1389.                 return PEAR::throwError('No current article selected'$response$this->_currentStatusResponse());
  1390.                 break;
  1391.             case 430: // 430, RFC2980: 'No such article'
  1392.                 return PEAR::throwError('No current article selected'$response$this->_currentStatusResponse());
  1393.                 break;
  1394.             case 502: // RFC2980: 'no permission'
  1395.                 return PEAR::throwError('No permission'$response$this->_currentStatusResponse());
  1396.                 break;
  1397.             default:
  1398.                 return $this->_handleUnexpectedResponse($response);
  1399.         }
  1400.     }
  1401.     
  1402.     // }}}
  1403.     // {{{ cmdListgroup()
  1404.  
  1405.     /**
  1406.      *
  1407.      *
  1408.      * @param optional string $newsgroup
  1409.      * @param optional mixed $range
  1410.      *
  1411.      * @return optional mixed (array) on success or (object) pear_error on failure
  1412.      * @access protected
  1413.      */
  1414.     function cmdListgroup($newsgroup = null$range = null)
  1415.     {
  1416.         if (is_null($newsgroup)) {
  1417.             $command 'LISTGROUP';
  1418.         else {
  1419.             if (is_null($range)) {
  1420.                 $command 'LISTGROUP ' $newsgroup;
  1421.             else {
  1422.                 $command 'LISTGROUP ' $newsgroup ' ' $range;
  1423.             }
  1424.         }
  1425.  
  1426.         $response $this->_sendCommand($command);
  1427.         if (PEAR::isError($response)){
  1428.             return $response;
  1429.         }
  1430.  
  1431.         switch ($response{
  1432.             case NET_NNTP_PROTOCOL_RESPONSECODE_GROUP_SELECTED// 211, RFC2980: 'list of article numbers follow'
  1433.  
  1434.                 $articles $this->_getTextResponse();
  1435.                 if (PEAR::isError($articles)) {
  1436.                     return $articles;
  1437.                 }
  1438.         
  1439.                 $response_arr split(' 'trim($this->_currentStatusResponse()));
  1440.  
  1441.                 return array('count'    => $response_arr[0],
  1442.                              'first'    => $response_arr[1],
  1443.                              'last'     => $response_arr[2],
  1444.                              'group'    => $response_arr[3],
  1445.                              'articles' => $articles);
  1446.                 break;
  1447.             case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED// 412, RFC2980: 'Not currently in newsgroup'
  1448.                 return PEAR::throwError('Not currently in newsgroup'$response$this->_currentStatusResponse());
  1449.                 break;
  1450.             case 502: // RFC2980: 'no permission'
  1451.                 return PEAR::throwError('No permission'$response$this->_currentStatusResponse());
  1452.                 break;
  1453.             default:
  1454.                 return $this->_handleUnexpectedResponse($response);
  1455.         }
  1456.     }
  1457.  
  1458.     // }}}
  1459.     // {{{ cmdNewnews()
  1460.  
  1461.     /**
  1462.      *
  1463.      *
  1464.      * @param timestamp $time 
  1465.      * @param mixed $newsgroups (string or array of strings)
  1466.      * @param mixed $distribution (string or array of strings)
  1467.      *
  1468.      * @return mixed 
  1469.      * @access protected
  1470.      */
  1471.     function cmdNewnews($time$newsgroups$distribution = null)
  1472.     {
  1473.         $date date('ymd His'$time);
  1474.  
  1475.         if (is_array()) {
  1476.             $newsgroups implode(','$newsgroups);
  1477.         }
  1478.     
  1479.  
  1480.         if (is_null($distribution)) {
  1481.             $command 'NEWNEWS ' $newsgroups ' ' $date ' GMT';
  1482.         else {
  1483.             if (is_array()) {
  1484.             $distribution implode(','$distribution);
  1485.             }
  1486.  
  1487.             $command 'NEWNEWS ' $newsgroups ' ' $date ' GMT <' $distribution '>';
  1488.         }
  1489.  
  1490.     // TODO: the lenght of the request string may not exceed 510 chars
  1491.     
  1492.         $response $this->_sendCommand($command);
  1493.         if (PEAR::isError($response)){
  1494.             return $response;
  1495.         }
  1496.  
  1497.         switch ($response{
  1498.             case NET_NNTP_PROTOCOL_RESPONSECODE_NEW_ARTICLES_FOLLOW// 230, RFC977: 'list of new articles by message-id follows'
  1499.                 $messages = array();
  1500.                 foreach($this->_getTextResponse(as $line{
  1501.                     $messages[$line;
  1502.                 }
  1503.                 return $messages;
  1504.                 break;
  1505.             default:
  1506.                 return $this->_handleUnexpectedResponse($response);
  1507.         }
  1508.     }
  1509.  
  1510.     // }}}
  1511.     // {{{ cmdDate()
  1512.  
  1513.     /**
  1514.      * Get the date from the newsserver format of returned date
  1515.      *
  1516.      * @param bool $timestap when false function returns string, and when true function returns int/timestamp.
  1517.      *
  1518.      * @return mixed (string) 'YYYYMMDDhhmmss' / (int) timestamp on success or (object) pear_error on failure
  1519.      * @access protected
  1520.      */
  1521.     function cmdDate($timestamp = false)
  1522.     {
  1523.         $response $this->_sendCommand('DATE');
  1524.         if (PEAR::isError($response)){
  1525.             return $response;
  1526.         }
  1527.  
  1528.         switch ($response{
  1529.             case NET_NNTP_PROTOCOL_RESPONSECODE_SERVER_DATE// 111, RFC2980: 'YYYYMMDDhhmmss'
  1530.                 $d $this->_currentStatusResponse();
  1531.                 if ($timestamp === false{
  1532.                     return (string) $d;        
  1533.                 else {
  1534.                     return (int) strtotime(substr($d08).' '.$d[8].$d[9].':'.$d[10].$d[11].':'.$d[12].$d[13]);
  1535.                 }
  1536.                 break;
  1537.             default:
  1538.                 return $this->_handleUnexpectedResponse($response);
  1539.         }
  1540.     }
  1541.     // }}}
  1542.     // {{{ isConnected()
  1543.  
  1544.     /**
  1545.      * Test whether we are connected or not.
  1546.      *
  1547.      * @return bool true or false
  1548.      * @access protected
  1549.      */
  1550.     function isConnected()
  1551.     {
  1552.         return (is_resource($this->_socket->fp&& (!$this->_socket->eof()));
  1553.     }
  1554.  
  1555.     // }}}
  1556.     // {{{ setDebug()
  1557.  
  1558.     /**
  1559.      * Sets the debuging information on or off
  1560.      *
  1561.      * @param boolean $debug True or false
  1562.      *
  1563.      * @return bool previos state
  1564.      * @access protected
  1565.      */
  1566.     function setDebug($debug = true)
  1567.     {
  1568.         $tmp $this->_debug;
  1569.         $this->_debug $debug;
  1570.         return $tmp;
  1571.     }
  1572.  
  1573.     // }}}
  1574.     // {{{ _handleUnexpectedResponse()
  1575.  
  1576.     /**
  1577.      *
  1578.      *
  1579.      * @param int $code Status code number
  1580.      * @param string $text Status text
  1581.      *
  1582.      * @return mixed 
  1583.      * @access private
  1584.      */
  1585.     function _handleUnexpectedResponse($code = null$text = null)
  1586.     {
  1587.         if ($code === null{
  1588.             $code $this->_currentStatusResponse[0];
  1589.     }
  1590.  
  1591.         if ($text === null{
  1592.             $text $this->_currentStatusResponse();
  1593.     }
  1594.  
  1595.         return PEAR::throwError('Unexpected response'$code$text);
  1596.     }
  1597.  
  1598.     // }}}
  1599.     // {{{ _getStatusResponse()
  1600.  
  1601.     /**
  1602.      * Get servers status response after a command.
  1603.      *
  1604.      * @return mixed (int) statuscode on success or (object) pear_error on failure
  1605.      * @access private
  1606.      */
  1607.     function _getStatusResponse()
  1608.     {
  1609.         // Retrieve a line (terminated by "\r\n") from the server.
  1610.         $response $this->_socket->gets(256);
  1611.         if (PEAR::isError($response) ) {
  1612.             return PEAR::throwError('Failed to read from socket!'null$response->getMessage());
  1613.         }
  1614.  
  1615.         if ($this->_debug{
  1616.             echo "S: $response\r\n";
  1617.         }
  1618.  
  1619.         // Trim the start of the response in case of misplased whitespace (should not be needen!!!)
  1620.         $response ltrim($response);
  1621.  
  1622.         $this->_currentStatusResponse = array(
  1623.                                               (int) substr($response03),
  1624.                                               (string) rtrim(substr($response4))
  1625.                                              );
  1626.  
  1627.         return $this->_currentStatusResponse[0];
  1628.     }
  1629.     
  1630.     // }}}
  1631.     // {{{ __currentStatusResponse()
  1632.  
  1633.     /**
  1634.      *
  1635.      *
  1636.      * @return string status text
  1637.      * @access private
  1638.      */
  1639.     function _currentStatusResponse()
  1640.     {
  1641.         return $this->_currentStatusResponse[1];
  1642.     }
  1643.     
  1644.     // }}}
  1645.     // {{{ _getTextResponse()
  1646.  
  1647.     /**
  1648.      * Retrieve textural data
  1649.      *
  1650.      * Get data until a line with only a '.' in it is read and return data.
  1651.      *
  1652.      * @return mixed (array) text response on success or (object) pear_error on failure
  1653.      * @access private
  1654.      */
  1655.     function _getTextResponse()
  1656.     {
  1657.         $data = array();
  1658.         $line '';
  1659.     
  1660.         // Continue until connection is lost
  1661.         while(!$this->_socket->eof()) {
  1662.  
  1663.             // Retrieve and append up to 1024 characters from the server.
  1664.             $line .= $this->_socket->gets(1024)
  1665.             if (PEAR::isError($line) ) {
  1666.                 return PEAR::throwError'Failed to read from socket!'null$line->getMessage());
  1667.             }
  1668.         
  1669.             // Continue if the line is not terminated by CRLF
  1670.             if (substr($line-2!= "\r\n" || strlen($line< 2{
  1671.                 continue;
  1672.             }
  1673.  
  1674.             // Validate recieved line
  1675.             if (false{
  1676.                 // Lines should/may not be longer than 998+2 chars (RFC2822 2.3)
  1677.                 if (strlen($line> 1000{
  1678.                     return PEAR::throwError('Invalid line recieved!'null);
  1679.                 }
  1680.             }
  1681.  
  1682.             // Remove CRLF from the end of the line
  1683.             $line substr($line0-2);
  1684.  
  1685.             // Check if the line terminates the textresponse
  1686.             if ($line == '.'{
  1687.                 // return all previous lines
  1688.                 return $data;
  1689.                 break;
  1690.             }
  1691.  
  1692.             // If 1st char is '.' it's doubled (NNTP/RFC977 2.4.1)
  1693.             if (substr($line02== '..'{
  1694.                 $line substr($line1);
  1695.             }
  1696.             
  1697.             // Add the line to the array of lines
  1698.             $data[$line;
  1699.  
  1700.             // Reset/empty $line
  1701.             $line '';
  1702.         }
  1703.  
  1704.         return PEAR::throwError('Data stream not terminated with period'null);
  1705.     }
  1706.  
  1707.     // }}}
  1708.     // {{{ _sendCommand()
  1709.  
  1710.     /**
  1711.      * Send command
  1712.      *
  1713.      * Send a command to the server. A carriage return / linefeed (CRLF) sequence
  1714.      * will be appended to each command string before it is sent to the IMAP server.
  1715.      *
  1716.      * @param string $cmd The command to launch, ie: "ARTICLE 1004853"
  1717.      *
  1718.      * @return mixed (int) response code on success or (object) pear_error on failure
  1719.      * @access private
  1720.      */
  1721.     function _sendCommand($cmd)
  1722.     {
  1723.         // NNTP/RFC977 only allows command up to 512 (-2) chars.
  1724.         if (!strlen($cmd> 510{
  1725.             return PEAR::throwError('Failed to write to socket! (Command to long - max 510 chars)');
  1726.         }
  1727.  
  1728.         // Check if connected
  1729.         if (!$this->isConnected()) {
  1730.             return PEAR::throwError('Failed to write to socket! (connection lost!)');
  1731.         }
  1732.  
  1733.         // Send the command
  1734.         $R $this->_socket->writeLine($cmd);
  1735.         if PEAR::isError($R) ) {
  1736.             return PEAR::throwError('Failed to write to socket!'null$R->getMessage());
  1737.         }
  1738.     
  1739.         if ($this->_debug{
  1740.             echo "C: $cmd\r\n";
  1741.         }
  1742.  
  1743.         return $this->_getStatusResponse();
  1744.     }
  1745.     
  1746.     // }}}
  1747.  
  1748. }
  1749.  
  1750. // }}}
  1751.  
  1752. ?>

Documentation generated on Mon, 11 Mar 2019 14:05:50 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.