You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by hb...@apache.org on 2001/04/10 18:02:13 UTC
cvs commit: jakarta-james/src/org/apache/james/nntpserver ArticleWriter.java AuthState.java DateSinceFileFilter.java LISTGroup.java NNTPException.java NNTPHandler.java NNTPServer.java
hbedi 01/04/10 09:02:13
Added: src/org/apache/james/nntpserver ArticleWriter.java
AuthState.java DateSinceFileFilter.java
LISTGroup.java NNTPException.java NNTPHandler.java
NNTPServer.java
Log:
NNTP implementation 1st pass
Revision Changes Path
1.1 jakarta-james/src/org/apache/james/nntpserver/ArticleWriter.java
Index: ArticleWriter.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.nntpserver;
import java.io.*;
import org.apache.james.nntpserver.repository.NNTPArticle;
// this is used by ARTICLE, HEAD, BODY, STAT command.
// these commands are identical except in the writing the Article header
// and body to the response stream
// ARTICLE - writes header and body
// HEAD - writes headers
// BODY - writes body
// STAT - does not write anything
interface ArticleWriter {
void write(NNTPArticle article);
static class Factory {
static ArticleWriter ARTICLE(final PrintWriter prt) {
return new ArticleWriter() {
public void write(NNTPArticle article) {
article.writeArticle(prt);
prt.println(".");
}
};
}
static ArticleWriter HEAD(final PrintWriter prt) {
return new ArticleWriter() {
public void write(NNTPArticle article) {
article.writeHead(prt);
prt.println(".");
}
};
}
static ArticleWriter BODY(final PrintWriter prt) {
return new ArticleWriter() {
public void write(NNTPArticle article) {
article.writeBody(prt);
prt.println(".");
}
};
}
static ArticleWriter STAT(final PrintWriter prt) {
return new ArticleWriter() {
public void write(NNTPArticle article) { }
};
}
static ArticleWriter OVER(final PrintWriter prt) {
return new ArticleWriter() {
public void write(NNTPArticle article) {
article.writeOverview(prt);
}
};
}
} // class Factory
}
1.1 jakarta-james/src/org/apache/james/nntpserver/AuthState.java
Index: AuthState.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.nntpserver;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.io.OutputStream;
import org.apache.james.services.UsersRepository;
/**
* @version 1.0.0, 31/03/2001
* @author Harmeet
*
* implements the authentication state. Should this be moved to a more common place ??
* Should there be an authenication service, that manufactures and hands to the different
* protocol servers AuthState objects.
*/
public class AuthState {
private final boolean requiredAuth;
private final UsersRepository repo;
private String user;
private String password;
public AuthState(boolean requiredAuth,UsersRepository repo) {
this.requiredAuth = requiredAuth;
this.repo = repo;
}
public boolean isAuthenticated() {
if ( requiredAuth )
return repo.test(user,password);
else
return true;
}
public void setUser(String user) {
this.user = user;
this.password = null;
}
public void setPassword(String password) {
this.password = password;
}
}
1.1 jakarta-james/src/org/apache/james/nntpserver/DateSinceFileFilter.java
Index: DateSinceFileFilter.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.nntpserver;
import java.io.File;
import java.io.FilenameFilter;
/**
* filters files according to their date of last modification
*
* @author Harmeet Bedi <ha...@kodemuse.com>
*/
public class DateSinceFileFilter implements FilenameFilter
{
private final long m_date;
public DateSinceFileFilter( long date )
{
m_date = date;
}
public boolean accept( final File file, final String name )
{
return (new File(file,name).lastModified() >= m_date);
}
}
1.1 jakarta-james/src/org/apache/james/nntpserver/LISTGroup.java
Index: LISTGroup.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.nntpserver;
import java.io.*;
import org.apache.james.nntpserver.repository.NNTPGroup;
// group information would be displayed differently depending on the
// LIST command paramter
interface LISTGroup {
void show(NNTPGroup group);
static class Factory {
static LISTGroup ACTIVE(final PrintWriter prt) {
return new LISTGroup() {
public void show(NNTPGroup group) {
prt.println(group.getName()+" "+group.getFirstArticleNumber()+" "+
group.getLastArticleNumber()+" "+
(group.isPostAllowed()?"y":"n"));
}
};
}
static LISTGroup NEWSGROUPS(final PrintWriter prt) {
return new LISTGroup() {
public void show(NNTPGroup group) {
prt.println(group.getName()+" "+group.getDescription());
}
};
}
} // class Factory
}
1.1 jakarta-james/src/org/apache/james/nntpserver/NNTPException.java
Index: NNTPException.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.nntpserver;
public class NNTPException extends RuntimeException {
private final Throwable t;
public NNTPException(String msg) {
super(msg);
this.t = null;
}
public NNTPException(String msg,Throwable t) {
super(msg+((t!=null)?": "+t.toString():""));
this.t = t;
}
public NNTPException(Throwable t) {
super(t.toString());
this.t = t;
}
}
1.1 jakarta-james/src/org/apache/james/nntpserver/NNTPHandler.java
Index: NNTPHandler.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.nntpserver;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import org.apache.avalon.AbstractLoggable;
import org.apache.avalon.ComponentManager;
import org.apache.avalon.ComponentManagerException;
import org.apache.avalon.Composer;
import org.apache.avalon.Context;
import org.apache.avalon.Contextualizable;
import org.apache.avalon.Initializable;
import org.apache.avalon.configuration.Configurable;
import org.apache.avalon.configuration.Configuration;
import org.apache.avalon.configuration.ConfigurationException;
import org.apache.avalon.util.ListUtils;
import org.apache.cornerstone.services.connection.ConnectionHandler;
import org.apache.cornerstone.services.scheduler.PeriodicTimeTrigger;
import org.apache.cornerstone.services.scheduler.Target;
import org.apache.cornerstone.services.scheduler.TimeScheduler;
import org.apache.james.Constants;
import org.apache.james.nntpserver.repository.NNTPArticle;
import org.apache.james.nntpserver.repository.NNTPGroup;
import org.apache.james.nntpserver.repository.NNTPLineReaderImpl;
import org.apache.james.nntpserver.repository.NNTPRepository;
import org.apache.james.nntpserver.repository.NNTPUtil;
import org.apache.james.services.UsersRepository;
import org.apache.james.services.UsersStore;
/**
* The NNTP protocol is defined by RFC 977.
* This implementation is based on IETF draft 13, posted on 2nd April '2001.
* URL: http://www.ietf.org/internet-drafts/draft-ietf-nntpext-base-13.txt
*
* Common NNTP extensions are in RFC 2980.
*
* @author Fedor Karpelevitch
* @author Harmeet <hb...@apache.org>
*/
public class NNTPHandler extends AbstractLoggable
implements ConnectionHandler, Contextualizable, Composer, Configurable, Target {
// timeout controllers
private TimeScheduler scheduler;
private int timeout;
// communciation.
private Socket socket;
private BufferedReader reader;
private PrintWriter writer;
// server greeting.
private String servername;
// authentication.
private AuthState authState;
private boolean authRequired = false;
private UsersRepository users;
// data abstractions.
private NNTPGroup group;
private NNTPRepository repo;
public void contextualize( final Context context ) {
//System.out.println(getClass().getName()+": contextualize");
servername = (String)context.get( Constants.HELO_NAME );
}
public void configure( Configuration configuration ) throws ConfigurationException {
//System.out.println(getClass().getName()+": configure");
//NNTPUtil.show(configuration,System.out);
timeout = configuration.getChild( "connectiontimeout" ).getValueAsInt( 120000 );
authRequired=configuration.getChild("authRequired").getValueAsBoolean(false);
authState = new AuthState(authRequired,users);
}
public void compose( final ComponentManager componentManager )
throws ComponentManagerException
{
//System.out.println(getClass().getName()+": compose - "+authRequired);
UsersStore usersStore = (UsersStore)componentManager.
lookup( "org.apache.james.services.UsersStore" );
users = usersStore.getRepository("LocalUsers");
scheduler = (TimeScheduler)componentManager.
lookup( "org.apache.cornerstone.services.scheduler.TimeScheduler" );
repo = (NNTPRepository)componentManager
.lookup("org.apache.james.nntpserver.repository.NNTPRepository");
}
public void handleConnection( Socket connection ) throws IOException {
try {
this.socket = connection;
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(socket.getOutputStream()) {
public void println() {
print("\r\n");
flush();
}
};
getLogger().info( "Connection from " + socket.getInetAddress());
} catch (Exception e) {
getLogger().error( "Cannot open connection from: " + e.getMessage(), e );
}
try {
final PeriodicTimeTrigger trigger = new PeriodicTimeTrigger( timeout, -1 );
scheduler.addTrigger( this.toString(), trigger, this );
// section 7.1
writer.println("200 "+servername+
(repo.isReadOnly()?" Posting Not Premitted":" Posting Permitted"));
while (parseCommand(reader.readLine()))
scheduler.resetTrigger(this.toString());
reader.close();
writer.close();
socket.close();
scheduler.removeTrigger(this.toString());
getLogger().info("Connection closed");
} catch (Exception e) {
doQUIT();
//writer.println("502 Error closing connection.");
//writer.flush();
getLogger().error( "Exception during connection:" + e.getMessage(), e );
try { socket.close(); } catch (IOException ioe) { }
}
}
public void targetTriggered( final String triggerName ) {
getLogger().error("Connection timeout on socket");
try {
writer.println("Connection timeout. Closing connection");
socket.close();
} catch (IOException e) { }
}
// checks if a command is allowed. The authentication state is validated.
private boolean isAllowed(String command) {
boolean allowed = authState.isAuthenticated();
// some commads are allowed, even if the user is not authenticated
allowed = allowed || command.equalsIgnoreCase("AUTHINFO");
allowed = allowed || command.equalsIgnoreCase("AUTHINFO");
allowed = allowed || command.equalsIgnoreCase("MODE");
allowed = allowed || command.equalsIgnoreCase("QUIT");
if ( allowed == false )
writer.println("502 User is not authenticated");
return allowed;
}
private boolean parseCommand(String commandRaw) {
if (commandRaw == null)
return false;
getLogger().info("Command received: " + commandRaw);
//System.out.println("NNTPHandler> "+commandRaw);
StringTokenizer tokens = new StringTokenizer(commandRaw);
if (!tokens.hasMoreTokens())
return false;
final String command = tokens.nextToken();
//System.out.println("allowed="+isAllowed(command)+","+authState.isAuthenticated());
if ( isAllowed(command) == false )
return true;
if ( command.equalsIgnoreCase("MODE") && tokens.hasMoreTokens() &&
tokens.nextToken().equalsIgnoreCase("READER") )
doMODEREADER();
else if ( command.equalsIgnoreCase("LIST") && tokens.hasMoreTokens() &&
tokens.nextToken().equalsIgnoreCase("EXTENSIONS") )
doLISTEXTENSIONS();
else if ( command.equalsIgnoreCase("LIST") && tokens.hasMoreTokens() &&
tokens.nextToken().equalsIgnoreCase("OVERVIEW.FMT") )
doLISTOVERVIEWFMT();
else if ( command.equalsIgnoreCase("GROUP") )
doGROUP(tokens.hasMoreTokens()?tokens.nextToken():null);
else if ( command.equalsIgnoreCase("LAST") )
doLAST();
else if ( command.equalsIgnoreCase("ARTICLE") )
doARTICLE(tokens.hasMoreTokens()?tokens.nextToken():null);
else if ( command.equalsIgnoreCase("HEAD") )
doHEAD(tokens.hasMoreTokens()?tokens.nextToken():null);
else if ( command.equalsIgnoreCase("BODY") )
doBODY(tokens.hasMoreTokens()?tokens.nextToken():null);
else if ( command.equalsIgnoreCase("STAT") )
doSTAT(tokens.hasMoreTokens()?tokens.nextToken():null);
else if ( command.equalsIgnoreCase("POST") )
doPOST();
else if ( command.equalsIgnoreCase("IHAVE") )
doIHAVE(tokens.hasMoreTokens()?tokens.nextToken():null);
else if ( command.equalsIgnoreCase("LIST") )
doLIST(tokens);
else if ( command.equalsIgnoreCase("QUIT") )
doQUIT();
else if ( command.equalsIgnoreCase("DATE") )
doDATE();
else if ( command.equalsIgnoreCase("HELP") )
doHELP();
else if ( command.equalsIgnoreCase("NEWNEWS") )
doNEWSGROUPS(tokens);
else if ( command.equalsIgnoreCase("NEWNEWS") )
doNEWNEWS(tokens);
else if ( command.equalsIgnoreCase("LISTGROUP") )
doLISTGROUP(tokens.hasMoreTokens()?tokens.nextToken():null);
else if ( command.equalsIgnoreCase("OVER") )
doOVER(tokens.hasMoreTokens()?tokens.nextToken():null);
else if ( command.equalsIgnoreCase("XOVER") )
doXOVER(tokens.hasMoreTokens()?tokens.nextToken():null);
else if ( command.equalsIgnoreCase("PAT") )
doPAT();
else if ( command.equalsIgnoreCase("HDR") )
doHDR(tokens);
else if ( command.equalsIgnoreCase("XHDR") )
doXHDR(tokens);
else if ( command.equalsIgnoreCase("AUTHINFO") )
doAUTHINFO(tokens);
else
writer.println("501 Syntax error");
return (command.equalsIgnoreCase("QUIT") == false);
}
// implements only the originnal AUTHINFO
// for simple and generic AUTHINFO, 501 is sent back. This is as
// per article 3.1.3 of RFC 2980
private void doAUTHINFO(StringTokenizer tok) {
String command = tok.nextToken();
if ( command.equalsIgnoreCase("USER") ) {
authState.setUser(tok.nextToken());
writer.println("381 More authentication information required");
} else if ( command.equalsIgnoreCase("PASS") ) {
authState.setPassword(tok.nextToken());
if ( authState.isAuthenticated() )
writer.println("281 Authentication accepted");
else
writer.println("482 Authentication rejected");
}
}
private void doNEWNEWS(StringTokenizer tok) {
writer.println("230 list of new articles by message-id follows");
Iterator iter = repo.getArticlesSince(getDateFrom(tok));
while ( iter.hasNext() )
writer.println("<"+((NNTPArticle)iter.next()).getUniqueID()+">");
writer.println(".");
}
private void doNEWSGROUPS(StringTokenizer tok) {
writer.println("230 list of new newsgroups follows");
Iterator iter = repo.getGroupsSince(getDateFrom(tok));
while ( iter.hasNext() )
writer.println(((NNTPGroup)iter.next()).getName());
writer.println(".");
}
// returns the date from @param input.
// The input tokens are assumed to be in format date time [GMT|UTC] .
// 'date' is in format [XX]YYMMDD. 'time' is in format 'HHMMSS'
// NOTE: This routine would do with some format checks.
private Date getDateFrom(StringTokenizer tok) {
String date = tok.nextToken();
String time = tok.nextToken();
boolean utc = ( tok.hasMoreTokens() );
Date d = new Date();
DateFormat df = ( date.length() == 8 ) ? DF_DATEFROM_LONG : DF_DATEFROM_SHORT;
try {
Date dt = df.parse(date+" "+time);
if ( utc )
dt = new Date(dt.getTime()+UTC_OFFSET);
return dt;
} catch ( ParseException pe ) {
throw new NNTPException("date extraction failed: "+date+","+time+","+utc);
}
}
private void doHELP() {
writer.println("100 Help text follows");
writer.println(".");
}
// used to calculate DATE from - see 11.3
public static final DateFormat DF_DATEFROM_LONG = new SimpleDateFormat("yyyyMMdd HHmmss");
public static final DateFormat DF_DATEFROM_SHORT = new SimpleDateFormat("yyMMdd HHmmss");
// Date format for the DATE keyword - see 11.1.1
public static final DateFormat DF_DATE = new SimpleDateFormat("yyyyMMddHHmmss");
public static final long UTC_OFFSET = Calendar.getInstance().get(Calendar.ZONE_OFFSET);
private void doDATE() {
//Calendar c = Calendar.getInstance();
//long UTC_OFFSET = c.get(c.ZONE_OFFSET) + c.get(c.DST_OFFSET);
Date dt = new Date(System.currentTimeMillis()-UTC_OFFSET);
String dtStr = DF_DATE.format(new Date(dt.getTime() - UTC_OFFSET));
writer.println("111 "+dtStr);
}
private void doQUIT() {
writer.println("205 closing connection");
}
private void doLIST(StringTokenizer tok) {
// see section 9.4.1
String wildmat = "*";
LISTGroup output = LISTGroup.Factory.ACTIVE(writer);
if ( tok.hasMoreTokens() ) {
String param = tok.nextToken();
// list of variations not supported - 9.4.2.1, 9.4.3.1, 9.4.4.1
String[] notSupported = { "ACTIVE.TIMES", "DISTRIBUTIONS", "DISTRIB.PATS" };
for ( int i = 0 ; i < notSupported.length ; i++ ) {
if ( param.equalsIgnoreCase("ACTIVE.TIMES") ) {
writer.println("503 program error, function not performed");
return;
}
}
if ( param.equalsIgnoreCase("NEWSGROUPS") )
output = LISTGroup.Factory.NEWSGROUPS(writer);
else
assert(param,param.equalsIgnoreCase("ACTIVE"));
if ( tok.hasMoreTokens() )
wildmat = tok.nextToken();
}
Iterator iter = repo.getMatchedGroups(wildmat);
writer.println("215 list of newsgroups follows");
while ( iter.hasNext() )
output.show((NNTPGroup)iter.next());
writer.println(".");
}
private void assert(String id,boolean b) {
if ( b == false )
throw new RuntimeException(id);
}
private void doIHAVE(String id) {
// see section 9.3.2.1
assert(id,id.startsWith("<") && id.endsWith(">"));
NNTPArticle article = repo.getArticleFromID(id);
if ( article != null )
writer.println("435 article not wanted - do not send it");
else {
writer.println("335 send article to be transferred. End with <CR-LF>.<CR-LF>");
createArticle();
writer.println("235 article received ok");
}
}
private void doPOST() {
// see section 9.3.1.1
writer.println("340 send article to be posted. End with <CR-LF>.<CR-LF>");
createArticle();
writer.println("240 article received ok");
}
private void createArticle() {
repo.createArticle(new NNTPLineReaderImpl(reader));
}
private void doSTAT(String param) {
doARTICLE(param,ArticleWriter.Factory.STAT(writer));
}
private void doBODY(String param) {
doARTICLE(param,ArticleWriter.Factory.BODY(writer));
}
private void doHEAD(String param) {
doARTICLE(param,ArticleWriter.Factory.HEAD(writer));
}
private void doARTICLE(String param) {
doARTICLE(param,ArticleWriter.Factory.ARTICLE(writer));
}
private void doARTICLE(String param,ArticleWriter articleWriter) {
// section 9.2.1
NNTPArticle article = null;
if ( (param != null) && param.startsWith("<") && param.endsWith(">") ) {
article = repo.getArticleFromID(param.substring(1,param.length()-2));
if ( article == null )
writer.println("430 no such article");
else
writer.println("220 0 "+param+" article retrieved and follows");
}
else {
if ( group == null )
writer.println("412 no newsgroup selected");
else {
if ( param == null ) {
if ( group.getCurrentArticleNumber() < 0 )
writer.println("420 no current article selected");
else
article = group.getCurrentArticle();
}
else
article = group.getArticle(Integer.parseInt(param));
if ( article == null )
writer.println("423 no such article number in this group");
else
writer.println("220 "+article.getArticleNumber()+" "+
article.getUniqueID()+" article retrieved and follows");
}
}
if ( article != null )
articleWriter.write(article);
}
private void doNEXT() {
// section 9.1.1.3.1
if ( group == null )
writer.println("412 no newsgroup selected");
else if ( group.getCurrentArticleNumber() < 0 )
writer.println("420 no current article has been selected");
else if ( group.getCurrentArticleNumber() >= group.getLastArticleNumber() )
writer.println("421 no next article in this group");
else {
NNTPArticle article = group.getCurrentArticle();
group.setCurrentArticleNumber(group.getCurrentArticleNumber()+1);
writer.println("223 "+article.getArticleNumber()+" "+article.getUniqueID());
}
}
private void doLAST() {
// section 9.1.1.2.1
if ( group == null )
writer.println("412 no newsgroup selected");
else if ( group.getCurrentArticleNumber() < 0 )
writer.println("420 no current article has been selected");
else if ( group.getCurrentArticleNumber() <= group.getFirstArticleNumber() )
writer.println("422 no previous article in this group");
else {
NNTPArticle article = group.getCurrentArticle();
group.setCurrentArticleNumber(group.getCurrentArticleNumber()-1);
writer.println("223 "+article.getArticleNumber()+" "+article.getUniqueID());
}
}
private void doGROUP(String groupName) {
group = repo.getGroup(groupName);
// section 9.1.1.1
if ( group == null )
writer.println("411 no such newsgroup");
else
writer.println("211 "+group.getNumberOfArticles()+" "+
group.getFirstArticleNumber()+" "+
group.getLastArticleNumber()+" "+
group.getName()+" group selected");
}
private void doLISTEXTENSIONS() {
// 8.1.1
writer.println("202 Extensions supported:");
writer.println("LISTGROUP");
writer.println("AUTHINFO");
writer.println("OVER");
writer.println("XOVER");
writer.println("HDR");
writer.println("XHDR");
writer.println(".");
}
private void doMODEREADER() {
// 7.2
writer.println(repo.isReadOnly()
? "201 Posting Not Permitted" : "200 Posting Permitted");
}
private void doLISTGROUP(String groupName) {
// 9.5.1.1.1
NNTPGroup group = null;
if (groupName==null) {
if ( group == null )
writer.println("412 not currently in newsgroup");
}
else {
group = repo.getGroup(groupName);
if ( group == null )
writer.println("411 no such newsgroup");
}
if ( group != null ) {
writer.println("211 list of article numbers follow");
for (Iterator iter = group.getArticles();iter.hasNext();) {
NNTPArticle article = (NNTPArticle)iter.next();
writer.println(article.getArticleNumber());
}
writer.println(".");
this.group = group;
group.setCurrentArticleNumber(group.getFirstArticleNumber());
}
}
private void doLISTOVERVIEWFMT() {
// 9.5.3.1.1
// 503 means information is not available as per 9.5.2.1
writer.println("503 program error, function not performed");
}
private void doPAT() {
// 9.5.3.1.1 in draft-12
writer.println("500 Command not recognized");
}
private void doXHDR(StringTokenizer tok) {
doHDR(tok);
}
private void doHDR(StringTokenizer tok) {
// 9.5.3
writer.println("500 Command not recognized");
String hdr = tok.nextToken();
String range = tok.hasMoreTokens() ? tok.nextToken() : null;
NNTPArticle[] article = getRange(range);
if ( article == null )
writer.println("412 no newsgroup selected");
else if ( article.length == 0 )
writer.println("430 no such article");
else {
writer.println("221 Header follows");
for ( int i = 0 ; i < article.length ; i++ ) {
String val = article[i].getHeader(hdr);
if ( val == null )
val = "";
writer.println(article[i].getArticleNumber()+" "+val);
}
writer.println(".");
}
}
// returns the list of articles that match the range.
// @return null indicates insufficient information to
// fetch the list of articles
private NNTPArticle[] getRange(String range) {
// check for msg id
if ( range != null && range.startsWith("<") ) {
NNTPArticle article = repo.getArticleFromID(range);
return ( article == null )
? new NNTPArticle[0] : new NNTPArticle[] { article };
}
if ( group == null )
return null;
if ( range == null )
range = ""+group.getCurrentArticleNumber();
int start = -1;
int end = -1;
int idx = range.indexOf('-');
if ( idx == -1 ) {
start = end = Integer.parseInt(range);
} else {
start = Integer.parseInt(range.substring(0,idx));
if ( idx+1 == range.length() )
end = group.getLastArticleNumber();
else
end = Integer.parseInt(range.substring(idx+1));
}
List list = new ArrayList();
for ( int i = start ; i <= end ; i++ ) {
NNTPArticle article = group.getArticle(i);
if ( article != null )
list.add(article);
}
return (NNTPArticle[])list.toArray(new NNTPArticle[0]);
}
private void doXOVER(String range) {
doOVER(range);
}
private void doOVER(String range) {
// 9.5.2.2.1
if ( group == null ) {
writer.println("412 No newsgroup selected");
return;
}
NNTPArticle[] article = getRange(range);
ArticleWriter articleWriter = ArticleWriter.Factory.OVER(writer);
if ( article.length == 0 )
writer.println("420 No article(s) selected");
else {
writer.println("224 Overview information follows");
for ( int i = 0 ; i < article.length ; i++ )
articleWriter.write(article[i]);
writer.println(".");
}
}
}
1.1 jakarta-james/src/org/apache/james/nntpserver/NNTPServer.java
Index: NNTPServer.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.nntpserver;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.apache.avalon.Component;
import org.apache.avalon.ComponentManager;
import org.apache.avalon.ComponentManagerException;
import org.apache.avalon.DefaultComponentManager;
import org.apache.avalon.Initializable;
import org.apache.avalon.configuration.Configuration;
import org.apache.avalon.configuration.ConfigurationException;
import org.apache.cornerstone.services.connection.AbstractService;
import org.apache.cornerstone.services.connection.ConnectionHandlerFactory;
import org.apache.cornerstone.services.connection.DefaultHandlerFactory;
import org.apache.james.nntpserver.repository.NNTPRepository;
import org.apache.james.nntpserver.repository.NNTPUtil;
/**
* @author Harmeet <hb...@apache.org>
*/
public class NNTPServer extends AbstractService {
private Component repository;
protected ConnectionHandlerFactory createFactory() {
return new DefaultHandlerFactory( NNTPHandler.class );
}
public void configure( final Configuration configuration )
throws ConfigurationException
{
//System.out.println(getClass().getName()+": configure");
m_port = configuration.getChild( "port" ).getValueAsInt( 119 );
try {
String bindAddress = configuration.getChild( "bind" ).getValue( null );
if( null != bindAddress )
m_bindTo = InetAddress.getByName( bindAddress );
} catch( final UnknownHostException unhe ) {
throw new ConfigurationException( "Malformed bind parameter", unhe );
}
final String useTLS = configuration.getChild("useTLS").getValue( "" );
if( useTLS.equals( "TRUE" ) )
m_serverSocketType = "ssl";
repository = (Component)NNTPUtil.createInstance
(configuration.getChild("repository"),getLogger(),
"org.apache.james.nntpserver.repository.NNTPRepositoryImpl");
super.configure( configuration.getChild( "nntphandler" ) );
getLogger().info("configured NNTPServer to run at : "+m_port);
}
public void compose( final ComponentManager componentManager )
throws ComponentManagerException
{
//System.out.println(getClass().getName()+": compose");
DefaultComponentManager mgr = new DefaultComponentManager(componentManager);
mgr.put("org.apache.james.nntpserver.repository.NNTPRepository",repository);
super.compose(mgr);
}
public void init() throws Exception {
//System.out.println(getClass().getName()+": init");
super.init();
if ( repository instanceof Initializable )
((Initializable)repository).init();
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: james-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: james-dev-help@jakarta.apache.org