From a45062fa14b1a3fcb9307805aafa7eb877d7859b Mon Sep 17 00:00:00 2001 From: adam Date: Fri, 7 Jan 2005 18:15:44 +0000 Subject: [PATCH] maxuid support and clarified IMAP distinction between by-UID and by-Num darcs-hash:20050107181544-5007d-a3cab9591f5390af8bd844f30f398c28da4fffdc.gz --- src/org/ibex/mail/protocol/IMAP.java | 133 +++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 60 deletions(-) diff --git a/src/org/ibex/mail/protocol/IMAP.java b/src/org/ibex/mail/protocol/IMAP.java index 40ea210..4867c41 100644 --- a/src/org/ibex/mail/protocol/IMAP.java +++ b/src/org/ibex/mail/protocol/IMAP.java @@ -68,6 +68,8 @@ public class IMAP { public int unseen(String mailbox); public int recent(String mailbox); public int count(String mailbox); + public int count(); + public int maxuid(); public int uidNext(String mailbox); public int uidValidity(String mailbox); public int[] search(Query q, boolean uid); @@ -174,9 +176,17 @@ public class IMAP { public void expunge(Mailbox.Iterator it) { client.expunge(it.uid()); it.delete(); } public void subscribe(String mailbox) { } public void unsubscribe(String mailbox) { } + public int maxuid() { + int ret = 0; + Mailbox mb = selected(); + if (mb == null) return 0; + for(Mailbox.Iterator it = mb.iterator(); it.next(); ) ret = it.uid(); + return ret; + } public int unseen(String mailbox) { return mailbox(mailbox, false).count(Query.not(Query.seen())); } public int recent(String mailbox) { return mailbox(mailbox, false).count(Query.recent()); } public int count(String mailbox) { return mailbox(mailbox, false).count(Query.all()); } + public int count() { return selected().count(Query.all()); } public int uidNext(String mailbox) { return mailbox(mailbox, false).uidNext(); } public int uidValidity(String mailbox) { return mailbox(mailbox, false).uidValidity(); } public void select(String mailbox, boolean examineOnly) { @@ -199,12 +209,12 @@ public class IMAP { private void doFlags(Query q, int flags, boolean uid, int style, boolean silent) { for(Mailbox.Iterator it = selected().iterator(q);it.next();) { boolean recent = it.recent(); + try { throw new Exception("flags " + flags); } catch (Exception e) { Log.error(this, e); } if (style == -1) it.removeFlags(flags); else if (style == 0) it.setFlags(flags); else if (style == 1) it.addFlags(flags); it.recent(recent); - // FIXME - //if (!silent) client.fetch(it.num(), it.flags(), -1, null, it.uid()); + if (!silent) client.fetch(it.num(), it.flags(), -1, null, it.uid()); } } public void rename(String from0, String to) { @@ -239,7 +249,7 @@ public class IMAP { Parser.Token token() { return parser.token(); } void println(String s) { conn.println(s); } void newline() { parser.newline(); } - Query query() { return parser.query(); } + Query query(int max) { return parser.query(max, maxn(true)); } public void login(String u, String p) { Object ret; @@ -256,6 +266,8 @@ public class IMAP { } } + private int maxn(boolean uid) { return uid ? api.maxuid() : api.count(); } + public void handleRequest(Connection conn) { this.conn = conn; parser = new Parser(conn); @@ -278,7 +290,7 @@ 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()), 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; @@ -286,7 +298,7 @@ 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()), + case FETCH: selected(); fetch(Query.set(lastuid=uid, token().set(maxn(uid))), lastfetch=token().lx(), 0, 0, 0, uid, 0); break; case SEARCH: selected(); println("* SEARCH " + Printer.join(api.search(query(), uid))); break; case EXAMINE: @@ -328,7 +340,7 @@ public class IMAP { break; } case STORE: { selected(); - Query q = uid ? Query.uid(token().set()) : Query.num(token().set()); + Query q = uid ? Query.uid(token().set(maxn(uid))) : Query.num(token().set(maxn(uid))); String s = token().atom().toUpperCase(); int flags = token().flags(); if (s.equals("FLAGS")) api.setFlags(q, flags, uid, false); @@ -368,8 +380,8 @@ public class IMAP { * - emit a fetch reply for the parsed spec with respect to message m */ private void fetch(Object o, Parser.Token[] t, int num, int flags, int size, boolean uid, int muid) { - Query q = o instanceof Query ? (Query)o : null; - Message m = o instanceof Message ? (Message)o : null; + Query q = o == null ? null : o instanceof Query ? (Query)o : null; + Message m = o == null ? null : o instanceof Message ? (Message)o : null; boolean e = q == null; lastfetch = t; @@ -377,7 +389,7 @@ public class IMAP { String[] headers = null; int start = -1, end = -1; StringBuffer r = new StringBuffer(); - if (e) { r.append(uid ? muid : num); r.append(" FETCH ("); } + if (e) { r.append(num); r.append(" FETCH ("); } int initlen = r.length(); if (uid) { boolean good = false; @@ -513,49 +525,53 @@ public class IMAP { private Stream stream; public Parser(Stream from) { this.stream = from; } public Token token(String s) { return new Token(s); } - protected Query query() { + protected Query query(int max, int maxuid) { String s = null; boolean not = false; Query q = null; + Query ret = null; while(true) { 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()); + else if (t.type == t.SET) return Query.num(t.set(max)); s = t.atom().toUpperCase(); - if (s.equals("NOT")) { not = true; continue; } - if (s.equals("OR")) return Query.or(query(), query()); // FIXME parse rest of list - if (s.equals("AND")) return Query.and(query(), query()); + 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 + if (s.equals("AND")) return Query.and(query(max, maxuid), query(max, maxuid)); + + if (s.startsWith("UN")) { not = true; s = s.substring(2); } + if (s.equals("ANSWERED")) q = Query.answered(); + else if (s.equals("DELETED")) q = Query.deleted(); + else if (s.equals("ALL")) q = Query.all(); + else if (s.equals("DRAFT")) q = Query.draft(); + else if (s.equals("FLAGGED")) q = Query.flagged(); + else if (s.equals("RECENT")) q = Query.recent(); + else if (s.equals("SEEN")) q = Query.seen(); + else if (s.equals("OLD")) { not = true; q = Query.recent(); } + else if (s.equals("NEW")) q = Query.and(Query.recent(), Query.not(Query.seen())); + else if (s.equals("KEYWORD")) q = Query.header("keyword", token().flag()); + else if (s.equals("HEADER")) q = Query.header(token().astring(), token().astring()); + else if (s.equals("BCC")) q = Query.header("bcc", token().astring()); + else if (s.equals("CC")) q = Query.header("cc", token().astring()); + else if (s.equals("FROM")) q = Query.header("from", token().astring()); + else if (s.equals("TO")) q = Query.header("to", token().astring()); + else if (s.equals("SUBJECT")) q = Query.header("subject", token().astring()); + else if (s.equals("LARGER")) q = Query.size(token().n(), Integer.MAX_VALUE); + else if (s.equals("SMALLER")) q = Query.size(Integer.MIN_VALUE, token().n()); + else if (s.equals("BODY")) q = Query.body(token().astring()); + else if (s.equals("TEXT")) q = Query.full(token().astring()); + else if (s.equals("BEFORE")) q = Query.arrival(new Date(0), token().date()); + else if (s.equals("SINCE")) q = Query.arrival(token().date(), new Date(Long.MAX_VALUE)); + else if (s.equals("ON")) { Date d = token().date(); q = Query.arrival(d, new Date(d.getTime() + 24 * 60 * 60)); } + else if (s.equals("SENTBEFORE")) q = Query.sent(new Date(0), token().date()); + else if (s.equals("SENTSINCE")) q = Query.sent(token().date(), new Date(Long.MAX_VALUE)); + else if (s.equals("SENTON")) { Date d = token().date(); q = Query.sent(d, new Date(d.getTime() + 24 * 60 * 60)); } + else if (s.equals("UID")) q = Query.uid(token().set(max)); + q = not ? Query.not(q) : q; + ret = ret == null ? q : Query.and(ret, q); } - if (s.startsWith("UN")) { not = true; s = s.substring(2); } - if (s.equals("ANSWERED")) q = Query.answered(); - else if (s.equals("DELETED")) q = Query.deleted(); - else if (s.equals("ALL")) q = Query.all(); - else if (s.equals("DRAFT")) q = Query.draft(); - else if (s.equals("FLAGGED")) q = Query.flagged(); - else if (s.equals("RECENT")) q = Query.recent(); - else if (s.equals("SEEN")) q = Query.seen(); - else if (s.equals("OLD")) { not = true; q = Query.recent(); } - else if (s.equals("NEW")) q = Query.and(Query.recent(), Query.not(Query.seen())); - else if (s.equals("KEYWORD")) q = Query.header("keyword", token().flag()); - else if (s.equals("HEADER")) q = Query.header(token().astring(), token().astring()); - else if (s.equals("BCC")) q = Query.header("bcc", token().astring()); - else if (s.equals("CC")) q = Query.header("cc", token().astring()); - else if (s.equals("FROM")) q = Query.header("from", token().astring()); - else if (s.equals("TO")) q = Query.header("to", token().astring()); - else if (s.equals("SUBJECT")) q = Query.header("subject", token().astring()); - else if (s.equals("LARGER")) q = Query.size(token().n(), Integer.MAX_VALUE); - else if (s.equals("SMALLER")) q = Query.size(Integer.MIN_VALUE, token().n()); - else if (s.equals("BODY")) q = Query.body(token().astring()); - else if (s.equals("TEXT")) q = Query.full(token().astring()); - else if (s.equals("BEFORE")) q = Query.arrival(new Date(0), token().date()); - else if (s.equals("SINCE")) q = Query.arrival(token().date(), new Date(Long.MAX_VALUE)); - else if (s.equals("ON")) { Date d = token().date(); q = Query.arrival(d, new Date(d.getTime() + 24 * 60 * 60)); } - else if (s.equals("SENTBEFORE")) q = Query.sent(new Date(0), token().date()); - else if (s.equals("SENTSINCE")) q = Query.sent(token().date(), new Date(Long.MAX_VALUE)); - else if (s.equals("SENTON")) { Date d = token().date(); q = Query.sent(d, new Date(d.getTime() + 24 * 60 * 60)); } - else if (s.equals("UID")) q = Query.uid(token().set()); - return not ? Query.not(q) : q; + return ret; } private static void bad(String s) { throw new Server.Bad(s); } @@ -615,30 +631,27 @@ public class IMAP { } return ret; } - public int[] set() { + public int[] set(int largest) { if (type != ATOM) bad("expected a messageid set"); Vec.Int ids = new Vec.Int(); StringTokenizer st = new StringTokenizer(s, ","); while(st.hasMoreTokens()) { String s = st.nextToken(); if (s.indexOf(':') == -1) { - if (s.equals("*")) { - ids.addElement(0); - ids.addElement(Integer.MAX_VALUE); - } else { - ids.addElement(Integer.parseInt(s)); - ids.addElement(Integer.parseInt(s)); - } - continue; } + if (s.equals("*")) { + ids.addElement(largest); + ids.addElement(largest); + } else { + ids.addElement(Integer.parseInt(s)); + ids.addElement(Integer.parseInt(s)); + } + continue; } int start = Integer.parseInt(s.substring(0, s.indexOf(':'))); String end_s = s.substring(s.indexOf(':')+1); - if (end_s.equals("*")) { ids.addElement(start); ids.addElement(Integer.MAX_VALUE); } - else { - int end = Integer.parseInt(end_s); - for(int j=Math.min(start,end); j<=Math.max(start,end); j++) { - ids.addElement(j); - ids.addElement(j); - } + int end = end_s.equals("*") ? largest : Integer.parseInt(end_s); + for(int j=Math.min(start,end); j<=Math.max(start,end); j++) { + ids.addElement(j); + ids.addElement(j); } } return ids.dump(); @@ -783,7 +796,7 @@ public class IMAP { public static String qq(String s) { StringBuffer ret = new StringBuffer(); ret.append('{'); - ret.append(s.length()); + ret.append(s.getBytes().length); ret.append('}'); ret.append('\r'); ret.append('\n'); -- 1.7.10.4