--- /dev/null
+// Copyright 2007 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;
+import org.ibex.util.*;
+import org.ibex.mail.*;
+import org.ibex.js.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.text.*;
+
+/** an Access Control List */
+public class Acl {
+
+ public static class PermissionDenied extends RuntimeException {
+ public PermissionDenied() { super(); }
+ }
+
+ /** an entry on an Acl; indicates permissions for a particular principal */
+ public static class Entry {
+ public final boolean read;
+ public final boolean list;
+ public final boolean write;
+ public final boolean delete;
+ public final boolean insert;
+ public final boolean lock;
+ public final boolean admin;
+ public final boolean post;
+ public final boolean flags;
+ public final boolean mkdir;
+ private String toString = null;
+
+ public Entry(boolean read,
+ boolean list,
+ boolean insert,
+ boolean delete,
+ boolean write,
+ boolean lock,
+ boolean admin,
+ boolean post,
+ boolean flags,
+ boolean mkdir) {
+ this.read = read;
+ this.list = list;
+ this.write = write;
+ this.delete = delete;
+ this.insert = insert;
+ this.lock = lock;
+ this.admin = admin;
+ this.post = post;
+ this.flags = flags;
+ this.mkdir = mkdir;
+ }
+
+ public Entry(String asString) {
+ this.toString = asString;
+ this.read = asString.indexOf('r') != -1;
+ this.list = asString.indexOf('l') != -1;
+ this.insert = asString.indexOf('i') != -1;
+ this.delete = asString.indexOf('d') != -1;
+ this.write = asString.indexOf('w') != -1;
+ this.lock = asString.indexOf('k') != -1;
+ this.admin = asString.indexOf('a') != -1;
+ this.post = asString.indexOf('p') != -1;
+ this.flags = asString.indexOf('f') != -1;
+ this.mkdir = asString.indexOf('m') != -1;
+ }
+
+ public String toString() {
+ if (toString != null) return toString;
+ StringBuffer sb = new StringBuffer();
+ if (this.read) sb.append('r');
+ if (this.list) sb.append('l');
+ if (this.insert) sb.append('i');
+ if (this.delete) sb.append('d');
+ if (this.write) sb.append('w');
+ if (this.lock) sb.append('k');
+ if (this.admin) sb.append('a');
+ if (this.post) sb.append('p');
+ if (this.flags) sb.append('f');
+ if (this.mkdir) sb.append('m');
+ return toString = sb.toString();
+ }
+ }
+
+}
import org.prevayler.*;
import org.ibex.mail.*;
import org.ibex.util.*;
+import org.ibex.js.*;
import org.ibex.io.*;
+import org.ibex.io.Fountain;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import javax.servlet.http.*;
/** An exceptionally crude implementation of Mailbox relying on POSIXy filesystem semantics */
-public class FileBasedMailbox extends Mailbox.Default {
+public class FileBasedMailbox extends Mailbox.Default implements MailboxTree {
public static final long MAGIC_DATE = 0;
private static final char slash = File.separatorChar;
- private static final WeakHashMap<String,Mailbox> instances = new WeakHashMap<String,Mailbox>();
+ private static final WeakHashMap<String,FileBasedMailbox> instances = new WeakHashMap<String,FileBasedMailbox>();
public String toString() { return "[FileBasedMailbox " + path.getAbsolutePath() + "]"; }
- public Mailbox slash(String name, boolean create) { return getFileBasedMailbox(path.getAbsolutePath()+slash+name, create); }
+
+ public MailboxTree slash(String name, boolean create) { return getFileBasedMailbox(path.getAbsolutePath()+slash+name, create); }
+
+ public void rmdir(String subdir) { throw new RuntimeException("FIXME not implemented"); }
+ public void rename(String subdir, MailboxTree newParent, String newName) { throw new RuntimeException("FIXME not implemented"); }
+ public Mailbox getMailbox() { return this; }
+
+ public JS get(JS key) throws JSExn {
+ return (JS)slash(JSU.toString(key), true);
+ }
// FIXME: should be a File()
- public static synchronized Mailbox getFileBasedMailbox(String path, boolean create) {
+ public static synchronized MailboxTree getFileBasedMailbox(String path, boolean create) {
try {
- Mailbox ret = instances.get(path);
+ MailboxTree ret = instances.get(path);
if (ret == null) {
if (!create && !(new File(path).exists())) return null;
- if (new File(new File(path)+"/subscribers").exists()) {
- ret = new MailingList(new File(path), new FileBasedMailbox(new File(path)));
- } else {
- ret = new FileBasedMailbox(new File(path));
- }
- instances.put(path, ret);
+ ret = new FileBasedMailbox(new File(path));
+ instances.put(path, (FileBasedMailbox)ret);
}
return ret;
} catch (Exception e) {
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);
+ Mailbox mbox = FileBasedMailbox.getFileBasedMailbox(path, false).getMailbox();
if (mbox == null) throw new IOException("no such mailbox: " + path);
Vec msgs = new Vec();
public MailboxWrapper(Login auth, Client c) { this.auth=auth; this.client=c;}
public void setClient(IMAP.Client client) { }
+ private String dirname(String name) { return name.substring(0, name.lastIndexOf(sep)); }
+ private String basename(String name) { return name.substring(name.lastIndexOf(sep)+1); }
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;
+ MailboxTree mt = mailboxTree(name, create, throwexn);
+ return mt==null ? null : mt.getMailbox();
+ }
+ private MailboxTree mailboxTree(String name, boolean create) { return mailboxTree(name, create, true); }
+ private MailboxTree mailboxTree(String name, boolean create, boolean throwexn) {
MailboxTree m = root;
for(StringTokenizer st = new StringTokenizer(name, sep + ""); st.hasMoreTokens();)
if ((m = m.slash(st.nextToken(), create)) == null) {
if (throwexn) throw new Server.No("no such mailbox " + name);
return null;
}
- return m.getMailbox();
+ return m;
}
// FEATURE: not accurate when a wildcard and subsequent non-wildcards both match a single component
if (ref.length() == 0) { client.list(sep, start, lsub, false); return; }
while (start.endsWith(""+sep)) start = start.substring(0, start.length() - 1);
if (ref.endsWith("%")) ref = ref + sep;
- String[] children = (start.length() == 0 ? root : mailbox(start, false)).children();
+ String[] children = (start.length() == 0 ? root : mailboxTree(start, false)).children();
for(int i=0; i<children.length; i++) {
String s = children[i], pre = ref, kid = start + (start.length() > 0 ? sep+"" : "") + s;
if (mailbox(kid, false) == null) continue;
- Mailbox phant = mailbox(kid, false, false);
+ MailboxTree phant = mailboxTree(kid, false, false);
if (phant != null) {
- boolean phantom = phant.phantom();
+ boolean phantom = phant.getMailbox()==null;
while(true) {
if (pre.length() == 0) {
if (s.length() == 0) client.list(sep, kid, lsub, phantom);
for(Mailbox.Iterator it=selected().iterator(q);it.next();) to.insert(it.cur(), it.getFlags() | Mailbox.Flag.RECENT); }
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 delete(String m0) { mailboxTree(dirname(m0),false).rmdir(basename(m0)); }
+ public void rename(String from0, String to) {
+ Mailbox from = mailbox(from0, false);
+ if (from.equals(inbox)) { from.copy(Query.all(), mailbox(to, true)); }
+ else if (to.equalsIgnoreCase("inbox")) { from.copy(Query.all(), mailbox(to, true)); delete(from0); }
+ else mailboxTree(dirname(from0), false).rename(dirname(from0), mailboxTree(dirname(to), false), basename(to));
+ }
+
public void create(String m) { mailbox(m, true, false); }
public void append(String m,int f,Date a,String b) { try {
// FIXME: more efficient streaming here?
}
}
- public void rename(String from0, String to) {
- Mailbox from = mailbox(from0, false);
- if (from.equals(inbox)) { from.copy(Query.all(), mailbox(to, true)); }
- else if (to.equalsIgnoreCase("inbox")) { from.copy(Query.all(), mailbox(to, true)); from.destroy(false); }
- else from.rename(to);
- }
public void fetch(Query q, int spec, String[] headers, int start, int end, boolean uid) {
for(Mailbox.Iterator it = selected().iterator(q); it.next(); ) {
Message message = ((spec & (BODYSTRUCTURE | ENVELOPE | INTERNALDATE | FIELDS | FIELDSNOT | RFC822 |
} else {
Account account = (Account)ret;
((MailboxWrapper)api).root = root = account.getMailbox(IMAP.class);
- ((MailboxWrapper)api).inbox = inbox = root.slash("INBOX", false).getMailbox();
- if (inbox == null) ((MailboxWrapper)api).inbox = inbox = root.getMailbox();
+ MailboxTree ibt = root.slash("INBOX", false);
+ Mailbox ib = ibt==null ? null : ibt.getMailbox();
+ ((MailboxWrapper)api).inbox = inbox = ib;
}
}
import java.text.*;
/** abstract superclass for mailboxes, which store messages along with their flags */
-public abstract class Mailbox extends MailboxTree implements Target {
-
- public JS get(JS key) throws JSExn {
- return slash(JSU.toString(key), true);
- }
+public abstract class Mailbox extends JS.Obj implements Target {
public static final String STORAGE_ROOT =
System.getProperty("ibex.mail.root", File.separatorChar + "var" + File.separatorChar + "org.ibex.mail");
// Required Methods //////////////////////////////////////////////////////////////////////////////
- /** phantom mailboxes merely contain others; like the alt.binaries in alt.binaries.warez */
- public abstract boolean phantom();
public abstract Mailbox.Iterator iterator(Query q);
public abstract void insert(Message message, int flags);
public abstract void copy(Query q, Mailbox dest);
public abstract int count(Query q);
public abstract int uidNext();
- public abstract void rename(String newName); /* FIXME: IMAP semantics require creating parent dirs */
- public abstract void destroy(boolean recursive);
- public Mailbox getMailbox() { return this; }
// Thunks ////////////////////////////////////////////////////////////////////////////
/** default, inefficient implementation of Mailbox; only requires a few methods to be implemented */
public static abstract class Default extends Mailbox {
public Mailbox.Iterator iterator(Query q) { return new Mailbox.Iterator.QueryIterator(q, this); }
- public boolean phantom() { return false; }
public void copy(Query q, Mailbox dest) { for(Mailbox.Iterator it = iterator(q); it.next();) dest.insert(it.cur(), it.getFlags()); }
public int count(Query q) { int count = 0; for(Mailbox.Iterator it = iterator(q); it.next();) count++; return count; }
public void rename(String newName) { throw new MailException("not supported"); }
public void setFlags(int flags) { it.setFlags(flags); }
}
+ public static class AclWrapper extends Wrapper {
+ private Acl.Entry acl;
+ public AclWrapper(Iterator it, Acl.Entry acl) { super(it); this.acl = acl; }
+ public Message cur() { if (acl.read && acl.list) return super.cur(); else throw new Acl.PermissionDenied(); }
+ public Headers head() { if (acl.read && acl.list) return super.head(); else throw new Acl.PermissionDenied(); }
+ public boolean next() { if (acl.list) return super.next(); else throw new Acl.PermissionDenied(); }
+ public int uid() { if (acl.list) return super.uid(); else throw new Acl.PermissionDenied(); }
+ public int nntpNumber() { if (acl.list) return super.nntpNumber(); else throw new Acl.PermissionDenied(); }
+ public int imapNumber() { if (acl.list) return super.imapNumber(); else throw new Acl.PermissionDenied(); }
+ public void delete() { if (acl.delete) super.delete(); else throw new Acl.PermissionDenied(); }
+ public int getFlags() { if (acl.list) return super.getFlags(); else throw new Acl.PermissionDenied(); }
+ public void setFlags(int flags) { if (acl.flags) super.setFlags(flags); else throw new Acl.PermissionDenied(); }
+ }
+
class QueryIterator extends Mailbox.Iterator.Wrapper {
Query q;
public QueryIterator(Query q, Mailbox m) { super(m.iterator()); this.q = q; }
}
public static class MailboxWrapper extends Mailbox {
-
private Mailbox m;
public MailboxWrapper(Mailbox m) { this.m = m; }
- public boolean phantom() { return m.phantom(); }
+ public Mailbox.Iterator iterator() { return m.iterator(); }
public Mailbox.Iterator iterator(Query q) { return m.iterator(q); }
public void insert(Message message, int flags) { m.insert(message, flags); }
public void post(Message message) { m.insert(message, Flag.RECENT); }
public void copy(Query q, Mailbox dest) { m.copy(q, dest); }
public int count(Query q) { return m.count(q); }
public int uidNext() { return m.uidNext(); }
- public void rename(String newName) { m.rename(newName); }
- public void destroy(boolean recursive) { m.destroy(recursive); }
- public MailboxTree slash(String name, boolean create) { return m.slash(name, create); }
- public String[] children() { return m.children(); }
- public Mailbox.Iterator iterator() { return m.iterator(); }
public int uidValidity() { return m.uidValidity(); }
}
+ public static abstract class AclWrapper extends MailboxWrapper {
+ private Mailbox m;
+ private Acl.Entry acl;
+ public AclWrapper(Mailbox m, Acl.Entry acl) { super(m); this.acl = acl; }
+ public Mailbox.Iterator iterator(Query q) { if (acl.list) return new Mailbox.Iterator.AclWrapper(m.iterator(q), acl); else throw new Acl.PermissionDenied(); }
+ public Mailbox.Iterator iterator() { if (acl.list) return new Mailbox.Iterator.AclWrapper(m.iterator(), acl); else throw new Acl.PermissionDenied(); }
+ public void insert(Message message, int flags) { if (acl.insert) m.insert(message, flags); else throw new Acl.PermissionDenied(); }
+ public int uidValidity() { if (acl.list) return m.uidValidity(); else throw new Acl.PermissionDenied(); }
+ public void post(Message message) { if (acl.post) m.insert(message, Flag.RECENT); else throw new Acl.PermissionDenied(); }
+ public void move(Query q, Mailbox dest) { if (acl.list && acl.read && acl.delete) m.move(q, dest); else throw new Acl.PermissionDenied(); }
+ public void copy(Query q, Mailbox dest) { if (acl.list && acl.read) m.copy(q, dest); else throw new Acl.PermissionDenied(); }
+ public int count(Query q) { if (acl.list) return m.count(q); else throw new Acl.PermissionDenied(); }
+ public int uidNext() { if (acl.list) return m.uidNext(); else throw new Acl.PermissionDenied(); }
+ }
}
-// Copyright 2000-2005 the Contributors, as shown in the revision logs.
+// Copyright 2007 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.
import java.text.*;
/** a node on the "mailbox tree" */
-public abstract class MailboxTree extends JS.Obj {
+public interface MailboxTree {
public abstract MailboxTree slash(String name, boolean create);
public abstract String[] children();
- public Mailbox getMailbox() { return null; }
+ public abstract void rmdir(String subdir);
+ public abstract void rename(String subdir, MailboxTree newParent, String newName); /* FIXME: IMAP semantics require creating parent dirs */
+ public abstract Mailbox getMailbox();
+
+ public static class Wrapper implements MailboxTree {
+ protected MailboxTree mt;
+ public Wrapper(MailboxTree mt) { this.mt = mt; }
+ public MailboxTree slash(String name, boolean create) { return mt.slash(name, create); }
+ public String[] children() { return mt.children(); }
+ public void rmdir(String subdir) { mt.rmdir(subdir); }
+ public void rename(String subdir, MailboxTree newParent, String newName) { mt.rename(subdir, newParent, newName); }
+ public Mailbox getMailbox() { return mt.getMailbox(); }
+ }
+
+ public static class AclWrapper extends Wrapper {
+ protected Acl.Entry acl;
+ public AclWrapper(MailboxTree mt, Acl.Entry acl) { super(mt); this.acl = acl; }
+ public Mailbox getMailbox() {
+ return new Mailbox.AclWrapper(mt.getMailbox(), acl) {
+ public MailboxTree slash(String name, boolean create) {
+ return MailboxTree.AclWrapper.this.slash(name, create);
+ }
+ };
+ }
+ public MailboxTree slash(String name, boolean create) {
+ if (!acl.mkdir) create = false;
+ if (!acl.list) return null;
+ return super.slash(name, create);
+ }
+ public void rmdir(String subdir) {
+ if (!acl.delete) throw new Acl.PermissionDenied();
+ super.rmdir(subdir);
+ }
+ public void rename(String subdir, MailboxTree newParent, String newName) {
+ // FIXME: acl-checking on parent?
+ if (!acl.delete) throw new Acl.PermissionDenied();
+ super.rename(subdir, newParent, newName);
+ }
+ }
+
}
private KerberosAuth ka = new KerberosAuth("MEGACZ.COM", "chaitin.megacz.com");
public Account anonymous() {
try {
- final Mailbox root =
+ final MailboxTree root =
FileBasedMailbox.getFileBasedMailbox(Mailbox.STORAGE_ROOT + "/list", false);
if (root==null) return null;
- return new Account("anonymous", null, root){
+ return new Account("anonymous", null, root) {
public MailboxTree getMailbox(Class protocol) {
return super.getMailbox(protocol);
}
String[] s = who.children();
for(int i=0; i<s.length; i++) {
v.addElement(new Group(prefix + s[i], true, 0, 0, 0)); // FIXME numbers
- Group[] g2 = list(who.slash(s[i], false).getMailbox(), prefix + s[i] + ".");
+ Group[] g2 = list(who.slash(s[i], false), prefix + s[i] + ".");
for(int j=0; j<g2.length; j++) v.addElement(g2[j]);
}
Group[] ret = new Group[v.size()];
} catch (IOException e) { throw new JSExn(e.toString()); }
case "mail.whitelist": return JSReflection.wrap(org.ibex.mail.SMTP.whitelist);
case "mail.my.mailbox":
- Mailbox root = FileBasedMailbox.getFileBasedMailbox(Mailbox.STORAGE_ROOT, true);
- return root.slash("user", true).slash("megacz", true);
+ MailboxTree root = FileBasedMailbox.getFileBasedMailbox(Mailbox.STORAGE_ROOT, true);
+ return (JS)root.slash("user", true).slash("megacz", true);
case "mail.list": return METHOD;
//#end
return super.get(name);