almost there
authoradam <adam@megacz.com>
Mon, 31 May 2004 07:31:55 +0000 (07:31 +0000)
committeradam <adam@megacz.com>
Mon, 31 May 2004 07:31:55 +0000 (07:31 +0000)
darcs-hash:20040531073155-5007d-0ae88c44a9a321d3bb79647ab22056ab610e7d09.gz

src/org/ibex/mail/Message.java
src/org/ibex/mail/Query.java
src/org/ibex/mail/protocol/IMAP.java
src/org/ibex/mail/target/Mailbox.java

index 9e2105b..d492ed1 100644 (file)
@@ -26,13 +26,13 @@ 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());
             return super.get(o);
         }
-        public Object put(Object k, Object v) {
+        public Object put(Object k, Object v) { throw new Error("you cannot write to a CaseInsensitiveHash"); }
+        void add(Object k, Object v) {
             if (k instanceof String) return super.put(((String)k).toLowerCase(), v);
             else return super.put(k, v);
         }
@@ -70,35 +70,6 @@ public class Message extends JSReflection {
         w.write(body);
         w.flush();
     }
-    /*
-    public static class Persistent {
-        public boolean deleted = false;
-        public boolean seen = false;
-        public boolean flagged = false;
-        public boolean draft = false;
-        public boolean answered = false;
-        public boolean recent = false;
-        public int uid = 0;
-    }
-    */
-        /*
-    public static class StoredMessage extends Message {
-        public int uid;
-        public final int uid;
-        public final int uidValidity;     // must be reset if mailbox is deleted and then recreated
-        public final int sequenceNumber;  // starts at 1; numbers reshuffled upon EXPUNGE
-        public Hash keywords = new Hash();  
-        public boolean deleted = false;
-        public boolean seen = false;
-        public boolean flagged = false;
-        public boolean draft = false;
-        public boolean answered = false;
-        public boolean recent = false;    // "Message is "recently" arrived in this mailbox.  This
-                                          // session is the first session to have been notified
-                                          // about this message;
-        public StoredMessage(LineReader rs) throws IOException, MailException.Malformed { super(rs); }
-    }
-        */
 
     public class Trace {
         final String returnPath;
@@ -155,7 +126,7 @@ public class Message extends JSReflection {
             all.append("\r\n");
             if (s.length() == 0 || Character.isSpace(s.charAt(0))) {
                 if (key == null) throw new Malformed("Message began with a blank line; no headers");
-                headers.put(key, headers.get(key) + s);
+                headers.add(key, headers.get(key) + s);
                 continue;
             }
             if (s.indexOf(':') == -1) throw new Malformed("Header line does not contain colon: " + s);
@@ -173,7 +144,7 @@ public class Message extends JSReflection {
             } else {
                 // just append it to the previous one; valid for Comments/Keywords
                 if (headers.get(key) != null) val = headers.get(key) + " " + val;
-                headers.put(key, val);
+                headers.add(key, val);
             }            
         }
 
@@ -229,9 +200,6 @@ public class Message extends JSReflection {
             "        MessageId: " + messageid;
     }
 
-    public Message bounce(String reason) {
-        //  use null-sender for error messages (don't send errors to the null addr)
-        // FIXME
-        throw new RuntimeException("bounce not implemented");
-    }
+    //  use null-sender for error messages (don't send errors to the null addr)
+    public Message bounce(String reason) { throw new RuntimeException("bounce not implemented"); }  // FIXME!
 }
index 45b5659..bc8495c 100644 (file)
@@ -25,8 +25,9 @@ public class Query {
     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); }
+    public static Query body(String text)                   { return new Query(BODY, null, 0, 0, 0, null, text, null, null); }
+    public static Query body(String text)                   { return new Query(FULL, null, 0, 0, 0, null, text, null, 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;
@@ -44,6 +45,7 @@ public class Query {
     public static int SIZE       = 9;
     public static int FLAGS      = 10;
     public static int BODY       = 11;
+    public static int FULL       = 12;
 
     public final int type;
     public final Query[] q;
@@ -68,7 +70,8 @@ public class Query {
             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;
+            case BODY:       return m.body.indexOf(text) != -1;
+            case FULL:       return m.body.indexOf(text) != -1 || m.allHeaders.indexOf(text) != -1;
             default:         throw new Error("this should not happen");
         }
     }
index c8fd904..0db26a2 100644 (file)
@@ -310,13 +310,13 @@ public class IMAP extends MessageProtocol {
                 break;
             }
             if (s.startsWith("UN"))        { not = true; s = s.substring(2); }
-            if (s.equals("ANSWERED"))        q = Query.Flag.ANSWERED;
-            else if (s.equals("DELETED"))    q = Query.Flag.DELETED;
-            else if (s.equals("DRAFT"))      q = Query.Flag.DRAFT;
-            else if (s.equals("FLAGGED"))    q = Query.Flag.FLAGGED;
-            else if (s.equals("RECENT"))     q = Query.Flag.RECENT;
-            else if (s.equals("SEEN"))       q = Query.Flag.SEEN;
-            else if (s.equals("OLD"))      { not = true; q = Query.Flag.RECENT; }
+            if (s.equals("ANSWERED"))        q = Query.flags(Flag.ANSWERED);
+            else if (s.equals("DELETED"))    q = Query.flags(Flag.DELETED);
+            else if (s.equals("DRAFT"))      q = Query.flags(Flag.DRAFT);
+            else if (s.equals("FLAGGED"))    q = Query.flags(Flag.FLAGGED);
+            else if (s.equals("RECENT"))     q = Query.flags(Flag.RECENT);
+            else if (s.equals("SEEN"))       q = Query.flags(Flag.SEEN);
+            else if (s.equals("OLD"))      { not = true; q = Query.flags(Flag.RECENT); }
             else if (s.equals("NEW"))        q = Query.and(Query.Flag.RECENT, Query.not(Query.Flag.SEEN));
             else if (s.equals("KEYWORD"))    q = Query.header("keyword", flag());
             else if (s.equals("HEADER"))     q = Query.header(astring(), astring());
@@ -327,14 +327,14 @@ public class IMAP extends MessageProtocol {
             else if (s.equals("SUBJECT"))    q = Query.header("subject", astring());
             else if (s.equals("LARGER"))     q = Query.size(n(), true);
             else if (s.equals("SMALLER"))    q = Query.size(n(), false);
-            else if (s.equals("BODY"))       q = Query.fullText(astring(), true, false);
+            else if (s.equals("BODY"))       q = Query.body(astring(), true, false);
             else if (s.equals("TEXT"))       q = Query.fullText(astring(), true, true);
-            else if (s.equals("BEFORE"))     q = Query.arrival(date(), true, false);
-            else if (s.equals("SINCE"))      q = Query.arrival(date(), false, true);
-            else if (s.equals("ON"))         q = Query.arrival(date(), true, true);
-            else if (s.equals("SENTBEFORE")) q = Query.sent(date(), true, false);
-            else if (s.equals("SENTSINCE"))  q = Query.sent(date(), false, true);
-            else if (s.equals("SENTON"))     q = Query.sent(date(), true, true);
+            else if (s.equals("BEFORE"))     q = Query.arrival(new Date(0), date());
+            else if (s.equals("SINCE"))      q = Query.arrival(date(), new Date(Long.MAX_VALUE));
+            else if (s.equals("ON"))         q = null; // FIXME
+            else if (s.equals("SENTBEFORE")) q = Query.sent(new Date(0), date());
+            else if (s.equals("SENTSINCE"))  q = Query.sent(date(), new Date(Long.MAX_VALUE));
+            else if (s.equals("SENTON"))     q = null; // FIXME
             else if (s.equals("UID"))        q = Query.uid(set());
             return q;
         }
index a7f32d5..5ff7a8b 100644 (file)
@@ -7,7 +7,20 @@ import java.net.*;
 import java.util.*;
 import java.text.*;
 
-public class Mailbox extends Target {
+public abstract 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(); }
@@ -25,148 +38,22 @@ public class Mailbox extends Target {
     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"); }
+    public abstract void visit(Message.Visitor v) throws MailException;   // only abstract method
+    public int  add(Message message)                      throws MailException { throw new MailException("not implemented"); }
+    public int  delete(Message message)                   throws MailException { throw new MailException("not implemented"); }
+    public void move(Query q, Mailbox dest, boolean copy) throws MailException { throw new MailException("not implemented"); }
+    public int  count(Query q) throws MailException { CounterVisitor cv = new CounterVisitor(this,q); visit(cv); return cv.count; }
+    public void query(Query q, final Message.Visitor v) throws MailException {
+        visit(new Message.Visitor() { public void visit(Message m) { if (q.match(Mailbox.this,m)) v.visit(Mailbox.this,m); } }); } 
 
     // 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"); }
-
-    /** 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;
-        public Transcript(String path) throws MailException { new File(this.path = path).mkdirs(); }
-        private static String lastTime = null;
-        private static int lastCounter = 0;
-
-        /** returns a message identifier */
-        public synchronized int add(Message message) throws MailException {
-            try {
-                File today = new File(path + File.separatorChar + (new SimpleDateFormat("yy-MMM-dd").format(new Date())));
-                today.mkdirs();
-                
-                String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
-                synchronized (Transcript.class) {
-                    if (lastTime != null && lastTime.equals(time)) {
-                        time += "." + (++lastCounter);
-                    } else {
-                        lastTime = time;
-                    }
-                }
-                
-                File target = new File(today.getPath() + File.separatorChar + time + ".txt");
-                OutputStream os = new FileOutputStream(target);
-                message.dump(os);
-                os.close();
-                return -1;
-            } catch (IOException e) { throw new MailException.IOException(e); }
-        }
-    }
-
-    public static class FileBased extends Mailbox {
-        private String path;
-        private FileBased(String path) throws MailException { new File(this.path = path).mkdirs(); }
-        public Mailbox slash(String name, boolean create) throws MailException {
-            // FIXME: create
-            return new FileBased(path + "/" + name);
-        }
-
-        public int[] list() {
-            String[] names = new File(path).list();
-            int[] ret = new int[names.length];
-            for(int i=0, j=0; j<ret.length; i++, j++) {
-                try {
-                    ret[j] = Integer.parseInt(names[i].substring(0, names[i].length() - 1));
-                } catch (NumberFormatException nfe) {
-                    Log.warn(FileBased.class, "NumberFormatException: " + names[i].substring(0, names[i].length() - 1));
-                    j--;
-                    int[] newret = new int[ret.length - 1];
-                    System.arraycopy(ret, 0, newret, 0, newret.length);
-                    ret = newret;
-                }
-            }
-            return ret;
-        }
-
-        /** returns a message identifier */
-        public synchronized int add(Message message) throws MailException {
-            try {
-                int[] all = list();
-                int max = 0;
-                for(int i=0; i<all.length; i++) max = Math.max(max, all[i]);
-                int target = max++;
-                File f = new File(path + File.separatorChar + max + ".-");
-                FileOutputStream fo = new FileOutputStream(f);
-                message.dump(fo);
-                fo.close();
-                f.renameTo(new File(path + File.separatorChar + max + "."));
-                return target;
-            } catch (IOException e) { throw new MailException.IOException(e); }
-        }
+    public void    rename(String newName) throws MailException { throw new MailException("you cannot rename this mailbox"); }
+    public void    destroy() throws MailException { throw new MailException("you cannot destroy this mailbox"); }
+    public Mailbox slash(String name, boolean create) throws MailException { throw new MailException("no submailboxes"); }
 
-        public Message get(int messagenum) throws MailException {
-            File f = new File(path + File.separatorChar + messagenum + ".");        
-            if (!f.exists()) throw new MailException.IOException(new FileNotFoundException(f.toString()));
-            //try {
-                // FIXME: need to store envelope from/to
-                //Message ret = new Message(new LineReader(new InputStreamReader(new FileInputStream(f))));
-                // FIXME: set answered/read/etc here
-                //return ret;
-            //return null;
-                /*
-            } catch (MailException.Malformed malf) {
-                Log.error(this, "This should never happen");
-                Log.error(this, malf);
-                return null;
-            }
-                */
-            return null;
-        }
-
-        // query types: stringmatch (headers, body), header element, deletion status, date range, message size
-        public Message[] query(int maxResults) {
-            throw new RuntimeException("FileBased.query() not implemented yet");
-        }
-
-    }
-
-
-    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();
-        }
+    private static class CounterVisitor implements Message.Visitor {
+        public int count = 0;
+        public void visit(Message m) { if (q.match(Mailbox.this, m)) count++; }
     }
 
     public final void    accept(Message m) throws MailException { add(m); }