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.1.4.3 2004/07/30 10:40:41 heino Exp $
  58.  
  59. require_once 'PEAR.php';
  60. require_once 'Net/Socket.php';
  61.  
  62. // {{{ constants
  63.  
  64. define('NET_NNTP_PROTOCOL_CLIENT_DEFAULT_HOST''localhost');
  65. define('NET_NNTP_PROTOCOL_CLIENT_DEFAULT_PORT''119');
  66.  
  67. // }}}
  68. // {{{ Net_NNTP_Protocol_Client
  69.  
  70. /**
  71.  * The Net_NNTP_Protocol_Client class implements the NNTP standard acording to
  72.  * RFX 977, RFC 2980, RFC 850/1036, and RFC 822/2822
  73.  *
  74.  * @category   Net
  75.  * @package    Net_NNTP
  76.  * @author     Heino H. Gehlsen <heino@gehlsen.dk>
  77.  * @version    $Id: Client.php,v 1.1.4.3 2004/07/30 10:40:41 heino Exp $
  78.  * @access     public
  79.  * @see        Net_NNTP_Client
  80.  * @since      Class available since Release 0.11.0
  81.  */
  82. {
  83.     // {{{ properties
  84.  
  85.     /**
  86.      * The socket resource being used to connect to the IMAP server.
  87.      *
  88.      * @var resource 
  89.      * @access private
  90.      */
  91.     var $_socket = null;
  92.  
  93.     /**
  94.      *
  95.      *
  96.      * @var resource 
  97.      * @access private
  98.      */
  99.     var $_currentStatusResponse = null;
  100.  
  101.     /**
  102.      * Whether to enable internal debug messages.
  103.      *
  104.      * @var     bool 
  105.      * @access  private
  106.      */
  107.     var $_debug = false;
  108.  
  109.     // }}}
  110.     // {{{ constructor
  111.         
  112.     /**
  113.      *
  114.      */
  115.     function Net_NNTP_Protocol_Client({
  116.     $this->_socket = new Net_Socket();
  117.     }
  118.  
  119.     // }}}
  120.     // {{{ Connect()
  121.  
  122.     /**
  123.      * Connect to the server
  124.      *
  125.      * @param optional string $host The adress of the NNTP-server to connect to, defaults to 'localhost'.
  126.      * @param optional int $port The port number to connect to, defaults to 119.
  127.      *
  128.      * @return mixed (bool) true on success or (object) pear_error on failure
  129.      * @access public
  130.      */
  131.     function connect($host = NET_NNTP_PROTOCOL_CLIENT_DEFAULT_HOST$port = NET_NNTP_PROTOCOL_CLIENT_DEFAULT_PORT)
  132.     {
  133.         if ($this->isConnected() ) {
  134.         return PEAR::throwError('Already connected, disconnect first!'null);
  135.     }
  136.  
  137.     // Open Connection
  138.     $R @$this->_socket->connect($host$portfalse15);
  139.     if (PEAR::isError($R)) {
  140.         return PEAR::throwError('Could not connect to the server'null$R->getMessage());
  141.     }
  142.  
  143.     // Retrive the server's initial response.
  144.     $response $this->_getStatusResponse();
  145.     if (PEAR::isError($response)) {
  146.         return $response;
  147.         }
  148.  
  149.         switch ($response{
  150.         case 200: // Posting allowed
  151.         // TODO: Set some variable
  152.             return true;
  153.             break;
  154.         case 201: // Posting NOT allowed
  155.         // TODO: Set some variable
  156.             return true;
  157.             break;
  158.         case 502: // 'access restriction or permission denied'
  159.         return PEAR::throwError('Server refused connection'$response$this->currentStatusResponse());
  160.         break;
  161.         default:
  162.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  163.     }
  164.     }
  165.  
  166.     // }}}
  167.     // {{{ disconnect()
  168.  
  169.     /**
  170.      * alias for cmdQuit()
  171.      *
  172.      * @access public
  173.      */
  174.     function disconnect()
  175.     {
  176.     return $this->cmdQuit();
  177.     }
  178.  
  179.     // }}}
  180.     // {{{ cmdQuit()
  181.  
  182.     /**
  183.      * Close connection to the server
  184.      *
  185.      * @access public
  186.      */
  187.     function cmdQuit()
  188.     {
  189.     // Tell the server to close the connection
  190.     $response $this->_sendCommand('QUIT');
  191.         if (PEAR::isError($response)) {
  192.             return $response;
  193.     }
  194.     
  195.         switch ($response{
  196.         case 205: // RFC977: 'closing connection - goodbye!'
  197.         // If socket is still open, close it.
  198.         if ($this->isConnected()) {
  199.                     $this->_socket->disconnect();
  200.         }
  201.         return true;
  202.         break;
  203.         default:
  204.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  205.     }
  206.  
  207.     }
  208.  
  209.     // }}}
  210.  
  211.     /**
  212.      * The authentication process i not yet standarized but used any way
  213.      * (http://www.mibsoftware.com/userkt/nntpext/index.html).
  214.      */
  215.      
  216.     // {{{ cmdAuthinfo()
  217.  
  218.     /**
  219.      * Authenticates the user using the original method
  220.      *
  221.      * @param string $user The username to authenticate as.
  222.      * @param string $pass The password to authenticate with.
  223.      *
  224.      * @return mixed (bool) true on success or (object) pear_error on failure
  225.      * @access private
  226.      */
  227.     function cmdAuthinfo($user$pass)
  228.     {
  229.     // Send the username
  230.         $response $this->_sendCommand('AUTHINFO user '.$user);
  231.         if (PEAR::isError($response)) {
  232.             return $response;
  233.     }
  234.  
  235.     // Send the password, if the server asks
  236.     if (($response == 381&& ($pass !== null)) {
  237.         // Send the password
  238.             $response $this->_sendCommand('AUTHINFO pass '.$pass);
  239.         if (PEAR::isError($response)) {
  240.             return $response;
  241.         }
  242.     }
  243.  
  244.         switch ($response{
  245.         case 281: // RFC2980: 'Authentication accepted'
  246.             return true;
  247.             break;
  248.         case 381: // RFC2980: 'More authentication information required'
  249.             return PEAR::throwError('Authentication uncompleted'$response$this->currentStatusResponse());
  250.             break;
  251.         case 482: // RFC2980: 'Authentication rejected'
  252.         return PEAR::throwError('Authentication rejected'$response$this->currentStatusResponse());
  253.         break;
  254.         case 502: // RFC2980: 'No permission'
  255.         return PEAR::throwError('Authentication rejected'$response$this->currentStatusResponse());
  256.         break;
  257. //        case 500:
  258. //        case 501:
  259. //            return PEAR::throwError('Authentication failed', $response, $this->currentStatusResponse());
  260. //            break;
  261.         default:
  262.         return PEAR::throwError('Unexpected authentication error!'$response$this->currentStatusResponse());
  263.     }
  264.     }
  265.     
  266.     // }}}
  267.     // {{{ cmdAuthinfoSimple()
  268.  
  269.     /**
  270.      * Authenticates the user using the simple method
  271.      *
  272.      * @param string $user The username to authenticate as.
  273.      * @param string $pass The password to authenticate with.
  274.      *
  275.      * @return mixed (bool) true on success or (object) pear_error on failure
  276.      * @access private
  277.      */
  278.     function cmdAuthinfoSimple($user$pass)
  279.     {
  280.         return PEAR::throwError("The auth mode: 'simple' is has not been implemented yet"null);
  281.     }
  282.     
  283.     // }}}
  284.     // {{{ cmdAuthinfoGeneric()
  285.  
  286.     /**
  287.      * Authenticates the user using the simple method
  288.      *
  289.      * @param string $user The username to authenticate as.
  290.      * @param string $pass The password to authenticate with.
  291.      *
  292.      * @return mixed (bool) true on success or (object) pear_error on failure
  293.      * @access private
  294.      */
  295.     function cmdAuthinfoGeneric($user$pass)
  296.     {
  297.         return PEAR::throwError("The auth mode: 'generic' is has not been implemented yet"null);
  298.     }
  299.     
  300.     // }}}
  301.     // {{{ cmdModeReader()
  302.  
  303.     /**
  304.      *
  305.      * @return mixed (bool) true when one can post on success or (object) pear_error on failure
  306.      * @access public
  307.      */
  308.     function cmdModeReader()
  309.     {
  310.         // tell the newsserver we want an article
  311.         $response $this->_sendCommand('MODE READER');
  312.         if (PEAR::isError($response)) {
  313.             return $response;
  314.         }
  315.     
  316.     switch ($response{
  317.             case 200: // RFC2980: 'Hello, you can post'
  318.             break;
  319.         case 201: // RFC2980: 'Hello, you can't post'
  320.         break;
  321.         default:
  322.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  323.     }
  324.  
  325.     }
  326.  
  327.     // }}}
  328.     // {{{ cmdArticle()
  329.  
  330.     /**
  331.      * Get an article from the currently open connection.
  332.      *
  333.      * @param mixed $article Either a message-id or a message-number of the article to fetch. If null or '', then use current article.
  334.      *
  335.      * @return mixed (array) article on success or (object) pear_error on failure
  336.      * @access public
  337.      */
  338.     function cmdArticle($article)
  339.     {
  340.         // tell the newsserver we want an article
  341.         $response $this->_sendCommand('ARTICLE '.$article);
  342.         if (PEAR::isError($response)) {
  343.             return $response;
  344.         }
  345.     
  346.     switch ($response{
  347.         case 220: // RFC977: 'n <a> article retrieved - head and body follow (n = article number, <a> = message-id)'
  348.         case 221: // RFC977: 'n <a> article retrieved - head follows'
  349.         case 222: // RFC977: 'n <a> article retrieved - body follows'
  350.         case 223: // RFC977: 'n <a> article retrieved - request text separately'
  351.         $data $this->_getTextResponse();
  352.             if (PEAR::isError($data)) {
  353.                     return $data;
  354.             }
  355.         return $data;
  356.         break;
  357.         case 412: // RFC977: 'no newsgroup has been selected'
  358.         return PEAR::throwError('No newsgroup has been selected'$response$this->currentStatusResponse());
  359.         break;
  360.         case 420: // RFC977: 'no current article has been selected'
  361.         return PEAR::throwError('No current article has been selected'$response$this->currentStatusResponse());
  362.         break;
  363.         case 423: // RFC977: 'no such article number in this group'
  364.         return PEAR::throwError('No such article number in this group'$response$this->currentStatusResponse());
  365.         break;
  366.         case 430: // RFC977: 'no such article found'
  367.         return PEAR::throwError('No such article found'$response$this->currentStatusResponse());
  368.         break;
  369.         default:
  370.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  371.     }
  372.  
  373.     }
  374.  
  375.     // }}}
  376.     // {{{ cmdHead()
  377.  
  378.     /**
  379.      * Get the headers of an article from the currently open connection.
  380.      *
  381.      * @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.
  382.      *
  383.      * @return mixed (array) headers on success or (object) pear_error on failure
  384.      * @access public
  385.      */
  386.     function cmdHead($article)
  387.     {
  388.         // tell the newsserver we want the header of an article
  389.         $response $this->_sendCommand('HEAD '.$article);
  390.         if (PEAR::isError($response)) {
  391.             return $response;
  392.         }
  393.  
  394.     switch ($response{
  395.         case 220: // RFC977: 'n <a> article retrieved - head and body follow (n = article number, <a> = message-id)'
  396.         case 221: // RFC977: 'n <a> article retrieved - head follows'
  397.         case 222: // RFC977: 'n <a> article retrieved - body follows'
  398.         case 223: // RFC977: 'n <a> article retrieved - request text separately'
  399.         $data $this->_getTextResponse();
  400.             if (PEAR::isError($data)) {
  401.                 return $data;
  402.             }
  403.             return $data;
  404.         break;
  405.         case 412: // RFC977: 'no newsgroup has been selected'
  406.         return PEAR::throwError('No newsgroup has been selected'$response$this->currentStatusResponse());
  407.         break;
  408.         case 420: // RFC977: 'no current article has been selected'
  409.         return PEAR::throwError('No current article has been selected'$response$this->currentStatusResponse());
  410.         break;
  411.         case 423: // RFC977: 'no such article number in this group'
  412.         return PEAR::throwError('No such article number in this group'$response$this->currentStatusResponse());
  413.         break;
  414.         case 430: // RFC977: 'no such article found'
  415.         return PEAR::throwError('No such article found'$response$this->currentStatusResponse());
  416.         break;
  417.         default:
  418.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  419.     }
  420.     }
  421.  
  422.     // }}}
  423.     // {{{ cmdBody()
  424.  
  425.     /**
  426.      * Get the body of an article from the currently open connection.
  427.      *
  428.      * @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.
  429.      *
  430.      * @return mixed (array) body on success or (object) pear_error on failure
  431.      * @access public
  432.      */
  433.     function cmdBody($article)
  434.     {
  435.         // tell the newsserver we want the body of an article
  436.         $response $this->_sendCommand('BODY '.$article);
  437.         if (PEAR::isError($response)) {
  438.             return $response;
  439.         }
  440.  
  441.     switch ($response{
  442.         case 220: // RFC977: 'n <a> article retrieved - head and body follow (n = article number, <a> = message-id)'
  443.         case 221: // RFC977: 'n <a> article retrieved - head follows'
  444.         case 222: // RFC977: 'n <a> article retrieved - body follows'
  445.         case 223: // RFC977: 'n <a> article retrieved - request text separately'
  446.         $data $this->_getTextResponse();
  447.             if (PEAR::isError($data)) {
  448.                 return $data;
  449.             }
  450.             return $data;
  451.         break;
  452.         case 412: // RFC977: 'no newsgroup has been selected'
  453.         return PEAR::throwError('No newsgroup has been selected'$response$this->currentStatusResponse());
  454.         break;
  455.         case 420: // RFC977: 'no current article has been selected'
  456.         return PEAR::throwError('No current article has been selected'$response$this->currentStatusResponse());
  457.         break;
  458.         case 423: // RFC977: 'no such article number in this group'
  459.         return PEAR::throwError('No such article number in this group'$response$this->currentStatusResponse());
  460.         break;
  461.         case 430: // RFC977: 'no such article found'
  462.         return PEAR::throwError('No such article found'$response$this->currentStatusResponse());
  463.         break;
  464.         default:
  465.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  466.     }
  467.     }
  468.  
  469.     // }}}
  470.     // {{{ cmdPost()
  471.  
  472.     /**
  473.      * Post an article to a newsgroup.
  474.      *
  475.      * Among the aditional headers you might think of adding could be:
  476.      * "NNTP-Posting-Host: <ip-of-author>", which should contain the IP-adress
  477.      * of the author of the post, so the message can be traced back to him.
  478.      * "Organization: <org>" which contain the name of the organization
  479.      * the post originates from.
  480.      *
  481.      * @param string $newsgroup The newsgroup to post to.
  482.      * @param string $subject The subject of the post.
  483.      * @param string $body The body of the post itself.
  484.      * @param string $from Name + email-adress of sender.
  485.      * @param optional string $aditional Aditional headers to send.
  486.      *
  487.      * @return mixed (bool) true on success or (object) pear_error on failure
  488.      * @access public
  489.      */
  490.     function cmdPost($newsgroup$subject$body$from$aditional '')
  491.     {
  492.         // tell the newsserver we want to post an article
  493.     $response $this->_sendCommand('POST');
  494.     if (PEAR::isError($response)) {
  495.         return $response;
  496.         }
  497.  
  498.     if ($response == 340// RFC977: 'send article to be posted. End with <CR-LF>.<CR-LF>'
  499.  
  500.         // should be presented in the format specified by RFC850
  501.         
  502.             $this->_socket->write("Newsgroups: $newsgroup\r\n");
  503.             $this->_socket->write("Subject: $subject\r\n");
  504.             $this->_socket->write("From: $from\r\n");
  505.             $this->_socket->write("X-poster: PEAR::Net_NNTP\r\n");
  506.             $this->_socket->write("$aditional\r\n");
  507.             $this->_socket->write("\r\n");
  508.             $this->_socket->write("$body\r\n");
  509.             $this->_socket->write(".\r\n");
  510.  
  511.         // Retrive server's response.
  512.         $response $this->_getStatusResponse();
  513.         if (PEAR::isError($response)) {
  514.         return $response;
  515.             }
  516.     }
  517.  
  518.     switch ($response{
  519.             case 240: // RFC977: 'article posted ok'
  520.         return true;
  521.         break;
  522.             case 340: // RFC977: 'send article to be posted. End with <CR-LF>.<CR-LF>'
  523.         // This should not happen here!
  524.         return PEAR::throwError('Unknown error during post'$response$this->currentStatusResponse());
  525.         break;
  526.             case 440: // RFC977: 'posting not allowed'
  527.         return PEAR::throwError('Posting not allowed'$response$this->currentStatusResponse());
  528.         break;
  529.             case 441: // RFC977: 'posting failed'
  530.         return PEAR::throwError('Posting failed'$response$this->currentStatusResponse());
  531.         break;
  532.         default:
  533.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  534.     }
  535.     }
  536.  
  537.     // }}}
  538.     // {{{ cmdGroup()
  539.  
  540.     /**
  541.      * Selects a news group (issue a GROUP command to the server)
  542.      *
  543.      * @param string $newsgroup The newsgroup name
  544.      *
  545.      * @return mixed (array) groupinfo on success or (object) pear_error on failure
  546.      * @access public
  547.      */
  548.     function cmdGroup($newsgroup)
  549.     {
  550.         $response $this->_sendCommand('GROUP '.$newsgroup);
  551.         if (PEAR::isError($response)) {
  552.             return $response;
  553.         }
  554.  
  555.     switch ($response{
  556.             case 211: // RFC977: 'n f l s group selected'
  557.             $response_arr split(' 'trim($this->currentStatusResponse()));
  558.             $response_arr['count'=$response_arr[0];
  559.             $response_arr['first'=$response_arr[1];
  560.         $response_arr['last']  =$response_arr[2];
  561.         $response_arr['group'=$response_arr[3];
  562.         return $response_arr;
  563.         break;
  564.         case 411: // RFC977: 'no such news group'
  565.         return PEAR::throwError('No such news group'$response$this->currentStatusResponse());
  566.         break;
  567.         default:
  568.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  569.     }
  570.     
  571.     }
  572.  
  573.     // }}}
  574.     // {{{ cmdList()
  575.  
  576.     /**
  577.      * Fetches a list of all avaible newsgroups
  578.      *
  579.      * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure
  580.      * @access public
  581.      */
  582.     function cmdList()
  583.     {
  584.         $response $this->_sendCommand('LIST');
  585.         if (PEAR::isError($response)){
  586.             return $response;
  587.         }
  588.  
  589.     switch ($response{
  590.             case 215: // RFC977: 'list of newsgroups follows'
  591.         $data $this->_getTextResponse();
  592.             if (PEAR::isError($data)) {
  593.                 return $data;
  594.             }
  595.             foreach($data as $line{
  596.                 $arr explode(' 'trim($line));
  597.                 $arr['group']    =$arr[0];
  598.                 $arr['last']     =$arr[1];
  599.                 $arr['first']    =$arr[2];
  600.                 $arr['posting' = (bool) ($arr[3== 'y');
  601.                 $groups[$arr[0]] $arr;
  602.             }
  603.             return $groups;
  604.         break;
  605.         default:
  606.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  607.     }
  608.     }
  609.  
  610.     // }}}
  611.     // {{{ cmdListNewsgroups()
  612.  
  613.     /**
  614.      * Fetches a list of (all) avaible newsgroup descriptions.
  615.      *
  616.      * @param string $wildmat Wildmat of the groups, that is to be listed, defaults to '';
  617.      *
  618.      * @return mixed (array) nested array with description of existing newsgroups on success or (object) pear_error on failure
  619.      * @access public
  620.      */
  621.     function cmdListNewsgroups($wildmat '')
  622.     {
  623.         if (empty($wildmat)) {
  624.             $command 'LIST NEWSGROUPS';
  625.         else {
  626.             $command 'LIST NEWSGROUPS '.$wildmat;
  627.         }
  628.         $response $this->_sendCommand($command);
  629.         if (PEAR::isError($response)){
  630.             return $response;
  631.         }
  632.  
  633.     switch ($response{
  634.         case 215: // RFC2980: 'information follows'
  635.         $data $this->_getTextResponse();
  636.             if (PEAR::isError($data)) {
  637.                 return $data;
  638.             }
  639.  
  640.             foreach($data as $line{
  641.                 preg_match("/^(.*?)\s(.*?$)/"trim($line)$matches);
  642.                 $groups[$matches[1]] = (string) $matches[2];
  643.             }
  644.  
  645.             return $groups;
  646.         break;
  647.         case 503: // RFC2980: 'program error, function not performed'
  648.         return PEAR::throwError('Internal server error, function not performed'$response$this->currentStatusResponse());
  649.         break;
  650.         default:
  651.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  652.     }
  653.  
  654.     }
  655.  
  656.     // }}}
  657.     /**
  658.      * Fetches a list of (all) avaible newsgroup descriptions.
  659.      * Depresated as of RFC2980.
  660.      *
  661.      * @param string $wildmat Wildmat of the groups, that is to be listed, defaults to '*';
  662.      *
  663.      * @return mixed (array) nested array with description of existing newsgroups on success or (object) pear_error on failure
  664.      * @access public
  665.      */
  666.     function cmdXGTitle($wildmat '*')
  667.     {
  668.         $response $this->_sendCommand('XGTITLE '.$wildmat);
  669.         if (PEAR::isError($response)){
  670.             return $response;
  671.         }
  672.  
  673.     switch ($response{
  674.         case 282: // RFC2980: 'list of groups and descriptions follows'
  675.         $data $this->_getTextResponse();
  676.             if (PEAR::isError($data)) {
  677.                 return $data;
  678.             }
  679.  
  680.             foreach($data as $line{
  681.                 preg_match("/^(.*?)\s(.*?$)/"trim($line)$matches);
  682.                 $groups[$matches[1]] = (string) $matches[2];
  683.             }
  684.  
  685.             return $groups;
  686.         break;
  687.           
  688.         case 481: // RFC2980: 'Groups and descriptions unavailable'
  689.         return PEAR::throwError('Groups and descriptions unavailable'$response$this->currentStatusResponse());
  690.         break;
  691.         default:
  692.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  693.     }
  694.  
  695.     }
  696.  
  697.     // }}}
  698.     // {{{ cmdNewgroups()
  699.  
  700.     /**
  701.      * Fetches a list of all newsgroups created since a specified date.
  702.      *
  703.      * @param int $time Last time you checked for groups (timestamp).
  704.      *
  705.      * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure
  706.      * @access public
  707.      */
  708.     function cmdNewgroups($time)
  709.     {
  710.         $response $this->_sendCommand('NEWGROUPS '.date('ymd His'$time));
  711.         if (PEAR::isError($response)){
  712.             return $response;
  713.         }
  714.  
  715.     switch ($response{
  716.         case 231: // REF977: 'list of new newsgroups follows'
  717.         $groups = array();
  718.             foreach($this->_getTextResponse(as $line{
  719.             $arr explode(' '$line);
  720.             $groups[$arr[0]]['group'$arr[0];
  721.             $groups[$arr[0]]['last'$arr[1];
  722.             $groups[$arr[0]]['first'$arr[2];
  723.             $groups[$arr[0]]['posting'$arr[3];
  724.         }
  725.         return $groups;
  726.         break;
  727.         default:
  728.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  729.     }
  730.     }
  731.  
  732.     // }}}
  733.     // {{{ cmdListOverviewFmt()
  734.  
  735.     /**
  736.      * Returns a list of avaible headers which are send from newsserver to client for every news message
  737.      *
  738.      * @return mixed (array) of header names on success or (object) pear_error on failure
  739.      * @access public
  740.      */
  741.     function cmdListOverviewFmt()
  742.     {
  743.     $response $this->_sendCommand('LIST OVERVIEW.FMT');
  744.         if (PEAR::isError($response)){
  745.             return $response;
  746.         }
  747.  
  748.     switch ($response{
  749.         case 215: // RFC2980: 'information follows'
  750.         $data $this->_getTextResponse();
  751.             if (PEAR::isError($data)) {
  752.                 return $data;
  753.             }
  754.  
  755.             $format = array('number');
  756.             // XXX Use the splitHeaders() algorithm for supporting
  757.             //     multiline headers?
  758.             foreach ($data as $line{
  759.                 $line current(explode(':'trim($line)));
  760.                 $format[$line;
  761.             }
  762.             return $format;
  763.         break;
  764.         case 503: // RFC2980: 'program error, function not performed'
  765.         return PEAR::throwError('Internal server error, function not performed'$response$this->currentStatusResponse());
  766.         break;
  767.         default:
  768.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  769.     }
  770.     }
  771.  
  772.     // }}}
  773.     // {{{ cmdXOver()
  774.  
  775.     /**
  776.      * Fetch message header from message number $first until $last
  777.      *
  778.      * The format of the returned array is:
  779.      * $messages[message_id][header_name]
  780.      *
  781.      * @param integer $first first article to fetch
  782.      * @param integer $last  last article to fetch
  783.      *
  784.      * @return mixed (array) nested array of message and there headers on success or (object) pear_error on failure
  785.      * @access public
  786.      */
  787.     function cmdXOver($first$last)
  788.     {
  789.         $format $this->cmdListOverviewFmt();
  790.         if (PEAR::isError($format)){
  791.             return $formt;
  792.         }
  793.  
  794.         $response $this->_sendCommand('XOVER '.$first.'-'.$last);
  795.         if (PEAR::isError($response)){
  796.             return $response;
  797.         }
  798.  
  799.     switch ($response{
  800.         case 224: // RFC2980: 'Overview information follows'
  801.         $data $this->_getTextResponse();
  802.             if (PEAR::isError($data)) {
  803.                 return $data;
  804.             }
  805.         $messages = array();
  806.             foreach($data as $line{
  807.                 $i=0;
  808.                 foreach(explode("\t"trim($line)) as $line{
  809.                     $message[$format[$i++]] $line;
  810.                 }
  811.                 $messages[$message['Message-ID']] $message;
  812.             }
  813.             return $messages;
  814.         break;
  815.         case 412: // RFC2980: 'No news group current selected'
  816.         return PEAR::throwError('No news group current selected'$response$this->currentStatusResponse());
  817.         break;
  818.         case 420: // RFC2980: 'No article(s) selected'
  819.         return PEAR::throwError('No article(s) selected'$response$this->currentStatusResponse());
  820.         break;
  821.         case 502: // RFC2980: 'no permission'
  822.         return PEAR::throwError('No permission'$response$this->currentStatusResponse());
  823.         break;
  824.         default:
  825.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  826.     }
  827.     }
  828.     
  829.     // }}}
  830.     // {{{ cmdXROver()
  831.  
  832.     /**
  833.      * Fetch message references from message number $first to $last
  834.      *
  835.      * @param integer $first first article to fetch
  836.      * @param integer $last  last article to fetch
  837.      *
  838.      * @return mixed (array) assoc. array of message references on success or (object) pear_error on failure
  839.      * @access public
  840.      */
  841.     function cmdXROver($first$last)
  842.     {
  843.         $response $this->_sendCommand('XROVER '.$first.'-'.$last);
  844.         if (PEAR::isError($response)){
  845.             return $response;
  846.         }
  847.  
  848.     switch ($response{
  849.         case 224: // RFC2980: 'Overview information follows'
  850.         $data $this->_getTextResponse();
  851.             if (PEAR::isError($data)) {
  852.                 return $data;
  853.             }
  854.  
  855.             foreach($data as $line{
  856.  
  857.                 $references preg_split("/ +/"trim($line)-1PREG_SPLIT_NO_EMPTY);
  858.  
  859.                 $id array_shift($references);
  860.  
  861.                 $messages[$id$references;
  862.             }
  863.             return $messages;
  864.         break;
  865.         case 412: // RFC2980: 'No news group current selected'
  866.         return PEAR::throwError('No news group current selected'$response$this->currentStatusResponse());
  867.         break;
  868.         case 420: // RFC2980: 'No article(s) selected'
  869.         return PEAR::throwError('No article(s) selected'$response$this->currentStatusResponse());
  870.         break;
  871.         case 502: // RFC2980: 'no permission'
  872.         return PEAR::throwError('No permission'$response$this->currentStatusResponse());
  873.         break;
  874.         default:
  875.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  876.     }
  877.     }
  878.  
  879.     // }}}
  880.     // {{{ cmdListgroup()
  881.  
  882.     /**
  883.      *
  884.      * @param string $newsgroup 
  885.      *
  886.      * @return mixed (array) on success or (object) pear_error on failure
  887.      */
  888.     function cmdListgroup($newsgroup)
  889.     {
  890.         $response $this->_sendCommand('LISTGROUP '.$newsgroup);
  891.         if (PEAR::isError($response)){
  892.             return $response;
  893.         }
  894.  
  895.     switch ($response{
  896.         case 211: // RFC2980: 'list of article numbers follow'
  897.         $data $this->_getTextResponse();
  898.             if (PEAR::isError($data)) {
  899.                 return $data;
  900.             }
  901.             return $data;
  902.         break;
  903.         case 412: // RFC2980: 'Not currently in newsgroup'
  904.         return PEAR::throwError('Not currently in newsgroup'$response$this->currentStatusResponse());
  905.         break;
  906.         case 502: // RFC2980: 'no permission'
  907.         return PEAR::throwError('No permission'$response$this->currentStatusResponse());
  908.         break;
  909.         default:
  910.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  911.     }
  912.     }
  913.  
  914.     // }}}
  915.     // {{{ cmdNewnews()
  916.  
  917.     /**
  918.      *
  919.      */
  920.     function cmdNewnews($time$newsgroups '*')
  921.     {
  922.     // TODO: the lenght of the request string may not exceed 510 chars
  923.     
  924.         $response $this->_sendCommand('NEWNEWS '.$newsgroups.' '.date('ymd His'$time));
  925.         if (PEAR::isError($response)){
  926.             return $response;
  927.         }
  928.  
  929.     switch ($response{
  930.         case 230: // RFC977: 'list of new articles by message-id follows'
  931.         $messages = array();
  932.             foreach($this->_getTextResponse(as $line{
  933.             $messages[$line;
  934.         }
  935.         return $messages;
  936.         break;
  937.         default:
  938.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  939.     }
  940.     }
  941.  
  942.     // }}}
  943.     // {{{ cmdDate()
  944.  
  945.     /**
  946.      * Get the date from the newsserver format of returned date
  947.      *
  948.      * @param bool $timestap when false function returns string, and when true function returns int/timestamp.
  949.      *
  950.      * @return mixed (string) 'YYYYMMDDhhmmss' / (int) timestamp on success or (object) pear_error on failure
  951.      * @access public
  952.      */
  953.     function cmdDate($timestamp = false)
  954.     {
  955.         $response $this->_sendCommand('DATE');
  956.         if (PEAR::isError($response)){
  957.             return $response;
  958.         }
  959.  
  960.     switch ($response{
  961.         case 111: // RFC2980: 'YYYYMMDDhhmmss'
  962.                 $d $this->currentStatusResponse();
  963.         if ($timestamp === false{
  964.             return (string) $d;        
  965.         else {
  966.                 return (int) strtotime(substr($d08).' '.$d[8].$d[9].':'.$d[10].$d[11].':'.$d[12].$d[13]);
  967.         }
  968.         break;
  969.         default:
  970.         return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  971.     }
  972.     }
  973.     // }}}
  974.     // {{{ isConnected()
  975.  
  976.     /**
  977.      * Test whether we are connected or not.
  978.      *
  979.      * @return bool true or false
  980.      *
  981.      * @access public
  982.      */
  983.     function isConnected()
  984.     {
  985.     return (is_resource($this->_socket->fp&& (!$this->_socket->eof()));
  986.     }
  987.  
  988.     // }}}
  989.     // {{{ setDebug()
  990.  
  991.     /**
  992.      * Sets the debuging information on or off
  993.      *
  994.      * @param boolean True or false
  995.      *
  996.      * @return bool previos state
  997.      * @access public
  998.      */
  999.     function setDebug($debug = true)
  1000.     {
  1001.         $tmp $this->_debug;
  1002.         $this->_debug $debug;
  1003.         return $tmp;
  1004.     }
  1005.  
  1006.     // }}}
  1007.     // {{{ _getStatusResponse()
  1008.  
  1009.     /**
  1010.      * Get servers statusresponse after a command.
  1011.      *
  1012.      * @return mixed (int) statuscode on success or (object) pear_error on failure
  1013.      * @access private
  1014.      */
  1015.     function _getStatusResponse()
  1016.     {
  1017.     // Retrieve a line (terminated by "\r\n") from the server.
  1018.     $response $this->_socket->gets(256);
  1019.         if (PEAR::isError($response) ) {
  1020.             return PEAR::throwError('Failed to read from socket!'null$response->getMessage());
  1021.         }
  1022.  
  1023.         if ($this->_debug{
  1024.             echo "S: $response\r\n";
  1025.         }
  1026.  
  1027.     // Trim the start of the response in case of misplased whitespace (should not be needen!!!)
  1028.     $response ltrim($response);
  1029.  
  1030.         $this->_currentStatusResponse = array(
  1031.                           (int) substr($response03),
  1032.                                           (string) rtrim(substr($response4))
  1033.                          );
  1034.  
  1035.     return $this->_currentStatusResponse[0];
  1036.     }
  1037.     
  1038.     // }}}
  1039.     // {{{ currentStatusResponse()
  1040.  
  1041.     /**
  1042.      *
  1043.      *
  1044.      * @return string status text
  1045.      * @access private
  1046.      */
  1047.     function currentStatusResponse()
  1048.     {
  1049.     return $this->_currentStatusResponse[1];
  1050.     }
  1051.     
  1052.     // }}}
  1053.     // {{{ _getTextResponse()
  1054.  
  1055.     /**
  1056.      * Get data until a line with only a '.' in it is read and return data.
  1057.      *
  1058.      * @return mixed (array) text response on success or (object) pear_error on failure
  1059.      * @access private
  1060.      */
  1061.     function _getTextResponse()
  1062.     {
  1063.         $data = array();
  1064.         $line '';
  1065.     
  1066.         // Continue until connection is lost
  1067.         while(!$this->_socket->eof()) {
  1068.  
  1069.             // Retrieve and append up to 1024 characters from the server.
  1070.             $line .= $this->_socket->gets(1024)
  1071.             if (PEAR::isError($line) ) {
  1072.                 return PEAR::throwError'Failed to read from socket!'null$line->getMessage());
  1073.             }
  1074.         
  1075.             // Continue if the line is not terminated by CRLF
  1076.             if (substr($line-2!= "\r\n" || strlen($line< 2{
  1077.                 continue;
  1078.             }
  1079.  
  1080.             // Validate recieved line
  1081.             if (false{
  1082.                 // Lines should/may not be longer than 998+2 chars (RFC2822 2.3)
  1083.                 if (strlen($line> 1000{
  1084.                     return PEAR::throwError('Invalid line recieved!'null);
  1085.                 }
  1086.             }
  1087.  
  1088.             // Remove CRLF from the end of the line
  1089.             $line substr($line0-2);
  1090.  
  1091.             // Check if the line terminates the textresponse
  1092.             if ($line == '.'{
  1093.                 // return all previous lines
  1094.                 return $data;
  1095.                 break;
  1096.             }
  1097.  
  1098.             // If 1st char is '.' it's doubled (NNTP/RFC977 2.4.1)
  1099.             if (substr($line02== '..'{
  1100.                 $line substr($line1);
  1101.             }
  1102.             
  1103.             // Add the line to the array of lines
  1104.             $data[$line;
  1105.  
  1106.             // Reset/empty $line
  1107.             $line '';
  1108.         }
  1109.  
  1110.         return PEAR::throwError('Data stream not terminated with period'null);
  1111.     }
  1112.  
  1113.     // }}}
  1114.     // {{{ _sendCommand()
  1115.  
  1116.     /**
  1117.      * Send a command to the server. A carriage return / linefeed (CRLF) sequence
  1118.      * will be appended to each command string before it is sent to the IMAP server.
  1119.      *
  1120.      * @param string $cmd The command to launch, ie: "ARTICLE 1004853"
  1121.      *
  1122.      * @return mixed (int) response code on success or (object) pear_error on failure
  1123.      * @access private
  1124.      */
  1125.     function _sendCommand($cmd)
  1126.     {
  1127.         // NNTP/RFC977 only allows command up to 512 (-2) chars.
  1128.         if (!strlen($cmd> 510{
  1129.             return PEAR::throwError('Failed to write to socket! (Command to long - max 510 chars)');
  1130.         }
  1131.  
  1132.         // Check if connected
  1133.     if (!$this->isConnected()) {
  1134.             return PEAR::throwError('Failed to write to socket! (connection lost!)');
  1135.         }
  1136.  
  1137.     // Send the command
  1138.     $R $this->_socket->writeLine($cmd);
  1139.         if PEAR::isError($R) ) {
  1140.             return PEAR::throwError('Failed to write to socket!'null$R->getMessage());
  1141.         }
  1142.     
  1143.         if ($this->_debug{
  1144.             echo "C: $cmd\r\n";
  1145.         }
  1146.  
  1147.     return $this->_getStatusResponse();
  1148.     }
  1149.     
  1150.     // }}}
  1151.  
  1152. }
  1153.  
  1154. // }}}
  1155.  
  1156. ?>

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