Viewing file: Client.php (69.24 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
/** * * * PHP versions 4 and 5 * * <pre> * +-----------------------------------------------------------------------+ * | | * | W3CŪ SOFTWARE NOTICE AND LICENSE | * | http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 | * | | * | This work (and included software, documentation such as READMEs, | * | or other related items) is being provided by the copyright holders | * | under the following license. By obtaining, using and/or copying | * | this work, you (the licensee) agree that you have read, understood, | * | and will comply with the following terms and conditions. | * | | * | Permission to copy, modify, and distribute this software and its | * | documentation, with or without modification, for any purpose and | * | without fee or royalty is hereby granted, provided that you include | * | the following on ALL copies of the software and documentation or | * | portions thereof, including modifications: | * | | * | 1. The full text of this NOTICE in a location viewable to users | * | of the redistributed or derivative work. | * | | * | 2. Any pre-existing intellectual property disclaimers, notices, | * | or terms and conditions. If none exist, the W3C Software Short | * | Notice should be included (hypertext is preferred, text is | * | permitted) within the body of any redistributed or derivative | * | code. | * | | * | 3. Notice of any changes or modifications to the files, including | * | the date changes were made. (We recommend you provide URIs to | * | the location from which the code is derived.) | * | | * | THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT | * | HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, | * | INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR | * | FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE | * | OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, | * | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. | * | | * | COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, | * | SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE | * | SOFTWARE OR DOCUMENTATION. | * | | * | The name and trademarks of copyright holders may NOT be used in | * | advertising or publicity pertaining to the software without | * | specific, written prior permission. Title to copyright in this | * | software and any associated documentation will at all times | * | remain with copyright holders. | * | | * +-----------------------------------------------------------------------+ * </pre> * * @category Net * @package Net_NNTP * @author Heino H. Gehlsen <heino@gehlsen.dk> * @copyright 2002-2011 Heino H. Gehlsen <heino@gehlsen.dk>. All Rights Reserved. * @license http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 W3CŪ SOFTWARE NOTICE AND LICENSE * @version SVN: $Id: Client.php 330426 2013-05-31 14:41:46Z janpascal $ * @link http://pear.php.net/package/Net_NNTP * @see */
// Warn about PHP bugs if (version_compare(PHP_VERSION, '5.2.11') === 0) { trigger_error('PHP bug #16657 breaks feof() on socket streams! Connection consistency might be compromised!', E_USER_WARNING); }
/** * */ require_once 'PEAR.php'; //require_once 'Net/NNTP/Error.php'; require_once 'Net/NNTP/Protocol/Responsecode.php';
// {{{ constants
/** * Default host * * @access public * @ignore */ define('NET_NNTP_PROTOCOL_CLIENT_DEFAULT_HOST', 'localhost');
/** * Default port * * @access public * @ignore */ define('NET_NNTP_PROTOCOL_CLIENT_DEFAULT_PORT', '119');
// }}} // {{{ Net_NNTP_Protocol_Client
/** * Low level NNTP Client * * Implements the client part of the NNTP standard acording to: * - RFC 977, * - RFC 2980, * - RFC 850/1036, and * - RFC 822/2822 * * Each NNTP command is represented by a method: cmd*() * * WARNING: The Net_NNTP_Protocol_Client class is considered an internal class * (and should therefore currently not be extended directly outside of * the Net_NNTP package). Therefore its API is NOT required to be fully * stable, for as long as such changes doesn't affect the public API of * the Net_NNTP_Client class, which is considered stable. * * TODO: cmdListActiveTimes() * cmdDistribPats() * * @category Net * @package Net_NNTP * @author Heino H. Gehlsen <heino@gehlsen.dk> * @version package: 1.5.0 (stable) * @version api: 0.9.0 (alpha) * @access private * @see Net_NNTP_Client */ class Net_NNTP_Protocol_Client extends PEAR { // {{{ properties
/** * The socket resource being used to connect to the NNTP server. * * @var resource * @access private */ var $_socket = null;
/** * Contains the last recieved status response code and text * * @var array * @access private */ var $_currentStatusResponse = null;
/** * * * @var object * @access private */ var $_logger = null;
// }}} // {{{ constructor
/** * Constructor * * @access public */ function Net_NNTP_Protocol_Client() {
// // parent::PEAR('Net_NNTP_Error'); parent::PEAR(); }
// }}} // {{{ getPackageVersion()
/** * * * @access public */ function getPackageVersion() { return '1.5.0'; }
// }}} // {{{ getApiVersion()
/** * * * @access public */ function getApiVersion() { return '0.9.0'; }
// }}} // {{{ setLogger()
/** * * * @param object $logger * * @access protected */ function setLogger($logger) { $this->_logger = $logger; }
// }}} // {{{ setDebug()
/** * @deprecated */ function setDebug($debug = true) { trigger_error('You are using deprecated API v1.0 in Net_NNTP_Protocol_Client: setDebug() ! Debugging in now automatically handled when a logger is given.', E_USER_NOTICE); }
// }}} // {{{ _sendCommand()
/** * Send command * * Send a command to the server. A carriage return / linefeed (CRLF) sequence * will be appended to each command string before it is sent to the IMAP server. * * @param string $cmd The command to launch, ie: "ARTICLE 1004853" * * @return mixed (int) response code on success or (object) pear_error on failure * @access private */ function _sendCommand($cmd) { // NNTP/RFC977 only allows command up to 512 (-2) chars. if (!strlen($cmd) > 510) { return $this->throwError('Failed writing to socket! (Command to long - max 510 chars)'); }
/***************************************************************************************/ /* Credit: Thanks to Brendan Coles <bcoles@gmail.com> (http://itsecuritysolutions.org) */ /* for pointing out possibility to inject pipelined NNTP commands into pretty */ /* much any Net_NNTP command-sending function with user input, by appending */ /* a new line character followed by the injection. */ /***************************************************************************************/ // Prevent new line (and possible future) characters in the NNTP commands // Net_NNTP does not support pipelined commands. Inserting a new line charecter // allows sending multiple commands and thereby making the communication between // NET_NNTP and the server out of sync... if (preg_match_all('/\r?\n/', $cmd, $matches, PREG_PATTERN_ORDER)) { foreach ($matches[0] as $key => $match) { $this->_logger->debug("Illegal character in command: ". htmlentities(str_replace(array("\r","\n"), array("'Carriage Return'", "'New Line'"), $match))); } return $this->throwError("Illegal character(s) in NNTP command!"); }
// Check if connected if (!$this->_isConnected()) { return $this->throwError('Failed to write to socket! (connection lost!)'); }
// Send the command $R = @fwrite($this->_socket, $cmd . "\r\n"); if ($R === false) { return $this->throwError('Failed to write to socket!'); }
// if ($this->_logger && $this->_logger->_isMasked(PEAR_LOG_DEBUG)) { $this->_logger->debug('C: ' . $cmd); }
// return $this->_getStatusResponse(); }
// }}} // {{{ _getStatusResponse()
/** * Get servers status response after a command. * * @return mixed (int) statuscode on success or (object) pear_error on failure * @access private */ function _getStatusResponse() { // Retrieve a line (terminated by "\r\n") from the server. // RFC says max is 510, but IETF says "be liberal in what you accept"... $response = @fgets($this->_socket, 4096); if ($response === false) { return $this->throwError('Failed to read from socket...!'); }
// if ($this->_logger && $this->_logger->_isMasked(PEAR_LOG_DEBUG)) { $this->_logger->debug('S: ' . rtrim($response, "\r\n")); }
// Trim the start of the response in case of misplased whitespace (should not be needen!!!) $response = ltrim($response);
$this->_currentStatusResponse = array( (int) substr($response, 0, 3), (string) rtrim(substr($response, 4)) );
// return $this->_currentStatusResponse[0]; }
// }}} // {{{ _getTextResponse()
/** * Retrieve textural data * * Get data until a line with only a '.' in it is read and return data. * * @return mixed (array) text response on success or (object) pear_error on failure * @access private */ function _getTextResponse() { $data = array(); $line = '';
// $debug = $this->_logger && $this->_logger->_isMasked(PEAR_LOG_DEBUG);
// Continue until connection is lost while (!feof($this->_socket)) {
// Retrieve and append up to 1024 characters from the server. $recieved = @fgets($this->_socket, 1024);
if ($recieved === false) { return $this->throwError('Failed to read line from socket.', null); }
$line .= $recieved;
// Continue if the line is not terminated by CRLF if (substr($line, -2) != "\r\n" || strlen($line) < 2) { continue; }
// Validate recieved line if (false) { // Lines should/may not be longer than 998+2 chars (RFC2822 2.3) if (strlen($line) > 1000) { if ($this->_logger) { $this->_logger->notice('Max line length...'); } return $this->throwError('Invalid line recieved!', null); } }
// Remove CRLF from the end of the line $line = substr($line, 0, -2);
// Check if the line terminates the textresponse if ($line == '.') {
if ($this->_logger) { $this->_logger->debug('T: ' . $line); }
// return all previous lines return $data; }
// If 1st char is '.' it's doubled (NNTP/RFC977 2.4.1) if (substr($line, 0, 2) == '..') { $line = substr($line, 1); }
// if ($debug) { $this->_logger->debug('T: ' . $line); }
// Add the line to the array of lines $data[] = $line;
// Reset/empty $line $line = ''; }
if ($this->_logger) { $this->_logger->warning('Broke out of reception loop! This souldn\'t happen unless connection has been lost?'); }
// return $this->throwError('End of stream! Connection lost?', null); }
// }}} // {{{ _sendText()
/** * * * @access private */ function _sendArticle($article) { /* data should be in the format specified by RFC850 */
switch (true) { case is_string($article): // @fwrite($this->_socket, $article); @fwrite($this->_socket, "\r\n.\r\n");
// if ($this->_logger && $this->_logger->_isMasked(PEAR_LOG_DEBUG)) { foreach (explode("\r\n", $article) as $line) { $this->_logger->debug('D: ' . $line); } $this->_logger->debug('D: .'); } break;
case is_array($article): // $header = reset($article); $body = next($article);
/* Experimental... // If header is an array, implode it. if (is_array($header)) { $header = implode("\r\n", $header) . "\r\n"; } */
// Send header (including separation line) @fwrite($this->_socket, $header); @fwrite($this->_socket, "\r\n");
// if ($this->_logger && $this->_logger->_isMasked(PEAR_LOG_DEBUG)) { foreach (explode("\r\n", $header) as $line) { $this->_logger->debug('D: ' . $line); } }
/* Experimental... // If body is an array, implode it. if (is_array($body)) { $header = implode("\r\n", $body) . "\r\n"; } */
// Send body @fwrite($this->_socket, $body); @fwrite($this->_socket, "\r\n.\r\n");
// if ($this->_logger && $this->_logger->_isMasked(PEAR_LOG_DEBUG)) { foreach (explode("\r\n", $body) as $line) { $this->_logger->debug('D: ' . $line); } $this->_logger->debug('D: .'); } break;
default: return $this->throwError('Ups...', null, null); }
return true; }
// }}} // {{{ _currentStatusResponse()
/** * * * @return string status text * @access private */ function _currentStatusResponse() { return $this->_currentStatusResponse[1]; }
// }}} // {{{ _handleUnexpectedResponse()
/** * * * @param int $code Status code number * @param string $text Status text * * @return mixed * @access private */ function _handleUnexpectedResponse($code = null, $text = null) { if ($code === null) { $code = $this->_currentStatusResponse[0]; }
if ($text === null) { $text = $this->_currentStatusResponse(); }
switch ($code) { case NET_NNTP_PROTOCOL_RESPONSECODE_NOT_PERMITTED: // 502, 'access restriction or permission denied' / service permanently unavailable return $this->throwError('Command not permitted / Access restriction / Permission denied', $code, $text); break; default: return $this->throwError("Unexpected response: '$text'", $code, $text); } }
// }}}
/* Session administration commands */
// {{{ Connect()
/** * Connect to a NNTP server * * @param string $host (optional) The address of the NNTP-server to connect to, defaults to 'localhost'. * @param mixed $encryption (optional) * @param int $port (optional) The port number to connect to, defaults to 119. * @param int $timeout (optional) * * @return mixed (bool) on success (true when posting allowed, otherwise false) or (object) pear_error on failure * @access protected */ function connect($host = null, $encryption = null, $port = null, $timeout = null) { // if ($this->_isConnected() ) { return $this->throwError('Already connected, disconnect first!', null); }
// v1.0.x API if (is_int($encryption)) { trigger_error('You are using deprecated API v1.0 in Net_NNTP_Protocol_Client: connect() !', E_USER_NOTICE); $port = $encryption; $encryption = false; }
// if (is_null($host)) { $host = 'localhost'; }
// Choose transport based on encryption, and if no port is given, use default for that encryption switch ($encryption) { case null: case false: $transport = 'tcp'; $port = is_null($port) ? 119 : $port; break; case 'ssl': case 'tls': $transport = $encryption; $port = is_null($port) ? 563 : $port; break; default: trigger_error('$encryption parameter must be either tcp, tls or ssl.', E_USER_ERROR); }
// if (is_null($timeout)) { $timeout = 15; }
// Open Connection $R = stream_socket_client($transport . '://' . $host . ':' . $port, $errno, $errstr, $timeout); if ($R === false) { if ($this->_logger) { $this->_logger->notice("Connection to $transport://$host:$port failed."); } return $R; }
$this->_socket = $R;
// if ($this->_logger) { $this->_logger->info("Connection to $transport://$host:$port has been established."); }
// Retrive the server's initial response. $response = $this->_getStatusResponse(); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_READY_POSTING_ALLOWED: // 200, Posting allowed // TODO: Set some variable before return
return true; break; case NET_NNTP_PROTOCOL_RESPONSECODE_READY_POSTING_PROHIBITED: // 201, Posting NOT allowed // if ($this->_logger) { $this->_logger->info('Posting not allowed!'); }
// TODO: Set some variable before return
return false; break; case 400: return $this->throwError('Server refused connection', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NOT_PERMITTED: // 502, 'access restriction or permission denied' / service permanently unavailable return $this->throwError('Server refused connection', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ disconnect()
/** * alias for cmdQuit() * * @access protected */ function disconnect() { return $this->cmdQuit(); }
// }}} // {{{ cmdCapabilities()
/** * Returns servers capabilities * * @return mixed (array) list of capabilities on success or (object) pear_error on failure * @access protected */ function cmdCapabilities() { // tell the newsserver we want an article $response = $this->_sendCommand('CAPABILITIES'); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_CAPABILITIES_FOLLOW: // 101, Draft: 'Capability list follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; } return $data; break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdModeReader()
/** * * * @return mixed (bool) true when posting allowed, false when postind disallowed or (object) pear_error on failure * @access protected */ function cmdModeReader() { // tell the newsserver we want an article $response = $this->_sendCommand('MODE READER'); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_READY_POSTING_ALLOWED: // 200, RFC2980: 'Hello, you can post'
// TODO: Set some variable before return
return true; break; case NET_NNTP_PROTOCOL_RESPONSECODE_READY_POSTING_PROHIBITED: // 201, RFC2980: 'Hello, you can't post' if ($this->_logger) { $this->_logger->info('Posting not allowed!'); }
// TODO: Set some variable before return
return false; break; case NET_NNTP_PROTOCOL_RESPONSECODE_NOT_PERMITTED: // 502, 'access restriction or permission denied' / service permanently unavailable return $this->throwError('Connection being closed, since service so permanently unavailable', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdQuit()
/** * Disconnect from the NNTP server * * @return mixed (bool) true on success or (object) pear_error on failure * @access protected */ function cmdQuit() { // Tell the server to close the connection $response = $this->_sendCommand('QUIT'); if (PEAR::isError($response)) { return $response; }
switch ($response) { case 205: // RFC977: 'closing connection - goodbye!' // If socket is still open, close it. if ($this->_isConnected()) { fclose($this->_socket); }
if ($this->_logger) { $this->_logger->info('Connection closed.'); }
return true; break; default: return $this->_handleUnexpectedResponse($response); } }
// }}}
/* */
// {{{ cmdStartTLS()
/** * * * @return mixed (bool) on success or (object) pear_error on failure * @access protected */ function cmdStartTLS() { $response = $this->_sendCommand('STARTTLS'); if (PEAR::isError($response)) { return $response; }
switch ($response) { case 382: // RFC4642: 'continue with TLS negotiation' $encrypted = stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); switch (true) { case $encrypted === true: if ($this->_logger) { $this->_logger->info('TLS encryption started.'); } return true; break; case $encrypted === true: if ($this->_logger) { $this->_logger->info('TLS encryption failed.'); } return $this->throwError('Could not initiate TLS negotiation', $response, $this->_currentStatusResponse()); break; case is_int($encrypted): return $this->throwError('', $response, $this->_currentStatusResponse()); break; default: return $this->throwError('Internal error - unknown response from stream_socket_enable_crypto()', $response, $this->_currentStatusResponse()); } break; case 580: // RFC4642: 'can not initiate TLS negotiation' return $this->throwError('', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}}
/* Article posting and retrieval */
/* Group and article selection */
// {{{ cmdGroup()
/** * Selects a news group (issue a GROUP command to the server) * * @param string $newsgroup The newsgroup name * * @return mixed (array) groupinfo on success or (object) pear_error on failure * @access protected */ function cmdGroup($newsgroup) { $response = $this->_sendCommand('GROUP '.$newsgroup); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_GROUP_SELECTED: // 211, RFC977: 'n f l s group selected' $response_arr = explode(' ', trim($this->_currentStatusResponse()));
if ($this->_logger) { $this->_logger->info('Group selected: '.$response_arr[3]); }
return array('group' => $response_arr[3], 'first' => $response_arr[1], 'last' => $response_arr[2], 'count' => $response_arr[0]); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_GROUP: // 411, RFC977: 'no such news group' return $this->throwError('No such news group', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdListgroup()
/** * * * @param optional string $newsgroup * @param optional mixed $range * * @return optional mixed (array) on success or (object) pear_error on failure * @access protected */ function cmdListgroup($newsgroup = null, $range = null) { if (is_null($newsgroup)) { $command = 'LISTGROUP'; } else { if (is_null($range)) { $command = 'LISTGROUP ' . $newsgroup; } else { $command = 'LISTGROUP ' . $newsgroup . ' ' . $range; } }
$response = $this->_sendCommand($command); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_GROUP_SELECTED: // 211, RFC2980: 'list of article numbers follow'
$articles = $this->_getTextResponse(); if (PEAR::isError($articles)) { return $articles; }
$response_arr = explode(' ', trim($this->_currentStatusResponse()), 4);
// If server does not return group summary in status response, return null'ed array if (!is_numeric($response_arr[0]) || !is_numeric($response_arr[1]) || !is_numeric($response_arr[2]) || empty($response_arr[3])) { return array('group' => null, 'first' => null, 'last' => null, 'count' => null, 'articles' => $articles); }
return array('group' => $response_arr[3], 'first' => $response_arr[1], 'last' => $response_arr[2], 'count' => $response_arr[0], 'articles' => $articles); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC2980: 'Not currently in newsgroup' return $this->throwError('Not currently in newsgroup', $response, $this->_currentStatusResponse()); break; case 502: // RFC2980: 'no permission' return $this->throwError('No permission', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdLast()
/** * * * @return mixed (array) or (string) or (int) or (object) pear_error on failure * @access protected */ function cmdLast() { // $response = $this->_sendCommand('LAST'); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_ARTICLE_SELECTED: // 223, RFC977: 'n a article retrieved - request text separately (n = article number, a = unique article id)' $response_arr = explode(' ', trim($this->_currentStatusResponse()));
if ($this->_logger) { $this->_logger->info('Selected previous article: ' . $response_arr[0] .' - '. $response_arr[1]); }
return array($response_arr[0], (string) $response_arr[1]); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC977: 'no newsgroup selected' return $this->throwError('No newsgroup has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED: // 420, RFC977: 'no current article has been selected' return $this->throwError('No current article has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_PREVIOUS_ARTICLE: // 422, RFC977: 'no previous article in this group' return $this->throwError('No previous article in this group', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdNext()
/** * * * @return mixed (array) or (string) or (int) or (object) pear_error on failure * @access protected */ function cmdNext() { // $response = $this->_sendCommand('NEXT'); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_ARTICLE_SELECTED: // 223, RFC977: 'n a article retrieved - request text separately (n = article number, a = unique article id)' $response_arr = explode(' ', trim($this->_currentStatusResponse()));
if ($this->_logger) { $this->_logger->info('Selected previous article: ' . $response_arr[0] .' - '. $response_arr[1]); }
return array($response_arr[0], (string) $response_arr[1]); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC977: 'no newsgroup selected' return $this->throwError('No newsgroup has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED: // 420, RFC977: 'no current article has been selected' return $this->throwError('No current article has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_NEXT_ARTICLE: // 421, RFC977: 'no next article in this group' return $this->throwError('No next article in this group', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}}
/* Retrieval of articles and article sections */
// {{{ cmdArticle()
/** * Get an article from the currently open connection. * * @param mixed $article Either a message-id or a message-number of the article to fetch. If null or '', then use current article. * * @return mixed (array) article on success or (object) pear_error on failure * @access protected */ function cmdArticle($article = null) { if (is_null($article)) { $command = 'ARTICLE'; } else { $command = 'ARTICLE ' . $article; }
// tell the newsserver we want an article $response = $this->_sendCommand($command); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_ARTICLE_FOLLOWS: // 220, RFC977: 'n <a> article retrieved - head and body follow (n = article number, <a> = message-id)' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
if ($this->_logger) { $this->_logger->info(($article == null ? 'Fetched current article' : 'Fetched article: '.$article)); } return $data; break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC977: 'no newsgroup has been selected' return $this->throwError('No newsgroup has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED: // 420, RFC977: 'no current article has been selected' return $this->throwError('No current article has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_NUMBER: // 423, RFC977: 'no such article number in this group' return $this->throwError('No such article number in this group', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_ID: // 430, RFC977: 'no such article found' return $this->throwError('No such article found', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdHead()
/** * Get the headers of an article from the currently open connection. * * @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. * * @return mixed (array) headers on success or (object) pear_error on failure * @access protected */ function cmdHead($article = null) { if (is_null($article)) { $command = 'HEAD'; } else { $command = 'HEAD ' . $article; }
// tell the newsserver we want the header of an article $response = $this->_sendCommand($command); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_HEAD_FOLLOWS: // 221, RFC977: 'n <a> article retrieved - head follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
if ($this->_logger) { $this->_logger->info(($article == null ? 'Fetched current article header' : 'Fetched article header for article: '.$article)); }
return $data; break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC977: 'no newsgroup has been selected' return $this->throwError('No newsgroup has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED: // 420, RFC977: 'no current article has been selected' return $this->throwError('No current article has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_NUMBER: // 423, RFC977: 'no such article number in this group' return $this->throwError('No such article number in this group', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_ID: // 430, RFC977: 'no such article found' return $this->throwError('No such article found', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdBody()
/** * Get the body of an article from the currently open connection. * * @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. * * @return mixed (array) body on success or (object) pear_error on failure * @access protected */ function cmdBody($article = null) { if (is_null($article)) { $command = 'BODY'; } else { $command = 'BODY ' . $article; }
// tell the newsserver we want the body of an article $response = $this->_sendCommand($command); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_BODY_FOLLOWS: // 222, RFC977: 'n <a> article retrieved - body follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
if ($this->_logger) { $this->_logger->info(($article == null ? 'Fetched current article body' : 'Fetched article body for article: '.$article)); }
return $data; break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC977: 'no newsgroup has been selected' return $this->throwError('No newsgroup has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED: // 420, RFC977: 'no current article has been selected' return $this->throwError('No current article has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_NUMBER: // 423, RFC977: 'no such article number in this group' return $this->throwError('No such article number in this group', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_ID: // 430, RFC977: 'no such article found' return $this->throwError('No such article found', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdStat
/** * * * @param mixed $article * * @return mixed (array) or (string) or (int) or (object) pear_error on failure * @access protected */ function cmdStat($article = null) { if (is_null($article)) { $command = 'STAT'; } else { $command = 'STAT ' . $article; }
// tell the newsserver we want an article $response = $this->_sendCommand($command); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_ARTICLE_SELECTED: // 223, RFC977: 'n <a> article retrieved - request text separately' (actually not documented, but copied from the ARTICLE command) $response_arr = explode(' ', trim($this->_currentStatusResponse()));
if ($this->_logger) { $this->_logger->info('Selected article: ' . $response_arr[0].' - '.$response_arr[1]); }
return array($response_arr[0], (string) $response_arr[1]); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC977: 'no newsgroup has been selected' (actually not documented, but copied from the ARTICLE command) return $this->throwError('No newsgroup has been selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_NUMBER: // 423, RFC977: 'no such article number in this group' (actually not documented, but copied from the ARTICLE command) return $this->throwError('No such article number in this group', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_ID: // 430, RFC977: 'no such article found' (actually not documented, but copied from the ARTICLE command) return $this->throwError('No such article found', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}}
/* Article posting */
// {{{ cmdPost()
/** * Post an article to a newsgroup. * * @return mixed (bool) true on success or (object) pear_error on failure * @access protected */ function cmdPost() { // tell the newsserver we want to post an article $response = $this->_sendCommand('POST'); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_POSTING_SEND: // 340, RFC977: 'send article to be posted. End with <CR-LF>.<CR-LF>' return true; break; case NET_NNTP_PROTOCOL_RESPONSECODE_POSTING_PROHIBITED: // 440, RFC977: 'posting not allowed' return $this->throwError('Posting not allowed', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); }
}
// }}} // {{{ cmdPost2()
/** * Post an article to a newsgroup. * * @param mixed $article (string/array) * * @return mixed (bool) true on success or (object) pear_error on failure * @access protected */ function cmdPost2($article) { /* should be presented in the format specified by RFC850 */
// $this->_sendArticle($article);
// Retrive server's response. $response = $this->_getStatusResponse(); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_POSTING_SUCCESS: // 240, RFC977: 'article posted ok' return true; break; case NET_NNTP_PROTOCOL_RESPONSECODE_POSTING_FAILURE: // 441, RFC977: 'posting failed' return $this->throwError('Posting failed', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdIhave()
/** * * * @param string $id * * @return mixed (bool) true on success or (object) pear_error on failure * @access protected */ function cmdIhave($id) { // tell the newsserver we want to post an article $response = $this->_sendCommand('IHAVE ' . $id); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_SEND: // 335 return true; break; case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_UNWANTED: // 435 return $this->throwError('Article not wanted', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_FAILURE: // 436 return $this->throwError('Transfer not possible; try again later', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdIhave2()
/** * * * @param mixed $article (string/array) * * @return mixed (bool) true on success or (object) pear_error on failure * @access protected */ function cmdIhave2($article) { /* should be presented in the format specified by RFC850 */
// $this->_sendArticle($article);
// Retrive server's response. $response = $this->_getStatusResponse(); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_SUCCESS: // 235 return true; break; case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_FAILURE: // 436 return $this->throwError('Transfer not possible; try again later', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_TRANSFER_REJECTED: // 437 return $this->throwError('Transfer rejected; do not retry', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}}
/* Information commands */
// {{{ cmdDate()
/** * Get the date from the newsserver format of returned date * * @return mixed (string) 'YYYYMMDDhhmmss' / (int) timestamp on success or (object) pear_error on failure * @access protected */ function cmdDate() { $response = $this->_sendCommand('DATE'); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_SERVER_DATE: // 111, RFC2980: 'YYYYMMDDhhmmss' return $this->_currentStatusResponse(); break; default: return $this->_handleUnexpectedResponse($response); } } // }}} // {{{ cmdHelp()
/** * Returns the server's help text * * @return mixed (array) help text on success or (object) pear_error on failure * @access protected */ function cmdHelp() { // tell the newsserver we want an article $response = $this->_sendCommand('HELP'); if (PEAR::isError($response)) { return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_HELP_FOLLOWS: // 100 $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; } return $data; break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdNewgroups()
/** * Fetches a list of all newsgroups created since a specified date. * * @param int $time Last time you checked for groups (timestamp). * @param optional string $distributions (deprecaded in rfc draft) * * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure * @access protected */ function cmdNewgroups($time, $distributions = null) { $date = gmdate('ymd His', $time);
if (is_null($distributions)) { $command = 'NEWGROUPS ' . $date . ' GMT'; } else { $command = 'NEWGROUPS ' . $date . ' GMT <' . $distributions . '>'; }
$response = $this->_sendCommand($command); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_NEW_GROUPS_FOLLOW: // 231, REF977: 'list of new newsgroups follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
$groups = array(); foreach($data as $line) { $arr = explode(' ', trim($line));
$group = array('group' => $arr[0], 'last' => $arr[1], 'first' => $arr[2], 'posting' => $arr[3]);
$groups[$group['group']] = $group; } return $groups;
default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdNewnews()
/** * * * @param timestamp $time * @param mixed $newsgroups (string or array of strings) * @param mixed $distribution (string or array of strings) * * @return mixed * @access protected */ function cmdNewnews($time, $newsgroups, $distribution = null) { $date = gmdate('ymd His', $time);
if (is_array($newsgroups)) { $newsgroups = implode(',', $newsgroups); }
if (is_null($distribution)) { $command = 'NEWNEWS ' . $newsgroups . ' ' . $date . ' GMT'; } else { if (is_array($distribution)) { $distribution = implode(',', $distribution); }
$command = 'NEWNEWS ' . $newsgroups . ' ' . $date . ' GMT <' . $distribution . '>'; }
// TODO: the lenght of the request string may not exceed 510 chars
$response = $this->_sendCommand($command); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_NEW_ARTICLES_FOLLOW: // 230, RFC977: 'list of new articles by message-id follows' $messages = array(); foreach($this->_getTextResponse() as $line) { $messages[] = $line; } return $messages; break; default: return $this->_handleUnexpectedResponse($response); } }
// }}}
/* The LIST commands */
// {{{ cmdList()
/** * Fetches a list of all avaible newsgroups * * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure * @access protected */ function cmdList() { $response = $this->_sendCommand('LIST'); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_GROUPS_FOLLOW: // 215, RFC977: 'list of newsgroups follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
$groups = array(); foreach($data as $line) { $arr = explode(' ', trim($line));
$group = array('group' => $arr[0], 'last' => $arr[1], 'first' => $arr[2], 'posting' => $arr[3]);
$groups[$group['group']] = $group; } return $groups; break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdListActive()
/** * Fetches a list of all avaible newsgroups * * @param string $wildmat * * @return mixed (array) nested array with informations about existing newsgroups on success or (object) pear_error on failure * @access protected */ function cmdListActive($wildmat = null) { if (is_null($wildmat)) { $command = 'LIST ACTIVE'; } else { $command = 'LIST ACTIVE ' . $wildmat; }
$response = $this->_sendCommand($command); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_GROUPS_FOLLOW: // 215, RFC977: 'list of newsgroups follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
$groups = array(); foreach($data as $line) { $arr = explode(' ', trim($line));
$group = array('group' => $arr[0], 'last' => $arr[1], 'first' => $arr[2], 'posting' => $arr[3]);
$groups[$group['group']] = $group; }
if ($this->_logger) { $this->_logger->info('Fetched list of available groups'); }
return $groups; break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdListNewsgroups()
/** * Fetches a list of (all) avaible newsgroup descriptions. * * @param string $wildmat Wildmat of the groups, that is to be listed, defaults to null; * * @return mixed (array) nested array with description of existing newsgroups on success or (object) pear_error on failure * @access protected */ function cmdListNewsgroups($wildmat = null) { if (is_null($wildmat)) { $command = 'LIST NEWSGROUPS'; } else { $command = 'LIST NEWSGROUPS ' . $wildmat; }
$response = $this->_sendCommand($command); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_GROUPS_FOLLOW: // 215, RFC2980: 'information follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
$groups = array();
foreach($data as $line) { if (preg_match("/^(\S+)\s+(.*)$/", ltrim($line), $matches)) { $groups[$matches[1]] = (string) $matches[2]; } else { if ($this->_logger) { $this->_logger->warning("Recieved non-standard line: '$line'"); } } }
if ($this->_logger) { $this->_logger->info('Fetched group descriptions'); }
return $groups; break; case 503: // RFC2980: 'program error, function not performed' return $this->throwError('Internal server error, function not performed', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}}
/* Article field access commands */
// {{{ cmdOver()
/** * Fetch message header from message number $first until $last * * The format of the returned array is: * $messages[][header_name] * * @param optional string $range articles to fetch * * @return mixed (array) nested array of message and there headers on success or (object) pear_error on failure * @access protected */ function cmdOver($range = null) { if (is_null($range)) { $command = 'OVER'; } else { $command = 'OVER ' . $range; }
$response = $this->_sendCommand($command); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_OVERVIEW_FOLLOWS: // 224, RFC2980: 'Overview information follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
foreach ($data as $key => $value) { $data[$key] = explode("\t", trim($value)); }
if ($this->_logger) { $this->_logger->info('Fetched overview ' . ($range == null ? 'for current article' : 'for range: '.$range)); }
return $data; break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC2980: 'No news group current selected' return $this->throwError('No news group current selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED: // 420, RFC2980: 'No article(s) selected' return $this->throwError('No article(s) selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_SUCH_ARTICLE_NUMBER: // 423:, Draft27: 'No articles in that range' return $this->throwError('No articles in that range', $response, $this->_currentStatusResponse()); break; case 502: // RFC2980: 'no permission' return $this->throwError('No permission', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdXOver()
/** * Fetch message header from message number $first until $last * * The format of the returned array is: * $messages[message_id][header_name] * * @param optional string $range articles to fetch * * @return mixed (array) nested array of message and there headers on success or (object) pear_error on failure * @access protected */ function cmdXOver($range = null) { // deprecated API (the code _is_ still in alpha state) if (func_num_args() > 1 ) { die('The second parameter in cmdXOver() has been deprecated! Use x-y instead...'); }
if (is_null($range)) { $command = 'XOVER'; } else { $command = 'XOVER ' . $range; }
$response = $this->_sendCommand($command); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_OVERVIEW_FOLLOWS: // 224, RFC2980: 'Overview information follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
foreach ($data as $key => $value) { $data[$key] = explode("\t", trim($value)); }
if ($this->_logger) { $this->_logger->info('Fetched overview ' . ($range == null ? 'for current article' : 'for range: '.$range)); }
return $data; break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC2980: 'No news group current selected' return $this->throwError('No news group current selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED: // 420, RFC2980: 'No article(s) selected' return $this->throwError('No article(s) selected', $response, $this->_currentStatusResponse()); break; case 502: // RFC2980: 'no permission' return $this->throwError('No permission', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdListOverviewFmt()
/** * Returns a list of avaible headers which are send from newsserver to client for every news message * * @return mixed (array) of header names on success or (object) pear_error on failure * @access protected */ function cmdListOverviewFmt() { $response = $this->_sendCommand('LIST OVERVIEW.FMT'); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_GROUPS_FOLLOW: // 215, RFC2980: 'information follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
$format = array();
foreach ($data as $line) {
// Check if postfixed by ':full' (case-insensitive) if (0 == strcasecmp(substr($line, -5, 5), ':full')) { // ':full' is _not_ included in tag, but value set to true $format[substr($line, 0, -5)] = true; } else { // ':' is _not_ included in tag; value set to false $format[substr($line, 0, -1)] = false; } }
if ($this->_logger) { $this->_logger->info('Fetched overview format'); } return $format; break; case 503: // RFC2980: 'program error, function not performed' return $this->throwError('Internal server error, function not performed', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdXHdr()
/** * * * The format of the returned array is: * $messages[message_id] * * @param optional string $field * @param optional string $range articles to fetch * * @return mixed (array) nested array of message and there headers on success or (object) pear_error on failure * @access protected */ function cmdXHdr($field, $range = null) { if (is_null($range)) { $command = 'XHDR ' . $field; } else { $command = 'XHDR ' . $field . ' ' . $range; }
$response = $this->_sendCommand($command); if (PEAR::isError($response)){ return $response; }
switch ($response) { case 221: // 221, RFC2980: 'Header follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
$return = array(); foreach($data as $line) { $line = explode(' ', trim($line), 2); $return[$line[0]] = $line[1]; }
return $return; break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC2980: 'No news group current selected' return $this->throwError('No news group current selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED: // 420, RFC2980: 'No current article selected' return $this->throwError('No current article selected', $response, $this->_currentStatusResponse()); break; case 430: // 430, RFC2980: 'No such article' return $this->throwError('No such article', $response, $this->_currentStatusResponse()); break; case 502: // RFC2980: 'no permission' return $this->throwError('No permission', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}}
/** * Fetches a list of (all) avaible newsgroup descriptions. * Depresated as of RFC2980. * * @param string $wildmat Wildmat of the groups, that is to be listed, defaults to '*'; * * @return mixed (array) nested array with description of existing newsgroups on success or (object) pear_error on failure * @access protected */ function cmdXGTitle($wildmat = '*') { $response = $this->_sendCommand('XGTITLE '.$wildmat); if (PEAR::isError($response)){ return $response; }
switch ($response) { case 282: // RFC2980: 'list of groups and descriptions follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
$groups = array();
foreach($data as $line) { preg_match("/^(.*?)\s(.*?$)/", trim($line), $matches); $groups[$matches[1]] = (string) $matches[2]; }
return $groups; break;
case 481: // RFC2980: 'Groups and descriptions unavailable' return $this->throwError('Groups and descriptions unavailable', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdXROver()
/** * Fetch message references from message number $first to $last * * @param optional string $range articles to fetch * * @return mixed (array) assoc. array of message references on success or (object) pear_error on failure * @access protected */ function cmdXROver($range = null) { // Warn about deprecated API (the code _is_ still in alpha state) if (func_num_args() > 1 ) { die('The second parameter in cmdXROver() has been deprecated! Use x-y instead...'); }
if (is_null($range)) { $command = 'XROVER'; } else { $command = 'XROVER ' . $range; }
$response = $this->_sendCommand($command); if (PEAR::isError($response)){ return $response; }
switch ($response) { case NET_NNTP_PROTOCOL_RESPONSECODE_OVERVIEW_FOLLOWS: // 224, RFC2980: 'Overview information follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
$return = array(); foreach($data as $line) { $line = explode(' ', trim($line), 2); $return[$line[0]] = $line[1]; } return $return; break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_GROUP_SELECTED: // 412, RFC2980: 'No news group current selected' return $this->throwError('No news group current selected', $response, $this->_currentStatusResponse()); break; case NET_NNTP_PROTOCOL_RESPONSECODE_NO_ARTICLE_SELECTED: // 420, RFC2980: 'No article(s) selected' return $this->throwError('No article(s) selected', $response, $this->_currentStatusResponse()); break; case 502: // RFC2980: 'no permission' return $this->throwError('No permission', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}}
// {{{ cmdXPat()
/** * * * @param string $field * @param string $range * @param mixed $wildmat * * @return mixed (array) nested array of message and there headers on success or (object) pear_error on failure * @access protected */ function cmdXPat($field, $range, $wildmat) { if (is_array($wildmat)) { $wildmat = implode(' ', $wildmat); }
$response = $this->_sendCommand('XPAT ' . $field . ' ' . $range . ' ' . $wildmat); if (PEAR::isError($response)){ return $response; }
switch ($response) { case 221: // 221, RFC2980: 'Header follows' $data = $this->_getTextResponse(); if (PEAR::isError($data)) { return $data; }
$return = array(); foreach($data as $line) { $line = explode(' ', trim($line), 2); $return[$line[0]] = $line[1]; }
return $return; break; case 430: // 430, RFC2980: 'No such article' return $this->throwError('No current article selected', $response, $this->_currentStatusResponse()); break; case 502: // RFC2980: 'no permission' return $this->throwError('No permission', $response, $this->_currentStatusResponse()); break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdAuthinfo()
/** * Authenticate using 'original' method * * @param string $user The username to authenticate as. * @param string $pass The password to authenticate with. * * @return mixed (bool) true on success or (object) pear_error on failure * @access protected */ function cmdAuthinfo($user, $pass) { // Send the username $response = $this->_sendCommand('AUTHINFO user '.$user); if (PEAR::isError($response)) { return $response; }
// Send the password, if the server asks if (($response == 381) && ($pass !== null)) { // Send the password $response = $this->_sendCommand('AUTHINFO pass '.$pass); if (PEAR::isError($response)) { return $response; } }
switch ($response) { case 281: // RFC2980: 'Authentication accepted' if ($this->_logger) { $this->_logger->info("Authenticated (as user '$user')"); }
// TODO: Set some variable before return
return true; break; case 381: // RFC2980: 'More authentication information required' return $this->throwError('Authentication uncompleted', $response, $this->_currentStatusResponse()); break; case 482: // RFC2980: 'Authentication rejected' return $this->throwError('Authentication rejected', $response, $this->_currentStatusResponse()); break; case 502: // RFC2980: 'No permission' return $this->throwError('Authentication rejected', $response, $this->_currentStatusResponse()); break; // case 500: // case 501: // return $this->throwError('Authentication failed', $response, $this->_currentStatusResponse()); // break; default: return $this->_handleUnexpectedResponse($response); } }
// }}} // {{{ cmdAuthinfoSimple()
/** * Authenticate using 'simple' method * * @param string $user The username to authenticate as. * @param string $pass The password to authenticate with. * * @return mixed (bool) true on success or (object) pear_error on failure * @access protected */ function cmdAuthinfoSimple($user, $pass) { return $this->throwError("The auth mode: 'simple' is has not been implemented yet", null); }
// }}} // {{{ cmdAuthinfoGeneric()
/** * Authenticate using 'generic' method * * @param string $user The username to authenticate as. * @param string $pass The password to authenticate with. * * @return mixed (bool) true on success or (object) pear_error on failure * @access protected */ function cmdAuthinfoGeneric($user, $pass) { return $this->throwError("The auth mode: 'generic' is has not been implemented yet", null); }
// }}} // {{{ _isConnected()
/** * Test whether we are connected or not. * * @return bool true or false * @access protected */ function _isConnected() { return (is_resource($this->_socket) && (!feof($this->_socket))); }
// }}}
}
// }}}
/* * Local variables: * tab-width: 4 * c-basic-offset: 4 * c-hanging-comment-ender-p: nil * End: */
?>
|