public static interface Client {
public void expunge(int uid);
public void list(char separator, String mailbox, boolean lsub, boolean phantom);
- public void fetch(int num, int flags, int size, Message m, int muid); // m may be null or incomplete
+ public void fetchResponse(int num, int flags, int size, Message m, int muid); // m may be null or incomplete
}
public static interface Server {
public void check() { }
public void noop() { }
public void logout() { }
- public void close() { for(Mailbox.Iterator it=selected().iterator(Query.deleted()); it.next();) it.delete(); }
+ public void close() {
+ for(Mailbox.Iterator it=selected().iterator(Query.deleted()); it.next();) {
+ Log.error("imap", "deleting a message due to close(): " + it.cur().summary());
+ it.delete();
+ }
+ }
public void expunge() { for(Mailbox.Iterator it = selected().iterator(Query.deleted());it.next();) expunge(it); }
- public void expunge(Mailbox.Iterator it) { client.expunge(it.uid()); it.delete(); }
+ public void expunge(Mailbox.Iterator it) {
+ client.expunge(it.uid());
+ Log.error("imap", "deleting a message due to expunge(): " + it.cur().summary());
+ it.delete();
+ }
public void subscribe(String mailbox) { }
public void unsubscribe(String mailbox) { }
public int maxuid() {
int ret = 0;
Mailbox mb = selected();
if (mb == null) return 0;
- for(Mailbox.Iterator it = mb.iterator(); it.next(); ) ret = it.uid();
- return ret;
+ return mb.maxuid();
}
public int unseen(String mailbox) { return mailbox(mailbox, false).count(Query.not(Query.seen())); }
public int recent(String mailbox) { return mailbox(mailbox, false).count(Query.recent()); }
Vec.Int vec = new Vec.Int();
for(Mailbox.Iterator it = selected().iterator(q); it.next();) {
vec.addElement(uid ? it.uid() : it.imapNumber());
- it.setFlags(it.getFlags() & ~Mailbox.Flag.RECENT);
+ if ((it.getFlags() & Mailbox.Flag.RECENT) != 0)
+ it.setFlags(it.getFlags() & ~Mailbox.Flag.RECENT);
}
return vec.dump();
}
else if (style == 0) it.setFlags(flags);
else if (style == 1) it.setFlags(it.getFlags() | flags);
//it.setFlag(Mailbox.Flag.RECENT, recent);
- if (!silent) client.fetch(it.imapNumber(), it.getFlags(), -1, null, it.uid());
+ if (!silent) client.fetchResponse(it.imapNumber(), it.getFlags(), -1, null, it.uid());
}
}
public void fetch(Query q, int spec, String[] headers, int start, int end, boolean uid) {
for(Mailbox.Iterator it = selected().iterator(q); it.next(); ) {
+ // FIXME it would be great if we could avoid instantiating the entire message just because RFC822SIZE was requested
Message message =
- ((spec & (BODYSTRUCTURE | ENVELOPE | INTERNALDATE | FIELDS | FIELDSNOT | RFC822 |
- RFC822TEXT | RFC822SIZE | HEADERNOT | HEADER)) != 0) ? it.cur() : null;
+ ((spec & (BODYSTRUCTURE | RFC822 | RFC822TEXT | RFC822SIZE)) != 0)
+ ? it.cur()
+ : ((spec & (ENVELOPE | INTERNALDATE | FIELDS | FIELDSNOT | RFC822SIZE | HEADERNOT | HEADER)) != 0)
+ ? Message.newMessage(Fountain.Util.concat(it.head(), Fountain.Util.create("\r\n\r\n")))
+ : null;
+ if (message != null) Log.warn("spec", spec);
long size = message == null ? 0 : message.getLength();
- client.fetch(it.imapNumber(), it.getFlags(), (int)size, message, it.uid());
+ client.fetchResponse(it.imapNumber(), it.getFlags(), (int)size, message,it.uid());
it.setFlags(it.getFlags() & ~Mailbox.Flag.RECENT);
}
}
} else {
Account account = (Account)ret;
((MailboxWrapper)api).root = root = account.getMailbox(IMAP.class);
- MailTree ibt = root.slash("INBOX", false);
+ MailTree ibt = root.slash("inbox", false);
Mailbox ib = ibt==null ? null : ibt.getMailbox();
((MailboxWrapper)api).inbox = inbox = ib;
}
case EXPUNGE: selected(); api.expunge(); break;
case UNSELECT: selected(); api.unselect(); selected = false; break;
case CREATE: api.create(token().astring()); break;
- case FETCH: selected(); lastuid = uid; fetch((uid
- ? Query.uid(token().set(maxn(uid)))
- : Query.imapNumber(token().set(maxn(uid)))),
- lastfetch=token().lx(), 0, 0, 0, uid, 0); break;
+ case FETCH: selected(); lastuid = uid; _fetch((uid
+ ? Query.uid(token().set(maxn(uid)))
+ : Query.imapNumber(token().set(maxn(uid)))),
+ lastfetch=token().lx(), 0, 0, 0, uid, 0); break;
case COPY: selected(); api.copy(uid
? Query.uid(token().set(maxn(uid)))
: Query.imapNumber(token().set(maxn(uid))), token().astring()); break;
println("* OK [UNSEEN " + api.unseen(mailbox) + "]");
println("* OK [UIDVALIDITY " + api.uidValidity(mailbox) + "] UIDs valid");
println("* OK [UIDNEXT " + api.uidNext(mailbox) + "]");
- println("* OK [PERMANENTFLAGS (\\Seen)]");
+ println("* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)]");
selected = true;
break; }
case STATUS: {
// Callbacks //////////////////////////////////////////////////////////////////////////////
public void expunge(int uid) { println("* " + uid + " EXPUNGE"); }
- public void fetch(int n, int f, int size, Message m, int muid) { fetch(m, lastfetch, n, f, size, lastuid, muid); }
+ public void fetchResponse(int n, int f, int size, Message m, int muid) { _fetch(m, lastfetch, n, f, size, lastuid, muid); }
public void list(char sep, String mb, boolean sub, boolean p) {
println("* " + (sub?"LSUB":"LIST")+" ("+(p?"\\Noselect":"")+") \""+sep+"\" \""+mb+"\"");}
* - parse the fetch request in Token[] t and return a fetch spec
* - emit a fetch reply for the parsed spec with respect to message m
*/
- private void fetch(Object o, Parser.Token[] t, int num, int flags, int size, boolean uid, int muid) {
+ private void _fetch(Object o, Parser.Token[] t, int num, int flags, int size, boolean uid, int muid) {
Query q = o == null ? null : o instanceof Query ? (Query)o : null;
Message m = o == null ? null : o instanceof Message ? (Message)o : null;
boolean e = q == null;
lastfetch = t;
int spec = 0; // spec; see constants for flags
+ int start = 0, len = 0;
String[] headers = null;
- int start = -1, end = -1;
StringBuffer r = new StringBuffer();
if (e) { r.append(num); r.append(" FETCH ("); }
int initlen = r.length();
if (s.equalsIgnoreCase("BODY.PEEK")) spec |= PEEK;
looked_at_body = true;
if (i >= t.length - 1 || t[i+1].type != Parser.Token.LIST) {
- spec |= BODYSTRUCTURE;
+ // not sure why this was uncommented....
+ //spec |= BODYSTRUCTURE;
if (e) { r.append(" "); r.append(Printer.bodystructure(m)); } continue;
//if (e) { r.append(" "); r.append(Printer.qq(m.getBody().getStream())); } continue;
}
else if (s.equals("HEADER.FIELDS.NOT")) { spec |= FIELDSNOT; payload=Fountain.Util.create(headers(r,t[i].l()[1].sl(),true,m,e)); }
else if (s.equals("MIME")) { throw new Server.Bad("MIME not supported"); }
else throw new Server.Bad("unknown section type " + s);
+
if (i<t.length - 1 && (t[i+1].s != null && t[i+1].s.startsWith("<"))) {
i++;
s = t[i].s.substring(1, t[i].s.indexOf('>'));
int dot = s.indexOf('.');
start = dot == -1 ? Integer.parseInt(s) : Integer.parseInt(s.substring(0, s.indexOf('.')));
- end = dot == -1 ? -1 : Integer.parseInt(s.substring(s.indexOf('.') + 1));
+ len = dot == -1 ? -1 : Integer.parseInt(s.substring(s.indexOf('.') + 1));
if (e) {
- //payload = payload.substring(start, Math.min(end+1,payload.getLength())); r.append("<"+start+">");
- // FIXME
- throw new RuntimeException("this had to be disabled");
+ if (start == 0 && len == -1) {
+ } else if (len == -1) {
+ payload = Fountain.Util.subFountain(payload, start);
+ } else {
+ len = (int)Math.min(len, payload.getLength()-start);
+ payload = Fountain.Util.subFountain(payload, start, len);
+ }
+ r.append("]");
+ r.append("<"+start+"> ");
}
+ } else {
+ if (e) r.append("] ");
}
- if (e) { r.append("] "); r.append(Printer.qq(payload.getStream())); }
+ if (e) r.append(Printer.qq(payload.getStream()));
}
}
if ((spec & PEEK) == 0 && looked_at_body && e)
r.append(")");
println("* " + r.toString());
} else {
- api.fetch(q, spec, headers, start, end, uid);
+ api.fetch(q, spec, headers, start, (len==-1?0:len), uid);
}
}
public Token(Parser.Token[] list) { this.s = null; n = 0; l = list; type = LIST; }
public Token(int number) { this.s = null; n = number; l = null; type = NUMBER; }
+ public String toString() {
+ // FIXME hack
+ switch(type) {
+ case NIL: return "NIL";
+ case LIST: return "(" + Printer.join(", ", l) + ")";
+ case QUOTED: return s;
+ case NUMBER: return n+"";
+ case ATOM: return s;
+ case BAREWORD: return s;
+ case SET: return "<set>";
+ }
+ return "???";
+ }
+
public String flag() { if (type != ATOM) bad("expected a flag"); return s; }
public int n() { if (type != NUMBER) bad("expected number"); return n; }
public int nz() { int n = n(); if (n == 0) bad("expected nonzero number"); return n; }
else if (flag.equals("\\Draft")) ret |= Mailbox.Flag.DRAFT;
else if (flag.equals("\\Answered")) ret |= Mailbox.Flag.ANSWERED;
else if (flag.equals("\\Recent")) ret |= Mailbox.Flag.RECENT;
+ else Log.warn(this, "unknown flag: " + flag);
}
return ret;
}
}
return ret.toString();
}
- private static String join(String delimit, String[] stuff) {
+ private static String join(String delimit, Object[] stuff) {
StringBuffer ret = new StringBuffer();
for(int i=0; i<stuff.length; i++) {
ret.append(stuff[i]);
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;
-
}