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

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