diff -r 000000000000 -r f907866f0e4b trunk/com/so/news/NNTPConnection.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/com/so/news/NNTPConnection.java Tue Jan 20 10:21:03 2009 +0100
@@ -0,0 +1,395 @@
+/*
+ * StarOffice News Server
+ * see AUTHORS for the list of contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.so.news;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.net.Socket;
+import java.net.SocketException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import com.so.news.command.ArticleCommand;
+import com.so.news.command.GroupCommand;
+import com.so.news.command.ListCommand;
+import com.so.news.command.PostCommand;
+import com.so.news.command.OverCommand;
+import com.so.news.storage.Article;
+import com.so.news.storage.Group;
+
+/**
+ * Represents the connection between the server and one client.
+ * @author Christian Lins (christian.lins@web.de)
+ */
+public class NNTPConnection extends Thread
+{
+ public static final String NEWLINE = "\r\n";
+ public static final String MESSAGE_ID_PATTERN = "<[^>]+>";
+
+ private boolean debug
+ = Boolean.parseBoolean(Config.getInstance().get("n3tpd.debug", "false"));
+ private Socket socket;
+ private boolean exit = false;
+ private BufferedWriter out;
+ private BufferedReader in;
+ private Article currentArticle = null;
+ private Group currentGroup = null;
+
+ /**
+ * Creates a new NNTPConnection instance using the given connected Socket.
+ * @param socket
+ * @throws java.io.IOException
+ */
+ public NNTPConnection(Socket socket)
+ throws IOException
+ {
+ this.socket = socket;
+ this.in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
+ // TODO: The output stream should be of type PrintStream so that many
+ // of the printX() methods of this class can go to trash
+
+ setDaemon(true); // Exits if the main thread is killed
+ }
+
+ /**
+ * Closes the associated socket end exits the Thread.
+ */
+ public void exit()
+ {
+ try
+ {
+ exit = true;
+ socket.close();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Prints a CharSequence to the sockets output stream.
+ */
+ public void print(CharSequence s) throws IOException
+ {
+ out.append(s);
+ }
+
+ public void println(CharSequence s) throws IOException
+ {
+ print(s);
+ print(NEWLINE);
+ if (debug)
+ System.out.println("<<< " + s);
+ }
+
+ public void printStatus(int code, String description) throws IOException
+ {
+ println("" + code + " " + description);
+ flush();
+ }
+
+ public void printTextLine(CharSequence line) throws IOException
+ {
+ if (line.length() > 0 && line.charAt(0) == '.')
+ print("..");
+ println(line);
+ }
+
+ public void printTextPart(CharSequence text) throws IOException
+ {
+ String[] lines = text.toString().split(NEWLINE);
+ for (String line : lines)
+ printTextLine(line);
+ }
+
+ public void printText(CharSequence text) throws IOException
+ {
+ printTextPart(text);
+ println(".");
+ flush();
+ }
+
+ public void flush() throws IOException
+ {
+ out.flush();
+ }
+
+ public String readln() throws IOException
+ {
+ String s = in.readLine();
+ if (s == null)
+ throw new IOException("Socket closed");
+ if (debug)
+ System.out.println(">>> " + s);
+ return s;
+ }
+
+ public String[] readCommand() throws IOException
+ {
+ return readln().split("[ ]+");
+ }
+
+ public List readText() throws IOException
+ {
+ List l = new LinkedList();
+ String s;
+ do
+ {
+ s = readln();
+ if (!s.equals("."))
+ {
+ if (s.startsWith(".."))
+ s = s.substring(1);
+ l.add(s);
+ }
+ }
+ while (!s.equals("."));
+ return l;
+ }
+
+ public String readTextLine() throws IOException
+ {
+ String s = null;
+ do
+ {
+ s = readln();
+ }
+ while (s == null);
+ if (s.equals("."))
+ return null;
+ if (s.startsWith(".."))
+ s = s.substring(1);
+ return s;
+ }
+
+ public void setCurrentArticle(Article current)
+ {
+ currentArticle = current;
+ }
+
+ public Article getCurrentArticle()
+ {
+ return currentArticle;
+ }
+
+ public void setCurrentGroup(Group current)
+ {
+ currentGroup = current;
+ }
+
+ public Group getCurrentGroup()
+ {
+ return currentGroup;
+ }
+
+ private void processCommand(String[] command)
+ throws Exception
+ {
+ if (command.length == 0)
+ return; // TODO Error
+
+ String commandName = command[0];
+
+ // RFC977
+ // TODO HELP command
+ // TODO NEWGROUPS command
+ // TODO NEWNEWS command
+
+ // RFC2980
+ // TODO LIST ACTIVE command
+ // TODO LIST ACTIVE.TIMES command
+ // TODO LIST DISTRIBUTIONS command
+ // TODO LIST DISTRIB.PATS command
+ // TODO XGTITLE command
+ // TODO XHDR command
+ // TODO XPAT command
+ // TODO XPATH command
+ // TODO XROVER command
+ // TODO XTHREAD command
+ // TODO AUTHINFO command
+
+ // STANDARD COMMANDS
+ if (commandName.equalsIgnoreCase("ARTICLE")
+ || commandName.equalsIgnoreCase("STAT")
+ || commandName.equalsIgnoreCase("HEAD")
+ || commandName.equalsIgnoreCase("BODY"))
+ {
+ ArticleCommand cmd = new ArticleCommand(this);
+ cmd.process(command);
+ }
+
+ else if (commandName.equalsIgnoreCase("LIST"))
+ {
+ ListCommand cmd = new ListCommand(this);
+ cmd.process(command);
+ }
+
+ else if (commandName.equalsIgnoreCase("GROUP"))
+ {
+ GroupCommand cmd = new GroupCommand(this);
+ cmd.process(command);
+ }
+
+ else if(commandName.equalsIgnoreCase("POST"))
+ {
+ PostCommand cmd = new PostCommand(this);
+ cmd.process(command);
+ }
+
+ else if (commandName.equalsIgnoreCase("CHECK")
+ || commandName.equalsIgnoreCase("TAKETHIS"))
+ {
+ // untested, RFC2980 compliant
+ printStatus(400, "not accepting articles");
+ return;
+ }
+
+ else if (commandName.equalsIgnoreCase("IHAVE")
+ || commandName.equalsIgnoreCase("XREPLIC"))
+ {
+ // untested, RFC977 compliant
+ printStatus(435, "article not wanted - do not send it");
+ return;
+ }
+
+ else if (commandName.equalsIgnoreCase("XCREATEGROUP"))
+ {
+ return;
+ }
+
+ else if (commandName.equalsIgnoreCase("SLAVE"))
+ {
+ // untested, RFC977 compliant
+ printStatus(202, "slave status noted");
+ return;
+ }
+
+ else if (commandName.equalsIgnoreCase("XINDEX"))
+ {
+ // untested, RFC2980 compliant
+ printStatus(418, "no tin-style index is available for this news group");
+ return;
+ }
+
+ else if (commandName.equalsIgnoreCase("DATE"))
+ {
+ printStatus(111, new SimpleDateFormat("yyyyMMddHHmmss")
+ .format(new Date()));
+ return;
+ }
+
+ else if (commandName.equalsIgnoreCase("MODE"))
+ {
+ if (command[1].equalsIgnoreCase("READER"))
+ {
+ // untested, RFC2980 compliant
+ printStatus(200, "Hello, you can post");
+ }
+ else if (command[1].equalsIgnoreCase("STREAM"))
+ {
+ printStatus(203, "Streaming is OK");
+ }
+ else
+ printStatus(501, "Command not supported");
+ }
+
+ else if (commandName.equalsIgnoreCase("QUIT"))
+ {
+ // untested, RFC977 compliant
+ printStatus(205, "closing connection - goodbye!");
+ exit();
+ return;
+ }
+
+ else if (commandName.equalsIgnoreCase("XSHUTDOWN"))
+ {
+ printStatus(205, "closing connection - goodbye!");
+ exit();
+ return;
+ }
+
+ // X COMMANDS
+ else if(commandName.equalsIgnoreCase("XOVER")
+ || commandName.equalsIgnoreCase("OVER"))
+ {
+ OverCommand cmd = new OverCommand(this);
+ cmd.process(command);
+ }
+
+ else
+ printStatus(501, "Command not supported");
+ }
+
+ /**
+ * Runloop of this Thread.
+ * @throws RuntimeException if this method is called directly.
+ */
+ @Override
+ public void run()
+ {
+ assert !this.equals(Thread.currentThread());
+
+ try
+ {
+ printStatus(200, Config.getInstance().get("n3tpd.hostname", "localhost")
+ + " " + Main.VERSION + " news server ready - (posting ok).");
+ }
+ catch (IOException e1)
+ {
+ exit();
+ }
+
+ while (!exit)
+ {
+ try
+ {
+ processCommand(readCommand());
+ }
+ catch (SocketException e)
+ {
+ if (exit)
+ return;
+ exit();
+ e.printStackTrace();
+ }
+ catch (IOException e)
+ {
+ if (exit)
+ return;
+ exit();
+ e.printStackTrace();
+ }
+ catch (Throwable e)
+ {
+ if (exit)
+ return;
+ e.printStackTrace();
+ // silently ignore
+ }
+ }
+ }
+
+}