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.6 2004/08/22 07:34:26 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.6 2004/08/22 07:34:26 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.      *
  699.      * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure
  700.      * @access public
  701.      */
  702.     function cmdNewgroups($time)
  703.     {
  704.         $response $this->_sendCommand('NEWGROUPS '.date('ymd His'$time));
  705.         if (PEAR::isError($response)){
  706.             return $response;
  707.         }
  708.  
  709.         switch ($response{
  710.             case 231: // REF977: 'list of new newsgroups follows'
  711.                 $groups = array();
  712.                 foreach($this->_getTextResponse(as $line{
  713.                     $arr explode(' '$line);
  714.                     $groups[$arr[0]]['group'$arr[0];
  715.                     $groups[$arr[0]]['last'$arr[1];
  716.                     $groups[$arr[0]]['first'$arr[2];
  717.                     $groups[$arr[0]]['posting'$arr[3];
  718.                 }
  719.                 return $groups;
  720.                 break;
  721.             default:
  722.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  723.         }
  724.     }
  725.  
  726.     // }}}
  727.     // {{{ cmdListOverviewFmt()
  728.  
  729.     /**
  730.      * Returns a list of avaible headers which are send from newsserver to client for every news message
  731.      *
  732.      * @return mixed (array) of header names on success or (object) pear_error on failure
  733.      * @access public
  734.      */
  735.     function cmdListOverviewFmt()
  736.     {
  737.         $response $this->_sendCommand('LIST OVERVIEW.FMT');
  738.         if (PEAR::isError($response)){
  739.             return $response;
  740.         }
  741.  
  742.         switch ($response{
  743.             case 215: // RFC2980: 'information follows'
  744.                 $data $this->_getTextResponse();
  745.                 if (PEAR::isError($data)) {
  746.                     return $data;
  747.                 }
  748.  
  749.                 $format = array('number');
  750.                 // XXX Use the splitHeaders() algorithm for supporting
  751.                 //     multiline headers?
  752.                 foreach ($data as $line{
  753.                     $line current(explode(':'trim($line)));
  754.                     $format[$line;
  755.                 }
  756.                 return $format;
  757.                 break;
  758.             case 503: // RFC2980: 'program error, function not performed'
  759.                 return PEAR::throwError('Internal server error, function not performed'$response$this->currentStatusResponse());
  760.                 break;
  761.             default:
  762.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  763.         }
  764.     }
  765.  
  766.     // }}}
  767.     // {{{ cmdXOver()
  768.  
  769.     /**
  770.      * Fetch message header from message number $first until $last
  771.      *
  772.      * The format of the returned array is:
  773.      * $messages[message_id][header_name]
  774.      *
  775.      * @param string $range articles to fetch
  776.      *
  777.      * @return mixed (array) nested array of message and there headers on success or (object) pear_error on failure
  778.      * @access public
  779.      */
  780.     function cmdXOver($range)
  781.     {
  782.     // deprecated API (the code _is_ still in alpha state)
  783.         if (func_num_args(> 1 {
  784.             die('The second parameter in cmdXOver() has been deprecated!');
  785.         }
  786.  
  787.         $format $this->cmdListOverviewFmt();
  788.         if (PEAR::isError($format)){
  789.             return $formt;
  790.         }
  791.  
  792.         $response $this->_sendCommand('XOVER '.$range);
  793.         if (PEAR::isError($response)){
  794.             return $response;
  795.         }
  796.  
  797.         switch ($response{
  798.             case 224: // RFC2980: 'Overview information follows'
  799.                 $data $this->_getTextResponse();
  800.                 if (PEAR::isError($data)) {
  801.                     return $data;
  802.                 }
  803.                 $messages = array();
  804.                 foreach($data as $line{
  805.                     $i=0;
  806.                     foreach(explode("\t"trim($line)) as $line{
  807.                         $message[$format[$i++]] $line;
  808.                     }
  809.                     $messages[$message['Message-ID']] $message;
  810.                 }
  811.                 return $messages;
  812.                 break;
  813.             case 412: // RFC2980: 'No news group current selected'
  814.                 return PEAR::throwError('No news group current selected'$response$this->currentStatusResponse());
  815.                 break;
  816.             case 420: // RFC2980: 'No article(s) selected'
  817.                 return PEAR::throwError('No article(s) selected'$response$this->currentStatusResponse());
  818.                 break;
  819.             case 502: // RFC2980: 'no permission'
  820.                 return PEAR::throwError('No permission'$response$this->currentStatusResponse());
  821.                 break;
  822.             default:
  823.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  824.         }
  825.     }
  826.     
  827.     // }}}
  828.     // {{{ cmdXROver()
  829.  
  830.     /**
  831.      * Fetch message references from message number $first to $last
  832.      *
  833.      * @param string $range articles to fetch
  834.      *
  835.      * @return mixed (array) assoc. array of message references on success or (object) pear_error on failure
  836.      * @access public
  837.      */
  838.     function cmdXROver($range)
  839.     {
  840.     // Warn about deprecated API (the code _is_ still in alpha state)
  841.         if (func_num_args(> 1 {
  842.             die('The second parameter in cmdXOver() has been deprecated!');
  843.         }
  844.  
  845.         $response $this->_sendCommand('XROVER '.$range);
  846.         if (PEAR::isError($response)){
  847.             return $response;
  848.         }
  849.  
  850.         switch ($response{
  851.             case 224: // RFC2980: 'Overview information follows'
  852.                 $data $this->_getTextResponse();
  853.                 if (PEAR::isError($data)) {
  854.                     return $data;
  855.                 }
  856.  
  857.                 foreach($data as $line{
  858.  
  859.                     $references preg_split("/ +/"trim($line)-1PREG_SPLIT_NO_EMPTY);
  860.  
  861.                     $id array_shift($references);
  862.  
  863.                     $messages[$id$references;
  864.                 }
  865.                 return $messages;
  866.                 break;
  867.             case 412: // RFC2980: 'No news group current selected'
  868.                 return PEAR::throwError('No news group current selected'$response$this->currentStatusResponse());
  869.                 break;
  870.             case 420: // RFC2980: 'No article(s) selected'
  871.                 return PEAR::throwError('No article(s) selected'$response$this->currentStatusResponse());
  872.                 break;
  873.             case 502: // RFC2980: 'no permission'
  874.                 return PEAR::throwError('No permission'$response$this->currentStatusResponse());
  875.                 break;
  876.             default:
  877.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  878.         }
  879.     }
  880.  
  881.     // }}}
  882.     // {{{ cmdListgroup()
  883.  
  884.     /**
  885.      *
  886.      * @param string $newsgroup 
  887.      *
  888.      * @return mixed (array) on success or (object) pear_error on failure
  889.      */
  890.     function cmdListgroup($newsgroup)
  891.     {
  892.         $response $this->_sendCommand('LISTGROUP '.$newsgroup);
  893.         if (PEAR::isError($response)){
  894.             return $response;
  895.         }
  896.  
  897.         switch ($response{
  898.             case 211: // RFC2980: 'list of article numbers follow'
  899.                 $data $this->_getTextResponse();
  900.                 if (PEAR::isError($data)) {
  901.                     return $data;
  902.                 }
  903.                 return $data;
  904.                 break;
  905.             case 412: // RFC2980: 'Not currently in newsgroup'
  906.                 return PEAR::throwError('Not currently in newsgroup'$response$this->currentStatusResponse());
  907.                 break;
  908.             case 502: // RFC2980: 'no permission'
  909.                 return PEAR::throwError('No permission'$response$this->currentStatusResponse());
  910.                 break;
  911.             default:
  912.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  913.         }
  914.     }
  915.  
  916.     // }}}
  917.     // {{{ cmdNewnews()
  918.  
  919.     /**
  920.      *
  921.      */
  922.     function cmdNewnews($time$newsgroups '*')
  923.     {
  924.     // TODO: the lenght of the request string may not exceed 510 chars
  925.     
  926.         $response $this->_sendCommand('NEWNEWS '.$newsgroups.' '.date('ymd His'$time));
  927.         if (PEAR::isError($response)){
  928.             return $response;
  929.         }
  930.  
  931.         switch ($response{
  932.             case 230: // RFC977: 'list of new articles by message-id follows'
  933.                 $messages = array();
  934.                 foreach($this->_getTextResponse(as $line{
  935.                     $messages[$line;
  936.                 }
  937.                 return $messages;
  938.                 break;
  939.             default:
  940.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  941.         }
  942.     }
  943.  
  944.     // }}}
  945.     // {{{ cmdDate()
  946.  
  947.     /**
  948.      * Get the date from the newsserver format of returned date
  949.      *
  950.      * @param bool $timestap when false function returns string, and when true function returns int/timestamp.
  951.      *
  952.      * @return mixed (string) 'YYYYMMDDhhmmss' / (int) timestamp on success or (object) pear_error on failure
  953.      * @access public
  954.      */
  955.     function cmdDate($timestamp = false)
  956.     {
  957.         $response $this->_sendCommand('DATE');
  958.         if (PEAR::isError($response)){
  959.             return $response;
  960.         }
  961.  
  962.         switch ($response{
  963.             case 111: // RFC2980: 'YYYYMMDDhhmmss'
  964.                 $d $this->currentStatusResponse();
  965.                 if ($timestamp === false{
  966.                     return (string) $d;        
  967.                 else {
  968.                     return (int) strtotime(substr($d08).' '.$d[8].$d[9].':'.$d[10].$d[11].':'.$d[12].$d[13]);
  969.                 }
  970.                 break;
  971.             default:
  972.                 return PEAR::throwError('Unidentified response code'$response$this->currentStatusResponse());
  973.         }
  974.     }
  975.     // }}}
  976.     // {{{ isConnected()
  977.  
  978.     /**
  979.      * Test whether we are connected or not.
  980.      *
  981.      * @return bool true or false
  982.      *
  983.      * @access public
  984.      */
  985.     function isConnected()
  986.     {
  987.         return (is_resource($this->_socket->fp&& (!$this->_socket->eof()));
  988.     }
  989.  
  990.     // }}}
  991.     // {{{ setDebug()
  992.  
  993.     /**
  994.      * Sets the debuging information on or off
  995.      *
  996.      * @param boolean True or false
  997.      *
  998.      * @return bool previos state
  999.      * @access public
  1000.      */
  1001.     function setDebug($debug = true)
  1002.     {
  1003.         $tmp $this->_debug;
  1004.         $this->_debug $debug;
  1005.         return $tmp;
  1006.     }
  1007.  
  1008.     // }}}
  1009.     // {{{ _getStatusResponse()
  1010.  
  1011.     /**
  1012.      * Get servers statusresponse after a command.
  1013.      *
  1014.      * @return mixed (int) statuscode on success or (object) pear_error on failure
  1015.      * @access private
  1016.      */
  1017.     function _getStatusResponse()
  1018.     {
  1019.         // Retrieve a line (terminated by "\r\n") from the server.
  1020.         $response $this->_socket->gets(256);
  1021.         if (PEAR::isError($response) ) {
  1022.             return PEAR::throwError('Failed to read from socket!'null$response->getMessage());
  1023.         }
  1024.  
  1025.         if ($this->_debug{
  1026.             echo "S: $response\r\n";
  1027.         }
  1028.  
  1029.         // Trim the start of the response in case of misplased whitespace (should not be needen!!!)
  1030.         $response ltrim($response);
  1031.  
  1032.         $this->_currentStatusResponse = array(
  1033.                                               (int) substr($response03),
  1034.                                               (string) rtrim(substr($response4))
  1035.                                              );
  1036.  
  1037.         return $this->_currentStatusResponse[0];
  1038.     }
  1039.     
  1040.     // }}}
  1041.     // {{{ currentStatusResponse()
  1042.  
  1043.     /**
  1044.      *
  1045.      *
  1046.      * @return string status text
  1047.      * @access private
  1048.      */
  1049.     function currentStatusResponse()
  1050.     {
  1051.         return $this->_currentStatusResponse[1];
  1052.     }
  1053.     
  1054.     // }}}
  1055.     // {{{ _getTextResponse()
  1056.  
  1057.     /**
  1058.      * Get data until a line with only a '.' in it is read and return data.
  1059.      *
  1060.      * @return mixed (array) text response on success or (object) pear_error on failure
  1061.      * @access private
  1062.      */
  1063.     function _getTextResponse()
  1064.     {
  1065.         $data = array();
  1066.         $line '';
  1067.     
  1068.         // Continue until connection is lost
  1069.         while(!$this->_socket->eof()) {
  1070.  
  1071.             // Retrieve and append up to 1024 characters from the server.
  1072.             $line .= $this->_socket->gets(1024)
  1073.             if (PEAR::isError($line) ) {
  1074.                 return PEAR::throwError'Failed to read from socket!'null$line->getMessage());
  1075.             }
  1076.         
  1077.             // Continue if the line is not terminated by CRLF
  1078.             if (substr($line-2!= "\r\n" || strlen($line< 2{
  1079.                 continue;
  1080.             }
  1081.  
  1082.             // Validate recieved line
  1083.             if (false{
  1084.                 // Lines should/may not be longer than 998+2 chars (RFC2822 2.3)
  1085.                 if (strlen($line> 1000{
  1086.                     return PEAR::throwError('Invalid line recieved!'null);
  1087.                 }
  1088.             }
  1089.  
  1090.             // Remove CRLF from the end of the line
  1091.             $line substr($line0-2);
  1092.  
  1093.             // Check if the line terminates the textresponse
  1094.             if ($line == '.'{
  1095.                 // return all previous lines
  1096.                 return $data;
  1097.                 break;
  1098.             }
  1099.  
  1100.             // If 1st char is '.' it's doubled (NNTP/RFC977 2.4.1)
  1101.             if (substr($line02== '..'{
  1102.                 $line substr($line1);
  1103.             }
  1104.             
  1105.             // Add the line to the array of lines
  1106.             $data[$line;
  1107.  
  1108.             // Reset/empty $line
  1109.             $line '';
  1110.         }
  1111.  
  1112.         return PEAR::throwError('Data stream not terminated with period'null);
  1113.     }
  1114.  
  1115.     // }}}
  1116.     // {{{ _sendCommand()
  1117.  
  1118.     /**
  1119.      * Send a command to the server. A carriage return / linefeed (CRLF) sequence
  1120.      * will be appended to each command string before it is sent to the IMAP server.
  1121.      *
  1122.      * @param string $cmd The command to launch, ie: "ARTICLE 1004853"
  1123.      *
  1124.      * @return mixed (int) response code on success or (object) pear_error on failure
  1125.      * @access private
  1126.      */
  1127.     function _sendCommand($cmd)
  1128.     {
  1129.         // NNTP/RFC977 only allows command up to 512 (-2) chars.
  1130.         if (!strlen($cmd> 510{
  1131.             return PEAR::throwError('Failed to write to socket! (Command to long - max 510 chars)');
  1132.         }
  1133.  
  1134.         // Check if connected
  1135.         if (!$this->isConnected()) {
  1136.             return PEAR::throwError('Failed to write to socket! (connection lost!)');
  1137.         }
  1138.  
  1139.         // Send the command
  1140.         $R $this->_socket->writeLine($cmd);
  1141.         if PEAR::isError($R) ) {
  1142.             return PEAR::throwError('Failed to write to socket!'null$R->getMessage());
  1143.         }
  1144.     
  1145.         if ($this->_debug{
  1146.             echo "C: $cmd\r\n";
  1147.         }
  1148.  
  1149.         return $this->_getStatusResponse();
  1150.     }
  1151.     
  1152.     // }}}
  1153.  
  1154. }
  1155.  
  1156. // }}}
  1157.  
  1158. ?>

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