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

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