From: adam Date: Mon, 14 Jun 2004 00:43:02 +0000 (+0000) Subject: compiling again X-Git-Url: http://git.megacz.com/?a=commitdiff_plain;h=b4686aff471826ff6b706b5f4c7895b7af3b1350;p=org.ibex.mail.git compiling again darcs-hash:20040614004302-5007d-d9ad0742a2ed14c2ee44a653c39e131f0422ce57.gz --- diff --git a/src/org/ibex/mail/Message.java b/src/org/ibex/mail/Message.java index 46f2e48..9139564 100644 --- a/src/org/ibex/mail/Message.java +++ b/src/org/ibex/mail/Message.java @@ -191,6 +191,7 @@ public class Message extends JSReflection { return ret.toString(); } + public String rfc822() { return allHeaders + "\r\n" + body; } public int rfc822size() { return allHeaders.length() + 2 /* CRLF */ + body.length(); } // FIXME: double check this public String summary() { diff --git a/src/org/ibex/mail/protocol/IMAP.java b/src/org/ibex/mail/protocol/IMAP.java index 21e937a..24a56c4 100644 --- a/src/org/ibex/mail/protocol/IMAP.java +++ b/src/org/ibex/mail/protocol/IMAP.java @@ -13,89 +13,118 @@ import java.io.*; // RFC 3691: UNSELECT // RFC 2971: ID +// FEATURE: MIME-queries // FEATURE: READ-WRITE / READ-ONLY status on SELECT // FEATURE: pipelining // FEATURE: support [charset] // FEATURE: \Noselect // FEATURE: subscriptions -public class IMAP extends MessageProtocol { - // Constants ////////////////////////////////////////////////////////////////////////////// +public class IMAP { - public static final char imapSeparator = '.'; public static final float version = (float)0.1; + // API Class ////////////////////////////////////////////////////////////////////////////// + + public static final int + PEEK=0x1, BODYSTRUCTURE=0x2, ENVELOPE=0x4, FLAGS=0x8, INTERNALDATE=0x10, + RFC822=0x20, RFC822TEXT=0x40, RFC822SIZE=0x80, NEGATEHEADERS=0x100, UID=0x200, RFC822HEADER=0x400; + + public static interface API { + public String[] capability(); + public Hashtable id(Hashtable clientId); + public void copy(Query q, String to); + public void login(String u, String p); + public void logout(); + public void unselect(); + public void delete(String m); + public void create(String m); + public void append(String m, int flags, Date arrival, String body); + public void check(); + public void noop(); + public void close(); + public void subscribe(String mailbox); + public void unsubscribe(String mailbox); + public int seen(String mailbox); + public int recent(String mailbox); + public int count(String mailbox); + public int uidNext(String mailbox); + public int uidValidity(String mailbox); + public void rename(String from, String to); + public void select(String mailbox, boolean examineOnly); + + public static interface Client { + public void expunge(int uid); + public void list(char separator, String mailbox); + public void lsub(char separator, String mailbox); + public void fetch(int uidnum, int flags, int size, Message m); // m may be null or incomplete + } - // Callbacks ////////////////////////////////////////////////////////////////////////////// - - public static interface Authenticator { public abstract Mailbox authenticate(String user, String pass); } + public void setFlags(Query q, int flags, boolean uid, Client c); + public void removeFlags(Query q, int flags, boolean uid, Client c); + public void addFlags(Query q, int flags, boolean uid, Client c); + public void expunge(Client client); + public void fetch(Query q, int spec, String[] headers, int start, int end, boolean uid, Client c); + public void lsub(String start, String ref, Client client); + public void list(String start, String ref, Client client); + + public static interface Authenticator { public abstract Mailbox authenticate(String user, String pass); } + public static class Exn extends MailException { public Exn(String s) { super(s); } } + public static class Bad extends Exn { public Bad(String s) { super(s); } } + public static class No extends Exn { public No(String s) { super(s); } } + } - // Exceptions ////////////////////////////////////////////////////////////////////////////// + // SocketWrapper ////////////////////////////////////////////////////////////////////////////// - public static class Exn extends MailException { public Exn(String s) { super(s); } } - public static class Bad extends Exn { public Bad(String s) { super(s); } } - public static class No extends Exn { public No(String s) { super(s); } } + public static class SocketWrapper /* implements API */ { + // eventually this will implement the client side of an IMAP socket conversation + } + // MailboxWrapper ////////////////////////////////////////////////////////////////////////////// - // Single Session Handler ////////////////////////////////////////////////////////////////////////////// + /** wraps an IMAP.API interface around a Mailbox */ + public static class MailboxWrapper implements API { - private static class Session extends Incoming { - Mailbox selected = null; - Mailbox selected() { if (selected == null) throw new Bad("no mailbox selected"); return selected; } - String selectedName = null; Mailbox inbox = null; - final Mailbox root; - final Socket conn; - final String vhost; - final Authenticator auth; - final PrintWriter pw; - final PushbackReader r; - final InputStream is; - public void init() { } - public Session(Socket conn, Mailbox root, Authenticator auth) throws IOException { - this(conn, java.net.InetAddress.getLocalHost().getHostName(), root, auth); } - public Session(Socket conn, String vhost, Mailbox root, Authenticator auth) throws IOException { - this.vhost = vhost; this.conn = conn; this.root = root; this.auth = auth; - this.pw = new PrintWriter(new OutputStreamWriter(conn.getOutputStream())); - this.r = new PushbackReader(new InputStreamReader(this.is = conn.getInputStream())); - } + Mailbox selected = null; + Mailbox selected() { if (selected == null) throw new API.Bad("no mailbox selected"); return selected; } + final API.Authenticator auth; - private void star(String s) { println("* " + s); } - private void println(String s) { pw.println(s); pw.flush(); } - private void print(String s) { pw.print(s); } + public static final char sep = '.'; + + private final Mailbox root; + public MailboxWrapper(Mailbox root, API.Authenticator auth) { this.root = root; this.auth = auth; } private Mailbox getMailbox(String name, boolean create) { Mailbox m = root; - for(StringTokenizer st = new StringTokenizer(name, imapSeparator + ""); st.hasMoreTokens();) - if ((m = m.slash(st.nextToken(), create)) == null) throw new No("no such mailbox " + name); + for(StringTokenizer st = new StringTokenizer(name, sep + ""); st.hasMoreTokens();) + if ((m = m.slash(st.nextToken(), create)) == null) throw new API.No("no such mailbox " + name); return m; } // FEATURE: not accurate when a wildcard and subsequent non-wildcards both match a single component - public HashSet lsub(String start, String ref, HashSet reply) { return list(start, ref, reply); } - public HashSet list(String start, String ref, HashSet reply) { - if (reply == null) reply = new HashSet(); - if (ref.length() == 0 && start.length() == 0) { reply.add(""); return reply; } - while (start.endsWith(""+imapSeparator)) start = start.substring(0, start.length() - 1); + public void lsub(String start, String ref, Client client) { list(start, ref, client); } + public void list(String start, String ref, Client client) { + if (ref.length() == 0 && start.length() == 0) { client.list(sep, ""); return; } + while (start.endsWith(""+sep)) start = start.substring(0, start.length() - 1); String[] children = (start.length() == 0 ? root : getMailbox(start, false)).children(); for(int i=0; i 0 ? imapSeparator+"" : "") + s; + String s = children[i], pre = ref, kid = start + (start.length() > 0 ? sep+"" : "") + s; while(true) { if (pre.length() == 0) { - if (s.length() == 0) reply.add(kid); + if (s.length() == 0) client.list(sep, kid); } else switch(pre.charAt(0)) { - case imapSeparator: if (s.length() == 0) list(kid, pre.substring(1), reply); break; - case '%': reply.add(kid); pre=pre.substring(1); list(kid, pre, reply); break; - case '*': reply.add(kid); list(kid, pre, reply); pre = pre.substring(1); break; - default: if (s.length()==0) break; - if (s.charAt(0) != pre.charAt(0)) break; - s = s.substring(1); pre = pre.substring(1); continue; + case sep: if (s.length() == 0) list(kid, pre.substring(1), client); break; + case '%': client.list(sep, kid); pre=pre.substring(1); list(kid, pre, client); break; + case '*': client.list(sep, kid); list(kid, pre, client); pre = pre.substring(1); break; + default: if (s.length()==0) break; + if (s.charAt(0) != pre.charAt(0)) break; + s = s.substring(1); pre = pre.substring(1); continue; } break; } } - return reply; } public String[] capability() { return new String[] { "IMAP4rev1", "UNSELECT", "ID" }; } @@ -110,18 +139,20 @@ public class IMAP extends MessageProtocol { return response; } - public void copy(Query q, Mailbox to) { for(Mailbox.Iterator it=selected.iterator(q);it.next();) to.add(it.cur()); } - public void login(String u, String p) { if ((inbox = auth.authenticate(u,p)) == null) throw new No("Login failed."); } + public void copy(Query q, String to0) { + Mailbox to = getMailbox(to0, false); for(Mailbox.Iterator it=selected.iterator(q);it.next();) to.add(it.cur()); } + public void login(String u, String p) { if ((inbox = auth.authenticate(u,p)) == null) throw new API.No("Login failed."); } public void logout() { } - public void unselect() { selected = null; selectedName = null; } - public void delete(Mailbox m) { if (!m.equals(inbox)) m.destroy(); } - public void create(String m) { if (!m.endsWith(""+imapSeparator) && !m.equalsIgnoreCase("inbox")) getMailbox(m, true); } - public void append(Mailbox m, int flags, Date arrival, String body) { m.add(new Message(null,null,body,arrival), flags); } + public void unselect() { selected = null; } + public void delete(String m0) { Mailbox m = getMailbox(m0, false); if (m != inbox) m.destroy(); } + public void create(String m) { if (!m.endsWith(""+sep)) getMailbox(m, true); } + public void append(String m, int flags, Date arrival, String body) { + getMailbox(m, false).add(new Message(null,null,body,arrival), flags); } public void check() { } public void noop() { } public void close() { for(Mailbox.Iterator it=selected().iterator(Query.deleted()); it.next();) it.delete(); unselect(); } - public void expunge() { for(Mailbox.Iterator it = selected().iterator(Query.deleted()); it.next();) expunge(it); } - public void expunge(Mailbox.Iterator it) { star(it.uid() + " EXPUNGE"); it.delete(); } + public void expunge(Client c) { for(Mailbox.Iterator it = selected().iterator(Query.deleted());it.next();) expunge(it,c); } + public void expunge(Mailbox.Iterator it, Client client) { client.expunge(it.uid()); it.delete(); } public void subscribe(String mailbox) { } public void unsubscribe(String mailbox) { } public int seen(String mailbox) { return getMailbox(mailbox, false).count(Query.seen()); } @@ -129,158 +160,265 @@ public class IMAP extends MessageProtocol { public int count(String mailbox) { return getMailbox(mailbox, false).count(Query.all()); } public int uidNext(String mailbox) { return getMailbox(mailbox, false).uidNext(); } public int uidValidity(String mailbox) { return getMailbox(mailbox, false).uidValidity(); } - public void rename(Mailbox from, String to) { - if (from.equals(inbox)) from.copy(Query.all(), getMailbox(to, true)); + public void select(String mailbox, boolean examineOnly) { selected = getMailbox(mailbox, false); } + public void setFlags(Query q, int f, boolean uid, Client c) { doFlags(q, f, c, uid, 0); } + public void addFlags(Query q, int f, boolean uid, Client c) { doFlags(q, f, c, uid, 1); } + public void removeFlags(Query q, int f, boolean uid, Client c) { doFlags(q, f, c, uid, -1); } + private void doFlags(Query q, int flags, Client c, boolean uid, int style) { + for(Mailbox.Iterator it = selected.iterator(q);it.next();) { + switch(style) { + case -1: it.removeFlags(flags); + case 0: it.setFlags(flags); + case 1: it.addFlags(flags); + } + if (c != null) c.fetch(uid ? it.uid() : it.num(), it.flags(), -1, null); + } + } + public void rename(String from0, String to) { + Mailbox from = getMailbox(from0, false); + if (from.equals(inbox)) { from.copy(Query.all(), getMailbox(to, true)); } else if (to.equalsIgnoreCase("inbox")) { from.copy(Query.all(), getMailbox(to, true)); from.destroy(); } else from.rename(to); } - - public void select(String mailbox, boolean examineOnly) { - selected = getMailbox(selectedName = mailbox, false); - star("FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)"); - star(selected.count(Query.all()) + " EXISTS"); - star(selected.count(Query.recent()) + " RECENT"); - star("OK [UIDVALIDITY " + selected.uidValidity() + "] UIDs valid"); + public void fetch(Query q, int spec, String[] headers, int start, int end, boolean uid, Client client) { + for(Mailbox.Iterator it = selected.iterator(q); it.next(); ) + client.fetch(uid ? it.uid() : it.num(), it.flags(), it.cur().rfc822size(), it.cur()); } + } - // uber FIXME - public void fetch(Query q, FetchRequest[] fr, boolean uid) { - for(Mailbox.Iterator it = selected.iterator(q); it.next(); ) { - Message m = it.cur(); - StringBuffer reply = new StringBuffer(); - boolean peek = false; - for(int i=0; i" + qq(payload.substring(0, frb.end + 1))); - else reply.append("<" + frb.start + "." + frb.end + ">" + qq(payload.substring(frb.start, frb.end + 1))); - } else throw new Error("this should never happen"); - star((uid ? it.uid() : it.num()) + " FETCH (" + reply.toString() + (uid ? " UID " + it.uid() : "") + ")"); - if (!peek) it.seen(true); - } - } - public void store(Query q, int addFlags, int removeFlags, boolean silent, boolean uid) { - for(Mailbox.Iterator it = selected.iterator(q); it.next(); ) { - it.addFlags(addFlags); - it.removeFlags(removeFlags); - if (!silent) star((uid ? it.uid() : it.num()) + " FETCH (FLAGS (" + flags(it) + "))"); - } + // Single Session Handler ////////////////////////////////////////////////////////////////////////////// + + /** takes an IMAP.API and exposes it to the world as an IMAP server on a TCP socket */ + private static class Server extends Parser implements API.Client { + String selectedName = null; + Mailbox inbox = null; + final API api; + final Mailbox root; + final Socket conn; + final String vhost; + public void init() { } + public Server(Socket conn, Mailbox root, API.Authenticator auth) throws IOException { + this(conn, java.net.InetAddress.getLocalHost().getHostName(), root, auth); } + public Server(Socket conn, String vhost, Mailbox root, API.Authenticator auth) throws IOException { + super(conn); + this.api = new IMAP.MailboxWrapper(root, auth); + this.vhost = vhost; this.conn = conn; this.root = root; } - private void emitListResponse(HashSet boxes) - { for(Iterator it = boxes.iterator(); it.hasNext(); ) star("LIST \"" + imapSeparator + "\" \"" + it.next() + "\""); } + // client callbacks + private Token[] lastfetch = null; // hack + private boolean lastuid = false; // hack + public void fetch(int uidnum, int flags, int size, Message m) { + fetch(null, lastfetch, uidnum, flags, size, lastuid, m); } + public void expunge(int uid) { star(uid + " EXPUNGE"); } + public void list(char separator, String mailbox) { star("LIST \"" + separator + "\" \""+mailbox+"\""); } + public void lsub(char separator, String mailbox) { star("LSUB \"" + separator + "\" \""+mailbox+"\""); } + public void star(String s) { println("* " + s); } public boolean handleRequest() throws IOException { - println("* OK " + vhost + " " + IMAP.class.getName() + " IMAP4rev1 [RFC3501] v" + version + " server ready"); + star("OK " + vhost + " " + IMAP.class.getName() + " IMAP4rev1 [RFC3501] v" + version + " server ready"); for(String tag = null;; newline()) try { - pw.flush(); + flush(); boolean uid = false; tag = null; tag = token().atom(); String command = token().atom(); if (command.equals("UID")) { uid = true; command = token().atom(); } switch(((Integer)commands.get(command.toUpperCase())).intValue()) { - case AUTHENTICATE: login(token().astring(), token().astring()); break; - case LIST: emitListResponse(list(token().q(), token().q(), null)); break; - case LSUB: emitListResponse(lsub(token().q(), token().q(), null)); break; - case SUBSCRIBE: subscribe(token().atom()); break; - case UNSUBSCRIBE: unsubscribe(token().atom()); break; - case CAPABILITY: star("CAPABILITY " + join(" ", capability())); break; + case AUTHENTICATE: api.login(token().astring(), token().astring()); break; + case LOGIN: api.login(token().astring(), token().astring()); break; + case LOGOUT: api.logout(); star("BYE"); conn.close(); return false; + case LIST: api.list(token().q(), token().q(), this); break; + case LSUB: api.lsub(token().q(), token().q(), this); break; + case SUBSCRIBE: api.subscribe(token().atom()); break; + case UNSUBSCRIBE: api.unsubscribe(token().atom()); break; + case CAPABILITY: star("CAPABILITY " + Printer.join(" ", api.capability())); break; //case ID: id(); break; - case LOGIN: login(token().astring(), token().astring()); break; - case LOGOUT: logout(); star("BYE"); conn.close(); return false; - case RENAME: rename(token().mailbox(), token().atom()); break; - case EXAMINE: select(token().astring(), true); break; - case SELECT: select(token().astring(), false); break; - case COPY: copy(Query.set(uid, token().set()), token().mailbox()); break; - case DELETE: delete(token().mailbox()); break; - case CHECK: check(); break; - case NOOP: noop(); break; - case CLOSE: close(); break; - case EXPUNGE: expunge(); break; - case UNSELECT: unselect(); break; - case CREATE: create(token().astring()); break; - case FETCH: fetch(Query.set(uid, token().set()), parseFetch(token().l()), uid); break; + case RENAME: api.rename(token().atom(), token().atom()); break; + case EXAMINE: api.select(token().astring(), true); break; + case COPY: api.copy(Query.set(uid, token().set()), token().atom()); break; + case DELETE: api.delete(token().atom()); break; + case CHECK: api.check(); break; + case NOOP: api.noop(); break; + case CLOSE: api.close(); break; + case EXPUNGE: api.expunge(this); break; + case UNSELECT: api.unselect(); break; + case CREATE: api.create(token().astring()); break; + case FETCH: fetch(Query.set(lastuid = uid, token().set()), token().l(), 0, 0, 0, uid, null); break; + case SELECT: { + String mailbox = token().astring(); + api.select(mailbox, false); + star("FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)"); + star(api.count(mailbox) + " EXISTS"); + star(api.recent(mailbox) + " RECENT"); + star("OK [UIDVALIDITY " + api.uidValidity(mailbox) + "] UIDs valid"); + break; } case STATUS: { String mailbox = token().atom(); Token[] list = token().l(); String response = ""; for(int i=0; ior emit a fetch reply. + * + * To avoid duplicating tedious parsing logic, this function + * performs both of the following tasks: + * - parse the fetch request in Token[] t and return a fetch spec + * - emit a fetch reply for the parsed spec with respect to message m + */ + private void fetch(Query q, Token[] t, int uidnum, int flags, int size, boolean uid, Message m) { + lastfetch = t; + boolean e = m != null; + int spec = 0; // spec; see constants for flags + String[] headers = null; + int start = -1, end = -1; + StringBuffer r = new StringBuffer(); // reply + if(e){ r.append(uidnum); r.append(" FETCH ("); } + for(int i=0; i0) r.append(" "); + String s = t[i].s.toUpperCase(); + r.append(s); + if (s.equals("BODYSTRUCTURE")) { spec|=BODYSTRUCTURE;if(e){r.append(" ");r.append(Printer.bodystructure(m));} + } else if (s.equals("ENVELOPE")) { spec|=ENVELOPE; if(e){r.append(" "); r.append(Printer.envelope(m));} + } else if (s.equals("FLAGS")) { spec|=FLAGS; if(e){r.append(" "); r.append(Printer.flags(flags));} + } else if (s.equals("INTERNALDATE")) { spec|=INTERNALDATE; if(e){r.append(" "); r.append(Printer.date(m.arrival));} + } else if (s.equals("RFC822")) { spec|=RFC822; if(e){r.append(" "); r.append(Printer.message(m));} + } else if (s.equals("RFC822.TEXT")) { spec|=RFC822TEXT; if(e){r.append(" ");r.append(Printer.qq(m.body));} + } else if (s.equals("RFC822.HEADER")) { spec|=RFC822HEADER; if(e){r.append(" ");r.append(Printer.qq(m.allHeaders));} + } else if (s.equals("RFC822.SIZE")) { spec|=RFC822SIZE; if(e){r.append(" "); r.append(m.rfc822size());} + } else if (s.equals("BODY.PEEK") || s.equals("BODY")) { + if (s.equalsIgnoreCase("BODY.PEEK")) spec |= PEEK; + String payload = ""; + if (i')); + int dot = s3.indexOf('.'); + start = dot == -1 ? Integer.parseInt(s3) : Integer.parseInt(s3.substring(0, s3.indexOf('.'))); + end = dot == -1 ? -1 : Integer.parseInt(s3.substring(s3.indexOf('.') + 1)); + if (e) { payload = payload.substring(start, end+1); r.append("<"+start+">"); } + } + } else { + if (e) payload = m.body; + } + if (e) { r.append(" "); r.append(Printer.qq(payload)); } + } else { + throw new API.No("unknown fetch argument: " + s); + } + } + if (e) { + r.append(")"); + star(r.toString()); + } else { + api.fetch(q, spec, headers, start, end, uid, this); + } + } + + private static final Hashtable commands = new Hashtable(); + private static final int UID = 0; static { commands.put("UID", new Integer(UID)); } + private static final int AUTHENTICATE = 1; static { commands.put("AUTHENTICATE", new Integer(AUTHENTICATE)); } + private static final int LIST = 2; static { commands.put("LIST", new Integer(LIST)); } + private static final int LSUB = 3; static { commands.put("LSUB", new Integer(LSUB)); } + private static final int SUBSCRIBE = 4; static { commands.put("SUBSCRIBE", new Integer(SUBSCRIBE)); } + private static final int UNSUBSCRIBE = 5; static { commands.put("UNSUBSCRIBE", new Integer(UNSUBSCRIBE)); } + private static final int CAPABILITY = 6; static { commands.put("CAPABILITY", new Integer(CAPABILITY)); } + private static final int ID = 7; static { commands.put("ID", new Integer(ID)); } + private static final int LOGIN = 8; static { commands.put("LOGIN", new Integer(LOGIN)); } + private static final int LOGOUT = 9; static { commands.put("LOGOUT", new Integer(LOGOUT)); } + private static final int RENAME = 10; static { commands.put("RENAME", new Integer(RENAME)); } + private static final int EXAMINE = 11; static { commands.put("EXAMINE", new Integer(EXAMINE)); } + private static final int SELECT = 12; static { commands.put("SELECT", new Integer(SELECT)); } + private static final int COPY = 13; static { commands.put("COPY", new Integer(COPY)); } + private static final int DELETE = 14; static { commands.put("DELETE", new Integer(DELETE)); } + private static final int CHECK = 15; static { commands.put("CHECK", new Integer(CHECK)); } + private static final int NOOP = 16; static { commands.put("NOOP", new Integer(NOOP)); } + private static final int CLOSE = 17; static { commands.put("CLOSE", new Integer(CLOSE)); } + private static final int EXPUNGE = 18; static { commands.put("EXPUNGE", new Integer(EXPUNGE)); } + private static final int UNSELECT = 19; static { commands.put("UNSELECT", new Integer(UNSELECT)); } + private static final int CREATE = 20; static { commands.put("CREATE", new Integer(CREATE)); } + private static final int STATUS = 21; static { commands.put("STATUS", new Integer(STATUS)); } + private static final int FETCH = 22; static { commands.put("FETCH", new Integer(FETCH)); } + private static final int APPEND = 23; static { commands.put("APPEND", new Integer(APPEND)); } + private static final int STORE = 24; static { commands.put("STORE", new Integer(STORE)); } + } + + public static class Parser { + private final Socket conn; + private final InputStream is; + private final PushbackReader r; + private final PrintWriter pw; + public Parser(Socket conn) throws IOException { + this.conn = conn; + this.is = conn.getInputStream(); + this.pw = new PrintWriter(new OutputStreamWriter(conn.getOutputStream())); + this.r = new PushbackReader(new InputStreamReader(this.is)); + } + protected void println(String s) { pw.println(s); pw.flush(); } + protected void flush() { pw.flush(); } Query query() { String s = null; boolean not = false; Query q = null; while(true) { Token t = token(); - if (t.type == t.LIST) throw new No("nested queries not yet supported"); + if (t.type == t.LIST) throw new API.No("nested queries not yet supported"); else if (t.type == t.SET) return Query.num(t.set()); s = t.atom(); if (s.equals("NOT")) { not = true; continue; } @@ -335,14 +473,23 @@ public class IMAP extends MessageProtocol { public Token(Token[] list) { l = list; s = null; type = LIST; n = 0; } public Token(int number) { n = number; l = null; s = null; type = NUMBER; } - public String flag() { if (type != ATOM) throw new Bad("expected a flag"); return s; } - public int n() { if (type != NUMBER) throw new Bad("expected number"); return n; } - public int nz() { int n = n(); if (n == 0) throw new Bad("expected nonzero number"); return n; } - public String q() { if (type == NIL) return null; if (type != QUOTED) throw new Bad("expected qstring"); return s; } - public Token[] l() { if (type == NIL) return null; if (type != LIST) throw new Bad("expected list"); return l; } + public String flag() { if (type != ATOM) throw new API.Bad("expected a flag"); return s; } + public int n() { if (type != NUMBER) throw new API.Bad("expected number"); return n; } + public int nz() { int n = n(); if (n == 0) throw new API.Bad("expected nonzero number"); return n; } + public String q() { if (type == NIL) return null; if (type != QUOTED) throw new API.Bad("expected qstring"); return s; } + public Token[] l() { if (type == NIL) return null; if (type != LIST) throw new API.Bad("expected list"); return l; } + public String[] sl() { + if (type == NIL) return null; + if (type != LIST) throw new API.Bad("expected list"); + String[] ret = new String[l.length]; + for(int i=0; i")) { - startend = s.substring(s.indexOf('<'), s.indexOf('>')); - s = s.substring(0, s.indexOf('<')); - } - - if (s.equalsIgnoreCase("BODY.PEEK")) b.peek = true; - else if (s.equalsIgnoreCase("BODY")) b.peek = false; - else throw new No("unknown fetch argument: " + s); - - if (i= '0' && s2.charAt(0) <= '9') { - mimeVec.addElement(new Integer(Integer.parseInt(s2.substring(0, s2.indexOf('.'))))); - s2 = s2.substring(s2.indexOf('.') + 1); - } - if (mimeVec.size() > 0) { - b.path = new int[mimeVec.size()]; - for(int j=0; j