package org.ibex.mail.protocol;
import org.ibex.io.*;
+import org.ibex.crypto.*;
import org.ibex.jinetd.Listener;
import org.ibex.jinetd.Worker;
-import org.ibex.net.*;
import org.ibex.mail.*;
import org.ibex.util.*;
import org.ibex.mail.target.*;
public class IMAP {
public IMAP() { }
- public static final float version = (float)0.1;
+ public static final float version = (float)0.2;
// API Class //////////////////////////////////////////////////////////////////////////////
public void fetch(Query q, int spec, String[] headers, int start, int end, boolean uid);
public void lsub(String start, String ref);
public void list(String start, String ref);
- public static interface Authenticator { public abstract Mailbox authenticate(String user, String pass); }
public static class Exn extends MailException { public Exn(String s) { super(s); } }
public static class Bad extends Exn { public Bad(String s) { super(s); } }
public static class No extends Exn { public No(String s) { super(s); } }
Mailbox selected = null;
Mailbox root = null;
Mailbox selected() { if (selected == null) throw new Bad("no mailbox selected"); return selected; }
- final Server.Authenticator auth;
+ final Login auth;
final Client client;
- public MailboxWrapper(Server.Authenticator auth, Client c) { this.auth=auth; this.client=c;}
+ public MailboxWrapper(Login auth, Client c) { this.auth=auth; this.client=c;}
- private Mailbox mailbox(String name, boolean create) {
+ private Mailbox mailbox(String name, boolean create) { return mailbox(name, create, true); }
+ private Mailbox mailbox(String name, boolean create, boolean throwexn) {
if (name.equalsIgnoreCase("inbox")) return inbox;
Mailbox m = root;
for(StringTokenizer st = new StringTokenizer(name, sep + ""); st.hasMoreTokens();)
- if ((m = m.slash(st.nextToken(), create)) == null) throw new Server.No("no such mailbox " + name);
+ if ((m = m.slash(st.nextToken(), create)) == null) {
+ if (throwexn) throw new Server.No("no such mailbox " + name);
+ return null;
+ }
return m;
}
for(Mailbox.Iterator it=selected().iterator(q);it.next();) to.add(it.cur(), it.flags() | Mailbox.Flag.RECENT); }
public void login(String u, String p) {
- if ((root = auth.authenticate(u,p)) == null) throw new No("Login failed.");
+ Account account = null;
+ if ((account = auth.login(u,p)) == null) throw new No("Login failed.");
+ root = account.getMailbox(IMAP.class);
inbox = mailbox("INBOX", false); // FEATURE: ??
+ if (inbox == null) inbox = root;
}
public void unselect() { selected = null; }
public void delete(String m0) { delete(mailbox(m0,false)); }
public void delete(Mailbox m) { if (!m.equals(inbox)) m.destroy(false); else throw new Bad("can't delete inbox"); }
- public void create(String m) { mailbox(m, true); }
+ public void create(String m) { mailbox(m, true, false); }
public void append(String m,int f,Date a,String b) { try {
- mailbox(m,false).add(new Message(null,null,b,a),f|Mailbox.Flag.RECENT);
+ mailbox(m,false).add(new Message(new Stream(b), new Message.Envelope(null,null,a)),f|Mailbox.Flag.RECENT);
} catch (Message.Malformed e) { throw new No(e.getMessage()); } }
public void check() { }
public void noop() { }
String selectedName = null;
Mailbox inbox = null, root = null;
Server api;
+ Login auth = null;
Parser parser = null;
Connection conn = null;
- public Listener() { }
+ public Listener(Login auth) { this.auth = auth; }
Parser.Token token() { return parser.token(); }
void println(String s) { conn.println(s); }
void newline() { parser.newline(); }
public void handleRequest(Connection conn) {
this.conn = conn;
parser = new Parser(conn);
- api = new IMAP.MailboxWrapper(new Main.BogusAuthenticator(), this);
+ api = new IMAP.MailboxWrapper(auth, this);
conn.setTimeout(30 * 60 * 1000);
println("* OK " + conn.vhost + " " + IMAP.class.getName() + " IMAP4rev1 [RFC3501] v" + version + " server ready");
for(String tag = null;; newline()) try {
String response = "";
for(int i=0; i<list.length; i++) {
String s = list[i].atom().toUpperCase();
+ if (i>0) response += " ";
if (s.equals("MESSAGES")) response += "MESSAGES " + api.count(mailbox);
if (s.equals("RECENT")) response += "RECENT " + api.recent(mailbox);
if (s.equals("UIDNEXT")) response += "UIDNEXT " + api.uidNext(mailbox);
if (s.equals("UIDVALIDITY")) response += "UIDVALIDITY " + api.uidValidity(mailbox);
if (s.equals("UNSEEN")) response += "UNSEEN " + api.unseen(mailbox);
}
- println("* STATUS " + selectedName + " (" + response + ")");
+ println("* STATUS " + mailbox + " (" + response + ")");
break;
}
case APPEND: {
- String m = token().atom();
+ String m = token().astring();
int flags = 0;
- Date arrival = null;
+ Date arrival = new Date();
Parser.Token t = token();
if (t.type == t.LIST) { flags = t.flags(); t = token(); }
- if (t.type == t.QUOTED) { arrival = t.datetime(); t = token(); }
- api.append(m, flags, arrival, token().q());
+ if (t.type != t.QUOTED) { arrival = t.datetime(); t = token(); }
+ api.append(m, flags, arrival, t.q());
break; }
case STORE: {
selected();
if (s.equals("BODYSTRUCTURE")) { spec|=BODYSTRUCTURE;if(e){r.append(" ");r.append(Printer.bodystructure(m));}
} else if (s.equals("ENVELOPE")) { spec|=ENVELOPE; if(e){r.append(" ");r.append(Printer.envelope(m));}
} else if (s.equals("FLAGS")) { spec|=FLAGS; if(e){r.append(" ");r.append(Printer.flags(flags));}
- } else if (s.equals("INTERNALDATE")) { spec|=INTERNALDATE; if(e){r.append(" ");r.append(Printer.date(m.arrival));}
+ } else if (s.equals("INTERNALDATE")) { spec|=INTERNALDATE; if(e){r.append(" ");r.append(Printer.date(m.envelope.arrival));}
} else if (s.equals("RFC822")) { spec|=RFC822; if(e){r.append(" ");r.append(Printer.message(m));}
} else if (s.equals("RFC822.TEXT")) { spec|=RFC822TEXT; if(e){r.append(" ");r.append(Printer.qq(m.body));}
- } else if (s.equals("RFC822.HEADER")){ spec|=HEADER;if(e){r.append(" ");r.append(Printer.qq(m.allHeaders+"\r\n"));}
+ } else if (s.equals("RFC822.HEADER")){ spec|=HEADER;if(e){r.append(" ");r.append(Printer.qq(m.headers.raw+"\r\n"));}
} else if (s.equals("RFC822.SIZE")) { spec|=RFC822SIZE; if(e){r.append(" ");r.append(m.size());}
} else if (s.equals("UID")) { spec|=UID; if(e){r.append(" ");r.append(muid); }
} else if (!(s.equals("BODY.PEEK") || s.equals("BODY"))) { throw new Server.No("unknown fetch argument: " + s);
Parser.Token[] list = t[++i].l();
s = list.length == 0 ? "" : list[0].s.toUpperCase();
r.append(s);
- if (list.length == 0) { spec |= RFC822TEXT; if(e) payload = m.allHeaders+"\r\n"+m.body; }
- else if (s.equals("")) { spec |= RFC822TEXT; if(e) payload = m.allHeaders+"\r\n"+m.body; }
+ if (list.length == 0) { spec |= RFC822TEXT; if(e) payload = m.headers.raw+"\r\n"+m.body; }
+ else if (s.equals("")) { spec |= RFC822TEXT; if(e) payload = m.headers.raw+"\r\n"+m.body; }
else if (s.equals("TEXT")) { spec |= RFC822TEXT; if(e) payload = m.body; }
- else if (s.equals("HEADER")) { spec |= HEADER; if(e) payload = m.allHeaders+"\r\n"; }
+ else if (s.equals("HEADER")) { spec |= HEADER; if(e) payload = m.headers.raw+"\r\n"; }
else if (s.equals("HEADER.FIELDS")) { spec |= FIELDS; payload=headers(r,t[i].l()[1].sl(),false,m,e); }
else if (s.equals("HEADER.FIELDS.NOT")) { spec |= FIELDSNOT; payload=headers(r,t[i].l()[1].sl(),true,m,e); }
else if (s.equals("MIME")) { throw new Server.Bad("MIME not supported"); }
if (!negate) {
if(e) for(int j=0; j<headers.length; j++) {
r.append(headers[j] + (j<headers.length-1?" ":""));
- if (m.headers.get(headers[j]) != null) payload += headers[j]+": "+m.headers.get(headers[j])+"\r\n";
+ if (m.headers.gets(headers[j]) != null) payload += headers[j]+": "+m.headers.gets(headers[j])+"\r\n";
}
} else {
throw new Server.No("HEADERS.NOT temporarily disaled");
if(e) { OUTER: for(Enumeration x=m.headers.keys(); x.hasMoreElements();) {
String key = (String)x.nextElement();
for(int j=0; j<headers.length; j++) if (key.equalsIgnoreCase(headers[j])) continue OUTER;
- payload += key + ": " + m.headers.get(key)+"\r\n";
+ payload += key + ": " + m.headers.gets(key)+"\r\n";
} }
*/
}
if (c == '\r' || c == '\n') { if (freak) bad("unexpected end of line"); return null; }
else if (c == '{') {
while(stream.peekc() != '}') sb.append(stream.getc());
+ stream.getc();
stream.println("+ Ready when you are...");
int octets = Integer.parseInt(sb.toString());
while(stream.peekc() == ' ') stream.getc(); // whitespace
- while (stream.getc() != '\n' && stream.getc() != '\r') { }
+ while(stream.peekc() == '\n' || stream.peekc() == '\r') stream.getc();
byte[] bytes = new byte[octets];
int numread = 0;
while(numread < bytes.length) {
public static String addressList(Object a) {
if (a == null) return "NIL";
if (a instanceof Address) return "("+address((Address)a)+")";
+ if (a instanceof String) return "("+address(Address.parse((String)a))+")";
Address[] aa = (Address[])a;
StringBuffer ret = new StringBuffer();
ret.append("(");
static String date(Date d) { return d.toString(); }
static String envelope(Message m) {
return
- "(" + quotify(m.arrival.toString()) +
+ "(" + quotify(m.envelope.arrival.toString()) +
" " + quotify(m.subject) +
" " + addressList(m.from) +
- " " + addressList(m.headers.get("sender")) +
+ " " + addressList(m.headers.gets("sender")) +
" " + addressList(m.replyto) +
" " + addressList(m.to) +
" " + addressList(m.cc) +
" " + addressList(m.bcc) +
- " " + quotify((String)m.headers.get("in-reply-to")) +
+ " " + quotify((String)m.headers.gets("in-reply-to")) +
" " + quotify(m.messageid) +
")";
}
// Main //////////////////////////////////////////////////////////////////////////////
- /** simple listener for testing purposes */
- public static void main(String[] args) throws Exception {
- ServerSocket ss = new ServerSocket(143);
- for(;;) {
- final Socket s = ss.accept();
- new Thread() { public void run() { try {
- final Mailbox root = FileBasedMailbox.getFileBasedMailbox(Mailbox.STORAGE_ROOT+File.separatorChar+"imap", true);
- new Listener();
- } catch (Exception e) { e.printStackTrace(); } } }.start();
- }
- }
-
public static final int
PEEK=0x1, BODYSTRUCTURE=0x2, ENVELOPE=0x4, FLAGS=0x8, INTERNALDATE=0x10, FIELDS=0x800, FIELDSNOT=0x1000,
RFC822=0x20, RFC822TEXT=0x40, RFC822SIZE=0x80, HEADERNOT=0x100, UID=0x200, HEADER=0x400;
+
}