From 437d6c7a525e2b0ac41ae161f6c3ae513c84aba1 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 7 Oct 2022 18:43:06 -0400 Subject: [PATCH] Flag deletes and replies to group stories as stories. --- .../securesms/jobs/PushGroupSendJob.java | 6 ++- .../securesms/jobs/ReactionSendJob.java | 4 +- .../securesms/jobs/RemoteDeleteSendJob.java | 20 ++++++++-- .../securesms/messages/GroupSendUtil.java | 39 +++++++++++++++---- .../api/SignalServiceMessageSender.java | 5 ++- 5 files changed, 59 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index 777c0c395..52a628fe8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -296,7 +296,7 @@ public final class PushGroupSendJob extends PushSendJob { .withExpiration(groupRecipient.getExpiresInSeconds()) .asGroupMessage(group) .build(); - return GroupSendUtil.sendResendableDataMessage(context, groupRecipient.requireGroupId().requireV2(), destinations, isRecipientUpdate, ContentHint.IMPLICIT, new MessageId(messageId, true), groupDataMessage, message.isUrgent()); + return GroupSendUtil.sendResendableDataMessage(context, groupRecipient.requireGroupId().requireV2(), null, destinations, isRecipientUpdate, ContentHint.IMPLICIT, new MessageId(messageId, true), groupDataMessage, message.isUrgent(), false); } else { throw new UndeliverableMessageException("Messages can no longer be sent to V1 groups!"); } @@ -351,12 +351,14 @@ public final class PushGroupSendJob extends PushSendJob { return GroupSendUtil.sendResendableDataMessage(context, groupRecipient.getGroupId().map(GroupId::requireV2).orElse(null), + null, destinations, isRecipientUpdate, ContentHint.RESENDABLE, new MessageId(messageId, true), groupMessageBuilder.build(), - message.isUrgent()); + message.isUrgent(), + message.getStoryType().isStory() || message.getParentStoryId() != null); } } catch (ServerRejectedException e) { throw new UndeliverableMessageException(e); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java index bf3ba72e8..01131cd28 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java @@ -238,12 +238,14 @@ public class ReactionSendJob extends BaseJob { boolean includesSelf = nonSelfDestinations.size() != destinations.size(); List results = GroupSendUtil.sendResendableDataMessage(context, conversationRecipient.getGroupId().map(GroupId::requireV2).orElse(null), + null, nonSelfDestinations, false, ContentHint.RESENDABLE, messageId, dataMessage, - true); + true, + false); if (includesSelf) { results.add(ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(dataMessage)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java index 04b285d9e..498cc8d2f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.jobs; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import com.annimon.stream.Stream; @@ -10,8 +11,11 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.MessageDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.database.model.DistributionListId; import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.database.model.StoryType; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.jobmanager.Data; @@ -29,6 +33,7 @@ import org.whispersystems.signalservice.api.crypto.ContentHint; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; +import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException; import java.io.IOException; @@ -157,7 +162,10 @@ public class RemoteDeleteSendJob extends BaseJob { List eligible = RecipientUtil.getEligibleForSending(Stream.of(recipients).map(Recipient::resolved).toList()); List skipped = Stream.of(SetUtil.difference(possible, eligible)).map(Recipient::getId).toList(); - GroupSendJobHelper.SendResult sendResult = deliver(conversationRecipient, eligible, targetSentTimestamp); + boolean isForStory = message.isMms() && (((MmsMessageRecord) message).getStoryType().isStory() || ((MmsMessageRecord) message).getParentStoryId() != null); + DistributionListId distributionListId = isForStory ? message.getRecipient().getDistributionListId().orElse(null) : null; + + GroupSendJobHelper.SendResult sendResult = deliver(conversationRecipient, eligible, targetSentTimestamp, isForStory, distributionListId); for (Recipient completion : sendResult.completed) { recipients.remove(completion.getId()); @@ -196,7 +204,11 @@ public class RemoteDeleteSendJob extends BaseJob { Log.w(TAG, "Failed to send remote delete to all recipients! (" + (initialRecipientCount - recipients.size() + "/" + initialRecipientCount + ")") ); } - private @NonNull GroupSendJobHelper.SendResult deliver(@NonNull Recipient conversationRecipient, @NonNull List destinations, long targetSentTimestamp) + private @NonNull GroupSendJobHelper.SendResult deliver(@NonNull Recipient conversationRecipient, + @NonNull List destinations, + long targetSentTimestamp, + boolean isForStory, + @Nullable DistributionListId distributionListId) throws IOException, UntrustedIdentityException { SignalServiceDataMessage.Builder dataMessageBuilder = SignalServiceDataMessage.newBuilder() @@ -210,12 +222,14 @@ public class RemoteDeleteSendJob extends BaseJob { SignalServiceDataMessage dataMessage = dataMessageBuilder.build(); List results = GroupSendUtil.sendResendableDataMessage(context, conversationRecipient.getGroupId().map(GroupId::requireV2).orElse(null), + distributionListId, destinations, false, ContentHint.RESENDABLE, new MessageId(messageId, isMms), dataMessage, - true); + true, + isForStory); return GroupSendJobHelper.getCompletedSends(destinations, results); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java b/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java index b8b7bca66..60e0438df 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java @@ -45,6 +45,7 @@ import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMess import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; +import org.whispersystems.signalservice.api.util.Preconditions; import org.whispersystems.signalservice.internal.push.exceptions.InvalidUnidentifiedAccessHeaderException; import org.whispersystems.signalservice.internal.push.http.CancelationSignal; import org.whispersystems.signalservice.internal.push.http.PartialSendCompleteListener; @@ -82,19 +83,41 @@ public final class GroupSendUtil { * * @param groupId The groupId of the group you're sending to, or null if you're sending to a collection of recipients not joined by a group. * @param isRecipientUpdate True if you've already sent this message to some recipients in the past, otherwise false. + * @param isForStory True if the message is related to a story, and should be sent with the story flag on the envelope */ @WorkerThread public static List sendResendableDataMessage(@NonNull Context context, @Nullable GroupId.V2 groupId, + @Nullable DistributionListId distributionListId, @NonNull List allTargets, boolean isRecipientUpdate, ContentHint contentHint, @NonNull MessageId messageId, @NonNull SignalServiceDataMessage message, - boolean urgent) + boolean urgent, + boolean isForStory) throws IOException, UntrustedIdentityException { - return sendMessage(context, groupId, getDistributionId(groupId), messageId, allTargets, isRecipientUpdate, false, DataSendOperation.resendable(message, contentHint, messageId, urgent), null); + Preconditions.checkArgument(groupId == null || distributionListId == null, "Cannot supply both a groupId and a distributionListId!"); + + DistributionId distributionId = groupId != null ? getDistributionId(groupId) : getDistributionId(distributionListId); + + return sendMessage(context, groupId, distributionId, messageId, allTargets, isRecipientUpdate, isForStory, DataSendOperation.resendable(message, contentHint, messageId, urgent, isForStory), null); + } + + @WorkerThread + public static List sendResendableStoryRelatedMessage(@NonNull Context context, + @Nullable GroupId.V2 groupId, + @NonNull DistributionListId distributionListId, + @NonNull List allTargets, + boolean isRecipientUpdate, + ContentHint contentHint, + @NonNull MessageId messageId, + @NonNull SignalServiceDataMessage message, + boolean urgent) + throws IOException, UntrustedIdentityException + { + return sendMessage(context, groupId, getDistributionId(distributionListId), messageId, allTargets, isRecipientUpdate, true, DataSendOperation.resendable(message, contentHint, messageId, urgent, true), null); } /** @@ -441,21 +464,23 @@ public final class GroupSendUtil { private final MessageId relatedMessageId; private final boolean resendable; private final boolean urgent; + private final boolean isForStory; - public static DataSendOperation resendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, @NonNull MessageId relatedMessageId, boolean urgent) { - return new DataSendOperation(message, contentHint, true, relatedMessageId, urgent); + public static DataSendOperation resendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, @NonNull MessageId relatedMessageId, boolean urgent, boolean isForStory) { + return new DataSendOperation(message, contentHint, true, relatedMessageId, urgent, isForStory); } public static DataSendOperation unresendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, boolean urgent) { - return new DataSendOperation(message, contentHint, false, null, urgent); + return new DataSendOperation(message, contentHint, false, null, urgent, false); } - private DataSendOperation(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, boolean resendable, @Nullable MessageId relatedMessageId, boolean urgent) { + private DataSendOperation(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, boolean resendable, @Nullable MessageId relatedMessageId, boolean urgent, boolean isForStory) { this.message = message; this.contentHint = contentHint; this.resendable = resendable; this.relatedMessageId = relatedMessageId; this.urgent = urgent; + this.isForStory = isForStory; if (resendable && relatedMessageId == null) { throw new IllegalArgumentException("If a message is resendable, it must have a related message ID!"); @@ -471,7 +496,7 @@ public final class GroupSendUtil { throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException, InvalidRegistrationIdException { SenderKeyGroupEvents listener = relatedMessageId != null ? new SenderKeyMetricEventListener(relatedMessageId.getId()) : SenderKeyGroupEvents.EMPTY; - return messageSender.sendGroupDataMessage(distributionId, targets, access, isRecipientUpdate, contentHint, message, listener, urgent); + return messageSender.sendGroupDataMessage(distributionId, targets, access, isRecipientUpdate, contentHint, message, listener, urgent, isForStory); } @Override diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 9566b8c4c..15fdfacd4 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -475,14 +475,15 @@ public class SignalServiceMessageSender { ContentHint contentHint, SignalServiceDataMessage message, SenderKeyGroupEvents sendEvents, - boolean urgent) + boolean urgent, + boolean isForStory) throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException { Log.d(TAG, "[" + message.getTimestamp() + "] Sending a group data message to " + recipients.size() + " recipients using DistributionId " + distributionId); Content content = createMessageContent(message); Optional groupId = message.getGroupId(); - List results = sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, contentHint, groupId, false, sendEvents, urgent, false); + List results = sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, contentHint, groupId, false, sendEvents, urgent, isForStory); sendEvents.onMessageSent();