public void check(String[] args, boolean examineOnly) { ok("check complete"); }
public void lsub(String[] args) { list(args); }
public void list(String[] args) { star("LIST () \".\" INBOX"); ok("LIST completed"); }
- public void create(String mailbox) {
- if (mailbox.charAt(mailbox.length() - 1) != '.' &&
- !mailbox.toLowerCase.equals("inbox"))
- if (!storage.create(mailbox))
- throw No("unable to CREATE mailbox");
- ok("CREATE completed");
- }
+ public void create(String mailbox) {if(!mailbox.endsWith(".")&&!mailbox.equalsIgnoreCase("inbox"))storage.create(mailbox);}
public void rename(Mailbox from, String to) {
if (from.getName().toLowerCase().equals("inbox")) {
int[] messages = from.list();
inbox.add(m);
from.delete(messages[i]);
}
- // FIXME delete from
+ from.delete();
} else {
from.rename(to);
}
if (!silent) ok("EXPUNGE completed");
}
- public void fetch(int[] messageSet, boolean examineOnly, boolean uid) {
- if (selected == null) throw new Bad("no mailbox selected");
- Object[] tokens = tokenize(args[0]);
- String[] headers;
- boolean negateHeaders;
- boolean flags;
- boolean internalDate;
- boolean size;
- boolean uid;
- int start, end;
- boolean setSeen;
- for(int i=0; i<tokens.length; i++) {
-
- }
- for(int i=0; i<messageSet.length; i++) {
- Message m = uid ? selected.getUID(messageSet[i]) : selected.get(messageSet[i]);
- fetch(m,
- String[] headers,
- boolean negateHeaders,
- boolean flags,
- boolean internalDate,
- boolean size,
- boolean uid,
- int start, int end,
- boolean setSeen);
- // FIXME
- }
- }
-
public void append(Mailbox m, Token flags, Date arrived, String literal) {
Message m = new Message(null, null, new StringReader(literal));
if (flags != null) flags.setFlags(m);
selected.add(m);
}
+ public void fetch(Message m, String[] headers, boolean negateHeaders, boolean flags, boolean internalDate,
+ boolean size, boolean uid, int start, int end, boolean setSeen, boolean envelope,
+ boolean header, boolean peek) {
+ String reply = "";
+ if (flags)
+ reply +=
+ "FLAGS ("
+ + m.deleted ? "\Deleted " : ""
+ + m.seen ? "\Seen " : ""
+ + m.flagged ? "\Flagged " : ""
+ + m.draft ? "\Draft " : ""
+ + m.answered ? "\Answered " : ""
+ + m.recent ? "\Recent " : ""
+ + ") ";
+ if (size) reply += "RFC822.SIZE " m.rfc822size() + " ";;
+ if (bodyStructure) // FIXME
+ reply += "(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" " + m.rfc822size() +" "+ m.numLines() +")";
+ if (envelope)
+ reply +=
+ "(" + quotify(m.date) +
+ " " + quotify(m.subject) +
+ " " + quotify(new Address[] { m.from }) + " " +
+ " " + quotify(new Address[] { m.sender }) + " " +
+ " " + quotify(new Address[] { m.replyTo }) + " " +
+ " " + quotify(new Address[] { m.to }) + " " +
+ " " + quotify(cc) + " " +
+ " " + quotify(bcc) + " " +
+ " " + quotify(m.headers.get("in-reply-to")) + " " +
+ " " + quotify(m.messageId) +
+ ") ";
+ if (internaldate) reply += quotify(m.arrival) + " ";
+ // FIXME
+ }
+
public void copy(int[] set, Mailbox target) {
for(int i=0; i<set.length; i++) {
Message m = selected.get(set[i]);
Token[] flags = null;
Date arrival = null;
if (t.type == LIST) { flags = t.l(); t = token(); }
- if (t.type == QUOTED) arrival = datetime();
- append(m, flags, arrival, literal());
+ if (t.type == QUOTED) { arrival = t.datetime(); t = token(); }
+ append(m, flags, arrival, t.literal());
} else if (command.equals("EXAMINE")) examine(mailbox());
else if (command.equals("COPY")) copy(set(), mailbox());
else if (command.equals("DELETE")) delete(mailbox());
Set s = set();
Token t = token();
Token[] tl = null;
- boolean envelope = false, internaldate = false, size = false, flags = false, uid=false;
+ int start = -1;
+ int end = -1;
+ boolean envelope=false, internaldate=false, size=false, flags=false, uid=false, header=false, peek=false;
if (t.type == Token.LIST) tl = t.l();
else if (t.atom().equals("FULL")) { flags=true; internaldate=true; size=true; envelope=true; body=true; }
else if (t.atom().equals("ALL")) { flags=true; internaldate=true; size=true; envelope=true; }
else throw new Bad("expected atom or list");
for (int i=0; i<tl.length; i++) {
String s = tl[i].atom();
- if (s.equals("1")) s = "RFC822";
- else if (s.startsWith("1.")) s = s.substring(2);
+ if (s.startsWith("BODY.PEEK")) { peek = true; s = "BODY" + s.substring(9); }
+ if (s.startsWith("BODY.1")) s = "BODY" + s.substring(6);
+ if (s.indexOf('<') != -1) {
+ start = Integer.parseInt(s.substring(s.indexOf('<')+1, s.indexOf(s.indexOf('<'), '.')));
+ end = Integer.parseInt(s.substring(s.indexOf(s.indexOf('<'), '.'), s.indexOf('>')));
+ s = s.substring(0, s.indexOf('<'));
+ }
if (s.equals("ENVELOPE")) envelope = true;
else if (s.equals("FLAGS")) flags = true;
else if (s.equals("INTERNALDATE")) internaldate = true;
- else if (s.equals("RFC822")) body = true; // FIXME: special return syntax?
+ else if (s.equals("RFC822")) body = true;
else if (s.equals("RFC822.HEADER")) header = true;
else if (s.equals("RFC822.SIZE")) size = true;
else if (s.equals("RFC822.TEXT")) body = true;
else if (s.equals("BODY")) body = true;
+ else if (s.equals("BODY[]")) body = true;
+ else if (s.equals("BODY[HEADER]")) header=true;
+ else if (s.startsWith("BODY[HEADER.FIELDS")) throw new No("partial headers not supported");
+ else if (s.startsWith("BODY[HEADER.FIELDS.NOT")) throw new No("partial headers not supported"):
else if (s.equals("BODYSTRUCTURE")) throw new No("FETCH BODYSTRUCTURE not supported");
+ else if (s.equals("TEXT")) body = true;
+ else if (s.equals("MIME")) throw new Bad("FETCH BODY.MIME not supported");
else if (s.equals("UID")) uid = true;
else if (s.startsWith("BODY")) throw new No("FETCH BODY[*] not supported");
else throw new Bad("unrecognized FETCH argument \"" + s + "\"");
}
- fetch(selected, null, false, flags, internaldate, size, uid, start, end, setseen, envelope);
+ fetch(selected, flags, internaldate, size, uid, start, end, setseen, envelope, header, peek);
} else if (command.equals("STATUS")) {
Mailbox m = mailbox();
Token[] attrs = l();
")";
}
- public static void fetch(Message m, String[] headers, boolean negateHeaders, boolean flags, boolean internalDate,
- boolean size, boolean uid, int start, int end, boolean setSeen, boolean envelope) {
- String reply = "";
- if (flags)
- reply +=
- "FLAGS ("
- + m.deleted ? "\Deleted " : ""
- + m.seen ? "\Seen " : ""
- + m.flagged ? "\Flagged " : ""
- + m.draft ? "\Draft " : ""
- + m.answered ? "\Answered " : ""
- + m.recent ? "\Recent " : ""
- + ") ";
- if (size) reply += "RFC822.SIZE " m.rfc822size() + " ";;
- if (bodyStructure) // FIXME
- reply += "(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" " + m.rfc822size() +" "+ m.numLines() +")";
- if (envelope)
- reply +=
- "(" + quotify(m.date) +
- " " + quotify(m.subject) +
- " " + quotify(new Address[] { m.from }) + " " +
- " " + quotify(new Address[] { m.sender }) + " " +
- " " + quotify(new Address[] { m.replyTo }) + " " +
- " " + quotify(new Address[] { m.to }) + " " +
- " " + quotify(cc) + " " +
- " " + quotify(bcc) + " " +
- " " + quotify(m.headers.get("in-reply-to")) + " " +
- " " + quotify(m.messageId) +
- ") ";
- if (internaldate) reply += quotify(m.arrival) + " ";
- // FIXME
- }
- public static Query parseQuery() {
- String tok = null;
+ public static Query query() {
+ String s = null;
boolean not = false;
while(true) {
Token t = token();
- if (t.type == t.LIST) { }
- else if (t.type == t.SET) { }
- tok = t.atom();
- if (tok.equals("NOT")) { not = true; continue; }
- if (tok.equals("OR")) { /* FIXME */ }
- if (tok.equals("AND")) { /* FIXME */ }
+ if (t.type == t.LIST) { /* FIXME */ }
+ else if (t.type == t.SET) return new Query.Set(t.set());
+ s = t.atom();
+ if (s.equals("NOT")) { not = true; continue; }
+ if (s.equals("OR")) { return new Query.OR(new Query[] { query(), query() }); }
+ if (s.equals("AND")) { return new Query.AND(new Query[] { query(), query() }); }
break;
}
- if (tok.startsWith("UN")) { not = true; tok = tok.substring(2); }
- if (tok.equals("ANSWERED")) q = new Query.Flag(Query.Flag.ANSWERED);
- else if (tok.equals("DELETED")) q = new Query.Flag(Query.Flag.ANSWERED);
- else if (tok.equals("DRAFT")) q = new Query.Flag(Query.Flag.ANSWERED);
- else if (tok.equals("FLAGGED")) q = new Query.Flag(Query.Flag.ANSWERED);
- else if (tok.equals("RECENT")) q = new Query.Flag(Query.Flag.ANSWERED);
- else if (tok.equals("SEEN")) q = new Query.Flag(Query.Flag.ANSWERED);
- else if (tok.equals("OLD")) { not = true; q = new Query.Flag(Query.Flag.RECENT); }
- else if (tok.equals("NEW")) q = new Query.And(new Query.Flag(Query.Flag.RECENT),
- new Query.Not(new Query.Flag(Query.Flag.SEEN)));
- else if (tok.equals("KEYWORD")) q = new Query.Header("keyword", flag());
- else if (tok.equals("HEADER")) q = new Query.Header(astring(), astring());
- else if (tok.equals("BCC")) q = new Query.Header("bcc", astring());
- else if (tok.equals("CC")) q = new Query.Header("cc", astring());
- else if (tok.equals("FROM")) q = new Query.Header("from", astring());
- else if (tok.equals("TO")) q = new Query.Header("to", astring());
- else if (tok.equals("SUBJECT")) q = new Query.Header("subject", astring());
- else if (tok.equals("BEFORE")) q = new Query.Arrival(date(), true, false);
- else if (tok.equals("SINCE")) q = new Query.Arrival(date(), false, true);
- else if (tok.equals("ON")) q = new Query.Arrival(date(), true, true);
- else if (tok.equals("LARGER")) q = new Query.Size(n(), true);
- else if (tok.equals("SMALLER")) q = new Query.Size(n(), false);
- else if (tok.equals("BODY")) q = new Query.FullText(astring(), true, false);
- else if (tok.equals("TEXT")) q = new Query.FullText(astring(), true, true);
- else if (tok.equals("SENTBEFORE")) { /* FIXME date() */ }
- else if (tok.equals("SENTSINCE")) { /* FIXME date() */ }
- else if (tok.equals("SENTON")) { /* FIXME date() */ }
- else if (tok.equals("UID")) { /* FIXME set() */ }
+ if (s.startsWith("UN")) { not = true; tok = s.substring(2); }
+ if (s.equals("ANSWERED")) q = new Query.Flag(Query.Flag.ANSWERED);
+ else if (s.equals("DELETED")) q = new Query.Flag(Query.Flag.ANSWERED);
+ else if (s.equals("DRAFT")) q = new Query.Flag(Query.Flag.ANSWERED);
+ else if (s.equals("FLAGGED")) q = new Query.Flag(Query.Flag.ANSWERED);
+ else if (s.equals("RECENT")) q = new Query.Flag(Query.Flag.ANSWERED);
+ else if (s.equals("SEEN")) q = new Query.Flag(Query.Flag.ANSWERED);
+ else if (s.equals("OLD")) { not = true; q = new Query.Flag(Query.Flag.RECENT); }
+ else if (s.equals("NEW"))q=new Query.And(new Query.Flag(Query.Flag.RECENT),new Query.Not(new Query.Flag(Query.Flag.SEEN)));
+ else if (s.equals("KEYWORD")) q = new Query.Header("keyword", flag());
+ else if (s.equals("HEADER")) q = new Query.Header(astring(), astring());
+ else if (s.equals("BCC")) q = new Query.Header("bcc", astring());
+ else if (s.equals("CC")) q = new Query.Header("cc", astring());
+ else if (s.equals("FROM")) q = new Query.Header("from", astring());
+ else if (s.equals("TO")) q = new Query.Header("to", astring());
+ else if (s.equals("SUBJECT")) q = new Query.Header("subject", astring());
+ else if (s.equals("LARGER")) q = new Query.Size(n(), true);
+ else if (s.equals("SMALLER")) q = new Query.Size(n(), false);
+ else if (s.equals("BODY")) q = new Query.FullText(astring(), true, false);
+ else if (s.equals("TEXT")) q = new Query.FullText(astring(), true, true);
+ else if (s.equals("BEFORE")) q = new Query.Arrival(date(), true, false);
+ else if (s.equals("SINCE")) q = new Query.Arrival(date(), false, true);
+ else if (s.equals("ON")) q = new Query.Arrival(date(), true, true);
+ else if (s.equals("SENTBEFORE")) q = new Query.Sent(date(), true, false);
+ else if (s.equals("SENTSINCE")) q = new Query.Sent(date(), false, true);
+ else if (s.equals("SENTON")) q = new Query.Sent(date(), true, true);
+ else if (s.equals("UID")) q = new Query.UID(set());
}
private static class Token {
public void deleteFlags(Message m) { }
public String mailboxPattern() {
- /* 1*(ATOM_CHAR / "%" / "*") / string */
+ if (type == ATOM) retrn s;
+ if (type == QUOTED) return s;
+ throw new Bad("exepected a mailbox pattern");
}
public String flag() throws Bad {
if (type != ATOM) throw new Bad("expected a flag");
if (type != QUOTED) throw new Bad("expected qstring");
return s;
}
- public String nstring() throws Bad {
- }
public Token[] l() throws Bad {
if (type == NIL) return null;
if (type != LIST) throw new Bad("expected parenthesized list");
return l;
}
public int[] set() throws Bad {
- // FIXME <set>,<set> <n> <n>:<n> * (* = largest)
+ if (type != ATOM) throw new Bad("expected a messageid set");
+ Vec ids = new Vec();
+ StringTokenizer st = new StringTokenizer(s, ",");
+ while(st.hasMoreTokens()) {
+ String s = st.nextToken();
+ if (s.indexOf(':') != -1) {
+ int start = Integer.parseInt(s.substring(0, s.indexOf(':')));
+ String end_s = s.indexOf(':'+1);
+ if (end_s.equals("*")) {
+ ids.addElement(new Integer(start));
+ ids.addElement(new Integer(-1));
+ } else {
+ int end = Integer.parseInt(end_s);
+ for(int j=start; j<=end; j++) ids.addElement(new Integer(j));
+ }
+ } else {
+ ids.addElement(new Integer(Integer.parseInt(s)));
+ }
+ }
+ int[] ret = new int[ids.size()];
+ ids.copyInto(ret);
+ return ret;
}
public Date date() throws Bad {
- // may be quoted or unquoted
- try {
- new SimpleDateFormat("dd-MMM-yyyy").parse(s);
- } catch (DateParsingException) {
- }
+ if (type != QUOTED && type != ATOM) throw new Bad("Expected quoted or unquoted date");
+ try { new SimpleDateFormat("dd-MMM-yyyy").parse(s);
+ } catch (ParseException p) { throw new Bad("invalid date format; " + p); }
+ }
+ public Date datetime() throws Bad {
+ if (type != QUOTED && type != ATOM) throw new Bad("Expected quoted or unquoted datetime");
+ try { new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss +zzzz").parse(s);
+ } catch (ParseException p) { throw new Bad("invalid datetime format; " + p); }
}
public String nstring() throws Bad {
if (type == NIL) return null;
throw new Bad("expected atom or string");
}
public String atom() throws Bad {
- // ATOM_CHAR ::= <any CHAR except atom_specials>
- // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials
if (type != ATOM) throw new Bad("expected atom");
+ for(int i=0; i<s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == '(' || c == ')' || c == '{' || c == ' ' || c == '%' || c == '*')
+ throw new Bad("invalid char in atom: " + c);
+ }
+ return s;
}
public Mailbox mailbox() throws Bad {
if (type == BAREWORD && s.toLowerCase().equals("inbox")) return Mailbox.INBOX;
return Mailbox.getMailbox(astring());
}
- public Date datetime() throws Bad {
- // expected to be a quoted string
-
- new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss +zzzz").parse(s);
- }
}
+ public static char getc() { return chars[pos++]; }
+ public static char peekc() { return chars[pos]; }
+ public static char ungetc() { pos--; }
-
- // literal ::= "{" number "}" CRLF *CHAR8 ;; Number represents the number of CHAR8 octets
-
- public static Token[] tokenize(String s) {
+ public static Token token() {
Vec toks = new Vec();
StringBuffer sb = new StringBuffer();
- for(int i=0; i<s.length();) {
- sb.setSize(0);
- if (s.charAt(i) == '\"') {
- for(i++; i < s.length(); i++) {
- if (s.charAt(i) == '\\') sb.append(s.charAt(++i));
- else if (s.charAt(i) == '\"') { i++; break; }
- else sb.append(s.charAt(i));
- }
- } else if (s.charAt(i) == '(') {
- // FIXME, deal with strings within parens
- } else {
- for(; i < s.length(); i++)
- if (s.charAt(i) == ' ' || s.charAt(i) == '\"') break;
- else sb.append(s.charAt(i));
- }
- toks.addElement(sb.toString());
- while (i < s.length() && s.charAt(i) == ' ') i++;
+ char c = getc(); while(c == ' ') c = getc();
+ if (c == '{') {
+ while(peekc() != '}') sb.append(getc());
+ int octets = Integer.parseInt(sb.toString());
+ while(peekc() == ' ') getc(); // whitespace
+ if (peekc() == '\r') getc();
+ if (peekc() == '\n') getc();
+ // FIXME: read octets number of octets
+ } else if (c == '\"') {
+ c = getc();
+ if (c == '\\') sb.append(getc());
+ else if (c == '\"') break;
+ else sb.append(c);
+ } else if (c == ')') {
+ return null;
+ } else if (c == '(') {
+ Vec toks = new Vector();
+ do { Token t = token(); if (t != null) toks.addElement(t); } while (t != null);
+ Token[] ret = new Token[toks.size()];
+ toks.copyInto(ret);
+ return ret;
+ } else while(true) {
+ c = peekc();
+ if (c == ' ' || c == '\"' || c == '(' || c == ')' || c == '{') break;
+ sb.append(getc());
}
return toks.toArray();
}