Java tutorial
/* * imoten - i mode.net mail tensou(forward) * * Copyright (C) 2010 ryu aka 508.P905 (http://code.google.com/p/imoten/) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * */ package immf; import java.util.ArrayList; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import javax.mail.internet.InternetAddress; import javax.net.ssl.SSLException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.xml.sax.Attributes; import org.xml.sax.helpers.DefaultHandler; class AppNotifications extends DefaultHandler implements Runnable { private static final Log log = LogFactory.getLog(AppNotifications.class); private static final String CredUrl = "https://www.appnotifications.com/user_session.xml"; private static final String PushHost = "www.appnotifications.com"; private static final String PushPath = "/account/notifications.json"; private static final String PushUrl = "https://" + PushHost + PushPath; private static final String IconUrl = "http://imode.net/cmn/images/favicon.ico"; private static final String Message = "?i?"; private static final int ReconnectInterval = 3000; private static final int ReconnectCount = 100; private StatusManager status; private Deque<ImodeMail> recieveMailQueue = new LinkedList<ImodeMail>(); private Deque<ImodeMail> sendMailQueue = new LinkedList<ImodeMail>(); private boolean elemCr = false; private boolean elemOk = false; private String email; private String password; private String message; private String sound; private String iconUrl; private boolean pushFromInfo; private boolean pushSubjectInfo; private boolean pushReplyButton; private String credentials; private String ok = ""; private int newmails = 0; private boolean dnsCache; private InetAddress pushaddr; enum sounds { beep1("5.caf"), bellmodern("10.caf"), bellonetone("21.caf"), bellsimple("11.caf"), belltriple( "12.caf"), detonatorcharge("20.caf"), digitalalarm("19.caf"), flourish("16.caf"), light( "17.caf"), magicchime("18.caf"), magiccoin1("3.caf"), notifier1("1.caf"), notifier2( "2.caf"), notifier3("4.caf"), orchestrallong( "13.caf"), orchestralshort("14.caf"), score("15.caf"); private String file; private sounds(String file) { this.file = file; } public String filename() { return this.file; } } public AppNotifications(Config conf, StatusManager status) { this.status = status; this.email = conf.getForwardPushEmail(); this.password = conf.getForwardPushPassword(); this.message = conf.getForwardPushMessage(); if (this.message == null) this.message = Message; this.sound = soundFile(conf.getForwardPushSound()); this.iconUrl = conf.getForwardPushIconUrl(); if (this.iconUrl.isEmpty()) this.iconUrl = IconUrl; this.pushFromInfo = conf.isForwardPushFrom(); this.pushSubjectInfo = conf.isForwardPushSubject(); this.pushReplyButton = conf.isForwardPushReplyButton(); this.dnsCache = conf.isForwardPushUseDnsCache(); this.credentials = status.getPushCredentials(); if (this.credentials == null) this.credentials = ""; if (this.email.length() > 0 && this.password.length() > 0) { Thread t = new Thread(this); t.setName("AppNotifications"); t.setDaemon(true); t.start(); } else { if (this.credentials.length() > 0) this.setCredentials(""); } } public void run() { boolean authOk = false; if (this.credentials.isEmpty()) { if (!this.auth()) { log.warn( "???????????"); return; } authOk = true; log.info("credentials??push???"); } else { log.info("push???"); } if (dnsCache) { log.info( "DNS??(SSL????????????)"); } int c = 0; while (true) { try { Thread.sleep(ReconnectInterval); } catch (Exception e) { } // ?????5 if (this.sendMailQueue.size() > 0 && c++ > ReconnectCount) { log.info("push"); } else if (this.recieveMailQueue.size() > newmails) { log.warn(""); } else if (newmails == 0) { continue; } else if (this.recieveMailQueue.size() < newmails) { // ??????? if (c++ > 100) { log.warn("?"); newmails = this.recieveMailQueue.size(); } continue; } c = 0; synchronized (this.recieveMailQueue) { for (int i = this.recieveMailQueue.size(); i > 0; i--) { this.sendMailQueue.add(this.recieveMailQueue.remove()); } newmails = 0; } // push int count = this.sendMailQueue.size(); ImodeMail mail = this.sendMailQueue.peek(); String pushMessage = this.message; String pushCommand = ""; if (!this.pushFromInfo) { pushMessage += "(" + Integer.toString(count) + ")"; } String delimiter = ""; if (!pushMessage.isEmpty()) { delimiter = "\n"; } if (mail != null && this.pushSubjectInfo) { String subject = mail.getSubject(); if (subject.isEmpty()) { subject = "(????)"; } pushMessage += delimiter + subject; delimiter = " "; } if (mail != null && this.pushFromInfo) { InternetAddress fromAddr = mail.getFromAddr(); String from = fromAddr.getPersonal(); if (from == null || from.isEmpty()) { from = fromAddr.getAddress(); } if (count == 1) { pushMessage += delimiter + "(" + from + ")"; } else { pushMessage += delimiter + "(" + from + "," + Integer.toString(count - 1) + ")"; } } if (mail != null && this.pushReplyButton) { // (%20)??+??????? InternetAddress fromAddr = mail.getFromAddr(); pushCommand = "mailto:" + fromAddr.getAddress(); for (InternetAddress to : mail.getToAddrList()) { pushCommand += "%2C" + to.getAddress(); } String ccList = ""; for (InternetAddress cc : mail.getCcAddrList()) { if (ccList.isEmpty()) { ccList = "?cc=" + cc.getAddress(); } else { ccList += "%2C" + cc.getAddress(); } } if (!ccList.isEmpty()) { pushCommand += ccList; //pushCommand += "&subject="; //}else{ //pushCommand += "?subject="; } /* * XXX * Apple URL Scheme Reference?? RFC2368 ????????? * 8bit?? subject ???? RFC2047 ?????? * ??????8bit??? Push ??? * Push ???????? */ } try { log.info("push:" + pushMessage.replace("\n", "/")); if (this.pushReplyButton) { this.send(pushMessage, pushCommand); } else { this.send(pushMessage); } authOk = true; this.sendMailQueue.clear(); } catch (SocketException se) { // log.warn("?push????", se); } catch (ClientProtocolException cpe) { // log.warn("?push????", cpe); } catch (UnknownHostException uhe) { // DNS log.warn("DNS?push????", uhe); } catch (SSLException ssle) { // SSL log.warn("SSL?push????", ssle); } catch (MyHttpException mhe) { // log.warn("??push????", mhe); } catch (Exception e) { log.warn("push???", e); if (authOk) { continue; } // ???????????????? this.credentials = ""; // status.ini???credentials??????????? if (this.auth()) { authOk = true; log.info("credentials???"); try { log.info("push:" + pushMessage.replace("\n", "/")); if (this.pushReplyButton) { this.send(pushMessage, pushCommand); } else { this.send(pushMessage); } this.sendMailQueue.clear(); } catch (Exception ex) { log.warn("pushpush????", e); this.setCredentials(""); this.sendMailQueue.clear(); return; } } else { log.warn("??push????"); this.setCredentials(""); this.sendMailQueue.clear(); return; } } } } public void pushPrepare(int folderId, int count) { if (!isActive()) { return; } if (folderId == ImodeNetClient.FolderIdSent) { return; } log.info("" + count + ""); newmails += count; } public void push(int folderId, ImodeMail mail) throws Exception { if (!isActive()) { return; } if (folderId == ImodeNetClient.FolderIdSent) { return; } this.recieveMailQueue.add(mail); } public void push(String message) { if (!isActive()) { return; } try { this.send(message); } catch (Exception e) { log.warn("push", e); } } public void pushError(int folderId) { if (!isActive()) { return; } if (folderId == ImodeNetClient.FolderIdSent) { return; } if (newmails > 0) newmails--; } private boolean auth() { boolean httpOk = false; while (!httpOk) { try { SAXParserFactory spfactory = SAXParserFactory.newInstance(); SAXParser parser = spfactory.newSAXParser(); byte[] xml = this.getCredentials(); InputStream is = new ByteArrayInputStream(xml); parser.parse(is, this); httpOk = true; } catch (Exception e) { log.warn("5????", e); try { Thread.sleep(ReconnectInterval * ReconnectCount); } catch (Exception ee) { } } } if (!ok.equals("OK")) { ok = ""; return false; } this.setCredentials(); return true; } private byte[] getCredentials() throws Exception { DefaultHttpClient httpClient = new DefaultHttpClient(); HttpPost post = new HttpPost(CredUrl); List<NameValuePair> formparams = new ArrayList<NameValuePair>(); formparams.add(new BasicNameValuePair("user_session[email]", this.email)); formparams.add(new BasicNameValuePair("user_session[password]", this.password)); UrlEncodedFormEntity entity = null; try { entity = new UrlEncodedFormEntity(formparams, "UTF-8"); } catch (Exception e) { } post.setEntity(entity); byte[] xml = null; try { HttpResponse res = httpClient.execute(post); int status = res.getStatusLine().getStatusCode(); if (status == 200) { xml = EntityUtils.toByteArray(res.getEntity()); } } finally { post.abort(); } return xml; } private void setCredentials() { this.status.setPushCredentials(this.credentials); try { this.status.save(); log.info("status?????"); } catch (Exception e) { log.error("Status File save Error.", e); } } private void setCredentials(String c) { this.credentials = c; this.setCredentials(); } /* * Push ???? * * [] * ?140?30 * * [action_loc_key?""] * 27(3) * [action_loc_key?"Reply"] * 26(4) * [action_loc_key?""] * 25(5) * * [run_command?50byte] * 21(9) * [run_command?80byte] * 17(13) */ private void send(String message, String command) throws Exception { String url = PushUrl; DefaultHttpClient httpClient = new DefaultHttpClient(); // appnotifications?DNS???IP?? // ??SSL??????????? if (dnsCache) { InetAddress ipaddr; try { ipaddr = InetAddress.getByName(PushHost); this.pushaddr = ipaddr; } catch (UnknownHostException e) { if (pushaddr != null) { log.warn("DNS lookup error, using cache..."); ipaddr = this.pushaddr; } else { throw (e); } } SSLSocketFactory sf = SSLSocketFactory.getSocketFactory(); sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); Scheme sc = new Scheme("https", sf, 443); httpClient.getConnectionManager().getSchemeRegistry().register(sc); url = "https://" + ipaddr.getHostAddress() + PushPath; } HttpPost post = new HttpPost(url); post.setHeader("Host", PushHost); // dnsCache List<NameValuePair> formparams = new ArrayList<NameValuePair>(); formparams.add(new BasicNameValuePair("user_credentials", this.credentials)); if (!this.sound.isEmpty()) { formparams.add(new BasicNameValuePair("notification[sound]", this.sound)); } formparams.add(new BasicNameValuePair("notification[message]", message)); formparams.add(new BasicNameValuePair("notification[icon_url]", this.iconUrl)); formparams.add(new BasicNameValuePair("notification[message_level]", "2")); formparams.add(new BasicNameValuePair("notification[silent]", "0")); if (command != null && !command.isEmpty()) { // ??????action_loc_key??? formparams.add(new BasicNameValuePair("notification[action_loc_key]", "Reply")); formparams.add(new BasicNameValuePair("notification[run_command]", command)); } else { formparams.add(new BasicNameValuePair("notification[action_loc_key]", "")); } UrlEncodedFormEntity entity = null; try { entity = new UrlEncodedFormEntity(formparams, "UTF-8"); } catch (Exception e) { } post.setEntity(entity); try { HttpResponse res = httpClient.execute(post); int status = res.getStatusLine().getStatusCode(); if (status != 200) { if (status >= 500) { throw new MyHttpException("http server error. status=" + status); } else { throw new Exception("http server error. status=" + status); } } String resString = EntityUtils.toString(res.getEntity()); log.info("?:" + resString); JSONObject json = JSONObject.fromObject(resString); int id = json.getInt("id"); if (id < 1) { throw new Exception("illegal id returned"); } } catch (JSONException e) { /* * (1)JSONObject.fromObject?exception???post? * (2)id????????? * (????? {"Response":"Not Authorized"} ??HTTP?status?401??) */ throw new Exception("wrong request"); } finally { post.abort(); } } private void send(String message) throws Exception { this.send(message, null); } private String soundFile(String key) { if (key == null || key.isEmpty()) { return ""; } String k = key.toLowerCase().replaceAll("\\s", ""); String filename = ""; for (sounds s : sounds.values()) { if (k.equals(s.toString())) { filename = s.filename(); log.info(":" + key); break; } } if (filename.isEmpty()) { log.info("?"); filename = "0.caf"; } return filename; } private boolean isActive() { if (this.credentials != null && this.credentials.length() > 0) { return true; } else { return false; } } /* * SAX */ public void startElement(String uri, String localName, String qName, Attributes attributes) { if (qName.equals("single-access-token")) { this.elemCr = true; } if (qName.equals("OK")) { this.elemOk = true; } } public void endElement(String uri, String localName, String qName) { if (qName.equals("single-access-token")) { this.elemCr = false; } if (qName.equals("OK")) { this.elemOk = false; } } public void characters(char[] ch, int offset, int length) { if (this.elemCr) { this.credentials = new String(ch, offset, length); } if (this.elemOk) { this.ok = new String(ch, offset, length); } } class MyHttpException extends Exception { private static final long serialVersionUID = -5836602194926641698L; public MyHttpException(String s) { super(s); } } }