From a0ed0842a0f871e2dfc70ea70f77ee4a7285648c Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Sun, 11 Jan 2015 20:27:34 -0800 Subject: [PATCH] Add expiration check on build freshness. // FREEBIE --- build.gradle | 55 +++++++++---------- res/values/strings.xml | 2 + .../securesms/ConversationListFragment.java | 5 +- .../securesms/TextSecureExpiredException.java | 7 +++ .../components/ExpiredBuildReminder.java | 35 ++++++++++++ .../securesms/components/Reminder.java | 4 ++ .../securesms/components/ReminderView.java | 24 +++++--- .../securesms/jobs/MmsSendJob.java | 5 +- .../securesms/jobs/PushGroupSendJob.java | 2 +- .../securesms/jobs/PushMediaSendJob.java | 2 +- .../securesms/jobs/PushSendJob.java | 2 +- .../securesms/jobs/PushTextSendJob.java | 2 +- .../thoughtcrime/securesms/jobs/SendJob.java | 29 ++++++++++ .../securesms/jobs/SmsSendJob.java | 4 +- src/org/thoughtcrime/securesms/util/Util.java | 7 ++- 15 files changed, 135 insertions(+), 50 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/TextSecureExpiredException.java create mode 100644 src/org/thoughtcrime/securesms/components/ExpiredBuildReminder.java create mode 100644 src/org/thoughtcrime/securesms/jobs/SendJob.java diff --git a/build.gradle b/build.gradle index 4e9c4d16a..73578a76c 100644 --- a/build.gradle +++ b/build.gradle @@ -98,6 +98,8 @@ android { defaultConfig { minSdkVersion 9 targetSdkVersion 19 + + buildConfigField "long", "BUILD_TIMESTAMP", System.currentTimeMillis() + "L" } compileOptions { @@ -105,44 +107,39 @@ android { targetCompatibility JavaVersion.VERSION_1_7 } - android { - buildTypes { - debug { - minifyEnabled false - } - release { - minifyEnabled false - } - } - - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['src'] - resources.srcDirs = ['src'] - aidl.srcDirs = ['src'] - renderscript.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - } - androidTest { - java.srcDirs = ['androidTest/java'] - resources.srcDirs = ['androidTest/java'] - aidl.srcDirs = ['androidTest/java'] - renderscript.srcDirs = ['androidTest/java'] - } - } - } - signingConfigs { release } + buildTypes { + debug { + minifyEnabled false + } release { + minifyEnabled false signingConfig signingConfigs.release } } + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + resources.srcDirs = ['src'] + aidl.srcDirs = ['src'] + renderscript.srcDirs = ['src'] + res.srcDirs = ['res'] + assets.srcDirs = ['assets'] + } + androidTest { + java.srcDirs = ['androidTest/java'] + resources.srcDirs = ['androidTest/java'] + aidl.srcDirs = ['androidTest/java'] + renderscript.srcDirs = ['androidTest/java'] + } + } + + lintOptions { abortOnError false } diff --git a/res/values/strings.xml b/res/values/strings.xml index d4850af9e..57dab652e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -816,6 +816,8 @@ Mark all read + Your build of TextSecure has expired! + Messages will no longer send successfully, please update to the most recent version. Use as default SMS app? TextSecure is not currently your default SMS app. Import system SMS? diff --git a/src/org/thoughtcrime/securesms/ConversationListFragment.java b/src/org/thoughtcrime/securesms/ConversationListFragment.java index b72fc2f48..1178c9e96 100644 --- a/src/org/thoughtcrime/securesms/ConversationListFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationListFragment.java @@ -43,6 +43,7 @@ import android.widget.AdapterView; import android.widget.ListView; import org.thoughtcrime.securesms.components.DefaultSmsReminder; +import org.thoughtcrime.securesms.components.ExpiredBuildReminder; import org.thoughtcrime.securesms.components.PushRegistrationReminder; import org.thoughtcrime.securesms.components.ReminderView; import org.thoughtcrime.securesms.components.SystemSmsImportReminder; @@ -163,7 +164,9 @@ public class ConversationListFragment extends ListFragment } private void initializeReminders() { - if (DefaultSmsReminder.isEligible(getActivity())) { + if (ExpiredBuildReminder.isEligible(getActivity())) { + reminderView.showReminder(new ExpiredBuildReminder()); + } else if (DefaultSmsReminder.isEligible(getActivity())) { reminderView.showReminder(new DefaultSmsReminder(getActivity())); } else if (SystemSmsImportReminder.isEligible(getActivity())) { reminderView.showReminder(new SystemSmsImportReminder(getActivity(), masterSecret)); diff --git a/src/org/thoughtcrime/securesms/TextSecureExpiredException.java b/src/org/thoughtcrime/securesms/TextSecureExpiredException.java new file mode 100644 index 000000000..f207b5d68 --- /dev/null +++ b/src/org/thoughtcrime/securesms/TextSecureExpiredException.java @@ -0,0 +1,7 @@ +package org.thoughtcrime.securesms; + +public class TextSecureExpiredException extends Exception { + public TextSecureExpiredException(String message) { + super(message); + } +} diff --git a/src/org/thoughtcrime/securesms/components/ExpiredBuildReminder.java b/src/org/thoughtcrime/securesms/components/ExpiredBuildReminder.java new file mode 100644 index 000000000..d8ee5d49c --- /dev/null +++ b/src/org/thoughtcrime/securesms/components/ExpiredBuildReminder.java @@ -0,0 +1,35 @@ +package org.thoughtcrime.securesms.components; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.provider.Telephony; +import android.util.Log; +import android.view.View; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.TextSecureExpiredException; +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.Util; + +public class ExpiredBuildReminder extends Reminder { + + private static final String TAG = ExpiredBuildReminder.class.getSimpleName(); + + public ExpiredBuildReminder() { + super(R.drawable.ic_dialog_alert_holo_dark, + R.string.reminder_header_expired_build, + R.string.reminder_header_expired_build_details); + } + + @Override + public boolean isDismissable() { + return false; + } + + public static boolean isEligible(Context context) { + return !Util.isBuildFresh(); + } + +} diff --git a/src/org/thoughtcrime/securesms/components/Reminder.java b/src/org/thoughtcrime/securesms/components/Reminder.java index 633467554..090f35d3e 100644 --- a/src/org/thoughtcrime/securesms/components/Reminder.java +++ b/src/org/thoughtcrime/securesms/components/Reminder.java @@ -46,4 +46,8 @@ public abstract class Reminder { public void setCancelListener(OnClickListener cancelListener) { this.cancelListener = cancelListener; } + + public boolean isDismissable() { + return true; + } } diff --git a/src/org/thoughtcrime/securesms/components/ReminderView.java b/src/org/thoughtcrime/securesms/components/ReminderView.java index b684814eb..3a127e2d2 100644 --- a/src/org/thoughtcrime/securesms/components/ReminderView.java +++ b/src/org/thoughtcrime/securesms/components/ReminderView.java @@ -56,15 +56,21 @@ public class ReminderView extends LinearLayout { icon.setImageResource(reminder.getIconResId()); title.setText(reminder.getTitleResId()); text.setText(reminder.getTextResId()); - ok.setOnClickListener(reminder.getOkListener()); - cancel.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - hide(); - if (reminder.getCancelListener() != null) reminder.getCancelListener().onClick(v); - } - }); - container.setVisibility(View.VISIBLE); + + if (reminder.isDismissable()) { + ok.setOnClickListener(reminder.getOkListener()); + cancel.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + hide(); + if (reminder.getCancelListener() != null) reminder.getCancelListener().onClick(v); + } + }); + container.setVisibility(View.VISIBLE); + } else { + ok.setVisibility(View.GONE); + cancel.setVisibility(View.GONE); + } } public void hide() { diff --git a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java index 41f8942db..a0d98f3c4 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java @@ -31,7 +31,6 @@ import org.whispersystems.libaxolotl.NoSessionException; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.util.Arrays; import ws.com.google.android.mms.MmsException; @@ -43,7 +42,7 @@ import ws.com.google.android.mms.pdu.PduPart; import ws.com.google.android.mms.pdu.SendConf; import ws.com.google.android.mms.pdu.SendReq; -public class MmsSendJob extends MasterSecretJob { +public class MmsSendJob extends SendJob { private static final String TAG = MmsSendJob.class.getSimpleName(); @@ -66,7 +65,7 @@ public class MmsSendJob extends MasterSecretJob { } @Override - public void onRun(MasterSecret masterSecret) throws MmsException, NoSuchMessageException, IOException { + public void onSend(MasterSecret masterSecret) throws MmsException, NoSuchMessageException, IOException { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); SendReq message = database.getOutgoingMessage(masterSecret, messageId); diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index 6b33e2722..ae940304c 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -66,7 +66,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { } @Override - public void onRun(MasterSecret masterSecret) throws MmsException, IOException, NoSuchMessageException { + public void onSend(MasterSecret masterSecret) throws MmsException, IOException, NoSuchMessageException { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); SendReq message = database.getOutgoingMessage(masterSecret, messageId); diff --git a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java index a2e40138c..4605914f4 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java @@ -57,7 +57,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType { } @Override - public void onRun(MasterSecret masterSecret) + public void onSend(MasterSecret masterSecret) throws RetryLaterException, MmsException, NoSuchMessageException { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); diff --git a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java index 95815eaf9..d3d476424 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -31,7 +31,7 @@ import ws.com.google.android.mms.ContentType; import ws.com.google.android.mms.pdu.PduPart; import ws.com.google.android.mms.pdu.SendReq; -public abstract class PushSendJob extends MasterSecretJob { +public abstract class PushSendJob extends SendJob { private static final String TAG = PushSendJob.class.getSimpleName(); diff --git a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java index 656d9bdad..343016803 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java @@ -51,7 +51,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType { } @Override - public void onRun(MasterSecret masterSecret) throws NoSuchMessageException, RetryLaterException { + public void onSend(MasterSecret masterSecret) throws NoSuchMessageException, RetryLaterException { EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsMessageRecord record = database.getMessage(masterSecret, messageId); String destination = record.getIndividualRecipient().getNumber(); diff --git a/src/org/thoughtcrime/securesms/jobs/SendJob.java b/src/org/thoughtcrime/securesms/jobs/SendJob.java new file mode 100644 index 000000000..e75db25ec --- /dev/null +++ b/src/org/thoughtcrime/securesms/jobs/SendJob.java @@ -0,0 +1,29 @@ +package org.thoughtcrime.securesms.jobs; + +import android.content.Context; + +import org.thoughtcrime.securesms.BuildConfig; +import org.thoughtcrime.securesms.TextSecureExpiredException; +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.util.Util; +import org.whispersystems.jobqueue.JobParameters; + +public abstract class SendJob extends MasterSecretJob { + + public SendJob(Context context, JobParameters parameters) { + super(context, parameters); + } + + @Override + public final void onRun(MasterSecret masterSecret) throws Exception { + if (!Util.isBuildFresh()) { + throw new TextSecureExpiredException(String.format("TextSecure expired (build %d, now %d)", + BuildConfig.BUILD_TIMESTAMP, + System.currentTimeMillis())); + } + + onSend(masterSecret); + } + + protected abstract void onSend(MasterSecret masterSecret) throws Exception; +} diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java index c0d604874..8752f84d2 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java @@ -31,7 +31,7 @@ import org.whispersystems.libaxolotl.NoSessionException; import java.util.ArrayList; -public class SmsSendJob extends MasterSecretJob { +public class SmsSendJob extends SendJob { private static final String TAG = SmsSendJob.class.getSimpleName(); @@ -48,7 +48,7 @@ public class SmsSendJob extends MasterSecretJob { } @Override - public void onRun(MasterSecret masterSecret) throws NoSuchMessageException { + public void onSend(MasterSecret masterSecret) throws NoSuchMessageException { EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsMessageRecord record = database.getMessage(masterSecret, messageId); diff --git a/src/org/thoughtcrime/securesms/util/Util.java b/src/org/thoughtcrime/securesms/util/Util.java index 8b159eee3..578a35083 100644 --- a/src/org/thoughtcrime/securesms/util/Util.java +++ b/src/org/thoughtcrime/securesms/util/Util.java @@ -32,6 +32,8 @@ import android.text.TextUtils; import android.text.style.StyleSpan; import android.widget.EditText; +import org.thoughtcrime.securesms.BuildConfig; +import org.thoughtcrime.securesms.TextSecureExpiredException; import org.whispersystems.textsecure.api.util.InvalidNumberException; import org.whispersystems.textsecure.api.util.PhoneNumberFormatter; @@ -288,6 +290,7 @@ public class Util { } } - - + public static boolean isBuildFresh() { + return BuildConfig.BUILD_TIMESTAMP + TimeUnit.DAYS.toMillis(180) > System.currentTimeMillis(); + } }