preliminary GMail support
authoradam <adam@megacz.com>
Fri, 3 Sep 2004 00:55:35 +0000 (00:55 +0000)
committeradam <adam@megacz.com>
Fri, 3 Sep 2004 00:55:35 +0000 (00:55 +0000)
darcs-hash:20040903005535-5007d-1e15174bdd354c04a3df28f0243d41c05b3e8632.gz

src/org/ibex/mail/protocol/GMail.java

index 058f821..8b277f5 100644 (file)
@@ -5,6 +5,8 @@ import org.ibex.jinetd.Listener;
 import org.ibex.jinetd.Worker;
 import org.ibex.mail.*;
 import org.ibex.util.*;
+import org.ibex.net.*;
+import org.ibex.js.*;
 import org.ibex.mail.target.*;
 import java.util.*;
 import java.net.*;
@@ -12,33 +14,262 @@ import java.text.*;
 import java.io.*;
 
 public class GMail extends Account {
-    public Mailbox getMailbox(Class protocol) { return this.root; }
-    public GMail(String user, String pass) {
-        super(user, Address.parse(user + "@gmail.com"));
-        Log.warn(GMail.class, "logging in " + user + "@gmail.com...");
+
+    public static final String login = "https://www.google.com/accounts/ServiceLoginBoxAuth";
+    public static final String gmail = "https://gmail.google.com/gmail";
+
+    private HTTP.Cookie.Jar jar = new HTTP.Cookie.Jar();
+    private String captchaString = null;
+    private String ctoken = null;
+    private String email = null;
+    private String password = null;
+
+
+    // Constructor //////////////////////////////////////////////////////////////////////////////
+
+    private static Hash cache = new Hash();
+    static { HTTP.userAgent = "Mozilla/5.0 (compatible;)"; }
+    public static GMail getGMail(String email, String pass) {
         try {
-            Process p =
-                Runtime.getRuntime().exec(new String[] {
-                    "/usr/bin/python",
-                    "/usr/local/libgmail/demos/archive.py",
-                    user,
-                    pass
-                    });
-            final Mailbox inbox = new MessageArrayMailbox(Mbox.parse(new Stream(p.getInputStream())));
-            this.root =
-                new Mailbox.Default() {
-                        public void add(Message m) { throw new RuntimeException("not supported"); }
-                        public void add(Message m, int i) { throw new RuntimeException("not supported"); }
-                        public int              uidValidity()  { return 1; }
-                        public Mailbox.Iterator iterator()     { return new Mailbox.Iterator.NullIterator(); }
-                        public int              uidNext()      { return 500; }
-                        public String[] children() { return new String[] { "gmail" }; }
-                        public Mailbox slash(String name, boolean create) { return inbox; }
-                    };
-            p.waitFor();
-            Log.warn(GMail.class, "    succeeded for " + user + "@gmail.com!");
+            GMail g = (GMail)cache.get(email, pass);
+            if (g == null) cache.put(email, pass, g = new GMail(email, pass));
+            g.init();
+            return g;
         } catch (Exception e) {
             Log.error(GMail.class, e);
+            return null;
+        }
+    }
+
+    public GMail(String email, String pass) throws IOException {
+        super(email.substring(0, email.indexOf('@')), Address.parse(email));
+        this.email = email; this.password = pass;
+        Log.warn(GMail.class, "logging in " + email);
+    }
+
+    void init() throws IOException {
+        if (success) return;
+        Vector v = makeRequest();
+        Message[] m = new Message[v.size()];
+        v.copyInto(m);
+        final Mailbox inbox = new MessageArrayMailbox(m);
+        this.root =
+            new Mailbox.Default() {
+                public void add(Message m)             { throw new RuntimeException("not supported"); }
+                public void add(Message m, int i)      { throw new RuntimeException("not supported"); }
+                public Mailbox.Iterator iterator()     { return new Mailbox.Iterator.NullIterator(); }
+                public int              uidNext()      { return 500; }
+                public String[] children() {
+                    if (success) return new String[] { "INBOX" };
+                    return new String[] { "Captcha" };
+                }
+                public Mailbox slash(String name, boolean create) { return inbox; }
+            };
+        Log.warn(GMail.class, "    succeeded for " + email);
+    }
+
+
+    // Implementation //////////////////////////////////////////////////////////////////////////////
+
+    public Vector makeRequest() throws IOException {
+        final Vector messages = new Vector();
+        Log.warn(this, "captchaString = " + captchaString);
+        Log.warn(this, "ctoken = " + ctoken);
+        String crud =
+            "continue="     + URLEncoder.encode(gmail) +
+            "&service=mail" +
+            "&Email="       + URLEncoder.encode(user) +
+            "&Passwd="      + password + 
+            "&null=Sign+in" +
+            (captchaString != null ? "&captcha="+URLEncoder.encode(captchaString) : "") +
+            (captchaString != null ? "&ctoken="+URLEncoder.encode(ctoken) : "");
+        captchaString = null;
+        Log.warn("[request]", crud);
+
+        String result =
+            captchaString == null ?
+            new String(InputStreamToByteArray.convert(new HTTP(login+"?"+crud).GET(null, jar)))
+            :
+            new String(InputStreamToByteArray.convert(new HTTP(login).POST("application/x-www-form-urlencoded", crud, null, jar)));
+        System.err.println(result);
+        
+        try {
+            if (result.indexOf("top.location") == -1) {
+                Log.warn(GMail.class,"no relocator found; checking for captcha");
+                ctoken = result.substring(result.indexOf("id=\"ctoken\" value=\"") + "id=\"ctoken\" value=\"".length());
+                ctoken = ctoken.substring(0, ctoken.indexOf("\""));
+
+                Log.warn(this, "captchaString = " + captchaString);
+                Log.warn(this, "ctoken = " + ctoken);
+
+                String image = result.substring(result.indexOf("Captcha?"));
+                image = image.substring(0, image.indexOf("\""));
+                Message m = new Message(new Stream(
+                                                   "From: google@google.com\r\n" +
+                                                   "To: you@yourself.com\r\n" +
+                                                   "Subject: Captcha\r\n" +
+                                                   "Date: Mon Aug 30 19:05:40 PDT 2004\r\n" +
+                                                   "Content-Type: text/html\r\n" +
+                                                   "\r\n" +
+                                                   "<html><body>\r\n" +
+                                                   "Hi there.  Google is lame; please type in the word you see below and " +
+                                                   "click submit.  You might have to click 'get mail' again after that.<br>  " +
+                                                   "<img src=\"https://www.google.com/accounts/"+image+"\">\r\n" +
+                                                   "<form method=get action=http://testing.megacz.com:8099/Captcha>\r\n"+
+                                                   "  <input type=text name=captcha>\r\n"+
+                                                   "  <input type=hidden name=email value=\""+email+"\">\r\n"+
+                                                   "  <input type=hidden name=pass value="+password+">\r\n"+
+                                                   "  <input type=hidden name=ctoken value=\""+ctoken+"\">\r\n"+
+                                                   "  <input type=submit>\r\n"+
+                                                   "</form>\r\n"+
+                                                   "</body></html>\r\n"),
+                                        null);
+                messages.addElement(m);
+                return messages;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return messages;
+        }
+        result = result.substring(result.indexOf("top.location"));
+        result = result.substring(result.indexOf("CheckCookie?continue=") + "CheckCookie?continue=".length());
+        result = result.substring(0, result.indexOf('\"'));
+        result = URLDecoder.decode(result);
+        Log.warn("[]", "getting " + result);
+
+        // just need the cookie off of this page
+        String s = new String(InputStreamToByteArray.convert(new HTTP(result).GET(null, jar)));
+        Log.warn("[]", s);
+
+        final Semaphore sem = new Semaphore();
+        try {
+            JSArray ret = grab(gmail + "?search=inbox&start=0&view=tl", jar);
+            for(int i=0; i<ret.length(); i++) {
+                JSArray j = (JSArray)ret.get(i);
+                if (!j.elementAt(0).equals("t")) continue;
+                for(int k=1; k<j.length(); k++) {
+                    final String threadid = (String)((JSArray)j.get(k)).get(0);
+                    sem.dec();
+                    new Thread() { public void run() {
+                        try {
+                            // FIXME: future: pipeline these requests over a single socket (maybe two)
+                            JSArray js2 =
+                                grab(gmail + "?search=query&start=0&view=cv&q=in:anywhere&th=" + URLEncoder.encode(threadid), jar);
+                            for(int i2=0; i2<js2.length(); i2++) {
+                                JSArray j2 = (JSArray)js2.get(i2);
+                                if (j2.elementAt(0).equals("t")) {
+                                    for(int k2=1; k2<j2.length(); k2++) {
+                                        JSArray m = (JSArray)j2.get(k2);
+                                        String from = (String)m.get(4);
+                                        String email = from.substring(from.indexOf("_email_") + "_email_".length());
+                                        email = email.substring(0, email.indexOf("\'"));
+                                        String name = from.substring(from.indexOf('>') + 1);
+                                        name = name.substring(0, name.indexOf("</span>"));
+                                        String subject = (String)m.get(6);
+                                        //Log.warn("[]", name + " <"+email+"> " + subject);
+                                        //Log.warn("[]", );
+                                    }
+                                } else if (j2.elementAt(0).equals("mi")) {
+                                    JSArray m = j2;
+                                    String date = m.get(9).toString();
+                                    String to = m.get(8).toString();
+                                    String toemail = m.get(10).toString();
+                                    String from = m.get(4).toString();
+                                    String name = m.get(6).toString();
+                                    String email = m.get(7).toString();
+                                    String subject = m.get(15).toString();
+                                    Log.warn("[]", name + " <"+email+"> " + subject);
+                                    Stream thestream = new Stream(new HTTP(gmail+"?search=query&start=0&view=om&th=" +
+                                                                           URLEncoder.encode(threadid)).GET(null, jar));
+                                    thestream.readln();
+                                    messages.addElement(new Message(thestream, null));
+                                }
+                            }
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+                            sem.release();
+                        }
+                    } }.start();
+                }
+            }
+            sem.release();
+            Log.warn(GMail.class, "block");
+            sem.block();
+            Log.warn(GMail.class, "release");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        success = true;
+        for(int i=0; i<connections.size(); i++)
+            try {
+                ((Connection)connections.elementAt(i)).close();
+            } catch (Exception e) {
+                Log.error(this, e);
+            }
+        return messages;
+    }
+
+    boolean success = false;
+
+    public JSArray grab(String url, HTTP.Cookie.Jar jar) throws JSExn, IOException  {
+        Stream stream = new Stream(new HTTP(url).GET(null, jar));                    
+        boolean inscript = false;
+        StringBuffer buf = new StringBuffer("var ret = []; var D = function(x){ret.push(x);};");
+        String s = null;
+        while((s = stream.readln()) != null) {
+            if (s.indexOf("<script>") != -1)  { inscript = true; continue; }
+            if (s.indexOf("</script>") != -1) { inscript = false; continue; }
+            if (inscript) buf.append(s);
+        }
+        buf.append("return ret;");
+        synchronized(GMail.class) {
+            JS js = JS.fromReader("google", 0, new StringReader(buf.toString()));
+            return (JSArray)js.call(null, null, null, null, 0);
+        }
+    }
+
+    public Mailbox getMailbox(Class protocol) { return this.root; }
+
+
+    // HTTP Listener for Captcha requests //////////////////////////////////////////////////////////////////////////////
+    
+    public static void handleRequest(Connection conn) {
+        String top = null;
+        for(String s = conn.readln(); s != null && s.length() > 0; s = conn.readln()) {
+            if (top == null) top = s;
+            Log.warn(GMail.class, s);
+        }
+        if (top.startsWith("GET /Captcha")) {
+            top = top.substring(top.indexOf('?')+1);
+            top = top.substring(0, top.indexOf(' '));
+            StringTokenizer st = new StringTokenizer(top, "&");
+            Hash h = new Hash();
+            while(st.hasMoreTokens()) {
+                String tok = st.nextToken();
+                h.put(URLDecoder.decode(tok.substring(0, tok.indexOf('='))),
+                      URLDecoder.decode(tok.substring(tok.indexOf('=')+1)));
+            }
+            ((GMail)cache.get((String)h.get("email"), (String)h.get("pass"))).setCaptcha(h);
+            conn.println("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n<html><body><script>window.close()</script></body></html>\r\n");
+        } else {
+            conn.println("HTTP/1.0 500 Error\r\n\r\n");
+        }
+        conn.flush();
+        conn.close();
+    }
+
+    public void setCaptcha(Hash h) {
+        captchaString = (String)h.get("captcha");
+        ctoken = (String)h.get("ctoken");
+        Log.warn(GMail.class, "captchaString = " + captchaString);
+        Log.warn(GMail.class, "ctoken = " + ctoken);
+        Log.warn(GMail.class, "initting..." + ctoken);
+        try {
+            init();
+            Log.warn(GMail.class, "  done..." + ctoken);
+        } catch (Exception e) {
+            Log.error(this, e);
         }
     }
 }