From: adam Date: Fri, 7 Jan 2005 18:24:41 +0000 (+0000) Subject: NNTP overhaul X-Git-Url: http://git.megacz.com/?a=commitdiff_plain;h=c219fd5a10b79a07e123423696dcb698219bf99a;p=org.ibex.mail.git NNTP overhaul darcs-hash:20050107182441-5007d-5f9a72d11f394d85d3d0b3f46fafa8b1b086d990.gz --- diff --git a/src/org/ibex/mail/protocol/NNTP.java b/src/org/ibex/mail/protocol/NNTP.java index 235b236..3f2817f 100644 --- a/src/org/ibex/mail/protocol/NNTP.java +++ b/src/org/ibex/mail/protocol/NNTP.java @@ -1,7 +1,3 @@ -// Copyright 2000-2005 the Contributors, as shown in the revision logs. -// Licensed under the Apache Public Source License 2.0 ("the License"). -// You may not use this file except in compliance with the License. - package org.ibex.mail.protocol; import org.ibex.util.*; import org.ibex.io.*; @@ -16,23 +12,22 @@ import java.text.*; /** NNTP send/recieve */ public class NNTP { - // FIXME: command lines limited to 512 chars + public static final DateFormat dateFormat = new SimpleDateFormat("YYYYMMDDhhmmss"); public static class No extends RuntimeException { int code = 400; } // 4xx response codes public static class Bad extends RuntimeException { int code = 500; public Bad(String s) { super(s); } } // 5xx response codes public static class Group { public Group(String n, boolean p, int f, int l, int c) { this.name=n;this.post=p;this.first=f;this.last=l;this.count=c;} - public final String name; + public final String name; // case insensitive public final boolean post; public final int first; public final int last; - public final int count; + public final int count; // an approximation; must be >= actual number } public static class Article { - public Article(String messageid, int num, Message message) { this.message=message;this.messageid=messageid;this.num=num;} - public final String messageid; + public Article(int num, Message message) { this.message = message; this.num = num;} public final int num; public final Message message; } @@ -56,20 +51,20 @@ public class NNTP { private int ptr = 0; public MailboxWrapper(Mailbox root) { this.root = root; } public Group group(String s) { ptr = 0; setgroup(s); return getgroup(s); } + public boolean ihave(String messageid) { /* FEATURE */ return false; } public boolean post(Message m) { /* FEATURE */ return false; } + public Article next() { return article(ptr++, false, false); } public Article last() { return article(ptr--, false, false); } public Article article(String i, boolean h, boolean b) { return article(Query.header("message-id",i),h,b); } public Article article(int n, boolean h, boolean b) { ptr = n; return article(Query.messagenum(n,n),h,b); } - private Article article(Query q, boolean head, boolean body) { + private Article article(Query q, boolean head, boolean body) { Mailbox.Iterator it = current.iterator(q); if (!it.next()) return null; - try { - Message m = body ? it.cur() : Message.newMessage(new Stream(it.head() + "\r\n")); - //Message m = it.cur(); // FIXME - return new Article(m.messageid, it.num(), m); - } catch (Exception e) { return null; } + //Message m = body ? it.cur() : it.head(); + Message m = it.cur(); // FIXME + return new Article(m.messageid, it.num(), m); } public Group[] list() { return list(root, ""); } private Group[] list(Mailbox who, String prefix) { @@ -110,6 +105,7 @@ public class NNTP { private void println(String s) { Log.warn("[nntp-write]", s); conn.println(s); } private void println() { Log.warn("[nntp-write]", ""); conn.println(""); } + private void print(String s) { Log.warn("[nntp-write]", s); conn.print(s); } private void article(String numOrMessageId, boolean head, boolean body) { String s = numOrMessageId.trim(); @@ -121,15 +117,24 @@ public class NNTP { return; } int code = (head && body) ? 220 : head ? 221 : body ? 222 : 223; - println(code + " " + a.num + " <" + a.messageid + "> get ready for some stuff..."); + println(code + " " + a.num + " <" + a.message.messageid + "> get ready for some stuff..."); if (head) println(a.message.headers.raw); if (head && body) println(); - if (body) println(a.message.body); + if (body) { + Stream stream = new Stream(a.message.body); + while(true) { + s = stream.readln(); + if (s == null) break; + if (s.startsWith(".")) print("."); + println(s); + } + } println("."); } public void handleRequest(Connection conn) { this.conn = conn; conn.setTimeout(30 * 60 * 1000); + conn.setNewline("\r\n"); println("200 " + conn.vhost + " [" + NNTP.class.getName() + "]"); String user = null; String pass = null; @@ -140,6 +145,7 @@ public class NNTP { StringTokenizer st = new StringTokenizer(line, " "); String command = st.nextToken().toUpperCase(); if (command.equals("AUTHINFO")) { + // FIXME technically the RFC says we need to use this info to generate a SEnder: header... String uop = st.nextToken().toUpperCase(); if (uop.equals("USER")) user = st.nextToken(); else if (uop.equals("PASS")) pass = st.nextToken(); @@ -156,7 +162,18 @@ public class NNTP { } if (command.equals("ARTICLE")) { article(st.hasMoreTokens() ? st.nextToken() : null, true, true); } else if (command.equals("HEAD")) { article(st.hasMoreTokens() ? st.nextToken() : null, true, false); - } else if (command.equals("MODE")) { println("201 Hello, you can post."); + } else if (command.equals("DATE")) { + // FIXME must be GMT + println("111 " + dateFormat.format(new Date())); + } else if (command.equals("MODE")) { + if (st.hasMoreTokens()) { + String arg = st.nextToken(); + if (arg.equalsIgnoreCase("STREAM")); + streaming = true; + println("203 Streaming permitted"); + } else { + println("201 Hello, you can post."); + } } else if (command.equals("BODY")) { article(st.hasMoreTokens() ? st.nextToken() : null, false, true); } else if (command.equals("STAT")) { article(st.hasMoreTokens() ? st.nextToken() : null, false, false); } else if (command.equals("HELP")) { println("100 you are beyond help."); println("."); @@ -169,20 +186,20 @@ public class NNTP { int end = Integer.parseInt(range.substring(range.indexOf('-') + 1)); Mailbox.Iterator it = api.current.iterator(Query.messagenum(start, end)); while(it.next()) { - try { - Message m = Message.newMessage(new Stream(it.head() + "\r\n")); - println(it.num()+"\t"+m.subject+"\t"+m.from+"\t"+m.date+"\t"+m.messageid+"\t"+ - m.headers.gets("references") + "\t" + m.size() + "\t" + m.lines); - } catch (Exception e) { Log.error(this, e); } + Message m = it.cur(); + println(it.num()+"\t"+m.subject+"\t"+m.from+"\t"+m.date+"\t"+m.messageid+"\t"+ + m.headers.gets("references") + "\t" + m.size() + "\t" + m.lines); } println("."); - } else if (command.equals("LAST")) { Article a = api.last(); println("223 "+a.num+" "+a.messageid+" ok"); - } else if (command.equals("NEXT")) { Article a = api.next(); println("223 "+a.num+" "+a.messageid+" ok"); + } else if (command.equals("LAST")) { Article a = api.last(); println("223 "+a.num+" "+a.message.messageid+" ok"); + } else if (command.equals("NEXT")) { Article a = api.next(); println("223 "+a.num+" "+a.message.messageid+" ok"); } else if (command.equals("QUIT")) { println("205 Bye."); conn.close(); return; } else if (command.equals("GROUP")) { Group g = api.group(st.nextToken().toLowerCase()); println("211 " + g.count + " " + g.first + " " + g.last + " " + g.name); } else if (command.equals("NEWGROUPS") || command.equals("NEWNEWS")) { + // FIXME: * and ! unsupported + // NEWNEWS is often not supported String groups = command.equals("NEWNEWS") ? st.nextToken() : null; String datetime = st.nextToken() + " " + st.nextToken(); String gmt = st.nextToken(); @@ -219,7 +236,21 @@ public class NNTP { } } else if (command.equals("POST")) { + // add NNTP-Posting-Host header // FIXME + // required headers: Newsgroups, Subject, Message-ID, Path, From, Date. No wildcars in newsgroups list + // Path header: prepend , (any punctuation separates the list) + // Expires header: the date when expiration happens (??) should we ignore this? + // Control header: body is the command. Inteprert posts to all.all.ctl as control messages, use Subject line if no Cntrol line + // "Approved" line is used for moderaion + // Xref: drop this header if you see it + + // Control messages + // cancel (do not forward if I am unable to cancel locally) + // ihave/sendme: do not support + // newgroup [moderated] -- body of message is a description of the group + // rmgroup + /* boolean postok = api.post(); if (!postok) { @@ -232,12 +263,85 @@ public class NNTP { //} } else if (command.equals("LIST")) { - if (st.hasMoreTokens()) throw new Bad("LIST " + st.nextToken() + " not supported"); Group[] g = api.list(); println("215 list of groups follows"); for(int i=0; i and not found + // 420 if no messages in range + } else if (command.equals("XPAT")) { + // just like XHDR, but a pattern follows the last argument (may contain whitespace) + println("221 yep"); + // print + println("."); + } else if (command.equals("LIST")) { + if (st.hasMoreTokens()) { + String argument = st.nextToken().toUppercase(); + if (argument.equalsIgnoreCase("EXTENSIONS")) { + println("202 Extensions supported:"); + println("STREAMING"); + println(""); + println("."); + } else if (argument.equals("ACTIVE")) { + String wildmat = st.hasMoreTokens() ? st.nextToken() : null; + // FIXME: deal with wildmat + // just like list, but only show active groups + } else if (argument.equals("SUBSCRIPTIONS")) { + // FIXME: show 215, default subscription list for new users, period + } else if (argument.equals("OVERVIEW.FMT")) { + println("215 Overview format:"); + println("Subject:"); + println("From:"); + println("Date:"); + println("Message-ID:"); + println("References:"); + println("Bytes:"); + println("Lines:"); + //println("Xref:full"); + println("."); + } else if (argument.equals("NEWSGROUPS")) { + String wildmat = st.hasMoreTokens() ? st.nextToken() : null; + // respond 215, print each newsgroup, a space, and the description; end with lone period + } else { + // barf here + } + } else { + Group[] g = api.list(); + println("215 list of groups follows"); + for(int i=0; i