package com.koushikdutta.tabletsms; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URISyntaxException; import java.net.URL; import org.apache.http.client.ClientProtocolException; import org.json.JSONException; import org.json.JSONObject; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.widget.RemoteViews; public class ServiceHelper { private static String LOGTAG = ServiceHelper.class.getSimpleName(); public static final String BASE_URL = Helper.SANDBOX ? "https://2.desksms.appspot.com" : "https://desksms.appspot.com"; public static final String AUTH_URL = BASE_URL + "/_ah/login"; public static final String API_URL = BASE_URL + "/api/v1"; public final static String REGISTER_URL = API_URL + "/register"; public static final String USER_URL = API_URL + "/user/default"; public final static String PING_URL = USER_URL + "/ping"; public final static String PUSH_URL = USER_URL + "/push"; public final static String IMAGE_URL = USER_URL + "/image"; public static final String SETTINGS_URL = USER_URL + "/settings"; public static final String SMS_URL = USER_URL + "/sms"; public static final String READ_URL = USER_URL + "/read"; public static final String DELETE_CONVERSATION_URL = USER_URL + "/delete/conversation"; public static final String WHOAMI_URL = USER_URL + "/whoami"; public static final String OUTBOX_URL = USER_URL + "/outbox"; public static final String TICKLE_URL = USER_URL + "/tickle"; static String numbersOnly(String number, boolean allowPlus) { StringBuilder ret = new StringBuilder(); for (int i = 0; i < number.length(); i++) { char c = number.charAt(i); if ((c >= '0' && c <= '9') || (c == '+' && allowPlus)) ret.append(c); } return ret.toString(); } static JSONObject retryExecuteAsJSONObject(Context context, String account, URL url, ConnectionCallback callback) throws ClientProtocolException, IOException, OperationCanceledException, AuthenticatorException, URISyntaxException, JSONException { HttpURLConnection conn = retryExecute(context, account, url, callback); JSONObject ret = null; if (conn != null) { ret = StreamUtility.downloadUriAsJSONObject(conn); conn.disconnect(); } return ret; } static String retryExecuteAsString(Context context, String account, URL url, ConnectionCallback callback) throws ClientProtocolException, IOException, OperationCanceledException, AuthenticatorException, URISyntaxException { HttpURLConnection conn = retryExecute(context, account, url, callback); String ret = null; if (conn != null) { ret = StreamUtility.downloadUriAsString(conn); conn.disconnect(); } return ret; } static void createAuthenticationNotification(Context context) { NotificationManager n = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); Notification notification = new Notification(R.drawable.icon, context.getString(R.string.authentification_notification), System.currentTimeMillis()); notification.contentView = new RemoteViews(context.getPackageName(), R.layout.notification); Intent intent = new Intent(context, MainActivity.class); intent.putExtra("relogin", true); notification.contentIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK); n.cancel(444); n.notify(444, notification); } public static interface ConnectionCallback { public void manage(HttpURLConnection conn) throws IOException; } public static class JSONPoster implements ConnectionCallback { JSONObject json; public JSONPoster(JSONObject json) { this.json = json; } @Override public void manage(HttpURLConnection conn) throws IOException { conn.setRequestProperty("Content-Type", "application/json"); byte[] bytes = json.toString().getBytes(); conn.setDoOutput(true); OutputStream os = conn.getOutputStream(); os.write(bytes); os.close(); } } public static class StringPoster implements ConnectionCallback { String s; public StringPoster(String s) { this.s = s; } @Override public void manage(HttpURLConnection conn) throws IOException { conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); byte[] bytes = s.getBytes(); conn.setDoOutput(true); OutputStream os = conn.getOutputStream(); os.write(bytes); os.close(); } } public static class FilePoster implements ConnectionCallback { File file; public FilePoster(File file) { this.file = file; } @Override public void manage(HttpURLConnection conn) throws IOException { conn.setRequestProperty("Content-Type", "application/json"); conn.setDoOutput(true); OutputStream os = conn.getOutputStream(); InputStream is = new FileInputStream(file); StreamUtility.copyStream(is, os); os.close(); is.close(); } } static HttpURLConnection setupConnection(Context context, URL url, ConnectionCallback callback) throws IOException { HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn = (HttpURLConnection)url.openConnection(); conn.setInstanceFollowRedirects(false); conn.setDoInput(true); addAuthentication(context, conn); if (callback != null) { callback.manage(conn); } return conn; } static HttpURLConnection retryExecuteAndDisconnect(Context context, String account, URL url, ConnectionCallback callback) throws ClientProtocolException, IOException, OperationCanceledException, AuthenticatorException, URISyntaxException { HttpURLConnection conn = retryExecute(context, account, url, callback); if (conn != null) conn.disconnect(); return conn; } private static HttpURLConnection retryExecute(Context context, String account, URL url, ConnectionCallback callback) throws ClientProtocolException, IOException, OperationCanceledException, AuthenticatorException, URISyntaxException { HttpURLConnection conn = setupConnection(context, url, callback); if (conn.getResponseCode() != 302) return conn; AccountManager accountManager = AccountManager.get(context); Account acct = new Account(account, "com.google"); String curAuthToken = Settings.getInstance(context).getString("web_connect_auth_token"); if (!Helper.isJavaScriptNullOrEmpty(curAuthToken)) accountManager.invalidateAuthToken(acct.type, curAuthToken); Bundle bundle = accountManager.getAuthToken(acct, TickleServiceHelper.AUTH_TOKEN_TYPE, false, null, null).getResult(); final String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); if (!Helper.isJavaScriptNullOrEmpty(authToken)) { Settings settings = Settings.getInstance(context); settings.setString("web_connect_auth_token", authToken); } if (authToken == null) { Log.e(LOGTAG, "Authentication failure."); createAuthenticationNotification(context); return null; } String newCookie = TickleServiceHelper.getCookie(context); if (newCookie == null) { Log.e(LOGTAG, "Authentication failure."); createAuthenticationNotification(context); return null; } conn = setupConnection(context, url, callback); if (conn.getResponseCode() != 302) return conn; createAuthenticationNotification(context); Log.e(LOGTAG, "Authentication failure."); return null; } static void addAuthentication(Context context, HttpURLConnection conn) { Settings settings = Settings.getInstance(context); String ascidCookie = settings.getString("Cookie"); conn.addRequestProperty("Cookie", ascidCookie); conn.addRequestProperty("X-Same-Domain", "1"); // XSRF } }