Gmailに認証後にメッセージの詳細を取得する
Gmailはメッセージの一覧をAtomで配信している。なので、メッセージのIDが埋め込まれたURLはそこから取得することができるが、そのままURLをブラウザに渡すとまだ認証されていないため、認証画面が出てくる。どうにかこうにかしてこの画面をすっ飛ばしたいなと思って書いたのが次のコード。例のごとくJUnit4です。で、CookieManagerを使いたかったので、Java6使ってます。もしあれだったらApache CommonsのHttpClientあたりを使うようにすればJava1.4くらいでも動きそうですな。それとHTMLのパースにJericho HTML Parser、ウィジェットにはSWTを使ってます。
public class LearningGmailConnection { private String email = "******"; private String passwd = "*****"; private String messageId = "******"; private static String loginformId = "gaia_loginform"; private static String gmailLoginURL = "https://www.google.com/accounts/ServiceLogin?service=mail&passive=true&rm=false&continue=http%3A%2F%2Fmail.google.com%2Fmail%2F%3Fui%3Dhtml%26zy%3Dl<mpl=default<mplcache=2"; @Test public void learningGmailURL() throws Exception { CookieManager manager = new CookieManager(); manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); CookieHandler.setDefault(manager); URL url = new URL(gmailLoginURL); Source source = new Source(url); List<?> forms = source.findAllElements(Tag.FORM); for (Iterator<?> itr = forms.iterator(); itr.hasNext();) { Element form = (Element) itr.next(); if (loginformId.equals(form.getAttributeValue("id"))) { authenticationToGmail(form); } } String script = makeCookies(manager); url = new URL(messageId); showBrowser(url,script); } private void authenticationToGmail(Element form) throws MalformedURLException, IOException, ProtocolException { URL action = new URL(form.getAttributeValue("action")); HttpURLConnection con = (HttpURLConnection) action .openConnection(); con.setRequestMethod("POST"); List<?> tags = form.findAllElements(Tag.INPUT); con.setDoOutput(true); DataOutputStream outStream = new DataOutputStream(con .getOutputStream()); String params = buildParameters(tags); byte[] bytes = params.getBytes(); outStream.write(bytes, 0, bytes.length); con.connect(); BufferedReader br = new BufferedReader(new InputStreamReader( con.getInputStream())); while (br.readLine() != null){} con.disconnect(); } private String buildParameters(List<?> tags) { StringBuilder builder = new StringBuilder( "Email=" + email + "&Passwd=" + passwd); for (Iterator<?> iitr = tags.iterator(); iitr.hasNext();) { Element tag = (Element) iitr.next(); if ("hidden".equals(tag.getAttributeValue("type"))) { String name = tag.getAttributeValue("name"); String value = tag.getAttributeValue("value"); builder.append("&"); builder.append(name); builder.append("="); builder.append(value); } } return builder.toString(); } private String makeCookies(CookieManager manager) { CookieStore store = manager.getCookieStore(); List<HttpCookie> cookies = store.getCookies(); StringBuilder scriptBuilder = new StringBuilder(); for (int i = 0; i < cookies.size(); i++) { HttpCookie cookie = cookies.get(i); scriptBuilder.append(createCookieScript(cookie)); } String script = scriptBuilder.toString(); return script; } private String createCookieScript(HttpCookie cookie) { StringBuilder builder = new StringBuilder("document.cookie=\""); builder.append(cookie.getName()); builder.append("="); builder.append(cookie.getValue()); builder.append(";"); String domain = cookie.getDomain(); if(domain != null){ builder.append("domain="); builder.append(domain); builder.append(";"); } builder.append("\"\n"); return builder.toString(); } private void showBrowser(URL url, String script) { showBrowser(url, null, script); } private void showBrowser(URL url, String html,String script) { if(url == null && html == null){ throw new NullPointerException("url or html, which one is needed."); } Display display = new Display(); Shell shell = new Shell(display); shell.setLayout(new FillLayout()); Browser browser = new Browser(shell, SWT.NONE); if(script != null){ browser.execute(script); } if(url != null){ browser.setUrl(url.toString()); } if(html != null){ browser.setText(html); } shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } }
前のエントリで書いたとおり、認証画面をどうにかこうにかしてPOSTすることでクッキーを取得し、それをブラウザに食わせるというやり方をとってみたらすんなりうまくいった。他のサイトでもFormで認証させる場合は同様のやり方でいける。
このコードを見ている限りコードの基盤はオブジェクト指向というよりも構造化なんだよなと思った。構造化されたコードをわかりやすく分類したものがクラスで、そのメッセージの飛ばし具合がオブジェクト指向っていうのがクラスベースオブジェクト指向。インスタンスベースオブジェクト指向はそもそもインスタンスがあるよねっていう世界だからJavaには合わないね。脱線。