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

Source for file Client.php

Documentation is available at Client.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
  3. // +-----------------------------------------------------------------------+
  4. // |                                                                       |
  5. // |                  http://www.heino.gehlsen.dk/software/license         |
  6. // |                                                                       |
  7. // +-----------------------------------------------------------------------+
  8. // |                                                                       |
  9. // | This work (including software, documents, or other related items) is  |
  10. // | being provided by the copyright holders under the following license.  |
  11. // | By obtaining, using and/or copying this work, you (the licensee)      |
  12. // | agree that you have read, understood, and will comply with the        |
  13. // | following terms and conditions:                                       |
  14. // |                                                                       |
  15. // | Permission to use, copy, modify, and distribute this software and     |
  16. // | its documentation, with or without modification, for any purpose and  |
  17. // | without fee or royalty is hereby granted, provided that you include   |
  18. // | the following on ALL copies of the software and documentation or      |
  19. // | portions thereof, including modifications, that you make:             |
  20. // |                                                                       |
  21. // | 1. The full text of this NOTICE in a location viewable to users of    |
  22. // |    the redistributed or derivative work.                              |
  23. // |                                                                       |
  24. // | 2. Any pre-existing intellectual property disclaimers, notices, or    |
  25. // |    terms and conditions. If none exist, a short notice of the         |
  26. // |    following form (hypertext is preferred, text is permitted) should  |
  27. // |    be used within the body of any redistributed or derivative code:   |
  28. // |     http://www.heino.gehlsen.dk/software/license"                     |
  29. // |                                                                       |
  30. // | 3. Notice of any changes or modifications to the files, including     |
  31. // |    the date changes were made. (We recommend you provide URIs to      |
  32. // |    the location from which the code is derived.)                      |
  33. // |                                                                       |
  34. // | THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT    |
  35. // | HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED,    |
  36. // | INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR        |
  37. // | FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE    |
  38. // | OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS,           |
  39. // | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.                               |
  40. // |                                                                       |
  41. // | COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT,        |
  42. // | SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE        |
  43. // | SOFTWARE OR DOCUMENTATION.                                            |
  44. // |                                                                       |
  45. // | The name and trademarks of copyright holders may NOT be used in       |
  46. // | advertising or publicity pertaining to the software without specific, |
  47. // | written prior permission. Title to copyright in this software and any |
  48. // | associated documentation will at all times remain with copyright      |
  49. // | holders.                                                              |
  50. // |                                                                       |
  51. // +-----------------------------------------------------------------------+
  52. // |                                                                       |
  53. // | except for the references to the copyright holder, which has either   |
  54. // | been changes or removed.                                              |
  55. // |                                                                       |
  56. // +-----------------------------------------------------------------------+
  57. // $Id: Client.php,v 1.3 2005/01/14 15:29:08 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.3 2005/01/14 15:29:08 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.  
  556.                 $data = array();
  557.                 $data['count'$response_arr[0];
  558.                 $data['first'$response_arr[1];
  559.                 $data['last']  $response_arr[2];
  560.                 $data['group'$response_arr[3];
  561.  
  562.                 return $data;
  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.     // {{{ cmdList()
  574.  
  575.     /**
  576.      * Fetches a list of all avaible newsgroups
  577.      *
  578.      * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure
  579.      * @access public
  580.      */
  581.     function cmdList()
  582.     {
  583.         $response $this->_sendCommand('LIST');
  584.         if (PEAR::isError($response)){
  585.             return $response;
  586.         }
  587.  
  588.         switch ($response{
  589.             case 215: // RFC977: 'list of newsgroups follows'
  590.                 $data $this->_getTextResponse();
  591.                 if (PEAR::isError($data)) {
  592.                     return $data;
  593.                 }
  594.                 foreach($data as $line{
  595.                     $arr explode(' 'trim($line));
  596.  
  597.                     $group = array();
  598.                     $group['group']    $arr[0];
  599.                     $group['last']     $arr[1];
  600.                     $group['first']    $arr[2];
  601.                     $group['posting' $arr[3];
  602.  
  603.                     $groups[$group['group']] $group;
  604.                 }
  605.                 return $groups;
  606.                 break;
  607.             default:
  608.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  609.         }
  610.     }
  611.  
  612.     // }}}
  613.     // {{{ cmdListNewsgroups()
  614.  
  615.     /**
  616.      * Fetches a list of (all) avaible newsgroup descriptions.
  617.      *
  618.      * @param string $wildmat Wildmat of the groups, that is to be listed, defaults to '';
  619.      *
  620.      * @return mixed (array) nested array with description of existing newsgroups on success or (object) pear_error on failure
  621.      * @access public
  622.      */
  623.     function cmdListNewsgroups($wildmat '')
  624.     {
  625.         if (empty($wildmat)) {
  626.             $command 'LIST NEWSGROUPS';
  627.         else {
  628.             $command 'LIST NEWSGROUPS '.$wildmat;
  629.         }
  630.         $response $this->_sendCommand($command);
  631.         if (PEAR::isError($response)){
  632.             return $response;
  633.         }
  634.  
  635.         switch ($response{
  636.             case 215: // RFC2980: 'information follows'
  637.                 $data $this->_getTextResponse();
  638.                 if (PEAR::isError($data)) {
  639.                     return $data;
  640.                 }
  641.  
  642.                 foreach($data as $line{
  643.                     preg_match("/^(.*?)\s(.*?$)/"trim($line)$matches);
  644.                     $groups[$matches[1]] = (string) $matches[2];
  645.                 }
  646.  
  647.                 return $groups;
  648.             break;
  649.             case 503: // RFC2980: 'program error, function not performed'
  650.                 return PEAR::throwError('Internal server error, function not performed'$response$this->currentStatusResponse());
  651.                 break;
  652.             default:
  653.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  654.         }
  655.     }
  656.  
  657.     // }}}
  658.     /**
  659.      * Fetches a list of (all) avaible newsgroup descriptions.
  660.      * Depresated as of RFC2980.
  661.      *
  662.      * @param string $wildmat Wildmat of the groups, that is to be listed, defaults to '*';
  663.      *
  664.      * @return mixed (array) nested array with description of existing newsgroups on success or (object) pear_error on failure
  665.      * @access public
  666.      */
  667.     function cmdXGTitle($wildmat '*')
  668.     {
  669.         $response $this->_sendCommand('XGTITLE '.$wildmat);
  670.         if (PEAR::isError($response)){
  671.             return $response;
  672.         }
  673.  
  674.         switch ($response{
  675.             case 282: // RFC2980: 'list of groups and descriptions follows'
  676.                 $data $this->_getTextResponse();
  677.                 if (PEAR::isError($data)) {
  678.                     return $data;
  679.                 }
  680.  
  681.                 foreach($data as $line{
  682.                     preg_match("/^(.*?)\s(.*?$)/"trim($line)$matches);
  683.                     $groups[$matches[1]] = (string) $matches[2];
  684.                 }
  685.  
  686.                 return $groups;
  687.                 break;
  688.           
  689.             case 481: // RFC2980: 'Groups and descriptions unavailable'
  690.                 return PEAR::throwError('Groups and descriptions unavailable'$response$this->currentStatusResponse());
  691.                 break;
  692.             default:
  693.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  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.      * @param optional string $distributions
  705.      *
  706.      * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure
  707.      * @access public
  708.      */
  709.     function cmdNewgroups($time$distributions = null)
  710.     {
  711.         $response $this->_sendCommand('NEWGROUPS '.date('ymd His'$time).' GMT'.($distributions !== null ? ' <'.$distributions.'>' ''));
  712.         if (PEAR::isError($response)){
  713.             return $response;
  714.         }
  715.  
  716.         switch ($response{
  717.             case 231: // REF977: 'list of new newsgroups follows'
  718.                 $groups = array();
  719.                 foreach($this->_getTextResponse(as $line{
  720.                     $arr explode(' '$line);
  721.                     $groups[$arr[0]]['group'$arr[0];
  722.                     $groups[$arr[0]]['last'$arr[1];
  723.                     $groups[$arr[0]]['first'$arr[2];
  724.                     $groups[$arr[0]]['posting'$arr[3];
  725.                 }
  726.                 return $groups;
  727.                 break;
  728.             default:
  729.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  730.         }
  731.     }
  732.  
  733.     // }}}
  734.     // {{{ cmdListOverviewFmt()
  735.  
  736.     /**
  737.      * Returns a list of avaible headers which are send from newsserver to client for every news message
  738.      *
  739.      * @return mixed (array) of header names on success or (object) pear_error on failure
  740.      * @access public
  741.      */
  742.     function cmdListOverviewFmt()
  743.     {
  744.         $response $this->_sendCommand('LIST OVERVIEW.FMT');
  745.         if (PEAR::isError($response)){
  746.             return $response;
  747.         }
  748.  
  749.         switch ($response{
  750.             case 215: // RFC2980: 'information follows'
  751.                 $data $this->_getTextResponse();
  752.                 if (PEAR::isError($data)) {
  753.                     return $data;
  754.                 }
  755.  
  756.                 $format = array('number');
  757.                 // XXX Use the splitHeaders() algorithm for supporting
  758.                 //     multiline headers?
  759.                 foreach ($data as $line{
  760.                     $line current(explode(':'trim($line)));
  761.                     $format[$line;
  762.                 }
  763.                 return $format;
  764.                 break;
  765.             case 503: // RFC2980: 'program error, function not performed'
  766.                 return PEAR::throwError('Internal server error, function not performed'$response$this->currentStatusResponse());
  767.                 break;
  768.             default:
  769.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  770.         }
  771.     }
  772.  
  773.     // }}}
  774.     // {{{ cmdXOver()
  775.  
  776.     /**
  777.      * Fetch message header from message number $first until $last
  778.      *
  779.      * The format of the returned array is:
  780.      * $messages[message_id][header_name]
  781.      *
  782.      * @param string $range articles 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($range)
  788.     {
  789.     // deprecated API (the code _is_ still in alpha state)
  790.         if (func_num_args(> 1 {
  791.             die('The second parameter in cmdXOver() has been deprecated!');
  792.         }
  793.  
  794.         $format $this->cmdListOverviewFmt();
  795.         if (PEAR::isError($format)){
  796.             return $formt;
  797.         }
  798.  
  799.         $response $this->_sendCommand('XOVER '.$range);
  800.         if (PEAR::isError($response)){
  801.             return $response;
  802.         }
  803.  
  804.         switch ($response{
  805.             case 224: // RFC2980: 'Overview information follows'
  806.                 $data $this->_getTextResponse();
  807.                 if (PEAR::isError($data)) {
  808.                     return $data;
  809.                 }
  810.                 $messages = array();
  811.                 foreach($data as $line{
  812.                     $i=0;
  813.                     foreach(explode("\t"trim($line)) as $line{
  814.                         $message[$format[$i++]] $line;
  815.                     }
  816.                     $messages[$message['Message-ID']] $message;
  817.                 }
  818.                 return $messages;
  819.                 break;
  820.             case 412: // RFC2980: 'No news group current selected'
  821.                 return PEAR::throwError('No news group current selected'$response$this->currentStatusResponse());
  822.                 break;
  823.             case 420: // RFC2980: 'No article(s) selected'
  824.                 return PEAR::throwError('No article(s) selected'$response$this->currentStatusResponse());
  825.                 break;
  826.             case 502: // RFC2980: 'no permission'
  827.                 return PEAR::throwError('No permission'$response$this->currentStatusResponse());
  828.                 break;
  829.             default:
  830.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  831.         }
  832.     }
  833.     
  834.     // }}}
  835.     // {{{ cmdXROver()
  836.  
  837.     /**
  838.      * Fetch message references from message number $first to $last
  839.      *
  840.      * @param string $range articles to fetch
  841.      *
  842.      * @return mixed (array) assoc. array of message references on success or (object) pear_error on failure
  843.      * @access public
  844.      */
  845.     function cmdXROver($range)
  846.     {
  847.     // Warn about deprecated API (the code _is_ still in alpha state)
  848.         if (func_num_args(> 1 {
  849.             die('The second parameter in cmdXROver() has been deprecated!');
  850.         }
  851.  
  852.         $response $this->_sendCommand('XROVER '.$range);
  853.         if (PEAR::isError($response)){
  854.             return $response;
  855.         }
  856.  
  857.         switch ($response{
  858.             case 224: // RFC2980: 'Overview information follows'
  859.                 $data $this->_getTextResponse();
  860.                 if (PEAR::isError($data)) {
  861.                     return $data;
  862.                 }
  863.  
  864.                 foreach($data as $line{
  865.  
  866.                     $references preg_split("/ +/"trim($line)-1PREG_SPLIT_NO_EMPTY);
  867.  
  868.                     $id array_shift($references);
  869.  
  870.                     $messages[$id$references;
  871.                 }
  872.                 return $messages;
  873.                 break;
  874.             case 412: // RFC2980: 'No news group current selected'
  875.                 return PEAR::throwError('No news group current selected'$response$this->currentStatusResponse());
  876.                 break;
  877.             case 420: // RFC2980: 'No article(s) selected'
  878.                 return PEAR::throwError('No article(s) selected'$response$this->currentStatusResponse());
  879.                 break;
  880.             case 502: // RFC2980: 'no permission'
  881.                 return PEAR::throwError('No permission'$response$this->currentStatusResponse());
  882.                 break;
  883.             default:
  884.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  885.         }
  886.     }
  887.  
  888.     // }}}
  889.     // {{{ cmdListgroup()
  890.  
  891.     /**
  892.      *
  893.      * @param string $newsgroup 
  894.      *
  895.      * @return mixed (array) on success or (object) pear_error on failure
  896.      */
  897.     function cmdListgroup($newsgroup)
  898.     {
  899.         $response $this->_sendCommand('LISTGROUP '.$newsgroup);
  900.         if (PEAR::isError($response)){
  901.             return $response;
  902.         }
  903.  
  904.         switch ($response{
  905.             case 211: // RFC2980: 'list of article numbers follow'
  906.                 $data $this->_getTextResponse();
  907.                 if (PEAR::isError($data)) {
  908.                     return $data;
  909.                 }
  910.                 return $data;
  911.                 break;
  912.             case 412: // RFC2980: 'Not currently in newsgroup'
  913.                 return PEAR::throwError('Not currently in newsgroup'$response$this->currentStatusResponse());
  914.                 break;
  915.             case 502: // RFC2980: 'no permission'
  916.                 return PEAR::throwError('No permission'$response$this->currentStatusResponse());
  917.                 break;
  918.             default:
  919.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  920.         }
  921.     }
  922.  
  923.     // }}}
  924.     // {{{ cmdNewnews()
  925.  
  926.     /**
  927.      *
  928.      */
  929.     function cmdNewnews($time$newsgroups '*')
  930.     {
  931.     // TODO: the lenght of the request string may not exceed 510 chars
  932.     
  933.         $response $this->_sendCommand('NEWNEWS '.$newsgroups.' '.date('ymd His'$time));
  934.         if (PEAR::isError($response)){
  935.             return $response;
  936.         }
  937.  
  938.         switch ($response{
  939.             case 230: // RFC977: 'list of new articles by message-id follows'
  940.                 $messages = array();
  941.                 foreach($this->_getTextResponse(as $line{
  942.                     $messages[$line;
  943.                 }
  944.                 return $messages;
  945.                 break;
  946.             default:
  947.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  948.         }
  949.     }
  950.  
  951.     // }}}
  952.     // {{{ cmdDate()
  953.  
  954.     /**
  955.      * Get the date from the newsserver format of returned date
  956.      *
  957.      * @param bool $timestap when false function returns string, and when true function returns int/timestamp.
  958.      *
  959.      * @return mixed (string) 'YYYYMMDDhhmmss' / (int) timestamp on success or (object) pear_error on failure
  960.      * @access public
  961.      */
  962.     function cmdDate($timestamp = false)
  963.     {
  964.         $response $this->_sendCommand('DATE');
  965.         if (PEAR::isError($response)){
  966.             return $response;
  967.         }
  968.  
  969.         switch ($response{
  970.             case 111: // RFC2980: 'YYYYMMDDhhmmss'
  971.                 $d $this->currentStatusResponse();
  972.                 if ($timestamp === false{
  973.                     return (string) $d;        
  974.                 else {
  975.                     return (int) strtotime(substr($d08).' '.$d[8].$d[9].':'.$d[10].$d[11].':'.$d[12].$d[13]);
  976.                 }
  977.                 break;
  978.             default:
  979.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  980.         }
  981.     }
  982.     // }}}
  983.     // {{{ isConnected()
  984.  
  985.     /**
  986.      * Test whether we are connected or not.
  987.      *
  988.      * @return bool true or false
  989.      *
  990.      * @access public
  991.      */
  992.     function isConnected()
  993.     {
  994.         return (is_resource($this->_socket->fp&& (!$this->_socket->eof()));
  995.     }
  996.  
  997.     // }}}
  998.     // {{{ setDebug()
  999.  
  1000.     /**
  1001.      * Sets the debuging information on or off
  1002.      *
  1003.      * @param boolean True or false
  1004.      *
  1005.      * @return bool previos state
  1006.      * @access public
  1007.      */
  1008.     function setDebug($debug = true)
  1009.     {
  1010.         $tmp $this->_debug;
  1011.         $this->_debug $debug;
  1012.         return $tmp;
  1013.     }
  1014.  
  1015.     // }}}
  1016.     // {{{ _getStatusResponse()
  1017.  
  1018.     /**
  1019.      * Get servers statusresponse after a command.
  1020.      *
  1021.      * @return mixed (int) statuscode on success or (object) pear_error on failure
  1022.      * @access private
  1023.      */
  1024.     function _getStatusResponse()
  1025.     {
  1026.         // Retrieve a line (terminated by "\r\n") from the server.
  1027.         $response $this->_socket->gets(256);
  1028.         if (PEAR::isError($response) ) {
  1029.             return PEAR::throwError('Failed to read from socket!'null$response->getMessage());
  1030.         }
  1031.  
  1032.         if ($this->_debug{
  1033.             echo "S: $response\r\n";
  1034.         }
  1035.  
  1036.         // Trim the start of the response in case of misplased whitespace (should not be needen!!!)
  1037.         $response ltrim($response);
  1038.  
  1039.         $this->_currentStatusResponse = array(
  1040.                                               (int) substr($response03),
  1041.                                               (string) rtrim(substr($response4))
  1042.                                              );
  1043.  
  1044.         return $this->_currentStatusResponse[0];
  1045.     }
  1046.     
  1047.     // }}}
  1048.     // {{{ currentStatusResponse()
  1049.  
  1050.     /**
  1051.      *
  1052.      *
  1053.      * @return string status text
  1054.      * @access private
  1055.      */
  1056.     function currentStatusResponse()
  1057.     {
  1058.         return $this->_currentStatusResponse[1];
  1059.     }
  1060.     
  1061.     // }}}
  1062.     // {{{ _getTextResponse()
  1063.  
  1064.     /**
  1065.      * Get data until a line with only a '.' in it is read and return data.
  1066.      *
  1067.      * @return mixed (array) text response on success or (object) pear_error on failure
  1068.      * @access private
  1069.      */
  1070.     function _getTextResponse()
  1071.     {
  1072.         $data = array();
  1073.         $line '';
  1074.     
  1075.         // Continue until connection is lost
  1076.         while(!$this->_socket->eof()) {
  1077.  
  1078.             // Retrieve and append up to 1024 characters from the server.
  1079.             $line .= $this->_socket->gets(1024)
  1080.             if (PEAR::isError($line) ) {
  1081.                 return PEAR::throwError'Failed to read from socket!'null$line->getMessage());
  1082.             }
  1083.         
  1084.             // Continue if the line is not terminated by CRLF
  1085.             if (substr($line-2!= "\r\n" || strlen($line< 2{
  1086.                 continue;
  1087.             }
  1088.  
  1089.             // Validate recieved line
  1090.             if (false{
  1091.                 // Lines should/may not be longer than 998+2 chars (RFC2822 2.3)
  1092.                 if (strlen($line> 1000{
  1093.                     return PEAR::throwError('Invalid line recieved!'null);
  1094.                 }
  1095.             }
  1096.  
  1097.             // Remove CRLF from the end of the line
  1098.             $line substr($line0-2);
  1099.  
  1100.             // Check if the line terminates the textresponse
  1101.             if ($line == '.'{
  1102.                 // return all previous lines
  1103.                 return $data;
  1104.                 break;
  1105.             }
  1106.  
  1107.             // If 1st char is '.' it's doubled (NNTP/RFC977 2.4.1)
  1108.             if (substr($line02== '..'{
  1109.                 $line substr($line1);
  1110.             }
  1111.             
  1112.             // Add the line to the array of lines
  1113.             $data[$line;
  1114.  
  1115.             // Reset/empty $line
  1116.             $line '';
  1117.         }
  1118.  
  1119.         return PEAR::throwError('Data stream not terminated with period'null);
  1120.     }
  1121.  
  1122.     // }}}
  1123.     // {{{ _sendCommand()
  1124.  
  1125.     /**
  1126.      * Send a command to the server. A carriage return / linefeed (CRLF) sequence
  1127.      * will be appended to each command string before it is sent to the IMAP server.
  1128.      *
  1129.      * @param string $cmd The command to launch, ie: "ARTICLE 1004853"
  1130.      *
  1131.      * @return mixed (int) response code on success or (object) pear_error on failure
  1132.      * @access private
  1133.      */
  1134.     function _sendCommand($cmd)
  1135.     {
  1136.         // NNTP/RFC977 only allows command up to 512 (-2) chars.
  1137.         if (!strlen($cmd> 510{
  1138.             return PEAR::throwError('Failed to write to socket! (Command to long - max 510 chars)');
  1139.         }
  1140.  
  1141.         // Check if connected
  1142.         if (!$this->isConnected()) {
  1143.             return PEAR::throwError('Failed to write to socket! (connection lost!)');
  1144.         }
  1145.  
  1146.         // Send the command
  1147.         $R $this->_socket->writeLine($cmd);
  1148.         if PEAR::isError($R) ) {
  1149.             return PEAR::throwError('Failed to write to socket!'null$R->getMessage());
  1150.         }
  1151.     
  1152.         if ($this->_debug{
  1153.             echo "C: $cmd\r\n";
  1154.         }
  1155.  
  1156.         return $this->_getStatusResponse();
  1157.     }
  1158.     
  1159.     // }}}
  1160.  
  1161. }
  1162.  
  1163. // }}}
  1164.  
  1165. ?>

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