checkpoint imap support
authoradam <adam@megacz.com>
Sun, 9 May 2004 05:32:30 +0000 (05:32 +0000)
committeradam <adam@megacz.com>
Sun, 9 May 2004 05:32:30 +0000 (05:32 +0000)
darcs-hash:20040509053230-5007d-03c154f72b37e6008e966f09ff303dc54ac852cd.gz

src/org/ibex/mail/Message.java
src/org/ibex/mail/protocol/IMAP.java
src/org/ibex/mail/protocol/Incoming.java
src/org/ibex/mail/protocol/SMTP.java
src/org/ibex/mail/target/Mailbox.java [moved from src/org/ibex/mail/target/FileSystem.java with 93% similarity]
src/org/ibex/mail/target/Script.java

index 0e0b13a..89009fe 100644 (file)
@@ -37,7 +37,7 @@ public class Message extends JSReflection {
     public final Address envelopeFrom;
     public final Address[] envelopeTo;
 
-    public final Date arrival;         // when the message first arrived at this machine
+    public final Date arrival;         // when the message first arrived at this machine; IMAP "internal date message attr"
     
     public void dump(OutputStream os) throws IOException {
         Writer w = new OutputStreamWriter(os);
@@ -52,9 +52,18 @@ public class Message extends JSReflection {
         /*
     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 read = 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); }
     }
         */
index fc72869..504e6a8 100644 (file)
@@ -2,9 +2,8 @@ package org.ibex.mail.protocol;
 import java.net.*;
 import java.io.*;
 
-class IMAPException extends IOException {
-}
-
+// FEATURE: pipelining
+// FEATURE: support [charset]
 public class IMAP extends MessageProtocol {
 
     public static void main(String[] args) throws Exception {
@@ -25,6 +24,461 @@ public class IMAP extends MessageProtocol {
         }
     }
 
-    public static void service(Socket s) throws IOException {
+    private static class Listener extends Incoming {
+        Socket conn;
+        String vhost;
+        Mailbox selected = null;
+        public void init() { }
+        public Listener(Socket conn, String vhost) { this.vhost = vhost; this.conn = conn; this.selected = null; }
+
+        public void login(String user, String password) { if (!auth(user, password)) throw new No("Liar, liar, pants on fire."); }
+        public void capability() { star("CAPABILITY IMAP4rev1"); ok("Completed"); }
+        public void noop() { ok("Completed"); }
+        public void logout() { star("BYE LOGOUT received"); ok("Completed"); }
+        public void delete(Mailbox m) { if (!m.getName().toLowerCase().equals("inbox")) m.delete(); }
+        public void subscribe(String[] args) { ok("SUBSCRIBE ignored"); }
+        public void unsubscribe(String[] args) { ok("UNSUBSCRIBE ignored"); }
+        public void check(String[] args, boolean examineOnly) { ok("check complete"); }
+        public void lsub(String[] args) { list(args); }
+        public void list(String[] args) { star("LIST () \".\" INBOX"); ok("LIST completed"); }
+        public void create(String mailbox) {
+            if (mailbox.charAt(mailbox.length() - 1) != '.' &&
+                !mailbox.toLowerCase.equals("inbox"))
+                if (!storage.create(mailbox))
+                    throw No("unable to CREATE mailbox");
+            ok("CREATE completed");
+        }
+        public void rename(Mailbox from, String to) {
+            if (from.getName().toLowerCase().equals("inbox")) {
+                int[] messages = from.list();
+                Malbox toBox = getMailbox(to);
+                for(int i=0; i<messages.length; i++) {
+                    Message m = from.get(messages[i]);
+                    toBox.add(m);
+                    from.delete(messages[i]);
+                }
+            } else if (to.toLowerCase().equals("inbox")) {
+                int[] messages = from.list();
+                Mailbox inbox = getMailbox("inbox");
+                for(int i=0; i<messages.length; i++) {
+                    Message m = from.get(messages[i]);
+                    inbox.add(m);
+                    from.delete(messages[i]);
+                }
+                // FIXME delete from
+            } else {
+                from.rename(to);
+            }
+        }
+
+        public void status(Mailbox mbox, boolean messages, boolean recent, boolean uidnext, boolean uidvalidity, boolean unseen) {
+            for(int i=0; i<toks.length; i++) {
+                if (toks[i].equals("MESSAGES")) messages = true;
+                else if (toks[i].equals("RECENT")) recent = true;
+                else if (toks[i].equals("UIDNEXT")) uidnext = true;
+                else if (toks[i].equals("UIDVALIDITY")) uidvalidity = true;
+                else if (toks[i].equals("UNSEEN")) unseen = true;
+            }
+            int[] messages = mbox.list();
+            int numRecent = 0, numUnseen = 0;
+            for(int i=0; i<messages.length; i++) {
+                Message m = mbox.get(messages[i]);
+                if (!m.seen) numUnseen++;
+                if (m.recent) numRecent++;
+            }
+            star("STATUS " + args[0] + " (" +
+                 (messages ? ("MESSAGES " + messages.length + " ") : "") +
+                 (recent ? ("RECENT " + numRecent + " ") : "") +
+                 (unseen ? ("UNSEEN " + numUnseen + " ") : "") +
+                 (uidvalidity ? ("UIDVALIDITY " + mbox.uidvalidity + " ") : "") +
+                 (uidnext ? ("UIDNEXT " + mbox.uidnext + " ") : "") + ")");
+        }
+
+        public void select(String[] args, boolean examineOnly) {
+            if (args.length < 1) throw new Bad("Not enough arguments");
+            selected = geMailbox(args[0]);
+            if (selected == null) throw new No("No such mailbox");
+            star("EXISTS");
+            star("1 RECENT");
+            star("OK [UNSEEN 12] Message 12 is first unseen");
+            star("OK [UIDVALIDITY 123123123] UIDs valid");
+            star("FLAGS (\Answered \Flagged \Deleted \Seen \Draft)");
+            ok("[READ-WRITE] " + (examineOnly ? "EXAMINE" : "SELECT") + " completed");
+        }
+        public void close(String[] args, boolean examineOnly) {
+            if (selected == null) throw new Bad("no mailbox selected");
+            expunge(new String[] { }, false, true);
+            selected = null;
+            ok("CLOSE completed");
+        }
+        public void expunge(String[] args, boolean examineOnly, boolean silent) {
+            if (selected == null) throw new Bad("no mailbox selected");
+            int[] messages = selected.list();
+            for(int i=0; i<messages.length; i++) {
+                Message m = selected.get(messages[i]);
+                if (m.deleted) {
+                    if (!silent) star(m.uid + " EXPUNGE");
+                    if (!examineOnly) selected.delete(i);
+                }
+            }
+            if (!silent) ok("EXPUNGE completed");
+        }
+
+        public void fetch(int[] messageSet, boolean examineOnly, boolean uid) {
+            if (selected == null) throw new Bad("no mailbox selected");
+            Object[] tokens = tokenize(args[0]);
+            String[] headers;
+            boolean negateHeaders;
+            boolean flags;
+            boolean internalDate;
+            boolean size;
+            boolean uid;
+            int start, end;
+            boolean setSeen;
+            for(int i=0; i<tokens.length; i++) {
+                
+            }
+            for(int i=0; i<messageSet.length; i++) {
+                Message m = uid ? selected.getUID(messageSet[i]) : selected.get(messageSet[i]);
+                fetch(m, 
+                 String[] headers,
+                 boolean negateHeaders,
+                 boolean flags,
+                 boolean internalDate,
+                 boolean size,
+                 boolean uid,
+                 int start, int end,
+                      boolean setSeen);
+                // FIXME
+            }
+        }
+
+        public void append(Mailbox m, Token flags, Date arrived, String literal) {
+            Message m = new Message(null, null, new StringReader(literal));
+            if (flags != null) flags.setFlags(m);
+            if (arrived != null) m.arrival = arrived;
+            selected.add(m);
+        }
+
+        public void copy(int[] set, Mailbox target) {
+            for(int i=0; i<set.length; i++) {
+                Message m = selected.get(set[i]);
+                target.add(m);  // sharing problem?  immutability helps
+            }
+        }
+
+        public boolean handleRequest(LineReader r, PrintWriter pw) {
+            pw.println("* OK " + vhost + " " + IMAP.class.getName() + " IMAP4 v0.1 server ready");
+            while(true) {
+                boolean uid = false;
+                String s = r.readLine();
+                if (s.indexOf(' ') == -1) { pw.println("* BAD Invalid tag"); continue; }
+                String tag = atom();
+                String command = atom();
+                if (command.equals("UID")) { uid = true; command = atom(); }
+                if (command.equals("AUTHENTICATE")) { authenticate(args); }
+                else if (command.equals("LIST")) list(mailbox(), mailboxPattern()); 
+                else if (command.equals("LSUB")) lsub(mailbox(), mailboxPattern()); 
+                else if (command.equals("CAPABILITY")) { capability(); }
+                else if (command.equals("LOGIN")) login(astring(), astring());
+                else if (command.equals("LOGOUT")) { logout(); conn.close(); return false; }
+                else if (command.equals("RENAME")) rename(mailbox(), atom());
+                else if (command.equals("APPEND")) {
+                    Mailbox m = mailbox();
+                    Token t = token();
+                    Token[] flags = null;
+                    Date arrival = null;
+                    if (t.type == LIST) { flags = t.l(); t = token(); }
+                    if (t.type == QUOTED) arrival = datetime();
+                    append(m, flags, arrival, literal());
+                } else if (command.equals("EXAMINE")) examine(mailbox());
+                else if (command.equals("COPY")) copy(set(), mailbox());
+                else if (command.equals("DELETE")) delete(mailbox());
+                else if (command.equals("CREATE")) create(mailbox());
+                else if (command.equals("STORE")) {
+                    int[] messages = set();
+                    String what = atom();
+                    Token[] flags = l();
+                    for(int i=0; i<messages.length; i++) {
+                        Message m = selected.get(messages[i]);
+                        if (what.charAt(0) == 'F') m.deleted = m.seen = m.flagged = m.draft = m.answered = m.recent = false;
+                        for(int i=0; i<flags.length; i++) {
+                            String flag = flags[i].flag();
+                            if (flag.equals("Deleted"))  m.deleted = what.charAt(0) != '-';
+                            if (flag.equals("Seen"))     m.seen = what.charAt(0) != '-';
+                            if (flag.equals("Flagged"))  m.flagged = what.charAt(0) != '-';
+                            if (flag.equals("Draft"))    m.draft = what.charAt(0) != '-';
+                            if (flag.equals("Answered")) m.answered = what.charAt(0) != '-';
+                            if (flag.equals("Recent"))   m.recent = what.charAt(0) != '-';
+                        }
+                        selected.add(m);  // re-add
+                    }
+                } else if (command.equals("FETCH")) {
+                    Set s = set();
+                    Token t = token();
+                    Token[] tl = null;
+                    boolean envelope = false, internaldate = false, size = false, flags = false, uid=false;
+                    if (t.type == Token.LIST) tl = t.l();
+                    else if (t.atom().equals("FULL")) { flags=true; internaldate=true; size=true; envelope=true; body=true; }
+                    else if (t.atom().equals("ALL"))  { flags=true; internaldate=true; size=true; envelope=true; }
+                    else if (t.atom().equals("FAST")) { flags=true; internaldate=true; size=true; }
+                    else tl = new Token[] { t };
+                    else throw new Bad("expected atom or list");
+                    for (int i=0; i<tl.length; i++) {
+                        String s = tl[i].atom();
+                        if (s.equals("1")) s = "RFC822";
+                        else if (s.startsWith("1.")) s = s.substring(2);
+                        if (s.equals("ENVELOPE")) envelope = true;
+                        else if (s.equals("FLAGS")) flags = true;
+                        else if (s.equals("INTERNALDATE")) internaldate = true;
+                        else if (s.equals("RFC822")) body = true; // FIXME: special return syntax?
+                        else if (s.equals("RFC822.HEADER")) header = true;
+                        else if (s.equals("RFC822.SIZE")) size = true;
+                        else if (s.equals("RFC822.TEXT")) body = true;
+                        else if (s.equals("BODY")) body = true;
+                        else if (s.equals("BODYSTRUCTURE")) throw new No("FETCH BODYSTRUCTURE not supported");
+                        else if (s.equals("UID")) uid = true;
+                        else if (s.startsWith("BODY")) throw new No("FETCH BODY[*] not supported");
+                        else throw new Bad("unrecognized FETCH argument \"" + s + "\"");
+                    }
+                    fetch(selected, null, false, flags, internaldate, size, uid, start, end, setseen, envelope);
+                } else if (command.equals("STATUS")) {
+                    Mailbox m = mailbox();
+                    Token[] attrs = l();
+                    boolean messages = false;
+                    boolean recent = false;
+                    boolean uidnext = false;
+                    boolean uidvalidity = false;
+                    boolean unseen = false;
+                    for(int i=0; i<attrs.length; i++) {
+                        String s = attrs[i].atom();
+                        if (s.equals("MESSAGES")) messages = true;
+                        if (s.equals("RECENT")) recent = true;
+                        if (s.equals("UIDNEXT")) uidnext = true;
+                        if (s.equals("UIDVALIDITY")) uidvalidity = true;
+                        if (s.equals("UNSEEN")) unseen = true;
+                    }
+                    status(mbox, messages, recent, uidnext, uidvalidity, unseen);
+                } else {
+                    throw new Bad("unrecognized command \"" + command + "\"");
+                }
+            }
+        }
+    }
+
+    public static String quotify(String s){return s==null?"NIL":"\""+s.replaceAll("\\\\", "\\\\").replaceAll("\"", "\\\\\"")+"\"";}
+    public static String address(Address a) { return "("+quotify(a.desciption)+" NIL "+quotify(a.user)+" "+quotify(a.host)+")"; }
+    public static String addressList(Object a) {
+        if (a == null) return "NIL";
+        if (a instanceof Address) return "("+address((Address)a)+")";
+        Address[] aa = (Address[])a;
+        StringBuffer ret = new StringBuffer();
+        ret.append("(");
+        for(int i=0; i<aa.length; i++) { ret.append(aa[i]); if (i < aa.length - 1) ret.append(" "); }
+        ret.append(")");
+        return ret.toString();
+    }
+        
+    public static String envelope(Message m) {
+        return
+            "(" + quotify(m.arrival.toString()) +
+            " " + quotify(m.subject) +          
+            " " + addressList(m.from) +      
+            " " + addressList(m.sender) +
+            " " + addressList(m.replyTo) + 
+            " " + addressList(m.to) + 
+            " " + addressList(m.cc) + 
+            " " + addressList(m.bcc) + 
+            " " + quotify(m.headers.get("in-reply-to")) +
+            " " + quotify(m.messageId) +
+            ")";
+    }
+    
+    public static void fetch(Message m, String[] headers, boolean negateHeaders, boolean flags, boolean internalDate,
+                             boolean size, boolean uid, int start, int end, boolean setSeen, boolean envelope) {
+        String reply = "";
+        if (flags)
+            reply +=
+                "FLAGS ("
+                + m.deleted ? "\Deleted " : ""
+                + m.seen ? "\Seen " : ""
+                + m.flagged ? "\Flagged " : ""
+                + m.draft ? "\Draft " : ""
+                + m.answered ? "\Answered " : ""
+                + m.recent ? "\Recent " : ""
+                + ") ";
+        if (size) reply += "RFC822.SIZE " m.rfc822size() + " ";; 
+        if (bodyStructure)    // FIXME
+            reply += "(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" " + m.rfc822size() +" "+ m.numLines() +")";
+        if (envelope)
+            reply +=
+                "(" + quotify(m.date) +
+                " " + quotify(m.subject) +
+                " " + quotify(new Address[] { m.from }) + " " + 
+                " " + quotify(new Address[] { m.sender }) + " " + 
+                " " + quotify(new Address[] { m.replyTo }) + " " + 
+                " " + quotify(new Address[] { m.to }) + " " + 
+                " " + quotify(cc) + " " + 
+                " " + quotify(bcc) + " " + 
+                " " + quotify(m.headers.get("in-reply-to")) + " " + 
+                " " + quotify(m.messageId) +
+                ") ";
+        if (internaldate) reply += quotify(m.arrival) + " ";
+        // FIXME
+    }
+
+    public static Query parseQuery() {
+        String tok = null;
+        boolean not = false;
+        while(true) {
+            Token t = token();
+            if (t.type == t.LIST) { }
+            else if (t.type == t.SET) { }
+            tok = t.atom();
+            if (tok.equals("NOT")) { not = true; continue; }
+            if (tok.equals("OR")) { /* FIXME */ }
+            if (tok.equals("AND")) { /* FIXME */ }
+            break;
+        }
+        if (tok.startsWith("UN")) { not = true; tok = tok.substring(2); }
+        if (tok.equals("ANSWERED")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (tok.equals("DELETED")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (tok.equals("DRAFT")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (tok.equals("FLAGGED")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (tok.equals("RECENT")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (tok.equals("SEEN")) q = new Query.Flag(Query.Flag.ANSWERED);
+        else if (tok.equals("OLD")) { not = true; q = new Query.Flag(Query.Flag.RECENT); }
+        else if (tok.equals("NEW")) q = new Query.And(new Query.Flag(Query.Flag.RECENT),
+                                                      new Query.Not(new Query.Flag(Query.Flag.SEEN)));
+        else if (tok.equals("KEYWORD")) q = new Query.Header("keyword", flag());
+        else if (tok.equals("HEADER")) q = new Query.Header(astring(), astring());
+        else if (tok.equals("BCC")) q = new Query.Header("bcc", astring());
+        else if (tok.equals("CC")) q = new Query.Header("cc", astring());
+        else if (tok.equals("FROM")) q = new Query.Header("from", astring());
+        else if (tok.equals("TO")) q = new Query.Header("to", astring());
+        else if (tok.equals("SUBJECT")) q = new Query.Header("subject", astring());
+        else if (tok.equals("BEFORE")) q = new Query.Arrival(date(), true, false);
+        else if (tok.equals("SINCE")) q = new Query.Arrival(date(), false, true);
+        else if (tok.equals("ON")) q = new Query.Arrival(date(), true, true);
+        else if (tok.equals("LARGER")) q = new Query.Size(n(), true);
+        else if (tok.equals("SMALLER")) q = new Query.Size(n(), false);
+        else if (tok.equals("BODY")) q = new Query.FullText(astring(), true, false);
+        else if (tok.equals("TEXT")) q = new Query.FullText(astring(), true, true);
+        else if (tok.equals("SENTBEFORE")) { /* FIXME date() */ }
+        else if (tok.equals("SENTSINCE"))  { /* FIXME date() */ }
+        else if (tok.equals("SENTON"))     { /* FIXME date() */ }
+        else if (tok.equals("UID"))        { /* FIXME set() */ }
+    }
+
+    private static class Token {
+        private byte type;
+        private final String s;
+        private final Token[] l;
+        private final int n;
+        private static final byte NIL = 0;
+        private static final byte LIST = 1;
+        private static final byte QUOTED = 2;
+        private static final byte NUMBER = 3;
+        public Token() { n = 0; list = null; s = null; type = NIL; }
+        public Token(String quoted) { s = quoted; l = null; type = QUOTED; n = 0; }
+        public Token(Token[] list) { l = list; s = null; type = LIST; n = 0; }
+        public Token(int number) { n = number; list = null; s = null; type = NUMBER; }
+
+        // assumes token is a flag list or a fag
+        public void setFlags(Message m) { }
+        public void addFlags(Message m) { }
+        public void deleteFlags(Message m) { }
+
+        public String mailboxPattern() {
+            /* 1*(ATOM_CHAR / "%" / "*") / string */
+        }
+        public String flag() throws Bad {
+            if (type != ATOM) throw new Bad("expected a flag");
+            return s;  // if first char != backslash, it is a keyword-flag
+        }
+        public int n() throws Bad {
+            if (type != NUMBER) throw new Bad("expected number");
+            return n;
+        }
+        public int nz() throws Bad {
+            if (type != NUMBER) throw new Bad("expected number");
+            if (n == 0) throw new Bad("expected nonzero number");
+            return n;
+        }
+        public String  q() throws Bad {
+            if (type == NIL) return null;
+            if (type != QUOTED) throw new Bad("expected qstring");
+            return s;
+        }
+        public String nstring() throws Bad {
+        }
+        public Token[] l() throws Bad {
+            if (type == NIL) return null;
+            if (type != LIST) throw new Bad("expected parenthesized list");
+            return l;
+        }
+        public int[] set() throws Bad {
+            // FIXME   <set>,<set>  <n>  <n>:<n>   *  (* = largest)
+        }
+        public Date date() throws Bad {
+            // may be quoted or unquoted
+            try {
+            new SimpleDateFormat("dd-MMM-yyyy").parse(s);
+            } catch (DateParsingException) {
+            }
+        }
+        public String nstring() throws Bad {
+            if (type == NIL) return null;
+            if (type == QUOTED) return s;
+            throw new Bad("expected NIL or string");
+        }
+        public String astring() throws Bad {
+            if (type == ATOM) return s;
+            if (type == QUOTED) return s;
+            throw new Bad("expected atom or string");
+        }
+        public String atom() throws Bad {
+            // ATOM_CHAR       ::= <any CHAR except atom_specials>
+            // atom_specials   ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials
+            if (type != ATOM) throw new Bad("expected atom");
+        }
+        public Mailbox mailbox() throws Bad {
+            if (type == BAREWORD && s.toLowerCase().equals("inbox")) return Mailbox.INBOX;
+            return Mailbox.getMailbox(astring());
+        }
+        public Date datetime() throws Bad {
+            // expected to be a quoted string
+            
+            new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss +zzzz").parse(s);
+        }
+
+    }
+
+
+
+    // literal         ::= "{" number "}" CRLF *CHAR8     ;; Number represents the number of CHAR8 octets
+
+    public static Token[] tokenize(String s) {
+        Vec toks = new Vec();
+        StringBuffer sb = new StringBuffer();
+        for(int i=0; i<s.length();) {
+            sb.setSize(0);
+            if (s.charAt(i) == '\"') {
+                for(i++; i < s.length(); i++) {
+                    if (s.charAt(i) == '\\') sb.append(s.charAt(++i));
+                    else if (s.charAt(i) == '\"') { i++; break; }
+                    else sb.append(s.charAt(i));
+                }
+            } else if (s.charAt(i) == '(') {
+                // FIXME, deal with strings within parens
+            } else {
+                for(; i < s.length(); i++)
+                    if (s.charAt(i) == ' ' || s.charAt(i) == '\"') break;
+                    else sb.append(s.charAt(i));
+            }
+            toks.addElement(sb.toString());
+            while (i < s.length() && s.charAt(i) == ' ') i++;
+        }
+        return toks.toArray();
     }
 }
index 2fb68eb..0f3adcb 100644 (file)
@@ -5,7 +5,7 @@ import java.io.*;
 
 public class Incoming {
     protected void accept(Message m) throws IOException, MailException {
-        FileSystem.transcript.add(m);     // currently, we write all inbound messages to the transcript
+        Mailbox.transcript.add(m);     // currently, we write all inbound messages to the transcript
         Target.root.accept(m);
     }
 }
index dad7a06..63563e0 100644 (file)
@@ -48,7 +48,7 @@ public class SMTP extends MessageProtocol {
                 return;
             }
             synchronized(Outgoing.class) {
-                FileSystem.root.slash("outgoing").add(m);
+                Mailbox.root.slash("outgoing").add(m);
                 queue.append(m);
                 Outgoing.class.notify();
             }
@@ -96,7 +96,7 @@ public class SMTP extends MessageProtocol {
                 w.print(".\r\n");
                 check(r.readLine());
                 Log.info(SMTP.Outgoing.class, "message accepted by " + mx);
-                FileSystem.root.slash("outgoing").delete(m);
+                Mailbox.root.slash("outgoing").delete(m);
                 s.close();
                 return true;
             } catch (Exception e) {
@@ -111,9 +111,9 @@ public class SMTP extends MessageProtocol {
         static void runq() {
             try {
                 Log.setThreadAnnotation("[outgoing smtp] ");
-                int[] outgoing = FileSystem.root.slash("outgoing").list();
+                int[] outgoing = Mailbox.root.slash("outgoing").list();
                 Log.info(SMTP.Outgoing.class, "outgoing thread started; " + outgoing.length + " messages to send");
-                for(int i=0; i<outgoing.length; i++) queue.append(FileSystem.root.slash("outgoing").get(outgoing[i]));
+                for(int i=0; i<outgoing.length; i++) queue.append(Mailbox.root.slash("outgoing").get(outgoing[i]));
                 while(true) {
                     int num = queue.size();
                     for(int i=0; i<num; i++) {
similarity index 93%
rename from src/org/ibex/mail/target/FileSystem.java
rename to src/org/ibex/mail/target/Mailbox.java
index 8be7d8f..8869bca 100644 (file)
@@ -8,7 +8,7 @@ import java.util.*;
 import java.text.*;
 
 // FIXME: appallingly inefficient
-public class FileSystem extends Target {
+public class Mailbox extends Target {
 
     private static final String STORAGE_ROOT =
         System.getProperty("ibex.mail.root", File.separatorChar + "var" + File.separatorChar + "org.ibex.mail");
@@ -24,7 +24,7 @@ public class FileSystem extends Target {
     }
 
     public void accept(Message m) throws IOException { add(m); }
-    public FileSystem slash(String name) throws IOException {
+    public Mailbox slash(String name) throws IOException {
         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 IOException {
@@ -36,7 +36,7 @@ public class FileSystem extends Target {
     public Message[] query(int maxResults) {
         throw new Error(this.getClass().getName() + " does not support the query() method"); }
 
-    public static class Mailbox extends FileSystem {
+    public static class Mailbox extends Mailbox {
         String user;
         private static Hashtable cache = new Hashtable();
         public static Mailbox getForUser(String user) {
@@ -45,7 +45,7 @@ public class FileSystem extends Target {
             return ret;
         }
         Mailbox(String user) { this.user = user; }
-        public FileSystem slash(String name) throws IOException {
+        public Mailbox slash(String name) throws IOException {
             throw new Error(this.getClass().getName() + " does not support the slash() method"); }
         public synchronized int add(Message message) throws IOException {
             FileOutputStream fos = new FileOutputStream("/var/mail/" + user, true);
@@ -59,7 +59,7 @@ public class FileSystem extends Target {
     }
 
     /** a fast-write, slow-read place to stash all messages we touch -- in case of a major f*ckup */
-    public static class Transcript extends FileSystem {
+    public static class Transcript extends Mailbox {
         private String path;
         public Transcript(String path) throws IOException { new File(this.path = path).mkdirs(); }
         private static String lastTime = null;
@@ -87,10 +87,10 @@ public class FileSystem extends Target {
         }
     }
 
-    public static class FileBased extends FileSystem {
+    public static class FileBased extends Mailbox {
         private String path;
         private FileBased(String path) throws IOException { new File(this.path = path).mkdirs(); }
-        public FileSystem slash(String name) throws IOException { return new FileBased(path + "/" + name); }
+        public Mailbox slash(String name) throws IOException { return new FileBased(path + "/" + name); }
 
         public int[] list() {
             String[] names = new File(path).list();
index 4e06040..b4269ba 100644 (file)
@@ -92,7 +92,7 @@ public class Script extends Target {
             if (name.equals("mail")) { return getSub("mail"); }
             if (name.equals("mail.my")) { return getSub("mail.my"); }
             if (name.equals("mail.my.prefs")) { return prefs; }
-            if (name.equals("mail.my.mailbox")) { return FileSystem.Mailbox.getForUser("megacz"); }
+            if (name.equals("mail.my.mailbox")) { return Mailbox.Mailbox.getForUser("megacz"); }
             return super.get(name);
         }