diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index 62de9499a1..700d64101a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -28,12 +28,14 @@ import com.annimon.stream.Collectors; import com.annimon.stream.Stream; import com.google.android.mms.pdu_alt.NotificationInd; import com.google.android.mms.pdu_alt.PduHeaders; +import com.google.protobuf.ByteString; import net.sqlcipher.database.SQLiteDatabase; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.session.libsession.utilities.GroupUtil; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.attachments.MmsNotificationAttachment; import org.session.libsession.database.documents.IdentityKeyMismatch; @@ -686,7 +688,14 @@ public class MmsDatabase extends MessagingDatabase { throws MmsException { if (threadId == -1) { - threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(retrieved.getRecipient()); + if(retrieved.isGroup()) { + ByteString decodedGroupId = ((OutgoingGroupMediaMessage)retrieved).getGroupContext().getId(); + String groupId = GroupUtil.doubleEncodeGroupID(decodedGroupId.toByteArray()); + Recipient group = Recipient.from(context, Address.fromSerialized(groupId), false); + threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(group); + } else { + threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(retrieved.getRecipient()); + } } long messageId = insertMessageOutbox(retrieved, threadId, false, null, serverTimestamp); if (messageId == -1) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index eaf72e6dbb..50d7232710 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -417,7 +417,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, .setName(name) .addAllMembers(members) .addAllAdmins(admins) - val infoMessage = OutgoingGroupMediaMessage(recipient, groupContextBuilder.build(), null, sentTimestamp, 0, null, listOf(), listOf()) + val infoMessage = OutgoingGroupMediaMessage(recipient, groupContextBuilder.build(), null, sentTimestamp, 0, false, null, listOf(), listOf()) val mmsDB = DatabaseFactory.getMmsDatabase(context) val mmsSmsDB = DatabaseFactory.getMmsSmsDatabase(context) if (mmsSmsDB.getMessageFor(sentTimestamp,userPublicKey) != null) return diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java index 2b614b4fd5..0a7df527fd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java @@ -2,9 +2,11 @@ package org.thoughtcrime.securesms.service; import android.content.Context; +import com.google.protobuf.ByteString; + import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate; +import org.session.libsession.messaging.messages.signal.OutgoingGroupMediaMessage; import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage; import org.session.libsession.messaging.threads.Address; import org.session.libsession.messaging.threads.DistributionTypes; @@ -73,32 +75,88 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM @Override public void setExpirationTimer(@NotNull ExpirationTimerUpdate message) { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - String userPublicKey = TextSecurePreferences.getLocalNumber(context); String senderPublicKey = message.getSender(); - int duration = message.getDuration(); - String groupPK = message.getGroupPublicKey(); + + // Notify the user + if (userPublicKey.equals(senderPublicKey)) { + // sender is a linked device + insertOutgoingExpirationTimerMessage(message); + } else { + insertIncomingExpirationTimerMessage(message); + } + + if (message.getId() != null) { + DatabaseFactory.getSmsDatabase(context).deleteMessage(message.getId()); + } + } + + private void insertIncomingExpirationTimerMessage(ExpirationTimerUpdate message) { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + + String senderPublicKey = message.getSender(); Long sentTimestamp = message.getSentTimestamp(); + String groupId = message.getGroupPublicKey(); + int duration = message.getDuration(); Optional groupInfo = Optional.absent(); - Address address; + Address address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : senderPublicKey); + Recipient recipient = Recipient.from(context, address, false); + + // if the sender is blocked, we don't display the update, except if it's in a closed group + if (recipient.isBlocked() && groupId == null) return; try { - if (groupPK != null) { - String groupID = GroupUtil.doubleEncodeGroupID(groupPK); + if (groupId != null) { + String groupID = GroupUtil.doubleEncodeGroupID(groupId); groupInfo = Optional.of(new SignalServiceGroup(GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL)); - address = Address.fromSerialized(groupID); - } else { - address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : senderPublicKey); + + Address groupAddress = Address.fromSerialized(groupID); + recipient = Recipient.from(context, groupAddress, false); } - Recipient recipient = Recipient.from(context, address, false); - if (recipient.isBlocked()) return; + IncomingMediaMessage mediaMessage = new IncomingMediaMessage(address, sentTimestamp, -1, + duration * 1000L, true, + false, + Optional.absent(), + groupInfo, + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent()); + //insert the timer update message + database.insertSecureDecryptedMessageInbox(mediaMessage, -1); - // Notify the user - if (userPublicKey.equals(senderPublicKey)) { - // sender is a linked device + //set the timer to the conversation + DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, duration); + + } catch (IOException | MmsException ioe) { + Log.e("Loki", "Failed to insert expiration update message."); + } + } + + private void insertOutgoingExpirationTimerMessage(ExpirationTimerUpdate message) { + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + + String senderPublicKey = message.getSender(); + Long sentTimestamp = message.getSentTimestamp(); + String groupId = message.getGroupPublicKey(); + int duration = message.getDuration(); + + Address address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : senderPublicKey); + Recipient recipient = Recipient.from(context, address, false); + + try { + if (groupId != null) { + // conversation is a closed group + GroupContext groupContext = SignalServiceProtos.GroupContext.newBuilder() + .setId(ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(groupId))).build(); + OutgoingGroupMediaMessage infoMessage = new OutgoingGroupMediaMessage(recipient, groupContext, null, sentTimestamp, duration * 1000L, true, null, Collections.emptyList(), Collections.emptyList()); + database.insertSecureDecryptedMessageOutbox(infoMessage, -1, sentTimestamp); + // we need the group ID as recipient for setExpireMessages below + recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(groupId)), false); + } else { + // conversation is a 1-1 OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipient, null, Collections.emptyList(), @@ -113,29 +171,12 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM Collections.emptyList(), Collections.emptyList()); database.insertSecureDecryptedMessageOutbox(mediaMessage, -1, sentTimestamp); - } else { - IncomingMediaMessage mediaMessage = new IncomingMediaMessage(address, sentTimestamp, -1, - duration * 1000L, true, - false, - Optional.absent(), - groupInfo, - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent()); - //insert the timer update message - database.insertSecureDecryptedMessageInbox(mediaMessage, -1); } //set the timer to the conversation DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, duration); - if (message.getId() != null) { - DatabaseFactory.getSmsDatabase(context).deleteMessage(message.getId()); - } - } catch (MmsException e) { - Log.e("Loki", "Failed to insert expiration update message."); - } catch (IOException ioe) { + } catch (MmsException | IOException ioe) { Log.e("Loki", "Failed to insert expiration update message."); } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java index 125aa26228..7f151ae7ee 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java @@ -58,6 +58,7 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { @Nullable final Attachment avatar, long sentTime, long expireIn, + boolean expirationUpdate, @Nullable QuoteModel quote, @NonNull List contacts, @NonNull List previews) @@ -65,7 +66,7 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { super(recipient, Base64.encodeBytes(group.toByteArray()), new LinkedList() {{if (avatar != null) add(avatar);}}, sentTime, - DistributionTypes.CONVERSATION, expireIn, false, quote, contacts, previews); + DistributionTypes.CONVERSATION, expireIn, expirationUpdate, quote, contacts, previews); this.group = group; } diff --git a/libsession/src/main/java/org/session/libsession/utilities/GroupUtil.kt b/libsession/src/main/java/org/session/libsession/utilities/GroupUtil.kt index 17aad994ea..7d7422494b 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/GroupUtil.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/GroupUtil.kt @@ -78,6 +78,11 @@ object GroupUtil { return getEncodedClosedGroupID(getEncodedClosedGroupID(Hex.fromStringCondensed(groupPublicKey)).toByteArray()) } + @JvmStatic + fun doubleEncodeGroupID(groupID: ByteArray): String { + return getEncodedClosedGroupID(getEncodedClosedGroupID(groupID).toByteArray()) + } + @JvmStatic @Throws(IOException::class) fun doubleDecodeGroupID(groupID: String): ByteArray {