From cf386353c4dd09ddc1377d773f9de57b6909d2da Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Sat, 12 Sep 2026 14:49:19 -0400 Subject: [PATCH] Convert the libsignal-service JSON models to Kotlin. --- .../securesms/testing/MockProvider.kt | 40 +- .../MessageBackupsFlowViewModel.kt | 2 +- .../thoughtcrime/securesms/badges/Badges.kt | 2 +- .../self/overview/BadgesOverviewViewModel.kt | 2 +- .../app/backups/BackupStateObserver.kt | 10 +- .../changenumber/ChangeNumberRepository.kt | 14 +- .../app/changenumber/ChangeNumberResult.kt | 2 +- ...ngOneTimeDonationConfigurationViewModel.kt | 2 +- ...nternalDonorErrorConfigurationViewModel.kt | 2 +- .../DonationsConfigurationExtensions.kt | 8 +- .../subscription/InAppPaymentsRepository.kt | 4 +- .../RecurringInAppPaymentRepository.kt | 4 +- .../completed/TerminalDonationRepository.kt | 2 +- .../donate/DonateToSignalState.kt | 2 +- .../donate/DonateToSignalViewModel.kt | 2 +- .../gateway/GatewaySelectorRepository.kt | 2 +- .../DonationProcessorErrorExtensions.kt | 9 +- .../list/DonationReceiptListRepository.kt | 2 +- .../securesms/database/RecipientTable.kt | 6 +- .../jobs/BackupSubscriptionCheckJob.kt | 4 +- .../jobs/InAppPaymentKeepAliveJob.kt | 2 +- .../securesms/jobs/IndividualSendJobV2.kt | 6 +- .../RefreshDonationSubscriptionStatusJob.kt | 2 +- .../securesms/jobs/RefreshOwnProfileJob.kt | 37 +- .../securesms/jobs/RetrieveProfileJob.kt | 9 +- .../linkdevice/LinkDeviceRepository.kt | 28 +- .../data/RegistrationRepository.kt | 6 +- .../api/SignalServiceMessageSender.java | 8 +- .../api/account/ChangePhoneNumberRequest.java | 93 ---- .../api/account/ChangePhoneNumberRequest.kt | 29 ++ .../BoostReceiptCredentialRequestJson.java | 29 -- .../BoostReceiptCredentialRequestJson.kt | 22 + .../api/donations/DonationsApi.kt | 4 +- ...PalConfirmOneTimePaymentIntentPayload.java | 40 -- ...ayPalConfirmOneTimePaymentIntentPayload.kt | 18 + ...yPalCreateOneTimePaymentIntentPayload.java | 36 -- ...PayPalCreateOneTimePaymentIntentPayload.kt | 17 + .../PayPalCreatePaymentMethodPayload.java | 21 - .../PayPalCreatePaymentMethodPayload.kt | 14 + .../ReceiptCredentialRequestJson.java | 20 - .../donations/ReceiptCredentialRequestJson.kt | 15 + .../ReceiptCredentialResponseJson.java | 36 -- .../ReceiptCredentialResponseJson.kt | 17 + .../RedeemDonationReceiptRequest.java | 50 --- .../donations/RedeemDonationReceiptRequest.kt | 21 + .../StripeOneTimePaymentIntentPayload.java | 29 -- .../StripeOneTimePaymentIntentPayload.kt | 16 + .../api/groupsv2/CredentialResponse.java | 20 - .../api/groupsv2/CredentialResponse.kt | 11 + .../api/groupsv2/TemporalCredential.java | 20 - .../api/groupsv2/TemporalCredential.kt | 11 + .../signalservice/api/keys/KeysApi.kt | 33 +- .../api/keys/OneTimePreKeyCounts.java | 32 -- .../api/keys/OneTimePreKeyCounts.kt | 13 + .../signalservice/api/message/MessageApi.kt | 4 +- .../api/messages/calls/TurnServerInfo.java | 51 --- .../api/messages/calls/TurnServerInfo.kt | 16 + .../api/messages/multidevice/DeviceInfo.java | 49 --- .../api/messages/multidevice/DeviceInfo.kt | 14 + .../RegisterAsSecondaryDeviceResponse.java | 36 -- .../RegisterAsSecondaryDeviceResponse.kt | 14 + .../api/payments/CurrencyConversion.java | 21 - .../api/payments/CurrencyConversion.kt | 11 + .../api/payments/CurrencyConversions.java | 21 - .../api/payments/CurrencyConversions.kt | 11 + .../api/profiles/SignalServiceProfile.java | 241 ----------- .../api/profiles/SignalServiceProfile.kt | 78 ++++ .../api/provisioning/ProvisioningMessage.java | 14 - .../api/provisioning/ProvisioningMessage.kt | 10 + .../api/push/SignedPreKeyEntity.java | 57 --- .../api/push/SignedPreKeyEntity.kt | 23 + .../NonNormalizedPhoneNumberException.java | 48 --- .../NonNormalizedPhoneNumberException.kt | 39 ++ .../api/storage/StorageAuthResponse.java | 27 -- .../api/storage/StorageAuthResponse.kt | 11 + .../api/subscriptions/ActiveSubscription.java | 399 ------------------ .../api/subscriptions/ActiveSubscription.kt | 207 +++++++++ .../PayPalConfirmPaymentIntentResponse.java | 21 - .../PayPalConfirmPaymentIntentResponse.kt | 13 + .../PayPalCreatePaymentIntentResponse.java | 27 -- .../PayPalCreatePaymentIntentResponse.kt | 14 + .../PayPalCreatePaymentMethodResponse.java | 23 - .../PayPalCreatePaymentMethodResponse.kt | 14 + .../api/subscriptions/StripeClientSecret.java | 24 -- .../api/subscriptions/StripeClientSecret.kt | 13 + .../internal/push/AuthCredentials.java | 34 -- .../internal/push/AuthCredentials.kt | 27 ++ .../internal/push/CdsiAuthResponse.java | 20 - .../internal/push/CdsiAuthResponse.kt | 11 + .../internal/push/ConfirmUsernameRequest.java | 20 - .../internal/push/ConfirmUsernameRequest.kt | 12 + .../internal/push/DeviceInfoList.java | 19 - .../internal/push/DeviceInfoList.kt | 12 + .../internal/push/DeviceLimit.java | 20 - .../internal/push/DeviceLimit.kt | 11 + .../internal/push/DonationIntentResult.java | 24 -- .../internal/push/DonationIntentResult.kt | 13 + .../internal/push/GroupMismatchedDevices.java | 24 -- .../internal/push/GroupMismatchedDevices.kt | 14 + .../internal/push/GroupStaleDevices.java | 23 - .../internal/push/GroupStaleDevices.kt | 14 + .../internal/push/IdentityCheckRequest.java | 57 --- .../internal/push/IdentityCheckRequest.kt | 29 ++ .../internal/push/IdentityCheckResponse.java | 56 --- .../internal/push/IdentityCheckResponse.kt | 22 + .../internal/push/KyberPreKeyEntity.java | 92 ---- .../internal/push/KyberPreKeyEntity.kt | 39 ++ .../internal/push/MismatchedDevices.java | 46 -- .../internal/push/MismatchedDevices.kt | 20 + .../internal/push/OutgoingPushMessage.java | 39 -- .../internal/push/OutgoingPushMessage.kt | 13 + .../push/OutgoingPushMessageList.java | 69 --- .../internal/push/OutgoingPushMessageList.kt | 20 + .../internal/push/PreKeyEntity.java | 67 --- .../internal/push/PreKeyEntity.kt | 36 ++ .../internal/push/PreKeyResponse.java | 37 -- .../internal/push/PreKeyResponse.kt | 18 + .../internal/push/PreKeyResponseItem.java | 50 --- .../internal/push/PreKeyResponseItem.kt | 17 + .../internal/push/PreKeyState.java | 48 --- .../internal/push/PreKeyState.kt | 16 + .../push/ProfileAvatarUploadAttributes.java | 58 --- .../push/ProfileAvatarUploadAttributes.kt | 16 + .../internal/push/ProofRequiredResponse.java | 24 -- .../internal/push/ProofRequiredResponse.kt | 11 + .../internal/push/PushServiceSocket.java | 27 +- .../internal/push/RegistrationLockFailure.kt | 20 + .../internal/push/SendMessageResponse.java | 30 -- .../internal/push/SendMessageResponse.kt | 16 + .../internal/push/StaleDevices.java | 34 -- .../internal/push/StaleDevices.kt | 19 + .../push/SubscriptionsConfiguration.java | 142 ------- .../push/SubscriptionsConfiguration.kt | 58 +++ .../internal/push/VerifyAccountResponse.java | 42 -- .../internal/push/VerifyAccountResponse.kt | 14 + .../websocket/DefaultErrorMapper.java | 13 +- .../api/json/ConvertedModelJsonTest.kt | 220 ++++++++++ .../subscriptions/ActiveSubscriptionTest.java | 25 -- .../subscriptions/ActiveSubscriptionTest.kt | 39 ++ 139 files changed, 1593 insertions(+), 2741 deletions(-) delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/ChangePhoneNumberRequest.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/ChangePhoneNumberRequest.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/BoostReceiptCredentialRequestJson.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/BoostReceiptCredentialRequestJson.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalConfirmOneTimePaymentIntentPayload.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalConfirmOneTimePaymentIntentPayload.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreateOneTimePaymentIntentPayload.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreateOneTimePaymentIntentPayload.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreatePaymentMethodPayload.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreatePaymentMethodPayload.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialRequestJson.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialRequestJson.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialResponseJson.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialResponseJson.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/RedeemDonationReceiptRequest.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/RedeemDonationReceiptRequest.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/StripeOneTimePaymentIntentPayload.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/StripeOneTimePaymentIntentPayload.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/CredentialResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/CredentialResponse.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/TemporalCredential.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/TemporalCredential.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/OneTimePreKeyCounts.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/OneTimePreKeyCounts.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RegisterAsSecondaryDeviceResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RegisterAsSecondaryDeviceResponse.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversion.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversion.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversions.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversions.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonNormalizedPhoneNumberException.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonNormalizedPhoneNumberException.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageAuthResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageAuthResponse.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalConfirmPaymentIntentResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalConfirmPaymentIntentResponse.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentIntentResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentIntentResponse.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentMethodResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentMethodResponse.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/StripeClientSecret.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/StripeClientSecret.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AuthCredentials.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AuthCredentials.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/CdsiAuthResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/CdsiAuthResponse.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ConfirmUsernameRequest.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ConfirmUsernameRequest.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationIntentResult.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationIntentResult.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupMismatchedDevices.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupMismatchedDevices.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupStaleDevices.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupStaleDevices.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckRequest.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckRequest.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckResponse.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/KyberPreKeyEntity.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/KyberPreKeyEntity.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProofRequiredResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProofRequiredResponse.kt create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/RegistrationLockFailure.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.kt delete mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/VerifyAccountResponse.java create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/VerifyAccountResponse.kt create mode 100644 lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/json/ConvertedModelJsonTest.kt delete mode 100644 lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscriptionTest.java create mode 100644 lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscriptionTest.kt diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/MockProvider.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/MockProvider.kt index be467837aa..d57fdbd705 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/MockProvider.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/MockProvider.kt @@ -15,7 +15,7 @@ import org.whispersystems.signalservice.internal.push.DeviceInfoList import org.whispersystems.signalservice.internal.push.PreKeyEntity import org.whispersystems.signalservice.internal.push.PreKeyResponse import org.whispersystems.signalservice.internal.push.PreKeyResponseItem -import org.whispersystems.signalservice.internal.push.PushServiceSocket +import org.whispersystems.signalservice.internal.push.RegistrationLockFailure import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataJson import org.whispersystems.signalservice.internal.push.VerifyAccountResponse import org.whispersystems.signalservice.internal.push.WhoAmIResponse @@ -26,18 +26,16 @@ import java.security.SecureRandom */ object MockProvider { - val lockedFailure = PushServiceSocket.RegistrationLockFailure().apply { - svr1Credentials = AuthCredentials.create("username", "password") + val lockedFailure = RegistrationLockFailure( + svr1Credentials = AuthCredentials.create("username", "password"), svr2Credentials = AuthCredentials.create("username", "password") - } + ) - val primaryOnlyDeviceList = DeviceInfoList().apply { + val primaryOnlyDeviceList = DeviceInfoList( devices = listOf( - DeviceInfo().apply { - id = 1 - } + DeviceInfo(id = 1) ) - } + ) val sessionMetadataJson = RegistrationSessionMetadataJson( id = "asdfasdfasdfasdf", @@ -50,11 +48,11 @@ object MockProvider { ) fun createVerifyAccountResponse(aci: ServiceId, newPni: ServiceId): VerifyAccountResponse { - return VerifyAccountResponse().apply { - uuid = aci.toString() - pni = newPni.toString() + return VerifyAccountResponse( + uuid = aci.toString(), + pni = newPni.toString(), storageCapable = false - } + ) } fun createWhoAmIResponse(aci: ServiceId, pni: ServiceId, e164: String): WhoAmIResponse { @@ -69,16 +67,16 @@ object MockProvider { val signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), identity.privateKey) val oneTimePreKey = PreKeyRecord(SecureRandom().nextInt(Medium.MAX_VALUE), ECKeyPair.generate()) - val device = PreKeyResponseItem().apply { - this.deviceId = deviceId - registrationId = KeyHelper.generateRegistrationId(false) - signedPreKey = SignedPreKeyEntity(signedPreKeyRecord.id.toLong(), signedPreKeyRecord.keyPair.publicKey, signedPreKeyRecord.signature) + val device = PreKeyResponseItem( + deviceId = deviceId, + registrationId = KeyHelper.generateRegistrationId(false), + signedPreKey = SignedPreKeyEntity(signedPreKeyRecord.id.toLong(), signedPreKeyRecord.keyPair.publicKey, signedPreKeyRecord.signature), preKey = PreKeyEntity(oneTimePreKey.id.toLong(), oneTimePreKey.keyPair.publicKey) - } + ) - return PreKeyResponse().apply { - identityKey = identity.publicKey + return PreKeyResponse( + identityKey = identity.publicKey, devices = listOf(device) - } + ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt index a06d07b588..27e3f85277 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt @@ -185,7 +185,7 @@ class MessageBackupsFlowViewModel( } activeSubscription.runIfSuccessful { subscription -> - if (subscription.willCancelAtPeriodEnd()) { + if (subscription.willCancelAtPeriodEnd) { Log.d(TAG, "Active subscription is cancelled. Clearing tier.") internalStateFlow.update { it.copy( diff --git a/app/src/main/java/org/thoughtcrime/securesms/badges/Badges.kt b/app/src/main/java/org/thoughtcrime/securesms/badges/Badges.kt index 7bccc78436..31542873a4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/badges/Badges.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/badges/Badges.kt @@ -123,7 +123,7 @@ object Badges { uriAndDensity.first, uriAndDensity.second, serviceBadge.expiration?.let { getTimestamp(it) } ?: 0, - serviceBadge.isVisible, + serviceBadge.visible, TimeUnit.SECONDS.toMillis(serviceBadge.duration) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/badges/self/overview/BadgesOverviewViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/badges/self/overview/BadgesOverviewViewModel.kt index 8ac5cff0ee..416e2863b1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/badges/self/overview/BadgesOverviewViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/badges/self/overview/BadgesOverviewViewModel.kt @@ -53,7 +53,7 @@ class BadgesOverviewViewModel( RecurringInAppPaymentRepository.getActiveSubscription(InAppPaymentSubscriberRecord.Type.DONATION), RecurringInAppPaymentRepository.getSubscriptions() ) { active, all -> - if (!active.isActive && active.activeSubscription?.willCancelAtPeriodEnd() == true) { + if (!active.isActive && active.activeSubscription?.willCancelAtPeriodEnd == true) { Optional.ofNullable(all.firstOrNull { it.level == active.activeSubscription?.level }?.badge?.id) } else { Optional.empty() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupStateObserver.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupStateObserver.kt index dd5f6accfd..a4576ebf9b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupStateObserver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupStateObserver.kt @@ -297,13 +297,13 @@ class BackupStateObserver( } } - val signalServiceSubscriptionIsActiveAndWillRenew = activeSubscription?.isActive == true && (!activeSubscription.isCanceled || activeSubscription.willCancelAtPeriodEnd()) + val signalServiceSubscriptionIsActiveAndWillRenew = activeSubscription?.isActive == true && (!activeSubscription.isCanceled || activeSubscription.willCancelAtPeriodEnd) Log.d(TAG, "[getNetworkBackupState][subscriptionStateMismatchDetected] signalServiceSubscriptionIsActiveAndWillRenew: $signalServiceSubscriptionIsActiveAndWillRenew") when { signalServiceSubscriptionIsActiveAndWillRenew && !googlePlayBillingSubscriptionIsActiveAndWillRenew -> { - val type = buildPaidTypeFromSubscription(activeSubscription.activeSubscription) + val type = buildPaidTypeFromSubscription(activeSubscription.activeSubscription!!) if (type == null) { Log.d(TAG, "[getNetworkBackupState][subscriptionMismatchDetected] failed to load backup configuration. Likely a network error.") @@ -313,8 +313,8 @@ class BackupStateObserver( Log.d(TAG, "[getNetworkBackupState][subscriptionMismatchDetected] found a subscription mismatch and successfully loaded configuration.") return BackupState.SubscriptionMismatchMissingGooglePlay( messageBackupsType = type, - renewalTime = activeSubscription.activeSubscription.endOfCurrentPeriod.seconds, - isBilledThroughOtherStore = InAppPaymentsRepository.isBackupBilledThroughOtherStore(activeSubscription.activeSubscription) + renewalTime = activeSubscription.activeSubscription!!.endOfCurrentPeriod.seconds, + isBilledThroughOtherStore = InAppPaymentsRepository.isBackupBilledThroughOtherStore(activeSubscription.activeSubscription!!) ) } @@ -397,7 +397,7 @@ class BackupStateObserver( getStateOnError() } else { when { - (subscription.isCanceled || subscription.willCancelAtPeriodEnd()) && subscription.isActive -> { + (subscription.isCanceled || subscription.willCancelAtPeriodEnd) && subscription.isActive -> { Log.d(TAG, "[getPaidBackupState] Found a canceled subscription.") InAppPaymentsRepository.updateBackupInAppPaymentWithCancelation(activeSubscription.successOrThrow()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt index 63e0577115..005602273f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt @@ -334,9 +334,9 @@ class ChangeNumberRepository( return ChangeNumberResult.from( result.map { accountRegistrationResponse: VerifyAccountResponse -> NumberChangeResult( - uuid = accountRegistrationResponse.uuid, - pni = accountRegistrationResponse.pni, - number = accountRegistrationResponse.number + uuid = accountRegistrationResponse.uuid!!, + pni = accountRegistrationResponse.pni!!, + number = accountRegistrationResponse.number!! ) } ) @@ -353,11 +353,11 @@ class ChangeNumberRepository( if (whoAmI.number == newE164 && whoAmI.pni != null) { Log.w(TAG, "Change number request did not succeed, but whoami reports the new number is already active. Treating the change as successful.") NetworkResult.Success( - VerifyAccountResponse().apply { - uuid = whoAmI.aci - pni = whoAmI.pni + VerifyAccountResponse( + uuid = whoAmI.aci, + pni = whoAmI.pni, number = whoAmI.number - } + ) ) } else { Log.i(TAG, "Change number request did not succeed and whoami does not report the new number; treating as a genuine failure.") diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberResult.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberResult.kt index 3e0ed33aa3..dbdc21fe58 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberResult.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberResult.kt @@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.registration.data.network.RegistrationResult import org.whispersystems.signalservice.api.SvrNoDataException import org.whispersystems.signalservice.api.svr.Svr3Credentials import org.whispersystems.signalservice.internal.push.AuthCredentials -import org.whispersystems.signalservice.internal.push.PushServiceSocket.RegistrationLockFailure +import org.whispersystems.signalservice.internal.push.RegistrationLockFailure import org.whispersystems.signalservice.internal.push.VerifyAccountResponse /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalPendingOneTimeDonationConfigurationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalPendingOneTimeDonationConfigurationViewModel.kt index ad1372e068..34f69dd10c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalPendingOneTimeDonationConfigurationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalPendingOneTimeDonationConfigurationViewModel.kt @@ -42,7 +42,7 @@ class InternalPendingOneTimeDonationConfigurationViewModel : ViewModel() { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { config -> - val badge = Badges.fromServiceBadge(config.levels.values.first().badge) + val badge = Badges.fromServiceBadge(config.levels.values.first().badge!!) state.value = state.value.copy(badge = Badges.toDatabaseBadge(badge)) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/donor/InternalDonorErrorConfigurationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/donor/InternalDonorErrorConfigurationViewModel.kt index 69c8b9baea..fd0751e758 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/donor/InternalDonorErrorConfigurationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/donor/InternalDonorErrorConfigurationViewModel.kt @@ -33,7 +33,7 @@ class InternalDonorErrorConfigurationViewModel : ViewModel() { val configuration = SignalNetwork.donationsService.getDonationsConfiguration(Locale.getDefault()).toNetworkResult().successOrNull() ?: return@launch val giftBadges = configuration.getGiftBadges() val boostBadges = configuration.getBoostBadges() - val subscriptionBadges = configuration.getSubscriptionLevels().values.map { Badges.fromServiceBadge(it.badge) } + val subscriptionBadges = configuration.getSubscriptionLevels().values.map { Badges.fromServiceBadge(it.badge!!) } store.update { it.copy(badges = giftBadges + boostBadges + subscriptionBadges) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensions.kt index 09794929c2..0e91897bda 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationsConfigurationExtensions.kt @@ -5,10 +5,10 @@ import org.signal.core.util.money.PlatformCurrencyUtil import org.thoughtcrime.securesms.badges.Badges import org.thoughtcrime.securesms.badges.models.Badge import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration -import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.BOOST_LEVEL -import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.GIFT_LEVEL +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.Companion.BOOST_LEVEL +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.Companion.GIFT_LEVEL +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.Companion.SUBSCRIPTION_LEVELS import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.LevelConfiguration -import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration.SUBSCRIPTION_LEVELS import java.math.BigDecimal import java.util.Currency @@ -79,7 +79,7 @@ fun SubscriptionsConfiguration.getBoostAmounts(paymentMethodAvailability: Paymen fun SubscriptionsConfiguration.getBadge(level: Int): Badge { require(level == GIFT_LEVEL || level == BOOST_LEVEL || SUBSCRIPTION_LEVELS.contains(level)) - return Badges.fromServiceBadge(levels[level]!!.badge) + return Badges.fromServiceBadge(levels[level]!!.badge!!) } fun SubscriptionsConfiguration.getSubscriptionLevels(): Map { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt index 85bb77c107..5f380daa66 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt @@ -98,7 +98,7 @@ object InAppPaymentsRepository { * This operation will only be performed if we find a latest payment for the given subscriber id in the END state without cancelation data. */ fun updateInAppPaymentWithCancelation(activeSubscription: ActiveSubscription, subscriberType: InAppPaymentSubscriberRecord.Type) { - if (activeSubscription.isCanceled || (subscriberType == InAppPaymentSubscriberRecord.Type.BACKUP && activeSubscription.willCancelAtPeriodEnd()) || activeSubscription.isFailedPayment) { + if (activeSubscription.isCanceled || (subscriberType == InAppPaymentSubscriberRecord.Type.BACKUP && activeSubscription.willCancelAtPeriodEnd) || activeSubscription.isFailedPayment) { writeCancelation(subscriberType, activeSubscription.chargeFailure) } } @@ -145,7 +145,7 @@ object InAppPaymentsRepository { * This operation will only be performed if we find a latest payment for the given subscriber id in the END state with cancelation data */ fun clearCancelation(activeSubscription: ActiveSubscription) { - if (!activeSubscription.isCanceled && !activeSubscription.willCancelAtPeriodEnd()) { + if (!activeSubscription.isCanceled && !activeSubscription.willCancelAtPeriodEnd) { val subscriber = getSubscriber(InAppPaymentSubscriberRecord.Type.BACKUP) ?: return val latestPayment = SignalDatabase.inAppPayments.getLatestBySubscriberId(subscriber.subscriberId) ?: return diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt index 2f9ec2d185..4291e0ad03 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt @@ -95,7 +95,7 @@ object RecurringInAppPaymentRepository { response.result.ifPresent { result -> val lastEndOfPeriod = SignalDatabase.inAppPayments.getByLatestEndOfPeriod(type.inAppPaymentType)?.endOfPeriodSeconds ?: 0L - if (result.isActive && result.activeSubscription.endOfCurrentPeriod > lastEndOfPeriod) { + if (result.isActive && result.activeSubscription!!.endOfCurrentPeriod > lastEndOfPeriod) { InAppPaymentKeepAliveJob.enqueueAndTrackTime(System.currentTimeMillis().milliseconds) } } @@ -118,7 +118,7 @@ object RecurringInAppPaymentRepository { Subscription( id = level.toString(), level = level, - badge = Badges.fromServiceBadge(levelConfig.badge), + badge = Badges.fromServiceBadge(levelConfig.badge!!), prices = config.getSubscriptionAmounts(level) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationRepository.kt index 63061aeb98..edec40072b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationRepository.kt @@ -22,7 +22,7 @@ class TerminalDonationRepository( .fromCallable { donationsService.getDonationsConfiguration(Locale.getDefault()) } .flatMap { it.flattenResult() } .map { it.levels[terminalDonation.level.toInt()]!! } - .map { Badges.fromServiceBadge(it.badge) } + .map { Badges.fromServiceBadge(it.badge!!) } .subscribeOn(Schedulers.io()) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalState.kt index b61f6c7633..fe7b0a5c54 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalState.kt @@ -126,7 +126,7 @@ data class DonateToSignalState( val isSubscriptionInProgress: Boolean = _activeSubscription?.isInProgress == true val activeLevel: Int? = _activeSubscription?.activeSubscription?.level val activeSubscription: ActiveSubscription.Subscription? = _activeSubscription?.activeSubscription - val isActiveSubscriptionEnding: Boolean = _activeSubscription?.isActive == true && _activeSubscription.activeSubscription.willCancelAtPeriodEnd() + val isActiveSubscriptionEnding: Boolean = _activeSubscription?.isActive == true && _activeSubscription.activeSubscription?.willCancelAtPeriodEnd == true val renewalTimestamp = TimeUnit.SECONDS.toMillis(activeSubscription?.endOfCurrentPeriod ?: 0L) val isSelectionValid = selectedSubscription != null && (!isSubscriptionActive || selectedSubscription.level != activeSubscription?.level) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalViewModel.kt index f0de862ca1..84fa80ac86 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/DonateToSignalViewModel.kt @@ -384,7 +384,7 @@ class DonateToSignalViewModel( private fun resolveSelectedSubscription(activeSubscription: ActiveSubscription, subscriptions: List): Subscription? { return if (activeSubscription.isActive) { - subscriptions.firstOrNull { it.level == activeSubscription.activeSubscription.level } + subscriptions.firstOrNull { it.level == activeSubscription.activeSubscription!!.level } } else { subscriptions.firstOrNull() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/gateway/GatewaySelectorRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/gateway/GatewaySelectorRepository.kt index cd7dfb9cc4..b7327f0106 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/gateway/GatewaySelectorRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/gateway/GatewaySelectorRepository.kt @@ -31,7 +31,7 @@ object GatewaySelectorRepository { GatewayConfiguration( availableGateways = available, - sepaEuroMaximum = if (configuration.sepaMaximumEuros != null) FiatMoney(configuration.sepaMaximumEuros, CurrencyUtil.EURO) else null + sepaEuroMaximum = configuration.sepaMaximumEuros?.let { FiatMoney(it, CurrencyUtil.EURO) } ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationProcessorErrorExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationProcessorErrorExtensions.kt index 0614ae49b9..eccf3d6b3c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationProcessorErrorExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/errors/DonationProcessorErrorExtensions.kt @@ -18,14 +18,15 @@ fun InAppPaymentProcessorError.toDonationError( return when (processor) { ActiveSubscription.Processor.STRIPE -> { check(method is PaymentSourceType.Stripe) - val declineCode = StripeDeclineCode.getFromCode(chargeFailure.code) - val failureCode = StripeFailureCode.getFromCode(chargeFailure.code) + val chargeFailureCode = chargeFailure.code + val declineCode = StripeDeclineCode.getFromCode(chargeFailureCode) + val failureCode = StripeFailureCode.getFromCode(chargeFailureCode) if (declineCode.isKnown()) { DonationError.PaymentSetupError.StripeDeclinedError(source, this, declineCode, method) } else if (failureCode.isKnown) { DonationError.PaymentSetupError.StripeFailureCodeError(source, this, failureCode, method) - } else if (chargeFailure.code != null) { - DonationError.PaymentSetupError.StripeCodedError(source, this, chargeFailure.code) + } else if (chargeFailureCode != null) { + DonationError.PaymentSetupError.StripeCodedError(source, this, chargeFailureCode) } else { DonationError.PaymentSetupError.GenericError(source, this) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/list/DonationReceiptListRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/list/DonationReceiptListRepository.kt index a28833bb2f..7161858eb1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/list/DonationReceiptListRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/list/DonationReceiptListRepository.kt @@ -22,7 +22,7 @@ class DonationReceiptListRepository { val subBadges = config.getSubscriptionLevels().map { DonationReceiptBadge( level = it.key, - badge = Badges.fromServiceBadge(it.value.badge), + badge = Badges.fromServiceBadge(it.value.badge!!), type = InAppPaymentReceiptRecord.Type.RECURRING_DONATION ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index 702df76172..b444952f59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -432,9 +432,9 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da @JvmStatic fun maskCapabilitiesToLong(capabilities: SignalServiceProfile.Capabilities): Long { var value: Long = 0 - value = Bitmask.update(value, Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isStorageServiceEncryptionV2).serialize().toLong()) - value = Bitmask.update(value, Capabilities.USERNAME_SYNC_MESSAGES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isUsernameSyncMessages).serialize().toLong()) - value = Bitmask.update(value, Capabilities.OPTIONAL_PHONE_NUMBER, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isOptionalPhoneNumber).serialize().toLong()) + value = Bitmask.update(value, Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.storageServiceEncryptionV2).serialize().toLong()) + value = Bitmask.update(value, Capabilities.USERNAME_SYNC_MESSAGES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.usernameSyncMessages).serialize().toLong()) + value = Bitmask.update(value, Capabilities.OPTIONAL_PHONE_NUMBER, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.optionalPhoneNumber).serialize().toLong()) return value } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupSubscriptionCheckJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupSubscriptionCheckJob.kt index 63a88ad4ad..04c509ec2b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupSubscriptionCheckJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupSubscriptionCheckJob.kt @@ -170,7 +170,7 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C checkForFailedOrCanceledSubscriptionState(activeSubscription) - val isSignalSubscriptionFailedOrCanceled = activeSubscription?.willCancelAtPeriodEnd() == true + val isSignalSubscriptionFailedOrCanceled = activeSubscription?.willCancelAtPeriodEnd == true if (hasActiveSignalSubscription && !isSignalSubscriptionFailedOrCanceled) { checkAndSynchronizeZkCredentialTierWithStoredLocalTier() } @@ -256,7 +256,7 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C * the "download your data" notifier sheet. */ private fun checkForFailedOrCanceledSubscriptionState(activeSubscription: ActiveSubscription?) { - if (activeSubscription?.willCancelAtPeriodEnd() == true && activeSubscription.activeSubscription != null) { + if (activeSubscription?.willCancelAtPeriodEnd == true && activeSubscription.activeSubscription != null) { Log.i(TAG, "Subscription either has a payment failure or has been canceled.") val response = SignalNetwork.accountApi.whoAmI() diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJob.kt index 68b300bc86..ddbd5a66cc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/InAppPaymentKeepAliveJob.kt @@ -300,7 +300,7 @@ class InAppPaymentKeepAliveJob private constructor( info(type, "Failed to load subscription configuration for level ${subscription.level} for type $type") null } else { - Badges.toDatabaseBadge(Badges.fromServiceBadge(subscriptionConfig.badge)) + Badges.toDatabaseBadge(Badges.fromServiceBadge(subscriptionConfig.badge!!)) } } else { warn(TAG, "Failed to load configuration while processing $type") diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/IndividualSendJobV2.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/IndividualSendJobV2.kt index 075ee98330..87b43b4f31 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/IndividualSendJobV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/IndividualSendJobV2.kt @@ -289,15 +289,15 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va is MessageService.SendError.ChallengeRequired -> { Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Challenge required (options=${error.options})", error) - val proofResponse = ProofRequiredResponse().apply { - token = error.token + val proofResponse = ProofRequiredResponse( + token = error.token, options = error.options.map { when (it) { ChallengeOption.PUSH_CHALLENGE -> "pushChallenge" ChallengeOption.CAPTCHA -> "captcha" } } - } + ) val proofException = ProofRequiredException(proofResponse, error.retryAfter?.inWholeSeconds ?: 0L) val threadRecipient = SignalDatabase.threads.getRecipientForThreadId(threadId) when (ProofRequiredExceptionHandler.handle(context, proofException, threadRecipient, threadId, messageId)) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshDonationSubscriptionStatusJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshDonationSubscriptionStatusJob.kt index 6dbbf28c0f..7f23a6070d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshDonationSubscriptionStatusJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshDonationSubscriptionStatusJob.kt @@ -70,7 +70,7 @@ class RefreshDonationSubscriptionStatusJob private constructor(parameters: Param val activeSubscription = SignalNetwork.donationsService.getSubscription(subscriber.subscriberId).resultOrThrow if (activeSubscription.isActive) { - val endOfCurrentPeriod = activeSubscription.activeSubscription.endOfCurrentPeriod + val endOfCurrentPeriod = activeSubscription.activeSubscription!!.endOfCurrentPeriod if (endOfCurrentPeriod > SignalStore.inAppPayments.getLastEndOfPeriod()) { Log.i(TAG, "Server reports active subscription with newer end-of-period. Updating local state.") SignalStore.inAppPayments.setLastEndOfPeriod(endOfCurrentPeriod) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.kt index fddb9f7a42..80a985ec0c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.kt @@ -108,10 +108,10 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob val profile = profileAndCredential.getProfile() - if (Util.isEmpty(profile.getName()) && - Util.isEmpty(profile.getAvatar()) && - Util.isEmpty(profile.getAbout()) && - Util.isEmpty(profile.getAboutEmoji()) + if (Util.isEmpty(profile.name) && + Util.isEmpty(profile.avatar) && + Util.isEmpty(profile.about) && + Util.isEmpty(profile.aboutEmoji) ) { Log.w(TAG, "The profile we retrieved was empty! Ignoring it.") @@ -125,13 +125,13 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob return } - setProfileName(profile.getName()) - setProfileAbout(profile.getAbout(), profile.getAboutEmoji()) - setProfileAvatar(profile.getAvatar()) - setProfileCapabilities(profile.getCapabilities()) - setProfileBadges(profile.getBadges()) - ensureUnidentifiedAccessCorrect(profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess()) - ensurePhoneNumberSharingIsCorrect(profile.getPhoneNumberSharing()) + setProfileName(profile.name) + setProfileAbout(profile.about, profile.aboutEmoji) + setProfileAvatar(profile.avatar) + setProfileCapabilities(profile.capabilities) + setProfileBadges(profile.badges) + ensureUnidentifiedAccessCorrect(profile.unidentifiedAccess, profile.unrestrictedUnidentifiedAccess) + ensurePhoneNumberSharingIsCorrect(profile.phoneNumberSharing) profileAndCredential.getExpiringProfileKeyCredential() .ifPresent { setExpiringProfileKeyCredential(self, ProfileKeyUtil.getSelfProfileKey(), it) } @@ -287,8 +287,8 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob .toSet() val remoteDonorBadgeIds = badges - .filter { it.getCategory() == Badge.Category.Donor.code } - .map { it.getId() } + .filter { it.category == Badge.Category.Donor.code } + .map { it.id } .toSet() val remoteHasSubscriptionBadges = remoteDonorBadgeIds.any { isSubscription(it) } @@ -320,13 +320,14 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob if (response.getResult().isPresent()) { val activeSubscription = response.getResult().get() - if (activeSubscription.isFailedPayment()) { + if (activeSubscription.isFailedPayment) { Log.d(TAG, "Unexpected expiry due to payment failure.", true) isDueToPaymentFailure = true } - if (activeSubscription.getChargeFailure() != null) { - Log.d(TAG, "Active payment contains a charge failure: " + activeSubscription.getChargeFailure().getCode(), true) + val chargeFailure = activeSubscription.chargeFailure + if (chargeFailure != null) { + Log.d(TAG, "Active payment contains a charge failure: " + chargeFailure.code, true) } } @@ -376,8 +377,8 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob SignalStore.inAppPayments.setExpiredGiftBadge(null) } - val userHasVisibleBadges = badges.any { it.isVisible() } - val userHasInvisibleBadges = badges.any { !it.isVisible() } + val userHasVisibleBadges = badges.any { it.visible } + val userHasInvisibleBadges = badges.any { !it.visible } val appBadges = badges.map { Badges.fromServiceBadge(it) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt index 347baef2a4..572f15b650 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt @@ -245,11 +245,12 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val return true } - if (localRecipientRecord.badges != remoteProfile.badges.map { Badges.fromServiceBadge(it) }) { + if (localRecipientRecord.badges != remoteProfile.badges.orEmpty().map { Badges.fromServiceBadge(it) }) { return true } - if (localRecipientRecord.capabilities.rawBits != maskCapabilitiesToLong(remoteProfile.capabilities)) { + val remoteCapabilities = remoteProfile.capabilities + if (remoteCapabilities != null && localRecipientRecord.capabilities.rawBits != maskCapabilitiesToLong(remoteCapabilities)) { return true } @@ -257,7 +258,7 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val val accessMode = deriveUnidentifiedAccessMode( profileKey = profileKey, unidentifiedAccessVerifier = remoteProfile.unidentifiedAccess, - unrestrictedUnidentifiedAccess = remoteProfile.isUnrestrictedUnidentifiedAccess + unrestrictedUnidentifiedAccess = remoteProfile.unrestrictedUnidentifiedAccess ) if (localRecipientRecord.sealedSenderAccessMode != accessMode) { @@ -297,7 +298,7 @@ class RetrieveProfileJob private constructor(parameters: Parameters, private val val recipientProfileKey = ProfileKeyUtil.profileKeyOrNull(recipient.profileKey) val badges = profile.badges?.map { Badges.fromServiceBadge(it) } - val accessMode = deriveUnidentifiedAccessMode(recipientProfileKey, profile.unidentifiedAccess, profile.isUnrestrictedUnidentifiedAccess) + val accessMode = deriveUnidentifiedAccessMode(recipientProfileKey, profile.unidentifiedAccess, profile.unrestrictedUnidentifiedAccess) if (badges != null && badges.size != recipient.badges.size) { Log.i(TAG, "Likely change in badges for ${recipient.id}. Going from ${recipient.badges.size} badge(s) to ${badges.size}.") diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt index 8bd77b83ae..8766863fdd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt @@ -88,26 +88,26 @@ object LinkDeviceRepository { } fun WaitForLinkedDeviceResponse.getPlaintextDevice(): Device { - val response = this - return DeviceInfo().apply { - id = response.id - name = response.name - lastSeen = response.lastSeen - registrationId = response.registrationId - createdAtCiphertext = response.createdAtCiphertext - }.toLocalDevice() + return DeviceInfo( + id = id, + name = name, + lastSeen = lastSeen, + registrationId = registrationId, + createdAtCiphertext = createdAtCiphertext + ).toLocalDevice() } private fun DeviceInfo.toLocalDevice(): Device { val createdAt = this.getPlaintextCreatedAt() - val defaultDevice = Device(getId(), getName(), createdAt, getLastSeen(), getRegistrationId()) + val encodedName = name + val defaultDevice = Device(id, encodedName, createdAt, lastSeen, registrationId) try { - if (getName().isNullOrEmpty() || getName().length < 4) { + if (encodedName.isNullOrEmpty() || encodedName.length < 4) { Log.w(TAG, "Invalid DeviceInfo name.") return defaultDevice } - val deviceName = DeviceName.ADAPTER.decode(Base64.decode(getName())) + val deviceName = DeviceName.ADAPTER.decode(Base64.decode(encodedName)) if (deviceName.ciphertext == null || deviceName.ephemeralPublic == null || deviceName.syntheticIv == null) { Log.w(TAG, "Got a DeviceName that wasn't properly populated.") return defaultDevice @@ -119,7 +119,7 @@ object LinkDeviceRepository { return defaultDevice } - return Device(getId(), String(plaintext), createdAt, getLastSeen(), getRegistrationId()) + return Device(id, String(plaintext), createdAt, lastSeen, registrationId) } catch (e: Exception) { Log.w(TAG, "Failed while reading the protobuf.", e) } @@ -156,9 +156,9 @@ object LinkDeviceRepository { private fun DeviceInfo.getPlaintextCreatedAt(): Long? { return try { - val associatedData = byteArrayOf(getId().toByte()) + this.getRegistrationId().toByteArray() + val associatedData = byteArrayOf(id.toByte()) + registrationId.toByteArray() val createdAtPlaintext = SignalStore.account.aciIdentityKey.privateKey.open( - ciphertext = Base64.decode(this.getCreatedAtCiphertext().toByteArray()), + ciphertext = Base64.decode(createdAtCiphertext!!.toByteArray()), info = DECRYPTION_INFO, associatedData = associatedData ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/data/RegistrationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/data/RegistrationRepository.kt index a92b8adf9e..13d359b7c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/data/RegistrationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/data/RegistrationRepository.kt @@ -464,10 +464,10 @@ object RegistrationRepository { val result: NetworkResult = api.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, aciPreKeyCollection, pniPreKeyCollection, registrationData.fcmToken, true) .map { accountRegistrationResponse: VerifyAccountResponse -> AccountRegistrationResult( - uuid = accountRegistrationResponse.uuid, - pni = accountRegistrationResponse.pni, + uuid = accountRegistrationResponse.uuid!!, + pni = accountRegistrationResponse.pni!!, storageCapable = accountRegistrationResponse.storageCapable, - number = accountRegistrationResponse.number, + number = accountRegistrationResponse.number!!, masterKey = masterKey, pin = pin, aciPreKeyCollection = aciPreKeyCollection, diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index d6baebdc18..ccb82a347a 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -2041,7 +2041,7 @@ public class SignalServiceMessageSender { try { SendMessageResponse response = NetworkResultUtil.toMessageSendLegacy(messages.getDestination(), messageApi.sendMessage(messages, sealedSenderAccess, story)); - return SendMessageResult.success(recipient, messages.getDevices(), response.sentUnidentified(), response.getNeedsSync() || aciStore.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent()); + return SendMessageResult.success(recipient, messages.getDevices(), response.getSentUnidentified(), response.getNeedsSync() || aciStore.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent()); } catch (AuthorizationFailedException | UnregisteredUserException | MismatchedDevicesException | @@ -2080,7 +2080,7 @@ public class SignalServiceMessageSender { SendMessageResponse response = socket.sendMessage(messages, sealedSenderAccess, story); - return SendMessageResult.success(recipient, messages.getDevices(), response.sentUnidentified(), response.getNeedsSync() || aciStore.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent()); + return SendMessageResult.success(recipient, messages.getDevices(), response.getSentUnidentified(), response.getNeedsSync() || aciStore.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent()); } catch (InvalidKeyException ike) { Log.w(TAG, ike); @@ -2302,7 +2302,7 @@ public class SignalServiceMessageSender { SendMessageResult result = SendMessageResult.success( recipient, messages.getDevices(), - response.sentUnidentified(), + response.getSentUnidentified(), response.getNeedsSync() || aciStore.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent() @@ -2345,7 +2345,7 @@ public class SignalServiceMessageSender { return SendMessageResult.success( recipient, messages.getDevices(), - response.sentUnidentified(), + response.getSentUnidentified(), response.getNeedsSync() || aciStore.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent() diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/ChangePhoneNumberRequest.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/ChangePhoneNumberRequest.java deleted file mode 100644 index 6534bf6f4b..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/ChangePhoneNumberRequest.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.whispersystems.signalservice.api.account; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.signal.libsignal.protocol.IdentityKey; -import org.whispersystems.signalservice.api.push.SignedPreKeyEntity; -import org.whispersystems.signalservice.internal.push.KyberPreKeyEntity; -import org.whispersystems.signalservice.internal.push.OutgoingPushMessage; -import org.signal.network.util.JsonUtil; - -import java.util.List; -import java.util.Map; - -public final class ChangePhoneNumberRequest { - @JsonProperty - private String sessionId; - - @JsonProperty - private String recoveryPassword; - - @JsonProperty - private String number; - - @JsonProperty("reglock") - private String registrationLock; - - @JsonProperty - @JsonSerialize(using = JsonUtil.IdentityKeySerializer.class) - @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) - private IdentityKey pniIdentityKey; - - @JsonProperty - private List deviceMessages; - - @JsonProperty - private Map devicePniSignedPrekeys; - - @JsonProperty("devicePniPqLastResortPrekeys") - private Map devicePniLastResortKyberPrekeys; - - @JsonProperty - private Map pniRegistrationIds; - - @SuppressWarnings("unused") - public ChangePhoneNumberRequest() {} - - public ChangePhoneNumberRequest(String sessionId, - String recoveryPassword, - String number, - String registrationLock, - IdentityKey pniIdentityKey, - List deviceMessages, - Map devicePniSignedPrekeys, - Map devicePniLastResortKyberPrekeys, - Map pniRegistrationIds) - { - this.sessionId = sessionId; - this.recoveryPassword = recoveryPassword; - this.number = number; - this.registrationLock = registrationLock; - this.pniIdentityKey = pniIdentityKey; - this.deviceMessages = deviceMessages; - this.devicePniSignedPrekeys = devicePniSignedPrekeys; - this.devicePniLastResortKyberPrekeys = devicePniLastResortKyberPrekeys; - this.pniRegistrationIds = pniRegistrationIds; - } - - public String getNumber() { - return number; - } - - public String getRegistrationLock() { - return registrationLock; - } - - public IdentityKey getPniIdentityKey() { - return pniIdentityKey; - } - - public List getDeviceMessages() { - return deviceMessages; - } - - public Map getDevicePniSignedPrekeys() { - return devicePniSignedPrekeys; - } - - public Map getPniRegistrationIds() { - return pniRegistrationIds; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/ChangePhoneNumberRequest.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/ChangePhoneNumberRequest.kt new file mode 100644 index 0000000000..2bb813484a --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/ChangePhoneNumberRequest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.account + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import org.signal.libsignal.protocol.IdentityKey +import org.signal.network.util.JsonUtil +import org.whispersystems.signalservice.api.push.SignedPreKeyEntity +import org.whispersystems.signalservice.internal.push.KyberPreKeyEntity +import org.whispersystems.signalservice.internal.push.OutgoingPushMessage + +class ChangePhoneNumberRequest( + val sessionId: String? = null, + val recoveryPassword: String? = null, + val number: String, + @JsonProperty("reglock") val registrationLock: String? = null, + @JsonSerialize(using = JsonUtil.IdentityKeySerializer::class) + @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer::class) + val pniIdentityKey: IdentityKey, + val deviceMessages: List, + val devicePniSignedPrekeys: Map, + @JsonProperty("devicePniPqLastResortPrekeys") val devicePniLastResortKyberPrekeys: Map, + val pniRegistrationIds: Map +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/BoostReceiptCredentialRequestJson.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/BoostReceiptCredentialRequestJson.java deleted file mode 100644 index 390e7a9b4e..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/BoostReceiptCredentialRequestJson.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2025 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.api.donations; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest; -import org.signal.core.util.Base64; -import org.whispersystems.signalservice.internal.push.DonationProcessor; - -class BoostReceiptCredentialRequestJson { - @JsonProperty("paymentIntentId") - private final String paymentIntentId; - - @JsonProperty("receiptCredentialRequest") - private final String receiptCredentialRequest; - - @JsonProperty("processor") - private final String processor; - - BoostReceiptCredentialRequestJson(String paymentIntentId, ReceiptCredentialRequest receiptCredentialRequest, DonationProcessor processor) { - this.paymentIntentId = paymentIntentId; - this.receiptCredentialRequest = Base64.encodeWithPadding(receiptCredentialRequest.serialize()); - this.processor = processor.getCode(); - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/BoostReceiptCredentialRequestJson.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/BoostReceiptCredentialRequestJson.kt new file mode 100644 index 0000000000..5df943bbda --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/BoostReceiptCredentialRequestJson.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.donations + +import org.signal.core.util.Base64 +import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest +import org.whispersystems.signalservice.internal.push.DonationProcessor + +internal class BoostReceiptCredentialRequestJson( + val paymentIntentId: String, + val receiptCredentialRequest: String, + val processor: String +) { + constructor(paymentIntentId: String, receiptCredentialRequest: ReceiptCredentialRequest, processor: DonationProcessor) : this( + paymentIntentId = paymentIntentId, + receiptCredentialRequest = Base64.encodeWithPadding(receiptCredentialRequest.serialize()), + processor = processor.code + ) +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/DonationsApi.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/DonationsApi.kt index 1a96a2e3fa..955494fee3 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/DonationsApi.kt +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/DonationsApi.kt @@ -236,7 +236,7 @@ class DonationsApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSo val body = ReceiptCredentialRequestJson(receiptCredentialRequest) val request = WebSocketRequestMessage.post("/v1/subscription/${subscriberId.serialize()}/receipt_credentials", body) return NetworkResult.fromWebSocketRequest(unauthWebSocket, request, webSocketResponseConverter = NetworkResult.LongPollingWebSocketConverter(ReceiptCredentialResponseJson::class)) - .map { it.receiptCredentialResponse } + .map { it.credentialResponse } .then { if (it != null) { NetworkResult.Success(it) @@ -260,7 +260,7 @@ class DonationsApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSo val body = BoostReceiptCredentialRequestJson(paymentIntentId, receiptCredentialRequest, processor) val request = WebSocketRequestMessage.post("/v1/subscription/boost/receipt_credentials", body) return NetworkResult.fromWebSocketRequest(unauthWebSocket, request, webSocketResponseConverter = NetworkResult.LongPollingWebSocketConverter(ReceiptCredentialResponseJson::class)) - .map { it.receiptCredentialResponse } + .map { it.credentialResponse } .then { if (it != null) { NetworkResult.Success(it) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalConfirmOneTimePaymentIntentPayload.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalConfirmOneTimePaymentIntentPayload.java deleted file mode 100644 index 85626ec9dc..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalConfirmOneTimePaymentIntentPayload.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2025 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.api.donations; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Request JSON for confirming a PayPal one-time payment intent - */ -class PayPalConfirmOneTimePaymentIntentPayload { - @JsonProperty - private String amount; - - @JsonProperty - private String currency; - - @JsonProperty - private long level; - - @JsonProperty - private String payerId; - - @JsonProperty - private String paymentId; - - @JsonProperty - private String paymentToken; - - public PayPalConfirmOneTimePaymentIntentPayload(String amount, String currency, long level, String payerId, String paymentId, String paymentToken) { - this.amount = amount; - this.currency = currency; - this.level = level; - this.payerId = payerId; - this.paymentId = paymentId; - this.paymentToken = paymentToken; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalConfirmOneTimePaymentIntentPayload.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalConfirmOneTimePaymentIntentPayload.kt new file mode 100644 index 0000000000..84d1c1fe75 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalConfirmOneTimePaymentIntentPayload.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.donations + +/** + * Request JSON for confirming a PayPal one-time payment intent + */ +internal class PayPalConfirmOneTimePaymentIntentPayload( + val amount: String, + val currency: String, + val level: Long, + val payerId: String, + val paymentId: String, + val paymentToken: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreateOneTimePaymentIntentPayload.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreateOneTimePaymentIntentPayload.java deleted file mode 100644 index 9fa3731974..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreateOneTimePaymentIntentPayload.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2025 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.api.donations; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Request JSON for creating a PayPal one-time payment intent - */ -class PayPalCreateOneTimePaymentIntentPayload { - @JsonProperty - private long amount; - - @JsonProperty - private String currency; - - @JsonProperty - private long level; - - @JsonProperty - private String returnUrl; - - @JsonProperty - private String cancelUrl; - - public PayPalCreateOneTimePaymentIntentPayload(long amount, String currency, long level, String returnUrl, String cancelUrl) { - this.amount = amount; - this.currency = currency; - this.level = level; - this.returnUrl = returnUrl; - this.cancelUrl = cancelUrl; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreateOneTimePaymentIntentPayload.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreateOneTimePaymentIntentPayload.kt new file mode 100644 index 0000000000..4011a4c26e --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreateOneTimePaymentIntentPayload.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.donations + +/** + * Request JSON for creating a PayPal one-time payment intent + */ +internal class PayPalCreateOneTimePaymentIntentPayload( + val amount: Long, + val currency: String, + val level: Long, + val returnUrl: String, + val cancelUrl: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreatePaymentMethodPayload.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreatePaymentMethodPayload.java deleted file mode 100644 index 358b9d9a5f..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreatePaymentMethodPayload.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.api.donations; - -import com.fasterxml.jackson.annotation.JsonProperty; - -class PayPalCreatePaymentMethodPayload { - @JsonProperty - private String returnUrl; - - @JsonProperty - private String cancelUrl; - - PayPalCreatePaymentMethodPayload(String returnUrl, String cancelUrl) { - this.returnUrl = returnUrl; - this.cancelUrl = cancelUrl; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreatePaymentMethodPayload.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreatePaymentMethodPayload.kt new file mode 100644 index 0000000000..b60a61ca6e --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/PayPalCreatePaymentMethodPayload.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.donations + +/** + * Request JSON for creating a recurring PayPal payment method + */ +internal class PayPalCreatePaymentMethodPayload( + val returnUrl: String, + val cancelUrl: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialRequestJson.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialRequestJson.java deleted file mode 100644 index a16c846a0a..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialRequestJson.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2025 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.api.donations; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest; -import org.signal.core.util.Base64; - -class ReceiptCredentialRequestJson { - @JsonProperty("receiptCredentialRequest") - private final String receiptCredentialRequest; - - ReceiptCredentialRequestJson(ReceiptCredentialRequest receiptCredentialRequest) { - this.receiptCredentialRequest = Base64.encodeWithPadding(receiptCredentialRequest.serialize()); - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialRequestJson.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialRequestJson.kt new file mode 100644 index 0000000000..4129d57c9e --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialRequestJson.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.donations + +import org.signal.core.util.Base64 +import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest + +internal class ReceiptCredentialRequestJson( + val receiptCredentialRequest: String +) { + constructor(receiptCredentialRequest: ReceiptCredentialRequest) : this(Base64.encodeWithPadding(receiptCredentialRequest.serialize())) +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialResponseJson.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialResponseJson.java deleted file mode 100644 index ed62e4893d..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialResponseJson.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2025 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.api.donations; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.signal.libsignal.zkgroup.InvalidInputException; -import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialResponse; -import org.signal.core.util.Base64; - -import java.io.IOException; - -import javax.annotation.Nullable; - -class ReceiptCredentialResponseJson { - - private final ReceiptCredentialResponse receiptCredentialResponse; - - ReceiptCredentialResponseJson(@JsonProperty("receiptCredentialResponse") String receiptCredentialResponse) { - ReceiptCredentialResponse response; - try { - response = new ReceiptCredentialResponse(Base64.decode(receiptCredentialResponse)); - } catch (IOException | InvalidInputException e) { - response = null; - } - - this.receiptCredentialResponse = response; - } - - public @Nullable ReceiptCredentialResponse getReceiptCredentialResponse() { - return receiptCredentialResponse; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialResponseJson.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialResponseJson.kt new file mode 100644 index 0000000000..4c31688a1c --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/ReceiptCredentialResponseJson.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.donations + +import com.fasterxml.jackson.annotation.JsonProperty +import org.signal.core.util.Base64 +import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialResponse + +internal class ReceiptCredentialResponseJson( + @JsonProperty("receiptCredentialResponse") receiptCredentialResponse: String +) { + /** Null if the server sent something that isn't a serialized [ReceiptCredentialResponse]. */ + val credentialResponse: ReceiptCredentialResponse? = runCatching { ReceiptCredentialResponse(Base64.decode(receiptCredentialResponse)) }.getOrNull() +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/RedeemDonationReceiptRequest.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/RedeemDonationReceiptRequest.java deleted file mode 100644 index 4fba59f176..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/RedeemDonationReceiptRequest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2025 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.api.donations; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation; - -/** - * POST /v1/donation/redeem-receipt - * - * Request object for redeeming a receipt from a donation transaction. - */ -class RedeemDonationReceiptRequest { - - private final String receiptCredentialPresentation; - private final boolean visible; - private final boolean primary; - - /** - * @param receiptCredentialPresentation base64-encoded no-newlines standard-character-set with-padding of the bytes of a {@link ReceiptCredentialPresentation} object - * @param visible boolean indicating if the new badge should be visible or not on the profile - * @param primary boolean indicating if the new badge should be primary or not on the profile; is always treated as false if `visible` is false - */ - @JsonCreator RedeemDonationReceiptRequest( - @JsonProperty("receiptCredentialPresentation") String receiptCredentialPresentation, - @JsonProperty("visible") boolean visible, - @JsonProperty("primary") boolean primary) { - - this.receiptCredentialPresentation = receiptCredentialPresentation; - this.visible = visible; - this.primary = primary; - } - - public String getReceiptCredentialPresentation() { - return receiptCredentialPresentation; - } - - public boolean isVisible() { - return visible; - } - - public boolean isPrimary() { - return primary; - } -} \ No newline at end of file diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/RedeemDonationReceiptRequest.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/RedeemDonationReceiptRequest.kt new file mode 100644 index 0000000000..af51be8f55 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/RedeemDonationReceiptRequest.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.donations + +/** + * POST /v1/donation/redeem-receipt + * + * Request object for redeeming a receipt from a donation transaction. + * + * @param receiptCredentialPresentation base64-encoded no-newlines standard-character-set with-padding of the bytes of a `ReceiptCredentialPresentation` + * @param visible Whether the new badge should be visible on the profile + * @param primary Whether the new badge should be primary on the profile; always treated as false if [visible] is false + */ +internal class RedeemDonationReceiptRequest( + val receiptCredentialPresentation: String, + val visible: Boolean, + val primary: Boolean +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/StripeOneTimePaymentIntentPayload.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/StripeOneTimePaymentIntentPayload.java deleted file mode 100644 index 298b16f63e..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/StripeOneTimePaymentIntentPayload.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2025 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.api.donations; - -import com.fasterxml.jackson.annotation.JsonProperty; - -class StripeOneTimePaymentIntentPayload { - @JsonProperty - private long amount; - - @JsonProperty - private String currency; - - @JsonProperty - private long level; - - @JsonProperty - private String paymentMethod; - - public StripeOneTimePaymentIntentPayload(long amount, String currency, long level, String paymentMethod) { - this.amount = amount; - this.currency = currency; - this.level = level; - this.paymentMethod = paymentMethod; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/StripeOneTimePaymentIntentPayload.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/StripeOneTimePaymentIntentPayload.kt new file mode 100644 index 0000000000..dfbd76fdb7 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/donations/StripeOneTimePaymentIntentPayload.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.donations + +/** + * Request JSON for creating a Stripe one-time payment intent + */ +internal class StripeOneTimePaymentIntentPayload( + val amount: Long, + val currency: String, + val level: Long, + val paymentMethod: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/CredentialResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/CredentialResponse.java deleted file mode 100644 index 02d8184e9d..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/CredentialResponse.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.whispersystems.signalservice.api.groupsv2; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class CredentialResponse { - - @JsonProperty - private TemporalCredential[] credentials; - - @JsonProperty - private TemporalCredential[] callLinkAuthCredentials; - - public TemporalCredential[] getCredentials() { - return credentials; - } - - public TemporalCredential[] getCallLinkAuthCredentials() { - return callLinkAuthCredentials; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/CredentialResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/CredentialResponse.kt new file mode 100644 index 0000000000..a62888e9e0 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/CredentialResponse.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.groupsv2 + +class CredentialResponse( + val credentials: Array = emptyArray(), + val callLinkAuthCredentials: Array = emptyArray() +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/TemporalCredential.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/TemporalCredential.java deleted file mode 100644 index 4779619240..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/TemporalCredential.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.whispersystems.signalservice.api.groupsv2; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class TemporalCredential { - - @JsonProperty - private byte[] credential; - - @JsonProperty - private long redemptionTime; - - public byte[] getCredential() { - return credential; - } - - public long getRedemptionTime() { - return redemptionTime; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/TemporalCredential.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/TemporalCredential.kt new file mode 100644 index 0000000000..d46f6cb5c1 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/groupsv2/TemporalCredential.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.groupsv2 + +class TemporalCredential( + val credential: ByteArray, + val redemptionTime: Long +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/KeysApi.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/KeysApi.kt index 22eb6cb708..3c684d0241 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/KeysApi.kt +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/KeysApi.kt @@ -250,7 +250,7 @@ class KeysApi( return this.map { response -> val bundles: MutableList = LinkedList() - for (device in response.getDevices()) { + for (device in response.devices) { var preKey: ECPublicKey? = null var signedPreKey: ECPublicKey? var signedPreKeySignature: ByteArray? @@ -260,39 +260,42 @@ class KeysApi( var kyberPreKey: KEMPublicKey? var kyberPreKeySignature: ByteArray? - if (device.getSignedPreKey() != null) { - val rawSignedPreKeyId = device.getSignedPreKey().keyId + val deviceSignedPreKey = device.signedPreKey + if (deviceSignedPreKey != null) { + val rawSignedPreKeyId = deviceSignedPreKey.keyId if (rawSignedPreKeyId !in 0..Int.MAX_VALUE.toLong()) { Log.w(TAG, "Signed pre-key ID for device ${device.deviceId} is out of valid range! Skipping.") continue } - signedPreKey = device.getSignedPreKey().publicKey + signedPreKey = deviceSignedPreKey.publicKey signedPreKeyId = rawSignedPreKeyId.toInt() - signedPreKeySignature = device.getSignedPreKey().signature + signedPreKeySignature = deviceSignedPreKey.signature } else { Log.w(TAG, "No signed prekey for device ${device.deviceId}! Skipping.") continue } - if (device.getPreKey() != null) { - val rawPreKeyId = device.getPreKey().keyId + val devicePreKey = device.preKey + if (devicePreKey != null) { + val rawPreKeyId = devicePreKey.keyId if (rawPreKeyId !in 0..Int.MAX_VALUE.toLong()) { Log.w(TAG, "Pre-key ID for device ${device.deviceId} is out of valid range! Skipping.") continue } preKeyId = rawPreKeyId.toInt() - preKey = device.getPreKey().publicKey + preKey = devicePreKey.publicKey } - if (device.getKyberPreKey() != null) { - val rawKyberPreKeyId = device.getKyberPreKey().keyId + val deviceKyberPreKey = device.kyberPreKey + if (deviceKyberPreKey != null) { + val rawKyberPreKeyId = deviceKyberPreKey.keyId if (rawKyberPreKeyId !in 0..Int.MAX_VALUE.toLong()) { Log.w(TAG, "Kyber pre-key ID for device ${device.deviceId} is out of valid range! Skipping.") continue } - kyberPreKey = device.getKyberPreKey().publicKey + kyberPreKey = deviceKyberPreKey.publicKey kyberPreKeyId = rawKyberPreKeyId.toInt() - kyberPreKeySignature = device.getKyberPreKey().signature + kyberPreKeySignature = deviceKyberPreKey.signature } else { Log.w(TAG, "No kyber prekey for device ${device.deviceId}! Skipping.") continue @@ -300,14 +303,14 @@ class KeysApi( bundles.add( PreKeyBundle( - device.getRegistrationId(), - device.getDeviceId(), + device.registrationId, + device.deviceId, preKeyId, preKey, signedPreKeyId, signedPreKey, signedPreKeySignature, - response.getIdentityKey(), + response.identityKey, kyberPreKeyId, kyberPreKey, kyberPreKeySignature diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/OneTimePreKeyCounts.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/OneTimePreKeyCounts.java deleted file mode 100644 index 6c0c2bfdfb..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/OneTimePreKeyCounts.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2025 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.api.keys; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class OneTimePreKeyCounts { - - @JsonProperty("count") - private int ecCount; - - @JsonProperty("pqCount") - private int kyberCount; - - public OneTimePreKeyCounts() {} - - public OneTimePreKeyCounts(int ecCount, int kyberCount) { - this.ecCount = ecCount; - this.kyberCount = kyberCount; - } - - public int getEcCount() { - return ecCount; - } - - public int getKyberCount() { - return kyberCount; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/OneTimePreKeyCounts.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/OneTimePreKeyCounts.kt new file mode 100644 index 0000000000..f0d38e487a --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/OneTimePreKeyCounts.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.keys + +import com.fasterxml.jackson.annotation.JsonProperty + +data class OneTimePreKeyCounts( + @JsonProperty("count") val ecCount: Int = 0, + @JsonProperty("pqCount") val kyberCount: Int = 0 +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/MessageApi.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/MessageApi.kt index 4f96a456d0..1a23c2e2a1 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/MessageApi.kt +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/message/MessageApi.kt @@ -33,14 +33,14 @@ class MessageApi( companion object { /** - * Adjust the default parsing of [SendMessageResponse] to set the non-server returned [SendMessageResponse.sentUnidentfied] + * Adjust the default parsing of [SendMessageResponse] to set the non-server returned [SendMessageResponse.sentUnidentified] * flag on the model. */ private val sendMessageResponseConverter = object : NetworkResult.WebSocketResponseConverter { override fun convert(response: WebsocketResponse): NetworkResult { return if (response.status == 200) { response.toSuccess(SendMessageResponse::class) - .map { it.apply { setSentUnidentfied(response.isUnidentified) } } + .map { it.apply { sentUnidentified = response.isUnidentified } } } else { response.toStatusCodeError() } diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.java deleted file mode 100644 index ef045c0e9b..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.whispersystems.signalservice.api.messages.calls; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -public class TurnServerInfo { - - @JsonProperty - private String username; - - @JsonProperty - private String password; - - @JsonProperty - private String hostname; - - @JsonProperty - private List urls; - - @JsonProperty - private List urlsWithIps; - - @JsonProperty - private Long ttl; - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - // Hostname for the ips in urlsWithIps - public String getHostname() { - return hostname; - } - - public List getUrls() { - return urls; - } - - public List getUrlsWithIps() { - return urlsWithIps; - } - - public Long getTtl() { - return ttl; - }; -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.kt new file mode 100644 index 0000000000..3bdb13722d --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/calls/TurnServerInfo.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.messages.calls + +data class TurnServerInfo( + val username: String? = null, + val password: String? = null, + /** Hostname for the ips in [urlsWithIps]. */ + val hostname: String? = null, + val urls: List? = null, + val urlsWithIps: List? = null, + val ttl: Long? = null +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.java deleted file mode 100644 index 5acd4edc23..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages.multidevice; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class DeviceInfo { - - @JsonProperty - public int id; - - @JsonProperty - public String name; - - @JsonProperty - public long lastSeen; - - @JsonProperty - public int registrationId; - - @JsonProperty - public String createdAtCiphertext; - - public DeviceInfo() {} - - public int getId() { - return id; - } - - public String getName() { - return name; - } - - public long getLastSeen() { - return lastSeen; - } - - public int getRegistrationId() { - return registrationId; - } - - public String getCreatedAtCiphertext() { - return createdAtCiphertext; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.kt new file mode 100644 index 0000000000..8d9aa4b39c --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceInfo.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.messages.multidevice + +data class DeviceInfo( + val id: Int = 0, + val name: String? = null, + val lastSeen: Long = 0, + val registrationId: Int = 0, + val createdAtCiphertext: String? = null +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RegisterAsSecondaryDeviceResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RegisterAsSecondaryDeviceResponse.java deleted file mode 100644 index d187aada37..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RegisterAsSecondaryDeviceResponse.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.whispersystems.signalservice.api.messages.multidevice; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.UUID; - -public class RegisterAsSecondaryDeviceResponse { - @JsonProperty - private UUID uuid; - - @JsonProperty - private UUID pni; - - @JsonProperty - private String deviceId; - - public RegisterAsSecondaryDeviceResponse() {} - - public RegisterAsSecondaryDeviceResponse(UUID uuid, UUID pni, String deviceId) { - this.uuid = uuid; - this.pni = pni; - this.deviceId = deviceId; - } - - public UUID getUuid() { - return uuid; - } - - public UUID getPni() { - return pni; - } - - public String getDeviceId() { - return deviceId; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RegisterAsSecondaryDeviceResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RegisterAsSecondaryDeviceResponse.kt new file mode 100644 index 0000000000..adb7e0b562 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RegisterAsSecondaryDeviceResponse.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.messages.multidevice + +import java.util.UUID + +class RegisterAsSecondaryDeviceResponse( + val uuid: UUID, + val pni: UUID, + val deviceId: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversion.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversion.java deleted file mode 100644 index a8389310a6..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversion.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.whispersystems.signalservice.api.payments; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Map; - -public final class CurrencyConversion { - @JsonProperty - private String base; - - @JsonProperty - private Map conversions; - - public String getBase() { - return base; - } - - public Map getConversions() { - return conversions; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversion.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversion.kt new file mode 100644 index 0000000000..b6b538c557 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversion.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.payments + +data class CurrencyConversion( + val base: String = "", + val conversions: Map = emptyMap() +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversions.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversions.java deleted file mode 100644 index 1f2d0abcb4..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversions.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.whispersystems.signalservice.api.payments; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -public final class CurrencyConversions { - @JsonProperty - private List currencies; - - @JsonProperty - private long timestamp; - - public List getCurrencies() { - return currencies; - } - - public long getTimestamp() { - return timestamp; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversions.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversions.kt new file mode 100644 index 0000000000..18a0669492 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/payments/CurrencyConversions.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.payments + +data class CurrencyConversions( + val currencies: List = emptyList(), + val timestamp: Long = 0 +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java deleted file mode 100644 index 5df13b3b70..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java +++ /dev/null @@ -1,241 +0,0 @@ -package org.whispersystems.signalservice.api.profiles; - - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.signal.core.util.logging.Log; -import org.signal.libsignal.zkgroup.InvalidInputException; -import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse; -import org.signal.core.models.ServiceId; -import org.signal.network.util.JsonUtil; - -import java.math.BigDecimal; -import java.util.List; - -public class SignalServiceProfile { - - public enum RequestType { - PROFILE, - PROFILE_AND_CREDENTIAL - } - - private static final String TAG = SignalServiceProfile.class.getSimpleName(); - - @JsonProperty - private String identityKey; - - @JsonProperty - private String name; - - @JsonProperty - private String about; - - @JsonProperty - private String aboutEmoji; - - @JsonProperty - private byte[] paymentAddress; - - @JsonProperty - private String avatar; - - @JsonProperty - private String unidentifiedAccess; - - @JsonProperty - private boolean unrestrictedUnidentifiedAccess; - - @JsonProperty - private Capabilities capabilities; - - @JsonProperty - @JsonSerialize(using = JsonUtil.ServiceIdSerializer.class) - @JsonDeserialize(using = JsonUtil.ServiceIdDeserializer.class) - private ServiceId uuid; - - @JsonProperty - private byte[] credential; - - @JsonProperty - private List badges; - - @JsonProperty - private String phoneNumberSharing; - - @JsonIgnore - private RequestType requestType; - - public SignalServiceProfile() {} - - public String getIdentityKey() { - return identityKey; - } - - public String getName() { - return name; - } - - public String getAbout() { - return about; - } - - public String getAboutEmoji() { - return aboutEmoji; - } - - public byte[] getPaymentAddress() { - return paymentAddress; - } - - public String getAvatar() { - return avatar; - } - - public String getUnidentifiedAccess() { - return unidentifiedAccess; - } - - public String getPhoneNumberSharing() { - return phoneNumberSharing; - } - - public boolean isUnrestrictedUnidentifiedAccess() { - return unrestrictedUnidentifiedAccess; - } - - public Capabilities getCapabilities() { - return capabilities; - } - - public List getBadges() { - return badges; - } - - public ServiceId getServiceId() { - return uuid; - } - - public RequestType getRequestType() { - return requestType; - } - - public void setRequestType(RequestType requestType) { - this.requestType = requestType; - } - - public static class Badge { - @JsonProperty - private String id; - - @JsonProperty - private String category; - - @JsonProperty - private String name; - - @JsonProperty - private String description; - - @JsonProperty - private List sprites6; - - @JsonProperty - private BigDecimal expiration; - - @JsonProperty - private boolean visible; - - @JsonProperty - private long duration; - - public String getId() { - return id; - } - - public String getCategory() { - return category; - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - public List getSprites6() { - return sprites6; - } - - public BigDecimal getExpiration() { - return expiration; - } - - public boolean isVisible() { - return visible; - } - - /** - * @return Duration badge is valid for, in seconds. - */ - public long getDuration() { - return duration; - } - } - - public static class Capabilities { - @JsonProperty - private boolean storage; - - @JsonProperty("ssre2") - private boolean storageServiceEncryptionV2; - - @JsonProperty("usernameChangeSyncMessage") - private boolean usernameSyncMessages; - - @JsonProperty - private boolean optionalPhoneNumber; - - @JsonCreator - public Capabilities() {} - - public Capabilities(boolean storage, boolean storageServiceEncryptionV2, boolean usernameSyncMessages, boolean optionalPhoneNumber) { - this.storage = storage; - this.storageServiceEncryptionV2 = storageServiceEncryptionV2; - this.usernameSyncMessages = usernameSyncMessages; - this.optionalPhoneNumber = optionalPhoneNumber; - } - - public boolean isStorage() { - return storage; - } - - public boolean isStorageServiceEncryptionV2() { - return storageServiceEncryptionV2; - } - - public boolean isUsernameSyncMessages() { - return usernameSyncMessages; - } - - public boolean isOptionalPhoneNumber() { - return optionalPhoneNumber; - } - } - - public ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredentialResponse() { - if (credential == null) return null; - - try { - return new ExpiringProfileKeyCredentialResponse(credential); - } catch (InvalidInputException e) { - Log.w(TAG, e); - return null; - } - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.kt new file mode 100644 index 0000000000..45e95c19d7 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.profiles + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import org.signal.core.models.ServiceId +import org.signal.core.util.logging.Log +import org.signal.libsignal.zkgroup.InvalidInputException +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredentialResponse +import org.signal.network.util.JsonUtil +import java.math.BigDecimal + +class SignalServiceProfile( + val identityKey: String? = null, + val name: String? = null, + val about: String? = null, + val aboutEmoji: String? = null, + val paymentAddress: ByteArray? = null, + val avatar: String? = null, + val unidentifiedAccess: String? = null, + val unrestrictedUnidentifiedAccess: Boolean = false, + val capabilities: Capabilities? = null, + @JsonProperty("uuid") + @JsonSerialize(using = JsonUtil.ServiceIdSerializer::class) + @JsonDeserialize(using = JsonUtil.ServiceIdDeserializer::class) + val serviceId: ServiceId? = null, + val credential: ByteArray? = null, + val badges: List? = null, + val phoneNumberSharing: String? = null +) { + + val expiringProfileKeyCredentialResponse: ExpiringProfileKeyCredentialResponse? + get() { + if (credential == null) { + return null + } + + return try { + ExpiringProfileKeyCredentialResponse(credential) + } catch (e: InvalidInputException) { + Log.w(TAG, e) + null + } + } + + enum class RequestType { + PROFILE, + PROFILE_AND_CREDENTIAL + } + + data class Badge( + val id: String = "", + val category: String = "", + val name: String = "", + val description: String = "", + val sprites6: List = emptyList(), + val expiration: BigDecimal? = null, + val visible: Boolean = false, + /** Duration the badge is valid for, in seconds. */ + val duration: Long = 0 + ) + + data class Capabilities( + val storage: Boolean = false, + @JsonProperty("ssre2") val storageServiceEncryptionV2: Boolean = false, + @JsonProperty("usernameChangeSyncMessage") val usernameSyncMessages: Boolean = false, + val optionalPhoneNumber: Boolean = false + ) + + companion object { + private val TAG = Log.tag(SignalServiceProfile::class) + } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.java deleted file mode 100644 index e20147cde5..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.whispersystems.signalservice.api.provisioning; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class ProvisioningMessage { - - @JsonProperty - private String body; - - public ProvisioningMessage(String body) { - this.body = body; - } - -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.kt new file mode 100644 index 0000000000..c4179452d2 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/provisioning/ProvisioningMessage.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.provisioning + +class ProvisioningMessage( + val body: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.java deleted file mode 100644 index 5d4edbb5ff..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.signal.libsignal.protocol.ecc.ECPublicKey; -import org.whispersystems.signalservice.internal.push.PreKeyEntity; -import org.signal.core.util.Base64; - -import java.io.IOException; - -public class SignedPreKeyEntity extends PreKeyEntity { - - @JsonProperty - @JsonSerialize(using = ByteArraySerializer.class) - @JsonDeserialize(using = ByteArrayDeserializer.class) - private byte[] signature; - - public SignedPreKeyEntity() {} - - public SignedPreKeyEntity(long keyId, ECPublicKey publicKey, byte[] signature) { - super(keyId, publicKey); - this.signature = signature; - } - - public byte[] getSignature() { - return signature; - } - - private static class ByteArraySerializer extends JsonSerializer { - @Override - public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - gen.writeString(Base64.encodeWithoutPadding(value)); - } - } - - private static class ByteArrayDeserializer extends JsonDeserializer { - - @Override - public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - return Base64.decode(p.getValueAsString()); - } - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.kt new file mode 100644 index 0000000000..6f1916a5cd --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/SignedPreKeyEntity.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.push + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import org.signal.libsignal.protocol.ecc.ECPublicKey +import org.whispersystems.signalservice.internal.push.ByteArrayDeserializerBase64 +import org.whispersystems.signalservice.internal.push.ByteArraySerializerBase64NoPadding +import org.whispersystems.signalservice.internal.push.PreKeyEntity + +class SignedPreKeyEntity( + val keyId: Long, + @JsonSerialize(using = PreKeyEntity.ECPublicKeySerializer::class) + @JsonDeserialize(using = PreKeyEntity.ECPublicKeyDeserializer::class) + val publicKey: ECPublicKey, + @JsonSerialize(using = ByteArraySerializerBase64NoPadding::class) + @JsonDeserialize(using = ByteArrayDeserializerBase64::class) + val signature: ByteArray +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonNormalizedPhoneNumberException.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonNormalizedPhoneNumberException.java deleted file mode 100644 index 15b8e9dc60..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonNormalizedPhoneNumberException.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.whispersystems.signalservice.api.push.exceptions; - -import org.signal.network.exceptions.MalformedResponseException; - -import org.signal.network.exceptions.NonSuccessfulResponseCodeException; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.signal.network.util.JsonUtil; - -import io.reactivex.rxjava3.annotations.NonNull; - -/** - * Response indicating we gave the server a non-normalized phone number. The expected normalized version of the number is provided. - */ -public class NonNormalizedPhoneNumberException extends NonSuccessfulResponseCodeException { - - private final String originalNumber; - private final String normalizedNumber; - - public static NonNormalizedPhoneNumberException forResponse(@NonNull String responseBody) throws MalformedResponseException { - JsonResponse response = JsonUtil.fromJsonResponse(responseBody, JsonResponse.class); - return new NonNormalizedPhoneNumberException(response.originalNumber, response.normalizedNumber); - } - - public NonNormalizedPhoneNumberException(String originalNumber, String normalizedNumber) { - super(400); - - this.originalNumber = originalNumber; - this.normalizedNumber = normalizedNumber; - } - - public String getOriginalNumber() { - return originalNumber; - } - - public String getNormalizedNumber() { - return normalizedNumber; - } - - private static class JsonResponse { - @JsonProperty - private String originalNumber; - - @JsonProperty - private String normalizedNumber; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonNormalizedPhoneNumberException.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonNormalizedPhoneNumberException.kt new file mode 100644 index 0000000000..2651d7f704 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonNormalizedPhoneNumberException.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.push.exceptions + +import org.signal.network.exceptions.MalformedResponseException +import org.signal.network.exceptions.NonSuccessfulResponseCodeException +import org.signal.network.util.JsonUtil + +/** + * Response indicating we gave the server a non-normalized phone number. The expected normalized version of the number is provided. + */ +class NonNormalizedPhoneNumberException( + val originalNumber: String, + val normalizedNumber: String +) : NonSuccessfulResponseCodeException(400) { + + /** The 400 response body. Separate from the exception itself so that it can be deserialized. */ + class Body( + val originalNumber: String? = null, + val normalizedNumber: String? = null + ) + + companion object { + @JvmStatic + @Throws(MalformedResponseException::class) + fun forResponse(responseBody: String): NonNormalizedPhoneNumberException { + val body = JsonUtil.fromJsonResponse(responseBody, Body::class.java) + + if (body.originalNumber == null || body.normalizedNumber == null) { + throw MalformedResponseException("Response is missing a number") + } + + return NonNormalizedPhoneNumberException(body.originalNumber, body.normalizedNumber) + } + } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageAuthResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageAuthResponse.java deleted file mode 100644 index 829c3e27b5..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageAuthResponse.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.whispersystems.signalservice.api.storage; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class StorageAuthResponse { - - @JsonProperty - private String username; - - @JsonProperty - private String password; - - public StorageAuthResponse() { } - - public StorageAuthResponse(String username, String password) { - this.username = username; - this.password = password; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageAuthResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageAuthResponse.kt new file mode 100644 index 0000000000..c176b5e968 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageAuthResponse.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.storage + +data class StorageAuthResponse( + val username: String, + val password: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.java deleted file mode 100644 index 0ca73187a6..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.java +++ /dev/null @@ -1,399 +0,0 @@ -package org.whispersystems.signalservice.api.subscriptions; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import javax.annotation.Nullable; - -public final class ActiveSubscription { - - public static final String PAYMENT_METHOD_SEPA_DEBIT = "SEPA_DEBIT"; - - public static final ActiveSubscription EMPTY = new ActiveSubscription(null, null); - - public enum Processor { - STRIPE("STRIPE"), - BRAINTREE("BRAINTREE"), - GOOGLE_PLAY_BILLING("GOOGLE_PLAY_BILLING"); - - private final String code; - - Processor(String code) { - this.code = code; - } - - public String getCode() { - return code; - } - - static Processor fromCode(String code) { - for (Processor value : Processor.values()) { - if (value.code.equals(code)) { - return value; - } - } - - return STRIPE; - } - } - - /** - * As per API documentation - */ - public enum PaymentMethod { - UNKNOWN("UNKNOWN"), - CARD("CARD"), - PAYPAL("PAYPAL"), - SEPA_DEBIT("SEPA_DEBIT"), - IDEAL("IDEAL"), - GOOGLE_PLAY_BILLING("GOOGLE_PLAY_BILLING"), - APPLE_APP_STORE("APPLE_APP_STORE"); - - private String code; - - PaymentMethod(String code) { - this.code = code; - } - - static PaymentMethod fromCode(String code) { - for (PaymentMethod method : PaymentMethod.values()) { - if (Objects.equals(method.code, code)) { - return method; - } - } - - return PaymentMethod.UNKNOWN; - } - } - - public enum Status { - /** - * The subscription is currently in a trial period and it's safe to provision your product for your customer. - * The subscription transitions automatically to active when the first payment is made. - */ - TRIALING("trialing"), - - /** - * The subscription is in good standing and the most recent payment was successful. It's safe to provision your product for your customer. - */ - ACTIVE("active"), - - /** - * Payment failed when you created the subscription. A successful payment needs to be made within 23 hours to activate the subscription. - */ - INCOMPLETE("incomplete"), - - /** - * The initial payment on the subscription failed and no successful payment was made within 23 hours of creating the subscription. - * These subscriptions don't bill customers. This status exists so you can track customers that failed to activate their subscriptions. - */ - INCOMPLETE_EXPIRED("incomplete_expired"), - - /** - * Payment on the latest invoice either failed or wasn't attempted. - */ - PAST_DUE("past_due"), - - /** - * The subscription has been canceled. During cancellation, automatic collection for all unpaid invoices is disabled (auto_advance=false). - */ - CANCELED("canceled"), - - /** - * The latest invoice hasn't been paid but the subscription remains in place. - * The latest invoice remains open and invoices continue to be generated but payments aren't attempted. - */ - UNPAID("unpaid"); - - private final String status; - - private static final Set FAILURE_STATUSES = new HashSet<>(Arrays.asList( - INCOMPLETE_EXPIRED, - PAST_DUE, - UNPAID - )); - - Status(String status) { - this.status = status; - } - - public static Status getStatus(String status) { - for (Status s : Status.values()) { - if (Objects.equals(status, s.status)) { - return s; - } - } - - throw new IllegalArgumentException("Unknown status " + status); - } - - static boolean isPaymentFailed(String status) { - return FAILURE_STATUSES.contains(getStatus(status)); - } - } - - private final Subscription activeSubscription; - private final ChargeFailure chargeFailure; - - @JsonCreator - public ActiveSubscription(@JsonProperty("subscription") Subscription activeSubscription, - @JsonProperty("chargeFailure") ChargeFailure chargeFailure) - { - this.activeSubscription = activeSubscription; - this.chargeFailure = chargeFailure; - } - - public Subscription getActiveSubscription() { - return activeSubscription; - } - - public ChargeFailure getChargeFailure() { - return chargeFailure; - } - - public boolean isActive() { - return activeSubscription != null && activeSubscription.isActive(); - } - - public boolean isPendingBankTransfer() { - return activeSubscription != null && Objects.equals(activeSubscription.paymentMethod, PAYMENT_METHOD_SEPA_DEBIT) && activeSubscription.paymentPending; - } - - public boolean isInProgress() { - return activeSubscription != null && !isActive() && (!isFailedPayment() || isPastDue()) && !isCanceled(); - } - - public boolean isPastDue() { - return activeSubscription != null && activeSubscription.isPastDue(); - } - - public boolean isFailedPayment() { - return chargeFailure != null || (activeSubscription != null && !isActive() && activeSubscription.isFailedPayment()); - } - - public boolean isCanceled() { - return activeSubscription != null && activeSubscription.isCanceled(); - } - - /** - * Backups-specific call that gives us a value that should align with autoRenew from the GPB payment. - */ - public boolean willCancelAtPeriodEnd() { - return activeSubscription == null || activeSubscription.willCancelAtPeriodEnd; - } - - public static final class Subscription { - private final int level; - private final String currency; - private final BigDecimal amount; - private final long endOfCurrentPeriod; - private final boolean isActive; - private final long billingCycleAnchor; - private final boolean willCancelAtPeriodEnd; - private final String status; - private final Processor processor; - private final PaymentMethod paymentMethod; - private final boolean paymentPending; - - @JsonCreator - public Subscription(@JsonProperty("level") int level, - @JsonProperty("currency") String currency, - @JsonProperty("amount") BigDecimal amount, - @JsonProperty("endOfCurrentPeriod") long endOfCurrentPeriod, - @JsonProperty("active") boolean isActive, - @JsonProperty("billingCycleAnchor") long billingCycleAnchor, - @JsonProperty("cancelAtPeriodEnd") boolean willCancelAtPeriodEnd, - @JsonProperty("status") String status, - @JsonProperty("processor") String processor, - @JsonProperty("paymentMethod") String paymentMethod, - @JsonProperty("paymentPending") boolean paymentPending) - { - this.level = level; - this.currency = currency; - this.amount = amount; - this.endOfCurrentPeriod = endOfCurrentPeriod; - this.isActive = isActive; - this.billingCycleAnchor = billingCycleAnchor; - this.willCancelAtPeriodEnd = willCancelAtPeriodEnd; - this.status = status; - this.processor = Processor.fromCode(processor); - this.paymentMethod = PaymentMethod.fromCode(paymentMethod); - this.paymentPending = paymentPending; - } - - public int getLevel() { - return level; - } - - public String getCurrency() { - return currency; - } - - public BigDecimal getAmount() { - return amount; - } - - /** - * UNIX Epoch Timestamp in seconds, can be used to calculate next billing date per - * https://stripe.com/docs/billing/subscriptions/billing-cycle - */ - public long getBillingCycleAnchor() { - return billingCycleAnchor; - } - - /** - * Whether this subscription is currently active. - */ - public boolean isActive() { - return isActive; - } - - /** - * UNIX Epoch Timestamp in seconds - */ - public long getEndOfCurrentPeriod() { - return endOfCurrentPeriod; - } - - /** - * Whether this subscription is set to end at the end of the current period. - */ - public boolean willCancelAtPeriodEnd() { - return willCancelAtPeriodEnd; - } - - /** - * The Stripe status of this subscription (see https://stripe.com/docs/billing/subscriptions/overview#subscription-statuses) - */ - public String getStatus() { - return status; - } - - public Processor getProcessor() { - return processor; - } - - public PaymentMethod getPaymentMethod() { - return paymentMethod; - } - - /** - * @return Whether the latest invoice for the subscription is in a non-terminal state - */ - public boolean isPaymentPending() { - return paymentPending; - } - - public boolean isFailedPayment() { - return Status.isPaymentFailed(getStatus()); - } - - public boolean isPastDue() { - return Status.getStatus(getStatus()) == Status.PAST_DUE; - } - - public boolean isCanceled() { - return Status.getStatus(getStatus()) == Status.CANCELED; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final Subscription that = (Subscription) o; - return level == that.level && endOfCurrentPeriod == that.endOfCurrentPeriod && isActive == that.isActive && billingCycleAnchor == that.billingCycleAnchor && willCancelAtPeriodEnd == that.willCancelAtPeriodEnd && currency - .equals(that.currency) && amount.equals(that.amount) && status.equals(that.status) && Objects.equals(paymentMethod, that.paymentMethod) && paymentPending == that.paymentPending; - } - - @Override - public int hashCode() { - return Objects.hash(level, currency, amount, endOfCurrentPeriod, isActive, billingCycleAnchor, willCancelAtPeriodEnd, status, paymentMethod, paymentPending); - } - } - - public static final class ChargeFailure { - private final String code; - private final String message; - private final String outcomeNetworkStatus; - private final String outcomeNetworkReason; - private final String outcomeType; - - @JsonCreator - public ChargeFailure(@JsonProperty("code") String code, - @JsonProperty("message") String message, - @JsonProperty("outcomeNetworkStatus") String outcomeNetworkStatus, - @JsonProperty("outcomeNetworkReason") String outcomeNetworkReason, - @JsonProperty("outcomeType") String outcomeType) - { - this.code = code; - this.message = message; - this.outcomeNetworkStatus = outcomeNetworkStatus; - this.outcomeNetworkReason = outcomeNetworkReason; - this.outcomeType = outcomeType; - } - - /** - * Error code explaining reason for charge failure if available (see the errors section for a list of codes). - *

- * See: https://stripe.com/docs/api/charges/object#charge_object-failure_code - */ - public String getCode() { - return code; - } - - /** - * Message to user further explaining reason for charge failure if available. - *

- * See: https://stripe.com/docs/api/charges/object#charge_object-failure_message - */ - public String getMessage() { - return message; - } - - /** - * Possible values are approved_by_network, declined_by_network, not_sent_to_network, and reversed_after_approval. - * The value reversed_after_approval indicates the payment was blocked by Stripe after bank authorization, - * and may temporarily appear as "pending" on a cardholder's statement. - *

- * See: https://stripe.com/docs/api/charges/object#charge_object-outcome-network_status - */ - public String getOutcomeNetworkStatus() { - return outcomeNetworkStatus; - } - - /** - * An enumerated value providing a more detailed explanation of the outcome's type. Charges blocked by Radar's default block rule have the value - * highest_risk_level. Charges placed in review by Radar's default review rule have the value elevated_risk_level. Charges authorized, blocked, or placed - * in review by custom rules have the value rule. See understanding declines for more details. - *

- * See: https://stripe.com/docs/api/charges/object#charge_object-outcome-reason - */ - public @Nullable String getOutcomeNetworkReason() { - return outcomeNetworkReason; - } - - /** - * Possible values are authorized, manual_review, issuer_declined, blocked, and invalid. See understanding declines and Radar reviews for details. - *

- * See: https://stripe.com/docs/api/charges/object#charge_object-outcome-type - */ - public String getOutcomeType() { - return outcomeType; - } - - @Override public String toString() { - return "ChargeFailure{" + - "code='" + code + '\'' + - ", outcomeNetworkStatus='" + outcomeNetworkStatus + '\'' + - ", outcomeNetworkReason='" + outcomeNetworkReason + '\'' + - ", outcomeType='" + outcomeType + '\'' + - '}'; - } - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.kt new file mode 100644 index 0000000000..b1a874015c --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.kt @@ -0,0 +1,207 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.subscriptions + +import com.fasterxml.jackson.annotation.JsonProperty +import java.math.BigDecimal + +class ActiveSubscription( + @JsonProperty("subscription") val activeSubscription: Subscription? = null, + val chargeFailure: ChargeFailure? = null +) { + + val isActive: Boolean + get() = activeSubscription != null && activeSubscription.isActive + + val isPendingBankTransfer: Boolean + get() = activeSubscription != null && activeSubscription.paymentMethod == PaymentMethod.SEPA_DEBIT && activeSubscription.paymentPending + + val isInProgress: Boolean + get() = activeSubscription != null && !isActive && (!isFailedPayment || isPastDue) && !isCanceled + + val isPastDue: Boolean + get() = activeSubscription != null && activeSubscription.isPastDue + + val isFailedPayment: Boolean + get() = chargeFailure != null || (activeSubscription != null && !isActive && activeSubscription.isFailedPayment) + + val isCanceled: Boolean + get() = activeSubscription != null && activeSubscription.isCanceled + + /** + * Backups-specific call that gives us a value that should align with autoRenew from the GPB payment. + */ + val willCancelAtPeriodEnd: Boolean + get() = activeSubscription == null || activeSubscription.willCancelAtPeriodEnd + + enum class Processor(val code: String) { + STRIPE("STRIPE"), + BRAINTREE("BRAINTREE"), + GOOGLE_PLAY_BILLING("GOOGLE_PLAY_BILLING"); + + companion object { + fun fromCode(code: String?): Processor = entries.firstOrNull { it.code == code } ?: STRIPE + } + } + + /** + * As per API documentation + */ + enum class PaymentMethod(val code: String) { + UNKNOWN("UNKNOWN"), + CARD("CARD"), + PAYPAL("PAYPAL"), + SEPA_DEBIT("SEPA_DEBIT"), + IDEAL("IDEAL"), + GOOGLE_PLAY_BILLING("GOOGLE_PLAY_BILLING"), + APPLE_APP_STORE("APPLE_APP_STORE"); + + companion object { + fun fromCode(code: String?): PaymentMethod = entries.firstOrNull { it.code == code } ?: UNKNOWN + } + } + + enum class Status(val code: String) { + /** + * The subscription is currently in a trial period and it's safe to provision your product for your customer. + * The subscription transitions automatically to active when the first payment is made. + */ + TRIALING("trialing"), + + /** + * The subscription is in good standing and the most recent payment was successful. It's safe to provision your product for your customer. + */ + ACTIVE("active"), + + /** + * Payment failed when you created the subscription. A successful payment needs to be made within 23 hours to activate the subscription. + */ + INCOMPLETE("incomplete"), + + /** + * The initial payment on the subscription failed and no successful payment was made within 23 hours of creating the subscription. + * These subscriptions don't bill customers. This status exists so you can track customers that failed to activate their subscriptions. + */ + INCOMPLETE_EXPIRED("incomplete_expired"), + + /** + * Payment on the latest invoice either failed or wasn't attempted. + */ + PAST_DUE("past_due"), + + /** + * The subscription has been canceled. During cancellation, automatic collection for all unpaid invoices is disabled (auto_advance=false). + */ + CANCELED("canceled"), + + /** + * The latest invoice hasn't been paid but the subscription remains in place. + * The latest invoice remains open and invoices continue to be generated but payments aren't attempted. + */ + UNPAID("unpaid"); + + companion object { + private val FAILURE_STATUSES = setOf(INCOMPLETE_EXPIRED, PAST_DUE, UNPAID) + + @JvmStatic + fun getStatus(status: String?): Status { + return entries.firstOrNull { it.code == status } ?: throw IllegalArgumentException("Unknown status $status") + } + + internal fun isPaymentFailed(status: String?): Boolean = FAILURE_STATUSES.contains(getStatus(status)) + } + } + + data class Subscription( + val level: Int = 0, + val currency: String = "", + val amount: BigDecimal = BigDecimal.ZERO, + /** UNIX Epoch Timestamp in seconds */ + val endOfCurrentPeriod: Long = 0, + /** Whether this subscription is currently active. */ + @JsonProperty("active") val isActive: Boolean = false, + /** + * UNIX Epoch Timestamp in seconds, can be used to calculate next billing date per + * https://stripe.com/docs/billing/subscriptions/billing-cycle + */ + val billingCycleAnchor: Long = 0, + /** Whether this subscription is set to end at the end of the current period. */ + @JsonProperty("cancelAtPeriodEnd") val willCancelAtPeriodEnd: Boolean = false, + /** The Stripe status of this subscription (see https://stripe.com/docs/billing/subscriptions/overview#subscription-statuses) */ + val status: String = "", + @JsonProperty("processor") private val processorCode: String? = null, + @JsonProperty("paymentMethod") private val paymentMethodCode: String? = null, + /** Whether the latest invoice for the subscription is in a non-terminal state. */ + val paymentPending: Boolean = false + ) { + val processor: Processor + get() = Processor.fromCode(processorCode) + + val paymentMethod: PaymentMethod + get() = PaymentMethod.fromCode(paymentMethodCode) + + val isFailedPayment: Boolean + get() = Status.isPaymentFailed(status) + + val isPastDue: Boolean + get() = Status.getStatus(status) == Status.PAST_DUE + + val isCanceled: Boolean + get() = Status.getStatus(status) == Status.CANCELED + } + + data class ChargeFailure( + /** + * Error code explaining reason for charge failure if available (see the errors section for a list of codes). + * + * See: [https://stripe.com/docs/api/charges/object#charge_object-failure_code] + */ + val code: String? = null, + + /** + * Message to user further explaining reason for charge failure if available. + * + * See: [https://stripe.com/docs/api/charges/object#charge_object-failure_message] + */ + val message: String? = null, + + /** + * Possible values are approved_by_network, declined_by_network, not_sent_to_network, and reversed_after_approval. + * The value reversed_after_approval indicates the payment was blocked by Stripe after bank authorization, + * and may temporarily appear as "pending" on a cardholder's statement. + * + * See: [https://stripe.com/docs/api/charges/object#charge_object-outcome-network_status] + */ + val outcomeNetworkStatus: String? = null, + + /** + * An enumerated value providing a more detailed explanation of the outcome's type. Charges blocked by Radar's default block rule have the value + * highest_risk_level. Charges placed in review by Radar's default review rule have the value elevated_risk_level. Charges authorized, blocked, or placed + * in review by custom rules have the value rule. See understanding declines for more details. + * + * See: [https://stripe.com/docs/api/charges/object#charge_object-outcome-reason] + */ + val outcomeNetworkReason: String? = null, + + /** + * Possible values are authorized, manual_review, issuer_declined, blocked, and invalid. See understanding declines and Radar reviews for details. + * + * See: [https://stripe.com/docs/api/charges/object#charge_object-outcome-type] + */ + val outcomeType: String? = null + ) { + override fun toString(): String { + return "ChargeFailure{code='$code', outcomeNetworkStatus='$outcomeNetworkStatus', outcomeNetworkReason='$outcomeNetworkReason', outcomeType='$outcomeType'}" + } + } + + companion object { + const val PAYMENT_METHOD_SEPA_DEBIT = "SEPA_DEBIT" + + @JvmField + val EMPTY = ActiveSubscription(null, null) + } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalConfirmPaymentIntentResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalConfirmPaymentIntentResponse.java deleted file mode 100644 index d83683825c..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalConfirmPaymentIntentResponse.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.whispersystems.signalservice.api.subscriptions; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Response object from creating a payment intent via PayPal - */ -public class PayPalConfirmPaymentIntentResponse { - - private final String paymentId; - - @JsonCreator - public PayPalConfirmPaymentIntentResponse(@JsonProperty("paymentId") String paymentId) { - this.paymentId = paymentId; - } - - public String getPaymentId() { - return paymentId; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalConfirmPaymentIntentResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalConfirmPaymentIntentResponse.kt new file mode 100644 index 0000000000..19969ac93a --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalConfirmPaymentIntentResponse.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.subscriptions + +/** + * Response object from confirming a payment intent via PayPal + */ +data class PayPalConfirmPaymentIntentResponse( + val paymentId: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentIntentResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentIntentResponse.java deleted file mode 100644 index fb87bca0e4..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentIntentResponse.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.whispersystems.signalservice.api.subscriptions; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Response object from creating a payment intent via PayPal - */ -public class PayPalCreatePaymentIntentResponse { - - private final String approvalUrl; - private final String paymentId; - - @JsonCreator - public PayPalCreatePaymentIntentResponse(@JsonProperty("approvalUrl") String approvalUrl, @JsonProperty("paymentId") String paymentId) { - this.approvalUrl = approvalUrl; - this.paymentId = paymentId; - } - - public String getApprovalUrl() { - return approvalUrl; - } - - public String getPaymentId() { - return paymentId; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentIntentResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentIntentResponse.kt new file mode 100644 index 0000000000..5708ead594 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentIntentResponse.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.subscriptions + +/** + * Response object from creating a payment intent via PayPal + */ +data class PayPalCreatePaymentIntentResponse( + val approvalUrl: String, + val paymentId: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentMethodResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentMethodResponse.java deleted file mode 100644 index 59d4bcb5c9..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentMethodResponse.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.whispersystems.signalservice.api.subscriptions; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class PayPalCreatePaymentMethodResponse { - private final String approvalUrl; - private final String token; - - @JsonCreator - public PayPalCreatePaymentMethodResponse(@JsonProperty("approvalUrl") String approvalUrl, @JsonProperty("token") String token) { - this.approvalUrl = approvalUrl; - this.token = token; - } - - public String getApprovalUrl() { - return approvalUrl; - } - - public String getToken() { - return token; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentMethodResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentMethodResponse.kt new file mode 100644 index 0000000000..045907cd51 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/PayPalCreatePaymentMethodResponse.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.subscriptions + +/** + * Response object from creating a payment method via PayPal + */ +data class PayPalCreatePaymentMethodResponse( + val approvalUrl: String, + val token: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/StripeClientSecret.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/StripeClientSecret.java deleted file mode 100644 index 952ecf82c8..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/StripeClientSecret.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.whispersystems.signalservice.api.subscriptions; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public final class StripeClientSecret { - - private final String id; - private final String clientSecret; - - @JsonCreator - public StripeClientSecret(@JsonProperty("clientSecret") String clientSecret) { - this.id = clientSecret.replaceFirst("_secret.*", ""); - this.clientSecret = clientSecret; - } - - public String getId() { - return id; - } - - public String getClientSecret() { - return clientSecret; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/StripeClientSecret.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/StripeClientSecret.kt new file mode 100644 index 0000000000..941667cd23 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/subscriptions/StripeClientSecret.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.subscriptions + +data class StripeClientSecret( + val clientSecret: String +) { + val id: String + get() = clientSecret.replaceFirst("_secret.*".toRegex(), "") +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AuthCredentials.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AuthCredentials.java deleted file mode 100644 index 270444269e..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AuthCredentials.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import okhttp3.Credentials; - -public class AuthCredentials { - - @JsonProperty - private String username; - - @JsonProperty - private String password; - - public static AuthCredentials create(String username, String password) { - AuthCredentials authCredentials = new AuthCredentials(); - authCredentials.username = username; - authCredentials.password = password; - return authCredentials; - } - - public String asBasic() { - return Credentials.basic(username, password); - } - - public String username() { return username; } - - public String password() { return password; } - - @Override - public String toString() { - return "AuthCredentials(xxx)"; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AuthCredentials.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AuthCredentials.kt new file mode 100644 index 0000000000..23640e50b7 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/AuthCredentials.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonProperty +import okhttp3.Credentials + +class AuthCredentials( + @field:JsonProperty private val username: String, + @field:JsonProperty private val password: String +) { + fun asBasic(): String = Credentials.basic(username, password) + + fun username(): String = username + + fun password(): String = password + + override fun toString(): String = "AuthCredentials(xxx)" + + companion object { + @JvmStatic + fun create(username: String, password: String): AuthCredentials = AuthCredentials(username, password) + } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/CdsiAuthResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/CdsiAuthResponse.java deleted file mode 100644 index 43f551765a..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/CdsiAuthResponse.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class CdsiAuthResponse { - - @JsonProperty - private String username; - - @JsonProperty - private String password; - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/CdsiAuthResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/CdsiAuthResponse.kt new file mode 100644 index 0000000000..3442f448dd --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/CdsiAuthResponse.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +data class CdsiAuthResponse( + val username: String, + val password: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ConfirmUsernameRequest.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ConfirmUsernameRequest.java deleted file mode 100644 index 1b7282812c..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ConfirmUsernameRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class ConfirmUsernameRequest { - @JsonProperty - private String usernameHash; - - @JsonProperty - private String zkProof; - - @JsonProperty - private String encryptedUsername; - - public ConfirmUsernameRequest(String usernameHash, String zkProof, String encryptedUsername) { - this.usernameHash = usernameHash; - this.zkProof = zkProof; - this.encryptedUsername = encryptedUsername; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ConfirmUsernameRequest.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ConfirmUsernameRequest.kt new file mode 100644 index 0000000000..86901f54af --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ConfirmUsernameRequest.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +class ConfirmUsernameRequest( + val usernameHash: String, + val zkProof: String, + val encryptedUsername: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.java deleted file mode 100644 index 6571ddfa0e..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; - -import java.util.List; - -public class DeviceInfoList { - - @JsonProperty - public List devices; - - public DeviceInfoList() {} - - public List getDevices() { - return devices; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.kt new file mode 100644 index 0000000000..97dc4028e8 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceInfoList.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo + +class DeviceInfoList( + val devices: List = emptyList() +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.java deleted file mode 100644 index 233eed58b8..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class DeviceLimit { - - @JsonProperty - private int current; - - @JsonProperty - private int max; - - public int getCurrent() { - return current; - } - - public int getMax() { - return max; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.kt new file mode 100644 index 0000000000..10d1001ad9 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DeviceLimit.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +data class DeviceLimit( + val current: Int = 0, + val max: Int = 0 +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationIntentResult.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationIntentResult.java deleted file mode 100644 index d9b47a950b..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationIntentResult.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class DonationIntentResult { - @JsonProperty("id") - private String id; - - @JsonProperty("client_secret") - private String clientSecret; - - public DonationIntentResult(@JsonProperty("id") String id, @JsonProperty("client_secret") String clientSecret) { - this.id = id; - this.clientSecret = clientSecret; - } - - public String getId() { - return id; - } - - public String getClientSecret() { - return clientSecret; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationIntentResult.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationIntentResult.kt new file mode 100644 index 0000000000..8449725a41 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/DonationIntentResult.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonProperty + +data class DonationIntentResult( + val id: String, + @JsonProperty("client_secret") val clientSecret: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupMismatchedDevices.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupMismatchedDevices.java deleted file mode 100644 index 8c3f0712d2..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupMismatchedDevices.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Represents the body of a 409 response from the service during a sender key send. - */ -public class GroupMismatchedDevices { - @JsonProperty - private String uuid; - - @JsonProperty - private MismatchedDevices devices; - - public GroupMismatchedDevices() {} - - public String getUuid() { - return uuid; - } - - public MismatchedDevices getDevices() { - return devices; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupMismatchedDevices.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupMismatchedDevices.kt new file mode 100644 index 0000000000..9dd401310f --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupMismatchedDevices.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +/** + * Represents the body of a 409 response from the service during a sender key send. + */ +data class GroupMismatchedDevices( + val uuid: String, + val devices: MismatchedDevices +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupStaleDevices.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupStaleDevices.java deleted file mode 100644 index 6ceaf281f9..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupStaleDevices.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Represents the body of a 410 response from the service during a sender key send. - */ -public class GroupStaleDevices { - - @JsonProperty - private String uuid; - - @JsonProperty - private StaleDevices devices; - - public String getUuid() { - return uuid; - } - - public StaleDevices getDevices() { - return devices; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupStaleDevices.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupStaleDevices.kt new file mode 100644 index 0000000000..329d0a85b1 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/GroupStaleDevices.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +/** + * Represents the body of a 410 response from the service during a sender key send. + */ +data class GroupStaleDevices( + val uuid: String, + val devices: StaleDevices +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckRequest.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckRequest.java deleted file mode 100644 index 9207e5b583..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckRequest.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.signal.libsignal.protocol.IdentityKey; -import org.signal.core.models.ServiceId; -import org.signal.network.util.JsonUtil; -import org.signal.core.util.Base64; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.List; - -import javax.annotation.Nonnull; - -public class IdentityCheckRequest { - @JsonProperty("elements") - private final List serviceIdFingerprintPairs; - - public IdentityCheckRequest(@Nonnull List serviceIdKeyPairs) { - this.serviceIdFingerprintPairs = serviceIdKeyPairs; - } - - public List getServiceIdFingerprintPairs() { - return serviceIdFingerprintPairs; - } - - public static final class ServiceIdFingerprintPair { - - @JsonProperty("uuid") - @JsonSerialize(using = JsonUtil.ServiceIdSerializer.class) - private final ServiceId serviceId; - - @JsonProperty - private final String fingerprint; - - public ServiceIdFingerprintPair(@Nonnull ServiceId serviceId, @Nonnull IdentityKey identityKey) { - this.serviceId = serviceId; - - try { - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); - this.fingerprint = Base64.encodeWithPadding(messageDigest.digest(identityKey.serialize()), 0, 4); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - public ServiceId getServiceId() { - return serviceId; - } - - public String getFingerprint() { - return fingerprint; - } - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckRequest.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckRequest.kt new file mode 100644 index 0000000000..741705360e --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckRequest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import org.signal.core.models.ServiceId +import org.signal.core.util.Base64 +import org.signal.libsignal.protocol.IdentityKey +import org.signal.network.util.JsonUtil +import java.security.MessageDigest + +class IdentityCheckRequest( + @JsonProperty("elements") val serviceIdFingerprintPairs: List +) { + + class ServiceIdFingerprintPair( + @JsonProperty("uuid") @JsonSerialize(using = JsonUtil.ServiceIdSerializer::class) val serviceId: ServiceId, + val fingerprint: String + ) { + constructor(serviceId: ServiceId, identityKey: IdentityKey) : this( + serviceId, + Base64.encodeWithPadding(MessageDigest.getInstance("SHA-256").digest(identityKey.serialize()), 0, 4) + ) + } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckResponse.java deleted file mode 100644 index 397a58c00d..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckResponse.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import org.signal.libsignal.protocol.IdentityKey; -import org.signal.core.models.ServiceId; -import org.signal.network.util.JsonUtil; - -import java.util.List; - -import javax.annotation.Nullable; - -public class IdentityCheckResponse { - - @JsonProperty("elements") - private List serviceIdKeyPairs; - - public IdentityCheckResponse() {} - - // Visible for testing - public IdentityCheckResponse(List serviceIdKeyPairs) { - this.serviceIdKeyPairs = serviceIdKeyPairs; - } - - public @Nullable List getServiceIdKeyPairs() { - return serviceIdKeyPairs; - } - - public static final class ServiceIdentityPair { - - @JsonProperty("uuid") - @JsonDeserialize(using = JsonUtil.ServiceIdDeserializer.class) - private ServiceId serviceId; - - @JsonProperty - @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) - private IdentityKey identityKey; - - public ServiceIdentityPair() {} - - // Visible for testing - public ServiceIdentityPair(ServiceId serviceId, IdentityKey identityKey) { - this.serviceId = serviceId; - this.identityKey = identityKey; - } - - public @Nullable ServiceId getServiceId() { - return serviceId; - } - - public @Nullable IdentityKey getIdentityKey() { - return identityKey; - } - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckResponse.kt new file mode 100644 index 0000000000..0002316ade --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/IdentityCheckResponse.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import org.signal.core.models.ServiceId +import org.signal.libsignal.protocol.IdentityKey +import org.signal.network.util.JsonUtil + +class IdentityCheckResponse( + @JsonProperty("elements") val serviceIdKeyPairs: List? = null +) { + + class ServiceIdentityPair( + @JsonProperty("uuid") @JsonDeserialize(using = JsonUtil.ServiceIdDeserializer::class) val serviceId: ServiceId? = null, + @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer::class) val identityKey: IdentityKey? = null + ) +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/KyberPreKeyEntity.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/KyberPreKeyEntity.java deleted file mode 100644 index 0612151524..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/KyberPreKeyEntity.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.kem.KEMPublicKey; -import org.signal.core.util.Base64; - -import java.io.IOException; - -public class KyberPreKeyEntity { - - @JsonProperty - private long keyId; - - @JsonProperty - @JsonSerialize(using = KEMPublicKeySerializer.class) - @JsonDeserialize(using = KEMPublicKeyDeserializer.class) - private KEMPublicKey publicKey; - - @JsonProperty - @JsonSerialize(using = ByteArraySerializer.class) - @JsonDeserialize(using = ByteArrayDeserializer.class) - private byte[] signature; - - public KyberPreKeyEntity() {} - - public KyberPreKeyEntity(long keyId, KEMPublicKey publicKey, byte[] signature) { - this.keyId = keyId; - this.publicKey = publicKey; - this.signature = signature; - } - - public long getKeyId() { - return keyId; - } - - public KEMPublicKey getPublicKey() { - return publicKey; - } - - public byte[] getSignature() { - return signature; - } - - private static class KEMPublicKeySerializer extends JsonSerializer { - @Override - public void serialize(KEMPublicKey value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - gen.writeString(Base64.encodeWithoutPadding(value.serialize())); - } - } - - private static class KEMPublicKeyDeserializer extends JsonDeserializer { - @Override - public KEMPublicKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - try { - return new KEMPublicKey(Base64.decode(p.getValueAsString()), 0); - } catch (InvalidKeyException e) { - throw new IOException(e); - } - } - } - - private static class ByteArraySerializer extends JsonSerializer { - @Override - public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - gen.writeString(Base64.encodeWithoutPadding(value)); - } - } - - private static class ByteArrayDeserializer extends JsonDeserializer { - - @Override - public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - return Base64.decode(p.getValueAsString()); - } - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/KyberPreKeyEntity.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/KyberPreKeyEntity.kt new file mode 100644 index 0000000000..5852e3fe4c --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/KyberPreKeyEntity.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonSerializer +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import org.signal.core.util.Base64 +import org.signal.libsignal.protocol.kem.KEMPublicKey + +class KyberPreKeyEntity( + val keyId: Long, + @JsonSerialize(using = KEMPublicKeySerializer::class) + @JsonDeserialize(using = KEMPublicKeyDeserializer::class) + val publicKey: KEMPublicKey, + @JsonSerialize(using = ByteArraySerializerBase64NoPadding::class) + @JsonDeserialize(using = ByteArrayDeserializerBase64::class) + val signature: ByteArray +) { + class KEMPublicKeySerializer : JsonSerializer() { + override fun serialize(value: KEMPublicKey, gen: JsonGenerator, serializers: SerializerProvider) { + gen.writeString(Base64.encodeWithoutPadding(value.serialize())) + } + } + + class KEMPublicKeyDeserializer : JsonDeserializer() { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): KEMPublicKey { + return KEMPublicKey(Base64.decode(p.valueAsString), 0) + } + } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.java deleted file mode 100644 index 97f44ab52d..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2026 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.signal.libsignal.net.MismatchedDeviceException; - -import java.util.ArrayList; -import java.util.List; - -public class MismatchedDevices { - @JsonProperty - public List missingDevices; - - @JsonProperty - public List extraDevices; - - public List getMissingDevices() { - return missingDevices; - } - - public List getExtraDevices() { - return extraDevices; - } - - public static MismatchedDevices fromLibSignal(MismatchedDeviceException.Entry entry) { - MismatchedDevices result = new MismatchedDevices(); - - result.missingDevices = toList(entry.getMissingDevices()); - result.extraDevices = toList(entry.getExtraDevices()); - - return result; - } - - private static List toList(int[] array) { - List list = new ArrayList<>(array.length); - for (int value : array) { - list.add(value); - } - return list; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.kt new file mode 100644 index 0000000000..aeed5ce7e8 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/MismatchedDevices.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import org.signal.libsignal.net.MismatchedDeviceException + +data class MismatchedDevices( + val missingDevices: List = emptyList(), + val extraDevices: List = emptyList() +) { + companion object { + @JvmStatic + fun fromLibSignal(entry: MismatchedDeviceException.Entry): MismatchedDevices { + return MismatchedDevices(entry.missingDevices.toList(), entry.extraDevices.toList()) + } + } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java deleted file mode 100644 index 618d2ca135..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class OutgoingPushMessage { - - @JsonProperty - public int type; - @JsonProperty - public int destinationDeviceId; - @JsonProperty - public int destinationRegistrationId; - @JsonProperty - public String content; - - public OutgoingPushMessage() {} - - public OutgoingPushMessage(int type, - int destinationDeviceId, - int destinationRegistrationId, - String content) - { - this.type = type; - this.destinationDeviceId = destinationDeviceId; - this.destinationRegistrationId = destinationRegistrationId; - this.content = content; - } - - public int getDestinationDeviceId() { - return destinationDeviceId; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.kt new file mode 100644 index 0000000000..e45628b3ee --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +data class OutgoingPushMessage( + val type: Int, + val destinationDeviceId: Int, + val destinationRegistrationId: Int, + val content: String +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java deleted file mode 100644 index 2fb23e2a10..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; -import java.util.stream.Collectors; - -public class OutgoingPushMessageList { - - @JsonProperty - private String destination; - - @JsonProperty - private long timestamp; - - @JsonProperty - private List messages; - - @JsonProperty - private boolean online; - - @JsonProperty - private boolean urgent; - - public OutgoingPushMessageList(String destination, - long timestamp, - List messages, - boolean online, - boolean urgent) - { - this.timestamp = timestamp; - this.destination = destination; - this.messages = messages; - this.online = online; - this.urgent = urgent; - } - - public String getDestination() { - return destination; - } - - public List getMessages() { - return messages; - } - - public long getTimestamp() { - return timestamp; - } - - public boolean isOnline() { - return online; - } - - public boolean isUrgent() { - return urgent; - } - - @JsonIgnore - public List getDevices() { - return messages.stream().map(OutgoingPushMessage::getDestinationDeviceId).collect(Collectors.toList()); - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.kt new file mode 100644 index 0000000000..26b1177057 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonIgnore + +data class OutgoingPushMessageList( + val destination: String, + val timestamp: Long, + val messages: List, + val online: Boolean, + val urgent: Boolean +) { + @get:JsonIgnore + val devices: List + get() = messages.map { it.destinationDeviceId } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.java deleted file mode 100644 index b99c892c58..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.ecc.ECPublicKey; -import org.signal.core.util.Base64; - -import java.io.IOException; - -public class PreKeyEntity { - - @JsonProperty - private long keyId; - - @JsonProperty - @JsonSerialize(using = ECPublicKeySerializer.class) - @JsonDeserialize(using = ECPublicKeyDeserializer.class) - private ECPublicKey publicKey; - - public PreKeyEntity() {} - - public PreKeyEntity(long keyId, ECPublicKey publicKey) { - this.keyId = keyId; - this.publicKey = publicKey; - } - - public long getKeyId() { - return keyId; - } - - public ECPublicKey getPublicKey() { - return publicKey; - } - - private static class ECPublicKeySerializer extends JsonSerializer { - @Override - public void serialize(ECPublicKey value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - gen.writeString(Base64.encodeWithoutPadding(value.serialize())); - } - } - - private static class ECPublicKeyDeserializer extends JsonDeserializer { - @Override - public ECPublicKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - try { - return new ECPublicKey(Base64.decode(p.getValueAsString())); - } catch (InvalidKeyException e) { - throw new IOException(e); - } - } - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.kt new file mode 100644 index 0000000000..12b0927938 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyEntity.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonSerializer +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import org.signal.core.util.Base64 +import org.signal.libsignal.protocol.ecc.ECPublicKey + +data class PreKeyEntity( + val keyId: Long, + @JsonSerialize(using = ECPublicKeySerializer::class) + @JsonDeserialize(using = ECPublicKeyDeserializer::class) + val publicKey: ECPublicKey +) { + class ECPublicKeySerializer : JsonSerializer() { + override fun serialize(value: ECPublicKey, gen: JsonGenerator, serializers: SerializerProvider) { + gen.writeString(Base64.encodeWithoutPadding(value.serialize())) + } + } + + class ECPublicKeyDeserializer : JsonDeserializer() { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): ECPublicKey { + return ECPublicKey(Base64.decode(p.valueAsString)) + } + } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.java deleted file mode 100644 index 536b1c4a69..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.signal.libsignal.protocol.IdentityKey; -import org.signal.network.util.JsonUtil; - -import java.util.List; - -public class PreKeyResponse { - - @JsonProperty - @JsonSerialize(using = JsonUtil.IdentityKeySerializer.class) - @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) - public IdentityKey identityKey; - - @JsonProperty - public List devices; - - public IdentityKey getIdentityKey() { - return identityKey; - } - - public List getDevices() { - return devices; - } - - -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.kt new file mode 100644 index 0000000000..7ee495652e --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponse.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import org.signal.libsignal.protocol.IdentityKey +import org.signal.network.util.JsonUtil + +class PreKeyResponse( + @JsonSerialize(using = JsonUtil.IdentityKeySerializer::class) + @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer::class) + val identityKey: IdentityKey, + val devices: List = emptyList() +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.java deleted file mode 100644 index b28066fef3..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - *

- * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.whispersystems.signalservice.api.push.SignedPreKeyEntity; - -public class PreKeyResponseItem { - - @JsonProperty - public int deviceId; - - @JsonProperty - public int registrationId; - - @JsonProperty - public SignedPreKeyEntity signedPreKey; - - @JsonProperty - public PreKeyEntity preKey; - - @JsonProperty("pqPreKey") - public KyberPreKeyEntity kyberPreKey; - - public int getDeviceId() { - return deviceId; - } - - public int getRegistrationId() { - return registrationId; - } - - public SignedPreKeyEntity getSignedPreKey() { - return signedPreKey; - } - - public PreKeyEntity getPreKey() { - return preKey; - } - - public KyberPreKeyEntity getKyberPreKey() { - return kyberPreKey; - } - -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.kt new file mode 100644 index 0000000000..7740de82be --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyResponseItem.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonProperty +import org.whispersystems.signalservice.api.push.SignedPreKeyEntity + +class PreKeyResponseItem( + val deviceId: Int = 0, + val registrationId: Int = 0, + val signedPreKey: SignedPreKeyEntity? = null, + val preKey: PreKeyEntity? = null, + @JsonProperty("pqPreKey") val kyberPreKey: KyberPreKeyEntity? = null +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.java deleted file mode 100644 index c346f3565d..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import org.signal.libsignal.protocol.IdentityKey; -import org.whispersystems.signalservice.api.push.SignedPreKeyEntity; -import org.signal.network.util.JsonUtil; - -import java.util.List; - -public class PreKeyState { - - @JsonProperty("preKeys") - private List oneTimeEcPreKeys; - - @JsonProperty("signedPreKey") - private SignedPreKeyEntity signedPreKey; - - @JsonProperty("pqLastResortPreKey") - private KyberPreKeyEntity lastResortKyberKey; - - @JsonProperty("pqPreKeys") - private List oneTimeKyberKeys; - - public PreKeyState() {} - - public PreKeyState( - SignedPreKeyEntity signedPreKey, - List oneTimeEcPreKeys, - KyberPreKeyEntity lastResortKyberPreKey, - List oneTimeKyberPreKeys - ) { - this.signedPreKey = signedPreKey; - this.oneTimeEcPreKeys = oneTimeEcPreKeys; - this.lastResortKyberKey = lastResortKyberPreKey; - this.oneTimeKyberKeys = oneTimeKyberPreKeys; - } - - public List getPreKeys() { - return oneTimeEcPreKeys; - } - - public SignedPreKeyEntity getSignedPreKey() { - return signedPreKey; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.kt new file mode 100644 index 0000000000..b33d9fe633 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PreKeyState.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonProperty +import org.whispersystems.signalservice.api.push.SignedPreKeyEntity + +class PreKeyState( + @JsonProperty("signedPreKey") val signedPreKey: SignedPreKeyEntity? = null, + @JsonProperty("preKeys") val oneTimeEcPreKeys: List? = null, + @JsonProperty("pqLastResortPreKey") val lastResortKyberKey: KyberPreKeyEntity? = null, + @JsonProperty("pqPreKeys") val oneTimeKyberKeys: List? = null +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.java deleted file mode 100644 index 9a215beb48..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class ProfileAvatarUploadAttributes { - - @JsonProperty - private String key; - - @JsonProperty - private String credential; - - @JsonProperty - private String acl; - - @JsonProperty - private String algorithm; - - @JsonProperty - private String date; - - @JsonProperty - private String policy; - - @JsonProperty - private String signature; - - public ProfileAvatarUploadAttributes() {} - - public String getKey() { - return key; - } - - public String getCredential() { - return credential; - } - - public String getAcl() { - return acl; - } - - public String getAlgorithm() { - return algorithm; - } - - public String getDate() { - return date; - } - - public String getPolicy() { - return policy; - } - - public String getSignature() { - return signature; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.kt new file mode 100644 index 0000000000..f2784b9620 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProfileAvatarUploadAttributes.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +data class ProfileAvatarUploadAttributes( + val key: String = "", + val credential: String = "", + val acl: String = "", + val algorithm: String = "", + val date: String = "", + val policy: String = "", + val signature: String = "" +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProofRequiredResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProofRequiredResponse.java deleted file mode 100644 index 2d8308eeec..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProofRequiredResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -public class ProofRequiredResponse { - - @JsonProperty - public String token; - - @JsonProperty - public List options; - - public ProofRequiredResponse() {} - - public String getToken() { - return token; - } - - public List getOptions() { - return options; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProofRequiredResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProofRequiredResponse.kt new file mode 100644 index 0000000000..a0ffd96762 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/ProofRequiredResponse.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +data class ProofRequiredResponse( + val token: String = "", + val options: List = emptyList() +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index 7169d91503..2e94e66d72 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -357,7 +357,7 @@ public class PushServiceSocket { String responseText = makeServiceRequest(String.format("/v1/messages/%s?story=%s", bundle.getDestination(), story ? "true" : "false"), "PUT", JsonUtil.toJson(bundle), NO_HEADERS, NO_HANDLER, sealedSenderAccess); SendMessageResponse response = JsonUtil.fromJson(responseText, SendMessageResponse.class); - response.setSentUnidentfied(sealedSenderAccess != null); + response.setSentUnidentified(sealedSenderAccess != null); return response; } catch (NotFoundException nfe) { @@ -1212,10 +1212,10 @@ public class PushServiceSocket { case 423: RegistrationLockFailure accountLockFailure = readResponseJson(response, RegistrationLockFailure.class); - throw new LockedException(accountLockFailure.length, - accountLockFailure.timeRemaining, - accountLockFailure.svr2Credentials, - accountLockFailure.svr3Credentials); + throw new LockedException(accountLockFailure.getLength(), + accountLockFailure.getTimeRemaining(), + accountLockFailure.getSvr2Credentials(), + accountLockFailure.getSvr3Credentials()); case 428: ProofRequiredResponse proofRequiredResponse = readResponseJson(response, ProofRequiredResponse.class); String retryAfterRaw = response.header("Retry-After"); @@ -1610,23 +1610,6 @@ public class PushServiceSocket { public enum VerificationCodeTransport { SMS, VOICE } - public static class RegistrationLockFailure { - @JsonProperty - public int length; - - @JsonProperty - public long timeRemaining; - - @JsonProperty("backupCredentials") - public AuthCredentials svr1Credentials; - - @JsonProperty - public AuthCredentials svr2Credentials; - - @JsonProperty - public Svr3Credentials svr3Credentials; - } - private static class ConnectionHolder { private final OkHttpClient client; diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/RegistrationLockFailure.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/RegistrationLockFailure.kt new file mode 100644 index 0000000000..02b88b3fc6 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/RegistrationLockFailure.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonProperty +import org.whispersystems.signalservice.api.svr.Svr3Credentials + +/** + * Body of a 423 response, telling us the account is registration-locked and how to unlock it. + */ +class RegistrationLockFailure( + val length: Int = 0, + val timeRemaining: Long = 0, + @JsonProperty("backupCredentials") val svr1Credentials: AuthCredentials? = null, + val svr2Credentials: AuthCredentials? = null, + val svr3Credentials: Svr3Credentials? = null +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.java deleted file mode 100644 index 36457ceb48..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class SendMessageResponse { - - @JsonProperty - private boolean needsSync; - - private boolean sentUnidentfied; - - public SendMessageResponse() {} - - public SendMessageResponse(boolean needsSync, boolean sentUnidentified) { - this.needsSync = needsSync; - this.sentUnidentfied = sentUnidentified; - } - - public boolean getNeedsSync() { - return needsSync; - } - - public boolean sentUnidentified() { - return sentUnidentfied; - } - - public void setSentUnidentfied(boolean value) { - this.sentUnidentfied = value; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.kt new file mode 100644 index 0000000000..22442e4c4b --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SendMessageResponse.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonIgnore + +class SendMessageResponse( + val needsSync: Boolean = false +) { + /** Not part of the response body; set by the caller based on how the request was authenticated. */ + @JsonIgnore + var sentUnidentified: Boolean = false +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.java deleted file mode 100644 index 632ff4f10d..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2026 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.signal.libsignal.net.MismatchedDeviceException; - -import java.util.ArrayList; -import java.util.List; - -public class StaleDevices { - - @JsonProperty - private List staleDevices; - - public List getStaleDevices() { - return staleDevices; - } - - public static StaleDevices fromLibSignal(MismatchedDeviceException.Entry entry) { - StaleDevices result = new StaleDevices(); - int[] stale = entry.getStaleDevices(); - List list = new ArrayList<>(stale.length); - for (int value : stale) { - list.add(value); - } - result.staleDevices = list; - return result; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.kt new file mode 100644 index 0000000000..998261b524 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/StaleDevices.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import org.signal.libsignal.net.MismatchedDeviceException + +data class StaleDevices( + val staleDevices: List = emptyList() +) { + companion object { + @JvmStatic + fun fromLibSignal(entry: MismatchedDeviceException.Entry): StaleDevices { + return StaleDevices(entry.staleDevices.toList()) + } + } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java deleted file mode 100644 index 5c16fd3a71..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; - -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Response JSON for a call to /v1/subscriptions/configuration - */ -public class SubscriptionsConfiguration { - - public static final String PAYPAL = "PAYPAL"; - public static final String CARD = "CARD"; - public static final String SEPA_DEBIT = "SEPA_DEBIT"; - public static final String IDEAL = "IDEAL"; - - public static final int BOOST_LEVEL = 1; - public static final int GIFT_LEVEL = 100; - public static final int BACKUPS_LEVEL = 201; - public static final HashSet SUBSCRIPTION_LEVELS = new HashSet<>(Arrays.asList(500, 1000, 2000)); - - @JsonProperty("currencies") - private Map currencies; - - @JsonProperty("levels") - private Map levels; - - @JsonProperty("sepaMaximumEuros") - private BigDecimal sepaMaximumEuros; - - @JsonProperty("backup") - private BackupConfiguration backupConfiguration; - - public static class CurrencyConfiguration { - @JsonProperty("minimum") - private BigDecimal minimum; - - @JsonProperty("oneTime") - private Map> oneTime; - - @JsonProperty("subscription") - private Map subscription; - - @JsonProperty("backupSubscription") - private Map backupSubscription; - - @JsonProperty("supportedPaymentMethods") - private Set supportedPaymentMethods; - - public BigDecimal getMinimum() { - return minimum; - } - - public Map> getOneTime() { - return oneTime; - } - - public Map getSubscription() { - return subscription; - } - - public Map getBackupSubscription() { - return backupSubscription; - } - - public Set getSupportedPaymentMethods() { - return supportedPaymentMethods; - } - } - - public static class LevelConfiguration { - - @JsonProperty("badge") - private SignalServiceProfile.Badge badge; - - public SignalServiceProfile.Badge getBadge() { - return badge; - } - } - - public static class BackupConfiguration { - @JsonProperty("levels") - private Map backupLevelConfigurationMap; - - @JsonProperty("freeTierMediaDays") - private int freeTierMediaDays; - - public Map getBackupLevelConfigurationMap() { - return backupLevelConfigurationMap; - } - - public int getFreeTierMediaDays() { - return freeTierMediaDays; - } - } - - public static class BackupLevelConfiguration { - @JsonProperty("storageAllowanceBytes") - private long storageAllowanceBytes; - - @JsonProperty("playProductId") - private String playProductId; - - @JsonProperty("mediaTtlDays") - private long mediaTtlDays; - - public long getStorageAllowanceBytes() { - return storageAllowanceBytes; - } - - public String getPlayProductId() { - return playProductId; - } - - public long getMediaTtlDays() { - return mediaTtlDays; - } - } - - public Map getCurrencies() { - return currencies; - } - - public Map getLevels() { - return levels; - } - - public BigDecimal getSepaMaximumEuros() { - return sepaMaximumEuros; - } - - public BackupConfiguration getBackupConfiguration() { - return backupConfiguration; - } -} \ No newline at end of file diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.kt new file mode 100644 index 0000000000..beee671034 --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonProperty +import org.whispersystems.signalservice.api.profiles.SignalServiceProfile +import java.math.BigDecimal + +/** + * Response JSON for a call to /v1/subscriptions/configuration + */ +data class SubscriptionsConfiguration( + val currencies: Map = emptyMap(), + val levels: Map = emptyMap(), + val sepaMaximumEuros: BigDecimal? = null, + @JsonProperty("backup") val backupConfiguration: BackupConfiguration = BackupConfiguration() +) { + + data class CurrencyConfiguration( + val minimum: BigDecimal = BigDecimal.ZERO, + val oneTime: Map> = emptyMap(), + val subscription: Map = emptyMap(), + val backupSubscription: Map = emptyMap(), + val supportedPaymentMethods: Set = emptySet() + ) + + data class LevelConfiguration( + val badge: SignalServiceProfile.Badge? = null + ) + + data class BackupConfiguration( + @JsonProperty("levels") val backupLevelConfigurationMap: Map = emptyMap(), + val freeTierMediaDays: Int = 0 + ) + + data class BackupLevelConfiguration( + val storageAllowanceBytes: Long = 0, + val playProductId: String = "", + val mediaTtlDays: Long = 0 + ) + + companion object { + const val PAYPAL = "PAYPAL" + const val CARD = "CARD" + const val SEPA_DEBIT = "SEPA_DEBIT" + const val IDEAL = "IDEAL" + + const val BOOST_LEVEL = 1 + const val GIFT_LEVEL = 100 + const val BACKUPS_LEVEL = 201 + + @JvmField + val SUBSCRIPTION_LEVELS = setOf(500, 1000, 2000) + } +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/VerifyAccountResponse.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/VerifyAccountResponse.java deleted file mode 100644 index 08f6ebd938..0000000000 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/VerifyAccountResponse.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.whispersystems.signalservice.internal.push; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class VerifyAccountResponse { - @JsonProperty - public String uuid; - - @JsonProperty - public String pni; - - @JsonProperty - public boolean storageCapable; - - @JsonProperty - public String number; - - @JsonProperty - public boolean reregistration; - - @JsonCreator - public VerifyAccountResponse() {} - - public VerifyAccountResponse(String uuid, String pni, boolean storageCapable) { - this.uuid = uuid; - this.pni = pni; - this.storageCapable = storageCapable; - } - - public String getUuid() { - return uuid; - } - - public boolean isStorageCapable() { - return storageCapable; - } - - public String getPni() { - return pni; - } -} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/VerifyAccountResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/VerifyAccountResponse.kt new file mode 100644 index 0000000000..aee9181b5a --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/VerifyAccountResponse.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.internal.push + +class VerifyAccountResponse( + val uuid: String? = null, + val pni: String? = null, + val storageCapable: Boolean = false, + val number: String? = null, + val reregistration: Boolean = false +) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/DefaultErrorMapper.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/DefaultErrorMapper.java index f094dd4532..afed1a5802 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/DefaultErrorMapper.java +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/websocket/DefaultErrorMapper.java @@ -14,6 +14,7 @@ import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredExcepti import org.whispersystems.signalservice.api.push.exceptions.RateLimitException; import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException; import org.whispersystems.signalservice.internal.push.DeviceLimit; +import org.whispersystems.signalservice.internal.push.RegistrationLockFailure; import org.whispersystems.signalservice.internal.push.DeviceLimitExceededException; import org.whispersystems.signalservice.internal.push.LockedException; import org.whispersystems.signalservice.internal.push.MismatchedDevices; @@ -110,17 +111,17 @@ public final class DefaultErrorMapper implements ErrorMapper { case 417: return new ExpectationFailedException(); case 423: - PushServiceSocket.RegistrationLockFailure accountLockFailure; + RegistrationLockFailure accountLockFailure; try { - accountLockFailure = JsonUtil.fromJsonResponse(body, PushServiceSocket.RegistrationLockFailure.class); + accountLockFailure = JsonUtil.fromJsonResponse(body, RegistrationLockFailure.class); } catch (MalformedResponseException e) { return e; } - return new LockedException(accountLockFailure.length, - accountLockFailure.timeRemaining, - accountLockFailure.svr2Credentials, - accountLockFailure.svr3Credentials); + return new LockedException(accountLockFailure.getLength(), + accountLockFailure.getTimeRemaining(), + accountLockFailure.getSvr2Credentials(), + accountLockFailure.getSvr3Credentials()); case 428: ProofRequiredResponse proofRequiredResponse; try { diff --git a/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/json/ConvertedModelJsonTest.kt b/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/json/ConvertedModelJsonTest.kt new file mode 100644 index 0000000000..d5a67ddc1a --- /dev/null +++ b/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/json/ConvertedModelJsonTest.kt @@ -0,0 +1,220 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.json + +import assertk.assertThat +import assertk.assertions.hasSize +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull +import assertk.assertions.isNull +import com.fasterxml.jackson.databind.ObjectMapper +import org.junit.Test +import org.signal.core.models.ServiceId +import org.signal.core.util.Base64 +import org.signal.libsignal.protocol.IdentityKey +import org.signal.libsignal.protocol.ecc.ECKeyPair +import org.signal.libsignal.protocol.kem.KEMKeyPair +import org.signal.libsignal.protocol.kem.KEMKeyType +import org.signal.network.util.JsonUtil +import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest +import org.whispersystems.signalservice.api.donations.ReceiptCredentialResponseJson +import org.whispersystems.signalservice.api.keys.OneTimePreKeyCounts +import org.whispersystems.signalservice.api.profiles.SignalServiceProfile +import org.whispersystems.signalservice.api.push.SignedPreKeyEntity +import org.whispersystems.signalservice.api.subscriptions.StripeClientSecret +import org.whispersystems.signalservice.internal.push.AuthCredentials +import org.whispersystems.signalservice.internal.push.DonationIntentResult +import org.whispersystems.signalservice.internal.push.IdentityCheckRequest +import org.whispersystems.signalservice.internal.push.IdentityCheckResponse +import org.whispersystems.signalservice.internal.push.KyberPreKeyEntity +import org.whispersystems.signalservice.internal.push.OutgoingPushMessage +import org.whispersystems.signalservice.internal.push.PreKeyEntity +import org.whispersystems.signalservice.internal.push.PreKeyResponse +import org.whispersystems.signalservice.internal.push.PreKeyState +import org.whispersystems.signalservice.internal.push.RegistrationLockFailure +import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration + +/** + * These models moved from Java to Kotlin. Jackson resolves Kotlin constructor properties by name via + * `jackson-module-kotlin`, so an un-renamed `@JsonProperty` can be dropped — but a renamed one, or a hand-written + * serializer, cannot. This pins the ones where a conversion mistake would silently change the wire format. + */ +class ConvertedModelJsonTest { + + companion object { + private const val ACI_STRING = "3f0d3b5e-0b2c-4e1e-9c1b-2a9a8f3f3d10" + } + + private val mapper = ObjectMapper().findAndRegisterModules() + + private val ecKeyPair = ECKeyPair.generate() + private val kemKeyPair = KEMKeyPair.generate(KEMKeyType.KYBER_1024) + private val identityKey = IdentityKey(ecKeyPair.publicKey) + private val signature = byteArrayOf(1, 2, 3, 4, 5) + + private val ecPublic = Base64.encodeWithoutPadding(ecKeyPair.publicKey.serialize()) + private val kemPublic = Base64.encodeWithoutPadding(kemKeyPair.publicKey.serialize()) + private val identityKeyBase64 = Base64.encodeWithoutPadding(identityKey.serialize()) + private val signatureBase64 = Base64.encodeWithoutPadding(signature) + + private fun assertJsonEquals(expected: String, actual: String) { + assertThat(mapper.readTree(actual)).isEqualTo(mapper.readTree(expected)) + } + + /** Keys are base64 with no padding, per the hand-written serializers these carried in Java. */ + @Test + fun preKeyState() { + val body = PreKeyState( + signedPreKey = SignedPreKeyEntity(2, ecKeyPair.publicKey, signature), + oneTimeEcPreKeys = listOf(PreKeyEntity(1, ecKeyPair.publicKey)), + lastResortKyberKey = KyberPreKeyEntity(3, kemKeyPair.publicKey, signature), + oneTimeKyberKeys = listOf(KyberPreKeyEntity(4, kemKeyPair.publicKey, signature)) + ) + + assertJsonEquals( + """{"signedPreKey":{"keyId":2,"publicKey":"$ecPublic","signature":"$signatureBase64"},"preKeys":[{"keyId":1,"publicKey":"$ecPublic"}],"pqLastResortPreKey":{"keyId":3,"publicKey":"$kemPublic","signature":"$signatureBase64"},"pqPreKeys":[{"keyId":4,"publicKey":"$kemPublic","signature":"$signatureBase64"}]}""", + JsonUtil.toJson(body) + ) + } + + @Test + fun preKeyResponse() { + val json = """{"identityKey":"$identityKeyBase64","devices":[{"deviceId":1,"registrationId":1234,"signedPreKey":{"keyId":2,"publicKey":"$ecPublic","signature":"$signatureBase64"},"preKey":{"keyId":1,"publicKey":"$ecPublic"},"pqPreKey":{"keyId":3,"publicKey":"$kemPublic","signature":"$signatureBase64"}}]}""" + + val decoded = JsonUtil.fromJson(json, PreKeyResponse::class.java) + + assertThat(decoded.identityKey).isEqualTo(identityKey) + assertThat(decoded.devices).hasSize(1) + + val device = decoded.devices.first() + assertThat(device.deviceId).isEqualTo(1) + assertThat(device.registrationId).isEqualTo(1234) + assertThat(device.preKey?.publicKey).isEqualTo(ecKeyPair.publicKey) + assertThat(device.signedPreKey?.signature?.toList()).isEqualTo(signature.toList()) + assertThat(device.kyberPreKey?.publicKey).isEqualTo(kemKeyPair.publicKey) + } + + @Test + fun oneTimePreKeyCounts() { + val decoded = JsonUtil.fromJson("""{"count":5,"pqCount":7}""", OneTimePreKeyCounts::class.java) + + assertThat(decoded.ecCount).isEqualTo(5) + assertThat(decoded.kyberCount).isEqualTo(7) + } + + @Test + fun changePhoneNumberRequest() { + val body = ChangePhoneNumberRequest( + sessionId = "session", + recoveryPassword = "recovery", + number = "+15551234567", + registrationLock = "reglock", + pniIdentityKey = identityKey, + deviceMessages = listOf(OutgoingPushMessage(1, 2, 3, "content")), + devicePniSignedPrekeys = mapOf("1" to SignedPreKeyEntity(1, ecKeyPair.publicKey, signature)), + devicePniLastResortKyberPrekeys = mapOf("1" to KyberPreKeyEntity(2, kemKeyPair.publicKey, signature)), + pniRegistrationIds = mapOf("1" to 1234) + ) + + assertJsonEquals( + """{"sessionId":"session","recoveryPassword":"recovery","number":"+15551234567","reglock":"reglock","pniIdentityKey":"$identityKeyBase64","deviceMessages":[{"type":1,"destinationDeviceId":2,"destinationRegistrationId":3,"content":"content"}],"devicePniSignedPrekeys":{"1":{"keyId":1,"publicKey":"$ecPublic","signature":"$signatureBase64"}},"devicePniPqLastResortPrekeys":{"1":{"keyId":2,"publicKey":"$kemPublic","signature":"$signatureBase64"}},"pniRegistrationIds":{"1":1234}}""", + JsonUtil.toJson(body) + ) + } + + @Test + fun signalServiceProfile() { + val json = """{"identityKey":"identity","name":"name","about":"about","aboutEmoji":"emoji","paymentAddress":"AQIDBA==","avatar":"avatar","unidentifiedAccess":"access","unrestrictedUnidentifiedAccess":true,"capabilities":{"storage":true,"ssre2":false,"usernameChangeSyncMessage":true,"optionalPhoneNumber":false},"uuid":"$ACI_STRING","badges":[{"id":"BOOST","category":"donor","name":"Boost","description":"A boost","sprites6":["a","b"],"expiration":1675609546,"visible":true,"duration":100}],"phoneNumberSharing":"sharing"}""" + + val decoded = JsonUtil.fromJson(json, SignalServiceProfile::class.java) + + assertThat(decoded.serviceId).isEqualTo(ServiceId.ACI.parseOrThrow(ACI_STRING)) + assertThat(decoded.paymentAddress?.toList()).isEqualTo(listOf(1, 2, 3, 4)) + assertThat(decoded.unrestrictedUnidentifiedAccess).isEqualTo(true) + assertThat(decoded.capabilities?.storageServiceEncryptionV2).isEqualTo(false) + assertThat(decoded.capabilities?.usernameSyncMessages).isEqualTo(true) + assertThat(decoded.badges?.first()?.visible).isEqualTo(true) + assertThat(decoded.badges?.first()?.sprites6).isEqualTo(listOf("a", "b")) + } + + @Test + fun subscriptionsConfiguration() { + val json = """{"currencies":{"USD":{"minimum":100,"oneTime":{"1":[500,1000]},"subscription":{"500":500},"backupSubscription":{"201":199},"supportedPaymentMethods":["CARD","PAYPAL"]}},"levels":{"500":{"badge":{"id":"BOOST"}}},"sepaMaximumEuros":10000,"backup":{"levels":{"201":{"storageAllowanceBytes":100,"playProductId":"product","mediaTtlDays":60}},"freeTierMediaDays":30}}""" + + val decoded = JsonUtil.fromJson(json, SubscriptionsConfiguration::class.java) + + assertThat(decoded.currencies["USD"]?.supportedPaymentMethods).isEqualTo(setOf("CARD", "PAYPAL")) + assertThat(decoded.levels[500]?.badge?.id).isEqualTo("BOOST") + assertThat(decoded.backupConfiguration.freeTierMediaDays).isEqualTo(30) + assertThat(decoded.backupConfiguration.backupLevelConfigurationMap[201]?.playProductId).isEqualTo("product") + } + + @Test + fun donationIntentResult() { + val decoded = JsonUtil.fromJson("""{"id":"pi_1234","client_secret":"secret"}""", DonationIntentResult::class.java) + + assertThat(decoded.id).isEqualTo("pi_1234") + assertThat(decoded.clientSecret).isEqualTo("secret") + } + + @Test + fun identityCheckRequest() { + val pair = IdentityCheckRequest.ServiceIdFingerprintPair(ServiceId.ACI.parseOrThrow(ACI_STRING), identityKey) + + assertJsonEquals( + """{"elements":[{"uuid":"$ACI_STRING","fingerprint":"${pair.fingerprint}"}]}""", + JsonUtil.toJson(IdentityCheckRequest(listOf(pair))) + ) + } + + @Test + fun identityCheckResponse() { + val json = """{"elements":[{"uuid":"$ACI_STRING","identityKey":"$identityKeyBase64"}]}""" + + val decoded = JsonUtil.fromJson(json, IdentityCheckResponse::class.java) + + assertThat(decoded.serviceIdKeyPairs?.first()?.serviceId).isEqualTo(ServiceId.ACI.parseOrThrow(ACI_STRING)) + assertThat(decoded.serviceIdKeyPairs?.first()?.identityKey).isEqualTo(identityKey) + } + + @Test + fun registrationLockFailure() { + val json = """{"length":6,"timeRemaining":1234,"backupCredentials":{"username":"user1","password":"pass1"},"svr2Credentials":{"username":"user2","password":"pass2"}}""" + + val decoded = JsonUtil.fromJson(json, RegistrationLockFailure::class.java) + + assertThat(decoded.length).isEqualTo(6) + assertThat(decoded.timeRemaining).isEqualTo(1234L) + assertThat(decoded.svr1Credentials?.username()).isEqualTo("user1") + assertThat(decoded.svr2Credentials?.username()).isEqualTo("user2") + assertThat(decoded.svr3Credentials).isNull() + } + + /** The fields are private, so they need `@field:JsonProperty` for Jackson to write them back out. */ + @Test + fun authCredentials() { + val credentials = AuthCredentials.create("user", "pass") + + assertJsonEquals("""{"username":"user","password":"pass"}""", JsonUtil.toJson(credentials)) + assertThat(JsonUtil.fromJson("""{"username":"user","password":"pass"}""", AuthCredentials::class.java).asBasic()).isNotNull() + } + + /** + * Both of these had a single-argument `@JsonCreator` constructor in Java. Without an explicit mode, such a + * constructor is *delegating* — the parameter would receive the whole JSON object rather than the named field — so + * pin that the Kotlin module still binds them by property name. + */ + @Test + fun singleArgumentConstructorsBindByName() { + val secret = JsonUtil.fromJson("""{"clientSecret":"pi_1234_secret_5678"}""", StripeClientSecret::class.java) + assertThat(secret.clientSecret).isEqualTo("pi_1234_secret_5678") + assertThat(secret.id).isEqualTo("pi_1234") + + // Not a real zkgroup credential, so it decodes to null rather than throwing. A delegating creator would fail here. + val credential = JsonUtil.fromJson("""{"receiptCredentialResponse":"AQIDBA=="}""", ReceiptCredentialResponseJson::class.java) + assertThat(credential.credentialResponse).isNull() + } +} diff --git a/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscriptionTest.java b/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscriptionTest.java deleted file mode 100644 index c2bf6b3646..0000000000 --- a/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscriptionTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.whispersystems.signalservice.api.subscriptions; - -import org.junit.Test; -import org.signal.network.util.JsonUtil; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class ActiveSubscriptionTest { - @Test - public void givenActiveSubscription_whenIIsPaymentFailure_thenIExpectFalse() throws Exception { - String input = "{\"subscription\":{\"level\":2000,\"billingCycleAnchor\":1636124746.000000000,\"endOfCurrentPeriod\":1675609546.000000000,\"active\":true,\"cancelAtPeriodEnd\":false,\"currency\":\"USD\",\"amount\":2000,\"status\":\"active\"},\"chargeFailure\":null}"; - ActiveSubscription activeSubscription = JsonUtil.fromJson(input, ActiveSubscription.class); - - assertTrue(activeSubscription.isActive()); - assertFalse(activeSubscription.isFailedPayment()); - } - - @Test - public void givenNoActiveSubscription_whenIIsInProgress_thenIExpectFalse() throws Exception { - ActiveSubscription activeSubscription = new ActiveSubscription(null, null); - - assertFalse(activeSubscription.isInProgress()); - } -} \ No newline at end of file diff --git a/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscriptionTest.kt b/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscriptionTest.kt new file mode 100644 index 0000000000..59ef0e2d65 --- /dev/null +++ b/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscriptionTest.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.signalservice.api.subscriptions + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import org.junit.Test +import org.signal.network.util.JsonUtil + +class ActiveSubscriptionTest { + + @Test + fun `given an active subscription, when I check isPaymentFailure, then I expect false`() { + val input = """{"subscription":{"level":2000,"billingCycleAnchor":1636124746.000000000,"endOfCurrentPeriod":1675609546.000000000,"active":true,"cancelAtPeriodEnd":false,"currency":"USD","amount":2000,"status":"active"},"chargeFailure":null}""" + val activeSubscription = JsonUtil.fromJson(input, ActiveSubscription::class.java) + + assertThat(activeSubscription.isActive).isTrue() + assertThat(activeSubscription.isFailedPayment).isFalse() + } + + @Test + fun `given an absent processor and payment method, when I decode, then I expect the fallback values`() { + val input = """{"subscription":{"level":2000,"active":true,"status":"active"}}""" + val subscription = JsonUtil.fromJson(input, ActiveSubscription::class.java).activeSubscription!! + + assertThat(subscription.processor).isEqualTo(ActiveSubscription.Processor.STRIPE) + assertThat(subscription.paymentMethod).isEqualTo(ActiveSubscription.PaymentMethod.UNKNOWN) + } + + @Test + fun `given no active subscription, when I check isInProgress, then I expect false`() { + assertThat(ActiveSubscription(null, null).isInProgress).isFalse() + } +}