From 4ff26332350edaa3c6ebfa29408c61412a6a2e40 Mon Sep 17 00:00:00 2001 From: adam Date: Wed, 28 Feb 2007 10:18:51 +0000 Subject: [PATCH] change num() to imapNumber() and nntpNumber(), add comments about semantics darcs-hash:20070228101851-5007d-62915ed43ed5da012a1fb1f984b933b9484605b9.gz --- src/org/ibex/mail/Query.java | 20 +++++++++----- src/org/ibex/mail/protocol/GMail.java | 3 ++- src/org/ibex/mail/protocol/IMAP.java | 20 ++++++++------ src/org/ibex/mail/protocol/NNTP.java | 10 +++---- src/org/ibex/mail/protocol/SMTP.java | 9 ++----- src/org/ibex/mail/target/FileBasedMailbox.java | 7 ++--- src/org/ibex/mail/target/Mailbox.java | 29 ++++++++++++++++++--- src/org/ibex/mail/target/MailmanArchives.java | 4 +-- src/org/ibex/mail/target/MessageArrayMailbox.java | 2 +- src/org/ibex/mail/target/SqliteJdbcMailbox.java | 5 ++-- 10 files changed, 69 insertions(+), 40 deletions(-) diff --git a/src/org/ibex/mail/Query.java b/src/org/ibex/mail/Query.java index d308da8..14f3e09 100644 --- a/src/org/ibex/mail/Query.java +++ b/src/org/ibex/mail/Query.java @@ -26,7 +26,8 @@ public class Query { public static Query or(Query q1, Query q2) { return new Query(OR,new Query[] {q1,q2},0,0,0,null,null, null, null, null); } public static Query or(Query[] q) { return new Query(OR, q, 0, 0, 0, null, null, null, null, null); } public static Query uid(int min, int max) { return new Query(UID, null, min, max, 0, null, null, null, null, null); } - public static Query messagenum(int min,int max) { return new Query(NUM,null,min,max,0,null,null,null,null, null); } + public static Query imapNumber(int min,int max) { return new Query(IMAPNUM,null,min,max,0,null,null,null,null, null); } + public static Query nntpNumber(int min,int max) { return new Query(NNTPNUM,null,min,max,0,null,null,null,null, null); } public static Query sent(Date e, Date l) { return new Query(SENT,null,0,0,0,null,null,e,l,null); } public static Query arrival(Date e, Date l) { return new Query(ARRIVAL,null,0,0,0,null,null, e,l,null);} public static Query header(String k, String v) { return new Query(HEADER, null, 0, 0, 0, k, v, null, null, null);} @@ -34,7 +35,7 @@ public class Query { public static Query body(String text) { return new Query(BODY, null, 0, 0, 0, null, text, null, null, null);} public static Query full(String text) { return new Query(FULL, null, 0, 0, 0, null, text, null, null, null);} public static Query uid(int[] set) { return new Query(UID, null, 0 ,0 ,0, null, null, null, null, set);} - public static Query num(int[] set) { return new Query(NUM, null, 0 ,0 ,0, null, null, null, null, set);} + public static Query imapNumber(int[] set) { return new Query(IMAPNUM, null, 0 ,0 ,0, null, null, null, null, set);} public static Query all() { return new Query(ALL, null, 0, 0, 0, null, null, null, null, null); } public static Query deleted() { return new Query(DELETED, null, 0, 0, 0, null, null, null, null, null); } public static Query seen() { return new Query(SEEN, null, 0, 0, 0, null, null, null, null, null); } @@ -42,7 +43,7 @@ public class Query { public static Query draft() { return new Query(DRAFT, null, 0, 0, 0, null, null, null, null, null); } public static Query answered() { return new Query(ANSWERED, null, 0, 0, 0, null, null, null, null, null); } public static Query recent() { return new Query(RECENT, null, 0, 0, 0, null, null, null, null, null); } - public static Query set(boolean uid, int[] set) { return uid ? uid(set) : num(set); } + //public static Query set(boolean uid, int[] set) { return uid ? uid(set) : imapNumber(set); } private Query(int type, Query[] q,int min,int max, int flags, String key, String text, Date earliest, Date latest, int[] set) { this.type = type; this.q = q; this.min = min; this.max = max; this.flags = flags; this.key = key; this.text = text; @@ -59,13 +60,14 @@ public class Query { public static final int SIZE = 8; public static final int BODY = 9; public static final int FULL = 10; - public static final int NUM = 11; + public static final int IMAPNUM = 11; public static final int DELETED = 12; public static final int SEEN = 13; public static final int FLAGGED = 14; public static final int DRAFT = 15; public static final int ANSWERED = 16; public static final int RECENT = 17; + public static final int NNTPNUM = 18; public final int type; public final Query[] q; @@ -89,10 +91,14 @@ public class Query { if (set[i] <= it.uid() && set[i+1] >= it.uid()) return true; return false; } else return it.uid() >= min && it.uid() <= max; - case NUM: if (set != null) { - for(int i=0; i= it.num()) return true; + case IMAPNUM: if (set != null) { + for(int i=0; i= it.imapNumber()) return true; return false; } - else return it.num() >= min && it.num() <= max; + else return it.imapNumber() >= min && it.imapNumber() <= max; + case NNTPNUM: if (set != null) { + for(int i=0; i= it.nntpNumber()) return true; + return false; } + else return it.imapNumber() >= min && it.imapNumber() <= max; case SENT: return (latest==null||it.cur().date.before(latest)) && (earliest==null||it.cur().date.after(earliest)); case ARRIVAL: return (latest == null || it.cur().arrival.before(latest)) && diff --git a/src/org/ibex/mail/protocol/GMail.java b/src/org/ibex/mail/protocol/GMail.java index aadfa41..c43df9f 100644 --- a/src/org/ibex/mail/protocol/GMail.java +++ b/src/org/ibex/mail/protocol/GMail.java @@ -132,7 +132,8 @@ public class GMail extends Account { public Headers head() { return m.headers; } public boolean next() { return false; } public int uid() { return num; } - public int num() { return num; } + public int imapNumber() { return num; } + public int nntpNumber() { throw new RuntimeException("not supported"); } public void delete() { } public void set(String key, String val) { } public String get(String key) { return null; } diff --git a/src/org/ibex/mail/protocol/IMAP.java b/src/org/ibex/mail/protocol/IMAP.java index f6c084f..5b98105 100644 --- a/src/org/ibex/mail/protocol/IMAP.java +++ b/src/org/ibex/mail/protocol/IMAP.java @@ -207,7 +207,7 @@ public class IMAP { public int[] search(Query q, boolean uid) { Vec.Int vec = new Vec.Int(); for(Mailbox.Iterator it = selected().iterator(q); it.next();) { - vec.addElement(uid ? it.uid() : it.num()); + vec.addElement(uid ? it.uid() : it.imapNumber()); it.recent(false); } return vec.dump(); @@ -225,7 +225,7 @@ public class IMAP { else if (style == 0) it.setFlags(flags); else if (style == 1) it.addFlags(flags); it.recent(recent); - if (!silent) client.fetch(it.num(), it.flags(), -1, null, it.uid()); + if (!silent) client.fetch(it.imapNumber(), it.flags(), -1, null, it.uid()); } } public void rename(String from0, String to) { @@ -239,7 +239,7 @@ public class IMAP { Message message = ((spec & (BODYSTRUCTURE | ENVELOPE | INTERNALDATE | FIELDS | FIELDSNOT | RFC822 | RFC822TEXT | RFC822SIZE | HEADERNOT | HEADER)) != 0) ? it.cur() : null; int size = message == null ? 0 : message.getLength(); - client.fetch(it.num(), it.flags(), size, message, it.uid()); + client.fetch(it.imapNumber(), it.flags(), size, message, it.uid()); it.recent(false); } } @@ -301,7 +301,6 @@ public class IMAP { case SUBSCRIBE: api.subscribe(token().astring()); break; case UNSUBSCRIBE: api.unsubscribe(token().astring()); break; case RENAME: api.rename(token().astring(), token().astring()); break; - case COPY: selected(); api.copy(Query.set(uid, token().set(maxn(uid))), token().astring()); break; case DELETE: api.delete(token().atom()); break; case CHECK: selected(); api.check(); break; case NOOP: api.noop(); break; @@ -309,8 +308,13 @@ public class IMAP { case EXPUNGE: selected(); api.expunge(); break; case UNSELECT: selected(); api.unselect(); selected = false; break; case CREATE: api.create(token().astring()); break; - case FETCH: selected(); fetch(Query.set(lastuid=uid, token().set(maxn(uid))), + case FETCH: selected(); fetch(((lastuid=uid) + ? Query.uid(token().set(maxn(uid))) + : Query.imapNumber(token().set(maxn(uid)))), lastfetch=token().lx(), 0, 0, 0, uid, 0); break; + case COPY: selected(); api.copy(uid + ? Query.uid(token().set(maxn(uid))) + : Query.imapNumber(token().set(maxn(uid))), token().astring()); break; case SEARCH: { selected(); int[] result = api.search(query(maxn(uid)), uid); @@ -357,7 +361,7 @@ public class IMAP { break; } case STORE: { selected(); - Query q = uid ? Query.uid(token().set(maxn(uid))) : Query.num(token().set(maxn(uid))); + Query q = uid ? Query.uid(token().set(maxn(uid))) : Query.imapNumber(token().set(maxn(uid))); String s = token().atom().toUpperCase(); int flags = token().flags(); if (s.equals("FLAGS")) api.setFlags(q, flags, uid, false); @@ -460,7 +464,7 @@ public class IMAP { } else if (!(s.equals("BODY.PEEK") || s.equals("BODY"))) { throw new Server.No("unknown fetch argument: " + s); } else { if (s.equalsIgnoreCase("BODY.PEEK")) spec |= PEEK; - //else if (e) api.addFlags(Query.num(new int[] { num, num }), Mailbox.Flag.SEEN, false, false); + //else if (e) api.addFlags(Query.imapNumber(new int[] { num, num }), Mailbox.Flag.SEEN, false, false); if (i >= t.length - 1 || t[i+1].type != Parser.Token.LIST) { spec |= BODYSTRUCTURE; if (e) { r.append(" "); r.append(Printer.bodystructure(m)); } continue; @@ -563,7 +567,7 @@ public class IMAP { Parser.Token t = token(false); if (t == null) break; if (t.type == t.LIST) throw new Server.No("nested queries not yet supported FIXME"); - else if (t.type == t.SET) return Query.num(t.set(max)); + else if (t.type == t.SET) return Query.imapNumber(t.set(max)); s = t.atom().toUpperCase(); if (s.equals("NOT")) return Query.not(query(max, maxuid)); if (s.equals("OR")) return Query.or(query(max, maxuid), query(max, maxuid)); // FIXME parse rest of list diff --git a/src/org/ibex/mail/protocol/NNTP.java b/src/org/ibex/mail/protocol/NNTP.java index c4155fd..284cf04 100644 --- a/src/org/ibex/mail/protocol/NNTP.java +++ b/src/org/ibex/mail/protocol/NNTP.java @@ -78,13 +78,13 @@ public class NNTP { 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); } + public Article article(int n, boolean h, boolean b) { ptr = n; return article(Query.nntpNumber(n,n),h,b); } 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 Fountain.StringFountain(it.head() + "\r\n")); - return new Article(it.num(), m); + return new Article(it.nntpNumber(), m); } catch (Exception e) { return null; } } public Group[] list() { return list(root, ""); } @@ -208,18 +208,18 @@ public class NNTP { } 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("."); - } else if (command.equals("SLAVE")) { println("220 I don't care"); + } else if (command.equals("SLAVE")) { println("220 SLAVE was removed in RFC3977, you should not use it"); } else if (command.equals("XOVER")) { println("224 Overview information follows"); MailboxWrapper api = (MailboxWrapper)this.api; String range = st.hasMoreTokens() ? st.nextToken() : (api.ptr+"-"+api.ptr); int start = Integer.parseInt(range.substring(0, range.indexOf('-'))); int end = Integer.parseInt(range.substring(range.indexOf('-') + 1)); - Mailbox.Iterator it = api.current.iterator(Query.messagenum(start, end)); + Mailbox.Iterator it = api.current.iterator(Query.nntpNumber(start, end)); while(it.next()) { try { Message m = it.cur(); - println(it.num()+"\t"+m.subject+"\t"+m.from+"\t"+m.date+"\t"+m.messageid+"\t"+ + println(it.nntpNumber()+"\t"+m.subject+"\t"+m.from+"\t"+m.date+"\t"+m.messageid+"\t"+ m.headers.get("references") + "\t" + m.getLength() + "\t" + m.getNumLines()); } catch (Exception e) { Log.error(this, e); } } diff --git a/src/org/ibex/mail/protocol/SMTP.java b/src/org/ibex/mail/protocol/SMTP.java index 37686c6..af62e21 100644 --- a/src/org/ibex/mail/protocol/SMTP.java +++ b/src/org/ibex/mail/protocol/SMTP.java @@ -74,7 +74,7 @@ public class SMTP { // Server ////////////////////////////////////////////////////////////////////////////// public static class Server { - public void handleRequest(Connection conn) { + public void handleRequest(Connection conn) throws IOException { conn.setTimeout(5 * 60 * 1000); conn.setNewline("\r\n"); conn.println("220 " + conn.vhost + " SMTP " + this.getClass().getName()); @@ -84,7 +84,7 @@ public class SMTP { String remotehost = null; for(String command = conn.readln(); ; command = conn.readln()) try { if (command == null) return; - Log.warn("**"+conn.getRemoteAddress()+"**", command); + //Log.warn("**"+conn.getRemoteAddress()+"**", command); String c = command.toUpperCase(); if (c.startsWith("HELO")) { remotehost = c.substring(5).trim(); @@ -191,11 +191,6 @@ public class SMTP { } catch (MailException.Malformed mfe) { conn.println("501 " + mfe.toString()); } catch (MailException.MailboxFull mbf) { conn.println("452 " + mbf); } catch (Later.LaterException le) { conn.println("453 try again later"); - } catch (IOException ioe) { - //conn.println("554 " + ioe.toString()); - Log.error(this, ioe); - conn.close(); - return; } } else { conn.println("500 unrecognized command"); } } catch (Message.Malformed e) { conn.println("501 " + e.toString()); } diff --git a/src/org/ibex/mail/target/FileBasedMailbox.java b/src/org/ibex/mail/target/FileBasedMailbox.java index 4fc5176..0a8c013 100644 --- a/src/org/ibex/mail/target/FileBasedMailbox.java +++ b/src/org/ibex/mail/target/FileBasedMailbox.java @@ -152,7 +152,8 @@ public class FileBasedMailbox extends Mailbox.Default { public boolean next() { cur++; return !done(); } public boolean seen() { return false; } public boolean recent() { return false; } - public int num() { return cur+1; } // EUDORA insists that message numbers start at 1, not 0 + public int nntpNumber() { return cur+1; } // FIXME: lame + public int imapNumber() { return cur+1; } // EUDORA insists that message numbers start at 1, not 0 public int uid() { return done() ? -1 : Integer.parseInt(files[cur].substring(0, files[cur].length()-1)); } public void delete() { File f = file(); if (f != null && f.exists()) f.delete(); } public void seen(boolean seen) { } @@ -218,7 +219,7 @@ public class FileBasedMailbox extends Mailbox.Default { s[0] = (m.from==null?"":m.from.toString(true)); s[1] = m.subject; s[2] = (m.date + "").trim().replaceAll(" "," "); - s[3] = it.num() + ""; + s[3] = it.imapNumber() + ""; msgs.addElement(s); } String[][] messages; @@ -253,7 +254,7 @@ public class FileBasedMailbox extends Mailbox.Default { int target = Integer.parseInt(request.getParameter("msgnum")); Mailbox.Iterator it = mbox.iterator(); while(it.next()) - if (it.num() == target) + if (it.imapNumber() == target) break; if (it.cur() != null) { pw.println(" "); diff --git a/src/org/ibex/mail/target/Mailbox.java b/src/org/ibex/mail/target/Mailbox.java index 13b376f..dbc91bf 100644 --- a/src/org/ibex/mail/target/Mailbox.java +++ b/src/org/ibex/mail/target/Mailbox.java @@ -79,6 +79,7 @@ public abstract class Mailbox extends JS.Obj implements Target { public void recent(boolean on) { } public void set(String key, String val) { throw new MailException("not supported"); } public String get(String key) { throw new MailException("not supported"); } + public int nntpNumber() { throw new MailException("not supported"); } public int flags() { return (deleted() ? Flag.DELETED : 0) | @@ -118,10 +119,28 @@ public abstract class Mailbox extends JS.Obj implements Target { public abstract Message cur(); public abstract Headers head(); public abstract boolean next(); - public abstract int uid(); - public abstract int num(); public abstract void delete(); + /** a unique identifier for this message */ + public abstract int uid(); + + /** + * Message number according to IMAP semantics. + * - no two messages in the same mailbox may have the same imapNumber + * - sorting by uid must yield the same order as sorting them by imapNumber + * - imapNumber may only change if uidValidity changes + * - if uidValidity changes, imapNumbers may change or be reused + */ + public abstract int imapNumber(); + + /** + * Message number according to NNTP semantics. + * - no two messages in the same mailbox may have the same nntpNumber + * - article number may NEVER change or EVER be reused + * - uidValidity is irrelevant + */ + public abstract int nntpNumber(); + public abstract void set(String key, String val); public abstract String get(String key); @@ -152,7 +171,8 @@ public abstract class Mailbox extends JS.Obj implements Target { public boolean next() { return it.next(); } public int uid() { return it.uid(); } public int flags() { return it.flags(); } - public int num() { return it.num(); } + public int nntpNumber() { return it.nntpNumber(); } + public int imapNumber() { return it.imapNumber(); } public void set(String key, String val) { it.set(key, val); } public String get(String key) { return it.get(key); } public void delete() { it.delete(); } @@ -188,7 +208,8 @@ public abstract class Mailbox extends JS.Obj implements Target { public boolean next() { return false; } public int uid() { return 0; } public int flags() { return 0; } - public int num() { return 0; } + public int imapNumber() { return 0; } + public int nntpNumber() { throw new RuntimeException("this mailbox does not keep article numbers"); } public void set(String key, String val) { } public String get(String key) { return null; } public void delete() { } diff --git a/src/org/ibex/mail/target/MailmanArchives.java b/src/org/ibex/mail/target/MailmanArchives.java index 4e043e9..d9c3621 100644 --- a/src/org/ibex/mail/target/MailmanArchives.java +++ b/src/org/ibex/mail/target/MailmanArchives.java @@ -57,8 +57,8 @@ public class MailmanArchives extends Mailbox.Default { int num = 0; public int uid() { return num; } - public int num() { return num; } - + public int nntpNumber() { return num; } + public int imapNumber() { return num; } public Message cur() { return messages[num]; } public Headers head() { return messages[num].headers; } public boolean next() { return (++num) < messages.length; } diff --git a/src/org/ibex/mail/target/MessageArrayMailbox.java b/src/org/ibex/mail/target/MessageArrayMailbox.java index d2335b8..e733504 100644 --- a/src/org/ibex/mail/target/MessageArrayMailbox.java +++ b/src/org/ibex/mail/target/MessageArrayMailbox.java @@ -29,7 +29,7 @@ public class MessageArrayMailbox extends Mailbox.Default { public Headers head() { return messages[position].headers; } public boolean next() { return ++position < messages.length; } public int uid() { return position+1; } - public int num() { return position+1; } + public int imapNumber() { return position+1; } public void delete() { return; } public void set(String key, String val) { return; } diff --git a/src/org/ibex/mail/target/SqliteJdbcMailbox.java b/src/org/ibex/mail/target/SqliteJdbcMailbox.java index a4830a1..ccc9ccc 100644 --- a/src/org/ibex/mail/target/SqliteJdbcMailbox.java +++ b/src/org/ibex/mail/target/SqliteJdbcMailbox.java @@ -68,8 +68,9 @@ public class SqliteJdbcMailbox extends Mailbox.Default { public Headers head() { return cur().headers; } public boolean next() { try { m = null; count++; return rs.next(); } catch (Exception e) { throw new RuntimeException(e); } } public int uid() { throw new RuntimeException("not supported"); } - public int num() { return count; } // FIXME FIXME - public void delete() { throw new RuntimeException("not supported"); } + public int nntpNumber() { throw new RuntimeException("not supported"); } + public int imapNumber() { return count; } + public void delete() { throw new RuntimeException("not supported"); } } private static String streamToString(Stream stream) throws Exception { -- 1.7.10.4