home *** CD-ROM | disk | FTP | other *** search
- <?php
- //
- // +----------------------------------------------------------------------+
- // | PHP Version 4 |
- // +----------------------------------------------------------------------+
- // | Copyright (c) 1997-2003 The PHP Group |
- // +----------------------------------------------------------------------+
- // | This source file is subject to version 2.0 of the PHP license, |
- // | that is bundled with this package in the file LICENSE, and is |
- // | available at through the world-wide-web at |
- // | http://www.php.net/license/2_02.txt. |
- // | If you did not receive a copy of the PHP license and are unable to |
- // | obtain it through the world-wide-web, please send a note to |
- // | license@php.net so we can mail you a copy immediately. |
- // +----------------------------------------------------------------------+
- // | Authors: Martin Kaltoft <martin@nitro.dk> |
- // | Tomas V.V.Cox <cox@idecnet.com> |
- // | Heino H. Gehlsen <heino@gehlsen.dk> |
- // +----------------------------------------------------------------------+
- //
- // $Id: NNTP.php,v 1.15.2.3 2003/09/12 23:33:58 heino Exp $
-
- require_once 'PEAR.php';
-
- // Deprecated due to naming
- define('PEAR_NNTP_ALL', 0);
- define('PEAR_NNTP_NAMES', 1);
- define('PEAR_NNTP_LIST', 2);
-
- /* NNTP Authentication modes */
- define('NET_NNTP_AUTHORIGINAL', 'original');
- define('NET_NNTP_AUTHSIMPLE', 'simple');
- define('NET_NNTP_AUTHGENERIC', 'generic');
-
- // Deprecated due to naming
- define('PEAR_NNTP_AUTHORIGINAL', NET_NNTP_AUTHORIGINAL);
- define('PEAR_NNTP_AUTHSIMPLE', NET_NNTP_AUTHSIMPLE);
- define('PEAR_NNTP_AUTHGENERIC', NET_NNTP_AUTHGENERIC);
-
- /**
- * The NNTP:: class fetches UseNet news articles acording to the standard
- * based on RFC 1036.
- *
- * @version 0.2.2
- * @author Martin Kaltoft <martin@nitro.dk>
- * @author Tomas V.V.Cox <cox@idecnet.com>
- * @author Heino H. Gehlsen <heino@gehlsen.dk>
- */
-
- class Net_NNTP extends PEAR
- {
-
- var $max = '';
- var $min = '';
- var $user = null;
- var $pass = null;
- var $authmode = null;
-
- /** File pointer of the nntp-connection */
- var $fp = null;
-
- /**
- * Connect to the newsserver
- *
- * @param string $nntpserver The adress of the NNTP-server to connect to.
- * @param int $port (optional) the port-number to connect to, defaults to 119.
- * @param string $user (optional) The user name to authenticate with
- * @param string $pass (optional) The password
- * @param string $authmode (optional) The authentication mode
- * @return mixed True on success or Pear Error object on failure
- * @see Net_Nntp::authenticate()
- * @access public
- */
- function connect($nntpserver,
- $port = 119,
- $user = null,
- $pass = null,
- $authmode = NET_NNTP_AUTHORIGINAL)
- {
- $fp = @fsockopen($nntpserver, $port, $errno, $errstr, 15);
- if (!is_resource($fp)) {
- return $this->raiseError("Could not connect to NNTP-server $nntpserver");
- }
- socket_set_blocking($fp, true);
- if (!$fp) {
- return $this->raiseError('Not connected');
- }
- $response = fgets($fp, 256);
- if ($this->_debug) {
- print "<< $response\n";
- }
- $this->fp = $fp;
- $this->user = $user;
- $this->pass = $pass;
- $this->authmode = $authmode;
-
- return true;
- }
-
- /**
- * Connect to the newsserver, and issue a GROUP command
- * Once connection is prepared, we can only fetch articles from one group
- * at a time, to fetch from another group, a new connection has to be made.
- *
- * This is to avoid the GROUP command for every article, as it is very
- * ressource intensive on the newsserver especially when used for
- * groups with many articles.
- *
- * @param string $nntpserver The adress of the NNTP-server to connect to.
- * @param int $port (optional) the port-number to connect to, defaults to 119.
- * @param string $newsgroup The name of the newsgroup to use.
- * @param string $user (optional) The user name to authenticate with
- * @param string $pass (optional) The password
- * @param string $authmode (optional) The authentication mode
- * @return mixed True on success or Pear Error object on failure
- * @see Net_Nntp::authenticate()
- * @access public
- * @deprecated Use connect() instead
- */
- function prepareConnection($nntpserver,
- $port = 119,
- $newsgroup,
- $user = null,
- $pass = null,
- $authmode = NET_NNTP_AUTHORIGINAL)
- {
- /* connect to the server */
- $err = $this->connect($nntpserver, $port, $user, $pass, $authmode);
- if (PEAR::isError($err)) {
- return $err;
- }
-
- /* issue a GROUP command */
- $r = $this->command("GROUP $newsgroup");
-
- if (PEAR::isError($r) || $this->responseCode($r) > 299) {
- return $this->raiseError($r);
- }
- $response_arr = split(' ', $r);
- $this->max = $response_arr[3];
- $this->min = $response_arr[2];
-
- return true;
- }
-
- /**
- * @deprecated
- */
- function prepare_connection($nntpserver,
- $port = 119,
- $newsgroup,
- $user = null,
- $pass = null,
- $authmode = NET_NNTP_AUTHORIGINAL)
- {
- return $this->prepareConnection($nntpserver, $port, $newsgroup, $user, $pass, $authmode);
- }
-
- /**
- * Auth process (not yet standarized but used any way)
- * http://www.mibsoftware.com/userkt/nntpext/index.html
- *
- * @param string $user The user name
- * @param string $pass (optional) The password if needed
- * @param string $mode Authinfo form: original, simple, generic
- * @return mixed (bool) true on success or Pear Error obj on fail
- * @access public
- */
- function authenticate($user = null, $pass = null, $mode = NET_NNTP_AUTHORIGINAL)
- {
- if ($user === null) {
- return $this->raiseError('Authentication required but no user supplied');
- }
- switch ($mode) {
- case NET_NNTP_AUTHORIGINAL:
- /*
- 281 Authentication accepted
- 381 More authentication information required
- 480 Authentication required
- 482 Authentication rejected
- 502 No permission
- */
- $response = $this->command("AUTHINFO user $user", false);
- if ($this->responseCode($response) != 281) {
- if ($this->responseCode($response) == 381 && $pass !== null) {
- $response = $this->command("AUTHINFO pass $pass", false);
- }
- }
- if ($this->responseCode($response) != 281) {
- return $this->raiseError("Authentication failed: $response");
- }
- return true;
- break;
- case NET_NNTP_AUTHSIMPLE:
- case NET_NNTP_AUTHGENERIC:
- default:
- $this->raiseError("The auth mode: $mode isn't implemented");
- }
- }
-
- /**
- * Get an article from the currently open connection.
- * To get articles from another newsgroup a new prepare_connection() -
- * call has to be made with apropriate parameters
- *
- * @param mixed $article Either the message-id or the message-number on the server of the article to fetch
- * @return string the article
- * @access public
- */
- function getArticle($article)
- {
- /* tell the newsserver we want an article */
- $r = $this->command("ARTICLE $article");
- if (PEAR::isError($r) || $this->responseCode($r) > 299) {
- return $this->raiseError($r);
- }
- $post = null;
- while (!feof($this->fp)) {
- $line = rtrim(fgets($this->fp, 256), "\r\n");
-
- if ($line == ".") {
- break;
- } else {
- $post .= $line ."\n";
- }
- }
- return $post;
- }
-
- /**
- * @deprecated
- */
- function get_article($article)
- {
- return $this->getArticle($article);
- }
-
- /**
- * Post an article to a newsgroup.
- * Among the aditional headers you might think of adding could be:
- * "NNTP-Posting-Host: <ip-of-author>", which should contain the IP-adress
- * of the author of the post, so the message can be traced back to him.
- * Or "Organization: <org>" which contain the name of the organization
- * the post originates from.
- *
- * @param string $subject The subject of the post.
- * @param string $newsgroup The newsgroup to post to.
- * @param string $from Name + email-adress of sender.
- * @param string $body The body of the post itself.
- * @param string $aditionak (optional) Aditional headers to send.
- * @return string server response
- * @access public
- */
- function post($subject, $newsgroup, $from, $body, $aditional = "")
- {
- if (!@is_resource($this->fp)) {
- return $this->raiseError('Not connected');
- }
-
- /* tell the newsserver we want to post an article */
- fputs($this->fp, "POST\n");
-
- /* The servers' response */
- $response = rtrim(fgets($this->fp, 128), "\r\n");
-
- fputs($this->fp, "From: $from\n");
- fputs($this->fp, "Newsgroups: $newsgroup\n");
- fputs($this->fp, "Subject: $subject\n");
- fputs($this->fp, "X-poster: nntp_fetcher (0.1) by Martin Kaltoft\n");
- fputs($this->fp, "$aditional\n");
- fputs($this->fp, "\n$body\n.\n");
-
- /* The servers' response */
- $response = rtrim(fgets($this->fp, 128), "\r\n");
-
- return $response;
- }
-
-
- /**
- * Get the headers of an article from the currently open connection
- * To get the headers of an article from another newsgroup, a new
- * prepare_connection()-call has to be made with apropriate parameters
- *
- * @param string $article Either a message-id or a message-number of the article to fetch the headers from.
- * @return array Header
- * @access public
- */
- function getHeaders($article)
- {
- /* tell the newsserver we want an article */
- $r = $this->command("HEAD $article");
- if (PEAR::isError($r) || $this->responseCode($r) > 299) {
- return $this->raiseError($r);
- }
-
- $headers = '';
- while(!feof($this->fp)) {
- $line = rtrim(fgets($this->fp, 256), "\r\n");
-
- if ($line == '.') {
- break;
- } else {
- $headers .= $line . "\n";
- }
- }
- return $headers;
- }
-
- /**
- * @deprecated
- */
- function get_headers($article)
- {
- return $this->getHeaders($article);
- }
-
- /**
- * Returns the headers of a given article in the form of
- * an associative array. Ex:
- * array(
- * 'From' => 'foo@bar.com (Foo Smith)',
- * 'Subject' => 'Re: Using NNTP class',
- * ....
- * );
- *
- * @param $article string Article number or id
- * @return array Assoc array with headers names as key or Pear obj error
- * @access public
- */
- function splitHeaders($article)
- {
- $headers = $this->get_headers($article);
- if (PEAR::isError($headers)) {
- return $headers;
- }
-
- $lines = explode("\n", $headers);
- foreach ($lines as $line) {
- $line = trim($line);
- if (($pos = strpos($line, ':')) !== false) {
- $head = substr($line, 0, $pos);
- $ret[$head] = ltrim(substr($line, $pos+1));
- // if the field was longer than 256 chars, look also in the next line
- // XXX a better way to discover that than strpos?
- } else {
- $ret[$head] .= $line;
- }
- }
- if (isset($ret['References']) &&
- preg_match_all('|<.+>|U', $ret['References'], $matches))
- {
- $ret['References'] = $matches[0];
- }
- return $ret;
- }
-
- /**
- * @deprecated
- */
- function split_headers($article) {
- return $this->splitHeaders($article);
- }
-
- /**
- * Get the body of an article from the currently open connection.
- * To get the body of an article from another newsgroup, a new
- * prepare_connection()-call has to be made with apropriate parameters
- *
- * @param string $article Either a message-id or a message-number of the article to fetch the headers from.
- * @access public
- */
- function getBody($article)
- {
- /* tell the newsserver we want an article */
- $r = $this->command("BODY $article");
- if (PEAR::isError($r) || $this->responseCode($r) > 299) {
- return $this->raiseError($r);
- }
-
- $body = null;
- while (!feof($this->fp)) {
- $line = rtrim(fgets($this->fp, 256), "\r\n");
-
- if ($line == '.') {
- break;
- } else {
- $body .= $line ."\n";
- }
- }
- return $body;
- }
-
- /**
- * @deprecated
- */
- function get_body($article) {
- return $this->getBody($article);
- }
-
- /**
- * Get data until a line with only a '.' in it is read and return data.
- *
- * @return string data
- * @access private
- * @author Morgan Christiansson <mog@linux.nu>
- */
- function _getData()
- {
- $body = array();
- while(!feof($this->fp)) {
- $line = rtrim(fgets($this->fp, 256), "\r\n");
- if ($line == '.') {
- break;
- } else {
- $body[] = $line;
- }
- }
- return $body;
- }
-
- /**
- * @deprecated
- */
- function get_data() {
- return $this->_getData();
- }
-
- /**
- * Selects a news group (issue a GROUP command to the server)
- *
- * @param string $newsgroup The newsgroup name
- * @return mixed Array on success or Pear Error object on failure
- */
- function selectGroup($newsgroup)
- {
- $r = $this->command("GROUP $newsgroup");
- if (PEAR::isError($r) || $this->responseCode($r) > 299) {
- return $this->raiseError($r);
- }
- $response_arr = split(' ', $r);
- $this->max = $response_arr[3];
- $this->min = $response_arr[2];
-
- return array(
- "first" => $response_arr[2],
- "last" => $response_arr[3]
- );
- }
-
- /**
- * @deprecated
- */
- function select_group($newsgroup) {
- return $this->selectGroup($newsgroup);
- }
-
- /**
- * Fetches a list of all avaible newsgroups
- *
- * @param int $fetch PEAR_NNTP_ALL PEAR_NNTP_NAMES PEAR_NNTP_LIST
- * @return array nested array with informations about
- * existing newsgroups
- * @author Morgan Christiansson <mog@linux.nu>
- */
- function getGroups($fetch = true)
- {
- $this->command("LIST");
- foreach($this->_getData() as $line) {
- $arr = explode(" ",$line);
- $groups[$arr[0]]["group"] = $arr[0];
- $groups[$arr[0]]["last"] = $arr[1];
- $groups[$arr[0]]["first"] = $arr[2];
- $groups[$arr[0]]["posting_allowed"] = $arr[3];
- }
-
- $this->command("LIST NEWSGROUPS");
- foreach($this->_getData() as $line) {
- preg_match("/^(.*?)\s(.*?$)/",$line,$matches);
- $groups[$matches[1]]["desc"] = $matches[2];
- }
- return $groups;
- }
-
- /**
- * @deprecated
- */
- function get_groups($fetch=true) {
- return $this->getGroups($fetch);
- }
-
- /**
- * Returns a list of avaible headers
- * which are send from newsserver to client
- * for every news message
- *
- * @return array header names
- * @access public
- */
- function getOverviewFmt()
- {
- $this->command("LIST OVERVIEW.FMT");
- $format = array("number");
- // XXX Use the splitHeaders() algorithm for supporting
- // multiline headers?
- foreach ($body = $this->_getData() as $line) {
- $line = current(explode(":",$line));
- $format[] = $line;
- }
- return $format;
- }
-
- /**
- * @deprecated
- */
- function get_overview_fmt() {
- return $this->getOverviewFmt();
- }
-
- /**
- * Fetch message header
- * from message number $first until $last
- *
- * The format of the returned array is:
- * $messages[message_id][header_name]
- *
- * @param integer $first first article to fetch
- * @param integer $last last article to fetch
- * @return array nested array of message and
- * there headers
- * @access public
- */
- function getOverview($first,$last) {
- $format = $this->getOverviewFmt();
-
- $this->command("XOVER $first-$last");
- foreach($this->_getData() as $line) {
- $i=0;
- foreach(explode("\t",$line) as $line) {
- $message[$format[$i++]] = $line;
- }
- $messages[$message["Message-ID"]] = $message;
- }
-
- return $messages;
- }
-
- /**
- * @deprecated
- */
- function get_overview($first,$last) {
- return $this->getOverview($first, $last);
- }
-
- /**
- * Get the date from the newsserver
- * format of returned date:
- * $date['y'] - year
- * $date['m'] - month
- * $date['d'] - day
- *
- * @return array date
- * @access public
- */
- function date()
- {
- $r = $this->command('DATE');
- if (PEAR::isError($r) || $this->responseCode($r) > 299) {
- return $this->raiseError($r);
- }
- $data = explode(' ',$r);
- $date['y']=substr($data[1],0,4);
- $date['m']=substr($data[1],4,2);
- $date['d']=substr($data[1],6,2);
- return $date;
- }
-
- /**
- * Maximum article number in current group
- *
- * @return integer maximum
- * @access public
- */
- function max()
- {
- if (!@is_resource($this->fp)) {
- return $this->raiseError('Not connected');
- }
- return $this->max;
- }
-
- /**
- * Minimum article number in current group
- *
- * @return integer minimum
- * @access public
- */
- function min()
- {
- if (!@is_resource($this->fp)) {
- return $this->raiseError('Not connected');
- }
- return $this->min;
- }
-
- /**
- * Test whether we are connected or not.
- *
- * @return bool true or false
- * @access public
- */
- function isConnected()
- {
- if (@is_resource($this->fp)) {
- return true;
- }
- return false;
- }
-
- /**
- * @deprecated
- */
- function is_connected() {
- return $this->isConnected();
- }
-
- /**
- * Close connection to the newsserver
- *
- * @access public
- */
- function quit()
- {
- $this->command("QUIT");
- fclose($this->fp);
- }
-
- /**
- * returns the response code
- * of a newsserver command
- *
- * @param string $response newsserver answer
- * @return integer response code
- * @access public
- */
- function responseCode($response)
- {
- $parts = explode(' ', ltrim($response));
- return (int) $parts[0];
- }
-
- /**
- * sets debug on or off
- *
- * @param boolean $on true=on, false=off
- * @access public
- */
- function setDebug($on = true)
- {
- $this->_debug = $on;
- }
-
- /**
- * @deprecated
- */
- function set_debug($on = true) {
- return $this->setDebug();
- }
-
- /**
- * Issue a command to the NNTP server
- *
- * @param string $cmd The command to launch, ie: "ARTICLE 1004853"
- * @param bool $testauth Test or not the auth
- * @return mixed True on success or Pear Error object on failure
- * @access public
- */
- function command($cmd, $testauth = true)
- {
- if (!@is_resource($this->fp)) {
- return $this->raiseError('Not connected');
- }
- fputs($this->fp, "$cmd\r\n");
- if ($this->_debug) {
- print ">> $cmd\n";
- }
- $response = fgets($this->fp, 128);
- if ($this->_debug) {
- print "<< $response\n";
- }
- // From the spec: "In all cases, clients must provide
- // this information when requested by the server. Servers are
- // not required to accept authentication information that is
- // volunteered by the client"
- $code = $this->responseCode($response);
- if ($testauth && ($code == 450 || $code == 480)) {
- $error = $this->authenticate($this->user, $this->pass, $this->authmode);
- if (PEAR::isError($error)) {
- return $error;
- }
- /* re-issue the command */
- $response = $this->command($cmd, false);
- }
- return $response;
- }
- }
- ?>
-