more robust parsing using token(boolean)
[org.ibex.mail.git] / src / org / ibex / mail / IMAP.java
index 0632ea4..582a36d 100644 (file)
@@ -4,8 +4,8 @@
 
 package org.ibex.mail;
 import org.ibex.io.*;
+import org.ibex.net.*;
 import org.ibex.crypto.*;
-import org.ibex.jinetd.Listener;
 import org.ibex.mail.*;
 import org.ibex.util.*;
 import org.ibex.mail.target.*;
@@ -25,6 +25,10 @@ import java.io.*;
 //   RFC 3691: UNSELECT
 //   RFC 2971: ID
 
+// to review
+//   RFC 4466
+//   RFC 4469
+
 // FEATURE: make sure we're being case-insensitive enough; probably want to upcase atom()
 // FEATURE: MIME-queries and BODYSTRUCTURE
 // FEATURE: READ-WRITE / READ-ONLY status on SELECT
@@ -41,13 +45,6 @@ public class IMAP {
     public IMAP() { }
     public static final float version = (float)0.2;
 
-    // FIXME this is evil
-    public static String getBodyString(Message m) {
-        StringBuffer sb = new StringBuffer();
-        m.getBody().getStream().transcribe(sb);
-        return sb.toString();
-    }
-
     // API Class //////////////////////////////////////////////////////////////////////////////
 
     public static interface Client {
@@ -179,6 +176,7 @@ public class IMAP {
         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, false); }
         public void append(String m,int f,Date a,String b) { try {
+            // FIXME: more efficient streaming here?
             mailbox(m,false).insert(Message.newMessage(new Fountain.StringFountain(b)), f | Mailbox.Flag.RECENT);
         } catch (Message.Malformed e) { throw new No(e.getMessage()); } }
         public void check() { }
@@ -220,7 +218,7 @@ public class IMAP {
             for(Mailbox.Iterator it = selected().iterator(q);it.next();) {
                 // FIXME: what's going on here with the recent flag?
                 //boolean recent = it.getFlag(Mailbox.Flag.RECENT);
-                try { throw new Exception("flags " + flags); } catch (Exception e) { Log.error(this, e); }
+                //try { throw new Exception("flags " + flags); } catch (Exception e) { Log.error(this, e); }
                 if (style == -1)     it.setFlags(it.getFlags() & ~flags);
                 else if (style == 0) it.setFlags(flags);
                 else if (style == 1) it.setFlags(it.getFlags() | flags);
@@ -239,8 +237,8 @@ public class IMAP {
             for(Mailbox.Iterator it = selected().iterator(q); it.next(); ) {
                 Message message = ((spec & (BODYSTRUCTURE | ENVELOPE | INTERNALDATE | FIELDS | FIELDSNOT | RFC822 |
                                             RFC822TEXT | RFC822SIZE | HEADERNOT | HEADER)) != 0) ? it.cur() : null;
-                int size = message == null ? 0 : message.getLength();
-                client.fetch(it.imapNumber(), it.getFlags(), size, message, it.uid());
+                long size = message == null ? 0 : message.getLength();
+                client.fetch(it.imapNumber(), it.getFlags(), (int)size, message, it.uid());
                 it.setFlags(it.getFlags() & ~Mailbox.Flag.RECENT);
             }
         }
@@ -258,7 +256,11 @@ public class IMAP {
         Login auth;
         public Listener(Login auth) { api = new IMAP.MailboxWrapper(this.auth = auth, this); }
         Parser.Token token() { return parser.token(); }
-        void println(String s) { conn.println(s); }
+        Parser.Token token(boolean freak) { return parser.token(freak); }
+        void println(String s) {
+            conn.println(s);
+            Log.info("", s);
+        }
         void newline() { parser.newline(); }
         Query query(int max) { return parser.query(max, maxn(true)); }
 
@@ -308,10 +310,10 @@ public class IMAP {
                     case EXPUNGE:      selected(); api.expunge(); break;
                     case UNSELECT:     selected(); api.unselect(); selected = false; break;
                     case CREATE:       api.create(token().astring()); break;
-                    case FETCH:        selected(); fetch(((lastuid=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;
@@ -356,7 +358,8 @@ public class IMAP {
                         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(); }
+                        Parser.Token t2 = token(false);
+                        if (t2 != null) { arrival = t.datetime(); t = t2; }
                         api.append(m, flags, arrival, t.q());
                         break; }
                     case STORE: {
@@ -457,8 +460,8 @@ public class IMAP {
                 } 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("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(getBodyString(m)));}
-                } else if (s.equals("RFC822.HEADER")){ spec|=HEADER;if(e){r.append(" ");r.append(Printer.qq(m.headers.getString()+"\r\n"));}
+                } else if (s.equals("RFC822.TEXT")) {  spec|=RFC822TEXT;   if(e){r.append(" ");r.append(Printer.qq(m.getStream()));}
+                } else if (s.equals("RFC822.HEADER")){ spec|=HEADER;       if(e){r.append(" ");r.append(Printer.qq(m.headers.getStream().append("\r\n")));}
                 } else if (s.equals("RFC822.SIZE")) {  spec|=RFC822SIZE;   if(e){r.append(" ");r.append(m.getLength());}
                 } 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);
@@ -470,17 +473,17 @@ public class IMAP {
                        if (e) { r.append(" "); r.append(Printer.bodystructure(m)); } continue;
                        //{ if (e) { r.append(" "); r.append(Printer.qq(m.body)); } continue; }
                    }
-                    String payload = "";
+                    Fountain payload = Fountain.Util.create("");
                     r.append("[");
                     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.headers.getString()+"\r\n"+getBodyString(m); }
-                    else if (s.equals("") || s.equals("1")) { spec |= RFC822TEXT;   if(e) payload = m.headers.getString()+"\r\n"+getBodyString(m); }
-                    else if (s.equals("TEXT"))              { spec |= RFC822TEXT;   if(e) payload = getBodyString(m); }
-                    else if (s.equals("HEADER"))            { spec |= HEADER;       if(e) payload = m.headers.getString()+"\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); }
+                    if (list.length == 0)                   { spec |= RFC822TEXT;   if(e) payload = m; }
+                    else if (s.equals("") || s.equals("1")) { spec |= RFC822TEXT;   if(e) payload = m; }
+                    else if (s.equals("TEXT"))              { spec |= RFC822TEXT;   if(e) payload = m.getBody(); }
+                    else if (s.equals("HEADER"))            { spec |= HEADER;       if(e) payload = Fountain.Util.concat(m.headers, Fountain.Util.create("\r\n")); }
+                    else if (s.equals("HEADER.FIELDS"))     { spec |= FIELDS;       payload=Fountain.Util.create(headers(r,t[i].l()[1].sl(),false,m,e)); }
+                    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("<"))) {
@@ -489,9 +492,13 @@ public class IMAP {
                         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));
-                        if (e) { payload = payload.substring(start, Math.min(end+1,payload.length())); r.append("<"+start+">"); }
+                        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 (e) { r.append("] "); r.append(Printer.qq(payload)); }
+                    if (e) { r.append("] "); r.append(Printer.qq(payload.getStream())); }
                 }
             }
             if (e) {