Java tutorial
/* * Copyright (C) 2010 Lado Kumsiashvili * * This file is part of websms-connector-sms.ge * * 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 org.herrlado.websms.connector.smsge; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URLEncoder; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.entity.StringEntity; import org.apache.http.message.BasicHeader; import org.apache.http.protocol.HTTP; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.drawable.BitmapDrawable; import android.preference.PreferenceManager; import android.util.Log; import android.util.Pair; import de.ub0r.android.websms.connector.common.Connector; import de.ub0r.android.websms.connector.common.ConnectorSpec; import de.ub0r.android.websms.connector.common.ConnectorSpec.SubConnectorSpec; import de.ub0r.android.websms.connector.common.Utils; import de.ub0r.android.websms.connector.common.WebSMSException; /** * Receives commands coming as broadcast from WebSMS. * * @author lado */ public class ConnectorSmsge extends Connector { /** Tag for debug output. */ private static final String TAG = "WebSMS.sms.ge"; /** Login URL, to send Login (POST). */ private static final String LOGIN_URL = "http://www.sms.ge/ngeo/index.php"; /** Send SMS URL(POST) / Free SMS Count URL(GET). */ private static final String SMS_URL = "http://www.sms.ge/ngeo/send.php"; /** Encoding to use. */ private static final String ENCODING = "UTF-8"; /** HTTP Header User-Agent. */ private static final String FAKE_USER_AGENT = "Mozilla/5.0 (Windows; U;" + " Windows NT 5.1; de; rv:1.9.0.9) Gecko/2009040821" + " Firefox/3.0.9 (.NET CLR 3.5.30729)"; /** This String will be matched if the user is logged in. */ private static final String MATCH_LOGIN_SUCCESS = "logout.php"; private static final String URL_CAPTCHA = "http://www.sms.ge/ngeo/inc/include/securimage/securimage_show.php"; /** Object to sync with. */ private static final Object CAPTCHA_SYNC = new Object(); /** Timeout for entering the captcha. */ private static final long CAPTCHA_TIMEOUT = 60000; /** Solved Captcha. */ private static String captchaSolve = null; private static final String CHECK_WRONGCAPTCHA = "?? ? ????"; private static final String CHECK_WRONG_NUMBER = "? ? ? ? ????"; private static final String CHECK_SUCCESS = " ?? ?? ???"; private static final String CHECK_NO_GEOCELL = " ? ? ?"; //private static final String PARAM_uid = ""; //private static final String PARAM_sys = ""; private static final String PARAM_x = "31"; private static final String PARAM_y = "17"; private static final String PARAM_Send = "1"; // private static final String PARAM_captcha_code=""; // private static final String PARAM_message=""; // private static final String PARAM_num=""; // private static final String PRAM_phone=""; // private static final String PARAM_geolai=""; /** * {@inheritDoc} */ @Override public final ConnectorSpec initSpec(final Context context) { final String name = context.getString(R.string.smsge_name); final ConnectorSpec c = new ConnectorSpec(name); c.setAuthor(// . context.getString(R.string.smsge_author)); c.setBalance(null); // c.setPrefsTitle(context.getString(R.string.preferences)); c.setCapabilities(ConnectorSpec.CAPABILITIES_UPDATE | ConnectorSpec.CAPABILITIES_SEND | ConnectorSpec.CAPABILITIES_PREFS); c.addSubConnector(TAG, c.getName(), SubConnectorSpec.FEATURE_CUSTOMSENDER); return c; } /** * {@inheritDoc} */ @Override public final ConnectorSpec updateSpec(final Context context, final ConnectorSpec connectorSpec) { final SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(context); if (p.getBoolean(Preferences.ENABLED, false)) { if (p.getString(Preferences.PASSWORD, "").length() > 0) { connectorSpec.setReady(); } else { connectorSpec.setStatus(ConnectorSpec.STATUS_ENABLED); } } else { connectorSpec.setStatus(ConnectorSpec.STATUS_INACTIVE); } return connectorSpec; } private static Pair<String, String> getUidSys(String content) { int uididx = content.indexOf("uid"); uididx = content.indexOf("value=\"", uididx); uididx += 7; int uididx2 = content.indexOf("\"", uididx); String uid = content.substring(uididx, uididx2); int sysidx = content.indexOf("sys", uididx + uid.length() + 1); sysidx = content.indexOf("value=\"", sysidx); sysidx += 7; int sysidx2 = content.indexOf("\"", sysidx); String sys = content.substring(sysidx, sysidx2); return Pair.create(uid, sys); } /** * b This post data is needed for log in. * * @param username * username * @param password * password * @return array of params * @throws UnsupportedEncodingException * if the url can not be encoded */ private static String getLoginPost(final String username, final String password) throws UnsupportedEncodingException { final StringBuilder sb = new StringBuilder(); sb.append("userid="); sb.append(URLEncoder.encode(username, ENCODING)); sb.append("&passwd="); sb.append(URLEncoder.encode(password, ENCODING)); sb.append("&" + URLEncoder.encode("submit.x") + "=42"); sb.append("&" + URLEncoder.encode("submit.y") + "=5"); // Log.d(TAG, sb.toString()); return sb.toString(); } private Pair<String, String> normalizeNumber(String number) { if (number.startsWith("00995")) { number = number.substring(5); } else if (number.startsWith("+995")) { number = number.substring(4); } if (number.startsWith("5") == false) { throw new WebSMSException("Not a valid recipient!"); } return Pair.create("995" + number.substring(0, 3), number.substring(3)); } /** * These post data is needed for sending a sms. * * @param ctx * {@link ConnectorContext}s * @return array of params * @throws Exception * if an error occures. */ private String getSmsPost(final ConnectorContext ctx, Pair<String, String> uidsys, String captcha) throws Exception { final String[] to = ctx.getCommand().getRecipients(); if (to.length != 1) { throw new WebSMSException("Only one Recipient allowed"); } String msg = ctx.getCommand().getText(); if (msg.length() > 150) { throw new WebSMSException("Message text too long. Max. 150 chars allowed!"); } Pair<String, String> number = normalizeNumber(Utils.getRecipientsNumber(to[0])); StringBuilder sb1 = new StringBuilder(); sb1.append("geolai").append("=").append(number.first); sb1.append("&"); sb1.append("phone").append("=").append(number.second); sb1.append("&"); sb1.append("message").append("=").append(e(msg)); sb1.append("&"); sb1.append("num").append("=").append(msg.length()); sb1.append("&"); sb1.append("uid").append("=").append(e(uidsys.first)); sb1.append("&"); sb1.append("sys").append("=").append(e(uidsys.second)); sb1.append("&"); sb1.append("Send").append("=").append(e(PARAM_Send)); sb1.append("&"); sb1.append("x").append("=").append(PARAM_x); sb1.append("&"); sb1.append("y").append("=").append(PARAM_y); sb1.append("&"); sb1.append("captcha_code").append("=").append(captcha); final String post = sb1.toString(); Log.d(TAG, "request: " + post); return post; } private String e(String toenc) throws UnsupportedEncodingException { return URLEncoder.encode(toenc, ENCODING); } /** * Login to arcor. * * @param ctx * {@link ConnectorContext} * @return true if successfullu logged in, false otherwise. * @throws WebSMSException * if any Exception occures. */ private Pair<String, String> login(final ConnectorContext ctx) throws WebSMSException { try { final SharedPreferences p = ctx.getPreferences(); final HttpPost request = createPOST(LOGIN_URL); request.addHeader("Referer", "http://www.sms.ge/ngeo/index.php"); String post = getLoginPost(p.getString(Preferences.USERNAME, ""), p.getString(Preferences.PASSWORD, "")); request.setEntity(new StringEntity(post)); final HttpResponse response = ctx.getClient().execute(request); // response = ctx.getClient().execute( // new HttpGet("http://www.sms.ge/ngeo/main.php")); final String cutContent = Utils.stream2str(response.getEntity().getContent()); Log.d(TAG, cutContent); if (cutContent.indexOf(MATCH_LOGIN_SUCCESS) == -1) { throw new WebSMSException(ctx.getContext(), R.string.error_pw); } return getUidSys(cutContent); } catch (final Exception e) { throw new WebSMSException(e.getMessage()); } } /** * Create and Prepare a Post Request. Set also an User-Agent * * @param url * http post url * @param urlencodedparams * key=value pairs as url encoded string * @return HttpPost * @throws Exception * if an error occures */ private static HttpPost createPOST(final String url) throws Exception { final HttpPost post = new HttpPost(url); post.setHeader("User-Agent", FAKE_USER_AGENT); post.setHeader(new BasicHeader(HTTP.CONTENT_TYPE, URLEncodedUtils.CONTENT_TYPE)); return post; } /** * {@inheritDoc} */ @Override protected final void doUpdate(final Context context, final Intent intent) throws WebSMSException { final ConnectorContext ctx = ConnectorContext.create(context, intent); final String term = "\u221E"; this.getSpec(ctx.getContext()).setBalance(term); } /** * Sends an sms via HTTP POST. * * @param ctx * {@link ConnectorContext} * @return successfull? * @throws WebSMSException * on an error */ private boolean sendSms(final ConnectorContext ctx, Pair<String, String> uidsys, String captcha) throws WebSMSException { try { HttpPost post = createPOST(SMS_URL); String p = getSmsPost(ctx, uidsys, captcha); post.setEntity(new StringEntity(p)); final HttpResponse response = ctx.getClient().execute(post); final boolean sent = this.afterSmsSent(ctx, response); return sent; } catch (WebSMSException ex) { throw ex; } catch (final Exception ex) { throw new WebSMSException(ex.getMessage()); } } /** * Handles content after sms sending. * * @param ctx * {@link ConnectorContext} * @param response * HTTP Response * @return true if arcor returns success * @throws Exception * if an Error occures */ private boolean afterSmsSent(final ConnectorContext ctx, final HttpResponse response) throws Exception { final String body = Utils.stream2str(response.getEntity().getContent()); Log.d(TAG, "response: " + body); if (body == null || body.length() == 0) { throw new WebSMSException("No Response!");// TODO } if (body.indexOf(CHECK_SUCCESS) != -1) { return true; } if (body.indexOf(CHECK_WRONGCAPTCHA) != -1) { throw new WebSMSException(CHECK_WRONGCAPTCHA); } if (body.indexOf(CHECK_WRONG_NUMBER) != -1) { throw new WebSMSException(CHECK_WRONG_NUMBER); } if (body.indexOf(CHECK_NO_GEOCELL) != -1) { throw new WebSMSException(CHECK_NO_GEOCELL); } throw new WebSMSException( "SMS ? ??? :( ? ?? ? ??."); } /** * {@inheritDoc} * * @throws IOException */ @Override protected final void doSend(final Context context, final Intent intent) throws WebSMSException, IOException { final ConnectorContext ctx = ConnectorContext.create(context, intent); Pair<String, String> uidsys = login(ctx); String captcha = solveCaptcha(ctx); if (captcha == null) { throw new WebSMSException("Captcha not solved"); } sendSms(ctx, uidsys, captcha); } /** * Load captcha and wait for user input to solve it. * * @param context * {@link Context} * @param flow * _flowExecutionKey * @return true if captcha was solved * @throws IOException * IOException */ private String solveCaptcha(final ConnectorContext ctx) throws IOException { HttpGet cap = new HttpGet(URL_CAPTCHA); cap.addHeader("Referer", "http://www.sms.ge/ngeo/index.php"); cap.setHeader("User-Agent", FAKE_USER_AGENT); HttpResponse response = ctx.getClient().execute(cap); int resp = response.getStatusLine().getStatusCode(); if (resp != HttpURLConnection.HTTP_OK) { throw new WebSMSException(ctx.getContext(), R.string.error_http, "" + resp); } BitmapDrawable captcha = new BitmapDrawable(response.getEntity().getContent()); final Intent intent = new Intent(Connector.ACTION_CAPTCHA_REQUEST); intent.putExtra(Connector.EXTRA_CAPTCHA_DRAWABLE, captcha.getBitmap()); captcha = null; Context context = ctx.getContext(); this.getSpec(context).setToIntent(intent); context.sendBroadcast(intent); try { synchronized (CAPTCHA_SYNC) { CAPTCHA_SYNC.wait(CAPTCHA_TIMEOUT); } } catch (InterruptedException e) { Log.e(TAG, null, e); return null; } if (captchaSolve == null) { return captchaSolve; } // got user response, try to solve captcha Log.d(TAG, "got solved captcha: " + captchaSolve); return captchaSolve; } /** * {@inheritDoc} */ protected final void gotSolvedCaptcha(final Context context, final String solvedCaptcha) { captchaSolve = solvedCaptcha; synchronized (CAPTCHA_SYNC) { CAPTCHA_SYNC.notify(); } } }