mad changes
authoradam <adam@megacz.com>
Mon, 17 May 2004 05:38:55 +0000 (05:38 +0000)
committeradam <adam@megacz.com>
Mon, 17 May 2004 05:38:55 +0000 (05:38 +0000)
darcs-hash:20040517053855-5007d-50af8dc340a4d9b6a0eeca66a4c0899ca260b885.gz

src/org/ibex/mail/protocol/IMAP.java

index 504e6a8..6fe15a2 100644 (file)
@@ -41,13 +41,7 @@ public class IMAP extends MessageProtocol {
         public void check(String[] args, boolean examineOnly) { ok("check complete"); }
         public void lsub(String[] args) { list(args); }
         public void list(String[] args) { star("LIST () \".\" INBOX"); ok("LIST completed"); }
-        public void create(String mailbox) {
-            if (mailbox.charAt(mailbox.length() - 1) != '.' &&
-                !mailbox.toLowerCase.equals("inbox"))
-                if (!storage.create(mailbox))
-                    throw No("unable to CREATE mailbox");
-            ok("CREATE completed");
-        }
+        public void create(String mailbox) {if(!mailbox.endsWith(".")&&!mailbox.equalsIgnoreCase("inbox"))storage.create(mailbox);}
         public void rename(Mailbox from, String to) {
             if (from.getName().toLowerCase().equals("inbox")) {
                 int[] messages = from.list();
@@ -65,7 +59,7 @@ public class IMAP extends MessageProtocol {
                     inbox.add(m);
                     from.delete(messages[i]);
                 }
-                // FIXME delete from
+                from.delete();
             } else {
                 from.rename(to);
             }
@@ -124,35 +118,6 @@ public class IMAP extends MessageProtocol {
             if (!silent) ok("EXPUNGE completed");
         }
 
-        public void fetch(int[] messageSet, boolean examineOnly, boolean uid) {
-            if (selected == null) throw new Bad("no mailbox selected");
-            Object[] tokens = tokenize(args[0]);
-            String[] headers;
-            boolean negateHeaders;
-            boolean flags;
-            boolean internalDate;
-            boolean size;
-            boolean uid;
-            int start, end;
-            boolean setSeen;
-            for(int i=0; i<tokens.length; i++) {
-                
-            }
-            for(int i=0; i<messageSet.length; i++) {
-                Message m = uid ? selected.getUID(messageSet[i]) : selected.get(messageSet[i]);
-                fetch(m, 
-                 String[] headers,
-                 boolean negateHeaders,
-                 boolean flags,
-                 boolean internalDate,
-                 boolean size,
-                 boolean uid,
-                 int start, int end,
-                      boolean setSeen);
-                // FIXME
-            }
-        }
-
         public void append(Mailbox m, Token flags, Date arrived, String literal) {
             Message m = new Message(null, null, new StringReader(literal));
             if (flags != null) flags.setFlags(m);
@@ -160,6 +125,40 @@ public class IMAP extends MessageProtocol {
             selected.add(m);
         }
 
+        public void fetch(Message m, String[] headers, boolean negateHeaders, boolean flags, boolean internalDate,
+                          boolean size, boolean uid, int start, int end, boolean setSeen, boolean envelope,
+                          boolean header, boolean peek) {
+            String reply = "";
+            if (flags)
+                reply +=
+                    "FLAGS ("
+                    + m.deleted ? "\Deleted " : ""
+                    + m.seen ? "\Seen " : ""
+                    + m.flagged ? "\Flagged " : ""
+                    + m.draft ? "\Draft " : ""
+                    + m.answered ? "\Answered " : ""
+                    + m.recent ? "\Recent " : ""
+                    + ") ";
+            if (size) reply += "RFC822.SIZE " m.rfc822size() + " ";; 
+            if (bodyStructure)    // FIXME
+                reply += "(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" " + m.rfc822size() +" "+ m.numLines() +")";
+            if (envelope)
+                reply +=
+                    "(" + quotify(m.date) +
+                    " " + quotify(m.subject) +
+                    " " + quotify(new Address[] { m.from }) + " " + 
+                    " " + quotify(new Address[] { m.sender }) + " " + 
+                    " " + quotify(new Address[] { m.replyTo }) + " " + 
+                    " " + quotify(new Address[] { m.to }) + " " + 
+                    " " + quotify(cc) + " " + 
+                    " " + quotify(bcc) + " " + 
+                    " " + quotify(m.headers.get("in-reply-to")) + " " + 
+                    " " + quotify(m.messageId) +
+                    ") ";
+            if (internaldate) reply += quotify(m.arrival) + " ";
+            // FIXME
+        }
+
         public void copy(int[] set, Mailbox target) {
             for(int i=0; i<set.length; i++) {
                 Message m = selected.get(set[i]);
@@ -189,8 +188,8 @@ public class IMAP extends MessageProtocol {
                     Token[] flags = null;
                     Date arrival = null;
                     if (t.type == LIST) { flags = t.l(); t = token(); }
-                    if (t.type == QUOTED) arrival = datetime();
-                    append(m, flags, arrival, literal());
+                    if (t.type == QUOTED) { arrival = t.datetime(); t = token(); }
+                    append(m, flags, arrival, t.literal());
                 } else if (command.equals("EXAMINE")) examine(mailbox());
                 else if (command.equals("COPY")) copy(set(), mailbox());
                 else if (command.equals("DELETE")) delete(mailbox());
@@ -217,7 +216,9 @@ public class IMAP extends MessageProtocol {
                     Set s = set();
                     Token t = token();
                     Token[] tl = null;
-                    boolean envelope = false, internaldate = false, size = false, flags = false, uid=false;
+                    int start = -1;
+                    int end = -1;
+                    boolean envelope=false, internaldate=false, size=false, flags=false, uid=false, header=false, peek=false;
                     if (t.type == Token.LIST) tl = t.l();
                     else if (t.atom().equals("FULL")) { flags=true; internaldate=true; size=true; envelope=true; body=true; }
                     else if (t.atom().equals("ALL"))  { flags=true; internaldate=true; size=true; envelope=true; }
@@ -226,22 +227,33 @@ public class IMAP extends MessageProtocol {
                     else throw new Bad("expected atom or list");
                     for (int i=0; i<tl.length; i++) {
                         String s = tl[i].atom();
-                        if (s.equals("1")) s = "RFC822";
-                        else if (s.startsWith("1.")) s = s.substring(2);
+                        if (s.startsWith("BODY.PEEK")) { peek = true; s = "BODY" + s.substring(9); }
+                        if (s.startsWith("BODY.1")) s = "BODY" + s.substring(6);
+                        if (s.indexOf('<') != -1) {
+                            start = Integer.parseInt(s.substring(s.indexOf('<')+1, s.indexOf(s.indexOf('<'), '.')));
+                            end = Integer.parseInt(s.substring(s.indexOf(s.indexOf('<'), '.'), s.indexOf('>')));
+                            s = s.substring(0, s.indexOf('<'));
+                        }
                         if (s.equals("ENVELOPE")) envelope = true;
                         else if (s.equals("FLAGS")) flags = true;
                         else if (s.equals("INTERNALDATE")) internaldate = true;
-                        else if (s.equals("RFC822")) body = true; // FIXME: special return syntax?
+                        else if (s.equals("RFC822")) body = true;
                         else if (s.equals("RFC822.HEADER")) header = true;
                         else if (s.equals("RFC822.SIZE")) size = true;
                         else if (s.equals("RFC822.TEXT")) body = true;
                         else if (s.equals("BODY")) body = true;
+                        else if (s.equals("BODY[]")) body = true;
+                        else if (s.equals("BODY[HEADER]")) header=true;
+                        else if (s.startsWith("BODY[HEADER.FIELDS")) throw new No("partial headers not supported");
+                        else if (s.startsWith("BODY[HEADER.FIELDS.NOT")) throw new No("partial headers not supported"):
                         else if (s.equals("BODYSTRUCTURE")) throw new No("FETCH BODYSTRUCTURE not supported");
+                        else if (s.equals("TEXT")) body = true;
+                        else if (s.equals("MIME")) throw new Bad("FETCH BODY.MIME not supported");
                         else if (s.equals("UID")) uid = true;
                         else if (s.startsWith("BODY")) throw new No("FETCH BODY[*] not supported");
                         else throw new Bad("unrecognized FETCH argument \"" + s + "\"");
                     }
-                    fetch(selected, null, false, flags, internaldate, size, uid, start, end, setseen, envelope);
+                    fetch(selected, flags, internaldate, size, uid, start, end, setseen, envelope, header, peek);
                 } else if (command.equals("STATUS")) {
                     Mailbox m = mailbox();
                     Token[] attrs = l();
@@ -294,80 +306,47 @@ public class IMAP extends MessageProtocol {
             ")";
     }
     
-    public static void fetch(Message m, String[] headers, boolean negateHeaders, boolean flags, boolean internalDate,
-                             boolean size, boolean uid, int start, int end, boolean setSeen, boolean envelope) {
-        String reply = "";
-        if (flags)
-            reply +=
-                "FLAGS ("
-                + m.deleted ? "\Deleted " : ""
-                + m.seen ? "\Seen " : ""
-                + m.flagged ? "\Flagged " : ""
-                + m.draft ? "\Draft " : ""
-                + m.answered ? "\Answered " : ""
-                + m.recent ? "\Recent " : ""
-                + ") ";
-        if (size) reply += "RFC822.SIZE " m.rfc822size() + " ";; 
-        if (bodyStructure)    // FIXME
-            reply += "(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" " + m.rfc822size() +" "+ m.numLines() +")";
-        if (envelope)
-            reply +=
-                "(" + quotify(m.date) +
-                " " + quotify(m.subject) +
-                " " + quotify(new Address[] { m.from }) + " " + 
-                " " + quotify(new Address[] { m.sender }) + " " + 
-                " " + quotify(new Address[] { m.replyTo }) + " " + 
-                " " + quotify(new Address[] { m.to }) + " " + 
-                " " + quotify(cc) + " " + 
-                " " + quotify(bcc) + " " + 
-                " " + quotify(m.headers.get("in-reply-to")) + " " + 
-                " " + quotify(m.messageId) +
-                ") ";
-        if (internaldate) reply += quotify(m.arrival) + " ";
-        // FIXME
-    }
 
-    public static Query parseQuery() {
-        String tok = null;
+    public static Query query() {
+        String s = null;
         boolean not = false;
         while(true) {
             Token t = token();
-            if (t.type == t.LIST) { }
-            else if (t.type == t.SET) { }
-            tok = t.atom();
-            if (tok.equals("NOT")) { not = true; continue; }
-            if (tok.equals("OR")) { /* FIXME */ }
-            if (tok.equals("AND")) { /* FIXME */ }
+            if (t.type == t.LIST) { /* FIXME */ }
+            else if (t.type == t.SET) return new Query.Set(t.set());
+            s = t.atom();
+            if (s.equals("NOT")) { not = true; continue; }
+            if (s.equals("OR")) { return new Query.OR(new Query[] { query(), query() }); }
+            if (s.equals("AND")) { return new Query.AND(new Query[] { query(), query() }); }
             break;
         }
-        if (tok.startsWith("UN")) { not = true; tok = tok.substring(2); }
-        if (tok.equals("ANSWERED")) q = new Query.Flag(Query.Flag.ANSWERED);
-        else if (tok.equals("DELETED")) q = new Query.Flag(Query.Flag.ANSWERED);
-        else if (tok.equals("DRAFT")) q = new Query.Flag(Query.Flag.ANSWERED);
-        else if (tok.equals("FLAGGED")) q = new Query.Flag(Query.Flag.ANSWERED);
-        else if (tok.equals("RECENT")) q = new Query.Flag(Query.Flag.ANSWERED);
-        else if (tok.equals("SEEN")) q = new Query.Flag(Query.Flag.ANSWERED);
-        else if (tok.equals("OLD")) { not = true; q = new Query.Flag(Query.Flag.RECENT); }
-        else if (tok.equals("NEW")) q = new Query.And(new Query.Flag(Query.Flag.RECENT),
-                                                      new Query.Not(new Query.Flag(Query.Flag.SEEN)));
-        else if (tok.equals("KEYWORD")) q = new Query.Header("keyword", flag());
-        else if (tok.equals("HEADER")) q = new Query.Header(astring(), astring());
-        else if (tok.equals("BCC")) q = new Query.Header("bcc", astring());
-        else if (tok.equals("CC")) q = new Query.Header("cc", astring());
-        else if (tok.equals("FROM")) q = new Query.Header("from", astring());
-        else if (tok.equals("TO")) q = new Query.Header("to", astring());
-        else if (tok.equals("SUBJECT")) q = new Query.Header("subject", astring());
-        else if (tok.equals("BEFORE")) q = new Query.Arrival(date(), true, false);
-        else if (tok.equals("SINCE")) q = new Query.Arrival(date(), false, true);
-        else if (tok.equals("ON")) q = new Query.Arrival(date(), true, true);
-        else if (tok.equals("LARGER")) q = new Query.Size(n(), true);
-        else if (tok.equals("SMALLER")) q = new Query.Size(n(), false);
-        else if (tok.equals("BODY")) q = new Query.FullText(astring(), true, false);
-        else if (tok.equals("TEXT")) q = new Query.FullText(astring(), true, true);
-        else if (tok.equals("SENTBEFORE")) { /* FIXME date() */ }
-        else if (tok.equals("SENTSINCE"))  { /* FIXME date() */ }
-        else if (tok.equals("SENTON"))     { /* FIXME date() */ }
-        else if (tok.equals("UID"))        { /* FIXME set() */ }
+        if (s.startsWith("UN")) { not = true; tok = s.substring(2); }
+        if (s.equals("ANSWERED")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (s.equals("DELETED")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (s.equals("DRAFT")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (s.equals("FLAGGED")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (s.equals("RECENT")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (s.equals("SEEN")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (s.equals("OLD")) { not = true; q = new Query.Flag(Query.Flag.RECENT); }
+        else if (s.equals("NEW"))q=new Query.And(new Query.Flag(Query.Flag.RECENT),new Query.Not(new Query.Flag(Query.Flag.SEEN)));
+        else if (s.equals("KEYWORD")) q = new Query.Header("keyword", flag());
+        else if (s.equals("HEADER")) q = new Query.Header(astring(), astring());
+        else if (s.equals("BCC")) q = new Query.Header("bcc", astring());
+        else if (s.equals("CC")) q = new Query.Header("cc", astring());
+        else if (s.equals("FROM")) q = new Query.Header("from", astring());
+        else if (s.equals("TO")) q = new Query.Header("to", astring());
+        else if (s.equals("SUBJECT")) q = new Query.Header("subject", astring());
+        else if (s.equals("LARGER")) q = new Query.Size(n(), true);
+        else if (s.equals("SMALLER")) q = new Query.Size(n(), false);
+        else if (s.equals("BODY")) q = new Query.FullText(astring(), true, false);
+        else if (s.equals("TEXT")) q = new Query.FullText(astring(), true, true);
+        else if (s.equals("BEFORE")) q = new Query.Arrival(date(), true, false);
+        else if (s.equals("SINCE")) q = new Query.Arrival(date(), false, true);
+        else if (s.equals("ON")) q = new Query.Arrival(date(), true, true);
+        else if (s.equals("SENTBEFORE")) q = new Query.Sent(date(), true, false);
+        else if (s.equals("SENTSINCE")) q = new Query.Sent(date(), false, true);
+        else if (s.equals("SENTON")) q = new Query.Sent(date(), true, true);
+        else if (s.equals("UID")) q = new Query.UID(set());
     }
 
     private static class Token {
@@ -390,7 +369,9 @@ public class IMAP extends MessageProtocol {
         public void deleteFlags(Message m) { }
 
         public String mailboxPattern() {
-            /* 1*(ATOM_CHAR / "%" / "*") / string */
+            if (type == ATOM) retrn s;
+            if (type == QUOTED) return s;
+            throw new Bad("exepected a mailbox pattern");
         }
         public String flag() throws Bad {
             if (type != ATOM) throw new Bad("expected a flag");
@@ -410,22 +391,44 @@ public class IMAP extends MessageProtocol {
             if (type != QUOTED) throw new Bad("expected qstring");
             return s;
         }
-        public String nstring() throws Bad {
-        }
         public Token[] l() throws Bad {
             if (type == NIL) return null;
             if (type != LIST) throw new Bad("expected parenthesized list");
             return l;
         }
         public int[] set() throws Bad {
-            // FIXME   <set>,<set>  <n>  <n>:<n>   *  (* = largest)
+            if (type != ATOM) throw new Bad("expected a messageid set");
+            Vec ids = new Vec();
+            StringTokenizer st = new StringTokenizer(s, ",");
+            while(st.hasMoreTokens()) {
+                String s = st.nextToken();
+                if (s.indexOf(':') != -1) {
+                    int start = Integer.parseInt(s.substring(0, s.indexOf(':')));
+                    String end_s = s.indexOf(':'+1);
+                    if (end_s.equals("*")) {
+                        ids.addElement(new Integer(start));
+                        ids.addElement(new Integer(-1));
+                    } else {
+                        int end = Integer.parseInt(end_s);
+                        for(int j=start; j<=end; j++) ids.addElement(new Integer(j));
+                    }
+                } else {
+                    ids.addElement(new Integer(Integer.parseInt(s)));
+                }
+            }
+            int[] ret = new int[ids.size()];
+            ids.copyInto(ret);
+            return ret;
         }
         public Date date() throws Bad {
-            // may be quoted or unquoted
-            try {
-            new SimpleDateFormat("dd-MMM-yyyy").parse(s);
-            } catch (DateParsingException) {
-            }
+            if (type != QUOTED && type != ATOM) throw new Bad("Expected quoted or unquoted date");
+            try { new SimpleDateFormat("dd-MMM-yyyy").parse(s);
+            } catch (ParseException p) { throw new Bad("invalid date format; " + p); }
+        }
+        public Date datetime() throws Bad {
+            if (type != QUOTED && type != ATOM) throw new Bad("Expected quoted or unquoted datetime");
+            try { new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss +zzzz").parse(s);
+            } catch (ParseException p) { throw new Bad("invalid datetime format; " + p); }
         }
         public String nstring() throws Bad {
             if (type == NIL) return null;
@@ -438,46 +441,53 @@ public class IMAP extends MessageProtocol {
             throw new Bad("expected atom or string");
         }
         public String atom() throws Bad {
-            // ATOM_CHAR       ::= <any CHAR except atom_specials>
-            // atom_specials   ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials
             if (type != ATOM) throw new Bad("expected atom");
+            for(int i=0; i<s.length(); i++) {
+                char c = s.charAt(i);
+                if (c == '(' || c == ')' || c == '{' || c == ' ' || c == '%' || c == '*')
+                    throw new Bad("invalid char in atom: " + c);
+            }
+            return s;
         }
         public Mailbox mailbox() throws Bad {
             if (type == BAREWORD && s.toLowerCase().equals("inbox")) return Mailbox.INBOX;
             return Mailbox.getMailbox(astring());
         }
-        public Date datetime() throws Bad {
-            // expected to be a quoted string
-            
-            new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss +zzzz").parse(s);
-        }
 
     }
 
+    public static char getc() { return chars[pos++]; }
+    public static char peekc() { return chars[pos]; }
+    public static char ungetc() { pos--; }
 
-
-    // literal         ::= "{" number "}" CRLF *CHAR8     ;; Number represents the number of CHAR8 octets
-
-    public static Token[] tokenize(String s) {
+    public static Token token() {
         Vec toks = new Vec();
         StringBuffer sb = new StringBuffer();
-        for(int i=0; i<s.length();) {
-            sb.setSize(0);
-            if (s.charAt(i) == '\"') {
-                for(i++; i < s.length(); i++) {
-                    if (s.charAt(i) == '\\') sb.append(s.charAt(++i));
-                    else if (s.charAt(i) == '\"') { i++; break; }
-                    else sb.append(s.charAt(i));
-                }
-            } else if (s.charAt(i) == '(') {
-                // FIXME, deal with strings within parens
-            } else {
-                for(; i < s.length(); i++)
-                    if (s.charAt(i) == ' ' || s.charAt(i) == '\"') break;
-                    else sb.append(s.charAt(i));
-            }
-            toks.addElement(sb.toString());
-            while (i < s.length() && s.charAt(i) == ' ') i++;
+        char c = getc(); while(c == ' ') c = getc();
+        if (c == '{') {
+            while(peekc() != '}') sb.append(getc());
+            int octets = Integer.parseInt(sb.toString());
+            while(peekc() == ' ') getc();   // whitespace
+            if (peekc() == '\r')  getc();
+            if (peekc() == '\n')  getc();
+            // FIXME: read octets number of octets
+        } else if (c == '\"') {
+            c = getc();
+            if (c == '\\') sb.append(getc());
+            else if (c == '\"') break;
+            else sb.append(c);
+        } else if (c == ')') {
+            return null;
+        } else if (c == '(') {
+            Vec toks = new Vector();
+            do { Token t = token(); if (t != null) toks.addElement(t); } while (t != null);
+            Token[] ret = new Token[toks.size()];
+            toks.copyInto(ret);
+            return ret;
+        } else while(true) {
+            c = peekc();
+            if (c == ' ' || c == '\"' || c == '(' || c == ')' || c == '{') break;
+            sb.append(getc());
         }
         return toks.toArray();
     }