From a5e272be3f8d5280b702069604b7b1c6869e69bd Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 19 Apr 2024 19:25:27 +0200 Subject: [PATCH 001/144] Prepare next release --- CHANGELOG.md | 2 ++ build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f49b8b81..dd44885b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## [Unreleased] + ## [0.13.9] - 2024-10-28 ### Fixed diff --git a/build.gradle.kts b/build.gradle.kts index 8e74a5bf..c205612e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.10.3" } -version = "0.13.9" +version = "0.13.10-SNAPSHOT" java { sourceCompatibility = JavaVersion.VERSION_21 From e5251ae1589bc82dade0dc294dc7e66a7b25cc9b Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 21 Nov 2024 21:17:33 +0100 Subject: [PATCH 002/144] Switch to using toml version catalogs --- gradle/libs.versions.toml | 17 +++++++++++++++++ settings.gradle.kts | 19 ------------------- 2 files changed, 17 insertions(+), 19 deletions(-) create mode 100644 gradle/libs.versions.toml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..17a8dd46 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,17 @@ +[versions] +slf4j = "2.0.16" + +[libraries] +bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.78.1" +jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.18.0" +argparse4j = "net.sourceforge.argparse4j:argparse4j:0.9.0" +dbusjava = "com.github.hypfvieh:dbus-java-transport-native-unixsocket:5.0.0" +slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } +slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } +logback = "ch.qos.logback:logback-classic:1.5.11" + +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_110" +sqlite = "org.xerial:sqlite-jdbc:3.47.0.0" +hikari = "com.zaxxer:HikariCP:6.0.0" +junit-jupiter = "org.junit.jupiter:junit-jupiter:5.11.3" +junit-launcher = "org.junit.platform:junit-platform-launcher:1.11.3" diff --git a/settings.gradle.kts b/settings.gradle.kts index ef9ec486..fe7c45a9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,25 +3,6 @@ dependencyResolutionManagement { mavenLocal() mavenCentral() } - - versionCatalogs { - create("libs") { - library("bouncycastle", "org.bouncycastle", "bcprov-jdk18on").version("1.78.1") - library("jackson.databind", "com.fasterxml.jackson.core", "jackson-databind").version("2.18.0") - library("argparse4j", "net.sourceforge.argparse4j", "argparse4j").version("0.9.0") - library("dbusjava", "com.github.hypfvieh", "dbus-java-transport-native-unixsocket").version("5.0.0") - version("slf4j", "2.0.16") - library("slf4j.api", "org.slf4j", "slf4j-api").versionRef("slf4j") - library("slf4j.jul", "org.slf4j", "jul-to-slf4j").versionRef("slf4j") - library("logback", "ch.qos.logback", "logback-classic").version("1.5.11") - - library("signalservice", "com.github.turasa", "signal-service-java").version("2.15.3_unofficial_110") - library("sqlite", "org.xerial", "sqlite-jdbc").version("3.47.0.0") - library("hikari", "com.zaxxer", "HikariCP").version("6.0.0") - library("junit.jupiter", "org.junit.jupiter", "junit-jupiter").version("5.11.3") - library("junit.launcher", "org.junit.platform", "junit-platform-launcher").version("1.11.3") - } - } } rootProject.name = "signal-cli" From 3533500b736ce382b9cc1d53d29ff267324bf4be Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 21 Nov 2024 21:17:44 +0100 Subject: [PATCH 003/144] Update dependencies --- graalvm-config-dir/reflect-config.json | 16 ++++++++++++++++ gradle/libs.versions.toml | 8 ++++---- .../asamk/signal/logging/LogConfigurator.java | 11 ++++++----- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 8a80f9c7..abe02ac4 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -1499,6 +1499,10 @@ "name":"org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.CONTEXT$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.CompositeSignatures$Mappings", "methods":[{"name":"","parameterTypes":[] }] @@ -1559,6 +1563,14 @@ "name":"org.bouncycastle.jcajce.provider.asymmetric.LMS$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.MLDSA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.MLKEM$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.NTRU$Mappings", "methods":[{"name":"","parameterTypes":[] }] @@ -1567,6 +1579,10 @@ "name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.SLHDSA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.SPHINCSPlus$Mappings", "methods":[{"name":"","parameterTypes":[] }] diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 17a8dd46..d7d56d71 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,16 +2,16 @@ slf4j = "2.0.16" [libraries] -bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.78.1" -jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.18.0" +bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.79" +jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.18.1" argparse4j = "net.sourceforge.argparse4j:argparse4j:0.9.0" dbusjava = "com.github.hypfvieh:dbus-java-transport-native-unixsocket:5.0.0" slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } -logback = "ch.qos.logback:logback-classic:1.5.11" +logback = "ch.qos.logback:logback-classic:1.5.12" signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_110" sqlite = "org.xerial:sqlite-jdbc:3.47.0.0" -hikari = "com.zaxxer:HikariCP:6.0.0" +hikari = "com.zaxxer:HikariCP:6.2.1" junit-jupiter = "org.junit.jupiter:junit-jupiter:5.11.3" junit-launcher = "org.junit.platform:junit-platform-launcher:1.11.3" diff --git a/src/main/java/org/asamk/signal/logging/LogConfigurator.java b/src/main/java/org/asamk/signal/logging/LogConfigurator.java index 65e8c4f5..f6e23b70 100644 --- a/src/main/java/org/asamk/signal/logging/LogConfigurator.java +++ b/src/main/java/org/asamk/signal/logging/LogConfigurator.java @@ -42,7 +42,7 @@ public class LogConfigurator extends ContextAwareBase implements Configurator { }}); final var rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); - final var defaultLevel = verboseLevel > 2 ? Level.ALL : verboseLevel > 0 ? Level.INFO : Level.WARN; + final var defaultLevel = verboseLevel > 2 ? Level.TRACE : verboseLevel > 0 ? Level.INFO : Level.WARN; rootLogger.setLevel(defaultLevel); final var consoleLayout = verboseLevel == 0 || logFile != null @@ -51,13 +51,14 @@ public class LogConfigurator extends ContextAwareBase implements Configurator { final var consoleAppender = createLoggingConsoleAppender(lc, createLayoutWrappingEncoder(consoleLayout)); rootLogger.addAppender(consoleAppender); - lc.getLogger("org.asamk").setLevel(verboseLevel > 1 ? Level.ALL : verboseLevel > 0 ? Level.DEBUG : Level.INFO); + lc.getLogger("org.asamk") + .setLevel(verboseLevel > 1 ? Level.TRACE : verboseLevel > 0 ? Level.DEBUG : Level.INFO); lc.getLogger("org.asamk.Signal") - .setLevel(verboseLevel > 2 ? Level.ALL : verboseLevel > 1 ? Level.INFO : Level.WARN); + .setLevel(verboseLevel > 2 ? Level.TRACE : verboseLevel > 1 ? Level.INFO : Level.WARN); lc.getLogger("com.zaxxer.hikari.pool.PoolBase") - .setLevel(verboseLevel > 2 ? Level.ALL : verboseLevel > 1 ? Level.INFO : Level.WARN); + .setLevel(verboseLevel > 2 ? Level.TRACE : verboseLevel > 1 ? Level.INFO : Level.WARN); lc.getLogger("org.sqlite.core.NativeDB") - .setLevel(verboseLevel > 3 ? Level.ALL : verboseLevel > 1 ? Level.INFO : Level.WARN); + .setLevel(verboseLevel > 3 ? Level.TRACE : verboseLevel > 1 ? Level.INFO : Level.WARN); if (logFile != null) { consoleAppender.addFilter(new Filter<>() { From f2005593ecefd37c7e1666c2dc0c71b259271af0 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 23 Nov 2024 21:28:00 +0100 Subject: [PATCH 004/144] Reformat files --- .../org/asamk/signal/manager/Manager.java | 70 ++++++++---- .../signal/manager/RegistrationManager.java | 7 +- .../signal/manager/SignalAccountFiles.java | 6 +- .../manager/actions/ResendMessageAction.java | 4 +- .../manager/actions/SendReceiptAction.java | 4 +- .../org/asamk/signal/manager/api/Group.java | 4 +- .../signal/manager/api/MessageEnvelope.java | 14 +-- .../signal/manager/config/ServiceConfig.java | 3 +- .../signal/manager/groups/GroupUtils.java | 3 +- .../signal/manager/helper/AccountHelper.java | 16 ++- .../manager/helper/AttachmentHelper.java | 7 +- .../signal/manager/helper/ContactHelper.java | 4 +- .../signal/manager/helper/GroupHelper.java | 48 +++++--- .../signal/manager/helper/GroupV2Helper.java | 81 +++++++++----- .../signal/manager/helper/IdentityHelper.java | 8 +- .../helper/IncomingMessageHandler.java | 15 ++- .../signal/manager/helper/PinHelper.java | 11 +- .../signal/manager/helper/PreKeyHelper.java | 14 ++- .../signal/manager/helper/ProfileHelper.java | 19 ++-- .../signal/manager/helper/ReceiveHelper.java | 8 +- .../manager/helper/RecipientHelper.java | 9 +- .../signal/manager/helper/SendHelper.java | 29 +++-- .../signal/manager/helper/StickerHelper.java | 7 +- .../signal/manager/helper/StorageHelper.java | 19 +++- .../signal/manager/helper/SyncHelper.java | 21 ++-- .../helper/UnidentifiedAccessHelper.java | 3 +- .../signal/manager/internal/ManagerImpl.java | 88 ++++++++++----- .../internal/RegistrationManagerImpl.java | 7 +- .../manager/storage/AccountDatabase.java | 3 +- .../manager/storage/AttachmentStore.java | 18 +-- .../signal/manager/storage/Database.java | 3 +- .../signal/manager/storage/SignalAccount.java | 20 ++-- .../storage/UnknownStorageIdStore.java | 4 +- .../asamk/signal/manager/storage/Utils.java | 9 +- .../storage/accounts/AccountsStore.java | 8 +- .../configuration/ConfigurationStore.java | 6 +- .../manager/storage/groups/GroupInfoV2.java | 4 +- .../manager/storage/groups/GroupStore.java | 32 ++++-- .../storage/groups/LegacyGroupStore.java | 6 +- .../storage/identities/IdentityInfo.java | 4 +- .../storage/identities/IdentityKeyStore.java | 16 ++- .../identities/LegacyIdentityKeyStore.java | 8 +- .../storage/keyValue/KeyValueStore.java | 12 +- .../storage/profiles/LegacyProfileStore.java | 3 +- .../storage/profiles/ProfileStore.java | 3 +- .../protocol/LegacyJsonIdentityKeyStore.java | 7 +- .../protocol/LegacyJsonPreKeyStore.java | 3 +- .../protocol/LegacyJsonSessionStore.java | 3 +- .../protocol/LegacyJsonSignedPreKeyStore.java | 3 +- .../storage/protocol/SignalProtocolStore.java | 7 +- .../manager/storage/recipients/CdsiStore.java | 12 +- .../recipients/LegacyRecipientStore.java | 3 +- .../recipients/MergeRecipientHelper.java | 3 +- .../storage/recipients/RecipientStore.java | 105 ++++++++++++------ .../recipients/RecipientTrustedResolver.java | 4 +- .../storage/sendLog/MessageSendLogStore.java | 25 ++++- .../senderKeys/SenderKeyRecordStore.java | 8 +- .../senderKeys/SenderKeySharedStore.java | 11 +- .../storage/senderKeys/SenderKeyStore.java | 7 +- .../storage/sessions/SessionStore.java | 4 +- .../threads/LegacyJsonThreadStore.java | 7 +- .../syncStorage/AccountRecordProcessor.java | 4 +- .../syncStorage/ContactRecordProcessor.java | 4 +- .../syncStorage/StorageSyncModels.java | 12 +- .../syncStorage/StorageSyncValidations.java | 8 +- .../signal/manager/util/AttachmentUtils.java | 7 +- .../asamk/signal/manager/util/KeyUtils.java | 7 +- .../manager/util/NumberVerificationUtils.java | 24 +++- .../signal/manager/util/PaymentUtils.java | 8 +- .../signal/manager/util/ProfileUtils.java | 17 +-- .../signal/manager/util/StickerUtils.java | 8 +- .../org/asamk/signal/manager/util/Utils.java | 10 +- .../recipients/MergeRecipientHelperTest.java | 4 +- src/main/java/org/asamk/Signal.java | 76 ++++++++----- src/main/java/org/asamk/SignalControl.java | 7 +- src/main/java/org/asamk/signal/App.java | 6 +- .../asamk/signal/ReceiveMessageHandler.java | 60 +++------- .../signal/commands/AddDeviceCommand.java | 4 +- .../commands/AddStickerPackCommand.java | 4 +- .../asamk/signal/commands/BlockCommand.java | 4 +- .../asamk/signal/commands/CommandHandler.java | 9 +- .../asamk/signal/commands/DaemonCommand.java | 8 +- .../DeleteLocalAccountDataCommand.java | 4 +- .../commands/FinishChangeNumberCommand.java | 4 +- .../signal/commands/FinishLinkCommand.java | 4 +- .../signal/commands/GetAttachmentCommand.java | 4 +- .../signal/commands/GetAvatarCommand.java | 4 +- .../signal/commands/GetStickerCommand.java | 4 +- .../signal/commands/GetUserStatusCommand.java | 4 +- .../signal/commands/JoinGroupCommand.java | 4 +- .../commands/JsonRpcDispatcherCommand.java | 8 +- .../signal/commands/JsonRpcLocalCommand.java | 4 +- .../commands/JsonRpcMultiLocalCommand.java | 4 +- .../asamk/signal/commands/LinkCommand.java | 4 +- .../signal/commands/ListAccountsCommand.java | 4 +- .../signal/commands/ListContactsCommand.java | 4 +- .../signal/commands/ListDevicesCommand.java | 4 +- .../signal/commands/ListGroupsCommand.java | 8 +- .../commands/ListIdentitiesCommand.java | 4 +- .../commands/ListStickerPacksCommand.java | 4 +- .../signal/commands/QuitGroupCommand.java | 4 +- .../asamk/signal/commands/ReceiveCommand.java | 8 +- .../signal/commands/RegisterCommand.java | 9 +- .../signal/commands/RemoteDeleteCommand.java | 4 +- .../signal/commands/RemoveContactCommand.java | 4 +- .../signal/commands/RemoveDeviceCommand.java | 4 +- .../signal/commands/RemovePinCommand.java | 4 +- .../asamk/signal/commands/SendCommand.java | 7 +- .../signal/commands/SendContactsCommand.java | 4 +- .../SendMessageRequestResponseCommand.java | 4 +- .../SendPaymentNotificationCommand.java | 4 +- .../signal/commands/SendReactionCommand.java | 4 +- .../signal/commands/SendReceiptCommand.java | 4 +- .../commands/SendSyncRequestCommand.java | 4 +- .../signal/commands/SendTypingCommand.java | 4 +- .../asamk/signal/commands/SetPinCommand.java | 4 +- .../commands/StartChangeNumberCommand.java | 4 +- .../signal/commands/StartLinkCommand.java | 4 +- .../asamk/signal/commands/TrustCommand.java | 4 +- .../asamk/signal/commands/UnblockCommand.java | 4 +- .../signal/commands/UnregisterCommand.java | 4 +- .../signal/commands/UpdateAccountCommand.java | 4 +- .../commands/UpdateConfigurationCommand.java | 4 +- .../signal/commands/UpdateContactCommand.java | 4 +- .../signal/commands/UpdateGroupCommand.java | 8 +- .../signal/commands/UpdateProfileCommand.java | 4 +- .../commands/UploadStickerPackCommand.java | 4 +- .../asamk/signal/commands/VerifyCommand.java | 8 +- .../asamk/signal/commands/VersionCommand.java | 8 +- .../asamk/signal/dbus/DbusCommandHandler.java | 3 +- .../org/asamk/signal/dbus/DbusHandler.java | 5 +- .../dbus/DbusInterfacePropertiesHandler.java | 4 +- .../asamk/signal/dbus/DbusManagerImpl.java | 68 +++++++----- .../dbus/DbusMultiAccountManagerImpl.java | 4 +- .../dbus/DbusProvisioningManagerImpl.java | 4 +- .../dbus/DbusRegistrationManagerImpl.java | 7 +- .../signal/dbus/DbusSignalControlImpl.java | 11 +- .../org/asamk/signal/dbus/DbusSignalImpl.java | 49 ++++---- .../asamk/signal/http/HttpServerHandler.java | 4 +- .../signal/json/JsonMessageEnvelope.java | 4 +- .../asamk/signal/jsonrpc/JsonRpcRequest.java | 9 +- .../jsonrpc/SignalJsonRpcCommandHandler.java | 8 +- .../SignalJsonRpcDispatcherHandler.java | 20 +++- .../asamk/signal/jsonrpc/SocketHandler.java | 4 +- .../asamk/signal/logging/LogConfigurator.java | 6 +- .../org/asamk/signal/logging/Scrubber.java | 4 +- .../org/asamk/signal/util/CommandUtil.java | 6 +- .../signal/util/SendMessageResultUtils.java | 3 +- 148 files changed, 1056 insertions(+), 578 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 4b086994..85644f61 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -130,11 +130,15 @@ public interface Manager extends Closeable { void deleteUsername() throws IOException; void startChangeNumber( - String newNumber, boolean voiceVerification, String captcha + String newNumber, + boolean voiceVerification, + String captcha ) throws RateLimitException, IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException, NotPrimaryDeviceException, VerificationMethodNotAvailableException; void finishChangeNumber( - String newNumber, String verificationCode, String pin + String newNumber, + String verificationCode, + String pin ) throws IncorrectPinException, PinLockedException, IOException, NotPrimaryDeviceException; void unregister() throws IOException; @@ -142,7 +146,8 @@ public interface Manager extends Closeable { void deleteAccount() throws IOException; void submitRateLimitRecaptchaChallenge( - String challenge, String captcha + String challenge, + String captcha ) throws IOException, CaptchaRejectedException; List getLinkedDevices() throws IOException; @@ -156,17 +161,21 @@ public interface Manager extends Closeable { List getGroups(); SendGroupMessageResults quitGroup( - GroupId groupId, Set groupAdmins + GroupId groupId, + Set groupAdmins ) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException, UnregisteredRecipientException; void deleteGroup(GroupId groupId) throws IOException; Pair createGroup( - String name, Set members, String avatarFile + String name, + Set members, + String avatarFile ) throws IOException, AttachmentInvalidException, UnregisteredRecipientException; SendGroupMessageResults updateGroup( - final GroupId groupId, final UpdateGroup updateGroup + final GroupId groupId, + final UpdateGroup updateGroup ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException, GroupSendingNotAllowedException, UnregisteredRecipientException; Pair joinGroup( @@ -174,27 +183,29 @@ public interface Manager extends Closeable { ) throws IOException, InactiveGroupLinkException, PendingAdminApprovalException; SendMessageResults sendTypingMessage( - TypingAction action, Set recipients + TypingAction action, + Set recipients ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException; - SendMessageResults sendReadReceipt( - RecipientIdentifier.Single sender, List messageIds - ); + SendMessageResults sendReadReceipt(RecipientIdentifier.Single sender, List messageIds); - SendMessageResults sendViewedReceipt( - RecipientIdentifier.Single sender, List messageIds - ); + SendMessageResults sendViewedReceipt(RecipientIdentifier.Single sender, List messageIds); SendMessageResults sendMessage( - Message message, Set recipients, boolean notifySelf + Message message, + Set recipients, + boolean notifySelf ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException; SendMessageResults sendEditMessage( - Message message, Set recipients, long editTargetTimestamp + Message message, + Set recipients, + long editTargetTimestamp ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException; SendMessageResults sendRemoteDeleteMessage( - long targetSentTimestamp, Set recipients + long targetSentTimestamp, + Set recipients ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException; SendMessageResults sendMessageReaction( @@ -207,13 +218,16 @@ public interface Manager extends Closeable { ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException; SendMessageResults sendPaymentNotificationMessage( - byte[] receipt, String note, RecipientIdentifier.Single recipient + byte[] receipt, + String note, + RecipientIdentifier.Single recipient ) throws IOException; SendMessageResults sendEndSessionMessage(Set recipients) throws IOException; SendMessageResults sendMessageRequestResponse( - MessageEnvelope.Sync.MessageRequestResponse.Type type, Set recipientIdentifiers + MessageEnvelope.Sync.MessageRequestResponse.Type type, + Set recipientIdentifiers ); void hideRecipient(RecipientIdentifier.Single recipient); @@ -223,22 +237,27 @@ public interface Manager extends Closeable { void deleteContact(RecipientIdentifier.Single recipient); void setContactName( - RecipientIdentifier.Single recipient, String givenName, final String familyName + RecipientIdentifier.Single recipient, + String givenName, + final String familyName ) throws NotPrimaryDeviceException, UnregisteredRecipientException; void setContactsBlocked( - Collection recipient, boolean blocked + Collection recipient, + boolean blocked ) throws NotPrimaryDeviceException, IOException, UnregisteredRecipientException; void setGroupsBlocked( - Collection groupId, boolean blocked + Collection groupId, + boolean blocked ) throws GroupNotFoundException, IOException, NotPrimaryDeviceException; /** * Change the expiration timer for a contact */ void setExpirationTimer( - RecipientIdentifier.Single recipient, int messageExpirationTimer + RecipientIdentifier.Single recipient, + int messageExpirationTimer ) throws IOException, UnregisteredRecipientException; /** @@ -277,7 +296,9 @@ public interface Manager extends Closeable { * Receive new messages from server, returns if no new message arrive in a timespan of timeout. */ void receiveMessages( - Optional timeout, Optional maxMessages, ReceiveMessageHandler handler + Optional timeout, + Optional maxMessages, + ReceiveMessageHandler handler ) throws IOException, AlreadyReceivingException; void stopReceiveMessages(); @@ -309,7 +330,8 @@ public interface Manager extends Closeable { * @param recipient account of the identity */ boolean trustIdentityVerified( - RecipientIdentifier.Single recipient, IdentityVerificationCode verificationCode + RecipientIdentifier.Single recipient, + IdentityVerificationCode verificationCode ) throws UnregisteredRecipientException; /** diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java index 5660bb13..7d358df6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -13,11 +13,14 @@ import java.io.IOException; public interface RegistrationManager extends Closeable { void register( - boolean voiceVerification, String captcha, final boolean forceRegister + boolean voiceVerification, + String captcha, + final boolean forceRegister ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException, RateLimitException, VerificationMethodNotAvailableException; void verifyAccount( - String verificationCode, String pin + String verificationCode, + String pin ) throws IOException, PinLockedException, IncorrectPinException; void deleteLocalAccountData() throws IOException; diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java index 03c44013..4072a9df 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -85,7 +85,8 @@ public class SignalAccountFiles { } private Manager initManager( - String number, String accountPath + String number, + String accountPath ) throws IOException, NotRegisteredException, AccountCheckException { if (accountPath == null) { throw new NotRegisteredException(); @@ -152,7 +153,8 @@ public class SignalAccountFiles { } public RegistrationManager initRegistrationManager( - String number, Consumer newManagerListener + String number, + Consumer newManagerListener ) throws IOException { final var accountPath = accountsStore.getPathByNumber(number); if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) { diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/ResendMessageAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/ResendMessageAction.java index 9f399dd8..a8f67fd6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/actions/ResendMessageAction.java +++ b/lib/src/main/java/org/asamk/signal/manager/actions/ResendMessageAction.java @@ -13,7 +13,9 @@ public class ResendMessageAction implements HandleAction { private final MessageSendLogEntry messageSendLogEntry; public ResendMessageAction( - final RecipientId recipientId, final long timestamp, final MessageSendLogEntry messageSendLogEntry + final RecipientId recipientId, + final long timestamp, + final MessageSendLogEntry messageSendLogEntry ) { this.recipientId = recipientId; this.timestamp = timestamp; diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/SendReceiptAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/SendReceiptAction.java index c08438b5..6a5cfd59 100644 --- a/lib/src/main/java/org/asamk/signal/manager/actions/SendReceiptAction.java +++ b/lib/src/main/java/org/asamk/signal/manager/actions/SendReceiptAction.java @@ -15,7 +15,9 @@ public class SendReceiptAction implements HandleAction { private final List timestamps = new ArrayList<>(); public SendReceiptAction( - final RecipientId recipientId, final SignalServiceReceiptMessage.Type type, final long timestamp + final RecipientId recipientId, + final SignalServiceReceiptMessage.Type type, + final long timestamp ) { this.recipientId = recipientId; this.type = type; diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Group.java b/lib/src/main/java/org/asamk/signal/manager/api/Group.java index 5e7e36f8..4d1fdaa1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/Group.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/Group.java @@ -27,7 +27,9 @@ public record Group( ) { public static Group from( - final GroupInfo groupInfo, final RecipientAddressResolver recipientStore, final RecipientId selfRecipientId + final GroupInfo groupInfo, + final RecipientAddressResolver recipientStore, + final RecipientId selfRecipientId ) { return new Group(groupInfo.getGroupId(), groupInfo.getTitle(), diff --git a/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java b/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java index aefaa800..5598d438 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java @@ -338,7 +338,8 @@ public record MessageEnvelope( } static Attachment from( - SignalServiceDataMessage.Quote.QuotedAttachment a, final AttachmentFileProvider fileProvider + SignalServiceDataMessage.Quote.QuotedAttachment a, + final AttachmentFileProvider fileProvider ) { return new Attachment(Optional.empty(), Optional.empty(), @@ -510,9 +511,7 @@ public record MessageEnvelope( public record Preview(String title, String description, long date, String url, Optional image) { - static Preview from( - SignalServicePreview preview, final AttachmentFileProvider fileProvider - ) { + static Preview from(SignalServicePreview preview, final AttachmentFileProvider fileProvider) { return new Preview(preview.getTitle(), preview.getDescription(), preview.getDate(), @@ -832,9 +831,7 @@ public record MessageEnvelope( Optional textAttachment ) { - public static Story from( - SignalServiceStoryMessage storyMessage, final AttachmentFileProvider fileProvider - ) { + public static Story from(SignalServiceStoryMessage storyMessage, final AttachmentFileProvider fileProvider) { return new Story(storyMessage.getAllowsReplies().orElse(false), storyMessage.getGroupContext().map(c -> GroupUtils.getGroupIdV2(c.getMasterKey())), storyMessage.getFileAttachment().map(f -> Data.Attachment.from(f, fileProvider)), @@ -852,7 +849,8 @@ public record MessageEnvelope( ) { static TextAttachment from( - SignalServiceTextAttachment textAttachment, final AttachmentFileProvider fileProvider + SignalServiceTextAttachment textAttachment, + final AttachmentFileProvider fileProvider ) { return new TextAttachment(textAttachment.getText(), textAttachment.getStyle().map(Style::from), diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java index 7ee80815..482e0f60 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java @@ -33,7 +33,8 @@ public class ServiceConfig { } public static ServiceEnvironmentConfig getServiceEnvironmentConfig( - ServiceEnvironment serviceEnvironment, String userAgent + ServiceEnvironment serviceEnvironment, + String userAgent ) { final Interceptor userAgentInterceptor = chain -> chain.proceed(chain.request() .newBuilder() diff --git a/lib/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java index 76352d1a..ae686c36 100644 --- a/lib/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java @@ -18,7 +18,8 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; public class GroupUtils { public static void setGroupContext( - final SignalServiceDataMessage.Builder messageBuilder, final GroupInfo groupInfo + final SignalServiceDataMessage.Builder messageBuilder, + final GroupInfo groupInfo ) { if (groupInfo instanceof GroupInfoV1) { var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.DELIVER) diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java index 474a2822..d5e8581e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java @@ -165,7 +165,9 @@ public class AccountHelper { } public void startChangeNumber( - String newNumber, boolean voiceVerification, String captcha + String newNumber, + boolean voiceVerification, + String captcha ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException, RateLimitException, VerificationMethodNotAvailableException { final var accountManager = dependencies.createUnauthenticatedAccountManager(newNumber, account.getPassword()); final var registrationApi = accountManager.getRegistrationApi(); @@ -178,7 +180,9 @@ public class AccountHelper { } public void finishChangeNumber( - String newNumber, String verificationCode, String pin + String newNumber, + String verificationCode, + String pin ) throws IncorrectPinException, PinLockedException, IOException { for (var attempts = 0; attempts < 5; attempts++) { try { @@ -196,7 +200,9 @@ public class AccountHelper { } private void finishChangeNumberInternal( - String newNumber, String verificationCode, String pin + String newNumber, + String verificationCode, + String pin ) throws IncorrectPinException, PinLockedException, IOException { final var pniIdentity = KeyUtils.generateIdentityKeyPair(); final var encryptedDeviceMessages = new ArrayList(); @@ -308,9 +314,7 @@ public class AccountHelper { handlePniChangeNumberMessage(selfChangeNumber, updatePni); } - public void handlePniChangeNumberMessage( - final SyncMessage.PniChangeNumber pniChangeNumber, final PNI updatedPni - ) { + public void handlePniChangeNumberMessage(final SyncMessage.PniChangeNumber pniChangeNumber, final PNI updatedPni) { if (pniChangeNumber.identityKeyPair != null && pniChangeNumber.registrationId != null && pniChangeNumber.signedPreKey != null) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java index 3005325a..0ffa62ed 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java @@ -104,9 +104,7 @@ public class AttachmentHelper { retrieveAttachment(attachment, input -> IOUtils.copyStream(input, outputStream)); } - public void retrieveAttachment( - SignalServiceAttachment attachment, AttachmentHandler consumer - ) throws IOException { + public void retrieveAttachment(SignalServiceAttachment attachment, AttachmentHandler consumer) throws IOException { if (attachment.isStream()) { var input = attachment.asStream().getInputStream(); // don't close input stream here, it might be reused later (e.g. with contact sync messages ...) @@ -131,7 +129,8 @@ public class AttachmentHelper { } private InputStream retrieveAttachmentAsStream( - SignalServiceAttachmentPointer pointer, File tmpFile + SignalServiceAttachmentPointer pointer, + File tmpFile ) throws IOException { try { return dependencies.getMessageReceiver() diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ContactHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ContactHelper.java index 56ef89ed..fafb9ffe 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ContactHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ContactHelper.java @@ -49,7 +49,9 @@ public class ContactHelper { } public void setExpirationTimer( - RecipientId recipientId, int messageExpirationTimer, int messageExpirationTimerVersion + RecipientId recipientId, + int messageExpirationTimer, + int messageExpirationTimerVersion ) { var contact = account.getContactStore().getContact(recipientId); if (contact != null && ( diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index ad31f35b..0d838204 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -118,7 +118,9 @@ public class GroupHelper { } public GroupInfoV2 getOrMigrateGroup( - final GroupMasterKey groupMasterKey, final int revision, final byte[] signedGroupChange + final GroupMasterKey groupMasterKey, + final int revision, + final byte[] signedGroupChange ) { final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); @@ -166,7 +168,8 @@ public class GroupHelper { } private DecryptedGroup handleDecryptedGroupResponse( - GroupInfoV2 groupInfoV2, final DecryptedGroupResponse decryptedGroupResponse + GroupInfoV2 groupInfoV2, + final DecryptedGroupResponse decryptedGroupResponse ) { final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); ReceivedGroupSendEndorsements groupSendEndorsements = dependencies.getGroupsV2Operations() @@ -181,7 +184,8 @@ public class GroupHelper { } private GroupChange handleGroupChangeResponse( - final GroupInfoV2 groupInfoV2, final GroupChangeResponse groupChangeResponse + final GroupInfoV2 groupInfoV2, + final GroupChangeResponse groupChangeResponse ) { ReceivedGroupSendEndorsements groupSendEndorsements = dependencies.getGroupsV2Operations() .forGroup(GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey())) @@ -195,7 +199,9 @@ public class GroupHelper { } public Pair createGroup( - String name, Set members, String avatarFile + String name, + Set members, + String avatarFile ) throws IOException, AttachmentInvalidException { final var selfRecipientId = account.getSelfRecipientId(); if (members != null && members.contains(selfRecipientId)) { @@ -363,7 +369,8 @@ public class GroupHelper { } public SendGroupMessageResults quitGroup( - final GroupId groupId, final Set newAdmins + final GroupId groupId, + final Set newAdmins ) throws IOException, LastGroupAdminException, NotAGroupMemberException, GroupNotFoundException { var group = getGroupForUpdating(groupId); if (group instanceof GroupInfoV1) { @@ -396,9 +403,7 @@ public class GroupHelper { context.getJobExecutor().enqueueJob(new SyncStorageJob()); } - public SendGroupMessageResults sendGroupInfoRequest( - GroupIdV1 groupId, RecipientId recipientId - ) throws IOException { + public SendGroupMessageResults sendGroupInfoRequest(GroupIdV1 groupId, RecipientId recipientId) throws IOException { var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.REQUEST_INFO).withId(groupId.serialize()); var messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group.build()); @@ -408,7 +413,8 @@ public class GroupHelper { } public SendGroupMessageResults sendGroupInfoMessage( - GroupIdV1 groupId, RecipientId recipientId + GroupIdV1 groupId, + RecipientId recipientId ) throws IOException, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException { GroupInfoV1 g; var group = getGroupForUpdating(groupId); @@ -480,7 +486,9 @@ public class GroupHelper { } private void retrieveGroupV2Avatar( - GroupSecretParams groupSecretParams, String cdnKey, OutputStream outputStream + GroupSecretParams groupSecretParams, + String cdnKey, + OutputStream outputStream ) throws IOException { var groupOperations = dependencies.getGroupsV2Operations().forGroup(groupSecretParams); @@ -583,7 +591,10 @@ public class GroupHelper { } private SendGroupMessageResults updateGroupV1( - final GroupInfoV1 gv1, final String name, final Set members, final byte[] avatarFile + final GroupInfoV1 gv1, + final String name, + final Set members, + final byte[] avatarFile ) throws IOException, AttachmentInvalidException { updateGroupV1Details(gv1, name, members, avatarFile); @@ -596,7 +607,10 @@ public class GroupHelper { } private void updateGroupV1Details( - final GroupInfoV1 g, final String name, final Collection members, final byte[] avatarFile + final GroupInfoV1 g, + final String name, + final Collection members, + final byte[] avatarFile ) throws IOException { if (name != null) { g.name = name; @@ -615,7 +629,8 @@ public class GroupHelper { * Change the expiration timer for a group */ private void setExpirationTimer( - GroupInfoV1 groupInfoV1, int messageExpirationTimer + GroupInfoV1 groupInfoV1, + int messageExpirationTimer ) throws NotAGroupMemberException, GroupNotFoundException, IOException, GroupSendingNotAllowedException { groupInfoV1.messageExpirationTime = messageExpirationTimer; account.getGroupStore().updateGroup(groupInfoV1); @@ -828,7 +843,8 @@ public class GroupHelper { } private SendGroupMessageResults quitGroupV2( - final GroupInfoV2 groupInfoV2, final Set newAdmins + final GroupInfoV2 groupInfoV2, + final Set newAdmins ) throws LastGroupAdminException, IOException { final var currentAdmins = groupInfoV2.getAdminMembers(); newAdmins.removeAll(currentAdmins); @@ -882,7 +898,9 @@ public class GroupHelper { } private SendGroupMessageResults sendUpdateGroupV2Message( - GroupInfoV2 group, DecryptedGroup newDecryptedGroup, GroupChange groupChange + GroupInfoV2 group, + DecryptedGroup newDecryptedGroup, + GroupChange groupChange ) throws IOException { final var selfRecipientId = account.getSelfRecipientId(); final var members = group.getMembersIncludingPendingWithout(selfRecipientId); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java index 6c6acd65..05bc876a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java @@ -94,7 +94,8 @@ class GroupV2Helper { } DecryptedGroupJoinInfo getDecryptedGroupJoinInfo( - GroupMasterKey groupMasterKey, GroupLinkPassword password + GroupMasterKey groupMasterKey, + GroupLinkPassword password ) throws IOException, GroupLinkNotActiveException { var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); @@ -105,7 +106,9 @@ class GroupV2Helper { } GroupHistoryPage getDecryptedGroupHistoryPage( - final GroupSecretParams groupSecretParams, int fromRevision, long sendEndorsementsExpirationMs + final GroupSecretParams groupSecretParams, + int fromRevision, + long sendEndorsementsExpirationMs ) throws NotAGroupMemberException { try { final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams); @@ -138,9 +141,7 @@ class GroupV2Helper { return partialDecryptedGroup.revision; } - Pair createGroup( - String name, Set members, byte[] avatarFile - ) { + Pair createGroup(String name, Set members, byte[] avatarFile) { final var newGroup = buildNewGroup(name, members, avatarFile); if (newGroup == null) { return null; @@ -170,9 +171,7 @@ class GroupV2Helper { return new Pair<>(g, response); } - private GroupsV2Operations.NewGroup buildNewGroup( - String name, Set members, byte[] avatar - ) { + private GroupsV2Operations.NewGroup buildNewGroup(String name, Set members, byte[] avatar) { final var profileKeyCredential = context.getProfileHelper() .getExpiringProfileKeyCredential(context.getAccount().getSelfRecipientId()); if (profileKeyCredential == null) { @@ -202,7 +201,10 @@ class GroupV2Helper { } Pair updateGroup( - GroupInfoV2 groupInfoV2, String name, String description, byte[] avatarFile + GroupInfoV2 groupInfoV2, + String name, + String description, + byte[] avatarFile ) throws IOException { final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); var groupOperations = dependencies.getGroupsV2Operations().forGroup(groupSecretParams); @@ -225,7 +227,8 @@ class GroupV2Helper { } Pair addMembers( - GroupInfoV2 groupInfoV2, Set newMembers + GroupInfoV2 groupInfoV2, + Set newMembers ) throws IOException { GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -251,7 +254,8 @@ class GroupV2Helper { } Pair leaveGroup( - GroupInfoV2 groupInfoV2, Set membersToMakeAdmin + GroupInfoV2 groupInfoV2, + Set membersToMakeAdmin ) throws IOException { var pendingMembersList = groupInfoV2.getGroup().pendingMembers; final var selfAci = getSelfAci(); @@ -271,7 +275,8 @@ class GroupV2Helper { } Pair removeMembers( - GroupInfoV2 groupInfoV2, Set members + GroupInfoV2 groupInfoV2, + Set members ) throws IOException { final var memberUuids = members.stream() .map(context.getRecipientHelper()::resolveSignalServiceAddress) @@ -283,7 +288,8 @@ class GroupV2Helper { } Pair approveJoinRequestMembers( - GroupInfoV2 groupInfoV2, Set members + GroupInfoV2 groupInfoV2, + Set members ) throws IOException { final var memberUuids = members.stream() .map(context.getRecipientHelper()::resolveSignalServiceAddress) @@ -294,7 +300,8 @@ class GroupV2Helper { } Pair refuseJoinRequestMembers( - GroupInfoV2 groupInfoV2, Set members + GroupInfoV2 groupInfoV2, + Set members ) throws IOException { final var memberUuids = members.stream() .map(context.getRecipientHelper()::resolveSignalServiceAddress) @@ -304,7 +311,8 @@ class GroupV2Helper { } Pair revokeInvitedMembers( - GroupInfoV2 groupInfoV2, Set members + GroupInfoV2 groupInfoV2, + Set members ) throws IOException { var pendingMembersList = groupInfoV2.getGroup().pendingMembers; final var memberUuids = members.stream() @@ -318,7 +326,8 @@ class GroupV2Helper { } Pair banMembers( - GroupInfoV2 groupInfoV2, Set block + GroupInfoV2 groupInfoV2, + Set block ) throws IOException { GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -336,7 +345,8 @@ class GroupV2Helper { } Pair unbanMembers( - GroupInfoV2 groupInfoV2, Set block + GroupInfoV2 groupInfoV2, + Set block ) throws IOException { GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -359,7 +369,8 @@ class GroupV2Helper { } Pair setGroupLinkState( - GroupInfoV2 groupInfoV2, GroupLinkState state + GroupInfoV2 groupInfoV2, + GroupLinkState state ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -374,7 +385,8 @@ class GroupV2Helper { } Pair setEditDetailsPermission( - GroupInfoV2 groupInfoV2, GroupPermission permission + GroupInfoV2 groupInfoV2, + GroupPermission permission ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -384,7 +396,8 @@ class GroupV2Helper { } Pair setAddMemberPermission( - GroupInfoV2 groupInfoV2, GroupPermission permission + GroupInfoV2 groupInfoV2, + GroupPermission permission ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -468,7 +481,9 @@ class GroupV2Helper { } Pair setMemberAdmin( - GroupInfoV2 groupInfoV2, RecipientId recipientId, boolean admin + GroupInfoV2 groupInfoV2, + RecipientId recipientId, + boolean admin ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId); @@ -482,7 +497,8 @@ class GroupV2Helper { } Pair setMessageExpirationTimer( - GroupInfoV2 groupInfoV2, int messageExpirationTimer + GroupInfoV2 groupInfoV2, + int messageExpirationTimer ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var change = groupOperations.createModifyGroupTimerChange(messageExpirationTimer); @@ -490,7 +506,8 @@ class GroupV2Helper { } Pair setIsAnnouncementGroup( - GroupInfoV2 groupInfoV2, boolean isAnnouncementGroup + GroupInfoV2 groupInfoV2, + boolean isAnnouncementGroup ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var change = groupOperations.createAnnouncementGroupChange(isAnnouncementGroup); @@ -518,7 +535,8 @@ class GroupV2Helper { } private Pair revokeInvites( - GroupInfoV2 groupInfoV2, Set pendingMembers + GroupInfoV2 groupInfoV2, + Set pendingMembers ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var uuidCipherTexts = pendingMembers.stream().map(member -> { @@ -532,28 +550,32 @@ class GroupV2Helper { } private Pair approveJoinRequest( - GroupInfoV2 groupInfoV2, Set uuids + GroupInfoV2 groupInfoV2, + Set uuids ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); return commitChange(groupInfoV2, groupOperations.createApproveGroupJoinRequest(uuids)); } private Pair refuseJoinRequest( - GroupInfoV2 groupInfoV2, Set serviceIds + GroupInfoV2 groupInfoV2, + Set serviceIds ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); return commitChange(groupInfoV2, groupOperations.createRefuseGroupJoinRequest(serviceIds, false, List.of())); } private Pair ejectMembers( - GroupInfoV2 groupInfoV2, Set members + GroupInfoV2 groupInfoV2, + Set members ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(members, false, List.of())); } private Pair commitChange( - GroupInfoV2 groupInfoV2, GroupChange.Actions.Builder change + GroupInfoV2 groupInfoV2, + GroupChange.Actions.Builder change ) throws IOException { final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); final var groupOperations = dependencies.getGroupsV2Operations().forGroup(groupSecretParams); @@ -676,7 +698,8 @@ class GroupV2Helper { } private GroupsV2AuthorizationString getAuthorizationString( - final GroupSecretParams groupSecretParams, final long todaySeconds + final GroupSecretParams groupSecretParams, + final long todaySeconds ) throws VerificationFailedException { var authCredentialResponse = groupApiCredentials.get(todaySeconds); final var aci = getSelfAci(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java index cc71e15c..82d44fde 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java @@ -66,9 +66,7 @@ public class IdentityHelper { return fingerprint == null ? null : fingerprint.getScannableFingerprint(); } - private Fingerprint computeSafetyNumberFingerprint( - final ServiceId serviceId, final IdentityKey theirIdentityKey - ) { + private Fingerprint computeSafetyNumberFingerprint(final ServiceId serviceId, final IdentityKey theirIdentityKey) { if (!serviceId.isUnknown()) { return Utils.computeSafetyNumberForUuid(account.getAci(), account.getAciIdentityKeyPair().getPublicKey(), @@ -89,7 +87,9 @@ public class IdentityHelper { } private boolean trustIdentity( - RecipientId recipientId, BiFunction verifier, TrustLevel trustLevel + RecipientId recipientId, + BiFunction verifier, + TrustLevel trustLevel ) { final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId); final var serviceId = address.serviceId().orElse(null); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index 294b17f6..376a671b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -190,7 +190,9 @@ public final class IncomingMessageHandler { } private SignalServiceContent validate( - Envelope envelope, SignalServiceCipherResult cipherResult, long serverDeliveredTimestamp + Envelope envelope, + SignalServiceCipherResult cipherResult, + long serverDeliveredTimestamp ) throws ProtocolInvalidKeyException, ProtocolInvalidMessageException, UnsupportedDataMessageException, InvalidMessageStructureException { final var content = cipherResult.getContent(); final var envelopeMetadata = cipherResult.getMetadata(); @@ -280,7 +282,9 @@ public final class IncomingMessageHandler { } public List handleMessage( - SignalServiceEnvelope envelope, SignalServiceContent content, ReceiveConfig receiveConfig + SignalServiceEnvelope envelope, + SignalServiceContent content, + ReceiveConfig receiveConfig ) { var actions = new ArrayList(); final var senderDeviceAddress = getSender(envelope, content); @@ -381,7 +385,8 @@ public final class IncomingMessageHandler { } private boolean handlePniSignatureMessage( - final SignalServicePniSignatureMessage message, final SignalServiceAddress senderAddress + final SignalServicePniSignatureMessage message, + final SignalServiceAddress senderAddress ) { final var aci = senderAddress.getServiceId(); final var aciIdentity = account.getIdentityKeyStore().getIdentityInfo(aci); @@ -865,7 +870,9 @@ public final class IncomingMessageHandler { } private List handleSignalServiceStoryMessage( - SignalServiceStoryMessage message, RecipientId source, boolean ignoreAttachments + SignalServiceStoryMessage message, + RecipientId source, + boolean ignoreAttachments ) { var actions = new ArrayList(); if (message.getGroupContext().isPresent()) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java index 7669b383..a95635cf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java @@ -21,9 +21,7 @@ public class PinHelper { this.secureValueRecoveries = secureValueRecoveries; } - public void setRegistrationLockPin( - String pin, MasterKey masterKey - ) throws IOException { + public void setRegistrationLockPin(String pin, MasterKey masterKey) throws IOException { IOException exception = null; for (final var secureValueRecovery : secureValueRecoveries) { try { @@ -82,7 +80,8 @@ public class PinHelper { } public SecureValueRecovery.RestoreResponse.Success getRegistrationLockData( - String pin, LockedException lockedException + String pin, + LockedException lockedException ) throws IOException, IncorrectPinException { var svr2Credentials = lockedException.getSvr2Credentials(); if (svr2Credentials != null) { @@ -103,7 +102,9 @@ public class PinHelper { } public SecureValueRecovery.RestoreResponse.Success getRegistrationLockData( - SecureValueRecovery secureValueRecovery, AuthCredentials authCredentials, String pin + SecureValueRecovery secureValueRecovery, + AuthCredentials authCredentials, + String pin ) throws IOException, IncorrectPinException { final var restoreResponse = secureValueRecovery.restoreDataPreRegistration(authCredentials, null, pin); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java index 864ef656..339425c1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java @@ -30,9 +30,7 @@ public class PreKeyHelper { private final SignalAccount account; private final SignalDependencies dependencies; - public PreKeyHelper( - final SignalAccount account, final SignalDependencies dependencies - ) { + public PreKeyHelper(final SignalAccount account, final SignalDependencies dependencies) { this.account = account; this.dependencies = dependencies; } @@ -79,7 +77,8 @@ public class PreKeyHelper { } private boolean refreshPreKeysIfNecessary( - final ServiceIdType serviceIdType, final IdentityKeyPair identityKeyPair + final ServiceIdType serviceIdType, + final IdentityKeyPair identityKeyPair ) throws IOException { OneTimePreKeyCounts preKeyCounts; try { @@ -221,7 +220,8 @@ public class PreKeyHelper { } private List generateKyberPreKeys( - ServiceIdType serviceIdType, final IdentityKeyPair identityKeyPair + ServiceIdType serviceIdType, + final IdentityKeyPair identityKeyPair ) { final var accountData = account.getAccountData(serviceIdType); final var offset = accountData.getPreKeyMetadata().getNextKyberPreKeyId(); @@ -246,7 +246,9 @@ public class PreKeyHelper { } private KyberPreKeyRecord generateLastResortKyberPreKey( - ServiceIdType serviceIdType, IdentityKeyPair identityKeyPair, final int offset + ServiceIdType serviceIdType, + IdentityKeyPair identityKeyPair, + final int offset ) { final var accountData = account.getAccountData(serviceIdType); final var signedPreKeyId = accountData.getPreKeyMetadata().getNextKyberPreKeyId() + offset; diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java index 03d277f9..184eac6b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -271,7 +271,9 @@ public final class ProfileHelper { } private Profile decryptProfileAndDownloadAvatar( - final RecipientId recipientId, final ProfileKey profileKey, final SignalServiceProfile encryptedProfile + final RecipientId recipientId, + final ProfileKey profileKey, + final SignalServiceProfile encryptedProfile ) { final var avatarPath = encryptedProfile.getAvatar(); downloadProfileAvatar(recipientId, avatarPath, profileKey); @@ -280,7 +282,9 @@ public final class ProfileHelper { } public void downloadProfileAvatar( - final RecipientId recipientId, final String avatarPath, final ProfileKey profileKey + final RecipientId recipientId, + final String avatarPath, + final ProfileKey profileKey ) { var profile = account.getProfileStore().getProfile(recipientId); if (profile == null || !Objects.equals(avatarPath, profile.getAvatarUrlPath())) { @@ -308,7 +312,8 @@ public final class ProfileHelper { } private Single retrieveProfile( - RecipientId recipientId, SignalServiceProfile.RequestType requestType + RecipientId recipientId, + SignalServiceProfile.RequestType requestType ) { var unidentifiedAccess = getUnidentifiedAccess(recipientId); var profileKey = Optional.ofNullable(account.getProfileStore().getProfileKey(recipientId)); @@ -408,9 +413,7 @@ public final class ProfileHelper { }); } - private void downloadProfileAvatar( - RecipientAddress address, String avatarPath, ProfileKey profileKey - ) { + private void downloadProfileAvatar(RecipientAddress address, String avatarPath, ProfileKey profileKey) { if (avatarPath == null) { try { context.getAvatarStore().deleteProfileAvatar(address); @@ -430,7 +433,9 @@ public final class ProfileHelper { } private void retrieveProfileAvatar( - String avatarPath, ProfileKey profileKey, OutputStream outputStream + String avatarPath, + ProfileKey profileKey, + OutputStream outputStream ) throws IOException { var tmpFile = IOUtils.createTempFile(); try (var input = dependencies.getMessageReceiver() diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java index baa0d583..ab75bd16 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java @@ -83,7 +83,10 @@ public class ReceiveHelper { } public void receiveMessages( - Duration timeout, boolean returnOnTimeout, Integer maxMessages, Manager.ReceiveMessageHandler handler + Duration timeout, + boolean returnOnTimeout, + Integer maxMessages, + Manager.ReceiveMessageHandler handler ) throws IOException { account.setNeedsToRetryFailedMessages(true); hasCaughtUpWithOldMessages = false; @@ -264,7 +267,8 @@ public class ReceiveHelper { } private List retryFailedReceivedMessage( - final Manager.ReceiveMessageHandler handler, final CachedMessage cachedMessage + final Manager.ReceiveMessageHandler handler, + final CachedMessage cachedMessage ) { var envelope = cachedMessage.loadEnvelope(); if (envelope == null) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java index 24b3eb5f..6225b982 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java @@ -97,7 +97,8 @@ public class RecipientHelper { } public RecipientId resolveRecipientByUsernameOrLink( - String username, boolean forceRefresh + String username, + boolean forceRefresh ) throws UnregisteredRecipientException { final Username finalUsername; try { @@ -180,7 +181,8 @@ public class RecipientHelper { } private Map getRegisteredUsers( - final Set numbers, final boolean isPartialRefresh + final Set numbers, + final boolean isPartialRefresh ) throws IOException { Map registeredUsers = getRegisteredUsersV2(numbers, isPartialRefresh); @@ -211,7 +213,8 @@ public class RecipientHelper { } private Map getRegisteredUsersV2( - final Set numbers, boolean isPartialRefresh + final Set numbers, + boolean isPartialRefresh ) throws IOException { final var previousNumbers = isPartialRefresh ? Set.of() : account.getCdsiStore().getAllNumbers(); final var newNumbers = new HashSet<>(numbers) {{ diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index 0622b4a8..2fb55003 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java @@ -125,7 +125,8 @@ public class SendHelper { } public SendMessageResult sendReceiptMessage( - final SignalServiceReceiptMessage receiptMessage, final RecipientId recipientId + final SignalServiceReceiptMessage receiptMessage, + final RecipientId recipientId ) { final var messageSendLogStore = account.getMessageSendLogStore(); final var result = handleSendMessage(recipientId, @@ -157,7 +158,9 @@ public class SendHelper { } public SendMessageResult sendRetryReceipt( - DecryptionErrorMessage errorMessage, RecipientId recipientId, Optional groupId + DecryptionErrorMessage errorMessage, + RecipientId recipientId, + Optional groupId ) { logger.debug("Sending retry receipt for {} to {}, device: {}", errorMessage.getTimestamp(), @@ -183,7 +186,8 @@ public class SendHelper { } public SendMessageResult sendSelfMessage( - SignalServiceDataMessage.Builder messageBuilder, Optional editTargetTimestamp + SignalServiceDataMessage.Builder messageBuilder, + Optional editTargetTimestamp ) { final var recipientId = account.getSelfRecipientId(); final var contact = account.getContactStore().getContact(recipientId); @@ -214,9 +218,7 @@ public class SendHelper { } } - public SendMessageResult sendTypingMessage( - SignalServiceTypingMessage message, RecipientId recipientId - ) { + public SendMessageResult sendTypingMessage(SignalServiceTypingMessage message, RecipientId recipientId) { final var result = handleSendMessage(recipientId, (messageSender, address, unidentifiedAccess, includePniSignature) -> messageSender.sendTyping(List.of( address), List.of(unidentifiedAccess), message, null).getFirst()); @@ -225,7 +227,8 @@ public class SendHelper { } public List sendGroupTypingMessage( - SignalServiceTypingMessage message, GroupId groupId + SignalServiceTypingMessage message, + GroupId groupId ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { final var g = getGroupForSending(groupId); if (g.isAnnouncementGroup() && !g.isAdmin(account.getSelfRecipientId())) { @@ -238,7 +241,9 @@ public class SendHelper { } public SendMessageResult resendMessage( - final RecipientId recipientId, final long timestamp, final MessageSendLogEntry messageSendLogEntry + final RecipientId recipientId, + final long timestamp, + final MessageSendLogEntry messageSendLogEntry ) { logger.trace("Resending message {} to {}", timestamp, recipientId); if (messageSendLogEntry.groupId().isEmpty()) { @@ -552,7 +557,9 @@ public class SendHelper { } private List sendGroupMessageInternalWithLegacy( - final LegacySenderHandler sender, final Set recipientIds, final boolean isRecipientUpdate + final LegacySenderHandler sender, + final Set recipientIds, + final boolean isRecipientUpdate ) throws IOException { final var recipientIdList = new ArrayList<>(recipientIds); final var addresses = recipientIdList.stream() @@ -644,7 +651,9 @@ public class SendHelper { } private SendMessageResult sendMessage( - SignalServiceDataMessage message, RecipientId recipientId, Optional editTargetTimestamp + SignalServiceDataMessage message, + RecipientId recipientId, + Optional editTargetTimestamp ) { final var messageSendLogStore = account.getMessageSendLogStore(); final var urgent = true; diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StickerHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StickerHelper.java index 4a0d25fe..b6d90ff9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StickerHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StickerHelper.java @@ -30,7 +30,9 @@ public class StickerHelper { } public StickerPack addOrUpdateStickerPack( - final StickerPackId stickerPackId, final byte[] stickerPackKey, final boolean installed + final StickerPackId stickerPackId, + final byte[] stickerPackKey, + final boolean installed ) { final var sticker = account.getStickerStore().getStickerPack(stickerPackId); if (sticker != null) { @@ -50,7 +52,8 @@ public class StickerHelper { } public JsonStickerPack getOrRetrieveStickerPack( - StickerPackId packId, byte[] packKey + StickerPackId packId, + byte[] packKey ) throws InvalidStickerException { try { retrieveStickerPack(packId, packKey); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index c18e8df3..8c6b3c52 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -218,7 +218,9 @@ public class StorageHelper { } private boolean writeToStorage( - final StorageKey storageKey, final SignalStorageManifest remoteManifest, final boolean needsForcePush + final StorageKey storageKey, + final SignalStorageManifest remoteManifest, + final boolean needsForcePush ) throws IOException, RetryLaterException { final WriteOperationResult remoteWriteOperation; try (final var connection = account.getAccountDatabase().getConnection()) { @@ -408,7 +410,8 @@ public class StorageHelper { } private List getSignalStorageRecords( - final StorageKey storageKey, final List storageIds + final StorageKey storageKey, + final List storageIds ) throws IOException { List records; try { @@ -430,7 +433,8 @@ public class StorageHelper { } private List buildLocalStorageRecords( - final Connection connection, final List storageIds + final Connection connection, + final List storageIds ) throws SQLException { final var records = new ArrayList(); for (final var storageId : storageIds) { @@ -443,7 +447,8 @@ public class StorageHelper { } private SignalStorageRecord buildLocalStorageRecord( - Connection connection, StorageId storageId + Connection connection, + StorageId storageId ) throws SQLException { return switch (ManifestRecord.Identifier.Type.fromValue(storageId.getType())) { case ManifestRecord.Identifier.Type.CONTACT -> { @@ -484,7 +489,8 @@ public class StorageHelper { * exclusive to the local data set. */ private static IdDifferenceResult findIdDifference( - Collection remoteIds, Collection localIds + Collection remoteIds, + Collection localIds ) { final var base64Encoder = Base64.getEncoder(); final var remoteByRawId = remoteIds.stream() @@ -520,7 +526,8 @@ public class StorageHelper { } private List processKnownRecords( - final Connection connection, List records + final Connection connection, + List records ) throws SQLException { final var unknownRecords = new ArrayList(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java index 56665fda..70427f00 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java @@ -217,7 +217,9 @@ public class SyncHelper { @NotNull private DeviceContact getDeviceContact( - final RecipientAddress address, final RecipientId recipientId, final Contact contact + final RecipientAddress address, + final RecipientId recipientId, + final Contact contact ) throws IOException { var currentIdentity = address.serviceId().isEmpty() ? null @@ -262,7 +264,9 @@ public class SyncHelper { } public SendMessageResult sendVerifiedMessage( - SignalServiceAddress destination, IdentityKey identityKey, TrustLevel trustLevel + SignalServiceAddress destination, + IdentityKey identityKey, + TrustLevel trustLevel ) { var verifiedMessage = new VerifiedMessage(destination, identityKey, @@ -278,7 +282,8 @@ public class SyncHelper { } public SendMessageResult sendStickerOperationsMessage( - List installStickers, List removeStickers + List installStickers, + List removeStickers ) { var installStickerMessages = installStickers.stream().map(s -> getStickerPackOperationMessage(s, true)); var removeStickerMessages = removeStickers.stream().map(s -> getStickerPackOperationMessage(s, false)); @@ -288,7 +293,8 @@ public class SyncHelper { } private static StickerPackOperationMessage getStickerPackOperationMessage( - final StickerPack s, final boolean installed + final StickerPack s, + final boolean installed ) { return new StickerPackOperationMessage(s.packId().serialize(), s.packKey(), @@ -414,15 +420,14 @@ public class SyncHelper { } } - public SendMessageResult sendMessageRequestResponse( - final MessageRequestResponse.Type type, final GroupId groupId - ) { + public SendMessageResult sendMessageRequestResponse(final MessageRequestResponse.Type type, final GroupId groupId) { final var response = MessageRequestResponseMessage.forGroup(groupId.serialize(), localToRemoteType(type)); return context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forMessageRequestResponse(response)); } public SendMessageResult sendMessageRequestResponse( - final MessageRequestResponse.Type type, final RecipientId recipientId + final MessageRequestResponse.Type type, + final RecipientId recipientId ) { final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId); if (address.serviceId().isEmpty()) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java index 3899179c..fb39789e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java @@ -158,7 +158,8 @@ public class UnidentifiedAccessHelper { } private static byte[] getTargetUnidentifiedAccessKey( - final Profile targetProfile, final ProfileKey theirProfileKey + final Profile targetProfile, + final ProfileKey theirProfileKey ) { return switch (targetProfile.getUnidentifiedAccessMode()) { case ENABLED -> theirProfileKey == null ? null : UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey); diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java index 27bda583..2f2ba3db 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java @@ -417,7 +417,9 @@ public class ManagerImpl implements Manager { @Override public void startChangeNumber( - String newNumber, boolean voiceVerification, String captcha + String newNumber, + boolean voiceVerification, + String captcha ) throws RateLimitException, IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException, NotPrimaryDeviceException, VerificationMethodNotAvailableException { if (!account.isPrimaryDevice()) { throw new NotPrimaryDeviceException(); @@ -427,7 +429,9 @@ public class ManagerImpl implements Manager { @Override public void finishChangeNumber( - String newNumber, String verificationCode, String pin + String newNumber, + String verificationCode, + String pin ) throws IncorrectPinException, PinLockedException, IOException, NotPrimaryDeviceException { if (!account.isPrimaryDevice()) { throw new NotPrimaryDeviceException(); @@ -447,7 +451,8 @@ public class ManagerImpl implements Manager { @Override public void submitRateLimitRecaptchaChallenge( - String challenge, String captcha + String challenge, + String captcha ) throws IOException, CaptchaRejectedException { captcha = captcha == null ? null : captcha.replace("signalcaptcha://", ""); @@ -527,7 +532,8 @@ public class ManagerImpl implements Manager { @Override public SendGroupMessageResults quitGroup( - GroupId groupId, Set groupAdmins + GroupId groupId, + Set groupAdmins ) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException, UnregisteredRecipientException { final var newAdmins = context.getRecipientHelper().resolveRecipients(groupAdmins); return context.getGroupHelper().quitGroup(groupId, newAdmins); @@ -545,7 +551,9 @@ public class ManagerImpl implements Manager { @Override public Pair createGroup( - String name, Set members, String avatarFile + String name, + Set members, + String avatarFile ) throws IOException, AttachmentInvalidException, UnregisteredRecipientException { return context.getGroupHelper() .createGroup(name, @@ -555,7 +563,8 @@ public class ManagerImpl implements Manager { @Override public SendGroupMessageResults updateGroup( - final GroupId groupId, final UpdateGroup updateGroup + final GroupId groupId, + final UpdateGroup updateGroup ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException, GroupSendingNotAllowedException, UnregisteredRecipientException { return context.getGroupHelper() .updateGroup(groupId, @@ -596,7 +605,9 @@ public class ManagerImpl implements Manager { } private SendMessageResults sendMessage( - SignalServiceDataMessage.Builder messageBuilder, Set recipients, boolean notifySelf + SignalServiceDataMessage.Builder messageBuilder, + Set recipients, + boolean notifySelf ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { return sendMessage(messageBuilder, recipients, notifySelf, Optional.empty()); } @@ -644,7 +655,8 @@ public class ManagerImpl implements Manager { } private SendMessageResults sendTypingMessage( - SignalServiceTypingMessage.Action action, Set recipients + SignalServiceTypingMessage.Action action, + Set recipients ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { var results = new HashMap>(); final var timestamp = System.currentTimeMillis(); @@ -671,15 +683,14 @@ public class ManagerImpl implements Manager { @Override public SendMessageResults sendTypingMessage( - TypingAction action, Set recipients + TypingAction action, + Set recipients ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { return sendTypingMessage(action.toSignalService(), recipients); } @Override - public SendMessageResults sendReadReceipt( - RecipientIdentifier.Single sender, List messageIds - ) { + public SendMessageResults sendReadReceipt(RecipientIdentifier.Single sender, List messageIds) { final var timestamp = System.currentTimeMillis(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, @@ -689,9 +700,7 @@ public class ManagerImpl implements Manager { } @Override - public SendMessageResults sendViewedReceipt( - RecipientIdentifier.Single sender, List messageIds - ) { + public SendMessageResults sendViewedReceipt(RecipientIdentifier.Single sender, List messageIds) { final var timestamp = System.currentTimeMillis(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.VIEWED, messageIds, @@ -724,7 +733,9 @@ public class ManagerImpl implements Manager { @Override public SendMessageResults sendMessage( - Message message, Set recipients, boolean notifySelf + Message message, + Set recipients, + boolean notifySelf ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException { final var selfProfile = context.getProfileHelper().getSelfProfile(); if (selfProfile == null || selfProfile.getDisplayName().isEmpty()) { @@ -738,7 +749,9 @@ public class ManagerImpl implements Manager { @Override public SendMessageResults sendEditMessage( - Message message, Set recipients, long editTargetTimestamp + Message message, + Set recipients, + long editTargetTimestamp ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException { final var messageBuilder = SignalServiceDataMessage.newBuilder(); applyMessage(messageBuilder, message); @@ -746,7 +759,8 @@ public class ManagerImpl implements Manager { } private void applyMessage( - final SignalServiceDataMessage.Builder messageBuilder, final Message message + final SignalServiceDataMessage.Builder messageBuilder, + final Message message ) throws AttachmentInvalidException, IOException, UnregisteredRecipientException, InvalidStickerException { final var additionalAttachments = new ArrayList(); if (message.messageText().length() > ServiceConfig.MAX_MESSAGE_BODY_SIZE) { @@ -863,7 +877,8 @@ public class ManagerImpl implements Manager { @Override public SendMessageResults sendRemoteDeleteMessage( - long targetSentTimestamp, Set recipients + long targetSentTimestamp, + Set recipients ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp); final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete); @@ -915,7 +930,9 @@ public class ManagerImpl implements Manager { @Override public SendMessageResults sendPaymentNotificationMessage( - byte[] receipt, String note, RecipientIdentifier.Single recipient + byte[] receipt, + String note, + RecipientIdentifier.Single recipient ) throws IOException { final var paymentNotification = new SignalServiceDataMessage.PaymentNotification(receipt, note); final var payment = new SignalServiceDataMessage.Payment(paymentNotification, null); @@ -958,7 +975,8 @@ public class ManagerImpl implements Manager { @Override public SendMessageResults sendMessageRequestResponse( - final MessageRequestResponse.Type type, final Set recipients + final MessageRequestResponse.Type type, + final Set recipients ) { var results = new HashMap>(); for (final var recipient : recipients) { @@ -1021,7 +1039,9 @@ public class ManagerImpl implements Manager { @Override public void setContactName( - RecipientIdentifier.Single recipient, String givenName, final String familyName + RecipientIdentifier.Single recipient, + String givenName, + final String familyName ) throws NotPrimaryDeviceException, UnregisteredRecipientException { if (!account.isPrimaryDevice()) { throw new NotPrimaryDeviceException(); @@ -1033,7 +1053,8 @@ public class ManagerImpl implements Manager { @Override public void setContactsBlocked( - Collection recipients, boolean blocked + Collection recipients, + boolean blocked ) throws IOException, UnregisteredRecipientException { if (recipients.isEmpty()) { return; @@ -1067,7 +1088,8 @@ public class ManagerImpl implements Manager { @Override public void setGroupsBlocked( - final Collection groupIds, final boolean blocked + final Collection groupIds, + final boolean blocked ) throws GroupNotFoundException, IOException { if (groupIds.isEmpty()) { return; @@ -1093,7 +1115,8 @@ public class ManagerImpl implements Manager { @Override public void setExpirationTimer( - RecipientIdentifier.Single recipient, int messageExpirationTimer + RecipientIdentifier.Single recipient, + int messageExpirationTimer ) throws IOException, UnregisteredRecipientException { var recipientId = context.getRecipientHelper().resolveRecipient(recipient); context.getContactHelper().setExpirationTimer(recipientId, messageExpirationTimer); @@ -1255,7 +1278,9 @@ public class ManagerImpl implements Manager { @Override public void receiveMessages( - Optional timeout, Optional maxMessages, ReceiveMessageHandler handler + Optional timeout, + Optional maxMessages, + ReceiveMessageHandler handler ) throws IOException, AlreadyReceivingException { receiveMessages(timeout.orElse(Duration.ofMinutes(1)), timeout.isPresent(), maxMessages.orElse(null), handler); } @@ -1275,7 +1300,10 @@ public class ManagerImpl implements Manager { } private void receiveMessages( - Duration timeout, boolean returnOnTimeout, Integer maxMessages, ReceiveMessageHandler handler + Duration timeout, + boolean returnOnTimeout, + Integer maxMessages, + ReceiveMessageHandler handler ) throws IOException, AlreadyReceivingException { synchronized (messageHandlers) { if (isReceiving()) { @@ -1431,7 +1459,8 @@ public class ManagerImpl implements Manager { @Override public boolean trustIdentityVerified( - RecipientIdentifier.Single recipient, IdentityVerificationCode verificationCode + RecipientIdentifier.Single recipient, + IdentityVerificationCode verificationCode ) throws UnregisteredRecipientException { return switch (verificationCode) { case IdentityVerificationCode.Fingerprint fingerprint -> trustIdentity(recipient, @@ -1450,7 +1479,8 @@ public class ManagerImpl implements Manager { } private boolean trustIdentity( - RecipientIdentifier.Single recipient, Function trustMethod + RecipientIdentifier.Single recipient, + Function trustMethod ) throws UnregisteredRecipientException { final var recipientId = context.getRecipientHelper().resolveRecipient(recipient); final var updated = trustMethod.apply(recipientId); diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java index 543d9a99..5faf1371 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java @@ -105,7 +105,9 @@ public class RegistrationManagerImpl implements RegistrationManager { @Override public void register( - boolean voiceVerification, String captcha, final boolean forceRegister + boolean voiceVerification, + String captcha, + final boolean forceRegister ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException, RateLimitException, VerificationMethodNotAvailableException { if (account.isRegistered() && account.getServiceEnvironment() != null @@ -145,7 +147,8 @@ public class RegistrationManagerImpl implements RegistrationManager { @Override public void verifyAccount( - String verificationCode, String pin + String verificationCode, + String pin ) throws IOException, PinLockedException, IncorrectPinException { if (account.isRegistered()) { throw new IOException("Account is already registered"); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java b/lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java index 63ba5a37..9e1c6303 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java @@ -611,7 +611,8 @@ public class AccountDatabase extends Database { } private static void createUuidMappingTable( - final Connection connection, final Statement statement + final Connection connection, + final Statement statement ) throws SQLException { statement.executeUpdate(""" CREATE TABLE tmp_mapping_table ( diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/AttachmentStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/AttachmentStore.java index 5e734174..d25f1a72 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/AttachmentStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/AttachmentStore.java @@ -22,7 +22,8 @@ public class AttachmentStore { } public void storeAttachmentPreview( - final SignalServiceAttachmentPointer pointer, final AttachmentStorer storer + final SignalServiceAttachmentPointer pointer, + final AttachmentStorer storer ) throws IOException { storeAttachment(getAttachmentPreviewFile(pointer.getRemoteId(), pointer.getFileName(), @@ -30,7 +31,8 @@ public class AttachmentStore { } public void storeAttachment( - final SignalServiceAttachmentPointer pointer, final AttachmentStorer storer + final SignalServiceAttachmentPointer pointer, + final AttachmentStorer storer ) throws IOException { storeAttachment(getAttachmentFile(pointer), storer); } @@ -54,22 +56,24 @@ public class AttachmentStore { } private File getAttachmentPreviewFile( - SignalServiceAttachmentRemoteId attachmentId, Optional filename, Optional contentType + SignalServiceAttachmentRemoteId attachmentId, + Optional filename, + Optional contentType ) { final var extension = getAttachmentExtension(filename, contentType); return new File(attachmentsPath, attachmentId.toString() + extension + ".preview"); } private File getAttachmentFile( - SignalServiceAttachmentRemoteId attachmentId, Optional filename, Optional contentType + SignalServiceAttachmentRemoteId attachmentId, + Optional filename, + Optional contentType ) { final var extension = getAttachmentExtension(filename, contentType); return new File(attachmentsPath, attachmentId.toString() + extension); } - private static String getAttachmentExtension( - final Optional filename, final Optional contentType - ) { + private static String getAttachmentExtension(final Optional filename, final Optional contentType) { return filename.filter(f -> f.contains(".")) .map(f -> f.substring(f.lastIndexOf(".") + 1)) .or(() -> contentType.flatMap(MimeUtils::guessExtensionFromMimeType)) diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/Database.java b/lib/src/main/java/org/asamk/signal/manager/storage/Database.java index 71704698..2fb09a4f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/Database.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/Database.java @@ -24,7 +24,8 @@ public abstract class Database implements AutoCloseable { } public static T initDatabase( - File databaseFile, Function newDatabase + File databaseFile, + Function newDatabase ) throws SQLException { HikariDataSource dataSource = null; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 87cf3aa3..5c8a5530 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -189,7 +189,10 @@ public class SignalAccount implements Closeable { } public static SignalAccount load( - File dataPath, String accountPath, boolean waitForLock, final Settings settings + File dataPath, + String accountPath, + boolean waitForLock, + final Settings settings ) throws IOException { logger.trace("Opening account file"); final var fileName = getFileName(dataPath, accountPath); @@ -316,7 +319,9 @@ public class SignalAccount implements Closeable { } public void finishLinking( - final int deviceId, final PreKeyCollection aciPreKeys, final PreKeyCollection pniPreKeys + final int deviceId, + final PreKeyCollection aciPreKeys, + final PreKeyCollection pniPreKeys ) { this.registered = true; this.deviceId = deviceId; @@ -375,7 +380,9 @@ public class SignalAccount implements Closeable { } private void mergeRecipients( - final Connection connection, RecipientId recipientId, RecipientId toBeMergedRecipientId + final Connection connection, + RecipientId recipientId, + RecipientId toBeMergedRecipientId ) throws SQLException { getMessageCache().mergeRecipients(recipientId, toBeMergedRecipientId); getGroupStore().mergeRecipients(connection, recipientId, toBeMergedRecipientId); @@ -438,9 +445,7 @@ public class SignalAccount implements Closeable { return f.exists() && !f.isDirectory() && f.length() > 0L; } - private void load( - File dataPath, String accountPath, final Settings settings - ) throws IOException { + private void load(File dataPath, String accountPath, final Settings settings) throws IOException { logger.trace("Loading account file {}", accountPath); this.dataPath = dataPath; this.accountPath = accountPath; @@ -786,7 +791,8 @@ public class SignalAccount implements Closeable { } private void loadLegacyStores( - final JsonNode rootNode, final LegacyJsonSignalProtocolStore legacySignalProtocolStore + final JsonNode rootNode, + final LegacyJsonSignalProtocolStore legacySignalProtocolStore ) { var legacyRecipientStoreNode = rootNode.get("recipientStore"); if (legacyRecipientStoreNode != null) { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/UnknownStorageIdStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/UnknownStorageIdStore.java index 75389751..5ea5eff0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/UnknownStorageIdStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/UnknownStorageIdStore.java @@ -41,9 +41,7 @@ public class UnknownStorageIdStore { } } - public List getUnknownStorageIds( - Connection connection, Collection types - ) throws SQLException { + public List getUnknownStorageIds(Connection connection, Collection types) throws SQLException { final var typesCommaSeparated = types.stream().map(String::valueOf).collect(Collectors.joining(",")); final var sql = ( """ diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/Utils.java b/lib/src/main/java/org/asamk/signal/manager/storage/Utils.java index fa2e8916..e4858f21 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/Utils.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/Utils.java @@ -72,7 +72,8 @@ public class Utils { } public static T executeQuerySingleRow( - PreparedStatement statement, ResultSetMapper mapper + PreparedStatement statement, + ResultSetMapper mapper ) throws SQLException { final var resultSet = statement.executeQuery(); if (!resultSet.next()) { @@ -82,7 +83,8 @@ public class Utils { } public static Optional executeQueryForOptional( - PreparedStatement statement, ResultSetMapper mapper + PreparedStatement statement, + ResultSetMapper mapper ) throws SQLException { final var resultSet = statement.executeQuery(); if (!resultSet.next()) { @@ -92,7 +94,8 @@ public class Utils { } public static Stream executeQueryForStream( - PreparedStatement statement, ResultSetMapper mapper + PreparedStatement statement, + ResultSetMapper mapper ) throws SQLException { final var resultSet = statement.executeQuery(); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java index 02379941..a0fa3012 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java @@ -41,7 +41,9 @@ public class AccountsStore { private final AccountLoader accountLoader; public AccountsStore( - final File dataPath, final ServiceEnvironment serviceEnvironment, final AccountLoader accountLoader + final File dataPath, + final ServiceEnvironment serviceEnvironment, + final AccountLoader accountLoader ) throws IOException { this.dataPath = dataPath; this.serviceEnvironment = getServiceEnvironmentString(serviceEnvironment); @@ -202,7 +204,9 @@ public class AccountsStore { } private AccountsStorage upgradeAccountsFile( - final FileChannel fileChannel, final AccountsStorage storage, final int accountsVersion + final FileChannel fileChannel, + final AccountsStorage storage, + final int accountsVersion ) { try { List newAccounts = storage.accounts(); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/configuration/ConfigurationStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/configuration/ConfigurationStore.java index 558d4f86..ef4a4545 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/configuration/ConfigurationStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/configuration/ConfigurationStore.java @@ -59,7 +59,8 @@ public class ConfigurationStore { } public void setUnidentifiedDeliveryIndicators( - final Connection connection, final boolean value + final Connection connection, + final boolean value ) throws SQLException { if (keyValueStore.storeEntry(connection, unidentifiedDeliveryIndicators, value)) { recipientStore.rotateSelfStorageId(connection); @@ -125,7 +126,8 @@ public class ConfigurationStore { } public void setPhoneNumberSharingMode( - final Connection connection, final PhoneNumberSharingMode value + final Connection connection, + final PhoneNumberSharingMode value ) throws SQLException { if (keyValueStore.storeEntry(connection, phoneNumberSharingMode, value)) { recipientStore.rotateSelfStorageId(connection); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java index 89306ecd..f15b94a8 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java @@ -31,7 +31,9 @@ public final class GroupInfoV2 extends GroupInfo { private final RecipientResolver recipientResolver; public GroupInfoV2( - final GroupIdV2 groupId, final GroupMasterKey masterKey, final RecipientResolver recipientResolver + final GroupIdV2 groupId, + final GroupMasterKey masterKey, + final RecipientResolver recipientResolver ) { this.groupId = groupId; this.masterKey = masterKey; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java index c4c075bf..26c4c08d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupStore.java @@ -121,7 +121,10 @@ public class GroupStore { } public void storeStorageRecord( - final Connection connection, final GroupId groupId, final StorageId storageId, final byte[] storageRecord + final Connection connection, + final GroupId groupId, + final StorageId storageId, + final byte[] storageRecord ) throws SQLException { final var groupTable = groupId instanceof GroupIdV1 ? TABLE_GROUP_V1 : TABLE_GROUP_V2; final var deleteSql = ( @@ -250,7 +253,8 @@ public class GroupStore { } public GroupInfoV2 getGroupOrPartialMigrate( - Connection connection, final GroupMasterKey groupMasterKey + Connection connection, + final GroupMasterKey groupMasterKey ) throws SQLException { final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); final var groupId = GroupUtils.getGroupIdV2(groupSecretParams); @@ -258,9 +262,7 @@ public class GroupStore { return getGroupOrPartialMigrate(connection, groupMasterKey, groupId); } - public GroupInfoV2 getGroupOrPartialMigrate( - final GroupMasterKey groupMasterKey, final GroupIdV2 groupId - ) { + public GroupInfoV2 getGroupOrPartialMigrate(final GroupMasterKey groupMasterKey, final GroupIdV2 groupId) { try (final var connection = database.getConnection()) { return getGroupOrPartialMigrate(connection, groupMasterKey, groupId); } catch (SQLException e) { @@ -269,7 +271,9 @@ public class GroupStore { } private GroupInfoV2 getGroupOrPartialMigrate( - Connection connection, final GroupMasterKey groupMasterKey, final GroupIdV2 groupId + Connection connection, + final GroupMasterKey groupMasterKey, + final GroupIdV2 groupId ) throws SQLException { switch (getGroup(connection, (GroupId) groupId)) { case GroupInfoV1 groupInfoV1 -> { @@ -325,7 +329,9 @@ public class GroupStore { } public void mergeRecipients( - final Connection connection, final RecipientId recipientId, final RecipientId toBeMergedRecipientId + final Connection connection, + final RecipientId recipientId, + final RecipientId toBeMergedRecipientId ) throws SQLException { final var sql = ( """ @@ -360,7 +366,9 @@ public class GroupStore { } public void updateStorageIds( - Connection connection, Map storageIdV1Map, Map storageIdV2Map + Connection connection, + Map storageIdV1Map, + Map storageIdV2Map ) throws SQLException { final var sql = ( """ @@ -385,9 +393,7 @@ public class GroupStore { } } - public void updateStorageId( - Connection connection, GroupId groupId, StorageId storageId - ) throws SQLException { + public void updateStorageId(Connection connection, GroupId groupId, StorageId storageId) throws SQLException { final var sqlV1 = ( """ UPDATE %s @@ -460,7 +466,9 @@ public class GroupStore { } private void insertOrReplaceGroup( - final Connection connection, Long internalId, final GroupInfo group + final Connection connection, + Long internalId, + final GroupInfo group ) throws SQLException { if (group instanceof GroupInfoV1 groupV1) { if (internalId != null) { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/LegacyGroupStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/LegacyGroupStore.java index 1d8c8799..383912dd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/LegacyGroupStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/LegacyGroupStore.java @@ -151,7 +151,8 @@ public class LegacyGroupStore { @Override public List deserialize( - JsonParser jsonParser, DeserializationContext deserializationContext + JsonParser jsonParser, + DeserializationContext deserializationContext ) throws IOException { var addresses = new ArrayList(); JsonNode node = jsonParser.getCodec().readTree(jsonParser); @@ -184,7 +185,8 @@ public class LegacyGroupStore { @Override public List deserialize( - JsonParser jsonParser, DeserializationContext deserializationContext + JsonParser jsonParser, + DeserializationContext deserializationContext ) throws IOException { var groups = new ArrayList<>(); JsonNode node = jsonParser.getCodec().readTree(jsonParser); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityInfo.java index d00d455c..0b9b8c17 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityInfo.java @@ -11,9 +11,7 @@ public class IdentityInfo { private final TrustLevel trustLevel; private final long addedTimestamp; - IdentityInfo( - final String address, IdentityKey identityKey, TrustLevel trustLevel, long addedTimestamp - ) { + IdentityInfo(final String address, IdentityKey identityKey, TrustLevel trustLevel, long addedTimestamp) { this.address = address; this.identityKey = identityKey; this.trustLevel = trustLevel; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java index a4b355ea..5e32ba12 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java @@ -49,7 +49,9 @@ public class IdentityKeyStore { } public IdentityKeyStore( - final Database database, final TrustNewIdentity trustNewIdentity, RecipientStore recipientStore + final Database database, + final TrustNewIdentity trustNewIdentity, + RecipientStore recipientStore ) { this.database = database; this.trustNewIdentity = trustNewIdentity; @@ -65,7 +67,9 @@ public class IdentityKeyStore { } public boolean saveIdentity( - final Connection connection, final ServiceId serviceId, final IdentityKey identityKey + final Connection connection, + final ServiceId serviceId, + final IdentityKey identityKey ) throws SQLException { return saveIdentity(connection, serviceId.toString(), identityKey); } @@ -82,7 +86,9 @@ public class IdentityKeyStore { } private boolean saveIdentity( - final Connection connection, final String address, final IdentityKey identityKey + final Connection connection, + final String address, + final IdentityKey identityKey ) throws SQLException { final var identityInfo = loadIdentity(connection, address); if (identityInfo != null && identityInfo.getIdentityKey().equals(identityKey)) { @@ -230,9 +236,7 @@ public class IdentityKeyStore { logger.debug("Complete identities migration took {}ms", (System.nanoTime() - start) / 1000000); } - private IdentityInfo loadIdentity( - final Connection connection, final String address - ) throws SQLException { + private IdentityInfo loadIdentity(final Connection connection, final String address) throws SQLException { final var sql = ( """ SELECT i.address, i.identity_key, i.added_timestamp, i.trust_level diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/identities/LegacyIdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/identities/LegacyIdentityKeyStore.java index a7f6a4aa..fc6e93e1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/identities/LegacyIdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/identities/LegacyIdentityKeyStore.java @@ -41,7 +41,9 @@ public class LegacyIdentityKeyStore { static final Pattern identityFileNamePattern = Pattern.compile("(\\d+)"); private static List getIdentities( - final File identitiesPath, final RecipientResolver resolver, final RecipientAddressResolver addressResolver + final File identitiesPath, + final RecipientResolver resolver, + final RecipientAddressResolver addressResolver ) { final var files = identitiesPath.listFiles(); if (files == null) { @@ -66,7 +68,9 @@ public class LegacyIdentityKeyStore { } private static IdentityInfo loadIdentityLocked( - final RecipientId recipientId, RecipientAddressResolver addressResolver, final File identitiesPath + final RecipientId recipientId, + RecipientAddressResolver addressResolver, + final File identitiesPath ) { final var file = getIdentityFile(recipientId, identitiesPath); if (!file.exists()) { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/keyValue/KeyValueStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/keyValue/KeyValueStore.java index c51f32f1..e5bbc188 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/keyValue/KeyValueStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/keyValue/KeyValueStore.java @@ -74,7 +74,9 @@ public class KeyValueStore { } public boolean storeEntry( - final Connection connection, final KeyValueEntry key, final T value + final Connection connection, + final KeyValueEntry key, + final T value ) throws SQLException { final var entry = getEntry(key); if (Objects.equals(entry, value)) { @@ -98,7 +100,8 @@ public class KeyValueStore { @SuppressWarnings("unchecked") private static T readValueFromResultSet( - final KeyValueEntry key, final ResultSet resultSet + final KeyValueEntry key, + final ResultSet resultSet ) throws SQLException { Object value; final var clazz = key.clazz(); @@ -134,7 +137,10 @@ public class KeyValueStore { } private static void setParameterValue( - final PreparedStatement statement, final int parameterIndex, final Class clazz, final T value + final PreparedStatement statement, + final int parameterIndex, + final Class clazz, + final T value ) throws SQLException { if (clazz == int.class || clazz == Integer.class) { if (value == null) { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacyProfileStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacyProfileStore.java index abb66470..39d6b7fb 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacyProfileStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacyProfileStore.java @@ -34,7 +34,8 @@ public class LegacyProfileStore { @Override public List deserialize( - JsonParser jsonParser, DeserializationContext deserializationContext + JsonParser jsonParser, + DeserializationContext deserializationContext ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java index 5d3a95a8..ec91a628 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java @@ -18,6 +18,7 @@ public interface ProfileStore { void storeProfileKey(RecipientId recipientId, ProfileKey profileKey); void storeExpiringProfileKeyCredential( - RecipientId recipientId, ExpiringProfileKeyCredential expiringProfileKeyCredential + RecipientId recipientId, + ExpiringProfileKeyCredential expiringProfileKeyCredential ); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonIdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonIdentityKeyStore.java index 0d12a303..632efc7e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonIdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonIdentityKeyStore.java @@ -32,7 +32,9 @@ public class LegacyJsonIdentityKeyStore { private final int localRegistrationId; private LegacyJsonIdentityKeyStore( - final List identities, IdentityKeyPair identityKeyPair, int localRegistrationId + final List identities, + IdentityKeyPair identityKeyPair, + int localRegistrationId ) { this.identities = identities; this.identityKeyPair = identityKeyPair; @@ -77,7 +79,8 @@ public class LegacyJsonIdentityKeyStore { @Override public LegacyJsonIdentityKeyStore deserialize( - JsonParser jsonParser, DeserializationContext deserializationContext + JsonParser jsonParser, + DeserializationContext deserializationContext ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonPreKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonPreKeyStore.java index 7412f95a..feb27e97 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonPreKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonPreKeyStore.java @@ -26,7 +26,8 @@ public class LegacyJsonPreKeyStore { @Override public LegacyJsonPreKeyStore deserialize( - JsonParser jsonParser, DeserializationContext deserializationContext + JsonParser jsonParser, + DeserializationContext deserializationContext ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSessionStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSessionStore.java index ee675d78..b971078c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSessionStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSessionStore.java @@ -31,7 +31,8 @@ public class LegacyJsonSessionStore { @Override public LegacyJsonSessionStore deserialize( - JsonParser jsonParser, DeserializationContext deserializationContext + JsonParser jsonParser, + DeserializationContext deserializationContext ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSignedPreKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSignedPreKeyStore.java index cdcba916..ed1b5d95 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSignedPreKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyJsonSignedPreKeyStore.java @@ -26,7 +26,8 @@ public class LegacyJsonSignedPreKeyStore { @Override public LegacyJsonSignedPreKeyStore deserialize( - JsonParser jsonParser, DeserializationContext deserializationContext + JsonParser jsonParser, + DeserializationContext deserializationContext ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java index f334a2be..b631f632 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java @@ -172,7 +172,9 @@ public class SignalProtocolStore implements SignalServiceAccountDataStore { @Override public void storeSenderKey( - final SignalProtocolAddress sender, final UUID distributionId, final SenderKeyRecord record + final SignalProtocolAddress sender, + final UUID distributionId, + final SenderKeyRecord record ) { senderKeyStore.storeSenderKey(sender, distributionId, record); } @@ -189,7 +191,8 @@ public class SignalProtocolStore implements SignalServiceAccountDataStore { @Override public void markSenderKeySharedWith( - final DistributionId distributionId, final Collection addresses + final DistributionId distributionId, + final Collection addresses ) { senderKeyStore.markSenderKeySharedWith(distributionId, addresses); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/CdsiStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/CdsiStore.java index 1170e651..4b68adbe 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/CdsiStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/CdsiStore.java @@ -98,9 +98,7 @@ public class CdsiStore { } } - private static void removeNumbers( - final Connection connection, final Set numbers - ) throws SQLException { + private static void removeNumbers(final Connection connection, final Set numbers) throws SQLException { final var sql = ( """ DELETE FROM %s @@ -116,7 +114,9 @@ public class CdsiStore { } private static void addNumbers( - final Connection connection, final Set numbers, final long lastSeen + final Connection connection, + final Set numbers, + final long lastSeen ) throws SQLException { final var sql = ( """ @@ -135,7 +135,9 @@ public class CdsiStore { } private static void updateLastSeen( - final Connection connection, final Set numbers, final long lastSeen + final Connection connection, + final Set numbers, + final long lastSeen ) throws SQLException { final var sql = ( """ diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore.java index fb1e188b..907aafe4 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore.java @@ -27,7 +27,8 @@ public class LegacyRecipientStore { @Override public List deserialize( - JsonParser jsonParser, DeserializationContext deserializationContext + JsonParser jsonParser, + DeserializationContext deserializationContext ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelper.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelper.java index edc1121e..95c17102 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelper.java @@ -15,7 +15,8 @@ public class MergeRecipientHelper { private static final Logger logger = LoggerFactory.getLogger(MergeRecipientHelper.class); static Pair> resolveRecipientTrustedLocked( - Store store, RecipientAddress address + Store store, + RecipientAddress address ) throws SQLException { // address has at least one of serviceId/pni and optionally number/username diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java index 2aad97d6..00048560 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java @@ -208,7 +208,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } public RecipientId resolveRecipientByNumber( - final String number, Supplier serviceIdSupplier + final String number, + Supplier serviceIdSupplier ) throws UnregisteredRecipientException { final Optional byNumber; try (final var connection = database.getConnection()) { @@ -238,7 +239,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } public RecipientId resolveRecipientByUsername( - final String username, Supplier aciSupplier + final String username, + Supplier aciSupplier ) throws UnregisteredRecipientException { final Optional byUsername; try (final var connection = database.getConnection()) { @@ -301,7 +303,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re @Override public RecipientId resolveRecipientTrusted( - final Optional aci, final Optional pni, final Optional number + final Optional aci, + final Optional pni, + final Optional number ) { return resolveRecipientTrusted(new RecipientAddress(aci, pni, number, Optional.empty())); } @@ -392,7 +396,10 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } public List getRecipients( - boolean onlyContacts, Optional blocked, Set recipientIds, Optional name + boolean onlyContacts, + Optional blocked, + Set recipientIds, + Optional name ) { final var sqlWhere = new ArrayList(); if (onlyContacts) { @@ -614,14 +621,17 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } public void storeProfileKey( - Connection connection, RecipientId recipientId, final ProfileKey profileKey + Connection connection, + RecipientId recipientId, + final ProfileKey profileKey ) throws SQLException { storeProfileKey(connection, recipientId, profileKey, true); } @Override public void storeExpiringProfileKeyCredential( - RecipientId recipientId, final ExpiringProfileKeyCredential profileKeyCredential + RecipientId recipientId, + final ExpiringProfileKeyCredential profileKeyCredential ) { try (final var connection = database.getConnection()) { storeExpiringProfileKeyCredential(connection, recipientId, profileKeyCredential); @@ -661,7 +671,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } public void updateStorageId( - Connection connection, RecipientId recipientId, StorageId storageId + Connection connection, + RecipientId recipientId, + StorageId storageId ) throws SQLException { final var sql = ( """ @@ -813,7 +825,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } public void storeContact( - final Connection connection, final RecipientId recipientId, final Contact contact + final Connection connection, + final RecipientId recipientId, + final Contact contact ) throws SQLException { final var sql = ( """ @@ -852,7 +866,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } public int removeStorageIdsFromLocalOnlyUnregisteredRecipients( - final Connection connection, final List storageIds + final Connection connection, + final List storageIds ) throws SQLException { final var sql = ( """ @@ -965,7 +980,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private void markUnregisteredAndSplitIfNecessary( - final Connection connection, final RecipientId recipientId + final Connection connection, + final RecipientId recipientId ) throws SQLException { markUnregistered(connection, recipientId); final var address = resolveRecipientAddress(connection, recipientId); @@ -977,7 +993,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private void markDiscoverable( - final Connection connection, final RecipientId recipientId, final boolean discoverable + final Connection connection, + final RecipientId recipientId, + final boolean discoverable ) throws SQLException { final var sql = ( """ @@ -993,9 +1011,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } } - private void markRegistered( - final Connection connection, final RecipientId recipientId - ) throws SQLException { + private void markRegistered(final Connection connection, final RecipientId recipientId) throws SQLException { final var sql = ( """ UPDATE %s @@ -1009,9 +1025,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } } - private void markUnregistered( - final Connection connection, final RecipientId recipientId - ) throws SQLException { + private void markUnregistered(final Connection connection, final RecipientId recipientId) throws SQLException { final var sql = ( """ UPDATE %s @@ -1046,7 +1060,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } public void storeProfile( - final Connection connection, final RecipientId recipientId, final Profile profile + final Connection connection, + final RecipientId recipientId, + final Profile profile ) throws SQLException { final var sql = ( """ @@ -1079,7 +1095,10 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private void storeProfileKey( - Connection connection, RecipientId recipientId, final ProfileKey profileKey, boolean resetProfile + Connection connection, + RecipientId recipientId, + final ProfileKey profileKey, + boolean resetProfile ) throws SQLException { if (profileKey != null) { final var recipientProfileKey = getProfileKey(connection, recipientId); @@ -1111,7 +1130,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private RecipientAddress resolveRecipientAddress( - final Connection connection, final RecipientId recipientId + final Connection connection, + final RecipientId recipientId ) throws SQLException { final var sql = ( """ @@ -1150,7 +1170,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private Pair> resolveRecipientTrustedLocked( - final Connection connection, final RecipientAddress address, final boolean isSelf + final Connection connection, + final RecipientAddress address, + final boolean isSelf ) throws SQLException { if (address.hasSingleIdentifier() || ( !isSelf && selfAddressProvider.getSelfAddress().matches(address) @@ -1168,7 +1190,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private void mergeRecipients( - final Connection connection, final RecipientId recipientId, final List toBeMergedRecipientIds + final Connection connection, + final RecipientId recipientId, + final List toBeMergedRecipientIds ) throws SQLException { for (final var toBeMergedRecipientId : toBeMergedRecipientIds) { recipientMergeHandler.mergeRecipients(connection, recipientId, toBeMergedRecipientId); @@ -1177,9 +1201,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } } - private RecipientId resolveRecipientLocked( - Connection connection, RecipientAddress address - ) throws SQLException { + private RecipientId resolveRecipientLocked(Connection connection, RecipientAddress address) throws SQLException { final var byAci = address.aci().isEmpty() ? Optional.empty() : findByServiceId(connection, address.aci().get()); @@ -1236,7 +1258,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private RecipientId addNewRecipient( - final Connection connection, final RecipientAddress address + final Connection connection, + final RecipientAddress address ) throws SQLException { final var sql = ( """ @@ -1277,7 +1300,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private void updateRecipientAddress( - Connection connection, RecipientId recipientId, final RecipientAddress address + Connection connection, + RecipientId recipientId, + final RecipientAddress address ) throws SQLException { recipientAddressCache.entrySet().removeIf(e -> e.getValue().id().equals(recipientId)); final var sql = ( @@ -1312,7 +1337,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private void mergeRecipientsLocked( - Connection connection, RecipientId recipientId, RecipientId toBeMergedRecipientId + Connection connection, + RecipientId recipientId, + RecipientId toBeMergedRecipientId ) throws SQLException { final var contact = getContact(connection, recipientId); if (contact == null) { @@ -1343,7 +1370,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private Optional findByNumber( - final Connection connection, final String number + final Connection connection, + final String number ) throws SQLException { final var sql = """ SELECT r._id, r.number, r.aci, r.pni, r.username @@ -1358,7 +1386,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private Optional findByUsername( - final Connection connection, final String username + final Connection connection, + final String username ) throws SQLException { final var sql = """ SELECT r._id, r.number, r.aci, r.pni, r.username @@ -1373,7 +1402,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private Optional findByServiceId( - final Connection connection, final ServiceId serviceId + final Connection connection, + final ServiceId serviceId ) throws SQLException { var recipientWithAddress = Optional.ofNullable(recipientAddressCache.get(serviceId)); if (recipientWithAddress.isPresent()) { @@ -1394,7 +1424,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private Set findAllByAddress( - final Connection connection, final RecipientAddress address + final Connection connection, + final RecipientAddress address ) throws SQLException { final var sql = """ SELECT r._id, r.number, r.aci, r.pni, r.username @@ -1447,7 +1478,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re } private ExpiringProfileKeyCredential getExpiringProfileKeyCredential( - final Connection connection, final RecipientId recipientId + final Connection connection, + final RecipientId recipientId ) throws SQLException { final var sql = ( """ @@ -1593,7 +1625,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re public interface RecipientMergeHandler { void mergeRecipients( - final Connection connection, RecipientId recipientId, RecipientId toBeMergedRecipientId + final Connection connection, + RecipientId recipientId, + RecipientId toBeMergedRecipientId ) throws SQLException; } @@ -1617,7 +1651,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re @Override public void updateRecipientAddress( - final RecipientId recipientId, final RecipientAddress address + final RecipientId recipientId, + final RecipientAddress address ) throws SQLException { RecipientStore.this.updateRecipientAddress(connection, recipientId, address); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientTrustedResolver.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientTrustedResolver.java index 598ef791..06d29060 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientTrustedResolver.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientTrustedResolver.java @@ -44,7 +44,9 @@ public interface RecipientTrustedResolver { @Override public RecipientId resolveRecipientTrusted( - final Optional aci, final Optional pni, final Optional number + final Optional aci, + final Optional pni, + final Optional number ) { return recipientTrustedResolverSupplier.get().resolveRecipientTrusted(aci, pni, number); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java index 910da3cf..20bfdb6a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java @@ -83,7 +83,10 @@ public class MessageSendLogStore implements AutoCloseable { } public List findMessages( - final ServiceId serviceId, final int deviceId, final long timestamp, final boolean isSenderKey + final ServiceId serviceId, + final int deviceId, + final long timestamp, + final boolean isSenderKey ) { final var sql = """ SELECT group_id, content, content_hint, urgent @@ -111,7 +114,10 @@ public class MessageSendLogStore implements AutoCloseable { } public long insertIfPossible( - long sentTimestamp, SendMessageResult sendMessageResult, ContentHint contentHint, boolean urgent + long sentTimestamp, + SendMessageResult sendMessageResult, + ContentHint contentHint, + boolean urgent ) { if (sendLogDisabled) { return -1; @@ -129,7 +135,10 @@ public class MessageSendLogStore implements AutoCloseable { } public long insertIfPossible( - long sentTimestamp, List sendMessageResults, ContentHint contentHint, boolean urgent + long sentTimestamp, + List sendMessageResults, + ContentHint contentHint, + boolean urgent ) { if (sendLogDisabled) { return -1; @@ -164,7 +173,8 @@ public class MessageSendLogStore implements AutoCloseable { } public void addRecipientToExistingEntryIfPossible( - final long contentId, final List sendMessageResults + final long contentId, + final List sendMessageResults ) { if (sendLogDisabled) { return; @@ -322,7 +332,8 @@ public class MessageSendLogStore implements AutoCloseable { } private void insertRecipientsForExistingContent( - final long contentId, final List recipientDevices + final long contentId, + final List recipientDevices ) { try (final var connection = database.getConnection()) { connection.setAutoCommit(false); @@ -334,7 +345,9 @@ public class MessageSendLogStore implements AutoCloseable { } private void insertRecipientsForExistingContent( - final long contentId, final List recipientDevices, final Connection connection + final long contentId, + final List recipientDevices, + final Connection connection ) throws SQLException { final var sql = """ INSERT INTO %s (address, device_id, content_id) diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java index 39e1df97..dd48fcdc 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java @@ -59,7 +59,9 @@ public class SenderKeyRecordStore implements SenderKeyStore { @Override public void storeSenderKey( - final SignalProtocolAddress address, final UUID distributionId, final SenderKeyRecord record + final SignalProtocolAddress address, + final UUID distributionId, + final SenderKeyRecord record ) { final var key = getKey(address, distributionId); @@ -165,7 +167,9 @@ public class SenderKeyRecordStore implements SenderKeyStore { } private void storeSenderKey( - final Connection connection, final Key key, final SenderKeyRecord senderKeyRecord + final Connection connection, + final Key key, + final SenderKeyRecord senderKeyRecord ) throws SQLException { final var sqlUpdate = """ UPDATE %s diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeySharedStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeySharedStore.java index 284ba08f..175524f5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeySharedStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeySharedStore.java @@ -65,7 +65,8 @@ public class SenderKeySharedStore { } public void markSenderKeySharedWith( - final DistributionId distributionId, final Collection addresses + final DistributionId distributionId, + final Collection addresses ) { final var newEntries = addresses.stream() .map(a -> new SenderKeySharedEntry(a.getName(), a.getDeviceId())) @@ -138,9 +139,7 @@ public class SenderKeySharedStore { } } - public void deleteSharedWith( - final ServiceId serviceId, final int deviceId, final DistributionId distributionId - ) { + public void deleteSharedWith(final ServiceId serviceId, final int deviceId, final DistributionId distributionId) { try (final var connection = database.getConnection()) { final var sql = ( """ @@ -192,7 +191,9 @@ public class SenderKeySharedStore { } private void markSenderKeysSharedWith( - final Connection connection, final DistributionId distributionId, final Set newEntries + final Connection connection, + final DistributionId distributionId, + final Set newEntries ) throws SQLException { final var sql = ( """ diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyStore.java index d60249b6..5ae2790e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyStore.java @@ -25,7 +25,9 @@ public class SenderKeyStore implements SignalServiceSenderKeyStore { @Override public void storeSenderKey( - final SignalProtocolAddress sender, final UUID distributionId, final SenderKeyRecord record + final SignalProtocolAddress sender, + final UUID distributionId, + final SenderKeyRecord record ) { senderKeyRecordStore.storeSenderKey(sender, distributionId, record); } @@ -42,7 +44,8 @@ public class SenderKeyStore implements SignalServiceSenderKeyStore { @Override public void markSenderKeySharedWith( - final DistributionId distributionId, final Collection addresses + final DistributionId distributionId, + final Collection addresses ) { senderKeySharedStore.markSenderKeySharedWith(distributionId, addresses); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java index 067dc5d3..86fe875e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java @@ -342,7 +342,9 @@ public class SessionStore implements SignalServiceSessionStore { } private void storeSession( - final Connection connection, final Key key, final SessionRecord session + final Connection connection, + final Key key, + final SessionRecord session ) throws SQLException { synchronized (cachedSessions) { cachedSessions.put(key, session); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java index 428e9e95..efe65c64 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java @@ -35,7 +35,9 @@ public class LegacyJsonThreadStore { @Override public void serialize( - final Map value, final JsonGenerator jgen, final SerializerProvider provider + final Map value, + final JsonGenerator jgen, + final SerializerProvider provider ) throws IOException { jgen.writeObject(value.values()); } @@ -45,7 +47,8 @@ public class LegacyJsonThreadStore { @Override public Map deserialize( - JsonParser jsonParser, DeserializationContext deserializationContext + JsonParser jsonParser, + DeserializationContext deserializationContext ) throws IOException { var threads = new HashMap(); JsonNode node = jsonParser.getCodec().readTree(jsonParser); diff --git a/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java b/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java index 31e7a90b..18dd1869 100644 --- a/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java +++ b/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java @@ -33,7 +33,9 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor inserts, RecipientAddress self + SignalStorageManifest manifest, + List inserts, + RecipientAddress self ) { validateManifestAndInserts(manifest, inserts, self); } private static void validateManifestAndInserts( - SignalStorageManifest manifest, List inserts, RecipientAddress self + SignalStorageManifest manifest, + List inserts, + RecipientAddress self ) { int accountCount = 0; for (StorageId id : manifest.getStorageIds()) { diff --git a/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java index ac15a30a..bab4c433 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java @@ -13,7 +13,8 @@ import java.util.UUID; public class AttachmentUtils { public static SignalServiceAttachmentStream createAttachmentStream( - String attachment, ResumableUploadSpec resumableUploadSpec + String attachment, + ResumableUploadSpec resumableUploadSpec ) throws AttachmentInvalidException { try { final var streamDetails = Utils.createStreamDetails(attachment); @@ -25,7 +26,9 @@ public class AttachmentUtils { } public static SignalServiceAttachmentStream createAttachmentStream( - StreamDetails streamDetails, Optional name, ResumableUploadSpec resumableUploadSpec + StreamDetails streamDetails, + Optional name, + ResumableUploadSpec resumableUploadSpec ) throws ResumeLocationInvalidException { // TODO maybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option final var uploadTimestamp = System.currentTimeMillis(); diff --git a/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java index bfcb750c..c3dcf3fe 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java @@ -63,7 +63,8 @@ public class KeyUtils { } public static SignedPreKeyRecord generateSignedPreKeyRecord( - final int signedPreKeyId, final ECPrivateKey privateKey + final int signedPreKeyId, + final ECPrivateKey privateKey ) { var keyPair = Curve.generateKeyPair(); byte[] signature; @@ -75,9 +76,7 @@ public class KeyUtils { return new SignedPreKeyRecord(signedPreKeyId, System.currentTimeMillis(), keyPair, signature); } - public static List generateKyberPreKeyRecords( - final int offset, final ECPrivateKey privateKey - ) { + public static List generateKyberPreKeyRecords(final int offset, final ECPrivateKey privateKey) { var records = new ArrayList(PREKEY_BATCH_SIZE); for (var i = 0; i < PREKEY_BATCH_SIZE; i++) { var preKeyId = (offset + i) % PREKEY_MAXIMUM_ID; diff --git a/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java index 03b1c03a..e27ba980 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java @@ -88,7 +88,9 @@ public class NumberVerificationUtils { } public static void requestVerificationCode( - RegistrationApi registrationApi, String sessionId, boolean voiceVerification + RegistrationApi registrationApi, + String sessionId, + boolean voiceVerification ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException { final var locale = Utils.getDefaultLocale(Locale.US); final var response = registrationApi.requestSmsVerificationCode(sessionId, @@ -108,7 +110,11 @@ public class NumberVerificationUtils { } public static Pair verifyNumber( - String sessionId, String verificationCode, String pin, PinHelper pinHelper, Verifier verifier + String sessionId, + String verificationCode, + String pin, + PinHelper pinHelper, + Verifier verifier ) throws IOException, PinLockedException, IncorrectPinException { verificationCode = verificationCode.replace("-", ""); try { @@ -138,7 +144,8 @@ public class NumberVerificationUtils { } private static RegistrationSessionMetadataResponse validateSession( - final RegistrationApi registrationApi, final String sessionId + final RegistrationApi registrationApi, + final String sessionId ) throws IOException { if (sessionId == null || sessionId.isEmpty()) { throw new NoSuchSessionException(); @@ -153,7 +160,8 @@ public class NumberVerificationUtils { } private static RegistrationSessionMetadataResponse getValidSession( - final RegistrationApi registrationApi, final String sessionId + final RegistrationApi registrationApi, + final String sessionId ) throws IOException { try { return validateSession(registrationApi, sessionId); @@ -164,7 +172,9 @@ public class NumberVerificationUtils { } private static RegistrationSessionMetadataResponse submitCaptcha( - RegistrationApi registrationApi, String sessionId, String captcha + RegistrationApi registrationApi, + String sessionId, + String captcha ) throws IOException, CaptchaRequiredException { captcha = captcha == null ? null : captcha.replace("signalcaptcha://", ""); try { @@ -184,7 +194,9 @@ public class NumberVerificationUtils { public interface Verifier { VerifyAccountResponse verify( - String sessionId, String verificationCode, String registrationLock + String sessionId, + String verificationCode, + String registrationLock ) throws IOException; } } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java index fe338e73..36275d8f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java @@ -20,9 +20,7 @@ public class PaymentUtils { /** * Signs the supplied address bytes with the {@link IdentityKeyPair}'s private key and returns a proto that includes it, and it's signature. */ - public static PaymentAddress signPaymentsAddress( - byte[] publicAddressBytes, ECPrivateKey privateKey - ) { + public static PaymentAddress signPaymentsAddress(byte[] publicAddressBytes, ECPrivateKey privateKey) { byte[] signature = privateKey.calculateSignature(publicAddressBytes); return new PaymentAddress.Builder().mobileCoinAddress(new PaymentAddress.MobileCoinAddress.Builder().address( @@ -34,9 +32,7 @@ public class PaymentUtils { *

* Returns the validated bytes if so, otherwise returns null. */ - public static byte[] verifyPaymentsAddress( - PaymentAddress paymentAddress, ECPublicKey publicKey - ) { + public static byte[] verifyPaymentsAddress(PaymentAddress paymentAddress, ECPublicKey publicKey) { final var mobileCoinAddress = paymentAddress.mobileCoinAddress; if (mobileCoinAddress == null || mobileCoinAddress.address == null || mobileCoinAddress.signature == null) { logger.debug("Got payment address without mobile coin address, ignoring."); diff --git a/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java index 17e26ee8..def75049 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java @@ -23,9 +23,7 @@ public class ProfileUtils { private static final Logger logger = LoggerFactory.getLogger(ProfileUtils.class); - public static Profile decryptProfile( - final ProfileKey profileKey, final SignalServiceProfile encryptedProfile - ) { + public static Profile decryptProfile(final ProfileKey profileKey, final SignalServiceProfile encryptedProfile) { var profileCipher = new ProfileCipher(profileKey); IdentityKey identityKey = null; try { @@ -64,7 +62,8 @@ public class ProfileUtils { } public static Profile.UnidentifiedAccessMode getUnidentifiedAccessMode( - final SignalServiceProfile encryptedProfile, final ProfileCipher profileCipher + final SignalServiceProfile encryptedProfile, + final ProfileCipher profileCipher ) { if (encryptedProfile.isUnrestrictedUnidentifiedAccess()) { return Profile.UnidentifiedAccessMode.UNRESTRICTED; @@ -90,7 +89,8 @@ public class ProfileUtils { } private static String decryptString( - final String encrypted, final ProfileCipher profileCipher + final String encrypted, + final ProfileCipher profileCipher ) throws InvalidCiphertextException { try { return encrypted == null ? null : profileCipher.decryptString(Base64.getDecoder().decode(encrypted)); @@ -100,7 +100,8 @@ public class ProfileUtils { } private static Optional decryptBoolean( - final String encrypted, final ProfileCipher profileCipher + final String encrypted, + final ProfileCipher profileCipher ) throws InvalidCiphertextException { try { return encrypted == null @@ -112,7 +113,9 @@ public class ProfileUtils { } private static byte[] decryptAndVerifyMobileCoinAddress( - final byte[] encryptedPaymentAddress, final ProfileCipher profileCipher, final ECPublicKey publicKey + final byte[] encryptedPaymentAddress, + final ProfileCipher profileCipher, + final ECPublicKey publicKey ) throws InvalidCiphertextException { byte[] decrypted; try { diff --git a/lib/src/main/java/org/asamk/signal/manager/util/StickerUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/StickerUtils.java index 302535d9..d017f0a5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/StickerUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/StickerUtils.java @@ -100,7 +100,9 @@ public class StickerUtils { } private static Pair getInputStreamAndLength( - final String rootPath, final ZipFile zip, final String subfile + final String rootPath, + final ZipFile zip, + final String subfile ) throws IOException { if (zip != null) { final var entry = zip.getEntry(subfile); @@ -112,7 +114,9 @@ public class StickerUtils { } private static String getContentType( - final String rootPath, final ZipFile zip, final String subfile + final String rootPath, + final ZipFile zip, + final String subfile ) throws IOException { if (zip != null) { final var entry = zip.getEntry(subfile); diff --git a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java index 16aa3275..ee1f608f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java @@ -59,7 +59,10 @@ public class Utils { } public static Fingerprint computeSafetyNumberForNumber( - String ownNumber, IdentityKey ownIdentityKey, String theirNumber, IdentityKey theirIdentityKey + String ownNumber, + IdentityKey ownIdentityKey, + String theirNumber, + IdentityKey theirIdentityKey ) { // Version 1: E164 user final var version = 1; @@ -70,7 +73,10 @@ public class Utils { } public static Fingerprint computeSafetyNumberForUuid( - ServiceId ownServiceId, IdentityKey ownIdentityKey, ServiceId theirServiceId, IdentityKey theirIdentityKey + ServiceId ownServiceId, + IdentityKey ownIdentityKey, + ServiceId theirServiceId, + IdentityKey theirIdentityKey ) { // Version 2: UUID user final var version = 2; diff --git a/lib/src/test/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelperTest.java b/lib/src/test/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelperTest.java index b57dc6fc..0931b008 100644 --- a/lib/src/test/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelperTest.java +++ b/lib/src/test/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelperTest.java @@ -217,9 +217,7 @@ class MergeRecipientHelperTest { } @Override - public void updateRecipientAddress( - final RecipientId recipientId, final RecipientAddress address - ) { + public void updateRecipientAddress(final RecipientId recipientId, final RecipientAddress address) { recipients.removeIf(r -> r.id().equals(recipientId)); recipients.add(new RecipientWithAddress(recipientId, address)); } diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 1d715659..ac1f6d50 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -27,39 +27,44 @@ public interface Signal extends DBusInterface { void unsubscribeReceive(); long sendMessage( - String message, List attachments, String recipient + String message, + List attachments, + String recipient ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity; long sendMessage( - String message, List attachments, List recipients + String message, + List attachments, + List recipients ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity; - void sendTyping( - String recipient, boolean stop - ) throws Error.Failure, Error.UntrustedIdentity; + void sendTyping(String recipient, boolean stop) throws Error.Failure, Error.UntrustedIdentity; - void sendReadReceipt( - String recipient, List messageIds - ) throws Error.Failure, Error.UntrustedIdentity; + void sendReadReceipt(String recipient, List messageIds) throws Error.Failure, Error.UntrustedIdentity; - void sendViewedReceipt( - String recipient, List messageIds - ) throws Error.Failure, Error.UntrustedIdentity; + void sendViewedReceipt(String recipient, List messageIds) throws Error.Failure, Error.UntrustedIdentity; + + long sendRemoteDeleteMessage(long targetSentTimestamp, String recipient) throws Error.Failure, Error.InvalidNumber; long sendRemoteDeleteMessage( - long targetSentTimestamp, String recipient - ) throws Error.Failure, Error.InvalidNumber; - - long sendRemoteDeleteMessage( - long targetSentTimestamp, List recipients + long targetSentTimestamp, + List recipients ) throws Error.Failure, Error.InvalidNumber; long sendMessageReaction( - String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, String recipient + String emoji, + boolean remove, + String targetAuthor, + long targetSentTimestamp, + String recipient ) throws Error.InvalidNumber, Error.Failure; long sendMessageReaction( - String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List recipients + String emoji, + boolean remove, + String targetAuthor, + long targetSentTimestamp, + List recipients ) throws Error.InvalidNumber, Error.Failure; long sendPaymentNotification(byte[] receipt, String note, String recipient) throws Error.Failure; @@ -68,9 +73,7 @@ public interface Signal extends DBusInterface { void sendSyncRequest() throws Error.Failure; - long sendNoteToSelfMessage( - String message, List attachments - ) throws Error.AttachmentInvalid, Error.Failure; + long sendNoteToSelfMessage(String message, List attachments) throws Error.AttachmentInvalid, Error.Failure; void sendEndSessionMessage(List recipients) throws Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity; @@ -79,19 +82,27 @@ public interface Signal extends DBusInterface { void deleteContact(final String recipient) throws Error.Failure; long sendGroupMessage( - String message, List attachments, byte[] groupId + String message, + List attachments, + byte[] groupId ) throws Error.GroupNotFound, Error.Failure, Error.AttachmentInvalid, Error.InvalidGroupId; void sendGroupTyping( - final byte[] groupId, final boolean stop + final byte[] groupId, + final boolean stop ) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity; long sendGroupRemoteDeleteMessage( - long targetSentTimestamp, byte[] groupId + long targetSentTimestamp, + byte[] groupId ) throws Error.Failure, Error.GroupNotFound, Error.InvalidGroupId; long sendGroupMessageReaction( - String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, byte[] groupId + String emoji, + boolean remove, + String targetAuthor, + long targetSentTimestamp, + byte[] groupId ) throws Error.GroupNotFound, Error.Failure, Error.InvalidNumber, Error.InvalidGroupId; String getContactName(String number) throws Error.InvalidNumber; @@ -119,12 +130,17 @@ public interface Signal extends DBusInterface { List getGroupMembers(byte[] groupId) throws Error.InvalidGroupId; byte[] createGroup( - String name, List members, String avatar + String name, + List members, + String avatar ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber; @Deprecated byte[] updateGroup( - byte[] groupId, String name, List members, String avatar + byte[] groupId, + String name, + List members, + String avatar ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound, Error.InvalidGroupId; @Deprecated @@ -156,7 +172,11 @@ public interface Signal extends DBusInterface { ) throws Error.Failure; void updateProfile( - String name, String about, String aboutEmoji, String avatarPath, boolean removeAvatar + String name, + String about, + String aboutEmoji, + String avatarPath, + boolean removeAvatar ) throws Error.Failure; void removePin(); diff --git a/src/main/java/org/asamk/SignalControl.java b/src/main/java/org/asamk/SignalControl.java index fe51a7f7..3eea790f 100644 --- a/src/main/java/org/asamk/SignalControl.java +++ b/src/main/java/org/asamk/SignalControl.java @@ -13,11 +13,14 @@ import java.util.List; public interface SignalControl extends DBusInterface { void register( - String number, boolean voiceVerification + String number, + boolean voiceVerification ) throws Error.Failure, Error.InvalidNumber, Error.RequiresCaptcha; void registerWithCaptcha( - String number, boolean voiceVerification, String captcha + String number, + boolean voiceVerification, + String captcha ) throws Error.Failure, Error.InvalidNumber, Error.RequiresCaptcha; void verify(String number, String verificationCode) throws Error.Failure, Error.InvalidNumber; diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index f8585386..d3d74c3e 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -292,7 +292,8 @@ public class App { } private RegistrationManager loadRegistrationManager( - final String account, final SignalAccountFiles signalAccountFiles + final String account, + final SignalAccountFiles signalAccountFiles ) throws UnexpectedErrorException { try { return signalAccountFiles.initRegistrationManager(account); @@ -306,7 +307,8 @@ public class App { } private Manager loadManager( - final String account, final SignalAccountFiles signalAccountFiles + final String account, + final SignalAccountFiles signalAccountFiles ) throws CommandException { logger.trace("Loading account file for {}", account); try { diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index d96835a5..d7fd1104 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -101,9 +101,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { writer.println(); } - private void printDataMessage( - PlainTextWriter writer, MessageEnvelope.Data message - ) { + private void printDataMessage(PlainTextWriter writer, MessageEnvelope.Data message) { writer.println("Message timestamp: {}", DateUtils.formatTimestamp(message.timestamp())); if (message.isViewOnce()) { writer.println("=VIEW ONCE="); @@ -197,16 +195,12 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } - private void printEditMessage( - PlainTextWriter writer, MessageEnvelope.Edit message - ) { + private void printEditMessage(PlainTextWriter writer, MessageEnvelope.Edit message) { writer.println("Edit: Target message timestamp: {}", DateUtils.formatTimestamp(message.targetSentTimestamp())); printDataMessage(writer.indentedWriter(), message.dataMessage()); } - private void printStoryMessage( - PlainTextWriter writer, MessageEnvelope.Story message - ) { + private void printStoryMessage(PlainTextWriter writer, MessageEnvelope.Story message) { writer.println("Story: with replies: {}", message.allowsReplies()); if (message.groupId().isPresent()) { writer.println("Group info:"); @@ -226,9 +220,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } - private void printTypingMessage( - final PlainTextWriter writer, final MessageEnvelope.Typing typingMessage - ) { + private void printTypingMessage(final PlainTextWriter writer, final MessageEnvelope.Typing typingMessage) { writer.println("Action: {}", typingMessage.type()); writer.println("Timestamp: {}", DateUtils.formatTimestamp(typingMessage.timestamp())); if (typingMessage.groupId().isPresent()) { @@ -238,9 +230,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } - private void printReceiptMessage( - final PlainTextWriter writer, final MessageEnvelope.Receipt receiptMessage - ) { + private void printReceiptMessage(final PlainTextWriter writer, final MessageEnvelope.Receipt receiptMessage) { writer.println("When: {}", DateUtils.formatTimestamp(receiptMessage.when())); if (receiptMessage.type() == MessageEnvelope.Receipt.Type.DELIVERY) { writer.println("Is delivery receipt"); @@ -257,9 +247,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } - private void printCallMessage( - final PlainTextWriter writer, final MessageEnvelope.Call callMessage - ) { + private void printCallMessage(final PlainTextWriter writer, final MessageEnvelope.Call callMessage) { if (callMessage.destinationDeviceId().isPresent()) { final var deviceId = callMessage.destinationDeviceId().get(); writer.println("Destination device id: {}", deviceId); @@ -302,9 +290,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } - private void printSyncMessage( - final PlainTextWriter writer, final MessageEnvelope.Sync syncMessage - ) { + private void printSyncMessage(final PlainTextWriter writer, final MessageEnvelope.Sync syncMessage) { if (syncMessage.contacts().isPresent()) { final var contactsMessage = syncMessage.contacts().get(); var type = contactsMessage.isComplete() ? "complete" : "partial"; @@ -392,9 +378,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } - private void printPreview( - final PlainTextWriter writer, final MessageEnvelope.Data.Preview preview - ) { + private void printPreview(final PlainTextWriter writer, final MessageEnvelope.Data.Preview preview) { writer.println("Title: {}", preview.title()); writer.println("Description: {}", preview.description()); writer.println("Date: {}", DateUtils.formatTimestamp(preview.date())); @@ -405,25 +389,19 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } - private void printSticker( - final PlainTextWriter writer, final MessageEnvelope.Data.Sticker sticker - ) { + private void printSticker(final PlainTextWriter writer, final MessageEnvelope.Data.Sticker sticker) { writer.println("Pack id: {}", Hex.toStringCondensed(sticker.packId().serialize())); writer.println("Sticker id: {}", sticker.stickerId()); } - private void printReaction( - final PlainTextWriter writer, final MessageEnvelope.Data.Reaction reaction - ) { + private void printReaction(final PlainTextWriter writer, final MessageEnvelope.Data.Reaction reaction) { writer.println("Emoji: {}", reaction.emoji()); writer.println("Target author: {}", formatContact(reaction.targetAuthor())); writer.println("Target timestamp: {}", DateUtils.formatTimestamp(reaction.targetSentTimestamp())); writer.println("Is remove: {}", reaction.isRemove()); } - private void printQuote( - final PlainTextWriter writer, final MessageEnvelope.Data.Quote quote - ) { + private void printQuote(final PlainTextWriter writer, final MessageEnvelope.Data.Quote quote) { writer.println("Id: {}", quote.id()); writer.println("Author: {}", formatContact(quote.author())); if (quote.text().isPresent()) { @@ -541,17 +519,13 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } - private void printGroupContext( - final PlainTextWriter writer, final MessageEnvelope.Data.GroupContext groupContext - ) { + private void printGroupContext(final PlainTextWriter writer, final MessageEnvelope.Data.GroupContext groupContext) { printGroupInfo(writer, groupContext.groupId()); writer.println("Revision: {}", groupContext.revision()); writer.println("Type: {}", groupContext.isGroupUpdate() ? "UPDATE" : "DELIVER"); } - private void printStoryContext( - final PlainTextWriter writer, final MessageEnvelope.Data.StoryContext storyContext - ) { + private void printStoryContext(final PlainTextWriter writer, final MessageEnvelope.Data.StoryContext storyContext) { writer.println("Sender: {}", formatContact(storyContext.author())); writer.println("Sent timestamp: {}", storyContext.sentTimestamp()); } @@ -567,15 +541,11 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } - private void printMention( - PlainTextWriter writer, MessageEnvelope.Data.Mention mention - ) { + private void printMention(PlainTextWriter writer, MessageEnvelope.Data.Mention mention) { writer.println("- {}: {} (length: {})", formatContact(mention.recipient()), mention.start(), mention.length()); } - private void printTextStyle( - PlainTextWriter writer, TextStyle textStyle - ) { + private void printTextStyle(PlainTextWriter writer, TextStyle textStyle) { writer.println("- {}: {} (length: {})", textStyle.style().name(), textStyle.start(), textStyle.length()); } diff --git a/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java b/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java index c40e4901..43e1a0b7 100644 --- a/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java +++ b/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java @@ -38,7 +38,9 @@ public class AddDeviceCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final URI linkUri; try { diff --git a/src/main/java/org/asamk/signal/commands/AddStickerPackCommand.java b/src/main/java/org/asamk/signal/commands/AddStickerPackCommand.java index 89c82200..2d720362 100644 --- a/src/main/java/org/asamk/signal/commands/AddStickerPackCommand.java +++ b/src/main/java/org/asamk/signal/commands/AddStickerPackCommand.java @@ -36,7 +36,9 @@ public class AddStickerPackCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var uris = ns.getList("uri"); for (final var uri : uris) { diff --git a/src/main/java/org/asamk/signal/commands/BlockCommand.java b/src/main/java/org/asamk/signal/commands/BlockCommand.java index 1518c53a..e7d0af49 100644 --- a/src/main/java/org/asamk/signal/commands/BlockCommand.java +++ b/src/main/java/org/asamk/signal/commands/BlockCommand.java @@ -35,7 +35,9 @@ public class BlockCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var contacts = ns.getList("recipient"); final var recipients = CommandUtil.getSingleRecipientIdentifiers(contacts, m.getSelfNumber()); diff --git a/src/main/java/org/asamk/signal/commands/CommandHandler.java b/src/main/java/org/asamk/signal/commands/CommandHandler.java index 8e138b3f..86fede61 100644 --- a/src/main/java/org/asamk/signal/commands/CommandHandler.java +++ b/src/main/java/org/asamk/signal/commands/CommandHandler.java @@ -20,13 +20,15 @@ public class CommandHandler { } public void handleProvisioningCommand( - final ProvisioningCommand command, final ProvisioningManager provisioningManager + final ProvisioningCommand command, + final ProvisioningManager provisioningManager ) throws CommandException { command.handleCommand(ns, provisioningManager, outputWriter); } public void handleRegistrationCommand( - final RegistrationCommand command, final RegistrationManager registrationManager + final RegistrationCommand command, + final RegistrationManager registrationManager ) throws CommandException { command.handleCommand(ns, registrationManager); } @@ -36,7 +38,8 @@ public class CommandHandler { } public void handleMultiLocalCommand( - final MultiLocalCommand command, MultiAccountManager multiAccountManager + final MultiLocalCommand command, + MultiAccountManager multiAccountManager ) throws CommandException { command.handleCommand(ns, multiAccountManager, outputWriter); } diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 59c2245e..197e1286 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -92,7 +92,9 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { Shutdown.installHandler(); logger.info("Starting daemon in single-account mode for " + m.getSelfNumber()); @@ -117,7 +119,9 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { @Override public void handleCommand( - final Namespace ns, final MultiAccountManager c, final OutputWriter outputWriter + final Namespace ns, + final MultiAccountManager c, + final OutputWriter outputWriter ) throws CommandException { Shutdown.installHandler(); logger.info("Starting daemon in multi-account mode"); diff --git a/src/main/java/org/asamk/signal/commands/DeleteLocalAccountDataCommand.java b/src/main/java/org/asamk/signal/commands/DeleteLocalAccountDataCommand.java index 51c8582d..71f8867c 100644 --- a/src/main/java/org/asamk/signal/commands/DeleteLocalAccountDataCommand.java +++ b/src/main/java/org/asamk/signal/commands/DeleteLocalAccountDataCommand.java @@ -60,7 +60,9 @@ public class DeleteLocalAccountDataCommand implements RegistrationCommand, JsonR @Override public void handleCommand( - Map request, RegistrationManager m, JsonWriter jsonWriter + Map request, + RegistrationManager m, + JsonWriter jsonWriter ) throws CommandException { Namespace commandNamespace = new JsonRpcNamespace(request == null ? Map.of() : request); handleCommand(commandNamespace, m); diff --git a/src/main/java/org/asamk/signal/commands/FinishChangeNumberCommand.java b/src/main/java/org/asamk/signal/commands/FinishChangeNumberCommand.java index c2c18dde..8fce4dbf 100644 --- a/src/main/java/org/asamk/signal/commands/FinishChangeNumberCommand.java +++ b/src/main/java/org/asamk/signal/commands/FinishChangeNumberCommand.java @@ -33,7 +33,9 @@ public class FinishChangeNumberCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var newNumber = ns.getString("number"); final var verificationCode = ns.getString("verification-code"); diff --git a/src/main/java/org/asamk/signal/commands/FinishLinkCommand.java b/src/main/java/org/asamk/signal/commands/FinishLinkCommand.java index a0fa0ab7..c9282616 100644 --- a/src/main/java/org/asamk/signal/commands/FinishLinkCommand.java +++ b/src/main/java/org/asamk/signal/commands/FinishLinkCommand.java @@ -32,7 +32,9 @@ public class FinishLinkCommand implements JsonRpcMultiCommand registered; diff --git a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java index f9e9f513..1ecc5aca 100644 --- a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java @@ -33,7 +33,9 @@ public class JoinGroupCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final GroupInviteLinkUrl linkUrl; var uri = ns.getString("uri"); diff --git a/src/main/java/org/asamk/signal/commands/JsonRpcDispatcherCommand.java b/src/main/java/org/asamk/signal/commands/JsonRpcDispatcherCommand.java index 0eb99351..5ce9daf4 100644 --- a/src/main/java/org/asamk/signal/commands/JsonRpcDispatcherCommand.java +++ b/src/main/java/org/asamk/signal/commands/JsonRpcDispatcherCommand.java @@ -59,7 +59,9 @@ public class JsonRpcDispatcherCommand implements LocalCommand, MultiLocalCommand @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { Shutdown.installHandler(); final var receiveMode = ns.get("receive-mode"); @@ -79,7 +81,9 @@ public class JsonRpcDispatcherCommand implements LocalCommand, MultiLocalCommand @Override public void handleCommand( - final Namespace ns, final MultiAccountManager c, final OutputWriter outputWriter + final Namespace ns, + final MultiAccountManager c, + final OutputWriter outputWriter ) throws CommandException { Shutdown.installHandler(); final var receiveMode = ns.get("receive-mode"); diff --git a/src/main/java/org/asamk/signal/commands/JsonRpcLocalCommand.java b/src/main/java/org/asamk/signal/commands/JsonRpcLocalCommand.java index d87b97cd..212c3e87 100644 --- a/src/main/java/org/asamk/signal/commands/JsonRpcLocalCommand.java +++ b/src/main/java/org/asamk/signal/commands/JsonRpcLocalCommand.java @@ -18,9 +18,7 @@ public interface JsonRpcLocalCommand extends JsonRpcSingleCommand() {}; } - default void handleCommand( - Map request, Manager m, JsonWriter jsonWriter - ) throws CommandException { + default void handleCommand(Map request, Manager m, JsonWriter jsonWriter) throws CommandException { Namespace commandNamespace = new JsonRpcNamespace(request == null ? Map.of() : request); handleCommand(commandNamespace, m, jsonWriter); } diff --git a/src/main/java/org/asamk/signal/commands/JsonRpcMultiLocalCommand.java b/src/main/java/org/asamk/signal/commands/JsonRpcMultiLocalCommand.java index 6082f6c5..f14edeff 100644 --- a/src/main/java/org/asamk/signal/commands/JsonRpcMultiLocalCommand.java +++ b/src/main/java/org/asamk/signal/commands/JsonRpcMultiLocalCommand.java @@ -19,7 +19,9 @@ public interface JsonRpcMultiLocalCommand extends JsonRpcMultiCommand request, MultiAccountManager c, JsonWriter jsonWriter + Map request, + MultiAccountManager c, + JsonWriter jsonWriter ) throws CommandException { Namespace commandNamespace = new JsonRpcNamespace(request == null ? Map.of() : request); handleCommand(commandNamespace, c, jsonWriter); diff --git a/src/main/java/org/asamk/signal/commands/LinkCommand.java b/src/main/java/org/asamk/signal/commands/LinkCommand.java index ed0cc0ed..7c29a3a9 100644 --- a/src/main/java/org/asamk/signal/commands/LinkCommand.java +++ b/src/main/java/org/asamk/signal/commands/LinkCommand.java @@ -33,7 +33,9 @@ public class LinkCommand implements ProvisioningCommand { @Override public void handleCommand( - final Namespace ns, final ProvisioningManager m, final OutputWriter outputWriter + final Namespace ns, + final ProvisioningManager m, + final OutputWriter outputWriter ) throws CommandException { final var writer = (PlainTextWriter) outputWriter; diff --git a/src/main/java/org/asamk/signal/commands/ListAccountsCommand.java b/src/main/java/org/asamk/signal/commands/ListAccountsCommand.java index b19a1686..b9542934 100644 --- a/src/main/java/org/asamk/signal/commands/ListAccountsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListAccountsCommand.java @@ -23,7 +23,9 @@ public class ListAccountsCommand implements JsonRpcMultiLocalCommand { @Override public void handleCommand( - final Namespace ns, final MultiAccountManager c, final OutputWriter outputWriter + final Namespace ns, + final MultiAccountManager c, + final OutputWriter outputWriter ) throws CommandException { final var accountNumbers = c.getAccountNumbers(); switch (outputWriter) { diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index 3865d829..81a1bcb1 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -47,7 +47,9 @@ public class ListContactsCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var allRecipients = Boolean.TRUE.equals(ns.getBoolean("all-recipients")); final var blocked = ns.getBoolean("blocked"); diff --git a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java index c4ae6e72..e9b03a17 100644 --- a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java @@ -33,7 +33,9 @@ public class ListDevicesCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { List devices; try { diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index debe4ad0..62bb4f14 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -48,9 +48,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { .collect(Collectors.toSet()); } - private static void printGroupPlainText( - PlainTextWriter writer, Group group, boolean detailed - ) { + private static void printGroupPlainText(PlainTextWriter writer, Group group, boolean detailed) { if (detailed) { final var groupInviteLink = group.groupInviteLinkUrl(); @@ -79,7 +77,9 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { var groups = m.getGroups(); diff --git a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java index 6b49a812..70b939d6 100644 --- a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java @@ -46,7 +46,9 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { var number = ns.getString("number"); diff --git a/src/main/java/org/asamk/signal/commands/ListStickerPacksCommand.java b/src/main/java/org/asamk/signal/commands/ListStickerPacksCommand.java index 233eb4b5..db081211 100644 --- a/src/main/java/org/asamk/signal/commands/ListStickerPacksCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListStickerPacksCommand.java @@ -27,7 +27,9 @@ public class ListStickerPacksCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager c, final OutputWriter outputWriter + final Namespace ns, + final Manager c, + final OutputWriter outputWriter ) throws CommandException { final var stickerPacks = c.getStickerPacks(); switch (outputWriter) { diff --git a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java index f1b8ef5b..346398a5 100644 --- a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java @@ -44,7 +44,9 @@ public class QuitGroupCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var groupId = CommandUtil.getGroupId(ns.getString("group-id")); diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index ba126f7e..37da35e1 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -66,7 +66,9 @@ public class ReceiveCommand implements LocalCommand, JsonRpcSingleCommandgetList("recipient"); diff --git a/src/main/java/org/asamk/signal/commands/RemoveContactCommand.java b/src/main/java/org/asamk/signal/commands/RemoveContactCommand.java index aca52778..253cb7ad 100644 --- a/src/main/java/org/asamk/signal/commands/RemoveContactCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemoveContactCommand.java @@ -31,7 +31,9 @@ public class RemoveContactCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { var recipientString = ns.getString("recipient"); var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber()); diff --git a/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java b/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java index 68a53616..eab0a6be 100644 --- a/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java @@ -30,7 +30,9 @@ public class RemoveDeviceCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { try { final var deviceId = ns.getInt("device-id"); diff --git a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java index 8085bc65..bc55f9d2 100644 --- a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java @@ -27,7 +27,9 @@ public class RemovePinCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { try { m.setRegistrationLockPin(Optional.empty()); diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 004ba506..9784171a 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -108,7 +108,9 @@ public class SendCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var notifySelf = Boolean.TRUE.equals(ns.getBoolean("notify-self")); final var isNoteToSelf = Boolean.TRUE.equals(ns.getBoolean("note-to-self")); @@ -257,7 +259,8 @@ public class SendCommand implements JsonRpcLocalCommand { } private List parseMentions( - final String selfNumber, final List mentionStrings + final String selfNumber, + final List mentionStrings ) throws UserErrorException { final var mentionPattern = Pattern.compile("(\\d+):(\\d+):(.+)"); final var mentions = new ArrayList(); diff --git a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java index a5128937..a67950c1 100644 --- a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java @@ -24,7 +24,9 @@ public class SendContactsCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { try { m.sendContacts(); diff --git a/src/main/java/org/asamk/signal/commands/SendMessageRequestResponseCommand.java b/src/main/java/org/asamk/signal/commands/SendMessageRequestResponseCommand.java index 53cd3ea7..732fd749 100644 --- a/src/main/java/org/asamk/signal/commands/SendMessageRequestResponseCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendMessageRequestResponseCommand.java @@ -31,7 +31,9 @@ public class SendMessageRequestResponseCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var recipientStrings = ns.getList("recipient"); final var groupIdStrings = ns.getList("group-id"); diff --git a/src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java b/src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java index 77181e3e..0814658e 100644 --- a/src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java @@ -31,7 +31,9 @@ public class SendPaymentNotificationCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var recipientString = ns.getString("recipient"); final var recipientIdentifier = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber()); diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index 56fa81f2..da877d76 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -53,7 +53,9 @@ public class SendReactionCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var isNoteToSelf = Boolean.TRUE.equals(ns.getBoolean("note-to-self")); final var recipientStrings = ns.getList("recipient"); diff --git a/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java b/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java index adae7c4d..93c8a564 100644 --- a/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java @@ -35,7 +35,9 @@ public class SendReceiptCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var recipientString = ns.getString("recipient"); final var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber()); diff --git a/src/main/java/org/asamk/signal/commands/SendSyncRequestCommand.java b/src/main/java/org/asamk/signal/commands/SendSyncRequestCommand.java index 1f35ba2b..eda364c0 100644 --- a/src/main/java/org/asamk/signal/commands/SendSyncRequestCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendSyncRequestCommand.java @@ -24,7 +24,9 @@ public class SendSyncRequestCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { try { m.requestAllSyncData(); diff --git a/src/main/java/org/asamk/signal/commands/SendTypingCommand.java b/src/main/java/org/asamk/signal/commands/SendTypingCommand.java index 9293ec6a..a9e699cc 100644 --- a/src/main/java/org/asamk/signal/commands/SendTypingCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendTypingCommand.java @@ -38,7 +38,9 @@ public class SendTypingCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var recipientStrings = ns.getList("recipient"); final var groupIdStrings = ns.getList("group-id"); diff --git a/src/main/java/org/asamk/signal/commands/SetPinCommand.java b/src/main/java/org/asamk/signal/commands/SetPinCommand.java index f6b5f7ad..4d4a458f 100644 --- a/src/main/java/org/asamk/signal/commands/SetPinCommand.java +++ b/src/main/java/org/asamk/signal/commands/SetPinCommand.java @@ -29,7 +29,9 @@ public class SetPinCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { try { var registrationLockPin = ns.getString("pin"); diff --git a/src/main/java/org/asamk/signal/commands/StartChangeNumberCommand.java b/src/main/java/org/asamk/signal/commands/StartChangeNumberCommand.java index 84fcf527..c75b3077 100644 --- a/src/main/java/org/asamk/signal/commands/StartChangeNumberCommand.java +++ b/src/main/java/org/asamk/signal/commands/StartChangeNumberCommand.java @@ -39,7 +39,9 @@ public class StartChangeNumberCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var newNumber = ns.getString("number"); final var voiceVerification = Boolean.TRUE.equals(ns.getBoolean("voice")); diff --git a/src/main/java/org/asamk/signal/commands/StartLinkCommand.java b/src/main/java/org/asamk/signal/commands/StartLinkCommand.java index ac269e95..895b5dae 100644 --- a/src/main/java/org/asamk/signal/commands/StartLinkCommand.java +++ b/src/main/java/org/asamk/signal/commands/StartLinkCommand.java @@ -23,7 +23,9 @@ public class StartLinkCommand implements JsonRpcMultiCommand { @Override public void handleCommand( - final Void request, final MultiAccountManager m, final JsonWriter jsonWriter + final Void request, + final MultiAccountManager m, + final JsonWriter jsonWriter ) throws CommandException { final URI deviceLinkUri; try { diff --git a/src/main/java/org/asamk/signal/commands/TrustCommand.java b/src/main/java/org/asamk/signal/commands/TrustCommand.java index a130c03c..7f18507a 100644 --- a/src/main/java/org/asamk/signal/commands/TrustCommand.java +++ b/src/main/java/org/asamk/signal/commands/TrustCommand.java @@ -33,7 +33,9 @@ public class TrustCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { var recipientString = ns.getString("recipient"); var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber()); diff --git a/src/main/java/org/asamk/signal/commands/UnblockCommand.java b/src/main/java/org/asamk/signal/commands/UnblockCommand.java index 1299d0db..a6b5e6b3 100644 --- a/src/main/java/org/asamk/signal/commands/UnblockCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnblockCommand.java @@ -35,7 +35,9 @@ public class UnblockCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var contacts = ns.getList("recipient"); final var recipients = CommandUtil.getSingleRecipientIdentifiers(contacts, m.getSelfNumber()); diff --git a/src/main/java/org/asamk/signal/commands/UnregisterCommand.java b/src/main/java/org/asamk/signal/commands/UnregisterCommand.java index 83694953..e044f691 100644 --- a/src/main/java/org/asamk/signal/commands/UnregisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnregisterCommand.java @@ -28,7 +28,9 @@ public class UnregisterCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { try { if (Boolean.TRUE.equals(ns.getBoolean("delete-account"))) { diff --git a/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java b/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java index a750bc88..9c8c18b8 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java @@ -47,7 +47,9 @@ public class UpdateAccountCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var deviceName = ns.getString("device-name"); final var unrestrictedUnidentifiedSender = ns.getBoolean("unrestricted-unidentified-sender"); diff --git a/src/main/java/org/asamk/signal/commands/UpdateConfigurationCommand.java b/src/main/java/org/asamk/signal/commands/UpdateConfigurationCommand.java index d1feff8a..03882e25 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateConfigurationCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateConfigurationCommand.java @@ -38,7 +38,9 @@ public class UpdateConfigurationCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var readReceipts = ns.getBoolean("read-receipts"); final var unidentifiedDeliveryIndicators = ns.getBoolean("unidentified-delivery-indicators"); diff --git a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java index 7ff91e9b..815c15da 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java @@ -33,7 +33,9 @@ public class UpdateContactCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { var recipientString = ns.getString("recipient"); var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber()); diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index f29f0732..1231d3d4 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -102,7 +102,9 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { final var groupIdString = ns.getString("group-id"); var groupId = CommandUtil.getGroupId(groupIdString); @@ -180,7 +182,9 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand { } private void outputResult( - final OutputWriter outputWriter, final SendGroupMessageResults results, final GroupId groupId + final OutputWriter outputWriter, + final SendGroupMessageResults results, + final GroupId groupId ) { switch (outputWriter) { case PlainTextWriter writer -> { diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index cea2da0d..b122f58c 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -36,7 +36,9 @@ public class UpdateProfileCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { var givenName = ns.getString("given-name"); var familyName = ns.getString("family-name"); diff --git a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java index b4c09b41..710f4d35 100644 --- a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java +++ b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java @@ -36,7 +36,9 @@ public class UploadStickerPackCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { var path = new File(ns.getString("path")); diff --git a/src/main/java/org/asamk/signal/commands/VerifyCommand.java b/src/main/java/org/asamk/signal/commands/VerifyCommand.java index 54d05a4d..a8543c5a 100644 --- a/src/main/java/org/asamk/signal/commands/VerifyCommand.java +++ b/src/main/java/org/asamk/signal/commands/VerifyCommand.java @@ -55,13 +55,17 @@ public class VerifyCommand implements RegistrationCommand, JsonRpcRegistrationCo @Override public void handleCommand( - final VerifyParams request, final RegistrationManager m, final JsonWriter jsonWriter + final VerifyParams request, + final RegistrationManager m, + final JsonWriter jsonWriter ) throws CommandException { verify(m, request.verificationCode(), request.pin()); } private void verify( - final RegistrationManager m, final String verificationCode, final String pin + final RegistrationManager m, + final String verificationCode, + final String pin ) throws UserErrorException, IOErrorException { try { m.verifyAccount(verificationCode, pin); diff --git a/src/main/java/org/asamk/signal/commands/VersionCommand.java b/src/main/java/org/asamk/signal/commands/VersionCommand.java index 01f1a2d7..498e5e71 100644 --- a/src/main/java/org/asamk/signal/commands/VersionCommand.java +++ b/src/main/java/org/asamk/signal/commands/VersionCommand.java @@ -35,14 +35,18 @@ public class VersionCommand implements JsonRpcLocalCommand, JsonRpcMultiLocalCom @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { outputVersion(outputWriter); } @Override public void handleCommand( - final Namespace ns, final MultiAccountManager c, final OutputWriter outputWriter + final Namespace ns, + final MultiAccountManager c, + final OutputWriter outputWriter ) throws CommandException { outputVersion(outputWriter); } diff --git a/src/main/java/org/asamk/signal/dbus/DbusCommandHandler.java b/src/main/java/org/asamk/signal/dbus/DbusCommandHandler.java index 5e272895..546a433f 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusCommandHandler.java +++ b/src/main/java/org/asamk/signal/dbus/DbusCommandHandler.java @@ -87,7 +87,8 @@ public class DbusCommandHandler { } private static String tryGetSingleAccountObjectPath( - final DBusConnection dBusConn, final String busname + final DBusConnection dBusConn, + final String busname ) throws DBusException, CommandException { var control = dBusConn.getRemoteObject(busname, DbusConfig.getObjectPath(), SignalControl.class); try { diff --git a/src/main/java/org/asamk/signal/dbus/DbusHandler.java b/src/main/java/org/asamk/signal/dbus/DbusHandler.java index e71d7281..b6799de7 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusHandler.java +++ b/src/main/java/org/asamk/signal/dbus/DbusHandler.java @@ -28,7 +28,10 @@ public class DbusHandler implements AutoCloseable { private final boolean noReceiveOnStart; public DbusHandler( - final boolean isDbusSystem, final String busname, final Manager m, final boolean noReceiveOnStart + final boolean isDbusSystem, + final String busname, + final Manager m, + final boolean noReceiveOnStart ) { this.isDbusSystem = isDbusSystem; this.dbusRunner = (connection) -> { diff --git a/src/main/java/org/asamk/signal/dbus/DbusInterfacePropertiesHandler.java b/src/main/java/org/asamk/signal/dbus/DbusInterfacePropertiesHandler.java index d3c2ca83..73cfab67 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusInterfacePropertiesHandler.java +++ b/src/main/java/org/asamk/signal/dbus/DbusInterfacePropertiesHandler.java @@ -12,9 +12,7 @@ public class DbusInterfacePropertiesHandler { private final String interfaceName; private final List> properties; - public DbusInterfacePropertiesHandler( - final String interfaceName, final List> properties - ) { + public DbusInterfacePropertiesHandler(final String interfaceName, final List> properties) { this.interfaceName = interfaceName; this.properties = properties; } diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index bd5bdfb2..159407e1 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -201,14 +201,18 @@ public class DbusManagerImpl implements Manager { @Override public void startChangeNumber( - final String newNumber, final boolean voiceVerification, final String captcha + final String newNumber, + final boolean voiceVerification, + final String captcha ) throws RateLimitException, IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException { throw new UnsupportedOperationException(); } @Override public void finishChangeNumber( - final String newNumber, final String verificationCode, final String pin + final String newNumber, + final String verificationCode, + final String pin ) throws IncorrectPinException, PinLockedException, IOException { throw new UnsupportedOperationException(); } @@ -270,7 +274,8 @@ public class DbusManagerImpl implements Manager { @Override public SendGroupMessageResults quitGroup( - final GroupId groupId, final Set groupAdmins + final GroupId groupId, + final Set groupAdmins ) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException { if (!groupAdmins.isEmpty()) { throw new UnsupportedOperationException(); @@ -296,7 +301,9 @@ public class DbusManagerImpl implements Manager { @Override public Pair createGroup( - final String name, final Set members, final String avatarFile + final String name, + final Set members, + final String avatarFile ) throws IOException, AttachmentInvalidException { final var newGroupId = signal.createGroup(emptyIfNull(name), members.stream().map(RecipientIdentifier.Single::getIdentifier).toList(), @@ -306,7 +313,8 @@ public class DbusManagerImpl implements Manager { @Override public SendGroupMessageResults updateGroup( - final GroupId groupId, final UpdateGroup updateGroup + final GroupId groupId, + final UpdateGroup updateGroup ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException, GroupSendingNotAllowedException { final var group = getRemoteObject(signal.getGroup(groupId.serialize()), Signal.Group.class); if (updateGroup.getName() != null) { @@ -380,7 +388,8 @@ public class DbusManagerImpl implements Manager { @Override public SendMessageResults sendTypingMessage( - final TypingAction action, final Set recipients + final TypingAction action, + final Set recipients ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { return handleMessage(recipients, numbers -> { numbers.forEach(n -> signal.sendTyping(n, action == TypingAction.STOP)); @@ -395,24 +404,22 @@ public class DbusManagerImpl implements Manager { } @Override - public SendMessageResults sendReadReceipt( - final RecipientIdentifier.Single sender, final List messageIds - ) { + public SendMessageResults sendReadReceipt(final RecipientIdentifier.Single sender, final List messageIds) { signal.sendReadReceipt(sender.getIdentifier(), messageIds); return new SendMessageResults(0, Map.of()); } @Override - public SendMessageResults sendViewedReceipt( - final RecipientIdentifier.Single sender, final List messageIds - ) { + public SendMessageResults sendViewedReceipt(final RecipientIdentifier.Single sender, final List messageIds) { signal.sendViewedReceipt(sender.getIdentifier(), messageIds); return new SendMessageResults(0, Map.of()); } @Override public SendMessageResults sendMessage( - final Message message, final Set recipients, final boolean notifySelf + final Message message, + final Set recipients, + final boolean notifySelf ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { return handleMessage(recipients, numbers -> signal.sendMessage(message.messageText(), message.attachments(), numbers), @@ -422,14 +429,17 @@ public class DbusManagerImpl implements Manager { @Override public SendMessageResults sendEditMessage( - final Message message, final Set recipients, final long editTargetTimestamp + final Message message, + final Set recipients, + final long editTargetTimestamp ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException { throw new UnsupportedOperationException(); } @Override public SendMessageResults sendRemoteDeleteMessage( - final long targetSentTimestamp, final Set recipients + final long targetSentTimestamp, + final Set recipients ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { return handleMessage(recipients, numbers -> signal.sendRemoteDeleteMessage(targetSentTimestamp, numbers), @@ -466,7 +476,9 @@ public class DbusManagerImpl implements Manager { @Override public SendMessageResults sendPaymentNotificationMessage( - final byte[] receipt, final String note, final RecipientIdentifier.Single recipient + final byte[] receipt, + final String note, + final RecipientIdentifier.Single recipient ) throws IOException { final var timestamp = signal.sendPaymentNotification(receipt, note, recipient.getIdentifier()); return new SendMessageResults(timestamp, Map.of()); @@ -502,14 +514,17 @@ public class DbusManagerImpl implements Manager { @Override public void setContactName( - final RecipientIdentifier.Single recipient, final String givenName, final String familyName + final RecipientIdentifier.Single recipient, + final String givenName, + final String familyName ) throws NotPrimaryDeviceException { signal.setContactName(recipient.getIdentifier(), givenName); } @Override public void setContactsBlocked( - final Collection recipients, final boolean blocked + final Collection recipients, + final boolean blocked ) throws NotPrimaryDeviceException, IOException { for (final var recipient : recipients) { signal.setContactBlocked(recipient.getIdentifier(), blocked); @@ -518,7 +533,8 @@ public class DbusManagerImpl implements Manager { @Override public void setGroupsBlocked( - final Collection groupIds, final boolean blocked + final Collection groupIds, + final boolean blocked ) throws GroupNotFoundException, IOException { for (final var groupId : groupIds) { setGroupProperty(groupId, "IsBlocked", blocked); @@ -532,7 +548,8 @@ public class DbusManagerImpl implements Manager { @Override public void setExpirationTimer( - final RecipientIdentifier.Single recipient, final int messageExpirationTimer + final RecipientIdentifier.Single recipient, + final int messageExpirationTimer ) throws IOException { signal.setExpirationTimer(recipient.getIdentifier(), messageExpirationTimer); } @@ -597,7 +614,9 @@ public class DbusManagerImpl implements Manager { @Override public void receiveMessages( - Optional timeout, Optional maxMessages, ReceiveMessageHandler handler + Optional timeout, + Optional maxMessages, + ReceiveMessageHandler handler ) throws IOException, AlreadyReceivingException { if (receiveThread != null) { throw new AlreadyReceivingException("Already receiving message."); @@ -784,7 +803,8 @@ public class DbusManagerImpl implements Manager { @Override public boolean trustIdentityVerified( - final RecipientIdentifier.Single recipient, final IdentityVerificationCode verificationCode + final RecipientIdentifier.Single recipient, + final IdentityVerificationCode verificationCode ) { throw new UnsupportedOperationException(); } @@ -1124,9 +1144,7 @@ public class DbusManagerImpl implements Manager { } @SuppressWarnings("unchecked") - private T getValue( - final Map> stringVariantMap, final String field - ) { + private T getValue(final Map> stringVariantMap, final String field) { return (T) stringVariantMap.get(field).getValue(); } } diff --git a/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java index 27e28779..2b9a0a77 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java @@ -34,7 +34,9 @@ public class DbusMultiAccountManagerImpl implements MultiAccountManager { private final String busname; public DbusMultiAccountManagerImpl( - final SignalControl signalControl, DBusConnection connection, final String busname + final SignalControl signalControl, + DBusConnection connection, + final String busname ) { this.signalControl = signalControl; this.connection = connection; diff --git a/src/main/java/org/asamk/signal/dbus/DbusProvisioningManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusProvisioningManagerImpl.java index 2e026c8a..e3cf4c12 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusProvisioningManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusProvisioningManagerImpl.java @@ -27,7 +27,9 @@ public class DbusProvisioningManagerImpl implements ProvisioningManager { } public DbusProvisioningManagerImpl( - final SignalControl signalControl, DBusConnection connection, URI deviceLinkUri + final SignalControl signalControl, + DBusConnection connection, + URI deviceLinkUri ) { this.signalControl = signalControl; this.connection = connection; diff --git a/src/main/java/org/asamk/signal/dbus/DbusRegistrationManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusRegistrationManagerImpl.java index 788e0c91..0c02c680 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusRegistrationManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusRegistrationManagerImpl.java @@ -27,7 +27,9 @@ public class DbusRegistrationManagerImpl implements RegistrationManager { @Override public void register( - final boolean voiceVerification, final String captcha, final boolean forceRegister + final boolean voiceVerification, + final String captcha, + final boolean forceRegister ) throws IOException, CaptchaRequiredException { if (forceRegister) { throw new UnsupportedOperationException(); @@ -41,7 +43,8 @@ public class DbusRegistrationManagerImpl implements RegistrationManager { @Override public void verifyAccount( - final String verificationCode, final String pin + final String verificationCode, + final String pin ) throws IOException, PinLockedException, IncorrectPinException { if (pin == null) { signalControl.verify(number, verificationCode); diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java index def343a9..cf6999a9 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java @@ -49,14 +49,17 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { @Override public void register( - final String number, final boolean voiceVerification + final String number, + final boolean voiceVerification ) throws Error.Failure, Error.InvalidNumber { registerWithCaptcha(number, voiceVerification, null); } @Override public void registerWithCaptcha( - final String number, final boolean voiceVerification, final String captcha + final String number, + final boolean voiceVerification, + final String captcha ) throws Error.Failure, Error.InvalidNumber { if (!Manager.isValidNumber(number, null)) { throw new SignalControl.Error.InvalidNumber( @@ -86,7 +89,9 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { @Override public void verifyWithPin( - final String number, final String verificationCode, final String pin + final String number, + final String verificationCode, + final String pin ) throws Error.Failure, Error.InvalidNumber { try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) { registrationManager.verifyAccount(verificationCode, pin); diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 1b8b7825..bbae26b5 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -81,7 +81,10 @@ public class DbusSignalImpl implements Signal, AutoCloseable { private static final Logger logger = LoggerFactory.getLogger(DbusSignalImpl.class); public DbusSignalImpl( - final Manager m, DBusConnection connection, final String objectPath, final boolean noReceiveOnStart + final Manager m, + DBusConnection connection, + final String objectPath, + final boolean noReceiveOnStart ) { this.m = m; this.connection = connection; @@ -258,16 +261,12 @@ public class DbusSignalImpl implements Signal, AutoCloseable { } @Override - public long sendRemoteDeleteMessage( - final long targetSentTimestamp, final String recipient - ) { + public long sendRemoteDeleteMessage(final long targetSentTimestamp, final String recipient) { return sendRemoteDeleteMessage(targetSentTimestamp, List.of(recipient)); } @Override - public long sendRemoteDeleteMessage( - final long targetSentTimestamp, final List recipients - ) { + public long sendRemoteDeleteMessage(final long targetSentTimestamp, final List recipients) { try { final var results = m.sendRemoteDeleteMessage(targetSentTimestamp, getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream() @@ -323,7 +322,9 @@ public class DbusSignalImpl implements Signal, AutoCloseable { @Override public long sendPaymentNotification( - final byte[] receipt, final String note, final String recipient + final byte[] receipt, + final String note, + final String recipient ) throws Error.Failure { try { final var results = m.sendPaymentNotificationMessage(receipt, @@ -338,7 +339,8 @@ public class DbusSignalImpl implements Signal, AutoCloseable { @Override public void sendTyping( - final String recipient, final boolean stop + final String recipient, + final boolean stop ) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity { try { final var results = m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START, @@ -355,7 +357,8 @@ public class DbusSignalImpl implements Signal, AutoCloseable { @Override public void sendReadReceipt( - final String recipient, final List messageIds + final String recipient, + final List messageIds ) throws Error.Failure, Error.UntrustedIdentity { final var results = m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), messageIds); checkSendMessageResults(results); @@ -363,7 +366,8 @@ public class DbusSignalImpl implements Signal, AutoCloseable { @Override public void sendViewedReceipt( - final String recipient, final List messageIds + final String recipient, + final List messageIds ) throws Error.Failure, Error.UntrustedIdentity { final var results = m.sendViewedReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), messageIds); checkSendMessageResults(results); @@ -389,7 +393,8 @@ public class DbusSignalImpl implements Signal, AutoCloseable { @Override public long sendNoteToSelfMessage( - final String messageText, final List attachments + final String messageText, + final List attachments ) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity { try { final var message = new Message(messageText, @@ -461,7 +466,8 @@ public class DbusSignalImpl implements Signal, AutoCloseable { @Override public void sendGroupTyping( - final byte[] groupId, final boolean stop + final byte[] groupId, + final boolean stop ) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity { try { final var results = m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START, @@ -475,9 +481,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable { } @Override - public long sendGroupRemoteDeleteMessage( - final long targetSentTimestamp, final byte[] groupId - ) { + public long sendGroupRemoteDeleteMessage(final long targetSentTimestamp, final byte[] groupId) { try { final var results = m.sendRemoteDeleteMessage(targetSentTimestamp, Set.of(getGroupRecipientIdentifier(groupId))); @@ -621,7 +625,9 @@ public class DbusSignalImpl implements Signal, AutoCloseable { @Override public byte[] createGroup( - final String name, final List members, final String avatar + final String name, + final List members, + final String avatar ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber { return updateGroupInternal(new byte[0], name, members, avatar); } @@ -900,7 +906,8 @@ public class DbusSignalImpl implements Signal, AutoCloseable { } private static void checkGroupSendMessageResults( - long timestamp, Collection results + long timestamp, + Collection results ) throws DBusExecutionException { if (results.size() == 1) { checkSendMessageResult(timestamp, results.stream().findFirst().get()); @@ -927,7 +934,8 @@ public class DbusSignalImpl implements Signal, AutoCloseable { } private static Set getSingleRecipientIdentifiers( - final Collection recipientStrings, final String localNumber + final Collection recipientStrings, + final String localNumber ) throws DBusExecutionException { final var identifiers = new HashSet(); for (var recipientString : recipientStrings) { @@ -937,7 +945,8 @@ public class DbusSignalImpl implements Signal, AutoCloseable { } private static RecipientIdentifier.Single getSingleRecipientIdentifier( - final String recipientString, final String localNumber + final String recipientString, + final String localNumber ) throws DBusExecutionException { try { return RecipientIdentifier.Single.fromString(recipientString, localNumber); diff --git a/src/main/java/org/asamk/signal/http/HttpServerHandler.java b/src/main/java/org/asamk/signal/http/HttpServerHandler.java index c9c8fa16..e1780665 100644 --- a/src/main/java/org/asamk/signal/http/HttpServerHandler.java +++ b/src/main/java/org/asamk/signal/http/HttpServerHandler.java @@ -240,7 +240,9 @@ public class HttpServerHandler implements AutoCloseable { } private List> subscribeReceiveHandlers( - final List managers, final ServerSentEventSender sender, Callable unsubscribe + final List managers, + final ServerSentEventSender sender, + Callable unsubscribe ) { return managers.stream().map(m1 -> { final var receiveMessageHandler = new JsonReceiveMessageHandler(m1, s -> { diff --git a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java index 7ba4b307..8ab8ea83 100644 --- a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java +++ b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java @@ -28,9 +28,7 @@ public record JsonMessageEnvelope( @JsonInclude(JsonInclude.Include.NON_NULL) JsonTypingMessage typingMessage ) { - public static JsonMessageEnvelope from( - MessageEnvelope envelope, Throwable exception, Manager m - ) { + public static JsonMessageEnvelope from(MessageEnvelope envelope, Throwable exception, Manager m) { final RecipientAddress sourceAddress; final Integer sourceDevice; if (envelope.sourceAddress().isPresent()) { diff --git a/src/main/java/org/asamk/signal/jsonrpc/JsonRpcRequest.java b/src/main/java/org/asamk/signal/jsonrpc/JsonRpcRequest.java index e90d740c..29866c1a 100644 --- a/src/main/java/org/asamk/signal/jsonrpc/JsonRpcRequest.java +++ b/src/main/java/org/asamk/signal/jsonrpc/JsonRpcRequest.java @@ -38,7 +38,9 @@ public final class JsonRpcRequest extends JsonRpcMessage { private ValueNode id; public static JsonRpcRequest forNotification( - final String method, final ContainerNode params, final ValueNode id + final String method, + final ContainerNode params, + final ValueNode id ) { return new JsonRpcRequest("2.0", method, params, id); } @@ -47,7 +49,10 @@ public final class JsonRpcRequest extends JsonRpcMessage { } private JsonRpcRequest( - final String jsonrpc, final String method, final ContainerNode params, final ValueNode id + final String jsonrpc, + final String method, + final ContainerNode params, + final ValueNode id ) { this.jsonrpc = jsonrpc; this.method = method; diff --git a/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcCommandHandler.java b/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcCommandHandler.java index 96456a4e..48eb4a45 100644 --- a/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcCommandHandler.java +++ b/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcCommandHandler.java @@ -56,7 +56,9 @@ public class SignalJsonRpcCommandHandler { } public JsonNode handleRequest( - final ObjectMapper objectMapper, final String method, ContainerNode params + final ObjectMapper objectMapper, + final String method, + ContainerNode params ) throws JsonRpcException { var command = getCommand(method); if (c != null) { @@ -197,7 +199,9 @@ public class SignalJsonRpcCommandHandler { } private JsonNode runCommand( - final ObjectMapper objectMapper, final ContainerNode params, final CommandRunner command + final ObjectMapper objectMapper, + final ContainerNode params, + final CommandRunner command ) throws JsonRpcException { final Object[] result = {null}; final JsonWriter commandJsonWriter = s -> { diff --git a/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java b/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java index c8b4bc15..5d3fa261 100644 --- a/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java +++ b/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java @@ -43,7 +43,9 @@ public class SignalJsonRpcDispatcherHandler { private SignalJsonRpcCommandHandler commandHandler; public SignalJsonRpcDispatcherHandler( - final JsonWriter jsonWriter, final Supplier lineSupplier, final boolean noReceiveOnStart + final JsonWriter jsonWriter, + final Supplier lineSupplier, + final boolean noReceiveOnStart ) { this.noReceiveOnStart = noReceiveOnStart; this.objectMapper = Util.createJsonObjectMapper(); @@ -167,7 +169,9 @@ public class SignalJsonRpcDispatcherHandler { @Override public void handleCommand( - final Void request, final Manager m, final JsonWriter jsonWriter + final Void request, + final Manager m, + final JsonWriter jsonWriter ) throws CommandException { final var subscriptionId = subscribeReceive(m, false); jsonWriter.write(subscriptionId); @@ -175,7 +179,9 @@ public class SignalJsonRpcDispatcherHandler { @Override public void handleCommand( - final Void request, final MultiAccountManager c, final JsonWriter jsonWriter + final Void request, + final MultiAccountManager c, + final JsonWriter jsonWriter ) throws CommandException { final var subscriptionId = subscribeReceive(c.getManagers(), false); jsonWriter.write(subscriptionId); @@ -196,7 +202,9 @@ public class SignalJsonRpcDispatcherHandler { @Override public void handleCommand( - final JsonNode request, final Manager m, final JsonWriter jsonWriter + final JsonNode request, + final Manager m, + final JsonWriter jsonWriter ) throws CommandException { final var subscriptionId = getSubscriptionId(request); if (subscriptionId == null) { @@ -210,7 +218,9 @@ public class SignalJsonRpcDispatcherHandler { @Override public void handleCommand( - final JsonNode request, final MultiAccountManager c, final JsonWriter jsonWriter + final JsonNode request, + final MultiAccountManager c, + final JsonWriter jsonWriter ) throws CommandException { final var subscriptionId = getSubscriptionId(request); if (subscriptionId == null) { diff --git a/src/main/java/org/asamk/signal/jsonrpc/SocketHandler.java b/src/main/java/org/asamk/signal/jsonrpc/SocketHandler.java index dc620bc8..2e0d50e4 100644 --- a/src/main/java/org/asamk/signal/jsonrpc/SocketHandler.java +++ b/src/main/java/org/asamk/signal/jsonrpc/SocketHandler.java @@ -39,7 +39,9 @@ public class SocketHandler implements AutoCloseable { } public SocketHandler( - final ServerSocketChannel serverChannel, final MultiAccountManager c, final boolean noReceiveOnStart + final ServerSocketChannel serverChannel, + final MultiAccountManager c, + final boolean noReceiveOnStart ) { this.serverChannel = serverChannel; this.socketHandler = channel -> getSignalJsonRpcDispatcherHandler(channel).handleConnection(c); diff --git a/src/main/java/org/asamk/signal/logging/LogConfigurator.java b/src/main/java/org/asamk/signal/logging/LogConfigurator.java index f6e23b70..2ee7b5f0 100644 --- a/src/main/java/org/asamk/signal/logging/LogConfigurator.java +++ b/src/main/java/org/asamk/signal/logging/LogConfigurator.java @@ -83,7 +83,8 @@ public class LogConfigurator extends ContextAwareBase implements Configurator { } private ConsoleAppender createLoggingConsoleAppender( - final LoggerContext lc, final LayoutWrappingEncoder layoutEncoder + final LoggerContext lc, + final LayoutWrappingEncoder layoutEncoder ) { return new ConsoleAppender<>() {{ setContext(lc); @@ -95,7 +96,8 @@ public class LogConfigurator extends ContextAwareBase implements Configurator { } private FileAppender createLoggingFileAppender( - final LoggerContext lc, final LayoutWrappingEncoder layoutEncoder + final LoggerContext lc, + final LayoutWrappingEncoder layoutEncoder ) { return new FileAppender<>() {{ setContext(lc); diff --git a/src/main/java/org/asamk/signal/logging/Scrubber.java b/src/main/java/org/asamk/signal/logging/Scrubber.java index 6078c6ba..9d796a64 100644 --- a/src/main/java/org/asamk/signal/logging/Scrubber.java +++ b/src/main/java/org/asamk/signal/logging/Scrubber.java @@ -249,9 +249,7 @@ public final class Scrubber { return scrub(in, IPV4_PATTERN, (matcher, output) -> output.append(IPV4_CENSOR)); } - private static CharSequence scrub( - CharSequence in, Pattern pattern, ProcessMatch processMatch - ) { + private static CharSequence scrub(CharSequence in, Pattern pattern, ProcessMatch processMatch) { final StringBuilder output = new StringBuilder(in.length()); final Matcher matcher = pattern.matcher(in); diff --git a/src/main/java/org/asamk/signal/util/CommandUtil.java b/src/main/java/org/asamk/signal/util/CommandUtil.java index 039ac2a7..61f5013e 100644 --- a/src/main/java/org/asamk/signal/util/CommandUtil.java +++ b/src/main/java/org/asamk/signal/util/CommandUtil.java @@ -84,7 +84,8 @@ public class CommandUtil { } public static Set getSingleRecipientIdentifiers( - final Collection recipientStrings, final String localNumber + final Collection recipientStrings, + final String localNumber ) throws UserErrorException { if (recipientStrings == null) { return Set.of(); @@ -97,7 +98,8 @@ public class CommandUtil { } public static RecipientIdentifier.Single getSingleRecipientIdentifier( - final String recipientString, final String localNumber + final String recipientString, + final String localNumber ) throws UserErrorException { try { return RecipientIdentifier.Single.fromString(recipientString, localNumber); diff --git a/src/main/java/org/asamk/signal/util/SendMessageResultUtils.java b/src/main/java/org/asamk/signal/util/SendMessageResultUtils.java index 76932402..a23e3c54 100644 --- a/src/main/java/org/asamk/signal/util/SendMessageResultUtils.java +++ b/src/main/java/org/asamk/signal/util/SendMessageResultUtils.java @@ -41,7 +41,8 @@ public class SendMessageResultUtils { } public static void outputResult( - final OutputWriter outputWriter, final SendMessageResults sendMessageResults + final OutputWriter outputWriter, + final SendMessageResults sendMessageResults ) throws CommandException { switch (outputWriter) { case PlainTextWriter writer -> { From ff6cb5262ad3d987bd113fac18afd8722fca38a4 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 23 Nov 2024 22:29:01 +0100 Subject: [PATCH 005/144] Update libsignal-service Support for storage encryption v2 and account entropy pool Fixes #1632 --- graalvm-config-dir/reflect-config.json | 81 +++- gradle/libs.versions.toml | 2 +- .../signal/manager/api/MessageEnvelope.java | 11 +- .../org/asamk/signal/manager/api/Profile.java | 3 +- .../signal/manager/config/LiveConfig.java | 5 +- .../signal/manager/config/ServiceConfig.java | 3 +- .../signal/manager/config/StagingConfig.java | 5 +- .../signal/manager/helper/AccountHelper.java | 40 +- .../signal/manager/helper/GroupV2Helper.java | 4 +- .../helper/IncomingMessageHandler.java | 29 +- .../signal/manager/helper/PreKeyHelper.java | 2 +- .../signal/manager/helper/ProfileHelper.java | 19 +- .../manager/helper/RecipientHelper.java | 1 - .../signal/manager/helper/StorageHelper.java | 365 +++++++++++------- .../signal/manager/helper/SyncHelper.java | 16 +- .../manager/internal/SignalDependencies.java | 18 + .../signal/manager/jobs/SyncStorageJob.java | 16 +- .../signal/manager/storage/SignalAccount.java | 89 ++++- .../syncStorage/AccountRecordProcessor.java | 183 ++++----- .../syncStorage/ContactRecordProcessor.java | 323 ++++++++-------- .../syncStorage/GroupV1RecordProcessor.java | 54 ++- .../syncStorage/GroupV2RecordProcessor.java | 78 ++-- .../syncStorage/StorageSyncModels.java | 133 ++++--- .../syncStorage/StorageSyncValidations.java | 52 +-- .../syncStorage/WriteOperationResult.java | 4 +- .../asamk/signal/manager/util/KeyUtils.java | 5 + .../manager/util/NumberVerificationUtils.java | 2 +- .../signal/manager/util/ProfileUtils.java | 3 + .../org/asamk/signal/manager/util/Utils.java | 45 +++ 29 files changed, 952 insertions(+), 639 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index abe02ac4..230ede2f 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -124,6 +124,13 @@ "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.squareup.wire.Message", + "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] +}, +{ + "name":"com.squareup.wire.ProtoAdapter" +}, { "name":"com.squareup.wire.internal.ImmutableList", "allDeclaredFields":true, @@ -209,9 +216,14 @@ { "name":"java.io.FilePermission" }, +{ + "name":"java.io.OutputStream" +}, { "name":"java.io.Serializable", - "allDeclaredMethods":true + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredClasses":true }, { "name":"java.lang.Boolean", @@ -577,6 +589,9 @@ { "name":"kotlin.String" }, +{ + "name":"kotlin.Unit" +}, { "name":"kotlin.collections.AbstractCollection", "allDeclaredFields":true, @@ -629,6 +644,9 @@ { "name":"long[]" }, +{ + "name":"okio.BufferedSink" +}, { "name":"okio.ByteString" }, @@ -1025,7 +1043,7 @@ "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "methods":[{"name":"groupId","parameterTypes":[] }, {"name":"type","parameterTypes":[] }] + "methods":[{"name":"groupId","parameterTypes":[] }, {"name":"groupName","parameterTypes":[] }, {"name":"revision","parameterTypes":[] }, {"name":"type","parameterTypes":[] }] }, { "name":"org.asamk.signal.json.JsonMention", @@ -1247,7 +1265,7 @@ "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":["int","long","java.lang.String","boolean","java.lang.String","java.lang.String","java.lang.String","int","boolean","java.lang.String","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"","parameterTypes":["int","java.lang.String","boolean","java.lang.String","java.lang.String","java.lang.String","int","boolean","java.lang.String","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"","parameterTypes":["int","java.lang.String","boolean","java.lang.String","java.lang.String","java.lang.String","int","boolean","java.lang.String","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"aciAccountData","parameterTypes":[] }, {"name":"deviceId","parameterTypes":[] }, {"name":"encryptedDeviceName","parameterTypes":[] }, {"name":"isMultiDevice","parameterTypes":[] }, {"name":"number","parameterTypes":[] }, {"name":"password","parameterTypes":[] }, {"name":"pinMasterKey","parameterTypes":[] }, {"name":"pniAccountData","parameterTypes":[] }, {"name":"profileKey","parameterTypes":[] }, {"name":"registered","parameterTypes":[] }, {"name":"registrationLockPin","parameterTypes":[] }, {"name":"serviceEnvironment","parameterTypes":[] }, {"name":"storageKey","parameterTypes":[] }, {"name":"timestamp","parameterTypes":[] }, {"name":"username","parameterTypes":[] }, {"name":"usernameLinkEntropy","parameterTypes":[] }, {"name":"usernameLinkServerId","parameterTypes":[] }, {"name":"version","parameterTypes":[] }] + "methods":[{"name":"","parameterTypes":["int","long","java.lang.String","boolean","java.lang.String","java.lang.String","java.lang.String","int","boolean","java.lang.String","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"","parameterTypes":["int","long","java.lang.String","boolean","java.lang.String","java.lang.String","java.lang.String","int","boolean","java.lang.String","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"","parameterTypes":["int","java.lang.String","boolean","java.lang.String","java.lang.String","java.lang.String","int","boolean","java.lang.String","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"","parameterTypes":["int","java.lang.String","boolean","java.lang.String","java.lang.String","java.lang.String","int","boolean","java.lang.String","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"accountEntropyPool","parameterTypes":[] }, {"name":"aciAccountData","parameterTypes":[] }, {"name":"deviceId","parameterTypes":[] }, {"name":"encryptedDeviceName","parameterTypes":[] }, {"name":"isMultiDevice","parameterTypes":[] }, {"name":"mediaRootBackupKey","parameterTypes":[] }, {"name":"number","parameterTypes":[] }, {"name":"password","parameterTypes":[] }, {"name":"pinMasterKey","parameterTypes":[] }, {"name":"pniAccountData","parameterTypes":[] }, {"name":"profileKey","parameterTypes":[] }, {"name":"registered","parameterTypes":[] }, {"name":"registrationLockPin","parameterTypes":[] }, {"name":"serviceEnvironment","parameterTypes":[] }, {"name":"storageKey","parameterTypes":[] }, {"name":"timestamp","parameterTypes":[] }, {"name":"username","parameterTypes":[] }, {"name":"usernameLinkEntropy","parameterTypes":[] }, {"name":"usernameLinkServerId","parameterTypes":[] }, {"name":"version","parameterTypes":[] }] }, { "name":"org.asamk.signal.manager.storage.SignalAccount$Storage$AccountData", @@ -2257,7 +2275,7 @@ "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "methods":[{"name":"getAnnouncementGroup","parameterTypes":[] }, {"name":"getChangeNumber","parameterTypes":[] }, {"name":"getDeleteSync","parameterTypes":[] }, {"name":"getGiftBadges","parameterTypes":[] }, {"name":"getPaymentActivation","parameterTypes":[] }, {"name":"getPni","parameterTypes":[] }, {"name":"getSenderKey","parameterTypes":[] }, {"name":"getStorage","parameterTypes":[] }, {"name":"getStories","parameterTypes":[] }, {"name":"getVersionedExpirationTimer","parameterTypes":[] }] + "methods":[{"name":"getAnnouncementGroup","parameterTypes":[] }, {"name":"getChangeNumber","parameterTypes":[] }, {"name":"getDeleteSync","parameterTypes":[] }, {"name":"getGiftBadges","parameterTypes":[] }, {"name":"getPaymentActivation","parameterTypes":[] }, {"name":"getPni","parameterTypes":[] }, {"name":"getSenderKey","parameterTypes":[] }, {"name":"getStorage","parameterTypes":[] }, {"name":"getStorageServiceEncryptionV2","parameterTypes":[] }, {"name":"getStories","parameterTypes":[] }, {"name":"getVersionedExpirationTimer","parameterTypes":[] }] }, { "name":"org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest", @@ -2284,6 +2302,13 @@ { "name":"org.whispersystems.signalservice.api.groupsv2.TemporalCredential[]" }, +{ + "name":"org.whispersystems.signalservice.api.link.LinkedDeviceVerificationCodeResponse", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"","parameterTypes":["java.lang.String","java.lang.String","int","kotlin.jvm.internal.DefaultConstructorMarker"] }] +}, { "name":"org.whispersystems.signalservice.api.messages.calls.HangupMessage", "allDeclaredFields":true, @@ -2891,7 +2916,17 @@ }, { "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord", - "allDeclaredFields":true + "allDeclaredFields":true, + "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$Builder" +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$Companion" +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$PhoneNumberSharingMode" }, { "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$PinnedConversation", @@ -2907,7 +2942,17 @@ }, { "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord", - "allDeclaredFields":true + "allDeclaredFields":true, + "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord$Builder" +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord$Companion" +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord$IdentityState" }, { "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord$Name", @@ -2915,11 +2960,28 @@ }, { "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV1Record", - "allDeclaredFields":true + "allDeclaredFields":true, + "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV1Record$Builder" +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV1Record$Companion" }, { "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record", - "allDeclaredFields":true + "allDeclaredFields":true, + "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record$Builder" +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record$Companion" +}, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record$StorySendMode" }, { "name":"org.whispersystems.signalservice.internal.storage.protos.ManifestRecord", @@ -2929,6 +2991,9 @@ "name":"org.whispersystems.signalservice.internal.storage.protos.ManifestRecord$Identifier", "fields":[{"name":"raw_"}, {"name":"type_"}] }, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.OptionalBool" +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.Payments", "allDeclaredFields":true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d7d56d71..e52a4ae7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.12" -signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_110" +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_111" sqlite = "org.xerial:sqlite-jdbc:3.47.0.0" hikari = "com.zaxxer:HikariCP:6.2.1" junit-jupiter = "org.junit.jupiter:junit-jupiter:5.11.3" diff --git a/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java b/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java index 5598d438..d8438f5d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java @@ -611,11 +611,12 @@ public record MessageEnvelope( RecipientResolver recipientResolver, RecipientAddressResolver addressResolver ) { - return new Blocked(blockedListMessage.getAddresses() - .stream() - .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d)) - .toApiRecipientAddress()) - .toList(), blockedListMessage.getGroupIds().stream().map(GroupId::unknownVersion).toList()); + return new Blocked(blockedListMessage.individuals.stream() + .map(d -> new RecipientAddress(d.getAci() == null ? null : d.getAci().toString(), + null, + d.getE164(), + null)) + .toList(), blockedListMessage.groupIds.stream().map(GroupId::unknownVersion).toList()); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Profile.java b/lib/src/main/java/org/asamk/signal/manager/api/Profile.java index 5b1ecf3a..acbaac11 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/Profile.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/Profile.java @@ -161,7 +161,8 @@ public class Profile { } public enum Capability { - storage; + storage, + storageServiceEncryptionV2Capability; public static Capability valueOfOrNull(String value) { try { diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java index f7859dae..8e844392 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -51,7 +51,7 @@ class LiveConfig { private static final byte[] backupServerPublicParams = Base64.getDecoder() .decode("AJwNSU55fsFCbgaxGRD11wO1juAs8Yr5GF8FPlGzzvdJJIKH5/4CC7ZJSOe3yL2vturVaRU2Cx0n751Vt8wkj1bozK3CBV1UokxV09GWf+hdVImLGjXGYLLhnI1J2TWEe7iWHyb553EEnRb5oxr9n3lUbNAJuRmFM7hrr0Al0F0wrDD4S8lo2mGaXe0MJCOM166F8oYRQqpFeEHfiLnxA1O8ZLh7vMdv4g9jI5phpRBTsJ5IjiJrWeP0zdIGHEssUeprDZ9OUJ14m0v61eYJMKsf59Bn+mAT2a7YfB+Don9O"); - private static Environment LIBSIGNAL_NET_ENV = Environment.PRODUCTION; + private static final Environment LIBSIGNAL_NET_ENV = Environment.PRODUCTION; static SignalServiceConfiguration createDefaultServiceConfiguration( final List interceptors @@ -71,7 +71,8 @@ class LiveConfig { proxy, zkGroupServerPublicParams, genericServerPublicParams, - backupServerPublicParams); + backupServerPublicParams, + false); } static ECPublicKey getUnidentifiedSenderTrustRoot() { diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java index 482e0f60..24365cfe 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java @@ -29,7 +29,8 @@ public class ServiceConfig { public static AccountAttributes.Capabilities getCapabilities(boolean isPrimaryDevice) { final var deleteSync = !isPrimaryDevice; - return new AccountAttributes.Capabilities(true, deleteSync, true); + final var storageEncryptionV2 = !isPrimaryDevice; + return new AccountAttributes.Capabilities(true, deleteSync, true, storageEncryptionV2); } public static ServiceEnvironmentConfig getServiceEnvironmentConfig( diff --git a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java index a3212c07..741afa16 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java @@ -51,7 +51,7 @@ class StagingConfig { private static final byte[] backupServerPublicParams = Base64.getDecoder() .decode("AHYrGb9IfugAAJiPKp+mdXUx+OL9zBolPYHYQz6GI1gWjpEu5me3zVNSvmYY4zWboZHif+HG1sDHSuvwFd0QszSwuSF4X4kRP3fJREdTZ5MCR0n55zUppTwfHRW2S4sdQ0JGz7YDQIJCufYSKh0pGNEHL6hv79Agrdnr4momr3oXdnkpVBIp3HWAQ6IbXQVSG18X36GaicI1vdT0UFmTwU2KTneluC2eyL9c5ff8PcmiS+YcLzh0OKYQXB5ZfQ06d6DiINvDQLy75zcfUOniLAj0lGJiHxGczin/RXisKSR8"); - private static Network.Environment LIBSIGNAL_NET_ENV = Network.Environment.STAGING; + private static final Network.Environment LIBSIGNAL_NET_ENV = Network.Environment.STAGING; static SignalServiceConfiguration createDefaultServiceConfiguration( final List interceptors @@ -71,7 +71,8 @@ class StagingConfig { proxy, zkGroupServerPublicParams, genericServerPublicParams, - backupServerPublicParams); + backupServerPublicParams, + false); } static ECPublicKey getUnidentifiedSenderTrustRoot() { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java index d5e8581e..3eb2051d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java @@ -3,7 +3,6 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.DeviceLinkUrl; import org.asamk.signal.manager.api.IncorrectPinException; -import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.api.RateLimitException; @@ -27,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; +import org.whispersystems.signalservice.api.link.LinkedDeviceVerificationCodeResponse; import org.whispersystems.signalservice.api.push.ServiceId.ACI; import org.whispersystems.signalservice.api.push.ServiceId.PNI; import org.whispersystems.signalservice.api.push.ServiceIdType; @@ -56,6 +56,7 @@ import java.util.concurrent.TimeUnit; import okio.ByteString; import static org.asamk.signal.manager.config.ServiceConfig.PREKEY_MAXIMUM_ID; +import static org.asamk.signal.manager.util.Utils.handleResponseException; import static org.whispersystems.signalservice.internal.util.Util.isEmpty; public class AccountHelper { @@ -289,12 +290,11 @@ public class AccountHelper { (sessionId1, verificationCode1, registrationLock) -> { final var registrationApi = dependencies.getRegistrationApi(); try { - Utils.handleResponseException(registrationApi.verifyAccount(sessionId1, verificationCode1)); + handleResponseException(registrationApi.verifyAccount(sessionId1, verificationCode1)); } catch (AlreadyVerifiedException e) { // Already verified so can continue changing number } - return Utils.handleResponseException(registrationApi.changeNumber(new ChangePhoneNumberRequest( - sessionId1, + return handleResponseException(registrationApi.changeNumber(new ChangePhoneNumberRequest(sessionId1, null, newNumber, registrationLock, @@ -482,26 +482,28 @@ public class AccountHelper { dependencies.getAccountManager().setAccountAttributes(account.getAccountAttributes(null)); } - public void addDevice(DeviceLinkUrl deviceLinkInfo) throws IOException, InvalidDeviceLinkException, org.asamk.signal.manager.api.DeviceLimitExceededException { - String verificationCode; + public void addDevice(DeviceLinkUrl deviceLinkInfo) throws IOException, org.asamk.signal.manager.api.DeviceLimitExceededException { + final var linkDeviceApi = dependencies.getLinkDeviceApi(); + final LinkedDeviceVerificationCodeResponse verificationCode; try { - verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode(); + verificationCode = handleResponseException(linkDeviceApi.getDeviceVerificationCode()); } catch (DeviceLimitExceededException e) { throw new org.asamk.signal.manager.api.DeviceLimitExceededException("Too many linked devices", e); } - try { - dependencies.getAccountManager() - .addDevice(deviceLinkInfo.deviceIdentifier(), - deviceLinkInfo.deviceKey(), - account.getAciIdentityKeyPair(), - account.getPniIdentityKeyPair(), - account.getProfileKey(), - account.getOrCreatePinMasterKey(), - verificationCode); - } catch (InvalidKeyException e) { - throw new InvalidDeviceLinkException("Invalid device link", e); - } + handleResponseException(dependencies.getLinkDeviceApi() + .linkDevice(account.getNumber(), + account.getAci(), + account.getPni(), + deviceLinkInfo.deviceIdentifier(), + deviceLinkInfo.deviceKey(), + account.getAciIdentityKeyPair(), + account.getPniIdentityKeyPair(), + account.getProfileKey(), + account.getOrCreatePinMasterKey(), + account.getOrCreateMediaRootBackupKey(), + verificationCode.getVerificationCode(), + null)); account.setMultiDevice(true); context.getJobExecutor().enqueueJob(new SyncStorageJob()); } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java index 05bc876a..a3330d5c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java @@ -82,7 +82,7 @@ class GroupV2Helper { final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams); return dependencies.getGroupsV2Api().getGroup(groupSecretParams, groupsV2AuthorizationString); } catch (NonSuccessfulResponseCodeException e) { - if (e.getCode() == 403) { + if (e.code == 403) { throw new NotAGroupMemberException(GroupUtils.getGroupIdV2(groupSecretParams), null); } logger.warn("Failed to retrieve Group V2 info, ignoring: {}", e.getMessage()); @@ -119,7 +119,7 @@ class GroupV2Helper { false, sendEndorsementsExpirationMs); } catch (NonSuccessfulResponseCodeException e) { - if (e.getCode() == 403) { + if (e.code == 403) { throw new NotAGroupMemberException(GroupUtils.getGroupIdV2(groupSecretParams), null); } logger.warn("Failed to retrieve Group V2 history, ignoring: {}", e.getMessage()); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index 376a671b..5deffb3e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -31,6 +31,7 @@ import org.asamk.signal.manager.internal.SignalDependencies; import org.asamk.signal.manager.jobs.RetrieveStickerPackJob; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfoV1; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.stickers.StickerPack; import org.signal.libsignal.metadata.ProtocolInvalidKeyException; @@ -525,12 +526,12 @@ public final class IncomingMessageHandler { } if (syncMessage.getBlockedList().isPresent()) { final var blockedListMessage = syncMessage.getBlockedList().get(); - for (var address : blockedListMessage.getAddresses()) { - context.getContactHelper() - .setContactBlocked(account.getRecipientResolver().resolveRecipient(address), true); + for (var individual : blockedListMessage.individuals) { + final var address = new RecipientAddress(individual.getAci(), individual.getE164()); + final var recipientId = account.getRecipientResolver().resolveRecipient(address); + context.getContactHelper().setContactBlocked(recipientId, true); } - for (var groupId : blockedListMessage.getGroupIds() - .stream() + for (var groupId : blockedListMessage.groupIds.stream() .map(GroupId::unknownVersion) .collect(Collectors.toSet())) { try { @@ -585,14 +586,22 @@ public final class IncomingMessageHandler { } if (syncMessage.getKeys().isPresent()) { final var keysMessage = syncMessage.getKeys().get(); - if (keysMessage.getStorageService().isPresent()) { - final var storageKey = keysMessage.getStorageService().get(); + if (keysMessage.getAccountEntropyPool() != null) { + final var aep = keysMessage.getAccountEntropyPool(); + account.setAccountEntropyPool(aep); + actions.add(SyncStorageDataAction.create()); + } else if (keysMessage.getMaster() != null) { + final var masterKey = keysMessage.getMaster(); + account.setMasterKey(masterKey); + actions.add(SyncStorageDataAction.create()); + } else if (keysMessage.getStorageService() != null) { + final var storageKey = keysMessage.getStorageService(); account.setStorageKey(storageKey); actions.add(SyncStorageDataAction.create()); } - if (keysMessage.getMaster().isPresent()) { - final var masterKey = keysMessage.getMaster().get(); - account.setMasterKey(masterKey); + if (keysMessage.getMediaRootBackupKey() != null) { + final var mrb = keysMessage.getMediaRootBackupKey(); + account.setMediaRootBackupKey(mrb); actions.add(SyncStorageDataAction.create()); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java index 339425c1..b17a9206 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java @@ -172,7 +172,7 @@ public class PreKeyHelper { // This can happen when the primary device has changed phone number logger.warn("Failed to updated pre keys: {}", e.getMessage()); } catch (NonSuccessfulResponseCodeException e) { - if (serviceIdType != ServiceIdType.PNI || e.getCode() != 422) { + if (serviceIdType != ServiceIdType.PNI || e.code != 422) { throw e; } logger.warn("Failed to set PNI pre keys, ignoring for now. Account needs to be reregistered to fix this."); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java index 184eac6b..0905bb30 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -336,13 +336,6 @@ public final class ProfileHelper { final var profile = account.getProfileStore().getProfile(recipientId); - if (recipientId.equals(account.getSelfRecipientId())) { - final var isUnrestricted = encryptedProfile.isUnrestrictedUnidentifiedAccess(); - if (account.isUnrestrictedUnidentifiedAccess() != isUnrestricted) { - account.setUnrestrictedUnidentifiedAccess(isUnrestricted); - } - } - Profile newProfile = null; if (profileKey.isPresent()) { logger.trace("Decrypting profile"); @@ -358,6 +351,18 @@ public final class ProfileHelper { .build(); } + if (recipientId.equals(account.getSelfRecipientId())) { + final var isUnrestricted = encryptedProfile.isUnrestrictedUnidentifiedAccess(); + if (account.isUnrestrictedUnidentifiedAccess() != isUnrestricted) { + account.setUnrestrictedUnidentifiedAccess(isUnrestricted); + } + if (account.isPrimaryDevice() && profile != null && newProfile.getCapabilities() + .contains(Profile.Capability.storageServiceEncryptionV2Capability) && !profile.getCapabilities() + .contains(Profile.Capability.storageServiceEncryptionV2Capability)) { + context.getJobExecutor().enqueueJob(new SyncStorageJob(true)); + } + } + try { logger.trace("Storing identity"); final var identityKey = new IdentityKey(Base64.getDecoder().decode(encryptedProfile.getIdentityKey())); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java index 6225b982..5da66b7e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java @@ -239,7 +239,6 @@ public class RecipientHelper { newNumbers, account.getRecipientStore().getServiceIdToProfileKeyMap(), token, - dependencies.getServiceEnvironmentConfig().cdsiMrenclave(), null, dependencies.getLibSignalNetwork(), newToken -> { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index 8c6b3c52..740c0b5e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -2,6 +2,7 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.api.GroupIdV1; import org.asamk.signal.manager.api.GroupIdV2; +import org.asamk.signal.manager.api.Profile; import org.asamk.signal.manager.internal.SignalDependencies; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.recipients.RecipientId; @@ -17,11 +18,17 @@ import org.signal.core.util.SetUtil; import org.signal.libsignal.protocol.InvalidKeyException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.storage.RecordIkm; import org.whispersystems.signalservice.api.storage.SignalStorageManifest; import org.whispersystems.signalservice.api.storage.SignalStorageRecord; import org.whispersystems.signalservice.api.storage.StorageId; import org.whispersystems.signalservice.api.storage.StorageKey; +import org.whispersystems.signalservice.api.storage.StorageRecordConvertersKt; +import org.whispersystems.signalservice.api.storage.StorageServiceRepository; +import org.whispersystems.signalservice.api.storage.StorageServiceRepository.ManifestIfDifferentVersionResult; +import org.whispersystems.signalservice.api.storage.StorageServiceRepository.WriteStorageRecordsResult; import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord; +import org.whispersystems.signalservice.internal.storage.protos.StorageRecord; import java.io.IOException; import java.sql.Connection; @@ -32,9 +39,10 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.stream.Collectors; +import static org.asamk.signal.manager.util.Utils.handleResponseException; + public class StorageHelper { private static final Logger logger = LoggerFactory.getLogger(StorageHelper.class); @@ -54,7 +62,7 @@ public class StorageHelper { } public void syncDataWithStorage() throws IOException { - final var storageKey = account.getOrCreateStorageKey(); + var storageKey = account.getOrCreateStorageKey(); if (storageKey == null) { if (!account.isPrimaryDevice()) { logger.debug("Storage key unknown, requesting from primary device."); @@ -65,52 +73,76 @@ public class StorageHelper { logger.trace("Reading manifest from remote storage"); final var localManifestVersion = account.getStorageManifestVersion(); - final var localManifest = account.getStorageManifest().orElse(SignalStorageManifest.EMPTY); - SignalStorageManifest remoteManifest; - try { - remoteManifest = dependencies.getAccountManager() - .getStorageManifestIfDifferentVersion(storageKey, localManifestVersion) - .orElse(localManifest); - } catch (InvalidKeyException e) { - logger.warn("Manifest couldn't be decrypted."); - if (account.isPrimaryDevice()) { - try { - forcePushToStorage(storageKey); - } catch (RetryLaterException rle) { - // TODO retry later - return; - } - } - return; - } - - logger.trace("Manifest versions: local {}, remote {}", localManifestVersion, remoteManifest.getVersion()); + final var localManifest = account.getStorageManifest().orElse(SignalStorageManifest.Companion.getEMPTY()); + final var storageServiceRepository = dependencies.getStorageServiceRepository(); + final var result = storageServiceRepository.getStorageManifestIfDifferentVersion(storageKey, + localManifestVersion); var needsForcePush = false; - if (remoteManifest.getVersion() > localManifestVersion) { - logger.trace("Remote version was newer, reading records."); - needsForcePush = readDataFromStorage(storageKey, localManifest, remoteManifest); - } else if (remoteManifest.getVersion() < localManifest.getVersion()) { - logger.debug("Remote storage manifest version was older. User might have switched accounts."); - } - logger.trace("Done reading data from remote storage"); + final var remoteManifest = switch (result) { + case ManifestIfDifferentVersionResult.DifferentVersion diff -> { + final var manifest = diff.getManifest(); + storeManifestLocally(manifest); + yield manifest; + } + case ManifestIfDifferentVersionResult.DecryptionError ignore -> { + logger.warn("Manifest couldn't be decrypted."); + if (account.isPrimaryDevice()) { + needsForcePush = true; + } else { + context.getSyncHelper().requestSyncKeys(); + } + yield null; + } + case ManifestIfDifferentVersionResult.SameVersion ignored -> localManifest; + case ManifestIfDifferentVersionResult.NetworkError e -> throw e.getException(); + case ManifestIfDifferentVersionResult.StatusCodeError e -> throw e.getException(); + default -> throw new RuntimeException("Unhandled ManifestIfDifferentVersionResult type"); + }; - if (localManifest != remoteManifest) { - storeManifestLocally(remoteManifest); - } + if (remoteManifest != null) { + logger.trace("Manifest versions: local {}, remote {}", localManifestVersion, remoteManifest.version); - readRecordsWithPreviouslyUnknownTypes(storageKey); + if (remoteManifest.version > localManifestVersion) { + logger.trace("Remote version was newer, reading records."); + needsForcePush = readDataFromStorage(storageKey, localManifest, remoteManifest); + } else if (remoteManifest.version < localManifest.version) { + logger.debug("Remote storage manifest version was older. User might have switched accounts."); + } + logger.trace("Done reading data from remote storage"); + + readRecordsWithPreviouslyUnknownTypes(storageKey, remoteManifest); + } logger.trace("Adding missing storageIds to local data"); account.getRecipientStore().setMissingStorageIds(); account.getGroupStore().setMissingStorageIds(); var needsMultiDeviceSync = false; - try { - needsMultiDeviceSync = writeToStorage(storageKey, remoteManifest, needsForcePush); - } catch (RetryLaterException e) { - // TODO retry later - return; + + if (account.needsStorageKeyMigration()) { + logger.debug("Storage needs force push due to new account entropy pool"); + // Set new aep and reset previous master key and storage key + account.setAccountEntropyPool(account.getOrCreateAccountEntropyPool()); + storageKey = account.getOrCreateStorageKey(); + context.getSyncHelper().sendKeysMessage(); + needsForcePush = true; + } else if (remoteManifest == null) { + if (account.isPrimaryDevice()) { + needsForcePush = true; + } + } else if (remoteManifest.recordIkm == null && account.getSelfRecipientProfile() + .getCapabilities() + .contains(Profile.Capability.storageServiceEncryptionV2Capability)) { + logger.debug("The SSRE2 capability is supported, but no recordIkm is set! Force pushing."); + needsForcePush = true; + } else { + try { + needsMultiDeviceSync = writeToStorage(storageKey, remoteManifest, needsForcePush); + } catch (RetryLaterException e) { + // TODO retry later + return; + } } if (needsForcePush) { @@ -131,6 +163,23 @@ public class StorageHelper { logger.debug("Done syncing data with remote storage"); } + public void forcePushToStorage() throws IOException { + if (!account.isPrimaryDevice()) { + return; + } + + final var storageKey = account.getOrCreateStorageKey(); + if (storageKey == null) { + return; + } + + try { + forcePushToStorage(storageKey); + } catch (RetryLaterException e) { + // TODO retry later + } + } + private boolean readDataFromStorage( final StorageKey storageKey, final SignalStorageManifest localManifest, @@ -140,14 +189,14 @@ public class StorageHelper { try (final var connection = account.getAccountDatabase().getConnection()) { connection.setAutoCommit(false); - var idDifference = findIdDifference(remoteManifest.getStorageIds(), localManifest.getStorageIds()); + var idDifference = findIdDifference(remoteManifest.storageIds, localManifest.storageIds); if (idDifference.hasTypeMismatches() && account.isPrimaryDevice()) { logger.debug("Found type mismatches in the ID sets! Scheduling a force push after this sync completes."); needsForcePush = true; } - logger.debug("Pre-Merge ID Difference :: " + idDifference); + logger.debug("Pre-Merge ID Difference :: {}", idDifference); if (!idDifference.localOnlyIds().isEmpty()) { final var updated = account.getRecipientStore() @@ -161,15 +210,15 @@ public class StorageHelper { } if (!idDifference.isEmpty()) { - final var remoteOnlyRecords = getSignalStorageRecords(storageKey, idDifference.remoteOnlyIds()); + final var remoteOnlyRecords = getSignalStorageRecords(storageKey, + remoteManifest, + idDifference.remoteOnlyIds()); if (remoteOnlyRecords.size() != idDifference.remoteOnlyIds().size()) { - logger.debug("Could not find all remote-only records! Requested: " - + idDifference.remoteOnlyIds() - .size() - + ", Found: " - + remoteOnlyRecords.size() - + ". These stragglers should naturally get deleted during the sync."); + logger.debug( + "Could not find all remote-only records! Requested: {}, Found: {}. These stragglers should naturally get deleted during the sync.", + idDifference.remoteOnlyIds().size(), + remoteOnlyRecords.size()); } final var unknownInserts = processKnownRecords(connection, remoteOnlyRecords); @@ -194,18 +243,21 @@ public class StorageHelper { return needsForcePush; } - private void readRecordsWithPreviouslyUnknownTypes(final StorageKey storageKey) throws IOException { + private void readRecordsWithPreviouslyUnknownTypes( + final StorageKey storageKey, + final SignalStorageManifest remoteManifest + ) throws IOException { try (final var connection = account.getAccountDatabase().getConnection()) { connection.setAutoCommit(false); final var knownUnknownIds = account.getUnknownStorageIdStore() .getUnknownStorageIds(connection, KNOWN_TYPES); if (!knownUnknownIds.isEmpty()) { - logger.debug("We have " + knownUnknownIds.size() + " unknown records that we can now process."); + logger.debug("We have {} unknown records that we can now process.", knownUnknownIds.size()); - final var remote = getSignalStorageRecords(storageKey, knownUnknownIds); + final var remote = getSignalStorageRecords(storageKey, remoteManifest, knownUnknownIds); - logger.debug("Found " + remote.size() + " of the known-unknowns remotely."); + logger.debug("Found {} of the known-unknowns remotely.", remote.size()); processKnownRecords(connection, remote); account.getUnknownStorageIdStore() @@ -227,15 +279,16 @@ public class StorageHelper { connection.setAutoCommit(false); final var localStorageIds = getAllLocalStorageIds(connection); - final var idDifference = findIdDifference(remoteManifest.getStorageIds(), localStorageIds); - logger.debug("ID Difference :: " + idDifference); + final var idDifference = findIdDifference(remoteManifest.storageIds, localStorageIds); + logger.debug("ID Difference :: {}", idDifference); final var remoteDeletes = idDifference.remoteOnlyIds().stream().map(StorageId::getRaw).toList(); final var remoteInserts = buildLocalStorageRecords(connection, idDifference.localOnlyIds()); // TODO check if local storage record proto matches remote, then reset to remote storage_id - remoteWriteOperation = new WriteOperationResult(new SignalStorageManifest(remoteManifest.getVersion() + 1, + remoteWriteOperation = new WriteOperationResult(new SignalStorageManifest(remoteManifest.version + 1, account.getDeviceId(), + remoteManifest.recordIkm, localStorageIds), remoteInserts, remoteDeletes); connection.commit(); @@ -244,39 +297,37 @@ public class StorageHelper { } if (remoteWriteOperation.isEmpty()) { - logger.debug("No remote writes needed. Still at version: " + remoteManifest.getVersion()); + logger.debug("No remote writes needed. Still at version: {}", remoteManifest.version); return false; } logger.debug("We have something to write remotely."); - logger.debug("WriteOperationResult :: " + remoteWriteOperation); + logger.debug("WriteOperationResult :: {}", remoteWriteOperation); StorageSyncValidations.validate(remoteWriteOperation, remoteManifest, needsForcePush, account.getSelfRecipientAddress()); - final Optional conflict; - try { - conflict = dependencies.getAccountManager() - .writeStorageRecords(storageKey, - remoteWriteOperation.manifest(), - remoteWriteOperation.inserts(), - remoteWriteOperation.deletes()); - } catch (InvalidKeyException e) { - logger.warn("Failed to decrypt conflicting storage manifest: {}", e.getMessage()); - throw new IOException(e); + final var result = dependencies.getStorageServiceRepository() + .writeStorageRecords(storageKey, + remoteWriteOperation.manifest(), + remoteWriteOperation.inserts(), + remoteWriteOperation.deletes()); + switch (result) { + case WriteStorageRecordsResult.ConflictError ignored -> { + logger.debug("Hit a conflict when trying to resolve the conflict! Retrying."); + throw new RetryLaterException(); + } + case WriteStorageRecordsResult.NetworkError networkError -> throw networkError.getException(); + case WriteStorageRecordsResult.StatusCodeError statusCodeError -> throw statusCodeError.getException(); + case WriteStorageRecordsResult.Success ignored -> { + logger.debug("Saved new manifest. Now at version: {}", remoteWriteOperation.manifest().version); + storeManifestLocally(remoteWriteOperation.manifest()); + return true; + } + default -> throw new IllegalStateException("Unexpected value: " + result); } - - if (conflict.isPresent()) { - logger.debug("Hit a conflict when trying to resolve the conflict! Retrying."); - throw new RetryLaterException(); - } - - logger.debug("Saved new manifest. Now at version: " + remoteWriteOperation.manifest().getVersion()); - storeManifestLocally(remoteWriteOperation.manifest()); - - return true; } private void forcePushToStorage( @@ -284,7 +335,8 @@ public class StorageHelper { ) throws IOException, RetryLaterException { logger.debug("Force pushing local state to remote storage"); - final var currentVersion = dependencies.getAccountManager().getStorageManifestVersion(); + final var currentVersion = handleResponseException(dependencies.getStorageServiceRepository() + .getManifestVersion()); final var newVersion = currentVersion + 1; final var newStorageRecords = new ArrayList(); final Map newContactStorageIds; @@ -302,15 +354,16 @@ public class StorageHelper { final var recipient = account.getRecipientStore().getRecipient(connection, recipientId); final var accountRecord = StorageSyncModels.localToRemoteRecord(account.getConfigurationStore(), recipient, - account.getUsernameLink(), - storageId.getRaw()); - newStorageRecords.add(accountRecord); + account.getUsernameLink()); + newStorageRecords.add(new SignalStorageRecord(storageId, + new StorageRecord.Builder().account(accountRecord).build())); } else { final var recipient = account.getRecipientStore().getRecipient(connection, recipientId); final var address = recipient.getAddress().getIdentifier(); final var identity = account.getIdentityKeyStore().getIdentityInfo(connection, address); - final var record = StorageSyncModels.localToRemoteRecord(recipient, identity, storageId.getRaw()); - newStorageRecords.add(record); + final var record = StorageSyncModels.localToRemoteRecord(recipient, identity); + newStorageRecords.add(new SignalStorageRecord(storageId, + new StorageRecord.Builder().contact(record).build())); } } @@ -319,8 +372,9 @@ public class StorageHelper { for (final var groupId : groupV1Ids) { final var storageId = newGroupV1StorageIds.get(groupId); final var group = account.getGroupStore().getGroup(connection, groupId); - final var record = StorageSyncModels.localToRemoteRecord(group, storageId.getRaw()); - newStorageRecords.add(record); + final var record = StorageSyncModels.localToRemoteRecord(group); + newStorageRecords.add(new SignalStorageRecord(storageId, + new StorageRecord.Builder().groupV1(record).build())); } final var groupV2Ids = account.getGroupStore().getGroupV2Ids(connection); @@ -328,8 +382,9 @@ public class StorageHelper { for (final var groupId : groupV2Ids) { final var storageId = newGroupV2StorageIds.get(groupId); final var group = account.getGroupStore().getGroup(connection, groupId); - final var record = StorageSyncModels.localToRemoteRecord(group, storageId.getRaw()); - newStorageRecords.add(record); + final var record = StorageSyncModels.localToRemoteRecord(group); + newStorageRecords.add(new SignalStorageRecord(storageId, + new StorageRecord.Builder().groupV2(record).build())); } connection.commit(); @@ -338,34 +393,46 @@ public class StorageHelper { } final var newStorageIds = newStorageRecords.stream().map(SignalStorageRecord::getId).toList(); - final var manifest = new SignalStorageManifest(newVersion, account.getDeviceId(), newStorageIds); + final RecordIkm recordIkm; + if (account.getSelfRecipientProfile() + .getCapabilities() + .contains(Profile.Capability.storageServiceEncryptionV2Capability)) { + logger.debug("Generating and including a new recordIkm."); + recordIkm = RecordIkm.Companion.generate(); + } else { + logger.debug("SSRE2 not yet supported. Not including recordIkm."); + recordIkm = null; + } + + final var manifest = new SignalStorageManifest(newVersion, account.getDeviceId(), recordIkm, newStorageIds); StorageSyncValidations.validateForcePush(manifest, newStorageRecords, account.getSelfRecipientAddress()); - final Optional conflict; - try { - if (newVersion > 1) { - logger.trace("Force-pushing data. Inserting {} IDs.", newStorageRecords.size()); - conflict = dependencies.getAccountManager() - .resetStorageRecords(storageServiceKey, manifest, newStorageRecords); - } else { - logger.trace("First version, normal push. Inserting {} IDs.", newStorageRecords.size()); - conflict = dependencies.getAccountManager() - .writeStorageRecords(storageServiceKey, manifest, newStorageRecords, Collections.emptyList()); + final WriteStorageRecordsResult result; + if (newVersion > 1) { + logger.trace("Force-pushing data. Inserting {} IDs.", newStorageRecords.size()); + result = dependencies.getStorageServiceRepository() + .resetAndWriteStorageRecords(storageServiceKey, manifest, newStorageRecords); + } else { + logger.trace("First version, normal push. Inserting {} IDs.", newStorageRecords.size()); + result = dependencies.getStorageServiceRepository() + .writeStorageRecords(storageServiceKey, manifest, newStorageRecords, Collections.emptyList()); + } + + switch (result) { + case WriteStorageRecordsResult.ConflictError ignored -> { + logger.debug("Hit a conflict. Trying again."); + throw new RetryLaterException(); } - } catch (InvalidKeyException e) { - logger.debug("Hit an invalid key exception, which likely indicates a conflict.", e); - throw new RetryLaterException(); + case WriteStorageRecordsResult.NetworkError networkError -> throw networkError.getException(); + case WriteStorageRecordsResult.StatusCodeError statusCodeError -> throw statusCodeError.getException(); + case WriteStorageRecordsResult.Success ignored -> { + logger.debug("Force push succeeded. Updating local manifest version to: {}", manifest.version); + storeManifestLocally(manifest); + } + default -> throw new IllegalStateException("Unexpected value: " + result); } - if (conflict.isPresent()) { - logger.debug("Hit a conflict. Trying again."); - throw new RetryLaterException(); - } - - logger.debug("Force push succeeded. Updating local manifest version to: " + manifest.getVersion()); - storeManifestLocally(manifest); - try (final var connection = account.getAccountDatabase().getConnection()) { connection.setAutoCommit(false); account.getRecipientStore().updateStorageIds(connection, newContactStorageIds); @@ -405,22 +472,35 @@ public class StorageHelper { private void storeManifestLocally( final SignalStorageManifest remoteManifest ) { - account.setStorageManifestVersion(remoteManifest.getVersion()); + account.setStorageManifestVersion(remoteManifest.version); account.setStorageManifest(remoteManifest); } private List getSignalStorageRecords( final StorageKey storageKey, + final SignalStorageManifest manifest, final List storageIds ) throws IOException { - List records; - try { - records = dependencies.getAccountManager().readStorageRecords(storageKey, storageIds); - } catch (InvalidKeyException e) { - logger.warn("Failed to read storage records, ignoring."); - return List.of(); - } - return records; + final var result = dependencies.getStorageServiceRepository() + .readStorageRecords(storageKey, manifest.recordIkm, storageIds); + return switch (result) { + case StorageServiceRepository.StorageRecordResult.DecryptionError decryptionError -> { + if (decryptionError.getException() instanceof InvalidKeyException) { + logger.warn("Failed to read storage records, ignoring."); + yield List.of(); + } else if (decryptionError.getException() instanceof IOException ioe) { + throw ioe; + } else { + throw new IOException(decryptionError.getException()); + } + } + case StorageServiceRepository.StorageRecordResult.NetworkError networkError -> + throw networkError.getException(); + case StorageServiceRepository.StorageRecordResult.StatusCodeError statusCodeError -> + throw statusCodeError.getException(); + case StorageServiceRepository.StorageRecordResult.Success success -> success.getRecords(); + default -> throw new IllegalStateException("Unexpected value: " + result); + }; } private List getAllLocalStorageIds(final Connection connection) throws SQLException { @@ -436,12 +516,10 @@ public class StorageHelper { final Connection connection, final List storageIds ) throws SQLException { - final var records = new ArrayList(); + final var records = new ArrayList(storageIds.size()); for (final var storageId : storageIds) { final var record = buildLocalStorageRecord(connection, storageId); - if (record != null) { - records.add(record); - } + records.add(record); } return records; } @@ -455,25 +533,31 @@ public class StorageHelper { final var recipient = account.getRecipientStore().getRecipient(connection, storageId); final var address = recipient.getAddress().getIdentifier(); final var identity = account.getIdentityKeyStore().getIdentityInfo(connection, address); - yield StorageSyncModels.localToRemoteRecord(recipient, identity, storageId.getRaw()); + final var record = StorageSyncModels.localToRemoteRecord(recipient, identity); + yield new SignalStorageRecord(storageId, new StorageRecord.Builder().contact(record).build()); } case ManifestRecord.Identifier.Type.GROUPV1 -> { final var groupV1 = account.getGroupStore().getGroupV1(connection, storageId); - yield StorageSyncModels.localToRemoteRecord(groupV1, storageId.getRaw()); + final var record = StorageSyncModels.localToRemoteRecord(groupV1); + yield new SignalStorageRecord(storageId, new StorageRecord.Builder().groupV1(record).build()); } case ManifestRecord.Identifier.Type.GROUPV2 -> { final var groupV2 = account.getGroupStore().getGroupV2(connection, storageId); - yield StorageSyncModels.localToRemoteRecord(groupV2, storageId.getRaw()); + final var record = StorageSyncModels.localToRemoteRecord(groupV2); + yield new SignalStorageRecord(storageId, new StorageRecord.Builder().groupV2(record).build()); } case ManifestRecord.Identifier.Type.ACCOUNT -> { final var selfRecipient = account.getRecipientStore() .getRecipient(connection, account.getSelfRecipientId()); - yield StorageSyncModels.localToRemoteRecord(account.getConfigurationStore(), + + final var record = StorageSyncModels.localToRemoteRecord(account.getConfigurationStore(), selfRecipient, - account.getUsernameLink(), - storageId.getRaw()); + account.getUsernameLink()); + yield new SignalStorageRecord(storageId, new StorageRecord.Builder().account(record).build()); + } + case null, default -> { + throw new AssertionError("Got unknown local storage record type: " + storageId); } - case null, default -> throw new AssertionError("Got unknown local storage record type: " + storageId); }; } @@ -537,13 +621,24 @@ public class StorageHelper { final var groupV2RecordProcessor = new GroupV2RecordProcessor(account, connection); for (final var record : records) { - logger.debug("Reading record of type {}", record.getType()); - switch (ManifestRecord.Identifier.Type.fromValue(record.getType())) { - case ACCOUNT -> accountRecordProcessor.process(record.getAccount().get()); - case GROUPV1 -> groupV1RecordProcessor.process(record.getGroupV1().get()); - case GROUPV2 -> groupV2RecordProcessor.process(record.getGroupV2().get()); - case CONTACT -> contactRecordProcessor.process(record.getContact().get()); - case null, default -> unknownRecords.add(record.getId()); + if (record.getProto().account != null) { + logger.debug("Reading record {} of type account", record.getId()); + accountRecordProcessor.process(StorageRecordConvertersKt.toSignalAccountRecord(record.getProto().account, + record.getId())); + } else if (record.getProto().groupV1 != null) { + logger.debug("Reading record {} of type groupV1", record.getId()); + groupV1RecordProcessor.process(StorageRecordConvertersKt.toSignalGroupV1Record(record.getProto().groupV1, + record.getId())); + } else if (record.getProto().groupV2 != null) { + logger.debug("Reading record {} of type groupV2", record.getId()); + groupV2RecordProcessor.process(StorageRecordConvertersKt.toSignalGroupV2Record(record.getProto().groupV2, + record.getId())); + } else if (record.getProto().contact != null) { + logger.debug("Reading record {} of type contact", record.getId()); + contactRecordProcessor.process(StorageRecordConvertersKt.toSignalContactRecord(record.getProto().contact, + record.getId())); + } else { + unknownRecords.add(record.getId()); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java index 70427f00..39726df8 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java @@ -247,10 +247,14 @@ public class SyncHelper { } public SendMessageResult sendBlockedList() { - var addresses = new ArrayList(); + var addresses = new ArrayList(); for (var record : account.getContactStore().getContacts()) { if (record.second().isBlocked()) { - addresses.add(context.getRecipientHelper().resolveSignalServiceAddress(record.first())); + final var address = account.getRecipientAddressResolver().resolveRecipientAddress(record.first()); + if (address.aci().isPresent() || address.number().isPresent()) { + addresses.add(new BlockedListMessage.Individual(address.aci().orElse(null), + address.number().orElse(null))); + } } } var groupIds = new ArrayList(); @@ -276,8 +280,10 @@ public class SyncHelper { } public SendMessageResult sendKeysMessage() { - var keysMessage = new KeysMessage(Optional.ofNullable(account.getOrCreateStorageKey()), - Optional.ofNullable(account.getOrCreatePinMasterKey())); + var keysMessage = new KeysMessage(account.getOrCreateStorageKey(), + account.getOrCreatePinMasterKey(), + account.getOrCreateAccountEntropyPool(), + account.getOrCreateMediaRootBackupKey()); return context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forKeys(keysMessage)); } @@ -405,7 +411,7 @@ public class SyncHelper { builder.withMessageExpirationTimeVersion(c.getExpirationTimerVersion().get()); } else { logger.debug( - "[ContactSync] {} was synced with an old expiration timer. Ignoring. Received: {} Current: ${}", + "[ContactSync] {} was synced with an old expiration timer. Ignoring. Received: {} Current: {}", recipientId, c.getExpirationTimerVersion(), contact == null ? 1 : contact.messageExpirationTimeVersion()); diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java index 7ee2b39e..90e3b159 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java @@ -15,10 +15,13 @@ import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; +import org.whispersystems.signalservice.api.link.LinkDeviceApi; import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.registration.RegistrationApi; import org.whispersystems.signalservice.api.services.ProfileService; +import org.whispersystems.signalservice.api.storage.StorageServiceApi; +import org.whispersystems.signalservice.api.storage.StorageServiceRepository; import org.whispersystems.signalservice.api.svr.SecureValueRecovery; import org.whispersystems.signalservice.api.util.CredentialsProvider; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; @@ -49,6 +52,8 @@ public class SignalDependencies { private SignalServiceAccountManager accountManager; private GroupsV2Api groupsV2Api; private RegistrationApi registrationApi; + private LinkDeviceApi linkDeviceApi; + private StorageServiceApi storageServiceApi; private GroupsV2Operations groupsV2Operations; private ClientZkOperations clientZkOperations; @@ -155,6 +160,19 @@ public class SignalDependencies { return getOrCreate(() -> registrationApi, () -> registrationApi = getAccountManager().getRegistrationApi()); } + public LinkDeviceApi getLinkDeviceApi() { + return getOrCreate(() -> linkDeviceApi, () -> linkDeviceApi = new LinkDeviceApi(getPushServiceSocket())); + } + + private StorageServiceApi getStorageServiceApi() { + return getOrCreate(() -> storageServiceApi, + () -> storageServiceApi = new StorageServiceApi(getPushServiceSocket())); + } + + public StorageServiceRepository getStorageServiceRepository() { + return new StorageServiceRepository(getStorageServiceApi()); + } + public GroupsV2Operations getGroupsV2Operations() { return getOrCreate(() -> groupsV2Operations, () -> groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.signalServiceConfiguration()), diff --git a/lib/src/main/java/org/asamk/signal/manager/jobs/SyncStorageJob.java b/lib/src/main/java/org/asamk/signal/manager/jobs/SyncStorageJob.java index 85cfbe2c..d695c7b1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/jobs/SyncStorageJob.java +++ b/lib/src/main/java/org/asamk/signal/manager/jobs/SyncStorageJob.java @@ -8,13 +8,27 @@ import java.io.IOException; public class SyncStorageJob implements Job { + private final boolean forcePush; + private static final Logger logger = LoggerFactory.getLogger(SyncStorageJob.class); + public SyncStorageJob() { + this.forcePush = false; + } + + public SyncStorageJob(final boolean forcePush) { + this.forcePush = forcePush; + } + @Override public void run(Context context) { logger.trace("Running storage sync job"); try { - context.getStorageHelper().syncDataWithStorage(); + if (forcePush) { + context.getStorageHelper().forcePushToStorage(); + } else { + context.getStorageHelper().syncDataWithStorage(); + } } catch (IOException e) { logger.warn("Failed to sync storage data", e); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 5c8a5530..2529ad4a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -65,10 +65,12 @@ import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.AccountEntropyPool; import org.whispersystems.signalservice.api.SignalServiceAccountDataStore; import org.whispersystems.signalservice.api.SignalServiceDataStore; import org.whispersystems.signalservice.api.account.AccountAttributes; import org.whispersystems.signalservice.api.account.PreKeyCollection; +import org.whispersystems.signalservice.api.backup.MediaRootBackupKey; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.push.ServiceId; @@ -138,6 +140,8 @@ public class SignalAccount implements Closeable { private String registrationLockPin; private MasterKey pinMasterKey; private StorageKey storageKey; + private AccountEntropyPool accountEntropyPool; + private MediaRootBackupKey mediaRootBackupKey; private ProfileKey profileKey; private Settings settings; @@ -305,6 +309,7 @@ public class SignalAccount implements Closeable { this.isMultiDevice = true; setLastReceiveTimestamp(0L); this.pinMasterKey = masterKey; + this.accountEntropyPool = null; getKeyValueStore().storeEntry(storageManifestVersion, -1L); this.setStorageManifest(null); this.storageKey = null; @@ -339,6 +344,7 @@ public class SignalAccount implements Closeable { final PreKeyCollection pniPreKeys ) { this.pinMasterKey = masterKey; + this.accountEntropyPool = null; getKeyValueStore().storeEntry(storageManifestVersion, -1L); this.setStorageManifest(null); this.storageKey = null; @@ -499,6 +505,12 @@ public class SignalAccount implements Closeable { if (storage.storageKey != null) { storageKey = new StorageKey(base64.decode(storage.storageKey)); } + if (storage.accountEntropyPool != null) { + accountEntropyPool = new AccountEntropyPool(storage.accountEntropyPool); + } + if (storage.mediaRootBackupKey != null) { + mediaRootBackupKey = new MediaRootBackupKey(base64.decode(storage.mediaRootBackupKey)); + } if (storage.profileKey != null) { try { profileKey = new ProfileKey(base64.decode(storage.profileKey)); @@ -981,6 +993,8 @@ public class SignalAccount implements Closeable { registrationLockPin, pinMasterKey == null ? null : base64.encodeToString(pinMasterKey.serialize()), storageKey == null ? null : base64.encodeToString(storageKey.serialize()), + accountEntropyPool == null ? null : accountEntropyPool.getValue(), + mediaRootBackupKey == null ? null : base64.encodeToString(mediaRootBackupKey.getValue()), profileKey == null ? null : base64.encodeToString(profileKey.serialize()), usernameLink == null ? null : base64.encodeToString(usernameLink.getEntropy()), usernameLink == null ? null : usernameLink.getServerId().toString()); @@ -1442,6 +1456,10 @@ public class SignalAccount implements Closeable { return selfRecipientId; } + public Profile getSelfRecipientProfile() { + return recipientStore.getProfile(selfRecipientId); + } + public String getSessionId(final String forNumber) { final var keyValueStore = getKeyValueStore(); final var sessionNumber = keyValueStore.getEntry(verificationSessionNumber); @@ -1512,31 +1530,50 @@ public class SignalAccount implements Closeable { public MasterKey getPinBackedMasterKey() { if (registrationLockPin == null) { return null; + } else if (!isPrimaryDevice()) { + return getMasterKey(); } - return pinMasterKey; + return getOrCreatePinMasterKey(); } public MasterKey getOrCreatePinMasterKey() { - if (pinMasterKey == null) { - pinMasterKey = KeyUtils.createMasterKey(); - save(); + final var key = getMasterKey(); + if (key != null) { + return key; } + + pinMasterKey = KeyUtils.createMasterKey(); + save(); return pinMasterKey; } + private MasterKey getMasterKey() { + if (pinMasterKey != null) { + return pinMasterKey; + } else if (accountEntropyPool != null) { + return accountEntropyPool.deriveMasterKey(); + } + return null; + } + public void setMasterKey(MasterKey masterKey) { if (isPrimaryDevice()) { return; } this.pinMasterKey = masterKey; + if (masterKey != null) { + this.storageKey = null; + } save(); } public StorageKey getOrCreateStorageKey() { - if (pinMasterKey != null) { - return pinMasterKey.deriveStorageServiceKey(); - } else if (storageKey != null) { + if (storageKey != null) { return storageKey; + } else if (pinMasterKey != null) { + return pinMasterKey.deriveStorageServiceKey(); + } else if (accountEntropyPool != null) { + return accountEntropyPool.deriveMasterKey().deriveStorageServiceKey(); } else if (!isPrimaryDevice() || !isMultiDevice()) { // Only upload storage, if a pin master key already exists or linked devices exist return null; @@ -1553,6 +1590,40 @@ public class SignalAccount implements Closeable { save(); } + public AccountEntropyPool getOrCreateAccountEntropyPool() { + if (accountEntropyPool == null) { + accountEntropyPool = AccountEntropyPool.Companion.generate(); + save(); + } + return accountEntropyPool; + } + + public void setAccountEntropyPool(final AccountEntropyPool accountEntropyPool) { + this.accountEntropyPool = accountEntropyPool; + if (accountEntropyPool != null) { + this.storageKey = null; + this.pinMasterKey = null; + } + save(); + } + + public boolean needsStorageKeyMigration() { + return isPrimaryDevice() && (storageKey != null || pinMasterKey != null); + } + + public MediaRootBackupKey getOrCreateMediaRootBackupKey() { + if (mediaRootBackupKey == null) { + mediaRootBackupKey = KeyUtils.createMediaRootBackupKey(); + save(); + } + return mediaRootBackupKey; + } + + public void setMediaRootBackupKey(final MediaRootBackupKey mediaRootBackupKey) { + this.mediaRootBackupKey = mediaRootBackupKey; + save(); + } + public String getRecoveryPassword() { final var masterKey = getPinBackedMasterKey(); if (masterKey == null) { @@ -1575,7 +1646,7 @@ public class SignalAccount implements Closeable { return Optional.empty(); } try (var inputStream = new FileInputStream(storageManifestFile)) { - return Optional.of(SignalStorageManifest.deserialize(inputStream.readAllBytes())); + return Optional.of(SignalStorageManifest.Companion.deserialize(inputStream.readAllBytes())); } catch (IOException e) { logger.warn("Failed to read local storage manifest.", e); return Optional.empty(); @@ -1882,6 +1953,8 @@ public class SignalAccount implements Closeable { String registrationLockPin, String pinMasterKey, String storageKey, + String accountEntropyPool, + String mediaRootBackupKey, String profileKey, String usernameLinkEntropy, String usernameLinkServerId diff --git a/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java b/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java index 18dd1869..b2f7dd29 100644 --- a/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java +++ b/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java @@ -12,8 +12,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.UsernameLinkComponents; import org.whispersystems.signalservice.api.storage.SignalAccountRecord; -import org.whispersystems.signalservice.api.util.OptionalUtil; +import org.whispersystems.signalservice.api.storage.StorageId; import org.whispersystems.signalservice.api.util.UuidUtil; +import org.whispersystems.signalservice.internal.storage.protos.AccountRecord; import org.whispersystems.signalservice.internal.storage.protos.OptionalBool; import java.sql.Connection; @@ -21,6 +22,9 @@ import java.sql.SQLException; import java.util.Arrays; import java.util.Optional; +import static org.asamk.signal.manager.util.Utils.firstNonEmpty; +import static org.asamk.signal.manager.util.Utils.firstNonNull; + /** * Processes {@link SignalAccountRecord}s. */ @@ -43,10 +47,10 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor update) throws SQLException { final var accountRecord = update.newRecord(); + final var accountProto = accountRecord.getProto(); - if (!accountRecord.getE164().equals(account.getNumber())) { + if (!accountProto.e164.equals(account.getNumber())) { jobExecutor.enqueueJob(new CheckWhoAmIJob()); } - account.getConfigurationStore().setReadReceipts(connection, accountRecord.isReadReceiptsEnabled()); - account.getConfigurationStore().setTypingIndicators(connection, accountRecord.isTypingIndicatorsEnabled()); + account.getConfigurationStore().setReadReceipts(connection, accountProto.readReceipts); + account.getConfigurationStore().setTypingIndicators(connection, accountProto.typingIndicators); account.getConfigurationStore() - .setUnidentifiedDeliveryIndicators(connection, accountRecord.isSealedSenderIndicatorsEnabled()); - account.getConfigurationStore().setLinkPreviews(connection, accountRecord.isLinkPreviewsEnabled()); + .setUnidentifiedDeliveryIndicators(connection, accountProto.sealedSenderIndicators); + account.getConfigurationStore().setLinkPreviews(connection, accountProto.linkPreviews); account.getConfigurationStore() .setPhoneNumberSharingMode(connection, - StorageSyncModels.remoteToLocal(accountRecord.getPhoneNumberSharingMode())); - account.getConfigurationStore().setPhoneNumberUnlisted(connection, accountRecord.isPhoneNumberUnlisted()); + StorageSyncModels.remoteToLocal(accountProto.phoneNumberSharingMode)); + account.getConfigurationStore().setPhoneNumberUnlisted(connection, accountProto.unlistedPhoneNumber); - account.setUsername(accountRecord.getUsername() != null && !accountRecord.getUsername().isEmpty() - ? accountRecord.getUsername() - : null); - if (accountRecord.getUsernameLink() != null) { - final var usernameLink = accountRecord.getUsernameLink(); + account.setUsername(!accountProto.username.isEmpty() ? accountProto.username : null); + if (accountProto.usernameLink != null) { + final var usernameLink = accountProto.usernameLink; account.setUsernameLink(new UsernameLinkComponents(usernameLink.entropy.toByteArray(), UuidUtil.parseOrThrow(usernameLink.serverId.toByteArray()))); account.getConfigurationStore().setUsernameLinkColor(connection, usernameLink.color.name()); } - if (accountRecord.getProfileKey().isPresent()) { + if (accountProto.profileKey.size() > 0) { ProfileKey profileKey; try { - profileKey = new ProfileKey(accountRecord.getProfileKey().get()); + profileKey = new ProfileKey(accountProto.profileKey.toByteArray()); } catch (InvalidInputException e) { logger.debug("Received invalid profile key from storage"); profileKey = null; } if (profileKey != null) { account.setProfileKey(profileKey); - final var avatarPath = accountRecord.getAvatarUrlPath().orElse(null); + final var avatarPath = accountProto.avatarUrlPath.isEmpty() ? null : accountProto.avatarUrlPath; jobExecutor.enqueueJob(new DownloadProfileAvatarJob(avatarPath)); } } final var profile = account.getRecipientStore().getProfile(connection, account.getSelfRecipientId()); final var builder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile); - builder.withGivenName(accountRecord.getGivenName().orElse(null)); - builder.withFamilyName(accountRecord.getFamilyName().orElse(null)); + builder.withGivenName(accountProto.givenName); + builder.withFamilyName(accountProto.familyName); account.getRecipientStore().storeProfile(connection, account.getSelfRecipientId(), builder.build()); account.getRecipientStore() .storeStorageRecord(connection, account.getSelfRecipientId(), accountRecord.getId(), - accountRecord.toProto().encode()); + accountProto.encode()); } @Override @@ -221,7 +198,7 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor { private static final Logger logger = LoggerFactory.getLogger(ContactRecordProcessor.class); @@ -55,20 +61,24 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor getMatching(SignalContactRecord remote) throws SQLException { - final var address = getRecipientAddress(remote); + final var address = getRecipientAddress(remote.getProto()); final var recipientId = account.getRecipientStore().resolveRecipient(connection, address); final var recipient = account.getRecipientStore().getRecipient(connection, recipientId); @@ -85,141 +95,120 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor 0 && ( + !account.isPrimaryDevice() + || remote.identityState != local.identityState + || local.identityKey.size() == 0 )) { - identityState = remote.getIdentityState(); - identityKey = remote.getIdentityKey().get(); + identityState = remote.identityState; + identityKey = remote.identityKey; } else { - identityState = local.getIdentityState(); - identityKey = local.getIdentityKey().orElse(null); + identityState = local.identityState; + identityKey = local.identityKey.size() > 0 ? local.identityKey : ByteString.EMPTY; } - if (local.getAci().isPresent() - && local.getIdentityKey().isPresent() - && remote.getIdentityKey().isPresent() - && !Arrays.equals(local.getIdentityKey().get(), remote.getIdentityKey().get())) { + if (!local.aci.isEmpty() + && local.identityKey.size() > 0 + && remote.identityKey.size() > 0 + && !local.identityKey.equals(remote.identityKey)) { logger.debug("The local and remote identity keys do not match for {}. Enqueueing a profile fetch.", - local.getAci().orElse(null)); + local.aci); final var address = getRecipientAddress(local); jobExecutor.enqueueJob(new DownloadProfileJob(address)); } - final var e164sMatchButPnisDont = local.getNumber().isPresent() - && local.getNumber() - .get() - .equals(remote.getNumber().orElse(null)) - && local.getPni().isPresent() - && remote.getPni().isPresent() - && !local.getPni().get().equals(remote.getPni().get()); - - final var pnisMatchButE164sDont = local.getPni().isPresent() - && local.getPni() - .get() - .equals(remote.getPni().orElse(null)) - && local.getNumber().isPresent() - && remote.getNumber().isPresent() - && !local.getNumber().get().equals(remote.getNumber().get()); - - PNI pni; + String pni; String e164; - if (!account.isPrimaryDevice() && (e164sMatchButPnisDont || pnisMatchButE164sDont)) { - if (e164sMatchButPnisDont) { - logger.debug("Matching E164s, but the PNIs differ! Trusting our local pair."); - } else if (pnisMatchButE164sDont) { - logger.debug("Matching PNIs, but the E164s differ! Trusting our local pair."); + if (account.isPrimaryDevice()) { + final var e164sMatchButPnisDont = !local.e164.isEmpty() + && local.e164.equals(remote.e164) + && !local.pni.isEmpty() + && !remote.pni.isEmpty() + && !local.pni.equals(remote.pni); + + final var pnisMatchButE164sDont = !local.pni.isEmpty() + && local.pni.equals(remote.pni) + && !local.e164.isEmpty() + && !remote.e164.isEmpty() + && !local.e164.equals(remote.e164); + + if (e164sMatchButPnisDont || pnisMatchButE164sDont) { + if (e164sMatchButPnisDont) { + logger.debug("Matching E164s, but the PNIs differ! Trusting our local pair."); + } else if (pnisMatchButE164sDont) { + logger.debug("Matching PNIs, but the E164s differ! Trusting our local pair."); + } + jobExecutor.enqueueJob(new RefreshRecipientsJob()); + pni = local.pni; + e164 = local.e164; + } else { + pni = firstNonEmpty(remote.pni, local.pni); + e164 = firstNonEmpty(remote.e164, local.e164); } - jobExecutor.enqueueJob(new RefreshRecipientsJob()); - pni = local.getPni().get(); - e164 = local.getNumber().get(); } else { - pni = OptionalUtil.or(remote.getPni(), local.getPni()).orElse(null); - e164 = OptionalUtil.or(remote.getNumber(), local.getNumber()).orElse(null); + pni = firstNonEmpty(remote.pni, local.pni); + e164 = firstNonEmpty(remote.e164, local.e164); } - final var unknownFields = remote.serializeUnknownFields(); - final var aci = local.getAci().isEmpty() ? remote.getAci().orElse(null) : local.getAci().get(); - final var profileKey = OptionalUtil.or(remote.getProfileKey(), local.getProfileKey()).orElse(null); - final var username = OptionalUtil.or(remote.getUsername(), local.getUsername()).orElse(""); - final var blocked = remote.isBlocked(); - final var profileSharing = remote.isProfileSharingEnabled(); - final var archived = remote.isArchived(); - final var forcedUnread = remote.isForcedUnread(); - final var muteUntil = remote.getMuteUntil(); - final var hideStory = remote.shouldHideStory(); - final var unregisteredTimestamp = remote.getUnregisteredTimestamp(); - final var hidden = remote.isHidden(); - final var systemGivenName = account.isPrimaryDevice() - ? local.getSystemGivenName().orElse("") - : remote.getSystemGivenName().orElse(""); - final var systemFamilyName = account.isPrimaryDevice() - ? local.getSystemFamilyName().orElse("") - : remote.getSystemFamilyName().orElse(""); - final var systemNickname = remote.getSystemNickname().orElse(""); - final var nicknameGivenName = remote.getNicknameGivenName().orElse(""); - final var nicknameFamilyName = remote.getNicknameFamilyName().orElse(""); - final var pniSignatureVerified = remote.isPniSignatureVerified() || local.isPniSignatureVerified(); - final var note = remote.getNote().or(local::getNote).orElse(""); - - final var mergedBuilder = new SignalContactRecord.Builder(remote.getId().getRaw(), aci, unknownFields).setE164( - e164) - .setPni(pni) - .setProfileGivenName(profileGivenName) - .setProfileFamilyName(profileFamilyName) - .setSystemGivenName(systemGivenName) - .setSystemFamilyName(systemFamilyName) - .setSystemNickname(systemNickname) - .setProfileKey(profileKey) - .setUsername(username) - .setIdentityState(identityState) - .setIdentityKey(identityKey) - .setBlocked(blocked) - .setProfileSharingEnabled(profileSharing) - .setArchived(archived) - .setForcedUnread(forcedUnread) - .setMuteUntil(muteUntil) - .setHideStory(hideStory) - .setUnregisteredTimestamp(unregisteredTimestamp) - .setHidden(hidden) - .setPniSignatureVerified(pniSignatureVerified) - .setNicknameGivenName(nicknameGivenName) - .setNicknameFamilyName(nicknameFamilyName) - .setNote(note); + final var mergedBuilder = SignalContactRecord.Companion.newBuilder(remote.unknownFields().toByteArray()) + .aci(local.aci.isEmpty() ? remote.aci : local.aci) + .e164(e164) + .pni(pni) + .givenName(profileGivenName) + .familyName(profileFamilyName) + .systemGivenName(account.isPrimaryDevice() ? local.systemGivenName : remote.systemGivenName) + .systemFamilyName(account.isPrimaryDevice() ? local.systemFamilyName : remote.systemFamilyName) + .systemNickname(remote.systemNickname) + .profileKey(firstNonEmpty(remote.profileKey, local.profileKey)) + .username(firstNonEmpty(remote.username, local.username)) + .identityState(identityState) + .identityKey(identityKey) + .blocked(remote.blocked) + .whitelisted(remote.whitelisted) + .archived(remote.archived) + .markedUnread(remote.markedUnread) + .mutedUntilTimestamp(remote.mutedUntilTimestamp) + .hideStory(remote.hideStory) + .unregisteredAtTimestamp(remote.unregisteredAtTimestamp) + .hidden(remote.hidden) + .pniSignatureVerified(remote.pniSignatureVerified || local.pniSignatureVerified) + .nickname(remote.nickname) + .note(remote.note); final var merged = mergedBuilder.build(); final var matchesRemote = doProtosMatch(merged, remote); if (matchesRemote) { - return remote; + return remoteRecord; } final var matchesLocal = doProtosMatch(merged, local); if (matchesLocal) { - return local; + return localRecord; } - return mergedBuilder.setId(KeyUtils.createRawStorageId()).build(); + return new SignalContactRecord(StorageId.forContact(KeyUtils.createRawStorageId()), mergedBuilder.build()); } @Override @@ -231,7 +220,8 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor update) throws SQLException { final var contactRecord = update.newRecord(); - final var address = getRecipientAddress(contactRecord); + final var contactProto = contactRecord.getProto(); + final var address = getRecipientAddress(contactProto); final var recipientId = account.getRecipientStore().resolveRecipientTrusted(connection, address); final var recipient = account.getRecipientStore().getRecipient(connection, recipientId); @@ -251,95 +241,92 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor

- * The monitor is also responsible for sending heartbeats/keep-alive messages to prevent - * timeouts. - */ final class SignalWebSocketHealthMonitor implements HealthMonitor { private static final Logger logger = LoggerFactory.getLogger(SignalWebSocketHealthMonitor.class); + /** + * This is the amount of time in between sent keep alives. Must be greater than [KEEP_ALIVE_TIMEOUT] + */ private static final long KEEP_ALIVE_SEND_CADENCE = TimeUnit.SECONDS.toMillis(OkHttpWebSocketConnection.KEEPALIVE_FREQUENCY_SECONDS); - private static final long MAX_TIME_SINCE_SUCCESSFUL_KEEP_ALIVE = KEEP_ALIVE_SEND_CADENCE * 3; - private SignalWebSocket signalWebSocket; + /** + * This is the amount of time we will wait for a response to the keep alive before we consider the websockets dead. + * It is required that this value be less than [KEEP_ALIVE_SEND_CADENCE] + */ + private static final long KEEP_ALIVE_TIMEOUT = TimeUnit.SECONDS.toMillis(20); + + private final Executor executor = Executors.newSingleThreadExecutor(); private final SleepTimer sleepTimer; - - private volatile KeepAliveSender keepAliveSender; - - private final HealthState identified = new HealthState(); - private final HealthState unidentified = new HealthState(); + private SignalWebSocket webSocket = null; + private volatile KeepAliveSender keepAliveSender = null; + private boolean needsKeepAlive = false; + private long lastKeepAliveReceived = 0; public SignalWebSocketHealthMonitor(SleepTimer sleepTimer) { this.sleepTimer = sleepTimer; } - public void monitor(SignalWebSocket signalWebSocket) { - Preconditions.checkNotNull(signalWebSocket); - Preconditions.checkArgument(this.signalWebSocket == null, "monitor can only be called once"); + void monitor(SignalWebSocket webSocket) { + Preconditions.checkNotNull(webSocket); + Preconditions.checkArgument(this.webSocket == null, "monitor can only be called once"); - this.signalWebSocket = signalWebSocket; + executor.execute(() -> { - //noinspection ResultOfMethodCallIgnored - signalWebSocket.getWebSocketState() - .subscribeOn(Schedulers.computation()) - .observeOn(Schedulers.computation()) - .distinctUntilChanged() - .subscribe(s -> onStateChange(s, identified)); + this.webSocket = webSocket; - //noinspection ResultOfMethodCallIgnored - signalWebSocket.getUnidentifiedWebSocketState() - .subscribeOn(Schedulers.computation()) - .observeOn(Schedulers.computation()) - .distinctUntilChanged() - .subscribe(s -> onStateChange(s, unidentified)); + webSocket.getState() + .subscribeOn(Schedulers.computation()) + .observeOn(Schedulers.computation()) + .distinctUntilChanged() + .subscribe(this::onStateChanged); + + webSocket.setKeepAliveChangedListener(this::updateKeepAliveSenderStatus); + }); } - private synchronized void onStateChange(WebSocketConnectionState connectionState, HealthState healthState) { - switch (connectionState) { - case CONNECTED -> logger.debug("WebSocket is now connected"); - case AUTHENTICATION_FAILED -> logger.debug("WebSocket authentication failed"); - case FAILED -> logger.debug("WebSocket connection failed"); - } + private void onStateChanged(WebSocketConnectionState connectionState) { + executor.execute(() -> { + needsKeepAlive = connectionState == WebSocketConnectionState.CONNECTED; - healthState.needsKeepAlive = connectionState == WebSocketConnectionState.CONNECTED; - - if (keepAliveSender == null && isKeepAliveNecessary()) { - keepAliveSender = new KeepAliveSender(); - keepAliveSender.start(); - } else if (keepAliveSender != null && !isKeepAliveNecessary()) { - keepAliveSender.shutdown(); - keepAliveSender = null; - } + updateKeepAliveSenderStatus(); + }); } @Override public void onKeepAliveResponse(long sentTimestamp, boolean isIdentifiedWebSocket) { - if (isIdentifiedWebSocket) { - identified.lastKeepAliveReceived = System.currentTimeMillis(); - } else { - unidentified.lastKeepAliveReceived = System.currentTimeMillis(); - } + final var keepAliveTime = System.currentTimeMillis(); + executor.execute(() -> lastKeepAliveReceived = keepAliveTime); } @Override public void onMessageError(int status, boolean isIdentifiedWebSocket) { - if (status == 409) { - HealthState healthState = (isIdentifiedWebSocket ? identified : unidentified); - if (healthState.mismatchErrorTracker.addSample(System.currentTimeMillis())) { - logger.warn("Received too many mismatch device errors, forcing new websockets."); - signalWebSocket.forceNewWebSockets(); - signalWebSocket.connect(); - } + } + + private Unit updateKeepAliveSenderStatus() { + if (keepAliveSender == null && sendKeepAlives()) { + keepAliveSender = new KeepAliveSender(); + keepAliveSender.start(); + } else if (keepAliveSender != null && !sendKeepAlives()) { + keepAliveSender.shutdown(); + keepAliveSender = null; } + return Unit.INSTANCE; } - private boolean isKeepAliveNecessary() { - return identified.needsKeepAlive || unidentified.needsKeepAlive; - } - - private static class HealthState { - - private final HttpErrorTracker mismatchErrorTracker = new HttpErrorTracker(5, TimeUnit.MINUTES.toMillis(1)); - - private volatile boolean needsKeepAlive; - private volatile long lastKeepAliveReceived; + private boolean sendKeepAlives() { + return needsKeepAlive && webSocket != null && webSocket.getShouldSendKeepAlives(); } /** - * Sends periodic heartbeats/keep-alives over both WebSockets to prevent connection timeouts. If - * either WebSocket fails 3 times to get a return heartbeat both are forced to be recreated. + * Sends periodic heartbeats/keep-alives over the WebSocket to prevent connection timeouts. If + * the WebSocket fails to get a return heartbeat after [KEEP_ALIVE_TIMEOUT] seconds, it is forced to be recreated. */ - private class KeepAliveSender extends Thread { + private final class KeepAliveSender extends Thread { private volatile boolean shouldKeepRunning = true; + @Override public void run() { - identified.lastKeepAliveReceived = System.currentTimeMillis(); - unidentified.lastKeepAliveReceived = System.currentTimeMillis(); + logger.debug("[KeepAliveSender({})] started", this.threadId()); + lastKeepAliveReceived = System.currentTimeMillis(); - while (shouldKeepRunning && isKeepAliveNecessary()) { + var keepAliveSendTime = System.currentTimeMillis(); + while (shouldKeepRunning && sendKeepAlives()) { try { - sleepTimer.sleep(KEEP_ALIVE_SEND_CADENCE); + final var nextKeepAliveSendTime = keepAliveSendTime + KEEP_ALIVE_SEND_CADENCE; + sleepUntil(nextKeepAliveSendTime); - if (shouldKeepRunning && isKeepAliveNecessary()) { - long keepAliveRequiredSinceTime = System.currentTimeMillis() - - MAX_TIME_SINCE_SUCCESSFUL_KEEP_ALIVE; + if (shouldKeepRunning && sendKeepAlives()) { + keepAliveSendTime = System.currentTimeMillis(); + webSocket.sendKeepAlive(); + } - if (identified.lastKeepAliveReceived < keepAliveRequiredSinceTime - || unidentified.lastKeepAliveReceived < keepAliveRequiredSinceTime) { - logger.warn("Missed keep alives, identified last: " - + identified.lastKeepAliveReceived - + " unidentified last: " - + unidentified.lastKeepAliveReceived - + " needed by: " - + keepAliveRequiredSinceTime); - signalWebSocket.forceNewWebSockets(); - signalWebSocket.connect(); - } else { - signalWebSocket.sendKeepAlive(); + final var responseRequiredTime = keepAliveSendTime + KEEP_ALIVE_TIMEOUT; + sleepUntil(responseRequiredTime); + + if (shouldKeepRunning && sendKeepAlives()) { + if (lastKeepAliveReceived < keepAliveSendTime) { + logger.debug("Missed keep alive, last: {} needed by: {}", + lastKeepAliveReceived, + responseRequiredTime); + webSocket.forceNewWebSocket(); } } } catch (Throwable e) { - logger.warn("Error occurred in KeepAliveSender, ignoring ...", e); + logger.warn("Keep alive sender failed", e); + } + } + logger.debug("[KeepAliveSender({})] ended", threadId()); + } + + void sleepUntil(long timeMillis) { + while (System.currentTimeMillis() < timeMillis) { + final var waitTime = timeMillis - System.currentTimeMillis(); + if (waitTime > 0) { + try { + sleepTimer.sleep(waitTime); + } catch (InterruptedException e) { + logger.warn("WebSocket health monitor interrupted", e); + } } } } - public void shutdown() { + void shutdown() { shouldKeepRunning = false; } } - - private static final class HttpErrorTracker { - - private final long[] timestamps; - private final long errorTimeRange; - - public HttpErrorTracker(int samples, long errorTimeRange) { - this.timestamps = new long[samples]; - this.errorTimeRange = errorTimeRange; - } - - public synchronized boolean addSample(long now) { - long errorsMustBeAfter = now - errorTimeRange; - int count = 1; - int minIndex = 0; - - for (int i = 0; i < timestamps.length; i++) { - if (timestamps[i] < errorsMustBeAfter) { - timestamps[i] = 0; - } else if (timestamps[i] != 0) { - count++; - } - - if (timestamps[i] < timestamps[minIndex]) { - minIndex = i; - } - } - - timestamps[minIndex] = now; - - if (count >= timestamps.length) { - Arrays.fill(timestamps, 0); - return true; - } - return false; - } - } } + diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java index a0fa3012..7be4a401 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java @@ -1,6 +1,7 @@ package org.asamk.signal.manager.storage.accounts; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.i18n.phonenumbers.PhoneNumberUtil; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.ServiceEnvironment; @@ -10,7 +11,6 @@ import org.asamk.signal.manager.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.ServiceId.ACI; -import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -181,7 +181,7 @@ public class AccountsStore { return Arrays.stream(files) .filter(File::isFile) .map(File::getName) - .filter(file -> PhoneNumberFormatter.isValidNumber(file, null)) + .filter(file -> PhoneNumberUtil.getInstance().isPossibleNumber(file, null)) .collect(Collectors.toSet()); } diff --git a/lib/src/main/java/org/asamk/signal/manager/syncStorage/ContactRecordProcessor.java b/lib/src/main/java/org/asamk/signal/manager/syncStorage/ContactRecordProcessor.java index 2863874b..d4841c7a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/syncStorage/ContactRecordProcessor.java +++ b/lib/src/main/java/org/asamk/signal/manager/syncStorage/ContactRecordProcessor.java @@ -195,7 +195,8 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor localNumber.length()) return "+" + number; + + int difference = localNumber.length() - number.length(); + + return "+" + localNumber.substring(0, difference) + number; + } + + public static String formatNumber(String number, String localNumber) throws InvalidNumberException { + if (number == null) { + throw new InvalidNumberException("Null String passed as number."); + } + + if (number.contains("@")) { + throw new InvalidNumberException("Possible attempt to use email address."); + } + + number = number.replaceAll("[^0-9+]", ""); + + if (number.isEmpty()) { + throw new InvalidNumberException("No valid characters found."); + } + + try { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumber localNumberObject = util.parse(localNumber, null); + + String localCountryCode = util.getRegionCodeForNumber(localNumberObject); + logger.trace("Got local CC: {}", localCountryCode); + + PhoneNumber numberObject = util.parse(number, localCountryCode); + return util.format(numberObject, PhoneNumberFormat.E164); + } catch (NumberParseException e) { + logger.debug("{}: {}", e.getClass().getSimpleName(), e.getMessage()); + return impreciseFormatNumber(number, localNumber); + } + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java index b90a7873..9d97ed7a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java @@ -162,7 +162,11 @@ public class Utils { throw new IOException(throwableOptional); } } - return response.successOrThrow(); + try { + return response.successOrThrow(); + } catch (Throwable e) { + throw new AssertionError(e); + } } public static ByteString firstNonEmpty(ByteString... strings) { From 1295ef69ca8a794b40abd0dd6290fd2ca250713a Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 16 Mar 2025 22:07:29 +0100 Subject: [PATCH 079/144] Use record patterns --- .../manager/helper/RecipientHelper.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java index 58e7ba59..5a3524ec 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java @@ -25,6 +25,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.UUID; import static org.asamk.signal.manager.config.ServiceConfig.MAXIMUM_ONE_OFF_REQUEST_SIZE; import static org.asamk.signal.manager.util.Utils.handleResponseException; @@ -77,12 +78,11 @@ public class RecipientHelper { } public RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws UnregisteredRecipientException { - if (recipient instanceof RecipientIdentifier.Uuid uuidRecipient) { - return account.getRecipientResolver().resolveRecipient(ACI.from(uuidRecipient.uuid())); - } else if (recipient instanceof RecipientIdentifier.Pni pniRecipient) { - return account.getRecipientResolver().resolveRecipient(PNI.from(pniRecipient.pni())); - } else if (recipient instanceof RecipientIdentifier.Number numberRecipient) { - final var number = numberRecipient.number(); + if (recipient instanceof RecipientIdentifier.Uuid(UUID uuid)) { + return account.getRecipientResolver().resolveRecipient(ACI.from(uuid)); + } else if (recipient instanceof RecipientIdentifier.Pni(UUID pni)) { + return account.getRecipientResolver().resolveRecipient(PNI.from(pni)); + } else if (recipient instanceof RecipientIdentifier.Number(String number)) { return account.getRecipientStore().resolveRecipientByNumber(number, () -> { try { return getRegisteredUserByNumber(number); @@ -90,8 +90,7 @@ public class RecipientHelper { return null; } }); - } else if (recipient instanceof RecipientIdentifier.Username usernameRecipient) { - var username = usernameRecipient.username(); + } else if (recipient instanceof RecipientIdentifier.Username(String username)) { return resolveRecipientByUsernameOrLink(username, false); } throw new AssertionError("Unexpected RecipientIdentifier: " + recipient); @@ -145,8 +144,8 @@ public class RecipientHelper { try { return Optional.of(resolveRecipient(recipient)); } catch (UnregisteredRecipientException e) { - if (recipient instanceof RecipientIdentifier.Number r) { - return account.getRecipientStore().resolveRecipientByNumberOptional(r.number()); + if (recipient instanceof RecipientIdentifier.Number(String number)) { + return account.getRecipientStore().resolveRecipientByNumberOptional(number); } else { return Optional.empty(); } From f18015ff2ef10d49f8ab0365253e8cece9577dc2 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 18 Mar 2025 18:13:38 +0100 Subject: [PATCH 080/144] f --- graalvm-config-dir/reflect-config.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 481999d3..50e17987 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2991,6 +2991,9 @@ "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$UsernameLink", "allDeclaredFields":true }, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.AvatarColor" +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord", "allDeclaredFields":true, From 67302eb9c31e3bcce4c39ea90d5bdb1f77306a02 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 18 Mar 2025 18:20:44 +0100 Subject: [PATCH 081/144] Replace cached envelopes when moving Fixes #1730 --- .../signal/manager/storage/messageCache/MessageCache.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java b/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java index 51882a44..e4fd8c01 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java @@ -10,6 +10,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.Arrays; import java.util.Collections; import java.util.Objects; @@ -75,7 +76,7 @@ public class MessageCache { return cachedMessage; } logger.debug("Moving cached message {} to {}", cachedMessage.getFile().toPath(), cacheFile.toPath()); - Files.move(cachedMessage.getFile().toPath(), cacheFile.toPath()); + Files.move(cachedMessage.getFile().toPath(), cacheFile.toPath(), StandardCopyOption.REPLACE_EXISTING); return new CachedMessage(cacheFile); } From 6befda7ef1dabc760db516be5080f1c0bb1c657c Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 22 Mar 2025 10:55:16 +0100 Subject: [PATCH 082/144] Update graalvm build tools --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 67a180fb..38c3e265 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ plugins { application eclipse `check-lib-versions` - id("org.graalvm.buildtools.native") version "0.10.5" + id("org.graalvm.buildtools.native") version "0.10.6" } allprojects { From 14b07be0dcc9d61eeebc857338367515a3e338b3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 31 Mar 2025 09:11:05 +0200 Subject: [PATCH 083/144] Always renew session when failing to decrypt message --- .../signal/manager/actions/RenewSessionAction.java | 4 +--- .../actions/SendRetryMessageRequestAction.java | 11 +---------- .../signal/manager/helper/IncomingMessageHandler.java | 7 +++---- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/RenewSessionAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/RenewSessionAction.java index 40117246..35058940 100644 --- a/lib/src/main/java/org/asamk/signal/manager/actions/RenewSessionAction.java +++ b/lib/src/main/java/org/asamk/signal/manager/actions/RenewSessionAction.java @@ -19,9 +19,7 @@ public class RenewSessionAction implements HandleAction { @Override public void execute(Context context) throws Throwable { context.getAccount().getAccountData(accountId).getSessionStore().archiveSessions(serviceId); - if (!recipientId.equals(context.getAccount().getSelfRecipientId())) { - context.getSendHelper().sendNullMessage(recipientId); - } + context.getSendHelper().sendNullMessage(recipientId); } @Override diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/SendRetryMessageRequestAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/SendRetryMessageRequestAction.java index 865f062f..96a94128 100644 --- a/lib/src/main/java/org/asamk/signal/manager/actions/SendRetryMessageRequestAction.java +++ b/lib/src/main/java/org/asamk/signal/manager/actions/SendRetryMessageRequestAction.java @@ -7,7 +7,6 @@ import org.signal.libsignal.metadata.ProtocolException; import org.signal.libsignal.protocol.message.CiphertextMessage; import org.signal.libsignal.protocol.message.DecryptionErrorMessage; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.internal.push.Envelope; import java.util.Optional; @@ -15,29 +14,21 @@ import java.util.Optional; public class SendRetryMessageRequestAction implements HandleAction { private final RecipientId recipientId; - private final ServiceId serviceId; private final ProtocolException protocolException; private final SignalServiceEnvelope envelope; - private final ServiceId accountId; public SendRetryMessageRequestAction( final RecipientId recipientId, - final ServiceId serviceId, final ProtocolException protocolException, - final SignalServiceEnvelope envelope, - final ServiceId accountId + final SignalServiceEnvelope envelope ) { this.recipientId = recipientId; - this.serviceId = serviceId; this.protocolException = protocolException; this.envelope = envelope; - this.accountId = accountId; } @Override public void execute(Context context) throws Throwable { - context.getAccount().getAccountData(accountId).getSessionStore().archiveSessions(serviceId); - int senderDevice = protocolException.getSenderDevice(); Optional groupId = protocolException.getGroupId().isPresent() ? Optional.of(GroupId.unknownVersion( protocolException.getGroupId().get())) : Optional.empty(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index 90d67a36..1e808e64 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -165,12 +165,11 @@ public final class IncomingMessageHandler { if (serviceId != null) { final var isSelf = sender.equals(account.getSelfRecipientId()) && e.getSenderDevice() == account.getDeviceId(); + logger.debug("Received invalid message, queuing renew session action."); + actions.add(new RenewSessionAction(sender, serviceId, destination)); if (!isSelf) { logger.debug("Received invalid message, requesting message resend."); - actions.add(new SendRetryMessageRequestAction(sender, serviceId, e, envelope, destination)); - } else { - logger.debug("Received invalid message, queuing renew session action."); - actions.add(new RenewSessionAction(sender, serviceId, destination)); + actions.add(new SendRetryMessageRequestAction(sender, e, envelope)); } } else { logger.debug("Received invalid message from invalid sender: {}", e.getSender()); From adb6787d5b3d1dae30669e38ca6cdfaf753f6578 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 31 Mar 2025 09:11:28 +0200 Subject: [PATCH 084/144] Refresh prekeys when receiving message with invalid key id --- .../asamk/signal/manager/helper/IncomingMessageHandler.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index 1e808e64..3bb7479a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -157,6 +157,9 @@ public final class IncomingMessageHandler { } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException | ProtocolInvalidMessageException e) { logger.debug("Failed to decrypt incoming message", e); + if (e instanceof ProtocolInvalidKeyIdException) { + actions.add(RefreshPreKeysAction.create()); + } final var sender = account.getRecipientResolver().resolveRecipient(e.getSender()); if (context.getContactHelper().isContactBlocked(sender)) { logger.debug("Received invalid message from blocked contact, ignoring."); From 3dc8844cb4e1feb916e5af37cb7817312e033cb8 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 31 Mar 2025 09:19:20 +0200 Subject: [PATCH 085/144] Update libraries --- gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b8d57a12..5d633271 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,15 +3,15 @@ slf4j = "2.0.17" [libraries] bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.80" -jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.18.2" +jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.18.3" argparse4j = "net.sourceforge.argparse4j:argparse4j:0.9.0" dbusjava = "com.github.hypfvieh:dbus-java-transport-native-unixsocket:5.0.0" slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } -logback = "ch.qos.logback:logback-classic:1.5.17" +logback = "ch.qos.logback:logback-classic:1.5.18" signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_119" sqlite = "org.xerial:sqlite-jdbc:3.49.1.0" -hikari = "com.zaxxer:HikariCP:6.2.1" +hikari = "com.zaxxer:HikariCP:6.3.0" junit-jupiter = "org.junit.jupiter:junit-jupiter:5.12.0" junit-launcher = "org.junit.platform:junit-platform-launcher:1.12.0" From 69a9b30732aac5ff79012e98f844527074f391a1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 31 Mar 2025 14:45:02 +0200 Subject: [PATCH 086/144] Update libsignal-service --- gradle/libs.versions.toml | 2 +- .../java/org/asamk/signal/manager/helper/ReceiveHelper.java | 2 ++ .../asamk/signal/manager/internal/SignalDependencies.java | 5 +++-- .../manager/internal/SignalWebSocketHealthMonitor.java | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5d633271..49f54ff8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.18" -signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_119" +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_120" sqlite = "org.xerial:sqlite-jdbc:3.49.1.0" hikari = "com.zaxxer:HikariCP:6.3.0" junit-jupiter = "org.junit.jupiter:junit-jupiter:5.12.0" diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java index eb935188..71f69081 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java @@ -100,6 +100,7 @@ public class ReceiveHelper { .distinctUntilChanged() .subscribe(this::onWebSocketStateChange); signalWebSocket.connect(); + signalWebSocket.registerKeepAliveToken("receive"); try { receiveMessagesInternal(signalWebSocket, timeout, returnOnTimeout, maxMessages, handler, queuedActions); @@ -107,6 +108,7 @@ public class ReceiveHelper { hasCaughtUpWithOldMessages = false; handleQueuedActions(queuedActions.keySet()); queuedActions.clear(); + signalWebSocket.removeKeepAliveToken("receive"); signalWebSocket.disconnect(); webSocketStateDisposable.dispose(); shouldStop = false; diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java index 972b422f..c19e6b66 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java @@ -41,6 +41,7 @@ import java.net.Proxy; import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; public class SignalDependencies { @@ -257,7 +258,7 @@ public class SignalDependencies { Optional.of(credentialsProvider), userAgent, healthMonitor, - allowStories)); + allowStories), timer, TimeUnit.SECONDS.toMillis(10)); healthMonitor.monitor(authenticatedSignalWebSocket); }); } @@ -273,7 +274,7 @@ public class SignalDependencies { Optional.empty(), userAgent, healthMonitor, - allowStories)); + allowStories), timer, TimeUnit.SECONDS.toMillis(10)); healthMonitor.monitor(unauthenticatedSignalWebSocket); }); } diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalWebSocketHealthMonitor.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalWebSocketHealthMonitor.java index b5b855ac..dfae5985 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalWebSocketHealthMonitor.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalWebSocketHealthMonitor.java @@ -90,7 +90,7 @@ final class SignalWebSocketHealthMonitor implements HealthMonitor { } private boolean sendKeepAlives() { - return needsKeepAlive && webSocket != null && webSocket.getShouldSendKeepAlives(); + return needsKeepAlive && webSocket != null && webSocket.shouldSendKeepAlives(); } /** From db2182aa7d8b44a49691f7e6d49bac9961f841eb Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 6 Apr 2025 19:37:18 +0200 Subject: [PATCH 087/144] Update libsignal-service --- graalvm-config-dir/jni-config.json | 4 ++ graalvm-config-dir/reflect-config.json | 29 +++++++++++- gradle/libs.versions.toml | 2 +- .../signal/manager/config/ServiceConfig.java | 3 +- .../signal/manager/helper/PreKeyHelper.java | 8 ++-- .../signal/manager/helper/ProfileHelper.java | 10 +++-- .../helper/UnidentifiedAccessHelper.java | 7 ++- .../manager/internal/SignalDependencies.java | 45 +++++++++++++++++-- .../org/asamk/signal/manager/util/Utils.java | 15 +------ run_tests.sh | 1 + 10 files changed, 94 insertions(+), 30 deletions(-) diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index 7c68c6d6..0284d58f 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -90,6 +90,10 @@ "name":"org.signal.libsignal.internal.CompletableFuture", "methods":[{"name":"","parameterTypes":[] }, {"name":"complete","parameterTypes":["java.lang.Object"] }, {"name":"completeExceptionally","parameterTypes":["java.lang.Throwable"] }] }, +{ + "name":"org.signal.libsignal.internal.NativeHandleGuard$SimpleOwner", + "methods":[{"name":"unsafeNativeHandleWithoutGuard","parameterTypes":[] }] +}, { "name":"org.signal.libsignal.net.CdsiLookupResponse", "methods":[{"name":"","parameterTypes":["java.util.Map","int"] }] diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 50e17987..cdf38732 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -39,6 +39,9 @@ { "name":"[Ljava.sql.Statement;" }, +{ + "name":"[Lorg.asamk.signal.commands.ListStickerPacksCommand$JsonStickerPack$JsonSticker;" +}, { "name":"[Lorg.asamk.signal.json.JsonAttachment;" }, @@ -2302,7 +2305,7 @@ "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "methods":[{"name":"getAnnouncementGroup","parameterTypes":[] }, {"name":"getChangeNumber","parameterTypes":[] }, {"name":"getDeleteSync","parameterTypes":[] }, {"name":"getGiftBadges","parameterTypes":[] }, {"name":"getPaymentActivation","parameterTypes":[] }, {"name":"getPni","parameterTypes":[] }, {"name":"getSenderKey","parameterTypes":[] }, {"name":"getStorage","parameterTypes":[] }, {"name":"getStorageServiceEncryptionV2","parameterTypes":[] }, {"name":"getStories","parameterTypes":[] }, {"name":"getVersionedExpirationTimer","parameterTypes":[] }] + "methods":[{"name":"getAnnouncementGroup","parameterTypes":[] }, {"name":"getAttachmentBackfill","parameterTypes":[] }, {"name":"getChangeNumber","parameterTypes":[] }, {"name":"getDeleteSync","parameterTypes":[] }, {"name":"getGiftBadges","parameterTypes":[] }, {"name":"getPaymentActivation","parameterTypes":[] }, {"name":"getPni","parameterTypes":[] }, {"name":"getSenderKey","parameterTypes":[] }, {"name":"getStorage","parameterTypes":[] }, {"name":"getStorageServiceEncryptionV2","parameterTypes":[] }, {"name":"getStories","parameterTypes":[] }, {"name":"getVersionedExpirationTimer","parameterTypes":[] }] }, { "name":"org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest", @@ -2329,6 +2332,13 @@ { "name":"org.whispersystems.signalservice.api.groupsv2.TemporalCredential[]" }, +{ + "name":"org.whispersystems.signalservice.api.keys.OneTimePreKeyCounts", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.api.link.LinkedDeviceVerificationCodeResponse", "allDeclaredFields":true, @@ -2396,7 +2406,14 @@ "name":"org.whispersystems.signalservice.api.profiles.SignalServiceProfileWrite", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true + "allDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":["java.lang.String","byte[]","byte[]","byte[]","byte[]","byte[]","boolean","boolean","byte[]","java.util.List"] }, {"name":"getAbout","parameterTypes":[] }, {"name":"getAboutEmoji","parameterTypes":[] }, {"name":"getAvatar","parameterTypes":[] }, {"name":"getBadgeIds","parameterTypes":[] }, {"name":"getCommitment","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPaymentAddress","parameterTypes":[] }, {"name":"getPhoneNumberSharing","parameterTypes":[] }, {"name":"getSameAvatar","parameterTypes":[] }, {"name":"getVersion","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.signalservice.api.provisioning.ProvisioningMessage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true }, { "name":"org.whispersystems.signalservice.api.push.ServiceId", @@ -2441,6 +2458,12 @@ "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] }, +{ + "name":"org.whispersystems.signalservice.api.ratelimit.SubmitRecaptchaChallengePayload", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.storage.StorageAuthResponse", "allDeclaredFields":true, @@ -2965,6 +2988,7 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord", "allDeclaredFields":true, + "fields":[{"name":"avatarColor"}, {"name":"avatarUrlPath"}, {"name":"backupSubscriberData"}, {"name":"backupTier"}, {"name":"displayBadgesOnProfile"}, {"name":"e164"}, {"name":"familyName"}, {"name":"givenName"}, {"name":"hasBackup"}, {"name":"hasCompletedUsernameOnboarding"}, {"name":"hasSeenGroupStoryEducationSheet"}, {"name":"hasSetMyStoriesPrivacy"}, {"name":"hasViewedOnboardingStory"}, {"name":"keepMutedChatsArchived"}, {"name":"linkPreviews"}, {"name":"noteToSelfArchived"}, {"name":"noteToSelfMarkedUnread"}, {"name":"payments"}, {"name":"phoneNumberSharingMode"}, {"name":"pinnedConversations"}, {"name":"preferContactAvatars"}, {"name":"preferredReactionEmoji"}, {"name":"primarySendsSms"}, {"name":"profileKey"}, {"name":"readReceipts"}, {"name":"sealedSenderIndicators"}, {"name":"storiesDisabled"}, {"name":"storyViewReceiptsEnabled"}, {"name":"subscriberCurrencyCode"}, {"name":"subscriberId"}, {"name":"subscriptionManuallyCancelled"}, {"name":"typingIndicators"}, {"name":"universalExpireTimer"}, {"name":"unlistedPhoneNumber"}, {"name":"username"}, {"name":"usernameLink"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { @@ -3027,6 +3051,7 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record", "allDeclaredFields":true, + "fields":[{"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"dontNotifyForMentionsIfMuted"}, {"name":"hideStory"}, {"name":"markedUnread"}, {"name":"masterKey"}, {"name":"mutedUntilTimestamp"}, {"name":"storySendMode"}, {"name":"whitelisted"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 49f54ff8..74f97a76 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.18" -signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_120" +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_121" sqlite = "org.xerial:sqlite-jdbc:3.49.1.0" hikari = "com.zaxxer:HikariCP:6.3.0" junit-jupiter = "org.junit.jupiter:junit-jupiter:5.12.0" diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java index fed6f457..587fc89b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java @@ -30,7 +30,8 @@ public class ServiceConfig { public static AccountAttributes.Capabilities getCapabilities(boolean isPrimaryDevice) { final var deleteSync = !isPrimaryDevice; final var storageEncryptionV2 = !isPrimaryDevice; - return new AccountAttributes.Capabilities(true, deleteSync, true, storageEncryptionV2); + final var attachmentBackfill = !isPrimaryDevice; + return new AccountAttributes.Capabilities(true, deleteSync, true, storageEncryptionV2, attachmentBackfill); } public static ServiceEnvironmentConfig getServiceEnvironmentConfig( diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java index b17a9206..bf7ad580 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/PreKeyHelper.java @@ -11,17 +11,19 @@ import org.signal.libsignal.protocol.state.PreKeyRecord; import org.signal.libsignal.protocol.state.SignedPreKeyRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.NetworkResultUtil; import org.whispersystems.signalservice.api.account.PreKeyUpload; +import org.whispersystems.signalservice.api.keys.OneTimePreKeyCounts; import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; -import org.whispersystems.signalservice.internal.push.OneTimePreKeyCounts; import java.io.IOException; import java.util.List; import static org.asamk.signal.manager.config.ServiceConfig.PREKEY_STALE_AGE; import static org.asamk.signal.manager.config.ServiceConfig.SIGNED_PREKEY_ROTATE_AGE; +import static org.asamk.signal.manager.util.Utils.handleResponseException; public class PreKeyHelper { @@ -82,7 +84,7 @@ public class PreKeyHelper { ) throws IOException { OneTimePreKeyCounts preKeyCounts; try { - preKeyCounts = dependencies.getAccountManager().getPreKeyCounts(serviceIdType); + preKeyCounts = handleResponseException(dependencies.getKeysApi().getAvailablePreKeyCounts(serviceIdType)); } catch (AuthorizationFailedException e) { logger.debug("Failed to get pre key count, ignoring: " + e.getClass().getSimpleName()); preKeyCounts = new OneTimePreKeyCounts(0, 0); @@ -143,7 +145,7 @@ public class PreKeyHelper { kyberPreKeyRecords); var needsReset = false; try { - dependencies.getAccountManager().setPreKeys(preKeyUpload); + NetworkResultUtil.toPreKeysLegacy(dependencies.getKeysApi().setPreKeys(preKeyUpload)); try { if (preKeyRecords != null) { account.addPreKeys(serviceIdType, preKeyRecords); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java index 0905bb30..af16ed1d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -23,6 +23,7 @@ import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.NetworkResultUtil; import org.whispersystems.signalservice.api.crypto.SealedSenderAccess; import org.whispersystems.signalservice.api.profiles.AvatarUploadParams; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; @@ -196,9 +197,10 @@ public final class ProfileHelper { : avatar == null ? AvatarUploadParams.unchanged(true) : AvatarUploadParams.unchanged(false); final var paymentsAddress = Optional.ofNullable(newProfile.getMobileCoinAddress()) .map(address -> PaymentUtils.signPaymentsAddress(address, - account.getAciIdentityKeyPair().getPrivateKey())); + account.getAciIdentityKeyPair().getPrivateKey())) + .orElse(null); logger.debug("Uploading new profile"); - final var avatarPath = dependencies.getAccountManager() + final var avatarPath = NetworkResultUtil.toSetProfileLegacy(dependencies.getProfileApi() .setVersionedProfile(account.getAci(), account.getProfileKey(), newProfile.getInternalServiceName(), @@ -208,9 +210,9 @@ public final class ProfileHelper { avatarUploadParams, List.of(/* TODO implement support for badges */), account.getConfigurationStore().getPhoneNumberSharingMode() - == PhoneNumberSharingMode.EVERYBODY); + == PhoneNumberSharingMode.EVERYBODY)); if (!avatarUploadParams.keepTheSame) { - builder.withAvatarUrlPath(avatarPath.orElse(null)); + builder.withAvatarUrlPath(avatarPath); } newProfile = builder.build(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java index fb39789e..cd2719a1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java @@ -18,6 +18,8 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; +import static org.asamk.signal.manager.util.Utils.handleResponseException; + public class UnidentifiedAccessHelper { private static final Logger logger = LoggerFactory.getLogger(UnidentifiedAccessHelper.class); @@ -109,7 +111,8 @@ public class UnidentifiedAccessHelper { return privacySenderCertificate.getSerialized(); } try { - final var certificate = dependencies.getAccountManager().getSenderCertificateForPhoneNumberPrivacy(); + final var certificate = handleResponseException(dependencies.getCertificateApi() + .getSenderCertificateForPhoneNumberPrivacy()); privacySenderCertificate = new SenderCertificate(certificate); return certificate; } catch (IOException | InvalidCertificateException e) { @@ -125,7 +128,7 @@ public class UnidentifiedAccessHelper { return senderCertificate.getSerialized(); } try { - final var certificate = dependencies.getAccountManager().getSenderCertificate(); + final var certificate = handleResponseException(dependencies.getCertificateApi().getSenderCertificate()); this.senderCertificate = new SenderCertificate(certificate); return certificate; } catch (IOException | InvalidCertificateException e) { diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java index c19e6b66..6470d4e9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java @@ -14,12 +14,17 @@ import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.account.AccountApi; +import org.whispersystems.signalservice.api.attachment.AttachmentApi; import org.whispersystems.signalservice.api.cds.CdsApi; +import org.whispersystems.signalservice.api.certificate.CertificateApi; import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; +import org.whispersystems.signalservice.api.keys.KeysApi; import org.whispersystems.signalservice.api.link.LinkDeviceApi; +import org.whispersystems.signalservice.api.message.MessageApi; +import org.whispersystems.signalservice.api.profiles.ProfileApi; import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.ratelimit.RateLimitChallengeApi; @@ -68,6 +73,10 @@ public class SignalDependencies { private RegistrationApi registrationApi; private LinkDeviceApi linkDeviceApi; private StorageServiceApi storageServiceApi; + private CertificateApi certificateApi; + private AttachmentApi attachmentApi; + private MessageApi messageApi; + private KeysApi keysApi; private GroupsV2Operations groupsV2Operations; private ClientZkOperations clientZkOperations; @@ -80,6 +89,7 @@ public class SignalDependencies { private List secureValueRecovery; private ProfileService profileService; + private ProfileApi profileApi; SignalDependencies( final ServiceEnvironmentConfig serviceEnvironmentConfig, @@ -174,7 +184,8 @@ public class SignalDependencies { public SignalServiceAccountManager getAccountManager() { return getOrCreate(() -> accountManager, - () -> accountManager = new SignalServiceAccountManager(getAccountApi(), + () -> accountManager = new SignalServiceAccountManager(getAuthenticatedSignalWebSocket(), + getAccountApi(), getPushServiceSocket(), getGroupsV2Operations())); } @@ -231,6 +242,27 @@ public class SignalDependencies { return new StorageServiceRepository(getStorageServiceApi()); } + public CertificateApi getCertificateApi() { + return getOrCreate(() -> certificateApi, + () -> certificateApi = new CertificateApi(getAuthenticatedSignalWebSocket())); + } + + public AttachmentApi getAttachmentApi() { + return getOrCreate(() -> attachmentApi, + () -> attachmentApi = new AttachmentApi(getAuthenticatedSignalWebSocket(), getPushServiceSocket())); + } + + public MessageApi getMessageApi() { + return getOrCreate(() -> messageApi, + () -> messageApi = new MessageApi(getAuthenticatedSignalWebSocket(), + getUnauthenticatedSignalWebSocket())); + } + + public KeysApi getKeysApi() { + return getOrCreate(() -> keysApi, + () -> keysApi = new KeysApi(getAuthenticatedSignalWebSocket(), getUnauthenticatedSignalWebSocket())); + } + public GroupsV2Operations getGroupsV2Operations() { return getOrCreate(() -> groupsV2Operations, () -> groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.signalServiceConfiguration()), @@ -289,8 +321,9 @@ public class SignalDependencies { () -> messageSender = new SignalServiceMessageSender(getPushServiceSocket(), dataStore, sessionLock, - getAuthenticatedSignalWebSocket(), - getUnauthenticatedSignalWebSocket(), + getAttachmentApi(), + getMessageApi(), + getKeysApi(), Optional.empty(), executor, ServiceConfig.MAX_ENVELOPE_SIZE)); @@ -304,10 +337,14 @@ public class SignalDependencies { .toList()); } + public ProfileApi getProfileApi() { + return getOrCreate(() -> profileApi, + () -> profileApi = new ProfileApi(getAuthenticatedSignalWebSocket(), getPushServiceSocket())); + } + public ProfileService getProfileService() { return getOrCreate(() -> profileService, () -> profileService = new ProfileService(getClientZkProfileOperations(), - getMessageReceiver(), getAuthenticatedSignalWebSocket(), getUnauthenticatedSignalWebSocket())); } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java index 9d97ed7a..daee1d77 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java @@ -7,6 +7,7 @@ import org.signal.libsignal.protocol.fingerprint.NumericFingerprintGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.NetworkResult; +import org.whispersystems.signalservice.api.NetworkResultUtil; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.util.StreamDetails; @@ -154,19 +155,7 @@ public class Utils { } public static T handleResponseException(final NetworkResult response) throws IOException { - final var throwableOptional = response.getCause(); - if (throwableOptional != null) { - if (throwableOptional instanceof IOException ioException) { - throw ioException; - } else { - throw new IOException(throwableOptional); - } - } - try { - return response.successOrThrow(); - } catch (Throwable e) { - throw new AssertionError(e); - } + return NetworkResultUtil.toBasicLegacy(response); } public static ByteString firstNonEmpty(ByteString... strings) { diff --git a/run_tests.sh b/run_tests.sh index 253532ec..02ab0286 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -141,6 +141,7 @@ exec 3> "$FIFO_FILE" echo '{"jsonrpc":"2.0","id":"id","method":"listGroups"}' >&3 echo '{"jsonrpc":"2.0","id":"id","method":"listDevices"}' >&3 echo '{"jsonrpc":"2.0","id":"id","method":"listIdentities"}' >&3 + echo '{"jsonrpc":"2.0","id":"id","method":"listStickerPacks"}' >&3 echo '{"jsonrpc":"2.0","id":"id","method":"sendSyncRequest"}' >&3 echo '{"jsonrpc":"2.0","id":"id","method":"sendContacts"}' >&3 echo '{"jsonrpc":"2.0","id":"id","method":"version"}' >&3 From b998f322f58fb20c6e545918ee5ec9af80d3aae5 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 6 Apr 2025 20:10:46 +0200 Subject: [PATCH 088/144] Bump version to 0.13.14 --- CHANGELOG.md | 13 ++++++++++++- build.gradle.kts | 2 +- data/org.asamk.SignalCli.metainfo.xml | 3 +++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40ff89f8..3474b123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ # Changelog -## [Unreleased] +## [0.13.14] - 2025-04-06 + +Requires libsignal-client version 0.68.1. + +### Fixed + +- Fix pre key import from old data files + +### Changed + +- Use websocket connection instead of HTTP for more requests +- Improve handling of messages with decryption error ## [0.13.13] - 2025-02-28 diff --git a/build.gradle.kts b/build.gradle.kts index 38c3e265..bfc09725 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.13.14-SNAPSHOT" + version = "0.13.14" } java { diff --git a/data/org.asamk.SignalCli.metainfo.xml b/data/org.asamk.SignalCli.metainfo.xml index a0af7892..b6f81a4d 100644 --- a/data/org.asamk.SignalCli.metainfo.xml +++ b/data/org.asamk.SignalCli.metainfo.xml @@ -45,6 +45,9 @@ intense + + https://github.com/AsamK/signal-cli/releases/tag/v0.13.14 + https://github.com/AsamK/signal-cli/releases/tag/v0.13.13 From bd7948e24657e1f324e423eb43b13aeedf6f3637 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 19 Apr 2024 19:25:27 +0200 Subject: [PATCH 089/144] Prepare next release --- CHANGELOG.md | 2 ++ build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3474b123..16899ff1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## [Unreleased] + ## [0.13.14] - 2025-04-06 Requires libsignal-client version 0.68.1. diff --git a/build.gradle.kts b/build.gradle.kts index bfc09725..17c632af 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.13.14" + version = "0.13.15-SNAPSHOT" } java { From ce813e4529811b7e97c46e13bf716f08a4139785 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 8 Apr 2025 16:24:57 +0200 Subject: [PATCH 090/144] Update client dependencies --- client/Cargo.lock | 435 +++++++++++++++++++++++----------------------- 1 file changed, 218 insertions(+), 217 deletions(-) diff --git a/client/Cargo.lock b/client/Cargo.lock index b85be350..d0cd01f4 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -69,15 +69,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "async-trait" -version = "0.1.85" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", @@ -108,7 +108,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -119,21 +119,21 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.10" +version = "1.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" dependencies = [ "shlex", ] @@ -152,9 +152,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.26" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.26" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -209,9 +209,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" dependencies = [ "core-foundation-sys", "libc", @@ -236,15 +236,15 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -343,9 +343,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", @@ -374,9 +374,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -395,12 +395,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", + "futures-core", "http", "http-body", "pin-project-lite", @@ -408,15 +408,15 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -452,9 +452,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -462,6 +462,7 @@ dependencies = [ "http", "http-body", "hyper", + "libc", "pin-project-lite", "socket2", "tokio", @@ -510,9 +511,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -534,9 +535,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -555,9 +556,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -610,9 +611,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", @@ -626,22 +627,24 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jni" -version = "0.19.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", + "cfg-if", "combine", "jni-sys", "log", "thiserror 1.0.69", "walkdir", + "windows-sys 0.45.0", ] [[package]] @@ -652,9 +655,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jsonrpsee" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" +checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", @@ -665,9 +668,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" +checksum = "456196007ca3a14db478346f58c7238028d55ee15c1df15115596e411ff27925" dependencies = [ "async-trait", "bytes", @@ -689,9 +692,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3638bc4617f96675973253b3a45006933bde93c2fd8a6170b33c777cc389e5b" +checksum = "c872b6c9961a4ccc543e321bb5b89f6b2d2c7fe8b61906918273a3333c95400c" dependencies = [ "async-trait", "base64", @@ -714,9 +717,9 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" +checksum = "5e65763c942dfc9358146571911b0cd1c361c2d63e2d2305622d40d36376ca80" dependencies = [ "heck", "proc-macro-crate", @@ -727,9 +730,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a178c60086f24cc35bb82f57c651d0d25d99c4742b4d335de04e97fa1f08a8a1" +checksum = "08a8e70baf945b6b5752fc8eb38c918a48f1234daf11355e07106d963f860089" dependencies = [ "http", "serde", @@ -739,27 +742,27 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "linux-raw-sys" -version = "0.4.15" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "litemap" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "log" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" @@ -769,9 +772,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" dependencies = [ "adler2", ] @@ -787,34 +790,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "object" version = "0.36.7" @@ -826,15 +801,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "percent-encoding" @@ -844,18 +819,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.8" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.8" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", @@ -876,42 +851,41 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", "getrandom", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -924,15 +898,15 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" -version = "0.38.43" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags", "errno", @@ -943,9 +917,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.21" +version = "0.23.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" +checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" dependencies = [ "log", "once_cell", @@ -958,37 +932,27 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.7.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ "openssl-probe", - "rustls-pemfile", "rustls-pki-types", "schannel", "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-platform-verifier" -version = "0.3.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbb878bdfdf63a336a5e63561b1835e7a8c91524f51621db870169eac84b490" +checksum = "4a5467026f437b4cb2a533865eaa73eb840019a0916f4b9ec563c6e617e086c9" dependencies = [ "core-foundation", "core-foundation-sys", @@ -1001,8 +965,8 @@ dependencies = [ "rustls-webpki", "security-framework", "security-framework-sys", - "webpki-roots", - "winapi", + "webpki-root-certs", + "windows-sys 0.59.0", ] [[package]] @@ -1013,9 +977,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.102.8" +version = "0.103.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ "ring", "rustls-pki-types", @@ -1024,9 +988,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -1048,15 +1012,14 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ "bitflags", "core-foundation", "core-foundation-sys", "libc", - "num-bigint", "security-framework-sys", ] @@ -1072,18 +1035,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -1092,9 +1055,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -1120,7 +1083,7 @@ dependencies = [ "log", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-util", ] @@ -1136,26 +1099,20 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1176,9 +1133,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.96" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -1198,9 +1155,9 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" +checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ "rustix", "windows-sys 0.59.0", @@ -1217,11 +1174,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] @@ -1237,9 +1194,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -1258,9 +1215,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.43.0" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", @@ -1285,9 +1242,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls", "tokio", @@ -1306,9 +1263,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ "bytes", "futures-core", @@ -1325,9 +1282,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "toml_datetime", @@ -1401,9 +1358,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "untrusted" @@ -1466,30 +1423,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "webpki-roots" -version = "0.26.7" +name = "webpki-root-certs" +version = "0.26.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +checksum = "09aed61f5e8d2c18344b3faa33a4c837855fe56642757754775548fee21386c4" dependencies = [ "rustls-pki-types", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.9" @@ -1500,10 +1441,13 @@ dependencies = [ ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-sys" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] [[package]] name = "windows-sys" @@ -1511,7 +1455,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1520,7 +1464,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -1529,28 +1488,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1563,24 +1540,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -1589,9 +1590,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.24" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] @@ -1634,18 +1635,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", From 1e685c7cab19b2cd57d79cf5d54999b5549164fd Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 9 Apr 2025 20:44:10 +0200 Subject: [PATCH 091/144] Extend merge/split logging --- .../manager/storage/recipients/MergeRecipientHelper.java | 7 ++++--- .../signal/manager/storage/recipients/RecipientStore.java | 7 ++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelper.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelper.java index 0a96785b..1454c8a7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/MergeRecipientHelper.java @@ -83,10 +83,11 @@ public class MergeRecipientHelper { recipientsToBeStripped.add(recipient); } - logger.debug("Got separate recipients for high trust identifiers {}, need to merge ({}) and strip ({})", + logger.debug("Got separate recipients for high trust identifiers {}, need to merge ({}, {}) and strip ({})", address, - recipientsToBeMerged.stream().map(r -> r.id().toString()).collect(Collectors.joining(", ")), - recipientsToBeStripped.stream().map(r -> r.id().toString()).collect(Collectors.joining(", "))); + resultingRecipient.map(RecipientWithAddress::address), + recipientsToBeMerged.stream().map(r -> r.address().toString()).collect(Collectors.joining(", ")), + recipientsToBeStripped.stream().map(r -> r.address().toString()).collect(Collectors.joining(", "))); RecipientAddress finalAddress = resultingRecipient.map(RecipientWithAddress::address).orElse(null); for (final var recipient : recipientsToBeMerged) { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java index 8d006416..442cbf4e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java @@ -994,7 +994,12 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re ) throws SQLException { markUnregistered(connection, recipientId); final var address = resolveRecipientAddress(connection, recipientId); - if (address.aci().isPresent() && address.pni().isPresent()) { + final var needSplit = address.aci().isPresent() && address.pni().isPresent(); + logger.trace("Marking unregistered recipient {} as unregistered (and split={}): {}", + recipientId, + needSplit, + address); + if (needSplit) { final var numberAddress = new RecipientAddress(address.pni().get(), address.number().orElse(null)); updateRecipientAddress(connection, recipientId, address.removeIdentifiersFrom(numberAddress)); addNewRecipient(connection, numberAddress); From 4e455d85d6e311cf229efae7f4d4acc9208d6931 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 26 Apr 2025 09:04:05 +0200 Subject: [PATCH 092/144] Add more logging to register --- .../asamk/signal/manager/internal/RegistrationManagerImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java index e103fec3..0d20cc0b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java @@ -129,12 +129,15 @@ public class RegistrationManagerImpl implements RegistrationManager { } final var registrationApi = unauthenticatedAccountManager.getRegistrationApi(); + logger.trace("Creating verification session"); String sessionId = NumberVerificationUtils.handleVerificationSession(registrationApi, account.getSessionId(account.getNumber()), id -> account.setSessionId(account.getNumber(), id), voiceVerification, captcha); + logger.trace("Requesting verification code"); NumberVerificationUtils.requestVerificationCode(registrationApi, sessionId, voiceVerification); + logger.debug("Successfully requested verification code"); account.setRegistered(false); } catch (DeprecatedVersionException e) { logger.debug("Signal-Server returned deprecated version exception", e); From 5e11cf1c508810388de9dce18034723a66a8b453 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 8 May 2025 19:37:41 +0200 Subject: [PATCH 093/144] Update gradle --- gradle/wrapper/gradle-wrapper.jar | Bin 43705 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 4 ++-- gradlew.bat | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 9bbc975c742b298b441bfb90dbc124400a3751b9..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 642 zcmdmamFde>rVZJA^}0Q$xegf!xPEW^+5YDM%iT2bEgct9o+jH~+sJas#HZ=szO|** z=Pj=X_vx?W&DSwKck|WWn~hffsvnQ+42*W$b7b0$SCcOoZ`{W{^$^pk;4>8-A*-)$ z?n(Po`1$6Jn_u?t-L+tsPyZ2#X}8T6OS8pAU;kdgd+_Hw4z4TW0p9E!T+=f7-c&O% zFic^X{7^$?^Ho04eona9n#mGMxKhA=~8B%JN`M zMhm5wc-2v)$``sY$!Q`9xiU@DhI73ZxiGEKg>yIPs)NmWwMdF-ngLXpZSqV5ez36n zVkxF2rjrjWR+_xr6e6@_u@s~2uv{9vi*1pj2)BjFD+-%@&pRVP1f{O1glxTOp2-62Ph;v z`N1+vCd)9ea)af*Ol1*JCfnp$%Uu}%OuoN7g2}3C@`L5FlP#(sA=|h@iixuZC?qp^ z=L$=v$ZoI}|87Wh=&h7udff{aieKr*l+zDp?pf)_bbRvUf>kn;HCDMXNlgbbo!QRK I1x7am0No)LiU0rr delta 584 zcmexzm1*ZyrVZJAexH5Moc8h7)w{^+t*dqJ%=yhh23L$9JpFV=_k`zJ-?Q4DI*eSe z+ES)HSrVnWLtJ&)lO%hRkV9zl5qqWRt0e;bb zPPo`)y?HTAyZI&u&X<|2$FDHCf4;!v8}p=?Tm`^F0`u(|1ttf~&t$qP3KUSD>@TJQ zRwJ}Pim6NzEc8KA6)e;S6gs8=7IIL8sQL*MYEuRYO;Uj<%3UbMbV&^&!Zvx+LKmjT z8Zch6rYP7Tw?$Hn(UTJwWiS=$f{lB(C=e*%usDV})0AQIK~sat=ND@+Gg*Pyij!rR z*fa02W|%BsV++>4W{DKDGSIUEHd2$P+8ct!RF+CHDowUuTEZOZ%rJSQv*qOXOSPDN zT|sP-$p*_3ncsWB*qoD7JQcyZ9xan%cJP6Tb4-?AZpr*F6v98hoNaPJm@HV`yya5N z))6pqFXn@}P(3T0nEzM8*c_9KtE9o|_pFd&K35GBXP^9Kg(b6GH-z8S4GDzIl~T+b zdLd#meKKHu$5u))8cu$=GKINkGDPOUD)!0$C(BH(U!}!-e;Q0ok8Sc?V1zRO04>ts AA^-pY diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f853b1..ca025c83 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index faf93008..23d15a93 100755 --- a/gradlew +++ b/gradlew @@ -114,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9b42019c..5eed7ee8 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell From a2900085c9fa85c86ccabd00e843593a9f480816 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 8 May 2025 19:37:57 +0200 Subject: [PATCH 094/144] Use java 24 in CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f27b4e1..ad4dbf15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '21', '23' ] + java: [ '21', '24' ] steps: - uses: actions/checkout@v4 From 4fd9e55c3c13f5be80d6abdd21f2c608e65903d0 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 8 May 2025 20:04:49 +0200 Subject: [PATCH 095/144] Enable native access required by Java 24 Fixes #1765 --- build.gradle.kts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 17c632af..bd091026 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,6 +24,7 @@ java { application { mainClass.set("org.asamk.signal.Main") + applicationDefaultJvmArgs = listOf("--enable-native-access=ALL-UNNAMED") } graalvmNative { @@ -112,7 +113,8 @@ tasks.withType { attributes( "Implementation-Title" to project.name, "Implementation-Version" to project.version, - "Main-Class" to application.mainClass.get() + "Main-Class" to application.mainClass.get(), + "Enable-Native-Access" to "ALL-UNNAMED", ) } } From bcc1eadc7de7320d12246a4958d83e7f167d9195 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 8 May 2025 20:36:27 +0200 Subject: [PATCH 096/144] Remove unused e164 field from account record --- .../signal/manager/syncStorage/AccountRecordProcessor.java | 7 +------ .../signal/manager/syncStorage/StorageSyncModels.java | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java b/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java index 656297bf..21c0d97a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java +++ b/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java @@ -2,7 +2,6 @@ package org.asamk.signal.manager.syncStorage; import org.asamk.signal.manager.api.Profile; import org.asamk.signal.manager.internal.JobExecutor; -import org.asamk.signal.manager.jobs.CheckWhoAmIJob; import org.asamk.signal.manager.jobs.DownloadProfileAvatarJob; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.util.KeyUtils; @@ -146,7 +145,7 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor Date: Thu, 8 May 2025 20:42:13 +0200 Subject: [PATCH 097/144] Add compatibility flag for graalvm native build --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index bd091026..6feb1b8b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,6 +33,7 @@ graalvmNative { buildArgs.add("--install-exit-handlers") buildArgs.add("-Dfile.encoding=UTF-8") buildArgs.add("-J-Dfile.encoding=UTF-8") + buildArgs.add("-march=compatibility") resources.autodetect() configurationFileDirectories.from(file("graalvm-config-dir")) if (System.getenv("GRAALVM_HOME") == null) { From e594f3b237d2aa3dfe2383301398d8c9fed86ac0 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 8 May 2025 21:18:25 +0200 Subject: [PATCH 098/144] Update libsignal-service --- graalvm-config-dir/reflect-config.json | 1 + gradle/libs.versions.toml | 2 +- .../java/org/asamk/signal/manager/config/LiveConfig.java | 3 +++ .../java/org/asamk/signal/manager/config/StagingConfig.java | 3 +++ .../java/org/asamk/signal/manager/helper/AccountHelper.java | 4 ++-- .../org/asamk/signal/manager/helper/RecipientHelper.java | 1 - .../signal/manager/internal/ProvisioningManagerImpl.java | 3 --- .../asamk/signal/manager/internal/SignalDependencies.java | 5 ++--- src/main/java/org/asamk/signal/BaseConfig.java | 2 +- 9 files changed, 13 insertions(+), 11 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index cdf38732..d9b07c03 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -3021,6 +3021,7 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord", "allDeclaredFields":true, + "fields":[{"name":"aci"}, {"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"e164"}, {"name":"familyName"}, {"name":"givenName"}, {"name":"hidden"}, {"name":"hideStory"}, {"name":"identityKey"}, {"name":"identityState"}, {"name":"markedUnread"}, {"name":"mutedUntilTimestamp"}, {"name":"nickname"}, {"name":"note"}, {"name":"pni"}, {"name":"pniSignatureVerified"}, {"name":"profileKey"}, {"name":"systemFamilyName"}, {"name":"systemGivenName"}, {"name":"systemNickname"}, {"name":"unregisteredAtTimestamp"}, {"name":"username"}, {"name":"whitelisted"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 74f97a76..3182d4bf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.18" -signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_121" +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_122" sqlite = "org.xerial:sqlite-jdbc:3.49.1.0" hikari = "com.zaxxer:HikariCP:6.3.0" junit-jupiter = "org.junit.jupiter:junit-jupiter:5.12.0" diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java index 6a707fa5..7085132d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -5,6 +5,7 @@ import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.whispersystems.signalservice.api.push.TrustStore; +import org.whispersystems.signalservice.internal.configuration.HttpProxy; import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; import org.whispersystems.signalservice.internal.configuration.SignalCdsiUrl; import org.whispersystems.signalservice.internal.configuration.SignalProxy; @@ -41,6 +42,7 @@ class LiveConfig { private static final Optional dns = Optional.empty(); private static final Optional proxy = Optional.empty(); + private static final Optional systemProxy = Optional.empty(); private static final byte[] zkGroupServerPublicParams = Base64.getDecoder() .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P+NameAZYOD12qRkxosQQP5uux6B2nRyZ7sAV54DgFyLiRcq1FvwKw2EPQdk4HDoePrO/RNUbyNddnM/mMgj4FW65xCoT1LmjrIjsv/Ggdlx46ueczhMgtBunx1/w8k8V+l8LVZ8gAT6wkU5J+DPQalQguMg12Jzug3q4TbdHiGCmD9EunCwOmsLuLJkz6EcSYXtrlDEnAM+hicw7iergYLLlMXpfTdGxJCWJmP4zqUFeTTmsmhsjGBt7NiEB/9pFFEB3pSbf4iiUukw63Eo8Aqnf4iwob6X1QviCWuc8t0LUlT9vALgh/f2DPVOOmR0RW6bgRvc7DSF20V/omg+YBw=="); @@ -68,6 +70,7 @@ class LiveConfig { interceptors, dns, proxy, + systemProxy, zkGroupServerPublicParams, genericServerPublicParams, backupServerPublicParams, diff --git a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java index 7eb04b3c..6b6ae9b5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java @@ -5,6 +5,7 @@ import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.whispersystems.signalservice.api.push.TrustStore; +import org.whispersystems.signalservice.internal.configuration.HttpProxy; import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; import org.whispersystems.signalservice.internal.configuration.SignalCdsiUrl; import org.whispersystems.signalservice.internal.configuration.SignalProxy; @@ -41,6 +42,7 @@ class StagingConfig { private static final Optional dns = Optional.empty(); private static final Optional proxy = Optional.empty(); + private static final Optional systemProxy = Optional.empty(); private static final byte[] zkGroupServerPublicParams = Base64.getDecoder() .decode("ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXZSSsOZ6s7M1+rTJN0bI5CKY2PX29y5Ok3jSWufIKcgKOnWoP67d5b2du2ZVJjpjfibNIHbT/cegy/sBLoFwtHogVYUewANUAXIaMPyCLRArsKhfJ5wBtTminG/PAvuBdJ70Z/bXVPf8TVsR292zQ65xwvWTejROW6AZX6aqucUjlENAErBme1YHmOSpU6tr6doJ66dPzVAWIanmO/5mgjNEDeK7DDqQdB1xd03HT2Qs2TxY3kCK8aAb/0iM0HQiXjxZ9HIgYhbtvGEnDKW5ILSUydqH/KBhW4Pb0jZWnqN/YgbWDKeJxnDbYcUob5ZY5Lt5ZCMKuaGUvCJRrCtuugSMaqjowCGRempsDdJEt+cMaalhZ6gczklJB/IbdwENW9KeVFPoFNFzhxWUIS5ML9riVYhAtE6JE5jX0xiHNVIIPthb458cfA8daR0nYfYAUKogQArm0iBezOO+mPk5vCNWI+wwkyFCqNDXz/qxl1gAntuCJtSfq9OC3NkdhQlgYQ=="); @@ -68,6 +70,7 @@ class StagingConfig { interceptors, dns, proxy, + systemProxy, zkGroupServerPublicParams, genericServerPublicParams, backupServerPublicParams, diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java index 7d40f99c..f94a3dca 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java @@ -535,9 +535,9 @@ public class AccountHelper { account.getAciIdentityKeyPair(), account.getPniIdentityKeyPair(), account.getProfileKey(), + account.getOrCreateAccountEntropyPool(), account.getOrCreatePinMasterKey(), account.getOrCreateMediaRootBackupKey(), - account.getOrCreateAccountEntropyPool(), verificationCode.getVerificationCode(), null)); account.setMultiDevice(true); @@ -595,7 +595,7 @@ public class AccountHelper { } account.setRegistrationLockPin(null); - dependencies.getAccountManager().deleteAccount(); + handleResponseException(dependencies.getAccountApi().deleteAccount()); account.setRegistered(false); unregisteredListener.call(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java index 5a3524ec..ecb85254 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java @@ -241,7 +241,6 @@ public class RecipientHelper { token, null, dependencies.getLibSignalNetwork(), - false, newToken -> { if (isPartialRefresh) { account.getCdsiStore().updateAfterPartialCdsQuery(newNumbers); diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ProvisioningManagerImpl.java index e5a381e1..eeca842a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ProvisioningManagerImpl.java @@ -29,7 +29,6 @@ import org.asamk.signal.manager.util.KeyUtils; import org.signal.libsignal.protocol.IdentityKeyPair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; @@ -76,7 +75,6 @@ public class ProvisioningManagerImpl implements ProvisioningManager { tempIdentityKey = KeyUtils.generateIdentityKeyPair(); password = KeyUtils.createPassword(); - final var clientZkOperations = ClientZkOperations.create(serviceEnvironmentConfig.signalServiceConfiguration()); final var credentialsProvider = new DynamicCredentialsProvider(null, null, null, @@ -85,7 +83,6 @@ public class ProvisioningManagerImpl implements ProvisioningManager { final var pushServiceSocket = new PushServiceSocket(serviceEnvironmentConfig.signalServiceConfiguration(), credentialsProvider, userAgent, - clientZkOperations.getProfileOperations(), ServiceConfig.AUTOMATIC_NETWORK_RETRY); final var provisioningSocket = new ProvisioningSocket(serviceEnvironmentConfig.signalServiceConfiguration(), userAgent); diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java index 6470d4e9..abdebd9a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java @@ -147,7 +147,6 @@ public class SignalDependencies { () -> pushServiceSocket = new PushServiceSocket(serviceEnvironmentConfig.signalServiceConfiguration(), credentialsProvider, userAgent, - getClientZkProfileOperations(), ServiceConfig.AUTOMATIC_NETWORK_RETRY)); } @@ -290,7 +289,7 @@ public class SignalDependencies { Optional.of(credentialsProvider), userAgent, healthMonitor, - allowStories), timer, TimeUnit.SECONDS.toMillis(10)); + allowStories), () -> true, timer, TimeUnit.SECONDS.toMillis(10)); healthMonitor.monitor(authenticatedSignalWebSocket); }); } @@ -306,7 +305,7 @@ public class SignalDependencies { Optional.empty(), userAgent, healthMonitor, - allowStories), timer, TimeUnit.SECONDS.toMillis(10)); + allowStories), () -> true, timer, TimeUnit.SECONDS.toMillis(10)); healthMonitor.monitor(unauthenticatedSignalWebSocket); }); } diff --git a/src/main/java/org/asamk/signal/BaseConfig.java b/src/main/java/org/asamk/signal/BaseConfig.java index 9218fe5f..101b0e3a 100644 --- a/src/main/java/org/asamk/signal/BaseConfig.java +++ b/src/main/java/org/asamk/signal/BaseConfig.java @@ -8,7 +8,7 @@ public class BaseConfig { public static final String PROJECT_VERSION = BaseConfig.class.getPackage().getImplementationVersion(); static final String USER_AGENT_SIGNAL_ANDROID = Optional.ofNullable(System.getenv("SIGNAL_CLI_USER_AGENT")) - .orElse("Signal-Android/7.35.0"); + .orElse("Signal-Android/7.41.3"); static final String USER_AGENT_SIGNAL_CLI = PROJECT_NAME == null ? "signal-cli" : PROJECT_NAME + "/" + PROJECT_VERSION; From 7aafb05995ee09305fa36bcb79a03dc1d057a0eb Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 8 May 2025 21:19:47 +0200 Subject: [PATCH 099/144] Update dependencies --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3182d4bf..9986e329 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ slf4j = "2.0.17" [libraries] bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.80" -jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.18.3" +jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.19.0" argparse4j = "net.sourceforge.argparse4j:argparse4j:0.9.0" dbusjava = "com.github.hypfvieh:dbus-java-transport-native-unixsocket:5.0.0" slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } From d0d0021f57a4f94749597a5324ee6eefbc54d5a3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 8 May 2025 21:49:51 +0200 Subject: [PATCH 100/144] Bump version to 0.13.15 --- CHANGELOG.md | 13 ++++++++++++- build.gradle.kts | 2 +- data/org.asamk.SignalCli.metainfo.xml | 3 +++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16899ff1..86fb8178 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ # Changelog -## [Unreleased] +## [0.13.15] - 2025-05-08 + +Requires libsignal-client version 0.70.0. + +### Fixed + +- Fix native access warning with Java 24 +- Fix storage sync loop due to old removed e164 field + +### Changed + +- Increased compatibility of native build with older/virtual CPUs ## [0.13.14] - 2025-04-06 diff --git a/build.gradle.kts b/build.gradle.kts index 6feb1b8b..9ec777c1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.13.15-SNAPSHOT" + version = "0.13.15" } java { diff --git a/data/org.asamk.SignalCli.metainfo.xml b/data/org.asamk.SignalCli.metainfo.xml index b6f81a4d..5e80335e 100644 --- a/data/org.asamk.SignalCli.metainfo.xml +++ b/data/org.asamk.SignalCli.metainfo.xml @@ -45,6 +45,9 @@ intense + + https://github.com/AsamK/signal-cli/releases/tag/v0.13.15 + https://github.com/AsamK/signal-cli/releases/tag/v0.13.14 From 56e11d0857c6131667695b95c9c2805c8a938150 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 8 May 2025 22:55:26 +0200 Subject: [PATCH 101/144] Update codeql v3 --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ff13469a..f778268a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java @@ -43,7 +43,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -57,4 +57,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From bb124a922defe76565386d767ff318db28d897bd Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 19 Apr 2024 19:25:27 +0200 Subject: [PATCH 102/144] Prepare next release --- CHANGELOG.md | 2 ++ build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86fb8178..2d306b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## [Unreleased] + ## [0.13.15] - 2025-05-08 Requires libsignal-client version 0.70.0. diff --git a/build.gradle.kts b/build.gradle.kts index 9ec777c1..267673d6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.13.15" + version = "0.13.16-SNAPSHOT" } java { From 74909408c4d56fd8ee05c4720d1229a040b46c3b Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 10 May 2025 10:18:03 +0200 Subject: [PATCH 103/144] Add missing reflect config Fixes #1768 --- graalvm-config-dir/reflect-config.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index d9b07c03..e27f68d9 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -1409,6 +1409,12 @@ "name":"org.asamk.signal.manager.storage.profiles.LegacyProfileStore$ProfileStoreDeserializer", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.asamk.signal.manager.storage.profiles.LegacySignalProfile", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.profiles.LegacySignalProfileEntry", "allDeclaredFields":true, From a9bb8d9aaef94bf1e251b822532da8a91f870dd2 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 1 Jun 2025 16:11:21 +0200 Subject: [PATCH 104/144] Update gradle --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ca025c83..002b867c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From e89803464b0e1fd284045bd4100c05ec856c7011 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 1 Jun 2025 21:51:03 +0200 Subject: [PATCH 105/144] Update libsignal-service --- graalvm-config-dir/jni-config.json | 13 ++++++++++- graalvm-config-dir/reflect-config.json | 3 --- gradle/libs.versions.toml | 2 +- .../signal/manager/config/LiveConfig.java | 5 ++-- .../signal/manager/config/StagingConfig.java | 5 ++-- .../signal/manager/helper/AccountHelper.java | 2 +- .../manager/internal/SignalDependencies.java | 3 ++- .../signal/manager/storage/SignalAccount.java | 2 +- .../storage/identities/IdentityKeyStore.java | 23 +++++++++++-------- .../identities/SignalIdentityKeyStore.java | 2 +- .../storage/protocol/SignalProtocolStore.java | 2 +- 11 files changed, 39 insertions(+), 23 deletions(-) diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index 0284d58f..23a2160c 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -27,6 +27,10 @@ { "name":"java.lang.ClassNotFoundException" }, +{ + "name":"java.lang.Enum", + "methods":[{"name":"ordinal","parameterTypes":[] }] +}, { "name":"java.lang.IllegalArgumentException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] @@ -48,6 +52,10 @@ { "name":"java.lang.String" }, +{ + "name":"java.lang.Thread", + "methods":[{"name":"currentThread","parameterTypes":[] }, {"name":"getStackTrace","parameterTypes":[] }] +}, { "name":"java.lang.Throwable", "methods":[{"name":"getMessage","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] @@ -88,7 +96,7 @@ }, { "name":"org.signal.libsignal.internal.CompletableFuture", - "methods":[{"name":"","parameterTypes":[] }, {"name":"complete","parameterTypes":["java.lang.Object"] }, {"name":"completeExceptionally","parameterTypes":["java.lang.Throwable"] }] + "methods":[{"name":"","parameterTypes":[] }, {"name":"complete","parameterTypes":["java.lang.Object"] }, {"name":"completeExceptionally","parameterTypes":["java.lang.Throwable"] }, {"name":"setCancellationId","parameterTypes":["long"] }] }, { "name":"org.signal.libsignal.internal.NativeHandleGuard$SimpleOwner", @@ -195,6 +203,9 @@ "name":"org.signal.libsignal.protocol.state.IdentityKeyStore$Direction", "fields":[{"name":"RECEIVING"}, {"name":"SENDING"}] }, +{ + "name":"org.signal.libsignal.protocol.state.IdentityKeyStore$IdentityChange" +}, { "name":"org.signal.libsignal.protocol.state.KyberPreKeyRecord", "fields":[{"name":"unsafeHandle"}] diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index e27f68d9..cbec1c86 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2994,7 +2994,6 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord", "allDeclaredFields":true, - "fields":[{"name":"avatarColor"}, {"name":"avatarUrlPath"}, {"name":"backupSubscriberData"}, {"name":"backupTier"}, {"name":"displayBadgesOnProfile"}, {"name":"e164"}, {"name":"familyName"}, {"name":"givenName"}, {"name":"hasBackup"}, {"name":"hasCompletedUsernameOnboarding"}, {"name":"hasSeenGroupStoryEducationSheet"}, {"name":"hasSetMyStoriesPrivacy"}, {"name":"hasViewedOnboardingStory"}, {"name":"keepMutedChatsArchived"}, {"name":"linkPreviews"}, {"name":"noteToSelfArchived"}, {"name":"noteToSelfMarkedUnread"}, {"name":"payments"}, {"name":"phoneNumberSharingMode"}, {"name":"pinnedConversations"}, {"name":"preferContactAvatars"}, {"name":"preferredReactionEmoji"}, {"name":"primarySendsSms"}, {"name":"profileKey"}, {"name":"readReceipts"}, {"name":"sealedSenderIndicators"}, {"name":"storiesDisabled"}, {"name":"storyViewReceiptsEnabled"}, {"name":"subscriberCurrencyCode"}, {"name":"subscriberId"}, {"name":"subscriptionManuallyCancelled"}, {"name":"typingIndicators"}, {"name":"universalExpireTimer"}, {"name":"unlistedPhoneNumber"}, {"name":"username"}, {"name":"usernameLink"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { @@ -3027,7 +3026,6 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord", "allDeclaredFields":true, - "fields":[{"name":"aci"}, {"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"e164"}, {"name":"familyName"}, {"name":"givenName"}, {"name":"hidden"}, {"name":"hideStory"}, {"name":"identityKey"}, {"name":"identityState"}, {"name":"markedUnread"}, {"name":"mutedUntilTimestamp"}, {"name":"nickname"}, {"name":"note"}, {"name":"pni"}, {"name":"pniSignatureVerified"}, {"name":"profileKey"}, {"name":"systemFamilyName"}, {"name":"systemGivenName"}, {"name":"systemNickname"}, {"name":"unregisteredAtTimestamp"}, {"name":"username"}, {"name":"whitelisted"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { @@ -3058,7 +3056,6 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record", "allDeclaredFields":true, - "fields":[{"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"dontNotifyForMentionsIfMuted"}, {"name":"hideStory"}, {"name":"markedUnread"}, {"name":"masterKey"}, {"name":"mutedUntilTimestamp"}, {"name":"storySendMode"}, {"name":"whitelisted"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9986e329..a90ecb53 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.18" -signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_122" +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_123" sqlite = "org.xerial:sqlite-jdbc:3.49.1.0" hikari = "com.zaxxer:HikariCP:6.3.0" junit-jupiter = "org.junit.jupiter:junit-jupiter:5.12.0" diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java index 7085132d..111695f6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -29,7 +29,8 @@ class LiveConfig { private static final byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() .decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"); private static final String CDSI_MRENCLAVE = "0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57"; - private static final String SVR2_MRENCLAVE = "9314436a9a144992bb3680770ea5fd7934a7ffd29257844a33763a238903d570"; + private static final String SVR2_MRENCLAVE_LEGACY = "9314436a9a144992bb3680770ea5fd7934a7ffd29257844a33763a238903d570"; + private static final String SVR2_MRENCLAVE = "093be9ea32405e85ae28dbb48eb668aebeb7dbe29517b9b86ad4bec4dfe0e6a6"; private static final String URL = "https://chat.signal.org"; private static final String CDN_URL = "https://cdn.signal.org"; @@ -91,7 +92,7 @@ class LiveConfig { createDefaultServiceConfiguration(interceptors), getUnidentifiedSenderTrustRoot(), CDSI_MRENCLAVE, - List.of(SVR2_MRENCLAVE)); + List.of(SVR2_MRENCLAVE, SVR2_MRENCLAVE_LEGACY)); } private LiveConfig() { diff --git a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java index 6b6ae9b5..e72c66b5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java @@ -29,7 +29,8 @@ class StagingConfig { private static final byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() .decode("BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx"); private static final String CDSI_MRENCLAVE = "0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57"; - private static final String SVR2_MRENCLAVE = "38e01eff4fe357dc0b0e8ef7a44b4abc5489fbccba3a78780f3872c277f62bf3"; + private static final String SVR2_MRENCLAVE_LEGACY = "38e01eff4fe357dc0b0e8ef7a44b4abc5489fbccba3a78780f3872c277f62bf3"; + private static final String SVR2_MRENCLAVE = "2e8cefe6e3f389d8426adb24e9b7fb7adf10902c96f06f7bbcee36277711ed91"; private static final String URL = "https://chat.staging.signal.org"; private static final String CDN_URL = "https://cdn-staging.signal.org"; @@ -91,7 +92,7 @@ class StagingConfig { createDefaultServiceConfiguration(interceptors), getUnidentifiedSenderTrustRoot(), CDSI_MRENCLAVE, - List.of(SVR2_MRENCLAVE)); + List.of(SVR2_MRENCLAVE, SVR2_MRENCLAVE_LEGACY)); } private StagingConfig() { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java index f94a3dca..1d5849c3 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java @@ -105,7 +105,7 @@ public class AccountHelper { if (!account.isPrimaryDevice() && account.getPniIdentityKeyPair() == null) { throw new IOException("Missing PNI identity key, relinking required"); } - if (account.getPreviousStorageVersion() < 4 + if (account.getPreviousStorageVersion() < 10 && account.isPrimaryDevice() && account.getRegistrationLockPin() != null) { migrateRegistrationPin(); diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java index abdebd9a..9a8e1fd5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java @@ -325,7 +325,8 @@ public class SignalDependencies { getKeysApi(), Optional.empty(), executor, - ServiceConfig.MAX_ENVELOPE_SIZE)); + ServiceConfig.MAX_ENVELOPE_SIZE, + () -> true)); } public List getSecureValueRecovery() { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 8d2fbd0f..83040906 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -116,7 +116,7 @@ public class SignalAccount implements Closeable { private static final Logger logger = LoggerFactory.getLogger(SignalAccount.class); private static final int MINIMUM_STORAGE_VERSION = 1; - private static final int CURRENT_STORAGE_VERSION = 9; + private static final int CURRENT_STORAGE_VERSION = 10; private final Object LOCK = new Object(); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java index 5e32ba12..a2d03a21 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java @@ -8,6 +8,7 @@ import org.asamk.signal.manager.storage.recipients.RecipientStore; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.state.IdentityKeyStore.Direction; +import org.signal.libsignal.protocol.state.IdentityKeyStore.IdentityChange; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.ServiceId; @@ -62,11 +63,11 @@ public class IdentityKeyStore { return identityChanges; } - public boolean saveIdentity(final ServiceId serviceId, final IdentityKey identityKey) { + public IdentityChange saveIdentity(final ServiceId serviceId, final IdentityKey identityKey) { return saveIdentity(serviceId.toString(), identityKey); } - public boolean saveIdentity( + public IdentityChange saveIdentity( final Connection connection, final ServiceId serviceId, final IdentityKey identityKey @@ -74,9 +75,9 @@ public class IdentityKeyStore { return saveIdentity(connection, serviceId.toString(), identityKey); } - boolean saveIdentity(final String address, final IdentityKey identityKey) { + IdentityChange saveIdentity(final String address, final IdentityKey identityKey) { if (isRetryingDecryption) { - return false; + return IdentityChange.NEW_OR_UNCHANGED; } try (final var connection = database.getConnection()) { return saveIdentity(connection, address, identityKey); @@ -85,20 +86,24 @@ public class IdentityKeyStore { } } - private boolean saveIdentity( + private IdentityChange saveIdentity( final Connection connection, final String address, final IdentityKey identityKey ) throws SQLException { final var identityInfo = loadIdentity(connection, address); - if (identityInfo != null && identityInfo.getIdentityKey().equals(identityKey)) { + if (identityInfo == null) { + saveNewIdentity(connection, address, identityKey, true); + return IdentityChange.NEW_OR_UNCHANGED; + } + if (identityInfo.getIdentityKey().equals(identityKey)) { // Identity already exists, not updating the trust level logger.trace("Not storing new identity for recipient {}, identity already stored", address); - return false; + return IdentityChange.NEW_OR_UNCHANGED; } - saveNewIdentity(connection, address, identityKey, identityInfo == null); - return true; + saveNewIdentity(connection, address, identityKey, false); + return IdentityChange.REPLACED_EXISTING; } public void setRetryingDecryption(final boolean retryingDecryption) { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java index 5a1a676e..1ac7c9a6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java @@ -33,7 +33,7 @@ public class SignalIdentityKeyStore implements org.signal.libsignal.protocol.sta } @Override - public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { + public IdentityChange saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { return identityKeyStore.saveIdentity(address.getName(), identityKey); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java index b631f632..e1cf9a88 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java @@ -65,7 +65,7 @@ public class SignalProtocolStore implements SignalServiceAccountDataStore { } @Override - public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { + public IdentityChange saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { return identityKeyStore.saveIdentity(address, identityKey); } From 6b46314eabaa769c16415baf62c8e2f4697286ae Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 3 Jun 2025 19:24:21 +0200 Subject: [PATCH 106/144] Update dependencies --- graalvm-config-dir/reflect-config.json | 1 + gradle/libs.versions.toml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index cbec1c86..c1c5a63d 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -3056,6 +3056,7 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record", "allDeclaredFields":true, + "fields":[{"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"dontNotifyForMentionsIfMuted"}, {"name":"hideStory"}, {"name":"markedUnread"}, {"name":"masterKey"}, {"name":"mutedUntilTimestamp"}, {"name":"storySendMode"}, {"name":"whitelisted"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a90ecb53..d712041f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,5 +13,5 @@ logback = "ch.qos.logback:logback-classic:1.5.18" signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_123" sqlite = "org.xerial:sqlite-jdbc:3.49.1.0" hikari = "com.zaxxer:HikariCP:6.3.0" -junit-jupiter = "org.junit.jupiter:junit-jupiter:5.12.0" -junit-launcher = "org.junit.platform:junit-platform-launcher:1.12.0" +junit-jupiter = "org.junit.jupiter:junit-jupiter:5.13.0" +junit-launcher = "org.junit.platform:junit-platform-launcher:1.13.0" From bf87fcc652bc37a9843b4dc79f985005452a1c41 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 3 Jun 2025 22:22:51 +0200 Subject: [PATCH 107/144] Ensure messages are created with a unique timestamp Fixes #1783 --- .../signal/manager/internal/ManagerImpl.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java index c5a7552f..0a67e0a0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java @@ -131,6 +131,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -160,6 +161,7 @@ public class ManagerImpl implements Manager { private final List closedListeners = new ArrayList<>(); private final List addressChangedListeners = new ArrayList<>(); private final CompositeDisposable disposable = new CompositeDisposable(); + private final AtomicLong lastMessageTimestamp = new AtomicLong(); public ManagerImpl( SignalAccount account, @@ -598,6 +600,24 @@ public class ManagerImpl implements Manager { return context.getGroupHelper().joinGroup(inviteLinkUrl); } + private long getNextMessageTimestamp() { + while (true) { + final var last = lastMessageTimestamp.get(); + final var timestamp = System.currentTimeMillis(); + if (last == timestamp) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + continue; + } + if (lastMessageTimestamp.compareAndSet(last, timestamp)) { + return timestamp; + } + } + } + private SendMessageResults sendMessage( SignalServiceDataMessage.Builder messageBuilder, Set recipients, @@ -613,7 +633,7 @@ public class ManagerImpl implements Manager { Optional editTargetTimestamp ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { var results = new HashMap>(); - long timestamp = System.currentTimeMillis(); + long timestamp = getNextMessageTimestamp(); messageBuilder.withTimestamp(timestamp); for (final var recipient : recipients) { if (recipient instanceof RecipientIdentifier.NoteToSelf || ( @@ -653,7 +673,7 @@ public class ManagerImpl implements Manager { Set recipients ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { var results = new HashMap>(); - final var timestamp = System.currentTimeMillis(); + final var timestamp = getNextMessageTimestamp(); for (var recipient : recipients) { if (recipient instanceof RecipientIdentifier.Single single) { final var message = new SignalServiceTypingMessage(action, timestamp, Optional.empty()); @@ -685,7 +705,7 @@ public class ManagerImpl implements Manager { @Override public SendMessageResults sendReadReceipt(RecipientIdentifier.Single sender, List messageIds) { - final var timestamp = System.currentTimeMillis(); + final var timestamp = getNextMessageTimestamp(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp); @@ -695,7 +715,7 @@ public class ManagerImpl implements Manager { @Override public SendMessageResults sendViewedReceipt(RecipientIdentifier.Single sender, List messageIds) { - final var timestamp = System.currentTimeMillis(); + final var timestamp = getNextMessageTimestamp(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.VIEWED, messageIds, timestamp); From 7e9727aa382d746b9eb12c772c7251947793b68a Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 7 Jun 2025 16:14:20 +0200 Subject: [PATCH 108/144] Update tests --- run_tests.sh | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/run_tests.sh b/run_tests.sh index 02ab0286..339d9d7b 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -176,6 +176,17 @@ run_main -a "$NUMBER_2" receive run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi run_main -a "$NUMBER_1" receive run_main -a "$NUMBER_2" receive +run_main -a "$NUMBER_1" updateAccount --discoverable-by-number=true +run_main -a "$NUMBER_2" removeContact --forget "$NUMBER_1" +run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi +run_main -a "$NUMBER_2" send "$NUMBER_1" -m hii +run_main -a "$NUMBER_1" updateAccount --discoverable-by-number=false +run_main -a "$NUMBER_1" receive +run_main -a "$NUMBER_2" receive +run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi +run_main -a "$NUMBER_2" send "$NUMBER_1" -m hii +run_main -a "$NUMBER_1" receive +run_main -a "$NUMBER_2" receive ## Groups GROUP_ID=$(run_main -a "$NUMBER_1" --output=json updateGroup -n GRUPPE -a LICENSE -m "$NUMBER_1" | jq -r '.groupId') run_main -a "$NUMBER_1" send "$NUMBER_2" -m first @@ -238,7 +249,9 @@ for OUTPUT in "plain-text" "json"; do run_linked -a "$NUMBER_1" --output="$OUTPUT" receive done -run_main -a "$NUMBER_1" removeDevice -d 2 +run_main -a "$NUMBER_1" --output="$OUTPUT" receive +run_main -a "$NUMBER_1" removeDevice -d 2 || true +run_main -a "$NUMBER_1" removeDevice -d 2 || true ## Unregister if [ "$TEST_REGISTER" -eq 1 ]; then From 2f8328847c32e1908e6dcc2d188a28509627d302 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 7 Jun 2025 16:13:09 +0200 Subject: [PATCH 109/144] Update dependencies --- graalvm-config-dir/reflect-config.json | 11 +++++++++++ gradle/libs.versions.toml | 8 ++++---- .../signal/manager/internal/SignalDependencies.java | 5 ++++- src/main/java/org/asamk/signal/BaseConfig.java | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index c1c5a63d..d1c2075b 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -1626,6 +1626,10 @@ "name":"org.bouncycastle.jcajce.provider.asymmetric.NTRU$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.NoSig$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings", "methods":[{"name":"","parameterTypes":[] }] @@ -2996,6 +3000,9 @@ "allDeclaredFields":true, "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$BackupTierHistory" +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$Builder" }, @@ -3005,6 +3012,9 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$IAPSubscriberData" }, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$NotificationProfileManualOverride" +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$PhoneNumberSharingMode" }, @@ -3026,6 +3036,7 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord", "allDeclaredFields":true, + "fields":[{"name":"aci"}, {"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"e164"}, {"name":"familyName"}, {"name":"givenName"}, {"name":"hidden"}, {"name":"hideStory"}, {"name":"identityKey"}, {"name":"identityState"}, {"name":"markedUnread"}, {"name":"mutedUntilTimestamp"}, {"name":"nickname"}, {"name":"note"}, {"name":"pni"}, {"name":"pniSignatureVerified"}, {"name":"profileKey"}, {"name":"systemFamilyName"}, {"name":"systemGivenName"}, {"name":"systemNickname"}, {"name":"unregisteredAtTimestamp"}, {"name":"username"}, {"name":"whitelisted"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d712041f..a6b9fda1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ slf4j = "2.0.17" [libraries] -bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.80" +bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.81" jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.19.0" argparse4j = "net.sourceforge.argparse4j:argparse4j:0.9.0" dbusjava = "com.github.hypfvieh:dbus-java-transport-native-unixsocket:5.0.0" @@ -10,8 +10,8 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.18" -signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_123" +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_124" sqlite = "org.xerial:sqlite-jdbc:3.49.1.0" hikari = "com.zaxxer:HikariCP:6.3.0" -junit-jupiter = "org.junit.jupiter:junit-jupiter:5.13.0" -junit-launcher = "org.junit.platform:junit-platform-launcher:1.13.0" +junit-jupiter = "org.junit.jupiter:junit-jupiter:5.13.1" +junit-launcher = "org.junit.platform:junit-platform-launcher:1.13.1" diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java index 9a8e1fd5..f954588f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java @@ -339,7 +339,10 @@ public class SignalDependencies { public ProfileApi getProfileApi() { return getOrCreate(() -> profileApi, - () -> profileApi = new ProfileApi(getAuthenticatedSignalWebSocket(), getPushServiceSocket())); + () -> profileApi = new ProfileApi(getAuthenticatedSignalWebSocket(), + getUnauthenticatedSignalWebSocket(), + getPushServiceSocket(), + getClientZkProfileOperations())); } public ProfileService getProfileService() { diff --git a/src/main/java/org/asamk/signal/BaseConfig.java b/src/main/java/org/asamk/signal/BaseConfig.java index 101b0e3a..8a38c36a 100644 --- a/src/main/java/org/asamk/signal/BaseConfig.java +++ b/src/main/java/org/asamk/signal/BaseConfig.java @@ -8,7 +8,7 @@ public class BaseConfig { public static final String PROJECT_VERSION = BaseConfig.class.getPackage().getImplementationVersion(); static final String USER_AGENT_SIGNAL_ANDROID = Optional.ofNullable(System.getenv("SIGNAL_CLI_USER_AGENT")) - .orElse("Signal-Android/7.41.3"); + .orElse("Signal-Android/7.44.1"); static final String USER_AGENT_SIGNAL_CLI = PROJECT_NAME == null ? "signal-cli" : PROJECT_NAME + "/" + PROJECT_VERSION; From 17cd99be5967419c5c518ba444c8c58fdaa65b0f Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 7 Jun 2025 16:58:00 +0200 Subject: [PATCH 110/144] Bump version to 0.13.16 --- CHANGELOG.md | 8 +++++++- build.gradle.kts | 2 +- data/org.asamk.SignalCli.metainfo.xml | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d306b4b..f29e3d93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog -## [Unreleased] +## [0.13.16] - 2025-06-07 + +Requires libsignal-client version 0.73.2. + +### Changed + +- Ensure every sent message gets a unique timestamp ## [0.13.15] - 2025-05-08 diff --git a/build.gradle.kts b/build.gradle.kts index 267673d6..3be525f1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.13.16-SNAPSHOT" + version = "0.13.16" } java { diff --git a/data/org.asamk.SignalCli.metainfo.xml b/data/org.asamk.SignalCli.metainfo.xml index 5e80335e..b7483db9 100644 --- a/data/org.asamk.SignalCli.metainfo.xml +++ b/data/org.asamk.SignalCli.metainfo.xml @@ -45,6 +45,9 @@ intense + + https://github.com/AsamK/signal-cli/releases/tag/v0.13.16 + https://github.com/AsamK/signal-cli/releases/tag/v0.13.15 From 02573449402003871f44f153b06bec7a9e47d521 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 19 Apr 2024 19:25:27 +0200 Subject: [PATCH 111/144] Prepare next release --- CHANGELOG.md | 2 ++ build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f29e3d93..3ea3c01b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## [Unreleased] + ## [0.13.16] - 2025-06-07 Requires libsignal-client version 0.73.2. diff --git a/build.gradle.kts b/build.gradle.kts index 3be525f1..8fadb883 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.13.16" + version = "0.13.17-SNAPSHOT" } java { From 6b60a6d5a5aacb579cbd64074f25d1014bc93e00 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 8 Jun 2025 14:48:25 +0200 Subject: [PATCH 112/144] Fix NPR when loading an inactive group Fixes #1786 --- .../main/java/org/asamk/signal/manager/helper/GroupHelper.java | 3 +++ .../java/org/asamk/signal/manager/helper/GroupV2Helper.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 0d838204..682bd996 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -551,6 +551,9 @@ public class GroupHelper { while (true) { final var page = context.getGroupV2Helper() .getDecryptedGroupHistoryPage(groupSecretParams, fromRevision, sendEndorsementsExpirationMs); + if (page == null) { + break; + } page.getChangeLogs() .stream() .map(DecryptedGroupChangeLog::getChange) diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java index 900d7ea9..8eb66843 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java @@ -44,6 +44,7 @@ import org.whispersystems.signalservice.api.push.ServiceId.PNI; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; import org.whispersystems.signalservice.api.util.UuidUtil; +import org.whispersystems.signalservice.internal.push.exceptions.NotInGroupException; import java.io.IOException; import java.util.ArrayList; @@ -119,6 +120,8 @@ class GroupV2Helper { groupsV2AuthorizationString, false, sendEndorsementsExpirationMs); + } catch (NotInGroupException e) { + throw new NotAGroupMemberException(GroupUtils.getGroupIdV2(groupSecretParams), null); } catch (NonSuccessfulResponseCodeException e) { if (e.code == 403) { throw new NotAGroupMemberException(GroupUtils.getGroupIdV2(groupSecretParams), null); From a0d5744c4945791eb57436d0f1288b09bd41132a Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 8 Jun 2025 16:22:03 +0200 Subject: [PATCH 113/144] Improve behavior when pin data doesn't exist on the server --- lib/src/main/java/org/asamk/signal/manager/Manager.java | 3 ++- .../java/org/asamk/signal/manager/RegistrationManager.java | 3 ++- .../asamk/signal/manager/api/PinLockMissingException.java | 3 +++ .../java/org/asamk/signal/manager/helper/AccountHelper.java | 5 +++-- .../java/org/asamk/signal/manager/helper/PinHelper.java | 6 +++++- .../java/org/asamk/signal/manager/internal/ManagerImpl.java | 3 ++- .../signal/manager/internal/RegistrationManagerImpl.java | 3 ++- .../asamk/signal/manager/util/NumberVerificationUtils.java | 5 +++-- .../asamk/signal/commands/FinishChangeNumberCommand.java | 3 +++ src/main/java/org/asamk/signal/commands/VerifyCommand.java | 3 +++ .../java/org/asamk/signal/dbus/DbusSignalControlImpl.java | 3 +++ 11 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/api/PinLockMissingException.java diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index affbaa9d..238084e7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -30,6 +30,7 @@ import org.asamk.signal.manager.api.NotAGroupMemberException; import org.asamk.signal.manager.api.NotPrimaryDeviceException; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.PendingAdminApprovalException; +import org.asamk.signal.manager.api.PinLockMissingException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.api.RateLimitException; import org.asamk.signal.manager.api.ReceiveConfig; @@ -140,7 +141,7 @@ public interface Manager extends Closeable { String newNumber, String verificationCode, String pin - ) throws IncorrectPinException, PinLockedException, IOException, NotPrimaryDeviceException; + ) throws IncorrectPinException, PinLockedException, IOException, NotPrimaryDeviceException, PinLockMissingException; void unregister() throws IOException; diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java index 7d358df6..5f4f4463 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -3,6 +3,7 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; +import org.asamk.signal.manager.api.PinLockMissingException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.api.RateLimitException; import org.asamk.signal.manager.api.VerificationMethodNotAvailableException; @@ -21,7 +22,7 @@ public interface RegistrationManager extends Closeable { void verifyAccount( String verificationCode, String pin - ) throws IOException, PinLockedException, IncorrectPinException; + ) throws IOException, PinLockedException, IncorrectPinException, PinLockMissingException; void deleteLocalAccountData() throws IOException; diff --git a/lib/src/main/java/org/asamk/signal/manager/api/PinLockMissingException.java b/lib/src/main/java/org/asamk/signal/manager/api/PinLockMissingException.java new file mode 100644 index 00000000..7887d45f --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/api/PinLockMissingException.java @@ -0,0 +1,3 @@ +package org.asamk.signal.manager.api; + +public class PinLockMissingException extends Exception {} diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java index 1d5849c3..de3e2402 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java @@ -4,6 +4,7 @@ import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.DeviceLinkUrl; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; +import org.asamk.signal.manager.api.PinLockMissingException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.api.RateLimitException; import org.asamk.signal.manager.api.VerificationMethodNotAvailableException; @@ -185,7 +186,7 @@ public class AccountHelper { String newNumber, String verificationCode, String pin - ) throws IncorrectPinException, PinLockedException, IOException { + ) throws IncorrectPinException, PinLockedException, IOException, PinLockMissingException { for (var attempts = 0; attempts < 5; attempts++) { try { finishChangeNumberInternal(newNumber, verificationCode, pin); @@ -205,7 +206,7 @@ public class AccountHelper { String newNumber, String verificationCode, String pin - ) throws IncorrectPinException, PinLockedException, IOException { + ) throws IncorrectPinException, PinLockedException, IOException, PinLockMissingException { final var pniIdentity = KeyUtils.generateIdentityKeyPair(); final var encryptedDeviceMessages = new ArrayList(); final var devicePniSignedPreKeys = new HashMap(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java index a95635cf..40878647 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java @@ -88,7 +88,11 @@ public class PinHelper { IOException exception = null; for (final var secureValueRecovery : secureValueRecoveries) { try { - return getRegistrationLockData(secureValueRecovery, svr2Credentials, pin); + final var lockData = getRegistrationLockData(secureValueRecovery, svr2Credentials, pin); + if (lockData == null) { + continue; + } + return lockData; } catch (IOException e) { exception = e; } diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java index 0a67e0a0..ffb74de2 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java @@ -48,6 +48,7 @@ import org.asamk.signal.manager.api.NotPrimaryDeviceException; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.PendingAdminApprovalException; import org.asamk.signal.manager.api.PhoneNumberSharingMode; +import org.asamk.signal.manager.api.PinLockMissingException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.api.Profile; import org.asamk.signal.manager.api.RateLimitException; @@ -428,7 +429,7 @@ public class ManagerImpl implements Manager { String newNumber, String verificationCode, String pin - ) throws IncorrectPinException, PinLockedException, IOException, NotPrimaryDeviceException { + ) throws IncorrectPinException, PinLockedException, IOException, NotPrimaryDeviceException, PinLockMissingException { if (!account.isPrimaryDevice()) { throw new NotPrimaryDeviceException(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java index 0d20cc0b..72c4b63f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java @@ -21,6 +21,7 @@ import org.asamk.signal.manager.RegistrationManager; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; +import org.asamk.signal.manager.api.PinLockMissingException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.api.RateLimitException; import org.asamk.signal.manager.api.UpdateProfile; @@ -149,7 +150,7 @@ public class RegistrationManagerImpl implements RegistrationManager { public void verifyAccount( String verificationCode, String pin - ) throws IOException, PinLockedException, IncorrectPinException { + ) throws IOException, PinLockedException, IncorrectPinException, PinLockMissingException { if (account.isRegistered()) { throw new IOException("Account is already registered"); } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java index 921d7fc1..659a8c67 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java @@ -4,6 +4,7 @@ import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.api.PinLockMissingException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.api.RateLimitException; import org.asamk.signal.manager.api.VerificationMethodNotAvailableException; @@ -114,7 +115,7 @@ public class NumberVerificationUtils { String pin, PinHelper pinHelper, Verifier verifier - ) throws IOException, PinLockedException, IncorrectPinException { + ) throws IOException, PinLockedException, IncorrectPinException, PinLockMissingException { verificationCode = verificationCode.replace("-", ""); try { final var response = verifier.verify(sessionId, verificationCode, null); @@ -127,7 +128,7 @@ public class NumberVerificationUtils { final var registrationLockData = pinHelper.getRegistrationLockData(pin, e); if (registrationLockData == null) { - throw e; + throw new PinLockMissingException(); } var registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock(); diff --git a/src/main/java/org/asamk/signal/commands/FinishChangeNumberCommand.java b/src/main/java/org/asamk/signal/commands/FinishChangeNumberCommand.java index 8fce4dbf..1242c194 100644 --- a/src/main/java/org/asamk/signal/commands/FinishChangeNumberCommand.java +++ b/src/main/java/org/asamk/signal/commands/FinishChangeNumberCommand.java @@ -9,6 +9,7 @@ import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.NotPrimaryDeviceException; +import org.asamk.signal.manager.api.PinLockMissingException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.output.OutputWriter; @@ -50,6 +51,8 @@ public class FinishChangeNumberCommand implements JsonRpcLocalCommand { + "\nUse '--pin PIN_CODE' to specify the registration lock PIN"); } catch (IncorrectPinException e) { throw new UserErrorException("Verification failed! Invalid pin, tries remaining: " + e.getTriesRemaining()); + } catch (PinLockMissingException e) { + throw new UserErrorException("Account is pin locked, but pin data has been deleted on the server."); } catch (NotPrimaryDeviceException e) { throw new UserErrorException("This command doesn't work on linked devices."); } catch (IOException e) { diff --git a/src/main/java/org/asamk/signal/commands/VerifyCommand.java b/src/main/java/org/asamk/signal/commands/VerifyCommand.java index a8543c5a..6a5046bc 100644 --- a/src/main/java/org/asamk/signal/commands/VerifyCommand.java +++ b/src/main/java/org/asamk/signal/commands/VerifyCommand.java @@ -11,6 +11,7 @@ import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.RegistrationManager; import org.asamk.signal.manager.api.IncorrectPinException; +import org.asamk.signal.manager.api.PinLockMissingException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.output.JsonWriter; import org.slf4j.Logger; @@ -76,6 +77,8 @@ public class VerifyCommand implements RegistrationCommand, JsonRpcRegistrationCo + "\nUse '--pin PIN_CODE' to specify the registration lock PIN"); } catch (IncorrectPinException e) { throw new UserErrorException("Verification failed! Invalid pin, tries remaining: " + e.getTriesRemaining()); + } catch (PinLockMissingException e) { + throw new UserErrorException("Account is pin locked, but pin data has been deleted on the server."); } catch (IOException e) { throw new IOErrorException("Verify error: " + e.getMessage(), e); } diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java index cf6999a9..50150a98 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java @@ -10,6 +10,7 @@ import org.asamk.signal.manager.RegistrationManager; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; +import org.asamk.signal.manager.api.PinLockMissingException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.api.RateLimitException; import org.asamk.signal.manager.api.UserAlreadyExistsException; @@ -105,6 +106,8 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { + (e.getTimeRemaining() / 1000 / 60 / 60)); } catch (IncorrectPinException e) { throw new Error.Failure("Verification failed! Invalid pin, tries remaining: " + e.getTriesRemaining()); + } catch (PinLockMissingException e) { + throw new Error.Failure("Account is pin locked, but pin data has been deleted on the server."); } } From 5dc66f839d3792fbd32f830f9a82d5fbc4223711 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 10 Jun 2025 19:36:43 +0200 Subject: [PATCH 114/144] Close attachment input streams after upload Fixes #1790 --- .../manager/helper/AttachmentHelper.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java index 0ffa62ed..b55153f0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java @@ -44,14 +44,20 @@ public class AttachmentHelper { } public List uploadAttachments(final List attachments) throws AttachmentInvalidException, IOException { - var attachmentStreams = createAttachmentStreams(attachments); + final var attachmentStreams = createAttachmentStreams(attachments); - // Upload attachments here, so we only upload once even for multiple recipients - var attachmentPointers = new ArrayList(attachmentStreams.size()); - for (var attachmentStream : attachmentStreams) { - attachmentPointers.add(uploadAttachment(attachmentStream)); + try { + // Upload attachments here, so we only upload once even for multiple recipients + final var attachmentPointers = new ArrayList(attachmentStreams.size()); + for (final var attachmentStream : attachmentStreams) { + attachmentPointers.add(uploadAttachment(attachmentStream)); + } + return attachmentPointers; + } finally { + for (final var attachmentStream : attachmentStreams) { + attachmentStream.close(); + } } - return attachmentPointers; } private List createAttachmentStreams(List attachments) throws AttachmentInvalidException, IOException { From 70c79eac01304cd2bae5da2973a33e3253d400dc Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 24 Jun 2025 23:13:00 +0200 Subject: [PATCH 115/144] Keep all unhandled fields of remote storage record Fixes #1792 --- .../signal/manager/syncStorage/AccountRecordProcessor.java | 2 +- .../signal/manager/syncStorage/ContactRecordProcessor.java | 2 +- .../signal/manager/syncStorage/GroupV1RecordProcessor.java | 2 +- .../signal/manager/syncStorage/GroupV2RecordProcessor.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java b/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java index 21c0d97a..30816060 100644 --- a/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java +++ b/lib/src/main/java/org/asamk/signal/manager/syncStorage/AccountRecordProcessor.java @@ -111,7 +111,7 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor Date: Wed, 25 Jun 2025 00:20:42 +0200 Subject: [PATCH 116/144] Update dependencies --- CHANGELOG.md | 2 ++ graalvm-config-dir/reflect-config.json | 5 ++++- gradle/libs.versions.toml | 10 +++++----- src/main/java/org/asamk/signal/BaseConfig.java | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ea3c01b..ecb19833 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased] +Requires libsignal-client version 0.74.0. + ## [0.13.16] - 2025-06-07 Requires libsignal-client version 0.73.2. diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index d1c2075b..0bfd1cdf 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -671,6 +671,10 @@ { "name":"long[]" }, +{ + "name":"okhttp3.internal.connection.RealConnectionPool", + "fields":[{"name":"addressStates"}] +}, { "name":"okio.BufferedSink" }, @@ -3036,7 +3040,6 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord", "allDeclaredFields":true, - "fields":[{"name":"aci"}, {"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"e164"}, {"name":"familyName"}, {"name":"givenName"}, {"name":"hidden"}, {"name":"hideStory"}, {"name":"identityKey"}, {"name":"identityState"}, {"name":"markedUnread"}, {"name":"mutedUntilTimestamp"}, {"name":"nickname"}, {"name":"note"}, {"name":"pni"}, {"name":"pniSignatureVerified"}, {"name":"profileKey"}, {"name":"systemFamilyName"}, {"name":"systemGivenName"}, {"name":"systemNickname"}, {"name":"unregisteredAtTimestamp"}, {"name":"username"}, {"name":"whitelisted"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a6b9fda1..4ff08539 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,15 +3,15 @@ slf4j = "2.0.17" [libraries] bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.81" -jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.19.0" +jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.19.1" argparse4j = "net.sourceforge.argparse4j:argparse4j:0.9.0" dbusjava = "com.github.hypfvieh:dbus-java-transport-native-unixsocket:5.0.0" slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.18" -signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_124" -sqlite = "org.xerial:sqlite-jdbc:3.49.1.0" +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_125" +sqlite = "org.xerial:sqlite-jdbc:3.50.1.0" hikari = "com.zaxxer:HikariCP:6.3.0" -junit-jupiter = "org.junit.jupiter:junit-jupiter:5.13.1" -junit-launcher = "org.junit.platform:junit-platform-launcher:1.13.1" +junit-jupiter = "org.junit.jupiter:junit-jupiter:5.13.2" +junit-launcher = "org.junit.platform:junit-platform-launcher:1.13.2" diff --git a/src/main/java/org/asamk/signal/BaseConfig.java b/src/main/java/org/asamk/signal/BaseConfig.java index 8a38c36a..1f0922fe 100644 --- a/src/main/java/org/asamk/signal/BaseConfig.java +++ b/src/main/java/org/asamk/signal/BaseConfig.java @@ -8,7 +8,7 @@ public class BaseConfig { public static final String PROJECT_VERSION = BaseConfig.class.getPackage().getImplementationVersion(); static final String USER_AGENT_SIGNAL_ANDROID = Optional.ofNullable(System.getenv("SIGNAL_CLI_USER_AGENT")) - .orElse("Signal-Android/7.44.1"); + .orElse("Signal-Android/7.46.1"); static final String USER_AGENT_SIGNAL_CLI = PROJECT_NAME == null ? "signal-cli" : PROJECT_NAME + "/" + PROJECT_VERSION; From e6113d4d96f7677422d348b16408d2a4990fb934 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 28 Jun 2025 14:35:56 +0200 Subject: [PATCH 117/144] Update libsignal-service-java --- graalvm-config-dir/reflect-config.json | 1 - gradle/libs.versions.toml | 2 +- .../signal/manager/api/DeviceLinkUrl.java | 3 +-- .../signal/manager/config/LiveConfig.java | 3 +-- .../signal/manager/config/StagingConfig.java | 3 +-- .../manager/helper/AttachmentHelper.java | 9 ++++++++- .../internal/SignalWebSocketHealthMonitor.java | 8 +++++--- .../manager/storage/prekeys/PreKeyStore.java | 7 ++++--- .../storage/prekeys/SignedPreKeyStore.java | 7 ++++--- .../asamk/signal/manager/util/KeyUtils.java | 18 +++++++----------- src/main/java/org/asamk/signal/BaseConfig.java | 2 +- 11 files changed, 33 insertions(+), 30 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 0bfd1cdf..c5459804 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -3070,7 +3070,6 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record", "allDeclaredFields":true, - "fields":[{"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"dontNotifyForMentionsIfMuted"}, {"name":"hideStory"}, {"name":"markedUnread"}, {"name":"masterKey"}, {"name":"mutedUntilTimestamp"}, {"name":"storySendMode"}, {"name":"whitelisted"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4ff08539..680ca54a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.18" -signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_125" +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_126" sqlite = "org.xerial:sqlite-jdbc:3.50.1.0" hikari = "com.zaxxer:HikariCP:6.3.0" junit-jupiter = "org.junit.jupiter:junit-jupiter:5.13.2" diff --git a/lib/src/main/java/org/asamk/signal/manager/api/DeviceLinkUrl.java b/lib/src/main/java/org/asamk/signal/manager/api/DeviceLinkUrl.java index 54271dac..2bb63507 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/DeviceLinkUrl.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/DeviceLinkUrl.java @@ -2,7 +2,6 @@ package org.asamk.signal.manager.api; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.ECPublicKey; import java.net.URI; @@ -37,7 +36,7 @@ public record DeviceLinkUrl(String deviceIdentifier, ECPublicKey deviceKey) { } ECPublicKey deviceKey; try { - deviceKey = Curve.decodePoint(publicKeyBytes, 0); + deviceKey = new ECPublicKey(publicKeyBytes); } catch (InvalidKeyException e) { throw new InvalidDeviceLinkException("Invalid device link", e); } diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java index 111695f6..890d7081 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -2,7 +2,6 @@ package org.asamk.signal.manager.config; import org.signal.libsignal.net.Network.Environment; import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.internal.configuration.HttpProxy; @@ -80,7 +79,7 @@ class LiveConfig { static ECPublicKey getUnidentifiedSenderTrustRoot() { try { - return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0); + return new ECPublicKey(UNIDENTIFIED_SENDER_TRUST_ROOT); } catch (InvalidKeyException e) { throw new AssertionError(e); } diff --git a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java index e72c66b5..6d8f08c7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java @@ -2,7 +2,6 @@ package org.asamk.signal.manager.config; import org.signal.libsignal.net.Network; import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.internal.configuration.HttpProxy; @@ -80,7 +79,7 @@ class StagingConfig { static ECPublicKey getUnidentifiedSenderTrustRoot() { try { - return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0); + return new ECPublicKey(UNIDENTIFIED_SENDER_TRUST_ROOT); } catch (InvalidKeyException e) { throw new AssertionError(e); } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java index b55153f0..08526c72 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java @@ -9,6 +9,7 @@ import org.asamk.signal.manager.util.IOUtils; import org.signal.libsignal.protocol.InvalidMessageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; @@ -138,9 +139,15 @@ public class AttachmentHelper { SignalServiceAttachmentPointer pointer, File tmpFile ) throws IOException { + if (pointer.getDigest().isEmpty()) { + throw new IOException("Attachment pointer has no digest."); + } try { return dependencies.getMessageReceiver() - .retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE); + .retrieveAttachment(pointer, + tmpFile, + ServiceConfig.MAX_ATTACHMENT_SIZE, + AttachmentCipherInputStream.IntegrityCheck.forEncryptedDigest(pointer.getDigest().get())); } catch (MissingConfigurationException | InvalidMessageException e) { throw new IOException(e); } diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalWebSocketHealthMonitor.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalWebSocketHealthMonitor.java index dfae5985..7d927ddd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalWebSocketHealthMonitor.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalWebSocketHealthMonitor.java @@ -56,7 +56,10 @@ final class SignalWebSocketHealthMonitor implements HealthMonitor { .distinctUntilChanged() .subscribe(this::onStateChanged); - webSocket.setKeepAliveChangedListener(this::updateKeepAliveSenderStatus); + webSocket.addKeepAliveChangeListener(() -> { + executor.execute(this::updateKeepAliveSenderStatus); + return Unit.INSTANCE; + }); }); } @@ -78,7 +81,7 @@ final class SignalWebSocketHealthMonitor implements HealthMonitor { public void onMessageError(int status, boolean isIdentifiedWebSocket) { } - private Unit updateKeepAliveSenderStatus() { + private void updateKeepAliveSenderStatus() { if (keepAliveSender == null && sendKeepAlives()) { keepAliveSender = new KeepAliveSender(); keepAliveSender.start(); @@ -86,7 +89,6 @@ final class SignalWebSocketHealthMonitor implements HealthMonitor { keepAliveSender.shutdown(); keepAliveSender = null; } - return Unit.INSTANCE; } private boolean sendKeepAlives() { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/prekeys/PreKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/prekeys/PreKeyStore.java index d2d55710..8e8bd2ab 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/prekeys/PreKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/prekeys/PreKeyStore.java @@ -4,8 +4,9 @@ import org.asamk.signal.manager.storage.Database; import org.asamk.signal.manager.storage.Utils; import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.InvalidKeyIdException; -import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.ECKeyPair; +import org.signal.libsignal.protocol.ecc.ECPrivateKey; +import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.signal.libsignal.protocol.state.PreKeyRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -176,8 +177,8 @@ public class PreKeyStore implements SignalServicePreKeyStore { private PreKeyRecord getPreKeyRecordFromResultSet(ResultSet resultSet) throws SQLException { try { final var keyId = resultSet.getInt("key_id"); - final var publicKey = Curve.decodePoint(resultSet.getBytes("public_key"), 0); - final var privateKey = Curve.decodePrivatePoint(resultSet.getBytes("private_key")); + final var publicKey = new ECPublicKey(resultSet.getBytes("public_key")); + final var privateKey = new ECPrivateKey(resultSet.getBytes("private_key")); return new PreKeyRecord(keyId, new ECKeyPair(publicKey, privateKey)); } catch (InvalidKeyException e) { return null; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/prekeys/SignedPreKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/prekeys/SignedPreKeyStore.java index d62e939d..4644c3ba 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/prekeys/SignedPreKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/prekeys/SignedPreKeyStore.java @@ -4,8 +4,9 @@ import org.asamk.signal.manager.storage.Database; import org.asamk.signal.manager.storage.Utils; import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.InvalidKeyIdException; -import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.ECKeyPair; +import org.signal.libsignal.protocol.ecc.ECPrivateKey; +import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.signal.libsignal.protocol.state.SignedPreKeyRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -238,8 +239,8 @@ public class SignedPreKeyStore implements org.signal.libsignal.protocol.state.Si private SignedPreKeyRecord getSignedPreKeyRecordFromResultSet(ResultSet resultSet) throws SQLException { try { final var keyId = resultSet.getInt("key_id"); - final var publicKey = Curve.decodePoint(resultSet.getBytes("public_key"), 0); - final var privateKey = Curve.decodePrivatePoint(resultSet.getBytes("private_key")); + final var publicKey = new ECPublicKey(resultSet.getBytes("public_key")); + final var privateKey = new ECPrivateKey(resultSet.getBytes("private_key")); final var signature = resultSet.getBytes("signature"); final var timestamp = resultSet.getLong("timestamp"); return new SignedPreKeyRecord(keyId, timestamp, new ECKeyPair(publicKey, privateKey), signature); diff --git a/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java index 37a0bf82..31c3cd39 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java @@ -4,7 +4,7 @@ import org.asamk.signal.manager.storage.SignalAccount; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKeyPair; import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.ecc.Curve; +import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECPrivateKey; import org.signal.libsignal.protocol.kem.KEMKeyPair; import org.signal.libsignal.protocol.kem.KEMKeyType; @@ -33,8 +33,8 @@ public class KeyUtils { public static IdentityKeyPair getIdentityKeyPair(byte[] publicKeyBytes, byte[] privateKeyBytes) { try { - IdentityKey publicKey = new IdentityKey(publicKeyBytes); - ECPrivateKey privateKey = Curve.decodePrivatePoint(privateKeyBytes); + final var publicKey = new IdentityKey(publicKeyBytes); + final var privateKey = new ECPrivateKey(privateKeyBytes); return new IdentityKeyPair(publicKey, privateKey); } catch (InvalidKeyException e) { @@ -43,7 +43,7 @@ public class KeyUtils { } public static IdentityKeyPair generateIdentityKeyPair() { - var djbKeyPair = Curve.generateKeyPair(); + var djbKeyPair = ECKeyPair.generate(); var djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey()); var djbPrivateKey = djbKeyPair.getPrivateKey(); @@ -54,7 +54,7 @@ public class KeyUtils { var records = new ArrayList(PREKEY_BATCH_SIZE); for (var i = 0; i < PREKEY_BATCH_SIZE; i++) { var preKeyId = (offset + i) % PREKEY_MAXIMUM_ID; - var keyPair = Curve.generateKeyPair(); + var keyPair = ECKeyPair.generate(); var record = new PreKeyRecord(preKeyId, keyPair); records.add(record); @@ -66,13 +66,9 @@ public class KeyUtils { final int signedPreKeyId, final ECPrivateKey privateKey ) { - var keyPair = Curve.generateKeyPair(); + var keyPair = ECKeyPair.generate(); byte[] signature; - try { - signature = Curve.calculateSignature(privateKey, keyPair.getPublicKey().serialize()); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } + signature = privateKey.calculateSignature(keyPair.getPublicKey().serialize()); return new SignedPreKeyRecord(signedPreKeyId, System.currentTimeMillis(), keyPair, signature); } diff --git a/src/main/java/org/asamk/signal/BaseConfig.java b/src/main/java/org/asamk/signal/BaseConfig.java index 1f0922fe..730485bc 100644 --- a/src/main/java/org/asamk/signal/BaseConfig.java +++ b/src/main/java/org/asamk/signal/BaseConfig.java @@ -8,7 +8,7 @@ public class BaseConfig { public static final String PROJECT_VERSION = BaseConfig.class.getPackage().getImplementationVersion(); static final String USER_AGENT_SIGNAL_ANDROID = Optional.ofNullable(System.getenv("SIGNAL_CLI_USER_AGENT")) - .orElse("Signal-Android/7.46.1"); + .orElse("Signal-Android/7.47.1"); static final String USER_AGENT_SIGNAL_CLI = PROJECT_NAME == null ? "signal-cli" : PROJECT_NAME + "/" + PROJECT_VERSION; From fa9bb3c21030fb0ef89a440859b796a1e5ebd64a Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 28 Jun 2025 14:57:20 +0200 Subject: [PATCH 118/144] Bump version to 0.13.17 --- CHANGELOG.md | 14 ++++++++++++-- build.gradle.kts | 2 +- data/org.asamk.SignalCli.metainfo.xml | 3 +++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecb19833..62abfe70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,18 @@ # Changelog -## [Unreleased] +## [0.13.17] - 2025-06-28 -Requires libsignal-client version 0.74.0. +Requires libsignal-client version 0.76.0. + +### Fixed + +- Fix issue when loading an older inactive group +- Close attachment input streams after upload +- Fix storage sync behavior with unhandled fields + +### Changed + +- Improve behavior when pin data doesn't exist on the server ## [0.13.16] - 2025-06-07 diff --git a/build.gradle.kts b/build.gradle.kts index 8fadb883..11715e36 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.13.17-SNAPSHOT" + version = "0.13.17" } java { diff --git a/data/org.asamk.SignalCli.metainfo.xml b/data/org.asamk.SignalCli.metainfo.xml index b7483db9..95646534 100644 --- a/data/org.asamk.SignalCli.metainfo.xml +++ b/data/org.asamk.SignalCli.metainfo.xml @@ -45,6 +45,9 @@ intense + + https://github.com/AsamK/signal-cli/releases/tag/v0.13.17 + https://github.com/AsamK/signal-cli/releases/tag/v0.13.16 From e7ca02f1fb1a06caf712a3316648947e99fed5da Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 19 Apr 2024 19:25:27 +0200 Subject: [PATCH 119/144] Prepare next release --- CHANGELOG.md | 2 ++ build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62abfe70..5437bd63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## [Unreleased] + ## [0.13.17] - 2025-06-28 Requires libsignal-client version 0.76.0. diff --git a/build.gradle.kts b/build.gradle.kts index 11715e36..c3568444 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.13.17" + version = "0.13.18-SNAPSHOT" } java { From 069325af4715b63725ac4a41874dd453dea563cf Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 29 Jun 2025 11:16:32 +0200 Subject: [PATCH 120/144] Extend shutdown request with optional error --- src/main/java/org/asamk/signal/Shutdown.java | 15 ++++++++++++--- .../org/asamk/signal/commands/DaemonCommand.java | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/asamk/signal/Shutdown.java b/src/main/java/org/asamk/signal/Shutdown.java index a59497c9..c51c12e4 100644 --- a/src/main/java/org/asamk/signal/Shutdown.java +++ b/src/main/java/org/asamk/signal/Shutdown.java @@ -1,5 +1,6 @@ package org.asamk.signal; +import org.asamk.signal.commands.exceptions.CommandException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,7 +12,7 @@ import sun.misc.Signal; public class Shutdown { private static final Logger logger = LoggerFactory.getLogger(Shutdown.class); - private static final CompletableFuture shutdown = new CompletableFuture<>(); + private static final CompletableFuture shutdown = new CompletableFuture<>(); private static final CompletableFuture shutdownComplete = new CompletableFuture<>(); private static boolean initialized = false; @@ -43,9 +44,17 @@ public class Shutdown { shutdown.complete(null); } - public static void waitForShutdown() throws InterruptedException { + public static void triggerShutdown(CommandException exception) { + logger.debug("Triggering shutdown with exception.", exception); + shutdown.complete(exception); + } + + public static void waitForShutdown() throws InterruptedException, CommandException { try { - shutdown.get(); + final var result = shutdown.get(); + if (result instanceof CommandException e) { + throw e; + } } catch (ExecutionException e) { throw new RuntimeException(e); } diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 197e1286..6790c65d 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -97,7 +97,7 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { final OutputWriter outputWriter ) throws CommandException { Shutdown.installHandler(); - logger.info("Starting daemon in single-account mode for " + m.getSelfNumber()); + logger.info("Starting daemon in single-account mode for {}", m.getSelfNumber()); final var noReceiveStdOut = Boolean.TRUE.equals(ns.getBoolean("no-receive-stdout")); final var receiveMode = ns.get("receive-mode"); final var receiveConfig = getReceiveConfig(ns); From cb06cbdcca718e7a2f5e9360f3eb6b319a9ea933 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 29 Jun 2025 11:21:31 +0200 Subject: [PATCH 121/144] Shut down when dbus daemon connection goes away unexpectedly Fixes #1800 --- .../java/org/asamk/signal/dbus/DbusHandler.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/dbus/DbusHandler.java b/src/main/java/org/asamk/signal/dbus/DbusHandler.java index b6799de7..2ed6d11f 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusHandler.java +++ b/src/main/java/org/asamk/signal/dbus/DbusHandler.java @@ -1,17 +1,21 @@ package org.asamk.signal.dbus; import org.asamk.signal.DbusConfig; +import org.asamk.signal.Shutdown; import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.MultiAccountManager; +import org.freedesktop.dbus.connections.IDisconnectCallback; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder; import org.freedesktop.dbus.exceptions.DBusException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -94,7 +98,9 @@ public class DbusHandler implements AutoCloseable { final var busType = isDbusSystem ? DBusConnection.DBusBusType.SYSTEM : DBusConnection.DBusBusType.SESSION; logger.debug("Starting DBus server on {} bus: {}", busType, busname); try { - dBusConnection = DBusConnectionBuilder.forType(busType).build(); + dBusConnection = DBusConnectionBuilder.forType(busType) + .withDisconnectCallback(new DisconnectCallback()) + .build(); dbusRunner.run(dBusConnection); } catch (DBusException e) { throw new UnexpectedErrorException("Dbus command failed: " + e.getMessage(), e); @@ -141,4 +147,13 @@ public class DbusHandler implements AutoCloseable { void run(DBusConnection connection) throws DBusException; } + + private static final class DisconnectCallback implements IDisconnectCallback { + + @Override + public void disconnectOnError(IOException ex) { + logger.debug("DBus daemon disconnected unexpectedly, shutting down"); + Shutdown.triggerShutdown(new IOErrorException("Unexpected dbus daemon disconnect", ex)); + } + } } From 3180eba83694cf24d23cf9f3e4d73fa5e8791533 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 8 Jul 2025 17:34:04 +0200 Subject: [PATCH 122/144] Exit if account check fails at startup Fixes #1804 --- .../signal/manager/SignalAccountFiles.java | 22 ++++++++++++++----- src/main/java/org/asamk/signal/App.java | 2 ++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java index 4072a9df..cd0b5a21 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -2,6 +2,7 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.api.AccountCheckException; import org.asamk.signal.manager.api.NotRegisteredException; +import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.ServiceEnvironment; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; @@ -63,19 +64,28 @@ public class SignalAccountFiles { return accountsStore.getAllNumbers(); } - public MultiAccountManager initMultiAccountManager() throws IOException { - final var managers = accountsStore.getAllAccounts().parallelStream().map(a -> { + public MultiAccountManager initMultiAccountManager() throws IOException, AccountCheckException { + final var managerPairs = accountsStore.getAllAccounts().parallelStream().map(a -> { try { - return initManager(a.number(), a.path()); - } catch (NotRegisteredException | IOException | AccountCheckException e) { + return new Pair(initManager(a.number(), a.path()), null); + } catch (NotRegisteredException e) { logger.warn("Ignoring {}: {} ({})", a.number(), e.getMessage(), e.getClass().getSimpleName()); return null; - } catch (Throwable e) { + } catch (AccountCheckException | IOException e) { logger.error("Failed to load {}: {} ({})", a.number(), e.getMessage(), e.getClass().getSimpleName()); - throw e; + return new Pair(null, e); } }).filter(Objects::nonNull).toList(); + for (final var pair : managerPairs) { + if (pair.second() instanceof IOException e) { + throw e; + } else if (pair.second() instanceof AccountCheckException e) { + throw e; + } + } + + final var managers = managerPairs.stream().map(Pair::first).toList(); return new MultiAccountManagerImpl(managers, this); } diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index e494d201..cf731ea9 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -291,6 +291,8 @@ public class App { commandHandler.handleMultiLocalCommand(command, multiAccountManager); } catch (IOException e) { throw new IOErrorException("Failed to load local accounts file", e); + } catch (AccountCheckException e) { + throw new UnexpectedErrorException("Failed to load local accounts file", e); } } From 887ed3bb448624095f950674931cf60924c5f219 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 8 Jul 2025 17:35:17 +0200 Subject: [PATCH 123/144] Show better error message when sending fails due to missing pre keys --- .../java/org/asamk/signal/commands/SendCommand.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 39be74ee..cce1d21e 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -250,8 +250,14 @@ public class SendCommand implements JsonRpcLocalCommand { : m.sendMessage(message, recipientIdentifiers, notifySelf); outputResult(outputWriter, results); } catch (AttachmentInvalidException | IOException e) { - throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() - .getSimpleName() + ")", e); + if (e instanceof IOException io && io.getMessage().contains("No prekeys available")) { + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + "), maybe one of the devices of the recipient wasn't online for a while.", + e); + } else { + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")", e); + } } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { throw new UserErrorException(e.getMessage()); } catch (UnregisteredRecipientException e) { From 1b7f75559008ea2dc557bc3e5228bd0db7a30b3f Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Jul 2025 10:05:14 +0200 Subject: [PATCH 124/144] Update dependencies --- client/Cargo.lock | 512 ++++++++++++++++++++--------------- client/Cargo.toml | 2 +- client/src/transports/mod.rs | 7 +- 3 files changed, 295 insertions(+), 226 deletions(-) diff --git a/client/Cargo.lock b/client/Cargo.lock index d0cd01f4..ed225c51 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -13,15 +13,15 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -34,44 +34,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", - "once_cell", + "once_cell_polyfill", "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "async-trait" @@ -90,17 +90,11 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -119,9 +113,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bytes" @@ -131,9 +125,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.18" +version = "1.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" dependencies = [ "shlex", ] @@ -146,15 +140,15 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "clap" -version = "4.5.35" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" dependencies = [ "clap_builder", "clap_derive", @@ -162,9 +156,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" dependencies = [ "anstream", "anstyle", @@ -175,9 +169,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck", "proc-macro2", @@ -187,15 +181,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "combine" @@ -209,9 +203,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -242,12 +236,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -326,9 +320,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -343,9 +337,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.4.8" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" dependencies = [ "atomic-waker", "bytes", @@ -362,9 +356,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "heck" @@ -434,11 +428,10 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", "http", "hyper", "hyper-util", @@ -452,12 +445,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", @@ -472,21 +466,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -495,31 +490,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -527,67 +502,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "idna" version = "1.0.3" @@ -601,9 +563,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -611,14 +573,25 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", ] +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -655,9 +628,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jsonrpsee" -version = "0.24.9" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" +checksum = "1fba77a59c4c644fd48732367624d1bcf6f409f9c9a286fbc71d2f1fc0b2ea16" dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", @@ -668,9 +641,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.9" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456196007ca3a14db478346f58c7238028d55ee15c1df15115596e411ff27925" +checksum = "693c93cbb7db25f4108ed121304b671a36002c2db67dff2ee4391a688c738547" dependencies = [ "async-trait", "bytes", @@ -684,19 +657,19 @@ dependencies = [ "rustc-hash", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tokio-stream", + "tower", "tracing", ] [[package]] name = "jsonrpsee-http-client" -version = "0.24.9" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c872b6c9961a4ccc543e321bb5b89f6b2d2c7fe8b61906918273a3333c95400c" +checksum = "6962d2bd295f75e97dd328891e58fce166894b974c1f7ce2e7597f02eeceb791" dependencies = [ - "async-trait", "base64", "http-body", "hyper", @@ -708,18 +681,17 @@ dependencies = [ "rustls-platform-verifier", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tower", - "tracing", "url", ] [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.9" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e65763c942dfc9358146571911b0cd1c361c2d63e2d2305622d40d36376ca80" +checksum = "2fa4f5daed39f982a1bb9d15449a28347490ad42b212f8eaa2a2a344a0dce9e9" dependencies = [ "heck", "proc-macro-crate", @@ -730,33 +702,33 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.9" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a8e70baf945b6b5752fc8eb38c918a48f1234daf11355e07106d963f860089" +checksum = "66df7256371c45621b3b7d2fb23aea923d577616b9c0e9c0b950a6ea5c2be0ca" dependencies = [ "http", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] name = "libc" -version = "0.2.171" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "log" @@ -766,28 +738,28 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "miniz_oxide" -version = "0.8.7" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -805,6 +777,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "openssl-probe" version = "0.1.6" @@ -849,6 +827,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "proc-macro-crate" version = "3.3.0" @@ -860,9 +847,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -892,9 +879,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -904,9 +891,9 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", @@ -917,9 +904,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.25" +version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" dependencies = [ "log", "once_cell", @@ -944,15 +931,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] [[package]] name = "rustls-platform-verifier" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5467026f437b4cb2a533865eaa73eb840019a0916f4b9ec563c6e617e086c9" +checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ "core-foundation", "core-foundation-sys", @@ -965,7 +955,7 @@ dependencies = [ "rustls-webpki", "security-framework", "security-framework-sys", - "webpki-root-certs", + "webpki-root-certs 0.26.11", "windows-sys 0.59.0", ] @@ -977,9 +967,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -1090,24 +1080,21 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1133,9 +1120,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -1143,10 +1130,16 @@ dependencies = [ ] [[package]] -name = "synstructure" -version = "0.13.1" +name = "sync_wrapper" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -1205,9 +1198,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -1215,15 +1208,17 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", + "slab", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -1263,9 +1258,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -1276,15 +1271,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "toml_datetime", @@ -1293,17 +1288,16 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -1324,7 +1318,6 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1332,9 +1325,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -1343,9 +1336,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] @@ -1379,12 +1372,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -1418,15 +1405,24 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "webpki-root-certs" -version = "0.26.8" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09aed61f5e8d2c18344b3faa33a4c837855fe56642757754775548fee21386c4" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.1", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86138b15b2b7d561bc4469e77027b8dd005a43dc502e9031d1f5afc8ce1f280e" dependencies = [ "rustls-pki-types", ] @@ -1467,6 +1463,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -1491,13 +1496,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -1510,6 +1531,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -1522,6 +1549,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -1534,12 +1567,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -1552,6 +1597,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -1564,6 +1615,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -1576,6 +1633,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -1589,31 +1652,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winnow" -version = "0.7.6" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -1623,9 +1686,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -1661,10 +1724,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] -name = "zerovec" -version = "0.10.4" +name = "zerotrie" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -1673,9 +1747,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/client/Cargo.toml b/client/Cargo.toml index c435149f..694ee652 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -12,7 +12,7 @@ log = "0.4" serde = "1" serde_json = "1" tokio = { version = "1", features = ["rt", "macros", "net", "rt-multi-thread"] } -jsonrpsee = { version = "0.24", features = [ +jsonrpsee = { version = "0.25", features = [ "macros", "async-client", "http-client", diff --git a/client/src/transports/mod.rs b/client/src/transports/mod.rs index 04f4390f..3d0c4195 100644 --- a/client/src/transports/mod.rs +++ b/client/src/transports/mod.rs @@ -1,8 +1,5 @@ use futures_util::{stream::StreamExt, Sink, SinkExt, Stream}; -use jsonrpsee::core::{ - async_trait, - client::{ReceivedMessage, TransportReceiverT, TransportSenderT}, -}; +use jsonrpsee::core::client::{ReceivedMessage, TransportReceiverT, TransportSenderT}; use thiserror::Error; pub mod ipc; @@ -21,7 +18,6 @@ struct Sender> { inner: T, } -#[async_trait] impl + Unpin + 'static> TransportSenderT for Sender { @@ -48,7 +44,6 @@ struct Receiver { inner: T, } -#[async_trait] impl> + Unpin + 'static> TransportReceiverT for Receiver { From ff846bc6784f1245bc2b1c83651fea3b673c563a Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Jul 2025 10:05:57 +0200 Subject: [PATCH 125/144] Fix clippy warnings --- client/src/main.rs | 2 +- client/src/transports/mod.rs | 6 +++--- client/src/transports/stream_codec.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/main.rs b/client/src/main.rs index 35c1ab22..68de03d4 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -70,7 +70,7 @@ async fn handle_command( .await .map_err(|e| RpcError::Custom(format!("JSON-RPC command startLink failed: {e:?}")))? .device_link_uri; - println!("{}", url); + println!("{url}"); client.finish_link(url, name).await } CliCommands::ListAccounts => client.list_accounts().await, diff --git a/client/src/transports/mod.rs b/client/src/transports/mod.rs index 3d0c4195..ad552cbf 100644 --- a/client/src/transports/mod.rs +++ b/client/src/transports/mod.rs @@ -27,7 +27,7 @@ impl + Unpin + 'static> T self.inner .send(body) .await - .map_err(|e| Errors::Other(format!("{:?}", e)))?; + .map_err(|e| Errors::Other(format!("{e:?}")))?; Ok(()) } @@ -35,7 +35,7 @@ impl + Unpin + 'static> T self.inner .close() .await - .map_err(|e| Errors::Other(format!("{:?}", e)))?; + .map_err(|e| Errors::Other(format!("{e:?}")))?; Ok(()) } } @@ -53,7 +53,7 @@ impl> + Unpin + 'static> match self.inner.next().await { None => Err(Errors::Closed), Some(Ok(msg)) => Ok(ReceivedMessage::Text(msg)), - Some(Err(e)) => Err(Errors::Other(format!("{:?}", e))), + Some(Err(e)) => Err(Errors::Other(format!("{e:?}"))), } } } diff --git a/client/src/transports/stream_codec.rs b/client/src/transports/stream_codec.rs index 6f77306f..e46233cb 100644 --- a/client/src/transports/stream_codec.rs +++ b/client/src/transports/stream_codec.rs @@ -41,7 +41,7 @@ impl Decoder for StreamCodec { match str::from_utf8(line.as_ref()) { Ok(s) => Ok(Some(s.to_string())), - Err(_) => Err(io::Error::new(io::ErrorKind::Other, "invalid UTF-8")), + Err(_) => Err(io::Error::other("invalid UTF-8")), } } else { Ok(None) From d54be747da7fec55686eae27a26dbd266e4fbb6d Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Jul 2025 10:15:27 +0200 Subject: [PATCH 126/144] Remove unused dependency --- client/Cargo.lock | 1 - client/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/client/Cargo.lock b/client/Cargo.lock index ed225c51..01f28e0c 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -1070,7 +1070,6 @@ dependencies = [ "clap", "futures-util", "jsonrpsee", - "log", "serde", "serde_json", "thiserror 2.0.12", diff --git a/client/Cargo.toml b/client/Cargo.toml index 694ee652..85daf1e2 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" [dependencies] anyhow = "1" clap = { version = "4", features = ["cargo", "derive", "wrap_help"] } -log = "0.4" serde = "1" serde_json = "1" tokio = { version = "1", features = ["rt", "macros", "net", "rt-multi-thread"] } From a96626c4685cfaf589deea45ff2d46e5d6883f3c Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Jul 2025 10:16:57 +0200 Subject: [PATCH 127/144] Update to rust 2024 edition --- client/Cargo.toml | 2 +- client/src/jsonrpc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/Cargo.toml b/client/Cargo.toml index 85daf1e2..aff9ede4 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "signal-cli-client" version = "0.0.1" -edition = "2021" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index b085cde5..6598d525 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -417,6 +417,6 @@ pub async fn connect_unix( Ok(ClientBuilder::default().build_with_tokio(sender, receiver)) } -pub async fn connect_http(uri: &str) -> Result { +pub async fn connect_http(uri: &str) -> Result, Error> { HttpClientBuilder::default().build(uri) } From ca33249170118be0d2fe3e9deed4ad23b34ac875 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Jul 2025 11:03:28 +0200 Subject: [PATCH 128/144] Handle rate limit exception correctly when querying usernames Fixes #1797 --- graalvm-config-dir/jni-config.json | 6 ++++- .../org/asamk/signal/manager/Manager.java | 2 +- .../manager/helper/RecipientHelper.java | 25 +++++++++++++------ .../signal/manager/internal/ManagerImpl.java | 2 +- .../signal/commands/GetUserStatusCommand.java | 13 +++++++--- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index 23a2160c..523a6c03 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -58,7 +58,7 @@ }, { "name":"java.lang.Throwable", - "methods":[{"name":"getMessage","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] + "methods":[{"name":"getMessage","parameterTypes":[] }, {"name":"setStackTrace","parameterTypes":["java.lang.StackTraceElement[]"] }, {"name":"toString","parameterTypes":[] }] }, { "name":"java.lang.UnsatisfiedLinkError", @@ -126,6 +126,10 @@ "name":"org.signal.libsignal.net.NetworkException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, +{ + "name":"org.signal.libsignal.net.RetryLaterException", + "methods":[{"name":"","parameterTypes":["long"] }] +}, { "name":"org.signal.libsignal.protocol.DuplicateMessageException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 238084e7..ccb661e1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -96,7 +96,7 @@ public interface Manager extends Closeable { */ Map getUserStatus(Set numbers) throws IOException, RateLimitException; - Map getUsernameStatus(Set usernames); + Map getUsernameStatus(Set usernames) throws IOException; void updateAccountAttributes( String deviceName, diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java index ecb85254..a73bb741 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java @@ -17,6 +17,7 @@ import org.whispersystems.signalservice.api.push.ServiceId.PNI; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.CdsiInvalidArgumentException; import org.whispersystems.signalservice.api.push.exceptions.CdsiInvalidTokenException; +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; import java.io.IOException; import java.util.Collection; @@ -68,7 +69,7 @@ public class RecipientHelper { .toSignalServiceAddress(); } - public Set resolveRecipients(Collection recipients) throws UnregisteredRecipientException { + public Set resolveRecipients(Collection recipients) throws UnregisteredRecipientException, IOException { final var recipientIds = new HashSet(recipients.size()); for (var number : recipients) { final var recipientId = resolveRecipient(number); @@ -91,7 +92,11 @@ public class RecipientHelper { } }); } else if (recipient instanceof RecipientIdentifier.Username(String username)) { - return resolveRecipientByUsernameOrLink(username, false); + try { + return resolveRecipientByUsernameOrLink(username, false); + } catch (Exception e) { + return null; + } } throw new AssertionError("Unexpected RecipientIdentifier: " + recipient); } @@ -99,7 +104,7 @@ public class RecipientHelper { public RecipientId resolveRecipientByUsernameOrLink( String username, boolean forceRefresh - ) throws UnregisteredRecipientException { + ) throws UnregisteredRecipientException, IOException { final Username finalUsername; try { finalUsername = getUsernameFromUsernameOrLink(username); @@ -110,11 +115,15 @@ public class RecipientHelper { try { final var aci = handleResponseException(dependencies.getUsernameApi().getAciByUsername(finalUsername)); return account.getRecipientStore().resolveRecipientTrusted(aci, finalUsername.getUsername()); - } catch (IOException e) { - throw new UnregisteredRecipientException(new org.asamk.signal.manager.api.RecipientAddress(null, - null, - null, - username)); + } catch (NonSuccessfulResponseCodeException e) { + if (e.code == 404) { + throw new UnregisteredRecipientException(new org.asamk.signal.manager.api.RecipientAddress(null, + null, + null, + username)); + } + logger.debug("Failed to get uuid for username: {}", username, e); + throw e; } } return account.getRecipientStore().resolveRecipientByUsername(finalUsername.getUsername(), () -> { diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java index ffb74de2..eb672d24 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java @@ -285,7 +285,7 @@ public class ManagerImpl implements Manager { } @Override - public Map getUsernameStatus(Set usernames) { + public Map getUsernameStatus(Set usernames) throws IOException { final var registeredUsers = new HashMap(); for (final var username : usernames) { try { diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index e3d36d85..eab4155c 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -64,9 +64,16 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand { } final var usernames = ns.getList("username"); - final var registeredUsernames = usernames == null - ? Map.of() - : m.getUsernameStatus(new HashSet<>(usernames)); + final Map registeredUsernames; + try { + registeredUsernames = usernames == null ? Map.of() : m.getUsernameStatus(new HashSet<>(usernames)); + } catch (IOException e) { + throw new IOErrorException("Unable to check if users are registered: " + + e.getMessage() + + " (" + + e.getClass().getSimpleName() + + ")", e); + } // Output switch (outputWriter) { From 4ce194afe28e8918d066b267dec8ea9cf6b4b2a0 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Jul 2025 11:03:54 +0200 Subject: [PATCH 129/144] Add missing username parameter to getUserStatus command in json-rpc client --- client/src/cli.rs | 2 ++ client/src/jsonrpc.rs | 1 + client/src/main.rs | 9 +++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/client/src/cli.rs b/client/src/cli.rs index 4ff9d458..e0abb717 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -84,6 +84,8 @@ pub enum CliCommands { }, GetUserStatus { recipient: Vec, + #[arg(long)] + username: Vec, }, JoinGroup { #[arg(long)] diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index 6598d525..3b6d583b 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -70,6 +70,7 @@ pub trait Rpc { &self, account: Option, recipients: Vec, + usernames: Vec, ) -> Result; #[method(name = "joinGroup", param_kind = map)] diff --git a/client/src/main.rs b/client/src/main.rs index 68de03d4..6466b2e6 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -60,8 +60,13 @@ async fn handle_command( .delete_local_account_data(cli.account, ignore_registered) .await } - CliCommands::GetUserStatus { recipient } => { - client.get_user_status(cli.account, recipient).await + CliCommands::GetUserStatus { + recipient, + username, + } => { + client + .get_user_status(cli.account, recipient, username) + .await } CliCommands::JoinGroup { uri } => client.join_group(cli.account, uri).await, CliCommands::Link { name } => { From dbdff831323a3242527eb4e3e4a823322e1c9262 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Jul 2025 11:09:24 +0200 Subject: [PATCH 130/144] Update README Fixes #1803 --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e6d6e5ff..c7228c09 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,12 @@ of all country codes.) signal-cli -a ACCOUNT send -m "This is a message" RECIPIENT ``` +* Send a message to a username, usernames need to be prefixed with `u:` + + ```sh + signal-cli -a ACCOUNT send -m "This is a message" u:USERNAME.000 + ``` + * Pipe the message content from another process. uname -a | signal-cli -a ACCOUNT send --message-from-stdin RECIPIENT From 3d4070a13907152b312c863a2ea885ac57935365 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Jul 2025 11:41:52 +0200 Subject: [PATCH 131/144] Compile UnixStream support only on unix systems --- client/src/cli.rs | 1 + client/src/jsonrpc.rs | 1 + client/src/main.rs | 37 +++++++++++++++++++++--------------- client/src/transports/mod.rs | 1 + 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/client/src/cli.rs b/client/src/cli.rs index e0abb717..90809e1b 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -15,6 +15,7 @@ pub struct Cli { pub json_rpc_tcp: Option>, /// UNIX socket address and port of signal-cli daemon + #[cfg(unix)] #[arg(long, conflicts_with = "json_rpc_tcp")] pub json_rpc_socket: Option>, diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index 3b6d583b..6874652d 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -410,6 +410,7 @@ pub async fn connect_tcp( Ok(ClientBuilder::default().build_with_tokio(sender, receiver)) } +#[cfg(unix)] pub async fn connect_unix( socket_path: impl AsRef, ) -> Result { diff --git a/client/src/main.rs b/client/src/main.rs index 6466b2e6..f82ee237 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -482,23 +482,30 @@ async fn connect(cli: Cli) -> Result { handle_command(cli, client).await } else { - let socket_path = cli - .json_rpc_socket - .clone() - .unwrap_or(None) - .or_else(|| { - std::env::var_os("XDG_RUNTIME_DIR").map(|runtime_dir| { - PathBuf::from(runtime_dir) - .join(DEFAULT_SOCKET_SUFFIX) - .into() + #[cfg(windows)] + { + Err(RpcError::Custom("Invalid socket".into())) + } + #[cfg(unix)] + { + let socket_path = cli + .json_rpc_socket + .clone() + .unwrap_or(None) + .or_else(|| { + std::env::var_os("XDG_RUNTIME_DIR").map(|runtime_dir| { + PathBuf::from(runtime_dir) + .join(DEFAULT_SOCKET_SUFFIX) + .into() + }) }) - }) - .unwrap_or_else(|| ("/run".to_owned() + DEFAULT_SOCKET_SUFFIX).into()); - let client = jsonrpc::connect_unix(socket_path) - .await - .map_err(|e| RpcError::Custom(format!("Failed to connect to socket: {e}")))?; + .unwrap_or_else(|| ("/run".to_owned() + DEFAULT_SOCKET_SUFFIX).into()); + let client = jsonrpc::connect_unix(socket_path) + .await + .map_err(|e| RpcError::Custom(format!("Failed to connect to socket: {e}")))?; - handle_command(cli, client).await + handle_command(cli, client).await + } } } diff --git a/client/src/transports/mod.rs b/client/src/transports/mod.rs index ad552cbf..ed1963a0 100644 --- a/client/src/transports/mod.rs +++ b/client/src/transports/mod.rs @@ -2,6 +2,7 @@ use futures_util::{stream::StreamExt, Sink, SinkExt, Stream}; use jsonrpsee::core::client::{ReceivedMessage, TransportReceiverT, TransportSenderT}; use thiserror::Error; +#[cfg(unix)] pub mod ipc; mod stream_codec; pub mod tcp; From dc787be17ba3ec2396c992b77fff3aa7d98d259c Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Jul 2025 11:31:38 +0200 Subject: [PATCH 132/144] Build rust json-rpc client in CI --- .github/workflows/ci.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad4dbf15..23e19225 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,3 +69,28 @@ jobs: with: name: signal-cli-native path: build/native/nativeCompile/signal-cli + + build-client: + strategy: + matrix: + os: + - ubuntu + - macos + - windows + runs-on: ${{ matrix.os }}-latest + defaults: + run: + working-directory: ./client + steps: + - uses: actions/checkout@v4 + - name: Install rust + run: rustup default stable + - name: Build client + run: cargo build --release --verbose + - name: Archive production artifacts + uses: actions/upload-artifact@v4 + with: + name: signal-cli-client-${{ matrix.os }} + path: | + client/target/release/signal-cli-client + client/target/release/signal-cli-client.exe From c924d5c03a318d8c7d4dd7be287eaf0d523ecd91 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 14 Jul 2025 15:59:26 +0200 Subject: [PATCH 133/144] Update libsignal-service-java --- graalvm-config-dir/reflect-config.json | 1 + gradle/libs.versions.toml | 2 +- .../asamk/signal/manager/helper/IncomingMessageHandler.java | 5 +++-- .../asamk/signal/manager/internal/SignalDependencies.java | 4 +++- .../signal/manager/syncStorage/ContactRecordProcessor.java | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index c5459804..0bfd1cdf 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -3070,6 +3070,7 @@ { "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record", "allDeclaredFields":true, + "fields":[{"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"dontNotifyForMentionsIfMuted"}, {"name":"hideStory"}, {"name":"markedUnread"}, {"name":"masterKey"}, {"name":"mutedUntilTimestamp"}, {"name":"storySendMode"}, {"name":"whitelisted"}], "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }] }, { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 680ca54a..4edfe40a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.18" -signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_126" +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_127" sqlite = "org.xerial:sqlite-jdbc:3.50.1.0" hikari = "com.zaxxer:HikariCP:6.3.0" junit-jupiter = "org.junit.jupiter:junit-jupiter:5.13.2" diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index 3bb7479a..dd9cb38f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -41,6 +41,7 @@ import org.signal.libsignal.metadata.ProtocolNoSessionException; import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; import org.signal.libsignal.metadata.SelfSendException; import org.signal.libsignal.protocol.InvalidMessageException; +import org.signal.libsignal.protocol.UsePqRatchet; import org.signal.libsignal.protocol.groups.GroupSessionBuilder; import org.signal.libsignal.protocol.message.DecryptionErrorMessage; import org.signal.libsignal.zkgroup.InvalidInputException; @@ -105,7 +106,7 @@ public final class IncomingMessageHandler { try { final var cipherResult = dependencies.getCipher(destination == null || destination.equals(account.getAci()) ? ServiceIdType.ACI : ServiceIdType.PNI) - .decrypt(envelope.getProto(), envelope.getServerDeliveredTimestamp()); + .decrypt(envelope.getProto(), envelope.getServerDeliveredTimestamp(), UsePqRatchet.NO); content = validate(envelope.getProto(), cipherResult, envelope.getServerDeliveredTimestamp()); if (content == null) { return new Pair<>(List.of(), null); @@ -143,7 +144,7 @@ public final class IncomingMessageHandler { try { final var cipherResult = dependencies.getCipher(destination == null || destination.equals(account.getAci()) ? ServiceIdType.ACI : ServiceIdType.PNI) - .decrypt(envelope.getProto(), envelope.getServerDeliveredTimestamp()); + .decrypt(envelope.getProto(), envelope.getServerDeliveredTimestamp(), UsePqRatchet.NO); content = validate(envelope.getProto(), cipherResult, envelope.getServerDeliveredTimestamp()); if (content == null) { return new Pair<>(List.of(), null); diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java index f954588f..0bc895cb 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java @@ -5,6 +5,7 @@ import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.metadata.certificate.CertificateValidator; import org.signal.libsignal.net.Network; +import org.signal.libsignal.protocol.UsePqRatchet; import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -326,7 +327,8 @@ public class SignalDependencies { Optional.empty(), executor, ServiceConfig.MAX_ENVELOPE_SIZE, - () -> true)); + () -> true, + UsePqRatchet.NO)); } public List getSecureValueRecovery() { diff --git a/lib/src/main/java/org/asamk/signal/manager/syncStorage/ContactRecordProcessor.java b/lib/src/main/java/org/asamk/signal/manager/syncStorage/ContactRecordProcessor.java index 3858bd06..e1fee99b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/syncStorage/ContactRecordProcessor.java +++ b/lib/src/main/java/org/asamk/signal/manager/syncStorage/ContactRecordProcessor.java @@ -37,7 +37,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor Date: Mon, 14 Jul 2025 16:42:06 +0200 Subject: [PATCH 134/144] Add support for sending view once messages Closes #1812 --- client/src/cli.rs | 3 +++ client/src/jsonrpc.rs | 1 + client/src/main.rs | 2 ++ lib/src/main/java/org/asamk/signal/manager/api/Message.java | 1 + .../java/org/asamk/signal/manager/internal/ManagerImpl.java | 1 + man/signal-cli.1.adoc | 5 +++++ src/main/java/org/asamk/signal/commands/SendCommand.java | 5 +++++ src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java | 3 +++ 8 files changed, 21 insertions(+) diff --git a/client/src/cli.rs b/client/src/cli.rs index 90809e1b..7fa5d1c5 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -179,6 +179,9 @@ pub enum CliCommands { #[arg(short = 'a', long)] attachment: Vec, + #[arg(long)] + view_once: bool, + #[arg(long)] mention: Vec, diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index 6874652d..fe0dc668 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -183,6 +183,7 @@ pub trait Rpc { endSession: bool, message: String, attachments: Vec, + view_once: bool, mentions: Vec, textStyle: Vec, quoteTimestamp: Option, diff --git a/client/src/main.rs b/client/src/main.rs index f82ee237..ac12331d 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -144,6 +144,7 @@ async fn handle_command( end_session, message, attachment, + view_once, mention, text_style, quote_timestamp, @@ -170,6 +171,7 @@ async fn handle_command( end_session, message.unwrap_or_default(), attachment, + view_once, mention, text_style, quote_timestamp, diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Message.java b/lib/src/main/java/org/asamk/signal/manager/api/Message.java index 48277e6d..9b372451 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/Message.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/Message.java @@ -6,6 +6,7 @@ import java.util.Optional; public record Message( String messageText, List attachments, + boolean viewOnce, List mentions, Optional quote, Optional sticker, diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java index eb672d24..726d0aaf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java @@ -810,6 +810,7 @@ public class ManagerImpl implements Manager { } else if (!additionalAttachments.isEmpty()) { messageBuilder.withAttachments(additionalAttachments); } + messageBuilder.withViewOnce(message.viewOnce()); if (!message.mentions().isEmpty()) { messageBuilder.withMentions(resolveMentions(message.mentions())); } diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 32321095..73286aac 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -316,6 +316,11 @@ Data URI encoded attachments must follow the RFC 2397. Additionally a file name can be added: e.g.: `data:;filename=;base64,` +*--view-once*:: +Send the message as a view once message. +A conformant client will only allow the receiver to view the message once. +View Once is only supported for messages that include an image attachment. + *--sticker* STICKER:: Send a sticker of a locally known sticker pack (syntax: stickerPackId:stickerId). Shouldn't be used together with `-m` as the official clients don't support this. diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index cce1d21e..e022723e 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -66,6 +66,9 @@ public class SendCommand implements JsonRpcLocalCommand { .help("Add an attachment. " + "Can be either a file path or a data URI. Data URI encoded attachments must follow the RFC 2397. Additionally a file name can be added, e.g. " + "data:;filename=;base64,."); + subparser.addArgument("--view-once") + .action(Arguments.storeTrue()) + .help("Send the message as a view once message"); subparser.addArgument("-e", "--end-session", "--endsession") .help("Clear session state and send end session message.") .action(Arguments.storeTrue()); @@ -164,6 +167,7 @@ public class SendCommand implements JsonRpcLocalCommand { if (attachments == null) { attachments = List.of(); } + final var viewOnce = ns.getBoolean("view-once"); final var selfNumber = m.getSelfNumber(); @@ -239,6 +243,7 @@ public class SendCommand implements JsonRpcLocalCommand { try { final var message = new Message(messageText, attachments, + viewOnce, mentions, Optional.ofNullable(quote), Optional.ofNullable(sticker), diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index ac77df99..725df8b1 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -236,6 +236,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable { try { final var message = new Message(messageText, attachments, + false, List.of(), Optional.empty(), Optional.empty(), @@ -399,6 +400,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable { try { final var message = new Message(messageText, attachments, + false, List.of(), Optional.empty(), Optional.empty(), @@ -444,6 +446,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable { try { final var message = new Message(messageText, attachments, + false, List.of(), Optional.empty(), Optional.empty(), From 3e981d66e9534db61953078b3ca8faf16ed9dd2d Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 14 Jul 2025 18:52:41 +0200 Subject: [PATCH 135/144] Fix null pointer regression --- src/main/java/org/asamk/signal/commands/SendCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index e022723e..98051b23 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -167,7 +167,7 @@ public class SendCommand implements JsonRpcLocalCommand { if (attachments == null) { attachments = List.of(); } - final var viewOnce = ns.getBoolean("view-once"); + final var viewOnce = Boolean.TRUE.equals(ns.getBoolean("view-once")); final var selfNumber = m.getSelfNumber(); From 783201d12e7ef621554fe16640ddfd7f0534ff37 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 16 Jul 2025 19:17:21 +0200 Subject: [PATCH 136/144] Fix incorrect error message --- src/main/java/org/asamk/signal/App.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index cf731ea9..6352aa13 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -292,7 +292,7 @@ public class App { } catch (IOException e) { throw new IOErrorException("Failed to load local accounts file", e); } catch (AccountCheckException e) { - throw new UnexpectedErrorException("Failed to load local accounts file", e); + throw new UnexpectedErrorException("Failed to load account file", e); } } From 2225e69277a72926fd201269c554553f95af9163 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 16 Jul 2025 19:37:03 +0200 Subject: [PATCH 137/144] Update sqlite-jdbc --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4edfe40a..fec44a43 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.18" signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_127" -sqlite = "org.xerial:sqlite-jdbc:3.50.1.0" +sqlite = "org.xerial:sqlite-jdbc:3.50.2.0" hikari = "com.zaxxer:HikariCP:6.3.0" junit-jupiter = "org.junit.jupiter:junit-jupiter:5.13.2" junit-launcher = "org.junit.platform:junit-platform-launcher:1.13.2" From dbc454ba9e71b6ed98e8b3a9682fefe849773263 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 16 Jul 2025 19:40:10 +0200 Subject: [PATCH 138/144] Bump version to 0.13.18 --- CHANGELOG.md | 18 +++++++++++++++++- build.gradle.kts | 2 +- data/org.asamk.SignalCli.metainfo.xml | 3 +++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5437bd63..bb795535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,22 @@ # Changelog -## [Unreleased] +## [0.13.18] - 2025-07-16 + +Requires libsignal-client version 0.76.3. + +### Added + +- Added `--view-once` parameter to send command to send view once images + +### Fixed + +- Handle rate limit exception correctly when querying usernames + +### Improved + +- Shut down when dbus daemon connection goes away unexpectedly +- In daemon mode, exit immediately if account check fails at startup +- Improve behavior when sending to devices that have no available prekeys ## [0.13.17] - 2025-06-28 diff --git a/build.gradle.kts b/build.gradle.kts index c3568444..97853d19 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.13.18-SNAPSHOT" + version = "0.13.18" } java { diff --git a/data/org.asamk.SignalCli.metainfo.xml b/data/org.asamk.SignalCli.metainfo.xml index 95646534..c6c07411 100644 --- a/data/org.asamk.SignalCli.metainfo.xml +++ b/data/org.asamk.SignalCli.metainfo.xml @@ -45,6 +45,9 @@ intense + + https://github.com/AsamK/signal-cli/releases/tag/v0.13.18 + https://github.com/AsamK/signal-cli/releases/tag/v0.13.17 From a0960fcabd4b4a81e84feb83773e01160acec7e4 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 19 Apr 2024 19:25:27 +0200 Subject: [PATCH 139/144] Prepare next release --- CHANGELOG.md | 2 ++ build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb795535..74d0bdc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## [Unreleased] + ## [0.13.18] - 2025-07-16 Requires libsignal-client version 0.76.3. diff --git a/build.gradle.kts b/build.gradle.kts index 97853d19..ea69d97e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.13.18" + version = "0.13.19-SNAPSHOT" } java { From be48afb2b5b4b138f09dcb112541fa56106a16d6 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 16 Jul 2025 20:56:02 +0200 Subject: [PATCH 140/144] Fix container build --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b1f4027d..b7ce03f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -182,7 +182,7 @@ jobs: tar xf ./"${ARCHIVE_DIR}"/*.tar.gz rm -r signal-cli-archive-* signal-cli-native mkdir -p build/install/ - mv ./signal-cli-*/ build/install/signal-cli + mv ./signal-cli-"${GITHUB_REF_NAME#v}"/ build/install/signal-cli - name: Build Image id: build_image From f9a36c6e0404d06bd396b24b5ea699e49ed29b89 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 16 Jul 2025 20:59:26 +0200 Subject: [PATCH 141/144] Fix send parameters to be all camel case Fixes #1814 --- client/src/jsonrpc.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index fe0dc668..66ef9d9d 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -183,7 +183,7 @@ pub trait Rpc { endSession: bool, message: String, attachments: Vec, - view_once: bool, + viewOnce: bool, mentions: Vec, textStyle: Vec, quoteTimestamp: Option, @@ -192,10 +192,10 @@ pub trait Rpc { quoteMention: Vec, quoteTextStyle: Vec, quoteAttachment: Vec, - preview_url: Option, - preview_title: Option, - preview_description: Option, - preview_image: Option, + previewUrl: Option, + previewTitle: Option, + previewDescription: Option, + previewImage: Option, sticker: Option, storyTimestamp: Option, storyAuthor: Option, From b453d7a0b9c6617d44d802c8ed310c17c1fe9257 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 2 Aug 2025 12:05:01 +0200 Subject: [PATCH 142/144] Add new svr2 mrenclave --- .../java/org/asamk/signal/manager/config/LiveConfig.java | 7 ++++--- .../org/asamk/signal/manager/config/StagingConfig.java | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java index 890d7081..c76da017 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -28,8 +28,9 @@ class LiveConfig { private static final byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() .decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"); private static final String CDSI_MRENCLAVE = "0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57"; - private static final String SVR2_MRENCLAVE_LEGACY = "9314436a9a144992bb3680770ea5fd7934a7ffd29257844a33763a238903d570"; - private static final String SVR2_MRENCLAVE = "093be9ea32405e85ae28dbb48eb668aebeb7dbe29517b9b86ad4bec4dfe0e6a6"; + private static final String SVR2_MRENCLAVE_LEGACY_LEGACY = "9314436a9a144992bb3680770ea5fd7934a7ffd29257844a33763a238903d570"; + private static final String SVR2_MRENCLAVE_LEGACY = "093be9ea32405e85ae28dbb48eb668aebeb7dbe29517b9b86ad4bec4dfe0e6a6"; + private static final String SVR2_MRENCLAVE = "29cd63c87bea751e3bfd0fbd401279192e2e5c99948b4ee9437eafc4968355fb"; private static final String URL = "https://chat.signal.org"; private static final String CDN_URL = "https://cdn.signal.org"; @@ -91,7 +92,7 @@ class LiveConfig { createDefaultServiceConfiguration(interceptors), getUnidentifiedSenderTrustRoot(), CDSI_MRENCLAVE, - List.of(SVR2_MRENCLAVE, SVR2_MRENCLAVE_LEGACY)); + List.of(SVR2_MRENCLAVE, SVR2_MRENCLAVE_LEGACY, SVR2_MRENCLAVE_LEGACY_LEGACY)); } private LiveConfig() { diff --git a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java index 6d8f08c7..e25fe21b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java @@ -28,8 +28,9 @@ class StagingConfig { private static final byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() .decode("BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx"); private static final String CDSI_MRENCLAVE = "0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57"; - private static final String SVR2_MRENCLAVE_LEGACY = "38e01eff4fe357dc0b0e8ef7a44b4abc5489fbccba3a78780f3872c277f62bf3"; - private static final String SVR2_MRENCLAVE = "2e8cefe6e3f389d8426adb24e9b7fb7adf10902c96f06f7bbcee36277711ed91"; + private static final String SVR2_MRENCLAVE_LEGACY_LEGACY = "38e01eff4fe357dc0b0e8ef7a44b4abc5489fbccba3a78780f3872c277f62bf3"; + private static final String SVR2_MRENCLAVE_LEGACY = "2e8cefe6e3f389d8426adb24e9b7fb7adf10902c96f06f7bbcee36277711ed91"; + private static final String SVR2_MRENCLAVE = "a75542d82da9f6914a1e31f8a7407053b99cc99a0e7291d8fbd394253e19b036"; private static final String URL = "https://chat.staging.signal.org"; private static final String CDN_URL = "https://cdn-staging.signal.org"; @@ -91,7 +92,7 @@ class StagingConfig { createDefaultServiceConfiguration(interceptors), getUnidentifiedSenderTrustRoot(), CDSI_MRENCLAVE, - List.of(SVR2_MRENCLAVE, SVR2_MRENCLAVE_LEGACY)); + List.of(SVR2_MRENCLAVE, SVR2_MRENCLAVE_LEGACY, SVR2_MRENCLAVE_LEGACY_LEGACY)); } private StagingConfig() { From 42f10670b654b2314e05fee142ec6b18a8196362 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 17 Aug 2025 17:35:14 +0200 Subject: [PATCH 143/144] Replace deprecated groovy utils --- buildSrc/src/main/kotlin/CheckLibVersionsPlugin.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/buildSrc/src/main/kotlin/CheckLibVersionsPlugin.kt b/buildSrc/src/main/kotlin/CheckLibVersionsPlugin.kt index eb74acb2..ae8ae4de 100644 --- a/buildSrc/src/main/kotlin/CheckLibVersionsPlugin.kt +++ b/buildSrc/src/main/kotlin/CheckLibVersionsPlugin.kt @@ -1,12 +1,10 @@ @file:Suppress("DEPRECATION") -import groovy.util.XmlSlurper -import groovy.util.slurpersupport.GPathResult -import org.codehaus.groovy.runtime.ResourceGroovyMethods import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.artifacts.Dependency +import javax.xml.parsers.DocumentBuilderFactory class CheckLibVersionsPlugin : Plugin { override fun apply(project: Project) { @@ -28,10 +26,10 @@ class CheckLibVersionsPlugin : Plugin { val name = dependency.name val metaDataUrl = "https://repo1.maven.org/maven2/$path/$name/maven-metadata.xml" try { - val url = ResourceGroovyMethods.toURL(metaDataUrl) - val metaDataText = ResourceGroovyMethods.getText(url) - val metadata = XmlSlurper().parseText(metaDataText) - val newest = (metadata.getProperty("versioning") as GPathResult).getProperty("latest") + val dbf = DocumentBuilderFactory.newInstance() + val db = dbf.newDocumentBuilder() + val doc = db.parse(metaDataUrl); + val newest = doc.getElementsByTagName("latest").item(0).textContent if (version != newest.toString()) { println("UPGRADE {\"group\": \"$group\", \"name\": \"$name\", \"current\": \"$version\", \"latest\": \"$newest\"}") } From f6d81e3c056cb42dd8e21b6ebd46d7cc119be221 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 17 Aug 2025 17:35:59 +0200 Subject: [PATCH 144/144] Update gradle --- gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 45457 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..8bdaf60c75ab801e22807dde59e12a8735a34077 100644 GIT binary patch delta 37256 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 delta 35551 zcmYJZV|bna)5V*{Y~1X)L1WvtZQHhXxMQoaZ98df+je97^#6O#xz79h)jhM;%=fb< zejogP5xmysJ1}Y-zK;P#^eNya^!*RyrWsaa*o?`cG4E0x(uI5*J=Ql{I8pVHbrf*&ViJbv&0$Zx^9HzKJYQ+2@eUCip7Q~vv%wZxh=X(hybkQ-d%4h08A3r-BgR1yDQOhGU!yc)KY_R) z<~z-KN~9P>0@{5up2;>ZO7$o~VmdL?8yt&VFrbN!Ax~@SD^gB(*;lok#cYX1yF0ri zTfoNS4~q_qcA&~muAcevb&3QXO?~0wIJt9T@@k%iwWyg|@`P{EtB0FDW2TTpJ449e zuN$b!Af;6128-YK{g=RgMOrWWfwmiBb%I9~ClxAv$Tv$EFuBIYWT39uPZWMY_)u>-6QS>Dpp%(#NEFIeU zjJN#v$j{|sq!va#kM7Uh3#%b(XnIqbX?K%PlWA%C!0rz)hR9!_CvWd*YWqemcDG<_ ztH|`aB23nP=k&Rwy!(xW{j|Wn?pi2hNM1G%1t1en-wK?TTrRDhBR7g@m1Q#C7R_i_ zL3gbJo7pkkx%%3RHtl+`z|2k&Q(IqCA$2glZe)H(AF@Q`UUFJnn$##p$J+Wg29V06 z^$W;@!nT*;@Fm6WWuq~~ZbeD|5ihjEEcv%uhGHE&8e;#tPwF|FJFRb1H*J)HAb-%_ zATZ3|un`ABE3ffkn8#v4L?T+D&Ath57i3+NL7H6VrjcSx00}9XLCoNTea8^xLS$ul zj~YlyyKT+NZn9!<(nGF`y+z)ulWL?2y{qJxmB*f{ug(}O0}n4IaigLNKcqBbBr*t= zAbGz_({CW|vYA*MC0CMUm#7EfqwiX&)Q#eM9U657>_Z_=xQ_KLM zO%6h`rx~)x-7(vp@br}&k(TFMBXDg~(68W~7Id{DO7>I%!1Is@@Z$NA0*S#kM~}+M zO;#+U>;QsYyR6@9itLyZXt?aMAe&1UyFw@2JH?lLl_gE+<6YSM)@Ls;5 zX&SY^f>-?i>qi@tYFRsQFtCPi5dY~o7hMQ=A%`xA!7Ch4v_2OI`%GK?^Fs@VApw2} zQc^|&han&EY+T$iZ))h?oVJ-iFcS2P_&EdlYjyzUIxot79StR&<&wfumAu}Bs9%YpbNZ+1Q6_U5E>>Jo(Gcc?vo73mT|MU zjZUVk4qN7C;+OIaIiiV369ED#h6Bf;tb$G|3w$vB9@Xu`$R4ZvbCmXCj*}^O+=%@F z?=UU%P|G2nihG9%jS$(?h*>v|@=Mlj^g-^oXqx>TK_|sk=2c$Oy!7?DbCN)O^j5Ja zz{rC@_R^7N3(lv$2dGRhkafdoB)-0To|uCK*;$MQWvw&`~J&*b;AnbCAg8}xm^Q^Ypo+fh_OqPzc* zWPK%OH*$E-|C-La5++UiU(+>1{?~KIM86Uve~<&^=M6CY^aS9WD6nq)uraZ1sL^LQ zf3yG5CeC$~Vv=FGYEP}28=rH_Wqf6pxo_YXK*uDxxt$y!H09AXhZG#cTCTkC-a5{_ z%N+N9-9Ij&2NQD)+FiUmcCVLTBwkJp)>R@`@l}*9Yd2O!N_+zuTc;?ak-CRawvt;k z^zi~^YhZmxD>SpY>PBSc3m2?38$48*!Epy=%tQ!zr8U^!w1IVI>7>_GI=Fd7wc{Y# zVCxmr1UiIe5`EI?@3BbcO$i!mIZXkKBc3HkXM5>}@Sv#ulzG$CRGIiCSrXn0jUO%2 z%qFL7?!3E?^5LSxzZ%b9UbO1!=<`B$bqax(RaPih2k`E=37ylvM0v@1i!}hfFH2}w zvN4&MnPa5&YkDRf!YI&JbZMmYxkFo?CzP#){V*K`yvg4bB12^1P-ArAWn@og8pJ7{ zy>T8}r;g02H$f}sj9NjTvesSpv8>v?J?qC)J#KIT40LBAhIPXy_OX~v?1ArOJy zS?%=pXOb4ddE_iQcSy{>LEg!ldXtnK!TlE;VI+vU8O^`&j4kL8atsZ4XSD~#g`Oy7 zGeqF!ev<8TyfzmZbk;|X0~V2gb_O) z_@8OloSoSzC5RX0@CzBks;Dq5iQ0hyOD%F5+l^6>C-0{ET4N;K8!XeeGZ%@J-Dk7enSJ zxiQ``wpU9n8nmzC5P}3s(FoeBXGkf+k{S-V&gy@9;e{_NBv0L=|T!{Qb zcmbg?KO`F&&H99L0;=@mYUbvJw@i%PP!!X7-kRqpAVkrW}Z(P}X7Kut#HlOn0( z9;4KaiG_OrL*-N#+++{f|Fi@p@qK^}0t`$y5e3H*cP^%2H{CvQuOlDf63e=PD_TZ*Er2A}3kqg z;SOi^KKTtFvm~xW?E-yT+S`VA&i2P9?e^Ep;W8N8{ud%WA#Z!l#p6tFI^TdS?E--m zatLuAurYb^6m)i$f<38)L*6!tRLzz7JyexEo#5zHSdQ;Jcr8?=e>Yx%4t=t`t(49O z(Qdt&vg?Iuu4z5uQP{KpX8?1h82cjLX5+DUWdfiQhQMoZTU_7Ogs() z$Y5@4-O?}G&H*$|%Z)z1Qf_vwu{LA8sm4|TOxMcfxlpwYT~GbXSf$v&PVWDfP*~Bf zBjj&*S2=|F_lS8UgH~Ar&gHZS$3gla3sqMKU1XLSYuBq zC|pj}*|05*nI|HNO3`8=>8mw3s@OgK3kzgS-~- zA4}J0_nB-EjHu~K>{aJWO{7RJ@p(q(?Zof=u+?*Q71nl9MNkhA>8$SNiaF>*kfe9-5ZZw9$5s?X_wRv+66j-AiQFTAX9C6boKn)z=SGf_R zs~dTH*P?QqE2LOcv3qjg9_gq)g*=!pQR~e%#vNv(;L4<1^$%3%xsZbL>dFQTTTB7L zYJX{FIgt1AxOn_SE#tU=ueLfv1x8GC!^TY4aWf6AO2AdhCKRXWJ54saLUsu}9e?UIF{9wu)__c$BjVfHHJV;A zhYVV#cIZ5%7iJAy*D|&hb93@El0wF)$Nce4RlU%4s}FbBKDa0lNj0b?i9*!eliscz zodbJd(Id6B#d8UVh-(`Q;ednhCz)^jlD5p2xStUJkK;xI@Xh<>1S@qFad|%OkqbW8 znVl68ZQ*?W*2Pk+^~|laLAs~x#?dbF3&$%-@9lZgq1rG%{)bP1H0d|CU}c!^Dzb*B zmNfDgX?o{Rf5?QfzwnSI21 zkYHzU9R=B?O7mO6gH7q(FltF9hECeLF~*f%HF(3jjpO8j1^k%VLT4%(f70AKl7vuV zemQmc>s02~G!f*z)z$29iJA93EdehD1_jCx^f<^ub{-T7yt-^~5_>@qTbGwMJx7lP6}LNr(_prpAFt zWd~4xIkP1FMzdYf%d;^c2==XPj+g~5Pf#g-& zLgR>80`CNs$QgV}R+hyjnn!Tn^!A|Gzkt^;Sk(-{c6Ie$(>6cGjhBwRj57B;6MV6U zyBD+W@8+8^8|o~h6Ky`hPWl!mg*{7|`$dUGT&_U?A+-lycI%k=(ck3<-YA_u(K+?` z6GhRf$0LMU#JLrFB1u0M2>KU(LKmH?S;g@*4R76n57qV%1 zSR+cm4zfql_dUk+8De}Do~3@VQP8`qqx@vav-B0=e}nJJ|1xs}8VtkQ-oc40NO4+*oMypQV@`FbPBrinn*))GcdlkzS`|6!Qz~ z=|xUIk$K-iz81%pmo}fF5wuA3zU1}IKF-W`zMR(I27;CL8a&tbeC6NBSvxw*k2E)z zr{Px>re&`;;S;Q7v*^^&j$9##Ukl6(>kT!v`N_ zo;v(qg(sg1qnFN$u!z%@WY=leHXC-yQ_d%dU3&h8Ab(Q!4#hKMUu)`vJOzd+1+D~d z1GFL1{z4#D1;d6N!6+}RhlFAD^OKEb=o9wk89C~RJ#*B#{M|a$oWi^ULxBqZwPtYvb9qofWYm z-n-zqIruA~1uuY#RX?v|oB?YR{DRCPM+~$?ob@BF53nk;>w1POhuK5?hCRzHe&qwM zMXV+PsT6T%4z2MHI8V07A{{rfr4j?zBOSz8P3yxlfoavEL2|fI&TorKhD?!WDIw8t z1oMR*Ex3k3vm{4R@^X#CjyxQWdqw(RqYe1?a?AdEt)%|%wIY}}PD%z;v6i1#0Qh~! zO^SBJX8)#`7iec=sslMBIznn8;Xorm`W%w!8meT$?X*TTFoJx;{w#=;DuNF5=O24^ zgE&m7l$G<&e)7zDa@u-)$|39li!uz@y&E0XdM!vle(iREKZ`2ADwR~FUxO(gy zaI5`|_# z0pHNAj-FHF0G+}T$qxU#SCB|GLd_;1Ae6I)axC>LhcSk&!ID55;6I*#p`(v?jrA51j3d%qd;tN)@r8pvbNX_tH_#~N z5tdENu+KVm=kWn;p}ypq)7i}U^BLwI=oNA`1bm-#febi8rK0G<49$NbP#c5ue&Pu7 z3U!x7=M5eWdkTg~)yy$~Vphfo_zx%}xy7tD@1{-JKC=bGXHb2BK| zo-7D9UqX>ZaO6L)B%_lnHJ?-+HR)fpaLFtR?Ren&uh_ZVli996H3AA|AMSWCx z(%F_pOiH)=nDY;2Bnmey!G4Ggjhn&>*HJ`&5JI%GG$*g%HVdXiP=tA+jsfi%t65SQ zq?8j@cE+Bp9a)o|x@%LWY-}k@^@y9xbBTQ@;wq`faHl|ph<=HXT*CvgeQIn9fN?2% zaEpawYPn71V2!CJwB!yHSs!4SG)S#!H4Q&Pi<3cJFx~KaN@k1S5p^P%5s52rhuHTF zak86IyZ%nd?z;0=;0KE<{D*@T%0noMMfj_;lmuARJFca#WQQIk9MRp(lG+~PWB@`V z+4RgO(x)k=C=3^Un!H2>C|fGO=^QV%dxpB7r^@yI{)&PCy-a8-zEqw7u*N0&MhT66 zEMb$K|H3WCKF!$lf`A7eMEnftQ zO|p_WO>P0~mBVF3!B32v0Sid^A&1v~MkGk1t%ND6K=chQUkS3bjKks1iySv-xud>I z@s|o;A+Q&&EYuH-Fa!|#(@Xey=h)N!$kXid^6L}A|9d6Fv$O9KHF|-vj)W!UleoL%#wE7t;Gp<9x6 zlP(A-RpHA9!+c%*&DDaTw7I)w8i(Oxdr~Jc)^YfG{30!>_gJmt$q4t0wN{w4p`(IB zE9;H8xVP*6{uue&OfU8s`uRl2_Ln zkaBW*#cY7M3ei&`b2Ann*n6F<+kn|pSeiChX8Tq>&TAc-^w3$NL zVYFD*2}8aZH2~m2)l9-}UWDObZ~L+RygAsbUt1|x4!X#at|TrttAK*=jZFZsSUB4) zRU%4i@vTj&!83g04C;0fVZ!elG=`UbQfnxws6c^Jj8ERma2K-1GpNYyuvMWm*e_<4 zFZ*8cHFyuU`W+4*NJb}|{D|QjO3g??e)Hd^q|@S#`u*Pk6aGKM8%ZMoRQx|(lM_ip zP*Os9o#jz~mrOQ=!lVEn_$E>$h59q_|I>9$XNCl9GV(4x2hqbHnEL{%AtHr1;=zOu zv!m$k6=vYqhbN>z(sSR=<>O%O>-PF~E1t-i}gF}=)MYQ*u}$xl{BrHy={Y@&GH zY^eOuJu2KnU|P@SAyt3zwtQgH6T~S?epQugU7ciG^Mg|lw?YKCW-QG4LB3p}Sfdg- z27dlz>5oBeYyKrI!6@OcCmIIm#qu2StheP>>R4nu?I zJX#965ONPvine}|{x#GkJ(VXCU&jpZc#1RD;cL%H2Oy@ntD)gkdXIEdy-(nFwKoA& zKEB<=tRiF#E-caJpS+XqIMj!Hk2aSQ6*il?8sOPCYI4A3=o};dsIC0( zl;d>jysNuE)hP4MbRhdd+hu^uS@@}u%YeU6Dti4f~w4u_y-OdV|-qWIxu4wxJi&zm+Z`*e%3g|;(`+{7XM!8 zI>6wx(N55j-A424OTn?gL$aU6?r{&=juA0SF-}bGgQQs&@?vkfyrVB7^;R1P{`ct5 zSYq8F_%0IAw_iq0m+B!tqZQeI@T!PqYd8Zc+YxT-&$81~?80r}3jq-Kw6m5GQFz^8bHe!Tw8p6A5v?|G&v4YC<_OFj`et8(kd3Zy1t&pix4_hUScI5e=LO z3Ip}sB1(fY?x&!wh;-;Ck><+Zp-m*ID!u3X_UZj1y~m;TX06SdGR*2ICyy+)El$_nQ&f5ED0iBF!_aW8}C03bB zAa-+d`AYlG4icGOUBO7x%i_lRnWIgu!D!?Or+Lh*8!JlH-Nhs#---JNS8Lu9xbyp( zi=3)7GVBc|dDnRrjbHs}eT1<4s=@^xP0O3eFoqkj=Gur3C;jZ*^LU-!G zr&*jKRJ`b)QNDABj-aK1i%9+LYQB-*YE`!mR=!E;-HA5HyAYuMj+w$8Vd$bQI+a`% zBNviFF7}{{4kf%^Ngs?MxJFSRickS!an?y$;TN1* znzYVm@a+xh<%(Q71yt=WF6&CM1l2?@r}UrI}22@E%dS9)9y=L2PL;JFofWk(y`JSpqLDX z8`jpc2kNx@96s@MrU8K6%hFvm5_0s8<170FhOtjByI{uf3{v9os)~n=NJAO_0g1Zh zVABd%%;0+$Tz4F}mq9k)JX0wBgj|4%_~q(CJ#F}89%9Yf=qMtvk%2?vD}Q|%b3zGl zuRRj}rUz--cqt4AEj&XE(cdfb_LxcXJCxE9Q>oZ0+TeqGW4`5SteqNH)ie2OE?)C> zGmdGj{J<(1dsjwkSByP8Qi#9nr;(Di{|6(bzlmkanv_1s{ln8=tZ?++&C+cm2V&O5 z5qnmhLjzB9DDMC$&+!g%fZpeQzOuivZ;UL0o8mz8{0y~V;R6+pC9%{iKNB#edaaM4 z0O6a;t(SwW!?E^?-!0{acYzJtJ+Q0c07uB*-=x8?))4$@F7Xvs$dausbVP~M16O-& z|LGHA!}v^{v?uZN2aQN*0yRKy=)_+8Z=3GlecZ=zBgaY!W2hW@i#*L zG3Vt0S*qV2a*$1-J?jyVvkLZtBa%WSA@W;JSQ831TF zHx5%;G(+9{m^RQELa{DUM!OL-xQAyL#DXlSTQTaf>*qxgf3xC_th+-(&IDA-Fu7b#_o*gJKFMg|~NnuNAh zv~7Qb&ksZTx6lS{m$%8YIk%vQr=fd@?-X;5+UIr21qNe-#=m~Wlewu4Wv=M7{m}Lfct-P!JypG))+PpVMO!;aoe!Ey2G4tIji181H9N%Z5*!>P0%&9)kd z^Hs!}Q*DKeliE$PiF>8T%{C7p38Rv)Q*BDz;;HcPC)3LCvY;AN)^sPbtSn?`2W5v9 zbOb1ejHL1uDHlqHfnn|nmmhW*d6qyWiAXM7L>n4^?n0tzyX65Bw9YCtV$MG$u5fnSPCIzPKdidn!{cKt=OInFY<O_65e(4m6jj>(r+GP9S`_g_21ajkkIIA~ZBwyHSPy2z}M zn-v^#)4X19DfwQOA7nVAW-Zhlih~Yps=Z|=$bhoF%G&98-|oR~g+Won(9v#}up5t z5i8fYQVE~dd_2`s{W<2wHGTIVT98YnqTQKJWg6`Rq!VeYU)UsVI>~b$L;jv3yKkg? ztY0kN-oAMgldw=*G!p_#cg_;zApXv~vrQG@4jOG4gih|S%_sE2zmM`D`h**C=B_#! z23%l_d`385|8cZPLsDtzQaCJP~T z9PjnVf7sCGNU)XXpRw%z3uf^XYq`0BlT!TxD4$E^Wlf)rXN$t$^NkQylaxeJdLu(3 z0(Trc(u%FwC0AwPi5~@h5Ri!}p27H%IA}fYm?oYYwkQ5RO%G%FLsTMkMh&x1lJ`(A z`p=Enzmy+ey--Pm)<$&9E#pj38SO{oTn3Ev+XWsZk#yoYdKMFhX0!RDf<(RpA$Uhm z2ng91dQrV?@2-4n7(j5#se(a7MRjuFm2$>r;wJdhM%`_|)@?*$oR?`+*nlxxH4V|! zwYWcOX8R1yOiUP51^w2R_@Y>v2_r04&U)q?nydYlf6jvNMrTG?zH@KFD7A%p2E4?x zKyd~{KdR6>+4ebG9~x_Syayv0lyEJ+r2S+3$JG(=Kd7%2Fg4zWuMFD)F;yxkj19jz zm%>fxU3Xb9TtCM`S)tpmg-hZrvx;RQkRR4oCsUN2y|7}cAgi*_+(>?H<~EQFT}Eo(2^iFDwC9AkZet# z5#q&Qmt?l+QFxYOt6#!xe7#%SG`XV;8*A;Vz`aJ#Yl%X9^HsR^sZ4YeN&bkonEJ*P6MVr|jJh2uo4C4RRoavA zop>D5G0n?cjd0Eq!X>n=8c|MhZ%a!)4Gz)n`cJxU?l5C;mDuGYOX@iWsgO8D9JF@2 z!hD_J@aFY8h}+A;)lYm9L+n$qEIoTc?1;DNB(a z8>2L)>6rAXg-qsq?TKuWs8Q}vEjPw1XyR4qY?8`HMrCKW!+i?^f6$K^!Gi{oMuFB{ z3sLRPcwGu}dw&7)N1aF%m$ezL5SztBv-fTH(|6vo{1|3W-SI*%5-ILg5L4aQ4$!7U zFWMOO_BkIBCS2lSZC~L2ZkEj76ma41B_qwF?sjU z|04y*)sb?(||E&lT#$>pD6CWnNH!Fw((H;ycad1NT?yqe5d^?Y^y0yDtE z1@Eb@=|QUL6Dg-$Rcs|JcWlKk=gF`nLC9LC7#AOCB@v!OPeeZ@VI^XHFg@!30M@Z& zH}`Aem^%G99V1y?$1UANu5|4Oe(cWypx;HrAm~Pm*U&g^mBo$^c&3efTJQYK0nru& zpE`jk7Qkugl9NO>Qir$>7P%}u?1(1X5lzcIM&-KE#iXjeSgf%mz3Fq1anZ<|vZbjM zoq({xgU*zx4JmaG>2YBMSR{BPFm&x~Pr|^^`MfgdSK}J&%#Rb(Tc$kpMDJHEE2@d2 zKSM{yYa+*vvLgdCy-V1U`hULZA+V^by46N3F{#agLYz4` zUG#=hr0u_hMPfT8T*J+se_{RTmzSh|(WqxzM; zSfBs7)+8`1DDJe-GCROPxx#p;_w=>Pl|mSC{~L-(!^0-=PBN&37@ZApI0@R-6gw)KsEY5($Mcyky-?|xirLHS zW9XR{=TXubo?YMKgF6Qrf($ifB(Mq*<UH0{XTb81#ye;beWBetn$eD6e+qycgClN!mf#Dg z%>N&YA5v93>ibvOg8wQjE-D6O9g4$}+-Y~HC8<&WPF#;R@QqaN-*M2Me{19L#REq} zLq%F0=g(Ur9|$bEpN=~a&lDo--@c)xTDrQbx=v0!5$gAR;~3HnK~7Djhq;eeFHOJ56K3EIa+d&YO$3sACzE^b)+nbAM_Ua^30JqT$TiegvS$OGq^n2tqs%Ie17$;kFs;gc zPESj9ydud2g$?iG9m)8BY8uw=dQCF}(PU_iCIVW{_?VYX(_c$DSzoJ+QRC~Gu6opX zdLa`ulUY2;(_Z5CUd*>hHecxHQV9m?M3j{9tQ3D+zRcJ9Z2z*?g+hcpl-w4d7z_7N z>ZJB`lBv#(d5X8=mr0!s&0=l5LssT$ue`Eup}(dt6n1pnVTTf8s6#ddnp~s*&l}HL z@A+c>6^G!z;_!+q02S@$)i6FU=N76QrKNBwRN@v3Xy9ap5rQiNkkmj)XiH^+qVZ&P zxNk#_=PSEwa`7mg*F*i;9)`&4``PhJO15)D=!wl=EEhTu1sPzIDL(%s*m2B#?9&Z= zf4HjwOS$IkcSk0uRKH5IwX=oWW=oZ=FrLa#n>p_wh~4-Dq<;X{R?vZ$zgCzrOAY;1 zL0wtJa2ays6zZM#oBd6$Z20Y$`k{q7Rpio~XW!V_`CZn^9R-S;r)7LfpSzAe?CI-w zQ5Yf6fauLx-)e}}=nsgyPgp?E7NU`5xb;8aY8Buz7IV-{KDM6l^d^*21HImjY{k3`_gibq~f&{L87;FV|hGZfi1^G{_&M|VK1UbXzE^}wXWXvHo@5ZjI(%@UW2 zNVlHFJC-tYoVeidFa;ByulY32ktG+^p7N^s?c1#ab3NtdKwpc9Eq`w^ z*CYoZNaB|IN|2UvK@((bk8)l|*v5M^s4IQH*fryjZRiDrWA9*EkyGl#I1G$|FDE_i zgH1ug8)VFKX&qrm%XAEK^0n3Hn)9{@xrFcUh1QLx-`CR~$)F+V?N@gzv zmuVq-oA4n}1`4|GlBvK0QGm<*(AMYg&zlEw|2E?0$Xx5apBLGKQ=O!~&H)r-dHlxp zedq0_{0#2zDM+4We*9aoQD6Yiti4@qch$SmuOs$k=dPW6kFEm8o+bO`@5Gov2BgZ^ z>Oa+`F*~9#?BN%$e~0<^ZvGs))DbAz;;?e(~n8zm1*Xb`ObOfp6K&Rm}pt}`QLsK%fjbE z^>4p8_`mb*Z_>iRb)|U)4Bb#|X;^jC0bCq~c_Hm@y-uhB#CrY#-wgj=@8Hb|<4PoY zB?Ly15bnV|N5!Nln&IWR48=Na?Cv!VVvh#jwpXnt{oo|kIrlK~R<7_ya zfT<$dX82?Phi!HT$DCLZWiPAG!)a8N$fq&rg!ea4`L5E`Y_gBVu&st<*6)X~weIV6 zERyq-kgLiSa;ac*^+Zvcno7k;gvGTyA~#&!@zSXBi*1=)PV?G&+CPzqkI2qyN%amx zqyuxVjx4~v91TZ7?b2}tRCKwE%P#SGZ#^pY@i%X?_mNnu6I zx|-<)3UwM0D4#ghZ~0u<3wttP?AT}T0g}Vch{Hw}ytK`&SuwQU-O8ncSnZe=t%Eaq z*;!*5YEmY3vVOd6DC+6B&7k*0eq=xs;v|girvzhi4nCc@x^AQE7IiV|B zmDv%?DdMv-99BR?9kaEuwR`d*6}I?=Wg<01qR7k3FR=O@Ngp%^A+9BB3zC$%+k3!s|8zvD=&uc?5seXWIj_r8qqOLD|z5uV7zRkK9=Xj|w4D zUSkg5YzZA7c-i_!!R;_cfH^ZRu)M2xw_thT#I%gB5mp#H<$I;NSw z@(Ybo(*#Duk{I({!QP#Oe1GOYNNE3tb%7`UUoi59dwP8IFBn0E`u~EFL~I<4L}xjA zpgNono+|cNj|n^XrXA60b3jpJ3{hU2+x$99fKZ|y5e!jAAsy|~=;gRs`evG`85>Np z*H1nF2yt3f#ZIb-HP}rSkz6ZFOk|N85z)anK82fnKYKIwO;YQ>@^|C*Julr)-TS`F zZ(GLG{Lc*jt{meI2RpslLlBq{QZB!(fprnZ5hn(szM?Af#S6hkW$iy?&KTufg2-Eq zoV4(iCJbD{#6u@t<|-|4RM5z3Y9t1OB!6M5ghU0%W-N&<+ZJ|-8OHz_vLsM?@st9s z;SRNQ7CG2eXyq1A?S2)8Gv%g-bp7&oexR-7k70QXNp_Ww>B{9jT6Nsq?=|I_^peapI zNvyZH2QoT6n7h^NwAJK-i@WI?^!P>vc)wfbEj77TIC8yV9B+R0BBUDzo(+}?u?9&u zjE+0i-!b`t2txd6MzOVgt>s+l9D&@3n z9E3$+Q`j}IRYN+r5sJkLjx#!v1Z!se;FEZy48OJ+Y=)Xl4Omj8k86Y4+ftjSr=fll z?8_H**ta6|(ID>D0;GQdV+$V*aQn+cCLC`qL$TKD=3(f6AXM4%>G&fIs&n@jC9MZp z@z^>f@UeBX+9E01l__>?KhIDm%tq6}x0WH^@(DMwu9XxjS)QC*j=xZcGCkiqB6|UT zD9ZFLlq6sz>7kY}yh@NNx}O#w_S=O%8ig)Z;mYa77cCpdYOH1ebrma#2=(^ReQ1&JHOs)BKK?l8&dw+`8|qy)nPosH{NTwW{{1YGuFiRZsibY+9*Xv)wRQ&)qmrJhxUU{rctQ`QrP*?8oHl>91P-P(P7?}mpv3Su``@mVTy^(5Zc3cq z?kz^?E^vdSo$+)zZFsbntf=UNUuN`|7|SBz26IM;z2Id`J(^}Olp6Mf>%n0y%2=g# zx*q%714I3L<^{?Idm^@LxtIOiS>WDSLF?b!f;&dZ{EXAhP(g zcAH&IB^6cHz>*E~1SL;(d;1ofH~nmUFwGKf4K)_cMHzx3&@XXwAG$HJlu44b-v?RE z!iNA?DPeqxNM540_3U)WjIz1jgZrpH2Z=ry0Qgs3qSrN1IaIptQ6@#r5`UC;7e_>_ z0ybQ~t8mw7vv!~F0rIg38Xuk0liu!#u?opCWD^+$@Pxo80Y0(Q+8Eyj!1xSlw&~$1 zjgbc9uo3wdKWe5Xfgu^@awCgNn)%ZhfywLo=Yz>EO~#1AgFe&nme?6zNNDHpp?(!D zlS4OJsXNkNkCG+*?oM26hr5eVg%@e$wEEq>Fz6Vg(Bj~fuZVoqQ?3!adu_+%nTp=& znS-{4Kz42diDx|F+3X+41mjLW60Ul&D2dD2@{#A8YTE=rmz>jXPo_MVgQ?e;V;|jH z_`PCq`mS_EDUQ+;p@$*w?InYuqFz8Y?Y!n>!NMy&0A zWPsg>tA!#h6#RISxT>{9K%c6t<~;4HOo@_9!~8GtMn^BHk>z`LrQHt-c7!#ugH0v= zVquYF5f<4RLOPtOB@W4=PvepS*ax1h&bx-ce^AHxbV%QcwKenN4>boXm!JpCb>v#r3gw^ZjH(-u!CnsbT?%7 zg~XQ2Cqg^T?BfCM>p4Gt&K1F}Xt zh)9g&_GHa&Nti>k+l=lM$yOug%U&WvXGmF{pQ%IZd~?q=K|8B^v_uqtA6=6yB&Z9a zDQ*c6B%o}_BOJHYkh>!Jrf!goWU6D_s%t;}c}?BOjY4yBEhK^@=+A;Q>rr(E!5bV2U!P}6@{1@%8Z zpZ<>Te2DLmXlj2DPV5wX#x@~*e*YpTW85X5mK7tGrTbEWj(z6WeMh;R2JXy~wR}bW z;lCp0QTqEO^gHYudx5Duv^>fpI@}L?r?;MzUiQ?Er`cO{6QVNx9`2o6p!PLi^7ME; zjkZlpGAF3OoUo>*3W00L{JI~G++vzTP&*jnpg{Q<&aR&bmtbg9E1#kum6Xqa|*7kYom2Kwr$%sJGPS@cWkqh z?AW$#+qP|WY<29M{=akT+^ktOYt5Tg>tfb;$9M*JV23Ql9vo_KYkASyx6Rtox9l1L zd@8uEkzyY~iq&8-h3lS*qR-m5Zr&mIS9)c|uQvwKzrFv-E_=lXB9LYcVEJomFcPv%WsO|wTLrX#D#BWQ@(!Pl0 z(OC99`(1v*g7REkKN1HziV&8B$32B8J**q~3V2j*Hd|v~`eTI*8my5<8|kJO3!Wl& zlopfFB6)00Q5crg&J}W%w&Z)NN(K*QnIxuR_@;$ed^X<4g48i;Lct>kJ9V|>-ntn* zI0Mvo{#~kk)1>ogX8ye^u9vs=1uBSBY95Df~Hqz8pjD&ak=m$4H>HI4#_CtJ!h!rpbp6mC@l;-t_vUqeyHI=>R_R7d)J}0!> z|J#s$@|M?s3h94hPPNio(t2V)004yZ#y4#iGJj%eOuVAYOkylHmDcIBY=B{iYtd23 z(A;dwY+^?+eb19~qZ(h>&aUIzW(n<&LeKg6b>S_5)oHks-*7e z)*oJd42G4t`OaLIZx}CG`g2u#b?NDaeg%1BAUI=|4 z*-Hp<&2RHtYhMT6lmjx^ z@w2<0!ln%K8+IEkQAVq3wlsOvVoYQX#VZ}OxlKqtE>jb6PEW}p&;XXa$~ikI;U$^M zPPz0)kx{yfbR~GxGUU;gh&PIiH^r5Mnvh9Mu~MR|l4q<;kL>87AOn8-CeIY!r+2Bk zn{@b%o8oqN@|x$lg4)vPl`WvcCKb3&s0|+WrwiQ1qYstQ7AP#Yq^2ywCa26_7$*B- zYvvnmaZRF1cKEn3L)1fj>(PKVKbunIGm9sy3)pf zgzO6StB^#n$_GPPTc4sPYb+MaC9^%7T7k-z82vsB(gz{c@av9Q(VPRoVm+#?#h*D* zYQLa{c~}-Qd|~9ddXi={b19(N572cliB{8csAg8LWCJ7=GlBZ&$lw{4jq*)8vS<1m zR<-^5*PjThmgz^ZwxM9`@TTzKq3Lstu&(~KQG!WJKb1@y<|aB=Pg3@ZvQXUT6!Kr` z(lv7MP-L?R`w#6l_iP=50=ir#OB9Ktm&QiFj=EG}jUH4JL2Dh3DTWAIL~uL4OE+0e#Eq(~z#-O)uKPtE!u z;nDejaT`8BO^FE9T~*WwE7@aPKnHE84*qK8;qcayJ$~4L47TfoaTLItB!_(~r$2$W z&*Op>w5K1bclDB`EJPrK{D#(DeNsHt3Hjra}({;;pkN3_H2ic~7A%JSZ`pYuF zDjc;;OHp2#AdWbZIoDVsp9Lc~3nxzKf|mY+2T7-MG` z^sZ4^qEaaEEvmG0166~k!qFu;hcDs}j$(x8GmqIcK3GD1PMpAO#rZ*6fuFf%38Eyy z3P9Fi{rk2QUudl{N!I8H5N^$Ep@Ic$0odvw(f1llL8a0;^V@_4IrP=4R6?w+rFoj9 z5Stn%9fzB9L-Tc;Pi-$1VIX4qs#K~}=QF-+pLK*4T2_Gp{yPLOgW41NVg``VpoEDu z6Jrg-cRs;C2n%Y~KUIaXM{c(4f#MCe3wu1SvzEvlaZ=S#KledOwdmf1?@Q%0p z!PQIQ^c-&>mCs!Dq!oM&m@mz-z!1znvjmuN{?fMV6`O^#>x~38a->UZ_VD?!Zq0KZ zKz-s+`t(y{$Y4uWs7`hZDZT;@J0A>mZ*=%;ZojlRY(0KF%`v> ze)U$D>dS~*!FLKwo5^I9v1W{qihO&QMJEF9t5x$-ZlbiC2bL;}iJ1=P2E&toGJGn; zy%-!KE!J^$KS0fobx8q(>gULa88DYGiiH*>gUs|Bnh-eS#;6@ zHNN~v4Dx&7=sv+%anI}u=de7^fKhX|V#oo*}Yv zlo=Ig5JpbsfvKh%YHp2^)aVgCAG%$}5}au^Oly%9ea>n6?snX)vtpuQa&%+Cpuee@ zZg0J7=s9PKL0C1*bs3yExahoh=y{ZfV2%CCjNy@sm_r~(mF&E9w51jsfhnH}x-+sk zg~J3<^92=I8m1#*dm|(aju%-clHL090^u3= z+U8>Y#qJ7$9)Z4{i1lb@n`?oi9dfjD;4-&!r+_i$B^&%IebvNl!3nh9mGI1CQMmNuwpfl88ttWh0JF5r68@ z>H}dY`Ms3a>#&jDy!bIUsri>M`S+_8d!Xq|BsLh>zF&92>1FflX6>DzAhFp_VVH2+ zu1NfK22P@^JPv9w&^k7zFzr(uY}n`4E8a{aWqI`B(j>RM65m)&kPE+8$p0LW5L-g9 zY}S9snvosn5r;;YXPls|3t3JOsI@S+&q_7PXUtQ|Xe+gSyNJ_3DoYSk;Z_uL02d(+?X zV55OIw}}SUL2WjA#cqm2!En8*F`H8|u?Qk`bMRZOCzA!D-OJq`v07CNUXXZ`*9P`R zM=R#IM}r9%cY`4#%;I_yvOo5khrG2)Yqk9OVI<-VEYiA~+eYGSp@igJEU}}2o)Wxn z8}=VV$83+i2Lpv#jNx0ejQ8&*RC_i4h&#>6LGLBRWI%W7|0qAUUT!GUrV|U+XS!_*a zaOH|~G#JTYmnN>0r$bsWddlt=KPWcos_5{SViV$<9cl+>Z#C5tUMrcc#8};=_GnLBtooYi|QZ_gkW!1xjoi?a3y~aFr`l6 zbwU|&Ce8GcshcEr2$B~7GeLmKvt=JZB$&oXHb|sL8B`Jieg>WhePs&)&xv+^Qi$%C^~M^G8Lu5L$uX?{{hXgFiik;j~YENafq6g zAu9sgmwZ0l%yuHCEhZBs@CnmHn_e$Z=0sMuYsu)lLuss`_Cai%eobRe7OPw(IjGzO z@jL{Yb<=H;sq#`CzfBiF0w4Cbh?h?At*<{OgW@uWDC?7-hI$#+1)fgUs6IqgHfzc0 zY>jxssdEtPNu}r?;lL1+bv^>PYB3GhE^QTu8%)T2^fIv(G`WBaQJC{6P$0_%g&@^Y z4u9msMy)77SNI&sH!qP1ir6h@rBW^m&~Y+WhNY0bh$lxo8yq1a&wDhLm|Cw*kqu$B z40LIy4W@vXu1O0MuXPEA4x_b1Qyn!qmy2LB?{Jm0tK?8pb2ikOtPuv1>gnbHc){p2 zO*A>FQI9FOoakZS*!3q*OW|vWd8DmUdFS}0GL_+BKkM3BHH)hE$&At`%V}Ea7C2pg zEVz}7fOsQ$kAg`y1;G&0y(=!A`6`B`cW6T_dUwQLpaM*hLBrv(kSAvOoG%uqG3WuIBy|iIT!O1oJ)03*MIhZGB1s3Fr zbadADOCGwu`F2r^zk@iL#U;v|X1O^eJJ0W$ER!}a$SThxZgg(#bxeyI_!K)O%DEIZ zH-TgaOOWmHV`V)cBTbCz9fh{D|F{lkoMhjmg+?BaWYk>=P9e(|%A=rc?3w(m39 z153$)_r?usuh94dxK!v7e>V5b^ZU_67jhzI)FQS6#5wR~EZw~BODiXbTfsMPTxsUy z^RAy?AiK0SM32mzuJzeFsFz3aj}5BdGRS8O0^rI?-}>{-JEw;#E(YZ69aBY^ zn1@Q_v*9CFW zVh|ffv3|fiEhVmZy@Q8eOE)}PuNTU1@;Sb_r9$D|r6evnUrt%x;v%-3`kw_vOiZDA zHI&7GzhZi|JMZVxy_En*eLC`L4SMCl2yqP>5^J`5Cv0M03V2X5bA^5d08JxPr0TE6 zJ9Q8X3~W!czn$YZ;HsDS#?8O8u0c);b(Pa6@3(+xmy`Dc($=cx;nhA})U%O=@)H70 z!gKe36Zj39%nzrWePz*mFUvH7*c9&&mhfv4qV+HkKF^91Iutoe6m(0eY%X2n1oEfx2Syu zr)+`0y|-9KvbitV)g$Kuq!@Q!w&QX|1$P8Twi_>J8Z~tDNJZJuF=|}}cX%cQjPZlv zfA!zcYVY~X+l^^?3KW!66Zo=6-EnxX#PH?do@lWHgk~lS3h{}K{L#G2tg}=>kd||I z>FHTUBoSlo5Dq>|vTE z!a0fUkIj;o$q~}7_A6DKHpn?q)VZcOcm&Uq%~I$Uvgp*-!hBLyxTS^`Y1SZA`m6!g znSK%FUt1lZ1(s24tLo=SGAqlXArV!9Y=|5dTGY z@tM;>6O=!xIx#7HqCaJ02L2^IU~q!1L?`jr>kOC=f$R2q8Uqq#n29=I%3|7c8#1^UYA zTl^7Mhhs$z5Wox};Hltx!_dL9_6E%v0R3 zEEUgfvPN|S?PG)MbNjKE=vIrH{FIe3;3&WygUORaIo`A15ez?Nt)Ps-8`2)3*^z>| z=maa{GXs@Pb!1-L<~-%O;U#$RQRC53xfQuB8NOAyRat!ka9{JXbFl}upmnW5Ks)*Vvm|Rkw5j^@z+1mSAjW75|q*R@;jajWKYd0_I$vf zHc!TMpiq~|CC+`IR+k2rmI1sHFnLqvJYzr@oT`X>3sYv?+2?;r;_2LRH`c18fUt;?rN)Vs#o3wXCbq-q>HD0ZkXnKV= z4~0ZDvDfpN!tuYM{wJ-Ds)LA8V1R&3(EKN+4?3~{5xjNOF~0v4P5<`sdAI0vlYL%x z#dEP;vkNQgj z780N;EaC!$GQ54N#JHH_TF{&GuQdq`(t+y1T!)jbd#~u<}pFG zqBD9ID8YtV@uUg$yW*lU(5-1U0z1ZZ)LWU)WWi%ADotXbXk4Fc5AG?WKRVomUHR&U zg%qZ-r-SJ-64ysC($s~EiwTy|uAuoZ#rmhfxKt1%YIle|O1&Aq&9EGs-S7Z=$9NQ# z6jn5oC3lTcIFpH8MUPrA@*MA_3BN^66KP2w5T1|F4t_LRX~^a>7SG4WtgD_Q#UV<{ zWQP<20yL2eJ2Pq|3Eu|+Hy#hbi^bnUXUiUGuGFyv zs=_dlRSRfv4U2-NCW4bz*a3wN1SZNIiv zc}k*sE^#t)Yf8e%L@I?j5#UC=T2~+nd>$>c{6KrP?ue02n=)X7*y8A_g>U4bE<>fx zn^XNLS)#YV1BM)C=UfB@c!Hu0lr&BNcLU{eR}L>ns!Dld`s;Cz3ndKC%f=8xov)jU zFksRhA)0Z|wYo+3H=@gUb^;!pP>;pH;H-~-Y8&|@q5cqzkusWkzuo=CB?(hPz`cOPUU@{ z45M()PR?OM;zsDv36}4{XVExZD%+_zU}|UTdxQ`agJey^tjDMu8x|PL4zLu$YN#Gg zac^JT1)9~8(h)Q)vlp23<5n>MMWJSj`F4!8;!U>rBliu1XiR19DW*K3>ssz%XzrlZ z>T(ilVxdTbppRZv!VzCpPZu11FculZqk!-oio3sI2PW~mL@}U{#S>!~Cukrhz)*U< zxCP%sG5j&rFpOtuFI$Ed@FG%oFk7y$u$qAmQi%D5op{MqZbv(24&Lx!*2v}}34c;b-T$3oHSoDKtKWgWd49pek zLt5`4Qs$&G#?tYz)%`$9orWSPjDFtp-FZ21nU^{^iD}BF!L^ne!z=uimewXs-5E|? z@OIlw`dih7KMW-Wc!%tnx$FgKC>@Q;%wH}cxmX@_QCM$Z(K28Kqgp?cY-naQc9=nh zh&|$=)|T=u*mLA3QEGFWmidEUg@_(j=Y!nrpQdoI8&} zLX*#V{^7zuO0pT8o48>(q%b$e)P}PbY>*Ji;Kqtt5wWfSR7VPw!`Kerp#>$FSjVD1 zyEn1oWI_Lk*w111nre0&Xwc?3*tPJUG8mY|^^N`$MR&3;3mkI#(&^#pMMFlQ)u%Wa zI|?GWPmHfMb(FZ)UBqjBU#vbRYNJe7C~-OU2rR540+MH5{S=GhMaBRYB+R5^w2rfc z_FbhFTCtA-i&}46Bsk8qZGvSF(5N{7VKe-!ZAbg9lG!Br{tW+#yyfcRYT=Y=hy9X< zq(6p_U(K ztjidkM$kB>?`bO@Z}U57#IO6Bxt+m99z6_(Jkcw%ZE%=mbvf!T(S=1??l_skWfC!6 z<0npNUtLzRE@7FZ^|E+-+1wC1OL7HFdW!S(De8$!WBaormcH_MW=SlK2|2qJHzJ>q zDq5onP)IK=bZ^YF^t~eAnY5$w`{N=FpK4^T$%kvgIr}1H9wbR zZmn7R{e)BH=}nr+*H|{Eeb+A{h8wz(m#j2nfK~?CQ9K$;{65Zemx)n)zz2|bpvTXvK-q%!c}2fB;1?K4va&bR+O*|=0usSt&VXNHWTOV*m^?9ezvJe$rFiV1}DnC2tXn) z1KE;xekCl(%Bgs@|8SUpW0lLtdWPM%vg{2#t=i~&d)x^iC@b6aw|wMNI@|Qe*%=^6 z;|St;_Wzbqif%vi3Eq^Zl6E)H+9z$EWWKo(lD`fh_p$;9TFS&9pihdDCZ83#eg2e4&ym1V(me zr1td8c?L5=B6giGe^hAtfEZv(0d<+`Fh>8bu7VTh$GvbgeBxhGqz3ruTFnDGZ?4bby{>^hk5gC?Yc3$5#XC@0}(3o=(- zyUzILDQMeTTxKDsEcr=eDla3q z838_;pIx}C*~QLY_)yLWyUwN`yw6O^-5D}u6LG8$sKevXS4>Yk(1ddng?WkG(k~7y z&`UzSKchFWBsJ)3yg2HDl#~2mdYSmZahducZ$*^mE7hDzy{sj_0HfBE2Goe)NzjNyqY%)p zN@1sc8>-w#cZ_e7S*RRtPS9s+k@afCPI(}y*Iek{_pB#EW{OB9?=|QeUUH4Tkaz~K z*Igi;-`}|IP`{H)@11rnJxpg6+Qm)cS3M5ZMUu&(x#!c1mHM~Dw&%qC+st+9CiN_t zx^eC%`M305c>y*59R$uk`u{ulo!_Z+Cl~IX+D4a_n&bgGwFtw{m6zbBxhn^{tI$@D z2=Q>pRODU)rHKmt2L!_%rOX#xo?ep0zlw1njkqA~6c8d^!;yB`0YXtjETdtLYZj7@#K9xF=i2+v$$dNTYGsQ!T&38wBw;Nw0khstDzRxOlfbe&PprTCN@8W( zR@S!sxFjEId`Y!k(%BqXN@!!pW{oR!e^s+WzZUawzNLa+kv3MwZPF|`a;IIz#o5A% zs~_q04~8L{=bi2%FDxmO*yr?1REWKyc)XX5Ret=1s(!j?MfT4tbFUW4AgC%=1CEncd;5chU88@|&4Ln&HFSRj$tr>U-(rdEPNy(THTacB4qxv+? zOu%42c&+mmLtftxwUwG$1Lo$hsIv_=vs}L)0BkLE!T-Me&m2Bb>%?e3B_NCk-l(gu z7zlV<0AfOc$!Xncl7&CF6afm2SPMR3gFH$Bx{9RXcuHztfG*6MsT)>;#j4E4m}N|h zC2DDS(umXcii-|aGytZk@aH*3r|V*o3~_sUlBs*J8$)6^~?WvqIGH{l?F&T>**Cj+Wxqo1m)h$_7E5 zu_NZ)DC@trr{~9MM&}*2X~x(B)tiVj11~i(1O%P?IG-*TXg^Q`l7J|chNX}1(OHZZ z*`~3sG3x-zQumzt=5UzpYkXz`&B>#WLyV^LA~(Rrl;yG3iT`|}*T$o2civkT2WQD< zzzUUhmEy$sb^s{OMO1oYQ&e7bGx+=DBC=j-uKWpXj3eNDIZ@#vrqO_n!*im0ITB%U z*;aMZ)r@2X$`0k}8QEz3B1{P>JrvUiR0;P8U^wxco#NQB~W?;3S{_^?2n+>C|3 z3)+kYw}hxx8B>f7a03!~y_aj}FE3#i5i{5m6IH{g_~E`>v=GxYMfI-qXJ_a(dtR(m z2aH(h*ImwSOP|RNo*xcQ2%K%8q$)Rdequ&)rEUs_(7e0J0o~u7G7g}v5L-2`D4^V- z&fGcztMg!CHHa=sHMoBYS##HrAv`I?ajIsDW}Y&NFsL-`;nGX zB^B8avzBcu-c0p$D5a`2)8FSdR zY0*mkKJyKJJNqG`(<2G~YAHNda*Ic*60(>l`c6$Vc7YvxhRO~mf?EJ)(-RnWPBE?7 zk^y$0W%c!K-D!jm)6_T$wSlEWE){ypTsZ(9$0h;xpfLjTU|VYxr9bJEU&2{W6cOE) zfuOP01)NqKMdzJKv(B|gQ=MevXp>{+aQJ}EbrGHG;gUcms$KV9)}}A#(AewA$m5VA zl5lGf1^OIqkz1G}Bz4uJ{dkXu`n|vD?gjyksLLddFQ8Y4;NIXYbP5->Y9DomPi_p& zpQckVEGOoz6U{d1Th?nGgg}zRt-kQ;vEc^^6 zVCJ&NK~2CiFa$Ap(P9#tFAfkz%$8uspk&Q}%l=Hm#ooP|Ss=H*!ya1XnVb)N0Lvo6 z_X6F=DQDsYmwkjhyLv!O`RtEaQRlj5z;1^(4|b<@$?;#{reg71B4r!tG~`|NQWDYu z02`s}8-KjpdButf$=w{O#dP!&AT7ks{fOBk8b%fy9{S`AddI9~qzjPWQ52f#@D^6` zwnSp6zZ2`aqbWjJtvK!A)m2^2&5NzOl;pAQs`i_pmcmLmdOtI^5nfVaw0ZlB$|J;J zK~cBJcCOVPQ0W|kxWLvmNcl#itO*P<0@@at;*o2y z%1LplUjKo=h9*tsm2;r9%XK-*LIQW2)6?UiS-XBN+mvY_s$$C#YU4l02@vd|Pb4}A<}n(yG-)6}xaE>UQ`6mh{ebJYoH7`hFHRr*e9cq$ z7n3EA$5+*|9}cU37+5A#fx@8}R1cU9+A+^y5UsRKA3b@S72E8u-4da@V}vFMJ2Sz(bh8Z;F$$ z-n`oTS+p+LcIkK}6Us4&v((d6oP1z3ZNn@r@o8H@9H^DwSIR36@bB)C7UJ9=I8^9* z;E-Obx6SLBjxN2nvB(?e=%UbKFEJK;AYPga=!1RoA)Swl#a7FVMIrpnx8JWid7f>k zvtDf4Z|QHn>?$NRh`Vo5LJY>7&W=n%1KK*d?JItMequ0do)#f!4UX*vI8XI9ACc|g zcNk&OB^E{y6@yW5;6$6>zuvS@bv1ls-zDBw5A`>3FvD370UNvkJ0zw#GhZ(1l<+)K z^m=cR0lfy+TA8+A6j|gN>V(Ee0-psi=bbBidnU``vWe38ZGa}~0`02wUivev)*l5@ z@>yq73uFjE9fqG<_-+8I6*^LKPCw9FkMm`GvTaq6y+99HV7Xb%UG71c;k}A>s}3pD0Es!IpL3IFo{|(9*-Septi8N<-q3U@qrBYx;PO3e73Hj2JP8 zIqS2Z*Zc*FfUJNLdK7d%S=GFf<~<5y{mWnJoqJO(o*|LHsbnE?)}ld?5}&7j!;m() zK<*QQ5EZiz_OLg_P01GC9%hQil3t^AYZ-FudTzKGfi8A+ZZ)7j;G%HoKYuf)1AY{fKg2R8|= z4to{$D&xO7DK?22Brl-gHRfa-j-?-3gm)s{e8^qBGcs!C&zE-Dn}60UY@DjY4%aNa zO`-}SH2HI;V1`506%k%FSQJUQ6EZBML>5gc0lgg}t|Kumb*yepD{?zttH(Gt;$;*T zGiz@Cx_Ihz;pG-b$79|+sSRirUBeaq6nk0odFaxV+xF(*#rBNfp+5yJ--30H7#X9*$cN&u@Sw^Zk6e0- z=ihx{bP%W(T3Q&YFsOACnw&dwieB|i`*CNRc29YTOD&(?pnSnHoAWMuX?mw`H!-7R zcZ!={9>m2fZ*Q$Do(uCY7tf?~DOXYX1+=t^2=&fMc_S4Ngs@%=1)N_n*01+sB6&u- z)JO>hJ)YG2X5>7$yaK%cUd*aUb`7@{#@pp&=06vsYJC{D-896xFRzgL+)}rU&V|P2 zJol3rMEn)RQV|n>8;4V($)H`J;C^2(%8gFo&AIg=CEGa-W8zdHBC>o-k83r_2cD?Z z&CYJe0k-@g02TySL(`nZ0?wN;f3h2&06$=eE+2oaU0`@~IlSsgm@}F2TXd2x7&x-` zj@fNow!4d=x32f)ME~Tn2{kr9y%WFl)aN#U+BOJ0EXJDX6R%fman$7D&FPlVR4xBh zYSb!HWV^OwzMeTaScM?IZ(l;b0m3hiMm}V+JwU)@G3nslX#ZWURORZ$QB2N$!2MF(_8v6^r|Nbi(jIJ0lYx9OiI4u z)^1>!dpDWvrGFNAE3=XHRo+E1L~C^2jj>m=31jIsi3*%wga4d9T2dl+4Hk`RIt?$e zS6KY>gQQPsQD~P+GO#a!$PV+dxVos4k$`~+oo}8Vl-p9GiaKH>0`VerZOf2x z&&WL@NR!-K#e^XspgZHXQRhcoZG+^ngaqGy#CIt-<50GEeY^ISYXS8y&7qY7kHn8F z#)zK-tJop;&sf9VdOIQ4!eXtccf;hc0bxq+5)T-|pIB$}91|JBvcTK%gY6&Hc)7TO z8j(KVdKX0{y8oX+fO{`Mhv0yPe}w>$eS8 z&Hgge!-^tDPw#^Z9sutm3a3d`8(d5PQQKuZuN1J%TeHDk9}u-&nC&7YxP^(o)UX?T zzv4SSxbnW;ycC|=kG}37VE(tCTQu1)%ka$O)&B2kP%t|w*t+%2 z>m&BRS1zbQ{_VaEkm0s7>0FQgY`t`z{A}`&IoFPeB%{pxX6QR7Q=>{aM6rAbHYw-5 z^Zu`ml!Y`v_Vr&6hzI_E+Jr?s2e7_RlqN+*xGt~Fw>j99L1ID4_?Ohb{z8rw!^1x= zztw4i1huiO!>tkr_ zr0r#_b3amg@^w1jBJ3daM;%Qs!F%=~81_A+7{|jr8W_k1trDAwDD;c$FM%>#1sL7N zcsZBYF%$E;2DMt&iduLYvoG62t~|)i#majmuPp~?!7=vE4{-xw-Q4VY)(q{?X-3TE%R#`451jj5O$j7WB3@xozn}|((q0-a=%-J|?xJ$Sv zR#;3#_@d13!n`i*j2+VGjmF)I(AHccEYBMJy+9Teq(*5Vy8VGu~Xr<|8-|v~nx<7K>hG?US%2io{O1CsLl;#^^8j@TB26 zIz7S@U6$by>qx4f@=@m7f3xpPm=6g4fBAmG|I4?S<3vil@r6!gPND$He-8n~bA{Jc z>Ey-eQk4F&`x5i0A9~j15^cFM>oQjY*P#9~@WT*#gAmDNg%M^2zrOgsPt(7@K7RcG zF+3+(+M=%eNjp+X|0H}Q=+YOklf6t&?uLpL5z+f&nB-0wMCE00h` zCjVb!3J|S`-kHfXDY*Vvolf7TYm7mW+}Q3P654J;4g0me9>w?pc70;12Uu^VO@2GU z&mk&llq#nKZMi{_Py=_SOrKyL!h~e50#Q%+&I3M@$Hc2{8KzT0fxRC?Uo4w|MIXNt zx8)iv_a`2)+gsIR!YpI6C;4lR$%^_@rdgZl6Q7hvW!X8g(U)h#XG<~Jhy$D?Lr?(s%o1P zf*2B4*7ik7!kQJ{3K^b)pOW<-FdZtiQ5{Z%df!&Zs;fl)mxM)d5RyBIVQNT?(2#4NL_kU*= zUW?W(ZPzSOVIOjZuP6$z{^hLvQhk&VHbEe&;$MQjfmF_3RIXmaME*=L?rNz=c!h^2OB71la2QL2`%{ZHxS!+OsSa@rfm4VOdg$N%2AHGvogv5MhPk` zzq+MUrJ*|}*45%Ah~$#M!HPQwFLbTdx@M1Ze*M1vq1$wk2~BZdk_98tZjX&XHOuudfQb#TY!Rkk9O+&)~NYe*^h>!0;i&i}ZZkoDph|&B)$|RncOvF|_0( z)@Ief?%k^RRWh?xmZ2eH8*qd3R$Am@;!;R|S@w&!yzshTO+1nvc~x}mdop^7syHt& z&`hALB}Tq6;VssVa3Vm4CclbU4)`ePEsc*>F5RG(G81yXr0*d+3QOD6jd<+bQ|=qe zEg)^3(vekM&8t~`7_6&u?JvtM4X!Tq3r+Na`9rvL6*>X(g+Y1njA|~Y@O_=r%c=bm zb7xD!z|M_2UDk#KFv!Qz)f(Nub;S_(_ZH5(k2%xZKNg$NI7_gGQMgwEar<7ypmoq@Xyp^l5ENeZnT>EQJPd zGy}S|R<)6>1>6&zOhaVb3!3f&DF7%r9~+wFB?NhX68cj7Wfn&+5X`wTFyxliNA^aE zn)m>|@%5i>tw;H0{{;4rfcgaa{{y*t^-u}*_=(mTSU{aT4dEoJWbomp0ROl++s!?j7<0K zNWbD!X3_wdslzJbS!l9=YDT)HBn}Sk#R>Qm*AiwcW_XSAczSj1vnh)uc*k~8jKJw| zR~qfYM_|#EGkW8?3r%AXK;YyyIiz4WNV#~N9WkADoYuIbN{0LQj0@Q6!0Xn>fH$MI z*~z{n5i;mkz{;HLWqTDfsIq*jN`k^9tgPN?lfJpvdA2DRM>DA`LU*${lLs`o;u()T zjastG?_pI9*6uk)Vd}|{^2uSyRTSvU7ByNnRp9$;Hb&9L0iK5;=-xIk9hUNsW9c;l zM+9|jZq=Vi67F<_8f*bO==TUDG1y8hvDO?xe4gsyTBk&`HUJ;!bn&f&Lix_@z>$kAsnBnnC@W{OA4LQa}zN`~Z8PGRtJX7&;-g92K*81-14G zw?}^c6?#H)6e5ZLkxwUhwrlC`z0l8A^HLDV)P4|&nBzKJivJPMCwR2Wqv^fTPt0Id*@-!WtqVF=%Ao*Ju~%rebC9~ew+)m|AH_Cvt!HR z^K9sS^e~i)h;`sVv49&&^j9LTDQ0URO>Za(Sp)(C7Q1FJ7;&;NLn+AciH`rGkY#d$ z+Dc2acu>bl2QR8n(!=42F)&;l;Bm&+>|~5mHAaY{jntv*D~i>Wm?S&vX{fUEO}GYn z&wE?nj~uT!1jIrrwDn{2D>GD%zA|d>!T*p~6j$j;Qt~j7OJ&8Wk$mEFI^m8rmzQ_X zPXHRtqgbj%P$y(WJRlP6IW7iUu_n)REU=r}G1H$lxHgnj{d_AqZe^yYw%}2~;?8Km zL@{0{i?Oy+QD9+rnKd(1=R(Dz^gGFH?L!Eqf&)SBvhFas66s|{~4NB0J3VH08}LoC;7pt{?To`2Wj z`tA$Q7yTsRX9CqaC80xNomy>AS`%T`+pMI6cSVTSgLo?}Df>TNoq1Ff*B-}XOj#5H z7KjB#mas1ZPY`5_2LiGNN}E7{00o4SO3+{{V1UT>s9_TZ;)W;+h><0c3If6dMB)Mn z0?I>u8huqGgrz7_+&URO!6E0&ADR2f?|1K=$;{k)?tH)VIO}^qHKNAV^sWyPd|vRx z^PQ$DH*BAJ8f5n|)rfn7hV8vB{gNC}QJ((1_2)EGi*HRnd0-?)KQQ(EJ&T>MvFW}_ z)31p-$TQ z?1>6awB;{splC~gq5Mv}yp%dMY?UvWIOX~f7<*m1&T;5+16_AC!1{;paBQb-#5m&l zW0RasrJ9ljtyp7k(;zw}0bLPIb>qJE;Zz>+CrHXus|yyR1{;F!j@aPJ zbEL=tCb_4i^guP{L+C_J!hvF8+5kQHj%}{f9}Q*m7f*;c7Y&@APWtF>u>`$sFKLd7 z9e3ztUaGm~?D?C>^Hr1&i5=({|92Pj%$}9T?>}C>S{UMzs@S{@^NF3WtTa7!%+5n{ zO+41j+K1jdGGJY=UYm9zn$ElhzvB~z5w+L}5?!EJ%dahDUj4(FtI{RiitxOpbiFQgP& zc=l+yxHpdVlEjI>7ixc|;EEwAqcD&3A$|UHwi`8LpV>9iBRzO^+Vz zTkxY!WNb8vsb~{%-jMA)Gput>7QzzH=Vxi>#?cAFxT}Y;uct1l$TQLu3|h(i2Dw7! zE$(@7l(#A+i|t~ju*pcn@aUtypT&QLTe>5(XV4*|I&x{8xQ+C7|9!gNO#SgBi1`g;_u?vqs!SA8IR|x`u}_qz3xPR zbBM3YP)l3xGqZ3xRuTXH;^fIO0VTJwRlrJ~?6PaZx0CoI9)|r>=5uEcru{iF5<$*u zY9i#D+n*{*;?L%O)ay!8ak_PAb(GW?RqETL zj{;dWUW!~gc7_FgEeCJcxC7`u%ws$>UfTz4|3X3PDYDNJ7A&m=KyMX2@JzF+cH-_P zQWA7GYk`CxjS=7>@JOvYu%|)(csNwv3O(@IBFg>L;6UAKcxfO&W>_wdLb)J7RooX) z9%R+o0bd)ux*|YGT2>j1i)@xP@fJ%skR|1&$W=%iEpVTjf#;v zErH)(z@Zzq%E}5ZH~_2OBy0PeYx4z^E92<`GOGcoOOeN>W;^K2bNdFC$Op4{8faH1 zXa^qb;28m{GU036vgi!H;{^aRiE5|~ZiqHS?t}nsNLAbokf|L*5CH*2xPgx@h5|Ch zT?nv70Odq*Q?mvb>1ibG1?^Q?(Y5J*2ZI`LAiq%oq=IPXtq9057=}8j25{=tHzOdaAq04U3WJGF zHb8)Eu@nl0M?mix5VQrHXwn1Vg*{Np7tn@G>2wf+yn)qeO%zHG5k)Z_0swIEkP2L< z)fp=kN*4i!7Ql64mukSEYkgE#5e4TZ8oL`*D!!E(Nx_UaSv j+6D+geLfC^M|+mQ*Ow$yL@ceNaI6S{mE76Panj42;u diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 002b867c..2a84e188 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a93..ef07e016 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License.