From 93f781b8dcb63ca9fde11d0e8647f768d00a6d42 Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Mon, 8 Oct 2018 00:13:12 +0200 Subject: [PATCH] remove some unsued sms code --- AndroidManifest.xml | 21 - .../securesms/backup/FullBackupBase.java | 65 --- .../securesms/backup/FullBackupExporter.java | 384 ------------------ .../securesms/database/ApnDatabase.java | 174 -------- .../securesms/jobs/MmsDownloadJob.java | 252 ------------ .../securesms/jobs/MmsReceiveJob.java | 98 ----- .../securesms/jobs/MmsSendJob.java | 298 -------------- .../securesms/jobs/SmsSendJob.java | 208 ---------- .../securesms/jobs/SmsSentJob.java | 96 ----- .../securesms/mms/CompatMmsConnection.java | 101 ----- .../mms/IncomingLegacyMmsConnection.java | 156 ------- .../securesms/mms/LegacyMmsConnection.java | 296 -------------- .../mms/OutgoingLegacyMmsConnection.java | 162 -------- .../securesms/service/MmsListener.java | 78 ---- .../service/SmsDeliveryListener.java | 67 --- .../securesms/sms/MessageSender.java | 11 +- .../util/MmsCharacterCalculator.java | 11 - .../util/SmsCharacterCalculator.java | 56 --- src/org/thoughtcrime/securesms/util/Util.java | 6 - .../securesms/TextSecureTestCase.java | 16 - .../thoughtcrime/securesms/BaseUnitTest.java | 78 ---- .../securesms/database/AddressTest.java | 59 --- .../CursorRecyclerViewAdapterTest.java | 78 ---- .../securesms/service/SmsListenerTest.java | 58 --- .../securesms/util/DelimiterUtilTest.java | 56 --- .../securesms/util/ListPartitionTest.java | 86 ---- .../util/PhoneNumberFormatterTest.java | 67 --- .../securesms/util/Rfc5724UriTest.java | 105 ----- .../securesms/util/ShortCodeUtilTest.java | 41 -- 29 files changed, 4 insertions(+), 3180 deletions(-) delete mode 100644 src/org/thoughtcrime/securesms/backup/FullBackupBase.java delete mode 100644 src/org/thoughtcrime/securesms/backup/FullBackupExporter.java delete mode 100644 src/org/thoughtcrime/securesms/database/ApnDatabase.java delete mode 100644 src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java delete mode 100644 src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java delete mode 100644 src/org/thoughtcrime/securesms/jobs/MmsSendJob.java delete mode 100644 src/org/thoughtcrime/securesms/jobs/SmsSendJob.java delete mode 100644 src/org/thoughtcrime/securesms/jobs/SmsSentJob.java delete mode 100644 src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java delete mode 100644 src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java delete mode 100644 src/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java delete mode 100644 src/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java delete mode 100644 src/org/thoughtcrime/securesms/service/MmsListener.java delete mode 100644 src/org/thoughtcrime/securesms/service/SmsDeliveryListener.java delete mode 100644 src/org/thoughtcrime/securesms/util/MmsCharacterCalculator.java delete mode 100644 src/org/thoughtcrime/securesms/util/SmsCharacterCalculator.java delete mode 100644 test/androidTest/java/org/thoughtcrime/securesms/TextSecureTestCase.java delete mode 100644 test/unitTest/java/org/thoughtcrime/securesms/BaseUnitTest.java delete mode 100644 test/unitTest/java/org/thoughtcrime/securesms/database/AddressTest.java delete mode 100644 test/unitTest/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java delete mode 100644 test/unitTest/java/org/thoughtcrime/securesms/service/SmsListenerTest.java delete mode 100644 test/unitTest/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java delete mode 100644 test/unitTest/java/org/thoughtcrime/securesms/util/ListPartitionTest.java delete mode 100644 test/unitTest/java/org/thoughtcrime/securesms/util/PhoneNumberFormatterTest.java delete mode 100644 test/unitTest/java/org/thoughtcrime/securesms/util/Rfc5724UriTest.java delete mode 100644 test/unitTest/java/org/thoughtcrime/securesms/util/ShortCodeUtilTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c4e916fcc..cdd0709c2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -400,27 +400,6 @@ - - - - - - - - - - - - - - - - - diff --git a/src/org/thoughtcrime/securesms/backup/FullBackupBase.java b/src/org/thoughtcrime/securesms/backup/FullBackupBase.java deleted file mode 100644 index 431f60bd9..000000000 --- a/src/org/thoughtcrime/securesms/backup/FullBackupBase.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.thoughtcrime.securesms.backup; - - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import org.greenrobot.eventbus.EventBus; -import org.whispersystems.libsignal.util.ByteUtil; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public abstract class FullBackupBase { - - @SuppressWarnings("unused") - private static final String TAG = FullBackupBase.class.getSimpleName(); - - static class BackupStream { - static @NonNull byte[] getBackupKey(@NonNull String passphrase, @Nullable byte[] salt) { - try { - EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, 0)); - - MessageDigest digest = MessageDigest.getInstance("SHA-512"); - byte[] input = passphrase.replace(" ", "").getBytes(); - byte[] hash = input; - - if (salt != null) digest.update(salt); - - for (int i=0;i<250000;i++) { - if (i % 1000 == 0) EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, 0)); - digest.update(hash); - hash = digest.digest(input); - } - - return ByteUtil.trim(hash, 32); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - } - - public static class BackupEvent { - public enum Type { - PROGRESS, - FINISHED - } - - private final Type type; - private final int count; - - BackupEvent(Type type, int count) { - this.type = type; - this.count = count; - } - - public Type getType() { - return type; - } - - public int getCount() { - return count; - } - } - -} diff --git a/src/org/thoughtcrime/securesms/backup/FullBackupExporter.java b/src/org/thoughtcrime/securesms/backup/FullBackupExporter.java deleted file mode 100644 index ba165b269..000000000 --- a/src/org/thoughtcrime/securesms/backup/FullBackupExporter.java +++ /dev/null @@ -1,384 +0,0 @@ -package org.thoughtcrime.securesms.backup; - - -import android.content.Context; -import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.TextUtils; -import android.util.Log; - -import com.annimon.stream.function.Consumer; -import com.annimon.stream.function.Predicate; -import com.google.protobuf.ByteString; - -import net.sqlcipher.database.SQLiteDatabase; - -import org.greenrobot.eventbus.EventBus; -import org.thoughtcrime.securesms.attachments.AttachmentId; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream; -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.GroupReceiptDatabase; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.MmsSmsColumns; -import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase; -import org.thoughtcrime.securesms.database.SearchDatabase; -import org.thoughtcrime.securesms.database.SessionDatabase; -import org.thoughtcrime.securesms.database.SignedPreKeyDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.profiles.AvatarHelper; -import org.thoughtcrime.securesms.util.Conversions; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.kdf.HKDFv3; -import org.whispersystems.libsignal.util.ByteUtil; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.LinkedList; -import java.util.List; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class FullBackupExporter extends FullBackupBase { - - @SuppressWarnings("unused") - private static final String TAG = FullBackupExporter.class.getSimpleName(); - - public static void export(@NonNull Context context, - @NonNull AttachmentSecret attachmentSecret, - @NonNull SQLiteDatabase input, - @NonNull File output, - @NonNull String passphrase) - throws IOException - { - BackupFrameOutputStream outputStream = new BackupFrameOutputStream(output, passphrase); - outputStream.writeDatabaseVersion(input.getVersion()); - - List tables = exportSchema(input, outputStream); - int count = 0; - - for (String table : tables) { - if (table.equals(SmsDatabase.TABLE_NAME) || table.equals(MmsDatabase.TABLE_NAME)) { - count = exportTable(table, input, outputStream, cursor -> cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.EXPIRES_IN)) <= 0, null, count); - } else if (table.equals(GroupReceiptDatabase.TABLE_NAME)) { - count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptDatabase.MMS_ID))), null, count); - } else if (table.equals(AttachmentDatabase.TABLE_NAME)) { - count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID))), cursor -> exportAttachment(attachmentSecret, cursor, outputStream), count); - } else if (!table.equals(SignedPreKeyDatabase.TABLE_NAME) && - !table.equals(OneTimePreKeyDatabase.TABLE_NAME) && - !table.equals(SessionDatabase.TABLE_NAME) && - !table.startsWith(SearchDatabase.SMS_FTS_TABLE_NAME) && - !table.startsWith(SearchDatabase.MMS_FTS_TABLE_NAME)) - { - count = exportTable(table, input, outputStream, null, null, count); - } - } - - for (BackupProtos.SharedPreference preference : IdentityKeyUtil.getBackupRecord(context)) { - EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count)); - outputStream.write(preference); - } - - for (File avatar : AvatarHelper.getAvatarFiles(context)) { - EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count)); - outputStream.write(avatar.getName(), new FileInputStream(avatar), avatar.length()); - } - - outputStream.writeEnd(); - outputStream.close(); - EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, ++count)); - } - - private static List exportSchema(@NonNull SQLiteDatabase input, @NonNull BackupFrameOutputStream outputStream) - throws IOException - { - List tables = new LinkedList<>(); - - try (Cursor cursor = input.rawQuery("SELECT sql, name, type FROM sqlite_master", null)) { - while (cursor != null && cursor.moveToNext()) { - String sql = cursor.getString(0); - String name = cursor.getString(1); - String type = cursor.getString(2); - - if (sql != null) { - - boolean isSmsFtsSecretTable = name != null && !name.equals(SearchDatabase.SMS_FTS_TABLE_NAME) && name.startsWith(SearchDatabase.SMS_FTS_TABLE_NAME); - boolean isMmsFtsSecretTable = name != null && !name.equals(SearchDatabase.MMS_FTS_TABLE_NAME) && name.startsWith(SearchDatabase.MMS_FTS_TABLE_NAME); - - if (!isSmsFtsSecretTable && !isMmsFtsSecretTable) { - if ("table".equals(type)) { - tables.add(name); - } - - outputStream.write(BackupProtos.SqlStatement.newBuilder().setStatement(cursor.getString(0)).build()); - } - } - } - } - - return tables; - } - - private static int exportTable(@NonNull String table, - @NonNull SQLiteDatabase input, - @NonNull BackupFrameOutputStream outputStream, - @Nullable Predicate predicate, - @Nullable Consumer postProcess, - int count) - throws IOException - { - String template = "INSERT INTO " + table + " VALUES "; - - try (Cursor cursor = input.rawQuery("SELECT * FROM " + table, null)) { - while (cursor != null && cursor.moveToNext()) { - EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count)); - - if (predicate == null || predicate.test(cursor)) { - StringBuilder statement = new StringBuilder(template); - BackupProtos.SqlStatement.Builder statementBuilder = BackupProtos.SqlStatement.newBuilder(); - - statement.append('('); - - for (int i=0;i 0) { - InputStream inputStream; - - if (random != null && random.length == 32) inputStream = ModernDecryptingPartInputStream.createFor(attachmentSecret, random, new File(data), 0); - else inputStream = ClassicDecryptingPartInputStream.createFor(attachmentSecret, new File(data)); - - outputStream.write(new AttachmentId(rowId, uniqueId), inputStream, size); - } - } catch (IOException e) { - Log.w(TAG, e); - } - } - - private static long calculateVeryOldStreamLength(@NonNull AttachmentSecret attachmentSecret, @Nullable byte[] random, @NonNull String data) throws IOException { - long result = 0; - InputStream inputStream; - - if (random != null && random.length == 32) inputStream = ModernDecryptingPartInputStream.createFor(attachmentSecret, random, new File(data), 0); - else inputStream = ClassicDecryptingPartInputStream.createFor(attachmentSecret, new File(data)); - - int read; - byte[] buffer = new byte[8192]; - - while ((read = inputStream.read(buffer, 0, buffer.length)) != -1) { - result += read; - } - - return result; - } - - private static boolean isForNonExpiringMessage(@NonNull SQLiteDatabase db, long mmsId) { - String[] columns = new String[] { MmsDatabase.EXPIRES_IN }; - String where = MmsDatabase.ID + " = ?"; - String[] args = new String[] { String.valueOf(mmsId) }; - - try (Cursor mmsCursor = db.query(MmsDatabase.TABLE_NAME, columns, where, args, null, null, null)) { - if (mmsCursor != null && mmsCursor.moveToFirst()) { - return mmsCursor.getLong(0) == 0; - } - } - - return false; - } - - - private static class BackupFrameOutputStream extends BackupStream { - - private final OutputStream outputStream; - private final Cipher cipher; - private final Mac mac; - - private final byte[] cipherKey; - private final byte[] macKey; - - private byte[] iv; - private int counter; - - private BackupFrameOutputStream(@NonNull File output, @NonNull String passphrase) throws IOException { - try { - byte[] salt = Util.getSecretBytes(32); - byte[] key = getBackupKey(passphrase, salt); - byte[] derived = new HKDFv3().deriveSecrets(key, "Backup Export".getBytes(), 64); - byte[][] split = ByteUtil.split(derived, 32, 32); - - this.cipherKey = split[0]; - this.macKey = split[1]; - - this.cipher = Cipher.getInstance("AES/CTR/NoPadding"); - this.mac = Mac.getInstance("HmacSHA256"); - this.outputStream = new FileOutputStream(output); - this.iv = Util.getSecretBytes(16); - this.counter = Conversions.byteArrayToInt(iv); - - mac.init(new SecretKeySpec(macKey, "HmacSHA256")); - - byte[] header = BackupProtos.BackupFrame.newBuilder().setHeader(BackupProtos.Header.newBuilder() - .setIv(ByteString.copyFrom(iv)) - .setSalt(ByteString.copyFrom(salt))) - .build().toByteArray(); - - outputStream.write(Conversions.intToByteArray(header.length)); - outputStream.write(header); - } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) { - throw new AssertionError(e); - } - } - - public void write(BackupProtos.SharedPreference preference) throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder().setPreference(preference).build()); - } - - public void write(BackupProtos.SqlStatement statement) throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder().setStatement(statement).build()); - } - - public void write(@NonNull String avatarName, @NonNull InputStream in, long size) throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder() - .setAvatar(BackupProtos.Avatar.newBuilder() - .setName(avatarName) - .setLength(Util.toIntExact(size)) - .build()) - .build()); - - writeStream(in); - } - - public void write(@NonNull AttachmentId attachmentId, @NonNull InputStream in, long size) throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder() - .setAttachment(BackupProtos.Attachment.newBuilder() - .setRowId(attachmentId.getRowId()) - .setAttachmentId(attachmentId.getUniqueId()) - .setLength(Util.toIntExact(size)) - .build()) - .build()); - - writeStream(in); - } - - void writeDatabaseVersion(int version) throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder() - .setVersion(BackupProtos.DatabaseVersion.newBuilder().setVersion(version)) - .build()); - } - - void writeEnd() throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder().setEnd(true).build()); - } - - private void writeStream(@NonNull InputStream inputStream) throws IOException { - try { - Conversions.intToByteArray(iv, 0, counter++); - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(cipherKey, "AES"), new IvParameterSpec(iv)); - mac.update(iv); - - byte[] buffer = new byte[8192]; - int read; - - while ((read = inputStream.read(buffer)) != -1) { - byte[] ciphertext = cipher.update(buffer, 0, read); - - if (ciphertext != null) { - outputStream.write(ciphertext); - mac.update(ciphertext); - } - } - - byte[] remainder = cipher.doFinal(); - outputStream.write(remainder); - mac.update(remainder); - - byte[] attachmentDigest = mac.doFinal(); - outputStream.write(attachmentDigest, 0, 10); - } catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { - throw new AssertionError(e); - } - } - - private void write(@NonNull OutputStream out, @NonNull BackupProtos.BackupFrame frame) throws IOException { - try { - Conversions.intToByteArray(iv, 0, counter++); - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(cipherKey, "AES"), new IvParameterSpec(iv)); - - byte[] frameCiphertext = cipher.doFinal(frame.toByteArray()); - byte[] frameMac = mac.doFinal(frameCiphertext); - byte[] length = Conversions.intToByteArray(frameCiphertext.length + 10); - - out.write(length); - out.write(frameCiphertext); - out.write(frameMac, 0, 10); - } catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { - throw new AssertionError(e); - } - } - - - public void close() throws IOException { - outputStream.close(); - } - } -} diff --git a/src/org/thoughtcrime/securesms/database/ApnDatabase.java b/src/org/thoughtcrime/securesms/database/ApnDatabase.java deleted file mode 100644 index ad069ea6b..000000000 --- a/src/org/thoughtcrime/securesms/database/ApnDatabase.java +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright (C) 2014 Open 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.database; - -import android.content.Context; -import android.content.res.AssetManager; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.text.TextUtils; -import android.util.Log; - -import org.thoughtcrime.securesms.mms.LegacyMmsConnection.Apn; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; - -/** - * Database to query APN and MMSC information - */ -public class ApnDatabase { - private static final String TAG = ApnDatabase.class.getSimpleName(); - - private final SQLiteDatabase db; - private final Context context; - - private static final String DATABASE_NAME = "apns.db"; - private static final String ASSET_PATH = "databases" + File.separator + DATABASE_NAME; - - private static final String TABLE_NAME = "apns"; - private static final String ID_COLUMN = "_id"; - private static final String MCC_MNC_COLUMN = "mccmnc"; - private static final String MCC_COLUMN = "mcc"; - private static final String MNC_COLUMN = "mnc"; - private static final String CARRIER_COLUMN = "carrier"; - private static final String APN_COLUMN = "apn"; - private static final String MMSC_COLUMN = "mmsc"; - private static final String PORT_COLUMN = "port"; - private static final String TYPE_COLUMN = "type"; - private static final String PROTOCOL_COLUMN = "protocol"; - private static final String BEARER_COLUMN = "bearer"; - private static final String ROAMING_PROTOCOL_COLUMN = "roaming_protocol"; - private static final String CARRIER_ENABLED_COLUMN = "carrier_enabled"; - private static final String MMS_PROXY_COLUMN = "mmsproxy"; - private static final String MMS_PORT_COLUMN = "mmsport"; - private static final String PROXY_COLUMN = "proxy"; - private static final String MVNO_MATCH_DATA_COLUMN = "mvno_match_data"; - private static final String MVNO_TYPE_COLUMN = "mvno"; - private static final String AUTH_TYPE_COLUMN = "authtype"; - private static final String USER_COLUMN = "user"; - private static final String PASSWORD_COLUMN = "password"; - private static final String SERVER_COLUMN = "server"; - - private static final String BASE_SELECTION = MCC_MNC_COLUMN + " = ?"; - - private static ApnDatabase instance = null; - - public synchronized static ApnDatabase getInstance(Context context) throws IOException { - if (instance == null) instance = new ApnDatabase(context.getApplicationContext()); - return instance; - } - - private ApnDatabase(final Context context) throws IOException { - this.context = context; - - File dbFile = context.getDatabasePath(DATABASE_NAME); - - if (!dbFile.getParentFile().exists() && !dbFile.getParentFile().mkdir()) { - throw new IOException("couldn't make databases directory"); - } - - Util.copy(context.getAssets().open(ASSET_PATH, AssetManager.ACCESS_STREAMING), - new FileOutputStream(dbFile)); - - try { - this.db = SQLiteDatabase.openDatabase(context.getDatabasePath(DATABASE_NAME).getPath(), - null, - SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS); - } catch (SQLiteException e) { - throw new IOException(e); - } - } - - private Apn getCustomApnParameters() { - String mmsc = TextSecurePreferences.getMmscUrl(context).trim(); - - if (!TextUtils.isEmpty(mmsc) && !mmsc.startsWith("http")) - mmsc = "http://" + mmsc; - - String proxy = TextSecurePreferences.getMmscProxy(context); - String port = TextSecurePreferences.getMmscProxyPort(context); - String user = TextSecurePreferences.getMmscUsername(context); - String pass = TextSecurePreferences.getMmscPassword(context); - - return new Apn(mmsc, proxy, port, user, pass); - } - - public Apn getDefaultApnParameters(String mccmnc, String apn) { - if (mccmnc == null) { - Log.w(TAG, "mccmnc was null, returning null"); - return Apn.EMPTY; - } - - Cursor cursor = null; - - try { - if (apn != null) { - Log.w(TAG, "Querying table for MCC+MNC " + mccmnc + " and APN name " + apn); - cursor = db.query(TABLE_NAME, null, - BASE_SELECTION + " AND " + APN_COLUMN + " = ?", - new String[] {mccmnc, apn}, - null, null, null); - } - - if (cursor == null || !cursor.moveToFirst()) { - if (cursor != null) cursor.close(); - Log.w(TAG, "Querying table for MCC+MNC " + mccmnc + " without APN name"); - cursor = db.query(TABLE_NAME, null, - BASE_SELECTION, - new String[] {mccmnc}, - null, null, null); - } - - if (cursor != null && cursor.moveToFirst()) { - Apn params = new Apn(cursor.getString(cursor.getColumnIndexOrThrow(MMSC_COLUMN)), - cursor.getString(cursor.getColumnIndexOrThrow(MMS_PROXY_COLUMN)), - cursor.getString(cursor.getColumnIndexOrThrow(MMS_PORT_COLUMN)), - cursor.getString(cursor.getColumnIndexOrThrow(USER_COLUMN)), - cursor.getString(cursor.getColumnIndexOrThrow(PASSWORD_COLUMN))); - Log.w(TAG, "Returning preferred APN " + params); - return params; - } - - Log.w(TAG, "No matching APNs found, returning null"); - - return Apn.EMPTY; - } finally { - if (cursor != null) cursor.close(); - } - - } - - public Optional getMmsConnectionParameters(String mccmnc, String apn) { - Apn customApn = getCustomApnParameters(); - Apn defaultApn = getDefaultApnParameters(mccmnc, apn); - Apn result = new Apn(customApn, defaultApn, - TextSecurePreferences.getUseCustomMmsc(context), - TextSecurePreferences.getUseCustomMmscProxy(context), - TextSecurePreferences.getUseCustomMmscProxyPort(context), - TextSecurePreferences.getUseCustomMmscUsername(context), - TextSecurePreferences.getUseCustomMmscPassword(context)); - - if (TextUtils.isEmpty(result.getMmsc())) return Optional.absent(); - else return Optional.of(result); - } -} diff --git a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java deleted file mode 100644 index a1314be8d..000000000 --- a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java +++ /dev/null @@ -1,252 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import android.net.Uri; -import android.support.annotation.Nullable; -import android.util.Log; - -import com.google.android.mms.pdu_alt.CharacterSets; -import com.google.android.mms.pdu_alt.EncodedStringValue; -import com.google.android.mms.pdu_alt.PduBody; -import com.google.android.mms.pdu_alt.PduPart; -import com.google.android.mms.pdu_alt.RetrieveConf; - -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.UriAttachment; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.jobmanager.JobParameters; -import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement; -import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; -import org.thoughtcrime.securesms.mms.ApnUnavailableException; -import org.thoughtcrime.securesms.mms.CompatMmsConnection; -import org.thoughtcrime.securesms.mms.IncomingMediaMessage; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.MmsRadioException; -import org.thoughtcrime.securesms.mms.PartParser; -import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.providers.SingleUseBlobProvider; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.DuplicateMessageException; -import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.LegacyMessageException; -import org.whispersystems.libsignal.NoSessionException; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -public class MmsDownloadJob extends MasterSecretJob { - - private static final long serialVersionUID = 1L; - - private static final String TAG = MmsDownloadJob.class.getSimpleName(); - - private final long messageId; - private final long threadId; - private final boolean automatic; - - public MmsDownloadJob(Context context, long messageId, long threadId, boolean automatic) { - super(context, JobParameters.newBuilder() - .withPersistence() - .withRequirement(new MasterSecretRequirement(context)) - .withRequirement(new NetworkRequirement(context)) - .withGroupId("mms-operation") - .withWakeLock(true, 30, TimeUnit.SECONDS) - .create()); - - this.messageId = messageId; - this.threadId = threadId; - this.automatic = automatic; - } - - @Override - public void onAdded() { - if (automatic && KeyCachingService.getMasterSecret(context) == null) { - DatabaseFactory.getMmsDatabase(context).markIncomingNotificationReceived(threadId); - MessageNotifier.updateNotification(context); - } - } - - @Override - public void onRun(MasterSecret masterSecret) { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - Optional notification = database.getNotification(messageId); - - if (!notification.isPresent()) { - Log.w(TAG, "No notification for ID: " + messageId); - return; - } - - try { - if (notification.get().getContentLocation() == null) { - throw new MmsException("Notification content location was null."); - } - - if (!TextSecurePreferences.isPushRegistered(context)) { - throw new MmsException("Not registered"); - } - - database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_CONNECTING); - - String contentLocation = notification.get().getContentLocation(); - byte[] transactionId = new byte[0]; - - try { - if (notification.get().getTransactionId() != null) { - transactionId = notification.get().getTransactionId().getBytes(CharacterSets.MIMENAME_ISO_8859_1); - } else { - Log.w(TAG, "No transaction ID!"); - } - } catch (UnsupportedEncodingException e) { - Log.w(TAG, e); - } - - Log.w(TAG, "Downloading mms at " + Uri.parse(contentLocation).getHost() + ", subscription ID: " + notification.get().getSubscriptionId()); - - RetrieveConf retrieveConf = new CompatMmsConnection(context).retrieve(contentLocation, transactionId, notification.get().getSubscriptionId()); - - if (retrieveConf == null) { - throw new MmsException("RetrieveConf was null"); - } - - storeRetrievedMms(contentLocation, messageId, threadId, retrieveConf, notification.get().getSubscriptionId(), notification.get().getFrom()); - } catch (ApnUnavailableException e) { - Log.w(TAG, e); - handleDownloadError(messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE, - automatic); - } catch (MmsException e) { - Log.w(TAG, e); - handleDownloadError(messageId, threadId, - MmsDatabase.Status.DOWNLOAD_HARD_FAILURE, - automatic); - } catch (MmsRadioException | IOException e) { - Log.w(TAG, e); - handleDownloadError(messageId, threadId, - MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE, - automatic); - } catch (DuplicateMessageException e) { - Log.w(TAG, e); - database.markAsDecryptDuplicate(messageId, threadId); - } catch (LegacyMessageException e) { - Log.w(TAG, e); - database.markAsLegacyVersion(messageId, threadId); - } catch (NoSessionException e) { - Log.w(TAG, e); - database.markAsNoSession(messageId, threadId); - } catch (InvalidMessageException e) { - Log.w(TAG, e); - database.markAsDecryptFailed(messageId, threadId); - } - } - - @Override - public void onCanceled() { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE); - - if (automatic) { - database.markIncomingNotificationReceived(threadId); - MessageNotifier.updateNotification(context, threadId); - } - } - - @Override - public boolean onShouldRetryThrowable(Exception exception) { - return false; - } - - private void storeRetrievedMms(String contentLocation, - long messageId, long threadId, RetrieveConf retrieved, - int subscriptionId, @Nullable Address notificationFrom) - throws MmsException, NoSessionException, DuplicateMessageException, InvalidMessageException, - LegacyMessageException - { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - SingleUseBlobProvider provider = SingleUseBlobProvider.getInstance(); - Optional
group = Optional.absent(); - Set
members = new HashSet<>(); - String body = null; - List attachments = new LinkedList<>(); - - Address from; - - if (retrieved.getFrom() != null) { - from = Address.fromExternal(context, Util.toIsoString(retrieved.getFrom().getTextString())); - } else if (notificationFrom != null) { - from = notificationFrom; - } else { - from = Address.UNKNOWN; - } - - if (retrieved.getTo() != null) { - for (EncodedStringValue toValue : retrieved.getTo()) { - members.add(Address.fromExternal(context, Util.toIsoString(toValue.getTextString()))); - } - } - - if (retrieved.getCc() != null) { - for (EncodedStringValue ccValue : retrieved.getCc()) { - members.add(Address.fromExternal(context, Util.toIsoString(ccValue.getTextString()))); - } - } - - members.add(from); - members.add(Address.fromExternal(context, TextSecurePreferences.getLocalNumber(context))); - - if (retrieved.getBody() != null) { - body = PartParser.getMessageText(retrieved.getBody()); - PduBody media = PartParser.getSupportedMediaParts(retrieved.getBody()); - - for (int i=0;i 2) { - group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), true))); - } - - IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false); - Optional insertResult = database.insertMessageInbox(message, contentLocation, threadId); - - if (insertResult.isPresent()) { - database.delete(messageId); - MessageNotifier.updateNotification(context, insertResult.get().getThreadId()); - } - } - - private void handleDownloadError(long messageId, long threadId, int downloadStatus, boolean automatic) - { - MmsDatabase db = DatabaseFactory.getMmsDatabase(context); - - db.markDownloadState(messageId, downloadStatus); - - if (automatic) { - db.markIncomingNotificationReceived(threadId); - MessageNotifier.updateNotification(context, threadId); - } - } -} diff --git a/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java deleted file mode 100644 index a5ea75b98..000000000 --- a/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import android.util.Log; -import android.util.Pair; - -import com.google.android.mms.pdu_alt.GenericPdu; -import com.google.android.mms.pdu_alt.NotificationInd; -import com.google.android.mms.pdu_alt.PduHeaders; -import com.google.android.mms.pdu_alt.PduParser; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.jobmanager.JobParameters; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.util.Util; - -public class MmsReceiveJob extends ContextJob { - - private static final long serialVersionUID = 1L; - - private static final String TAG = MmsReceiveJob.class.getSimpleName(); - - private final byte[] data; - private final int subscriptionId; - - public MmsReceiveJob(Context context, byte[] data, int subscriptionId) { - super(context, JobParameters.newBuilder() - .withWakeLock(true) - .withPersistence().create()); - - this.data = data; - this.subscriptionId = subscriptionId; - } - - @Override - public void onAdded() { - - } - - @Override - public void onRun() { - if (data == null) { - Log.w(TAG, "Received NULL pdu, ignoring..."); - return; - } - - PduParser parser = new PduParser(data); - GenericPdu pdu = null; - - try { - pdu = parser.parse(); - } catch (RuntimeException e) { - Log.w(TAG, e); - } - - if (isNotification(pdu) && !isBlocked(pdu)) { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - Pair messageAndThreadId = database.insertMessageInbox((NotificationInd)pdu, subscriptionId); - - Log.w(TAG, "Inserted received MMS notification..."); - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MmsDownloadJob(context, - messageAndThreadId.first, - messageAndThreadId.second, - true)); - } else if (isNotification(pdu)) { - Log.w(TAG, "*** Received blocked MMS, ignoring..."); - } - } - - @Override - public void onCanceled() { - // TODO - } - - @Override - public boolean onShouldRetry(Exception exception) { - return false; - } - - private boolean isBlocked(GenericPdu pdu) { - if (pdu.getFrom() != null && pdu.getFrom().getTextString() != null) { - Recipient recipients = Recipient.from(context, Address.fromExternal(context, Util.toIsoString(pdu.getFrom().getTextString())), false); - return recipients.isBlocked(); - } - - return false; - } - - private boolean isNotification(GenericPdu pdu) { - return pdu != null && pdu.getMessageType() == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND; - } -} diff --git a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java deleted file mode 100644 index 8e561be02..000000000 --- a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java +++ /dev/null @@ -1,298 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import android.text.TextUtils; -import android.util.Log; -import android.webkit.MimeTypeMap; - -import com.android.mms.dom.smil.parser.SmilXmlSerializer; -import com.google.android.mms.ContentType; -import com.google.android.mms.InvalidHeaderValueException; -import com.google.android.mms.pdu_alt.CharacterSets; -import com.google.android.mms.pdu_alt.EncodedStringValue; -import com.google.android.mms.pdu_alt.PduBody; -import com.google.android.mms.pdu_alt.PduComposer; -import com.google.android.mms.pdu_alt.PduHeaders; -import com.google.android.mms.pdu_alt.PduPart; -import com.google.android.mms.pdu_alt.SendConf; -import com.google.android.mms.pdu_alt.SendReq; -import com.google.android.mms.smil.SmilHelper; -import com.klinker.android.send_message.Utils; - -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.ThreadDatabase; -import org.thoughtcrime.securesms.jobmanager.JobParameters; -import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement; -import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; -import org.thoughtcrime.securesms.mms.CompatMmsConnection; -import org.thoughtcrime.securesms.mms.MediaConstraints; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.MmsSendResult; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; -import org.thoughtcrime.securesms.transport.UndeliverableMessageException; -import org.thoughtcrime.securesms.util.Hex; -import org.thoughtcrime.securesms.util.NumberUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -public class MmsSendJob extends SendJob { - - private static final long serialVersionUID = 0L; - - private static final String TAG = MmsSendJob.class.getSimpleName(); - - private final long messageId; - - public MmsSendJob(Context context, long messageId) { - super(context, JobParameters.newBuilder() - .withGroupId("mms-operation") - .withRequirement(new NetworkRequirement(context)) - .withRequirement(new MasterSecretRequirement(context)) - .withPersistence() - .create()); - - this.messageId = messageId; - } - - @Override - public void onAdded() { - - } - - @Override - public void onSend(MasterSecret masterSecret) throws MmsException, NoSuchMessageException, IOException { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - OutgoingMediaMessage message = database.getOutgoingMessage(messageId); - - try { - SendReq pdu = constructSendPdu(message); - - validateDestinations(message, pdu); - - final byte[] pduBytes = getPduBytes(pdu); - final SendConf sendConf = new CompatMmsConnection(context).send(pduBytes, message.getSubscriptionId()); - final MmsSendResult result = getSendResult(sendConf, pdu); - - database.markAsSent(messageId, false); - markAttachmentsUploaded(messageId, message.getAttachments()); - } catch (UndeliverableMessageException | IOException e) { - Log.w(TAG, e); - database.markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } catch (InsecureFallbackApprovalException e) { - Log.w(TAG, e); - database.markAsPendingInsecureSmsFallback(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } - - @Override - public boolean onShouldRetryThrowable(Exception exception) { - return false; - } - - @Override - public void onCanceled() { - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - - private byte[] getPduBytes(SendReq message) - throws IOException, UndeliverableMessageException, InsecureFallbackApprovalException - { - byte[] pduBytes = new PduComposer(context, message).make(); - - if (pduBytes == null) { - throw new UndeliverableMessageException("PDU composition failed, null payload"); - } - - return pduBytes; - } - - private MmsSendResult getSendResult(SendConf conf, SendReq message) - throws UndeliverableMessageException - { - if (conf == null) { - throw new UndeliverableMessageException("No M-Send.conf received in response to send."); - } else if (conf.getResponseStatus() != PduHeaders.RESPONSE_STATUS_OK) { - throw new UndeliverableMessageException("Got bad response: " + conf.getResponseStatus()); - } else if (isInconsistentResponse(message, conf)) { - throw new UndeliverableMessageException("Mismatched response!"); - } else { - return new MmsSendResult(conf.getMessageId(), conf.getResponseStatus()); - } - } - - private boolean isInconsistentResponse(SendReq message, SendConf response) { - Log.w(TAG, "Comparing: " + Hex.toString(message.getTransactionId())); - Log.w(TAG, "With: " + Hex.toString(response.getTransactionId())); - return !Arrays.equals(message.getTransactionId(), response.getTransactionId()); - } - - private void validateDestinations(EncodedStringValue[] destinations) throws UndeliverableMessageException { - if (destinations == null) return; - - for (EncodedStringValue destination : destinations) { - if (destination == null || !NumberUtil.isValidSmsOrEmail(destination.getString())) { - throw new UndeliverableMessageException("Invalid destination: " + - (destination == null ? null : destination.getString())); - } - } - } - - private void validateDestinations(OutgoingMediaMessage media, SendReq message) throws UndeliverableMessageException { - validateDestinations(message.getTo()); - validateDestinations(message.getCc()); - validateDestinations(message.getBcc()); - - if (message.getTo() == null && message.getCc() == null && message.getBcc() == null) { - throw new UndeliverableMessageException("No to, cc, or bcc specified!"); - } - - if (media.isSecure()) { - throw new UndeliverableMessageException("Attempt to send encrypted MMS?"); - } - } - - private SendReq constructSendPdu(OutgoingMediaMessage message) - throws UndeliverableMessageException - { - SendReq req = new SendReq(); - String lineNumber = getMyNumber(context); - Address destination = message.getRecipient().getAddress(); - MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId()); - List scaledAttachments = scaleAndStripExifFromAttachments(mediaConstraints, message.getAttachments()); - - if (!TextUtils.isEmpty(lineNumber)) { - req.setFrom(new EncodedStringValue(lineNumber)); - } else { - req.setFrom(new EncodedStringValue(TextSecurePreferences.getLocalNumber(context))); - } - - if (destination.isMmsGroup()) { - List members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(destination.toGroupString(), false); - - for (Recipient member : members) { - if (message.getDistributionType() == ThreadDatabase.DistributionTypes.BROADCAST) { - req.addBcc(new EncodedStringValue(member.getAddress().serialize())); - } else { - req.addTo(new EncodedStringValue(member.getAddress().serialize())); - } - } - } else { - req.addTo(new EncodedStringValue(destination.serialize())); - } - - req.setDate(System.currentTimeMillis() / 1000); - - PduBody body = new PduBody(); - int size = 0; - - if (!TextUtils.isEmpty(message.getBody())) { - PduPart part = new PduPart(); - String name = String.valueOf(System.currentTimeMillis()); - part.setData(Util.toUtf8Bytes(message.getBody())); - part.setCharset(CharacterSets.UTF_8); - part.setContentType(ContentType.TEXT_PLAIN.getBytes()); - part.setContentId(name.getBytes()); - part.setContentLocation((name + ".txt").getBytes()); - part.setName((name + ".txt").getBytes()); - - body.addPart(part); - size += getPartSize(part); - } - - for (Attachment attachment : scaledAttachments) { - try { - if (attachment.getDataUri() == null) throw new IOException("Assertion failed, attachment for outgoing MMS has no data!"); - - String fileName = attachment.getFileName(); - PduPart part = new PduPart(); - - if (fileName == null) { - fileName = String.valueOf(Math.abs(Util.getSecureRandom().nextLong())); - String fileExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType(attachment.getContentType()); - - if (fileExtension != null) fileName = fileName + "." + fileExtension; - } - - if (attachment.getContentType().startsWith("text")) { - part.setCharset(CharacterSets.UTF_8); - } - - part.setContentType(attachment.getContentType().getBytes()); - part.setContentLocation(fileName.getBytes()); - part.setName(fileName.getBytes()); - - int index = fileName.lastIndexOf("."); - String contentId = (index == -1) ? fileName : fileName.substring(0, index); - part.setContentId(contentId.getBytes()); - part.setData(Util.readFully(PartAuthority.getAttachmentStream(context, attachment.getDataUri()))); - - body.addPart(part); - size += getPartSize(part); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - SmilXmlSerializer.serialize(SmilHelper.createSmilDocument(body), out); - PduPart smilPart = new PduPart(); - smilPart.setContentId("smil".getBytes()); - smilPart.setContentLocation("smil.xml".getBytes()); - smilPart.setContentType(ContentType.APP_SMIL.getBytes()); - smilPart.setData(out.toByteArray()); - body.addPart(0, smilPart); - - req.setBody(body); - req.setMessageSize(size); - req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes()); - req.setExpiry(7 * 24 * 60 * 60); - - try { - req.setPriority(PduHeaders.PRIORITY_NORMAL); - req.setDeliveryReport(PduHeaders.VALUE_NO); - req.setReadReport(PduHeaders.VALUE_NO); - } catch (InvalidHeaderValueException e) {} - - return req; - } - - private long getPartSize(PduPart part) { - return part.getName().length + part.getContentLocation().length + - part.getContentType().length + part.getData().length + - part.getContentId().length; - } - - private void notifyMediaMessageDeliveryFailed(Context context, long messageId) { - long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId); - Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); - - if (recipient != null) { - MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); - } - } - - private String getMyNumber(Context context) throws UndeliverableMessageException { - try { - return Utils.getMyPhoneNumber(context); - } catch (SecurityException e) { - throw new UndeliverableMessageException(e); - } - } -} diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java deleted file mode 100644 index a2ba460b9..000000000 --- a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java +++ /dev/null @@ -1,208 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.telephony.PhoneNumberUtils; -import android.telephony.SmsManager; -import android.util.Log; - -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.model.SmsMessageRecord; -import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; -import org.thoughtcrime.securesms.jobs.requirements.NetworkOrServiceRequirement; -import org.thoughtcrime.securesms.jobs.requirements.ServiceRequirement; -import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.service.SmsDeliveryListener; -import org.thoughtcrime.securesms.transport.UndeliverableMessageException; -import org.thoughtcrime.securesms.util.NumberUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.jobmanager.JobParameters; - -import java.util.ArrayList; - -public class SmsSendJob extends SendJob { - - private static final long serialVersionUID = -5118520036244759718L; - private static final String TAG = SmsSendJob.class.getSimpleName(); - - private final long messageId; - - public SmsSendJob(Context context, long messageId, String name) { - super(context, constructParameters(context, name)); - this.messageId = messageId; - } - - @Override - public void onAdded() {} - - @Override - public void onSend(MasterSecret masterSecret) throws NoSuchMessageException { - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - SmsMessageRecord record = database.getMessage(messageId); - - try { - Log.w(TAG, "Sending message: " + messageId); - - deliver(record); - } catch (UndeliverableMessageException ude) { - Log.w(TAG, ude); - DatabaseFactory.getSmsDatabase(context).markAsSentFailed(record.getId()); - MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId()); - } - } - - @Override - public boolean onShouldRetryThrowable(Exception throwable) { - return false; - } - - @Override - public void onCanceled() { - Log.w(TAG, "onCanceled()"); - long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId); - Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); - - DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId); - MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); - } - - private void deliver(SmsMessageRecord message) - throws UndeliverableMessageException - { - if (message.isSecure() || message.isKeyExchange() || message.isEndSession()) { - throw new UndeliverableMessageException("Trying to send a secure SMS?"); - } - - String recipient = message.getIndividualRecipient().getAddress().serialize(); - - // See issue #1516 for bug report, and discussion on commits related to #4833 for problems - // related to the original fix to #1516. This still may not be a correct fix if networks allow - // SMS/MMS sending to alphanumeric recipients other than email addresses, but should also - // help to fix issue #3099. - if (!NumberUtil.isValidEmail(recipient)) { - recipient = PhoneNumberUtils.stripSeparators(PhoneNumberUtils.convertKeypadLettersToDigits(recipient)); - } - - if (!NumberUtil.isValidSmsOrEmail(recipient)) { - throw new UndeliverableMessageException("Not a valid SMS destination! " + recipient); - } - - ArrayList messages = SmsManager.getDefault().divideMessage(message.getBody()); - ArrayList sentIntents = constructSentIntents(message.getId(), message.getType(), messages, false); - ArrayList deliveredIntents = constructDeliveredIntents(message.getId(), message.getType(), messages); - - // NOTE 11/04/14 -- There's apparently a bug where for some unknown recipients - // and messages, this will throw an NPE. We have no idea why, so we're just - // catching it and marking the message as a failure. That way at least it doesn't - // repeatedly crash every time you start the app. - try { - getSmsManagerFor(message.getSubscriptionId()).sendMultipartTextMessage(recipient, null, messages, sentIntents, deliveredIntents); - } catch (NullPointerException | IllegalArgumentException npe) { - Log.w(TAG, npe); - Log.w(TAG, "Recipient: " + recipient); - Log.w(TAG, "Message Parts: " + messages.size()); - - try { - for (int i=0;i constructSentIntents(long messageId, long type, - ArrayList messages, boolean secure) - { - ArrayList sentIntents = new ArrayList<>(messages.size()); - - for (String ignored : messages) { - sentIntents.add(PendingIntent.getBroadcast(context, 0, - constructSentIntent(context, messageId, type, secure, false), - 0)); - } - - return sentIntents; - } - - private ArrayList constructDeliveredIntents(long messageId, long type, ArrayList messages) { - if (!TextSecurePreferences.isSmsDeliveryReportsEnabled(context)) { - return null; - } - - ArrayList deliveredIntents = new ArrayList<>(messages.size()); - - for (String ignored : messages) { - deliveredIntents.add(PendingIntent.getBroadcast(context, 0, - constructDeliveredIntent(context, messageId, type), - 0)); - } - - return deliveredIntents; - } - - private Intent constructSentIntent(Context context, long messageId, long type, - boolean upgraded, boolean push) - { - Intent pending = new Intent(SmsDeliveryListener.SENT_SMS_ACTION, - Uri.parse("custom://" + messageId + System.currentTimeMillis()), - context, SmsDeliveryListener.class); - - pending.putExtra("type", type); - pending.putExtra("message_id", messageId); - pending.putExtra("upgraded", upgraded); - pending.putExtra("push", push); - - return pending; - } - - private Intent constructDeliveredIntent(Context context, long messageId, long type) { - Intent pending = new Intent(SmsDeliveryListener.DELIVERED_SMS_ACTION, - Uri.parse("custom://" + messageId + System.currentTimeMillis()), - context, SmsDeliveryListener.class); - pending.putExtra("type", type); - pending.putExtra("message_id", messageId); - - return pending; - } - - private SmsManager getSmsManagerFor(int subscriptionId) { - if (Build.VERSION.SDK_INT >= 22 && subscriptionId != -1) { - return SmsManager.getSmsManagerForSubscriptionId(subscriptionId); - } else { - return SmsManager.getDefault(); - } - } - - private static JobParameters constructParameters(Context context, String name) { - JobParameters.Builder builder = JobParameters.newBuilder() - .withPersistence() - .withRequirement(new MasterSecretRequirement(context)) - .withRetryCount(15) - .withGroupId(name); - - if (TextSecurePreferences.isWifiSmsEnabled(context)) { - builder.withRequirement(new NetworkOrServiceRequirement(context)); - } else { - builder.withRequirement(new ServiceRequirement(context)); - } - - return builder.create(); - } - - -} diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java deleted file mode 100644 index 1d26dcdc4..000000000 --- a/src/org/thoughtcrime/securesms/jobs/SmsSentJob.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.app.Activity; -import android.content.Context; -import android.telephony.SmsManager; -import android.util.Log; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.model.SmsMessageRecord; -import org.thoughtcrime.securesms.jobmanager.JobParameters; -import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; -import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.service.SmsDeliveryListener; - -public class SmsSentJob extends MasterSecretJob { - - private static final long serialVersionUID = -2624694558755317560L; - private static final String TAG = SmsSentJob.class.getSimpleName(); - - private final long messageId; - private final String action; - private final int result; - - public SmsSentJob(Context context, long messageId, String action, int result) { - super(context, JobParameters.newBuilder() - .withPersistence() - .withRequirement(new MasterSecretRequirement(context)) - .create()); - - this.messageId = messageId; - this.action = action; - this.result = result; - } - - @Override - public void onAdded() { - - } - - @Override - public void onRun(MasterSecret masterSecret) { - Log.w(TAG, "Got SMS callback: " + action + " , " + result); - - switch (action) { - case SmsDeliveryListener.SENT_SMS_ACTION: - handleSentResult(messageId, result); - break; - case SmsDeliveryListener.DELIVERED_SMS_ACTION: - handleDeliveredResult(messageId, result); - break; - } - } - - @Override - public boolean onShouldRetryThrowable(Exception throwable) { - return false; - } - - @Override - public void onCanceled() { - - } - - private void handleDeliveredResult(long messageId, int result) { - DatabaseFactory.getSmsDatabase(context).markStatus(messageId, result); - } - - private void handleSentResult(long messageId, int result) { - try { - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - SmsMessageRecord record = database.getMessage(messageId); - - switch (result) { - case Activity.RESULT_OK: - database.markAsSent(messageId, false); - break; - case SmsManager.RESULT_ERROR_NO_SERVICE: - case SmsManager.RESULT_ERROR_RADIO_OFF: - Log.w(TAG, "Service connectivity problem, requeuing..."); - ApplicationContext.getInstance(context) - .getJobManager() - .add(new SmsSendJob(context, messageId, record.getIndividualRecipient().getAddress().serialize())); - break; - default: - database.markAsSentFailed(messageId); - MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId()); - } - } catch (NoSuchMessageException e) { - Log.w(TAG, e); - } - } -} diff --git a/src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java b/src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java deleted file mode 100644 index 64f36f59c..000000000 --- a/src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.thoughtcrime.securesms.mms; - -import android.content.Context; -import android.os.Build; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - -import com.google.android.mms.pdu_alt.PduHeaders; -import com.google.android.mms.pdu_alt.RetrieveConf; -import com.google.android.mms.pdu_alt.SendConf; - -import org.thoughtcrime.securesms.transport.UndeliverableMessageException; - -import java.io.IOException; - -public class CompatMmsConnection implements OutgoingMmsConnection, IncomingMmsConnection { - private static final String TAG = CompatMmsConnection.class.getSimpleName(); - - private Context context; - - public CompatMmsConnection(Context context) { - this.context = context; - } - - @Nullable - @Override - public SendConf send(@NonNull byte[] pduBytes, int subscriptionId) - throws UndeliverableMessageException - { - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP_MR1) { - try { - Log.w(TAG, "Sending via Lollipop API"); - return new OutgoingLollipopMmsConnection(context).send(pduBytes, subscriptionId); - } catch (UndeliverableMessageException e) { - Log.w(TAG, e); - } - - Log.w(TAG, "Falling back to legacy connection..."); - } - - if (subscriptionId == -1) { - Log.w(TAG, "Sending via legacy connection"); - try { - SendConf result = new OutgoingLegacyMmsConnection(context).send(pduBytes, subscriptionId); - - if (result != null && result.getResponseStatus() == PduHeaders.RESPONSE_STATUS_OK) { - return result; - } else { - Log.w(TAG, "Got bad legacy response: " + (result != null ? result.getResponseStatus() : null)); - } - } catch (UndeliverableMessageException | ApnUnavailableException e) { - Log.w(TAG, e); - } - } - - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && VERSION.SDK_INT < VERSION_CODES.LOLLIPOP_MR1) { - Log.w(TAG, "Falling back to sending via Lollipop API"); - return new OutgoingLollipopMmsConnection(context).send(pduBytes, subscriptionId); - } - - throw new UndeliverableMessageException("Both lollipop and legacy connections failed..."); - } - - @Nullable - @Override - public RetrieveConf retrieve(@NonNull String contentLocation, - byte[] transactionId, - int subscriptionId) - throws MmsException, MmsRadioException, ApnUnavailableException, IOException - { - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP_MR1) { - Log.w(TAG, "Receiving via Lollipop API"); - try { - return new IncomingLollipopMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId); - } catch (MmsException e) { - Log.w(TAG, e); - } - - Log.w(TAG, "Falling back to receiving via legacy connection"); - } - - if (VERSION.SDK_INT < 22 || subscriptionId == -1) { - Log.w(TAG, "Receiving via legacy API"); - try { - return new IncomingLegacyMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId); - } catch (MmsRadioException | ApnUnavailableException | IOException e) { - Log.w(TAG, e); - } - } - - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && VERSION.SDK_INT < VERSION_CODES.LOLLIPOP_MR1) { - Log.w(TAG, "Falling back to receiving via Lollipop API"); - return new IncomingLollipopMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId); - } - - throw new IOException("Both lollipop and fallback APIs failed..."); - } -} diff --git a/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java b/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java deleted file mode 100644 index b184ce360..000000000 --- a/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (C) 2015 Open 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.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - -import com.google.android.mms.InvalidHeaderValueException; -import com.google.android.mms.pdu_alt.NotifyRespInd; -import com.google.android.mms.pdu_alt.PduComposer; -import com.google.android.mms.pdu_alt.PduHeaders; -import com.google.android.mms.pdu_alt.PduParser; -import com.google.android.mms.pdu_alt.RetrieveConf; - -import org.apache.http.Header; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpGetHC4; -import org.apache.http.client.methods.HttpUriRequest; - -import java.io.IOException; -import java.util.Arrays; - - -@SuppressWarnings("deprecation") -public class IncomingLegacyMmsConnection extends LegacyMmsConnection implements IncomingMmsConnection { - private static final String TAG = IncomingLegacyMmsConnection.class.getSimpleName(); - - public IncomingLegacyMmsConnection(Context context) throws ApnUnavailableException { - super(context); - } - - private HttpUriRequest constructRequest(Apn contentApn, boolean useProxy) throws IOException { - HttpGetHC4 request; - - try { - request = new HttpGetHC4(contentApn.getMmsc()); - } catch (IllegalArgumentException e) { - // #7339 - throw new IOException(e); - } - - for (Header header : getBaseHeaders()) { - request.addHeader(header); - } - - if (useProxy) { - HttpHost proxy = new HttpHost(contentApn.getProxy(), contentApn.getPort()); - request.setConfig(RequestConfig.custom().setProxy(proxy).build()); - } - - return request; - } - - @Override - public @Nullable RetrieveConf retrieve(@NonNull String contentLocation, - byte[] transactionId, int subscriptionId) - throws MmsRadioException, ApnUnavailableException, IOException - { - MmsRadio radio = MmsRadio.getInstance(context); - Apn contentApn = new Apn(contentLocation, apn.getProxy(), Integer.toString(apn.getPort()), apn.getUsername(), apn.getPassword()); - - if (isDirectConnect()) { - Log.w(TAG, "Connecting directly..."); - try { - return retrieve(contentApn, transactionId, false, false); - } catch (IOException | ApnUnavailableException e) { - Log.w(TAG, e); - } - } - - Log.w(TAG, "Changing radio to MMS mode.."); - radio.connect(); - - try { - Log.w(TAG, "Downloading in MMS mode with proxy..."); - - try { - return retrieve(contentApn, transactionId, true, true); - } catch (IOException | ApnUnavailableException e) { - Log.w(TAG, e); - } - - Log.w(TAG, "Downloading in MMS mode without proxy..."); - - return retrieve(contentApn, transactionId, true, false); - - } finally { - radio.disconnect(); - } - } - - public RetrieveConf retrieve(Apn contentApn, byte[] transactionId, boolean usingMmsRadio, boolean useProxyIfAvailable) - throws IOException, ApnUnavailableException - { - byte[] pdu = null; - - final boolean useProxy = useProxyIfAvailable && contentApn.hasProxy(); - final String targetHost = useProxy - ? contentApn.getProxy() - : Uri.parse(contentApn.getMmsc()).getHost(); - if (checkRouteToHost(context, targetHost, usingMmsRadio)) { - Log.w(TAG, "got successful route to host " + targetHost); - pdu = execute(constructRequest(contentApn, useProxy)); - } - - if (pdu == null) { - throw new IOException("Connection manager could not obtain route to host."); - } - - RetrieveConf retrieved = (RetrieveConf)new PduParser(pdu).parse(); - - if (retrieved == null) { - Log.w(TAG, "Couldn't parse PDU, byte response: " + Arrays.toString(pdu)); - Log.w(TAG, "Couldn't parse PDU, ASCII: " + new String(pdu)); - throw new IOException("Bad retrieved PDU"); - } - - sendRetrievedAcknowledgement(transactionId, usingMmsRadio, useProxy); - return retrieved; - } - - private void sendRetrievedAcknowledgement(byte[] transactionId, - boolean usingRadio, - boolean useProxy) - throws ApnUnavailableException - { - try { - NotifyRespInd notifyResponse = new NotifyRespInd(PduHeaders.CURRENT_MMS_VERSION, - transactionId, - PduHeaders.STATUS_RETRIEVED); - - OutgoingLegacyMmsConnection connection = new OutgoingLegacyMmsConnection(context); - connection.sendNotificationReceived(new PduComposer(context, notifyResponse).make(), usingRadio, useProxy); - } catch (InvalidHeaderValueException | IOException e) { - Log.w(TAG, e); - } - } -} diff --git a/src/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java b/src/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java deleted file mode 100644 index d65f3a34c..000000000 --- a/src/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java +++ /dev/null @@ -1,296 +0,0 @@ -/** - * 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.net.ConnectivityManager; -import android.telephony.TelephonyManager; -import android.text.TextUtils; -import android.util.Log; - -import org.apache.http.Header; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.impl.NoConnectionReuseStrategyHC4; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.client.LaxRedirectStrategy; -import org.apache.http.impl.conn.BasicHttpClientConnectionManager; -import org.apache.http.message.BasicHeader; -import org.thoughtcrime.securesms.database.ApnDatabase; -import org.thoughtcrime.securesms.util.ServiceUtil; -import org.thoughtcrime.securesms.util.TelephonyUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.guava.Optional; - -import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.URL; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -@SuppressWarnings("deprecation") -public abstract class LegacyMmsConnection { - - public static final String USER_AGENT = "Android-Mms/2.0"; - - private static final String TAG = LegacyMmsConnection.class.getSimpleName(); - - protected final Context context; - protected final Apn apn; - - protected LegacyMmsConnection(Context context) throws ApnUnavailableException { - this.context = context; - this.apn = getApn(context); - } - - public static Apn getApn(Context context) throws ApnUnavailableException { - - try { - Optional params = ApnDatabase.getInstance(context) - .getMmsConnectionParameters(TelephonyUtil.getMccMnc(context), - TelephonyUtil.getApn(context)); - - if (!params.isPresent()) { - throw new ApnUnavailableException("No parameters available from ApnDefaults."); - } - - return params.get(); - } catch (IOException ioe) { - throw new ApnUnavailableException("ApnDatabase threw an IOException", ioe); - } - } - - protected boolean isDirectConnect() { - // We think Sprint supports direct connection over wifi/data, but not Verizon - Set sprintMccMncs = new HashSet() {{ - add("312530"); - add("311880"); - add("311870"); - add("311490"); - add("310120"); - add("316010"); - add("312190"); - }}; - - return ServiceUtil.getTelephonyManager(context).getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA && - sprintMccMncs.contains(TelephonyUtil.getMccMnc(context)); - } - - @SuppressWarnings("TryWithIdenticalCatches") - protected static boolean 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!"); - } - Log.w(TAG, "returning vacuous success since MMS radio is not in use"); - return true; - } - - if (inetAddress == null) { - throw new IOException("Unable to lookup host: InetAddress.getByName() returned null."); - } - - byte[] ipAddressBytes = inetAddress.getAddress(); - if (ipAddressBytes == null) { - Log.w(TAG, "resolved IP address bytes are null, returning true to attempt a connection anyway."); - return true; - } - - Log.w(TAG, "Checking route to address: " + host + ", " + inetAddress.getHostAddress()); - ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); - - try { - final Method requestRouteMethod = manager.getClass().getMethod("requestRouteToHostAddress", Integer.TYPE, InetAddress.class); - final boolean routeToHostObtained = (Boolean) requestRouteMethod.invoke(manager, MmsRadio.TYPE_MOBILE_MMS, inetAddress); - Log.w(TAG, "requestRouteToHostAddress(" + inetAddress + ") -> " + routeToHostObtained); - return routeToHostObtained; - } catch (NoSuchMethodException nsme) { - Log.w(TAG, nsme); - } catch (IllegalAccessException iae) { - Log.w(TAG, iae); - } catch (InvocationTargetException ite) { - Log.w(TAG, ite); - } - - return false; - } - - protected static byte[] parseResponse(InputStream is) throws IOException { - InputStream in = new BufferedInputStream(is); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Util.copy(in, baos); - - Log.w(TAG, "Received full server response, " + baos.size() + " bytes"); - - return baos.toByteArray(); - } - - protected CloseableHttpClient constructHttpClient() throws IOException { - RequestConfig config = RequestConfig.custom() - .setConnectTimeout(20 * 1000) - .setConnectionRequestTimeout(20 * 1000) - .setSocketTimeout(20 * 1000) - .setMaxRedirects(20) - .build(); - - URL mmsc = new URL(apn.getMmsc()); - CredentialsProvider credsProvider = new BasicCredentialsProvider(); - - if (apn.hasAuthentication()) { - credsProvider.setCredentials(new AuthScope(mmsc.getHost(), mmsc.getPort() > -1 ? mmsc.getPort() : mmsc.getDefaultPort()), - new UsernamePasswordCredentials(apn.getUsername(), apn.getPassword())); - } - - return HttpClients.custom() - .setConnectionReuseStrategy(new NoConnectionReuseStrategyHC4()) - .setRedirectStrategy(new LaxRedirectStrategy()) - .setUserAgent(TextSecurePreferences.getMmsUserAgent(context, USER_AGENT)) - .setConnectionManager(new BasicHttpClientConnectionManager()) - .setDefaultRequestConfig(config) - .setDefaultCredentialsProvider(credsProvider) - .build(); - } - - protected byte[] execute(HttpUriRequest request) throws IOException { - Log.w(TAG, "connecting to " + apn.getMmsc()); - - CloseableHttpClient client = null; - CloseableHttpResponse response = null; - try { - client = constructHttpClient(); - response = client.execute(request); - - Log.w(TAG, "* response code: " + response.getStatusLine()); - - if (response.getStatusLine().getStatusCode() == 200) { - return parseResponse(response.getEntity().getContent()); - } - } catch (NullPointerException npe) { - // TODO determine root cause - // see: https://github.com/signalapp/Signal-Android/issues/4379 - throw new IOException(npe); - } finally { - if (response != null) response.close(); - if (client != null) client.close(); - } - - throw new IOException("unhandled response code"); - } - - protected List
getBaseHeaders() { - final String number = TelephonyUtil.getManager(context).getLine1Number(); ; - - return new LinkedList
() {{ - add(new BasicHeader("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic")); - add(new BasicHeader("x-wap-profile", "http://www.google.com/oha/rdf/ua-profile-kila.xml")); - add(new BasicHeader("Content-Type", "application/vnd.wap.mms-message")); - add(new BasicHeader("x-carrier-magic", "http://magic.google.com")); - if (!TextUtils.isEmpty(number)) { - add(new BasicHeader("x-up-calling-line-id", number)); - add(new BasicHeader("X-MDN", number)); - } - }}; - } - - public static class Apn { - - public static Apn EMPTY = new Apn("", "", "", "", ""); - - private final String mmsc; - private final String proxy; - private final String port; - private final String username; - private final String password; - - public Apn(String mmsc, String proxy, String port, String username, String password) { - this.mmsc = mmsc; - this.proxy = proxy; - this.port = port; - this.username = username; - this.password = password; - } - - public Apn(Apn customApn, Apn defaultApn, - boolean useCustomMmsc, - boolean useCustomProxy, - boolean useCustomProxyPort, - boolean useCustomUsername, - boolean useCustomPassword) - { - this.mmsc = useCustomMmsc ? customApn.mmsc : defaultApn.mmsc; - this.proxy = useCustomProxy ? customApn.proxy : defaultApn.proxy; - this.port = useCustomProxyPort ? customApn.port : defaultApn.port; - this.username = useCustomUsername ? customApn.username : defaultApn.username; - this.password = useCustomPassword ? customApn.password : defaultApn.password; - } - - public boolean hasProxy() { - return !TextUtils.isEmpty(proxy); - } - - public String getMmsc() { - return mmsc; - } - - public String getProxy() { - return hasProxy() ? proxy : null; - } - - public int getPort() { - return TextUtils.isEmpty(port) ? 80 : Integer.parseInt(port); - } - - public boolean hasAuthentication() { - return !TextUtils.isEmpty(username); - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - @Override - public String toString() { - return Apn.class.getSimpleName() + - "{ mmsc: \"" + mmsc + "\"" + - ", proxy: " + (proxy == null ? "none" : '"' + proxy + '"') + - ", port: " + (port == null ? "(none)" : port) + - ", user: " + (username == null ? "none" : '"' + username + '"') + - ", pass: " + (password == null ? "none" : '"' + password + '"') + " }"; - } - } -} diff --git a/src/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java b/src/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java deleted file mode 100644 index 4d9f36458..000000000 --- a/src/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright (C) 2015 Open 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.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - -import com.google.android.mms.pdu_alt.PduParser; -import com.google.android.mms.pdu_alt.SendConf; - -import org.apache.http.Header; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpPostHC4; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.entity.ByteArrayEntityHC4; -import org.thoughtcrime.securesms.transport.UndeliverableMessageException; - -import java.io.IOException; - - -@SuppressWarnings("deprecation") -public class OutgoingLegacyMmsConnection extends LegacyMmsConnection implements OutgoingMmsConnection { - private final static String TAG = OutgoingLegacyMmsConnection.class.getSimpleName(); - - public OutgoingLegacyMmsConnection(Context context) throws ApnUnavailableException { - super(context); - } - - private HttpUriRequest constructRequest(byte[] pduBytes, boolean useProxy) - throws IOException - { - try { - HttpPostHC4 request = new HttpPostHC4(apn.getMmsc()); - for (Header header : getBaseHeaders()) { - request.addHeader(header); - } - - request.setEntity(new ByteArrayEntityHC4(pduBytes)); - if (useProxy) { - HttpHost proxy = new HttpHost(apn.getProxy(), apn.getPort()); - request.setConfig(RequestConfig.custom().setProxy(proxy).build()); - } - return request; - } catch (IllegalArgumentException iae) { - throw new IOException(iae); - } - } - - public void sendNotificationReceived(byte[] pduBytes, boolean usingMmsRadio, boolean useProxyIfAvailable) - throws IOException - { - sendBytes(pduBytes, usingMmsRadio, useProxyIfAvailable); - } - - @Override - public @Nullable SendConf send(@NonNull byte[] pduBytes, int subscriptionId) throws UndeliverableMessageException { - try { - MmsRadio radio = MmsRadio.getInstance(context); - - if (isDirectConnect()) { - Log.w(TAG, "Sending MMS directly without radio change..."); - try { - return send(pduBytes, false, false); - } catch (IOException e) { - Log.w(TAG, e); - } - } - - Log.w(TAG, "Sending MMS with radio change and proxy..."); - radio.connect(); - - try { - try { - return send(pduBytes, true, true); - } catch (IOException e) { - Log.w(TAG, e); - } - - Log.w(TAG, "Sending MMS with radio change and without proxy..."); - - try { - return send(pduBytes, true, false); - } catch (IOException ioe) { - Log.w(TAG, ioe); - throw new UndeliverableMessageException(ioe); - } - } finally { - radio.disconnect(); - } - - } catch (MmsRadioException e) { - Log.w(TAG, e); - throw new UndeliverableMessageException(e); - } - - } - - private SendConf send(byte[] pduBytes, boolean useMmsRadio, boolean useProxyIfAvailable) throws IOException { - byte[] response = sendBytes(pduBytes, useMmsRadio, useProxyIfAvailable); - return (SendConf) new PduParser(response).parse(); - } - - private byte[] sendBytes(byte[] pduBytes, boolean useMmsRadio, boolean useProxyIfAvailable) throws IOException { - final boolean useProxy = useProxyIfAvailable && apn.hasProxy(); - final String targetHost = useProxy - ? apn.getProxy() - : Uri.parse(apn.getMmsc()).getHost(); - - Log.w(TAG, "Sending MMS of length: " + pduBytes.length - + (useMmsRadio ? ", using mms radio" : "") - + (useProxy ? ", using proxy" : "")); - - try { - if (checkRouteToHost(context, targetHost, useMmsRadio)) { - Log.w(TAG, "got successful route to host " + targetHost); - byte[] response = execute(constructRequest(pduBytes, useProxy)); - if (response != null) return response; - } - } catch (IOException ioe) { - Log.w(TAG, ioe); - } - throw new IOException("Connection manager could not obtain route to host."); - } - - - public static boolean isConnectionPossible(Context context) { - try { - ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo networkInfo = connectivityManager.getNetworkInfo(MmsRadio.TYPE_MOBILE_MMS); - if (networkInfo == null) { - Log.w(TAG, "MMS network info was null, unsupported by this device"); - return false; - } - - getApn(context); - return true; - } catch (ApnUnavailableException e) { - Log.w(TAG, e); - return false; - } - } -} diff --git a/src/org/thoughtcrime/securesms/service/MmsListener.java b/src/org/thoughtcrime/securesms/service/MmsListener.java deleted file mode 100644 index 3d8417d80..000000000 --- a/src/org/thoughtcrime/securesms/service/MmsListener.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * 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.service; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.provider.Telephony; -import android.util.Log; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.jobs.MmsReceiveJob; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; - -public class MmsListener extends BroadcastReceiver { - - private static final String TAG = MmsListener.class.getSimpleName(); - - private boolean isRelevant(Context context, Intent intent) { - if (!ApplicationMigrationService.isDatabaseImported(context)) { - return false; - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && - Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION.equals(intent.getAction()) && - Util.isDefaultSmsProvider(context)) - { - return false; - } - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT && - TextSecurePreferences.isInterceptAllMmsEnabled(context)) - { - return true; - } - - return false; - } - - @Override - public void onReceive(Context context, Intent intent) { - Log.w(TAG, "Got MMS broadcast..." + intent.getAction()); - - if ((Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION.equals(intent.getAction()) && - Util.isDefaultSmsProvider(context)) || - (Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION.equals(intent.getAction()) && - isRelevant(context, intent))) - { - Log.w(TAG, "Relevant!"); - int subscriptionId = intent.getExtras().getInt("subscription", -1); - - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MmsReceiveJob(context, intent.getByteArrayExtra("data"), subscriptionId)); - - abortBroadcast(); - } - } - - - -} diff --git a/src/org/thoughtcrime/securesms/service/SmsDeliveryListener.java b/src/org/thoughtcrime/securesms/service/SmsDeliveryListener.java deleted file mode 100644 index a579f6236..000000000 --- a/src/org/thoughtcrime/securesms/service/SmsDeliveryListener.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.thoughtcrime.securesms.service; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.telephony.SmsMessage; -import android.util.Log; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.jobs.SmsSentJob; - -public class SmsDeliveryListener extends BroadcastReceiver { - - private static final String TAG = SmsDeliveryListener.class.getSimpleName(); - - public static final String SENT_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SENT_SMS_ACTION"; - public static final String DELIVERED_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DELIVERED_SMS_ACTION"; - - @Override - public void onReceive(Context context, Intent intent) { - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - long messageId = intent.getLongExtra("message_id", -1); - - switch (intent.getAction()) { - case SENT_SMS_ACTION: - int result = getResultCode(); - - jobManager.add(new SmsSentJob(context, messageId, SENT_SMS_ACTION, result)); - break; - case DELIVERED_SMS_ACTION: - byte[] pdu = intent.getByteArrayExtra("pdu"); - - if (pdu == null) { - Log.w(TAG, "No PDU in delivery receipt!"); - break; - } - - SmsMessage message = SmsMessage.createFromPdu(pdu); - - if (message == null) { - Log.w(TAG, "Delivery receipt failed to parse!"); - break; - } - - int status = message.getStatus(); - - Log.w(TAG, "Original status: " + status); - - // Note: https://developer.android.com/reference/android/telephony/SmsMessage.html#getStatus() - // " CDMA: For not interfering with status codes from GSM, the value is shifted to the bits 31-16" - // Note: https://stackoverflow.com/a/33240109 - if ("3gpp2".equals(intent.getStringExtra("format"))) { - Log.w(TAG, "Correcting for CDMA delivery receipt..."); - if (status >> 24 <= 0) status = SmsDatabase.Status.STATUS_COMPLETE; - else if (status >> 24 == 2) status = SmsDatabase.Status.STATUS_PENDING; - else if (status >> 24 == 3) status = SmsDatabase.Status.STATUS_FAILED; - } - - jobManager.add(new SmsSentJob(context, messageId, DELIVERED_SMS_ACTION, status)); - break; - default: - Log.w(TAG, "Unknown action: " + intent.getAction()); - } - } -} diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index bf769fe53..342a81a99 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -17,7 +17,6 @@ package org.thoughtcrime.securesms.sms; import android.content.Context; -import android.database.Cursor; import android.support.annotation.NonNull; import android.util.Log; import android.util.Pair; @@ -34,11 +33,9 @@ import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.jobs.MmsSendJob; import org.thoughtcrime.securesms.jobs.PushGroupSendJob; import org.thoughtcrime.securesms.jobs.PushMediaSendJob; import org.thoughtcrime.securesms.jobs.PushTextSendJob; -import org.thoughtcrime.securesms.jobs.SmsSendJob; import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.push.AccountManagerFactory; @@ -209,13 +206,13 @@ public class MessageSender { } private static void sendSms(Context context, Recipient recipient, long messageId) { - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - jobManager.add(new SmsSendJob(context, messageId, recipient.getName())); +// JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); +// jobManager.add(new SmsSendJob(context, messageId, recipient.getName())); } private static void sendMms(Context context, long messageId) { - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - jobManager.add(new MmsSendJob(context, messageId)); +// JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); +// jobManager.add(new MmsSendJob(context, messageId)); } private static boolean isPushTextSend(Context context, Recipient recipient, boolean keyExchange) { diff --git a/src/org/thoughtcrime/securesms/util/MmsCharacterCalculator.java b/src/org/thoughtcrime/securesms/util/MmsCharacterCalculator.java deleted file mode 100644 index 9d4698c20..000000000 --- a/src/org/thoughtcrime/securesms/util/MmsCharacterCalculator.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.thoughtcrime.securesms.util; - -public class MmsCharacterCalculator extends CharacterCalculator { - - private static final int MAX_SIZE = 5000; - - @Override - public CharacterState calculateCharacters(String messageBody) { - return new CharacterState(1, MAX_SIZE - messageBody.length(), MAX_SIZE); - } -} diff --git a/src/org/thoughtcrime/securesms/util/SmsCharacterCalculator.java b/src/org/thoughtcrime/securesms/util/SmsCharacterCalculator.java deleted file mode 100644 index a822641d1..000000000 --- a/src/org/thoughtcrime/securesms/util/SmsCharacterCalculator.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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.util; - -import android.telephony.SmsMessage; -import android.util.Log; - -public class SmsCharacterCalculator extends CharacterCalculator { - - private static final String TAG = SmsCharacterCalculator.class.getSimpleName(); - - @Override - public CharacterState calculateCharacters(String messageBody) { - int[] length; - int messagesSpent; - int charactersSpent; - int charactersRemaining; - - try { - length = SmsMessage.calculateLength(messageBody, false); - messagesSpent = length[0]; - charactersSpent = length[1]; - charactersRemaining = length[2]; - } catch (NullPointerException e) { - Log.w(TAG, e); - messagesSpent = 1; - charactersSpent = messageBody.length(); - charactersRemaining = 1000; - } - - int maxMessageSize; - - if (messagesSpent > 0) { - maxMessageSize = (charactersSpent + charactersRemaining) / messagesSpent; - } else { - maxMessageSize = (charactersSpent + charactersRemaining); - } - - return new CharacterState(messagesSpent, charactersRemaining, maxMessageSize); - } -} - diff --git a/src/org/thoughtcrime/securesms/util/Util.java b/src/org/thoughtcrime/securesms/util/Util.java index 79a62f44c..89c62587b 100644 --- a/src/org/thoughtcrime/securesms/util/Util.java +++ b/src/org/thoughtcrime/securesms/util/Util.java @@ -50,7 +50,6 @@ import com.google.i18n.phonenumbers.Phonenumber; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.components.ComposeText; import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.mms.OutgoingLegacyMmsConnection; import org.whispersystems.libsignal.util.guava.Optional; import java.io.ByteArrayOutputStream; @@ -374,11 +373,6 @@ public class Util { return 90 - age; } - @TargetApi(VERSION_CODES.LOLLIPOP) - public static boolean isMmsCapable(Context context) { - return (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) || OutgoingLegacyMmsConnection.isConnectionPossible(context); - } - public static boolean isMainThread() { return Looper.myLooper() == Looper.getMainLooper(); } diff --git a/test/androidTest/java/org/thoughtcrime/securesms/TextSecureTestCase.java b/test/androidTest/java/org/thoughtcrime/securesms/TextSecureTestCase.java deleted file mode 100644 index 0de79c8d4..000000000 --- a/test/androidTest/java/org/thoughtcrime/securesms/TextSecureTestCase.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.content.Context; -import android.test.InstrumentationTestCase; - -public class TextSecureTestCase extends InstrumentationTestCase { - - @Override - public void setUp() { - System.setProperty("dexmaker.dexcache", getInstrumentation().getTargetContext().getCacheDir().getPath()); - } - - protected Context getContext() { - return getInstrumentation().getContext(); - } -} diff --git a/test/unitTest/java/org/thoughtcrime/securesms/BaseUnitTest.java b/test/unitTest/java/org/thoughtcrime/securesms/BaseUnitTest.java deleted file mode 100644 index 41d35df43..000000000 --- a/test/unitTest/java/org/thoughtcrime/securesms/BaseUnitTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Handler; -import android.os.Looper; -import android.preference.PreferenceManager; -import android.text.TextUtils; -import android.util.Log; - -import org.junit.Before; -import org.junit.runner.RunWith; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyFloat; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; - -@RunWith(PowerMockRunner.class) -@PrepareForTest({ Log.class, Handler.class, Looper.class, TextUtils.class, PreferenceManager.class }) -public abstract class BaseUnitTest { - - protected Context context = mock(Context.class); - protected SharedPreferences sharedPreferences = mock(SharedPreferences.class); - - @Before - public void setUp() throws Exception { - mockStatic(Looper.class); - mockStatic(Log.class); - mockStatic(Handler.class); - mockStatic(TextUtils.class); - mockStatic(PreferenceManager.class); - - when(PreferenceManager.getDefaultSharedPreferences(any(Context.class))).thenReturn(sharedPreferences); - when(Looper.getMainLooper()).thenReturn(null); - PowerMockito.whenNew(Handler.class).withAnyArguments().thenReturn(null); - - Answer logAnswer = new Answer() { - @Override public Void answer(InvocationOnMock invocation) throws Throwable { - final String tag = (String)invocation.getArguments()[0]; - final String msg = (String)invocation.getArguments()[1]; - System.out.println(invocation.getMethod().getName().toUpperCase() + "/[" + tag + "] " + msg); - return null; - } - }; - PowerMockito.doAnswer(logAnswer).when(Log.class, "d", anyString(), anyString()); - PowerMockito.doAnswer(logAnswer).when(Log.class, "i", anyString(), anyString()); - PowerMockito.doAnswer(logAnswer).when(Log.class, "w", anyString(), anyString()); - PowerMockito.doAnswer(logAnswer).when(Log.class, "e", anyString(), anyString()); - PowerMockito.doAnswer(logAnswer).when(Log.class, "wtf", anyString(), anyString()); - - PowerMockito.doAnswer(new Answer() { - @Override - public Boolean answer(InvocationOnMock invocation) throws Throwable { - final String s = (String)invocation.getArguments()[0]; - return s == null || s.length() == 0; - } - }).when(TextUtils.class, "isEmpty", anyString()); - - when(sharedPreferences.getString(anyString(), anyString())).thenReturn(""); - when(sharedPreferences.getLong(anyString(), anyLong())).thenReturn(0L); - when(sharedPreferences.getInt(anyString(), anyInt())).thenReturn(0); - when(sharedPreferences.getBoolean(anyString(), anyBoolean())).thenReturn(false); - when(sharedPreferences.getFloat(anyString(), anyFloat())).thenReturn(0f); - when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPreferences); - when(context.getPackageName()).thenReturn("org.thoughtcrime.securesms"); - } -} diff --git a/test/unitTest/java/org/thoughtcrime/securesms/database/AddressTest.java b/test/unitTest/java/org/thoughtcrime/securesms/database/AddressTest.java deleted file mode 100644 index be6ec02e4..000000000 --- a/test/unitTest/java/org/thoughtcrime/securesms/database/AddressTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.thoughtcrime.securesms.database; - - -import org.junit.Before; -import org.junit.Test; - -import static junit.framework.Assert.assertEquals; - -public class AddressTest { - - @Before - public void setup() {} - - @Test - public void testAddressString() throws Exception { - Address.ExternalAddressFormatter formatter = new Address.ExternalAddressFormatter("+14152222222"); - assertEquals(formatter.format("bonbon"), "bonbon"); - } - - @Test - public void testAddressShortCode() throws Exception { - Address.ExternalAddressFormatter formatter = new Address.ExternalAddressFormatter("+14152222222"); - assertEquals(formatter.format("40404"), "40404"); - } - - @Test - public void testEmailAddress() throws Exception { - Address.ExternalAddressFormatter formatter = new Address.ExternalAddressFormatter("+14152222222"); - assertEquals(formatter.format("junk@junk.net"), "junk@junk.net"); - } - - @Test - public void testNumberArbitrary() throws Exception { - Address.ExternalAddressFormatter formatter = new Address.ExternalAddressFormatter("+14152222222"); - assertEquals(formatter.format("(415) 111-1122"), "+14151111122"); - assertEquals(formatter.format("(415) 111 1123"), "+14151111123"); - assertEquals(formatter.format("415-111-1124"), "+14151111124"); - assertEquals(formatter.format("415.111.1125"), "+14151111125"); - assertEquals(formatter.format("+1 415.111.1126"), "+14151111126"); - assertEquals(formatter.format("+1 415 111 1127"), "+14151111127"); - assertEquals(formatter.format("+1 (415) 111 1128"), "+14151111128"); - - formatter = new Address.ExternalAddressFormatter("+442079460010"); - assertEquals(formatter.format("(020) 7946 0018"), "+442079460018"); - } - - @Test - public void testGroup() throws Exception { - Address.ExternalAddressFormatter formatter = new Address.ExternalAddressFormatter("+14152222222"); - assertEquals(formatter.format("__textsecure_group__!foobar"), "__textsecure_group__!foobar"); - } - - @Test - public void testLostLocalNumber() throws Exception { - Address.ExternalAddressFormatter formatter = new Address.ExternalAddressFormatter("US", true); - assertEquals(formatter.format("(415) 111-1122"), "+14151111122"); - } - -} diff --git a/test/unitTest/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java b/test/unitTest/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java deleted file mode 100644 index ec61223c3..000000000 --- a/test/unitTest/java/org/thoughtcrime/securesms/database/CursorRecyclerViewAdapterTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.thoughtcrime.securesms.database; - -import android.content.Context; -import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView.ViewHolder; -import android.view.View; -import android.view.ViewGroup; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class CursorRecyclerViewAdapterTest { - private CursorRecyclerViewAdapter adapter; - private Context context; - private Cursor cursor; - - @Before - public void setUp() { - context = mock(Context.class); - cursor = mock(Cursor.class); - when(cursor.getCount()).thenReturn(100); - when(cursor.moveToPosition(anyInt())).thenReturn(true); - - adapter = new CursorRecyclerViewAdapter(context, cursor) { - @Override - public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { - return null; - } - - @Override - public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) { - } - }; - } - - @Test - public void testSanityCount() throws Exception { - assertEquals(adapter.getItemCount(), 100); - } - - @Test - public void testHeaderCount() throws Exception { - adapter.setHeaderView(new View(context)); - assertEquals(adapter.getItemCount(), 101); - - assertEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.HEADER_TYPE); - assertNotEquals(adapter.getItemViewType(1), CursorRecyclerViewAdapter.HEADER_TYPE); - assertNotEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.HEADER_TYPE); - } - - @Test - public void testFooterCount() throws Exception { - adapter.setFooterView(new View(context)); - assertEquals(adapter.getItemCount(), 101); - assertEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.FOOTER_TYPE); - assertNotEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.FOOTER_TYPE); - assertNotEquals(adapter.getItemViewType(99), CursorRecyclerViewAdapter.FOOTER_TYPE); - } - - @Test - public void testHeaderFooterCount() throws Exception { - adapter.setHeaderView(new View(context)); - adapter.setFooterView(new View(context)); - assertEquals(adapter.getItemCount(), 102); - assertEquals(adapter.getItemViewType(101), CursorRecyclerViewAdapter.FOOTER_TYPE); - assertEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.HEADER_TYPE); - assertNotEquals(adapter.getItemViewType(1), CursorRecyclerViewAdapter.HEADER_TYPE); - assertNotEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.FOOTER_TYPE); - } -} - diff --git a/test/unitTest/java/org/thoughtcrime/securesms/service/SmsListenerTest.java b/test/unitTest/java/org/thoughtcrime/securesms/service/SmsListenerTest.java deleted file mode 100644 index 6f16602cc..000000000 --- a/test/unitTest/java/org/thoughtcrime/securesms/service/SmsListenerTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.thoughtcrime.securesms.service; - -import junit.framework.AssertionFailedError; - -import org.junit.Before; -import org.junit.Test; -import org.thoughtcrime.securesms.BaseUnitTest; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.contains; -import static org.mockito.Mockito.when; - -public class SmsListenerTest extends BaseUnitTest { - private static Map CHALLENGES = new HashMap() {{ - put("Your TextSecure verification code: 337-337", "337337"); - put("XXX\nYour TextSecure verification code: 1337-1337", "13371337"); - put("Your TextSecure verification code: 337-1337", "3371337"); - put("Your TextSecure verification code: 1337-337", "1337337"); - put("Your TextSecure verification code: 1337-1337", "13371337"); - put("XXXYour TextSecure verification code: 1337-1337", "13371337"); - put("Your TextSecure verification code: 1337-1337XXX", "13371337"); - put("Your TextSecure verification code 1337-1337", "13371337"); - - put("Your Signal verification code: 337-337", "337337"); - put("XXX\nYour Signal verification code: 1337-1337", "13371337"); - put("Your Signal verification code: 337-1337", "3371337"); - put("Your Signal verification code: 1337-337", "1337337"); - put("Your Signal verification code: 1337-1337", "13371337"); - put("XXXYour Signal verification code: 1337-1337", "13371337"); - put("Your Signal verification code: 1337-1337XXX", "13371337"); - put("Your Signal verification code 1337-1337", "13371337"); - }}; - - private SmsListener listener; - - @Before - @Override - public void setUp() throws Exception { - super.setUp(); - listener = new SmsListener(); - when(sharedPreferences.getBoolean(contains("pref_verifying"), anyBoolean())).thenReturn(true); - } - - @Test - public void testChallenges() throws Exception { - for (Entry challenge : CHALLENGES.entrySet()) { - if (!listener.isChallenge(context, challenge.getKey())) { - throw new AssertionFailedError("SmsListener didn't recognize body as a challenge."); - } - assertEquals(listener.parseChallenge(challenge.getKey()), challenge.getValue()); - } - } -} diff --git a/test/unitTest/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java b/test/unitTest/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java deleted file mode 100644 index 8d7a25e88..000000000 --- a/test/unitTest/java/org/thoughtcrime/securesms/util/DelimiterUtilTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.thoughtcrime.securesms.util; - - -import org.junit.Before; -import org.junit.Test; - -import static junit.framework.Assert.assertEquals; - -public class DelimiterUtilTest { - - @Before - public void setup() {} - - @Test - public void testEscape() { - assertEquals(DelimiterUtil.escape("MTV Music", ' '), "MTV\\ Music"); - assertEquals(DelimiterUtil.escape("MTV Music", ' '), "MTV\\ \\ Music"); - - assertEquals(DelimiterUtil.escape("MTV,Music", ','), "MTV\\,Music"); - assertEquals(DelimiterUtil.escape("MTV,,Music", ','), "MTV\\,\\,Music"); - - assertEquals(DelimiterUtil.escape("MTV Music", '+'), "MTV Music"); - } - - @Test - public void testSplit() { - String[] parts = DelimiterUtil.split("MTV\\ Music", ' '); - assertEquals(parts.length, 1); - assertEquals(parts[0], "MTV\\ Music"); - - parts = DelimiterUtil.split("MTV Music", ' '); - assertEquals(parts.length, 2); - assertEquals(parts[0], "MTV"); - assertEquals(parts[1], "Music"); - } - - @Test - public void testEscapeSplit() { - String input = "MTV Music"; - String intermediate = DelimiterUtil.escape(input, ' '); - String[] parts = DelimiterUtil.split(intermediate, ' '); - - assertEquals(parts.length, 1); - assertEquals(parts[0], "MTV\\ Music"); - assertEquals(DelimiterUtil.unescape(parts[0], ' '), "MTV Music"); - - input = "MTV\\ Music"; - intermediate = DelimiterUtil.escape(input, ' '); - parts = DelimiterUtil.split(intermediate, ' '); - - assertEquals(parts.length, 1); - assertEquals(parts[0], "MTV\\\\ Music"); - assertEquals(DelimiterUtil.unescape(parts[0], ' '), "MTV\\ Music"); - } - -} diff --git a/test/unitTest/java/org/thoughtcrime/securesms/util/ListPartitionTest.java b/test/unitTest/java/org/thoughtcrime/securesms/util/ListPartitionTest.java deleted file mode 100644 index 1ef955a83..000000000 --- a/test/unitTest/java/org/thoughtcrime/securesms/util/ListPartitionTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import org.junit.Test; -import org.thoughtcrime.securesms.BaseUnitTest; - -import java.util.LinkedList; -import java.util.List; - -import static org.junit.Assert.assertEquals; - -public class ListPartitionTest extends BaseUnitTest { - - @Test public void testPartitionEven() { - List list = new LinkedList<>(); - - for (int i=0;i<100;i++) { - list.add(i); - } - - List> partitions = Util.partition(list, 10); - - assertEquals(partitions.size(), 10); - - int counter = 0; - - for (int i=0;i partition = partitions.get(i); - assertEquals(partition.size(), 10); - - for (int j=0;j list = new LinkedList<>(); - - for (int i=0;i<100;i++) { - list.add(i); - } - - list.add(100); - - List> partitions = Util.partition(list, 10); - - assertEquals(partitions.size(), 11); - - int counter = 0; - - for (int i=0;i partition = partitions.get(i); - assertEquals(partition.size(), 10); - - for (int j=0;j list = new LinkedList<>(); - - for (int i=0;i<100;i++) { - list.add(i); - } - - List> partitions = Util.partition(list, 1); - - assertEquals(partitions.size(), 100); - - int counter = 0; - - for (int i=0;i partition = partitions.get(i); - assertEquals(partition.size(), 1); - - for (int j=0;j. - */ - -package org.thoughtcrime.securesms.util; - -import junit.framework.AssertionFailedError; - -import org.junit.Test; -import org.thoughtcrime.securesms.BaseUnitTest; - -import java.net.URISyntaxException; - -import static org.junit.Assert.assertTrue; - -public class Rfc5724UriTest extends BaseUnitTest { - - @Test public void testInvalidPath() throws Exception { - final String[] invalidSchemaUris = { - "", - ":", - "sms:", - ":sms", - "sms:?goto=fail", - "sms:?goto=fail&fail=goto" - }; - - for (String uri : invalidSchemaUris) { - try { - new Rfc5724Uri(uri); - throw new AssertionFailedError("URISyntaxException should be thrown"); - } catch (URISyntaxException e) { - // success - } - } - } - - @Test public void testGetSchema() throws Exception { - final String[][] uriTestPairs = { - {"sms:+15555555555", "sms"}, - {"sMs:+15555555555", "sMs"}, - {"smsto:+15555555555?", "smsto"}, - {"mms:+15555555555?a=b", "mms"}, - {"mmsto:+15555555555?a=b&c=d", "mmsto"} - }; - - for (String[] uriTestPair : uriTestPairs) { - final Rfc5724Uri testUri = new Rfc5724Uri(uriTestPair[0]); - assertTrue(testUri.getSchema().equals(uriTestPair[1])); - } - } - - @Test public void testGetPath() throws Exception { - final String[][] uriTestPairs = { - {"sms:+15555555555", "+15555555555"}, - {"sms:%2B555555555", "%2B555555555"}, - {"smsto:+15555555555?", "+15555555555"}, - {"mms:+15555555555?a=b", "+15555555555"}, - {"mmsto:+15555555555?a=b&c=d", "+15555555555"}, - {"sms:+15555555555,+14444444444", "+15555555555,+14444444444"}, - {"sms:+15555555555,+14444444444?", "+15555555555,+14444444444"}, - {"sms:+15555555555,+14444444444?a=b", "+15555555555,+14444444444"}, - {"sms:+15555555555,+14444444444?a=b&c=d", "+15555555555,+14444444444"} - }; - - for (String[] uriTestPair : uriTestPairs) { - final Rfc5724Uri testUri = new Rfc5724Uri(uriTestPair[0]); - assertTrue(testUri.getPath().equals(uriTestPair[1])); - } - } - - @Test public void testGetQueryParams() throws Exception { - final String[][] uriTestPairs = { - {"sms:+15555555555", "a", null}, - {"mms:+15555555555?b=", "a", null}, - {"mmsto:+15555555555?a=", "a", ""}, - {"sms:+15555555555?a=b", "a", "b"}, - {"sms:+15555555555?a=b&c=d", "a", "b"}, - {"sms:+15555555555?a=b&c=d", "b", null}, - {"sms:+15555555555?a=b&c=d", "c", "d"}, - {"sms:+15555555555?a=b&c=d", "d", null} - }; - - for (String[] uriTestPair : uriTestPairs) { - final Rfc5724Uri testUri = new Rfc5724Uri(uriTestPair[0]); - final String paramResult = testUri.getQueryParams().get(uriTestPair[1]); - - if (paramResult == null) assertTrue(uriTestPair[2] == null); - else assertTrue(paramResult.equals(uriTestPair[2])); - } - } -} diff --git a/test/unitTest/java/org/thoughtcrime/securesms/util/ShortCodeUtilTest.java b/test/unitTest/java/org/thoughtcrime/securesms/util/ShortCodeUtilTest.java deleted file mode 100644 index f6ebf685c..000000000 --- a/test/unitTest/java/org/thoughtcrime/securesms/util/ShortCodeUtilTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import org.junit.Test; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -public class ShortCodeUtilTest { - - @Test - public void testShortCodes() throws Exception { - assertTrue(ShortCodeUtil.isShortCode("+14152222222", "40404")); - assertTrue(ShortCodeUtil.isShortCode("+14152222222", "431")); - assertFalse(ShortCodeUtil.isShortCode("+14152222222", "4157778888")); - assertFalse(ShortCodeUtil.isShortCode("+14152222222", "+14157778888")); - assertFalse(ShortCodeUtil.isShortCode("+14152222222", "415-777-8888")); - assertFalse(ShortCodeUtil.isShortCode("+14152222222", "(415) 777-8888")); - assertFalse(ShortCodeUtil.isShortCode("+14152222222", "8882222")); - assertFalse(ShortCodeUtil.isShortCode("+14152222222", "888-2222")); - - assertTrue(ShortCodeUtil.isShortCode("+491723742522", "670")); - assertTrue(ShortCodeUtil.isShortCode("+491723742522", "115")); - assertFalse(ShortCodeUtil.isShortCode("+491723742522", "089-12345678")); - assertFalse(ShortCodeUtil.isShortCode("+491723742522", "089/12345678")); - assertFalse(ShortCodeUtil.isShortCode("+491723742522", "12345678")); - - assertTrue(ShortCodeUtil.isShortCode("+298123456", "4040")); - assertTrue(ShortCodeUtil.isShortCode("+298123456", "6701")); - assertTrue(ShortCodeUtil.isShortCode("+298123456", "433")); - assertFalse(ShortCodeUtil.isShortCode("+298123456", "123456")); - - assertTrue(ShortCodeUtil.isShortCode("+61414915066", "19808948")); - assertFalse(ShortCodeUtil.isShortCode("+61414915066", "119808948")); - - assertTrue(ShortCodeUtil.isShortCode("+79166503388", "8080")); - assertTrue(ShortCodeUtil.isShortCode("+79166503388", "6701")); - assertTrue(ShortCodeUtil.isShortCode("+79166503388", "431")); - assertFalse(ShortCodeUtil.isShortCode("+79166503388", "111-22-33")); - } - -}