From bec53dd0b091f3e94643fb9918e8d68c9fff98a5 Mon Sep 17 00:00:00 2001 From: adam Date: Mon, 31 May 2004 07:31:55 +0000 Subject: [PATCH] almost there darcs-hash:20040531073155-5007d-0ae88c44a9a321d3bb79647ab22056ab610e7d09.gz --- src/org/ibex/mail/Message.java | 44 ++------- src/org/ibex/mail/Query.java | 7 +- src/org/ibex/mail/protocol/IMAP.java | 28 +++--- src/org/ibex/mail/target/Mailbox.java | 167 ++++++--------------------------- 4 files changed, 52 insertions(+), 194 deletions(-) diff --git a/src/org/ibex/mail/Message.java b/src/org/ibex/mail/Message.java index 9e2105b..d492ed1 100644 --- a/src/org/ibex/mail/Message.java +++ b/src/org/ibex/mail/Message.java @@ -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! } diff --git a/src/org/ibex/mail/Query.java b/src/org/ibex/mail/Query.java index 45b5659..bc8495c 100644 --- a/src/org/ibex/mail/Query.java +++ b/src/org/ibex/mail/Query.java @@ -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"); } } diff --git a/src/org/ibex/mail/protocol/IMAP.java b/src/org/ibex/mail/protocol/IMAP.java index c8fd904..0db26a2 100644 --- a/src/org/ibex/mail/protocol/IMAP.java +++ b/src/org/ibex/mail/protocol/IMAP.java @@ -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; } diff --git a/src/org/ibex/mail/target/Mailbox.java b/src/org/ibex/mail/target/Mailbox.java index a7f32d5..5ff7a8b 100644 --- a/src/org/ibex/mail/target/Mailbox.java +++ b/src/org/ibex/mail/target/Mailbox.java @@ -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