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