From 45c4eafbd79f3ba4234854d6fbd73c2593be0c9b Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 10 Sep 2018 08:40:00 -0700 Subject: [PATCH] Add the ability to block groups. This also fixes the situation where we block group-leave messages, preventing blocked contacts from leaving groups. Fixes #7970 Also, this forced us to upgrade libsignal-service, which fixes the websocket timeout issues. Thanks to @dpapavas! Fixes #6644 --- build.gradle | 6 +- res/values/strings.xml | 8 ++ .../securesms/ConversationActivity.java | 67 +++++++------- .../RecipientPreferenceActivity.java | 90 ++++++++++++++----- .../SignalCommunicationModule.java | 8 +- .../jobs/MultiDeviceBlockedUpdateJob.java | 12 ++- .../jobs/MultiDeviceGroupUpdateJob.java | 4 +- .../securesms/jobs/PushDecryptJob.java | 37 ++++++++ .../securesms/jobs/PushReceivedJob.java | 17 ++-- .../securesms/util/GroupUtil.java | 38 +++++++- 10 files changed, 211 insertions(+), 76 deletions(-) diff --git a/build.gradle b/build.gradle index c806f99c80..58a1192b3d 100644 --- a/build.gradle +++ b/build.gradle @@ -77,7 +77,7 @@ dependencies { compile 'com.google.android.exoplayer:exoplayer-core:2.8.4' compile 'com.google.android.exoplayer:exoplayer-ui:2.8.4' - compile 'org.whispersystems:signal-service-android:2.7.6' + compile 'org.whispersystems:signal-service-android:2.8.1' compile 'org.whispersystems:webrtc-android:M69' compile "me.leolin:ShortcutBadger:1.1.16" @@ -171,7 +171,7 @@ dependencyVerification { 'com.google.android.gms:play-services-maps:45e8021e7ddac4a44a82a0e9698991389ded3023d35c58f38dbd86d54211ec0e', 'com.google.android.exoplayer:exoplayer-ui:027557b2d69b15e1852a2530b36971f0dcc177abae240ee35e05f63502cdb0a7', 'com.google.android.exoplayer:exoplayer-core:e69b409e11887c955deb373357c30eeabf183395db0092b4817e0f80bb467d5b', - 'org.whispersystems:signal-service-android:823eed29e64fb0aa30d2078cb5ec0245e2a0713a4028121329c5c28788ef27f8', + 'org.whispersystems:signal-service-android:414e91598abd941eb3be9a85702538cc9928d8c22f00e07716b83a096cbbe54d', 'org.whispersystems:webrtc-android:5493c92141ce884fc5ce8240d783232f4fe14bd17a8d0d7d1bd4944d0bd1682f', 'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774', 'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb', @@ -218,7 +218,7 @@ dependencyVerification { 'com.github.bumptech.glide:gifdecoder:59ccf3bb0cec11dab4b857382cbe0b171111b6fc62bf141adce4e1180889af15', 'com.android.support:support-annotations:af05330d997eb92a066534dbe0a3ea24347d26d7001221092113ae02a8f233da', 'org.whispersystems:signal-protocol-android:5b8acded7f2a40178eb90ab8e8cbfec89d170d91b3ff5e78487d1098df6185a1', - 'org.whispersystems:signal-service-java:6169643c65dcba8c784744006fc3afd9b6f309041b310a33a624121e3577433a', + 'org.whispersystems:signal-service-java:c7ab92374e9656ba86a8d859cec71d03a68bba3e7ec0b7c597b726bf720eac21', 'com.github.bumptech.glide:disklrucache:c1b1b6f5bbd01e2fcdc9d7f60913c8d338bdb65ed4a93bfa02b56f19daaade4b', 'com.github.bumptech.glide:annotations:bede99ef9f71517a4274bac18fd3e483e9f2b6108d7d6fe8f4949be4aa4d9512', 'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a', diff --git a/res/values/strings.xml b/res/values/strings.xml index f0c37757ad..0bf3d1287f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -148,7 +148,9 @@ Error leaving group Please choose a contact Unblock this contact? + Unblock this group? You will once again be able to receive messages and calls from this contact. + Existing members will be able to add you to the group again. Unblock Attachment exceeds size limits for the type of message you\'re sending. Camera unavailable @@ -479,9 +481,15 @@ Block this contact? You will no longer receive messages and calls from this contact. + Block and leave this group? + Block this group? + You will no longer receive messages or updates from this group. Block Unblock this contact? You will once again be able to receive messages and calls from this contact. + Unblock this group? + Existing members will be able to add you to the group again. + Error leaving group Unblock Enabled Disabled diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 339fb76942..bf551a1ac3 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -51,6 +51,8 @@ import android.support.v7.app.AlertDialog; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; + +import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.logging.Log; import android.util.Pair; import android.view.KeyEvent; @@ -70,7 +72,6 @@ import android.widget.TextView; import android.widget.Toast; import com.google.android.gms.location.places.ui.PlacePicker; -import com.google.protobuf.ByteString; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -192,7 +193,6 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.thoughtcrime.securesms.TransportOption.Type; import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; -import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; /** * Activity for displaying a message thread, as well as @@ -693,26 +693,34 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void handleUnblock() { + int titleRes = R.string.ConversationActivity_unblock_this_contact_question; + int bodyRes = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact; + + if (recipient.isGroupRecipient()) { + titleRes = R.string.ConversationActivity_unblock_this_group_question; + bodyRes = R.string.ConversationActivity_unblock_this_group_description; + } + //noinspection CodeBlock2Expr new AlertDialog.Builder(this) - .setTitle(R.string.ConversationActivity_unblock_this_contact_question) - .setMessage(R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(ConversationActivity.this) + .setTitle(titleRes) + .setMessage(bodyRes) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(ConversationActivity.this) .setBlocked(recipient, false); - ApplicationContext.getInstance(ConversationActivity.this) - .getJobManager() - .add(new MultiDeviceBlockedUpdateJob(ConversationActivity.this)); + ApplicationContext.getInstance(ConversationActivity.this) + .getJobManager() + .add(new MultiDeviceBlockedUpdateJob(ConversationActivity.this)); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - }).show(); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }).show(); } @TargetApi(Build.VERSION_CODES.KITKAT) @@ -847,24 +855,21 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity builder.setCancelable(true); builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group)); builder.setPositiveButton(R.string.yes, (dialog, which) -> { - Context self = ConversationActivity.this; + Recipient groupRecipient = getRecipient(); + long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(groupRecipient); + Optional leaveMessage = GroupUtil.createGroupLeaveMessage(this, groupRecipient); - try { - String groupId = getRecipient().getAddress().toGroupString(); - DatabaseFactory.getGroupDatabase(self).setActive(groupId, false); + if (threadId != -1 && leaveMessage.isPresent()) { + MessageSender.send(this, leaveMessage.get(), threadId, false, null); - GroupContext context = GroupContext.newBuilder() - .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId))) - .setType(GroupContext.Type.QUIT) - .build(); + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this); + String groupId = groupRecipient.getAddress().toGroupString(); + groupDatabase.setActive(groupId, false); + groupDatabase.remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this))); - OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipient(), context, null, System.currentTimeMillis(), 0, null, Collections.emptyList()); - MessageSender.send(self, outgoingMessage, threadId, false, null); - DatabaseFactory.getGroupDatabase(self).remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(self))); initializeEnabledCheck(); - } catch (IOException e) { - Log.w(TAG, e); - Toast.makeText(self, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show(); } }); diff --git a/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java b/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java index 3ea79f1b4a..f5b50ce0f9 100644 --- a/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java +++ b/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java @@ -3,7 +3,6 @@ package org.thoughtcrime.securesms; import android.annotation.SuppressLint; import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.graphics.Color; @@ -30,6 +29,7 @@ import android.support.v7.widget.Toolbar; import android.telephony.PhoneNumberUtils; import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; +import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.logging.Log; import android.util.Pair; import android.view.MenuItem; @@ -37,6 +37,7 @@ import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; import com.bumptech.glide.load.engine.DiskCacheStrategy; @@ -55,6 +56,7 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment; @@ -62,11 +64,13 @@ import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreference; import org.thoughtcrime.securesms.preferences.widgets.ContactPreference; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.util.CommunicationActions; import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicTheme; +import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; @@ -378,10 +382,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi if (recipient.isGroupRecipient()) { if (colorPreference != null) colorPreference.setVisible(false); - if (blockPreference != null) blockPreference.setVisible(false); if (identityPreference != null) identityPreference.setVisible(false); - if (privacyCategory != null) privacyCategory.setVisible(false); - if (divider != null) divider.setVisible(false); if (aboutCategory != null) getPreferenceScreen().removePreference(aboutCategory); if (aboutDivider != null) getPreferenceScreen().removePreference(aboutDivider); } else { @@ -659,31 +660,55 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi } private void handleBlock() { - new AlertDialog.Builder(getActivity()) - .setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question) - .setMessage(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) - .setCancelable(true) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_block, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - setBlocked(recipient, true); + new AsyncTask>() { + + @Override + protected Pair doInBackground(Void... voids) { + int titleRes = R.string.RecipientPreferenceActivity_block_this_contact_question; + int bodyRes = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact; + + if (recipient.isGroupRecipient()) { + bodyRes = R.string.RecipientPreferenceActivity_block_and_leave_group_description; + + if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(getContext()).isActive(recipient.getAddress().toGroupString())) { + titleRes = R.string.RecipientPreferenceActivity_block_and_leave_group; + } else { + titleRes = R.string.RecipientPreferenceActivity_block_group; } - }).show(); + } + + return new Pair<>(titleRes, bodyRes); + } + + @Override + protected void onPostExecute(Pair titleAndBody) { + new AlertDialog.Builder(getActivity()) + .setTitle(titleAndBody.first) + .setMessage(titleAndBody.second) + .setCancelable(true) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.RecipientPreferenceActivity_block, (dialog, which) -> { + setBlocked(recipient, true); + }).show(); + } + }.execute(); } private void handleUnblock() { + int titleRes = R.string.RecipientPreferenceActivity_unblock_this_contact_question; + int bodyRes = R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact; + + if (recipient.isGroupRecipient()) { + titleRes = R.string.RecipientPreferenceActivity_unblock_this_group_question; + bodyRes = R.string.RecipientPreferenceActivity_unblock_this_group_description; + } + new AlertDialog.Builder(getActivity()) - .setTitle(R.string.RecipientPreferenceActivity_unblock_this_contact_question) - .setMessage(R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact) - .setCancelable(true) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_unblock, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - setBlocked(recipient, false); - } - }).show(); + .setTitle(titleRes) + .setMessage(bodyRes) + .setCancelable(true) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.RecipientPreferenceActivity_unblock, (dialog, which) -> setBlocked(recipient, false)).show(); } private void setBlocked(final Recipient recipient, final boolean blocked) { @@ -695,6 +720,23 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi DatabaseFactory.getRecipientDatabase(context) .setBlocked(recipient, blocked); + if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.getAddress().toGroupString())) { + long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); + Optional leaveMessage = GroupUtil.createGroupLeaveMessage(context, recipient); + + if (threadId != -1 && leaveMessage.isPresent()) { + MessageSender.send(context, leaveMessage.get(), threadId, false, null); + + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + String groupId = recipient.getAddress().toGroupString(); + groupDatabase.setActive(groupId, false); + groupDatabase.remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context))); + } else { + Log.w(TAG, "Failed to leave group. Can't block."); + Toast.makeText(context, R.string.RecipientPreferenceActivity_error_leaving_group, Toast.LENGTH_LONG).show(); + } + } + ApplicationContext.getInstance(context) .getJobManager() .add(new MultiDeviceBlockedUpdateJob(context)); diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java index e68126c6cb..a4faa115e9 100644 --- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java +++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java @@ -44,6 +44,9 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.util.CredentialsProvider; +import org.whispersystems.signalservice.api.util.RealtimeSleepTimer; +import org.whispersystems.signalservice.api.util.SleepTimer; +import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.websocket.ConnectivityListener; import dagger.Module; @@ -124,10 +127,13 @@ public class SignalCommunicationModule { @Provides synchronized SignalServiceMessageReceiver provideSignalMessageReceiver() { if (this.messageReceiver == null) { + SleepTimer sleepTimer = TextSecurePreferences.isGcmDisabled(context) ? new RealtimeSleepTimer(context) : new UptimeSleepTimer(); + this.messageReceiver = new SignalServiceMessageReceiver(networkAccess.getConfiguration(context), new DynamicCredentialsProvider(context), BuildConfig.USER_AGENT, - new PipeConnectivityListener()); + new PipeConnectivityListener(), + sleepTimer); } return this.messageReceiver; diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java index 8e79ca9f4d..93a1e8ca4a 100644 --- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java @@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.jobmanager.JobParameters; import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.GroupUtil; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; @@ -47,17 +48,20 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) { - List blocked = new LinkedList<>(); + List blockedIndividuals = new LinkedList<>(); + List blockedGroups = new LinkedList<>(); Recipient recipient; while ((recipient = reader.getNext()) != null) { - if (!recipient.isGroupRecipient()) { - blocked.add(recipient.getAddress().serialize()); + if (recipient.isGroupRecipient()) { + blockedGroups.add(GroupUtil.getDecodedId(recipient.getAddress().toGroupString())); + } else { + blockedIndividuals.add(recipient.getAddress().serialize()); } } - messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blocked))); + messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups))); } } diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java index 460443fe85..3d1119b0ff 100644 --- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java @@ -75,7 +75,9 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject out.write(new DeviceGroup(record.getId(), Optional.fromNullable(record.getTitle()), members, getAvatar(record.getAvatar()), - record.isActive(), expirationTimer)); + record.isActive(), expirationTimer, + Optional.of(recipient.getColor().serialize()), + recipient.isBlocked())); } } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 9bc277f377..bdc9e89a09 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -175,6 +175,11 @@ public class PushDecryptJob extends ContextJob { SignalServiceContent content = cipher.decrypt(envelope); + if (shouldIgnore(envelope, content)) { + Log.i(TAG, "Ignoring message."); + return; + } + if (content.getDataMessage().isPresent()) { SignalServiceDataMessage message = content.getDataMessage().get(); boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent(); @@ -941,4 +946,36 @@ public class PushDecryptJob extends ContextJob { return Recipient.from(context, Address.fromExternal(context, envelope.getSource()), false); } } + + private boolean shouldIgnore(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) { + Recipient sender = Recipient.from(context, Address.fromExternal(context, envelope.getSource()), false); + + if (content.getDataMessage().isPresent()) { + SignalServiceDataMessage message = content.getDataMessage().get(); + Recipient conversation = getMessageDestination(envelope, message); + + if (conversation.isGroupRecipient() && conversation.isBlocked()) { + return true; + } else if (conversation.isGroupRecipient()) { + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + Optional groupId = message.getGroupInfo().isPresent() ? Optional.of(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)) + : Optional.absent(); + + boolean isTextMessage = message.getBody().isPresent(); + boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent(); + boolean isExpireMessage = message.isExpirationUpdate(); + boolean isContentMessage = !message.isGroupUpdate() && (isTextMessage || isMediaMessage || isExpireMessage); + boolean isGroupActive = groupId.isPresent() && groupDatabase.isActive(groupId.get()); + boolean isLeaveMessage = message.getGroupInfo().isPresent() && message.getGroupInfo().get().getType() == SignalServiceGroup.Type.QUIT; + + return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage); + } else { + return sender.isBlocked(); + } + } else if (content.getCallMessage().isPresent()) { + return sender.isBlocked(); + } + + return false; + } } diff --git a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java index fa73592bfd..924f93f12f 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java @@ -34,22 +34,17 @@ public abstract class PushReceivedJob extends ContextJob { if (envelope.isReceipt()) { handleReceipt(envelope); } else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage()) { - handleMessage(envelope, source); + handleMessage(envelope); } else { Log.w(TAG, "Received envelope of unknown type: " + envelope.getType()); } } - private void handleMessage(SignalServiceEnvelope envelope, Address source) { - Recipient recipients = Recipient.from(context, source, false); - JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); - - if (!recipients.isBlocked()) { - long messageId = DatabaseFactory.getPushDatabase(context).insert(envelope); - jobManager.add(new PushDecryptJob(context, messageId)); - } else { - Log.w(TAG, "*** Received blocked push message, ignoring..."); - } + private void handleMessage(SignalServiceEnvelope envelope) { + long messageId = DatabaseFactory.getPushDatabase(context).insert(envelope); + ApplicationContext.getInstance(context) + .getJobManager() + .add(new PushDecryptJob(context, messageId)); } private void handleReceipt(SignalServiceEnvelope envelope) { diff --git a/src/org/thoughtcrime/securesms/util/GroupUtil.java b/src/org/thoughtcrime/securesms/util/GroupUtil.java index 0d3ebf7416..857a35eb2c 100644 --- a/src/org/thoughtcrime/securesms/util/GroupUtil.java +++ b/src/org/thoughtcrime/securesms/util/GroupUtil.java @@ -3,14 +3,23 @@ package org.thoughtcrime.securesms.util; import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.WorkerThread; + +import com.google.protobuf.ByteString; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.database.Address; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; +import org.thoughtcrime.securesms.sms.MessageSender; +import org.whispersystems.libsignal.util.guava.Optional; import java.io.IOException; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -20,7 +29,7 @@ public class GroupUtil { private static final String ENCODED_SIGNAL_GROUP_PREFIX = "__textsecure_group__!"; private static final String ENCODED_MMS_GROUP_PREFIX = "__signal_mms_group__!"; - private static final String TAG = GroupUtil.class.getSimpleName(); + private static final String TAG = GroupUtil.class.getSimpleName(); public static String getEncodedId(byte[] groupId, boolean mms) { return (mms ? ENCODED_MMS_GROUP_PREFIX : ENCODED_SIGNAL_GROUP_PREFIX) + Hex.toStringCondensed(groupId); @@ -42,6 +51,33 @@ public class GroupUtil { return groupId.startsWith(ENCODED_MMS_GROUP_PREFIX); } + @WorkerThread + public static Optional createGroupLeaveMessage(@NonNull Context context, @NonNull Recipient groupRecipient) { + String encodedGroupId = groupRecipient.getAddress().toGroupString(); + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + + if (!groupDatabase.isActive(encodedGroupId)) { + Log.w(TAG, "Group has already been left."); + return Optional.absent(); + } + + ByteString decodedGroupId; + try { + decodedGroupId = ByteString.copyFrom(getDecodedId(encodedGroupId)); + } catch (IOException e) { + Log.w(TAG, "Failed to decode group ID.", e); + return Optional.absent(); + } + + GroupContext groupContext = GroupContext.newBuilder() + .setId(decodedGroupId) + .setType(GroupContext.Type.QUIT) + .build(); + + return Optional.of(new OutgoingGroupMediaMessage(groupRecipient, groupContext, null, System.currentTimeMillis(), 0, null, Collections.emptyList())); + } + + public static @NonNull GroupDescription getDescription(@NonNull Context context, @Nullable String encodedGroup) { if (encodedGroup == null) { return new GroupDescription(context, null);