home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / NNTP.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  21.7 KB  |  708 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Martin Kaltoft   <martin@nitro.dk>                          |
  17. // |          Tomas V.V.Cox    <cox@idecnet.com>                          |
  18. // |          Heino H. Gehlsen <heino@gehlsen.dk>                         |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: NNTP.php,v 1.15.2.3 2003/09/12 23:33:58 heino Exp $
  22.  
  23. require_once 'PEAR.php';
  24.  
  25. // Deprecated due to naming
  26. define('PEAR_NNTP_ALL',   0);
  27. define('PEAR_NNTP_NAMES', 1);
  28. define('PEAR_NNTP_LIST',  2);
  29.  
  30. /* NNTP Authentication modes */
  31. define('NET_NNTP_AUTHORIGINAL', 'original');
  32. define('NET_NNTP_AUTHSIMPLE',   'simple');
  33. define('NET_NNTP_AUTHGENERIC',  'generic');
  34.  
  35. // Deprecated due to naming
  36. define('PEAR_NNTP_AUTHORIGINAL', NET_NNTP_AUTHORIGINAL);
  37. define('PEAR_NNTP_AUTHSIMPLE',   NET_NNTP_AUTHSIMPLE);
  38. define('PEAR_NNTP_AUTHGENERIC',  NET_NNTP_AUTHGENERIC);
  39.  
  40. /**
  41.  * The NNTP:: class fetches UseNet news articles acording to the standard
  42.  * based on RFC 1036.
  43.  *
  44.  * @version 0.2.2
  45.  * @author Martin Kaltoft   <martin@nitro.dk>
  46.  * @author Tomas V.V.Cox    <cox@idecnet.com>
  47.  * @author Heino H. Gehlsen <heino@gehlsen.dk>
  48.  */
  49.  
  50. class Net_NNTP extends PEAR
  51. {
  52.  
  53.     var $max = '';
  54.     var $min = '';
  55.     var $user = null;
  56.     var $pass = null;
  57.     var $authmode = null;
  58.  
  59.     /** File pointer of the nntp-connection */
  60.     var $fp = null;
  61.  
  62.     /**
  63.      * Connect to the newsserver
  64.      *
  65.      * @param string $nntpserver The adress of the NNTP-server to connect to.
  66.      * @param int $port (optional) the port-number to connect to, defaults to 119.
  67.      * @param string $user (optional) The user name to authenticate with
  68.      * @param string $pass (optional) The password
  69.      * @param string $authmode (optional) The authentication mode
  70.      * @return mixed True on success or Pear Error object on failure
  71.      * @see Net_Nntp::authenticate()
  72.      * @access public
  73.      */
  74.     function connect($nntpserver,
  75.                      $port = 119,
  76.                      $user = null,
  77.                      $pass = null,
  78.                      $authmode = NET_NNTP_AUTHORIGINAL)
  79.     {
  80.         $fp = @fsockopen($nntpserver, $port, $errno, $errstr, 15);
  81.         if (!is_resource($fp)) {
  82.             return $this->raiseError("Could not connect to NNTP-server $nntpserver");
  83.         }
  84.         socket_set_blocking($fp, true);
  85.         if (!$fp) {
  86.             return $this->raiseError('Not connected');
  87.         }
  88.         $response = fgets($fp, 256);
  89.         if ($this->_debug) {
  90.             print "<< $response\n";
  91.         }
  92.         $this->fp   = $fp;
  93.         $this->user = $user;
  94.         $this->pass = $pass;
  95.         $this->authmode = $authmode;
  96.  
  97.         return true;
  98.     }
  99.  
  100.     /**
  101.      * Connect to the newsserver, and issue a GROUP command
  102.      * Once connection is prepared, we can only fetch articles from one group
  103.      * at a time, to fetch from another group, a new connection has to be made.
  104.      *
  105.      * This is to avoid the GROUP command for every article, as it is very
  106.      * ressource intensive on the newsserver especially when used for
  107.      * groups with many articles.
  108.      *
  109.      * @param string $nntpserver The adress of the NNTP-server to connect to.
  110.      * @param int $port (optional) the port-number to connect to, defaults to 119.
  111.      * @param string $newsgroup The name of the newsgroup to use.
  112.      * @param string $user (optional) The user name to authenticate with
  113.      * @param string $pass (optional) The password
  114.      * @param string $authmode (optional) The authentication mode
  115.      * @return mixed True on success or Pear Error object on failure
  116.      * @see Net_Nntp::authenticate()
  117.      * @access public
  118.      * @deprecated Use connect() instead
  119.      */
  120.     function prepareConnection($nntpserver,
  121.                                 $port = 119,
  122.                                 $newsgroup,
  123.                                 $user = null,
  124.                                 $pass = null,
  125.                                 $authmode = NET_NNTP_AUTHORIGINAL)
  126.     {
  127.         /* connect to the server */
  128.         $err = $this->connect($nntpserver, $port, $user, $pass, $authmode);
  129.         if (PEAR::isError($err)) {
  130.             return $err;
  131.         }
  132.  
  133.         /* issue a GROUP command */
  134.         $r = $this->command("GROUP $newsgroup");
  135.  
  136.         if (PEAR::isError($r) || $this->responseCode($r) > 299) {
  137.             return $this->raiseError($r);
  138.         }
  139.         $response_arr = split(' ', $r);
  140.         $this->max = $response_arr[3];
  141.         $this->min = $response_arr[2];
  142.  
  143.         return true;
  144.     }
  145.  
  146.     /**
  147.     * @deprecated
  148.     */
  149.     function prepare_connection($nntpserver,
  150.                                 $port = 119,
  151.                                 $newsgroup,
  152.                                 $user = null,
  153.                                 $pass = null,
  154.                                 $authmode = NET_NNTP_AUTHORIGINAL)
  155.     {
  156.         return $this->prepareConnection($nntpserver, $port, $newsgroup, $user, $pass, $authmode);
  157.     }
  158.  
  159.     /**
  160.     * Auth process (not yet standarized but used any way)
  161.     * http://www.mibsoftware.com/userkt/nntpext/index.html
  162.     *
  163.     * @param string $user The user name
  164.     * @param string $pass (optional) The password if needed
  165.     * @param string $mode Authinfo form: original, simple, generic
  166.     * @return mixed (bool) true on success or Pear Error obj on fail
  167.     * @access public
  168.     */
  169.     function authenticate($user = null, $pass = null, $mode = NET_NNTP_AUTHORIGINAL)
  170.     {
  171.         if ($user === null) {
  172.             return $this->raiseError('Authentication required but no user supplied');
  173.         }
  174.         switch ($mode) {
  175.             case NET_NNTP_AUTHORIGINAL:
  176.                 /*
  177.                     281 Authentication accepted
  178.                     381 More authentication information required
  179.                     480 Authentication required
  180.                     482 Authentication rejected
  181.                     502 No permission
  182.                 */
  183.                 $response = $this->command("AUTHINFO user $user", false);
  184.                 if ($this->responseCode($response) != 281) {
  185.                     if ($this->responseCode($response) == 381 && $pass !== null) {
  186.                         $response = $this->command("AUTHINFO pass $pass", false);
  187.                     }
  188.                 }
  189.                 if ($this->responseCode($response) != 281) {
  190.                     return $this->raiseError("Authentication failed: $response");
  191.                 }
  192.                 return true;
  193.                 break;
  194.             case NET_NNTP_AUTHSIMPLE:
  195.             case NET_NNTP_AUTHGENERIC:
  196.             default:
  197.                 $this->raiseError("The auth mode: $mode isn't implemented");
  198.         }
  199.     }
  200.  
  201.     /**
  202.      * Get an article from the currently open connection.
  203.      * To get articles from another newsgroup a new prepare_connection() -
  204.      * call has to be made with apropriate parameters
  205.      *
  206.      * @param mixed $article Either the message-id or the message-number on the server of the article to fetch
  207.      * @return string the article
  208.      * @access public
  209.      */
  210.     function getArticle($article)
  211.     {
  212.         /* tell the newsserver we want an article */
  213.         $r = $this->command("ARTICLE $article");
  214.         if (PEAR::isError($r) || $this->responseCode($r) > 299) {
  215.             return $this->raiseError($r);
  216.         }
  217.         $post = null;
  218.         while (!feof($this->fp)) {
  219.             $line = rtrim(fgets($this->fp, 256), "\r\n");
  220.  
  221.             if ($line == ".") {
  222.                 break;
  223.             } else {
  224.                 $post .= $line ."\n";
  225.             }
  226.         }
  227.         return $post;
  228.     }
  229.  
  230.     /**
  231.     * @deprecated
  232.     */
  233.     function get_article($article)
  234.     {
  235.         return $this->getArticle($article);
  236.     }
  237.  
  238.     /**
  239.      * Post an article to a newsgroup.
  240.      * Among the aditional headers you might think of adding could be:
  241.      * "NNTP-Posting-Host: <ip-of-author>", which should contain the IP-adress
  242.      * of the author of the post, so the message can be traced back to him.
  243.      * Or "Organization: <org>" which contain the name of the organization
  244.      * the post originates from.
  245.      *
  246.      * @param string $subject The subject of the post.
  247.      * @param string $newsgroup The newsgroup to post to.
  248.      * @param string $from Name + email-adress of sender.
  249.      * @param string $body The body of the post itself.
  250.      * @param string $aditionak (optional) Aditional headers to send.
  251.      * @return string server response
  252.      * @access public
  253.      */
  254.     function post($subject, $newsgroup, $from, $body, $aditional = "")
  255.     {
  256.         if (!@is_resource($this->fp)) {
  257.             return $this->raiseError('Not connected');
  258.         }
  259.  
  260.         /* tell the newsserver we want to post an article */
  261.         fputs($this->fp, "POST\n");
  262.  
  263.         /* The servers' response */
  264.         $response = rtrim(fgets($this->fp, 128), "\r\n");
  265.  
  266.         fputs($this->fp, "From: $from\n");
  267.         fputs($this->fp, "Newsgroups: $newsgroup\n");
  268.         fputs($this->fp, "Subject: $subject\n");
  269.         fputs($this->fp, "X-poster: nntp_fetcher (0.1) by Martin Kaltoft\n");
  270.         fputs($this->fp, "$aditional\n");
  271.         fputs($this->fp, "\n$body\n.\n");
  272.  
  273.         /* The servers' response */
  274.         $response = rtrim(fgets($this->fp, 128), "\r\n");
  275.  
  276.         return $response;
  277.     }
  278.  
  279.  
  280.     /**
  281.      * Get the headers of an article from the currently open connection
  282.      * To get the headers of an article from another newsgroup, a new
  283.      * prepare_connection()-call has to be made with apropriate parameters
  284.      *
  285.      * @param string $article Either a message-id or a message-number of the article to fetch the headers from.
  286.      * @return array Header
  287.      * @access public
  288.      */
  289.     function getHeaders($article)
  290.     {
  291.         /* tell the newsserver we want an article */
  292.         $r = $this->command("HEAD $article");
  293.         if (PEAR::isError($r) || $this->responseCode($r) > 299) {
  294.             return $this->raiseError($r);
  295.         }
  296.  
  297.         $headers = '';
  298.         while(!feof($this->fp)) {
  299.             $line = rtrim(fgets($this->fp, 256), "\r\n");
  300.  
  301.             if ($line == '.') {
  302.                 break;
  303.             } else {
  304.                 $headers .= $line . "\n";
  305.             }
  306.         }
  307.         return $headers;
  308.     }
  309.  
  310.     /**
  311.     * @deprecated
  312.     */
  313.     function get_headers($article)
  314.     {
  315.         return $this->getHeaders($article);
  316.     }
  317.  
  318.     /**
  319.     * Returns the headers of a given article in the form of
  320.     * an associative array. Ex:
  321.     * array(
  322.     *   'From'      => 'foo@bar.com (Foo Smith)',
  323.     *   'Subject'   => 'Re: Using NNTP class',
  324.     *   ....
  325.     *   );
  326.     *
  327.     * @param $article string Article number or id
  328.     * @return array Assoc array with headers names as key or Pear obj error
  329.     * @access public
  330.     */
  331.     function splitHeaders($article)
  332.     {
  333.         $headers = $this->get_headers($article);
  334.         if (PEAR::isError($headers)) {
  335.             return $headers;
  336.         }
  337.  
  338.         $lines = explode("\n", $headers);
  339.         foreach ($lines as $line) {
  340.             $line = trim($line);
  341.             if (($pos = strpos($line, ':')) !== false) {
  342.                 $head = substr($line, 0, $pos);
  343.                 $ret[$head] = ltrim(substr($line, $pos+1));
  344.             // if the field was longer than 256 chars, look also in the next line
  345.             // XXX a better way to discover that than strpos?
  346.             } else {
  347.                 $ret[$head] .= $line;
  348.             }
  349.         }
  350.         if (isset($ret['References']) &&
  351.             preg_match_all('|<.+>|U', $ret['References'], $matches))
  352.         {
  353.             $ret['References'] = $matches[0];
  354.         }
  355.         return $ret;
  356.     }
  357.  
  358.     /**
  359.     * @deprecated
  360.     */
  361.     function split_headers($article) {
  362.         return $this->splitHeaders($article);
  363.     }
  364.  
  365.     /**
  366.      * Get the body of an article from the currently open connection.
  367.      * To get the body of an article from another newsgroup, a new
  368.      * prepare_connection()-call has to be made with apropriate parameters
  369.      *
  370.      * @param string $article Either a message-id or a message-number of the article to fetch the headers from.
  371.      * @access public
  372.      */
  373.     function getBody($article)
  374.     {
  375.         /* tell the newsserver we want an article */
  376.         $r = $this->command("BODY $article");
  377.         if (PEAR::isError($r) || $this->responseCode($r) > 299) {
  378.             return $this->raiseError($r);
  379.         }
  380.  
  381.         $body = null;
  382.         while (!feof($this->fp)) {
  383.             $line = rtrim(fgets($this->fp, 256), "\r\n");
  384.  
  385.             if ($line == '.') {
  386.                 break;
  387.             } else {
  388.                 $body .= $line ."\n";
  389.             }
  390.         }
  391.         return $body;
  392.     }
  393.  
  394.     /**
  395.     * @deprecated
  396.     */
  397.     function get_body($article) {
  398.         return $this->getBody($article);
  399.     }
  400.  
  401.     /**
  402.      * Get data until a line with only a '.' in it is read and return data.
  403.      *
  404.      * @return string data
  405.      * @access private
  406.      * @author Morgan Christiansson <mog@linux.nu>
  407.      */
  408.     function _getData()
  409.     {
  410.         $body = array();
  411.         while(!feof($this->fp)) {
  412.             $line = rtrim(fgets($this->fp, 256), "\r\n");
  413.             if ($line == '.') {
  414.                 break;
  415.             } else {
  416.                 $body[] = $line;
  417.             }
  418.         }
  419.         return $body;
  420.     }
  421.  
  422.     /**
  423.     * @deprecated
  424.     */
  425.     function get_data() {
  426.         return $this->_getData();
  427.     }
  428.  
  429.     /**
  430.     * Selects a news group (issue a GROUP command to the server)
  431.     *
  432.     * @param string $newsgroup The newsgroup name
  433.     * @return mixed Array on success or Pear Error object on failure
  434.     */
  435.     function selectGroup($newsgroup)
  436.     {
  437.         $r = $this->command("GROUP $newsgroup");
  438.         if (PEAR::isError($r) || $this->responseCode($r) > 299) {
  439.             return $this->raiseError($r);
  440.         }
  441.         $response_arr = split(' ', $r);
  442.         $this->max = $response_arr[3];
  443.         $this->min = $response_arr[2];
  444.  
  445.         return array(
  446.                      "first" => $response_arr[2],
  447.                      "last"  => $response_arr[3]
  448.                     );
  449.     }
  450.  
  451.     /**
  452.     * @deprecated
  453.     */
  454.     function select_group($newsgroup) {
  455.         return $this->selectGroup($newsgroup);
  456.     }
  457.  
  458.     /**
  459.      * Fetches a list of all avaible newsgroups
  460.      *
  461.      * @param int $fetch PEAR_NNTP_ALL PEAR_NNTP_NAMES PEAR_NNTP_LIST
  462.      * @return array nested array with informations about
  463.      *               existing newsgroups
  464.      * @author Morgan Christiansson <mog@linux.nu>
  465.      */
  466.     function getGroups($fetch = true)
  467.     {
  468.         $this->command("LIST");
  469.         foreach($this->_getData() as $line) {
  470.             $arr = explode(" ",$line);
  471.             $groups[$arr[0]]["group"] = $arr[0];
  472.             $groups[$arr[0]]["last"] = $arr[1];
  473.             $groups[$arr[0]]["first"] = $arr[2];
  474.             $groups[$arr[0]]["posting_allowed"] = $arr[3];
  475.         }
  476.  
  477.         $this->command("LIST NEWSGROUPS");
  478.         foreach($this->_getData() as $line) {
  479.             preg_match("/^(.*?)\s(.*?$)/",$line,$matches);
  480.             $groups[$matches[1]]["desc"] = $matches[2];
  481.         }
  482.         return $groups;
  483.     }
  484.  
  485.     /**
  486.     * @deprecated
  487.     */
  488.     function get_groups($fetch=true) {
  489.         return $this->getGroups($fetch);
  490.     }
  491.  
  492.     /**
  493.     * Returns a list of avaible headers
  494.     * which are send from newsserver to client
  495.     * for every news message
  496.     *
  497.     * @return array header names
  498.     * @access public
  499.     */
  500.     function getOverviewFmt()
  501.     {
  502.         $this->command("LIST OVERVIEW.FMT");
  503.         $format = array("number");
  504.         // XXX Use the splitHeaders() algorithm for supporting
  505.         //     multiline headers?
  506.         foreach ($body = $this->_getData() as $line) {
  507.             $line = current(explode(":",$line));
  508.             $format[] = $line;
  509.         }
  510.         return $format;
  511.     }
  512.  
  513.     /**
  514.     * @deprecated
  515.     */
  516.     function get_overview_fmt() {
  517.         return $this->getOverviewFmt();
  518.     }
  519.  
  520.     /**
  521.     * Fetch message header
  522.     * from message number $first until $last
  523.     *
  524.     * The format of the returned array is:
  525.     * $messages[message_id][header_name]
  526.     *
  527.     * @param integer $first first article to fetch
  528.     * @param integer $last  last article to fetch
  529.     * @return array  nested array of message and
  530.     *                there headers
  531.     * @access public
  532.     */
  533.     function getOverview($first,$last) {
  534.         $format = $this->getOverviewFmt();
  535.  
  536.         $this->command("XOVER $first-$last");
  537.         foreach($this->_getData() as $line) {
  538.             $i=0;
  539.             foreach(explode("\t",$line) as $line) {
  540.                 $message[$format[$i++]] = $line;
  541.             }
  542.             $messages[$message["Message-ID"]] = $message;
  543.         }
  544.  
  545.         return $messages;
  546.     }
  547.  
  548.     /**
  549.     * @deprecated
  550.     */
  551.     function get_overview($first,$last) {
  552.         return $this->getOverview($first, $last);
  553.     }
  554.  
  555.     /**
  556.      * Get the date from the newsserver
  557.      * format of returned date:
  558.      * $date['y'] - year
  559.      * $date['m'] - month
  560.      * $date['d'] - day
  561.      *
  562.      * @return array date
  563.      * @access public
  564.      */
  565.     function date()
  566.     {
  567.         $r = $this->command('DATE');
  568.         if (PEAR::isError($r) || $this->responseCode($r) > 299) {
  569.             return $this->raiseError($r);
  570.         }
  571.         $data = explode(' ',$r);
  572.         $date['y']=substr($data[1],0,4);
  573.         $date['m']=substr($data[1],4,2);
  574.         $date['d']=substr($data[1],6,2);
  575.         return $date;
  576.     }
  577.  
  578.     /**
  579.      * Maximum article number in current group
  580.      *
  581.      * @return integer maximum
  582.      * @access public
  583.      */
  584.     function max()
  585.     {
  586.         if (!@is_resource($this->fp)) {
  587.             return $this->raiseError('Not connected');
  588.         }
  589.         return $this->max;
  590.     }
  591.  
  592.     /**
  593.      * Minimum article number in current group
  594.      *
  595.      * @return integer minimum
  596.      * @access public
  597.      */
  598.     function min()
  599.     {
  600.         if (!@is_resource($this->fp)) {
  601.             return $this->raiseError('Not connected');
  602.         }
  603.         return $this->min;
  604.     }
  605.  
  606.     /**
  607.      * Test whether we are connected or not.
  608.      *
  609.      * @return bool true or false
  610.      * @access public
  611.      */
  612.     function isConnected()
  613.     {
  614.         if (@is_resource($this->fp)) {
  615.             return true;
  616.         }
  617.         return false;
  618.     }
  619.  
  620.     /**
  621.     * @deprecated
  622.     */
  623.     function is_connected() {
  624.         return $this->isConnected();
  625.     }
  626.  
  627.     /**
  628.      * Close connection to the newsserver
  629.      *
  630.      * @access public
  631.      */
  632.     function quit()
  633.     {
  634.         $this->command("QUIT");
  635.         fclose($this->fp);
  636.     }
  637.  
  638.     /**
  639.     * returns the response code
  640.     * of a newsserver command
  641.     *
  642.     * @param string $response newsserver answer
  643.     * @return integer response code
  644.     * @access public
  645.     */
  646.     function responseCode($response)
  647.     {
  648.         $parts = explode(' ', ltrim($response));
  649.         return (int) $parts[0];
  650.     }
  651.  
  652.     /**
  653.     * sets debug on or off
  654.     *
  655.     * @param boolean $on true=on, false=off
  656.     * @access public
  657.     */
  658.     function setDebug($on = true)
  659.     {
  660.         $this->_debug = $on;
  661.     }
  662.  
  663.     /**
  664.     * @deprecated
  665.     */
  666.     function set_debug($on = true) {
  667.         return $this->setDebug();
  668.     }
  669.  
  670.     /**
  671.     * Issue a command to the NNTP server
  672.     *
  673.     * @param string $cmd The command to launch, ie: "ARTICLE 1004853"
  674.     * @param bool $testauth Test or not the auth
  675.     * @return mixed True on success or Pear Error object on failure
  676.     * @access public
  677.     */
  678.     function command($cmd, $testauth = true)
  679.     {
  680.         if (!@is_resource($this->fp)) {
  681.             return $this->raiseError('Not connected');
  682.         }
  683.         fputs($this->fp, "$cmd\r\n");
  684.         if ($this->_debug) {
  685.             print ">> $cmd\n";
  686.         }
  687.         $response = fgets($this->fp, 128);
  688.         if ($this->_debug) {
  689.             print "<< $response\n";
  690.         }
  691.         // From the spec: "In all cases, clients must provide
  692.         // this information when requested by the server. Servers are
  693.         // not required to accept authentication information that is
  694.         // volunteered by the client"
  695.         $code = $this->responseCode($response);
  696.         if ($testauth && ($code == 450 || $code == 480)) {
  697.             $error = $this->authenticate($this->user, $this->pass, $this->authmode);
  698.             if (PEAR::isError($error)) {
  699.                 return $error;
  700.             }
  701.             /* re-issue the command */
  702.             $response = $this->command($cmd, false);
  703.         }
  704.         return $response;
  705.     }
  706. }
  707. ?>
  708.