X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/a811d1a05a28d332705f30528db882a1031e8257..2ef59d692ae92615105c6aa2603677a3fa584200:/lib/src/main/java/org/asamk/signal/manager/Manager.java 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 be31493d..60263dd9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -21,7 +21,6 @@ import org.asamk.signal.manager.config.ServiceEnvironment; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdV1; -import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupUtils; @@ -35,10 +34,10 @@ import org.asamk.signal.manager.storage.contacts.ContactInfo; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.GroupInfoV2; +import org.asamk.signal.manager.storage.identities.IdentityInfo; import org.asamk.signal.manager.storage.messageCache.CachedMessage; import org.asamk.signal.manager.storage.profiles.SignalProfile; -import org.asamk.signal.manager.storage.profiles.SignalProfileEntry; -import org.asamk.signal.manager.storage.protocol.IdentityInfo; +import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.stickers.Sticker; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.IOUtils; @@ -60,11 +59,8 @@ import org.signal.libsignal.metadata.SelfSendException; import org.signal.libsignal.metadata.certificate.CertificateValidator; import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroup; -import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; -import org.signal.storageservice.protos.groups.local.DecryptedMember; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.VerificationFailedException; -import org.signal.zkgroup.auth.AuthCredentialResponse; import org.signal.zkgroup.groups.GroupMasterKey; import org.signal.zkgroup.groups.GroupSecretParams; import org.signal.zkgroup.profiles.ClientZkProfileOperations; @@ -76,26 +72,22 @@ import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.InvalidVersionException; import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceMessagePipe; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api; import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; -import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; @@ -107,7 +99,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifestUpload; import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage; import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact; @@ -117,7 +108,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; -import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage; import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; @@ -127,11 +117,9 @@ import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException; -import org.whispersystems.signalservice.api.storage.StorageKey; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.api.util.SleepTimer; -import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.internal.contacts.crypto.Quote; @@ -160,15 +148,16 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Function; import java.util.stream.Collectors; import static org.asamk.signal.manager.config.ServiceConfig.capabilities; @@ -189,6 +178,8 @@ public class Manager implements Closeable { private final SignalServiceMessageReceiver messageReceiver; private final ClientZkProfileOperations clientZkProfileOperations; + private final ExecutorService executor = Executors.newCachedThreadPool(); + private SignalServiceMessagePipe messagePipe = null; private SignalServiceMessagePipe unidentifiedMessagePipe = null; @@ -216,14 +207,13 @@ public class Manager implements Closeable { new DynamicCredentialsProvider(account.getUuid(), account.getUsername(), account.getPassword(), - account.getSignalingKey(), account.getDeviceId()), userAgent, groupsV2Operations, ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); this.groupsV2Api = accountManager.getGroupsV2Api(); - final KeyBackupService keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), + final var keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(), serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(), serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(), @@ -239,15 +229,12 @@ public class Manager implements Closeable { account.getUsername(), account.getPassword(), account.getDeviceId(), - account.getSignalingKey(), userAgent, null, timer, clientZkProfileOperations, ServiceConfig.AUTOMATIC_NETWORK_RETRY); - this.account.setResolver(this::resolveSignalServiceAddress); - this.unidentifiedAccessHelper = new UnidentifiedAccessHelper(account::getProfileKey, account.getProfileStore()::getProfileKey, this::getRecipientProfile, @@ -275,7 +262,7 @@ public class Manager implements Closeable { } private IdentityKeyPair getIdentityKeyPair() { - return account.getSignalProtocolStore().getIdentityKeyPair(); + return account.getIdentityKeyPair(); } public int getDeviceId() { @@ -285,29 +272,27 @@ public class Manager implements Closeable { public static Manager init( String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent ) throws IOException, NotRegisteredException { - PathConfig pathConfig = PathConfig.createDefault(settingsPath); + var pathConfig = PathConfig.createDefault(settingsPath); if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { throw new NotRegisteredException(); } - SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username); + var account = SignalAccount.load(pathConfig.getDataPath(), username); if (!account.isRegistered()) { throw new NotRegisteredException(); } - final ServiceEnvironmentConfig serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig( - serviceEnvironment, - userAgent); + final var serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); return new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent); } public static List getAllLocalUsernames(File settingsPath) { - PathConfig pathConfig = PathConfig.createDefault(settingsPath); - final File dataPath = pathConfig.getDataPath(); - final File[] files = dataPath.listFiles(); + var pathConfig = PathConfig.createDefault(settingsPath); + final var dataPath = pathConfig.getDataPath(); + final var files = dataPath.listFiles(); if (files == null) { return List.of(); @@ -341,16 +326,16 @@ public class Manager implements Closeable { */ public Map areUsersRegistered(Set numbers) throws IOException { // Note "contactDetails" has no optionals. It only gives us info on users who are registered - Map contactDetails = getRegisteredUsers(numbers); + var contactDetails = getRegisteredUsers(numbers); - Set registeredUsers = contactDetails.keySet(); + var registeredUsers = contactDetails.keySet(); return numbers.stream().collect(Collectors.toMap(x -> x, registeredUsers::contains)); } public void updateAccountAttributes() throws IOException { - accountManager.setAccountAttributes(account.getSignalingKey(), - account.getSignalProtocolStore().getLocalRegistrationId(), + accountManager.setAccountAttributes(null, + account.getLocalRegistrationId(), true, // set legacy pin only if no KBS master key is set account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null, @@ -369,9 +354,9 @@ public class Manager implements Closeable { * if it's Optional.absent(), the avatar will be removed */ public void setProfile(String name, String about, String aboutEmoji, Optional avatar) throws IOException { - SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(getSelfAddress()); - SignalProfile profile = profileEntry == null ? null : profileEntry.getProfile(); - SignalProfile newProfile = new SignalProfile(profile == null ? null : profile.getIdentityKey(), + var profileEntry = account.getProfileStore().getProfileEntry(getSelfAddress()); + var profile = profileEntry == null ? null : profileEntry.getProfile(); + var newProfile = new SignalProfile(profile == null ? null : profile.getIdentityKey(), name != null ? name : profile == null || profile.getName() == null ? "" : profile.getName(), about != null ? about : profile == null || profile.getAbout() == null ? "" : profile.getAbout(), aboutEmoji != null @@ -381,7 +366,7 @@ public class Manager implements Closeable { account.isUnrestrictedUnidentifiedAccess(), profile == null ? null : profile.getCapabilities()); - try (final StreamDetails streamDetails = avatar == null + try (final var streamDetails = avatar == null ? avatarStore.retrieveProfileAvatar(getSelfAddress()) : avatar.isPresent() ? Utils.createStreamDetailsFromFile(avatar.get()) : null) { accountManager.setVersionedProfile(account.getUuid(), @@ -425,7 +410,7 @@ public class Manager implements Closeable { } public List getLinkedDevices() throws IOException { - List devices = accountManager.getDevices(); + var devices = accountManager.getDevices(); account.setMultiDevice(devices.size() > 1); account.save(); return devices; @@ -433,20 +418,20 @@ public class Manager implements Closeable { public void removeLinkedDevices(int deviceId) throws IOException { accountManager.removeDevice(deviceId); - List devices = accountManager.getDevices(); + var devices = accountManager.getDevices(); account.setMultiDevice(devices.size() > 1); account.save(); } public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException { - DeviceLinkInfo info = DeviceLinkInfo.parseDeviceLinkUri(linkUri); + var info = DeviceLinkInfo.parseDeviceLinkUri(linkUri); addDevice(info.deviceIdentifier, info.deviceKey); } private void addDevice(String deviceIdentifier, ECPublicKey deviceKey) throws IOException, InvalidKeyException { - IdentityKeyPair identityKeyPair = getIdentityKeyPair(); - String verificationCode = accountManager.getNewDeviceVerificationCode(); + var identityKeyPair = getIdentityKeyPair(); + var verificationCode = accountManager.getNewDeviceVerificationCode(); accountManager.addDevice(deviceIdentifier, deviceKey, @@ -462,7 +447,7 @@ public class Manager implements Closeable { throw new RuntimeException("Only master device can set a PIN"); } if (pin.isPresent()) { - final MasterKey masterKey = account.getPinMasterKey() != null + final var masterKey = account.getPinMasterKey() != null ? account.getPinMasterKey() : KeyUtils.createMasterKey(); @@ -484,29 +469,27 @@ public class Manager implements Closeable { } void refreshPreKeys() throws IOException { - List oneTimePreKeys = generatePreKeys(); - final IdentityKeyPair identityKeyPair = getIdentityKeyPair(); - SignedPreKeyRecord signedPreKeyRecord = generateSignedPreKey(identityKeyPair); + var oneTimePreKeys = generatePreKeys(); + final var identityKeyPair = getIdentityKeyPair(); + var signedPreKeyRecord = generateSignedPreKey(identityKeyPair); accountManager.setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys); } private List generatePreKeys() { - final int offset = account.getPreKeyIdOffset(); + final var offset = account.getPreKeyIdOffset(); - List records = KeyUtils.generatePreKeyRecords(offset, ServiceConfig.PREKEY_BATCH_SIZE); + var records = KeyUtils.generatePreKeyRecords(offset, ServiceConfig.PREKEY_BATCH_SIZE); account.addPreKeys(records); - account.save(); return records; } private SignedPreKeyRecord generateSignedPreKey(IdentityKeyPair identityKeyPair) { - final int signedPreKeyId = account.getNextSignedPreKeyId(); + final var signedPreKeyId = account.getNextSignedPreKeyId(); - SignedPreKeyRecord record = KeyUtils.generateSignedPreKeyRecord(identityKeyPair, signedPreKeyId); + var record = KeyUtils.generateSignedPreKeyRecord(identityKeyPair, signedPreKeyId); account.addSignedPreKey(record); - account.save(); return record; } @@ -526,7 +509,6 @@ public class Manager implements Closeable { } private SignalServiceMessageSender createMessageSender() { - final ExecutorService executor = null; return new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(), account.getUuid(), account.getUsername(), @@ -544,7 +526,7 @@ public class Manager implements Closeable { ServiceConfig.AUTOMATIC_NETWORK_RETRY); } - private SignalProfile getRecipientProfile( + public SignalProfile getRecipientProfile( SignalServiceAddress address ) { return getRecipientProfile(address, false); @@ -553,11 +535,11 @@ public class Manager implements Closeable { private SignalProfile getRecipientProfile( SignalServiceAddress address, boolean force ) { - SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address); + var profileEntry = account.getProfileStore().getProfileEntry(address); if (profileEntry == null) { return null; } - long now = new Date().getTime(); + var now = new Date().getTime(); // Profiles are cached for 24h before retrieving them again if (!profileEntry.isRequestPending() && ( force @@ -576,8 +558,8 @@ public class Manager implements Closeable { profileEntry.setRequestPending(false); } - final ProfileKey profileKey = profileEntry.getProfileKey(); - final SignalProfile profile = decryptProfileAndDownloadAvatar(address, profileKey, encryptedProfile); + final var profileKey = profileEntry.getProfileKey(); + final var profile = decryptProfileAndDownloadAvatar(address, profileKey, encryptedProfile); account.getProfileStore() .updateProfile(address, profileKey, now, profile, profileEntry.getProfileKeyCredential()); return profile; @@ -586,7 +568,7 @@ public class Manager implements Closeable { } private ProfileKeyCredential getRecipientProfileKeyCredential(SignalServiceAddress address) { - SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address); + var profileEntry = account.getProfileStore().getProfileEntry(address); if (profileEntry == null) { return null; } @@ -600,9 +582,9 @@ public class Manager implements Closeable { return null; } - long now = new Date().getTime(); - final ProfileKeyCredential profileKeyCredential = profileAndCredential.getProfileKeyCredential().orNull(); - final SignalProfile profile = decryptProfileAndDownloadAvatar(address, + var now = new Date().getTime(); + final var profileKeyCredential = profileAndCredential.getProfileKeyCredential().orNull(); + final var profile = decryptProfileAndDownloadAvatar(address, profileEntry.getProfileKey(), profileAndCredential.getProfile()); account.getProfileStore() @@ -623,7 +605,7 @@ public class Manager implements Closeable { } private Optional createGroupAvatarAttachment(GroupId groupId) throws IOException { - final StreamDetails streamDetails = avatarStore.retrieveGroupAvatar(groupId); + final var streamDetails = avatarStore.retrieveGroupAvatar(groupId); if (streamDetails == null) { return Optional.absent(); } @@ -632,7 +614,7 @@ public class Manager implements Closeable { } private Optional createContactAvatarAttachment(SignalServiceAddress address) throws IOException { - final StreamDetails streamDetails = avatarStore.retrieveContactAvatar(address); + final var streamDetails = avatarStore.retrieveContactAvatar(address); if (streamDetails == null) { return Optional.absent(); } @@ -641,7 +623,7 @@ public class Manager implements Closeable { } private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { - GroupInfo g = getGroup(groupId); + var g = getGroup(groupId); if (g == null) { throw new GroupNotFoundException(groupId); } @@ -652,7 +634,7 @@ public class Manager implements Closeable { } private GroupInfo getGroupForUpdating(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { - GroupInfo g = getGroup(groupId); + var g = getGroup(groupId); if (g == null) { throw new GroupNotFoundException(groupId); } @@ -669,8 +651,7 @@ public class Manager implements Closeable { public Pair> sendGroupMessage( String messageText, List attachments, GroupId groupId ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException { - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .withBody(messageText); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText); if (attachments != null) { messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments)); } @@ -681,12 +662,11 @@ public class Manager implements Closeable { public Pair> sendGroupMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, GroupId groupId ) throws IOException, InvalidNumberException, NotAGroupMemberException, GroupNotFoundException { - SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, + var reaction = new SignalServiceDataMessage.Reaction(emoji, remove, canonicalizeAndResolveSignalServiceAddress(targetAuthor), targetSentTimestamp); - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .withReaction(reaction); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction); return sendGroupMessage(messageBuilder, groupId); } @@ -694,7 +674,7 @@ public class Manager implements Closeable { public Pair> sendGroupMessage( SignalServiceDataMessage.Builder messageBuilder, GroupId groupId ) throws IOException, GroupNotFoundException, NotAGroupMemberException { - final GroupInfo g = getGroupForSending(groupId); + final var g = getGroupForSending(groupId); GroupUtils.setGroupContext(messageBuilder, g); messageBuilder.withExpiration(g.getMessageExpirationTime()); @@ -705,18 +685,16 @@ public class Manager implements Closeable { public Pair> sendQuitGroupMessage(GroupId groupId) throws GroupNotFoundException, IOException, NotAGroupMemberException { SignalServiceDataMessage.Builder messageBuilder; - final GroupInfo g = getGroupForUpdating(groupId); + final var g = getGroupForUpdating(groupId); if (g instanceof GroupInfoV1) { - GroupInfoV1 groupInfoV1 = (GroupInfoV1) g; - SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT) - .withId(groupId.serialize()) - .build(); + var groupInfoV1 = (GroupInfoV1) g; + var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT).withId(groupId.serialize()).build(); messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group); groupInfoV1.removeMember(account.getSelfAddress()); account.getGroupStore().updateGroup(groupInfoV1); } else { - final GroupInfoV2 groupInfoV2 = (GroupInfoV2) g; - final Pair groupGroupChangePair = groupHelper.leaveGroup(groupInfoV2); + final var groupInfoV2 = (GroupInfoV2) g; + final var groupGroupChangePair = groupHelper.leaveGroup(groupInfoV2); groupInfoV2.setGroup(groupGroupChangePair.first()); messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().toByteArray()); account.getGroupStore().updateGroup(groupInfoV2); @@ -741,11 +719,11 @@ public class Manager implements Closeable { SignalServiceDataMessage.Builder messageBuilder; if (groupId == null) { // Create new group - GroupInfoV2 gv2 = groupHelper.createGroupV2(name == null ? "" : name, + var gv2 = groupHelper.createGroupV2(name == null ? "" : name, members == null ? List.of() : members, avatarFile); if (gv2 == null) { - GroupInfoV1 gv1 = new GroupInfoV1(GroupIdV1.createRandom()); + var gv1 = new GroupInfoV1(GroupIdV1.createRandom()); gv1.addMembers(List.of(account.getSelfAddress())); updateGroupV1(gv1, name, members, avatarFile); messageBuilder = getGroupUpdateMessageBuilder(gv1); @@ -759,36 +737,33 @@ public class Manager implements Closeable { g = gv2; } } else { - GroupInfo group = getGroupForUpdating(groupId); + var group = getGroupForUpdating(groupId); if (group instanceof GroupInfoV2) { - final GroupInfoV2 groupInfoV2 = (GroupInfoV2) group; + final var groupInfoV2 = (GroupInfoV2) group; Pair> result = null; if (groupInfoV2.isPendingMember(getSelfAddress())) { - Pair groupGroupChangePair = groupHelper.acceptInvite(groupInfoV2); + var groupGroupChangePair = groupHelper.acceptInvite(groupInfoV2); result = sendUpdateGroupMessage(groupInfoV2, groupGroupChangePair.first(), groupGroupChangePair.second()); } if (members != null) { - final Set newMembers = new HashSet<>(members); + final var newMembers = new HashSet<>(members); newMembers.removeAll(group.getMembers() .stream() .map(this::resolveSignalServiceAddress) .collect(Collectors.toSet())); if (newMembers.size() > 0) { - Pair groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, - newMembers); + var groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, newMembers); result = sendUpdateGroupMessage(groupInfoV2, groupGroupChangePair.first(), groupGroupChangePair.second()); } } if (result == null || name != null || avatarFile != null) { - Pair groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, - name, - avatarFile); + var groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, name, avatarFile); if (avatarFile != null) { avatarStore.storeGroupAvatar(groupInfoV2.getGroupId(), outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream)); @@ -800,7 +775,7 @@ public class Manager implements Closeable { return new Pair<>(group.getGroupId(), result.second()); } else { - GroupInfoV1 gv1 = (GroupInfoV1) group; + var gv1 = (GroupInfoV1) group; updateGroupV1(gv1, name, members, avatarFile); messageBuilder = getGroupUpdateMessageBuilder(gv1); g = gv1; @@ -809,8 +784,7 @@ public class Manager implements Closeable { account.getGroupStore().updateGroup(g); - final Pair> result = sendMessage(messageBuilder, - g.getMembersIncludingPendingWithout(account.getSelfAddress())); + final var result = sendMessage(messageBuilder, g.getMembersIncludingPendingWithout(account.getSelfAddress())); return new Pair<>(g.getGroupId(), result.second()); } @@ -825,15 +799,15 @@ public class Manager implements Closeable { } if (members != null) { - final Set newE164Members = new HashSet<>(); - for (SignalServiceAddress member : members) { + final var newE164Members = new HashSet(); + for (var member : members) { if (g.isMember(member) || !member.getNumber().isPresent()) { continue; } newE164Members.add(member.getNumber().get()); } - final Map registeredUsers = getRegisteredUsers(newE164Members); + final var registeredUsers = getRegisteredUsers(newE164Members); if (registeredUsers.size() != newE164Members.size()) { // Some of the new members are not registered on Signal newE164Members.removeAll(registeredUsers.keySet()); @@ -860,12 +834,12 @@ public class Manager implements Closeable { private Pair> sendJoinGroupMessage( GroupInviteLinkUrl inviteLinkUrl ) throws IOException, GroupLinkNotActiveException { - final DecryptedGroupJoinInfo groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(), + final var groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword()); - final GroupChange groupChange = groupHelper.joinGroup(inviteLinkUrl.getGroupMasterKey(), + final var groupChange = groupHelper.joinGroup(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword(), groupJoinInfo); - final GroupInfoV2 group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(), + final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(), groupJoinInfo.getRevision() + 1, groupChange.toByteArray()); @@ -874,7 +848,7 @@ public class Manager implements Closeable { return new Pair<>(group.getGroupId(), List.of()); } - final Pair> result = sendUpdateGroupMessage(group, group.getGroup(), groupChange); + final var result = sendUpdateGroupMessage(group, group.getGroup(), groupChange); return new Pair<>(group.getGroupId(), result.second()); } @@ -886,11 +860,11 @@ public class Manager implements Closeable { private GroupsV2AuthorizationString getGroupAuthForToday( final GroupSecretParams groupSecretParams ) throws IOException { - final int today = currentTimeDays(); + final var today = currentTimeDays(); // Returns credentials for the next 7 days - final HashMap credentials = groupsV2Api.getCredentials(today); + final var credentials = groupsV2Api.getCredentials(today); // TODO cache credentials until they expire - AuthCredentialResponse authCredentialResponse = credentials.get(today); + var authCredentialResponse = credentials.get(today); try { return groupsV2Api.getGroupsV2AuthorizationString(account.getUuid(), today, @@ -905,8 +879,7 @@ public class Manager implements Closeable { GroupInfoV2 group, DecryptedGroup newDecryptedGroup, GroupChange groupChange ) throws IOException { group.setGroup(newDecryptedGroup); - final SignalServiceDataMessage.Builder messageBuilder = getGroupUpdateMessageBuilder(group, - groupChange.toByteArray()); + final var messageBuilder = getGroupUpdateMessageBuilder(group, groupChange.toByteArray()); account.getGroupStore().updateGroup(group); return sendMessage(messageBuilder, group.getMembersIncludingPendingWithout(account.getSelfAddress())); } @@ -915,7 +888,7 @@ public class Manager implements Closeable { GroupIdV1 groupId, SignalServiceAddress recipient ) throws IOException, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException { GroupInfoV1 g; - GroupInfo group = getGroupForSending(groupId); + var group = getGroupForSending(groupId); if (!(group instanceof GroupInfoV1)) { throw new RuntimeException("Received an invalid group request for a v2 group!"); } @@ -925,20 +898,20 @@ public class Manager implements Closeable { throw new NotAGroupMemberException(groupId, g.name); } - SignalServiceDataMessage.Builder messageBuilder = getGroupUpdateMessageBuilder(g); + var messageBuilder = getGroupUpdateMessageBuilder(g); // Send group message only to the recipient who requested it return sendMessage(messageBuilder, List.of(recipient)); } private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV1 g) throws AttachmentInvalidException { - SignalServiceGroup.Builder group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.UPDATE) + var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.UPDATE) .withId(g.getGroupId().serialize()) .withName(g.name) .withMembers(new ArrayList<>(g.getMembers())); try { - final Optional attachment = createGroupAvatarAttachment(g.getGroupId()); + final var attachment = createGroupAvatarAttachment(g.getGroupId()); if (attachment.isPresent()) { group.withAvatar(attachment.get()); } @@ -952,7 +925,7 @@ public class Manager implements Closeable { } private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV2 g, byte[] signedGroupChange) { - SignalServiceGroupV2.Builder group = SignalServiceGroupV2.newBuilder(g.getMasterKey()) + var group = SignalServiceGroupV2.newBuilder(g.getMasterKey()) .withRevision(g.getGroup().getRevision()) .withSignedGroupChange(signedGroupChange); return SignalServiceDataMessage.newBuilder() @@ -963,11 +936,9 @@ public class Manager implements Closeable { Pair> sendGroupInfoRequest( GroupIdV1 groupId, SignalServiceAddress recipient ) throws IOException { - SignalServiceGroup.Builder group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.REQUEST_INFO) - .withId(groupId.serialize()); + var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.REQUEST_INFO).withId(groupId.serialize()); - SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .asGroupMessage(group.build()); + var messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group.build()); // Send group info request message to the recipient who sent us a message with this groupId return sendMessage(messageBuilder, List.of(recipient)); @@ -976,7 +947,7 @@ public class Manager implements Closeable { void sendReceipt( SignalServiceAddress remoteAddress, long messageId ) throws IOException, UntrustedIdentityException { - SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, + var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, List.of(messageId), System.currentTimeMillis()); @@ -988,15 +959,14 @@ public class Manager implements Closeable { public Pair> sendMessage( String messageText, List attachments, List recipients ) throws IOException, AttachmentInvalidException, InvalidNumberException { - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .withBody(messageText); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText); if (attachments != null) { - List attachmentStreams = AttachmentUtils.getSignalServiceAttachments(attachments); + var attachmentStreams = AttachmentUtils.getSignalServiceAttachments(attachments); // Upload attachments here, so we only upload once even for multiple recipients - SignalServiceMessageSender messageSender = createMessageSender(); - List attachmentPointers = new ArrayList<>(attachmentStreams.size()); - for (SignalServiceAttachment attachment : attachmentStreams) { + var messageSender = createMessageSender(); + var attachmentPointers = new ArrayList(attachmentStreams.size()); + for (var attachment : attachmentStreams) { if (attachment.isStream()) { attachmentPointers.add(messageSender.uploadAttachment(attachment.asStream())); } else if (attachment.isPointer()) { @@ -1012,34 +982,48 @@ public class Manager implements Closeable { public Pair sendSelfMessage( String messageText, List attachments ) throws IOException, AttachmentInvalidException { - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .withBody(messageText); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText); if (attachments != null) { messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments)); } return sendSelfMessage(messageBuilder); } + public Pair> sendRemoteDeleteMessage( + long targetSentTimestamp, List recipients + ) throws IOException, InvalidNumberException { + var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete); + return sendMessage(messageBuilder, getSignalServiceAddresses(recipients)); + } + + public Pair> sendGroupRemoteDeleteMessage( + long targetSentTimestamp, GroupId groupId + ) throws IOException, NotAGroupMemberException, GroupNotFoundException { + var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete); + return sendGroupMessage(messageBuilder, groupId); + } + public Pair> sendMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List recipients ) throws IOException, InvalidNumberException { - SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, + var reaction = new SignalServiceDataMessage.Reaction(emoji, remove, canonicalizeAndResolveSignalServiceAddress(targetAuthor), targetSentTimestamp); - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .withReaction(reaction); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction); return sendMessage(messageBuilder, getSignalServiceAddresses(recipients)); } public Pair> sendEndSessionMessage(List recipients) throws IOException, InvalidNumberException { - SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().asEndSessionMessage(); + var messageBuilder = SignalServiceDataMessage.newBuilder().asEndSessionMessage(); - final Collection signalServiceAddresses = getSignalServiceAddresses(recipients); + final var signalServiceAddresses = getSignalServiceAddresses(recipients); try { return sendMessage(messageBuilder, signalServiceAddresses); } catch (Exception e) { - for (SignalServiceAddress address : signalServiceAddresses) { + for (var address : signalServiceAddresses) { handleEndSession(address); } account.save(); @@ -1048,7 +1032,7 @@ public class Manager implements Closeable { } public String getContactName(String number) throws InvalidNumberException { - ContactInfo contact = account.getContactStore().getContact(canonicalizeAndResolveSignalServiceAddress(number)); + var contact = account.getContactStore().getContact(canonicalizeAndResolveSignalServiceAddress(number)); if (contact == null) { return ""; } else { @@ -1057,8 +1041,8 @@ public class Manager implements Closeable { } public void setContactName(String number, String name) throws InvalidNumberException { - final SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(number); - ContactInfo contact = account.getContactStore().getContact(address); + final var address = canonicalizeAndResolveSignalServiceAddress(number); + var contact = account.getContactStore().getContact(address); if (contact == null) { contact = new ContactInfo(address); } @@ -1072,7 +1056,7 @@ public class Manager implements Closeable { } private void setContactBlocked(SignalServiceAddress address, boolean blocked) { - ContactInfo contact = account.getContactStore().getContact(address); + var contact = account.getContactStore().getContact(address); if (contact == null) { contact = new ContactInfo(address); } @@ -1082,7 +1066,7 @@ public class Manager implements Closeable { } public void setGroupBlocked(final GroupId groupId, final boolean blocked) throws GroupNotFoundException { - GroupInfo group = getGroup(groupId); + var group = getGroup(groupId); if (group == null) { throw new GroupNotFoundException(groupId); } @@ -1096,7 +1080,7 @@ public class Manager implements Closeable { * Change the expiration timer for a contact */ public void setExpirationTimer(SignalServiceAddress address, int messageExpirationTimer) throws IOException { - ContactInfo contact = account.getContactStore().getContact(address); + var contact = account.getContactStore().getContact(address); contact.messageExpirationTime = messageExpirationTimer; account.getContactStore().updateContact(contact); sendExpirationTimerUpdate(address); @@ -1104,8 +1088,7 @@ public class Manager implements Closeable { } private void sendExpirationTimerUpdate(SignalServiceAddress address) throws IOException { - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .asExpirationUpdate(); + final var messageBuilder = SignalServiceDataMessage.newBuilder().asExpirationUpdate(); sendMessage(messageBuilder, List.of(address)); } @@ -1115,7 +1098,7 @@ public class Manager implements Closeable { public void setExpirationTimer( String number, int messageExpirationTimer ) throws IOException, InvalidNumberException { - SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(number); + var address = canonicalizeAndResolveSignalServiceAddress(number); setExpirationTimer(address, messageExpirationTimer); } @@ -1123,9 +1106,9 @@ public class Manager implements Closeable { * Change the expiration timer for a group */ public void setExpirationTimer(GroupId groupId, int messageExpirationTimer) { - GroupInfo g = getGroup(groupId); + var g = getGroup(groupId); if (g instanceof GroupInfoV1) { - GroupInfoV1 groupInfoV1 = (GroupInfoV1) g; + var groupInfoV1 = (GroupInfoV1) g; groupInfoV1.messageExpirationTime = messageExpirationTimer; account.getGroupStore().updateGroup(groupInfoV1); } else { @@ -1140,14 +1123,14 @@ public class Manager implements Closeable { * @return if successful, returns the URL to install the sticker pack in the signal app */ public String uploadStickerPack(File path) throws IOException, StickerPackInvalidException { - SignalServiceStickerManifestUpload manifest = StickerUtils.getSignalServiceStickerManifestUpload(path); + var manifest = StickerUtils.getSignalServiceStickerManifestUpload(path); - SignalServiceMessageSender messageSender = createMessageSender(); + var messageSender = createMessageSender(); - byte[] packKey = KeyUtils.createStickerUploadKey(); - String packId = messageSender.uploadStickerManifest(manifest, packKey); + var packKey = KeyUtils.createStickerUploadKey(); + var packId = messageSender.uploadStickerManifest(manifest, packKey); - Sticker sticker = new Sticker(Hex.fromStringCondensed(packId), packKey); + var sticker = new Sticker(Hex.fromStringCondensed(packId), packKey); account.getStickerStore().updateSticker(sticker); account.save(); @@ -1164,10 +1147,10 @@ public class Manager implements Closeable { } void requestSyncGroups() throws IOException { - SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS) .build(); - SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { @@ -1176,10 +1159,10 @@ public class Manager implements Closeable { } void requestSyncContacts() throws IOException { - SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS) .build(); - SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { @@ -1188,10 +1171,10 @@ public class Manager implements Closeable { } void requestSyncBlocked() throws IOException { - SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED) .build(); - SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { @@ -1200,10 +1183,10 @@ public class Manager implements Closeable { } void requestSyncConfiguration() throws IOException { - SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION) .build(); - SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { @@ -1212,10 +1195,10 @@ public class Manager implements Closeable { } void requestSyncKeys() throws IOException { - SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS) .build(); - SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { @@ -1238,24 +1221,16 @@ public class Manager implements Closeable { } private void sendSyncMessage(SignalServiceSyncMessage message) throws IOException, UntrustedIdentityException { - SignalServiceMessageSender messageSender = createMessageSender(); - try { - messageSender.sendMessage(message, unidentifiedAccessHelper.getAccessForSync()); - } catch (UntrustedIdentityException e) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); - throw e; - } + var messageSender = createMessageSender(); + messageSender.sendMessage(message, unidentifiedAccessHelper.getAccessForSync()); } private Collection getSignalServiceAddresses(Collection numbers) throws InvalidNumberException { - final Set signalServiceAddresses = new HashSet<>(numbers.size()); - final Set addressesMissingUuid = new HashSet<>(); + final var signalServiceAddresses = new HashSet(numbers.size()); + final var addressesMissingUuid = new HashSet(); - for (String number : numbers) { - final SignalServiceAddress resolvedAddress = canonicalizeAndResolveSignalServiceAddress(number); + for (var number : numbers) { + final var resolvedAddress = canonicalizeAndResolveSignalServiceAddress(number); if (resolvedAddress.getUuid().isPresent()) { signalServiceAddresses.add(resolvedAddress); } else { @@ -1263,7 +1238,7 @@ public class Manager implements Closeable { } } - final Set numbersMissingUuid = addressesMissingUuid.stream() + final var numbersMissingUuid = addressesMissingUuid.stream() .map(a -> a.getNumber().get()) .collect(Collectors.toSet()); Map registeredUsers; @@ -1274,11 +1249,10 @@ public class Manager implements Closeable { registeredUsers = Map.of(); } - for (SignalServiceAddress address : addressesMissingUuid) { - final String number = address.getNumber().get(); + for (var address : addressesMissingUuid) { + final var number = address.getNumber().get(); if (registeredUsers.containsKey(number)) { - final SignalServiceAddress newAddress = resolveSignalServiceAddress(new SignalServiceAddress( - registeredUsers.get(number), + final var newAddress = resolveSignalServiceAddress(new SignalServiceAddress(registeredUsers.get(number), number)); signalServiceAddresses.add(newAddress); } else { @@ -1303,7 +1277,7 @@ public class Manager implements Closeable { SignalServiceDataMessage.Builder messageBuilder, Collection recipients ) throws IOException { recipients = recipients.stream().map(this::resolveSignalServiceAddress).collect(Collectors.toSet()); - final long timestamp = System.currentTimeMillis(); + final var timestamp = System.currentTimeMillis(); messageBuilder.withTimestamp(timestamp); getOrCreateMessagePipe(); getOrCreateUnidentifiedMessagePipe(); @@ -1312,35 +1286,23 @@ public class Manager implements Closeable { message = messageBuilder.build(); if (message.getGroupContext().isPresent()) { try { - SignalServiceMessageSender messageSender = createMessageSender(); - final boolean isRecipientUpdate = false; - List result = messageSender.sendMessage(new ArrayList<>(recipients), + var messageSender = createMessageSender(); + final var isRecipientUpdate = false; + var result = messageSender.sendMessage(new ArrayList<>(recipients), unidentifiedAccessHelper.getAccessFor(recipients), isRecipientUpdate, message); - for (SendMessageResult r : result) { - if (r.getIdentityFailure() != null) { - account.getSignalProtocolStore() - .saveIdentity(r.getAddress(), - r.getIdentityFailure().getIdentityKey(), - TrustLevel.UNTRUSTED); - } - } return new Pair<>(timestamp, result); } catch (UntrustedIdentityException e) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); return new Pair<>(timestamp, List.of()); } } else { // Send to all individually, so sync messages are sent correctly messageBuilder.withProfileKey(account.getProfileKey().serialize()); - List results = new ArrayList<>(recipients.size()); - for (SignalServiceAddress address : recipients) { - final ContactInfo contact = account.getContactStore().getContact(address); - final int expirationTime = contact != null ? contact.messageExpirationTime : 0; + var results = new ArrayList(recipients.size()); + for (var address : recipients) { + final var contact = account.getContactStore().getContact(address); + final var expirationTime = contact != null ? contact.messageExpirationTime : 0; messageBuilder.withExpiration(expirationTime); message = messageBuilder.build(); results.add(sendMessage(address, message)); @@ -1349,7 +1311,7 @@ public class Manager implements Closeable { } } finally { if (message != null && message.isEndSession()) { - for (SignalServiceAddress recipient : recipients) { + for (var recipient : recipients) { handleEndSession(recipient); } } @@ -1360,19 +1322,19 @@ public class Manager implements Closeable { private Pair sendSelfMessage( SignalServiceDataMessage.Builder messageBuilder ) throws IOException { - final long timestamp = System.currentTimeMillis(); + final var timestamp = System.currentTimeMillis(); messageBuilder.withTimestamp(timestamp); getOrCreateMessagePipe(); getOrCreateUnidentifiedMessagePipe(); try { - final SignalServiceAddress address = getSelfAddress(); + final var address = getSelfAddress(); - final ContactInfo contact = account.getContactStore().getContact(address); - final int expirationTime = contact != null ? contact.messageExpirationTime : 0; + final var contact = account.getContactStore().getContact(address); + final var expirationTime = contact != null ? contact.messageExpirationTime : 0; messageBuilder.withExpiration(expirationTime); - SignalServiceDataMessage message = messageBuilder.build(); - final SendMessageResult result = sendSelfMessage(message); + var message = messageBuilder.build(); + final var result = sendSelfMessage(message); return new Pair<>(timestamp, result); } finally { account.save(); @@ -1380,31 +1342,27 @@ public class Manager implements Closeable { } private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException { - SignalServiceMessageSender messageSender = createMessageSender(); + var messageSender = createMessageSender(); - SignalServiceAddress recipient = account.getSelfAddress(); + var recipient = account.getSelfAddress(); - final Optional unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(recipient); - SentTranscriptMessage transcript = new SentTranscriptMessage(Optional.of(recipient), + final var unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(recipient); + var transcript = new SentTranscriptMessage(Optional.of(recipient), message.getTimestamp(), message, message.getExpiresInSeconds(), Map.of(recipient, unidentifiedAccess.isPresent()), false); - SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript); + var syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript); try { - long startTime = System.currentTimeMillis(); + var startTime = System.currentTimeMillis(); messageSender.sendMessage(syncMessage, unidentifiedAccess); return SendMessageResult.success(recipient, unidentifiedAccess.isPresent(), false, System.currentTimeMillis() - startTime); } catch (UntrustedIdentityException e) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); return SendMessageResult.identityFailure(recipient, e.getIdentityKey()); } } @@ -1412,44 +1370,31 @@ public class Manager implements Closeable { private SendMessageResult sendMessage( SignalServiceAddress address, SignalServiceDataMessage message ) throws IOException { - SignalServiceMessageSender messageSender = createMessageSender(); + var messageSender = createMessageSender(); try { return messageSender.sendMessage(address, unidentifiedAccessHelper.getAccessFor(address), message); } catch (UntrustedIdentityException e) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); return SendMessageResult.identityFailure(address, e.getIdentityKey()); } } private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, org.whispersystems.libsignal.UntrustedIdentityException { - SignalServiceCipher cipher = new SignalServiceCipher(account.getSelfAddress(), + var cipher = new SignalServiceCipher(account.getSelfAddress(), account.getSignalProtocolStore(), certificateValidator); try { return cipher.decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { if (e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) { - org.whispersystems.libsignal.UntrustedIdentityException identityException = (org.whispersystems.libsignal.UntrustedIdentityException) e - .getCause(); - final IdentityKey untrustedIdentity = identityException.getUntrustedIdentity(); - if (untrustedIdentity != null) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(identityException.getName()), - untrustedIdentity, - TrustLevel.UNTRUSTED); - } - throw identityException; + throw (org.whispersystems.libsignal.UntrustedIdentityException) e.getCause(); } throw new AssertionError(e); } } private void handleEndSession(SignalServiceAddress source) { - account.getSignalProtocolStore().deleteAllSessions(source); + account.getSessionStore().deleteAllSessions(source.getIdentifier()); } private List handleSignalServiceDataMessage( @@ -1459,14 +1404,14 @@ public class Manager implements Closeable { SignalServiceAddress destination, boolean ignoreAttachments ) { - List actions = new ArrayList<>(); + var actions = new ArrayList(); if (message.getGroupContext().isPresent()) { if (message.getGroupContext().get().getGroupV1().isPresent()) { - SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get(); - GroupIdV1 groupId = GroupId.v1(groupInfo.getGroupId()); - GroupInfo group = getGroup(groupId); + var groupInfo = message.getGroupContext().get().getGroupV1().get(); + var groupId = GroupId.v1(groupInfo.getGroupId()); + var group = getGroup(groupId); if (group == null || group instanceof GroupInfoV1) { - GroupInfoV1 groupV1 = (GroupInfoV1) group; + var groupV1 = (GroupInfoV1) group; switch (groupInfo.getType()) { case UPDATE: { if (groupV1 == null) { @@ -1474,7 +1419,7 @@ public class Manager implements Closeable { } if (groupInfo.getAvatar().isPresent()) { - SignalServiceAttachment avatar = groupInfo.getAvatar().get(); + var avatar = groupInfo.getAvatar().get(); downloadGroupAvatar(avatar, groupV1.getGroupId()); } @@ -1516,8 +1461,8 @@ public class Manager implements Closeable { } } if (message.getGroupContext().get().getGroupV2().isPresent()) { - final SignalServiceGroupV2 groupContext = message.getGroupContext().get().getGroupV2().get(); - final GroupMasterKey groupMasterKey = groupContext.getMasterKey(); + final var groupContext = message.getGroupContext().get().getGroupV2().get(); + final var groupMasterKey = groupContext.getMasterKey(); getOrMigrateGroup(groupMasterKey, groupContext.getRevision(), @@ -1525,15 +1470,15 @@ public class Manager implements Closeable { } } - final SignalServiceAddress conversationPartnerAddress = isSync ? destination : source; + final var conversationPartnerAddress = isSync ? destination : source; if (conversationPartnerAddress != null && message.isEndSession()) { handleEndSession(conversationPartnerAddress); } if (message.isExpirationUpdate() || message.getBody().isPresent()) { if (message.getGroupContext().isPresent()) { if (message.getGroupContext().get().getGroupV1().isPresent()) { - SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get(); - GroupInfoV1 group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId())); + var groupInfo = message.getGroupContext().get().getGroupV1().get(); + var group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId())); if (group != null) { if (group.messageExpirationTime != message.getExpiresInSeconds()) { group.messageExpirationTime = message.getExpiresInSeconds(); @@ -1544,7 +1489,7 @@ public class Manager implements Closeable { // disappearing message timer already stored in the DecryptedGroup } } else if (conversationPartnerAddress != null) { - ContactInfo contact = account.getContactStore().getContact(conversationPartnerAddress); + var contact = account.getContactStore().getContact(conversationPartnerAddress); if (contact == null) { contact = new ContactInfo(conversationPartnerAddress); } @@ -1554,9 +1499,18 @@ public class Manager implements Closeable { } } } - if (message.getAttachments().isPresent() && !ignoreAttachments) { - for (SignalServiceAttachment attachment : message.getAttachments().get()) { - downloadAttachment(attachment); + if (!ignoreAttachments) { + if (message.getAttachments().isPresent()) { + for (var attachment : message.getAttachments().get()) { + downloadAttachment(attachment); + } + } + if (message.getSharedContacts().isPresent()) { + for (var contact : message.getSharedContacts().get()) { + if (contact.getAvatar().isPresent()) { + downloadAttachment(contact.getAvatar().get().getAttachment()); + } + } } } if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) { @@ -1572,26 +1526,26 @@ public class Manager implements Closeable { this.account.getProfileStore().storeProfileKey(source, profileKey); } if (message.getPreviews().isPresent()) { - final List previews = message.getPreviews().get(); - for (SignalServiceDataMessage.Preview preview : previews) { + final var previews = message.getPreviews().get(); + for (var preview : previews) { if (preview.getImage().isPresent()) { downloadAttachment(preview.getImage().get()); } } } if (message.getQuote().isPresent()) { - final SignalServiceDataMessage.Quote quote = message.getQuote().get(); + final var quote = message.getQuote().get(); - for (SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment : quote.getAttachments()) { - final SignalServiceAttachment thumbnail = quotedAttachment.getThumbnail(); + for (var quotedAttachment : quote.getAttachments()) { + final var thumbnail = quotedAttachment.getThumbnail(); if (thumbnail != null) { downloadAttachment(thumbnail); } } } if (message.getSticker().isPresent()) { - final SignalServiceDataMessage.Sticker messageSticker = message.getSticker().get(); - Sticker sticker = account.getStickerStore().getSticker(messageSticker.getPackId()); + final var messageSticker = message.getSticker().get(); + var sticker = account.getStickerStore().getSticker(messageSticker.getPackId()); if (sticker == null) { sticker = new Sticker(messageSticker.getPackId(), messageSticker.getPackKey()); account.getStickerStore().updateSticker(sticker); @@ -1603,10 +1557,10 @@ public class Manager implements Closeable { private GroupInfoV2 getOrMigrateGroup( final GroupMasterKey groupMasterKey, final int revision, final byte[] signedGroupChange ) { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); - GroupIdV2 groupId = GroupUtils.getGroupIdV2(groupSecretParams); - GroupInfo groupInfo = getGroup(groupId); + var groupId = GroupUtils.getGroupIdV2(groupSecretParams); + var groupInfo = getGroup(groupId); final GroupInfoV2 groupInfoV2; if (groupInfo instanceof GroupInfoV1) { // Received a v2 group message for a v1 group, we need to locally migrate the group @@ -1633,7 +1587,7 @@ public class Manager implements Closeable { } if (group != null) { storeProfileKeysFromMembers(group); - final String avatar = group.getAvatar(); + final var avatar = group.getAvatar(); if (avatar != null && !avatar.isEmpty()) { downloadGroupAvatar(groupId, groupSecretParams, avatar); } @@ -1646,9 +1600,9 @@ public class Manager implements Closeable { } private void storeProfileKeysFromMembers(final DecryptedGroup group) { - for (DecryptedMember member : group.getMembersList()) { - final SignalServiceAddress address = resolveSignalServiceAddress(new SignalServiceAddress(UuidUtil.parseOrThrow( - member.getUuid().toByteArray()), null)); + for (var member : group.getMembersList()) { + final var address = resolveSignalServiceAddress(new SignalServiceAddress(UuidUtil.parseOrThrow(member.getUuid() + .toByteArray()), null)); try { account.getProfileStore() .storeProfileKey(address, new ProfileKey(member.getProfileKey().toByteArray())); @@ -1658,7 +1612,7 @@ public class Manager implements Closeable { } private void retryFailedReceivedMessages(ReceiveMessageHandler handler, boolean ignoreAttachments) { - for (CachedMessage cachedMessage : account.getMessageCache().getCachedMessages()) { + for (var cachedMessage : account.getMessageCache().getCachedMessages()) { retryFailedReceivedMessage(handler, ignoreAttachments, cachedMessage); } } @@ -1666,7 +1620,7 @@ public class Manager implements Closeable { private void retryFailedReceivedMessage( final ReceiveMessageHandler handler, final boolean ignoreAttachments, final CachedMessage cachedMessage ) { - SignalServiceEnvelope envelope = cachedMessage.loadEnvelope(); + var envelope = cachedMessage.loadEnvelope(); if (envelope == null) { return; } @@ -1675,14 +1629,23 @@ public class Manager implements Closeable { try { content = decryptMessage(envelope); } catch (org.whispersystems.libsignal.UntrustedIdentityException e) { + if (!envelope.hasSource()) { + final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) e) + .getName()); + try { + account.getMessageCache().replaceSender(cachedMessage, recipientId); + } catch (IOException ioException) { + logger.warn("Failed to move cached message to recipient folder: {}", ioException.getMessage()); + } + } return; } catch (Exception er) { // All other errors are not recoverable, so delete the cached message cachedMessage.delete(); return; } - List actions = handleMessage(envelope, content, ignoreAttachments); - for (HandleAction action : actions) { + var actions = handleMessage(envelope, content, ignoreAttachments); + for (var action : actions) { try { action.execute(this); } catch (Throwable e) { @@ -1706,9 +1669,9 @@ public class Manager implements Closeable { Set queuedActions = null; - final SignalServiceMessagePipe messagePipe = getOrCreateMessagePipe(); + final var messagePipe = getOrCreateMessagePipe(); - boolean hasCaughtUpWithOldMessages = false; + var hasCaughtUpWithOldMessages = false; while (true) { SignalServiceEnvelope envelope; @@ -1716,9 +1679,12 @@ public class Manager implements Closeable { Exception exception = null; final CachedMessage[] cachedMessage = {null}; try { - Optional result = messagePipe.readOrEmpty(timeout, unit, envelope1 -> { + var result = messagePipe.readOrEmpty(timeout, unit, envelope1 -> { + final var recipientId = envelope1.hasSource() + ? resolveRecipient(envelope1.getSourceIdentifier()) + : null; // store message on disk, before acknowledging receipt to the server - cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1); + cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId); }); if (result.isPresent()) { envelope = result.get(); @@ -1727,7 +1693,7 @@ public class Manager implements Closeable { hasCaughtUpWithOldMessages = true; if (queuedActions != null) { - for (HandleAction action : queuedActions) { + for (var action : queuedActions) { try { action.execute(this); } catch (Throwable e) { @@ -1745,15 +1711,11 @@ public class Manager implements Closeable { } catch (TimeoutException e) { if (returnOnTimeout) return; continue; - } catch (InvalidVersionException e) { - logger.warn("Error while receiving messages, ignoring: {}", e.getMessage()); - continue; } if (envelope.hasSource()) { // Store uuid if we don't have it already - SignalServiceAddress source = envelope.getSourceAddress(); - resolveSignalServiceAddress(source); + resolveRecipientTrusted(envelope.getSourceAddress()); } if (!envelope.isReceipt()) { try { @@ -1761,9 +1723,9 @@ public class Manager implements Closeable { } catch (Exception e) { exception = e; } - List actions = handleMessage(envelope, content, ignoreAttachments); + var actions = handleMessage(envelope, content, ignoreAttachments); if (hasCaughtUpWithOldMessages) { - for (HandleAction action : actions) { + for (var action : actions) { try { action.execute(this); } catch (Throwable e) { @@ -1785,8 +1747,19 @@ public class Manager implements Closeable { } else { handler.handleMessage(envelope, content, exception); } - if (!(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) { - if (cachedMessage[0] != null) { + if (cachedMessage[0] != null) { + if (exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) { + if (!envelope.hasSource()) { + final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) exception) + .getName()); + try { + cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId); + } catch (IOException ioException) { + logger.warn("Failed to move cached message to recipient folder: {}", + ioException.getMessage()); + } + } + } else { cachedMessage[0].delete(); } } @@ -1804,16 +1777,16 @@ public class Manager implements Closeable { } else { return false; } - ContactInfo sourceContact = account.getContactStore().getContact(source); + var sourceContact = account.getContactStore().getContact(source); if (sourceContact != null && sourceContact.blocked) { return true; } if (content != null && content.getDataMessage().isPresent()) { - SignalServiceDataMessage message = content.getDataMessage().get(); + var message = content.getDataMessage().get(); if (message.getGroupContext().isPresent()) { - GroupId groupId = GroupUtils.getGroupId(message.getGroupContext().get()); - GroupInfo group = getGroup(groupId); + var groupId = GroupUtils.getGroupId(message.getGroupContext().get()); + var group = getGroup(groupId); if (group != null && group.isBlocked()) { return true; } @@ -1835,16 +1808,16 @@ public class Manager implements Closeable { } if (content != null && content.getDataMessage().isPresent()) { - SignalServiceDataMessage message = content.getDataMessage().get(); + var message = content.getDataMessage().get(); if (message.getGroupContext().isPresent()) { if (message.getGroupContext().get().getGroupV1().isPresent()) { - SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get(); + var groupInfo = message.getGroupContext().get().getGroupV1().get(); if (groupInfo.getType() == SignalServiceGroup.Type.QUIT) { return false; } } - GroupId groupId = GroupUtils.getGroupId(message.getGroupContext().get()); - GroupInfo group = getGroup(groupId); + var groupId = GroupUtils.getGroupId(message.getGroupContext().get()); + var group = getGroup(groupId); if (group != null && !group.isMember(source)) { return true; } @@ -1856,7 +1829,7 @@ public class Manager implements Closeable { private List handleMessage( SignalServiceEnvelope envelope, SignalServiceContent content, boolean ignoreAttachments ) { - List actions = new ArrayList<>(); + var actions = new ArrayList(); if (content != null) { final SignalServiceAddress sender; if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { @@ -1868,7 +1841,7 @@ public class Manager implements Closeable { resolveSignalServiceAddress(sender); if (content.getDataMessage().isPresent()) { - SignalServiceDataMessage message = content.getDataMessage().get(); + var message = content.getDataMessage().get(); if (content.isNeedsReceipt()) { actions.add(new SendReceiptAction(sender, message.getTimestamp())); @@ -1882,10 +1855,10 @@ public class Manager implements Closeable { } if (content.getSyncMessage().isPresent()) { account.setMultiDevice(true); - SignalServiceSyncMessage syncMessage = content.getSyncMessage().get(); + var syncMessage = content.getSyncMessage().get(); if (syncMessage.getSent().isPresent()) { - SentTranscriptMessage message = syncMessage.getSent().get(); - final SignalServiceAddress destination = message.getDestination().orNull(); + var message = syncMessage.getSent().get(); + final var destination = message.getDestination().orNull(); actions.addAll(handleSignalServiceDataMessage(message.getMessage(), true, sender, @@ -1893,7 +1866,7 @@ public class Manager implements Closeable { ignoreAttachments)); } if (syncMessage.getRequest().isPresent()) { - RequestMessage rm = syncMessage.getRequest().get(); + var rm = syncMessage.getRequest().get(); if (rm.isContactsRequest()) { actions.add(SendSyncContactsAction.create()); } @@ -1909,14 +1882,12 @@ public class Manager implements Closeable { File tmpFile = null; try { tmpFile = IOUtils.createTempFile(); - final SignalServiceAttachment groupsMessage = syncMessage.getGroups().get(); - try (InputStream attachmentAsStream = retrieveAttachmentAsStream(groupsMessage.asPointer(), - tmpFile)) { - DeviceGroupsInputStream s = new DeviceGroupsInputStream(attachmentAsStream); + final var groupsMessage = syncMessage.getGroups().get(); + try (var attachmentAsStream = retrieveAttachmentAsStream(groupsMessage.asPointer(), tmpFile)) { + var s = new DeviceGroupsInputStream(attachmentAsStream); DeviceGroup g; while ((g = s.read()) != null) { - GroupInfoV1 syncGroup = account.getGroupStore() - .getOrCreateGroupV1(GroupId.v1(g.getId())); + var syncGroup = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(g.getId())); if (syncGroup != null) { if (g.getName().isPresent()) { syncGroup.name = g.getName().get(); @@ -1962,11 +1933,11 @@ public class Manager implements Closeable { } } if (syncMessage.getBlockedList().isPresent()) { - final BlockedListMessage blockedListMessage = syncMessage.getBlockedList().get(); - for (SignalServiceAddress address : blockedListMessage.getAddresses()) { + final var blockedListMessage = syncMessage.getBlockedList().get(); + for (var address : blockedListMessage.getAddresses()) { setContactBlocked(resolveSignalServiceAddress(address), true); } - for (GroupId groupId : blockedListMessage.getGroupIds() + for (var groupId : blockedListMessage.getGroupIds() .stream() .map(GroupId::unknownVersion) .collect(Collectors.toSet())) { @@ -1982,10 +1953,10 @@ public class Manager implements Closeable { File tmpFile = null; try { tmpFile = IOUtils.createTempFile(); - final ContactsMessage contactsMessage = syncMessage.getContacts().get(); - try (InputStream attachmentAsStream = retrieveAttachmentAsStream(contactsMessage.getContactsStream() + final var contactsMessage = syncMessage.getContacts().get(); + try (var attachmentAsStream = retrieveAttachmentAsStream(contactsMessage.getContactsStream() .asPointer(), tmpFile)) { - DeviceContactsInputStream s = new DeviceContactsInputStream(attachmentAsStream); + var s = new DeviceContactsInputStream(attachmentAsStream); if (contactsMessage.isComplete()) { account.getContactStore().clear(); } @@ -1994,8 +1965,8 @@ public class Manager implements Closeable { if (c.getAddress().matches(account.getSelfAddress()) && c.getProfileKey().isPresent()) { account.setProfileKey(c.getProfileKey().get()); } - final SignalServiceAddress address = resolveSignalServiceAddress(c.getAddress()); - ContactInfo contact = account.getContactStore().getContact(address); + final var address = resolveSignalServiceAddress(c.getAddress()); + var contact = account.getContactStore().getContact(address); if (contact == null) { contact = new ContactInfo(address); } @@ -2009,9 +1980,9 @@ public class Manager implements Closeable { account.getProfileStore().storeProfileKey(address, c.getProfileKey().get()); } if (c.getVerified().isPresent()) { - final VerifiedMessage verifiedMessage = c.getVerified().get(); - account.getSignalProtocolStore() - .setIdentityTrustLevel(verifiedMessage.getDestination(), + final var verifiedMessage = c.getVerified().get(); + account.getIdentityKeyStore() + .setIdentityTrustLevel(resolveRecipientTrusted(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); } @@ -2045,20 +2016,19 @@ public class Manager implements Closeable { } } if (syncMessage.getVerified().isPresent()) { - final VerifiedMessage verifiedMessage = syncMessage.getVerified().get(); - account.getSignalProtocolStore() - .setIdentityTrustLevel(resolveSignalServiceAddress(verifiedMessage.getDestination()), + final var verifiedMessage = syncMessage.getVerified().get(); + account.getIdentityKeyStore() + .setIdentityTrustLevel(resolveRecipientTrusted(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); } if (syncMessage.getStickerPackOperations().isPresent()) { - final List stickerPackOperationMessages = syncMessage.getStickerPackOperations() - .get(); - for (StickerPackOperationMessage m : stickerPackOperationMessages) { + final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get(); + for (var m : stickerPackOperationMessages) { if (!m.getPackId().isPresent()) { continue; } - Sticker sticker = account.getStickerStore().getSticker(m.getPackId().get()); + var sticker = account.getStickerStore().getSticker(m.getPackId().get()); if (sticker == null) { if (!m.getPackKey().isPresent()) { continue; @@ -2079,9 +2049,9 @@ public class Manager implements Closeable { } } if (syncMessage.getKeys().isPresent()) { - final KeysMessage keysMessage = syncMessage.getKeys().get(); + final var keysMessage = syncMessage.getKeys().get(); if (keysMessage.getStorageService().isPresent()) { - final StorageKey storageKey = keysMessage.getStorageService().get(); + final var storageKey = keysMessage.getStorageService().get(); account.setStorageKey(storageKey); } } @@ -2138,9 +2108,9 @@ public class Manager implements Closeable { logger.warn("Invalid state, can't store an attachment stream."); } - SignalServiceAttachmentPointer pointer = attachment.asPointer(); + var pointer = attachment.asPointer(); if (pointer.getPreview().isPresent()) { - final byte[] preview = pointer.getPreview().get(); + final var preview = pointer.getPreview().get(); try { attachmentStore.storeAttachmentPreview(pointer.getRemoteId(), outputStream -> outputStream.write(preview, 0, preview.length)); @@ -2160,15 +2130,15 @@ public class Manager implements Closeable { private void retrieveGroupV2Avatar( GroupSecretParams groupSecretParams, String cdnKey, OutputStream outputStream ) throws IOException { - GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); + var groupOperations = groupsV2Operations.forGroup(groupSecretParams); - File tmpFile = IOUtils.createTempFile(); + var tmpFile = IOUtils.createTempFile(); try (InputStream input = messageReceiver.retrieveGroupsV2ProfileAvatar(cdnKey, tmpFile, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { - byte[] encryptedData = IOUtils.readFully(input); + var encryptedData = IOUtils.readFully(input); - byte[] decryptedData = groupOperations.decryptAvatar(encryptedData); + var decryptedData = groupOperations.decryptAvatar(encryptedData); outputStream.write(decryptedData); } finally { try { @@ -2184,8 +2154,8 @@ public class Manager implements Closeable { private void retrieveProfileAvatar( String avatarPath, ProfileKey profileKey, OutputStream outputStream ) throws IOException { - File tmpFile = IOUtils.createTempFile(); - try (InputStream input = messageReceiver.retrieveProfileAvatar(avatarPath, + var tmpFile = IOUtils.createTempFile(); + try (var input = messageReceiver.retrieveProfileAvatar(avatarPath, tmpFile, profileKey, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { @@ -2206,10 +2176,10 @@ public class Manager implements Closeable { final SignalServiceAttachment attachment, final OutputStream outputStream ) throws IOException { if (attachment.isPointer()) { - SignalServiceAttachmentPointer pointer = attachment.asPointer(); + var pointer = attachment.asPointer(); retrieveAttachmentPointer(pointer, outputStream); } else { - SignalServiceAttachmentStream stream = attachment.asStream(); + var stream = attachment.asStream(); IOUtils.copyStream(stream.getInputStream(), outputStream); } } @@ -2217,8 +2187,8 @@ public class Manager implements Closeable { private void retrieveAttachmentPointer( SignalServiceAttachmentPointer pointer, OutputStream outputStream ) throws IOException { - File tmpFile = IOUtils.createTempFile(); - try (InputStream input = retrieveAttachmentAsStream(pointer, tmpFile)) { + var tmpFile = IOUtils.createTempFile(); + try (var input = retrieveAttachmentAsStream(pointer, tmpFile)) { IOUtils.copyStream(input, outputStream); } catch (MissingConfigurationException | InvalidMessageException e) { throw new IOException(e); @@ -2240,14 +2210,14 @@ public class Manager implements Closeable { } void sendGroups() throws IOException, UntrustedIdentityException { - File groupsFile = IOUtils.createTempFile(); + var groupsFile = IOUtils.createTempFile(); try { try (OutputStream fos = new FileOutputStream(groupsFile)) { - DeviceGroupsOutputStream out = new DeviceGroupsOutputStream(fos); - for (GroupInfo record : getGroups()) { + var out = new DeviceGroupsOutputStream(fos); + for (var record : getGroups()) { if (record instanceof GroupInfoV1) { - GroupInfoV1 groupInfo = (GroupInfoV1) record; + var groupInfo = (GroupInfoV1) record; out.write(new DeviceGroup(groupInfo.getGroupId().serialize(), Optional.fromNullable(groupInfo.name), new ArrayList<>(groupInfo.getMembers()), @@ -2263,8 +2233,8 @@ public class Manager implements Closeable { } if (groupsFile.exists() && groupsFile.length() > 0) { - try (FileInputStream groupsFileStream = new FileInputStream(groupsFile)) { - SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() + try (var groupsFileStream = new FileInputStream(groupsFile)) { + var attachmentStream = SignalServiceAttachment.newStreamBuilder() .withStream(groupsFileStream) .withContentType("application/octet-stream") .withLength(groupsFile.length()) @@ -2283,14 +2253,15 @@ public class Manager implements Closeable { } public void sendContacts() throws IOException, UntrustedIdentityException { - File contactsFile = IOUtils.createTempFile(); + var contactsFile = IOUtils.createTempFile(); try { try (OutputStream fos = new FileOutputStream(contactsFile)) { - DeviceContactsOutputStream out = new DeviceContactsOutputStream(fos); - for (ContactInfo record : account.getContactStore().getContacts()) { + var out = new DeviceContactsOutputStream(fos); + for (var record : account.getContactStore().getContacts()) { VerifiedMessage verifiedMessage = null; - IdentityInfo currentIdentity = account.getSignalProtocolStore().getIdentity(record.getAddress()); + var currentIdentity = account.getIdentityKeyStore() + .getIdentity(resolveRecipientTrusted(record.getAddress())); if (currentIdentity != null) { verifiedMessage = new VerifiedMessage(record.getAddress(), currentIdentity.getIdentityKey(), @@ -2298,7 +2269,7 @@ public class Manager implements Closeable { currentIdentity.getDateAdded().getTime()); } - ProfileKey profileKey = account.getProfileStore().getProfileKey(record.getAddress()); + var profileKey = account.getProfileStore().getProfileKey(record.getAddress()); out.write(new DeviceContact(record.getAddress(), Optional.fromNullable(record.name), createContactAvatarAttachment(record.getAddress()), @@ -2327,8 +2298,8 @@ public class Manager implements Closeable { } if (contactsFile.exists() && contactsFile.length() > 0) { - try (FileInputStream contactsFileStream = new FileInputStream(contactsFile)) { - SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() + try (var contactsFileStream = new FileInputStream(contactsFile)) { + var attachmentStream = SignalServiceAttachment.newStreamBuilder() .withStream(contactsFileStream) .withContentType("application/octet-stream") .withLength(contactsFile.length()) @@ -2347,14 +2318,14 @@ public class Manager implements Closeable { } void sendBlockedList() throws IOException, UntrustedIdentityException { - List addresses = new ArrayList<>(); - for (ContactInfo record : account.getContactStore().getContacts()) { + var addresses = new ArrayList(); + for (var record : account.getContactStore().getContacts()) { if (record.blocked) { addresses.add(record.getAddress()); } } - List groupIds = new ArrayList<>(); - for (GroupInfo record : getGroups()) { + var groupIds = new ArrayList(); + for (var record : getGroups()) { if (record.isBlocked()) { groupIds.add(record.getGroupId().serialize()); } @@ -2365,7 +2336,7 @@ public class Manager implements Closeable { private void sendVerifiedMessage( SignalServiceAddress destination, IdentityKey identityKey, TrustLevel trustLevel ) throws IOException, UntrustedIdentityException { - VerifiedMessage verifiedMessage = new VerifiedMessage(destination, + var verifiedMessage = new VerifiedMessage(destination, identityKey, trustLevel.toVerifiedState(), System.currentTimeMillis()); @@ -2377,25 +2348,24 @@ public class Manager implements Closeable { } public String getContactOrProfileName(String number) { - final SignalServiceAddress address = Utils.getSignalServiceAddressFromIdentifier(number); + final var address = Utils.getSignalServiceAddressFromIdentifier(number); - final ContactInfo contact = account.getContactStore().getContact(address); + final var contact = account.getContactStore().getContact(address); if (contact != null && !Util.isEmpty(contact.name)) { return contact.name; } - final SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address); + final var profileEntry = account.getProfileStore().getProfileEntry(address); if (profileEntry != null && profileEntry.getProfile() != null) { - return profileEntry.getProfile().getName(); + return profileEntry.getProfile().getDisplayName(); } - return null; } public GroupInfo getGroup(GroupId groupId) { - final GroupInfo group = account.getGroupStore().getGroup(groupId); + final var group = account.getGroupStore().getGroup(groupId); if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() == null) { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(((GroupInfoV2) group).getMasterKey()); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(((GroupInfoV2) group).getMasterKey()); ((GroupInfoV2) group).setGroup(groupHelper.getDecryptedGroup(groupSecretParams)); account.getGroupStore().updateGroup(group); } @@ -2403,11 +2373,12 @@ public class Manager implements Closeable { } public List getIdentities() { - return account.getSignalProtocolStore().getIdentities(); + return account.getIdentityKeyStore().getIdentities(); } public List getIdentities(String number) throws InvalidNumberException { - return account.getSignalProtocolStore().getIdentities(canonicalizeAndResolveSignalServiceAddress(number)); + final var identity = account.getIdentityKeyStore().getIdentity(canonicalizeAndResolveRecipient(number)); + return identity == null ? List.of() : List.of(identity); } /** @@ -2417,27 +2388,10 @@ public class Manager implements Closeable { * @param fingerprint Fingerprint */ public boolean trustIdentityVerified(String name, byte[] fingerprint) throws InvalidNumberException { - SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(name); - List ids = account.getSignalProtocolStore().getIdentities(address); - if (ids == null) { - return false; - } - for (IdentityInfo id : ids) { - if (!Arrays.equals(id.getIdentityKey().serialize(), fingerprint)) { - continue; - } - - account.getSignalProtocolStore() - .setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); - try { - sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); - } catch (IOException | UntrustedIdentityException e) { - logger.warn("Failed to send verification sync message: {}", e.getMessage()); - } - account.save(); - return true; - } - return false; + var recipientId = canonicalizeAndResolveRecipient(name); + return trustIdentity(recipientId, + identityKey -> Arrays.equals(identityKey.serialize(), fingerprint), + TrustLevel.TRUSTED_VERIFIED); } /** @@ -2447,27 +2401,11 @@ public class Manager implements Closeable { * @param safetyNumber Safety number */ public boolean trustIdentityVerifiedSafetyNumber(String name, String safetyNumber) throws InvalidNumberException { - SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(name); - List ids = account.getSignalProtocolStore().getIdentities(address); - if (ids == null) { - return false; - } - for (IdentityInfo id : ids) { - if (!safetyNumber.equals(computeSafetyNumber(address, id.getIdentityKey()))) { - continue; - } - - account.getSignalProtocolStore() - .setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); - try { - sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); - } catch (IOException | UntrustedIdentityException e) { - logger.warn("Failed to send verification sync message: {}", e.getMessage()); - } - account.save(); - return true; - } - return false; + var recipientId = canonicalizeAndResolveRecipient(name); + var address = account.getRecipientStore().resolveServiceAddress(recipientId); + return trustIdentity(recipientId, + identityKey -> safetyNumber.equals(computeSafetyNumber(address, identityKey)), + TrustLevel.TRUSTED_VERIFIED); } /** @@ -2475,24 +2413,31 @@ public class Manager implements Closeable { * * @param name username of the identity */ - public boolean trustIdentityAllKeys(String name) { - SignalServiceAddress address = resolveSignalServiceAddress(name); - List ids = account.getSignalProtocolStore().getIdentities(address); - if (ids == null) { + public boolean trustIdentityAllKeys(String name) throws InvalidNumberException { + var recipientId = canonicalizeAndResolveRecipient(name); + return trustIdentity(recipientId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED); + } + + private boolean trustIdentity( + RecipientId recipientId, Function verifier, TrustLevel trustLevel + ) { + var identity = account.getIdentityKeyStore().getIdentity(recipientId); + if (identity == null) { return false; } - for (IdentityInfo id : ids) { - if (id.getTrustLevel() == TrustLevel.UNTRUSTED) { - account.getSignalProtocolStore() - .setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED); - try { - sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED); - } catch (IOException | UntrustedIdentityException e) { - logger.warn("Failed to send verification sync message: {}", e.getMessage()); - } - } + + if (!verifier.apply(identity.getIdentityKey())) { + return false; } - account.save(); + + account.getIdentityKeyStore().setIdentityTrustLevel(recipientId, identity.getIdentityKey(), trustLevel); + try { + var address = account.getRecipientStore().resolveServiceAddress(recipientId); + sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel); + } catch (IOException | UntrustedIdentityException e) { + logger.warn("Failed to send verification sync message: {}", e.getMessage()); + } + return true; } @@ -2506,19 +2451,22 @@ public class Manager implements Closeable { theirIdentityKey); } + @Deprecated public SignalServiceAddress canonicalizeAndResolveSignalServiceAddress(String identifier) throws InvalidNumberException { - String canonicalizedNumber = UuidUtil.isUuid(identifier) + var canonicalizedNumber = UuidUtil.isUuid(identifier) ? identifier : PhoneNumberFormatter.formatNumber(identifier, account.getUsername()); return resolveSignalServiceAddress(canonicalizedNumber); } + @Deprecated public SignalServiceAddress resolveSignalServiceAddress(String identifier) { - SignalServiceAddress address = Utils.getSignalServiceAddressFromIdentifier(identifier); + var address = Utils.getSignalServiceAddressFromIdentifier(identifier); return resolveSignalServiceAddress(address); } + @Deprecated public SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address) { if (address.matches(account.getSelfAddress())) { return account.getSelfAddress(); @@ -2527,12 +2475,40 @@ public class Manager implements Closeable { return account.getRecipientStore().resolveServiceAddress(address); } + public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) { + return account.getRecipientStore().resolveServiceAddress(recipientId); + } + + public RecipientId canonicalizeAndResolveRecipient(String identifier) throws InvalidNumberException { + var canonicalizedNumber = UuidUtil.isUuid(identifier) + ? identifier + : PhoneNumberFormatter.formatNumber(identifier, account.getUsername()); + + return resolveRecipient(canonicalizedNumber); + } + + private RecipientId resolveRecipient(final String identifier) { + var address = Utils.getSignalServiceAddressFromIdentifier(identifier); + + return resolveRecipient(address); + } + + public RecipientId resolveRecipient(SignalServiceAddress address) { + return account.getRecipientStore().resolveRecipientUntrusted(address); + } + + private RecipientId resolveRecipientTrusted(SignalServiceAddress address) { + return account.getRecipientStore().resolveRecipient(address); + } + @Override public void close() throws IOException { close(true); } void close(boolean closeAccount) throws IOException { + executor.shutdown(); + if (messagePipe != null) { messagePipe.shutdown(); messagePipe = null;