X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2Fmail%2Ftarget%2FFileBasedMailbox.java;h=2fe59969cf6d438b094fc1d8e702f17e20dcbf3c;hb=f9f2e234ee208985e8086600f8ff37d6140ed9ce;hp=aaedf52a2d694f5b509d4ed44ff93febb2483099;hpb=54a106811798dd4d58cbf30af33a7066c0f4ebd3;p=org.ibex.mail.git diff --git a/src/org/ibex/mail/target/FileBasedMailbox.java b/src/org/ibex/mail/target/FileBasedMailbox.java index aaedf52..2fe5996 100644 --- a/src/org/ibex/mail/target/FileBasedMailbox.java +++ b/src/org/ibex/mail/target/FileBasedMailbox.java @@ -1,3 +1,7 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + package org.ibex.mail.target; import org.prevayler.*; import org.ibex.mail.*; @@ -9,12 +13,13 @@ import java.nio.channels.*; import java.net.*; import java.util.*; import java.text.*; +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 static final long MAGIC_DATE = 0; - private static final char slash = File.separatorChar; private static final WeakHashMap instances = new WeakHashMap(); public String toString() { return "[FileBasedMailbox " + path.getAbsolutePath() + "]"; } @@ -35,126 +40,12 @@ public class FileBasedMailbox extends Mailbox.Default { } } - // Instance ////////////////////////////////////////////////////////////////////////////// private File path; private FileLock lock; - private Prevayler prevayler; - private Cache cache; - - public static class Cache implements Serializable { - public final int uidValidity = Math.abs(new Random().nextInt()); - private final Hashtable byname = new Hashtable(); - private final Hashtable byuid = new Hashtable(); - private final ArrayList linear = new ArrayList(); - public final File dir; - private int uidNext = 0; - - public Cache(File dir) { this.dir = dir; } - - public void init(final Prevayler prevayler) throws IOException { - dir.mkdirs(); - long time = System.currentTimeMillis(); - Log.info(this, "initializing maildir " + dir.getParent()); - - // Drop entries whose files have vanished - ArrayList kill = new ArrayList(); - for(String s : byname.keySet()) - if (!new File(dir.getParent() + slash + s).exists()) - kill.add(byname.get(s).delete(null)); - for(Transaction t : kill) prevayler.execute(t); - - // Make entries for new files which have appeared - for(String file : new File(dir.getParent()).list()) { - File f = new File(dir.getParent() + slash + file); - if (file.charAt(0)!='.' && !f.isDirectory()) { - Entry e = get(file); - if (e == null) - prevayler.execute(Entry.create(f)); - else if ((f.lastModified() == MAGIC_DATE) != e.seen()) - prevayler.execute(e.seen(f, !e.seen())); - } - } - - // Take a snapshot for posterity - if (System.currentTimeMillis() - time > 1000 * 5) - Log.info(this, " done initializing maildir " + dir.getParent()); - new Thread() { public void run() { - try { prevayler.takeSnapshot(); } catch (Exception e) { Log.error(this, e); } } }.start(); - } - - - public synchronized int size() { return linear.size(); } - public synchronized int uidNext() { return uidNext; } - public synchronized Entry get(int uid) { return byuid.get(uid); } - public synchronized Entry getLinear(int num) { return linear.get(num); } - public synchronized Entry get(String name) { return byname.get(name); } - - public static class Entry implements Serializable { - private final byte[] header; - public final String name; - public final int uid; - private boolean seen; - private Entry(String name, boolean seen, int uid, byte[] header) { - this.name = name; this.seen = seen; this.uid = uid; this.header = header; } - - // Accessors ////////////////////////////////////////////////////////////////////////////// - - public boolean seen() { return seen; } - public MIME.Headers headers() { return new MIME.Headers(new Stream(new ByteArrayInputStream(header)), true); } - public Message message(Cache cache) { try { - FileInputStream fis = null; - try { - fis = new FileInputStream(cache.dir.getParent() + slash + name); - return Message.newMessage(new Stream(fis)); - } finally { if (fis != null) fis.close(); } - } catch (IOException e) { throw new MailException.IOException(e); - } catch (Message.Malformed e) { throw new MailException(e.getMessage()); } - } - - // Transactions ////////////////////////////////////////////////////////////////////////////// - - public static Transaction create(File f) throws IOException { - final boolean seen = f.lastModified() == MAGIC_DATE; - final String name = f.getName(); - final byte[] header = new MIME.Headers(new Stream(new FileInputStream(f)), true).toString().getBytes(); - return new Transaction() { - public void executeOn(Object o, Date now) { - Cache cache = (Cache)o; - synchronized(cache) { - Entry e = new Entry(name, seen, cache.uidNext++, header); - cache.linear.add(e); - cache.byuid.put(e.uid, e); - cache.byname.put(e.name, e); - } } }; - } - - public Transaction seen(File f, final boolean seen) { - f.setLastModified(seen ? MAGIC_DATE : System.currentTimeMillis()); - final int uid = this.uid; - return new Transaction() { - public void executeOn(Object o, Date now) { - Cache cache = (Cache)o; - synchronized(cache) { - cache.get(uid).seen = seen; - } } }; - } - public Transaction delete(File f) { - if (f != null && f.exists()) f.delete(); - final int uid = this.uid; - return new Transaction() { - public void executeOn(Object o, Date now) { - Cache cache = (Cache)o; - synchronized(cache) { - Cache.Entry e = cache.get(uid); - cache.byname.remove(e.name); - cache.linear.remove(e); - cache.byuid.remove(uid); - } } }; - } - } - } + private int uidNext; + private int uidValidity; // Helpers ////////////////////////////////////////////////////////////////////////////// @@ -170,27 +61,23 @@ public class FileBasedMailbox extends Mailbox.Default { path.mkdirs(); // acquire lock - lock = new RandomAccessFile(this.path.getAbsolutePath() + slash + ".lock", "rw").getChannel().tryLock(); + File lockfile = new File(this.path.getAbsolutePath() + slash + ".lock"); + lock = new RandomAccessFile(lockfile, "rw").getChannel().tryLock(); if (lock == null) throw new IOException("unable to lock FileBasedMailbox"); - - File cacheDir = new File(path.getAbsolutePath() + slash + ".cache"); - try { - prevayler = PrevaylerFactory.createPrevayler(new Cache(cacheDir), cacheDir.getAbsolutePath()); - cache = (Cache)prevayler.prevalentSystem(); - } catch (Throwable t) { - Log.warn(this, "error while attempting to reconstitute a FileBasedMailbox.cache:"); - Log.warn(this, t); - Log.warn(this, "discarding cache..."); - rmDashRf(cacheDir); - prevayler = PrevaylerFactory.createPrevayler(new Cache(cacheDir), cacheDir.getAbsolutePath()); - cache = (Cache)prevayler.prevalentSystem(); + uidValidity = (int)(lockfile.lastModified() & 0xffffffff); + uidNext = 0; + String[] files = path.list(); + for(int i=0; i=uidNext) uidNext = n; + } catch(Exception e) { /* DELIBERATE */ } } - cache.init(prevayler); } public Mailbox.Iterator iterator() { return new Iterator(); } - public int uidValidity() { return cache.uidValidity; } public synchronized void add(Message message) { add(message, Mailbox.Flag.RECENT); } public String[] children() { Vec vec = new Vec(); @@ -202,11 +89,12 @@ public class FileBasedMailbox extends Mailbox.Default { return (String[])vec.copyInto(new String[vec.size()]); } - public int uidNext() { return cache.uidNext(); } + public int uidValidity() { return uidValidity; } + public int uidNext() { return uidNext; } public synchronized void add(Message message, int flags) { try { String name, fullname; File target, f; - for(int i = cache.uidNext(); ; i++) { + for(int i = uidNext; ; i++) { name = i + "."; fullname = path.getAbsolutePath() + slash + name; target = new File(fullname); @@ -214,31 +102,173 @@ public class FileBasedMailbox extends Mailbox.Default { if (!f.exists() && !target.exists()) break; Log.error(this, "aieeee!!!! target of add() already exists: " + target.getAbsolutePath()); } - FileOutputStream fo = new FileOutputStream(f); - Stream stream = new Stream(fo); - message.dump(stream); - fo.close(); + Stream stream = new Stream(new FileOutputStream(f)); + message.getStream().transcribe(stream); + stream.close(); f.renameTo(new File(fullname)); + uidNext++; f = new File(fullname); if ((flags & Mailbox.Flag.SEEN) == Mailbox.Flag.SEEN) f.setLastModified(MAGIC_DATE); - prevayler.execute(Cache.Entry.create(f)); } catch (IOException e) { throw new MailException.IOException(e); } Log.info(this, path + " <= " + message.summary()); } private class Iterator extends Mailbox.Default.Iterator { int cur = -1; - private Cache.Entry entry() { return cache.getLinear(cur); } - private File file() { return new File(cache.dir.getParent() + slash + entry().name); } - public MIME.Headers head() { return done() ? null : entry().headers(); } - public boolean done() { return cur >= cache.size(); } + String[] files = path.list(new FilenameFilter() { public boolean accept(File dir, String name) { + return name.endsWith("."); + } }); + 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 done() ? false : entry().seen(); } + public boolean seen() { return false; } + public boolean recent() { return false; } public int num() { return cur+1; } // EUDORA insists that message numbers start at 1, not 0 - public int uid() { return done() ? -1 : entry().uid; } - public void delete() { prevayler.execute(entry().delete(file())); } - public void seen(boolean seen) { prevayler.execute(entry().seen(file(), seen)); } - public Message cur() { return entry().message(cache); } + public int uid() { return done() ? -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(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 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 { + String basename = request.getServletPath(); + PrintWriter pw = new PrintWriter(response.getWriter()); + pw.println(""); + if (top) { + pw.println(" "); + pw.println(" "); + pw.println(" "); + } else { + pw.println(" "); + pw.println(" "); + pw.println(" "); + } + pw.println(" "); + pw.println(""); + pw.flush(); + } + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + String frame = request.getParameter("frame"); + String basename = request.getServletPath(); + + if (frame == null) { frames(request, response, true); return; } + if (frame.equals("top")) { frames(request, response, false); return; } + if (frame.equals("topleft")) { return; } + + 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); + if (mbox == null) throw new IOException("no such mailbox: " + path); + + Vec msgs = new Vec(); + for(Mailbox.Iterator it = mbox.iterator(); it.next();) { + String[] s = new String[4]; + Message m = it.cur(); + s[0] = (m.from==null?"":m.from.toString(true)); + s[1] = m.subject; + s[2] = (m.date + "").trim().replaceAll(" "," "); + s[3] = it.num() + ""; + msgs.addElement(s); + } + String[][] messages; + msgs.copyInto(messages = new String[msgs.size()][]); + + if ("bottom".equals(frame)) { bottom(request, response, messages, mbox); return; } + if ("topright".equals(frame)) { topright(request, response, messages); return; } + } + + private void bottom(HttpServletRequest request, HttpServletResponse response, String[][] messages, Mailbox mbox) + throws IOException { + PrintWriter pw = new PrintWriter(response.getWriter()); + pw.println(""); + pw.println(" "); + pw.println("
");
+            if (request.getParameter("msgnum") != null) {
+                int target = Integer.parseInt(request.getParameter("msgnum"));
+                for(Mailbox.Iterator it = mbox.iterator(); it.next();) {
+                    if (it.num() == target) {
+                        StringBuffer tgt = new StringBuffer();
+                        it.cur().getBody().getStream().transcribe(tgt);
+                        pw.println(tgt.toString());
+                        break;
+                    }
+                }
+            }
+            pw.println("    
"); + pw.println(" "); + pw.println(""); + } + + private void topright(HttpServletRequest request, HttpServletResponse response, String[][] messages) throws IOException { + PrintWriter pw = new PrintWriter(response.getWriter()); + String basename = request.getServletPath(); + pw.println(""); + pw.println(" "); + pw.println(" "); + pw.println(" "); + pw.println(" "); + pw.println(" "); + pw.println(" "); + boolean odd=false; + for(int i=0; i"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + } + pw.println("
"+m[0]+""+m[1]+""+m[2]+"
"); + pw.println("
"); + pw.println(" "); + pw.println(""); + } + } }