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.*;
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);
}
}
}