X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2Fmail%2Fprotocol%2FNNTP.java;h=55d21360f1decc179dfb9ad62b8b8300f94008b9;hb=e2f945712cd67ace6e629f73beb662f7ecb7beaa;hp=f822ef5841b9e0891c653b0641fb14d8da6f5112;hpb=79d757b55fd4507718b35f584a02b449bea76603;p=org.ibex.mail.git diff --git a/src/org/ibex/mail/protocol/NNTP.java b/src/org/ibex/mail/protocol/NNTP.java index f822ef5..55d2136 100644 --- a/src/org/ibex/mail/protocol/NNTP.java +++ b/src/org/ibex/mail/protocol/NNTP.java @@ -24,7 +24,7 @@ public class NNTP { public static final DateFormat dateFormat = new SimpleDateFormat("yyyyMMDDhhmmss"); - public static class No extends RuntimeException { int code = 400; } // 4xx response codes + 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 { @@ -37,7 +37,7 @@ public class NNTP { } public static class Article { - public Article(int num, Message message) { this.message = message; this.num = num;} + public Article(int num, Message message) { this.message = message; this.num = num; } public final int num; public final Message message; } @@ -47,7 +47,8 @@ public class NNTP { public boolean ihave(String messageid); public Article next(); public Article last(); - public boolean post(Message m); + public boolean postok(); + public void post(Message m) throws IOException; public Article article(String messageid, boolean head, boolean body); public Article article(int messagenum, boolean head, boolean body); public Group[] list(); @@ -59,11 +60,20 @@ public class NNTP { private final Mailbox root; private Mailbox current; private int ptr = 0; - public MailboxWrapper(Mailbox root) { this.root = root; } - public Group group(String s) { ptr = 0; setgroup(s); return getgroup(s); } + private boolean post; + public MailboxWrapper(Mailbox root) { this(root, false); } + public MailboxWrapper(Mailbox root, boolean post) { this.root = root; this.post = post; } + public boolean postok() { return post; } + public void post(Message m) throws IOException { current.accept(m); } + public Group group(String s) { + ptr = 0; + Group g = getgroup(s); + if (g==null) return null; + setgroup(s); + return g; + } 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); } @@ -73,8 +83,7 @@ public class NNTP { 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 + Message m = body ? it.cur() : Message.newMessage(new Fountain.StringFountain(it.head() + "\r\n")); return new Article(it.num(), m); } catch (Exception e) { return null; } } @@ -94,14 +103,24 @@ public class NNTP { } private void setgroup(String s) { - current = root; + Mailbox ncurrent = root; for(StringTokenizer st = new StringTokenizer(s, "."); - st.hasMoreTokens(); - current = current.slash(st.nextToken(), false)); + ncurrent != null && st.hasMoreTokens(); + ncurrent = ncurrent.slash(st.nextToken(), false)); + if (ncurrent!=null) current=ncurrent; } private Group getgroup(String s) { Mailbox box = root; - for(StringTokenizer st = new StringTokenizer(s, "."); st.hasMoreTokens(); box = box.slash(st.nextToken(), false)); +v v v v v v v + Log.error("", "getgroup " + s); + Log.error("", "mailbox " + root); + for(StringTokenizer st = new StringTokenizer(s, "."); box!=null && st.hasMoreTokens(); box = box.slash(st.nextToken(), false)); +************* + for(StringTokenizer st = new StringTokenizer(s, "."); + box!=null && st.hasMoreTokens(); + box = box.slash(st.nextToken(), false)); +^ ^ ^ ^ ^ ^ ^ + if (box==null) return null; return new Group(s, true, 1, box.count(Query.all()), box.count(Query.all())); } @@ -109,7 +128,7 @@ public class NNTP { public String[] newnews(String[] groups, Date d, String[] distributions) { /* FIXME */ return null; } } - public static class Listener implements Worker { + public static class Listener { private Server api = null; private Login login; private Connection conn; @@ -130,10 +149,10 @@ public class NNTP { } int code = (head && body) ? 220 : head ? 221 : body ? 222 : 223; println(code + " " + a.num + " <" + a.message.messageid + "> get ready for some stuff..."); - if (head) println(a.message.headers.raw); + if (head) println(a.message.headers.getString()); if (head && body) println(); if (body) { - Stream stream = new Stream(a.message.body); + Stream stream = a.message.getBody().getStream(); while(true) { s = stream.readln(); if (s == null) break; @@ -151,7 +170,7 @@ public class NNTP { String user = null; String pass = null; Account account = login.anonymous(); - this.api = account == null ? null : new MailboxWrapper(account.getMailbox(NNTP.class)); + this.api = account == null ? null : new MailboxWrapper(account.getMailbox(NNTP.class), true); for(String line = conn.readln(); line != null; line = conn.readln()) try { Log.warn("[nntp-read]", line); StringTokenizer st = new StringTokenizer(line, " "); @@ -159,18 +178,24 @@ public class NNTP { 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(); - // FIXME error here + if (uop.equals("USER")) { + user = st.nextToken(); + println("381 More authentication required"); + continue; + } else if (uop.equals("PASS")) { + pass = st.nextToken(); + account = login.login(user, pass); + if (account == null) { println("502 Invalid"); continue; } + Mailbox box = account.getMailbox(NNTP.class); + this.api = new MailboxWrapper(box, true); + println("281 Good to go"); + continue; + } + throw new Bad("wtf are you talking about?"); } if (this.api == null) { if (user == null) { println("480 Authentication required"); continue; } if (pass == null) { println("381 Password required"); continue; } - account = login.login(user, pass); - if (account == null) { println("502 Invalid"); continue; } - this.api = new MailboxWrapper(account.getMailbox(NNTP.class)); - println("281 Good to go"); - continue; } 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); @@ -199,30 +224,34 @@ public class NNTP { Mailbox.Iterator it = api.current.iterator(Query.messagenum(start, end)); while(it.next()) { try { - Message m = Message.newMessage(new Stream(it.head() + "\r\n")); + 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); + m.headers.get("references") + "\t" + m.getLength() + "\t" + m.getNumLines()); } catch (Exception e) { Log.error(this, e); } } println("."); - } 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("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); + if (g==null) println("411 no such group"); + else 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(); - String distributions = gmt.equals("GMT") ? (st.hasMoreTokens() ? st.nextToken() : null) : gmt; + String distributions = gmt.equals("GMT") ? (st.hasMoreTokens() ? st.nextToken() : "") : gmt; while(st.hasMoreTokens()) distributions += " " + st.nextToken(); + // FIXME deal with GMT Date d = new Date(); try { - d = new SimpleDateFormat("yyMMDD HHMMSS").parse(gmt); + d = new SimpleDateFormat("yyMMDD HHMMSS").parse(datetime); } catch (ParseException pe) { Log.warn(this, pe); } @@ -250,12 +279,12 @@ 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 + // add NNTP-Posting-Host header // 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 + // 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 @@ -265,16 +294,43 @@ public class NNTP { // newgroup [moderated] -- body of message is a description of the group // rmgroup - /* - boolean postok = api.post(); + boolean postok = api.postok(); if (!postok) { println("440 no posting allowed"); } else { - */ - println("340 send the article"); - // FIME read the article here - println("240 article posted ok"); - //} + println("340 send the article"); + StringBuffer buf = new StringBuffer(); + while(true) { + String s = conn.readln(); + if (s == null) throw new RuntimeException("connection closed"); + if (s.equals(".")) break; + if (s.startsWith(".")) s = s.substring(1); + buf.append(s + "\r\n"); + } + String body = buf.toString(); + try { + Message m = Message.newMessage(new Fountain.StringFountain(body)); + if (m.headers.get("newsgroups")==null) + println("441 posted messages must have a Newsgroups header per RFC 977"); + else if (m.headers.get("newsgroups").indexOf('*')!=-1) + println("441 Newsgroups header in posted messages may not contain wildcards (*) per RFC 977"); + else if (m.headers.get("subject")==null) + println("441 posted messages must have a Subject header per RFC 977"); + // else if (m.headers.get("path")==null) + //println("441 posted messages must have a Path header per RFC 977"); + else if (m.headers.get("from")==null) + println("441 posted messages must have a From header per RFC 977"); + else if (m.headers.get("date")==null) + println("441 posted messages must have a Date header per RFC 977"); + else { + api.post(m); + println("240 article posted ok"); + } + } catch (Exception e) { + e.printStackTrace(); + println("441 posting failed: " + e); + } + } } else if (command.equals("XROVER")) { // equivalent to "XHDR References"