1 package org.ibex.mail.protocol;
3 import org.ibex.crypto.*;
4 import org.ibex.jinetd.Listener;
5 import org.ibex.jinetd.Worker;
6 import org.ibex.mail.*;
7 import org.ibex.util.*;
10 import org.ibex.mail.target.*;
16 public class GMail extends Account {
18 public static final String login = "https://www.google.com/accounts/ServiceLoginBoxAuth";
19 public static final String gmail = "https://gmail.google.com/gmail";
21 private HTTP.Cookie.Jar jar = new HTTP.Cookie.Jar();
22 private String captchaString = null;
23 private String ctoken = null;
24 private String email = null;
25 private String password = null;
28 // Constructor //////////////////////////////////////////////////////////////////////////////
30 private static Hash cache = new Hash();
31 static { HTTP.userAgent = "Mozilla/5.0 (compatible;)"; }
32 public static GMail getGMail(String email, String pass) {
34 GMail g = (GMail)cache.get(email, pass);
35 if (g == null) cache.put(email, pass, g = new GMail(email, pass));
38 } catch (Exception e) {
39 Log.error(GMail.class, e);
44 public GMail(String email, String pass) throws IOException {
45 super(email.substring(0, email.indexOf('@')), Address.parse(email));
46 this.email = email; this.password = pass;
47 Log.warn(GMail.class, "logging in " + email);
50 void init() throws IOException {
52 Vector v = makeRequest();
53 Message[] m = new Message[v.size()];
55 final Mailbox inbox = new MessageArrayMailbox(m);
57 new Mailbox.Default() {
58 public void add(Message m) { throw new RuntimeException("not supported"); }
59 public void add(Message m, int i) { throw new RuntimeException("not supported"); }
60 public Mailbox.Iterator iterator() { return new Mailbox.Iterator.NullIterator(); }
61 public int uidNext() { return 500; }
62 public String[] children() {
63 if (success) return new String[] { "INBOX" };
64 return new String[] { "Captcha" };
66 public Mailbox slash(String name, boolean create) { return inbox; }
68 Log.warn(GMail.class, " succeeded for " + email);
72 // Implementation //////////////////////////////////////////////////////////////////////////////
74 public Vector makeRequest() throws IOException {
75 final Vector messages = new Vector();
76 Log.warn(this, "captchaString = " + captchaString);
77 Log.warn(this, "ctoken = " + ctoken);
79 "continue=" + URLEncoder.encode(gmail) +
81 "&Email=" + URLEncoder.encode(user) +
82 "&Passwd=" + password +
84 (captchaString != null ? "&captcha="+URLEncoder.encode(captchaString) : "") +
85 (captchaString != null ? "&ctoken="+URLEncoder.encode(ctoken) : "");
87 Log.warn("[request]", crud);
90 captchaString == null ?
91 new String(InputStreamToByteArray.convert(new HTTP(login+"?"+crud).GET(null, jar)))
93 new String(InputStreamToByteArray.convert(new HTTP(login).POST("application/x-www-form-urlencoded", crud, null, jar)));
94 System.err.println(result);
97 if (result.indexOf("top.location") == -1) {
98 Log.warn(GMail.class,"no relocator found; checking for captcha");
99 ctoken = result.substring(result.indexOf("id=\"ctoken\" value=\"") + "id=\"ctoken\" value=\"".length());
100 ctoken = ctoken.substring(0, ctoken.indexOf("\""));
102 Log.warn(this, "captchaString = " + captchaString);
103 Log.warn(this, "ctoken = " + ctoken);
105 String image = result.substring(result.indexOf("Captcha?"));
106 image = image.substring(0, image.indexOf("\""));
107 Message m = new Message(new Stream(
108 "From: google@google.com\r\n" +
109 "To: you@yourself.com\r\n" +
110 "Subject: Captcha\r\n" +
111 "Date: Mon Aug 30 19:05:40 PDT 2004\r\n" +
112 "Content-Type: text/html\r\n" +
115 "Hi there. Google is lame; please type in the word you see below and " +
116 "click submit. You might have to click 'get mail' again after that.<br> " +
117 "<img src=\"https://www.google.com/accounts/"+image+"\">\r\n" +
118 "<form method=get action=http://testing.megacz.com:8099/Captcha>\r\n"+
119 " <input type=text name=captcha>\r\n"+
120 " <input type=hidden name=email value=\""+email+"\">\r\n"+
121 " <input type=hidden name=pass value="+password+">\r\n"+
122 " <input type=hidden name=ctoken value=\""+ctoken+"\">\r\n"+
123 " <input type=submit>\r\n"+
125 "</body></html>\r\n"),
127 messages.addElement(m);
130 } catch (Exception e) {
134 result = result.substring(result.indexOf("top.location"));
135 result = result.substring(result.indexOf("CheckCookie?continue=") + "CheckCookie?continue=".length());
136 result = result.substring(0, result.indexOf('\"'));
137 result = URLDecoder.decode(result);
138 Log.warn("[]", "getting " + result);
140 // just need the cookie off of this page
141 String s = new String(InputStreamToByteArray.convert(new HTTP(result).GET(null, jar)));
144 final Semaphore sem = new Semaphore();
146 JSArray ret = grab(gmail + "?search=inbox&start=0&view=tl", jar);
147 for(int i=0; i<ret.length(); i++) {
148 JSArray j = (JSArray)ret.get(i);
149 if (!j.elementAt(0).equals("t")) continue;
150 for(int k=1; k<j.length(); k++) {
151 final String threadid = (String)((JSArray)j.get(k)).get(0);
153 new Thread() { public void run() {
155 // FIXME: future: pipeline these requests over a single socket (maybe two)
157 grab(gmail + "?search=query&start=0&view=cv&q=in:anywhere&th=" + URLEncoder.encode(threadid), jar);
158 for(int i2=0; i2<js2.length(); i2++) {
159 JSArray j2 = (JSArray)js2.get(i2);
160 if (j2.elementAt(0).equals("t")) {
161 for(int k2=1; k2<j2.length(); k2++) {
162 JSArray m = (JSArray)j2.get(k2);
163 String from = (String)m.get(4);
164 String email = from.substring(from.indexOf("_email_") + "_email_".length());
165 email = email.substring(0, email.indexOf("\'"));
166 String name = from.substring(from.indexOf('>') + 1);
167 name = name.substring(0, name.indexOf("</span>"));
168 String subject = (String)m.get(6);
169 //Log.warn("[]", name + " <"+email+"> " + subject);
172 } else if (j2.elementAt(0).equals("mi")) {
174 String date = m.get(9).toString();
175 String to = m.get(8).toString();
176 String toemail = m.get(10).toString();
177 String from = m.get(4).toString();
178 String name = m.get(6).toString();
179 String email = m.get(7).toString();
180 String subject = m.get(15).toString();
181 Log.warn("[]", name + " <"+email+"> " + subject);
182 Stream thestream = new Stream(new HTTP(gmail+"?search=query&start=0&view=om&th=" +
183 URLEncoder.encode(threadid)).GET(null, jar));
185 messages.addElement(new Message(thestream, null));
188 } catch (Exception e) {
197 Log.warn(GMail.class, "block");
199 Log.warn(GMail.class, "release");
200 } catch (Exception e) {
204 for(int i=0; i<connections.size(); i++)
206 ((Connection)connections.elementAt(i)).close();
207 } catch (Exception e) {
213 boolean success = false;
215 public JSArray grab(String url, HTTP.Cookie.Jar jar) throws JSExn, IOException {
216 Stream stream = new Stream(new HTTP(url).GET(null, jar));
217 boolean inscript = false;
218 StringBuffer buf = new StringBuffer("var ret = []; var D = function(x){ret.push(x);};");
220 while((s = stream.readln()) != null) {
221 if (s.indexOf("<script>") != -1) { inscript = true; continue; }
222 if (s.indexOf("</script>") != -1) { inscript = false; continue; }
223 if (inscript) buf.append(s);
225 buf.append("return ret;");
226 synchronized(GMail.class) {
227 JS js = JS.fromReader("google", 0, new StringReader(buf.toString()));
228 return (JSArray)js.call(null, null, null, null, 0);
232 public Mailbox getMailbox(Class protocol) { return this.root; }
235 // HTTP Listener for Captcha requests //////////////////////////////////////////////////////////////////////////////
237 public static void handleRequest(Connection conn) {
239 for(String s = conn.readln(); s != null && s.length() > 0; s = conn.readln()) {
240 if (top == null) top = s;
241 Log.warn(GMail.class, s);
243 if (top.startsWith("GET /Captcha")) {
244 top = top.substring(top.indexOf('?')+1);
245 top = top.substring(0, top.indexOf(' '));
246 StringTokenizer st = new StringTokenizer(top, "&");
248 while(st.hasMoreTokens()) {
249 String tok = st.nextToken();
250 h.put(URLDecoder.decode(tok.substring(0, tok.indexOf('='))),
251 URLDecoder.decode(tok.substring(tok.indexOf('=')+1)));
253 ((GMail)cache.get((String)h.get("email"), (String)h.get("pass"))).setCaptcha(h);
254 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");
256 conn.println("HTTP/1.0 500 Error\r\n\r\n");
262 public void setCaptcha(Hash h) {
263 captchaString = (String)h.get("captcha");
264 ctoken = (String)h.get("ctoken");
265 Log.warn(GMail.class, "captchaString = " + captchaString);
266 Log.warn(GMail.class, "ctoken = " + ctoken);
267 Log.warn(GMail.class, "initting..." + ctoken);
270 Log.warn(GMail.class, " done..." + ctoken);
271 } catch (Exception e) {