able to load headers and message body in mozilla
authoradam <adam@megacz.com>
Sat, 12 Jun 2004 06:19:00 +0000 (06:19 +0000)
committeradam <adam@megacz.com>
Sat, 12 Jun 2004 06:19:00 +0000 (06:19 +0000)
darcs-hash:20040612061900-5007d-87b52a5d8924a72a8b7fd7adb1eac2a879771f43.gz

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

index 8dcd389..4037b4b 100644 (file)
@@ -78,10 +78,14 @@ public class Query {
             case NOT:        return !q[0].match(it);
             case OR:         for(int i=0; i<q.length; i++) if (q[i].match(it)) return true; return false;
             case AND:        for(int i=0; i<q.length; i++) if (!q[i].match(it)) return false; return true;
-            case UID:        if (set != null){for(int i=0; i<set.length; i++) if (set[i] == it.uid()) return true; return false; }
-                             else return it.uid() >= min && it.uid() <= max;
-            case NUM:        if (set != null){for(int i=0; i<set.length; i++) if (set[i] == it.uid()) return true; return false; }
+            case UID:        if (set != null) {
+                                 for(int i=0; i<set.length; i+=2) 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<set.length; i+=2) if (set[i] <= it.num() && set[i+1] >= it.num()) return true;
+                                 return false; }
+                             else return it.num() >= min && it.num() <= 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)) &&
index d98572e..04d7194 100644 (file)
@@ -156,54 +156,67 @@ public class IMAP extends MessageProtocol {
             if (flags != null) { /* FEATURE */ }
         }
 
-        public void fetch(Query q, Token t) {
-            Token[] tl = null;
-            int start = -1, end = -1;
-            boolean peek = false, fast = false, all = false, full = false;
-            if (t == null) { tl = new Token[] { new Token("BODY", false) }; }
-            else if (t.type == Token.LIST) tl = t.l();
-            else if (t.atom().equals("FULL")) full = true;
-            else if (t.atom().equals("ALL")) all = true;
-            else if (t.atom().equals("FAST")) fast = true;
-            StringBuffer reply = new StringBuffer();
+        public static String qq(String s) {
+            StringBuffer ret = new StringBuffer(s.length() + 20);
+            ret.append('{');
+            ret.append(s.length());
+            ret.append('}');
+            ret.append('\r');
+            ret.append('\n');
+            ret.append(s);
+            ret.append('\r');
+            ret.append('\n');
+            return ret.toString();
+        }
+
+        public void fetch(Query q, FetchRequest[] fr, boolean uid) {
+            System.err.println("query == " + q);
             for(Mailbox.Iterator it = selected.iterator(q); it.next(); ) {
                 Message m = it.cur();
-                for (int i=0; i<tl.length; i++) {
-                    String s = tl[i].atom();
-                    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) {
-                        String range = s.substring(s.indexOf('<') + 1, s.indexOf('>'));
-                        s = s.substring(0, s.indexOf('<'));
-                        if (range.indexOf(imapSeparator) == -1) end = Integer.MAX_VALUE;
-                        else {
-                            end = Integer.parseInt(range.substring(range.indexOf(imapSeparator) + 1));
-                            range = range.substring(0, range.indexOf(imapSeparator));
+                System.err.println("message == " + m);
+                StringBuffer reply = new StringBuffer();
+                boolean peek = false;
+                for(int i=0; i<fr.length; i++)
+                    if (fr[i] == null) continue;
+                    else if (fr[i] == BODYSTRUCTURE)        throw new Exn.Bad("BODYSTRUCTURE not supported");
+                    else if (fr[i] == ENVELOPE     )   reply.append("ENVELOPE " + envelope(m) + " ");
+                    else if (fr[i] == FLAGS        )   reply.append("FLAGS (" + flags(it) + ") ");
+                    else if (fr[i] == INTERNALDATE )   reply.append("INTERNALDATE " + quotify(m.arrival) + " ");
+                    else if (fr[i] == UID          )   reply.append("UID " + it.uid() + " ");
+                    else if (fr[i] == RFC822       ) { reply.append("RFC822 "); reply.append(qq(m.body)); }
+                    else if (fr[i] == RFC822HEADER ) { reply.append("RFC822.HEADER ");reply.append(qq(m.allHeaders));}
+                    else if (fr[i] == RFC822SIZE   )   reply.append("RFC822.SIZE " + m.rfc822size() + " ");
+                    else if (fr[i] == RFC822TEXT   ) { }
+                    else if (fr[i] instanceof FetchRequestBody) {
+                        FetchRequestBody frb = (FetchRequestBody)fr[i];
+                        StringBuffer payload = new StringBuffer();
+                        peek = frb.peek;
+                        // FIXME obey path[] here
+                        switch(frb.type) {
+                            case FetchRequestBody.PATH: case FetchRequestBody.TEXT:
+                                reply.append("BODY[TEXT] "); payload.append(m.body); break;
+                            case FetchRequestBody.MIME: throw new Exn.Bad("FETCH MIME not supported");
+                            case FetchRequestBody.HEADER: reply.append("BODY[HEADER] "); payload.append(m.allHeaders); break;
+                            case FetchRequestBody.FIELDS:
+                                reply.append("BODY[HEADER.FIELDS (");
+                                for(int j=0; j<frb.headers.length; j++) {
+                                    reply.append(frb.headers[j].s + " ");
+                                    payload.append(frb.headers[j].s + ": " + m.headers.get(frb.headers[j].s) + "\r\n");
+                                }
+                                reply.append(")] ");
+                                break;
+                            case FetchRequestBody.FIELDS_NOT: throw new Exn.Bad("FETCH HEADERS.NOT not supported");
                         }
-                        start = Integer.parseInt(range);
-                    }
-                    if (s.equals("ENVELOPE") || all || full)          reply.append("ENVELOPE " + envelope(m) + " ");
-                    if (s.equals("FLAGS") || full || all || fast)  reply.append("FLAGS (" + flags(it) + ") ");
-                    if (s.equals("INTERNALDATE") || full || all || fast) reply.append("INTERNALDATE "+quotify(m.arrival)+" ");
-                    if (s.equals("RFC822.SIZE") || full || all || fast) reply.append("RFC822.SIZE " + m.rfc822size() + " ");
-                    if (s.equals("RFC822.HEADER") || s.equals("BODY[HEADER]"))
-                        { reply.append("BODY[HEADER] {" + m.allHeaders.length() + "}\r\n"); reply.append(m.allHeaders); }
-                    if (s.equals("RFC822")||s.equals("RFC822.TEXT")||s.equals("BODY")||s.equals("BODY[]")||s.equals("TEXT")||full)
-                        { reply.append("BODY[TEXT] {" + m.body.length() + "}\r\n"); reply.append(m.body); }
-                    if (s.equals("UID"))                        reply.append("UID " + it.uid());
-                    if (s.equals("MIME"))                       throw new Exn.No("FETCH BODY.MIME not supported");
-                    if (s.startsWith("BODY[HEADER.FIELDS"))     throw new Exn.No("partial headers not supported");
-                    if (s.startsWith("BODY[HEADER.FIELDS.NOT")) throw new Exn.No("partial headers not supported");
-                    if (s.equals("BODYSTRUCTURE"))
-                        reply.append("(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" " +
-                                     m.rfc822size()+" "+ m.lines +")");
-                }
-                star(it.num() + " FETCH (" + reply.toString() + ")");
-                // FEATURE: range requests
-                // FEATURE set seen flag if not BODY.PEEK
+                        if (frb.start == -1) reply.append(qq(payload.toString()));
+                        else if (frb.start == -1) reply.append("<" + frb.end + ">" + 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);
             }
         }
 
+
         // FEATURE: hoist the parsing here
         public void store(Query q, String what, Token[] flags) {
             for(Mailbox.Iterator it = selected.iterator(q); it.next(); ) {
@@ -239,6 +252,8 @@ public class IMAP extends MessageProtocol {
                     if (command.equalsIgnoreCase("AUTHENTICATE"))    { login(astring(), astring()); }
                     else if (command.equalsIgnoreCase("LIST"))         list(mailbox(), mailboxPattern()); 
                     else if (command.equalsIgnoreCase("LSUB"))         lsub(mailbox(), mailboxPattern()); 
+                    else if (command.equalsIgnoreCase("SUBSCRIBE"))   { /* FIXME */ }
+                    else if (command.equalsIgnoreCase("UNSUBSCRIBE")) { /* FIXME */ }
                     else if (command.equalsIgnoreCase("CAPABILITY")) { capability(); }
                     else if (command.equalsIgnoreCase("LOGIN"))        login(astring(), astring());
                     else if (command.equalsIgnoreCase("LOGOUT"))     { logout(); conn.close(); return false; }
@@ -252,11 +267,12 @@ public class IMAP extends MessageProtocol {
                     else if (command.equalsIgnoreCase("NOOP"))         noop();
                     else if (command.equalsIgnoreCase("CREATE"))       create(astring());
                     else if (command.equalsIgnoreCase("STORE"))        store(Query.num(set()), atom(), l());
-                    else if (command.equalsIgnoreCase("FETCH"))        fetch(Query.num(set()), token());
+                    else if (command.equalsIgnoreCase("FETCH"))        fetch(uid ? Query.uid(set()) : Query.num(set()),
+                                                                             parseFetchRequest(l()), uid);
                     else if (command.equalsIgnoreCase("STATUS"))       status(mailbox(), l());
                     else                                     throw new Exn.Bad("unrecognized command \"" + command + "\"");
-                    pw.println(tag + " OK " + command + " Completed.");
-                    System.err.println(tag + " OK " + command + " Completed.");
+                    pw.println(tag + " OK uid " + command + " Completed.");
+                    System.err.println(tag + " OK uid " + command + " Completed.");
                 } catch (Exn.Bad b) { pw.println(tag + " Bad " + b.toString()); System.err.println(tag + " Bad " + b.toString()); b.printStackTrace();
                 } catch (Exn.No n) {  pw.println(tag + " OK " + n.toString()); System.err.println(tag + " OK " + n.toString());
                 }
@@ -354,10 +370,10 @@ public class IMAP extends MessageProtocol {
         }
 
         class Token {
-            private byte type;
-            private final String s;
-            private final Token[] l;
-            private final int n;
+            public byte type;
+            public final String s;
+            public final Token[] l;
+            public final int n;
             private static final byte NIL = 0;
             private static final byte LIST = 1;
             private static final byte QUOTED = 2;
@@ -409,13 +425,14 @@ public class IMAP extends MessageProtocol {
                         String end_s = s.substring(s.indexOf(':')+1);
                         if (end_s.equals("*")) {
                             ids.addElement(new Integer(start));
-                            ids.addElement(new Integer(-1));
+                            ids.addElement(new Integer(Integer.MAX_VALUE));
                         } 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)));
+                        ids.addElement(new Integer(Integer.parseInt(s)));
                     }
                 }
                 int[] ret = new int[ids.size()];
@@ -512,18 +529,21 @@ public class IMAP extends MessageProtocol {
                         else sb.append(c);
                     }
                     return new Token(sb.toString(), true);
-                } else if (c == ')') {
-                    return null;
-                } else if (c == '(') {
+
+                // NOTE: this is technically a violation of the IMAP grammar, since atoms like FOO[BAR should be legal
+                } else if (c == ']' || c == ')') { return null;
+                } else if (c == '[' || c == '(') {
                     Token t;
                     do { t = token(); if (t != null) toks.addElement(t); } while (t != null);
                     Token[] ret = new Token[toks.size()];
                     toks.copyInto(ret);
-                    return null;  // FIXME
+                    return new Token(ret);  // FIXME
+
                 } else while(true) {
                     sb.append(c);
                     c = peekc();
-                    if (c == ' ' || c == '\"' || c == '(' || c == ')' || c == '{' || c == '\n' || c == '\r')
+                    if (c == ' ' || c == '\"' || c == '(' || c == ')' || c == '[' || c == ']' ||
+                        c == '{' || c == '\n' || c == '\r')
                         return new Token(sb.toString(), false);
                     getc();
                 }
@@ -547,5 +567,106 @@ public class IMAP extends MessageProtocol {
         public String astring() throws Exn.Bad { return token().astring(); }
         public String atom() throws Exn.Bad { return token().atom(); }
         public Mailbox mailbox() throws Exn.Bad, IOException { return token().mailbox(); }
+
+        public final FetchRequest BODYSTRUCTURE = new FetchRequest();
+        public final FetchRequest ENVELOPE      = new FetchRequest();
+        public final FetchRequest FLAGS         = new FetchRequest();
+        public final FetchRequest INTERNALDATE  = new FetchRequest();
+        public final FetchRequest RFC822        = new FetchRequest();
+        public final FetchRequest RFC822HEADER  = new FetchRequest();
+        public final FetchRequest RFC822SIZE    = new FetchRequest();
+        public final FetchRequest RFC822TEXT    = new FetchRequest();
+        public final FetchRequest UID           = new FetchRequest();
+        public final FetchRequest BODY          = new FetchRequest();
+        public final FetchRequest BODYPEEK      = new FetchRequest();
+        public class FetchRequest {
+        }
+            public class FetchRequestBody extends FetchRequest {
+                int type = 0;
+                boolean peek = false;
+                int[] path = null;
+                int start = -1;
+                int end = -1;
+                Token[] headers = null;
+                public static final int PATH = 0;
+                public static final int TEXT = 1;
+                public static final int MIME = 2;
+                public static final int HEADER = 3;
+                public static final int FIELDS = 4;
+                public static final int FIELDS_NOT= 5;
+            }
+
+            public FetchRequest[] parseFetchRequest(Token[] t) {
+                FetchRequest[] ret = new FetchRequest[t.length];
+                for(int i=0; i<t.length; i++) {
+                    if (t[i] == null) continue;
+                    if (t[i].s.equalsIgnoreCase("BODYSTRUCTURE"))      ret[i] = BODYSTRUCTURE;
+                    else if (t[i].s.equalsIgnoreCase("ENVELOPE"))      ret[i] = ENVELOPE;
+                    else if (t[i].s.equalsIgnoreCase("FLAGS"))         ret[i] = FLAGS;
+                    else if (t[i].s.equalsIgnoreCase("INTERNALDATE"))  ret[i] = INTERNALDATE;
+                    else if (t[i].s.equalsIgnoreCase("RFC822"))        ret[i] = RFC822;
+                    else if (t[i].s.equalsIgnoreCase("RFC822.HEADER")) ret[i] = RFC822HEADER;
+                    else if (t[i].s.equalsIgnoreCase("RFC822.SIZE"))   ret[i] = RFC822SIZE;
+                    else if (t[i].s.equalsIgnoreCase("RFC822.TEXT"))   ret[i] = RFC822TEXT;
+                    else if (t[i].s.equalsIgnoreCase("UID"))           ret[i] = UID;
+                    else {
+                        FetchRequestBody b = new FetchRequestBody();
+                        String s = t[i].s.toUpperCase();
+
+                        String startend = null;
+                        if (s.indexOf('<') != -1 && s.endsWith(">")) {
+                            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 Exn.No("unknown fetch argument: " + s);
+                    
+                        if (i<t.length - 1 && (t[i+1].type == t[i].LIST)) {
+                            i++;
+                            Token[] t2 = t[i].l();
+                            if (t2.length == 0) {
+                                b.type = b.TEXT;
+                            } else {
+                                String s2 = t2[0].s.toUpperCase();
+                                Vec mimeVec = new Vec();
+                                while(s2.charAt(0) >= '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<b.path.length; j++) b.path[j] = ((Integer)mimeVec.elementAt(j)).intValue();
+                                }
+                                if (s2.equals(""))                       { b.type = b.PATH; }
+                                else if (s2.equals("HEADER"))            { b.type = b.HEADER; }
+                                else if (s2.equals("HEADER.FIELDS"))     { b.type = b.FIELDS; b.headers = t2[1].l(); }
+                                else if (s2.equals("HEADER.FIELDS.NOT")) { b.type = b.FIELDS_NOT; b.headers = t2[1].l(); }
+                                else if (s2.equals("MIME"))              { b.type = b.MIME; }
+                                else if (s2.equals("TEXT"))              { b.type = b.TEXT; }
+                            }
+                        }
+
+                        if (i<t.length - 1 && (t[i+1].s.startsWith("<"))) {
+                            i++;
+                            startend = t[i].s.substring(1, t[i].s.length() - 1);
+                        }
+
+                        if (startend != null) {
+                            if (startend.indexOf('.') == -1) {
+                                b.start = Integer.parseInt(startend);
+                                b.end = -1;
+                            } else {
+                                b.start = Integer.parseInt(startend.substring(0, startend.indexOf('.')));
+                                b.end = Integer.parseInt(startend.substring(startend.indexOf('.') + 1));
+                            }
+                        }
+                        ret[i] = b;
+                    }
+                }
+                return ret;
+        }
+        
     }
 }