--- /dev/null
+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;
+}
--- /dev/null
+package org.ibex.mail;
+
+// FEATURE: MIME RFC2045, 2046, 2049
+
+/** This class contains logic for encoding and decoding MIME multipart messages */
+public class MIME {
+}
// 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());
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");
+ }
}
}
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;
}
+
+ 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); }
}