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

Source for file Protocol.php

Documentation is available at Protocol.php

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

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