1 package org.ibex.mail.target;
2 import org.ibex.mail.*;
3 import org.ibex.util.*;
11 // FIXME: we can omit UIDNEXT!
12 // FIXME use directory date/time as UIDNEXT and file date/time as UID; need to 'correct' file date/time after changes
14 /** An exceptionally crude implementation of Mailbox relying on POSIXy filesystem semantics */
15 public class FileBasedMailbox extends Mailbox.Default {
17 public String toString() { return "[FileBasedMailbox " + path + "]"; }
19 private static final char slash = File.separatorChar;
20 private static final Hashtable instances = new Hashtable();
21 public static FileBasedMailbox getFileBasedMailbox(String path, boolean create) {
22 FileBasedMailbox ret = (FileBasedMailbox)instances.get(path);
23 if (ret != null) return ret;
24 File f = new File(path);
25 if (!create && !f.exists()) return null;
26 instances.put(path, ret = new FileBasedMailbox(path));
30 public static final FilenameFilter filter = new FilenameFilter() {
31 public boolean accept(File f, String s) {
32 return s.indexOf('.') != -1;
36 // Instance //////////////////////////////////////////////////////////////////////////////
40 private FileBasedMailbox(String path) throws MailException {
41 new File(this.path = path).mkdirs();
45 public Mailbox slash(String name, boolean create) {
46 return FileBasedMailbox.getFileBasedMailbox(path + slash + name, create); }
48 public String[] children() {
50 String[] list = new File(path).list();
51 for(int i=0; i<list.length; i++) {
52 if (!(new File(path + slash + list[i]).isDirectory())) continue;
53 vec.addElement(list[i]);
55 String[] ret = new String[vec.size()];
60 public Mailbox.Iterator iterator() { return new FileBasedMailbox.Iterator(); }
61 public int uidValidity() { return (int)(new File(path).lastModified() & 0xffffffL); }
63 public int uidNext() { return uidNext(false); }
64 public int uidNext(boolean inc) {
66 uidNext = new File(path + slash + "UIDNEXT");
67 if (!uidNext.exists()) {
68 File tmp = new File(uidNext + "-");
69 PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(tmp)));
73 tmp.renameTo(uidNext);
76 BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(uidNext)));
77 int ret = Integer.parseInt(br.readLine());
80 File tmp = new File(uidNext + "-");
81 PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(tmp)));
85 tmp.renameTo(uidNext);
88 } catch (IOException e) { throw new MailException.IOException(e); }
91 public synchronized void add(Message message) { add(message, Mailbox.Flag.RECENT); }
92 public synchronized void add(Message message, int flags) {
93 Log.info(this, "adding message to ["+toString()+"]:\n" + message.summary());
95 int num = new File(path).list(filter).length;
96 String name = path + slash + uidNext(true) + "." +
97 ((flags & Mailbox.Flag.DELETED) == Mailbox.Flag.DELETED ? "x" : "") +
98 ((flags & Mailbox.Flag.DRAFT) == Mailbox.Flag.DRAFT ? "d" : "") +
99 ((flags & Mailbox.Flag.RECENT) == Mailbox.Flag.RECENT ? "r" : "") +
100 ((flags & Mailbox.Flag.ANSWERED) == Mailbox.Flag.ANSWERED ? "a" : "") +
101 ((flags & Mailbox.Flag.FLAGGED) == Mailbox.Flag.FLAGGED ? "f" : "") +
102 ((flags & Mailbox.Flag.SEEN) == Mailbox.Flag.SEEN ? "s" : "");
103 Log.info(this, " with chosen filename " + name);
104 File target = new File(name);
105 File f = new File(target.getCanonicalPath() + "-");
106 FileOutputStream fo = new FileOutputStream(f);
107 message.dump(new Stream(fo));
110 Log.info(this, " done writing.");
111 } catch (IOException e) { throw new MailException.IOException(e); }
114 private class Iterator extends Mailbox.Default.Iterator {
116 private String[] names;
117 private boolean seen = false, deleted = false, draft = false, flagged = false, answered = false, recent = false;
118 public Iterator() { names = new File(path).list(filter); }
120 public Message cur() {
121 if (cur >= names.length) return null;
123 File file = new File(path + File.separatorChar + names[cur]);
124 FileInputStream fis = new FileInputStream(file);
125 Message ret = new Message(null, null, new Stream(fis));
128 } catch (IOException e) { throw new MailException.IOException(e);
129 } catch (Message.Malformed e) { throw new MailException(e.getMessage()); }
131 public boolean next() {
133 if (cur >= names.length) return false;
134 String name = names[cur].substring(names[cur].indexOf('.') + 1);
135 if (!(new File(path + File.separatorChar + names[cur])).exists()) return next();
136 seen = name.indexOf('s') != -1;
137 deleted = name.indexOf('x') != -1;
138 flagged = name.indexOf('f') != -1;
139 draft = name.indexOf('d') != -1;
140 answered = name.indexOf('a') != -1;
141 recent = name.indexOf('r') != -1;
144 public int num() { return cur; }
146 try { return Integer.parseInt(names[cur].substring(0, names[cur].indexOf('.')));
147 } catch (NumberFormatException nfe) {
148 Log.warn(FileBasedMailbox.class, "NumberFormatException: " + names[cur].substring(0, names[cur].length() - 1));
151 private void fixflags() {
153 names[cur].substring(0, names[cur].indexOf('.') + 1) +
155 (deleted ? "x" : "") +
156 (flagged ? "f" : "") +
158 (recent ? "r" : "") +
159 (answered ? "a" : "");
160 new File(path + File.separatorChar + names[cur]).renameTo(new File(path + File.separatorChar + newName));
161 names[cur] = newName;
164 public void delete() {
165 new File(path + File.separatorChar + names[cur]).delete();
166 // FIXME remove from list?
168 public void set(String key, String val) { throw new MailException("not supported"); }
169 public String get(String key) { throw new MailException("not supported"); }
171 public boolean seen() { return seen; }
172 public boolean deleted() { return deleted; }
173 public boolean flagged() { return flagged; }
174 public boolean draft() { return draft; }
175 public boolean answered() { return answered; }
176 public boolean recent() { return recent; }
177 public void seen(boolean on) { seen = on; fixflags(); }
178 public void deleted(boolean on) { deleted = on; fixflags(); }
179 public void flagged(boolean on) { flagged = on; fixflags(); }
180 public void draft(boolean on) { draft = on; fixflags(); }
181 public void answered(boolean on) { answered = on; fixflags(); }
182 public void recent(boolean on) { recent = on; fixflags(); }