diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b3085f2..9e2f55be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ ## [0.8.5] - 2021-08-07 ### Added -- Source name is included in JSON receive output +- Source name is included in JSON receive output (Thanks @technillogue) ### Fixed - Allow updateContact command to only set expiration timer without requiring a name parameter diff --git a/README.md b/README.md index f8eee725..9658dcba 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # signal-cli signal-cli is a commandline interface for [libsignal-service-java](https://github.com/WhisperSystems/libsignal-service-java). It supports registering, verifying, sending and receiving messages. -To be able to link to an existing Signal-Android/signal-cli instance, signal-cli uses a [patched libsignal-service-java](https://github.com/AsamK/libsignal-service-java), because libsignal-service-java does not yet support [provisioning as a slave device](https://github.com/WhisperSystems/libsignal-service-java/pull/21). +To be able to link to an existing Signal-Android/signal-cli instance, signal-cli uses a [patched libsignal-service-java](https://github.com/AsamK/libsignal-service-java), because libsignal-service-java does not yet support [provisioning as a linked device](https://github.com/WhisperSystems/libsignal-service-java/pull/21). For registering you need a phone number where you can receive SMS or incoming calls. signal-cli is primarily intended to be used on servers to notify admins of important events. For this use-case, it has a dbus interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)), that can be used to send messages from any programming language that has dbus bindings. diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index bb19e040..d18c13e2 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -1,7 +1,10 @@ [ { "name":"java.lang.ClassLoader", - "methods":[{"name":"getPlatformClassLoader","parameterTypes":[] }] + "methods":[ + {"name":"getPlatformClassLoader","parameterTypes":[] }, + {"name":"loadClass","parameterTypes":["java.lang.String"] } + ] }, { "name":"java.lang.IllegalStateException", @@ -14,6 +17,9 @@ "name":"java.lang.UnsatisfiedLinkError", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, +{ + "name":"jdk.internal.loader.ClassLoaders$PlatformClassLoader" +}, { "name":"org.asamk.signal.manager.storage.protocol.SignalProtocolStore", "methods":[ @@ -29,6 +35,10 @@ {"name":"storeSession","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.state.SessionRecord"] } ] }, +{ + "name":"org.graalvm.nativebridge.jni.JNIExceptionWrapperEntryPoints", + "methods":[{"name":"getClassName","parameterTypes":["java.lang.Class"] }] +}, { "name":"org.whispersystems.libsignal.DuplicateMessageException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] diff --git a/graalvm-config-dir/predefined-classes-config.json b/graalvm-config-dir/predefined-classes-config.json new file mode 100644 index 00000000..0e79b2c5 --- /dev/null +++ b/graalvm-config-dir/predefined-classes-config.json @@ -0,0 +1,8 @@ +[ + { + "type":"agent-extracted", + "classes":[ + ] + } +] + diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 2a55703a..854013d1 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -144,7 +144,7 @@ { "name":"java.nio.Buffer", "allDeclaredMethods":true, - "fields":[{"name":"address", "allowUnsafeAccess":true}] + "fields":[{"name":"address"}] }, { "name":"java.nio.ByteBuffer", @@ -486,6 +486,13 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.manager.storage.contacts.LegacyJsonContactsStore", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true, + "fields":[{"name":"contacts", "allowWrite":true}] +}, { "name":"org.asamk.signal.manager.storage.groups.GroupInfo", "allDeclaredFields":true, @@ -538,13 +545,6 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, -{ - "name":"org.asamk.signal.manager.storage.contacts.LegacyJsonContactsStore", - "allDeclaredFields":true, - "allDeclaredMethods":true, - "allDeclaredConstructors":true, - "fields":[{"name":"contacts", "allowWrite":true}] -}, { "name":"org.asamk.signal.manager.storage.profiles.LegacyProfileStore", "allDeclaredFields":true, @@ -1048,285 +1048,289 @@ { "name":"org.signal.storageservice.protos.groups.AccessControl", "fields":[ - {"name":"addFromInviteLink_", "allowUnsafeAccess":true}, - {"name":"attributes_", "allowUnsafeAccess":true}, - {"name":"members_", "allowUnsafeAccess":true} + {"name":"addFromInviteLink_"}, + {"name":"attributes_"}, + {"name":"members_"} ] }, { "name":"org.signal.storageservice.protos.groups.AvatarUploadAttributes", "fields":[ - {"name":"acl_", "allowUnsafeAccess":true}, - {"name":"algorithm_", "allowUnsafeAccess":true}, - {"name":"credential_", "allowUnsafeAccess":true}, - {"name":"date_", "allowUnsafeAccess":true}, - {"name":"key_", "allowUnsafeAccess":true}, - {"name":"policy_", "allowUnsafeAccess":true}, - {"name":"signature_", "allowUnsafeAccess":true} + {"name":"acl_"}, + {"name":"algorithm_"}, + {"name":"credential_"}, + {"name":"date_"}, + {"name":"key_"}, + {"name":"policy_"}, + {"name":"signature_"} ] }, { "name":"org.signal.storageservice.protos.groups.Group", "fields":[ - {"name":"accessControl_", "allowUnsafeAccess":true}, - {"name":"avatar_", "allowUnsafeAccess":true}, - {"name":"description_", "allowUnsafeAccess":true}, - {"name":"disappearingMessagesTimer_", "allowUnsafeAccess":true}, - {"name":"inviteLinkPassword_", "allowUnsafeAccess":true}, - {"name":"members_", "allowUnsafeAccess":true}, - {"name":"pendingMembers_", "allowUnsafeAccess":true}, - {"name":"publicKey_", "allowUnsafeAccess":true}, - {"name":"requestingMembers_", "allowUnsafeAccess":true}, - {"name":"revision_", "allowUnsafeAccess":true}, - {"name":"title_", "allowUnsafeAccess":true} + {"name":"accessControl_"}, + {"name":"announcementsOnly_"}, + {"name":"avatar_"}, + {"name":"description_"}, + {"name":"disappearingMessagesTimer_"}, + {"name":"inviteLinkPassword_"}, + {"name":"members_"}, + {"name":"pendingMembers_"}, + {"name":"publicKey_"}, + {"name":"requestingMembers_"}, + {"name":"revision_"}, + {"name":"title_"} ] }, { "name":"org.signal.storageservice.protos.groups.GroupAttributeBlob", "fields":[ - {"name":"contentCase_", "allowUnsafeAccess":true}, - {"name":"content_", "allowUnsafeAccess":true} + {"name":"contentCase_"}, + {"name":"content_"} ] }, { "name":"org.signal.storageservice.protos.groups.GroupChange", "fields":[ - {"name":"actions_", "allowUnsafeAccess":true}, - {"name":"changeEpoch_", "allowUnsafeAccess":true}, - {"name":"serverSignature_", "allowUnsafeAccess":true} + {"name":"actions_"}, + {"name":"changeEpoch_"}, + {"name":"serverSignature_"} ] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions", "fields":[ - {"name":"addMembers_", "allowUnsafeAccess":true}, - {"name":"addPendingMembers_", "allowUnsafeAccess":true}, - {"name":"addRequestingMembers_", "allowUnsafeAccess":true}, - {"name":"deleteMembers_", "allowUnsafeAccess":true}, - {"name":"deletePendingMembers_", "allowUnsafeAccess":true}, - {"name":"deleteRequestingMembers_", "allowUnsafeAccess":true}, - {"name":"modifyAddFromInviteLinkAccess_", "allowUnsafeAccess":true}, - {"name":"modifyAttributesAccess_", "allowUnsafeAccess":true}, - {"name":"modifyAvatar_", "allowUnsafeAccess":true}, - {"name":"modifyDescription_", "allowUnsafeAccess":true}, - {"name":"modifyDisappearingMessagesTimer_", "allowUnsafeAccess":true}, - {"name":"modifyInviteLinkPassword_", "allowUnsafeAccess":true}, - {"name":"modifyMemberAccess_", "allowUnsafeAccess":true}, - {"name":"modifyMemberProfileKeys_", "allowUnsafeAccess":true}, - {"name":"modifyMemberRoles_", "allowUnsafeAccess":true}, - {"name":"modifyTitle_", "allowUnsafeAccess":true}, - {"name":"promotePendingMembers_", "allowUnsafeAccess":true}, - {"name":"promoteRequestingMembers_", "allowUnsafeAccess":true}, - {"name":"revision_", "allowUnsafeAccess":true}, - {"name":"sourceUuid_", "allowUnsafeAccess":true} + {"name":"addMembers_"}, + {"name":"addPendingMembers_"}, + {"name":"addRequestingMembers_"}, + {"name":"deleteMembers_"}, + {"name":"deletePendingMembers_"}, + {"name":"deleteRequestingMembers_"}, + {"name":"modifyAddFromInviteLinkAccess_"}, + {"name":"modifyAnnouncementsOnly_"}, + {"name":"modifyAttributesAccess_"}, + {"name":"modifyAvatar_"}, + {"name":"modifyDescription_"}, + {"name":"modifyDisappearingMessagesTimer_"}, + {"name":"modifyInviteLinkPassword_"}, + {"name":"modifyMemberAccess_"}, + {"name":"modifyMemberProfileKeys_"}, + {"name":"modifyMemberRoles_"}, + {"name":"modifyTitle_"}, + {"name":"promotePendingMembers_"}, + {"name":"promoteRequestingMembers_"}, + {"name":"revision_"}, + {"name":"sourceUuid_"} ] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddMemberAction", "fields":[ - {"name":"added_", "allowUnsafeAccess":true}, - {"name":"joinFromInviteLink_", "allowUnsafeAccess":true} + {"name":"added_"}, + {"name":"joinFromInviteLink_"} ] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddPendingMemberAction", - "fields":[{"name":"added_", "allowUnsafeAccess":true}] + "fields":[{"name":"added_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddRequestingMemberAction", - "fields":[{"name":"added_", "allowUnsafeAccess":true}] + "fields":[{"name":"added_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteMemberAction", - "fields":[{"name":"deletedUserId_", "allowUnsafeAccess":true}] + "fields":[{"name":"deletedUserId_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeletePendingMemberAction", - "fields":[{"name":"deletedUserId_", "allowUnsafeAccess":true}] + "fields":[{"name":"deletedUserId_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteRequestingMemberAction", - "fields":[{"name":"deletedUserId_", "allowUnsafeAccess":true}] + "fields":[{"name":"deletedUserId_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyAddFromInviteLinkAccessControlAction", - "fields":[{"name":"addFromInviteLinkAccess_", "allowUnsafeAccess":true}] + "fields":[{"name":"addFromInviteLinkAccess_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyAttributesAccessControlAction", - "fields":[{"name":"attributesAccess_", "allowUnsafeAccess":true}] + "fields":[{"name":"attributesAccess_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyDescriptionAction", - "fields":[{"name":"description_", "allowUnsafeAccess":true}] + "fields":[{"name":"description_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyDisappearingMessagesTimerAction", - "fields":[{"name":"timer_", "allowUnsafeAccess":true}] + "fields":[{"name":"timer_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyInviteLinkPasswordAction", - "fields":[{"name":"inviteLinkPassword_", "allowUnsafeAccess":true}] + "fields":[{"name":"inviteLinkPassword_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberProfileKeyAction", - "fields":[{"name":"presentation_", "allowUnsafeAccess":true}] + "fields":[{"name":"presentation_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberRoleAction", "fields":[ - {"name":"role_", "allowUnsafeAccess":true}, - {"name":"userId_", "allowUnsafeAccess":true} + {"name":"role_"}, + {"name":"userId_"} ] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMembersAccessControlAction", - "fields":[{"name":"membersAccess_", "allowUnsafeAccess":true}] + "fields":[{"name":"membersAccess_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyTitleAction", - "fields":[{"name":"title_", "allowUnsafeAccess":true}] + "fields":[{"name":"title_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromotePendingMemberAction", - "fields":[{"name":"presentation_", "allowUnsafeAccess":true}] + "fields":[{"name":"presentation_"}] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromoteRequestingMemberAction", "fields":[ - {"name":"role_", "allowUnsafeAccess":true}, - {"name":"userId_", "allowUnsafeAccess":true} + {"name":"role_"}, + {"name":"userId_"} ] }, { "name":"org.signal.storageservice.protos.groups.GroupInviteLink", "fields":[ - {"name":"contentsCase_", "allowUnsafeAccess":true}, - {"name":"contents_", "allowUnsafeAccess":true} + {"name":"contentsCase_"}, + {"name":"contents_"} ] }, { "name":"org.signal.storageservice.protos.groups.GroupInviteLink$GroupInviteLinkContentsV1", "fields":[ - {"name":"groupMasterKey_", "allowUnsafeAccess":true}, - {"name":"inviteLinkPassword_", "allowUnsafeAccess":true} + {"name":"groupMasterKey_"}, + {"name":"inviteLinkPassword_"} ] }, { "name":"org.signal.storageservice.protos.groups.Member", "fields":[ - {"name":"joinedAtRevision_", "allowUnsafeAccess":true}, - {"name":"presentation_", "allowUnsafeAccess":true}, - {"name":"profileKey_", "allowUnsafeAccess":true}, - {"name":"role_", "allowUnsafeAccess":true}, - {"name":"userId_", "allowUnsafeAccess":true} + {"name":"joinedAtRevision_"}, + {"name":"presentation_"}, + {"name":"profileKey_"}, + {"name":"role_"}, + {"name":"userId_"} ] }, { "name":"org.signal.storageservice.protos.groups.PendingMember", "fields":[ - {"name":"addedByUserId_", "allowUnsafeAccess":true}, - {"name":"member_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true} + {"name":"addedByUserId_"}, + {"name":"member_"}, + {"name":"timestamp_"} ] }, { "name":"org.signal.storageservice.protos.groups.RequestingMember", "fields":[ - {"name":"presentation_", "allowUnsafeAccess":true}, - {"name":"profileKey_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true}, - {"name":"userId_", "allowUnsafeAccess":true} + {"name":"presentation_"}, + {"name":"profileKey_"}, + {"name":"timestamp_"}, + {"name":"userId_"} ] }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedGroup", "fields":[ - {"name":"accessControl_", "allowUnsafeAccess":true}, - {"name":"avatar_", "allowUnsafeAccess":true}, - {"name":"description_", "allowUnsafeAccess":true}, - {"name":"disappearingMessagesTimer_", "allowUnsafeAccess":true}, - {"name":"inviteLinkPassword_", "allowUnsafeAccess":true}, - {"name":"members_", "allowUnsafeAccess":true}, - {"name":"pendingMembers_", "allowUnsafeAccess":true}, - {"name":"requestingMembers_", "allowUnsafeAccess":true}, - {"name":"revision_", "allowUnsafeAccess":true}, - {"name":"title_", "allowUnsafeAccess":true} + {"name":"accessControl_"}, + {"name":"avatar_"}, + {"name":"description_"}, + {"name":"disappearingMessagesTimer_"}, + {"name":"inviteLinkPassword_"}, + {"name":"isAnnouncementGroup_"}, + {"name":"members_"}, + {"name":"pendingMembers_"}, + {"name":"requestingMembers_"}, + {"name":"revision_"}, + {"name":"title_"} ] }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedGroupChange", "fields":[ - {"name":"deleteMembers_", "allowUnsafeAccess":true}, - {"name":"deletePendingMembers_", "allowUnsafeAccess":true}, - {"name":"deleteRequestingMembers_", "allowUnsafeAccess":true}, - {"name":"editor_", "allowUnsafeAccess":true}, - {"name":"modifiedProfileKeys_", "allowUnsafeAccess":true}, - {"name":"modifyMemberRoles_", "allowUnsafeAccess":true}, - {"name":"newAttributeAccess_", "allowUnsafeAccess":true}, - {"name":"newAvatar_", "allowUnsafeAccess":true}, - {"name":"newDescription_", "allowUnsafeAccess":true}, - {"name":"newInviteLinkAccess_", "allowUnsafeAccess":true}, - {"name":"newInviteLinkPassword_", "allowUnsafeAccess":true}, - {"name":"newMemberAccess_", "allowUnsafeAccess":true}, - {"name":"newMembers_", "allowUnsafeAccess":true}, - {"name":"newPendingMembers_", "allowUnsafeAccess":true}, - {"name":"newRequestingMembers_", "allowUnsafeAccess":true}, - {"name":"newTimer_", "allowUnsafeAccess":true}, - {"name":"newTitle_", "allowUnsafeAccess":true}, - {"name":"promotePendingMembers_", "allowUnsafeAccess":true}, - {"name":"promoteRequestingMembers_", "allowUnsafeAccess":true}, - {"name":"revision_", "allowUnsafeAccess":true} + {"name":"deleteMembers_"}, + {"name":"deletePendingMembers_"}, + {"name":"deleteRequestingMembers_"}, + {"name":"editor_"}, + {"name":"modifiedProfileKeys_"}, + {"name":"modifyMemberRoles_"}, + {"name":"newAttributeAccess_"}, + {"name":"newAvatar_"}, + {"name":"newDescription_"}, + {"name":"newInviteLinkAccess_"}, + {"name":"newInviteLinkPassword_"}, + {"name":"newIsAnnouncementGroup_"}, + {"name":"newMemberAccess_"}, + {"name":"newMembers_"}, + {"name":"newPendingMembers_"}, + {"name":"newRequestingMembers_"}, + {"name":"newTimer_"}, + {"name":"newTitle_"}, + {"name":"promotePendingMembers_"}, + {"name":"promoteRequestingMembers_"}, + {"name":"revision_"} ] }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedMember", "fields":[ - {"name":"joinedAtRevision_", "allowUnsafeAccess":true}, - {"name":"profileKey_", "allowUnsafeAccess":true}, - {"name":"role_", "allowUnsafeAccess":true}, - {"name":"uuid_", "allowUnsafeAccess":true} + {"name":"joinedAtRevision_"}, + {"name":"profileKey_"}, + {"name":"role_"}, + {"name":"uuid_"} ] }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedModifyMemberRole", "fields":[ - {"name":"role_", "allowUnsafeAccess":true}, - {"name":"uuid_", "allowUnsafeAccess":true} + {"name":"role_"}, + {"name":"uuid_"} ] }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedPendingMember", "fields":[ - {"name":"addedByUuid_", "allowUnsafeAccess":true}, - {"name":"role_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true}, - {"name":"uuidCipherText_", "allowUnsafeAccess":true}, - {"name":"uuid_", "allowUnsafeAccess":true} + {"name":"addedByUuid_"}, + {"name":"role_"}, + {"name":"timestamp_"}, + {"name":"uuidCipherText_"}, + {"name":"uuid_"} ] }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedPendingMemberRemoval", "fields":[ - {"name":"uuidCipherText_", "allowUnsafeAccess":true}, - {"name":"uuid_", "allowUnsafeAccess":true} + {"name":"uuidCipherText_"}, + {"name":"uuid_"} ] }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedRequestingMember", "fields":[ - {"name":"profileKey_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true}, - {"name":"uuid_", "allowUnsafeAccess":true} + {"name":"profileKey_"}, + {"name":"timestamp_"}, + {"name":"uuid_"} ] }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedString", - "fields":[{"name":"value_", "allowUnsafeAccess":true}] + "fields":[{"name":"value_"}] }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedTimer", - "fields":[{"name":"duration_", "allowUnsafeAccess":true}] + "fields":[{"name":"duration_"}] }, { "name":"org.whispersystems.libsignal.state.IdentityKeyStore", @@ -1504,78 +1508,78 @@ { "name":"org.whispersystems.signalservice.internal.devices.DeviceNameProtos$DeviceName", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"ciphertext_", "allowUnsafeAccess":true}, - {"name":"ephemeralPublic_", "allowUnsafeAccess":true}, - {"name":"syntheticIv_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"ciphertext_"}, + {"name":"ephemeralPublic_"}, + {"name":"syntheticIv_"} ] }, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.BackupRequest", "fields":[ - {"name":"backupId_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"data_", "allowUnsafeAccess":true}, - {"name":"pin_", "allowUnsafeAccess":true}, - {"name":"serviceId_", "allowUnsafeAccess":true}, - {"name":"token_", "allowUnsafeAccess":true}, - {"name":"tries_", "allowUnsafeAccess":true}, - {"name":"validFrom_", "allowUnsafeAccess":true} + {"name":"backupId_"}, + {"name":"bitField0_"}, + {"name":"data_"}, + {"name":"pin_"}, + {"name":"serviceId_"}, + {"name":"token_"}, + {"name":"tries_"}, + {"name":"validFrom_"} ] }, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.BackupResponse", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"status_", "allowUnsafeAccess":true}, - {"name":"token_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"status_"}, + {"name":"token_"} ] }, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.DeleteRequest", "fields":[ - {"name":"backupId_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"serviceId_", "allowUnsafeAccess":true} + {"name":"backupId_"}, + {"name":"bitField0_"}, + {"name":"serviceId_"} ] }, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.Request", "fields":[ - {"name":"backup_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"delete_", "allowUnsafeAccess":true}, - {"name":"restore_", "allowUnsafeAccess":true} + {"name":"backup_"}, + {"name":"bitField0_"}, + {"name":"delete_"}, + {"name":"restore_"} ] }, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.Response", "fields":[ - {"name":"backup_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"delete_", "allowUnsafeAccess":true}, - {"name":"restore_", "allowUnsafeAccess":true} + {"name":"backup_"}, + {"name":"bitField0_"}, + {"name":"delete_"}, + {"name":"restore_"} ] }, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.RestoreRequest", "fields":[ - {"name":"backupId_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"pin_", "allowUnsafeAccess":true}, - {"name":"serviceId_", "allowUnsafeAccess":true}, - {"name":"token_", "allowUnsafeAccess":true}, - {"name":"validFrom_", "allowUnsafeAccess":true} + {"name":"backupId_"}, + {"name":"bitField0_"}, + {"name":"pin_"}, + {"name":"serviceId_"}, + {"name":"token_"}, + {"name":"validFrom_"} ] }, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.RestoreResponse", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"data_", "allowUnsafeAccess":true}, - {"name":"status_", "allowUnsafeAccess":true}, - {"name":"token_", "allowUnsafeAccess":true}, - {"name":"tries_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"data_"}, + {"name":"status_"}, + {"name":"token_"}, + {"name":"tries_"} ] }, { @@ -1685,31 +1689,31 @@ { "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisionEnvelope", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"body_", "allowUnsafeAccess":true}, - {"name":"publicKey_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"body_"}, + {"name":"publicKey_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisionMessage", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"identityKeyPrivate_", "allowUnsafeAccess":true}, - {"name":"identityKeyPublic_", "allowUnsafeAccess":true}, - {"name":"number_", "allowUnsafeAccess":true}, - {"name":"profileKey_", "allowUnsafeAccess":true}, - {"name":"provisioningCode_", "allowUnsafeAccess":true}, - {"name":"provisioningVersion_", "allowUnsafeAccess":true}, - {"name":"readReceipts_", "allowUnsafeAccess":true}, - {"name":"userAgent_", "allowUnsafeAccess":true}, - {"name":"uuid_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"identityKeyPrivate_"}, + {"name":"identityKeyPublic_"}, + {"name":"number_"}, + {"name":"profileKey_"}, + {"name":"provisioningCode_"}, + {"name":"provisioningVersion_"}, + {"name":"readReceipts_"}, + {"name":"userAgent_"}, + {"name":"uuid_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisioningUuid", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"uuid_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"uuid_"} ] }, { @@ -1743,439 +1747,458 @@ { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$AttachmentPointer", "fields":[ - {"name":"attachmentIdentifierCase_", "allowUnsafeAccess":true}, - {"name":"attachmentIdentifier_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"blurHash_", "allowUnsafeAccess":true}, - {"name":"caption_", "allowUnsafeAccess":true}, - {"name":"cdnNumber_", "allowUnsafeAccess":true}, - {"name":"contentType_", "allowUnsafeAccess":true}, - {"name":"digest_", "allowUnsafeAccess":true}, - {"name":"fileName_", "allowUnsafeAccess":true}, - {"name":"flags_", "allowUnsafeAccess":true}, - {"name":"height_", "allowUnsafeAccess":true}, - {"name":"key_", "allowUnsafeAccess":true}, - {"name":"size_", "allowUnsafeAccess":true}, - {"name":"thumbnail_", "allowUnsafeAccess":true}, - {"name":"uploadTimestamp_", "allowUnsafeAccess":true}, - {"name":"width_", "allowUnsafeAccess":true} + {"name":"attachmentIdentifierCase_"}, + {"name":"attachmentIdentifier_"}, + {"name":"bitField0_"}, + {"name":"blurHash_"}, + {"name":"caption_"}, + {"name":"cdnNumber_"}, + {"name":"contentType_"}, + {"name":"digest_"}, + {"name":"fileName_"}, + {"name":"flags_"}, + {"name":"height_"}, + {"name":"key_"}, + {"name":"size_"}, + {"name":"thumbnail_"}, + {"name":"uploadTimestamp_"}, + {"name":"width_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage", "fields":[ - {"name":"answer_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"busy_", "allowUnsafeAccess":true}, - {"name":"destinationDeviceId_", "allowUnsafeAccess":true}, - {"name":"hangup_", "allowUnsafeAccess":true}, - {"name":"iceUpdate_", "allowUnsafeAccess":true}, - {"name":"legacyHangup_", "allowUnsafeAccess":true}, - {"name":"multiRing_", "allowUnsafeAccess":true}, - {"name":"offer_", "allowUnsafeAccess":true}, - {"name":"opaque_", "allowUnsafeAccess":true} + {"name":"answer_"}, + {"name":"bitField0_"}, + {"name":"busy_"}, + {"name":"destinationDeviceId_"}, + {"name":"hangup_"}, + {"name":"iceUpdate_"}, + {"name":"legacyHangup_"}, + {"name":"multiRing_"}, + {"name":"offer_"}, + {"name":"opaque_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$Hangup", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"deviceId_", "allowUnsafeAccess":true}, - {"name":"id_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"deviceId_"}, + {"name":"id_"}, + {"name":"type_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$IceUpdate", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"id_", "allowUnsafeAccess":true}, - {"name":"line_", "allowUnsafeAccess":true}, - {"name":"mid_", "allowUnsafeAccess":true}, - {"name":"opaque_", "allowUnsafeAccess":true}, - {"name":"sdp_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"id_"}, + {"name":"line_"}, + {"name":"mid_"}, + {"name":"opaque_"}, + {"name":"sdp_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$Offer", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"id_", "allowUnsafeAccess":true}, - {"name":"opaque_", "allowUnsafeAccess":true}, - {"name":"sdp_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"id_"}, + {"name":"opaque_"}, + {"name":"sdp_"}, + {"name":"type_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$Opaque", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"data_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"data_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ContactDetails", "fields":[ - {"name":"archived_", "allowUnsafeAccess":true}, - {"name":"avatar_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"blocked_", "allowUnsafeAccess":true}, - {"name":"color_", "allowUnsafeAccess":true}, - {"name":"expireTimer_", "allowUnsafeAccess":true}, - {"name":"inboxPosition_", "allowUnsafeAccess":true}, - {"name":"name_", "allowUnsafeAccess":true}, - {"name":"number_", "allowUnsafeAccess":true}, - {"name":"profileKey_", "allowUnsafeAccess":true}, - {"name":"uuid_", "allowUnsafeAccess":true}, - {"name":"verified_", "allowUnsafeAccess":true} + {"name":"archived_"}, + {"name":"avatar_"}, + {"name":"bitField0_"}, + {"name":"blocked_"}, + {"name":"color_"}, + {"name":"expireTimer_"}, + {"name":"inboxPosition_"}, + {"name":"name_"}, + {"name":"number_"}, + {"name":"profileKey_"}, + {"name":"uuid_"}, + {"name":"verified_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ContactDetails$Avatar", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"contentType_", "allowUnsafeAccess":true}, - {"name":"length_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"contentType_"}, + {"name":"length_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Content", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"callMessage_", "allowUnsafeAccess":true}, - {"name":"dataMessage_", "allowUnsafeAccess":true}, - {"name":"decryptionErrorMessage_", "allowUnsafeAccess":true}, - {"name":"nullMessage_", "allowUnsafeAccess":true}, - {"name":"receiptMessage_", "allowUnsafeAccess":true}, - {"name":"senderKeyDistributionMessage_", "allowUnsafeAccess":true}, - {"name":"syncMessage_", "allowUnsafeAccess":true}, - {"name":"typingMessage_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"callMessage_"}, + {"name":"dataMessage_"}, + {"name":"decryptionErrorMessage_"}, + {"name":"nullMessage_"}, + {"name":"receiptMessage_"}, + {"name":"senderKeyDistributionMessage_"}, + {"name":"syncMessage_"}, + {"name":"typingMessage_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage", "fields":[ - {"name":"attachments_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"bodyRanges_", "allowUnsafeAccess":true}, - {"name":"body_", "allowUnsafeAccess":true}, - {"name":"contact_", "allowUnsafeAccess":true}, - {"name":"delete_", "allowUnsafeAccess":true}, - {"name":"expireTimer_", "allowUnsafeAccess":true}, - {"name":"flags_", "allowUnsafeAccess":true}, - {"name":"groupCallUpdate_", "allowUnsafeAccess":true}, - {"name":"groupV2_", "allowUnsafeAccess":true}, - {"name":"group_", "allowUnsafeAccess":true}, - {"name":"isViewOnce_", "allowUnsafeAccess":true}, - {"name":"payment_", "allowUnsafeAccess":true}, - {"name":"preview_", "allowUnsafeAccess":true}, - {"name":"profileKey_", "allowUnsafeAccess":true}, - {"name":"quote_", "allowUnsafeAccess":true}, - {"name":"reaction_", "allowUnsafeAccess":true}, - {"name":"requiredProtocolVersion_", "allowUnsafeAccess":true}, - {"name":"sticker_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true} + {"name":"attachments_"}, + {"name":"bitField0_"}, + {"name":"bodyRanges_"}, + {"name":"body_"}, + {"name":"contact_"}, + {"name":"delete_"}, + {"name":"expireTimer_"}, + {"name":"flags_"}, + {"name":"groupCallUpdate_"}, + {"name":"groupV2_"}, + {"name":"group_"}, + {"name":"isViewOnce_"}, + {"name":"payment_"}, + {"name":"preview_"}, + {"name":"profileKey_"}, + {"name":"quote_"}, + {"name":"reaction_"}, + {"name":"requiredProtocolVersion_"}, + {"name":"sticker_"}, + {"name":"timestamp_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$BodyRange", "fields":[ - {"name":"associatedValueCase_", "allowUnsafeAccess":true}, - {"name":"associatedValue_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"length_", "allowUnsafeAccess":true}, - {"name":"start_", "allowUnsafeAccess":true} + {"name":"associatedValueCase_"}, + {"name":"associatedValue_"}, + {"name":"bitField0_"}, + {"name":"length_"}, + {"name":"start_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact", "fields":[ - {"name":"address_", "allowUnsafeAccess":true}, - {"name":"avatar_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"email_", "allowUnsafeAccess":true}, - {"name":"name_", "allowUnsafeAccess":true}, - {"name":"number_", "allowUnsafeAccess":true}, - {"name":"organization_", "allowUnsafeAccess":true} + {"name":"address_"}, + {"name":"avatar_"}, + {"name":"bitField0_"}, + {"name":"email_"}, + {"name":"name_"}, + {"name":"number_"}, + {"name":"organization_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Avatar", "fields":[ - {"name":"avatar_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"isProfile_", "allowUnsafeAccess":true} + {"name":"avatar_"}, + {"name":"bitField0_"}, + {"name":"isProfile_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Email", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"label_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true}, - {"name":"value_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"label_"}, + {"name":"type_"}, + {"name":"value_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Name", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"displayName_", "allowUnsafeAccess":true}, - {"name":"familyName_", "allowUnsafeAccess":true}, - {"name":"givenName_", "allowUnsafeAccess":true}, - {"name":"middleName_", "allowUnsafeAccess":true}, - {"name":"prefix_", "allowUnsafeAccess":true}, - {"name":"suffix_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"displayName_"}, + {"name":"familyName_"}, + {"name":"givenName_"}, + {"name":"middleName_"}, + {"name":"prefix_"}, + {"name":"suffix_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Phone", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"label_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true}, - {"name":"value_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"label_"}, + {"name":"type_"}, + {"name":"value_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$PostalAddress", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"city_", "allowUnsafeAccess":true}, - {"name":"country_", "allowUnsafeAccess":true}, - {"name":"label_", "allowUnsafeAccess":true}, - {"name":"neighborhood_", "allowUnsafeAccess":true}, - {"name":"pobox_", "allowUnsafeAccess":true}, - {"name":"postcode_", "allowUnsafeAccess":true}, - {"name":"region_", "allowUnsafeAccess":true}, - {"name":"street_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"city_"}, + {"name":"country_"}, + {"name":"label_"}, + {"name":"neighborhood_"}, + {"name":"pobox_"}, + {"name":"postcode_"}, + {"name":"region_"}, + {"name":"street_"}, + {"name":"type_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Delete", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"targetSentTimestamp_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"targetSentTimestamp_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$GroupCallUpdate", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"eraId_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"eraId_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Preview", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"date_", "allowUnsafeAccess":true}, - {"name":"description_", "allowUnsafeAccess":true}, - {"name":"image_", "allowUnsafeAccess":true}, - {"name":"title_", "allowUnsafeAccess":true}, - {"name":"url_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"date_"}, + {"name":"description_"}, + {"name":"image_"}, + {"name":"title_"}, + {"name":"url_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Quote", "fields":[ - {"name":"attachments_", "allowUnsafeAccess":true}, - {"name":"authorE164_", "allowUnsafeAccess":true}, - {"name":"authorUuid_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"bodyRanges_", "allowUnsafeAccess":true}, - {"name":"id_", "allowUnsafeAccess":true}, - {"name":"text_", "allowUnsafeAccess":true} + {"name":"attachments_"}, + {"name":"authorE164_"}, + {"name":"authorUuid_"}, + {"name":"bitField0_"}, + {"name":"bodyRanges_"}, + {"name":"id_"}, + {"name":"text_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Reaction", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"emoji_", "allowUnsafeAccess":true}, - {"name":"remove_", "allowUnsafeAccess":true}, - {"name":"targetAuthorUuid_", "allowUnsafeAccess":true}, - {"name":"targetSentTimestamp_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"emoji_"}, + {"name":"remove_"}, + {"name":"targetAuthorUuid_"}, + {"name":"targetSentTimestamp_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Sticker", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"data_", "allowUnsafeAccess":true}, - {"name":"emoji_", "allowUnsafeAccess":true}, - {"name":"packId_", "allowUnsafeAccess":true}, - {"name":"packKey_", "allowUnsafeAccess":true}, - {"name":"stickerId_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"data_"}, + {"name":"emoji_"}, + {"name":"packId_"}, + {"name":"packKey_"}, + {"name":"stickerId_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Envelope", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"content_", "allowUnsafeAccess":true}, - {"name":"legacyMessage_", "allowUnsafeAccess":true}, - {"name":"relay_", "allowUnsafeAccess":true}, - {"name":"serverGuid_", "allowUnsafeAccess":true}, - {"name":"serverTimestamp_", "allowUnsafeAccess":true}, - {"name":"sourceDevice_", "allowUnsafeAccess":true}, - {"name":"sourceE164_", "allowUnsafeAccess":true}, - {"name":"sourceUuid_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"content_"}, + {"name":"legacyMessage_"}, + {"name":"relay_"}, + {"name":"serverGuid_"}, + {"name":"serverTimestamp_"}, + {"name":"sourceDevice_"}, + {"name":"sourceE164_"}, + {"name":"sourceUuid_"}, + {"name":"timestamp_"}, + {"name":"type_"} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$GroupContext", + "fields":[ + {"name":"avatar_"}, + {"name":"bitField0_"}, + {"name":"id_"}, + {"name":"membersE164_"}, + {"name":"members_"}, + {"name":"name_"}, + {"name":"type_"} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$GroupContext$Member", + "fields":[ + {"name":"bitField0_"}, + {"name":"e164_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$GroupContextV2", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"groupChange_", "allowUnsafeAccess":true}, - {"name":"masterKey_", "allowUnsafeAccess":true}, - {"name":"revision_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"groupChange_"}, + {"name":"masterKey_"}, + {"name":"revision_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$NullMessage", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"padding_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"padding_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ReceiptMessage", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"timestamp_"}, + {"name":"type_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"blocked_", "allowUnsafeAccess":true}, - {"name":"configuration_", "allowUnsafeAccess":true}, - {"name":"contacts_", "allowUnsafeAccess":true}, - {"name":"fetchLatest_", "allowUnsafeAccess":true}, - {"name":"groups_", "allowUnsafeAccess":true}, - {"name":"keys_", "allowUnsafeAccess":true}, - {"name":"messageRequestResponse_", "allowUnsafeAccess":true}, - {"name":"outgoingPayment_", "allowUnsafeAccess":true}, - {"name":"padding_", "allowUnsafeAccess":true}, - {"name":"read_", "allowUnsafeAccess":true}, - {"name":"request_", "allowUnsafeAccess":true}, - {"name":"sent_", "allowUnsafeAccess":true}, - {"name":"stickerPackOperation_", "allowUnsafeAccess":true}, - {"name":"verified_", "allowUnsafeAccess":true}, - {"name":"viewOnceOpen_", "allowUnsafeAccess":true}, - {"name":"viewed_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"blocked_"}, + {"name":"configuration_"}, + {"name":"contacts_"}, + {"name":"fetchLatest_"}, + {"name":"groups_"}, + {"name":"keys_"}, + {"name":"messageRequestResponse_"}, + {"name":"outgoingPayment_"}, + {"name":"padding_"}, + {"name":"read_"}, + {"name":"request_"}, + {"name":"sent_"}, + {"name":"stickerPackOperation_"}, + {"name":"verified_"}, + {"name":"viewOnceOpen_"}, + {"name":"viewed_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Blocked", "fields":[ - {"name":"groupIds_", "allowUnsafeAccess":true}, - {"name":"numbers_", "allowUnsafeAccess":true}, - {"name":"uuids_", "allowUnsafeAccess":true} + {"name":"groupIds_"}, + {"name":"numbers_"}, + {"name":"uuids_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Contacts", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"blob_", "allowUnsafeAccess":true}, - {"name":"complete_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"blob_"}, + {"name":"complete_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$FetchLatest", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"type_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Keys", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"storageService_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"storageService_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Read", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"senderE164_", "allowUnsafeAccess":true}, - {"name":"senderUuid_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"senderE164_"}, + {"name":"senderUuid_"}, + {"name":"timestamp_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Request", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"type_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"destinationE164_", "allowUnsafeAccess":true}, - {"name":"destinationUuid_", "allowUnsafeAccess":true}, - {"name":"expirationStartTimestamp_", "allowUnsafeAccess":true}, - {"name":"isRecipientUpdate_", "allowUnsafeAccess":true}, - {"name":"message_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true}, - {"name":"unidentifiedStatus_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"destinationE164_"}, + {"name":"destinationUuid_"}, + {"name":"expirationStartTimestamp_"}, + {"name":"isRecipientUpdate_"}, + {"name":"message_"}, + {"name":"timestamp_"}, + {"name":"unidentifiedStatus_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent$UnidentifiedDeliveryStatus", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"destinationE164_", "allowUnsafeAccess":true}, - {"name":"destinationUuid_", "allowUnsafeAccess":true}, - {"name":"unidentified_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"destinationE164_"}, + {"name":"destinationUuid_"}, + {"name":"unidentified_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$StickerPackOperation", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"packId_", "allowUnsafeAccess":true}, - {"name":"packKey_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"packId_"}, + {"name":"packKey_"}, + {"name":"type_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Viewed", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"senderE164_", "allowUnsafeAccess":true}, - {"name":"senderUuid_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"senderE164_"}, + {"name":"senderUuid_"}, + {"name":"timestamp_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TypingMessage", "fields":[ - {"name":"action_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"groupId_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true} + {"name":"action_"}, + {"name":"bitField0_"}, + {"name":"groupId_"}, + {"name":"timestamp_"} ] }, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Verified", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"destinationE164_", "allowUnsafeAccess":true}, - {"name":"destinationUuid_", "allowUnsafeAccess":true}, - {"name":"identityKey_", "allowUnsafeAccess":true}, - {"name":"nullMessage_", "allowUnsafeAccess":true}, - {"name":"state_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"destinationE164_"}, + {"name":"destinationUuid_"}, + {"name":"identityKey_"}, + {"name":"nullMessage_"}, + {"name":"state_"} ] }, { @@ -2187,34 +2210,34 @@ { "name":"org.whispersystems.signalservice.internal.serialize.protos.AddressProto", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"e164_", "allowUnsafeAccess":true}, - {"name":"relay_", "allowUnsafeAccess":true}, - {"name":"uuid_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"e164_"}, + {"name":"relay_"}, + {"name":"uuid_"} ] }, { "name":"org.whispersystems.signalservice.internal.serialize.protos.MetadataProto", "fields":[ - {"name":"address_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"groupId_", "allowUnsafeAccess":true}, - {"name":"needsReceipt_", "allowUnsafeAccess":true}, - {"name":"senderDevice_", "allowUnsafeAccess":true}, - {"name":"serverDeliveredTimestamp_", "allowUnsafeAccess":true}, - {"name":"serverGuid_", "allowUnsafeAccess":true}, - {"name":"serverReceivedTimestamp_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true} + {"name":"address_"}, + {"name":"bitField0_"}, + {"name":"groupId_"}, + {"name":"needsReceipt_"}, + {"name":"senderDevice_"}, + {"name":"serverDeliveredTimestamp_"}, + {"name":"serverGuid_"}, + {"name":"serverReceivedTimestamp_"}, + {"name":"timestamp_"} ] }, { "name":"org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"dataCase_", "allowUnsafeAccess":true}, - {"name":"data_", "allowUnsafeAccess":true}, - {"name":"localAddress_", "allowUnsafeAccess":true}, - {"name":"metadata_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"dataCase_"}, + {"name":"data_"}, + {"name":"localAddress_"}, + {"name":"metadata_"} ] }, { @@ -2232,32 +2255,32 @@ { "name":"org.whispersystems.signalservice.internal.websocket.WebSocketProtos$WebSocketMessage", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"request_", "allowUnsafeAccess":true}, - {"name":"response_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"request_"}, + {"name":"response_"}, + {"name":"type_"} ] }, { "name":"org.whispersystems.signalservice.internal.websocket.WebSocketProtos$WebSocketRequestMessage", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"body_", "allowUnsafeAccess":true}, - {"name":"headers_", "allowUnsafeAccess":true}, - {"name":"id_", "allowUnsafeAccess":true}, - {"name":"path_", "allowUnsafeAccess":true}, - {"name":"verb_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"body_"}, + {"name":"headers_"}, + {"name":"id_"}, + {"name":"path_"}, + {"name":"verb_"} ] }, { "name":"org.whispersystems.signalservice.internal.websocket.WebSocketProtos$WebSocketResponseMessage", "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"body_", "allowUnsafeAccess":true}, - {"name":"headers_", "allowUnsafeAccess":true}, - {"name":"id_", "allowUnsafeAccess":true}, - {"name":"message_", "allowUnsafeAccess":true}, - {"name":"status_", "allowUnsafeAccess":true} + {"name":"bitField0_"}, + {"name":"body_"}, + {"name":"headers_"}, + {"name":"id_"}, + {"name":"message_"}, + {"name":"status_"} ] }, { @@ -2345,6 +2368,10 @@ "name":"sun.security.provider.certpath.PKIXCertPathValidator", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"sun.security.rsa.PSSParameters", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.rsa.RSAKeyFactory$Legacy", "methods":[{"name":"","parameterTypes":[] }] diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index a33c695e..dcb99cee 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - api("com.github.turasa:signal-service-java:2.15.3_unofficial_24") + api("com.github.turasa:signal-service-java:2.15.3_unofficial_25") implementation("com.google.protobuf:protobuf-javalite:3.10.0") implementation("org.bouncycastle:bcprov-jdk15on:1.69") implementation("org.slf4j:slf4j-api:1.7.30") 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 98b02c7f..0fcb42d2 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -65,14 +65,12 @@ import org.signal.libsignal.metadata.ProtocolLegacyMessageException; import org.signal.libsignal.metadata.ProtocolNoSessionException; import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; 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.zkgroup.InvalidInputException; import org.signal.zkgroup.VerificationFailedException; import org.signal.zkgroup.groups.GroupMasterKey; import org.signal.zkgroup.groups.GroupSecretParams; -import org.signal.zkgroup.profiles.ClientZkProfileOperations; import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKeyCredential; import org.slf4j.Logger; @@ -86,19 +84,12 @@ 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.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.SignalServiceMessagePipe; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; -import org.whispersystems.signalservice.api.SignalServiceMessageSender; +import org.whispersystems.signalservice.api.InvalidMessageStructureException; import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.crypto.ContentHint; -import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; 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.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; @@ -133,9 +124,8 @@ import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserExce import org.whispersystems.signalservice.api.util.DeviceNameUtil; 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.UptimeSleepTimer; import org.whispersystems.signalservice.api.util.UuidUtil; +import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException; import org.whispersystems.signalservice.internal.contacts.crypto.Quote; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; @@ -182,23 +172,13 @@ public class Manager implements Closeable { private final static Logger logger = LoggerFactory.getLogger(Manager.class); - private final CertificateValidator certificateValidator; - private final ServiceEnvironmentConfig serviceEnvironmentConfig; - private final String userAgent; + private final SignalDependencies dependencies; private SignalAccount account; - private final SignalServiceAccountManager accountManager; - private final GroupsV2Api groupsV2Api; - private final GroupsV2Operations groupsV2Operations; - private final SignalServiceMessageReceiver messageReceiver; - private final ClientZkProfileOperations clientZkProfileOperations; private final ExecutorService executor = Executors.newCachedThreadPool(); - private SignalServiceMessagePipe messagePipe = null; - private SignalServiceMessagePipe unidentifiedMessagePipe = null; - private final UnidentifiedAccessHelper unidentifiedAccessHelper; private final ProfileHelper profileHelper; private final GroupV2Helper groupV2Helper; @@ -224,42 +204,19 @@ public class Manager implements Closeable { ) { this.account = account; this.serviceEnvironmentConfig = serviceEnvironmentConfig; - this.certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot()); - this.userAgent = userAgent; - this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( - serviceEnvironmentConfig.getSignalServiceConfiguration())) : null; - final SleepTimer timer = new UptimeSleepTimer(); - this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), - new DynamicCredentialsProvider(account.getUuid(), - account.getUsername(), - account.getPassword(), - account.getDeviceId()), - userAgent, - groupsV2Operations, - ServiceConfig.AUTOMATIC_NETWORK_RETRY, - timer); - this.groupsV2Api = accountManager.getGroupsV2Api(); - final var keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), - serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(), - serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(), - serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(), - 10); - this.pinHelper = new PinHelper(keyBackupService); - this.clientZkProfileOperations = capabilities.isGv2() - ? ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()) - .getProfileOperations() - : null; - this.messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(), - account.getUuid(), + final var credentialsProvider = new DynamicCredentialsProvider(account.getUuid(), account.getUsername(), account.getPassword(), - account.getDeviceId(), + account.getDeviceId()); + this.dependencies = new SignalDependencies(account.getSelfAddress(), + serviceEnvironmentConfig, userAgent, - null, - timer, - clientZkProfileOperations, - ServiceConfig.AUTOMATIC_NETWORK_RETRY); + credentialsProvider, + account.getSignalProtocolStore(), + executor, + sessionLock); + this.pinHelper = new PinHelper(dependencies.getKeyBackupService()); this.unidentifiedAccessHelper = new UnidentifiedAccessHelper(account::getProfileKey, account.getProfileStore()::getProfileKey, @@ -267,14 +224,14 @@ public class Manager implements Closeable { this::getSenderCertificate); this.profileHelper = new ProfileHelper(account.getProfileStore()::getProfileKey, unidentifiedAccessHelper::getAccessFor, - unidentified -> unidentified ? getOrCreateUnidentifiedMessagePipe() : getOrCreateMessagePipe(), - () -> messageReceiver, + dependencies::getProfileService, + dependencies::getMessageReceiver, this::resolveSignalServiceAddress); this.groupV2Helper = new GroupV2Helper(this::getRecipientProfileKeyCredential, this::getRecipientProfile, account::getSelfRecipientId, - groupsV2Operations, - groupsV2Api, + dependencies.getGroupsV2Operations(), + dependencies.getGroupsV2Api(), this::getGroupAuthForToday, this::resolveSignalServiceAddress); this.avatarStore = new AvatarStore(pathConfig.getAvatarsPath()); @@ -350,11 +307,11 @@ public class Manager implements Closeable { days); } } - if (accountManager.getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) { + if (dependencies.getAccountManager().getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) { refreshPreKeys(); } if (account.getUuid() == null) { - account.setUuid(accountManager.getOwnUuid()); + account.setUuid(dependencies.getAccountManager().getOwnUuid()); } updateAccountAttributes(); } @@ -376,17 +333,18 @@ public class Manager implements Closeable { } public void updateAccountAttributes() throws IOException { - accountManager.setAccountAttributes(account.getEncryptedDeviceName(), - null, - account.getLocalRegistrationId(), - true, - // set legacy pin only if no KBS master key is set - account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null, - account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), - account.getSelfUnidentifiedAccessKey(), - account.isUnrestrictedUnidentifiedAccess(), - capabilities, - account.isDiscoverableByPhoneNumber()); + dependencies.getAccountManager() + .setAccountAttributes(account.getEncryptedDeviceName(), + null, + account.getLocalRegistrationId(), + true, + // set legacy pin only if no KBS master key is set + account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null, + account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), + account.getSelfUnidentifiedAccessKey(), + account.isUnrestrictedUnidentifiedAccess(), + capabilities, + account.isDiscoverableByPhoneNumber()); } /** @@ -418,13 +376,14 @@ public class Manager implements Closeable { try (final var streamDetails = avatar == null ? avatarStore.retrieveProfileAvatar(getSelfAddress()) : avatar.isPresent() ? Utils.createStreamDetailsFromFile(avatar.get()) : null) { - accountManager.setVersionedProfile(account.getUuid(), - account.getProfileKey(), - newProfile.getInternalServiceName(), - newProfile.getAbout() == null ? "" : newProfile.getAbout(), - newProfile.getAboutEmoji() == null ? "" : newProfile.getAboutEmoji(), - Optional.absent(), - streamDetails); + dependencies.getAccountManager() + .setVersionedProfile(account.getUuid(), + account.getProfileKey(), + newProfile.getInternalServiceName(), + newProfile.getAbout() == null ? "" : newProfile.getAbout(), + newProfile.getAboutEmoji() == null ? "" : newProfile.getAboutEmoji(), + Optional.absent(), + streamDetails); } if (avatar != null) { @@ -447,19 +406,19 @@ public class Manager implements Closeable { // When setting an empty GCM id, the Signal-Server also sets the fetchesMessages property to false. // If this is the master device, other users can't send messages to this number anymore. // If this is a linked device, other users can still send messages, but this device doesn't receive them anymore. - accountManager.setGcmId(Optional.absent()); + dependencies.getAccountManager().setGcmId(Optional.absent()); account.setRegistered(false); } public void deleteAccount() throws IOException { - accountManager.deleteAccount(); + dependencies.getAccountManager().deleteAccount(); account.setRegistered(false); } public List getLinkedDevices() throws IOException { - var devices = accountManager.getDevices(); + var devices = dependencies.getAccountManager().getDevices(); account.setMultiDevice(devices.size() > 1); var identityKey = account.getIdentityKeyPair().getPrivateKey(); return devices.stream().map(d -> { @@ -476,8 +435,8 @@ public class Manager implements Closeable { } public void removeLinkedDevices(int deviceId) throws IOException { - accountManager.removeDevice(deviceId); - var devices = accountManager.getDevices(); + dependencies.getAccountManager().removeDevice(deviceId); + var devices = dependencies.getAccountManager().getDevices(); account.setMultiDevice(devices.size() > 1); } @@ -489,13 +448,14 @@ public class Manager implements Closeable { private void addDevice(String deviceIdentifier, ECPublicKey deviceKey) throws IOException, InvalidKeyException { var identityKeyPair = getIdentityKeyPair(); - var verificationCode = accountManager.getNewDeviceVerificationCode(); + var verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode(); - accountManager.addDevice(deviceIdentifier, - deviceKey, - identityKeyPair, - Optional.of(account.getProfileKey().serialize()), - verificationCode); + dependencies.getAccountManager() + .addDevice(deviceIdentifier, + deviceKey, + identityKeyPair, + Optional.of(account.getProfileKey().serialize()), + verificationCode); account.setMultiDevice(true); } @@ -512,9 +472,6 @@ public class Manager implements Closeable { account.setRegistrationLockPin(pin.get(), masterKey); } else { - // Remove legacy registration lock - accountManager.removeRegistrationLockV1(); - // Remove KBS Pin pinHelper.removeRegistrationLockPin(); @@ -527,7 +484,7 @@ public class Manager implements Closeable { final var identityKeyPair = getIdentityKeyPair(); var signedPreKeyRecord = generateSignedPreKey(identityKeyPair); - accountManager.setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys); + dependencies.getAccountManager().setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys); } private List generatePreKeys() { @@ -548,39 +505,6 @@ public class Manager implements Closeable { return record; } - private SignalServiceMessagePipe getOrCreateMessagePipe() { - if (messagePipe == null) { - messagePipe = messageReceiver.createMessagePipe(); - } - return messagePipe; - } - - private SignalServiceMessagePipe getOrCreateUnidentifiedMessagePipe() { - if (unidentifiedMessagePipe == null) { - unidentifiedMessagePipe = messageReceiver.createUnidentifiedMessagePipe(); - } - return unidentifiedMessagePipe; - } - - private SignalServiceMessageSender createMessageSender() { - return new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(), - account.getUuid(), - account.getUsername(), - account.getPassword(), - account.getDeviceId(), - account.getSignalProtocolStore(), - sessionLock, - userAgent, - account.isMultiDevice(), - Optional.fromNullable(messagePipe), - Optional.fromNullable(unidentifiedMessagePipe), - Optional.absent(), - clientZkProfileOperations, - executor, - ServiceConfig.MAX_ENVELOPE_SIZE, - ServiceConfig.AUTOMATIC_NETWORK_RETRY); - } - public Profile getRecipientProfile( RecipientId recipientId ) { @@ -1180,14 +1104,15 @@ public class Manager implements Closeable { ) throws IOException { final var today = currentTimeDays(); // Returns credentials for the next 7 days - final var credentials = groupsV2Api.getCredentials(today); + final var credentials = dependencies.getGroupsV2Api().getCredentials(today); // TODO cache credentials until they expire var authCredentialResponse = credentials.get(today); try { - return groupsV2Api.getGroupsV2AuthorizationString(account.getUuid(), - today, - groupSecretParams, - authCredentialResponse); + return dependencies.getGroupsV2Api() + .getGroupsV2AuthorizationString(account.getUuid(), + today, + groupSecretParams, + authCredentialResponse); } catch (VerificationFailedException e) { throw new IOException(e); } @@ -1264,9 +1189,10 @@ public class Manager implements Closeable { List.of(messageId), System.currentTimeMillis()); - createMessageSender().sendReceipt(remoteAddress, - unidentifiedAccessHelper.getAccessFor(resolveRecipient(remoteAddress)), - receiptMessage); + dependencies.getMessageSender() + .sendReceipt(remoteAddress, + unidentifiedAccessHelper.getAccessFor(resolveRecipient(remoteAddress)), + receiptMessage); } public Pair> sendMessage( @@ -1277,7 +1203,7 @@ public class Manager implements Closeable { var attachmentStreams = AttachmentUtils.getSignalServiceAttachments(attachments); // Upload attachments here, so we only upload once even for multiple recipients - var messageSender = createMessageSender(); + var messageSender = dependencies.getMessageSender(); var attachmentPointers = new ArrayList(attachmentStreams.size()); for (var attachment : attachmentStreams) { if (attachment.isStream()) { @@ -1351,11 +1277,6 @@ public class Manager implements Closeable { } } - public String getContactName(String number) throws InvalidNumberException { - var contact = account.getContactStore().getContact(canonicalizeAndResolveRecipient(number)); - return contact == null || contact.getName() == null ? "" : contact.getName(); - } - public void setContactName(String number, String name) throws InvalidNumberException, NotMasterDeviceException { if (!account.isMasterDevice()) { throw new NotMasterDeviceException(); @@ -1442,7 +1363,7 @@ public class Manager implements Closeable { public String uploadStickerPack(File path) throws IOException, StickerPackInvalidException { var manifest = StickerUtils.getSignalServiceStickerManifestUpload(path); - var messageSender = createMessageSender(); + var messageSender = dependencies.getMessageSender(); var packKey = KeyUtils.createStickerUploadKey(); var packIdString = messageSender.uploadStickerManifest(manifest, packKey); @@ -1536,9 +1457,9 @@ public class Manager implements Closeable { byte[] certificate; try { if (account.isPhoneNumberShared()) { - certificate = accountManager.getSenderCertificate(); + certificate = dependencies.getAccountManager().getSenderCertificate(); } else { - certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy(); + certificate = dependencies.getAccountManager().getSenderCertificateForPhoneNumberPrivacy(); } } catch (IOException e) { logger.warn("Failed to get sender certificate, ignoring: {}", e.getMessage()); @@ -1549,7 +1470,7 @@ public class Manager implements Closeable { } private void sendSyncMessage(SignalServiceSyncMessage message) throws IOException, UntrustedIdentityException { - var messageSender = createMessageSender(); + var messageSender = dependencies.getMessageSender(); messageSender.sendSyncMessage(message, unidentifiedAccessHelper.getAccessForSync()); } @@ -1604,9 +1525,10 @@ public class Manager implements Closeable { private Map getRegisteredUsers(final Set numbers) throws IOException { try { - return accountManager.getRegisteredUsers(ServiceConfig.getIasKeyStore(), - numbers, - serviceEnvironmentConfig.getCdsMrenclave()); + return dependencies.getAccountManager() + .getRegisteredUsers(ServiceConfig.getIasKeyStore(), + numbers, + serviceEnvironmentConfig.getCdsMrenclave()); } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) { throw new IOException(e); } @@ -1623,7 +1545,7 @@ public class Manager implements Closeable { ) throws IOException, UntrustedIdentityException { final var timestamp = System.currentTimeMillis(); var message = new SignalServiceTypingMessage(action.toSignalService(), timestamp, Optional.absent()); - var messageSender = createMessageSender(); + var messageSender = dependencies.getMessageSender(); for (var recipientId : recipientIds) { final var address = resolveSignalServiceAddress(recipientId); messageSender.sendTyping(address, unidentifiedAccessHelper.getAccessFor(recipientId), message); @@ -1638,7 +1560,7 @@ public class Manager implements Closeable { final var message = new SignalServiceTypingMessage(action.toSignalService(), timestamp, Optional.of(groupId.serialize())); - final var messageSender = createMessageSender(); + final var messageSender = dependencies.getMessageSender(); final var recipientIdList = new ArrayList<>(g.getMembersWithout(account.getSelfRecipientId())); final var addresses = recipientIdList.stream() .map(this::resolveSignalServiceAddress) @@ -1651,14 +1573,13 @@ public class Manager implements Closeable { ) throws IOException { final var timestamp = System.currentTimeMillis(); messageBuilder.withTimestamp(timestamp); - getOrCreateMessagePipe(); - getOrCreateUnidentifiedMessagePipe(); + SignalServiceDataMessage message = null; try { message = messageBuilder.build(); if (message.getGroupContext().isPresent()) { try { - var messageSender = createMessageSender(); + var messageSender = dependencies.getMessageSender(); final var isRecipientUpdate = false; final var recipientIdList = new ArrayList<>(recipientIds); final var addresses = recipientIdList.stream() @@ -1668,7 +1589,9 @@ public class Manager implements Closeable { unidentifiedAccessHelper.getAccessFor(recipientIdList), isRecipientUpdate, ContentHint.DEFAULT, - message); + message, + sendResult -> logger.trace("Partial message send result: {}", sendResult.isSuccess()), + () -> false); for (var r : result) { if (r.getIdentityFailure() != null) { @@ -1712,8 +1635,6 @@ public class Manager implements Closeable { ) throws IOException { final var timestamp = System.currentTimeMillis(); messageBuilder.withTimestamp(timestamp); - getOrCreateMessagePipe(); - getOrCreateUnidentifiedMessagePipe(); final var recipientId = account.getSelfRecipientId(); final var contact = account.getContactStore().getContact(recipientId); @@ -1726,7 +1647,7 @@ public class Manager implements Closeable { } private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException { - var messageSender = createMessageSender(); + var messageSender = dependencies.getMessageSender(); var recipientId = account.getSelfRecipientId(); @@ -1741,12 +1662,7 @@ public class Manager implements Closeable { var syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript); try { - var startTime = System.currentTimeMillis(); - messageSender.sendSyncMessage(syncMessage, unidentifiedAccess); - return SendMessageResult.success(recipient, - unidentifiedAccess.isPresent(), - false, - System.currentTimeMillis() - startTime); + return messageSender.sendSyncMessage(syncMessage, unidentifiedAccess); } catch (UntrustedIdentityException e) { return SendMessageResult.identityFailure(recipient, e.getIdentityKey()); } @@ -1755,7 +1671,7 @@ public class Manager implements Closeable { private SendMessageResult sendMessage( RecipientId recipientId, SignalServiceDataMessage message ) throws IOException { - var messageSender = createMessageSender(); + var messageSender = dependencies.getMessageSender(); final var address = resolveSignalServiceAddress(recipientId); try { @@ -1777,7 +1693,7 @@ public class Manager implements Closeable { } private SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException { - var messageSender = createMessageSender(); + var messageSender = dependencies.getMessageSender(); final var address = resolveSignalServiceAddress(recipientId); try { @@ -1793,12 +1709,8 @@ public class Manager implements Closeable { } } - private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, ProtocolUntrustedIdentityException { - var cipher = new SignalServiceCipher(account.getSelfAddress(), - account.getSignalProtocolStore(), - sessionLock, - certificateValidator); - return cipher.decrypt(envelope); + private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, ProtocolUntrustedIdentityException, InvalidMessageStructureException { + return dependencies.getCipher().decrypt(envelope); } private void handleEndSession(RecipientId recipientId) { @@ -2082,7 +1994,8 @@ public class Manager implements Closeable { Set queuedActions = null; - final var messagePipe = getOrCreateMessagePipe(); + final var signalWebSocket = dependencies.getSignalWebSocket(); + signalWebSocket.connect(); var hasCaughtUpWithOldMessages = false; @@ -2094,7 +2007,7 @@ public class Manager implements Closeable { account.setLastReceiveTimestamp(System.currentTimeMillis()); logger.debug("Checking for new message from server"); try { - var result = messagePipe.readOrEmpty(timeout, unit, envelope1 -> { + var result = signalWebSocket.readOrEmpty(unit.toMillis(timeout), envelope1 -> { final var recipientId = envelope1.hasSource() ? resolveRecipient(envelope1.getSourceIdentifier()) : null; @@ -2132,6 +2045,10 @@ public class Manager implements Closeable { } else { throw e; } + } catch (WebSocketUnavailableException e) { + logger.debug("Pipe unexpectedly unavailable, connecting"); + signalWebSocket.connect(); + continue; } catch (TimeoutException e) { if (returnOnTimeout) return; continue; @@ -2602,12 +2519,11 @@ public class Manager implements Closeable { private void retrieveGroupV2Avatar( GroupSecretParams groupSecretParams, String cdnKey, OutputStream outputStream ) throws IOException { - var groupOperations = groupsV2Operations.forGroup(groupSecretParams); + var groupOperations = dependencies.getGroupsV2Operations().forGroup(groupSecretParams); var tmpFile = IOUtils.createTempFile(); - try (InputStream input = messageReceiver.retrieveGroupsV2ProfileAvatar(cdnKey, - tmpFile, - ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { + try (InputStream input = dependencies.getMessageReceiver() + .retrieveGroupsV2ProfileAvatar(cdnKey, tmpFile, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { var encryptedData = IOUtils.readFully(input); var decryptedData = groupOperations.decryptAvatar(encryptedData); @@ -2627,10 +2543,11 @@ public class Manager implements Closeable { String avatarPath, ProfileKey profileKey, OutputStream outputStream ) throws IOException { var tmpFile = IOUtils.createTempFile(); - try (var input = messageReceiver.retrieveProfileAvatar(avatarPath, - tmpFile, - profileKey, - ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { + try (var input = dependencies.getMessageReceiver() + .retrieveProfileAvatar(avatarPath, + tmpFile, + profileKey, + ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { // Use larger buffer size to prevent AssertionError: Need: 12272 but only have: 8192 ... IOUtils.copyStream(input, outputStream, (int) ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE); } finally { @@ -2678,7 +2595,8 @@ public class Manager implements Closeable { private InputStream retrieveAttachmentAsStream( SignalServiceAttachmentPointer pointer, File tmpFile ) throws IOException, InvalidMessageException, MissingConfigurationException { - return messageReceiver.retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE); + return dependencies.getMessageReceiver() + .retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE); } void sendGroups() throws IOException, UntrustedIdentityException { @@ -2979,7 +2897,10 @@ public class Manager implements Closeable { } private void enqueueJob(Job job) { - var context = new Context(account, accountManager, messageReceiver, stickerPackStore); + var context = new Context(account, + dependencies.getAccountManager(), + dependencies.getMessageReceiver(), + stickerPackStore); job.run(context); } @@ -2991,15 +2912,7 @@ public class Manager implements Closeable { void close(boolean closeAccount) throws IOException { executor.shutdown(); - if (messagePipe != null) { - messagePipe.shutdown(); - messagePipe = null; - } - - if (unidentifiedMessagePipe != null) { - unidentifiedMessagePipe.shutdown(); - unidentifiedMessagePipe = null; - } + dependencies.getSignalWebSocket().disconnect(); if (closeAccount && account != null) { account.close(); diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 44a53e38..eda24052 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -32,8 +32,6 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; import org.whispersystems.signalservice.api.util.DeviceNameUtil; -import org.whispersystems.signalservice.api.util.SleepTimer; -import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import java.io.File; @@ -62,7 +60,6 @@ public class ProvisioningManager { tempIdentityKey = KeyUtils.generateIdentityKeyPair(); registrationId = KeyHelper.generateRegistrationId(false); password = KeyUtils.createPassword(); - final SleepTimer timer = new UptimeSleepTimer(); GroupsV2Operations groupsV2Operations; try { groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())); @@ -73,8 +70,7 @@ public class ProvisioningManager { new DynamicCredentialsProvider(null, null, password, SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, - ServiceConfig.AUTOMATIC_NETWORK_RETRY, - timer); + ServiceConfig.AUTOMATIC_NETWORK_RETRY); } public static ProvisioningManager init( @@ -174,7 +170,7 @@ public class ProvisioningManager { } } - private boolean canRelinkExistingAccount(final String number) throws UserAlreadyExists, IOException { + private boolean canRelinkExistingAccount(final String number) throws IOException { final SignalAccount signalAccount; try { signalAccount = SignalAccount.load(pathConfig.getDataPath(), number, false); diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java index 0ebde3c2..653f9cb4 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -33,8 +33,6 @@ import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.util.SleepTimer; -import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.internal.push.LockedException; import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; @@ -68,7 +66,6 @@ public class RegistrationManager implements Closeable { this.serviceEnvironmentConfig = serviceEnvironmentConfig; this.userAgent = userAgent; - final SleepTimer timer = new UptimeSleepTimer(); GroupsV2Operations groupsV2Operations; try { groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())); @@ -81,8 +78,7 @@ public class RegistrationManager implements Closeable { null, account.getUsername(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, - ServiceConfig.AUTOMATIC_NETWORK_RETRY, - timer); + ServiceConfig.AUTOMATIC_NETWORK_RETRY); final var keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(), serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(), diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java new file mode 100644 index 00000000..fef8351f --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java @@ -0,0 +1,150 @@ +package org.asamk.signal.manager; + +import org.asamk.signal.manager.config.ServiceConfig; +import org.asamk.signal.manager.config.ServiceEnvironmentConfig; +import org.signal.libsignal.metadata.certificate.CertificateValidator; +import org.signal.zkgroup.profiles.ClientZkProfileOperations; +import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.KeyBackupService; +import org.whispersystems.signalservice.api.SignalServiceAccountManager; +import org.whispersystems.signalservice.api.SignalServiceDataStore; +import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; +import org.whispersystems.signalservice.api.SignalServiceMessageSender; +import org.whispersystems.signalservice.api.SignalSessionLock; +import org.whispersystems.signalservice.api.SignalWebSocket; +import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; +import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; +import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api; +import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.services.ProfileService; +import org.whispersystems.signalservice.api.util.SleepTimer; +import org.whispersystems.signalservice.api.util.UptimeSleepTimer; +import org.whispersystems.signalservice.api.websocket.WebSocketFactory; +import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; +import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; + +import java.util.concurrent.ExecutorService; + +import static org.asamk.signal.manager.config.ServiceConfig.capabilities; + +public class SignalDependencies { + + private final SignalServiceAccountManager accountManager; + private final GroupsV2Api groupsV2Api; + private final GroupsV2Operations groupsV2Operations; + + private final SignalWebSocket signalWebSocket; + private final SignalServiceMessageReceiver messageReceiver; + private final SignalServiceMessageSender messageSender; + + private final KeyBackupService keyBackupService; + private final ProfileService profileService; + private final SignalServiceCipher cipher; + + public SignalDependencies( + final SignalServiceAddress selfAddress, + final ServiceEnvironmentConfig serviceEnvironmentConfig, + final String userAgent, + final DynamicCredentialsProvider credentialsProvider, + final SignalServiceDataStore dataStore, + final ExecutorService executor, + final SignalSessionLock sessionLock + ) { + this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( + serviceEnvironmentConfig.getSignalServiceConfiguration())) : null; + final SleepTimer timer = new UptimeSleepTimer(); + this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), + credentialsProvider, + userAgent, + groupsV2Operations, + ServiceConfig.AUTOMATIC_NETWORK_RETRY); + this.groupsV2Api = accountManager.getGroupsV2Api(); + this.keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), + serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(), + serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(), + serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(), + 10); + final ClientZkProfileOperations clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create( + serviceEnvironmentConfig.getSignalServiceConfiguration()).getProfileOperations() : null; + this.messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(), + credentialsProvider, + userAgent, + clientZkProfileOperations, + ServiceConfig.AUTOMATIC_NETWORK_RETRY); + + final var healthMonitor = new SignalWebSocketHealthMonitor(timer); + final WebSocketFactory webSocketFactory = new WebSocketFactory() { + @Override + public WebSocketConnection createWebSocket() { + return new WebSocketConnection("normal", + serviceEnvironmentConfig.getSignalServiceConfiguration(), + Optional.of(credentialsProvider), + userAgent, + healthMonitor); + } + + @Override + public WebSocketConnection createUnidentifiedWebSocket() { + return new WebSocketConnection("unidentified", + serviceEnvironmentConfig.getSignalServiceConfiguration(), + Optional.absent(), + userAgent, + healthMonitor); + } + }; + this.signalWebSocket = new SignalWebSocket(webSocketFactory); + healthMonitor.monitor(signalWebSocket); + this.profileService = new ProfileService(clientZkProfileOperations, messageReceiver, signalWebSocket); + + final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot()); + this.cipher = new SignalServiceCipher(selfAddress, dataStore, sessionLock, certificateValidator); + this.messageSender = new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(), + credentialsProvider, + dataStore, + sessionLock, + userAgent, + signalWebSocket, + Optional.absent(), + clientZkProfileOperations, + executor, + ServiceConfig.MAX_ENVELOPE_SIZE, + ServiceConfig.AUTOMATIC_NETWORK_RETRY); + } + + public SignalServiceAccountManager getAccountManager() { + return accountManager; + } + + public GroupsV2Api getGroupsV2Api() { + return groupsV2Api; + } + + public GroupsV2Operations getGroupsV2Operations() { + return groupsV2Operations; + } + + public SignalWebSocket getSignalWebSocket() { + return signalWebSocket; + } + + public SignalServiceMessageReceiver getMessageReceiver() { + return messageReceiver; + } + + public SignalServiceMessageSender getMessageSender() { + return messageSender; + } + + public KeyBackupService getKeyBackupService() { + return keyBackupService; + } + + public ProfileService getProfileService() { + return profileService; + } + + public SignalServiceCipher getCipher() { + return cipher; + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java b/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java new file mode 100644 index 00000000..24a673ff --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java @@ -0,0 +1,200 @@ +package org.asamk.signal.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.whispersystems.libsignal.util.guava.Preconditions; +import org.whispersystems.signalservice.api.SignalWebSocket; +import org.whispersystems.signalservice.api.util.SleepTimer; +import org.whispersystems.signalservice.api.websocket.HealthMonitor; +import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState; +import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.schedulers.Schedulers; + +/** + * Monitors the health of the identified and unidentified WebSockets. If either one appears to be + * unhealthy, will trigger restarting both. + *

+ * The monitor is also responsible for sending heartbeats/keep-alive messages to prevent + * timeouts. + */ +public final class SignalWebSocketHealthMonitor implements HealthMonitor { + + private final static Logger logger = LoggerFactory.getLogger(SignalWebSocketHealthMonitor.class); + + private static final long KEEP_ALIVE_SEND_CADENCE = TimeUnit.SECONDS.toMillis(WebSocketConnection.KEEPALIVE_TIMEOUT_SECONDS); + private static final long MAX_TIME_SINCE_SUCCESSFUL_KEEP_ALIVE = KEEP_ALIVE_SEND_CADENCE * 3; + + private SignalWebSocket signalWebSocket; + private final SleepTimer sleepTimer; + + private volatile KeepAliveSender keepAliveSender; + + private final HealthState identified = new HealthState(); + private final HealthState unidentified = new HealthState(); + + public SignalWebSocketHealthMonitor(SleepTimer sleepTimer) { + this.sleepTimer = sleepTimer; + } + + public void monitor(SignalWebSocket signalWebSocket) { + Preconditions.checkNotNull(signalWebSocket); + Preconditions.checkArgument(this.signalWebSocket == null, "monitor can only be called once"); + + this.signalWebSocket = signalWebSocket; + + //noinspection ResultOfMethodCallIgnored + signalWebSocket.getWebSocketState() + .subscribeOn(Schedulers.computation()) + .observeOn(Schedulers.computation()) + .distinctUntilChanged() + .subscribe(s -> onStateChange(s, identified)); + + //noinspection ResultOfMethodCallIgnored + signalWebSocket.getUnidentifiedWebSocketState() + .subscribeOn(Schedulers.computation()) + .observeOn(Schedulers.computation()) + .distinctUntilChanged() + .subscribe(s -> onStateChange(s, unidentified)); + } + + private synchronized void onStateChange(WebSocketConnectionState connectionState, HealthState healthState) { + switch (connectionState) { + case CONNECTED: + logger.debug("WebSocket is now connected"); + break; + case AUTHENTICATION_FAILED: + logger.debug("WebSocket authentication failed"); + break; + case FAILED: + logger.debug("WebSocket connection failed"); + break; + } + + healthState.needsKeepAlive = connectionState == WebSocketConnectionState.CONNECTED; + + if (keepAliveSender == null && isKeepAliveNecessary()) { + keepAliveSender = new KeepAliveSender(); + keepAliveSender.start(); + } else if (keepAliveSender != null && !isKeepAliveNecessary()) { + keepAliveSender.shutdown(); + keepAliveSender = null; + } + } + + @Override + public void onKeepAliveResponse(long sentTimestamp, boolean isIdentifiedWebSocket) { + if (isIdentifiedWebSocket) { + identified.lastKeepAliveReceived = System.currentTimeMillis(); + } else { + unidentified.lastKeepAliveReceived = System.currentTimeMillis(); + } + } + + @Override + public void onMessageError(int status, boolean isIdentifiedWebSocket) { + if (status == 409) { + HealthState healthState = (isIdentifiedWebSocket ? identified : unidentified); + if (healthState.mismatchErrorTracker.addSample(System.currentTimeMillis())) { + logger.warn("Received too many mismatch device errors, forcing new websockets."); + signalWebSocket.forceNewWebSockets(); + } + } + } + + private boolean isKeepAliveNecessary() { + return identified.needsKeepAlive || unidentified.needsKeepAlive; + } + + private static class HealthState { + + private final HttpErrorTracker mismatchErrorTracker = new HttpErrorTracker(5, TimeUnit.MINUTES.toMillis(1)); + + private volatile boolean needsKeepAlive; + private volatile long lastKeepAliveReceived; + } + + /** + * Sends periodic heartbeats/keep-alives over both WebSockets to prevent connection timeouts. If + * either WebSocket fails 3 times to get a return heartbeat both are forced to be recreated. + */ + private class KeepAliveSender extends Thread { + + private volatile boolean shouldKeepRunning = true; + + public void run() { + identified.lastKeepAliveReceived = System.currentTimeMillis(); + unidentified.lastKeepAliveReceived = System.currentTimeMillis(); + + while (shouldKeepRunning && isKeepAliveNecessary()) { + try { + sleepTimer.sleep(KEEP_ALIVE_SEND_CADENCE); + + if (shouldKeepRunning && isKeepAliveNecessary()) { + long keepAliveRequiredSinceTime = System.currentTimeMillis() + - MAX_TIME_SINCE_SUCCESSFUL_KEEP_ALIVE; + + if (identified.lastKeepAliveReceived < keepAliveRequiredSinceTime + || unidentified.lastKeepAliveReceived < keepAliveRequiredSinceTime) { + logger.warn("Missed keep alives, identified last: " + + identified.lastKeepAliveReceived + + " unidentified last: " + + unidentified.lastKeepAliveReceived + + " needed by: " + + keepAliveRequiredSinceTime); + signalWebSocket.forceNewWebSockets(); + } else { + signalWebSocket.sendKeepAlive(); + } + } + } catch (Throwable e) { + logger.warn("Error occured in KeepAliveSender, ignoring ...", e); + } + } + } + + public void shutdown() { + shouldKeepRunning = false; + } + } + + private final static class HttpErrorTracker { + + private final long[] timestamps; + private final long errorTimeRange; + + public HttpErrorTracker(int samples, long errorTimeRange) { + this.timestamps = new long[samples]; + this.errorTimeRange = errorTimeRange; + } + + public synchronized boolean addSample(long now) { + long errorsMustBeAfter = now - errorTimeRange; + int count = 1; + int minIndex = 0; + + for (int i = 0; i < timestamps.length; i++) { + if (timestamps[i] < errorsMustBeAfter) { + timestamps[i] = 0; + } else if (timestamps[i] != 0) { + count++; + } + + if (timestamps[i] < timestamps[minIndex]) { + minIndex = i; + } + } + + timestamps[minIndex] = now; + + if (count >= timestamps.length) { + Arrays.fill(timestamps, 0); + return true; + } + return false; + } + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java index 9e0a4375..3567bd7f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java @@ -34,7 +34,12 @@ public class ServiceConfig { } catch (Throwable ignored) { zkGroupAvailable = false; } - capabilities = new AccountAttributes.Capabilities(false, zkGroupAvailable, false, zkGroupAvailable, false); + capabilities = new AccountAttributes.Capabilities(false, + zkGroupAvailable, + false, + zkGroupAvailable, + false, + false); try { TrustStore contactTrustStore = new IasTrustStore(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java index 34ae4bfa..206994f5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java @@ -207,7 +207,7 @@ public class GroupV2Helper { var change = name != null ? groupOperations.createModifyGroupTitle(name) : GroupChange.Actions.newBuilder(); if (description != null) { - change.setModifyDescription(groupOperations.createModifyGroupDescription(description)); + change.setModifyDescription(groupOperations.createModifyGroupDescriptionAction(description)); } if (avatarFile != null) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/MessagePipeProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/MessagePipeProvider.java deleted file mode 100644 index 7739928c..00000000 --- a/lib/src/main/java/org/asamk/signal/manager/helper/MessagePipeProvider.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.asamk.signal.manager.helper; - -import org.whispersystems.signalservice.api.SignalServiceMessagePipe; - -public interface MessagePipeProvider { - - SignalServiceMessagePipe getMessagePipe(boolean unidentified); -} diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java index 2676135b..c3c74b0b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -9,14 +9,12 @@ import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; -import org.whispersystems.signalservice.internal.util.concurrent.CascadingFuture; -import org.whispersystems.signalservice.internal.util.concurrent.ListenableFuture; +import org.whispersystems.signalservice.api.services.ProfileService; +import org.whispersystems.signalservice.internal.ServiceResponse; import java.io.IOException; -import java.util.Arrays; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; + +import io.reactivex.rxjava3.core.Single; public final class ProfileHelper { @@ -24,7 +22,7 @@ public final class ProfileHelper { private final UnidentifiedAccessProvider unidentifiedAccessProvider; - private final MessagePipeProvider messagePipeProvider; + private final ProfileServiceProvider profileServiceProvider; private final MessageReceiverProvider messageReceiverProvider; @@ -33,13 +31,13 @@ public final class ProfileHelper { public ProfileHelper( final ProfileKeyProvider profileKeyProvider, final UnidentifiedAccessProvider unidentifiedAccessProvider, - final MessagePipeProvider messagePipeProvider, + final ProfileServiceProvider profileServiceProvider, final MessageReceiverProvider messageReceiverProvider, final SignalServiceAddressResolver addressResolver ) { this.profileKeyProvider = profileKeyProvider; this.unidentifiedAccessProvider = unidentifiedAccessProvider; - this.messagePipeProvider = messagePipeProvider; + this.profileServiceProvider = profileServiceProvider; this.messageReceiverProvider = messageReceiverProvider; this.addressResolver = addressResolver; } @@ -48,8 +46,8 @@ public final class ProfileHelper { RecipientId recipientId, SignalServiceProfile.RequestType requestType ) throws IOException { try { - return retrieveProfile(recipientId, requestType).get(10, TimeUnit.SECONDS); - } catch (ExecutionException e) { + return retrieveProfile(recipientId, requestType).blockingGet(); + } catch (RuntimeException e) { if (e.getCause() instanceof PushNetworkException) { throw (PushNetworkException) e.getCause(); } else if (e.getCause() instanceof NotFoundException) { @@ -57,79 +55,55 @@ public final class ProfileHelper { } else { throw new IOException(e); } - } catch (InterruptedException | TimeoutException e) { - throw new PushNetworkException(e); } } - public ListenableFuture retrieveProfile( + public SignalServiceProfile retrieveProfileSync(String username) throws IOException { + return messageReceiverProvider.getMessageReceiver().retrieveProfileByUsername(username, Optional.absent()); + } + + public Single retrieveProfile( RecipientId recipientId, SignalServiceProfile.RequestType requestType - ) { + ) throws IOException { var unidentifiedAccess = getUnidentifiedAccess(recipientId); var profileKey = Optional.fromNullable(profileKeyProvider.getProfileKey(recipientId)); final var address = addressResolver.resolveSignalServiceAddress(recipientId); - if (unidentifiedAccess.isPresent()) { - return new CascadingFuture<>(Arrays.asList(() -> getPipeRetrievalFuture(address, - profileKey, - unidentifiedAccess, - requestType), - () -> getSocketRetrievalFuture(address, profileKey, unidentifiedAccess, requestType), - () -> getPipeRetrievalFuture(address, profileKey, Optional.absent(), requestType), - () -> getSocketRetrievalFuture(address, profileKey, Optional.absent(), requestType)), - e -> !(e instanceof NotFoundException)); - } else { - return new CascadingFuture<>(Arrays.asList(() -> getPipeRetrievalFuture(address, - profileKey, - Optional.absent(), - requestType), () -> getSocketRetrievalFuture(address, profileKey, Optional.absent(), requestType)), - e -> !(e instanceof NotFoundException)); - } + return retrieveProfile(address, profileKey, unidentifiedAccess, requestType); } - private ListenableFuture getPipeRetrievalFuture( + private Single retrieveProfile( SignalServiceAddress address, Optional profileKey, Optional unidentifiedAccess, SignalServiceProfile.RequestType requestType ) throws IOException { - var unidentifiedPipe = messagePipeProvider.getMessagePipe(true); - var pipe = unidentifiedPipe != null && unidentifiedAccess.isPresent() - ? unidentifiedPipe - : messagePipeProvider.getMessagePipe(false); - if (pipe != null) { - try { - return pipe.getProfile(address, profileKey, unidentifiedAccess, requestType); - } catch (NoClassDefFoundError e) { - // Native zkgroup lib not available for ProfileKey - if (!address.getNumber().isPresent()) { - throw new NotFoundException("Can't request profile without number"); - } - var addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber()); - return pipe.getProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType); - } - } + var profileService = profileServiceProvider.getProfileService(); - throw new IOException("No pipe available!"); - } - - private ListenableFuture getSocketRetrievalFuture( - SignalServiceAddress address, - Optional profileKey, - Optional unidentifiedAccess, - SignalServiceProfile.RequestType requestType - ) throws NotFoundException { - var receiver = messageReceiverProvider.getMessageReceiver(); + Single> responseSingle; try { - return receiver.retrieveProfile(address, profileKey, unidentifiedAccess, requestType); + responseSingle = profileService.getProfile(address, profileKey, unidentifiedAccess, requestType); } catch (NoClassDefFoundError e) { // Native zkgroup lib not available for ProfileKey if (!address.getNumber().isPresent()) { throw new NotFoundException("Can't request profile without number"); } var addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber()); - return receiver.retrieveProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType); + responseSingle = profileService.getProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType); } + + return responseSingle.map(pair -> { + var processor = new ProfileService.ProfileResponseProcessor(pair); + if (processor.hasResult()) { + return processor.getResult(); + } else if (processor.notFound()) { + throw new NotFoundException("Profile not found"); + } else { + throw pair.getExecutionError() + .or(pair.getApplicationError()) + .or(new IOException("Unknown error while retrieving profile")); + } + }); } private Optional getUnidentifiedAccess(RecipientId recipientId) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileServiceProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileServiceProvider.java new file mode 100644 index 00000000..4fffb15c --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileServiceProvider.java @@ -0,0 +1,8 @@ +package org.asamk.signal.manager.helper; + +import org.whispersystems.signalservice.api.services.ProfileService; + +public interface ProfileServiceProvider { + + ProfileService getProfileService(); +} diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index a19459c0..9b61fbb7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -168,7 +168,11 @@ public class SignalAccount implements Closeable { recipientStore::resolveRecipient, identityKey, registrationId); - signalProtocolStore = new SignalProtocolStore(preKeyStore, signedPreKeyStore, sessionStore, identityKeyStore); + signalProtocolStore = new SignalProtocolStore(preKeyStore, + signedPreKeyStore, + sessionStore, + identityKeyStore, + this::isMultiDevice); messageCache = new MessageCache(getMessageCachePath(dataPath, username)); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java index 5e504ec6..84923423 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java @@ -12,7 +12,7 @@ import org.whispersystems.libsignal.state.PreKeyStore; import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyStore; -import org.whispersystems.signalservice.api.SignalServiceProtocolStore; +import org.whispersystems.signalservice.api.SignalServiceDataStore; import org.whispersystems.signalservice.api.SignalServiceSessionStore; import org.whispersystems.signalservice.api.push.DistributionId; @@ -20,24 +20,28 @@ import java.util.Collection; import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.function.Supplier; -public class SignalProtocolStore implements SignalServiceProtocolStore { +public class SignalProtocolStore implements SignalServiceDataStore { private final PreKeyStore preKeyStore; private final SignedPreKeyStore signedPreKeyStore; private final SignalServiceSessionStore sessionStore; private final IdentityKeyStore identityKeyStore; + private final Supplier isMultiDevice; public SignalProtocolStore( final PreKeyStore preKeyStore, final SignedPreKeyStore signedPreKeyStore, final SignalServiceSessionStore sessionStore, - final IdentityKeyStore identityKeyStore + final IdentityKeyStore identityKeyStore, + final Supplier isMultiDevice ) { this.preKeyStore = preKeyStore; this.signedPreKeyStore = signedPreKeyStore; this.sessionStore = sessionStore; this.identityKeyStore = identityKeyStore; + this.isMultiDevice = isMultiDevice; } @Override @@ -177,9 +181,12 @@ public class SignalProtocolStore implements SignalServiceProtocolStore { } @Override - public void clearSenderKeySharedWith( - final DistributionId distributionId, final Collection addresses - ) { + public void clearSenderKeySharedWith(final Collection addresses) { // TODO } + + @Override + public boolean isMultiDevice() { + return isMultiDevice.get(); + } } diff --git a/src/main/java/org/asamk/signal/commands/JsonRpcLocalCommand.java b/src/main/java/org/asamk/signal/commands/JsonRpcLocalCommand.java index 3d2cd035..abe4e74d 100644 --- a/src/main/java/org/asamk/signal/commands/JsonRpcLocalCommand.java +++ b/src/main/java/org/asamk/signal/commands/JsonRpcLocalCommand.java @@ -13,9 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -public interface JsonRpcLocalCommand extends JsonRpcCommand> { - - void handleCommand(Namespace ns, Manager m) throws CommandException; +public interface JsonRpcLocalCommand extends JsonRpcCommand>, LocalCommand { default TypeReference> getRequestType() { return new TypeReference<>() {