restructuring; broke out Query, cleaned up Mailbox
[org.ibex.mail.git] / src / org / ibex / mail / target / Mailbox.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 public class Mailbox extends Target {
11
12     // metadata
13     public void   set(Message m, String key, String val) { throw new MailException.MetadataNotSupported(); }
14     public String get(Message m, String key)             { throw new MailException.MetadataNotSupported(); }
15
16     // flags
17     public final boolean getFlag(Message m, int flags)   { return (flags(m) & flag) == flags; }
18     public final void    setFlag(Message m, int flag)   { flags(m, flags | flag); }
19     public final void    clearFlag(Message m, int flag) { flags(m, flags & ~flag); }
20     protected abstract void flags(Message m, int newFlags);  // set the flags for a given message
21     protected abstract int  flags(Message m);                // get the flags for a given message
22     public    abstract int  uid(Message m);                  // get the uid for a given message              (see IMAP RFC)
23     public    abstract int  num(Message m);                  // get the "message number" for a given message (see IMAP RFC)
24     public    abstract int  uidNext();                       // get the next uid to be assigned 
25     public    abstract int  uidValidity();                   // get the uid validity identifier              (see IMAP RFC)
26
27     // messages
28     public int     add(Message message)                      throws MailException { throw new Error("not implemented"); }
29     public int     delete(Message message)                   throws MailException { throw new Error("not implemented"); }
30     public void    query(Query q, Message.Visitor v)         throws MailException { throw new Error("not implemented"); }
31     public int     count(Query q)                            throws MailException { throw new Error("not implemented"); }
32     public void    move(Query q, Mailbox dest, boolean copy) throws MailException { throw new Error("not implemented"); }
33
34     // submailboxes
35     public void    rename(String newName)                    throws MailException { throw new Error("not implemented"); }
36     public void    destroy()                                 throws MailException { throw new Error("not implemented"); }
37     public Mailbox slash(String name, boolean create)        throws MailException { throw new Error("not implemented"); }
38
39     /** a fast-write, slow-read place to stash all messages we touch -- in case of a major f*ckup */
40     public static class Transcript extends Mailbox {
41         private String path;
42         public Transcript(String path) throws MailException { new File(this.path = path).mkdirs(); }
43         private static String lastTime = null;
44         private static int lastCounter = 0;
45
46         /** returns a message identifier */
47         public synchronized int add(Message message) throws MailException {
48             try {
49                 File today = new File(path + File.separatorChar + (new SimpleDateFormat("yy-MMM-dd").format(new Date())));
50                 today.mkdirs();
51                 
52                 String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
53                 synchronized (Transcript.class) {
54                     if (lastTime != null && lastTime.equals(time)) {
55                         time += "." + (++lastCounter);
56                     } else {
57                         lastTime = time;
58                     }
59                 }
60                 
61                 File target = new File(today.getPath() + File.separatorChar + time + ".txt");
62                 OutputStream os = new FileOutputStream(target);
63                 message.dump(os);
64                 os.close();
65                 return -1;
66             } catch (IOException e) { throw new MailException.IOException(e); }
67         }
68     }
69
70     public static class FileBased extends Mailbox {
71         private String path;
72         private FileBased(String path) throws MailException { new File(this.path = path).mkdirs(); }
73         public Mailbox slash(String name, boolean create) throws MailException {
74             // FIXME: create
75             return new FileBased(path + "/" + name);
76         }
77
78         public int[] list() {
79             String[] names = new File(path).list();
80             int[] ret = new int[names.length];
81             for(int i=0, j=0; j<ret.length; i++, j++) {
82                 try {
83                     ret[j] = Integer.parseInt(names[i].substring(0, names[i].length() - 1));
84                 } catch (NumberFormatException nfe) {
85                     Log.warn(FileBased.class, "NumberFormatException: " + names[i].substring(0, names[i].length() - 1));
86                     j--;
87                     int[] newret = new int[ret.length - 1];
88                     System.arraycopy(ret, 0, newret, 0, newret.length);
89                     ret = newret;
90                 }
91             }
92             return ret;
93         }
94
95         /** returns a message identifier */
96         public synchronized int add(Message message) throws MailException {
97             try {
98                 int[] all = list();
99                 int max = 0;
100                 for(int i=0; i<all.length; i++) max = Math.max(max, all[i]);
101                 int target = max++;
102                 File f = new File(path + File.separatorChar + max + ".-");
103                 FileOutputStream fo = new FileOutputStream(f);
104                 message.dump(fo);
105                 fo.close();
106                 f.renameTo(new File(path + File.separatorChar + max + "."));
107                 return target;
108             } catch (IOException e) { throw new MailException.IOException(e); }
109         }
110
111         public Message get(int messagenum) throws MailException {
112             File f = new File(path + File.separatorChar + messagenum + ".");        
113             if (!f.exists()) throw new MailException.IOException(new FileNotFoundException(f.toString()));
114             //try {
115                 // FIXME: need to store envelope from/to
116                 //Message ret = new Message(new LineReader(new InputStreamReader(new FileInputStream(f))));
117                 // FIXME: set answered/read/etc here
118                 //return ret;
119             //return null;
120                 /*
121             } catch (MailException.Malformed malf) {
122                 Log.error(this, "This should never happen");
123                 Log.error(this, malf);
124                 return null;
125             }
126                 */
127             return null;
128         }
129
130         // query types: stringmatch (headers, body), header element, deletion status, date range, message size
131         public Message[] query(int maxResults) {
132             throw new RuntimeException("FileBased.query() not implemented yet");
133         }
134
135     }
136
137
138     String user;
139     private static Hashtable cache = new Hashtable();
140     public static Mailbox getForUser(String user) {
141         Mailbox ret = (Mailbox)cache.get(user);
142         if (ret == null) ret = new Mailbox(user);
143         return ret;
144     }
145     Mailbox(String user) { this.user = user; }
146     public Mailbox slash(String name) throws MailException {
147         throw new Error(this.getClass().getName() + " does not support the slash() method"); }
148     public synchronized int add(Message message) throws MailException {
149         FileOutputStream fos = new FileOutputStream("/var/mail/" + user, true);
150         PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos));
151         pw.println("From " + message.envelopeFrom);
152         pw.flush();
153         message.dump(fos);
154         fos.close();
155         return -1;
156     }
157
158
159     private static final String STORAGE_ROOT =
160         System.getProperty("ibex.mail.root", File.separatorChar + "var" + File.separatorChar + "org.ibex.mail");
161     public static FileBased root = null;
162     public static Transcript transcript = null;
163     static {
164         try {
165             root = new FileBased(STORAGE_ROOT + File.separatorChar);
166             transcript = new Transcript(STORAGE_ROOT + File.separatorChar + "transcript");
167         } catch (Exception e) {
168             e.printStackTrace();
169         }
170     }
171
172     public final void    accept(Message m) throws MailException { add(m); }
173 }