📄 proxynewsdb.java
字号:
// ProxyNewsDb - proxy netnews database
//
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// Visit the ACME Labs Java page for up-to-date versions of this and other
// fine Java utilities: http://www.acme.com/java/
package Acme.Nnrpd;
import java.io.*;
import java.util.*;
import java.net.*;
import Acme.Utils;
/// Proxy netnews database.
// <P>
// This is a news database that forwards requests to a remote news server.
// <P>
// <A HREF="/resources/classes/Acme/Nnrpd/ProxyNewsDb.java">Fetch the software.</A><BR>
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
public class ProxyNewsDb extends NewsDb
{
// This switch is for getting around the "LIKE_PULLERS DONT" option
// in INN 1.5+. In INN 1.5.1 the option is completely broken, and
// admins have to set "LIKE_PULLERS DO" in order to get acceptable
// reader performance. In versions after that, ...?
private static final boolean avoidArticleHack = false;
private String host;
private int port;
private Socket socket;
private boolean postingOk;
private String[] overviewFmt = null;
private OverviewCache overviewCache;
// The object's synchronization is basically on the I/O streams.
private DataInputStream din;
private PrintStream pout;
// Group cache.
private Hashtable groupTable = null;
private long groupsFetched;
// Group descriptions cache.
private Hashtable groupDescsTable = null;
private long groupDescsFetched;
// State variables.
private NewsDbGroup currentGroup = null;
private int currentArtNum = -1;
/// Constructor.
// The proxyHost string should be "hostname:portnum", or just a
// hostname in which case the default netnews port will be used.
// @param proxyHost the remote news server to connect to
// @param oCacheSize how many overview entries to save in the cache
// @exception NewsDbException if something goes wrong
public ProxyNewsDb( String proxyHost, int oCacheSize ) throws NewsDbException
{
int colon = proxyHost.indexOf( ':' );
if ( colon == -1 )
{
host = proxyHost;
port = NnrpdUtils.DEFAULT_PORT;
}
else
{
host = proxyHost.substring( 0, colon );
port = Utils.parseInt(
proxyHost.substring( colon + 1 ), NnrpdUtils.DEFAULT_PORT );
}
try
{
socket = new Socket( host, port );
din = new DataInputStream( new BufferedInputStream(
socket.getInputStream() ) );
pout = new PrintStream( new CrLfOutputStream(
new BufferedOutputStream( socket.getOutputStream() ) ) );
}
catch ( IOException e )
{
throw new NewsDbException(
"problem opening socket to " + host + ": " + e );
}
// Read initial response.
String[] resp = readResp();
int respNum = Utils.parseInt( resp[0], -1 );
switch ( respNum )
{
case 200:
postingOk = true;
break;
case 201:
postingOk = false;
break;
case 502:
throw new NewsDbException( "no read permission on " + host );
default:
throw new NewsDbException(
"unexpected response to " + host + " initial connect: " +
Utils.flattenStrarr( resp ) );
}
// Do SLAVE command - it probably does nothing, but it's in the spec.
pout.println( NnrpdUtils.CMD_SLAVE );
resp = readResp();
// Ignore response.
// See if there's an overview database. First try a LIST schema.
pout.println( NnrpdUtils.CMD_LIST + " schema" );
resp = readResp();
respNum = Utils.parseInt( resp[0], -1 );
switch ( respNum )
{
case 215:
try
{
overviewFmt = Utils.splitStr( NnrpdUtils.readText( din ) );
}
catch ( IOException ignore ) {}
break;
default:
// That didn't work. Now try a LIST overview.fmt.
pout.println( NnrpdUtils.CMD_LIST + " overview.fmt" );
resp = readResp();
respNum = Utils.parseInt( resp[0], -1 );
switch ( respNum )
{
case 215:
try
{
overviewFmt = Utils.splitStr( NnrpdUtils.readText( din ) );
}
catch ( IOException ignore ) {}
break;
default:
break;
}
break;
}
if ( overviewFmt != null )
{
for ( int i = 0; i < overviewFmt.length; ++i )
if ( overviewFmt[i].endsWith( ":" ) )
overviewFmt[i] = overviewFmt[i].substring(
0, overviewFmt[i].length() - 1 );
overviewCache = new OverviewCache( oCacheSize );
}
}
/// Attempt authorization.
// @exception NewsDbException if something goes wrong
public boolean authorize( String user, String password ) throws NewsDbException
{
pout.println( NnrpdUtils.CMD_AUTHINFO + " USER " + user );
String[] resp = readResp();
int respNum = Utils.parseInt( resp[0], -1 );
switch ( respNum )
{
case 381:
pout.println( NnrpdUtils.CMD_AUTHINFO + " PASS " + password );
resp = readResp();
respNum = Utils.parseInt( resp[0], -1 );
switch ( respNum )
{
case 281:
return true;
case 502:
return false;
default:
throw new NewsDbException(
"unexpected response to AUTHINFO command: " +
Utils.flattenStrarr( resp ) );
}
default:
throw new NewsDbException(
"unexpected response to AUTHINFO command: " +
Utils.flattenStrarr( resp ) );
}
}
/// Whether posting is allowed.
// @exception NewsDbException if something goes wrong
public boolean getPostingOk() throws NewsDbException
{
return postingOk;
}
/// Get a group by name.
// @exception NewsDbException if something goes wrong
public synchronized NewsDbGroup getGroup( String groupName ) throws NewsDbException
{
// First check current group.
if ( currentGroup != null &&
groupName.equals( currentGroup.getName() ) )
return currentGroup;
// Next check group cache.
checkGroups();
NewsDbGroup group = (NewsDbGroup) groupTable.get( groupName );
if ( group != null )
return group;
// Not there. Try a GROUP command just to be sure.
return setGroup( groupName );
}
/// Set the remote server's current group.
private synchronized NewsDbGroup setGroup( String groupName ) throws NewsDbException
{
pout.println( NnrpdUtils.CMD_GROUP + " " + groupName );
String[] resp = readResp();
int respNum = Utils.parseInt( resp[0], -1 );
switch ( respNum )
{
case 211:
try
{
currentGroup = new NewsDbGroup(
dbStamp, resp[4],
Integer.parseInt( resp[1] ),
Integer.parseInt( resp[2] ),
Integer.parseInt( resp[3] ),
'?', (String) groupDescsTable.get( groupName ) );
}
catch ( NumberFormatException e )
{
throw new NewsDbException(
"unparsable numbers in response to GROUP command: " +
Utils.flattenStrarr( resp ) );
}
currentArtNum = currentGroup.getFirstArtNum();
// Might as well put it in the group cache.
groupTable.put( currentGroup.getName(), currentGroup );
return currentGroup;
case 411:
return null;
default:
throw new NewsDbException(
"unexpected response to GROUP command: " +
Utils.flattenStrarr( resp ) );
}
}
/// Get an article by group and number.
// @exception NewsDbException if something goes wrong
public synchronized NewsDbArticle getArticle( NewsDbGroup group, int artNum ) throws NewsDbException
{
// Check that this group is one of ours.
if ( group.getDbStamp() != dbStamp )
throw new NewsDbException( "mismatched database stamps" );
// Make sure it's the current group on the remote server.
if ( currentGroup == null ||
! group.getName().equals( currentGroup.getName() ) )
setGroup( group.getName() );
// Do the ARTICLE command.
pout.println( NnrpdUtils.CMD_ARTICLE + " " + artNum );
// Get and interpret results.
String[] resp = readResp();
int respNum = Utils.parseInt( resp[0], -1 );
switch ( respNum )
{
case 220:
currentArtNum = artNum;
String text;
try
{
text = NnrpdUtils.readText( din );
}
catch ( IOException e )
{
throw new NewsDbException( "problem fetching article: " + e );
}
return new NewsDbArticle( dbStamp, text );
case 423:
return null;
default:
throw new NewsDbException(
"unexpected response to ARTICLE command: " +
Utils.flattenStrarr( resp ) );
}
}
/// Get an article by message-id.
// @exception NewsDbException if something goes wrong
public synchronized NewsDbArticle getArticle( String messageId ) throws NewsDbException
{
// Do the ARTICLE command.
pout.println( NnrpdUtils.CMD_ARTICLE + " " + messageId );
// Get and interpret results.
String[] resp = readResp();
int respNum = Utils.parseInt( resp[0], -1 );
switch ( respNum )
{
case 220:
String text;
try
{
text = NnrpdUtils.readText( din );
}
catch ( IOException e )
{
throw new NewsDbException( "problem fetching article: " + e );
}
return new NewsDbArticle( dbStamp, text );
case 430:
return null;
default:
throw new NewsDbException(
"unexpected response to ARTICLE command: " +
Utils.flattenStrarr( resp ) );
}
}
// Remember the most recent names array that matched, so we can do
// a quick address compare instead of a more expensive content compare.
private String[] lastOkNames = null;
/// Get specified headers from a range of articles by group and number.
// Some implementations have special faster methods for getting
// headers from articles, e.g. an XOVER command to a remote server
// or a local overview database. If no such fast method is available,
// this returns null and the caller should fall back on conventional
// means.
// @exception NewsDbException if something goes wrong
public synchronized String[][] getHeaders( String[] names, NewsDbGroup group, int firstArtNum, int lastArtNum ) throws NewsDbException
{
// Do we have an XOVER database?
if ( overviewFmt == null )
return null;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -