Initial import.
2 * StarOffice News Server
3 * see AUTHORS for the list of contributors
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 import java.io.BufferedReader;
22 import java.io.BufferedWriter;
24 import java.io.InputStreamReader;
25 import java.io.IOException;
26 import java.io.OutputStreamWriter;
27 import java.net.Socket;
28 import java.net.SocketException;
29 import java.text.SimpleDateFormat;
30 import java.util.Date;
31 import java.util.LinkedList;
32 import java.util.List;
33 import com.so.news.command.ArticleCommand;
34 import com.so.news.command.GroupCommand;
35 import com.so.news.command.ListCommand;
36 import com.so.news.command.PostCommand;
37 import com.so.news.command.OverCommand;
38 import com.so.news.storage.Article;
39 import com.so.news.storage.Group;
42 * Represents the connection between the server and one client.
43 * @author Christian Lins (christian.lins@web.de)
45 public class NNTPConnection extends Thread
47 public static final String NEWLINE = "\r\n";
48 public static final String MESSAGE_ID_PATTERN = "<[^>]+>";
51 = Boolean.parseBoolean(Config.getInstance().get("n3tpd.debug", "false"));
52 private Socket socket;
53 private boolean exit = false;
54 private BufferedWriter out;
55 private BufferedReader in;
56 private Article currentArticle = null;
57 private Group currentGroup = null;
60 * Creates a new NNTPConnection instance using the given connected Socket.
62 * @throws java.io.IOException
64 public NNTPConnection(Socket socket)
68 this.in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
69 this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
70 // TODO: The output stream should be of type PrintStream so that many
71 // of the printX() methods of this class can go to trash
73 setDaemon(true); // Exits if the main thread is killed
77 * Closes the associated socket end exits the Thread.
93 * Prints a CharSequence to the sockets output stream.
95 public void print(CharSequence s) throws IOException
100 public void println(CharSequence s) throws IOException
105 System.out.println("<<< " + s);
108 public void printStatus(int code, String description) throws IOException
110 println("" + code + " " + description);
114 public void printTextLine(CharSequence line) throws IOException
116 if (line.length() > 0 && line.charAt(0) == '.')
121 public void printTextPart(CharSequence text) throws IOException
123 String[] lines = text.toString().split(NEWLINE);
124 for (String line : lines)
128 public void printText(CharSequence text) throws IOException
135 public void flush() throws IOException
140 public String readln() throws IOException
142 String s = in.readLine();
144 throw new IOException("Socket closed");
146 System.out.println(">>> " + s);
150 public String[] readCommand() throws IOException
152 return readln().split("[ ]+");
155 public List<String> readText() throws IOException
157 List<String> l = new LinkedList<String>();
164 if (s.startsWith(".."))
169 while (!s.equals("."));
173 public String readTextLine() throws IOException
183 if (s.startsWith(".."))
188 public void setCurrentArticle(Article current)
190 currentArticle = current;
193 public Article getCurrentArticle()
195 return currentArticle;
198 public void setCurrentGroup(Group current)
200 currentGroup = current;
203 public Group getCurrentGroup()
208 private void processCommand(String[] command)
211 if (command.length == 0)
212 return; // TODO Error
214 String commandName = command[0];
218 // TODO NEWGROUPS command
219 // TODO NEWNEWS command
222 // TODO LIST ACTIVE command
223 // TODO LIST ACTIVE.TIMES command
224 // TODO LIST DISTRIBUTIONS command
225 // TODO LIST DISTRIB.PATS command
226 // TODO XGTITLE command
229 // TODO XPATH command
230 // TODO XROVER command
231 // TODO XTHREAD command
232 // TODO AUTHINFO command
235 if (commandName.equalsIgnoreCase("ARTICLE")
236 || commandName.equalsIgnoreCase("STAT")
237 || commandName.equalsIgnoreCase("HEAD")
238 || commandName.equalsIgnoreCase("BODY"))
240 ArticleCommand cmd = new ArticleCommand(this);
241 cmd.process(command);
244 else if (commandName.equalsIgnoreCase("LIST"))
246 ListCommand cmd = new ListCommand(this);
247 cmd.process(command);
250 else if (commandName.equalsIgnoreCase("GROUP"))
252 GroupCommand cmd = new GroupCommand(this);
253 cmd.process(command);
256 else if(commandName.equalsIgnoreCase("POST"))
258 PostCommand cmd = new PostCommand(this);
259 cmd.process(command);
262 else if (commandName.equalsIgnoreCase("CHECK")
263 || commandName.equalsIgnoreCase("TAKETHIS"))
265 // untested, RFC2980 compliant
266 printStatus(400, "not accepting articles");
270 else if (commandName.equalsIgnoreCase("IHAVE")
271 || commandName.equalsIgnoreCase("XREPLIC"))
273 // untested, RFC977 compliant
274 printStatus(435, "article not wanted - do not send it");
278 else if (commandName.equalsIgnoreCase("XCREATEGROUP"))
283 else if (commandName.equalsIgnoreCase("SLAVE"))
285 // untested, RFC977 compliant
286 printStatus(202, "slave status noted");
290 else if (commandName.equalsIgnoreCase("XINDEX"))
292 // untested, RFC2980 compliant
293 printStatus(418, "no tin-style index is available for this news group");
297 else if (commandName.equalsIgnoreCase("DATE"))
299 printStatus(111, new SimpleDateFormat("yyyyMMddHHmmss")
300 .format(new Date()));
304 else if (commandName.equalsIgnoreCase("MODE"))
306 if (command[1].equalsIgnoreCase("READER"))
308 // untested, RFC2980 compliant
309 printStatus(200, "Hello, you can post");
311 else if (command[1].equalsIgnoreCase("STREAM"))
313 printStatus(203, "Streaming is OK");
316 printStatus(501, "Command not supported");
319 else if (commandName.equalsIgnoreCase("QUIT"))
321 // untested, RFC977 compliant
322 printStatus(205, "closing connection - goodbye!");
327 else if (commandName.equalsIgnoreCase("XSHUTDOWN"))
329 printStatus(205, "closing connection - goodbye!");
335 else if(commandName.equalsIgnoreCase("XOVER")
336 || commandName.equalsIgnoreCase("OVER"))
338 OverCommand cmd = new OverCommand(this);
339 cmd.process(command);
343 printStatus(501, "Command not supported");
347 * Runloop of this Thread.
348 * @throws RuntimeException if this method is called directly.
353 assert !this.equals(Thread.currentThread());
357 printStatus(200, Config.getInstance().get("n3tpd.hostname", "localhost")
358 + " " + Main.VERSION + " news server ready - (posting ok).");
360 catch (IOException e1)
369 processCommand(readCommand());
371 catch (SocketException e)
378 catch (IOException e)