append sorta works; dates are broken
[org.ibex.mail.git] / src / org / ibex / mail / target / FileBasedMailbox.java
1 package org.ibex.mail.target;
2 import org.ibex.mail.*;
3 import org.ibex.util.*;
4 import org.ibex.mail.*;
5 import java.io.*;
6 import java.net.*;
7 import java.util.*;
8 import java.text.*;
9
10 /** An exceptionally crude implementation of Mailbox relying on POSIXy filesystem semantics */
11 public class FileBasedMailbox extends Mailbox.Default {
12
13     private static final char slash = File.separatorChar;
14     private static final Hashtable instances = new Hashtable();
15     public static FileBasedMailbox getFileBasedMailbox(String path, boolean create) {
16         FileBasedMailbox ret = (FileBasedMailbox)instances.get(path);
17         if (ret != null) return ret;
18         File f = new File(path);
19         if (!create && !f.exists()) return null;
20         instances.put(path, ret = new FileBasedMailbox(path));
21         return ret;
22     }
23
24     public static final FilenameFilter filter = new FilenameFilter() {
25             public boolean accept(File f, String s) {
26                 return s.indexOf('.') != -1;
27             } };
28
29
30     // Instance //////////////////////////////////////////////////////////////////////////////
31
32     private String path;
33     private File uidNext;
34     private FileBasedMailbox(String path) throws MailException {
35         new File(this.path = path).mkdirs();
36         uidNext(false);
37     }
38
39     public Mailbox          slash(String name, boolean create) {
40         return FileBasedMailbox.getFileBasedMailbox(path + slash + name, create); }
41     public Mailbox.Iterator iterator() { return new FileBasedMailbox.Iterator(); }
42     public int              uidValidity() { return (int)(new File(path).lastModified() & 0xffffffL); }
43
44     public int uidNext() { return uidNext(false); }
45     public int uidNext(boolean inc) {
46         try {
47             uidNext = new File(path + slash + "UIDNEXT");
48             if (!uidNext.exists()) {
49                 File tmp = new File(uidNext + "-");
50                 PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(tmp)));
51                 pw.println("1");
52                 pw.flush();
53                 pw.close();
54                 tmp.renameTo(uidNext);
55                 return 1;
56             }
57             BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(uidNext)));
58             int ret = Integer.parseInt(br.readLine());
59             if (inc) {
60                 File tmp = new File(uidNext + "-");
61                 PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(tmp)));
62                 pw.println(ret+1);
63                 pw.flush();
64                 pw.close();
65                 tmp.renameTo(uidNext);
66             }
67             return ret;
68         } catch (IOException e) { throw new MailException.IOException(e); }
69     }        
70
71     public synchronized int add(Message message) {
72         try {
73             int num = new File(path).list(filter).length;
74             File target = new File(path + slash + uidNext(true) + ".");
75             File f = new File(target.getCanonicalPath() + "-");
76             FileOutputStream fo = new FileOutputStream(f);
77             message.dump(fo);
78             fo.close();
79             f.renameTo(target);
80             return num;
81         } catch (IOException e) { throw new MailException.IOException(e); }
82     }
83
84     private class Iterator implements Mailbox.Default.Iterator {
85         int cur = -1;
86         private String[] names;
87         private boolean seen = false, deleted = false, draft = false, flagged = false, answered = false, recent = false;
88         public Iterator() { names = new File(path).list(filter); }
89
90         public Message cur() {
91             try {
92                 File file = new File(path + File.separatorChar + names[cur]);
93                 return new Message(/* FIXME */ null, /* FIXME */ new Address[] { },
94                                    new LineReader(new InputStreamReader(new FileInputStream(file))));
95             } catch (IOException e) { throw new MailException.IOException(e); }
96         }
97         public boolean next() {
98             cur++;
99             if (cur >= names.length) return false;
100             String name = names[cur].substring(names[cur].indexOf('.') + 1);
101             seen     = name.indexOf('s') != -1;
102             deleted  = name.indexOf('x') != -1;
103             flagged  = name.indexOf('f') != -1;
104             draft    = name.indexOf('d') != -1;
105             answered = name.indexOf('a') != -1;
106             recent   = name.indexOf('r') != -1;
107             return true;
108         }
109         public int num() { return cur; }
110         public int uid() {
111             try { return Integer.parseInt(names[cur].substring(0, names[cur].indexOf('.')));
112             } catch (NumberFormatException nfe) {
113                 Log.warn(FileBasedMailbox.class, "NumberFormatException: " + names[cur].substring(0, names[cur].length() - 1));
114                 return -1; } }
115
116         private void fixflags() {
117             String newName =
118                 names[cur].substring(0, names[cur].indexOf('.') + 1) +
119                 (seen ? "s" : "") +
120                 (deleted ? "x" : "") +
121                 (flagged ? "f" : "") +
122                 (draft ? "d" : "") +
123                 (recent ? "r" : "") +
124                 (answered ? "a" : "");
125             new File(names[cur]).renameTo(new File(path + File.separatorChar + newName));
126             names[cur] = newName;
127         }
128
129         public void    delete() { new File(names[cur]).delete(); }
130         public void    set(String key, String val) { throw new MailException("not supported"); }
131         public String  get(String key) { throw new MailException("not supported"); }
132
133         public boolean seen() { return seen; }
134         public boolean deleted() { return deleted; }
135         public boolean flagged() { return flagged; }
136         public boolean draft() { return draft; }
137         public boolean answered() { return answered; }
138         public boolean recent() { return recent; }
139         public void    seen(boolean on) { seen = on; fixflags(); }
140         public void    deleted(boolean on) { deleted = on; fixflags(); }
141         public void    flagged(boolean on) { flagged = on; fixflags(); }
142         public void    draft(boolean on) { draft = on; fixflags(); }
143         public void    answered(boolean on) { answered = on; fixflags(); }
144         public void    recent(boolean on) { recent = on; fixflags(); }
145
146     }
147 }