From: AsamK Date: Wed, 3 Nov 2021 19:43:39 +0000 (+0100) Subject: Refactor receive api X-Git-Tag: v0.10.0~90 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/9075cc1a309fbc90276d2878d480d1e9e9c81887?ds=sidebyside Refactor receive api --- diff --git a/lib/src/main/java/org/asamk/signal/manager/AttachmentStore.java b/lib/src/main/java/org/asamk/signal/manager/AttachmentStore.java index f983a90b..9ba57937 100644 --- a/lib/src/main/java/org/asamk/signal/manager/AttachmentStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/AttachmentStore.java @@ -25,7 +25,7 @@ public class AttachmentStore { public void storeAttachment( final SignalServiceAttachmentRemoteId attachmentId, final AttachmentStorer storer ) throws IOException { - storeAttachment(getAttachmentFile(attachmentId), storer); + storeAttachment(getAttachmentFile(attachmentId.toString()), storer); } private void storeAttachment(final File attachmentFile, final AttachmentStorer storer) throws IOException { @@ -39,8 +39,8 @@ public class AttachmentStore { return new File(attachmentsPath, attachmentId.toString() + ".preview"); } - public File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId) { - return new File(attachmentsPath, attachmentId.toString()); + public File getAttachmentFile(String attachmentId) { + return new File(attachmentsPath, attachmentId); } private void createAttachmentsDir() throws IOException { diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index ab750ff0..77be3074 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -6,6 +6,7 @@ import org.asamk.signal.manager.api.Identity; import org.asamk.signal.manager.api.InactiveGroupLinkException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.Message; +import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendGroupMessageResults; @@ -25,10 +26,6 @@ import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientAddress; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.Closeable; @@ -221,7 +218,7 @@ public interface Manager extends Closeable { boolean isContactBlocked(RecipientIdentifier.Single recipient); - File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId); + File getAttachmentFile(String attachmentId); void sendContacts() throws IOException; @@ -243,13 +240,11 @@ public interface Manager extends Closeable { boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient); - SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address); - @Override void close() throws IOException; interface ReceiveMessageHandler { - void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent decryptedContent, Throwable e); + void handleMessage(MessageEnvelope envelope, Throwable e); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index a1c06075..9994ad65 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -70,7 +70,6 @@ import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.messages.SendMessageResult; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; @@ -904,11 +903,11 @@ public class ManagerImpl implements Manager { receiveThread = new Thread(() -> { while (!Thread.interrupted()) { try { - receiveMessagesInternal(1L, TimeUnit.HOURS, false, (envelope, decryptedContent, e) -> { + receiveMessagesInternal(1L, TimeUnit.HOURS, false, (envelope, e) -> { synchronized (messageHandlers) { for (ReceiveMessageHandler h : messageHandlers) { try { - h.handleMessage(envelope, decryptedContent, e); + h.handleMessage(envelope, e); } catch (Exception ex) { logger.warn("Message handler failed, ignoring", ex); } @@ -1140,7 +1139,7 @@ public class ManagerImpl implements Manager { } @Override - public File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId) { + public File getAttachmentFile(String attachmentId) { return attachmentHelper.getAttachmentFile(attachmentId); } @@ -1298,11 +1297,6 @@ public class ManagerImpl implements Manager { this.identityHelper.handleIdentityFailure(recipientId, identityFailure); } - @Override - public SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address) { - return resolveSignalServiceAddress(resolveRecipient(address)); - } - private SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) { final var address = account.getRecipientStore().resolveRecipientAddress(recipientId); if (address.getUuid().isPresent()) { @@ -1347,6 +1341,10 @@ public class ManagerImpl implements Manager { } } + private RecipientId resolveRecipient(RecipientAddress address) { + return account.getRecipientStore().resolveRecipient(address); + } + private RecipientId resolveRecipient(SignalServiceAddress address) { return account.getRecipientStore().resolveRecipient(address); } diff --git a/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java b/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java index 3b90b9e4..e7bb5436 100644 --- a/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java +++ b/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java @@ -1,23 +1,23 @@ package org.asamk.signal.manager; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; public class UntrustedIdentityException extends Exception { - private final SignalServiceAddress sender; + private final RecipientAddress sender; private final Integer senderDevice; - public UntrustedIdentityException(final SignalServiceAddress sender) { + public UntrustedIdentityException(final RecipientAddress sender) { this(sender, null); } - public UntrustedIdentityException(final SignalServiceAddress sender, final Integer senderDevice) { + public UntrustedIdentityException(final RecipientAddress sender, final Integer senderDevice) { super("Untrusted identity: " + sender.getIdentifier()); this.sender = sender; this.senderDevice = senderDevice; } - public SignalServiceAddress getSender() { + public RecipientAddress getSender() { return sender; } diff --git a/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java b/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java new file mode 100644 index 00000000..98b754e2 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java @@ -0,0 +1,810 @@ +package org.asamk.signal.manager.api; + +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupUtils; +import org.asamk.signal.manager.helper.RecipientAddressResolver; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; +import org.asamk.signal.manager.storage.recipients.RecipientResolver; +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; +import org.whispersystems.signalservice.api.messages.SignalServiceContent; +import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; +import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; +import org.whispersystems.signalservice.api.messages.SignalServiceGroup; +import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext; +import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; +import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; +import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; +import org.whispersystems.signalservice.api.messages.calls.BusyMessage; +import org.whispersystems.signalservice.api.messages.calls.HangupMessage; +import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage; +import org.whispersystems.signalservice.api.messages.calls.OfferMessage; +import org.whispersystems.signalservice.api.messages.calls.OpaqueMessage; +import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; +import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; +import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage; +import org.whispersystems.signalservice.api.messages.multidevice.MessageRequestResponseMessage; +import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; +import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; +import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; +import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage; +import org.whispersystems.signalservice.api.messages.multidevice.ViewedMessage; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +public record MessageEnvelope( + Optional sourceAddress, + int sourceDevice, + long timestamp, + long serverReceivedTimestamp, + long serverDeliveredTimestamp, + boolean isUnidentifiedSender, + Optional receipt, + Optional typing, + Optional data, + Optional sync, + Optional call +) { + + public static final record Receipt(long when, Type type, List timestamps) { + + static Receipt from(final SignalServiceReceiptMessage receiptMessage) { + return new Receipt(receiptMessage.getWhen(), + Type.from(receiptMessage.getType()), + receiptMessage.getTimestamps()); + } + + public enum Type { + DELIVERY, + READ, + VIEWED, + UNKNOWN; + + static Type from(SignalServiceReceiptMessage.Type type) { + return switch (type) { + case DELIVERY -> DELIVERY; + case READ -> READ; + case VIEWED -> VIEWED; + case UNKNOWN -> UNKNOWN; + }; + } + } + } + + public static final record Typing(long timestamp, Type type, Optional groupId) { + + public static Typing from(final SignalServiceTypingMessage typingMessage) { + return new Typing(typingMessage.getTimestamp(), + typingMessage.isTypingStarted() ? Type.STARTED : Type.STOPPED, + Optional.ofNullable(typingMessage.getGroupId().transform(GroupId::unknownVersion).orNull())); + } + + public enum Type { + STARTED, + STOPPED, + } + } + + public static final record Data( + long timestamp, + Optional groupContext, + Optional groupCallUpdate, + Optional body, + int expiresInSeconds, + boolean isExpirationUpdate, + boolean isViewOnce, + boolean isEndSession, + boolean hasProfileKey, + Optional reaction, + Optional quote, + List attachments, + Optional remoteDeleteId, + Optional sticker, + List sharedContacts, + List mentions, + List previews + ) { + + static Data from( + final SignalServiceDataMessage dataMessage, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new Data(dataMessage.getTimestamp(), + Optional.ofNullable(dataMessage.getGroupContext().transform(GroupContext::from).orNull()), + Optional.ofNullable(dataMessage.getGroupCallUpdate().transform(GroupCallUpdate::from).orNull()), + Optional.ofNullable(dataMessage.getBody().orNull()), + dataMessage.getExpiresInSeconds(), + dataMessage.isExpirationUpdate(), + dataMessage.isViewOnce(), + dataMessage.isEndSession(), + dataMessage.getProfileKey().isPresent(), + Optional.ofNullable(dataMessage.getReaction() + .transform(r -> Reaction.from(r, recipientResolver, addressResolver)) + .orNull()), + Optional.ofNullable(dataMessage.getQuote() + .transform(q -> Quote.from(q, recipientResolver, addressResolver)) + .orNull()), + dataMessage.getAttachments() + .transform(a -> a.stream().map(Attachment::from).collect(Collectors.toList())) + .or(List.of()), + Optional.ofNullable(dataMessage.getRemoteDelete() + .transform(SignalServiceDataMessage.RemoteDelete::getTargetSentTimestamp) + .orNull()), + Optional.ofNullable(dataMessage.getSticker().transform(Sticker::from).orNull()), + dataMessage.getSharedContacts() + .transform(a -> a.stream().map(SharedContact::from).collect(Collectors.toList())) + .or(List.of()), + dataMessage.getMentions() + .transform(a -> a.stream() + .map(m -> Mention.from(m, recipientResolver, addressResolver)) + .collect(Collectors.toList())) + .or(List.of()), + dataMessage.getPreviews() + .transform(a -> a.stream().map(Preview::from).collect(Collectors.toList())) + .or(List.of())); + } + + public record GroupContext(GroupId groupId, boolean isGroupUpdate, int revision) { + + static GroupContext from(SignalServiceGroupContext groupContext) { + if (groupContext.getGroupV1().isPresent()) { + return new GroupContext(GroupId.v1(groupContext.getGroupV1().get().getGroupId()), + groupContext.getGroupV1Type() == SignalServiceGroup.Type.UPDATE, + 0); + } else if (groupContext.getGroupV2().isPresent()) { + final var groupV2 = groupContext.getGroupV2().get(); + return new GroupContext(GroupUtils.getGroupIdV2(groupV2.getMasterKey()), + groupV2.hasSignedGroupChange(), + groupV2.getRevision()); + } else { + throw new RuntimeException("Invalid group context state"); + } + } + } + + public record GroupCallUpdate(String eraId) { + + static GroupCallUpdate from(SignalServiceDataMessage.GroupCallUpdate groupCallUpdate) { + return new GroupCallUpdate(groupCallUpdate.getEraId()); + } + } + + public record Reaction( + long targetSentTimestamp, RecipientAddress targetAuthor, String emoji, boolean isRemove + ) { + + static Reaction from( + SignalServiceDataMessage.Reaction reaction, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new Reaction(reaction.getTargetSentTimestamp(), + addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(reaction.getTargetAuthor())), + reaction.getEmoji(), + reaction.isRemove()); + } + } + + public record Quote( + long id, + RecipientAddress author, + Optional text, + List mentions, + List attachments + ) { + + static Quote from( + SignalServiceDataMessage.Quote quote, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new Quote(quote.getId(), + addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(quote.getAuthor())), + Optional.ofNullable(quote.getText()), + quote.getMentions() + .stream() + .map(m -> Mention.from(m, recipientResolver, addressResolver)) + .collect(Collectors.toList()), + quote.getAttachments().stream().map(Attachment::from).collect(Collectors.toList())); + } + } + + public record Mention(RecipientAddress recipient, int start, int length) { + + static Mention from( + SignalServiceDataMessage.Mention mention, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new Mention(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(mention.getUuid())), + mention.getStart(), + mention.getLength()); + } + } + + public record Attachment( + Optional id, + Optional fileName, + String contentType, + Optional uploadTimestamp, + Optional size, + Optional preview, + Optional thumbnail, + Optional caption, + Optional width, + Optional height, + boolean isVoiceNote, + boolean isGif, + boolean isBorderless + ) { + + static Attachment from(SignalServiceAttachment attachment) { + if (attachment.isPointer()) { + final var a = attachment.asPointer(); + return new Attachment(Optional.of(a.getRemoteId().toString()), + Optional.ofNullable(a.getFileName().orNull()), + a.getContentType(), + a.getUploadTimestamp() == 0 ? Optional.empty() : Optional.of(a.getUploadTimestamp()), + Optional.ofNullable(a.getSize().transform(Integer::longValue).orNull()), + Optional.ofNullable(a.getPreview().orNull()), + Optional.empty(), + Optional.ofNullable(a.getCaption().orNull()), + a.getWidth() == 0 ? Optional.empty() : Optional.of(a.getWidth()), + a.getHeight() == 0 ? Optional.empty() : Optional.of(a.getHeight()), + a.getVoiceNote(), + a.isGif(), + a.isBorderless()); + } else { + final var a = attachment.asStream(); + return new Attachment(Optional.empty(), + Optional.ofNullable(a.getFileName().orNull()), + a.getContentType(), + a.getUploadTimestamp() == 0 ? Optional.empty() : Optional.of(a.getUploadTimestamp()), + Optional.of(a.getLength()), + Optional.ofNullable(a.getPreview().orNull()), + Optional.empty(), + Optional.ofNullable(a.getCaption().orNull()), + a.getWidth() == 0 ? Optional.empty() : Optional.of(a.getWidth()), + a.getHeight() == 0 ? Optional.empty() : Optional.of(a.getHeight()), + a.getVoiceNote(), + a.isGif(), + a.isBorderless()); + } + } + + static Attachment from(SignalServiceDataMessage.Quote.QuotedAttachment a) { + return new Attachment(Optional.empty(), + Optional.ofNullable(a.getFileName()), + a.getContentType(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + a.getThumbnail() == null ? Optional.empty() : Optional.of(Attachment.from(a.getThumbnail())), + Optional.empty(), + Optional.empty(), + Optional.empty(), + false, + false, + false); + } + } + + public record Sticker(byte[] packId, byte[] packKey, int stickerId) { + + static Sticker from(SignalServiceDataMessage.Sticker sticker) { + return new Sticker(sticker.getPackId(), sticker.getPackKey(), sticker.getStickerId()); + } + } + + public record SharedContact( + Name name, + Optional avatar, + List phone, + List email, + List
address, + Optional organization + ) { + + static SharedContact from(org.whispersystems.signalservice.api.messages.shared.SharedContact sharedContact) { + return new SharedContact(Name.from(sharedContact.getName()), + Optional.ofNullable(sharedContact.getAvatar().transform(Avatar::from).orNull()), + sharedContact.getPhone() + .transform(p -> p.stream().map(Phone::from).collect(Collectors.toList())) + .or(List.of()), + sharedContact.getEmail() + .transform(p -> p.stream().map(Email::from).collect(Collectors.toList())) + .or(List.of()), + sharedContact.getAddress() + .transform(p -> p.stream().map(Address::from).collect(Collectors.toList())) + .or(List.of()), + Optional.ofNullable(sharedContact.getOrganization().orNull())); + } + + public record Name( + Optional display, + Optional given, + Optional family, + Optional prefix, + Optional suffix, + Optional middle + ) { + + static Name from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Name name) { + return new Name(Optional.ofNullable(name.getDisplay().orNull()), + Optional.ofNullable(name.getGiven().orNull()), + Optional.ofNullable(name.getFamily().orNull()), + Optional.ofNullable(name.getPrefix().orNull()), + Optional.ofNullable(name.getSuffix().orNull()), + Optional.ofNullable(name.getMiddle().orNull())); + } + } + + public record Avatar(Attachment attachment, boolean isProfile) { + + static Avatar from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Avatar avatar) { + return new Avatar(Attachment.from(avatar.getAttachment()), avatar.isProfile()); + } + } + + public record Phone( + String value, Type type, Optional label + ) { + + static Phone from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Phone phone) { + return new Phone(phone.getValue(), + Type.from(phone.getType()), + Optional.ofNullable(phone.getLabel().orNull())); + } + + public enum Type { + HOME, + WORK, + MOBILE, + CUSTOM; + + static Type from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Phone.Type type) { + return switch (type) { + case HOME -> HOME; + case WORK -> WORK; + case MOBILE -> MOBILE; + case CUSTOM -> CUSTOM; + }; + } + } + } + + public record Email( + String value, Type type, Optional label + ) { + + static Email from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Email email) { + return new Email(email.getValue(), + Type.from(email.getType()), + Optional.ofNullable(email.getLabel().orNull())); + } + + public enum Type { + HOME, + WORK, + MOBILE, + CUSTOM; + + static Type from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Email.Type type) { + return switch (type) { + case HOME -> HOME; + case WORK -> WORK; + case MOBILE -> MOBILE; + case CUSTOM -> CUSTOM; + }; + } + } + } + + public record Address( + Type type, + Optional label, + Optional street, + Optional pobox, + Optional neighborhood, + Optional city, + Optional region, + Optional postcode, + Optional country + ) { + + static Address from(org.whispersystems.signalservice.api.messages.shared.SharedContact.PostalAddress address) { + return new Address(Address.Type.from(address.getType()), + Optional.ofNullable(address.getLabel().orNull()), + Optional.ofNullable(address.getLabel().orNull()), + Optional.ofNullable(address.getLabel().orNull()), + Optional.ofNullable(address.getLabel().orNull()), + Optional.ofNullable(address.getLabel().orNull()), + Optional.ofNullable(address.getLabel().orNull()), + Optional.ofNullable(address.getLabel().orNull()), + Optional.ofNullable(address.getLabel().orNull())); + } + + public enum Type { + HOME, + WORK, + CUSTOM; + + static Type from(org.whispersystems.signalservice.api.messages.shared.SharedContact.PostalAddress.Type type) { + return switch (type) { + case HOME -> HOME; + case WORK -> WORK; + case CUSTOM -> CUSTOM; + }; + } + } + } + } + + public record Preview(String title, String description, long date, String url, Optional image) { + + static Preview from(SignalServiceDataMessage.Preview preview) { + return new Preview(preview.getTitle(), + preview.getDescription(), + preview.getDate(), + preview.getUrl(), + Optional.ofNullable(preview.getImage().transform(Attachment::from).orNull())); + } + } + } + + public static final record Sync( + Optional sent, + Optional blocked, + List read, + List viewed, + Optional viewOnceOpen, + Optional contacts, + Optional groups, + Optional messageRequestResponse + ) { + + public static Sync from( + final SignalServiceSyncMessage syncMessage, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new Sync(Optional.ofNullable(syncMessage.getSent() + .transform(s -> Sent.from(s, recipientResolver, addressResolver)) + .orNull()), + Optional.ofNullable(syncMessage.getBlockedList() + .transform(b -> Blocked.from(b, recipientResolver, addressResolver)) + .orNull()), + syncMessage.getRead() + .transform(r -> r.stream() + .map(rm -> Read.from(rm, recipientResolver, addressResolver)) + .collect(Collectors.toList())) + .or(List.of()), + syncMessage.getViewed() + .transform(r -> r.stream() + .map(rm -> Viewed.from(rm, recipientResolver, addressResolver)) + .collect(Collectors.toList())) + .or(List.of()), + Optional.ofNullable(syncMessage.getViewOnceOpen() + .transform(rm -> ViewOnceOpen.from(rm, recipientResolver, addressResolver)) + .orNull()), + Optional.ofNullable(syncMessage.getContacts().transform(Contacts::from).orNull()), + Optional.ofNullable(syncMessage.getGroups().transform(Groups::from).orNull()), + Optional.ofNullable(syncMessage.getMessageRequestResponse() + .transform(m -> MessageRequestResponse.from(m, recipientResolver, addressResolver)) + .orNull())); + } + + public record Sent( + long timestamp, + long expirationStartTimestamp, + Optional destination, + Set recipients, + Data message + ) { + + static Sent from( + SentTranscriptMessage sentMessage, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new Sent(sentMessage.getTimestamp(), + sentMessage.getExpirationStartTimestamp(), + Optional.ofNullable(sentMessage.getDestination() + .transform(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient( + d))) + .orNull()), + sentMessage.getRecipients() + .stream() + .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))) + .collect(Collectors.toSet()), + Data.from(sentMessage.getMessage(), recipientResolver, addressResolver)); + } + } + + public record Blocked(List recipients, List groupIds) { + + static Blocked from( + BlockedListMessage blockedListMessage, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new Blocked(blockedListMessage.getAddresses() + .stream() + .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))) + .collect(Collectors.toList()), + blockedListMessage.getGroupIds() + .stream() + .map(GroupId::unknownVersion) + .collect(Collectors.toList())); + } + } + + public record Read(RecipientAddress sender, long timestamp) { + + static Read from( + ReadMessage readMessage, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new Read(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(readMessage.getSender())), + readMessage.getTimestamp()); + } + } + + public record Viewed(RecipientAddress sender, long timestamp) { + + static Viewed from( + ViewedMessage readMessage, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new Viewed(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(readMessage.getSender())), + readMessage.getTimestamp()); + } + } + + public record ViewOnceOpen(RecipientAddress sender, long timestamp) { + + static ViewOnceOpen from( + ViewOnceOpenMessage readMessage, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new ViewOnceOpen(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient( + readMessage.getSender())), readMessage.getTimestamp()); + } + } + + public record Contacts(boolean isComplete) { + + static Contacts from(ContactsMessage contactsMessage) { + return new Contacts(contactsMessage.isComplete()); + } + } + + public record Groups() { + + static Groups from(SignalServiceAttachment groupsMessage) { + return new Groups(); + } + } + + public record MessageRequestResponse(Type type, Optional groupId, Optional person) { + + static MessageRequestResponse from( + MessageRequestResponseMessage messageRequestResponse, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new MessageRequestResponse(Type.from(messageRequestResponse.getType()), + Optional.ofNullable(messageRequestResponse.getGroupId() + .transform(GroupId::unknownVersion) + .orNull()), + Optional.ofNullable(messageRequestResponse.getPerson() + .transform(p -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient( + p))) + .orNull())); + } + + public enum Type { + UNKNOWN, + ACCEPT, + DELETE, + BLOCK, + BLOCK_AND_DELETE, + UNBLOCK_AND_ACCEPT; + + static Type from(MessageRequestResponseMessage.Type type) { + return switch (type) { + case UNKNOWN -> UNKNOWN; + case ACCEPT -> ACCEPT; + case DELETE -> DELETE; + case BLOCK -> BLOCK; + case BLOCK_AND_DELETE -> BLOCK_AND_DELETE; + case UNBLOCK_AND_ACCEPT -> UNBLOCK_AND_ACCEPT; + }; + } + } + } + } + + public static final record Call( + Optional destinationDeviceId, + Optional groupId, + Optional timestamp, + Optional offer, + Optional answer, + Optional hangup, + Optional busy, + List iceUpdate, + Optional opaque + ) { + + public static Call from(final SignalServiceCallMessage callMessage) { + return new Call(Optional.ofNullable(callMessage.getDestinationDeviceId().orNull()), + Optional.ofNullable(callMessage.getGroupId().transform(GroupId::unknownVersion).orNull()), + Optional.ofNullable(callMessage.getTimestamp().orNull()), + Optional.ofNullable(callMessage.getOfferMessage().transform(Offer::from).orNull()), + Optional.ofNullable(callMessage.getAnswerMessage().transform(Answer::from).orNull()), + Optional.ofNullable(callMessage.getHangupMessage().transform(Hangup::from).orNull()), + Optional.ofNullable(callMessage.getBusyMessage().transform(Busy::from).orNull()), + callMessage.getIceUpdateMessages() + .transform(m -> m.stream().map(IceUpdate::from).collect(Collectors.toList())) + .or(List.of()), + Optional.ofNullable(callMessage.getOpaqueMessage().transform(Opaque::from).orNull())); + } + + public record Offer(long id, String sdp, Type type, byte[] opaque) { + + static Offer from(OfferMessage offerMessage) { + return new Offer(offerMessage.getId(), + offerMessage.getSdp(), + Type.from(offerMessage.getType()), + offerMessage.getOpaque()); + } + + public enum Type { + AUDIO_CALL, + VIDEO_CALL; + + static Type from(OfferMessage.Type type) { + return switch (type) { + case AUDIO_CALL -> AUDIO_CALL; + case VIDEO_CALL -> VIDEO_CALL; + }; + } + } + } + + public record Answer(long id, String sdp, byte[] opaque) { + + static Answer from(AnswerMessage answerMessage) { + return new Answer(answerMessage.getId(), answerMessage.getSdp(), answerMessage.getOpaque()); + } + } + + public record Busy(long id) { + + static Offer from(OfferMessage offerMessage) { + return new Offer(offerMessage.getId(), + offerMessage.getSdp(), + Offer.Type.from(offerMessage.getType()), + offerMessage.getOpaque()); + } + + static Busy from(BusyMessage busyMessage) { + return new Busy(busyMessage.getId()); + } + } + + public record Hangup(long id, Type type, int deviceId, boolean isLegacy) { + + static Hangup from(HangupMessage hangupMessage) { + return new Hangup(hangupMessage.getId(), + Type.from(hangupMessage.getType()), + hangupMessage.getDeviceId(), + hangupMessage.isLegacy()); + } + + public enum Type { + NORMAL, + ACCEPTED, + DECLINED, + BUSY, + NEED_PERMISSION; + + static Type from(HangupMessage.Type type) { + return switch (type) { + case NORMAL -> NORMAL; + case ACCEPTED -> ACCEPTED; + case DECLINED -> DECLINED; + case BUSY -> BUSY; + case NEED_PERMISSION -> NEED_PERMISSION; + }; + } + } + } + + public record IceUpdate(long id, String sdp, byte[] opaque) { + + static IceUpdate from(IceUpdateMessage iceUpdateMessage) { + return new IceUpdate(iceUpdateMessage.getId(), iceUpdateMessage.getSdp(), iceUpdateMessage.getOpaque()); + } + } + + public record Opaque(byte[] opaque, Urgency urgency) { + + static Opaque from(OpaqueMessage opaqueMessage) { + return new Opaque(opaqueMessage.getOpaque(), Urgency.from(opaqueMessage.getUrgency())); + } + + public enum Urgency { + DROPPABLE, + HANDLE_IMMEDIATELY; + + static Urgency from(OpaqueMessage.Urgency urgency) { + return switch (urgency) { + case DROPPABLE -> DROPPABLE; + case HANDLE_IMMEDIATELY -> HANDLE_IMMEDIATELY; + }; + } + } + } + } + + public static MessageEnvelope from( + SignalServiceEnvelope envelope, + SignalServiceContent content, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + final var source = !envelope.isUnidentifiedSender() && envelope.hasSourceUuid() + ? recipientResolver.resolveRecipient(envelope.getSourceAddress()) + : envelope.isUnidentifiedSender() && content != null + ? recipientResolver.resolveRecipient(content.getSender()) + : null; + final var sourceDevice = envelope.hasSourceDevice() + ? envelope.getSourceDevice() + : content != null ? content.getSenderDevice() : 0; + + Optional receipt; + Optional typing; + Optional data; + Optional sync; + Optional call; + if (content != null) { + receipt = Optional.ofNullable(content.getReceiptMessage().transform(Receipt::from).orNull()); + typing = Optional.ofNullable(content.getTypingMessage().transform(Typing::from).orNull()); + data = Optional.ofNullable(content.getDataMessage() + .transform(dataMessage -> Data.from(dataMessage, recipientResolver, addressResolver)) + .orNull()); + sync = Optional.ofNullable(content.getSyncMessage() + .transform(s -> Sync.from(s, recipientResolver, addressResolver)) + .orNull()); + call = Optional.ofNullable(content.getCallMessage().transform(Call::from).orNull()); + } else { + receipt = Optional.empty(); + typing = Optional.empty(); + data = Optional.empty(); + sync = Optional.empty(); + call = Optional.empty(); + } + + return new MessageEnvelope(source == null + ? Optional.empty() + : Optional.of(addressResolver.resolveRecipientAddress(source)), + sourceDevice, + envelope.getTimestamp(), + envelope.getServerReceivedTimestamp(), + envelope.getServerDeliveredTimestamp(), + envelope.isUnidentifiedSender(), + receipt, + typing, + data, + sync, + call); + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java index 449a575e..7eea6966 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java @@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException; import java.io.File; @@ -36,7 +35,7 @@ public class AttachmentHelper { this.attachmentStore = attachmentStore; } - public File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId) { + public File getAttachmentFile(String attachmentId) { return attachmentStore.getAttachmentFile(attachmentId); } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index 08252300..8e99357f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -19,6 +19,7 @@ import org.asamk.signal.manager.actions.SendSyncConfigurationAction; import org.asamk.signal.manager.actions.SendSyncContactsAction; import org.asamk.signal.manager.actions.SendSyncGroupsAction; import org.asamk.signal.manager.actions.SendSyncKeysAction; +import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupNotFoundException; @@ -109,8 +110,8 @@ public final class IncomingMessageHandler { content = dependencies.getCipher().decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender()); - final var exception = new UntrustedIdentityException(addressResolver.resolveSignalServiceAddress( - recipientId), e.getSenderDevice()); + final var exception = new UntrustedIdentityException(account.getRecipientStore() + .resolveRecipientAddress(recipientId), e.getSenderDevice()); return new Pair<>(List.of(), exception); } catch (Exception e) { return new Pair<>(List.of(), e); @@ -139,8 +140,8 @@ public final class IncomingMessageHandler { } catch (ProtocolUntrustedIdentityException e) { final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender()); actions.add(new RetrieveProfileAction(recipientId)); - exception = new UntrustedIdentityException(addressResolver.resolveSignalServiceAddress(recipientId), - e.getSenderDevice()); + exception = new UntrustedIdentityException(account.getRecipientStore() + .resolveRecipientAddress(recipientId), e.getSenderDevice()); } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException | ProtocolInvalidMessageException e) { final var sender = account.getRecipientStore().resolveRecipient(e.getSender()); final var senderProfile = profileProvider.getProfile(sender); @@ -196,7 +197,10 @@ public final class IncomingMessageHandler { } else { actions = List.of(); } - handler.handleMessage(envelope, content, exception); + handler.handleMessage(MessageEnvelope.from(envelope, + content, + recipientResolver, + account.getRecipientStore()::resolveRecipientAddress), exception); return actions; } } @@ -219,9 +223,17 @@ public final class IncomingMessageHandler { final var message = content.getSenderKeyDistributionMessage().get(); final var protocolAddress = new SignalProtocolAddress(addressResolver.resolveSignalServiceAddress(sender) .getIdentifier(), senderDeviceId); + logger.debug("Received a sender key distribution message for distributionId {} from {}", + message.getDistributionId(), + protocolAddress); dependencies.getMessageSender().processSenderKeyDistributionMessage(protocolAddress, message); } + if (content.getDecryptionErrorMessage().isPresent()) { + var message = content.getDecryptionErrorMessage().get(); + logger.debug("Received a decryption error message (resend request for {})", message.getTimestamp()); + } + if (content.getDataMessage().isPresent()) { var message = content.getDataMessage().get(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index 6c0fb2e9..dc912634 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java @@ -153,7 +153,7 @@ public class SendHelper { try { messageSender.sendReceipt(address, unidentifiedAccessHelper.getAccessFor(recipientId), receiptMessage); } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { - throw new UntrustedIdentityException(address); + throw new UntrustedIdentityException(account.getRecipientStore().resolveRecipientAddress(recipientId)); } } @@ -172,7 +172,7 @@ public class SendHelper { groupId.transform(GroupId::serialize), errorMessage); } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { - throw new UntrustedIdentityException(address); + throw new UntrustedIdentityException(account.getRecipientStore().resolveRecipientAddress(recipientId)); } } @@ -229,7 +229,7 @@ public class SendHelper { messageSender.sendTyping(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId), message); } } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { - throw new UntrustedIdentityException(address); + throw new UntrustedIdentityException(account.getRecipientStore().resolveRecipientAddress(recipientId)); } } diff --git a/src/main/java/org/asamk/signal/DbusReceiveMessageHandler.java b/src/main/java/org/asamk/signal/DbusReceiveMessageHandler.java index 95185052..e53095da 100644 --- a/src/main/java/org/asamk/signal/DbusReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/DbusReceiveMessageHandler.java @@ -2,17 +2,12 @@ package org.asamk.signal; import org.asamk.Signal; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.groups.GroupUtils; -import org.asamk.signal.util.Util; +import org.asamk.signal.manager.api.MessageEnvelope; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.types.Variant; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.Serializable; import java.util.ArrayList; @@ -21,8 +16,6 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import static org.asamk.signal.util.Util.getLegacyIdentifier; - public class DbusReceiveMessageHandler implements Manager.ReceiveMessageHandler { private final Manager m; @@ -36,211 +29,193 @@ public class DbusReceiveMessageHandler implements Manager.ReceiveMessageHandler } @Override - public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { + public void handleMessage(MessageEnvelope envelope, Throwable exception) { try { - sendDbusMessages(envelope, content); + sendDbusMessages(envelope); } catch (DBusException e) { e.printStackTrace(); } } - private void sendDbusMessages( - final SignalServiceEnvelope envelope, final SignalServiceContent content - ) throws DBusException { - if (envelope.isReceipt()) { - conn.sendMessage(new Signal.ReceiptReceived(objectPath, envelope.getTimestamp(), - // A receipt envelope always has a source address - getLegacyIdentifier(envelope.getSourceAddress()))); - conn.sendMessage(new Signal.ReceiptReceivedV2(objectPath, envelope.getTimestamp(), - // A receipt envelope always has a source address - getLegacyIdentifier(envelope.getSourceAddress()), "delivery", Map.of())); - } else if (content != null) { - final var sender = !envelope.isUnidentifiedSender() && envelope.hasSourceUuid() - ? envelope.getSourceAddress() - : content.getSender(); - final var senderString = getLegacyIdentifier(sender); - if (content.getReceiptMessage().isPresent()) { - final var receiptMessage = content.getReceiptMessage().get(); - final var type = switch (receiptMessage.getType()) { - case READ -> "read"; - case VIEWED -> "viewed"; - case DELIVERY -> "delivery"; - case UNKNOWN -> "unknown"; - }; - for (long timestamp : receiptMessage.getTimestamps()) { - conn.sendMessage(new Signal.ReceiptReceived(objectPath, timestamp, senderString)); - conn.sendMessage(new Signal.ReceiptReceivedV2(objectPath, - envelope.getTimestamp(), - senderString, - type, - Map.of())); - } - - } else if (content.getDataMessage().isPresent()) { - var message = content.getDataMessage().get(); - - var groupId = getGroupId(message); - if (!message.isEndSession() && ( - groupId == null - || message.getGroupContext().get().getGroupV1Type() == null - || message.getGroupContext().get().getGroupV1Type() == SignalServiceGroup.Type.DELIVER - )) { - conn.sendMessage(new Signal.MessageReceived(objectPath, - message.getTimestamp(), + private void sendDbusMessages(MessageEnvelope envelope) throws DBusException { + final var senderString = envelope.sourceAddress().map(RecipientAddress::getLegacyIdentifier).orElse(""); + if (envelope.receipt().isPresent()) { + final var receiptMessage = envelope.receipt().get(); + final var type = switch (receiptMessage.type()) { + case READ -> "read"; + case VIEWED -> "viewed"; + case DELIVERY -> "delivery"; + case UNKNOWN -> "unknown"; + }; + for (long timestamp : receiptMessage.timestamps()) { + conn.sendMessage(new Signal.ReceiptReceived(objectPath, timestamp, senderString)); + conn.sendMessage(new Signal.ReceiptReceivedV2(objectPath, timestamp, senderString, type, Map.of())); + } + } + if (envelope.data().isPresent()) { + var message = envelope.data().get(); + + var groupId = message.groupContext() + .map(MessageEnvelope.Data.GroupContext::groupId) + .map(GroupId::serialize) + .orElseGet(() -> new byte[0]); + var isGroupUpdate = message.groupContext() + .map(MessageEnvelope.Data.GroupContext::isGroupUpdate) + .orElse(false); + if (!message.isEndSession() && !isGroupUpdate) { + conn.sendMessage(new Signal.MessageReceived(objectPath, + message.timestamp(), + senderString, + groupId, + message.body().orElse(""), + getAttachments(message))); + conn.sendMessage(new Signal.MessageReceivedV2(objectPath, + message.timestamp(), + senderString, + groupId, + message.body().orElse(""), + getMessageExtras(message))); + } + } + if (envelope.sync().isPresent()) { + var syncMessage = envelope.sync().get(); + if (syncMessage.sent().isPresent()) { + var transcript = syncMessage.sent().get(); + + if (transcript.destination().isPresent() || transcript.message().groupContext().isPresent()) { + var message = transcript.message(); + var groupId = message.groupContext() + .map(MessageEnvelope.Data.GroupContext::groupId) + .map(GroupId::serialize) + .orElseGet(() -> new byte[0]); + + conn.sendMessage(new Signal.SyncMessageReceived(objectPath, + transcript.message().timestamp(), senderString, - groupId != null ? groupId : new byte[0], - message.getBody().or(""), + transcript.destination().map(RecipientAddress::getLegacyIdentifier).orElse(""), + groupId, + message.body().orElse(""), getAttachments(message))); - conn.sendMessage(new Signal.MessageReceivedV2(objectPath, - message.getTimestamp(), + conn.sendMessage(new Signal.SyncMessageReceivedV2(objectPath, + transcript.message().timestamp(), senderString, - groupId != null ? groupId : new byte[0], - message.getBody().or(""), + transcript.destination().map(RecipientAddress::getLegacyIdentifier).orElse(""), + groupId, + message.body().orElse(""), getMessageExtras(message))); } - } else if (content.getSyncMessage().isPresent()) { - var sync_message = content.getSyncMessage().get(); - if (sync_message.getSent().isPresent()) { - var transcript = sync_message.getSent().get(); - - if (transcript.getDestination().isPresent() || transcript.getMessage() - .getGroupContext() - .isPresent()) { - var message = transcript.getMessage(); - var groupId = getGroupId(message); - - conn.sendMessage(new Signal.SyncMessageReceived(objectPath, - transcript.getTimestamp(), - senderString, - transcript.getDestination().transform(Util::getLegacyIdentifier).or(""), - groupId != null ? groupId : new byte[0], - message.getBody().or(""), - getAttachments(message))); - conn.sendMessage(new Signal.SyncMessageReceivedV2(objectPath, - transcript.getTimestamp(), - senderString, - transcript.getDestination().transform(Util::getLegacyIdentifier).or(""), - groupId != null ? groupId : new byte[0], - message.getBody().or(""), - getMessageExtras(message))); - } - } } } - } - private byte[] getGroupId(final SignalServiceDataMessage message) { - return message.getGroupContext().isPresent() ? GroupUtils.getGroupId(message.getGroupContext().get()) - .serialize() : null; } - private List getAttachments(SignalServiceDataMessage message) { + private List getAttachments(MessageEnvelope.Data message) { var attachments = new ArrayList(); - if (message.getAttachments().isPresent()) { - for (var attachment : message.getAttachments().get()) { - if (attachment.isPointer()) { - attachments.add(m.getAttachmentFile(attachment.asPointer().getRemoteId()).getAbsolutePath()); + if (message.attachments().size() > 0) { + for (var attachment : message.attachments()) { + if (attachment.id().isPresent()) { + attachments.add(m.getAttachmentFile(attachment.id().get()).getAbsolutePath()); } } } return attachments; } - private HashMap> getMessageExtras(SignalServiceDataMessage message) { + private HashMap> getMessageExtras(MessageEnvelope.Data message) { var extras = new HashMap>(); - if (message.getAttachments().isPresent()) { - var attachments = message.getAttachments() - .get() + if (message.attachments().size() > 0) { + var attachments = message.attachments() .stream() - .filter(SignalServiceAttachment::isPointer) + .filter(a -> a.id().isPresent()) .map(a -> getAttachmentMap(m, a)) .collect(Collectors.toList()); extras.put("attachments", new Variant<>(attachments, "aa{sv}")); } - if (message.getMentions().isPresent()) { - var mentions = message.getMentions() - .get() - .stream() - .map(mention -> getMentionMap(m, mention)) - .collect(Collectors.toList()); + if (message.mentions().size() > 0) { + var mentions = message.mentions().stream().map(this::getMentionMap).collect(Collectors.toList()); extras.put("mentions", new Variant<>(mentions, "aa{sv}")); } - extras.put("expiresInSeconds", new Variant<>(message.getExpiresInSeconds())); - if (message.getQuote().isPresent()) { - extras.put("quote", new Variant<>(getQuoteMap(message.getQuote().get()), "a{sv}")); + extras.put("expiresInSeconds", new Variant<>(message.expiresInSeconds())); + if (message.quote().isPresent()) { + extras.put("quote", new Variant<>(getQuoteMap(message.quote().get()), "a{sv}")); } - if (message.getReaction().isPresent()) { - final var reaction = message.getReaction().get(); + if (message.reaction().isPresent()) { + final var reaction = message.reaction().get(); extras.put("reaction", new Variant<>(getReactionMap(reaction), "a{sv}")); } - if (message.getRemoteDelete().isPresent()) { + if (message.remoteDeleteId().isPresent()) { extras.put("remoteDelete", - new Variant<>(Map.of("timestamp", new Variant<>(message.getRemoteDelete())), "a{sv}")); + new Variant<>(Map.of("timestamp", new Variant<>(message.remoteDeleteId())), "a{sv}")); } - if (message.getSticker().isPresent()) { - final var sticker = message.getSticker().get(); + if (message.sticker().isPresent()) { + final var sticker = message.sticker().get(); extras.put("sticker", new Variant<>(getStickerMap(sticker), "a{sv}")); } extras.put("isViewOnce", new Variant<>(message.isViewOnce())); return extras; } - private Map> getQuoteMap(final SignalServiceDataMessage.Quote quote) { + private Map> getQuoteMap(final MessageEnvelope.Data.Quote quote) { return Map.of("id", - new Variant<>(quote.getId()), + new Variant<>(quote.id()), "author", - new Variant<>(getLegacyIdentifier(m.resolveSignalServiceAddress(quote.getAuthor()))), + new Variant<>(quote.author().getLegacyIdentifier()), "text", - new Variant<>(quote.getText())); + new Variant<>(quote.text())); } - private Map> getStickerMap(final SignalServiceDataMessage.Sticker sticker) { - return Map.of("packId", new Variant<>(sticker.getPackId()), "stickerId", new Variant<>(sticker.getStickerId())); + private Map> getStickerMap(final MessageEnvelope.Data.Sticker sticker) { + return Map.of("packId", new Variant<>(sticker.packId()), "stickerId", new Variant<>(sticker.stickerId())); } - private Map> getReactionMap(final SignalServiceDataMessage.Reaction reaction) { + private Map> getReactionMap(final MessageEnvelope.Data.Reaction reaction) { return Map.of("emoji", - new Variant<>(reaction.getEmoji()), + new Variant<>(reaction.emoji()), "targetAuthor", - new Variant<>(getLegacyIdentifier(m.resolveSignalServiceAddress(reaction.getTargetAuthor()))), + new Variant<>(reaction.targetAuthor().getLegacyIdentifier()), "targetSentTimestamp", - new Variant<>(reaction.getTargetSentTimestamp()), + new Variant<>(reaction.targetSentTimestamp()), "isRemove", new Variant<>(reaction.isRemove())); } - private Map> getAttachmentMap(final Manager m, final SignalServiceAttachment attachment) { - final var a = attachment.asPointer(); + private Map> getAttachmentMap( + final Manager m, final MessageEnvelope.Data.Attachment a + ) { final var map = new HashMap>(); - map.put("file", new Variant<>(m.getAttachmentFile(a.getRemoteId()).getAbsolutePath())); - map.put("remoteId", new Variant<>(a.getRemoteId().toString())); - map.put("isVoiceNote", new Variant<>(a.getVoiceNote())); + if (a.id().isPresent()) { + map.put("file", new Variant<>(m.getAttachmentFile(a.id().get()).getAbsolutePath())); + map.put("remoteId", new Variant<>(a.id().get())); + } + map.put("isVoiceNote", new Variant<>(a.isVoiceNote())); map.put("isBorderless", new Variant<>(a.isBorderless())); map.put("isGif", new Variant<>(a.isGif())); - if (a.getCaption().isPresent()) { - map.put("caption", new Variant<>(a.getCaption().get())); + if (a.caption().isPresent()) { + map.put("caption", new Variant<>(a.caption().get())); + } + if (a.fileName().isPresent()) { + map.put("fileName", new Variant<>(a.fileName().get())); } - if (a.getFileName().isPresent()) { - map.put("fileName", new Variant<>(a.getFileName().get())); + if (a.size().isPresent()) { + map.put("size", new Variant<>(a.size().get())); } - if (a.getSize().isPresent()) { - map.put("size", new Variant<>(a.getSize().get())); + if (a.width().isPresent()) { + map.put("width", new Variant<>(a.width().get())); } - if (a.getWidth() > 0 || a.getHeight() > 0) { - map.put("height", new Variant<>(a.getHeight())); - map.put("width", new Variant<>(a.getWidth())); + if (a.height().isPresent()) { + map.put("height", new Variant<>(a.height().get())); } return map; } private Map> getMentionMap( - final Manager m, final SignalServiceDataMessage.Mention mention + final MessageEnvelope.Data.Mention mention ) { return Map.of("recipient", - new Variant<>(getLegacyIdentifier(m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid())))), + new Variant<>(mention.recipient().getLegacyIdentifier()), "start", - new Variant<>(mention.getStart()), + new Variant<>(mention.start()), "length", - new Variant<>(mention.getLength())); + new Variant<>(mention.length())); } } diff --git a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java index 96f4acb5..1135e89a 100644 --- a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java @@ -3,10 +3,9 @@ package org.asamk.signal; import org.asamk.signal.json.JsonError; import org.asamk.signal.json.JsonMessageEnvelope; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.api.MessageEnvelope; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import java.util.HashMap; @@ -23,14 +22,14 @@ public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler } @Override - public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { + public void handleMessage(MessageEnvelope envelope, Throwable exception) { final var object = new HashMap(); if (exception != null) { object.put("error", JsonError.from(exception)); } if (envelope != null) { - object.put("envelope", JsonMessageEnvelope.from(envelope, content, exception, m)); + object.put("envelope", JsonMessageEnvelope.from(envelope, exception, m)); } jsonWriter.write(object); diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 200466ae..0b1ea5be 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -2,30 +2,17 @@ package org.asamk.signal; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.UntrustedIdentityException; +import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.groups.GroupId; -import org.asamk.signal.manager.groups.GroupUtils; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.util.DateUtils; import org.slf4j.helpers.MessageFormatter; -import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext; -import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; -import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.ArrayList; import java.util.Base64; import java.util.stream.Collectors; -import static org.asamk.signal.util.Util.getLegacyIdentifier; - public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { final Manager m; @@ -37,132 +24,104 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } @Override - public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { - if (envelope.hasSourceUuid()) { - var source = envelope.getSourceAddress(); - writer.println("Envelope from: {} (device: {})", formatContact(source), envelope.getSourceDevice()); - } else { - writer.println("Envelope from: unknown source"); - } - writer.println("Timestamp: {}", DateUtils.formatTimestamp(envelope.getTimestamp())); + public void handleMessage(MessageEnvelope envelope, Throwable exception) { + var source = envelope.sourceAddress(); + writer.println("Envelope from: {} (device: {})", + source.map(this::formatContact).orElse("unknown source"), + envelope.sourceDevice()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(envelope.timestamp())); + writer.println("Server timestamps: received: {} delivered: {}", + DateUtils.formatTimestamp(envelope.serverReceivedTimestamp()), + DateUtils.formatTimestamp(envelope.serverDeliveredTimestamp())); if (envelope.isUnidentifiedSender()) { writer.println("Sent by unidentified/sealed sender"); } - if (envelope.isReceipt()) { - writer.println("Got receipt."); - } else if (envelope.isSignalMessage() || envelope.isPreKeySignalMessage() || envelope.isUnidentifiedSender()) { - if (exception != null) { - if (exception instanceof UntrustedIdentityException e) { - writer.println( - "The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message."); - final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender())); - writer.println( - "Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted", - m.getSelfNumber(), - recipientName, - m.getSelfNumber(), - recipientName); - writer.println( - "If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification", - m.getSelfNumber(), - recipientName); - } else { - writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName()); - } - } - if (content == null) { - writer.println("No message content"); + if (exception != null) { + if (exception instanceof UntrustedIdentityException e) { + writer.println( + "The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message."); + final var recipientName = e.getSender().getLegacyIdentifier(); + writer.println( + "Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted", + m.getSelfNumber(), + recipientName, + m.getSelfNumber(), + recipientName); + writer.println( + "If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification", + m.getSelfNumber(), + recipientName); } else { - writer.println("Sender: {} (device: {})", - formatContact(content.getSender()), - content.getSenderDevice()); - writer.println("Server timestamps: received: {} delivered: {}", - DateUtils.formatTimestamp(content.getServerReceivedTimestamp()), - DateUtils.formatTimestamp(content.getServerDeliveredTimestamp())); - - if (content.getSenderKeyDistributionMessage().isPresent()) { - final var message = content.getSenderKeyDistributionMessage().get(); - writer.println("Received a sender key distribution message for distributionId {}", - message.getDistributionId()); - } - - if (content.getDataMessage().isPresent()) { - var message = content.getDataMessage().get(); - printDataMessage(writer, message); - } - if (content.getSyncMessage().isPresent()) { - writer.println("Received a sync message"); - var syncMessage = content.getSyncMessage().get(); - printSyncMessage(writer, syncMessage); - } - - if (content.getCallMessage().isPresent()) { - writer.println("Received a call message"); - var callMessage = content.getCallMessage().get(); - printCallMessage(writer.indentedWriter(), callMessage); - } - if (content.getReceiptMessage().isPresent()) { - writer.println("Received a receipt message"); - var receiptMessage = content.getReceiptMessage().get(); - printReceiptMessage(writer.indentedWriter(), receiptMessage); - } - if (content.getTypingMessage().isPresent()) { - writer.println("Received a typing message"); - var typingMessage = content.getTypingMessage().get(); - printTypingMessage(writer.indentedWriter(), typingMessage); - } - if (content.getDecryptionErrorMessage().isPresent()) { - writer.println("Received a decryption error message (resend request)"); - var decryptionErrorMessage = content.getDecryptionErrorMessage().get(); - printDecryptionErrorMessage(writer.indentedWriter(), decryptionErrorMessage); - } + writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName()); } - } else { - writer.println("Unknown message received."); + } + + if (envelope.data().isPresent()) { + var message = envelope.data().get(); + printDataMessage(writer, message); + } + if (envelope.sync().isPresent()) { + writer.println("Received a sync message"); + var syncMessage = envelope.sync().get(); + printSyncMessage(writer, syncMessage); + } + if (envelope.call().isPresent()) { + writer.println("Received a call message"); + var callMessage = envelope.call().get(); + printCallMessage(writer.indentedWriter(), callMessage); + } + if (envelope.receipt().isPresent()) { + writer.println("Received a receipt message"); + var receiptMessage = envelope.receipt().get(); + printReceiptMessage(writer.indentedWriter(), receiptMessage); + } + if (envelope.typing().isPresent()) { + writer.println("Received a typing message"); + var typingMessage = envelope.typing().get(); + printTypingMessage(writer.indentedWriter(), typingMessage); } writer.println(); } private void printDataMessage( - PlainTextWriter writer, SignalServiceDataMessage message + PlainTextWriter writer, MessageEnvelope.Data message ) { - writer.println("Message timestamp: {}", DateUtils.formatTimestamp(message.getTimestamp())); + writer.println("Message timestamp: {}", DateUtils.formatTimestamp(message.timestamp())); if (message.isViewOnce()) { writer.println("=VIEW ONCE="); } - if (message.getBody().isPresent()) { - writer.println("Body: {}", message.getBody().get()); + if (message.body().isPresent()) { + writer.println("Body: {}", message.body().get()); } - if (message.getGroupContext().isPresent()) { + if (message.groupContext().isPresent()) { writer.println("Group info:"); - final var groupContext = message.getGroupContext().get(); + final var groupContext = message.groupContext().get(); printGroupContext(writer.indentedWriter(), groupContext); } - if (message.getGroupCallUpdate().isPresent()) { + if (message.groupCallUpdate().isPresent()) { writer.println("Group call update:"); - final var groupCallUpdate = message.getGroupCallUpdate().get(); - writer.indentedWriter().println("Era id: {}", groupCallUpdate.getEraId()); + final var groupCallUpdate = message.groupCallUpdate().get(); + writer.indentedWriter().println("Era id: {}", groupCallUpdate.eraId()); } - if (message.getPreviews().isPresent()) { + if (message.previews().size() > 0) { writer.println("Previews:"); - final var previews = message.getPreviews().get(); + final var previews = message.previews(); for (var preview : previews) { writer.println("- Preview"); printPreview(writer.indentedWriter(), preview); } } - if (message.getSharedContacts().isPresent()) { - final var sharedContacts = message.getSharedContacts().get(); + if (message.sharedContacts().size() > 0) { writer.println("Contacts:"); - for (var contact : sharedContacts) { + for (var contact : message.sharedContacts()) { writer.println("- Contact:"); printSharedContact(writer.indentedWriter(), contact); } } - if (message.getSticker().isPresent()) { - final var sticker = message.getSticker().get(); + if (message.sticker().isPresent()) { + final var sticker = message.sticker().get(); writer.println("Sticker:"); printSticker(writer.indentedWriter(), sticker); } @@ -170,37 +129,37 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { writer.println("Is end session"); } if (message.isExpirationUpdate()) { - writer.println("Is Expiration update: {}", message.isExpirationUpdate()); + writer.println("Is Expiration update: true"); } - if (message.getExpiresInSeconds() > 0) { - writer.println("Expires in: {} seconds", message.getExpiresInSeconds()); + if (message.expiresInSeconds() > 0) { + writer.println("Expires in: {} seconds", message.expiresInSeconds()); } - if (message.getProfileKey().isPresent()) { - writer.println("Profile key update, key length: {}", message.getProfileKey().get().length); + if (message.hasProfileKey()) { + writer.println("Profile key update"); } - if (message.getReaction().isPresent()) { + if (message.reaction().isPresent()) { writer.println("Reaction:"); - final var reaction = message.getReaction().get(); + final var reaction = message.reaction().get(); printReaction(writer.indentedWriter(), reaction); } - if (message.getQuote().isPresent()) { + if (message.quote().isPresent()) { writer.println("Quote:"); - var quote = message.getQuote().get(); + var quote = message.quote().get(); printQuote(writer.indentedWriter(), quote); } - if (message.getRemoteDelete().isPresent()) { - final var remoteDelete = message.getRemoteDelete().get(); - writer.println("Remote delete message: timestamp = {}", remoteDelete.getTargetSentTimestamp()); + if (message.remoteDeleteId().isPresent()) { + final var remoteDelete = message.remoteDeleteId().get(); + writer.println("Remote delete message: timestamp = {}", remoteDelete); } - if (message.getMentions().isPresent()) { + if (message.mentions().size() > 0) { writer.println("Mentions:"); - for (var mention : message.getMentions().get()) { + for (var mention : message.mentions()) { printMention(writer, mention); } } - if (message.getAttachments().isPresent()) { + if (message.attachments().size() > 0) { writer.println("Attachments:"); - for (var attachment : message.getAttachments().get()) { + for (var attachment : message.attachments()) { writer.println("- Attachment:"); printAttachment(writer.indentedWriter(), attachment); } @@ -208,144 +167,116 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } private void printTypingMessage( - final PlainTextWriter writer, final SignalServiceTypingMessage typingMessage + final PlainTextWriter writer, final MessageEnvelope.Typing typingMessage ) { - writer.println("Action: {}", typingMessage.getAction()); - writer.println("Timestamp: {}", DateUtils.formatTimestamp(typingMessage.getTimestamp())); - if (typingMessage.getGroupId().isPresent()) { + writer.println("Action: {}", typingMessage.type()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(typingMessage.timestamp())); + if (typingMessage.groupId().isPresent()) { writer.println("Group Info:"); - final var groupId = GroupId.unknownVersion(typingMessage.getGroupId().get()); + final var groupId = typingMessage.groupId().get(); printGroupInfo(writer.indentedWriter(), groupId); } } - private void printDecryptionErrorMessage( - final PlainTextWriter writer, final DecryptionErrorMessage decryptionErrorMessage - ) { - writer.println("Device id: {}", decryptionErrorMessage.getDeviceId()); - writer.println("Timestamp: {}", DateUtils.formatTimestamp(decryptionErrorMessage.getTimestamp())); - writer.println("Ratchet key: {}", - decryptionErrorMessage.getRatchetKey().isPresent() ? "is present" : "not present"); - } - private void printReceiptMessage( - final PlainTextWriter writer, final SignalServiceReceiptMessage receiptMessage + final PlainTextWriter writer, final MessageEnvelope.Receipt receiptMessage ) { - writer.println("When: {}", DateUtils.formatTimestamp(receiptMessage.getWhen())); - if (receiptMessage.isDeliveryReceipt()) { + writer.println("When: {}", DateUtils.formatTimestamp(receiptMessage.when())); + if (receiptMessage.type() == MessageEnvelope.Receipt.Type.DELIVERY) { writer.println("Is delivery receipt"); } - if (receiptMessage.isReadReceipt()) { + if (receiptMessage.type() == MessageEnvelope.Receipt.Type.READ) { writer.println("Is read receipt"); } - if (receiptMessage.isViewedReceipt()) { + if (receiptMessage.type() == MessageEnvelope.Receipt.Type.VIEWED) { writer.println("Is viewed receipt"); } writer.println("Timestamps:"); - for (long timestamp : receiptMessage.getTimestamps()) { + for (long timestamp : receiptMessage.timestamps()) { writer.println("- {}", DateUtils.formatTimestamp(timestamp)); } } private void printCallMessage( - final PlainTextWriter writer, final SignalServiceCallMessage callMessage + final PlainTextWriter writer, final MessageEnvelope.Call callMessage ) { - if (callMessage.getDestinationDeviceId().isPresent()) { - final var deviceId = callMessage.getDestinationDeviceId().get(); + if (callMessage.destinationDeviceId().isPresent()) { + final var deviceId = callMessage.destinationDeviceId().get(); writer.println("Destination device id: {}", deviceId); } - if (callMessage.getGroupId().isPresent()) { - final var groupId = GroupId.unknownVersion(callMessage.getGroupId().get()); + if (callMessage.groupId().isPresent()) { + final var groupId = callMessage.groupId().get(); writer.println("Destination group id: {}", groupId); } - if (callMessage.getTimestamp().isPresent()) { - writer.println("Timestamp: {}", DateUtils.formatTimestamp(callMessage.getTimestamp().get())); + if (callMessage.timestamp().isPresent()) { + writer.println("Timestamp: {}", DateUtils.formatTimestamp(callMessage.timestamp().get())); } - if (callMessage.getAnswerMessage().isPresent()) { - var answerMessage = callMessage.getAnswerMessage().get(); - writer.println("Answer message: {}, sdp: {})", answerMessage.getId(), answerMessage.getSdp()); + if (callMessage.answer().isPresent()) { + var answerMessage = callMessage.answer().get(); + writer.println("Answer message: {}, sdp: {})", answerMessage.id(), answerMessage.sdp()); } - if (callMessage.getBusyMessage().isPresent()) { - var busyMessage = callMessage.getBusyMessage().get(); - writer.println("Busy message: {}", busyMessage.getId()); + if (callMessage.busy().isPresent()) { + var busyMessage = callMessage.busy().get(); + writer.println("Busy message: {}", busyMessage.id()); } - if (callMessage.getHangupMessage().isPresent()) { - var hangupMessage = callMessage.getHangupMessage().get(); - writer.println("Hangup message: {}", hangupMessage.getId()); + if (callMessage.hangup().isPresent()) { + var hangupMessage = callMessage.hangup().get(); + writer.println("Hangup message: {}", hangupMessage.id()); } - if (callMessage.getIceUpdateMessages().isPresent()) { + if (callMessage.iceUpdate().size() > 0) { writer.println("Ice update messages:"); - var iceUpdateMessages = callMessage.getIceUpdateMessages().get(); + var iceUpdateMessages = callMessage.iceUpdate(); for (var iceUpdateMessage : iceUpdateMessages) { - writer.println("- {}, sdp: {}", iceUpdateMessage.getId(), iceUpdateMessage.getSdp()); + writer.println("- {}, sdp: {}", iceUpdateMessage.id(), iceUpdateMessage.sdp()); } } - if (callMessage.getOfferMessage().isPresent()) { - var offerMessage = callMessage.getOfferMessage().get(); - writer.println("Offer message: {}, sdp: {}", offerMessage.getId(), offerMessage.getSdp()); + if (callMessage.offer().isPresent()) { + var offerMessage = callMessage.offer().get(); + writer.println("Offer message: {}, sdp: {}", offerMessage.id(), offerMessage.sdp()); } - if (callMessage.getOpaqueMessage().isPresent()) { - final var opaqueMessage = callMessage.getOpaqueMessage().get(); + if (callMessage.opaque().isPresent()) { + final var opaqueMessage = callMessage.opaque().get(); writer.println("Opaque message: size {}, urgency: {}", - opaqueMessage.getOpaque().length, - opaqueMessage.getUrgency().name()); + opaqueMessage.opaque().length, + opaqueMessage.urgency().name()); } } private void printSyncMessage( - final PlainTextWriter writer, final SignalServiceSyncMessage syncMessage + final PlainTextWriter writer, final MessageEnvelope.Sync syncMessage ) { - if (syncMessage.getContacts().isPresent()) { - final var contactsMessage = syncMessage.getContacts().get(); + if (syncMessage.contacts().isPresent()) { + final var contactsMessage = syncMessage.contacts().get(); var type = contactsMessage.isComplete() ? "complete" : "partial"; writer.println("Received {} sync contacts:", type); - printAttachment(writer.indentedWriter(), contactsMessage.getContactsStream()); } - if (syncMessage.getGroups().isPresent()) { - writer.println("Received sync groups:"); - printAttachment(writer.indentedWriter(), syncMessage.getGroups().get()); + if (syncMessage.groups().isPresent()) { + writer.println("Received sync groups."); } - if (syncMessage.getRead().isPresent()) { + if (syncMessage.read().size() > 0) { writer.println("Received sync read messages list"); - for (var rm : syncMessage.getRead().get()) { + for (var rm : syncMessage.read()) { writer.println("- From: {} Message timestamp: {}", - formatContact(rm.getSender()), - DateUtils.formatTimestamp(rm.getTimestamp())); + formatContact(rm.sender()), + DateUtils.formatTimestamp(rm.timestamp())); } } - if (syncMessage.getViewed().isPresent()) { + if (syncMessage.viewed().size() > 0) { writer.println("Received sync viewed messages list"); - for (var vm : syncMessage.getViewed().get()) { + for (var vm : syncMessage.viewed()) { writer.println("- From: {} Message timestamp: {}", - formatContact(vm.getSender()), - DateUtils.formatTimestamp(vm.getTimestamp())); + formatContact(vm.sender()), + DateUtils.formatTimestamp(vm.timestamp())); } } - if (syncMessage.getRequest().isPresent()) { - String type; - if (syncMessage.getRequest().get().isContactsRequest()) { - type = "contacts"; - } else if (syncMessage.getRequest().get().isGroupsRequest()) { - type = "groups"; - } else if (syncMessage.getRequest().get().isBlockedListRequest()) { - type = "blocked list"; - } else if (syncMessage.getRequest().get().isConfigurationRequest()) { - type = "configuration"; - } else if (syncMessage.getRequest().get().isKeysRequest()) { - type = "keys"; - } else { - type = ""; - } - writer.println("Received sync request for: {}", type); - } - if (syncMessage.getSent().isPresent()) { + if (syncMessage.sent().isPresent()) { writer.println("Received sync sent message"); - final var sentTranscriptMessage = syncMessage.getSent().get(); + final var sentTranscriptMessage = syncMessage.sent().get(); String to; - if (sentTranscriptMessage.getDestination().isPresent()) { - to = formatContact(sentTranscriptMessage.getDestination().get()); - } else if (sentTranscriptMessage.getRecipients().size() > 0) { - to = sentTranscriptMessage.getRecipients() + if (sentTranscriptMessage.destination().isPresent()) { + to = formatContact(sentTranscriptMessage.destination().get()); + } else if (sentTranscriptMessage.recipients().size() > 0) { + to = sentTranscriptMessage.recipients() .stream() .map(this::formatContact) .collect(Collectors.joining(", ")); @@ -354,257 +285,195 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } writer.indentedWriter().println("To: {}", to); writer.indentedWriter() - .println("Timestamp: {}", DateUtils.formatTimestamp(sentTranscriptMessage.getTimestamp())); - if (sentTranscriptMessage.getExpirationStartTimestamp() > 0) { + .println("Timestamp: {}", DateUtils.formatTimestamp(sentTranscriptMessage.timestamp())); + if (sentTranscriptMessage.expirationStartTimestamp() > 0) { writer.indentedWriter() .println("Expiration started at: {}", - DateUtils.formatTimestamp(sentTranscriptMessage.getExpirationStartTimestamp())); + DateUtils.formatTimestamp(sentTranscriptMessage.expirationStartTimestamp())); } - var message = sentTranscriptMessage.getMessage(); + var message = sentTranscriptMessage.message(); printDataMessage(writer.indentedWriter(), message); } - if (syncMessage.getBlockedList().isPresent()) { + if (syncMessage.blocked().isPresent()) { writer.println("Received sync message with block list"); - writer.println("Blocked numbers:"); - final var blockedList = syncMessage.getBlockedList().get(); - for (var address : blockedList.getAddresses()) { - writer.println("- {}", getLegacyIdentifier(address)); - } - } - if (syncMessage.getVerified().isPresent()) { - writer.println("Received sync message with verified identities:"); - final var verifiedMessage = syncMessage.getVerified().get(); - writer.println("- {}: {}", formatContact(verifiedMessage.getDestination()), verifiedMessage.getVerified()); - } - if (syncMessage.getConfiguration().isPresent()) { - writer.println("Received sync message with configuration:"); - final var configurationMessage = syncMessage.getConfiguration().get(); - if (configurationMessage.getReadReceipts().isPresent()) { - writer.println("- Read receipts: {}", - configurationMessage.getReadReceipts().get() ? "enabled" : "disabled"); - } - if (configurationMessage.getLinkPreviews().isPresent()) { - writer.println("- Link previews: {}", - configurationMessage.getLinkPreviews().get() ? "enabled" : "disabled"); + writer.println("Blocked:"); + final var blockedList = syncMessage.blocked().get(); + for (var address : blockedList.recipients()) { + writer.println("- {}", address.getLegacyIdentifier()); } - if (configurationMessage.getTypingIndicators().isPresent()) { - writer.println("- Typing indicators: {}", - configurationMessage.getTypingIndicators().get() ? "enabled" : "disabled"); + for (var groupId : blockedList.groupIds()) { + writer.println("- {}", groupId); } - if (configurationMessage.getUnidentifiedDeliveryIndicators().isPresent()) { - writer.println("- Unidentified Delivery Indicators: {}", - configurationMessage.getUnidentifiedDeliveryIndicators().get() ? "enabled" : "disabled"); - } - } - if (syncMessage.getFetchType().isPresent()) { - final var fetchType = syncMessage.getFetchType().get(); - writer.println("Received sync message with fetch type: {}", fetchType); } - if (syncMessage.getViewOnceOpen().isPresent()) { - final var viewOnceOpenMessage = syncMessage.getViewOnceOpen().get(); + if (syncMessage.viewOnceOpen().isPresent()) { + final var viewOnceOpenMessage = syncMessage.viewOnceOpen().get(); writer.println("Received sync message with view once open message:"); - writer.indentedWriter().println("Sender: {}", formatContact(viewOnceOpenMessage.getSender())); + writer.indentedWriter().println("Sender: {}", formatContact(viewOnceOpenMessage.sender())); writer.indentedWriter() - .println("Timestamp: {}", DateUtils.formatTimestamp(viewOnceOpenMessage.getTimestamp())); - } - if (syncMessage.getStickerPackOperations().isPresent()) { - final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get(); - writer.println("Received sync message with sticker pack operations:"); - for (var m : stickerPackOperationMessages) { - writer.println("- {}", m.getType().isPresent() ? m.getType().get() : ""); - if (m.getPackId().isPresent()) { - writer.indentedWriter() - .println("packId: {}", Base64.getEncoder().encodeToString(m.getPackId().get())); - } - if (m.getPackKey().isPresent()) { - writer.indentedWriter() - .println("packKey: {}", Base64.getEncoder().encodeToString(m.getPackKey().get())); - } - } + .println("Timestamp: {}", DateUtils.formatTimestamp(viewOnceOpenMessage.timestamp())); } - if (syncMessage.getMessageRequestResponse().isPresent()) { - final var requestResponseMessage = syncMessage.getMessageRequestResponse().get(); + if (syncMessage.messageRequestResponse().isPresent()) { + final var requestResponseMessage = syncMessage.messageRequestResponse().get(); writer.println("Received message request response:"); - writer.indentedWriter().println("Type: {}", requestResponseMessage.getType()); - if (requestResponseMessage.getGroupId().isPresent()) { + writer.indentedWriter().println("Type: {}", requestResponseMessage.type()); + if (requestResponseMessage.groupId().isPresent()) { writer.println("For group:"); - printGroupInfo(writer.indentedWriter(), - GroupId.unknownVersion(requestResponseMessage.getGroupId().get())); + printGroupInfo(writer.indentedWriter(), requestResponseMessage.groupId().get()); } - if (requestResponseMessage.getPerson().isPresent()) { - writer.indentedWriter() - .println("For Person: {}", formatContact(requestResponseMessage.getPerson().get())); - } - } - if (syncMessage.getKeys().isPresent()) { - final var keysMessage = syncMessage.getKeys().get(); - writer.println("Received sync message with keys:"); - if (keysMessage.getStorageService().isPresent()) { - writer.println("- storage key: length: {}", keysMessage.getStorageService().get().serialize().length); + if (requestResponseMessage.person().isPresent()) { + writer.indentedWriter().println("For Person: {}", formatContact(requestResponseMessage.person().get())); } } } private void printPreview( - final PlainTextWriter writer, final SignalServiceDataMessage.Preview preview + final PlainTextWriter writer, final MessageEnvelope.Data.Preview preview ) { - writer.println("Title: {}", preview.getTitle()); - writer.println("Description: {}", preview.getDescription()); - writer.println("Date: {}", DateUtils.formatTimestamp(preview.getDate())); - writer.println("Url: {}", preview.getUrl()); - if (preview.getImage().isPresent()) { + writer.println("Title: {}", preview.title()); + writer.println("Description: {}", preview.description()); + writer.println("Date: {}", DateUtils.formatTimestamp(preview.date())); + writer.println("Url: {}", preview.url()); + if (preview.image().isPresent()) { writer.println("Image:"); - printAttachment(writer.indentedWriter(), preview.getImage().get()); + printAttachment(writer.indentedWriter(), preview.image().get()); } } private void printSticker( - final PlainTextWriter writer, final SignalServiceDataMessage.Sticker sticker + final PlainTextWriter writer, final MessageEnvelope.Data.Sticker sticker ) { - writer.println("Pack id: {}", Base64.getEncoder().encodeToString(sticker.getPackId())); - writer.println("Pack key: {}", Base64.getEncoder().encodeToString(sticker.getPackKey())); - writer.println("Sticker id: {}", sticker.getStickerId()); - writer.println("Image:"); - printAttachment(writer.indentedWriter(), sticker.getAttachment()); + writer.println("Pack id: {}", Base64.getEncoder().encodeToString(sticker.packId())); + writer.println("Pack key: {}", Base64.getEncoder().encodeToString(sticker.packKey())); + writer.println("Sticker id: {}", sticker.stickerId()); } private void printReaction( - final PlainTextWriter writer, final SignalServiceDataMessage.Reaction reaction + final PlainTextWriter writer, final MessageEnvelope.Data.Reaction reaction ) { - writer.println("Emoji: {}", reaction.getEmoji()); - writer.println("Target author: {}", formatContact(reaction.getTargetAuthor())); - writer.println("Target timestamp: {}", DateUtils.formatTimestamp(reaction.getTargetSentTimestamp())); + writer.println("Emoji: {}", reaction.emoji()); + writer.println("Target author: {}", formatContact(reaction.targetAuthor())); + writer.println("Target timestamp: {}", DateUtils.formatTimestamp(reaction.targetSentTimestamp())); writer.println("Is remove: {}", reaction.isRemove()); } private void printQuote( - final PlainTextWriter writer, final SignalServiceDataMessage.Quote quote + final PlainTextWriter writer, final MessageEnvelope.Data.Quote quote ) { - writer.println("Id: {}", quote.getId()); - writer.println("Author: {}", formatContact(quote.getAuthor())); - writer.println("Text: {}", quote.getText()); - if (quote.getMentions() != null && quote.getMentions().size() > 0) { + writer.println("Id: {}", quote.id()); + writer.println("Author: {}", formatContact(quote.author())); + writer.println("Text: {}", quote.text()); + if (quote.mentions() != null && quote.mentions().size() > 0) { writer.println("Mentions:"); - for (var mention : quote.getMentions()) { + for (var mention : quote.mentions()) { printMention(writer, mention); } } - if (quote.getAttachments().size() > 0) { + if (quote.attachments().size() > 0) { writer.println("Attachments:"); - for (var attachment : quote.getAttachments()) { - writer.println("- Filename: {}", attachment.getFileName()); + for (var attachment : quote.attachments()) { + writer.println("- Filename: {}", attachment.fileName()); writer.indent(w -> { - w.println("Type: {}", attachment.getContentType()); + w.println("Type: {}", attachment.contentType()); w.println("Thumbnail:"); - if (attachment.getThumbnail() != null) { - printAttachment(w, attachment.getThumbnail()); + if (attachment.thumbnail().isPresent()) { + printAttachment(w, attachment.thumbnail().get()); } }); } } } - private void printSharedContact(final PlainTextWriter writer, final SharedContact contact) { + private void printSharedContact(final PlainTextWriter writer, final MessageEnvelope.Data.SharedContact contact) { writer.println("Name:"); - var name = contact.getName(); + var name = contact.name(); writer.indent(w -> { - if (name.getDisplay().isPresent() && !name.getDisplay().get().isBlank()) { - w.println("Display name: {}", name.getDisplay().get()); + if (name.display().isPresent() && !name.display().get().isBlank()) { + w.println("Display name: {}", name.display().get()); } - if (name.getGiven().isPresent() && !name.getGiven().get().isBlank()) { - w.println("First name: {}", name.getGiven().get()); + if (name.given().isPresent() && !name.given().get().isBlank()) { + w.println("First name: {}", name.given().get()); } - if (name.getMiddle().isPresent() && !name.getMiddle().get().isBlank()) { - w.println("Middle name: {}", name.getMiddle().get()); + if (name.middle().isPresent() && !name.middle().get().isBlank()) { + w.println("Middle name: {}", name.middle().get()); } - if (name.getFamily().isPresent() && !name.getFamily().get().isBlank()) { - w.println("Family name: {}", name.getFamily().get()); + if (name.family().isPresent() && !name.family().get().isBlank()) { + w.println("Family name: {}", name.family().get()); } - if (name.getPrefix().isPresent() && !name.getPrefix().get().isBlank()) { - w.println("Prefix name: {}", name.getPrefix().get()); + if (name.prefix().isPresent() && !name.prefix().get().isBlank()) { + w.println("Prefix name: {}", name.prefix().get()); } - if (name.getSuffix().isPresent() && !name.getSuffix().get().isBlank()) { - w.println("Suffix name: {}", name.getSuffix().get()); + if (name.suffix().isPresent() && !name.suffix().get().isBlank()) { + w.println("Suffix name: {}", name.suffix().get()); } }); - if (contact.getAvatar().isPresent()) { - var avatar = contact.getAvatar().get(); + if (contact.avatar().isPresent()) { + var avatar = contact.avatar().get(); writer.println("Avatar: (profile: {})", avatar.isProfile()); - printAttachment(writer.indentedWriter(), avatar.getAttachment()); + printAttachment(writer.indentedWriter(), avatar.attachment()); } - if (contact.getOrganization().isPresent()) { - writer.println("Organisation: {}", contact.getOrganization().get()); + if (contact.organization().isPresent()) { + writer.println("Organisation: {}", contact.organization().get()); } - if (contact.getPhone().isPresent()) { + if (contact.phone().size() > 0) { writer.println("Phone details:"); - for (var phone : contact.getPhone().get()) { + for (var phone : contact.phone()) { writer.println("- Phone:"); writer.indent(w -> { - if (phone.getValue() != null) { - w.println("Number: {}", phone.getValue()); - } - if (phone.getType() != null) { - w.println("Type: {}", phone.getType()); - } - if (phone.getLabel().isPresent() && !phone.getLabel().get().isBlank()) { - w.println("Label: {}", phone.getLabel().get()); + w.println("Number: {}", phone.value()); + w.println("Type: {}", phone.type()); + if (phone.label().isPresent() && !phone.label().get().isBlank()) { + w.println("Label: {}", phone.label().get()); } }); } } - if (contact.getEmail().isPresent()) { + if (contact.email().size() > 0) { writer.println("Email details:"); - for (var email : contact.getEmail().get()) { + for (var email : contact.email()) { writer.println("- Email:"); writer.indent(w -> { - if (email.getValue() != null) { - w.println("Address: {}", email.getValue()); - } - if (email.getType() != null) { - w.println("Type: {}", email.getType()); - } - if (email.getLabel().isPresent() && !email.getLabel().get().isBlank()) { - w.println("Label: {}", email.getLabel().get()); + w.println("Address: {}", email.value()); + w.println("Type: {}", email.type()); + if (email.label().isPresent() && !email.label().get().isBlank()) { + w.println("Label: {}", email.label().get()); } }); } } - if (contact.getAddress().isPresent()) { + if (contact.address().size() > 0) { writer.println("Address details:"); - for (var address : contact.getAddress().get()) { + for (var address : contact.address()) { writer.println("- Address:"); writer.indent(w -> { - if (address.getType() != null) { - w.println("Type: {}", address.getType()); - } - if (address.getLabel().isPresent() && !address.getLabel().get().isBlank()) { - w.println("Label: {}", address.getLabel().get()); + w.println("Type: {}", address.type()); + if (address.label().isPresent() && !address.label().get().isBlank()) { + w.println("Label: {}", address.label().get()); } - if (address.getStreet().isPresent() && !address.getStreet().get().isBlank()) { - w.println("Street: {}", address.getStreet().get()); + if (address.street().isPresent() && !address.street().get().isBlank()) { + w.println("Street: {}", address.street().get()); } - if (address.getPobox().isPresent() && !address.getPobox().get().isBlank()) { - w.println("Pobox: {}", address.getPobox().get()); + if (address.pobox().isPresent() && !address.pobox().get().isBlank()) { + w.println("Pobox: {}", address.pobox().get()); } - if (address.getNeighborhood().isPresent() && !address.getNeighborhood().get().isBlank()) { - w.println("Neighbourhood: {}", address.getNeighborhood().get()); + if (address.neighborhood().isPresent() && !address.neighborhood().get().isBlank()) { + w.println("Neighbourhood: {}", address.neighborhood().get()); } - if (address.getCity().isPresent() && !address.getCity().get().isBlank()) { - w.println("City: {}", address.getCity().get()); + if (address.city().isPresent() && !address.city().get().isBlank()) { + w.println("City: {}", address.city().get()); } - if (address.getRegion().isPresent() && !address.getRegion().get().isBlank()) { - w.println("Region: {}", address.getRegion().get()); + if (address.region().isPresent() && !address.region().get().isBlank()) { + w.println("Region: {}", address.region().get()); } - if (address.getPostcode().isPresent() && !address.getPostcode().get().isBlank()) { - w.println("Postcode: {}", address.getPostcode().get()); + if (address.postcode().isPresent() && !address.postcode().get().isBlank()) { + w.println("Postcode: {}", address.postcode().get()); } - if (address.getCountry().isPresent() && !address.getCountry().get().isBlank()) { - w.println("Country: {}", address.getCountry().get()); + if (address.country().isPresent() && !address.country().get().isBlank()) { + w.println("Country: {}", address.country().get()); } }); } @@ -612,30 +481,11 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } private void printGroupContext( - final PlainTextWriter writer, final SignalServiceGroupContext groupContext + final PlainTextWriter writer, final MessageEnvelope.Data.GroupContext groupContext ) { - final var groupId = GroupUtils.getGroupId(groupContext); - if (groupContext.getGroupV1().isPresent()) { - var groupInfo = groupContext.getGroupV1().get(); - printGroupInfo(writer, groupId); - writer.println("Type: {}", groupInfo.getType()); - if (groupInfo.getMembers().isPresent()) { - writer.println("Members:"); - for (var member : groupInfo.getMembers().get()) { - writer.println("- {}", formatContact(member)); - } - } - if (groupInfo.getAvatar().isPresent()) { - writer.println("Avatar:"); - printAttachment(writer.indentedWriter(), groupInfo.getAvatar().get()); - } - } else if (groupContext.getGroupV2().isPresent()) { - final var groupInfo = groupContext.getGroupV2().get(); - printGroupInfo(writer, groupId); - writer.println("Revision: {}", groupInfo.getRevision()); - writer.println("Master key length: {}", groupInfo.getMasterKey().serialize().length); - writer.println("Has signed group change: {}", groupInfo.hasSignedGroupChange()); - } + printGroupInfo(writer, groupContext.groupId()); + writer.println("Revision: {}", groupContext.revision()); + writer.println("Type: {}", groupContext.isGroupUpdate() ? "UPDATE" : "DELIVER"); } private void printGroupInfo(final PlainTextWriter writer, final GroupId groupId) { @@ -650,58 +500,59 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } private void printMention( - PlainTextWriter writer, SignalServiceDataMessage.Mention mention + PlainTextWriter writer, MessageEnvelope.Data.Mention mention ) { - final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid())); - writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength()); + writer.println("- {}: {} (length: {})", formatContact(mention.recipient()), mention.start(), mention.length()); } - private void printAttachment(PlainTextWriter writer, SignalServiceAttachment attachment) { - writer.println("Content-Type: {}", attachment.getContentType()); - writer.println("Type: {}", attachment.isPointer() ? "Pointer" : attachment.isStream() ? "Stream" : ""); - if (attachment.isPointer()) { - final var pointer = attachment.asPointer(); - writer.println("Id: {} Key length: {}", pointer.getRemoteId(), pointer.getKey().length); - if (pointer.getUploadTimestamp() > 0) { - writer.println("Upload timestamp: {}", DateUtils.formatTimestamp(pointer.getUploadTimestamp())); - } - if (pointer.getCaption().isPresent()) { - writer.println("Caption: {}", pointer.getCaption().get()); - } - if (pointer.getFileName().isPresent()) { - writer.println("Filename: {}", pointer.getFileName().get()); - } + private void printAttachment(PlainTextWriter writer, MessageEnvelope.Data.Attachment attachment) { + writer.println("Content-Type: {}", attachment.contentType()); + writer.println("Type: {}", attachment.id().isPresent() ? "Pointer" : "Stream"); + if (attachment.id().isPresent()) { + writer.println("Id: {}", attachment.id().get()); + } + if (attachment.uploadTimestamp().isPresent()) { + writer.println("Upload timestamp: {}", DateUtils.formatTimestamp(attachment.uploadTimestamp().get())); + } + if (attachment.caption().isPresent()) { + writer.println("Caption: {}", attachment.caption().get()); + } + if (attachment.fileName().isPresent()) { + writer.println("Filename: {}", attachment.fileName().get()); + } + if (attachment.size().isPresent() || attachment.preview().isPresent()) { writer.println("Size: {}{}", - pointer.getSize().isPresent() ? pointer.getSize().get() + " bytes" : "", - pointer.getPreview().isPresent() ? " (Preview is available: " - + pointer.getPreview().get().length + attachment.size().isPresent() ? attachment.size().get() + " bytes" : "", + attachment.preview().isPresent() ? " (Preview is available: " + + attachment.preview().get().length + " bytes)" : ""); - final var flags = new ArrayList(); - if (pointer.getVoiceNote()) { - flags.add("voice note"); - } - if (pointer.isBorderless()) { - flags.add("borderless"); - } - if (pointer.isGif()) { - flags.add("video gif"); - } - if (flags.size() > 0) { - writer.println("Flags: {}", String.join(", ", flags)); - } - if (pointer.getWidth() > 0 || pointer.getHeight() > 0) { - writer.println("Dimensions: {}x{}", pointer.getWidth(), pointer.getHeight()); - } - var file = m.getAttachmentFile(pointer.getRemoteId()); + } + final var flags = new ArrayList(); + if (attachment.isVoiceNote()) { + flags.add("voice note"); + } + if (attachment.isBorderless()) { + flags.add("borderless"); + } + if (attachment.isGif()) { + flags.add("video gif"); + } + if (flags.size() > 0) { + writer.println("Flags: {}", String.join(", ", flags)); + } + if (attachment.width().isPresent() || attachment.height().isPresent()) { + writer.println("Dimensions: {}x{}", attachment.width().orElse(0), attachment.height().orElse(0)); + } + if (attachment.id().isPresent()) { + var file = m.getAttachmentFile(attachment.id().get()); if (file.exists()) { writer.println("Stored plaintext in: {}", file); } } } - private String formatContact(SignalServiceAddress address) { - address = m.resolveSignalServiceAddress(address); - final var number = getLegacyIdentifier(address); + private String formatContact(RecipientAddress address) { + final var number = address.getLegacyIdentifier(); final var name = m.getContactOrProfileName(RecipientIdentifier.Single.fromAddress(address)); if (name == null || name.isEmpty()) { return number; diff --git a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java index 6f6fac2f..8c9e6b4c 100644 --- a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java @@ -14,7 +14,6 @@ import org.asamk.signal.util.Hex; import org.asamk.signal.util.Util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.Base64; import java.util.List; @@ -29,15 +28,13 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand { return "listIdentities"; } - private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, Identity theirId) { - final SignalServiceAddress address = theirId.recipient().toSignalServiceAddress(); - var digits = Util.formatSafetyNumber(theirId.safetyNumber()); + private static void printIdentityFingerprint(PlainTextWriter writer, Identity theirId) { writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}", - address.getNumber().orNull(), + theirId.recipient().getNumber().orElse(null), theirId.trustLevel(), theirId.dateAdded(), Hex.toString(theirId.getFingerprint()), - digits); + Util.formatSafetyNumber(theirId.safetyNumber())); } @Override @@ -61,7 +58,7 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand { if (outputWriter instanceof PlainTextWriter writer) { for (var id : identities) { - printIdentityFingerprint(writer, m, id); + printIdentityFingerprint(writer, id); } } else { final var writer = (JsonWriter) outputWriter; diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index f2cc4cbb..0d87cdb5 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -33,8 +33,6 @@ import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.interfaces.DBusInterface; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; import org.whispersystems.signalservice.api.util.UuidUtil; @@ -459,7 +457,7 @@ public class DbusManagerImpl implements Manager { } @Override - public File getAttachmentFile(final SignalServiceAttachmentRemoteId attachmentId) { + public File getAttachmentFile(final String attachmentId) { throw new UnsupportedOperationException(); } @@ -551,11 +549,6 @@ public class DbusManagerImpl implements Manager { throw new UnsupportedOperationException(); } - @Override - public SignalServiceAddress resolveSignalServiceAddress(final SignalServiceAddress address) { - return address; - } - @Override public void close() throws IOException { } diff --git a/src/main/java/org/asamk/signal/json/JsonAttachment.java b/src/main/java/org/asamk/signal/json/JsonAttachment.java index b78722d1..ad46a567 100644 --- a/src/main/java/org/asamk/signal/json/JsonAttachment.java +++ b/src/main/java/org/asamk/signal/json/JsonAttachment.java @@ -1,22 +1,14 @@ package org.asamk.signal.json; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; +import org.asamk.signal.manager.api.MessageEnvelope; record JsonAttachment(String contentType, String filename, String id, Long size) { - static JsonAttachment from(SignalServiceAttachment attachment) { - if (attachment.isPointer()) { - final var pointer = attachment.asPointer(); - final var id = pointer.getRemoteId().toString(); - final var filename = pointer.getFileName().orNull(); - final var size = pointer.getSize().transform(Integer::longValue).orNull(); - return new JsonAttachment(attachment.getContentType(), filename, id, size); - } else { - final var stream = attachment.asStream(); - final var filename = stream.getFileName().orNull(); - final var size = stream.getLength(); - return new JsonAttachment(attachment.getContentType(), filename, null, size); - } + static JsonAttachment from(MessageEnvelope.Data.Attachment attachment) { + final var id = attachment.id().orElse(null); + final var filename = attachment.fileName().orElse(null); + final var size = attachment.size().orElse(null); + return new JsonAttachment(attachment.contentType(), filename, id, size); } static JsonAttachment from(String filename) { diff --git a/src/main/java/org/asamk/signal/json/JsonCallMessage.java b/src/main/java/org/asamk/signal/json/JsonCallMessage.java index e30aeafa..a1a5d8df 100644 --- a/src/main/java/org/asamk/signal/json/JsonCallMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonCallMessage.java @@ -2,28 +2,65 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonInclude; -import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; -import org.whispersystems.signalservice.api.messages.calls.BusyMessage; -import org.whispersystems.signalservice.api.messages.calls.HangupMessage; -import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage; -import org.whispersystems.signalservice.api.messages.calls.OfferMessage; -import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; +import org.asamk.signal.manager.api.MessageEnvelope; +import java.util.Base64; import java.util.List; +import java.util.stream.Collectors; record JsonCallMessage( - @JsonInclude(JsonInclude.Include.NON_NULL) OfferMessage offerMessage, - @JsonInclude(JsonInclude.Include.NON_NULL) AnswerMessage answerMessage, - @JsonInclude(JsonInclude.Include.NON_NULL) BusyMessage busyMessage, - @JsonInclude(JsonInclude.Include.NON_NULL) HangupMessage hangupMessage, - @JsonInclude(JsonInclude.Include.NON_NULL) List iceUpdateMessages + @JsonInclude(JsonInclude.Include.NON_NULL) Offer offerMessage, + @JsonInclude(JsonInclude.Include.NON_NULL) Answer answerMessage, + @JsonInclude(JsonInclude.Include.NON_NULL) Busy busyMessage, + @JsonInclude(JsonInclude.Include.NON_NULL) Hangup hangupMessage, + @JsonInclude(JsonInclude.Include.NON_EMPTY) List iceUpdateMessages ) { - static JsonCallMessage from(SignalServiceCallMessage callMessage) { - return new JsonCallMessage(callMessage.getOfferMessage().orNull(), - callMessage.getAnswerMessage().orNull(), - callMessage.getBusyMessage().orNull(), - callMessage.getHangupMessage().orNull(), - callMessage.getIceUpdateMessages().orNull()); + static JsonCallMessage from(MessageEnvelope.Call callMessage) { + return new JsonCallMessage(callMessage.offer().map(Offer::from).orElse(null), + callMessage.answer().map(Answer::from).orElse(null), + callMessage.busy().map(Busy::from).orElse(null), + callMessage.hangup().map(Hangup::from).orElse(null), + callMessage.iceUpdate().stream().map(IceUpdate::from).collect(Collectors.toList())); + } + + record Offer(long id, String sdp, String type, String opaque) { + + public static Offer from(final MessageEnvelope.Call.Offer offer) { + return new Offer(offer.id(), + offer.sdp(), + offer.type().name(), + Base64.getEncoder().encodeToString(offer.opaque())); + } + } + + public record Answer(long id, String sdp, String opaque) { + + public static Answer from(final MessageEnvelope.Call.Answer answer) { + return new Answer(answer.id(), answer.sdp(), Base64.getEncoder().encodeToString(answer.opaque())); + } + } + + public record Busy(long id) { + + public static Busy from(final MessageEnvelope.Call.Busy busy) { + return new Busy(busy.id()); + } + } + + public record Hangup(long id, String type, int deviceId, boolean isLegacy) { + + public static Hangup from(final MessageEnvelope.Call.Hangup hangup) { + return new Hangup(hangup.id(), hangup.type().name(), hangup.deviceId(), hangup.isLegacy()); + } + } + + public record IceUpdate(long id, String sdp, String opaque) { + + public static IceUpdate from(final MessageEnvelope.Call.IceUpdate iceUpdate) { + return new IceUpdate(iceUpdate.id(), + iceUpdate.sdp(), + Base64.getEncoder().encodeToString(iceUpdate.opaque())); + } } } diff --git a/src/main/java/org/asamk/signal/json/JsonContactAddress.java b/src/main/java/org/asamk/signal/json/JsonContactAddress.java index 6ffd355b..7184f532 100644 --- a/src/main/java/org/asamk/signal/json/JsonContactAddress.java +++ b/src/main/java/org/asamk/signal/json/JsonContactAddress.java @@ -1,10 +1,10 @@ package org.asamk.signal.json; +import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.util.Util; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; public record JsonContactAddress( - SharedContact.PostalAddress.Type type, + String type, String label, String street, String pobox, @@ -15,15 +15,15 @@ public record JsonContactAddress( String country ) { - static JsonContactAddress from(SharedContact.PostalAddress address) { - return new JsonContactAddress(address.getType(), - Util.getStringIfNotBlank(address.getLabel()), - Util.getStringIfNotBlank(address.getStreet()), - Util.getStringIfNotBlank(address.getPobox()), - Util.getStringIfNotBlank(address.getNeighborhood()), - Util.getStringIfNotBlank(address.getCity()), - Util.getStringIfNotBlank(address.getRegion()), - Util.getStringIfNotBlank(address.getPostcode()), - Util.getStringIfNotBlank(address.getCountry())); + static JsonContactAddress from(MessageEnvelope.Data.SharedContact.Address address) { + return new JsonContactAddress(address.type().name(), + Util.getStringIfNotBlank(address.label()), + Util.getStringIfNotBlank(address.street()), + Util.getStringIfNotBlank(address.pobox()), + Util.getStringIfNotBlank(address.neighborhood()), + Util.getStringIfNotBlank(address.city()), + Util.getStringIfNotBlank(address.region()), + Util.getStringIfNotBlank(address.postcode()), + Util.getStringIfNotBlank(address.country())); } } diff --git a/src/main/java/org/asamk/signal/json/JsonContactAvatar.java b/src/main/java/org/asamk/signal/json/JsonContactAvatar.java index 1bf53d04..56b1a4e3 100644 --- a/src/main/java/org/asamk/signal/json/JsonContactAvatar.java +++ b/src/main/java/org/asamk/signal/json/JsonContactAvatar.java @@ -1,10 +1,10 @@ package org.asamk.signal.json; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; +import org.asamk.signal.manager.api.MessageEnvelope; public record JsonContactAvatar(JsonAttachment attachment, boolean isProfile) { - static JsonContactAvatar from(SharedContact.Avatar avatar) { - return new JsonContactAvatar(JsonAttachment.from(avatar.getAttachment()), avatar.isProfile()); + static JsonContactAvatar from(MessageEnvelope.Data.SharedContact.Avatar avatar) { + return new JsonContactAvatar(JsonAttachment.from(avatar.attachment()), avatar.isProfile()); } } diff --git a/src/main/java/org/asamk/signal/json/JsonContactEmail.java b/src/main/java/org/asamk/signal/json/JsonContactEmail.java index ea6014c1..7db600db 100644 --- a/src/main/java/org/asamk/signal/json/JsonContactEmail.java +++ b/src/main/java/org/asamk/signal/json/JsonContactEmail.java @@ -1,11 +1,11 @@ package org.asamk.signal.json; +import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.util.Util; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -public record JsonContactEmail(String value, SharedContact.Email.Type type, String label) { +public record JsonContactEmail(String value, String type, String label) { - static JsonContactEmail from(SharedContact.Email email) { - return new JsonContactEmail(email.getValue(), email.getType(), Util.getStringIfNotBlank(email.getLabel())); + static JsonContactEmail from(MessageEnvelope.Data.SharedContact.Email email) { + return new JsonContactEmail(email.value(), email.type().name(), Util.getStringIfNotBlank(email.label())); } } diff --git a/src/main/java/org/asamk/signal/json/JsonContactName.java b/src/main/java/org/asamk/signal/json/JsonContactName.java index 9c7af32b..fdf11298 100644 --- a/src/main/java/org/asamk/signal/json/JsonContactName.java +++ b/src/main/java/org/asamk/signal/json/JsonContactName.java @@ -1,18 +1,18 @@ package org.asamk.signal.json; +import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.util.Util; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; public record JsonContactName( String display, String given, String family, String prefix, String suffix, String middle ) { - static JsonContactName from(SharedContact.Name name) { - return new JsonContactName(Util.getStringIfNotBlank(name.getDisplay()), - Util.getStringIfNotBlank(name.getGiven()), - Util.getStringIfNotBlank(name.getFamily()), - Util.getStringIfNotBlank(name.getPrefix()), - Util.getStringIfNotBlank(name.getSuffix()), - Util.getStringIfNotBlank(name.getMiddle())); + static JsonContactName from(MessageEnvelope.Data.SharedContact.Name name) { + return new JsonContactName(Util.getStringIfNotBlank(name.display()), + Util.getStringIfNotBlank(name.given()), + Util.getStringIfNotBlank(name.family()), + Util.getStringIfNotBlank(name.prefix()), + Util.getStringIfNotBlank(name.suffix()), + Util.getStringIfNotBlank(name.middle())); } } diff --git a/src/main/java/org/asamk/signal/json/JsonContactPhone.java b/src/main/java/org/asamk/signal/json/JsonContactPhone.java index 6c23339e..5da482ff 100644 --- a/src/main/java/org/asamk/signal/json/JsonContactPhone.java +++ b/src/main/java/org/asamk/signal/json/JsonContactPhone.java @@ -1,11 +1,11 @@ package org.asamk.signal.json; +import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.util.Util; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -public record JsonContactPhone(String value, SharedContact.Phone.Type type, String label) { +public record JsonContactPhone(String value, String type, String label) { - static JsonContactPhone from(SharedContact.Phone phone) { - return new JsonContactPhone(phone.getValue(), phone.getType(), Util.getStringIfNotBlank(phone.getLabel())); + static JsonContactPhone from(MessageEnvelope.Data.SharedContact.Phone phone) { + return new JsonContactPhone(phone.value(), phone.type().name(), Util.getStringIfNotBlank(phone.label())); } } diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index 1e898175..a3c2d7f2 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -3,8 +3,7 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonInclude; import org.asamk.Signal; -import org.asamk.signal.manager.Manager; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; +import org.asamk.signal.manager.api.MessageEnvelope; import java.util.List; import java.util.stream.Collectors; @@ -24,66 +23,32 @@ record JsonDataMessage( @JsonInclude(JsonInclude.Include.NON_NULL) JsonGroupInfo groupInfo ) { - static JsonDataMessage from(SignalServiceDataMessage dataMessage, Manager m) { - final var timestamp = dataMessage.getTimestamp(); - final JsonGroupInfo groupInfo; - if (dataMessage.getGroupContext().isPresent()) { - final var groupContext = dataMessage.getGroupContext().get(); - if (groupContext.getGroupV1().isPresent()) { - var group = groupContext.getGroupV1().get(); - groupInfo = JsonGroupInfo.from(group); - } else if (groupContext.getGroupV2().isPresent()) { - var group = groupContext.getGroupV2().get(); - groupInfo = JsonGroupInfo.from(group); - } else { - groupInfo = null; - } - } else { - groupInfo = null; - } - final var message = dataMessage.getBody().orNull(); - final var expiresInSeconds = dataMessage.getExpiresInSeconds(); + static JsonDataMessage from(MessageEnvelope.Data dataMessage) { + final var timestamp = dataMessage.timestamp(); + final var groupInfo = dataMessage.groupContext().isPresent() ? JsonGroupInfo.from(dataMessage.groupContext() + .get()) : null; + final var message = dataMessage.body().orElse(null); + final var expiresInSeconds = dataMessage.expiresInSeconds(); final var viewOnce = dataMessage.isViewOnce(); - final var reaction = dataMessage.getReaction().isPresent() ? JsonReaction.from(dataMessage.getReaction().get(), - m) : null; - final var quote = dataMessage.getQuote().isPresent() ? JsonQuote.from(dataMessage.getQuote().get(), m) : null; - final List mentions; - if (dataMessage.getMentions().isPresent()) { - mentions = dataMessage.getMentions() - .get() - .stream() - .map(mention -> JsonMention.from(mention, m)) - .collect(Collectors.toList()); - } else { - mentions = List.of(); - } - final var remoteDelete = dataMessage.getRemoteDelete().isPresent() - ? JsonRemoteDelete.from(dataMessage.getRemoteDelete().get()) - : null; - final List attachments; - if (dataMessage.getAttachments().isPresent()) { - attachments = dataMessage.getAttachments() - .get() - .stream() - .map(JsonAttachment::from) - .collect(Collectors.toList()); - } else { - attachments = List.of(); - } - final var sticker = dataMessage.getSticker().isPresent() - ? JsonSticker.from(dataMessage.getSticker().get()) + final var reaction = dataMessage.reaction().map(JsonReaction::from).orElse(null); + final var quote = dataMessage.quote().isPresent() ? JsonQuote.from(dataMessage.quote().get()) : null; + final var mentions = dataMessage.mentions().size() > 0 ? dataMessage.mentions() + .stream() + .map(JsonMention::from) + .collect(Collectors.toList()) : null; + final var remoteDelete = dataMessage.remoteDeleteId().isPresent() + ? new JsonRemoteDelete(dataMessage.remoteDeleteId().get()) : null; + final var attachments = dataMessage.attachments().size() > 0 ? dataMessage.attachments() + .stream() + .map(JsonAttachment::from) + .collect(Collectors.toList()) : null; + final var sticker = dataMessage.sticker().isPresent() ? JsonSticker.from(dataMessage.sticker().get()) : null; - final List contacts; - if (dataMessage.getSharedContacts().isPresent()) { - contacts = dataMessage.getSharedContacts() - .get() - .stream() - .map(JsonSharedContact::from) - .collect(Collectors.toList()); - } else { - contacts = List.of(); - } + final var contacts = dataMessage.sharedContacts().size() > 0 ? dataMessage.sharedContacts() + .stream() + .map(JsonSharedContact::from) + .collect(Collectors.toList()) : null; return new JsonDataMessage(timestamp, message, expiresInSeconds, diff --git a/src/main/java/org/asamk/signal/json/JsonGroupInfo.java b/src/main/java/org/asamk/signal/json/JsonGroupInfo.java index 2c1a5b80..f00ace99 100644 --- a/src/main/java/org/asamk/signal/json/JsonGroupInfo.java +++ b/src/main/java/org/asamk/signal/json/JsonGroupInfo.java @@ -1,42 +1,17 @@ package org.asamk.signal.json; -import com.fasterxml.jackson.annotation.JsonInclude; - -import org.asamk.signal.manager.groups.GroupUtils; -import org.asamk.signal.util.Util; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; +import org.asamk.signal.manager.api.MessageEnvelope; import java.util.Base64; -import java.util.List; -import java.util.stream.Collectors; - -record JsonGroupInfo( - String groupId, - String type, - @JsonInclude(JsonInclude.Include.NON_NULL) String name, - @JsonInclude(JsonInclude.Include.NON_NULL) List members -) { - static JsonGroupInfo from(SignalServiceGroup groupInfo) { - return new JsonGroupInfo(Base64.getEncoder().encodeToString(groupInfo.getGroupId()), - groupInfo.getType().toString(), - groupInfo.getName().orNull(), - groupInfo.getMembers().isPresent() ? groupInfo.getMembers() - .get() - .stream() - .map(Util::getLegacyIdentifier) - .collect(Collectors.toList()) : null); - } +record JsonGroupInfo(String groupId, String type) { - static JsonGroupInfo from(SignalServiceGroupV2 groupInfo) { - return new JsonGroupInfo(GroupUtils.getGroupIdV2(groupInfo.getMasterKey()).toBase64(), - groupInfo.hasSignedGroupChange() ? "UPDATE" : "DELIVER", - null, - null); + static JsonGroupInfo from(MessageEnvelope.Data.GroupContext groupContext) { + return new JsonGroupInfo(groupContext.groupId().toBase64(), + groupContext.isGroupUpdate() ? "UPDATE" : "DELIVER"); } static JsonGroupInfo from(byte[] groupId) { - return new JsonGroupInfo(Base64.getEncoder().encodeToString(groupId), "DELIVER", null, null); + return new JsonGroupInfo(Base64.getEncoder().encodeToString(groupId), "DELIVER"); } } diff --git a/src/main/java/org/asamk/signal/json/JsonMention.java b/src/main/java/org/asamk/signal/json/JsonMention.java index 66fcc1f1..d8001088 100644 --- a/src/main/java/org/asamk/signal/json/JsonMention.java +++ b/src/main/java/org/asamk/signal/json/JsonMention.java @@ -1,19 +1,17 @@ package org.asamk.signal.json; -import org.asamk.signal.manager.Manager; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.asamk.signal.manager.api.MessageEnvelope; -import static org.asamk.signal.util.Util.getLegacyIdentifier; +import java.util.UUID; public record JsonMention(@Deprecated String name, String number, String uuid, int start, int length) { - static JsonMention from(SignalServiceDataMessage.Mention mention, Manager m) { - final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid())); - return new JsonMention(getLegacyIdentifier(address), - address.getNumber().orNull(), - address.getUuid().toString(), - mention.getStart(), - mention.getLength()); + static JsonMention from(MessageEnvelope.Data.Mention mention) { + final var address = mention.recipient(); + return new JsonMention(address.getLegacyIdentifier(), + address.getNumber().orElse(null), + address.getUuid().map(UUID::toString).orElse(null), + mention.start(), + mention.length()); } } diff --git a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java index f5d70d28..04340d0f 100644 --- a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java +++ b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java @@ -5,14 +5,12 @@ import com.fasterxml.jackson.annotation.JsonInclude; import org.asamk.Signal; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.UntrustedIdentityException; +import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.RecipientIdentifier; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.util.List; - -import static org.asamk.signal.util.Util.getLegacyIdentifier; +import java.util.UUID; public record JsonMessageEnvelope( @Deprecated String source, @@ -29,29 +27,23 @@ public record JsonMessageEnvelope( ) { public static JsonMessageEnvelope from( - SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception, Manager m + MessageEnvelope envelope, Throwable exception, Manager m ) { final String source; final String sourceNumber; final String sourceUuid; final Integer sourceDevice; - if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) { - final var sourceAddress = m.resolveSignalServiceAddress(envelope.getSourceAddress()); - source = getLegacyIdentifier(sourceAddress); - sourceNumber = sourceAddress.getNumber().orNull(); - sourceUuid = sourceAddress.getUuid().toString(); - sourceDevice = envelope.getSourceDevice(); - } else if (envelope.isUnidentifiedSender() && content != null) { - final var sender = m.resolveSignalServiceAddress(content.getSender()); - source = getLegacyIdentifier(sender); - sourceNumber = sender.getNumber().orNull(); - sourceUuid = sender.getUuid().toString(); - sourceDevice = content.getSenderDevice(); + if (envelope.sourceAddress().isPresent()) { + final var sourceAddress = envelope.sourceAddress().get(); + source = sourceAddress.getLegacyIdentifier(); + sourceNumber = sourceAddress.getNumber().orElse(null); + sourceUuid = sourceAddress.getUuid().map(UUID::toString).orElse(null); + sourceDevice = envelope.sourceDevice(); } else if (exception instanceof UntrustedIdentityException e) { - final var sender = m.resolveSignalServiceAddress(e.getSender()); - source = getLegacyIdentifier(sender); - sourceNumber = sender.getNumber().orNull(); - sourceUuid = sender.getUuid().toString(); + final var sender = e.getSender(); + source = sender.getLegacyIdentifier(); + sourceNumber = sender.getNumber().orElse(null); + sourceUuid = sender.getUuid().map(UUID::toString).orElse(null); sourceDevice = e.getSenderDevice(); } else { source = null; @@ -66,27 +58,13 @@ public record JsonMessageEnvelope( name = null; } final var sourceName = name; - final var timestamp = envelope.getTimestamp(); - final JsonReceiptMessage receiptMessage; - if (envelope.isReceipt()) { - receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp)); - } else if (content != null && content.getReceiptMessage().isPresent()) { - receiptMessage = JsonReceiptMessage.from(content.getReceiptMessage().get()); - } else { - receiptMessage = null; - } - final var typingMessage = content != null && content.getTypingMessage().isPresent() ? JsonTypingMessage.from( - content.getTypingMessage().get()) : null; + final var timestamp = envelope.timestamp(); + final var receiptMessage = envelope.receipt().map(JsonReceiptMessage::from).orElse(null); + final var typingMessage = envelope.typing().map(JsonTypingMessage::from).orElse(null); - final var dataMessage = content != null && content.getDataMessage().isPresent() - ? JsonDataMessage.from(content.getDataMessage().get(), m) - : null; - final var syncMessage = content != null && content.getSyncMessage().isPresent() - ? JsonSyncMessage.from(content.getSyncMessage().get(), m) - : null; - final var callMessage = content != null && content.getCallMessage().isPresent() - ? JsonCallMessage.from(content.getCallMessage().get()) - : null; + final var dataMessage = envelope.data().map(JsonDataMessage::from).orElse(null); + final var syncMessage = envelope.sync().map(JsonSyncMessage::from).orElse(null); + final var callMessage = envelope.call().map(JsonCallMessage::from).orElse(null); return new JsonMessageEnvelope(source, sourceNumber, diff --git a/src/main/java/org/asamk/signal/json/JsonQuote.java b/src/main/java/org/asamk/signal/json/JsonQuote.java index f0d1831c..05dc8f41 100644 --- a/src/main/java/org/asamk/signal/json/JsonQuote.java +++ b/src/main/java/org/asamk/signal/json/JsonQuote.java @@ -2,15 +2,13 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonInclude; -import org.asamk.signal.manager.Manager; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; +import org.asamk.signal.manager.api.MessageEnvelope; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; -import static org.asamk.signal.util.Util.getLegacyIdentifier; - public record JsonQuote( long id, @Deprecated String author, @@ -21,30 +19,23 @@ public record JsonQuote( List attachments ) { - static JsonQuote from(SignalServiceDataMessage.Quote quote, Manager m) { - final var id = quote.getId(); - final var address = m.resolveSignalServiceAddress(quote.getAuthor()); - final var author = getLegacyIdentifier(address); - final var authorNumber = address.getNumber().orNull(); - final var authorUuid = address.getUuid().toString(); - final var text = quote.getText(); - - final List mentions; - if (quote.getMentions() != null && quote.getMentions().size() > 0) { - mentions = quote.getMentions() - .stream() - .map(quotedMention -> JsonMention.from(quotedMention, m)) - .collect(Collectors.toList()); - } else { - mentions = null; - } - - final List attachments; - if (quote.getAttachments().size() > 0) { - attachments = quote.getAttachments().stream().map(JsonQuotedAttachment::from).collect(Collectors.toList()); - } else { - attachments = new ArrayList<>(); - } + static JsonQuote from(MessageEnvelope.Data.Quote quote) { + final var id = quote.id(); + final var address = quote.author(); + final var author = address.getLegacyIdentifier(); + final var authorNumber = address.getNumber().orElse(null); + final var authorUuid = address.getUuid().map(UUID::toString).orElse(null); + final var text = quote.text().orElse(null); + + final var mentions = quote.mentions().size() > 0 ? quote.mentions() + .stream() + .map(JsonMention::from) + .collect(Collectors.toList()) : null; + + final var attachments = quote.attachments().size() > 0 ? quote.attachments() + .stream() + .map(JsonQuotedAttachment::from) + .collect(Collectors.toList()) : new ArrayList(); return new JsonQuote(id, author, authorNumber, authorUuid, text, mentions, attachments); } diff --git a/src/main/java/org/asamk/signal/json/JsonQuotedAttachment.java b/src/main/java/org/asamk/signal/json/JsonQuotedAttachment.java index a72ba7c6..e5022798 100644 --- a/src/main/java/org/asamk/signal/json/JsonQuotedAttachment.java +++ b/src/main/java/org/asamk/signal/json/JsonQuotedAttachment.java @@ -2,21 +2,18 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonInclude; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; +import org.asamk.signal.manager.api.MessageEnvelope; public record JsonQuotedAttachment( String contentType, String filename, @JsonInclude(JsonInclude.Include.NON_NULL) JsonAttachment thumbnail ) { - static JsonQuotedAttachment from(SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment) { - final var contentType = quotedAttachment.getContentType(); - final var filename = quotedAttachment.getFileName(); - final JsonAttachment thumbnail; - if (quotedAttachment.getThumbnail() != null) { - thumbnail = JsonAttachment.from(quotedAttachment.getThumbnail()); - } else { - thumbnail = null; - } + static JsonQuotedAttachment from(MessageEnvelope.Data.Attachment quotedAttachment) { + final var contentType = quotedAttachment.contentType(); + final var filename = quotedAttachment.fileName().orElse(null); + final var thumbnail = quotedAttachment.thumbnail().isPresent() + ? JsonAttachment.from(quotedAttachment.thumbnail().get()) + : null; return new JsonQuotedAttachment(contentType, filename, thumbnail); } } diff --git a/src/main/java/org/asamk/signal/json/JsonReaction.java b/src/main/java/org/asamk/signal/json/JsonReaction.java index 9dc64591..04037b6e 100644 --- a/src/main/java/org/asamk/signal/json/JsonReaction.java +++ b/src/main/java/org/asamk/signal/json/JsonReaction.java @@ -1,9 +1,8 @@ package org.asamk.signal.json; -import org.asamk.signal.manager.Manager; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction; +import org.asamk.signal.manager.api.MessageEnvelope; -import static org.asamk.signal.util.Util.getLegacyIdentifier; +import java.util.UUID; public record JsonReaction( String emoji, @@ -14,13 +13,13 @@ public record JsonReaction( boolean isRemove ) { - static JsonReaction from(Reaction reaction, Manager m) { - final var emoji = reaction.getEmoji(); - final var address = m.resolveSignalServiceAddress(reaction.getTargetAuthor()); - final var targetAuthor = getLegacyIdentifier(address); - final var targetAuthorNumber = address.getNumber().orNull(); - final var targetAuthorUuid = address.getUuid().toString(); - final var targetSentTimestamp = reaction.getTargetSentTimestamp(); + static JsonReaction from(MessageEnvelope.Data.Reaction reaction) { + final var emoji = reaction.emoji(); + final var address = reaction.targetAuthor(); + final var targetAuthor = address.getLegacyIdentifier(); + final var targetAuthorNumber = address.getNumber().orElse(null); + final var targetAuthorUuid = address.getUuid().map(UUID::toString).orElse(null); + final var targetSentTimestamp = reaction.targetSentTimestamp(); final var isRemove = reaction.isRemove(); return new JsonReaction(emoji, targetAuthor, diff --git a/src/main/java/org/asamk/signal/json/JsonReceiptMessage.java b/src/main/java/org/asamk/signal/json/JsonReceiptMessage.java index 7cdd0b6a..5616fdb4 100644 --- a/src/main/java/org/asamk/signal/json/JsonReceiptMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonReceiptMessage.java @@ -1,17 +1,17 @@ package org.asamk.signal.json; -import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; +import org.asamk.signal.manager.api.MessageEnvelope; import java.util.List; record JsonReceiptMessage(long when, boolean isDelivery, boolean isRead, boolean isViewed, List timestamps) { - static JsonReceiptMessage from(SignalServiceReceiptMessage receiptMessage) { - final var when = receiptMessage.getWhen(); - final var isDelivery = receiptMessage.isDeliveryReceipt(); - final var isRead = receiptMessage.isReadReceipt(); - final var isViewed = receiptMessage.isViewedReceipt(); - final var timestamps = receiptMessage.getTimestamps(); + static JsonReceiptMessage from(MessageEnvelope.Receipt receiptMessage) { + final var when = receiptMessage.when(); + final var isDelivery = receiptMessage.type() == MessageEnvelope.Receipt.Type.DELIVERY; + final var isRead = receiptMessage.type() == MessageEnvelope.Receipt.Type.READ; + final var isViewed = receiptMessage.type() == MessageEnvelope.Receipt.Type.VIEWED; + final var timestamps = receiptMessage.timestamps(); return new JsonReceiptMessage(when, isDelivery, isRead, isViewed, timestamps); } diff --git a/src/main/java/org/asamk/signal/json/JsonRemoteDelete.java b/src/main/java/org/asamk/signal/json/JsonRemoteDelete.java index 611d1141..5a5727e5 100644 --- a/src/main/java/org/asamk/signal/json/JsonRemoteDelete.java +++ b/src/main/java/org/asamk/signal/json/JsonRemoteDelete.java @@ -1,10 +1,3 @@ package org.asamk.signal.json; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; - -record JsonRemoteDelete(long timestamp) { - - static JsonRemoteDelete from(SignalServiceDataMessage.RemoteDelete remoteDelete) { - return new JsonRemoteDelete(remoteDelete.getTargetSentTimestamp()); - } -} +record JsonRemoteDelete(long timestamp) {} diff --git a/src/main/java/org/asamk/signal/json/JsonSharedContact.java b/src/main/java/org/asamk/signal/json/JsonSharedContact.java index f52be3eb..4c8e5eb5 100644 --- a/src/main/java/org/asamk/signal/json/JsonSharedContact.java +++ b/src/main/java/org/asamk/signal/json/JsonSharedContact.java @@ -2,43 +2,40 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonInclude; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; +import org.asamk.signal.manager.api.MessageEnvelope; import java.util.List; import java.util.stream.Collectors; public record JsonSharedContact( JsonContactName name, - JsonContactAvatar avatar, + @JsonInclude(JsonInclude.Include.NON_NULL) JsonContactAvatar avatar, @JsonInclude(JsonInclude.Include.NON_NULL) List phone, @JsonInclude(JsonInclude.Include.NON_NULL) List email, @JsonInclude(JsonInclude.Include.NON_NULL) List address, String organization ) { - static JsonSharedContact from(SharedContact contact) { - final var name = JsonContactName.from(contact.getName()); - final var avatar = contact.getAvatar().isPresent() ? JsonContactAvatar.from(contact.getAvatar().get()) : null; + static JsonSharedContact from(MessageEnvelope.Data.SharedContact contact) { + final var name = JsonContactName.from(contact.name()); + final var avatar = contact.avatar().isPresent() ? JsonContactAvatar.from(contact.avatar().get()) : null; - final var phone = contact.getPhone().isPresent() ? contact.getPhone() - .get() + final var phone = contact.phone().size() > 0 ? contact.phone() .stream() .map(JsonContactPhone::from) .collect(Collectors.toList()) : null; - final var email = contact.getEmail().isPresent() ? contact.getEmail() - .get() + final var email = contact.email().size() > 0 ? contact.email() .stream() .map(JsonContactEmail::from) .collect(Collectors.toList()) : null; - final var address = contact.getAddress().isPresent() ? contact.getAddress() - .get() + final var address = contact.address().size() > 0 ? contact.address() .stream() .map(JsonContactAddress::from) .collect(Collectors.toList()) : null; - final var organization = contact.getOrganization().orNull(); + final var organization = contact.organization().orElse(null); return new JsonSharedContact(name, avatar, phone, email, address, organization); } diff --git a/src/main/java/org/asamk/signal/json/JsonSticker.java b/src/main/java/org/asamk/signal/json/JsonSticker.java index 9278ee37..96435ad6 100644 --- a/src/main/java/org/asamk/signal/json/JsonSticker.java +++ b/src/main/java/org/asamk/signal/json/JsonSticker.java @@ -1,15 +1,16 @@ package org.asamk.signal.json; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; +import org.asamk.signal.manager.api.MessageEnvelope; import java.util.Base64; public record JsonSticker(String packId, String packKey, int stickerId) { - static JsonSticker from(SignalServiceDataMessage.Sticker sticker) { - final var packId = Base64.getEncoder().encodeToString(sticker.getPackId()); - final var packKey = Base64.getEncoder().encodeToString(sticker.getPackKey()); - final var stickerId = sticker.getStickerId(); + static JsonSticker from(MessageEnvelope.Data.Sticker sticker) { + final var encoder = Base64.getEncoder(); + final var packId = encoder.encodeToString(sticker.packId()); + final var packKey = encoder.encodeToString(sticker.packKey()); + final var stickerId = sticker.stickerId(); return new JsonSticker(packId, packKey, stickerId); } } diff --git a/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java index 98139c84..805c32c8 100644 --- a/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java @@ -3,10 +3,9 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonUnwrapped; import org.asamk.Signal; -import org.asamk.signal.manager.Manager; -import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; +import org.asamk.signal.manager.api.MessageEnvelope; -import static org.asamk.signal.util.Util.getLegacyIdentifier; +import java.util.UUID; record JsonSyncDataMessage( @Deprecated String destination, @@ -15,16 +14,16 @@ record JsonSyncDataMessage( @JsonUnwrapped JsonDataMessage dataMessage ) { - static JsonSyncDataMessage from(SentTranscriptMessage transcriptMessage, Manager m) { - if (transcriptMessage.getDestination().isPresent()) { - final var address = transcriptMessage.getDestination().get(); - return new JsonSyncDataMessage(getLegacyIdentifier(address), - address.getNumber().orNull(), - address.getUuid().toString(), - JsonDataMessage.from(transcriptMessage.getMessage(), m)); + static JsonSyncDataMessage from(MessageEnvelope.Sync.Sent transcriptMessage) { + if (transcriptMessage.destination().isPresent()) { + final var address = transcriptMessage.destination().get(); + return new JsonSyncDataMessage(address.getLegacyIdentifier(), + address.getNumber().orElse(null), + address.getUuid().map(UUID::toString).orElse(null), + JsonDataMessage.from(transcriptMessage.message())); } else { - return new JsonSyncDataMessage(null, null, null, JsonDataMessage.from(transcriptMessage.getMessage(), m)); + return new JsonSyncDataMessage(null, null, null, JsonDataMessage.from(transcriptMessage.message())); } } diff --git a/src/main/java/org/asamk/signal/json/JsonSyncMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncMessage.java index 2628844f..554b0724 100644 --- a/src/main/java/org/asamk/signal/json/JsonSyncMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonSyncMessage.java @@ -3,11 +3,10 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonInclude; import org.asamk.Signal; -import org.asamk.signal.manager.Manager; -import org.asamk.signal.util.Util; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; +import org.asamk.signal.manager.api.MessageEnvelope; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; -import java.util.Base64; import java.util.List; import java.util.stream.Collectors; @@ -39,43 +38,40 @@ record JsonSyncMessage( this.type = type; } - static JsonSyncMessage from(SignalServiceSyncMessage syncMessage, Manager m) { - final var sentMessage = syncMessage.getSent().isPresent() ? JsonSyncDataMessage.from(syncMessage.getSent() - .get(), m) : null; + static JsonSyncMessage from(MessageEnvelope.Sync syncMessage) { + final var sentMessage = syncMessage.sent().isPresent() + ? JsonSyncDataMessage.from(syncMessage.sent().get()) + : null; final List blockedNumbers; final List blockedGroupIds; - if (syncMessage.getBlockedList().isPresent()) { - final var base64 = Base64.getEncoder(); - blockedNumbers = syncMessage.getBlockedList() + if (syncMessage.blocked().isPresent()) { + blockedNumbers = syncMessage.blocked() .get() - .getAddresses() + .recipients() .stream() - .map(Util::getLegacyIdentifier) + .map(RecipientAddress::getLegacyIdentifier) .collect(Collectors.toList()); - blockedGroupIds = syncMessage.getBlockedList() + blockedGroupIds = syncMessage.blocked() .get() - .getGroupIds() + .groupIds() .stream() - .map(base64::encodeToString) + .map(GroupId::toBase64) .collect(Collectors.toList()); } else { blockedNumbers = null; blockedGroupIds = null; } - final var readMessages = syncMessage.getRead().isPresent() ? syncMessage.getRead() - .get() + final var readMessages = syncMessage.read().size() > 0 ? syncMessage.read() .stream() .map(JsonSyncReadMessage::from) .collect(Collectors.toList()) : null; final JsonSyncMessageType type; - if (syncMessage.getContacts().isPresent()) { + if (syncMessage.contacts().isPresent()) { type = JsonSyncMessageType.CONTACTS_SYNC; - } else if (syncMessage.getGroups().isPresent()) { + } else if (syncMessage.groups().isPresent()) { type = JsonSyncMessageType.GROUPS_SYNC; - } else if (syncMessage.getRequest().isPresent()) { - type = JsonSyncMessageType.REQUEST_SYNC; } else { type = null; } diff --git a/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java index 05cb48f3..3d04f94d 100644 --- a/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java @@ -1,19 +1,19 @@ package org.asamk.signal.json; -import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; +import org.asamk.signal.manager.api.MessageEnvelope; -import static org.asamk.signal.util.Util.getLegacyIdentifier; +import java.util.UUID; record JsonSyncReadMessage( @Deprecated String sender, String senderNumber, String senderUuid, long timestamp ) { - static JsonSyncReadMessage from(final ReadMessage readMessage) { - final var senderAddress = readMessage.getSender(); - final var sender = getLegacyIdentifier(senderAddress); - final var senderNumber = senderAddress.getNumber().orNull(); - final var senderUuid = senderAddress.getUuid().toString(); - final var timestamp = readMessage.getTimestamp(); + static JsonSyncReadMessage from(MessageEnvelope.Sync.Read readMessage) { + final var senderAddress = readMessage.sender(); + final var sender = senderAddress.getLegacyIdentifier(); + final var senderNumber = senderAddress.getNumber().orElse(null); + final var senderUuid = senderAddress.getUuid().map(UUID::toString).orElse(null); + final var timestamp = readMessage.timestamp(); return new JsonSyncReadMessage(sender, senderNumber, senderUuid, timestamp); } } diff --git a/src/main/java/org/asamk/signal/json/JsonTypingMessage.java b/src/main/java/org/asamk/signal/json/JsonTypingMessage.java index b231d67e..0df82b1c 100644 --- a/src/main/java/org/asamk/signal/json/JsonTypingMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonTypingMessage.java @@ -2,25 +2,17 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonInclude; -import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; - -import java.util.Base64; +import org.asamk.signal.manager.api.MessageEnvelope; +import org.asamk.signal.manager.groups.GroupId; record JsonTypingMessage( String action, long timestamp, @JsonInclude(JsonInclude.Include.NON_NULL) String groupId ) { - JsonTypingMessage(final String action, final long timestamp, final String groupId) { - this.action = action; - this.timestamp = timestamp; - this.groupId = groupId; - } - - static JsonTypingMessage from(SignalServiceTypingMessage typingMessage) { - final var action = typingMessage.getAction().name(); - final var timestamp = typingMessage.getTimestamp(); - final var encoder = Base64.getEncoder(); - final var groupId = typingMessage.getGroupId().transform(encoder::encodeToString).orNull(); + static JsonTypingMessage from(MessageEnvelope.Typing typingMessage) { + final var action = typingMessage.type().name(); + final var timestamp = typingMessage.timestamp(); + final var groupId = typingMessage.groupId().map(GroupId::toBase64).orElse(null); return new JsonTypingMessage(action, timestamp, groupId); } } diff --git a/src/main/java/org/asamk/signal/util/Util.java b/src/main/java/org/asamk/signal/util/Util.java index 5ded399e..48e6f35d 100644 --- a/src/main/java/org/asamk/signal/util/Util.java +++ b/src/main/java/org/asamk/signal/util/Util.java @@ -10,6 +10,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Optional; import java.util.stream.Collectors; public class Util { @@ -17,8 +18,8 @@ public class Util { private Util() { } - public static String getStringIfNotBlank(org.whispersystems.libsignal.util.guava.Optional value) { - var string = value.orNull(); + public static String getStringIfNotBlank(Optional value) { + var string = value.orElse(null); if (string == null || string.isBlank()) { return null; }