restructuring; broke out Query, cleaned up Mailbox
authoradam <adam@megacz.com>
Mon, 31 May 2004 06:13:08 +0000 (06:13 +0000)
committeradam <adam@megacz.com>
Mon, 31 May 2004 06:13:08 +0000 (06:13 +0000)
darcs-hash:20040531061308-5007d-fe7e563b65b1ed9b90425754bf769009c64af353.gz

src/org/ibex/mail/Flag.java [new file with mode: 0644]
src/org/ibex/mail/MIME.java [new file with mode: 0644]
src/org/ibex/mail/Message.java
src/org/ibex/mail/Query.java
src/org/ibex/mail/target/Mailbox.java

diff --git a/src/org/ibex/mail/Flag.java b/src/org/ibex/mail/Flag.java
new file mode 100644 (file)
index 0000000..9707d76
--- /dev/null
@@ -0,0 +1,10 @@
+package org.ibex.mail;
+
+public class Flag {
+    public static final int DELETED  = 0x0001;
+    public static final int SEEN     = 0x0002;
+    public static final int FLAGGED  = 0x0004;
+    public static final int DRAFT    = 0x0010;
+    public static final int ANSWERED = 0x0020;
+    public static final int RECENT   = 0x0040;
+}
diff --git a/src/org/ibex/mail/MIME.java b/src/org/ibex/mail/MIME.java
new file mode 100644 (file)
index 0000000..76ea5b1
--- /dev/null
@@ -0,0 +1,7 @@
+package org.ibex.mail;
+
+// FEATURE: MIME RFC2045, 2046, 2049
+
+/** This class contains logic for encoding and decoding MIME multipart messages */
+public class MIME {
+}
index 5ee6864..9e2105b 100644 (file)
@@ -11,14 +11,22 @@ import java.io.*;
 // folded headers: can insert CRLF anywhere that whitespace appears (before the whitespace)
 // date/time parsing: see spec, 3.3
 
-// FEATURE: MIME RFC2045, 2046, 2049
 // FEATURE: PGP-signature-parsing
 // FEATURE: mailing list header parsing
 // FEATURE: delivery status notification (and the sneaky variety)
 // FEATURE: threading as in http://www.jwz.org/doc/threading.html
+// FEATURE: lazy body
 
+/** 
+ *  [immutable] This class encapsulates a message "floating in the
+ *  ether": RFC2822 data but no storage-specific flags or other
+ *  metadata.
+ */
 public class Message extends JSReflection {
 
+    public static interface Visitor { public abstract void visit(Message m); }
+
+    // FIXME make this immutable
     private static class CaseInsensitiveHash extends Hashtable {
         public Object get(Object o) {
             if (o instanceof String) return super.get(((String)o).toLowerCase());
index e39f8b4..45b5659 100644 (file)
@@ -1,29 +1,76 @@
 package org.ibex.mail;
 import java.util.*;
 
+/**
+ *  [immutable] This class encapsulates a query against a mailbox.
+ *
+ *  The default implementation of Mailbox.query() simply retrieves
+ *  each message and calls Query.match() on it to filter out any
+ *  messages which do not match.
+ *
+ *  Production-quality Mailbox implementations will want to inspect
+ *  the Query object passed to Mailbox.query() to perform more
+ *  efficient queries.
+ */
 public class Query {
 
-    public static Query not(Query q) { return null; }
-    public static Query and(Query a1, Query a2) { return null; }
-    public static Query or(Query a1, Query a2) { return null; }
-    public static Query set(int[] set) { return null; }
-    public static Query sent(Date d, boolean foo0, boolean foo) { return null; }
-    public static Query arrival(Date d, boolean foo, boolean bar) { return null; }
-    public static Query uid(int uid) { return null; }
-    public static Query uid(int[] uid) { return null; }
-    public static Query header(String name) { return null; }
-    public static Query header(String name, String val) { return null; }
-    public static Query size(int size, boolean foo) { return null; }
-    public static Query fullText(String searchFor, boolean foo, boolean bar) { return null; }
+    public static Query not(Query q)            { return new Query(NOT, new Query[] { q },      0, 0, 0, null, null, null, null); }
+    public static Query and(Query q1, Query q2) { return new Query(AND, new Query[] { q1, q2 }, 0, 0, 0, null, null, null, null); }
+    public static Query and(Query[] q)          { return new Query(AND, q,                      0, 0, 0, null, null, null, null); }
+    public static Query or(Query q1, Query q2)  { return new Query(OR,  new Query[] { q1, q2 }, 0, 0, 0, null, null, null, null); }
+    public static Query or(Query[] q)           { return new Query(OR,  q,                      0, 0, 0, null, null, null, null); }
+    public static Query uid(int min, int max)   { return new Query(UID, null, min, max, 0, null, null, null, null); }
+    public static Query messagenum(int min, int max) { return new Query(MESSAGENUM, null, min, max, 0, null, null, null, null); }
+    public static Query sent(Date earliest, Date latest)    { return new Query(SENT, null,0,0,0,null,null, earliest, latest); }
+    public static Query arrival(Date earliest, Date latest) { return new Query(ARRIVAL, null,0,0,0,null,null, earliest, latest); }
+    public static Query header(String name, String val)     { return new Query(HEADER, null, 0, 0, 0, name, val, null, null); }
+    public static Query size(int min, int max)              { return new Query(SIZE, null, min, max, 0, null, null, null, null); }
+    public static Query body(String text)                   { return new Query(BODY, null, 0, 0, 0, null, text, null, null); }
+    public static Query flags(int flags)                    { return new Query(FLAGS, null, 0, 0, flags, null, null, null, null); }
 
-    // FIXME
-    public static class Flag {
-        public static final Query ANSWERED = null;
-        public static final Query SEEN     = null;
-        public static final Query DRAFT    = null;
-        public static final Query FLAGGED  = null;
-        public static final Query RECENT   = null;
-        public static final Query DELETED  = null;
+    private Query(int type, Query[] q, int min, int max, int flags, String key, String text, Date earliest, Date latest) {
+        this.type = type; this.q = q; this.min = min; this.max = max; this.flags = flags; this.key = key; this.text = text;
+        this.earliest = earliest; this.latest = latest; }
+
+    public static int ALL        = 0;
+    public static int NOT        = 1;
+    public static int AND        = 2;
+    public static int OR         = 3;
+    public static int UID        = 4;
+    public static int MESSAGENUM = 5; 
+    public static int SENT       = 6;
+    public static int ARRIVAL    = 7;
+    public static int HEADER     = 8;
+    public static int SIZE       = 9;
+    public static int FLAGS      = 10;
+    public static int BODY       = 11;
+
+    public final int type;
+    public final Query[] q;
+    public final int min;
+    public final int max;
+    public final int flags;
+    public final String key;
+    public final String text;
+    public final Date earliest;
+    public final Date latest;
+
+    public boolean match(Mailbox mbox, Message m) {
+        switch(type) {
+            case ALL:        return true;
+            case NOT:        return !q[0].match(mbox, m);
+            case OR:         for(int i=0; i<q.length; i++) if (q[i].match(mbox, m)) return true; return false;
+            case AND:        for(int i=0; i<q.length; i++) if (!q[i].match(mbox, m)) return false; return true;
+            case UID:        return mbox.uid(m)        >= min && mbox.uid <= max;
+            case MESSAGENUM: return mbox.messagenum(m) >= min && mbox.messagenum(m) <= max;
+            case SENT:       return (latest == null || m.sent.before(latest))    &&(earliest == null || m.sent.after(earliest));
+            case ARRIVAL:    return (latest == null || m.arrival.before(latest)) &&(earliest == null || m.arrival.after(earliest));
+            case SIZE:       return m.size >= min && m.size <= max;
+            case FLAGS:      return mbox.getFlags(flags);
+            case HEADER:     return m.headers.get(key) != null && ((String)m.headers.get(key)).indexOf(text) != -1;
+            case HEADER:     return m.body.indexOf(text) != -1;
+            default:         throw new Error("this should not happen");
+        }
     }
 
 }
index 53ce945..a7f32d5 100644 (file)
@@ -7,80 +7,35 @@ import java.net.*;
 import java.util.*;
 import java.text.*;
 
-// FIXME: appallingly inefficient
 public class Mailbox extends Target {
 
-    private static final String STORAGE_ROOT =
-        System.getProperty("ibex.mail.root", File.separatorChar + "var" + File.separatorChar + "org.ibex.mail");
-    public static FileBased root = null;
-    public static Transcript transcript = null;
-    static {
-        try {
-            root = new FileBased(STORAGE_ROOT + File.separatorChar);
-            transcript = new Transcript(STORAGE_ROOT + File.separatorChar + "transcript");
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
+    // metadata
+    public void   set(Message m, String key, String val) { throw new MailException.MetadataNotSupported(); }
+    public String get(Message m, String key)             { throw new MailException.MetadataNotSupported(); }
+
+    // flags
+    public final boolean getFlag(Message m, int flags)   { return (flags(m) & flag) == flags; }
+    public final void    setFlag(Message m, int flag)   { flags(m, flags | flag); }
+    public final void    clearFlag(Message m, int flag) { flags(m, flags & ~flag); }
+    protected abstract void flags(Message m, int newFlags);  // set the flags for a given message
+    protected abstract int  flags(Message m);                // get the flags for a given message
+    public    abstract int  uid(Message m);                  // get the uid for a given message              (see IMAP RFC)
+    public    abstract int  num(Message m);                  // get the "message number" for a given message (see IMAP RFC)
+    public    abstract int  uidNext();                       // get the next uid to be assigned 
+    public    abstract int  uidValidity();                   // get the uid validity identifier              (see IMAP RFC)
+
+    // messages
+    public int     add(Message message)                      throws MailException { throw new Error("not implemented"); }
+    public int     delete(Message message)                   throws MailException { throw new Error("not implemented"); }
+    public void    query(Query q, Message.Visitor v)         throws MailException { throw new Error("not implemented"); }
+    public int     count(Query q)                            throws MailException { throw new Error("not implemented"); }
+    public void    move(Query q, Mailbox dest, boolean copy) throws MailException { throw new Error("not implemented"); }
+
+    // submailboxes
+    public void    rename(String newName)                    throws MailException { throw new Error("not implemented"); }
+    public void    destroy()                                 throws MailException { throw new Error("not implemented"); }
+    public Mailbox slash(String name, boolean create)        throws MailException { throw new Error("not implemented"); }
 
-    public int uidvalidity;
-    public int uidnext;
-
-    public void setDeleted(int i, boolean b) { }
-    public void setSeen(int i, boolean b) { }
-    public void setFlagged(int i, boolean b) { }
-    public void setDraft(int i, boolean b) { }
-    public void setAnswered(int i, boolean b) { }
-    public void setRecent(int i, boolean b) { }
-    public boolean deleted(int i) { return false; }
-    public boolean seen(int i) { return false; }
-    public boolean flagged(int i) { return false; }
-    public boolean draft(int i) { return false; }
-    public boolean answered(int i) { return false; }
-    public boolean recent(int i) { return false; }
-    public int uid(int i) { return -1; }
-    public int uid(Message m) { return -1; }
-
-    public void moveAllMessagesTo(Mailbox m) throws MailException { throw new Error("moveAllMessagesTo() not implemented"); }
-    public String getName() throws MailException { throw new Error("Mailbox.getName() not implemented"); }
-    public void rename(String newName) throws MailException { throw new Error("Mailbox.rename() not implemented"); }
-    public void destroy() throws MailException { throw new Error("Mailbox.destroy() not implemented"); }
-    public void accept(Message m) throws MailException { add(m); }
-    public Mailbox slash(String name) throws MailException { return slash(name, false); }
-    public Mailbox slash(String name, boolean create) throws MailException {
-        throw new Error(this.getClass().getName() + " does not support the slash() method"); }
-    public int[] list() { throw new Error(this.getClass().getName() + " does not support the list() method"); }
-    public int add(Message message) throws MailException {
-        throw new Error(this.getClass().getName() + " does not support the add() method"); }
-    public int delete(Message message) throws MailException {
-        throw new Error(this.getClass().getName() + " does not support the delete() method"); }
-    public Message get(int messagenum) throws MailException {
-        throw new Error(this.getClass().getName() + " does not support the get() method"); }
-    public Message[] query(int maxResults) {
-        throw new Error(this.getClass().getName() + " does not support the query() method"); }
-    /*
-    public static class Mailbox extends Mailbox {
-        String user;
-        private static Hashtable cache = new Hashtable();
-        public static Mailbox getForUser(String user) {
-            Mailbox ret = (Mailbox)cache.get(user);
-            if (ret == null) ret = new Mailbox(user);
-            return ret;
-        }
-        Mailbox(String user) { this.user = user; }
-        public Mailbox slash(String name) throws MailException {
-            throw new Error(this.getClass().getName() + " does not support the slash() method"); }
-        public synchronized int add(Message message) throws MailException {
-            FileOutputStream fos = new FileOutputStream("/var/mail/" + user, true);
-            PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos));
-            pw.println("From " + message.envelopeFrom);
-            pw.flush();
-            message.dump(fos);
-            fos.close();
-            return -1;
-        }
-    }
-    */
     /** a fast-write, slow-read place to stash all messages we touch -- in case of a major f*ckup */
     public static class Transcript extends Mailbox {
         private String path;
@@ -179,4 +134,40 @@ public class Mailbox extends Target {
 
     }
 
+
+    String user;
+    private static Hashtable cache = new Hashtable();
+    public static Mailbox getForUser(String user) {
+        Mailbox ret = (Mailbox)cache.get(user);
+        if (ret == null) ret = new Mailbox(user);
+        return ret;
+    }
+    Mailbox(String user) { this.user = user; }
+    public Mailbox slash(String name) throws MailException {
+        throw new Error(this.getClass().getName() + " does not support the slash() method"); }
+    public synchronized int add(Message message) throws MailException {
+        FileOutputStream fos = new FileOutputStream("/var/mail/" + user, true);
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos));
+        pw.println("From " + message.envelopeFrom);
+        pw.flush();
+        message.dump(fos);
+        fos.close();
+        return -1;
+    }
+
+
+    private static final String STORAGE_ROOT =
+        System.getProperty("ibex.mail.root", File.separatorChar + "var" + File.separatorChar + "org.ibex.mail");
+    public static FileBased root = null;
+    public static Transcript transcript = null;
+    static {
+        try {
+            root = new FileBased(STORAGE_ROOT + File.separatorChar);
+            transcript = new Transcript(STORAGE_ROOT + File.separatorChar + "transcript");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public final void    accept(Message m) throws MailException { add(m); }
 }