remove FileBasedMailbox.get(JS), be sure to set the path
[org.ibex.mail.git] / src / org / ibex / mail / FileBasedMailbox.java
index 0621689..c965b9a 100644 (file)
@@ -6,7 +6,9 @@ package org.ibex.mail;
 import org.prevayler.*;
 import org.ibex.mail.*;
 import org.ibex.util.*;
+import org.ibex.js.*;
 import org.ibex.io.*;
+import org.ibex.io.Fountain;
 import java.io.*;
 import java.nio.*;
 import java.nio.channels.*;
@@ -17,26 +19,36 @@ import javax.servlet.*;
 import javax.servlet.http.*;
 
 /** An exceptionally crude implementation of Mailbox relying on POSIXy filesystem semantics */
-public class FileBasedMailbox extends Mailbox.Default {
+public class FileBasedMailbox extends Mailbox.Default implements MailTree {
 
-    public static final long MAGIC_DATE = 0;
+    public static final long MAGIC_DATE =  0;
     private static final char slash = File.separatorChar;
-    private static final WeakHashMap<String,Mailbox> instances = new WeakHashMap<String,Mailbox>();
+
+    // FIXME: ideally this should be weak, but we end up getting duplicates of SqliteMailboxes
+    private static final HashMap<String,MailTree> instances = new HashMap<String,MailTree>();
+
     public String toString() { return "[FileBasedMailbox " + path.getAbsolutePath() + "]"; }
-    public Mailbox slash(String name, boolean create) { return getFileBasedMailbox(path.getAbsolutePath()+slash+name, create); }
+
+    public MailTree slash(String name, boolean create) { return getFileBasedMailbox(path.getAbsolutePath()+slash+name, create); }
+
+    public void         rmdir(String subdir) { throw new RuntimeException("FIXME not implemented"); }
+    public void         rename(String subdir, MailTree newParent, String newName) { throw new RuntimeException("FIXME not implemented"); }
+    public Mailbox      getMailbox() { return this; }
 
     // FIXME: should be a File()
-    public static synchronized Mailbox getFileBasedMailbox(String path, boolean create) {
+    public static synchronized MailTree getFileBasedMailbox(String path, boolean create) {
+        if (path.endsWith(".sqlite")) path = path.substring(0, path.length()-".sqlite".length());
+       path = new File(path).getAbsolutePath().toString();
         try {
-            Mailbox ret = instances.get(path);
+            MailTree ret = instances.get(path);
             if (ret == null) {
-                if (!create && !(new File(path).exists())) return null;
-                if (new File(new File(path)+"/subscribers").exists()) {
-                    ret = new MailingList(new File(path), new FileBasedMailbox(new File(path)));
-                } else {
-                    ret = new FileBasedMailbox(new File(path));
-                }
-                instances.put(path, ret);
+                Log.error("n", "no match for " + path + " in " + instances.hashCode());
+                if (new File(path+".sqlite").exists()) ret = new SqliteMailbox(path+".sqlite");
+                else if (new File(path).exists()) ret = new FileBasedMailbox(new File(path));
+                else if (create) ret = new SqliteMailbox(path+".sqlite");
+                else return null;
+                instances.put(path, (MailTree)ret);
+                Log.error("n", "filling " + path + " with " + instances.get(path));
             }
             return ret;
         } catch (Exception e) {
@@ -68,6 +80,7 @@ public class FileBasedMailbox extends Mailbox.Default {
         // acquire lock
         File lockfile = new File(this.path.getAbsolutePath() + slash + ".lock");
         lock = new RandomAccessFile(lockfile, "rw").getChannel().tryLock();
+        // FIXME!!!
         /*
         if (lock == null) {
             Log.warn(this, "warning: blocking waiting for a lock on " + path);
@@ -84,8 +97,10 @@ public class FileBasedMailbox extends Mailbox.Default {
             try {
                 if (files[i].indexOf('.') <= 0) continue;
                 files[i] = files[i].substring(0, files[i].indexOf('.'));
-                int n = Integer.parseInt(files[i]);
-                if (n>=uidNext) uidNext = n;
+                try {
+                    int n = Integer.parseInt(files[i]);
+                    if (n>=uidNext) uidNext = n;
+                } catch (NumberFormatException nfe) { continue; }
             } catch(Exception e) { Log.error(this, e); }
         }
     }
@@ -118,12 +133,17 @@ public class FileBasedMailbox extends Mailbox.Default {
         for(int i=0; i<list.length; i++) {
             File f = new File(path.getAbsolutePath() + slash + list[i]);
             if (f.isDirectory() && f.getName().charAt(0) != '.') vec.addElement(list[i]);
+            if (!f.isDirectory() && f.getName().endsWith(".sqlite"))
+                vec.addElement(list[i].substring(0, list[i].length()-".sqlite".length()));
         }
         return (String[])vec.copyInto(new String[vec.size()]);
     }
 
     public int uidValidity() { return uidValidity; }
-    public int uidNext() {  return uidNext; }
+
+    // UGLY: Apple Mail doesn't like UID=0, so we add one
+    public int uidNext() {  return uidNext+1; }
+
     public synchronized void insert(Message message, int flags) {
         try {
             String name, fullname; File target, f;
@@ -141,11 +161,14 @@ public class FileBasedMailbox extends Mailbox.Default {
             f.renameTo(new File(fullname));
             uidNext++;
             f = new File(fullname);
-            if ((flags & Mailbox.Flag.SEEN) == Mailbox.Flag.SEEN) f.setLastModified(MAGIC_DATE);
+            if ((flags & Mailbox.Flag.SEEN) == 0)  f.setLastModified(MAGIC_DATE);
+            else if (f.lastModified()==MAGIC_DATE) f.setLastModified(System.currentTimeMillis());
         } catch (IOException e) { throw new MailException.IOException(e); }
         Log.info(this, path + " <= " + message.summary());
     }
 
+    
+
     private static FilenameFilter filter =
         new FilenameFilter() { public boolean accept(File dir, String name) {
             return name.endsWith(".");
@@ -157,31 +180,27 @@ public class FileBasedMailbox extends Mailbox.Default {
         private File file() { return new File(path.getAbsolutePath() + slash + files[cur]); }
         public boolean done() { return cur >= files.length; }
         public boolean next() { cur++; return !done(); }
-        public boolean seen() { return false; }
         public boolean recent() { return false; }
         public int nntpNumber() { return cur+1; }  // FIXME: lame
         public int imapNumber() { return cur+1; }  // EUDORA insists that message numbers start at 1, not 0
-        public int uid() { return done() ? -1 : Integer.parseInt(files[cur].substring(0, files[cur].length()-1)); }
+
+        // UGLY: Apple Mail doesn't like UID=0, so we add one
+        public int uid() { return done() ? -1 : 1+Integer.parseInt(files[cur].substring(0, files[cur].length()-1)); }
         public void delete() { File f = file(); if (f != null && f.exists()) f.delete(); }
-        public void seen(boolean seen) { }
-        public Headers head() {
-            if (done()) return null;
-            FileInputStream fis = null;
-            try {
-                return new Headers.Original(new Stream(new FileInputStream(file())));
-            } catch (IOException e) { throw new MailException.IOException(e);
-            } finally { if (fis != null) try { fis.close(); } catch (Exception e) { /* DELIBERATE */ } }
-        }
-        public Message cur() {
-            FileInputStream fis = null;
-            try {
-                return Message.newMessage(new Fountain.File(file()));
-                //} catch (IOException e) { throw new MailException.IOException(e);
-            } catch (Message.Malformed e) { throw new MailException(e.getMessage());
-            } finally { if (fis != null) try { fis.close(); } catch (Exception e) { /* DELIBERATE */ } }
+        public int  getFlags() { return file().lastModified()==MAGIC_DATE ? 0 : Flag.SEEN; }
+        public void setFlags(int flags) {
+            File f = file();
+            if ((flags & Mailbox.Flag.SEEN) == 0)  f.setLastModified(MAGIC_DATE);
+            else if (f.lastModified()==MAGIC_DATE) f.setLastModified(System.currentTimeMillis());
+            // FIXME: other flags?
+            // FIXME: definitely need DELETED flag in order to do message moves!
         }
+        public Headers head() { return done() ? null : new Headers(new Fountain.File(file())); }
+        public Message cur() { return Message.newMessage(new Fountain.File(file())); }
     }
 
+    // there's no reason this has to operate on a FileBasedMailbox -- it could operate on arbitrary mailboxes!
+    // use this for file attachments: http://jakarta.apache.org/commons/fileupload/
     public static class Servlet extends HttpServlet {
         public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { doGet(request, response); }
         private void frames(HttpServletRequest request, HttpServletResponse response, boolean top) throws IOException {
@@ -216,7 +235,7 @@ public class FileBasedMailbox extends Mailbox.Default {
             if (request.getServletPath().indexOf("..") != -1) throw new IOException(".. not allowed in image paths");
             ServletContext cx = getServletContext();
             String path = cx.getRealPath(request.getServletPath());
-            Mailbox mbox = FileBasedMailbox.getFileBasedMailbox(path, false);
+            Mailbox mbox = FileBasedMailbox.getFileBasedMailbox(path, false).getMailbox();
             if (mbox == null) throw new IOException("no such mailbox: " + path);
 
             Vec msgs = new Vec();