/** * Copyright (C) 2011 Whisper Systems * * 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 . */ package org.thoughtcrime.securesms.mms; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; import android.database.sqlite.SQLiteException; import android.net.ConnectivityManager; import android.net.Uri; import android.net.http.AndroidHttpClient; import android.preference.PreferenceManager; import android.util.Log; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.conn.params.ConnRouteParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.service.MmsDownloader; import org.thoughtcrime.securesms.util.Conversions; import org.thoughtcrime.securesms.util.Util; import java.io.DataInputStream; import java.io.IOException; import java.net.InetAddress; public class MmsCommunication { protected static MmsConnectionParameters getLocallyConfiguredMmsConnectionParameters(Context context) throws ApnUnavailableException { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); if (preferences.getBoolean(ApplicationPreferencesActivity.USE_LOCAL_MMS_APNS_PREF, false)) { String mmsc = preferences.getString(ApplicationPreferencesActivity.MMSC_HOST_PREF, null); if (mmsc == null || !mmsc.startsWith("http")) throw new ApnUnavailableException("Malformed locally configured MMSC: " + mmsc); String proxy = preferences.getString(ApplicationPreferencesActivity.MMSC_PROXY_HOST_PREF, null); String port = preferences.getString(ApplicationPreferencesActivity.MMSC_PROXY_PORT_PREF, null); return new MmsConnectionParameters(mmsc, proxy, port); } throw new ApnUnavailableException("No locally configured parameters available"); } protected static MmsConnectionParameters getLocalMmsConnectionParameters(Context context) throws ApnUnavailableException { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); if (preferences.getBoolean(ApplicationPreferencesActivity.USE_LOCAL_MMS_APNS_PREF, false)) { return getLocallyConfiguredMmsConnectionParameters(context); } else { MmsConnectionParameters params = ApnDefaults.getMmsConnectionParameters(context); if (params == null) { throw new ApnUnavailableException("No parameters available from ApnDefaults."); } return params; } } protected static MmsConnectionParameters getMmsConnectionParameters(Context context, String apn, boolean proxyIfPossible) throws ApnUnavailableException { Cursor cursor = null; try { cursor = DatabaseFactory.getMmsDatabase(context).getCarrierMmsInformation(apn); if (cursor == null || !cursor.moveToFirst()) return getLocalMmsConnectionParameters(context); do { String mmsc = cursor.getString(cursor.getColumnIndexOrThrow("mmsc")); String proxy = null; String port = null; if (proxyIfPossible) { proxy = cursor.getString(cursor.getColumnIndexOrThrow("mmsproxy")); port = cursor.getString(cursor.getColumnIndexOrThrow("mmsport")); } if (!Util.isEmpty(mmsc)) return new MmsConnectionParameters(mmsc, proxy, port); } while (cursor.moveToNext()); return getLocalMmsConnectionParameters(context); } catch (SQLiteException sqe) { Log.w("MmsCommunication", sqe); return getLocalMmsConnectionParameters(context); } catch (SecurityException se) { Log.w("MmsCommunication", se); return getLocalMmsConnectionParameters(context); } catch (IllegalArgumentException iae) { Log.w("MmsCommunication", iae); return getLocalMmsConnectionParameters(context); } finally { if (cursor != null) cursor.close(); } } protected static void checkRouteToHost(Context context, MmsConnectionParameters parameters, String url, boolean usingMmsRadio) throws IOException { if (parameters == null || !parameters.hasProxy()) checkRouteToHost(context, Uri.parse(url).getHost(), usingMmsRadio); else checkRouteToHost(context, parameters.getProxy(), usingMmsRadio); } private static void checkRouteToHost(Context context, String host, boolean usingMmsRadio) throws IOException { InetAddress inetAddress = InetAddress.getByName(host); if (!usingMmsRadio) { if (inetAddress.isSiteLocalAddress()) { throw new IOException("RFC1918 address in non-MMS radio situation!"); } return; } byte[] ipAddressBytes = inetAddress.getAddress(); int ipAddress = Conversions.byteArrayToIntLittleEndian(ipAddressBytes, 0); ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); if (!manager.requestRouteToHost(MmsDownloader.TYPE_MOBILE_MMS, ipAddress)) throw new IOException("Connection manager could not obtain route to host."); // if (!manager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE, ipAddress)) // throw new IOException("Connection manager could not obtain route to host."); } protected static AndroidHttpClient constructHttpClient(Context context, MmsConnectionParameters mmsConfig) { AndroidHttpClient client = AndroidHttpClient.newInstance("Android-Mms/2.0", context); HttpParams params = client.getParams(); HttpProtocolParams.setContentCharset(params, "UTF-8"); HttpConnectionParams.setSoTimeout(params, 20 * 1000); if (mmsConfig.hasProxy()) { ConnRouteParams.setDefaultProxy(params, new HttpHost(mmsConfig.getProxy(), mmsConfig.getPort())); } return client; } protected static byte[] parseResponse(HttpEntity entity) throws IOException { if (entity == null || entity.getContentLength() == 0) throw new IOException("Null response"); if (entity.getContentLength() < 0) throw new IOException("Unknown content length!"); byte[] responseBytes = new byte[(int)entity.getContentLength()]; DataInputStream dataInputStream = new DataInputStream(entity.getContent()); dataInputStream.readFully(responseBytes); dataInputStream.close(); entity.consumeContent(); return responseBytes; } protected static class MmsConnectionParameters { private final String mmsc; private final String proxy; private final String port; public MmsConnectionParameters(String mmsc, String proxy, String port) { this.mmsc = mmsc; this.proxy = proxy; this.port = port; } public boolean hasProxy() { return proxy != null && proxy.trim().length() != 0; } public String getMmsc() { return mmsc; } public String getProxy() { if (!hasProxy()) return null; return proxy; } public int getPort() { if (port == null || port.trim().length() == 0) return 80; return Integer.parseInt(port); } } }