From 166bec0f8d2f3291dde4f30964692b550ff8ac40 Mon Sep 17 00:00:00 2001 From: morph027 <600106+morph027@users.noreply.github.com> Date: Sun, 23 Jan 2022 20:48:55 +0100 Subject: [PATCH 001/938] add org.whispersystems.signalservice.internal.push.SignalServiceProtos.storyContext_ to graalvm reflect config (#868) Signed-off-by: morph027 Co-authored-by: morph027 --- graalvm-config-dir/reflect-config.json | 1 + 1 file changed, 1 insertion(+) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index a9065791..5681cced 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2347,6 +2347,7 @@ {"name":"reaction_"}, {"name":"requiredProtocolVersion_"}, {"name":"sticker_"}, + {"name":"storyContext_"}, {"name":"timestamp_"} ]} , From 5d23b1ed9d222a3741bebff67e4af9b901a21b0e Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 23 Jan 2022 20:50:56 +0100 Subject: [PATCH 002/938] Improve error handling of getUserStatus command for invalid phonen numbers --- .../org/asamk/signal/manager/helper/RecipientHelper.java | 2 +- .../org/asamk/signal/commands/GetUserStatusCommand.java | 6 +++++- 2 files changed, 6 insertions(+), 2 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 5c4896e1..38acd37b 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 @@ -105,7 +105,7 @@ public class RecipientHelper { .getRegisteredUsers(ServiceConfig.getIasKeyStore(), numbers, serviceEnvironmentConfig.getCdsMrenclave()); - } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) { + } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException | NumberFormatException e) { throw new IOException(e); } diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 50e9b17f..5ab237f9 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -42,7 +42,11 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand { try { registered = m.areUsersRegistered(new HashSet<>(ns.getList("recipient"))); } catch (IOException e) { - throw new IOErrorException("Unable to check if users are registered", e); + throw new IOErrorException("Unable to check if users are registered: " + + e.getMessage() + + " (" + + e.getClass().getSimpleName() + + ")", e); } // Output From e5537dc4dbf23ec9ea02e80e102707950c401e08 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 26 Jan 2022 19:22:46 +0100 Subject: [PATCH 003/938] Update graalvm config --- graalvm-config-dir/jni-config.json | 141 +- graalvm-config-dir/reflect-config.json | 2041 +++++++++++------------ graalvm-config-dir/resource-config.json | 6 +- 3 files changed, 1088 insertions(+), 1100 deletions(-) diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index 51e2b4e1..4865f747 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -6,41 +6,41 @@ {"name":"groups"}, {"name":"uid"}, {"name":"username"} - ]} -, + ] +}, { "name":"java.lang.Boolean", - "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] +}, { "name":"java.lang.ClassLoader", "methods":[ {"name":"getPlatformClassLoader","parameterTypes":[] }, {"name":"loadClass","parameterTypes":["java.lang.String"] } - ]} -, + ] +}, { "name":"java.lang.IllegalStateException", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { - "name":"java.lang.NoSuchMethodError"} -, + "name":"java.lang.NoSuchMethodError" +}, { "name":"java.lang.UnsatisfiedLinkError", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"java.util.UUID", "methods":[ {"name":"","parameterTypes":["long","long"] }, {"name":"getLeastSignificantBits","parameterTypes":[] }, {"name":"getMostSignificantBits","parameterTypes":[] } - ]} -, + ] +}, { - "name":"jdk.internal.loader.ClassLoaders$PlatformClassLoader"} -, + "name":"jdk.internal.loader.ClassLoaders$PlatformClassLoader" +}, { "name":"org.asamk.signal.manager.storage.protocol.SignalProtocolStore", "methods":[ @@ -56,115 +56,100 @@ {"name":"saveIdentity","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.IdentityKey"] }, {"name":"storeSenderKey","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","java.util.UUID","org.whispersystems.libsignal.groups.state.SenderKeyRecord"] }, {"name":"storeSession","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.state.SessionRecord"] } - ]} -, + ] +}, { - "name":"org.graalvm.nativebridge.jni.JNIExceptionWrapperEntryPoints", - "methods":[{"name":"getClassName","parameterTypes":["java.lang.Class"] }]} -, + "name":"org.graalvm.jniutils.JNIExceptionWrapperEntryPoints", + "methods":[{"name":"getClassName","parameterTypes":["java.lang.Class"] }] +}, { "name":"org.whispersystems.libsignal.DuplicateMessageException", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.whispersystems.libsignal.IdentityKey", "methods":[ {"name":"","parameterTypes":["byte[]"] }, {"name":"serialize","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.whispersystems.libsignal.IdentityKeyPair", - "methods":[{"name":"serialize","parameterTypes":[] }]} -, + "methods":[{"name":"serialize","parameterTypes":[] }] +}, { "name":"org.whispersystems.libsignal.InvalidMessageException", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.whispersystems.libsignal.SignalProtocolAddress", - "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }] +}, { "name":"org.whispersystems.libsignal.UntrustedIdentityException", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.whispersystems.libsignal.groups.state.SenderKeyRecord", "fields":[{"name":"unsafeHandle"}], - "methods":[ - {"name":"","parameterTypes":["long"] }, - {"name":"nativeHandle","parameterTypes":[] } - ]} -, + "methods":[{"name":"","parameterTypes":["long"] }] +}, { - "name":"org.whispersystems.libsignal.groups.state.SenderKeyStore"} -, + "name":"org.whispersystems.libsignal.groups.state.SenderKeyStore" +}, { "name":"org.whispersystems.libsignal.logging.Log", - "methods":[{"name":"log","parameterTypes":["int","java.lang.String","java.lang.String"] }]} -, + "methods":[{"name":"log","parameterTypes":["int","java.lang.String","java.lang.String"] }] +}, { "name":"org.whispersystems.libsignal.protocol.PlaintextContent", - "fields":[{"name":"unsafeHandle"}], - "methods":[{"name":"nativeHandle","parameterTypes":[] }]} -, + "fields":[{"name":"unsafeHandle"}] +}, { "name":"org.whispersystems.libsignal.protocol.PreKeySignalMessage", "fields":[{"name":"unsafeHandle"}], - "methods":[ - {"name":"","parameterTypes":["long"] }, - {"name":"nativeHandle","parameterTypes":[] } - ]} -, + "methods":[{"name":"","parameterTypes":["long"] }] +}, { "name":"org.whispersystems.libsignal.protocol.SenderKeyMessage", "fields":[{"name":"unsafeHandle"}], - "methods":[{"name":"","parameterTypes":["long"] }]} -, + "methods":[{"name":"","parameterTypes":["long"] }] +}, { "name":"org.whispersystems.libsignal.protocol.SignalMessage", "fields":[{"name":"unsafeHandle"}], - "methods":[ - {"name":"","parameterTypes":["long"] }, - {"name":"nativeHandle","parameterTypes":[] } - ]} -, + "methods":[{"name":"","parameterTypes":["long"] }] +}, { - "name":"org.whispersystems.libsignal.state.IdentityKeyStore"} -, + "name":"org.whispersystems.libsignal.state.IdentityKeyStore" +}, { "name":"org.whispersystems.libsignal.state.IdentityKeyStore$Direction", "fields":[ {"name":"RECEIVING"}, {"name":"SENDING"} - ]} -, + ] +}, { "name":"org.whispersystems.libsignal.state.PreKeyRecord", - "fields":[{"name":"unsafeHandle"}], - "methods":[{"name":"nativeHandle","parameterTypes":[] }]} -, + "fields":[{"name":"unsafeHandle"}] +}, { - "name":"org.whispersystems.libsignal.state.PreKeyStore"} -, + "name":"org.whispersystems.libsignal.state.PreKeyStore" +}, { "name":"org.whispersystems.libsignal.state.SessionRecord", "fields":[{"name":"unsafeHandle"}], - "methods":[ - {"name":"","parameterTypes":["byte[]"] }, - {"name":"nativeHandle","parameterTypes":[] } - ]} -, + "methods":[{"name":"","parameterTypes":["byte[]"] }] +}, { - "name":"org.whispersystems.libsignal.state.SessionStore"} -, + "name":"org.whispersystems.libsignal.state.SessionStore" +}, { "name":"org.whispersystems.libsignal.state.SignedPreKeyRecord", - "fields":[{"name":"unsafeHandle"}], - "methods":[{"name":"nativeHandle","parameterTypes":[] }]} -, + "fields":[{"name":"unsafeHandle"}] +}, { - "name":"org.whispersystems.libsignal.state.SignedPreKeyStore"} - + "name":"org.whispersystems.libsignal.state.SignedPreKeyStore" +} ] diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 5681cced..ede0d53f 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2,437 +2,437 @@ { "name":"[B", "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true} -, + "queryAllPublicMethods":true +}, { - "name":"[C"} -, + "name":"[C" +}, { "name":"[I", "queryAllDeclaredMethods":true, - "queryAllPublicMethods":true} -, + "queryAllPublicMethods":true +}, { - "name":"[J"} -, + "name":"[J" +}, { - "name":"[Ljava.lang.String;"} -, + "name":"[Ljava.lang.String;" +}, { - "name":"[Lorg.whispersystems.signalservice.api.groupsv2.TemporalCredential;"} -, + "name":"[Lorg.whispersystems.signalservice.api.groupsv2.TemporalCredential;" +}, { - "name":"[Lorg.whispersystems.signalservice.internal.push.GroupMismatchedDevices;"} -, + "name":"[Lorg.whispersystems.signalservice.internal.push.GroupMismatchedDevices;" +}, { - "name":"[Lorg.whispersystems.signalservice.internal.push.GroupStaleDevices;"} -, + "name":"[Lorg.whispersystems.signalservice.internal.push.GroupStaleDevices;" +}, { "name":"byte[]", "allDeclaredMethods":true, - "allPublicMethods":true} -, + "allPublicMethods":true +}, { - "name":"char[]"} -, + "name":"char[]" +}, { "name":"com.fasterxml.jackson.databind.ext.Java7HandlersImpl", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.google.protobuf.AbstractProtobufList", "allDeclaredFields":true, - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"com.google.protobuf.GeneratedMessageLite", - "fields":[{"name":"unknownFields"}]} -, + "fields":[{"name":"unknownFields"}] +}, { "name":"com.google.protobuf.Internal$LongList", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"com.google.protobuf.Internal$ProtobufList", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"com.google.protobuf.LongArrayList", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"com.google.protobuf.PrimitiveNonBoxingCollection", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"com.sun.crypto.provider.AESCipher$General", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.ARCFOURCipher", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.DESCipher", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.DESedeCipher", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.DHParameters", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.HmacCore$HmacSHA256", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.HmacCore$HmacSHA384", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.TlsKeyMaterialGenerator", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.TlsMasterSecretGenerator", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.TlsPrfGenerator$V12", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"int", "allDeclaredMethods":true, - "allPublicMethods":true} -, + "allPublicMethods":true +}, { "name":"int[]", "allDeclaredMethods":true, - "allPublicMethods":true} -, + "allPublicMethods":true +}, { "name":"java.io.Serializable", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"java.lang.Boolean", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"java.lang.Class", - "methods":[{"name":"getRecordComponents","parameterTypes":[] }]} -, + "methods":[{"name":"getRecordComponents","parameterTypes":[] }] +}, { "name":"java.lang.Comparable", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"java.lang.Double", - "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, { "name":"java.lang.Enum", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"java.lang.Integer", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"java.lang.Iterable", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"java.lang.Long", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"java.lang.Number", "allDeclaredFields":true, - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"java.lang.Record", "allDeclaredFields":true, - "queryAllDeclaredMethods":true} -, + "queryAllDeclaredMethods":true +}, { "name":"java.lang.String", - "allPublicMethods":true} -, + "allPublicMethods":true +}, { "name":"java.lang.Throwable", "queryAllPublicMethods":true, - "methods":[{"name":"addSuppressed","parameterTypes":["java.lang.Throwable"] }]} -, + "methods":[{"name":"addSuppressed","parameterTypes":["java.lang.Throwable"] }] +}, { "name":"java.lang.reflect.Method", - "methods":[{"name":"isDefault","parameterTypes":[] }]} -, + "methods":[{"name":"isDefault","parameterTypes":[] }] +}, { "name":"java.lang.reflect.RecordComponent", "methods":[ {"name":"getName","parameterTypes":[] }, {"name":"getType","parameterTypes":[] } - ]} -, + ] +}, { "name":"java.nio.Buffer", "allDeclaredMethods":true, - "fields":[{"name":"address"}]} -, + "fields":[{"name":"address"}] +}, { "name":"java.nio.ByteBuffer", "allDeclaredMethods":true, - "allPublicMethods":true} -, + "allPublicMethods":true +}, { - "name":"java.security.KeyStoreSpi"} -, + "name":"java.security.KeyStoreSpi" +}, { - "name":"java.security.SecureRandomParameters"} -, + "name":"java.security.SecureRandomParameters" +}, { - "name":"java.security.cert.PKIXRevocationChecker"} -, + "name":"java.security.cert.PKIXRevocationChecker" +}, { - "name":"java.security.interfaces.ECPrivateKey"} -, + "name":"java.security.interfaces.ECPrivateKey" +}, { - "name":"java.security.interfaces.ECPublicKey"} -, + "name":"java.security.interfaces.ECPublicKey" +}, { - "name":"java.security.interfaces.RSAPrivateKey"} -, + "name":"java.security.interfaces.RSAPrivateKey" +}, { - "name":"java.security.interfaces.RSAPublicKey"} -, + "name":"java.security.interfaces.RSAPublicKey" +}, { "name":"java.util.AbstractCollection", "allDeclaredFields":true, - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"java.util.AbstractList", "allDeclaredFields":true, - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"java.util.ArrayList", "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"java.util.Collection", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"java.util.HashSet", "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"java.util.LinkedHashMap", "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"java.util.List", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"java.util.Locale", - "methods":[{"name":"getUnicodeLocaleType","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"getUnicodeLocaleType","parameterTypes":["java.lang.String"] }] +}, { "name":"java.util.Optional", "allDeclaredFields":true, "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true} -, + "queryAllDeclaredConstructors":true +}, { "name":"java.util.RandomAccess", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"java.util.UUID", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"javax.security.auth.x500.X500Principal", - "methods":[{"name":"","parameterTypes":["sun.security.x509.X500Name"] }]} -, + "methods":[{"name":"","parameterTypes":["sun.security.x509.X500Name"] }] +}, { "name":"long", "allDeclaredMethods":true, - "allPublicMethods":true} -, + "allPublicMethods":true +}, { - "name":"long[]"} -, + "name":"long[]" +}, { "name":"org.asamk.Signal", "allDeclaredMethods":true, - "allDeclaredClasses":true} -, + "allDeclaredClasses":true +}, { "name":"org.asamk.Signal$Configuration", "allDeclaredClasses":true, - "queryAllDeclaredMethods":true} -, + "queryAllDeclaredMethods":true +}, { "name":"org.asamk.Signal$Device", "allDeclaredMethods":true, - "allDeclaredClasses":true} -, + "allDeclaredClasses":true +}, { "name":"org.asamk.Signal$Error$Failure", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.asamk.Signal$Error$UntrustedIdentity", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.asamk.Signal$Group", "allDeclaredMethods":true, - "allDeclaredClasses":true} -, + "allDeclaredClasses":true +}, { "name":"org.asamk.Signal$MessageReceived", "allDeclaredConstructors":true, - "allPublicConstructors":true} -, + "allPublicConstructors":true +}, { "name":"org.asamk.Signal$MessageReceivedV2", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, - "methods":[{"name":"","parameterTypes":["java.lang.String","long","java.lang.String","byte[]","java.lang.String","java.util.Map"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String","long","java.lang.String","byte[]","java.lang.String","java.util.Map"] }] +}, { "name":"org.asamk.Signal$ReceiptReceived", "allDeclaredConstructors":true, - "allPublicConstructors":true} -, + "allPublicConstructors":true +}, { "name":"org.asamk.Signal$ReceiptReceivedV2", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, - "methods":[{"name":"","parameterTypes":["java.lang.String","long","java.lang.String","java.lang.String","java.util.Map"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String","long","java.lang.String","java.lang.String","java.util.Map"] }] +}, { "name":"org.asamk.Signal$StructDevice", "allDeclaredFields":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":["org.freedesktop.dbus.DBusPath","java.lang.Long","java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["org.freedesktop.dbus.DBusPath","java.lang.Long","java.lang.String"] }] +}, { "name":"org.asamk.Signal$StructGroup", - "allDeclaredFields":true} -, + "allDeclaredFields":true +}, { "name":"org.asamk.Signal$SyncMessageReceived", "allDeclaredConstructors":true, - "allPublicConstructors":true} -, + "allPublicConstructors":true +}, { "name":"org.asamk.Signal$SyncMessageReceivedV2", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, - "methods":[{"name":"","parameterTypes":["java.lang.String","long","java.lang.String","java.lang.String","byte[]","java.lang.String","java.util.Map"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String","long","java.lang.String","java.lang.String","byte[]","java.lang.String","java.util.Map"] }] +}, { "name":"org.asamk.SignalControl", "allDeclaredMethods":true, - "allDeclaredClasses":true} -, + "allDeclaredClasses":true +}, { "name":"org.asamk.SignalControl$Error$Failure", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.asamk.signal.commands.FinishLinkCommand$FinishLinkParams", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] +}, { "name":"org.asamk.signal.commands.FinishLinkCommand$JsonFinishLink", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"number","parameterTypes":[] }]} -, + "methods":[{"name":"number","parameterTypes":[] }] +}, { "name":"org.asamk.signal.commands.GetUserStatusCommand$JsonUserStatus", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.commands.ListAccountsCommand$JsonAccount", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"number","parameterTypes":[] }]} -, + "methods":[{"name":"number","parameterTypes":[] }] +}, { "name":"org.asamk.signal.commands.ListContactsCommand$JsonContact", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.commands.ListDevicesCommand$JsonDevice", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.commands.ListGroupsCommand$JsonGroup", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.commands.ListGroupsCommand$JsonGroupMember", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.commands.ListIdentitiesCommand$JsonIdentity", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.commands.ListStickerPacksCommand$JsonStickerPack", "allDeclaredFields":true, @@ -446,8 +446,8 @@ {"name":"stickers","parameterTypes":[] }, {"name":"title","parameterTypes":[] }, {"name":"url","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.commands.ListStickerPacksCommand$JsonStickerPack$JsonSticker", "allDeclaredFields":true, @@ -457,41 +457,41 @@ {"name":"contentType","parameterTypes":[] }, {"name":"emoji","parameterTypes":[] }, {"name":"id","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.commands.RegisterCommand$RegistrationParams", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.String"] }] +}, { "name":"org.asamk.signal.commands.StartLinkCommand$JsonLink", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"deviceLinkUri","parameterTypes":[] }]} -, + "methods":[{"name":"deviceLinkUri","parameterTypes":[] }] +}, { "name":"org.asamk.signal.commands.VerifyCommand$VerifyParams", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] +}, { "name":"org.asamk.signal.json.JsonAttachment", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonCallMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonCallMessage$Answer", "allDeclaredFields":true, @@ -501,15 +501,15 @@ {"name":"id","parameterTypes":[] }, {"name":"opaque","parameterTypes":[] }, {"name":"sdp","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.json.JsonCallMessage$Busy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"id","parameterTypes":[] }]} -, + "methods":[{"name":"id","parameterTypes":[] }] +}, { "name":"org.asamk.signal.json.JsonCallMessage$Hangup", "allDeclaredFields":true, @@ -520,8 +520,8 @@ {"name":"id","parameterTypes":[] }, {"name":"isLegacy","parameterTypes":[] }, {"name":"type","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.json.JsonCallMessage$IceUpdate", "allDeclaredFields":true, @@ -531,8 +531,8 @@ {"name":"id","parameterTypes":[] }, {"name":"opaque","parameterTypes":[] }, {"name":"sdp","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.json.JsonCallMessage$Offer", "allDeclaredFields":true, @@ -543,68 +543,68 @@ {"name":"opaque","parameterTypes":[] }, {"name":"sdp","parameterTypes":[] }, {"name":"type","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.json.JsonContactAddress", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonContactAvatar", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonContactEmail", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonContactName", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonContactPhone", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonDataMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonError", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonGroupInfo", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonMention", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonMessageEnvelope", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonPayment", "allDeclaredFields":true, @@ -613,32 +613,32 @@ "methods":[ {"name":"note","parameterTypes":[] }, {"name":"receipt","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.json.JsonQuote", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonQuotedAttachment", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonReaction", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonReceiptMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonRecipientAddress", "allDeclaredFields":true, @@ -647,14 +647,14 @@ "methods":[ {"name":"number","parameterTypes":[] }, {"name":"uuid","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.json.JsonRemoteDelete", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonSendMessageResult", "allDeclaredFields":true, @@ -666,90 +666,90 @@ {"name":"retryAfterSeconds","parameterTypes":[] }, {"name":"token","parameterTypes":[] }, {"name":"type","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.json.JsonSendMessageResult$Type", "allDeclaredFields":true, - "queryAllDeclaredMethods":true} -, + "queryAllDeclaredMethods":true +}, { "name":"org.asamk.signal.json.JsonSharedContact", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonSticker", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonSyncDataMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonSyncMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonSyncMessageType", "allDeclaredFields":true, - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"org.asamk.signal.json.JsonSyncReadMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonTypingMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.jsonrpc.JsonRpcBatchMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.jsonrpc.JsonRpcException", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.jsonrpc.JsonRpcMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.jsonrpc.JsonRpcRequest", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.jsonrpc.JsonRpcResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.jsonrpc.JsonRpcResponse$Error", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.JsonStickerPack", "allDeclaredFields":true, @@ -761,8 +761,8 @@ {"name":"cover","parameterTypes":[] }, {"name":"stickers","parameterTypes":[] }, {"name":"title","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.manager.JsonStickerPack$JsonSticker", "allDeclaredFields":true, @@ -770,18 +770,17 @@ "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":["java.lang.Integer","java.lang.String","java.lang.String","java.lang.String"] }, - {"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"contentType","parameterTypes":[] }, {"name":"emoji","parameterTypes":[] }, {"name":"file","parameterTypes":[] }, {"name":"id","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.manager.api.PhoneNumberSharingMode", "allDeclaredFields":true, - "queryAllDeclaredMethods":true} -, + "queryAllDeclaredMethods":true +}, { "name":"org.asamk.signal.manager.storage.configuration.ConfigurationStore$Storage", "allDeclaredFields":true, @@ -795,48 +794,48 @@ {"name":"readReceipts","parameterTypes":[] }, {"name":"typingIndicators","parameterTypes":[] }, {"name":"unidentifiedDeliveryIndicators","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.manager.storage.contacts.LegacyContactInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true} -, + "queryAllDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.contacts.LegacyJsonContactsStore", "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "fields":[{"name":"contacts", "allowWrite":true}]} -, + "fields":[{"name":"contacts", "allowWrite":true}] +}, { "name":"org.asamk.signal.manager.storage.groups.GroupInfo", "allDeclaredFields":true, - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"org.asamk.signal.manager.storage.groups.GroupInfoV1", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.groups.GroupStore$GroupsDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.asamk.signal.manager.storage.groups.GroupStore$Storage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.groups.GroupStore$Storage$GroupV1", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.groups.GroupStore$Storage$GroupV1$JsonRecipientAddress", "allDeclaredFields":true, @@ -846,596 +845,596 @@ {"name":"","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"number","parameterTypes":[] }, {"name":"uuid","parameterTypes":[] } - ]} -, + ] +}, { "name":"org.asamk.signal.manager.storage.groups.GroupStore$Storage$GroupV1$MembersDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.asamk.signal.manager.storage.groups.GroupStore$Storage$GroupV1$MembersSerializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.asamk.signal.manager.storage.groups.GroupStore$Storage$GroupV2", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.identities.IdentityKeyStore$IdentityStorage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.profiles.LegacyProfileStore", "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "fields":[{"name":"profiles", "allowWrite":true}]} -, + "fields":[{"name":"profiles", "allowWrite":true}] +}, { "name":"org.asamk.signal.manager.storage.profiles.LegacyProfileStore$ProfileStoreDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.asamk.signal.manager.storage.profiles.LegacySignalProfileEntry", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.profiles.ProfileStore", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.profiles.SignalProfile", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.profiles.SignalProfile$Capabilities", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.protocol.LegacyJsonIdentityKeyStore$JsonIdentityKeyStoreDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.asamk.signal.manager.storage.protocol.LegacyJsonPreKeyStore$JsonPreKeyStoreDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.asamk.signal.manager.storage.protocol.LegacyJsonSessionStore$JsonSessionStoreDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.asamk.signal.manager.storage.protocol.LegacyJsonSignalProtocolStore", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.protocol.LegacyJsonSignedPreKeyStore$JsonSignedPreKeyStoreDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.asamk.signal.manager.storage.recipients.LegacyRecipientStore", "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "fields":[{"name":"addresses", "allowWrite":true}]} -, + "fields":[{"name":"addresses", "allowWrite":true}] +}, { "name":"org.asamk.signal.manager.storage.recipients.LegacyRecipientStore$RecipientStoreDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.asamk.signal.manager.storage.recipients.RecipientAddress", "allDeclaredFields":true, "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true} -, + "queryAllDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.recipients.RecipientStore$Storage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.recipients.RecipientStore$Storage$Recipient", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.recipients.RecipientStore$Storage$Recipient$Contact", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.recipients.RecipientStore$Storage$Recipient$Profile", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.senderKeys.SenderKeySharedStore$Storage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.senderKeys.SenderKeySharedStore$Storage$SharedSenderKey", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.stickers.StickerStore", "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "fields":[{"name":"stickers", "allowWrite":true}]} -, + "fields":[{"name":"stickers", "allowWrite":true}] +}, { "name":"org.asamk.signal.manager.storage.stickers.StickerStore$Storage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.stickers.StickerStore$Storage$Sticker", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.util.SecurityProvider$DefaultRandom", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.DH$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.DSA$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.DSTU4145$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.EC$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.ECGOST$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.EdEC$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.ElGamal$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.GM$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.GOST$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.IES$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.X509$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.edec.SignatureSpi$Ed25519", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.edec.SignatureSpi$Ed448", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.Blake2b$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.Blake2s$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.DSTU7564$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.GOST3411$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.Haraka$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.Keccak$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.MD2$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.MD4$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.MD5$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD128$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD160$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD256$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD320$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.SHA1$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.SHA224$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.SHA256$Digest", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.SHA256$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.SHA3$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.SHA384$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.SHA512$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.SM3$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.Skein$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.Tiger$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.Whirlpool$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.keystore.BC$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.keystore.BCFKS$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.keystore.PKCS12$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi$Std", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.AES$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.ARC4$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.ARIA$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Blowfish$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.CAST5$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.CAST6$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Camellia$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.ChaCha$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.DES$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.DESede$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.DSTU7624$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.GOST28147$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.GOST3412_2015$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Grain128$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Grainv1$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.HC128$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Noekeon$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.OpenSSLPBKDF$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF1$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Poly1305$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.RC2$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.RC5$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.RC6$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Rijndael$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.SCRYPT$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.SEED$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.SM4$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Salsa20$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Serpent$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Shacal2$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.SipHash$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.SipHash128$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Skipjack$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.TEA$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.TLSKDF$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Threefish$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Twofish$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.VMPC$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.VMPCKSA3$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.XSalsa20$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.XTEA$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.symmetric.Zuc$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.pqc.jcajce.provider.LMS$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.pqc.jcajce.provider.McEliece$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.pqc.jcajce.provider.NH$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.pqc.jcajce.provider.QTESLA$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.pqc.jcajce.provider.Rainbow$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.pqc.jcajce.provider.SPHINCS$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.pqc.jcajce.provider.XMSS$Mappings", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.freedesktop.dbus.errors.ServiceUnknown", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.freedesktop.dbus.errors.UnknownMethod", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.freedesktop.dbus.errors.UnknownObject", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.freedesktop.dbus.interfaces.DBus$NameAcquired", - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.freedesktop.dbus.interfaces.Introspectable", "allDeclaredMethods":true, - "allDeclaredClasses":true} -, + "allDeclaredClasses":true +}, { "name":"org.freedesktop.dbus.interfaces.Peer", "allDeclaredMethods":true, - "allDeclaredClasses":true} -, + "allDeclaredClasses":true +}, { "name":"org.freedesktop.dbus.interfaces.Properties", "allDeclaredMethods":true, - "allDeclaredClasses":true} -, + "allDeclaredClasses":true +}, { "name":"org.freedesktop.dbus.interfaces.Properties$PropertiesChanged", - "allPublicConstructors":true} -, + "allPublicConstructors":true +}, { "name":"org.signal.storageservice.protos.groups.AccessControl", "fields":[ {"name":"addFromInviteLink_"}, {"name":"attributes_"}, {"name":"members_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.AvatarUploadAttributes", "fields":[ @@ -1446,8 +1445,8 @@ {"name":"key_"}, {"name":"policy_"}, {"name":"signature_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.Group", "fields":[ @@ -1463,23 +1462,23 @@ {"name":"requestingMembers_"}, {"name":"revision_"}, {"name":"title_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.GroupAttributeBlob", "fields":[ {"name":"contentCase_"}, {"name":"content_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange", "fields":[ {"name":"actions_"}, {"name":"changeEpoch_"}, {"name":"serverSignature_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions", "fields":[ @@ -1504,107 +1503,107 @@ {"name":"promoteRequestingMembers_"}, {"name":"revision_"}, {"name":"sourceUuid_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddMemberAction", "fields":[ {"name":"added_"}, {"name":"joinFromInviteLink_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddPendingMemberAction", - "fields":[{"name":"added_"}]} -, + "fields":[{"name":"added_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddRequestingMemberAction", - "fields":[{"name":"added_"}]} -, + "fields":[{"name":"added_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteMemberAction", - "fields":[{"name":"deletedUserId_"}]} -, + "fields":[{"name":"deletedUserId_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeletePendingMemberAction", - "fields":[{"name":"deletedUserId_"}]} -, + "fields":[{"name":"deletedUserId_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteRequestingMemberAction", - "fields":[{"name":"deletedUserId_"}]} -, + "fields":[{"name":"deletedUserId_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyAddFromInviteLinkAccessControlAction", - "fields":[{"name":"addFromInviteLinkAccess_"}]} -, + "fields":[{"name":"addFromInviteLinkAccess_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyAnnouncementsOnlyAction", - "fields":[{"name":"announcementsOnly_"}]} -, + "fields":[{"name":"announcementsOnly_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyAttributesAccessControlAction", - "fields":[{"name":"attributesAccess_"}]} -, + "fields":[{"name":"attributesAccess_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyAvatarAction", - "fields":[{"name":"avatar_"}]} -, + "fields":[{"name":"avatar_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyDescriptionAction", - "fields":[{"name":"description_"}]} -, + "fields":[{"name":"description_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyDisappearingMessagesTimerAction", - "fields":[{"name":"timer_"}]} -, + "fields":[{"name":"timer_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyInviteLinkPasswordAction", - "fields":[{"name":"inviteLinkPassword_"}]} -, + "fields":[{"name":"inviteLinkPassword_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberProfileKeyAction", - "fields":[{"name":"presentation_"}]} -, + "fields":[{"name":"presentation_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberRoleAction", "fields":[ {"name":"role_"}, {"name":"userId_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMembersAccessControlAction", - "fields":[{"name":"membersAccess_"}]} -, + "fields":[{"name":"membersAccess_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyTitleAction", - "fields":[{"name":"title_"}]} -, + "fields":[{"name":"title_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromotePendingMemberAction", - "fields":[{"name":"presentation_"}]} -, + "fields":[{"name":"presentation_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromoteRequestingMemberAction", "fields":[ {"name":"role_"}, {"name":"userId_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.GroupInviteLink", "fields":[ {"name":"contentsCase_"}, {"name":"contents_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.GroupInviteLink$GroupInviteLinkContentsV1", "fields":[ {"name":"groupMasterKey_"}, {"name":"inviteLinkPassword_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.Member", "fields":[ @@ -1613,16 +1612,16 @@ {"name":"profileKey_"}, {"name":"role_"}, {"name":"userId_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.PendingMember", "fields":[ {"name":"addedByUserId_"}, {"name":"member_"}, {"name":"timestamp_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.RequestingMember", "fields":[ @@ -1630,15 +1629,15 @@ {"name":"profileKey_"}, {"name":"timestamp_"}, {"name":"userId_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedApproveMember", "fields":[ {"name":"role_"}, {"name":"uuid_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedGroup", "fields":[ @@ -1653,8 +1652,8 @@ {"name":"requestingMembers_"}, {"name":"revision_"}, {"name":"title_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedGroupChange", "fields":[ @@ -1679,8 +1678,8 @@ {"name":"promotePendingMembers_"}, {"name":"promoteRequestingMembers_"}, {"name":"revision_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedMember", "fields":[ @@ -1688,15 +1687,15 @@ {"name":"profileKey_"}, {"name":"role_"}, {"name":"uuid_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedModifyMemberRole", "fields":[ {"name":"role_"}, {"name":"uuid_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedPendingMember", "fields":[ @@ -1705,244 +1704,244 @@ {"name":"timestamp_"}, {"name":"uuidCipherText_"}, {"name":"uuid_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedPendingMemberRemoval", "fields":[ {"name":"uuidCipherText_"}, {"name":"uuid_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedRequestingMember", "fields":[ {"name":"profileKey_"}, {"name":"timestamp_"}, {"name":"uuid_"} - ]} -, + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedString", - "fields":[{"name":"value_"}]} -, + "fields":[{"name":"value_"}] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedTimer", - "fields":[{"name":"duration_"}]} -, + "fields":[{"name":"duration_"}] +}, { "name":"org.signal.zkgroup.internal.ByteArray", "allDeclaredFields":true, - "queryAllDeclaredMethods":true} -, + "queryAllDeclaredMethods":true +}, { "name":"org.signal.zkgroup.profiles.ProfileKey", "allDeclaredFields":true, "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true} -, + "queryAllDeclaredConstructors":true +}, { "name":"org.signal.zkgroup.profiles.ProfileKeyCredential", "allDeclaredFields":true, "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true} -, + "queryAllDeclaredConstructors":true +}, { "name":"org.whispersystems.libsignal.state.IdentityKeyStore", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"org.whispersystems.libsignal.state.PreKeyStore", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"org.whispersystems.libsignal.state.SessionStore", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"org.whispersystems.libsignal.state.SignalProtocolStore", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"org.whispersystems.libsignal.state.SignedPreKeyStore", - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"org.whispersystems.signalservice.api.account.AccountAttributes", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.account.AccountAttributes$Capabilities", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.groupsv2.CredentialResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.groupsv2.TemporalCredential", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { - "name":"org.whispersystems.signalservice.api.groupsv2.TemporalCredential[]"} -, + "name":"org.whispersystems.signalservice.api.groupsv2.TemporalCredential[]" +}, { "name":"org.whispersystems.signalservice.api.messages.calls.HangupMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.messages.calls.HangupMessage$Type", "allDeclaredFields":true, - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.messages.calls.OfferMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.messages.calls.OfferMessage$Type", "allDeclaredFields":true, - "allDeclaredMethods":true} -, + "allDeclaredMethods":true +}, { "name":"org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.profiles.SignalServiceProfile", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.profiles.SignalServiceProfile$Badge", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.profiles.SignalServiceProfile$Capabilities", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.profiles.SignalServiceProfileWrite", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.push.ACI", "allDeclaredFields":true, "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true} -, + "queryAllDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.push.AccountIdentifier", "allDeclaredFields":true, - "queryAllDeclaredMethods":true} -, + "queryAllDeclaredMethods":true +}, { "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity$ByteArrayDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity$ByteArraySerializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.api.storage.StorageAuthResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.crypto.SignatureBodyEntity", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.DiscoveryRequest", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.DiscoveryResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.KeyBackupRequest", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.KeyBackupResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.MultiRemoteAttestationResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.QueryEnvelope", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationRequest", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.TokenResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.devices.DeviceNameProtos$DeviceName", "fields":[ @@ -1950,8 +1949,8 @@ {"name":"ciphertext_"}, {"name":"ephemeralPublic_"}, {"name":"syntheticIv_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.BackupRequest", "fields":[ @@ -1963,24 +1962,24 @@ {"name":"token_"}, {"name":"tries_"}, {"name":"validFrom_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.BackupResponse", "fields":[ {"name":"bitField0_"}, {"name":"status_"}, {"name":"token_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.DeleteRequest", "fields":[ {"name":"backupId_"}, {"name":"bitField0_"}, {"name":"serviceId_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.Request", "fields":[ @@ -1988,8 +1987,8 @@ {"name":"bitField0_"}, {"name":"delete_"}, {"name":"restore_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.Response", "fields":[ @@ -1997,8 +1996,8 @@ {"name":"bitField0_"}, {"name":"delete_"}, {"name":"restore_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.RestoreRequest", "fields":[ @@ -2008,8 +2007,8 @@ {"name":"serviceId_"}, {"name":"token_"}, {"name":"validFrom_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.RestoreResponse", "fields":[ @@ -2018,134 +2017,134 @@ {"name":"status_"}, {"name":"token_"}, {"name":"tries_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.AttachmentV2UploadAttributes", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.AuthCredentials", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.ConfirmCodeMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.DeviceCode", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.DeviceId", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.DeviceInfoList", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.GroupMismatchedDevices", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.push.GroupStaleDevices", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.push.MismatchedDevices", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.OutgoingPushMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.OutgoingPushMessageList", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.PreKeyEntity", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.PreKeyEntity$ECPublicKeyDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.push.PreKeyEntity$ECPublicKeySerializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.push.PreKeyResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.PreKeyResponseItem", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.PreKeyState", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.PreKeyStatus", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.ProfileAvatarUploadAttributes", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.ProvisioningMessage", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisionEnvelope", "fields":[ {"name":"bitField0_"}, {"name":"body_"}, {"name":"publicKey_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisionMessage", "fields":[ @@ -2159,50 +2158,50 @@ {"name":"readReceipts_"}, {"name":"userAgent_"}, {"name":"uuid_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisioningUuid", "fields":[ {"name":"bitField0_"}, {"name":"uuid_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.PushServiceSocket$RegistrationLockFailure", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.PushServiceSocket$RegistrationLockV2", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.SendGroupMessageResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.push.SendMessageResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.SenderCertificate", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.SenderCertificate$ByteArrayDesieralizer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$AttachmentPointer", "fields":[ @@ -2222,8 +2221,8 @@ {"name":"thumbnail_"}, {"name":"uploadTimestamp_"}, {"name":"width_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage", "fields":[ @@ -2237,8 +2236,8 @@ {"name":"multiRing_"}, {"name":"offer_"}, {"name":"opaque_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$Answer", "fields":[ @@ -2246,8 +2245,8 @@ {"name":"id_"}, {"name":"opaque_"}, {"name":"sdp_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$Hangup", "fields":[ @@ -2255,8 +2254,8 @@ {"name":"deviceId_"}, {"name":"id_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$IceUpdate", "fields":[ @@ -2266,8 +2265,8 @@ {"name":"mid_"}, {"name":"opaque_"}, {"name":"sdp_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$Offer", "fields":[ @@ -2276,16 +2275,16 @@ {"name":"opaque_"}, {"name":"sdp_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$Opaque", "fields":[ {"name":"bitField0_"}, {"name":"data_"}, {"name":"urgency_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ContactDetails", "fields":[ @@ -2301,16 +2300,16 @@ {"name":"profileKey_"}, {"name":"uuid_"}, {"name":"verified_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ContactDetails$Avatar", "fields":[ {"name":"bitField0_"}, {"name":"contentType_"}, {"name":"length_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Content", "fields":[ @@ -2323,8 +2322,8 @@ {"name":"senderKeyDistributionMessage_"}, {"name":"syncMessage_"}, {"name":"typingMessage_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage", "fields":[ @@ -2349,8 +2348,8 @@ {"name":"sticker_"}, {"name":"storyContext_"}, {"name":"timestamp_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$BodyRange", "fields":[ @@ -2359,8 +2358,8 @@ {"name":"bitField0_"}, {"name":"length_"}, {"name":"start_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact", "fields":[ @@ -2371,16 +2370,16 @@ {"name":"name_"}, {"name":"number_"}, {"name":"organization_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Avatar", "fields":[ {"name":"avatar_"}, {"name":"bitField0_"}, {"name":"isProfile_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Email", "fields":[ @@ -2388,8 +2387,8 @@ {"name":"label_"}, {"name":"type_"}, {"name":"value_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Name", "fields":[ @@ -2400,8 +2399,8 @@ {"name":"middleName_"}, {"name":"prefix_"}, {"name":"suffix_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Phone", "fields":[ @@ -2409,8 +2408,8 @@ {"name":"label_"}, {"name":"type_"}, {"name":"value_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$PostalAddress", "fields":[ @@ -2424,29 +2423,29 @@ {"name":"region_"}, {"name":"street_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Delete", "fields":[ {"name":"bitField0_"}, {"name":"targetSentTimestamp_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$GroupCallUpdate", "fields":[ {"name":"bitField0_"}, {"name":"eraId_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Payment", "fields":[ {"name":"itemCase_"}, {"name":"item_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Payment$Notification", "fields":[ @@ -2454,15 +2453,15 @@ {"name":"note_"}, {"name":"transactionCase_"}, {"name":"transaction_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Payment$Notification$MobileCoin", "fields":[ {"name":"bitField0_"}, {"name":"receipt_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Preview", "fields":[ @@ -2472,8 +2471,8 @@ {"name":"image_"}, {"name":"title_"}, {"name":"url_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Quote", "fields":[ @@ -2484,8 +2483,8 @@ {"name":"bodyRanges_"}, {"name":"id_"}, {"name":"text_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Quote$QuotedAttachment", "fields":[ @@ -2493,8 +2492,8 @@ {"name":"contentType_"}, {"name":"fileName_"}, {"name":"thumbnail_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Reaction", "fields":[ @@ -2503,8 +2502,8 @@ {"name":"remove_"}, {"name":"targetAuthorUuid_"}, {"name":"targetSentTimestamp_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Sticker", "fields":[ @@ -2514,8 +2513,8 @@ {"name":"packId_"}, {"name":"packKey_"}, {"name":"stickerId_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Envelope", "fields":[ @@ -2530,8 +2529,8 @@ {"name":"sourceUuid_"}, {"name":"timestamp_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$GroupContext", "fields":[ @@ -2542,15 +2541,15 @@ {"name":"members_"}, {"name":"name_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$GroupContext$Member", "fields":[ {"name":"bitField0_"}, {"name":"e164_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$GroupContextV2", "fields":[ @@ -2558,8 +2557,8 @@ {"name":"groupChange_"}, {"name":"masterKey_"}, {"name":"revision_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$GroupDetails", "fields":[ @@ -2575,38 +2574,38 @@ {"name":"membersE164_"}, {"name":"members_"}, {"name":"name_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$GroupDetails$Avatar", "fields":[ {"name":"bitField0_"}, {"name":"contentType_"}, {"name":"length_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$GroupDetails$Member", "fields":[ {"name":"bitField0_"}, {"name":"e164_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$NullMessage", "fields":[ {"name":"bitField0_"}, {"name":"padding_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ReceiptMessage", "fields":[ {"name":"bitField0_"}, {"name":"timestamp_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage", "fields":[ @@ -2627,16 +2626,16 @@ {"name":"verified_"}, {"name":"viewOnceOpen_"}, {"name":"viewed_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Blocked", "fields":[ {"name":"groupIds_"}, {"name":"numbers_"}, {"name":"uuids_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Configuration", "fields":[ @@ -2646,37 +2645,37 @@ {"name":"readReceipts_"}, {"name":"typingIndicators_"}, {"name":"unidentifiedDeliveryIndicators_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Contacts", "fields":[ {"name":"bitField0_"}, {"name":"blob_"}, {"name":"complete_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$FetchLatest", "fields":[ {"name":"bitField0_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Groups", "fields":[ {"name":"bitField0_"}, {"name":"blob_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Keys", "fields":[ {"name":"bitField0_"}, {"name":"storageService_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$MessageRequestResponse", "fields":[ @@ -2685,8 +2684,8 @@ {"name":"threadE164_"}, {"name":"threadUuid_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Read", "fields":[ @@ -2694,15 +2693,15 @@ {"name":"senderE164_"}, {"name":"senderUuid_"}, {"name":"timestamp_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Request", "fields":[ {"name":"bitField0_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent", "fields":[ @@ -2714,8 +2713,8 @@ {"name":"message_"}, {"name":"timestamp_"}, {"name":"unidentifiedStatus_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent$UnidentifiedDeliveryStatus", "fields":[ @@ -2723,8 +2722,8 @@ {"name":"destinationE164_"}, {"name":"destinationUuid_"}, {"name":"unidentified_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$StickerPackOperation", "fields":[ @@ -2732,8 +2731,8 @@ {"name":"packId_"}, {"name":"packKey_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$ViewOnceOpen", "fields":[ @@ -2741,8 +2740,8 @@ {"name":"senderE164_"}, {"name":"senderUuid_"}, {"name":"timestamp_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Viewed", "fields":[ @@ -2750,8 +2749,8 @@ {"name":"senderE164_"}, {"name":"senderUuid_"}, {"name":"timestamp_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TypingMessage", "fields":[ @@ -2759,8 +2758,8 @@ {"name":"bitField0_"}, {"name":"groupId_"}, {"name":"timestamp_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Verified", "fields":[ @@ -2770,29 +2769,29 @@ {"name":"identityKey_"}, {"name":"nullMessage_"}, {"name":"state_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.push.StaleDevices", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.push.VerifyAccountResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true} -, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.serialize.protos.AddressProto", "fields":[ {"name":"bitField0_"}, {"name":"e164_"}, {"name":"uuid_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.serialize.protos.MetadataProto", "fields":[ @@ -2805,8 +2804,8 @@ {"name":"serverGuid_"}, {"name":"serverReceivedTimestamp_"}, {"name":"timestamp_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto", "fields":[ @@ -2815,8 +2814,8 @@ {"name":"data_"}, {"name":"localAddress_"}, {"name":"metadata_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.sticker.StickerProtos$Pack", "fields":[ @@ -2825,8 +2824,8 @@ {"name":"cover_"}, {"name":"stickers_"}, {"name":"title_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.sticker.StickerProtos$Pack$Sticker", "fields":[ @@ -2834,101 +2833,101 @@ {"name":"contentType_"}, {"name":"emoji_"}, {"name":"id_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord", - "allDeclaredFields":true} -, + "allDeclaredFields":true +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$PinnedConversation", "fields":[ {"name":"identifierCase_"}, {"name":"identifier_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord$PinnedConversation$Contact", "fields":[ {"name":"e164_"}, {"name":"uuid_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord", - "allDeclaredFields":true} -, + "allDeclaredFields":true +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV1Record", - "allDeclaredFields":true} -, + "allDeclaredFields":true +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.GroupV2Record", - "allDeclaredFields":true} -, + "allDeclaredFields":true +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.ManifestRecord", "fields":[ {"name":"identifiers_"}, {"name":"version_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.ManifestRecord$Identifier", "fields":[ {"name":"raw_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.Payments", - "allDeclaredFields":true} -, + "allDeclaredFields":true +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.ReadOperation", - "fields":[{"name":"readKey_"}]} -, + "fields":[{"name":"readKey_"}] +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.StorageItem", "fields":[ {"name":"key_"}, {"name":"value_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.StorageItems", - "fields":[{"name":"items_"}]} -, + "fields":[{"name":"items_"}] +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.StorageManifest", "fields":[ {"name":"value_"}, {"name":"version_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.storage.protos.StorageRecord", "fields":[ {"name":"recordCase_"}, {"name":"record_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.util.JsonUtil$AciDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.util.JsonUtil$IdentityKeyDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.util.JsonUtil$IdentityKeySerializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.util.JsonUtil$UuidDeserializer", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.websocket.WebSocketProtos$WebSocketMessage", "fields":[ @@ -2936,8 +2935,8 @@ {"name":"request_"}, {"name":"response_"}, {"name":"type_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.websocket.WebSocketProtos$WebSocketRequestMessage", "fields":[ @@ -2947,8 +2946,8 @@ {"name":"id_"}, {"name":"path_"}, {"name":"verb_"} - ]} -, + ] +}, { "name":"org.whispersystems.signalservice.internal.websocket.WebSocketProtos$WebSocketResponseMessage", "fields":[ @@ -2958,8 +2957,8 @@ {"name":"id_"}, {"name":"message_"}, {"name":"status_"} - ]} -, + ] +}, { "name":"sun.misc.Unsafe", "allDeclaredFields":true, @@ -2999,110 +2998,110 @@ {"name":"putLong","parameterTypes":["java.lang.Object","long","long"] }, {"name":"putObject","parameterTypes":["java.lang.Object","long","java.lang.Object"] }, {"name":"putShort","parameterTypes":["long","short"] } - ]} -, + ] +}, { "name":"sun.security.provider.DSA$SHA224withDSA", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.JavaKeyStore$DualFormatJKS", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.JavaKeyStore$JKS", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.NativePRNG", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.NativePRNG$NonBlocking", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.SHA", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.SHA2$SHA224", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.SHA2$SHA256", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.SHA5$SHA384", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.SHA5$SHA512", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.SecureRandom", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.provider.certpath.PKIXCertPathValidator", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.rsa.PSSParameters", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.rsa.RSAKeyFactory$Legacy", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.rsa.RSAPSSSignature", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.rsa.RSASignature$SHA224withRSA", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.rsa.RSASignature$SHA256withRSA", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.rsa.RSASignature$SHA512withRSA", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.ssl.SSLContextImpl$TLSContext", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", - "methods":[{"name":"","parameterTypes":[] }]} -, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"sun.security.x509.AuthorityKeyIdentifierExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, { "name":"sun.security.x509.BasicConstraintsExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, { "name":"sun.security.x509.CRLDistributionPointsExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, { "name":"sun.security.x509.KeyUsageExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, { "name":"sun.security.x509.SubjectAlternativeNameExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]} -, + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, { "name":"sun.security.x509.SubjectKeyIdentifierExtension", - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]} - + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +} ] diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index e2f9db0e..8a486340 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -192,6 +192,10 @@ } ]}, "bundles":[{ - "name":"net.sourceforge.argparse4j.internal.ArgumentParserImpl" + "name":"net.sourceforge.argparse4j.internal.ArgumentParserImpl", + "locales":[ + "", + "und" + ] }] } From 67146f9cc7a32466efff8c1170d5ac4590987890 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 26 Jan 2022 21:03:04 +0100 Subject: [PATCH 004/938] Create stores in SignalAccount lazily --- .../signal/manager/storage/SignalAccount.java | 215 ++++++++++-------- .../storage/recipients/RecipientStore.java | 5 +- .../senderKeys/SenderKeySharedStore.java | 5 +- .../storage/senderKeys/SenderKeyStore.java | 3 +- 4 files changed, 134 insertions(+), 94 deletions(-) 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 eac16e84..f23aea03 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 @@ -66,6 +66,7 @@ import java.util.Base64; import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.function.Supplier; public class SignalAccount implements Closeable { @@ -74,13 +75,16 @@ public class SignalAccount implements Closeable { private static final int MINIMUM_STORAGE_VERSION = 1; private static final int CURRENT_STORAGE_VERSION = 3; - private int previousStorageVersion; + private final Object LOCK = new Object(); private final ObjectMapper jsonProcessor = Utils.createStorageObjectMapper(); private final FileChannel fileChannel; private final FileLock lock; + private int previousStorageVersion; + + private File dataPath; private String account; private ACI aci; private String encryptedDeviceName; @@ -94,6 +98,9 @@ public class SignalAccount implements Closeable { private ProfileKey profileKey; private int preKeyIdOffset; private int nextSignedPreKeyId; + private IdentityKeyPair identityKeyPair; + private int localRegistrationId; + private TrustNewIdentity trustNewIdentity; private long lastReceiveTimestamp = 0; private boolean registered = false; @@ -165,9 +172,12 @@ public class SignalAccount implements Closeable { signalAccount.account = account; signalAccount.profileKey = profileKey; - signalAccount.initStores(dataPath, identityKey, registrationId, trustNewIdentity); + signalAccount.dataPath = dataPath; + signalAccount.identityKeyPair = identityKey; + signalAccount.localRegistrationId = registrationId; + signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account), - signalAccount.recipientStore, + signalAccount.getRecipientStore(), signalAccount::saveGroupStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); @@ -181,36 +191,6 @@ public class SignalAccount implements Closeable { return signalAccount; } - private void initStores( - final File dataPath, - final IdentityKeyPair identityKey, - final int registrationId, - final TrustNewIdentity trustNewIdentity - ) throws IOException { - recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, account), this::mergeRecipients); - - preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, account)); - signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, account)); - sessionStore = new SessionStore(getSessionsPath(dataPath, account), recipientStore); - identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, account), - recipientStore, - identityKey, - registrationId, - trustNewIdentity); - senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, account), - getSenderKeysPath(dataPath, account), - recipientStore::resolveRecipientAddress, - recipientStore); - signalProtocolStore = new SignalProtocolStore(preKeyStore, - signedPreKeyStore, - sessionStore, - identityKeyStore, - senderKeyStore, - this::isMultiDevice); - - messageCache = new MessageCache(getMessageCachePath(dataPath, account)); - } - public static SignalAccount createOrUpdateLinkedAccount( File dataPath, String account, @@ -240,9 +220,9 @@ public class SignalAccount implements Closeable { final var signalAccount = load(dataPath, account, true, trustNewIdentity); signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey); - signalAccount.recipientStore.resolveRecipientTrusted(signalAccount.getSelfAddress()); - signalAccount.sessionStore.archiveAllSessions(); - signalAccount.senderKeyStore.deleteAll(); + signalAccount.getRecipientStore().resolveRecipientTrusted(signalAccount.getSelfAddress()); + signalAccount.getSessionStore().archiveAllSessions(); + signalAccount.getSenderKeyStore().deleteAll(); signalAccount.clearAllPreKeys(); return signalAccount; } @@ -250,8 +230,8 @@ public class SignalAccount implements Closeable { private void clearAllPreKeys() { this.preKeyIdOffset = new SecureRandom().nextInt(Medium.MAX_VALUE); this.nextSignedPreKeyId = new SecureRandom().nextInt(Medium.MAX_VALUE); - this.preKeyStore.removeAllPreKeys(); - this.signedPreKeyStore.removeAllSignedPreKeys(); + this.getPreKeyStore().removeAllPreKeys(); + this.getSignedPreKeyStore().removeAllSignedPreKeys(); save(); } @@ -275,14 +255,17 @@ public class SignalAccount implements Closeable { signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey); - signalAccount.initStores(dataPath, identityKey, registrationId, trustNewIdentity); + signalAccount.dataPath = dataPath; + signalAccount.identityKeyPair = identityKey; + signalAccount.localRegistrationId = registrationId; + signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account), - signalAccount.recipientStore, + signalAccount.getRecipientStore(), signalAccount::saveGroupStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); - signalAccount.recipientStore.resolveRecipientTrusted(signalAccount.getSelfAddress()); + signalAccount.getRecipientStore().resolveRecipientTrusted(signalAccount.getSelfAddress()); signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION; signalAccount.migrateLegacyConfigs(); signalAccount.save(); @@ -335,19 +318,19 @@ public class SignalAccount implements Closeable { } private void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) { - sessionStore.mergeRecipients(recipientId, toBeMergedRecipientId); - identityKeyStore.mergeRecipients(recipientId, toBeMergedRecipientId); - messageCache.mergeRecipients(recipientId, toBeMergedRecipientId); - groupStore.mergeRecipients(recipientId, toBeMergedRecipientId); - senderKeyStore.mergeRecipients(recipientId, toBeMergedRecipientId); + getSessionStore().mergeRecipients(recipientId, toBeMergedRecipientId); + getIdentityKeyStore().mergeRecipients(recipientId, toBeMergedRecipientId); + getMessageCache().mergeRecipients(recipientId, toBeMergedRecipientId); + getGroupStore().mergeRecipients(recipientId, toBeMergedRecipientId); + getSenderKeyStore().mergeRecipients(recipientId, toBeMergedRecipientId); } public void removeRecipient(final RecipientId recipientId) { - sessionStore.deleteAllSessions(recipientId); - identityKeyStore.deleteIdentity(recipientId); - messageCache.deleteMessages(recipientId); - senderKeyStore.deleteAll(recipientId); - recipientStore.deleteRecipientData(recipientId); + getSessionStore().deleteAllSessions(recipientId); + getIdentityKeyStore().deleteIdentity(recipientId); + getMessageCache().deleteMessages(recipientId); + getSenderKeyStore().deleteAll(recipientId); + getRecipientStore().deleteRecipientData(recipientId); } public static File getFileName(File dataPath, String account) { @@ -505,7 +488,10 @@ public class SignalAccount implements Closeable { migratedLegacyConfig = true; } - initStores(dataPath, identityKeyPair, registrationId, trustNewIdentity); + this.dataPath = dataPath; + this.identityKeyPair = identityKeyPair; + this.localRegistrationId = registrationId; + this.trustNewIdentity = trustNewIdentity; migratedLegacyConfig = loadLegacyStores(rootNode, legacySignalProtocolStore) || migratedLegacyConfig; @@ -513,10 +499,12 @@ public class SignalAccount implements Closeable { groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class); groupStore = GroupStore.fromStorage(groupStoreStorage, getGroupCachePath(dataPath, account), - recipientStore, + getRecipientStore(), this::saveGroupStore); } else { - groupStore = new GroupStore(getGroupCachePath(dataPath, account), recipientStore, this::saveGroupStore); + groupStore = new GroupStore(getGroupCachePath(dataPath, account), + getRecipientStore(), + this::saveGroupStore); } if (rootNode.hasNonNull("stickerStore")) { @@ -551,7 +539,7 @@ public class SignalAccount implements Closeable { logger.debug("Migrating legacy recipient store."); var legacyRecipientStore = jsonProcessor.convertValue(legacyRecipientStoreNode, LegacyRecipientStore.class); if (legacyRecipientStore != null) { - recipientStore.resolveRecipientsTrusted(legacyRecipientStore.getAddresses()); + getRecipientStore().resolveRecipientsTrusted(legacyRecipientStore.getAddresses()); } getSelfRecipientId(); migrated = true; @@ -561,7 +549,7 @@ public class SignalAccount implements Closeable { logger.debug("Migrating legacy pre key store."); for (var entry : legacySignalProtocolStore.getLegacyPreKeyStore().getPreKeys().entrySet()) { try { - preKeyStore.storePreKey(entry.getKey(), new PreKeyRecord(entry.getValue())); + getPreKeyStore().storePreKey(entry.getKey(), new PreKeyRecord(entry.getValue())); } catch (IOException e) { logger.warn("Failed to migrate pre key, ignoring", e); } @@ -573,7 +561,7 @@ public class SignalAccount implements Closeable { logger.debug("Migrating legacy signed pre key store."); for (var entry : legacySignalProtocolStore.getLegacySignedPreKeyStore().getSignedPreKeys().entrySet()) { try { - signedPreKeyStore.storeSignedPreKey(entry.getKey(), new SignedPreKeyRecord(entry.getValue())); + getSignedPreKeyStore().storeSignedPreKey(entry.getKey(), new SignedPreKeyRecord(entry.getValue())); } catch (IOException e) { logger.warn("Failed to migrate signed pre key, ignoring", e); } @@ -585,7 +573,7 @@ public class SignalAccount implements Closeable { logger.debug("Migrating legacy session store."); for (var session : legacySignalProtocolStore.getLegacySessionStore().getSessions()) { try { - sessionStore.storeSession(new SignalProtocolAddress(session.address.getIdentifier(), + getSessionStore().storeSession(new SignalProtocolAddress(session.address.getIdentifier(), session.deviceId), new SessionRecord(session.sessionRecord)); } catch (Exception e) { logger.warn("Failed to migrate session, ignoring", e); @@ -597,9 +585,9 @@ public class SignalAccount implements Closeable { if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) { logger.debug("Migrating legacy identity session store."); for (var identity : legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentities()) { - RecipientId recipientId = recipientStore.resolveRecipientTrusted(identity.getAddress()); - identityKeyStore.saveIdentity(recipientId, identity.getIdentityKey(), identity.getDateAdded()); - identityKeyStore.setIdentityTrustLevel(recipientId, + RecipientId recipientId = getRecipientStore().resolveRecipientTrusted(identity.getAddress()); + getIdentityKeyStore().saveIdentity(recipientId, identity.getIdentityKey(), identity.getDateAdded()); + getIdentityKeyStore().setIdentityTrustLevel(recipientId, identity.getIdentityKey(), identity.getTrustLevel()); } @@ -611,8 +599,8 @@ public class SignalAccount implements Closeable { final var contactStoreNode = rootNode.get("contactStore"); final var contactStore = jsonProcessor.convertValue(contactStoreNode, LegacyJsonContactsStore.class); for (var contact : contactStore.getContacts()) { - final var recipientId = recipientStore.resolveRecipientTrusted(contact.getAddress()); - recipientStore.storeContact(recipientId, + final var recipientId = getRecipientStore().resolveRecipientTrusted(contact.getAddress()); + getRecipientStore().storeContact(recipientId, new Contact(contact.name, contact.color, contact.messageExpirationTime, @@ -639,9 +627,9 @@ public class SignalAccount implements Closeable { var profileStoreNode = rootNode.get("profileStore"); final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class); for (var profileEntry : legacyProfileStore.getProfileEntries()) { - var recipientId = recipientStore.resolveRecipient(profileEntry.getAddress()); - recipientStore.storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); - recipientStore.storeProfileKey(recipientId, profileEntry.getProfileKey()); + var recipientId = getRecipientStore().resolveRecipient(profileEntry.getAddress()); + getRecipientStore().storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); + getRecipientStore().storeProfileKey(recipientId, profileEntry.getProfileKey()); final var profile = profileEntry.getProfile(); if (profile != null) { final var capabilities = new HashSet(); @@ -668,7 +656,7 @@ public class SignalAccount implements Closeable { ? Profile.UnidentifiedAccessMode.ENABLED : Profile.UnidentifiedAccessMode.DISABLED, capabilities); - recipientStore.storeProfile(recipientId, newProfile); + getRecipientStore().storeProfile(recipientId, newProfile); } } } @@ -687,10 +675,10 @@ public class SignalAccount implements Closeable { } try { if (UuidUtil.isUuid(thread.id) || thread.id.startsWith("+")) { - final var recipientId = recipientStore.resolveRecipient(thread.id); - var contact = recipientStore.getContact(recipientId); + final var recipientId = getRecipientStore().resolveRecipient(thread.id); + var contact = getRecipientStore().getContact(recipientId); if (contact != null) { - recipientStore.storeContact(recipientId, + getRecipientStore().storeContact(recipientId, Contact.newBuilder(contact) .withMessageExpirationTime(thread.messageExpirationTime) .build()); @@ -738,13 +726,10 @@ public class SignalAccount implements Closeable { .put("isMultiDevice", isMultiDevice) .put("lastReceiveTimestamp", lastReceiveTimestamp) .put("password", password) - .put("registrationId", identityKeyStore.getLocalRegistrationId()) + .put("registrationId", localRegistrationId) .put("identityPrivateKey", - Base64.getEncoder() - .encodeToString(identityKeyStore.getIdentityKeyPair().getPrivateKey().serialize())) - .put("identityKey", - Base64.getEncoder() - .encodeToString(identityKeyStore.getIdentityKeyPair().getPublicKey().serialize())) + Base64.getEncoder().encodeToString(identityKeyPair.getPrivateKey().serialize())) + .put("identityKey", Base64.getEncoder().encodeToString(identityKeyPair.getPublicKey().serialize())) .put("registrationLockPin", registrationLockPin) .put("pinMasterKey", pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize())) @@ -796,7 +781,7 @@ public class SignalAccount implements Closeable { logger.error("Invalid pre key id {}, expected {}", record.getId(), preKeyIdOffset); throw new AssertionError("Invalid pre key id"); } - preKeyStore.storePreKey(record.getId(), record); + getPreKeyStore().storePreKey(record.getId(), record); preKeyIdOffset = (preKeyIdOffset + 1) % Medium.MAX_VALUE; } save(); @@ -807,21 +792,42 @@ public class SignalAccount implements Closeable { logger.error("Invalid signed pre key id {}, expected {}", record.getId(), nextSignedPreKeyId); throw new AssertionError("Invalid signed pre key id"); } - signalProtocolStore.storeSignedPreKey(record.getId(), record); + getSignedPreKeyStore().storeSignedPreKey(record.getId(), record); nextSignedPreKeyId = (nextSignedPreKeyId + 1) % Medium.MAX_VALUE; save(); } public SignalProtocolStore getSignalProtocolStore() { - return signalProtocolStore; + return getOrCreate(() -> signalProtocolStore, + () -> signalProtocolStore = new SignalProtocolStore(getPreKeyStore(), + getSignedPreKeyStore(), + getSessionStore(), + getIdentityKeyStore(), + getSenderKeyStore(), + this::isMultiDevice)); + } + + private PreKeyStore getPreKeyStore() { + return getOrCreate(() -> preKeyStore, () -> preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, account))); + } + + private SignedPreKeyStore getSignedPreKeyStore() { + return getOrCreate(() -> signedPreKeyStore, + () -> signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, account))); } public SessionStore getSessionStore() { - return sessionStore; + return getOrCreate(() -> sessionStore, + () -> sessionStore = new SessionStore(getSessionsPath(dataPath, account), getRecipientStore())); } public IdentityKeyStore getIdentityKeyStore() { - return identityKeyStore; + return getOrCreate(() -> identityKeyStore, + () -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, account), + getRecipientStore(), + identityKeyPair, + localRegistrationId, + trustNewIdentity)); } public GroupStore getGroupStore() { @@ -829,15 +835,17 @@ public class SignalAccount implements Closeable { } public ContactsStore getContactStore() { - return recipientStore; + return getRecipientStore(); } public RecipientStore getRecipientStore() { - return recipientStore; + return getOrCreate(() -> recipientStore, + () -> recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, account), + this::mergeRecipients)); } public ProfileStore getProfileStore() { - return recipientStore; + return getRecipientStore(); } public StickerStore getStickerStore() { @@ -845,7 +853,11 @@ public class SignalAccount implements Closeable { } public SenderKeyStore getSenderKeyStore() { - return senderKeyStore; + return getOrCreate(() -> senderKeyStore, + () -> senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, account), + getSenderKeysPath(dataPath, account), + getRecipientStore()::resolveRecipientAddress, + getRecipientStore())); } public ConfigurationStore getConfigurationStore() { @@ -853,7 +865,8 @@ public class SignalAccount implements Closeable { } public MessageCache getMessageCache() { - return messageCache; + return getOrCreate(() -> messageCache, + () -> messageCache = new MessageCache(getMessageCachePath(dataPath, account))); } public String getAccount() { @@ -874,7 +887,8 @@ public class SignalAccount implements Closeable { } public RecipientId getSelfRecipientId() { - return recipientStore.resolveRecipientTrusted(new RecipientAddress(aci == null ? null : aci.uuid(), account)); + return getRecipientStore().resolveRecipientTrusted(new RecipientAddress(aci == null ? null : aci.uuid(), + account)); } public String getEncryptedDeviceName() { @@ -895,11 +909,11 @@ public class SignalAccount implements Closeable { } public IdentityKeyPair getIdentityKeyPair() { - return signalProtocolStore.getIdentityKeyPair(); + return identityKeyPair; } public int getLocalRegistrationId() { - return signalProtocolStore.getLocalRegistrationId(); + return localRegistrationId; } public String getPassword() { @@ -1026,7 +1040,7 @@ public class SignalAccount implements Closeable { clearAllPreKeys(); getSessionStore().archiveAllSessions(); - senderKeyStore.deleteAll(); + getSenderKeyStore().deleteAll(); final var recipientId = getRecipientStore().resolveRecipientTrusted(getSelfAddress()); final var publicKey = getIdentityKeyPair().getPublicKey(); getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date()); @@ -1047,4 +1061,25 @@ public class SignalAccount implements Closeable { } } } + + private T getOrCreate(Supplier supplier, Callable creator) { + var value = supplier.get(); + if (value != null) { + return value; + } + + synchronized (LOCK) { + value = supplier.get(); + if (value != null) { + return value; + } + creator.call(); + return supplier.get(); + } + } + + private interface Callable { + + void call(); + } } 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 6dd327e8..1fd0ae4c 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 @@ -49,7 +49,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile private long lastId; private boolean isBulkUpdating; - public static RecipientStore load(File file, RecipientMergeHandler recipientMergeHandler) throws IOException { + public static RecipientStore load(File file, RecipientMergeHandler recipientMergeHandler) { final var objectMapper = Utils.createStorageObjectMapper(); try (var inputStream = new FileInputStream(file)) { final var storage = objectMapper.readValue(inputStream, Storage.class); @@ -114,6 +114,9 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile } catch (FileNotFoundException e) { logger.trace("Creating new recipient store."); return new RecipientStore(objectMapper, file, recipientMergeHandler, new HashMap<>(), 0); + } catch (IOException e) { + logger.warn("Failed to load recipient store", e); + throw new RuntimeException(e); } } 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 11e8ed69..9cc6ba42 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 @@ -42,7 +42,7 @@ public class SenderKeySharedStore { public static SenderKeySharedStore load( final File file, final RecipientAddressResolver addressResolver, final RecipientResolver resolver - ) throws IOException { + ) { final var objectMapper = Utils.createStorageObjectMapper(); try (var inputStream = new FileInputStream(file)) { final var storage = objectMapper.readValue(inputStream, Storage.class); @@ -70,6 +70,9 @@ public class SenderKeySharedStore { } catch (FileNotFoundException e) { logger.trace("Creating new shared sender key store."); return new SenderKeySharedStore(new HashMap<>(), objectMapper, file, addressResolver, resolver); + } catch (IOException e) { + logger.warn("Failed to load shared sender key store", e); + throw new RuntimeException(e); } } 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 3f08c389..8674945c 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 @@ -9,7 +9,6 @@ import org.whispersystems.signalservice.api.SignalServiceSenderKeyStore; import org.whispersystems.signalservice.api.push.DistributionId; import java.io.File; -import java.io.IOException; import java.util.Collection; import java.util.Set; import java.util.UUID; @@ -24,7 +23,7 @@ public class SenderKeyStore implements SignalServiceSenderKeyStore { final File senderKeysPath, final RecipientAddressResolver addressResolver, final RecipientResolver resolver - ) throws IOException { + ) { this.senderKeyRecordStore = new SenderKeyRecordStore(senderKeysPath, resolver); this.senderKeySharedStore = SenderKeySharedStore.load(file, addressResolver, resolver); } From ede0dfeef45cc9fc1dbd952285eb82ad162a7224 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 26 Jan 2022 21:55:23 +0100 Subject: [PATCH 005/938] Fix output for envelope receipts --- .../java/org/asamk/signal/manager/api/MessageEnvelope.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 aa34a459..dde8e166 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 @@ -816,7 +816,9 @@ public record MessageEnvelope( .orNull()); call = Optional.ofNullable(content.getCallMessage().transform(Call::from).orNull()); } else { - receipt = Optional.empty(); + receipt = envelope.isReceipt() ? Optional.of(new Receipt(envelope.getServerReceivedTimestamp(), + Receipt.Type.DELIVERY, + List.of(envelope.getTimestamp()))) : Optional.empty(); typing = Optional.empty(); data = Optional.empty(); sync = Optional.empty(); From 238455ad6c74add28a4bb73645172c60e3d6129a Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 26 Jan 2022 21:57:01 +0100 Subject: [PATCH 006/938] Mark profile for refresh when receiving a profile key message --- .../signal/manager/storage/recipients/RecipientStore.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 1fd0ae4c..36f82481 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 @@ -301,7 +301,13 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile public void storeProfileKey(RecipientId recipientId, final ProfileKey profileKey) { synchronized (recipients) { final var recipient = recipients.get(recipientId); - if (profileKey != null && profileKey.equals(recipient.getProfileKey())) { + if (profileKey != null && profileKey.equals(recipient.getProfileKey()) && ( + recipient.getProfile() == null || ( + recipient.getProfile().getUnidentifiedAccessMode() != Profile.UnidentifiedAccessMode.UNKNOWN + && recipient.getProfile().getUnidentifiedAccessMode() + != Profile.UnidentifiedAccessMode.DISABLED + ) + )) { return; } From d812c249baa5ac227a49425375b670d31c8c3584 Mon Sep 17 00:00:00 2001 From: morph027 <600106+morph027@users.noreply.github.com> Date: Wed, 26 Jan 2022 22:04:36 +0100 Subject: [PATCH 007/938] add GraalVM reflections for GroupJoinInfo (#872) Signed-off-by: morph027 Co-authored-by: morph027 --- graalvm-config-dir/reflect-config.json | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index ede0d53f..c7353969 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -1604,6 +1604,19 @@ {"name":"inviteLinkPassword_"} ] }, +{ + "name":"org.signal.storageservice.protos.groups.GroupJoinInfo", + "fields":[ + {"name":"addFromInviteLink_"}, + {"name":"avatar_"}, + {"name":"description_"}, + {"name":"memberCount_"}, + {"name":"pendingAdminApproval_"}, + {"name":"publicKey_"}, + {"name":"revision_"}, + {"name":"title_"} + ] +}, { "name":"org.signal.storageservice.protos.groups.Member", "fields":[ @@ -1680,6 +1693,20 @@ {"name":"revision_"} ] }, +{ + "name":"org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo", + "fields":[ + {"name":"addFromInviteLink_"}, + {"name":"avatar_"}, + {"name":"description_"}, + {"name":"isAnnouncementGroup_"}, + {"name":"memberCount_"}, + {"name":"pendingAdminApproval_"}, + {"name":"publicKey_"}, + {"name":"revision_"}, + {"name":"title_"} + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedMember", "fields":[ From ffaa9d2ed311d74cf14f25fdc0cc75b66991cc3b Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 27 Jan 2022 20:03:43 +0100 Subject: [PATCH 008/938] Output sender also for sealed sender messages that fail to decrypt --- .../asamk/signal/manager/api/MessageEnvelope.java | 12 +++++++++--- .../manager/helper/IncomingMessageHandler.java | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) 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 dde8e166..73e7fe19 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 @@ -5,6 +5,7 @@ import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.helper.RecipientAddressResolver; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientResolver; +import org.signal.libsignal.metadata.ProtocolException; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; import org.whispersystems.signalservice.api.messages.SignalServiceContent; @@ -789,16 +790,21 @@ public record MessageEnvelope( SignalServiceContent content, RecipientResolver recipientResolver, RecipientAddressResolver addressResolver, - final AttachmentFileProvider fileProvider + final AttachmentFileProvider fileProvider, + Exception exception ) { final var source = !envelope.isUnidentifiedSender() && envelope.hasSourceUuid() ? recipientResolver.resolveRecipient(envelope.getSourceAddress()) : envelope.isUnidentifiedSender() && content != null ? recipientResolver.resolveRecipient(content.getSender()) - : null; + : exception instanceof ProtocolException e + ? recipientResolver.resolveRecipient(e.getSender()) + : null; final var sourceDevice = envelope.hasSourceDevice() ? envelope.getSourceDevice() - : content != null ? content.getSenderDevice() : 0; + : content != null + ? content.getSenderDevice() + : exception instanceof ProtocolException e ? e.getSenderDevice() : 0; Optional receipt; Optional typing; 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 0ef974e0..d7a50971 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 @@ -184,7 +184,8 @@ public final class IncomingMessageHandler { content, account.getRecipientStore(), account.getRecipientStore()::resolveRecipientAddress, - context.getAttachmentHelper()::getAttachmentFile), exception); + context.getAttachmentHelper()::getAttachmentFile, + exception), exception); return actions; } } From 3491782912978f17a13c30b713a2d5f34e6a8a39 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 27 Jan 2022 22:42:49 +0100 Subject: [PATCH 009/938] Fix sender check for requesting message resend --- .../org/asamk/signal/manager/helper/IncomingMessageHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d7a50971..251dfde3 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 @@ -127,7 +127,7 @@ public final class IncomingMessageHandler { final var senderProfile = context.getProfileHelper().getRecipientProfile(sender); final var selfProfile = context.getProfileHelper() .getRecipientProfile(account.getSelfRecipientId()); - if (e.getSenderDevice() != account.getDeviceId() + if ((!sender.equals(account.getSelfRecipientId()) || e.getSenderDevice() != account.getDeviceId()) && senderProfile != null && senderProfile.getCapabilities().contains(Profile.Capability.senderKey) && selfProfile != null From 95cc0ae7fdaf0cc34742bce38bb456e02653db43 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 23 Jan 2022 20:50:23 +0100 Subject: [PATCH 010/938] Implement MessageSendLog for resending after encryption error --- graalvm-config-dir/jni-config.json | 34 ++ graalvm-config-dir/proxy-config.json | 3 + graalvm-config-dir/reflect-config.json | 81 +++- graalvm-config-dir/resource-config.json | 12 + lib/build.gradle.kts | 2 + .../org/asamk/signal/manager/Manager.java | 1 + .../org/asamk/signal/manager/ManagerImpl.java | 11 + .../manager/actions/ResendMessageAction.java | 42 ++ .../helper/IncomingMessageHandler.java | 76 +++- .../signal/manager/helper/SendHelper.java | 220 +++++++--- .../signal/manager/storage/Database.java | 94 +++++ .../signal/manager/storage/SignalAccount.java | 38 ++ .../storage/sendLog/MessageSendLogEntry.java | 11 + .../storage/sendLog/MessageSendLogStore.java | 396 ++++++++++++++++++ .../senderKeys/SenderKeySharedStore.java | 15 + .../storage/senderKeys/SenderKeyStore.java | 4 + 16 files changed, 960 insertions(+), 80 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/actions/ResendMessageAction.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/Database.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index 4865f747..acbf7f3f 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -62,6 +62,36 @@ "name":"org.graalvm.jniutils.JNIExceptionWrapperEntryPoints", "methods":[{"name":"getClassName","parameterTypes":["java.lang.Class"] }] }, +{ + "name":"org.sqlite.Collation" +}, +{ + "name":"org.sqlite.Function" +}, +{ + "name":"org.sqlite.Function$Aggregate" +}, +{ + "name":"org.sqlite.Function$Window" +}, +{ + "name":"org.sqlite.ProgressHandler" +}, +{ + "name":"org.sqlite.core.DB", + "methods":[{"name":"throwex","parameterTypes":["int"] }] +}, +{ + "name":"org.sqlite.core.DB$ProgressObserver" +}, +{ + "name":"org.sqlite.core.NativeDB", + "fields":[ + {"name":"colldatalist"}, + {"name":"pointer"}, + {"name":"udfdatalist"} + ] +}, { "name":"org.whispersystems.libsignal.DuplicateMessageException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] @@ -77,6 +107,10 @@ "name":"org.whispersystems.libsignal.IdentityKeyPair", "methods":[{"name":"serialize","parameterTypes":[] }] }, +{ + "name":"org.whispersystems.libsignal.InvalidKeyException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.whispersystems.libsignal.InvalidMessageException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] diff --git a/graalvm-config-dir/proxy-config.json b/graalvm-config-dir/proxy-config.json index 42660618..3110286d 100644 --- a/graalvm-config-dir/proxy-config.json +++ b/graalvm-config-dir/proxy-config.json @@ -1,4 +1,7 @@ [ + { + "interfaces":["java.sql.Connection"]} + , { "interfaces":["org.asamk.Signal"]} , diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index c7353969..c5e61cee 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -15,9 +15,15 @@ { "name":"[J" }, +{ + "name":"[Lcom.zaxxer.hikari.util.ConcurrentBag$IConcurrentBagEntry;" +}, { "name":"[Ljava.lang.String;" }, +{ + "name":"[Ljava.sql.Statement;" +}, { "name":"[Lorg.whispersystems.signalservice.api.groupsv2.TemporalCredential;" }, @@ -118,6 +124,48 @@ "name":"com.sun.crypto.provider.TlsPrfGenerator$V12", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.zaxxer.hikari.HikariConfig", + "allDeclaredFields":true, + "queryAllPublicMethods":true, + "methods":[ + {"name":"getCatalog","parameterTypes":[] }, + {"name":"getConnectionInitSql","parameterTypes":[] }, + {"name":"getConnectionTestQuery","parameterTypes":[] }, + {"name":"getConnectionTimeout","parameterTypes":[] }, + {"name":"getDataSource","parameterTypes":[] }, + {"name":"getDataSourceClassName","parameterTypes":[] }, + {"name":"getDataSourceJNDI","parameterTypes":[] }, + {"name":"getDataSourceProperties","parameterTypes":[] }, + {"name":"getDriverClassName","parameterTypes":[] }, + {"name":"getExceptionOverrideClassName","parameterTypes":[] }, + {"name":"getHealthCheckProperties","parameterTypes":[] }, + {"name":"getHealthCheckRegistry","parameterTypes":[] }, + {"name":"getIdleTimeout","parameterTypes":[] }, + {"name":"getInitializationFailTimeout","parameterTypes":[] }, + {"name":"getJdbcUrl","parameterTypes":[] }, + {"name":"getKeepaliveTime","parameterTypes":[] }, + {"name":"getLeakDetectionThreshold","parameterTypes":[] }, + {"name":"getMaxLifetime","parameterTypes":[] }, + {"name":"getMaximumPoolSize","parameterTypes":[] }, + {"name":"getMetricRegistry","parameterTypes":[] }, + {"name":"getMetricsTrackerFactory","parameterTypes":[] }, + {"name":"getMinimumIdle","parameterTypes":[] }, + {"name":"getPassword","parameterTypes":[] }, + {"name":"getPoolName","parameterTypes":[] }, + {"name":"getScheduledExecutor","parameterTypes":[] }, + {"name":"getSchema","parameterTypes":[] }, + {"name":"getThreadFactory","parameterTypes":[] }, + {"name":"getTransactionIsolation","parameterTypes":[] }, + {"name":"getUsername","parameterTypes":[] }, + {"name":"getValidationTimeout","parameterTypes":[] }, + {"name":"isAllowPoolSuspension","parameterTypes":[] }, + {"name":"isAutoCommit","parameterTypes":[] }, + {"name":"isIsolateInternalQueries","parameterTypes":[] }, + {"name":"isReadOnly","parameterTypes":[] }, + {"name":"isRegisterMbeans","parameterTypes":[] } + ] +}, { "name":"int", "allDeclaredMethods":true, @@ -1607,13 +1655,13 @@ { "name":"org.signal.storageservice.protos.groups.GroupJoinInfo", "fields":[ - {"name":"addFromInviteLink_"}, - {"name":"avatar_"}, - {"name":"description_"}, - {"name":"memberCount_"}, - {"name":"pendingAdminApproval_"}, - {"name":"publicKey_"}, - {"name":"revision_"}, + {"name":"addFromInviteLink_"}, + {"name":"avatar_"}, + {"name":"description_"}, + {"name":"memberCount_"}, + {"name":"pendingAdminApproval_"}, + {"name":"publicKey_"}, + {"name":"revision_"}, {"name":"title_"} ] }, @@ -1696,14 +1744,14 @@ { "name":"org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo", "fields":[ - {"name":"addFromInviteLink_"}, - {"name":"avatar_"}, - {"name":"description_"}, - {"name":"isAnnouncementGroup_"}, - {"name":"memberCount_"}, - {"name":"pendingAdminApproval_"}, - {"name":"publicKey_"}, - {"name":"revision_"}, + {"name":"addFromInviteLink_"}, + {"name":"avatar_"}, + {"name":"description_"}, + {"name":"isAnnouncementGroup_"}, + {"name":"memberCount_"}, + {"name":"pendingAdminApproval_"}, + {"name":"publicKey_"}, + {"name":"revision_"}, {"name":"title_"} ] }, @@ -1773,6 +1821,9 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, +{ + "name":"org.sqlite.JDBC" +}, { "name":"org.whispersystems.libsignal.state.IdentityKeyStore", "allDeclaredMethods":true diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index 8a486340..a52689ae 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -1,6 +1,12 @@ { "resources":{ "includes":[ + { + "pattern":"\\QMETA-INF/maven/org.xerial/sqlite-jdbc/pom.properties\\E" + }, + { + "pattern":"\\QMETA-INF/services/java.sql.Driver\\E" + }, { "pattern":"\\QMETA-INF/services/org.freedesktop.dbus.spi.transport.ITransportProvider\\E" }, @@ -187,6 +193,12 @@ { "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" }, + { + "pattern":"\\Qorg/sqlite/native/Linux/x86_64/libsqlitejdbc.so\\E" + }, + { + "pattern":"\\Qsqlite-jdbc.properties\\E" + }, { "pattern":"com/google/i18n/phonenumbers/data/.*" } diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 21dfb19a..5b891b8b 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -19,6 +19,8 @@ dependencies { implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") implementation("org.slf4j", "slf4j-api", "1.7.32") + implementation("org.xerial", "sqlite-jdbc", "3.36.0.3") + implementation("com.zaxxer", "HikariCP", "5.0.1") } configurations { 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 2c677dae..ed70bcee 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -67,6 +67,7 @@ public interface Manager extends Closeable { throw new NotRegisteredException(); } + account.initDatabase(); final var serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); return new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 0e100d72..7c8cff5d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -571,6 +571,17 @@ public class ManagerImpl implements Manager { ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp); final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete); + for (final var recipient : recipients) { + if (recipient instanceof RecipientIdentifier.Single r) { + try { + final var recipientId = context.getRecipientHelper().resolveRecipient(r); + account.getMessageSendLogStore().deleteEntryForRecipientNonGroup(targetSentTimestamp, recipientId); + } catch (UnregisteredRecipientException ignored) { + } + } else if (recipient instanceof RecipientIdentifier.Group r) { + account.getMessageSendLogStore().deleteEntryForGroup(targetSentTimestamp, r.groupId()); + } + } return sendMessage(messageBuilder, recipients); } 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 new file mode 100644 index 00000000..9f399dd8 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/actions/ResendMessageAction.java @@ -0,0 +1,42 @@ +package org.asamk.signal.manager.actions; + +import org.asamk.signal.manager.helper.Context; +import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.asamk.signal.manager.storage.sendLog.MessageSendLogEntry; + +import java.util.Objects; + +public class ResendMessageAction implements HandleAction { + + private final RecipientId recipientId; + private final long timestamp; + private final MessageSendLogEntry messageSendLogEntry; + + public ResendMessageAction( + final RecipientId recipientId, final long timestamp, final MessageSendLogEntry messageSendLogEntry + ) { + this.recipientId = recipientId; + this.timestamp = timestamp; + this.messageSendLogEntry = messageSendLogEntry; + } + + @Override + public void execute(Context context) throws Throwable { + context.getSendHelper().resendMessage(recipientId, timestamp, messageSendLogEntry); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final ResendMessageAction that = (ResendMessageAction) o; + return timestamp == that.timestamp + && recipientId.equals(that.recipientId) + && messageSendLogEntry.equals(that.messageSendLogEntry); + } + + @Override + public int hashCode() { + return Objects.hash(recipientId, timestamp, messageSendLogEntry); + } +} 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 251dfde3..eece81d3 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 @@ -7,6 +7,7 @@ import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.actions.HandleAction; import org.asamk.signal.manager.actions.RefreshPreKeysAction; import org.asamk.signal.manager.actions.RenewSessionAction; +import org.asamk.signal.manager.actions.ResendMessageAction; import org.asamk.signal.manager.actions.RetrieveProfileAction; import org.asamk.signal.manager.actions.RetrieveStorageDataAction; import org.asamk.signal.manager.actions.SendGroupInfoAction; @@ -41,6 +42,7 @@ import org.signal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.SignalProtocolAddress; +import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; @@ -165,6 +167,13 @@ public final class IncomingMessageHandler { // address/uuid is validated by unidentified sender certificate account.getRecipientStore().resolveRecipientTrusted(content.getSender()); } + if (envelope.isReceipt()) { + final var senderPair = getSender(envelope, content); + final var sender = senderPair.first(); + final var senderDeviceId = senderPair.second(); + account.getMessageSendLogStore().deleteEntryForRecipient(envelope.getTimestamp(), sender, senderDeviceId); + } + if (isMessageBlocked(envelope, content)) { logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp()); return List.of(); @@ -198,6 +207,14 @@ public final class IncomingMessageHandler { final var sender = senderPair.first(); final var senderDeviceId = senderPair.second(); + if (content.getReceiptMessage().isPresent()) { + final var message = content.getReceiptMessage().get(); + if (message.isDeliveryReceipt()) { + account.getMessageSendLogStore() + .deleteEntriesForRecipient(message.getTimestamps(), sender, senderDeviceId); + } + } + if (content.getSenderKeyDistributionMessage().isPresent()) { final var message = content.getSenderKeyDistributionMessage().get(); final var protocolAddress = new SignalProtocolAddress(context.getRecipientHelper() @@ -212,15 +229,10 @@ public final class IncomingMessageHandler { if (content.getDecryptionErrorMessage().isPresent()) { var message = content.getDecryptionErrorMessage().get(); logger.debug("Received a decryption error message (resend request for {})", message.getTimestamp()); - if (message.getRatchetKey().isPresent()) { - if (message.getDeviceId() == account.getDeviceId() && account.getSessionStore() - .isCurrentRatchetKey(sender, senderDeviceId, message.getRatchetKey().get())) { - logger.debug("Renewing the session with sender"); - actions.add(new RenewSessionAction(sender)); - } + if (message.getDeviceId() == account.getDeviceId()) { + handleDecryptionErrorMessage(actions, sender, senderDeviceId, message); } else { - logger.debug("Reset shared sender keys with this recipient"); - account.getSenderKeyStore().deleteSharedWith(sender); + logger.debug("Request is for another one of our devices"); } } @@ -246,6 +258,54 @@ public final class IncomingMessageHandler { return actions; } + private void handleDecryptionErrorMessage( + final List actions, + final RecipientId sender, + final int senderDeviceId, + final DecryptionErrorMessage message + ) { + final var logEntries = account.getMessageSendLogStore() + .findMessages(sender, senderDeviceId, message.getTimestamp(), !message.getRatchetKey().isPresent()); + + for (final var logEntry : logEntries) { + actions.add(new ResendMessageAction(sender, message.getTimestamp(), logEntry)); + } + + if (message.getRatchetKey().isPresent()) { + if (account.getSessionStore().isCurrentRatchetKey(sender, senderDeviceId, message.getRatchetKey().get())) { + if (logEntries.isEmpty()) { + logger.debug("Renewing the session with sender"); + actions.add(new RenewSessionAction(sender)); + } else { + logger.trace("Archiving the session with sender, a resend message has already been queued"); + context.getAccount().getSessionStore().archiveSessions(sender); + } + } + return; + } + + var found = false; + for (final var logEntry : logEntries) { + if (logEntry.groupId().isEmpty()) { + continue; + } + final var group = account.getGroupStore().getGroup(logEntry.groupId().get()); + if (group == null) { + continue; + } + found = true; + logger.trace("Deleting shared sender key with {} ({}): {}", + sender, + senderDeviceId, + group.getDistributionId()); + account.getSenderKeyStore().deleteSharedWith(sender, senderDeviceId, group.getDistributionId()); + } + if (!found) { + logger.debug("Reset all shared sender keys with this recipient, no related message found in send log"); + account.getSenderKeyStore().deleteSharedWith(sender); + } + } + private List handleSyncMessage( final SignalServiceSyncMessage syncMessage, final RecipientId sender, final boolean ignoreAttachments ) { 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 4fa1aaeb..aedd29a2 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 @@ -1,5 +1,7 @@ package org.asamk.signal.manager.helper; +import com.google.protobuf.ByteString; + import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.groups.GroupId; @@ -11,11 +13,13 @@ import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.asamk.signal.manager.storage.sendLog.MessageSendLogEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidRegistrationIdException; import org.whispersystems.libsignal.NoSessionException; +import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceMessageSender; @@ -45,6 +49,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; public class SendHelper { @@ -74,9 +79,7 @@ public class SendHelper { messageBuilder.withProfileKey(account.getProfileKey().serialize()); final var message = messageBuilder.build(); - final var result = sendMessage(message, recipientId); - handleSendMessageResult(result); - return result; + return sendMessage(message, recipientId); } /** @@ -90,29 +93,6 @@ public class SendHelper { return sendAsGroupMessage(messageBuilder, g); } - private List sendAsGroupMessage( - final SignalServiceDataMessage.Builder messageBuilder, final GroupInfo g - ) throws IOException, GroupSendingNotAllowedException { - GroupUtils.setGroupContext(messageBuilder, g); - messageBuilder.withExpiration(g.getMessageExpirationTimer()); - - final var message = messageBuilder.build(); - final var recipients = g.getMembersWithout(account.getSelfRecipientId()); - - if (g.isAnnouncementGroup() && !g.isAdmin(account.getSelfRecipientId())) { - if (message.getBody().isPresent() - || message.getAttachments().isPresent() - || message.getQuote().isPresent() - || message.getPreviews().isPresent() - || message.getMentions().isPresent() - || message.getSticker().isPresent()) { - throw new GroupSendingNotAllowedException(g.getGroupId(), g.getTitle()); - } - } - - return sendGroupMessage(message, recipients, g.getDistributionId()); - } - /** * Send a complete group message to the given recipients (should be current/old/new members) * This method should only be used for create/update/quit group messages. @@ -122,31 +102,7 @@ public class SendHelper { final Set recipientIds, final DistributionId distributionId ) throws IOException { - final var messageSender = dependencies.getMessageSender(); - final var results = sendGroupMessageInternal((recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendDataMessage( - recipients, - unidentifiedAccess, - isRecipientUpdate, - ContentHint.DEFAULT, - message, - SignalServiceMessageSender.LegacyGroupEvents.EMPTY, - sendResult -> logger.trace("Partial message send result: {}", sendResult.isSuccess()), - () -> false), - (distId, recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendGroupDataMessage(distId, - recipients, - unidentifiedAccess, - isRecipientUpdate, - ContentHint.DEFAULT, - message, - SignalServiceMessageSender.SenderKeyGroupEvents.EMPTY), - recipientIds, - distributionId); - - for (var r : results) { - handleSendMessageResult(r); - } - - return results; + return sendGroupMessage(message, recipientIds, distributionId, ContentHint.IMPLICIT); } public SendMessageResult sendDeliveryReceipt( @@ -162,10 +118,14 @@ public class SendHelper { public SendMessageResult sendReceiptMessage( final SignalServiceReceiptMessage receiptMessage, final RecipientId recipientId ) { - return handleSendMessage(recipientId, + final var messageSendLogStore = account.getMessageSendLogStore(); + final var result = handleSendMessage(recipientId, (messageSender, address, unidentifiedAccess) -> messageSender.sendReceipt(address, unidentifiedAccess, receiptMessage)); + messageSendLogStore.insertIfPossible(receiptMessage.getWhen(), result, ContentHint.IMPLICIT); + handleSendMessageResult(result); + return result; } public SendMessageResult sendRetryReceipt( @@ -175,15 +135,19 @@ public class SendHelper { errorMessage.getTimestamp(), recipientId, errorMessage.getDeviceId()); - return handleSendMessage(recipientId, + final var result = handleSendMessage(recipientId, (messageSender, address, unidentifiedAccess) -> messageSender.sendRetryReceipt(address, unidentifiedAccess, groupId.transform(GroupId::serialize), errorMessage)); + handleSendMessageResult(result); + return result; } public SendMessageResult sendNullMessage(RecipientId recipientId) { - return handleSendMessage(recipientId, SignalServiceMessageSender::sendNullMessage); + final var result = handleSendMessage(recipientId, SignalServiceMessageSender::sendNullMessage); + handleSendMessageResult(result); + return result; } public SendMessageResult sendSelfMessage( @@ -225,10 +189,12 @@ public class SendHelper { public SendMessageResult sendTypingMessage( SignalServiceTypingMessage message, RecipientId recipientId ) { - return handleSendMessage(recipientId, + final var result = handleSendMessage(recipientId, (messageSender, address, unidentifiedAccess) -> messageSender.sendTyping(address, unidentifiedAccess, message)); + handleSendMessageResult(result); + return result; } public List sendGroupTypingMessage( @@ -244,6 +210,142 @@ public class SendHelper { return sendGroupTypingMessage(message, recipientIds, distributionId); } + public SendMessageResult resendMessage( + final RecipientId recipientId, final long timestamp, final MessageSendLogEntry messageSendLogEntry + ) { + if (messageSendLogEntry.groupId().isEmpty()) { + return handleSendMessage(recipientId, + (messageSender, address, unidentifiedAccess) -> messageSender.resendContent(address, + unidentifiedAccess, + timestamp, + messageSendLogEntry.content(), + messageSendLogEntry.contentHint(), + Optional.absent())); + } + + final var groupId = messageSendLogEntry.groupId().get(); + final var group = account.getGroupStore().getGroup(groupId); + + if (group == null) { + logger.debug("Could not find a matching group for the groupId {}! Skipping message send.", + groupId.toBase64()); + return null; + } else if (!group.getMembers().contains(recipientId)) { + logger.warn("The target user is no longer in the group {}! Skipping message send.", groupId.toBase64()); + return null; + } + + final var senderKeyDistributionMessage = dependencies.getMessageSender() + .getOrCreateNewGroupSession(group.getDistributionId()); + final var distributionBytes = ByteString.copyFrom(senderKeyDistributionMessage.serialize()); + final var contentToSend = messageSendLogEntry.content() + .toBuilder() + .setSenderKeyDistributionMessage(distributionBytes) + .build(); + + final var result = handleSendMessage(recipientId, + (messageSender, address, unidentifiedAccess) -> messageSender.resendContent(address, + unidentifiedAccess, + timestamp, + contentToSend, + messageSendLogEntry.contentHint(), + Optional.of(group.getGroupId().serialize()))); + + if (result.isSuccess()) { + final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId); + final var addresses = result.getSuccess() + .getDevices() + .stream() + .map(device -> new SignalProtocolAddress(address.getIdentifier(), device)) + .collect(Collectors.toList()); + + account.getSenderKeyStore().markSenderKeySharedWith(group.getDistributionId(), addresses); + } + + return result; + } + + private List sendAsGroupMessage( + final SignalServiceDataMessage.Builder messageBuilder, final GroupInfo g + ) throws IOException, GroupSendingNotAllowedException { + GroupUtils.setGroupContext(messageBuilder, g); + messageBuilder.withExpiration(g.getMessageExpirationTimer()); + + final var message = messageBuilder.build(); + final var recipients = g.getMembersWithout(account.getSelfRecipientId()); + + if (g.isAnnouncementGroup() && !g.isAdmin(account.getSelfRecipientId())) { + if (message.getBody().isPresent() + || message.getAttachments().isPresent() + || message.getQuote().isPresent() + || message.getPreviews().isPresent() + || message.getMentions().isPresent() + || message.getSticker().isPresent()) { + throw new GroupSendingNotAllowedException(g.getGroupId(), g.getTitle()); + } + } + + return sendGroupMessage(message, recipients, g.getDistributionId(), ContentHint.RESENDABLE); + } + + private List sendGroupMessage( + final SignalServiceDataMessage message, + final Set recipientIds, + final DistributionId distributionId, + final ContentHint contentHint + ) throws IOException { + final var messageSender = dependencies.getMessageSender(); + final var messageSendLogStore = account.getMessageSendLogStore(); + final AtomicLong entryId = new AtomicLong(-1); + + final LegacySenderHandler legacySender = (recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendDataMessage( + recipients, + unidentifiedAccess, + isRecipientUpdate, + contentHint, + message, + SignalServiceMessageSender.LegacyGroupEvents.EMPTY, + sendResult -> { + logger.trace("Partial message send result: {}", sendResult.isSuccess()); + synchronized (entryId) { + if (entryId.get() == -1) { + final var newId = messageSendLogStore.insertIfPossible(message.getTimestamp(), + sendResult, + contentHint); + entryId.set(newId); + } else { + messageSendLogStore.addRecipientToExistingEntryIfPossible(entryId.get(), sendResult); + } + } + }, + () -> false); + final SenderKeySenderHandler senderKeySender = (distId, recipients, unidentifiedAccess, isRecipientUpdate) -> { + final var res = messageSender.sendGroupDataMessage(distId, + recipients, + unidentifiedAccess, + isRecipientUpdate, + contentHint, + message, + SignalServiceMessageSender.SenderKeyGroupEvents.EMPTY); + synchronized (entryId) { + if (entryId.get() == -1) { + final var newId = messageSendLogStore.insertIfPossible(message.getTimestamp(), res, contentHint); + entryId.set(newId); + } else { + messageSendLogStore.addRecipientToExistingEntryIfPossible(entryId.get(), res); + } + } + return res; + }; + final var results = sendGroupMessageInternal(legacySender, senderKeySender, recipientIds, distributionId); + + for (var r : results) { + handleSendMessageResult(r); + } + + return results; + } + private List sendGroupTypingMessage( final SignalServiceTypingMessage message, final Set recipientIds, @@ -462,12 +564,16 @@ public class SendHelper { private SendMessageResult sendMessage( SignalServiceDataMessage message, RecipientId recipientId ) { - return handleSendMessage(recipientId, + final var messageSendLogStore = account.getMessageSendLogStore(); + final var result = handleSendMessage(recipientId, (messageSender, address, unidentifiedAccess) -> messageSender.sendDataMessage(address, unidentifiedAccess, - ContentHint.DEFAULT, + ContentHint.RESENDABLE, message, SignalServiceMessageSender.IndividualSendEvents.EMPTY)); + messageSendLogStore.insertIfPossible(message.getTimestamp(), result, ContentHint.RESENDABLE); + handleSendMessageResult(result); + return result; } private SendMessageResult handleSendMessage(RecipientId recipientId, SenderHandler s) { 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 new file mode 100644 index 00000000..1d69236b --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/Database.java @@ -0,0 +1,94 @@ +package org.asamk.signal.manager.storage; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +import org.asamk.signal.manager.storage.sendLog.MessageSendLogStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sqlite.SQLiteConfig; + +import java.io.File; +import java.sql.Connection; +import java.sql.SQLException; + +public class Database implements AutoCloseable { + + private final static Logger logger = LoggerFactory.getLogger(SignalAccount.class); + private static final long DATABASE_VERSION = 1; + + private final HikariDataSource dataSource; + + private Database(final HikariDataSource dataSource) { + this.dataSource = dataSource; + } + + public static Database init(File databaseFile) throws SQLException { + HikariDataSource dataSource = null; + + try { + dataSource = getHikariDataSource(databaseFile.getAbsolutePath()); + + try (final var connection = dataSource.getConnection()) { + final var userVersion = getUserVersion(connection); + logger.trace("Current database version: {} Program database version: {}", + userVersion, + DATABASE_VERSION); + + if (userVersion > DATABASE_VERSION) { + logger.error("Database has been updated by a newer signal-cli version"); + throw new SQLException("Database has been updated by a newer signal-cli version"); + } else if (userVersion < DATABASE_VERSION) { + if (userVersion < 1) { + logger.debug("Updating database: Creating message send log tables"); + MessageSendLogStore.createSql(connection); + } + setUserVersion(connection, DATABASE_VERSION); + } + + final var result = new Database(dataSource); + dataSource = null; + return result; + } + } finally { + if (dataSource != null) { + dataSource.close(); + } + } + } + + public Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } + + @Override + public void close() throws SQLException { + dataSource.close(); + } + + private static long getUserVersion(final Connection connection) throws SQLException { + try (final var statement = connection.createStatement()) { + final var resultSet = statement.executeQuery("PRAGMA user_version"); + return resultSet.getLong(1); + } + } + + private static void setUserVersion(final Connection connection, long userVersion) throws SQLException { + try (final var statement = connection.createStatement()) { + statement.executeUpdate("PRAGMA user_version = " + userVersion); + } + } + + private static HikariDataSource getHikariDataSource(final String databaseFile) { + final var sqliteConfig = new SQLiteConfig(); + sqliteConfig.setBusyTimeout(10_000); + sqliteConfig.setTransactionMode(SQLiteConfig.TransactionMode.IMMEDIATE); + + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:sqlite:" + databaseFile); + config.setDataSourceProperties(sqliteConfig.toProperties()); + config.setMinimumIdle(1); + config.setConnectionInitSql("PRAGMA foreign_keys=ON"); + return new HikariDataSource(config); + } +} 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 f23aea03..862971c9 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 @@ -27,6 +27,7 @@ import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientStore; +import org.asamk.signal.manager.storage.sendLog.MessageSendLogStore; import org.asamk.signal.manager.storage.senderKeys.SenderKeyStore; import org.asamk.signal.manager.storage.sessions.SessionStore; import org.asamk.signal.manager.storage.stickers.StickerStore; @@ -62,6 +63,7 @@ import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.security.SecureRandom; +import java.sql.SQLException; import java.util.Base64; import java.util.Date; import java.util.HashSet; @@ -120,6 +122,9 @@ public class SignalAccount implements Closeable { private ConfigurationStore.Storage configurationStoreStorage; private MessageCache messageCache; + private MessageSendLogStore messageSendLogStore; + + private Database database; private SignalAccount(final FileChannel fileChannel, final FileLock lock) { this.fileChannel = fileChannel; @@ -227,6 +232,10 @@ public class SignalAccount implements Closeable { return signalAccount; } + public void initDatabase() { + getDatabase(); + } + private void clearAllPreKeys() { this.preKeyIdOffset = new SecureRandom().nextInt(Medium.MAX_VALUE); this.nextSignedPreKeyId = new SecureRandom().nextInt(Medium.MAX_VALUE); @@ -383,6 +392,10 @@ public class SignalAccount implements Closeable { return new File(getUserPath(dataPath, account), "recipients-store"); } + private static File getDatabaseFile(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "account.db"); + } + public static boolean userExists(File dataPath, String account) { if (account == null) { return false; @@ -869,6 +882,21 @@ public class SignalAccount implements Closeable { () -> messageCache = new MessageCache(getMessageCachePath(dataPath, account))); } + public Database getDatabase() { + return getOrCreate(() -> database, () -> { + try { + database = Database.init(getDatabaseFile(dataPath, account)); + } catch (SQLException e) { + throw new RuntimeException(e); + } + }); + } + + public MessageSendLogStore getMessageSendLogStore() { + return getOrCreate(() -> messageSendLogStore, + () -> messageSendLogStore = new MessageSendLogStore(getRecipientStore(), getDatabase())); + } + public String getAccount() { return account; } @@ -1050,6 +1078,16 @@ public class SignalAccount implements Closeable { @Override public void close() { synchronized (fileChannel) { + if (database != null) { + try { + database.close(); + } catch (SQLException e) { + logger.warn("Failed to close account database: {}", e.getMessage(), e); + } + } + if (messageSendLogStore != null) { + messageSendLogStore.close(); + } try { try { lock.close(); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java new file mode 100644 index 00000000..31a4252a --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java @@ -0,0 +1,11 @@ +package org.asamk.signal.manager.storage.sendLog; + +import org.asamk.signal.manager.groups.GroupId; +import org.whispersystems.signalservice.api.crypto.ContentHint; +import org.whispersystems.signalservice.internal.push.SignalServiceProtos; + +import java.util.Optional; + +public record MessageSendLogEntry( + Optional groupId, SignalServiceProtos.Content content, ContentHint contentHint +) {} 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 new file mode 100644 index 00000000..795919f6 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java @@ -0,0 +1,396 @@ +package org.asamk.signal.manager.storage.sendLog; + +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupUtils; +import org.asamk.signal.manager.storage.Database; +import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.asamk.signal.manager.storage.recipients.RecipientResolver; +import org.signal.zkgroup.InvalidInputException; +import org.signal.zkgroup.groups.GroupMasterKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.crypto.ContentHint; +import org.whispersystems.signalservice.api.messages.SendMessageResult; +import org.whispersystems.signalservice.internal.push.SignalServiceProtos; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.Duration; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class MessageSendLogStore implements AutoCloseable { + + private static final Logger logger = LoggerFactory.getLogger(MessageSendLogStore.class); + + private static final String TABLE_MESSAGE_SEND_LOG = "message_send_log"; + private static final String TABLE_MESSAGE_SEND_LOG_CONTENT = "message_send_log_content"; + + private static final Duration LOG_DURATION = Duration.ofDays(1); + + private final RecipientResolver recipientResolver; + private final Database database; + private final Thread cleanupThread; + + public MessageSendLogStore( + final RecipientResolver recipientResolver, final Database database + ) { + this.recipientResolver = recipientResolver; + this.database = database; + this.cleanupThread = new Thread(() -> { + try { + final var interval = Duration.ofHours(1).toMillis(); + while (true) { + try (final var connection = database.getConnection()) { + deleteOutdatedEntries(connection); + Thread.sleep(interval); + } catch (SQLException e) { + logger.warn("Deleting outdated entries failed"); + break; + } + } + } catch (InterruptedException e) { + logger.debug("Stopping msl cleanup thread"); + } + }); + cleanupThread.setDaemon(true); + cleanupThread.start(); + } + + public static void createSql(Connection connection) throws SQLException { + try (final var statement = connection.createStatement()) { + statement.executeUpdate(""" + CREATE TABLE message_send_log ( + _id INTEGER PRIMARY KEY, + content_id INTEGER NOT NULL REFERENCES message_send_log_content (_id) ON DELETE CASCADE, + recipient_id INTEGER NOT NULL, + device_id INTEGER NOT NULL + ); + CREATE TABLE message_send_log_content ( + _id INTEGER PRIMARY KEY, + group_id BLOB, + timestamp INTEGER NOT NULL, + content BLOB NOT NULL, + content_hint INTEGER NOT NULL + ); + CREATE INDEX mslc_timestamp_index ON message_send_log_content (timestamp); + CREATE INDEX msl_recipient_index ON message_send_log (recipient_id, device_id, content_id); + CREATE INDEX msl_content_index ON message_send_log (content_id); + """); + } + } + + public List findMessages( + final RecipientId recipientId, final int deviceId, final long timestamp, final boolean isSenderKey + ) { + try (final var connection = database.getConnection()) { + deleteOutdatedEntries(connection); + + try (final var statement = connection.prepareStatement( + "SELECT group_id, content, content_hint FROM %s l INNER JOIN %s lc ON l.content_id = lc._id WHERE l.recipient_id = ? AND l.device_id = ? AND lc.timestamp = ?".formatted( + TABLE_MESSAGE_SEND_LOG, + TABLE_MESSAGE_SEND_LOG_CONTENT))) { + statement.setLong(1, recipientId.id()); + statement.setInt(2, deviceId); + statement.setLong(3, timestamp); + try (var result = executeQueryForStream(statement, resultSet -> { + final var groupId = Optional.ofNullable(resultSet.getBytes("group_id")) + .map(GroupId::unknownVersion); + final SignalServiceProtos.Content content; + try { + content = SignalServiceProtos.Content.parseFrom(resultSet.getBinaryStream("content")); + } catch (IOException e) { + logger.warn("Failed to parse content from message send log", e); + return null; + } + final var contentHint = ContentHint.fromType(resultSet.getInt("content_hint")); + return new MessageSendLogEntry(groupId, content, contentHint); + })) { + return result.filter(Objects::nonNull) + .filter(e -> !isSenderKey || e.groupId().isPresent()) + .toList(); + } + } + } catch (SQLException e) { + logger.warn("Failed read from message send log", e); + return List.of(); + } + } + + public long insertIfPossible( + long sentTimestamp, SendMessageResult sendMessageResult, ContentHint contentHint + ) { + final RecipientDevices recipientDevice = getRecipientDevices(sendMessageResult); + if (recipientDevice == null) { + return -1; + } + + return insert(List.of(recipientDevice), + sentTimestamp, + sendMessageResult.getSuccess().getContent().get(), + contentHint); + } + + public long insertIfPossible( + long sentTimestamp, List sendMessageResults, ContentHint contentHint + ) { + final var recipientDevices = sendMessageResults.stream() + .map(this::getRecipientDevices) + .filter(Objects::nonNull) + .toList(); + if (recipientDevices.isEmpty()) { + return -1; + } + + final var content = sendMessageResults.stream() + .filter(r -> r.isSuccess() && r.getSuccess().getContent().isPresent()) + .map(r -> r.getSuccess().getContent().get()) + .findFirst() + .get(); + + return insert(recipientDevices, sentTimestamp, content, contentHint); + } + + public void addRecipientToExistingEntryIfPossible(final long contentId, final SendMessageResult sendMessageResult) { + final RecipientDevices recipientDevice = getRecipientDevices(sendMessageResult); + if (recipientDevice == null) { + return; + } + + insertRecipientsForExistingContent(contentId, List.of(recipientDevice)); + } + + public void addRecipientToExistingEntryIfPossible( + final long contentId, final List sendMessageResults + ) { + final var recipientDevices = sendMessageResults.stream() + .map(this::getRecipientDevices) + .filter(Objects::nonNull) + .toList(); + if (recipientDevices.isEmpty()) { + return; + } + + insertRecipientsForExistingContent(contentId, recipientDevices); + } + + public void deleteEntryForGroup(long sentTimestamp, GroupId groupId) { + try (final var connection = database.getConnection()) { + try (final var statement = connection.prepareStatement( + "DELETE FROM %s AS lc WHERE lc.timestamp = ? AND lc.group_id = ?".formatted( + TABLE_MESSAGE_SEND_LOG_CONTENT))) { + statement.setLong(1, sentTimestamp); + statement.setBytes(2, groupId.serialize()); + statement.executeUpdate(); + } + } catch (SQLException e) { + logger.warn("Failed delete from message send log", e); + } + } + + public void deleteEntryForRecipientNonGroup(long sentTimestamp, RecipientId recipientId) { + try (final var connection = database.getConnection()) { + connection.setAutoCommit(false); + try (final var statement = connection.prepareStatement( + "DELETE FROM %s AS lc WHERE lc.timestamp = ? AND lc.group_id IS NULL AND lc._id IN (SELECT content_id FROM %s l WHERE l.recipient_id = ?)".formatted( + TABLE_MESSAGE_SEND_LOG_CONTENT, + TABLE_MESSAGE_SEND_LOG))) { + statement.setLong(1, sentTimestamp); + statement.setLong(2, recipientId.id()); + statement.executeUpdate(); + } + + deleteOrphanedLogContents(connection); + connection.commit(); + } catch (SQLException e) { + logger.warn("Failed delete from message send log", e); + } + } + + public void deleteEntryForRecipient(long sentTimestamp, RecipientId recipientId, int deviceId) { + deleteEntriesForRecipient(List.of(sentTimestamp), recipientId, deviceId); + } + + public void deleteEntriesForRecipient(List sentTimestamps, RecipientId recipientId, int deviceId) { + try (final var connection = database.getConnection()) { + connection.setAutoCommit(false); + try (final var statement = connection.prepareStatement( + "DELETE FROM %s AS l WHERE l.content_id IN (SELECT _id FROM %s lc WHERE lc.timestamp = ?) AND l.recipient_id = ? AND l.device_id = ?".formatted( + TABLE_MESSAGE_SEND_LOG, + TABLE_MESSAGE_SEND_LOG_CONTENT))) { + for (final var sentTimestamp : sentTimestamps) { + statement.setLong(1, sentTimestamp); + statement.setLong(2, recipientId.id()); + statement.setInt(3, deviceId); + statement.executeUpdate(); + } + } + + deleteOrphanedLogContents(connection); + connection.commit(); + } catch (SQLException e) { + logger.warn("Failed delete from message send log", e); + } + } + + @Override + public void close() { + cleanupThread.interrupt(); + try { + cleanupThread.join(); + } catch (InterruptedException ignored) { + } + } + + private RecipientDevices getRecipientDevices(final SendMessageResult sendMessageResult) { + if (sendMessageResult.isSuccess() && sendMessageResult.getSuccess().getContent().isPresent()) { + final var recipientId = recipientResolver.resolveRecipient(sendMessageResult.getAddress()); + return new RecipientDevices(recipientId, sendMessageResult.getSuccess().getDevices()); + } else { + return null; + } + } + + private long insert( + final List recipientDevices, + final long sentTimestamp, + final SignalServiceProtos.Content content, + final ContentHint contentHint + ) { + byte[] groupId = getGroupId(content); + + try (final var connection = database.getConnection()) { + connection.setAutoCommit(false); + final long contentId; + try (final var statement = connection.prepareStatement( + "INSERT INTO %s (timestamp, group_id, content, content_hint) VALUES (?,?,?,?)".formatted( + TABLE_MESSAGE_SEND_LOG_CONTENT))) { + statement.setLong(1, sentTimestamp); + statement.setBytes(2, groupId); + statement.setBytes(3, content.toByteArray()); + statement.setInt(4, contentHint.getType()); + statement.executeUpdate(); + final var generatedKeys = statement.getGeneratedKeys(); + if (generatedKeys.next()) { + contentId = generatedKeys.getLong(1); + } else { + contentId = -1; + } + } + if (contentId == -1) { + logger.warn("Failed to insert message send log content"); + return -1; + } + insertRecipientsForExistingContent(contentId, recipientDevices, connection); + + connection.commit(); + return contentId; + } catch (SQLException e) { + logger.warn("Failed to insert into message send log", e); + return -1; + } + } + + private byte[] getGroupId(final SignalServiceProtos.Content content) { + try { + return !content.hasDataMessage() + ? null + : content.getDataMessage().hasGroup() + ? content.getDataMessage().getGroup().getId().toByteArray() + : content.getDataMessage().hasGroupV2() + ? GroupUtils.getGroupIdV2(new GroupMasterKey(content.getDataMessage() + .getGroupV2() + .getMasterKey() + .toByteArray())).serialize() + : null; + } catch (InvalidInputException e) { + logger.warn("Failed to parse groupId id from content"); + return null; + } + } + + private void insertRecipientsForExistingContent( + final long contentId, final List recipientDevices + ) { + try (final var connection = database.getConnection()) { + connection.setAutoCommit(false); + insertRecipientsForExistingContent(contentId, recipientDevices, connection); + connection.commit(); + } catch (SQLException e) { + logger.warn("Failed to append recipients to message send log", e); + } + } + + private void insertRecipientsForExistingContent( + final long contentId, final List recipientDevices, final Connection connection + ) throws SQLException { + try (final var statement = connection.prepareStatement( + "INSERT INTO %s (recipient_id, device_id, content_id) VALUES (?,?,?)".formatted(TABLE_MESSAGE_SEND_LOG))) { + for (final var recipientDevice : recipientDevices) { + for (final var deviceId : recipientDevice.deviceIds()) { + statement.setLong(1, recipientDevice.recipientId().id()); + statement.setInt(2, deviceId); + statement.setLong(3, contentId); + statement.executeUpdate(); + } + } + } + } + + private void deleteOutdatedEntries(final Connection connection) throws SQLException { + try (final var statement = connection.prepareStatement("DELETE FROM %s WHERE timestamp < ?".formatted( + TABLE_MESSAGE_SEND_LOG_CONTENT))) { + statement.setLong(1, System.currentTimeMillis() - LOG_DURATION.toMillis()); + final var rowCount = statement.executeUpdate(); + if (rowCount > 0) { + logger.debug("Removed {} outdated entries from the message send log", rowCount); + } + } + } + + private void deleteOrphanedLogContents(final Connection connection) throws SQLException { + try (final var statement = connection.prepareStatement( + "DELETE FROM %s WHERE _id NOT IN (SELECT content_id FROM %s)".formatted(TABLE_MESSAGE_SEND_LOG_CONTENT, + TABLE_MESSAGE_SEND_LOG))) { + statement.executeUpdate(); + } + } + + private Stream executeQueryForStream( + PreparedStatement statement, ResultSetMapper mapper + ) throws SQLException { + final var resultSet = statement.executeQuery(); + + return StreamSupport.stream(new Spliterators.AbstractSpliterator<>(Long.MAX_VALUE, Spliterator.ORDERED) { + @Override + public boolean tryAdvance(final Consumer consumer) { + try { + if (!resultSet.next()) { + return false; + } + consumer.accept(mapper.apply(resultSet)); + return true; + } catch (SQLException e) { + logger.warn("Failed to read from database result", e); + throw new RuntimeException(e); + } + } + }, false); + } + + private interface ResultSetMapper { + + T apply(ResultSet resultSet) throws SQLException; + } + + private record RecipientDevices(RecipientId recipientId, List deviceIds) {} +} 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 9cc6ba42..a5947ef2 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 @@ -164,6 +164,21 @@ public class SenderKeySharedStore { } } + public void deleteSharedWith( + final RecipientId recipientId, final int deviceId, final DistributionId distributionId + ) { + synchronized (sharedSenderKeys) { + final var entries = sharedSenderKeys.getOrDefault(distributionId.asUuid(), Set.of()); + + sharedSenderKeys.put(distributionId.asUuid(), new HashSet<>(entries) { + { + remove(new SenderKeySharedEntry(recipientId, deviceId)); + } + }); + saveLocked(); + } + } + public void deleteAllFor(final DistributionId distributionId) { synchronized (sharedSenderKeys) { if (sharedSenderKeys.remove(distributionId.asUuid()) != null) { 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 8674945c..5318b3f2 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 @@ -71,6 +71,10 @@ public class SenderKeyStore implements SignalServiceSenderKeyStore { senderKeySharedStore.deleteAllFor(recipientId); } + public void deleteSharedWith(RecipientId recipientId, int deviceId, DistributionId distributionId) { + senderKeySharedStore.deleteSharedWith(recipientId, deviceId, distributionId); + } + public void deleteOurKey(RecipientId selfRecipientId, DistributionId distributionId) { senderKeySharedStore.deleteAllFor(distributionId); senderKeyRecordStore.deleteSenderKey(selfRecipientId, distributionId.asUuid()); From 2e74acaabe9e24dda980c19ac174be10157f4578 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 29 Jan 2022 12:35:19 +0100 Subject: [PATCH 011/938] Add --log-file parameter to write logs to separate file Use logback for more control over the log output Fixes #845 --- build.gradle.kts | 3 +- graalvm-config-dir/reflect-config.json | 53 ++++++++ graalvm-config-dir/resource-config.json | 3 + man/signal-cli.1.adoc | 4 + src/main/java/org/asamk/signal/App.java | 3 + .../org/asamk/signal/LogConfigurator.java | 114 ++++++++++++++++++ src/main/java/org/asamk/signal/Main.java | 39 +++--- .../ch.qos.logback.classic.spi.Configurator | 1 + 8 files changed, 199 insertions(+), 21 deletions(-) create mode 100644 src/main/java/org/asamk/signal/LogConfigurator.java create mode 100644 src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator diff --git a/build.gradle.kts b/build.gradle.kts index 19ed08d7..fa87a606 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,7 +37,8 @@ dependencies { implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.1") implementation("net.sourceforge.argparse4j", "argparse4j", "0.9.0") implementation("com.github.hypfvieh", "dbus-java-transport-native-unixsocket", "4.0.0") - implementation("org.slf4j", "slf4j-simple", "1.7.32") + implementation("org.slf4j", "slf4j-api", "1.7.32") + implementation("ch.qos.logback", "logback-classic", "1.2.10") implementation("org.slf4j", "jul-to-slf4j", "1.7.32") implementation(project(":lib")) } diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index c5e61cee..46c106eb 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -38,6 +38,30 @@ "allDeclaredMethods":true, "allPublicMethods":true }, +{ + "name":"ch.qos.logback.classic.pattern.DateConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.classic.pattern.LevelConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.classic.pattern.LineSeparatorConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.classic.pattern.LoggerConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.classic.pattern.MessageConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"ch.qos.logback.classic.pattern.ThreadConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"char[]" }, @@ -176,6 +200,13 @@ "allDeclaredMethods":true, "allPublicMethods":true }, +{ + "name":"java.io.File", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.io.FilePermission" +}, { "name":"java.io.Serializable", "allDeclaredMethods":true @@ -228,6 +259,9 @@ "allDeclaredFields":true, "queryAllDeclaredMethods":true }, +{ + "name":"java.lang.RuntimePermission" +}, { "name":"java.lang.String", "allPublicMethods":true @@ -248,6 +282,16 @@ {"name":"getType","parameterTypes":[] } ] }, +{ + "name":"java.net.NetPermission" +}, +{ + "name":"java.net.SocketPermission" +}, +{ + "name":"java.net.URLPermission", + "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] +}, { "name":"java.nio.Buffer", "allDeclaredMethods":true, @@ -258,12 +302,18 @@ "allDeclaredMethods":true, "allPublicMethods":true }, +{ + "name":"java.security.AllPermission" +}, { "name":"java.security.KeyStoreSpi" }, { "name":"java.security.SecureRandomParameters" }, +{ + "name":"java.security.SecurityPermission" +}, { "name":"java.security.cert.PKIXRevocationChecker" }, @@ -322,6 +372,9 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, +{ + "name":"java.util.PropertyPermission" +}, { "name":"java.util.RandomAccess", "allDeclaredMethods":true diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index a52689ae..c0866365 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -4,6 +4,9 @@ { "pattern":"\\QMETA-INF/maven/org.xerial/sqlite-jdbc/pom.properties\\E" }, + { + "pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E" + }, { "pattern":"\\QMETA-INF/services/java.sql.Driver\\E" }, diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index b71f549c..e7296207 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -37,6 +37,10 @@ Print the version and quit. *--verbose*:: Raise log level and include lib signal logs. +*--log-file* LOG_FILE:: +Write log output to the given file. +If `--verbose` is also given, the detailed logs will only be written to the log file. + *--config* CONFIG:: Set the path, where to store the config. Make sure you have full read/write access to the given directory. diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 6c4726d5..d06cd798 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -69,6 +69,9 @@ public class App { parser.addArgument("--verbose") .help("Raise log level and include lib signal logs. Specify multiple times for even more logs.") .action(Arguments.count()); + parser.addArgument("--log-file") + .type(File.class) + .help("Write log output to the given file. If --verbose is also given, the detailed logs will only be written to the log file."); parser.addArgument("-c", "--config") .help("Set the path, where to store the config (Default: $XDG_DATA_HOME/signal-cli , $HOME/.local/share/signal-cli)."); diff --git a/src/main/java/org/asamk/signal/LogConfigurator.java b/src/main/java/org/asamk/signal/LogConfigurator.java new file mode 100644 index 00000000..9271d6b7 --- /dev/null +++ b/src/main/java/org/asamk/signal/LogConfigurator.java @@ -0,0 +1,114 @@ +package org.asamk.signal; + +import java.io.File; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.Configurator; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.FileAppender; +import ch.qos.logback.core.Layout; +import ch.qos.logback.core.encoder.LayoutWrappingEncoder; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.FilterReply; + +public class LogConfigurator extends ContextAwareBase implements Configurator { + + private static int verboseLevel = 0; + private static File logFile = null; + + public static void setVerboseLevel(int verboseLevel) { + LogConfigurator.verboseLevel = verboseLevel; + } + + public static void setLogFile(File logFile) { + LogConfigurator.logFile = logFile; + } + + public void configure(LoggerContext lc) { + final var rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); + + final var defaultLevel = verboseLevel > 1 ? Level.ALL : verboseLevel > 0 ? Level.DEBUG : Level.INFO; + rootLogger.setLevel(defaultLevel); + + final var consoleLayout = verboseLevel == 0 || logFile != null + ? createSimpleLoggingLayout(lc) + : createDetailedLoggingLayout(lc); + final var consoleAppender = createLoggingConsoleAppender(lc, createLayoutWrappingEncoder(consoleLayout)); + rootLogger.addAppender(consoleAppender); + + lc.getLogger("com.zaxxer.hikari") + .setLevel(verboseLevel > 1 ? Level.ALL : verboseLevel > 0 ? Level.INFO : Level.WARN); + + if (logFile != null) { + consoleAppender.addFilter(new Filter<>() { + @Override + public FilterReply decide(final ILoggingEvent event) { + return event.getLevel().isGreaterOrEqual(Level.INFO) + && !"LibSignal".equals(event.getLoggerName()) + && ( + event.getLevel().isGreaterOrEqual(Level.WARN) || !event.getLoggerName() + .startsWith("com.zaxxer.hikari") + ) + + ? FilterReply.NEUTRAL : FilterReply.DENY; + } + }); + + final var fileLayout = createDetailedLoggingLayout(lc); + final var fileAppender = createLoggingFileAppender(lc, createLayoutWrappingEncoder(fileLayout)); + rootLogger.addAppender(fileAppender); + } + } + + private ConsoleAppender createLoggingConsoleAppender( + final LoggerContext lc, final LayoutWrappingEncoder layoutEncoder + ) { + return new ConsoleAppender<>() {{ + setContext(lc); + setName("console"); + setTarget("System.err"); + setEncoder(layoutEncoder); + start(); + }}; + } + + private FileAppender createLoggingFileAppender( + final LoggerContext lc, final LayoutWrappingEncoder layoutEncoder + ) { + return new FileAppender<>() {{ + setContext(lc); + setName("file"); + setFile(logFile.getAbsolutePath()); + setEncoder(layoutEncoder); + start(); + }}; + } + + private LayoutWrappingEncoder createLayoutWrappingEncoder(final Layout l) { + return new LayoutWrappingEncoder<>() {{ + setContext(l.getContext()); + setLayout(l); + }}; + } + + private PatternLayout createSimpleLoggingLayout(final LoggerContext lc) { + return new PatternLayout() {{ + setPattern("%-5level %logger{0} - %msg%n"); + setContext(lc); + start(); + }}; + } + + private PatternLayout createDetailedLoggingLayout(final LoggerContext lc) { + return new PatternLayout() {{ + setPattern("%d{yyyy-MM-dd'T'HH:mm:ss.SSSXX} [%thread] %-5level %logger{36} - %msg%n"); + setContext(lc); + start(); + }}; + } +} diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 104a9105..f0b78590 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -17,6 +17,7 @@ package org.asamk.signal; import net.sourceforge.argparse4j.ArgumentParsers; +import net.sourceforge.argparse4j.DefaultSettings; import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.ArgumentParserException; import net.sourceforge.argparse4j.inf.Namespace; @@ -31,6 +32,7 @@ import org.asamk.signal.util.SecurityProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.bridge.SLF4JBridgeHandler; +import java.io.File; import java.security.Security; public class Main { @@ -41,8 +43,11 @@ public class Main { installSecurityProviderWorkaround(); // Configuring the logger needs to happen before any logger is initialized - final var verboseLevel = getVerboseLevel(args); - configureLogging(verboseLevel); + + final var nsLog = parseArgs(args); + final var verboseLevel = nsLog == null ? 0 : nsLog.getInt("verbose"); + final var logFile = nsLog == null ? null : nsLog.get("log-file"); + configureLogging(verboseLevel, logFile); var parser = App.buildArgumentParser(); @@ -70,35 +75,29 @@ public class Main { Security.addProvider(new BouncyCastleProvider()); } - private static int getVerboseLevel(String[] args) { - var parser = ArgumentParsers.newFor("signal-cli").build().defaultHelp(false); + private static Namespace parseArgs(String[] args) { + var parser = ArgumentParsers.newFor("signal-cli", DefaultSettings.VERSION_0_9_0_DEFAULT_SETTINGS) + .includeArgumentNamesAsKeysInResult(true) + .build() + .defaultHelp(false); parser.addArgument("--verbose").action(Arguments.count()); + parser.addArgument("--log-file").type(File.class); - Namespace ns; try { - ns = parser.parseKnownArgs(args, null); + return parser.parseKnownArgs(args, null); } catch (ArgumentParserException e) { - return 0; + return null; } - - return ns.getInt("verbose"); } - private static void configureLogging(final int verboseLevel) { - final var defaultLogLevel = verboseLevel > 1 ? "trace" : verboseLevel > 0 ? "debug" : "info"; - System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", defaultLogLevel); + private static void configureLogging(final int verboseLevel, final File logFile) { + LogConfigurator.setVerboseLevel(verboseLevel); + LogConfigurator.setLogFile(logFile); + if (verboseLevel > 0) { - System.setProperty("org.slf4j.simpleLogger.showThreadName", "true"); - System.setProperty("org.slf4j.simpleLogger.showShortLogName", "false"); - System.setProperty("org.slf4j.simpleLogger.showDateTime", "true"); - System.setProperty("org.slf4j.simpleLogger.dateTimeFormat", "yyyy-MM-dd'T'HH:mm:ss.SSSXX"); java.util.logging.Logger.getLogger("") .setLevel(verboseLevel > 2 ? java.util.logging.Level.FINEST : java.util.logging.Level.INFO); Manager.initLogger(); - } else { - System.setProperty("org.slf4j.simpleLogger.showThreadName", "false"); - System.setProperty("org.slf4j.simpleLogger.showShortLogName", "true"); - System.setProperty("org.slf4j.simpleLogger.showDateTime", "false"); } SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install(); diff --git a/src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator b/src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator new file mode 100644 index 00000000..354cf5bd --- /dev/null +++ b/src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator @@ -0,0 +1 @@ +org.asamk.signal.LogConfigurator From 380c892e24fe2d60cd8963855511c15ca9858e7b Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 29 Jan 2022 15:01:34 +0100 Subject: [PATCH 012/938] Add more informative thread names --- .../org/asamk/signal/manager/ManagerImpl.java | 4 ++++ .../storage/sendLog/MessageSendLogStore.java | 1 + .../org/asamk/signal/commands/DaemonCommand.java | 16 ++++++++++++---- .../asamk/signal/dbus/DbusSignalControlImpl.java | 6 ++++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 7c8cff5d..fd536d10 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -81,6 +81,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; import java.util.stream.Collectors; @@ -749,6 +750,8 @@ public class ManagerImpl implements Manager { } } + private static final AtomicInteger threadNumber = new AtomicInteger(0); + private void startReceiveThreadIfRequired() { if (receiveThread != null) { return; @@ -784,6 +787,7 @@ public class ManagerImpl implements Manager { } } }); + receiveThread.setName("receive-" + threadNumber.getAndIncrement()); receiveThread.start(); } 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 795919f6..98f4ac69 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 @@ -62,6 +62,7 @@ public class MessageSendLogStore implements AutoCloseable { logger.debug("Stopping msl cleanup thread"); } }); + cleanupThread.setName("msl-cleanup"); cleanupThread.setDaemon(true); cleanupThread.start(); } diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 020c91c1..34a80226 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -36,6 +36,7 @@ import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; public class DaemonCommand implements MultiLocalCommand, LocalCommand { @@ -234,8 +235,10 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { }); } + private static final AtomicInteger threadNumber = new AtomicInteger(0); + private void runSocket(final ServerSocketChannel serverChannel, Consumer socketHandler) { - new Thread(() -> { + final var thread = new Thread(() -> { while (true) { final SocketChannel channel; final String clientString; @@ -250,16 +253,20 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { } break; } - new Thread(() -> { + final var connectionThread = new Thread(() -> { try (final var c = channel) { socketHandler.accept(c); logger.info("Connection closed: " + clientString); } catch (IOException e) { logger.warn("Failed to close channel", e); } - }).start(); + }); + connectionThread.setName("daemon-connection-" + threadNumber.getAndIncrement()); + connectionThread.start(); } - }).start(); + }); + thread.setName("daemon-listener"); + thread.start(); } private SignalJsonRpcDispatcherHandler getSignalJsonRpcDispatcherHandler( @@ -367,6 +374,7 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { final var signal = new DbusSignalImpl(m, conn, objectPath, noReceiveOnStart); conn.exportObject(signal); final var initThread = new Thread(signal::initObjects); + initThread.setName("dbus-init"); initThread.start(); logger.debug("Exported dbus object: " + objectPath); diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java index a11d8fa0..2d211444 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java @@ -96,14 +96,16 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { public String link(final String newDeviceName) throws Error.Failure { try { final URI deviceLinkUri = c.getNewProvisioningDeviceLinkUri(); - new Thread(() -> { + final var thread = new Thread(() -> { final ProvisioningManager provisioningManager = c.getProvisioningManagerFor(deviceLinkUri); try { provisioningManager.finishDeviceLink(newDeviceName); } catch (IOException | TimeoutException | UserAlreadyExists e) { e.printStackTrace(); } - }).start(); + }); + thread.setName("dbus-link"); + thread.start(); return deviceLinkUri.toString(); } catch (TimeoutException | IOException e) { throw new SignalControl.Error.Failure(e.getClass().getSimpleName() + " " + e.getMessage()); From e284b990768799354bc16a6ca9387f17a9841968 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 29 Jan 2022 15:02:30 +0100 Subject: [PATCH 013/938] Refactor JsonMessageEnvelope to remove unnecessary number canonicalization --- .../signal/json/JsonMessageEnvelope.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java index 0c1d0738..ab5d73e1 100644 --- a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java +++ b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java @@ -4,9 +4,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.UntrustedIdentityException; -import org.asamk.signal.manager.api.InvalidNumberException; import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.RecipientIdentifier; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import java.util.UUID; @@ -27,35 +27,35 @@ public record JsonMessageEnvelope( public static JsonMessageEnvelope from( MessageEnvelope envelope, Throwable exception, Manager m ) { + final RecipientAddress sourceAddress; + final Integer sourceDevice; + if (envelope.sourceAddress().isPresent()) { + sourceAddress = envelope.sourceAddress().get(); + sourceDevice = envelope.sourceDevice(); + } else if (exception instanceof UntrustedIdentityException e) { + sourceAddress = e.getSender(); + sourceDevice = e.getSenderDevice(); + } else { + sourceAddress = null; + sourceDevice = null; + } + final String source; final String sourceNumber; final String sourceUuid; - final Integer sourceDevice; - if (envelope.sourceAddress().isPresent()) { - final var sourceAddress = envelope.sourceAddress().get(); + final String sourceName; + if (sourceAddress != null) { source = sourceAddress.getLegacyIdentifier(); sourceNumber = sourceAddress.number().orElse(null); sourceUuid = sourceAddress.uuid().map(UUID::toString).orElse(null); - sourceDevice = envelope.sourceDevice(); - } else if (exception instanceof UntrustedIdentityException e) { - final var sender = e.getSender(); - source = sender.getLegacyIdentifier(); - sourceNumber = sender.number().orElse(null); - sourceUuid = sender.uuid().map(UUID::toString).orElse(null); - sourceDevice = e.getSenderDevice(); + sourceName = m.getContactOrProfileName(RecipientIdentifier.Single.fromAddress(envelope.sourceAddress() + .get())); } else { source = null; sourceNumber = null; sourceUuid = null; - sourceDevice = null; + sourceName = null; } - String name; - try { - name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(source, m.getSelfNumber())); - } catch (InvalidNumberException | NullPointerException e) { - name = null; - } - final var sourceName = name; final var timestamp = envelope.timestamp(); final var receiptMessage = envelope.receipt().map(JsonReceiptMessage::from).orElse(null); final var typingMessage = envelope.typing().map(JsonTypingMessage::from).orElse(null); From e5a8cdb056e247eed0486d7e856b8826cb4998a3 Mon Sep 17 00:00:00 2001 From: exquo <62397152+exquo@users.noreply.github.com> Date: Tue, 1 Feb 2022 19:57:32 +0000 Subject: [PATCH 014/938] Repackage signal-client native builds (#879) * Repackage signal-client native builds * Change repo to upstream * Use `listAccounts` to test run signal-cli * Use "macOS" in filename --- .github/workflows/ci.yml | 2 +- .github/workflows/repackage-native-libs.yml | 172 ++++++++++++++++++++ README.md | 2 +- 3 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/repackage-native-libs.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff73584b..17f23ddf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,6 @@ name: signal-cli CI -on: [ push, pull_request ] +on: [ push, pull_request, workflow_call ] jobs: build: diff --git a/.github/workflows/repackage-native-libs.yml b/.github/workflows/repackage-native-libs.yml new file mode 100644 index 00000000..9f731bd7 --- /dev/null +++ b/.github/workflows/repackage-native-libs.yml @@ -0,0 +1,172 @@ +name: repackage-native-libs + +on: + push: + tags: + - v* + + +jobs: + + ci_wf: + uses: AsamK/signal-cli/.github/workflows/ci.yml@master + # ${{ github.repository }} not accpeted here + + + lib_to_jar: + needs: ci_wf + runs-on: ubuntu-latest + + outputs: + signal_cli_version: ${{ steps.cli_ver.outputs.signal_cli_version }} + release_id: ${{ steps.create_release.outputs.id }} + + steps: + + - name: Download signal-cli build from CI workflow + uses: actions/download-artifact@v2 + + - name: Get signal-cli version + id: cli_ver + run: | + #echo ${GITHUB_REF#refs/tag/} + tree . + mv ./*/*.tar.gz . + ver=$(ls ./*.tar.gz | xargs basename | sed -E 's/signal-cli-(.*).tar.gz/\1/') + echo $ver + echo "::set-output name=signal_cli_version::${ver}" + tar -xzf ./*.tar.gz + + - name: Get signal-client jar version + id: lib_ver + run: | + JAR_PREFIX=signal-client-java- + jar_file=$(find ./signal-cli-*/lib/ -name "$JAR_PREFIX*.jar") + jar_version=$(echo "$jar_file" | xargs basename | sed "s/$JAR_PREFIX//; s/.jar//") + echo "$jar_version" + echo "::set-output name=signal_client_version::$jar_version" + + - name: Download signal-client builds + env: + RELEASES_URL: https://github.com/signalapp/libsignal-client/releases/download/ + FILE_NAMES: signal_jni.dll libsignal_jni.dylib + SIGNAL_CLIENT_VER: ${{ steps.lib_ver.outputs.signal_client_version }} + run: | + for file_name in $FILE_NAMES; do + curl -sOL "${RELEASES_URL}/v${SIGNAL_CLIENT_VER}/${file_name}" # note: added v + done + tree . + + - name: Replace Windows lib + env: + SIGNAL_CLI_VER: ${{ steps.cli_ver.outputs.signal_cli_version }} + SIGNAL_CLIENT_VER: ${{ steps.lib_ver.outputs.signal_client_version }} + run: | + mv signal_jni.dll libsignal_jni.so + zip -u ./signal-cli-${SIGNAL_CLI_VER}/lib/signal-client-java-${SIGNAL_CLIENT_VER}.jar ./libsignal_jni.so + tar -czf signal-cli-${SIGNAL_CLI_VER}-Windows.tar.gz signal-cli-${SIGNAL_CLI_VER}/ + + - name: Replace macOS lib + env: + SIGNAL_CLI_VER: ${{ steps.cli_ver.outputs.signal_cli_version }} + SIGNAL_CLIENT_VER: ${{ steps.lib_ver.outputs.signal_client_version }} + run: | + jar_file=./signal-cli-${SIGNAL_CLI_VER}/lib/signal-client-java-${SIGNAL_CLIENT_VER}.jar + zip -d "$jar_file" libsignal_jni.so + zip "$jar_file" libsignal_jni.dylib + tar -czf signal-cli-${SIGNAL_CLI_VER}-macOS.tar.gz signal-cli-${SIGNAL_CLI_VER}/ + + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ steps.cli_ver.outputs.signal_cli_version }} # note: added `v` + release_name: v${{ steps.cli_ver.outputs.signal_cli_version }} # note: added `v` + draft: true + + - name: Upload Linux archive + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: signal-cli-${{ steps.cli_ver.outputs.signal_cli_version }}.tar.gz + asset_name: signal-cli-${{ steps.cli_ver.outputs.signal_cli_version }}-Linux.tar.gz + asset_content_type: application/x-compressed-tar # .tar.gz + + - name: Upload windows archive + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: signal-cli-${{ steps.cli_ver.outputs.signal_cli_version }}-Windows.tar.gz + asset_name: signal-cli-${{ steps.cli_ver.outputs.signal_cli_version }}-Windows.tar.gz + asset_content_type: application/x-compressed-tar # .tar.gz + + - name: Upload macos archive + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: signal-cli-${{ steps.cli_ver.outputs.signal_cli_version }}-macOS.tar.gz + asset_name: signal-cli-${{ steps.cli_ver.outputs.signal_cli_version }}-macOS.tar.gz + asset_content_type: application/x-compressed-tar # .tar.gz + + + run_repackaged: + + needs: + - lib_to_jar + + strategy: + matrix: + runner: + - windows-latest + - macos-latest + + runs-on: ${{ matrix.runner }} + + defaults: + run: + shell: bash # Explicit for windows + + env: + JAVA_VERSION: 17 + + steps: + + - name: Download the release file + env: + SIGNAL_CLI_VER: ${{ needs.lib_to_jar.outputs.signal_cli_version }} + RELEASE_ID: ${{ needs.lib_to_jar.outputs.release_id }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + file_name=signal-cli-${SIGNAL_CLI_VER}-${RUNNER_OS}.tar.gz + echo "$file_name" + assets_json=$(curl -s \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets") + asset_dl_url=$(echo "$assets_json" | jq -r ".[] | select (.name == \"$file_name\") | .url") + echo "$asset_dl_url" + curl -sLOJ \ + -H 'Accept: application/octet-stream' \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + "$asset_dl_url" + tar -xzf "$file_name" + + - name: Set up JDK for running signal-cli executable + uses: actions/setup-java@v1 + with: + java-version: ${{ env.JAVA_VERSION }} + + - name: Run signal-cli + run: | + cd signal-cli-*/bin + if [[ "$RUNNER_OS" == 'Windows' ]]; then + EXECUTABLE_SUFFIX=".bat" + fi + ./signal-cli${EXECUTABLE_SUFFIX} listAccounts diff --git a/README.md b/README.md index dcfbad66..277b843e 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ System requirements: - at least Java Runtime Environment (JRE) 17 - native library: libsignal-client - The native lib is bundled for x86_64 Linux (with recent enough glibc, see #643), for other systems/architectures + The native libs are bundled for x86_64 Linux (with recent enough glibc, see #643), Windows and MacOS. For other systems/architectures see: [Provide native lib for libsignal](https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal) ### Install system-wide on Linux From be0993c5d8171aff9190e152b095677be4088112 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 30 Jan 2022 16:35:11 +0100 Subject: [PATCH 015/938] Improve JSON-RPC subscribeReceive method with subscription id --- graalvm-config-dir/reflect-config.json | 5 +- .../signal/manager/MultiAccountManager.java | 2 + .../manager/MultiAccountManagerImpl.java | 7 + man/signal-cli-jsonrpc.5.adoc | 16 ++ man/signal-cli.1.adoc | 7 +- .../asamk/signal/commands/DaemonCommand.java | 6 +- .../dbus/DbusMultiAccountManagerImpl.java | 8 + .../asamk/signal/jsonrpc/JsonRpcReader.java | 3 +- .../SignalJsonRpcDispatcherHandler.java | 147 ++++++++++++++---- 9 files changed, 158 insertions(+), 43 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 46c106eb..50d3c5d1 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -269,7 +269,10 @@ { "name":"java.lang.Throwable", "queryAllPublicMethods":true, - "methods":[{"name":"addSuppressed","parameterTypes":["java.lang.Throwable"] }] + "methods":[ + {"name":"addSuppressed","parameterTypes":["java.lang.Throwable"] }, + {"name":"getSuppressed","parameterTypes":[] } + ] }, { "name":"java.lang.reflect.Method", diff --git a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java index 32b03d3a..15b60594 100644 --- a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java @@ -10,6 +10,8 @@ public interface MultiAccountManager extends AutoCloseable { List getAccountNumbers(); + List getManagers(); + void addOnManagerAddedHandler(Consumer handler); void addOnManagerRemovedHandler(Consumer handler); diff --git a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java index dc72aeeb..83f0bb26 100644 --- a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java @@ -49,6 +49,13 @@ public class MultiAccountManagerImpl implements MultiAccountManager { } } + @Override + public List getManagers() { + synchronized (managers) { + return new ArrayList<>(managers); + } + } + void addManager(final Manager m) { synchronized (managers) { if (managers.contains(m)) { diff --git a/man/signal-cli-jsonrpc.5.adoc b/man/signal-cli-jsonrpc.5.adoc index 0a2ed950..f0b62ca9 100644 --- a/man/signal-cli-jsonrpc.5.adoc +++ b/man/signal-cli-jsonrpc.5.adoc @@ -76,6 +76,22 @@ The `method` field is the command name and the parameters can be sent as the `pa `--attachment ATTACH` becomes `"attachment":"ATTACH"` +=== Additional JSON-RPC commands + +For receiving message additional commands are provided in JSON-RPC mode with `--receive-mode=manual`. + +==== subscribeReceive + +Tells the daemon to start receiving messages, returns the subscription id as a single integer value in the result. + +==== unsubscribeReceive + +Stop a previous subscription for receiving messages. + +Params: + +- `subscription`: the subscription id returned by `subscribeReceive` + == Examples REQUEST: `{"jsonrpc":"2.0","method":"listGroups","id":"5"}` RESPONSE: `{"jsonrpc":"2.0","result":[...],"id":"5"}` diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index e7296207..02ae4ca4 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -8,7 +8,7 @@ vim:set ts=4 sw=4 tw=82 noet: == Name -signal-cli - A commandline and dbus interface for the Signal messenger +signal-cli - A commandline interface for the Signal messenger == Synopsis @@ -20,7 +20,7 @@ signal-cli is a commandline interface for libsignal-service-java. It supports registering, verifying, sending and receiving messages. For registering you need a phone number where you can receive SMS or incoming calls. signal-cli was primarily developed to be used on servers to notify admins of important events. -For this use-case, it has a dbus interface, that can be used to send messages from any programming language that has dbus bindings. +For this use-case, it has a dbus and a JSON-RPC interface, that can be used to send messages from other programs. For some functionality the Signal protocol requires that all messages have been received from the server. The `receive` command should be regularly executed. @@ -54,8 +54,9 @@ This flag must not be given for the `link` command. It is optional for the `daemon` command. For all other commands it is only optional if there is exactly one local user in the config directory. -*--service-environment* ENVIRONMENT +*--service-environment* ENVIRONMENT:: Choose the server environment to use: + - `live` (default) - `staging` diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 34a80226..043b19c2 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -155,7 +155,7 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { final var receiveMode = ns.get("receive-mode"); final var ignoreAttachments = Boolean.TRUE.equals(ns.getBoolean("ignore-attachments")); - c.getAccountNumbers().stream().map(c::getManager).filter(Objects::nonNull).forEach(m -> { + c.getManagers().forEach(m -> { m.setIgnoreAttachments(ignoreAttachments); addDefaultReceiveHandler(m, noReceiveStdOut ? null : outputWriter, receiveMode != ReceiveMode.ON_START); }); @@ -317,10 +317,8 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { connection.unExportObject(path); }); - final var initThreads = c.getAccountNumbers() + final var initThreads = c.getManagers() .stream() - .map(c::getManager) - .filter(Objects::nonNull) .map(m -> exportMultiAccountManager(connection, m, noReceiveOnStart)) .filter(Objects::nonNull) .toList(); diff --git a/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java index 9659fea5..2c5f720f 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java @@ -46,6 +46,14 @@ public class DbusMultiAccountManagerImpl implements MultiAccountManager { .toList(); } + @Override + public List getManagers() { + return signalControl.listAccounts() + .stream() + .map(a -> (Manager) new DbusManagerImpl(getRemoteObject(a, Signal.class), connection)) + .toList(); + } + @Override public void addOnManagerAddedHandler(final Consumer handler) { synchronized (onManagerAddedHandlers) { diff --git a/src/main/java/org/asamk/signal/jsonrpc/JsonRpcReader.java b/src/main/java/org/asamk/signal/jsonrpc/JsonRpcReader.java index 5b641266..f3784a2c 100644 --- a/src/main/java/org/asamk/signal/jsonrpc/JsonRpcReader.java +++ b/src/main/java/org/asamk/signal/jsonrpc/JsonRpcReader.java @@ -90,10 +90,11 @@ public class JsonRpcReader { String input = lineSupplier.get(); if (input == null) { - // Reached end of input stream + logger.trace("Reached end of JSON-RPC input stream."); break; } + logger.trace("Incoming JSON-RPC message: {}", input); JsonRpcMessage message = parseJsonRpcMessage(input); if (message == null) continue; diff --git a/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java b/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java index 8d13ad28..086681f7 100644 --- a/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java +++ b/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java @@ -5,7 +5,9 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ContainerNode; +import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.asamk.signal.commands.Command; @@ -21,6 +23,7 @@ import org.asamk.signal.json.JsonReceiveMessageHandler; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.MultiAccountManager; import org.asamk.signal.manager.RegistrationManager; +import org.asamk.signal.manager.api.Pair; import org.asamk.signal.output.JsonWriter; import org.asamk.signal.util.Util; import org.slf4j.Logger; @@ -29,8 +32,9 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.channels.OverlappingFileLockException; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; public class SignalJsonRpcDispatcherHandler { @@ -47,7 +51,7 @@ public class SignalJsonRpcDispatcherHandler { private final boolean noReceiveOnStart; private MultiAccountManager c; - private final Map receiveHandlers = new HashMap<>(); + private final Map>> receiveHandlers = new HashMap<>(); private Manager m; @@ -64,7 +68,7 @@ public class SignalJsonRpcDispatcherHandler { this.c = c; if (!noReceiveOnStart) { - c.getAccountNumbers().stream().map(c::getManager).filter(Objects::nonNull).forEach(this::subscribeReceive); + this.subscribeReceive(c.getManagers()); c.addOnManagerAddedHandler(this::subscribeReceive); c.addOnManagerRemovedHandler(this::unsubscribeReceive); } @@ -85,33 +89,46 @@ public class SignalJsonRpcDispatcherHandler { handleConnection(); } - private void subscribeReceive(final Manager m) { - if (receiveHandlers.containsKey(m)) { - return; - } + private static final AtomicInteger nextSubscriptionId = new AtomicInteger(0); - final var receiveMessageHandler = new JsonReceiveMessageHandler(m, - s -> jsonRpcSender.sendRequest(JsonRpcRequest.forNotification("receive", - objectMapper.valueToTree(s), - null))); - m.addReceiveHandler(receiveMessageHandler); - receiveHandlers.put(m, receiveMessageHandler); - - while (!m.hasCaughtUpWithOldMessages()) { - try { - synchronized (m) { - m.wait(); - } - } catch (InterruptedException ignored) { - } - } + private int subscribeReceive(final Manager manager) { + return subscribeReceive(List.of(manager)); } - void unsubscribeReceive(final Manager m) { - final var receiveMessageHandler = receiveHandlers.remove(m); - if (receiveMessageHandler != null) { - m.removeReceiveHandler(receiveMessageHandler); + private int subscribeReceive(final List managers) { + final var subscriptionId = nextSubscriptionId.getAndIncrement(); + final var handlers = managers.stream().map(m -> { + final var receiveMessageHandler = new JsonReceiveMessageHandler(m, s -> { + final ContainerNode params = objectMapper.valueToTree(s); + ((ObjectNode) params).set("subscription", IntNode.valueOf(subscriptionId)); + jsonRpcSender.sendRequest(JsonRpcRequest.forNotification("receive", params, null)); + }); + m.addReceiveHandler(receiveMessageHandler); + return new Pair<>(m, (Manager.ReceiveMessageHandler) receiveMessageHandler); + }).toList(); + receiveHandlers.put(subscriptionId, handlers); + + return subscriptionId; + } + + private boolean unsubscribeReceive(final int subscriptionId) { + final var handlers = receiveHandlers.remove(subscriptionId); + if (handlers == null) { + return false; } + for (final var pair : handlers) { + unsubscribeReceiveHandler(pair); + } + return true; + } + + private void unsubscribeReceive(final Manager m) { + final var subscriptionId = receiveHandlers.entrySet() + .stream() + .filter(e -> e.getValue().size() == 1 && e.getValue().get(0).first().equals(m)) + .map(Map.Entry::getKey) + .findFirst(); + subscriptionId.ifPresent(this::unsubscribeReceive); } private void handleConnection() { @@ -119,16 +136,28 @@ public class SignalJsonRpcDispatcherHandler { jsonRpcReader.readMessages((method, params) -> handleRequest(objectMapper, method, params), response -> logger.debug("Received unexpected response for id {}", response.getId())); } finally { - receiveHandlers.forEach(Manager::removeReceiveHandler); + receiveHandlers.forEach((_subscriptionId, handlers) -> handlers.forEach(this::unsubscribeReceiveHandler)); receiveHandlers.clear(); } } + private void unsubscribeReceiveHandler(final Pair pair) { + final var m = pair.first(); + final var handler = pair.second(); + m.removeReceiveHandler(handler); + } + private JsonNode handleRequest( final ObjectMapper objectMapper, final String method, ContainerNode params ) throws JsonRpcException { var command = getCommand(method); if (c != null) { + if (command instanceof JsonRpcSingleCommand jsonRpcCommand) { + final var manager = getManagerFromParams(params); + if (manager != null) { + return runCommand(objectMapper, params, new CommandRunnerImpl<>(manager, jsonRpcCommand)); + } + } if (command instanceof JsonRpcMultiCommand jsonRpcCommand) { return runCommand(objectMapper, params, new MultiCommandRunnerImpl<>(c, jsonRpcCommand)); } @@ -168,10 +197,15 @@ public class SignalJsonRpcDispatcherHandler { null)); } - private Manager getManagerFromParams(final ContainerNode params) { - if (params != null && params.has("account")) { + private Manager getManagerFromParams(final ContainerNode params) throws JsonRpcException { + if (params != null && params.hasNonNull("account")) { final var manager = c.getManager(params.get("account").asText()); ((ObjectNode) params).remove("account"); + if (manager == null) { + throw new JsonRpcException(new JsonRpcResponse.Error(JsonRpcResponse.Error.INVALID_PARAMS, + "Specified account does not exist", + null)); + } return manager; } return null; @@ -322,7 +356,7 @@ public class SignalJsonRpcDispatcherHandler { command.handleCommand(requestParams, jsonWriter); } - private class SubscribeReceiveCommand implements JsonRpcSingleCommand { + private class SubscribeReceiveCommand implements JsonRpcSingleCommand, JsonRpcMultiCommand { @Override public String getName() { @@ -333,22 +367,67 @@ public class SignalJsonRpcDispatcherHandler { public void handleCommand( final Void request, final Manager m, final JsonWriter jsonWriter ) throws CommandException { - subscribeReceive(m); + final var subscriptionId = subscribeReceive(m); + jsonWriter.write(subscriptionId); + } + + @Override + public void handleCommand( + final Void request, final MultiAccountManager c, final JsonWriter jsonWriter + ) throws CommandException { + final var subscriptionId = subscribeReceive(c.getManagers()); + jsonWriter.write(subscriptionId); } } - private class UnsubscribeReceiveCommand implements JsonRpcSingleCommand { + private class UnsubscribeReceiveCommand implements JsonRpcSingleCommand, JsonRpcMultiCommand { @Override public String getName() { return "unsubscribeReceive"; } + @Override + public TypeReference getRequestType() { + return new TypeReference<>() {}; + } + @Override public void handleCommand( - final Void request, final Manager m, final JsonWriter jsonWriter + final JsonNode request, final Manager m, final JsonWriter jsonWriter ) throws CommandException { - unsubscribeReceive(m); + final var subscriptionId = getSubscriptionId(request); + if (subscriptionId == null) { + unsubscribeReceive(m); + } else { + if (!unsubscribeReceive(subscriptionId)) { + throw new UserErrorException("Unknown subscription id"); + } + } + } + + @Override + public void handleCommand( + final JsonNode request, final MultiAccountManager c, final JsonWriter jsonWriter + ) throws CommandException { + final var subscriptionId = getSubscriptionId(request); + if (subscriptionId == null) { + throw new UserErrorException("Missing subscription parameter with subscription id"); + } else { + if (!unsubscribeReceive(subscriptionId)) { + throw new UserErrorException("Unknown subscription id"); + } + } + } + + private Integer getSubscriptionId(final JsonNode request) { + if (request instanceof ArrayNode req) { + return req.get(0).asInt(); + } else if (request instanceof ObjectNode req) { + return req.get("subscription").asInt(); + } else { + return null; + } } } } From 641856ebb41b1c93afc442d0a4cb76f88d8bf513 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 1 Feb 2022 21:52:34 +0100 Subject: [PATCH 016/938] Bump version --- CHANGELOG.md | 14 ++++++++++++++ build.gradle.kts | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a642ab2..c35f160a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ ## [Unreleased] +## [0.10.3] - 2022-02-01 +### Added +- MessageSendLog to cache sent message for 24h. + For resending messages in case the recipient fails to decrypt the message. +- New global `--log-file` parameter to write logs to a separate file. + (`--verbose` can be used to increase the log level) + +### Improved +- Better subscription handling for JSON-RPC `subscribeReceive` command + +### Fixed +- Output receipt data for unsealed sender receipts again +- Fix sending message resend requests to devices that happen to have the same deviceId + ## [0.10.2] - 2022-01-22 ### Fixed - Archive old sessions/sender keys when a recipient's identity key has changed diff --git a/build.gradle.kts b/build.gradle.kts index fa87a606..ddd37d49 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.9" } -version = "0.10.2" +version = "0.10.3" java { sourceCompatibility = JavaVersion.VERSION_17 From 8c6b90936534ccc2f543f203ecc21c49b27b9037 Mon Sep 17 00:00:00 2001 From: Sebastian Scheibner Date: Wed, 2 Feb 2022 15:02:21 +0100 Subject: [PATCH 017/938] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 277b843e..942c6564 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ See [latest version](https://github.com/AsamK/signal-cli/releases). ```sh export VERSION= -wget https://github.com/AsamK/signal-cli/releases/download/v"${VERSION}"/signal-cli-"${VERSION}".tar.gz -sudo tar xf signal-cli-"${VERSION}".tar.gz -C /opt +wget https://github.com/AsamK/signal-cli/releases/download/v"${VERSION}"/signal-cli-"${VERSION}"-Linux.tar.gz +sudo tar xf signal-cli-"${VERSION}"-Linux.tar.gz -C /opt sudo ln -sf /opt/signal-cli-"${VERSION}"/bin/signal-cli /usr/local/bin/ ``` From 73d44ba3fee81e5a19f1055bd90366e37f0341ec Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 2 Feb 2022 19:34:30 +0100 Subject: [PATCH 018/938] Switch to a less cpu intensive function to check if libsignal-client is available --- graalvm-config-dir/jni-config.json | 4 ++++ .../java/org/asamk/signal/manager/config/ServiceConfig.java | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index acbf7f3f..18ba01fc 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -19,6 +19,10 @@ {"name":"loadClass","parameterTypes":["java.lang.String"] } ] }, +{ + "name":"java.lang.IllegalArgumentException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"java.lang.IllegalStateException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] 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 8ecaa741..78decc81 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 @@ -47,7 +47,10 @@ public class ServiceConfig { public static boolean isSignalClientAvailable() { try { - org.signal.client.internal.Native.DeviceTransfer_GeneratePrivateKey(); + try { + org.signal.client.internal.Native.UuidCiphertext_CheckValidContents(new byte[0]); + } catch (IllegalArgumentException ignored) { + } return true; } catch (UnsatisfiedLinkError e) { logger.warn("Failed to call libsignal-client: {}", e.getMessage()); From aadd908464cbcd89222aea4603bebd4a9c5ee2b1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 3 Feb 2022 19:31:09 +0100 Subject: [PATCH 019/938] Add new CA certificate to trust store for chat server. Existing one expires in 2023. --- .../asamk/signal/manager/config/whisper.store | Bin 1107 -> 2655 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/lib/src/main/resources/org/asamk/signal/manager/config/whisper.store b/lib/src/main/resources/org/asamk/signal/manager/config/whisper.store index 664ca9563ce30963c1481225f08a7e8438a917c2..8815e396bac1558ec4ec8c05ab71507230aa829e 100644 GIT binary patch delta 1623 zcmcIjdoiC57$J7jV##=>Aw(V#jYmR=3}Xq4L_?G!WQedK zi?YZo>k;LZH$#stLM!i`>735#?AiTu|G4L#@B8jO=R5a&cYtpaiw6Jz#wn~o4!l}c z1W&t1h??}=E$O4|bWQ>Q(ykIOgP?-|CeZtwpQ{hS*CQ~{!|$BOMS{C40DzIVnV>3X zoilk#O%u}L^ht<;d<`++ExB+Q2!mmPrj;3?stZv~1sk7Zl;{eFv&or|AP>K4oJri8 zAPAz602E9_LHs;$DuM?CquLNLkO&v>VFV0ZeY`#W=@Q5^LP+%W@bQ?fW)~v93%Ws9g-0jR9AW3kSq^U`C z5EsLIhQ_TRA0L4%)jLY8bk@2@O22@ighp2UT=sbc&zW9ePxVIRB%OYO+Ld#;SU*8L*y^`RsVd zzE(cn!e@OTpUTomV0Fx96`Hf{Y%3yP5Ax!Cd?CsJP1yd^i#9k_e0KK5ngP`0gw2+M z)pb#qYplz2TqgG0F+18d=?-NIwu=`si_tYD4rs@%qQbJNp9W-<>kGB;YH|XWo`3J9`bST0Q*sn;{v>4~pbYVRl^9bnxCa*f22Y&fLgd45XusD4ax=}n0zH^BP~u?tgKVn@70bh09x{PBohC~actQ}SHP z@~*Pak*6l)LM4LSM{P)C#UOUk?m&)|S6P_#ke)!}NQOvK(ntnNJSZvA!PA%4?LtbA zz2MxpR3O^l`y3Rbcepusx1w3??ryR=xVME3IWq+(i(qgN0Q21;S4fDHZyXu~cM%BA z&+$NLF2-;%5`uFYgyRxI2r&e4ac*Rda&3ePk5oCm1R+<>=vgQV3}N6!qLG`; z_opqQI{~KvZFMV$3L_Hqu}-q8IjZZ54}V%q(0jw}OY2NzYm9HPx1W(Ezr7HWPkyX- z)P*Q)!H86}6dsY9(f)HL`8Nf3tikqlyG!eM`);b~2iKRqj`|WpVD!5a&iwg3%B_~y zsb(bhcou!o+R85e62WK6MNJ|8dR-hO_n}nENOn%w2>W`hF&^_W$OR%jZHX%aPqrjU7zSa&&qtO-I~23hukV;-ts>VY&59VtNT9DnCH`L zsd2nPiC)vlSnX!N*B`1)ugk0@2eU69op{hlD(h)-35p-x{(q=&3&norN}O67=QjZZHAyj delta 63 zcmcaFa+yPcfq{V$h(#VsCg>-0G2UMGjroJ3{^AwU`wVIr7}&RORFGp4W!U}cf0Nmc Tke5sf7vARlKDoHy-33(uqO=)i From 285bfafdc13fa3de2376e01d9390233ce5b6ebfa Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 4 Feb 2022 15:43:20 +0100 Subject: [PATCH 020/938] Log exception when saving file fails --- .../java/org/asamk/signal/manager/storage/SignalAccount.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 862971c9..f0cdba2b 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 @@ -768,7 +768,7 @@ public class SignalAccount implements Closeable { fileChannel.force(false); } } catch (Exception e) { - logger.error("Error saving file: {}", e.getMessage()); + logger.error("Error saving file: {}", e.getMessage(), e); } } } From b9f66248ac82422f9fac644737857478b7f2d075 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 6 Feb 2022 11:13:01 +0100 Subject: [PATCH 021/938] Use RecipientAddress in AvatarStore --- graalvm-config-dir/reflect-config.json | 4 ++++ .../org/asamk/signal/manager/AvatarStore.java | 24 ++++++++----------- .../signal/manager/helper/ProfileHelper.java | 11 +++++---- .../signal/manager/helper/SyncHelper.java | 9 +++---- .../signal/manager/storage/SignalAccount.java | 9 ++++--- 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 50d3c5d1..56351226 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -3226,6 +3226,10 @@ "name":"sun.security.x509.CRLDistributionPointsExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, +{ + "name":"sun.security.x509.ExtendedKeyUsageExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, { "name":"sun.security.x509.KeyUsageExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] diff --git a/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java b/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java index bfeef725..b4bd188c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java @@ -1,9 +1,9 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.Utils; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.StreamDetails; import java.io.File; @@ -20,11 +20,11 @@ public class AvatarStore { this.avatarsPath = avatarsPath; } - public StreamDetails retrieveContactAvatar(SignalServiceAddress address) throws IOException { + public StreamDetails retrieveContactAvatar(RecipientAddress address) throws IOException { return retrieveAvatar(getContactAvatarFile(address)); } - public StreamDetails retrieveProfileAvatar(SignalServiceAddress address) throws IOException { + public StreamDetails retrieveProfileAvatar(RecipientAddress address) throws IOException { return retrieveAvatar(getProfileAvatarFile(address)); } @@ -33,11 +33,11 @@ public class AvatarStore { return retrieveAvatar(groupAvatarFile); } - public void storeContactAvatar(SignalServiceAddress address, AvatarStorer storer) throws IOException { + public void storeContactAvatar(RecipientAddress address, AvatarStorer storer) throws IOException { storeAvatar(getContactAvatarFile(address), storer); } - public void storeProfileAvatar(SignalServiceAddress address, AvatarStorer storer) throws IOException { + public void storeProfileAvatar(RecipientAddress address, AvatarStorer storer) throws IOException { storeAvatar(getProfileAvatarFile(address), storer); } @@ -45,7 +45,7 @@ public class AvatarStore { storeAvatar(getGroupAvatarFile(groupId), storer); } - public void deleteProfileAvatar(SignalServiceAddress address) throws IOException { + public void deleteProfileAvatar(RecipientAddress address) throws IOException { deleteAvatar(getProfileAvatarFile(address)); } @@ -77,16 +77,12 @@ public class AvatarStore { return new File(avatarsPath, "group-" + groupId.toBase64().replace("/", "_")); } - private File getContactAvatarFile(SignalServiceAddress address) { - return new File(avatarsPath, "contact-" + getLegacyIdentifier(address)); + private File getContactAvatarFile(RecipientAddress address) { + return new File(avatarsPath, "contact-" + address.getLegacyIdentifier()); } - private String getLegacyIdentifier(final SignalServiceAddress address) { - return address.getNumber().or(() -> address.getAci().toString()); - } - - private File getProfileAvatarFile(SignalServiceAddress address) { - return new File(avatarsPath, "profile-" + getLegacyIdentifier(address)); + private File getProfileAvatarFile(RecipientAddress address) { + return new File(avatarsPath, "profile-" + address.getLegacyIdentifier()); } private void createAvatarsDir() throws IOException { 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 f1f5d1de..ec4d385e 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 @@ -4,6 +4,7 @@ import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.recipients.Profile; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.ProfileUtils; @@ -131,7 +132,7 @@ public final class ProfileHelper { if (uploadProfile) { try (final var streamDetails = avatar == null ? context.getAvatarStore() - .retrieveProfileAvatar(account.getSelfAddress()) + .retrieveProfileAvatar(account.getSelfRecipientAddress()) : avatar.isPresent() ? Utils.createStreamDetailsFromFile(avatar.get()) : null) { final var avatarPath = dependencies.getAccountManager() .setVersionedProfile(account.getAci(), @@ -150,10 +151,10 @@ public final class ProfileHelper { if (avatar != null) { if (avatar.isPresent()) { context.getAvatarStore() - .storeProfileAvatar(account.getSelfAddress(), + .storeProfileAvatar(account.getSelfRecipientAddress(), outputStream -> IOUtils.copyFileToStream(avatar.get(), outputStream)); } else { - context.getAvatarStore().deleteProfileAvatar(account.getSelfAddress()); + context.getAvatarStore().deleteProfileAvatar(account.getSelfRecipientAddress()); } } account.getProfileStore().storeProfile(account.getSelfRecipientId(), newProfile); @@ -219,7 +220,7 @@ public final class ProfileHelper { var profile = account.getProfileStore().getProfile(recipientId); if (profile == null || !Objects.equals(avatarPath, profile.getAvatarUrlPath())) { logger.trace("Downloading profile avatar for {}", recipientId); - downloadProfileAvatar(context.getRecipientHelper().resolveSignalServiceAddress(recipientId), + downloadProfileAvatar(account.getRecipientStore().resolveRecipientAddress(recipientId), avatarPath, profileKey); var builder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile); @@ -330,7 +331,7 @@ public final class ProfileHelper { } private void downloadProfileAvatar( - SignalServiceAddress address, String avatarPath, ProfileKey profileKey + RecipientAddress address, String avatarPath, ProfileKey profileKey ) { if (avatarPath == null) { try { 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 01726cb0..75bd296d 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 @@ -4,6 +4,7 @@ import org.asamk.signal.manager.TrustLevel; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.recipients.Contact; +import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.IOUtils; import org.slf4j.Logger; @@ -128,7 +129,7 @@ public class SyncHelper { var profileKey = account.getProfileStore().getProfileKey(recipientId); out.write(new DeviceContact(address, Optional.fromNullable(contact.getName()), - createContactAvatarAttachment(address), + createContactAvatarAttachment(new RecipientAddress(address)), Optional.fromNullable(contact.getColor()), Optional.fromNullable(verifiedMessage), Optional.fromNullable(profileKey), @@ -264,7 +265,7 @@ public class SyncHelper { account.getContactStore().storeContact(recipientId, builder.build()); if (c.getAvatar().isPresent()) { - downloadContactAvatar(c.getAvatar().get(), c.getAddress()); + downloadContactAvatar(c.getAvatar().get(), new RecipientAddress(c.getAddress())); } } } @@ -309,7 +310,7 @@ public class SyncHelper { context.getSendHelper().sendSyncMessage(message); } - private Optional createContactAvatarAttachment(SignalServiceAddress address) throws IOException { + private Optional createContactAvatarAttachment(RecipientAddress address) throws IOException { final var streamDetails = context.getAvatarStore().retrieveContactAvatar(address); if (streamDetails == null) { return Optional.absent(); @@ -318,7 +319,7 @@ public class SyncHelper { return Optional.of(AttachmentUtils.createAttachment(streamDetails, Optional.absent())); } - private void downloadContactAvatar(SignalServiceAttachment avatar, SignalServiceAddress address) { + private void downloadContactAvatar(SignalServiceAttachment avatar, RecipientAddress address) { try { context.getAvatarStore() .storeContactAvatar(address, 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 f0cdba2b..250bd36c 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 @@ -554,7 +554,7 @@ public class SignalAccount implements Closeable { if (legacyRecipientStore != null) { getRecipientStore().resolveRecipientsTrusted(legacyRecipientStore.getAddresses()); } - getSelfRecipientId(); + getRecipientStore().resolveRecipientTrusted(getSelfRecipientAddress()); migrated = true; } @@ -914,9 +914,12 @@ public class SignalAccount implements Closeable { return new SignalServiceAddress(aci, account); } + public RecipientAddress getSelfRecipientAddress() { + return new RecipientAddress(aci == null ? null : aci.uuid(), account); + } + public RecipientId getSelfRecipientId() { - return getRecipientStore().resolveRecipientTrusted(new RecipientAddress(aci == null ? null : aci.uuid(), - account)); + return getRecipientStore().resolveRecipient(getSelfRecipientAddress()); } public String getEncryptedDeviceName() { From 3040da99c41a92d325a4caf951b6c0efd0508845 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 6 Feb 2022 13:01:35 +0100 Subject: [PATCH 022/938] Extra Database base class from AccountDatabase --- .../manager/storage/AccountDatabase.java | 33 ++++++++++ .../signal/manager/storage/Database.java | 62 ++++++++++--------- .../signal/manager/storage/SignalAccount.java | 16 ++--- 3 files changed, 73 insertions(+), 38 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java 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 new file mode 100644 index 00000000..b4315940 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java @@ -0,0 +1,33 @@ +package org.asamk.signal.manager.storage; + +import com.zaxxer.hikari.HikariDataSource; + +import org.asamk.signal.manager.storage.sendLog.MessageSendLogStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.sql.Connection; +import java.sql.SQLException; + +public class AccountDatabase extends Database { + + private final static Logger logger = LoggerFactory.getLogger(AccountDatabase.class); + private static final long DATABASE_VERSION = 1; + + private AccountDatabase(final HikariDataSource dataSource) { + super(logger, DATABASE_VERSION, dataSource); + } + + public static AccountDatabase init(File databaseFile) throws SQLException { + return initDatabase(databaseFile, AccountDatabase::new); + } + + @Override + protected void upgradeDatabase(final Connection connection, final long oldVersion) throws SQLException { + if (oldVersion < 1) { + logger.debug("Updating database: Creating message send log tables"); + MessageSendLogStore.createSql(connection); + } + } +} 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 1d69236b..88d34421 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 @@ -3,53 +3,38 @@ package org.asamk.signal.manager.storage; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; -import org.asamk.signal.manager.storage.sendLog.MessageSendLogStore; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sqlite.SQLiteConfig; import java.io.File; import java.sql.Connection; import java.sql.SQLException; +import java.util.function.Function; -public class Database implements AutoCloseable { - - private final static Logger logger = LoggerFactory.getLogger(SignalAccount.class); - private static final long DATABASE_VERSION = 1; +public abstract class Database implements AutoCloseable { + private final Logger logger; + private final long databaseVersion; private final HikariDataSource dataSource; - private Database(final HikariDataSource dataSource) { + protected Database(final Logger logger, final long databaseVersion, final HikariDataSource dataSource) { + this.logger = logger; + this.databaseVersion = databaseVersion; this.dataSource = dataSource; } - public static Database init(File databaseFile) throws SQLException { + public static T initDatabase( + File databaseFile, Function newDatabase + ) throws SQLException { HikariDataSource dataSource = null; try { dataSource = getHikariDataSource(databaseFile.getAbsolutePath()); - try (final var connection = dataSource.getConnection()) { - final var userVersion = getUserVersion(connection); - logger.trace("Current database version: {} Program database version: {}", - userVersion, - DATABASE_VERSION); - - if (userVersion > DATABASE_VERSION) { - logger.error("Database has been updated by a newer signal-cli version"); - throw new SQLException("Database has been updated by a newer signal-cli version"); - } else if (userVersion < DATABASE_VERSION) { - if (userVersion < 1) { - logger.debug("Updating database: Creating message send log tables"); - MessageSendLogStore.createSql(connection); - } - setUserVersion(connection, DATABASE_VERSION); - } - - final var result = new Database(dataSource); - dataSource = null; - return result; - } + final var result = newDatabase.apply(dataSource); + result.initDb(); + dataSource = null; + return result; } finally { if (dataSource != null) { dataSource.close(); @@ -57,7 +42,7 @@ public class Database implements AutoCloseable { } } - public Connection getConnection() throws SQLException { + public final Connection getConnection() throws SQLException { return dataSource.getConnection(); } @@ -66,6 +51,23 @@ public class Database implements AutoCloseable { dataSource.close(); } + protected final void initDb() throws SQLException { + try (final var connection = dataSource.getConnection()) { + final var userVersion = getUserVersion(connection); + logger.trace("Current database version: {} Program database version: {}", userVersion, databaseVersion); + + if (userVersion > databaseVersion) { + logger.error("Database has been updated by a newer signal-cli version"); + throw new SQLException("Database has been updated by a newer signal-cli version"); + } else if (userVersion < databaseVersion) { + upgradeDatabase(connection, userVersion); + setUserVersion(connection, databaseVersion); + } + } + } + + protected abstract void upgradeDatabase(final Connection connection, long oldVersion) throws SQLException; + private static long getUserVersion(final Connection connection) throws SQLException { try (final var statement = connection.createStatement()) { final var resultSet = statement.executeQuery("PRAGMA user_version"); 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 250bd36c..16264546 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 @@ -124,7 +124,7 @@ public class SignalAccount implements Closeable { private MessageCache messageCache; private MessageSendLogStore messageSendLogStore; - private Database database; + private AccountDatabase accountDatabase; private SignalAccount(final FileChannel fileChannel, final FileLock lock) { this.fileChannel = fileChannel; @@ -233,7 +233,7 @@ public class SignalAccount implements Closeable { } public void initDatabase() { - getDatabase(); + getAccountDatabase(); } private void clearAllPreKeys() { @@ -882,10 +882,10 @@ public class SignalAccount implements Closeable { () -> messageCache = new MessageCache(getMessageCachePath(dataPath, account))); } - public Database getDatabase() { - return getOrCreate(() -> database, () -> { + public AccountDatabase getAccountDatabase() { + return getOrCreate(() -> accountDatabase, () -> { try { - database = Database.init(getDatabaseFile(dataPath, account)); + accountDatabase = AccountDatabase.init(getDatabaseFile(dataPath, account)); } catch (SQLException e) { throw new RuntimeException(e); } @@ -894,7 +894,7 @@ public class SignalAccount implements Closeable { public MessageSendLogStore getMessageSendLogStore() { return getOrCreate(() -> messageSendLogStore, - () -> messageSendLogStore = new MessageSendLogStore(getRecipientStore(), getDatabase())); + () -> messageSendLogStore = new MessageSendLogStore(getRecipientStore(), getAccountDatabase())); } public String getAccount() { @@ -1081,9 +1081,9 @@ public class SignalAccount implements Closeable { @Override public void close() { synchronized (fileChannel) { - if (database != null) { + if (accountDatabase != null) { try { - database.close(); + accountDatabase.close(); } catch (SQLException e) { logger.warn("Failed to close account database: {}", e.getMessage(), e); } From 5cccf521032954d7ad8e3f70e3cbef2ce1293e85 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 6 Feb 2022 17:21:39 +0100 Subject: [PATCH 023/938] Cleanup manager package --- .../org/asamk/signal/manager/Manager.java | 37 ++++---- .../org/asamk/signal/manager/ManagerImpl.java | 10 +- .../signal/manager/MultiAccountManager.java | 45 +++++++++ .../manager/MultiAccountManagerImpl.java | 12 +-- .../org/asamk/signal/manager/PathConfig.java | 4 +- .../signal/manager/ProvisioningManager.java | 3 +- .../manager/ProvisioningManagerImpl.java | 7 +- .../manager/RegistrationManagerImpl.java | 2 +- .../signal/manager/SignalDependencies.java | 2 +- .../manager/SignalWebSocketHealthMonitor.java | 2 +- .../manager/api/AccountCheckException.java | 12 +++ .../{ => api}/AttachmentInvalidException.java | 2 +- .../asamk/signal/manager/api/Identity.java | 1 - .../{ => api}/NotMasterDeviceException.java | 2 +- .../{ => api}/NotRegisteredException.java | 2 +- .../StickerPackInvalidException.java | 2 +- .../signal/manager/{ => api}/TrustLevel.java | 2 +- .../{ => api}/UntrustedIdentityException.java | 2 +- .../UserAlreadyExistsException.java} | 6 +- .../manager/helper/AttachmentHelper.java | 2 +- .../asamk/signal/manager/helper/Context.java | 2 +- .../signal/manager/helper/GroupHelper.java | 2 +- .../signal/manager/helper/IdentityHelper.java | 2 +- .../helper/IncomingMessageHandler.java | 4 +- .../signal/manager/helper/ReceiveHelper.java | 2 +- .../signal/manager/helper/StickerHelper.java | 2 +- .../signal/manager/helper/StorageHelper.java | 2 +- .../signal/manager/helper/SyncHelper.java | 2 +- .../signal/manager/storage/SignalAccount.java | 2 +- .../storage/identities/IdentityInfo.java | 2 +- .../storage/identities/IdentityKeyStore.java | 2 +- .../storage/protocol/LegacyIdentityInfo.java | 2 +- .../protocol/LegacyJsonIdentityKeyStore.java | 2 +- .../stickerPacks}/JsonStickerPack.java | 2 +- .../stickerPacks}/StickerPackStore.java | 3 +- .../signal/manager/util/AttachmentUtils.java | 2 +- .../signal/manager/util/StickerUtils.java | 4 +- src/main/java/org/asamk/signal/App.java | 93 +++++++------------ .../asamk/signal/ReceiveMessageHandler.java | 2 +- .../asamk/signal/commands/BlockCommand.java | 2 +- .../signal/commands/FinishLinkCommand.java | 4 +- .../asamk/signal/commands/LinkCommand.java | 4 +- .../signal/commands/RemovePinCommand.java | 2 +- .../asamk/signal/commands/SendCommand.java | 2 +- .../asamk/signal/commands/SetPinCommand.java | 2 +- .../asamk/signal/commands/UnblockCommand.java | 2 +- .../commands/UpdateConfigurationCommand.java | 2 +- .../signal/commands/UpdateContactCommand.java | 2 +- .../signal/commands/UpdateGroupCommand.java | 2 +- .../commands/UploadStickerPackCommand.java | 2 +- .../asamk/signal/dbus/DbusManagerImpl.java | 11 +-- .../dbus/DbusProvisioningManagerImpl.java | 8 +- .../signal/dbus/DbusSignalControlImpl.java | 6 +- .../org/asamk/signal/dbus/DbusSignalImpl.java | 6 +- .../signal/json/JsonMessageEnvelope.java | 2 +- 55 files changed, 190 insertions(+), 160 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/api/AccountCheckException.java rename lib/src/main/java/org/asamk/signal/manager/{ => api}/AttachmentInvalidException.java (88%) rename lib/src/main/java/org/asamk/signal/manager/{ => api}/NotMasterDeviceException.java (82%) rename lib/src/main/java/org/asamk/signal/manager/{ => api}/NotRegisteredException.java (79%) rename lib/src/main/java/org/asamk/signal/manager/{ => api}/StickerPackInvalidException.java (80%) rename lib/src/main/java/org/asamk/signal/manager/{ => api}/TrustLevel.java (97%) rename lib/src/main/java/org/asamk/signal/manager/{ => api}/UntrustedIdentityException.java (93%) rename lib/src/main/java/org/asamk/signal/manager/{UserAlreadyExists.java => api/UserAlreadyExistsException.java} (63%) rename lib/src/main/java/org/asamk/signal/manager/{ => storage/stickerPacks}/JsonStickerPack.java (89%) rename lib/src/main/java/org/asamk/signal/manager/{ => storage/stickerPacks}/StickerPackStore.java (96%) 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 ed70bcee..b7a97c46 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -1,5 +1,7 @@ package org.asamk.signal.manager; +import org.asamk.signal.manager.api.AccountCheckException; +import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.api.Configuration; import org.asamk.signal.manager.api.Device; import org.asamk.signal.manager.api.Group; @@ -9,11 +11,14 @@ import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.InvalidStickerException; import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.MessageEnvelope; +import org.asamk.signal.manager.api.NotMasterDeviceException; +import org.asamk.signal.manager.api.NotRegisteredException; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.manager.api.StickerPack; +import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; @@ -38,7 +43,6 @@ import java.io.File; import java.io.IOException; import java.net.URI; import java.time.Duration; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; @@ -53,7 +57,7 @@ public interface Manager extends Closeable { ServiceEnvironment serviceEnvironment, String userAgent, TrustNewIdentity trustNewIdentity - ) throws IOException, NotRegisteredException { + ) throws IOException, NotRegisteredException, AccountCheckException { var pathConfig = PathConfig.createDefault(settingsPath); if (!SignalAccount.userExists(pathConfig.dataPath(), number)) { @@ -70,7 +74,16 @@ public interface Manager extends Closeable { account.initDatabase(); final var serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); - return new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); + final var manager = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); + + try { + manager.checkAccountState(); + } catch (IOException e) { + manager.close(); + throw new AccountCheckException("Error while checking account " + account + ": " + e.getMessage(), e); + } + + return manager; } static void initLogger() { @@ -81,26 +94,8 @@ public interface Manager extends Closeable { return PhoneNumberFormatter.isValidNumber(e164Number, countryCode); } - static List getAllLocalAccountNumbers(File settingsPath) { - var pathConfig = PathConfig.createDefault(settingsPath); - final var dataPath = pathConfig.dataPath(); - final var files = dataPath.listFiles(); - - if (files == null) { - return List.of(); - } - - return Arrays.stream(files) - .filter(File::isFile) - .map(File::getName) - .filter(file -> PhoneNumberFormatter.isValidNumber(file, null)) - .toList(); - } - String getSelfNumber(); - void checkAccountState() throws IOException; - /** * This is used for checking a set of phone numbers for registration on Signal * diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index fd536d10..c15f1612 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -16,6 +16,7 @@ */ package org.asamk.signal.manager; +import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.api.Configuration; import org.asamk.signal.manager.api.Device; import org.asamk.signal.manager.api.Group; @@ -24,6 +25,7 @@ import org.asamk.signal.manager.api.InactiveGroupLinkException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.InvalidStickerException; import org.asamk.signal.manager.api.Message; +import org.asamk.signal.manager.api.NotMasterDeviceException; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendGroupMessageResults; @@ -31,6 +33,7 @@ import org.asamk.signal.manager.api.SendMessageResult; import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.manager.api.StickerPack; import org.asamk.signal.manager.api.StickerPackId; +import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; @@ -50,6 +53,8 @@ import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; +import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore; import org.asamk.signal.manager.storage.stickers.Sticker; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.KeyUtils; @@ -89,7 +94,7 @@ import java.util.stream.Stream; import io.reactivex.rxjava3.disposables.CompositeDisposable; -public class ManagerImpl implements Manager { +class ManagerImpl implements Manager { private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class); @@ -166,8 +171,7 @@ public class ManagerImpl implements Manager { return account.getAccount(); } - @Override - public void checkAccountState() throws IOException { + void checkAccountState() throws IOException { context.getAccountHelper().checkAccountState(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java index 15b60594..9f1cf2fd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java @@ -1,13 +1,58 @@ package org.asamk.signal.manager; +import org.asamk.signal.manager.api.AccountCheckException; +import org.asamk.signal.manager.api.NotRegisteredException; +import org.asamk.signal.manager.config.ServiceEnvironment; +import org.asamk.signal.manager.storage.identities.TrustNewIdentity; +import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; + +import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; public interface MultiAccountManager extends AutoCloseable { + static MultiAccountManager init( + final File settingsPath, + final ServiceEnvironment serviceEnvironment, + final String userAgent, + final TrustNewIdentity trustNewIdentity + ) { + final var logger = LoggerFactory.getLogger(MultiAccountManager.class); + final var managers = getAllLocalAccountNumbers(settingsPath).stream().map(a -> { + try { + return Manager.init(a, settingsPath, serviceEnvironment, userAgent, trustNewIdentity); + } catch (NotRegisteredException | IOException | AccountCheckException e) { + logger.warn("Ignoring {}: {} ({})", a, e.getMessage(), e.getClass().getSimpleName()); + return null; + } + }).filter(Objects::nonNull).toList(); + + return new MultiAccountManagerImpl(managers, settingsPath, serviceEnvironment, userAgent); + } + + static List getAllLocalAccountNumbers(File settingsPath) { + var pathConfig = PathConfig.createDefault(settingsPath); + final var dataPath = pathConfig.dataPath(); + final var files = dataPath.listFiles(); + + if (files == null) { + return List.of(); + } + + return Arrays.stream(files) + .filter(File::isFile) + .map(File::getName) + .filter(file -> PhoneNumberFormatter.isValidNumber(file, null)) + .toList(); + } + List getAccountNumbers(); List getManagers(); diff --git a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java index 83f0bb26..4c67fd12 100644 --- a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java @@ -17,7 +17,7 @@ import java.util.Set; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -public class MultiAccountManagerImpl implements MultiAccountManager { +class MultiAccountManagerImpl implements MultiAccountManager { private final static Logger logger = LoggerFactory.getLogger(MultiAccountManagerImpl.class); @@ -25,19 +25,19 @@ public class MultiAccountManagerImpl implements MultiAccountManager { private final Set> onManagerRemovedHandlers = new HashSet<>(); private final Set managers = new HashSet<>(); private final Map provisioningManagers = new HashMap<>(); - private final File dataPath; + private final File settingsPath; private final ServiceEnvironment serviceEnvironment; private final String userAgent; public MultiAccountManagerImpl( final Collection managers, - final File dataPath, + final File settingsPath, final ServiceEnvironment serviceEnvironment, final String userAgent ) { this.managers.addAll(managers); managers.forEach(m -> m.addClosedListener(() -> this.removeManager(m))); - this.dataPath = dataPath; + this.settingsPath = settingsPath; this.serviceEnvironment = serviceEnvironment; this.userAgent = userAgent; } @@ -119,12 +119,12 @@ public class MultiAccountManagerImpl implements MultiAccountManager { } private ProvisioningManager getNewProvisioningManager() { - return ProvisioningManager.init(dataPath, serviceEnvironment, userAgent, this::addManager); + return ProvisioningManager.init(settingsPath, serviceEnvironment, userAgent, this::addManager); } @Override public RegistrationManager getNewRegistrationManager(String account) throws IOException { - return RegistrationManager.init(account, dataPath, serviceEnvironment, userAgent, this::addManager); + return RegistrationManager.init(account, settingsPath, serviceEnvironment, userAgent, this::addManager); } @Override diff --git a/lib/src/main/java/org/asamk/signal/manager/PathConfig.java b/lib/src/main/java/org/asamk/signal/manager/PathConfig.java index d045ac4a..f3bacdc3 100644 --- a/lib/src/main/java/org/asamk/signal/manager/PathConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/PathConfig.java @@ -2,11 +2,11 @@ package org.asamk.signal.manager; import java.io.File; -public record PathConfig( +record PathConfig( File dataPath, File attachmentsPath, File avatarsPath, File stickerPacksPath ) { - public static PathConfig createDefault(final File settingsPath) { + static PathConfig createDefault(final File settingsPath) { return new PathConfig(new File(settingsPath, "data"), new File(settingsPath, "attachments"), new File(settingsPath, "avatars"), diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 3aa26f31..81834220 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -1,5 +1,6 @@ package org.asamk.signal.manager; +import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironment; @@ -32,5 +33,5 @@ public interface ProvisioningManager { URI getDeviceLinkUri() throws TimeoutException, IOException; - String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists; + String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExistsException; } diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java index 29f90277..bfa51091 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java @@ -16,6 +16,7 @@ */ package org.asamk.signal.manager; +import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.storage.SignalAccount; @@ -38,7 +39,7 @@ import java.net.URI; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -public class ProvisioningManagerImpl implements ProvisioningManager { +class ProvisioningManagerImpl implements ProvisioningManager { private final static Logger logger = LoggerFactory.getLogger(ProvisioningManagerImpl.class); @@ -87,14 +88,14 @@ public class ProvisioningManagerImpl implements ProvisioningManager { } @Override - public String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists { + public String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExistsException { var ret = accountManager.getNewDeviceRegistration(tempIdentityKey); var number = ret.getNumber(); logger.info("Received link information from {}, linking in progress ...", number); if (SignalAccount.userExists(pathConfig.dataPath(), number) && !canRelinkExistingAccount(number)) { - throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.dataPath(), number)); + throw new UserAlreadyExistsException(number, SignalAccount.getFileName(pathConfig.dataPath(), number)); } var encryptedDeviceName = deviceName == null diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index b5f737e3..c48a2e99 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -47,7 +47,7 @@ import java.util.function.Consumer; import static org.asamk.signal.manager.config.ServiceConfig.capabilities; -public class RegistrationManagerImpl implements RegistrationManager { +class RegistrationManagerImpl implements RegistrationManager { private final static Logger logger = LoggerFactory.getLogger(RegistrationManagerImpl.class); diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java index 92db1f0f..cc894602 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java @@ -52,7 +52,7 @@ public class SignalDependencies { private ProfileService profileService; private SignalServiceCipher cipher; - public SignalDependencies( + SignalDependencies( final ServiceEnvironmentConfig serviceEnvironmentConfig, final String userAgent, final DynamicCredentialsProvider credentialsProvider, diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java b/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java index b905f9b7..fd4db601 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java @@ -21,7 +21,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers; * The monitor is also responsible for sending heartbeats/keep-alive messages to prevent * timeouts. */ -public final class SignalWebSocketHealthMonitor implements HealthMonitor { +final class SignalWebSocketHealthMonitor implements HealthMonitor { private final static Logger logger = LoggerFactory.getLogger(SignalWebSocketHealthMonitor.class); diff --git a/lib/src/main/java/org/asamk/signal/manager/api/AccountCheckException.java b/lib/src/main/java/org/asamk/signal/manager/api/AccountCheckException.java new file mode 100644 index 00000000..7968f615 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/api/AccountCheckException.java @@ -0,0 +1,12 @@ +package org.asamk.signal.manager.api; + +public class AccountCheckException extends Exception { + + public AccountCheckException(String message) { + super(message); + } + + public AccountCheckException(String message, Exception e) { + super(message, e); + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/AttachmentInvalidException.java b/lib/src/main/java/org/asamk/signal/manager/api/AttachmentInvalidException.java similarity index 88% rename from lib/src/main/java/org/asamk/signal/manager/AttachmentInvalidException.java rename to lib/src/main/java/org/asamk/signal/manager/api/AttachmentInvalidException.java index 78fba6e0..4c5aa32e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/AttachmentInvalidException.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/AttachmentInvalidException.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.api; public class AttachmentInvalidException extends Exception { diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Identity.java b/lib/src/main/java/org/asamk/signal/manager/api/Identity.java index 37d298f9..65d86d0a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/Identity.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/Identity.java @@ -1,6 +1,5 @@ package org.asamk.signal.manager.api; -import org.asamk.signal.manager.TrustLevel; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.whispersystems.libsignal.IdentityKey; diff --git a/lib/src/main/java/org/asamk/signal/manager/NotMasterDeviceException.java b/lib/src/main/java/org/asamk/signal/manager/api/NotMasterDeviceException.java similarity index 82% rename from lib/src/main/java/org/asamk/signal/manager/NotMasterDeviceException.java rename to lib/src/main/java/org/asamk/signal/manager/api/NotMasterDeviceException.java index 1b67597e..22cbb1f7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/NotMasterDeviceException.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/NotMasterDeviceException.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.api; public class NotMasterDeviceException extends Exception { diff --git a/lib/src/main/java/org/asamk/signal/manager/NotRegisteredException.java b/lib/src/main/java/org/asamk/signal/manager/api/NotRegisteredException.java similarity index 79% rename from lib/src/main/java/org/asamk/signal/manager/NotRegisteredException.java rename to lib/src/main/java/org/asamk/signal/manager/api/NotRegisteredException.java index c1b35a1c..8b9c5e8e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/NotRegisteredException.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/NotRegisteredException.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.api; public class NotRegisteredException extends Exception { diff --git a/lib/src/main/java/org/asamk/signal/manager/StickerPackInvalidException.java b/lib/src/main/java/org/asamk/signal/manager/api/StickerPackInvalidException.java similarity index 80% rename from lib/src/main/java/org/asamk/signal/manager/StickerPackInvalidException.java rename to lib/src/main/java/org/asamk/signal/manager/api/StickerPackInvalidException.java index 52869acd..0f0271a7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/StickerPackInvalidException.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/StickerPackInvalidException.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.api; public class StickerPackInvalidException extends Exception { diff --git a/lib/src/main/java/org/asamk/signal/manager/TrustLevel.java b/lib/src/main/java/org/asamk/signal/manager/api/TrustLevel.java similarity index 97% rename from lib/src/main/java/org/asamk/signal/manager/TrustLevel.java rename to lib/src/main/java/org/asamk/signal/manager/api/TrustLevel.java index 9d58754c..4016f487 100644 --- a/lib/src/main/java/org/asamk/signal/manager/TrustLevel.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/TrustLevel.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.api; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.internal.storage.protos.ContactRecord; diff --git a/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java b/lib/src/main/java/org/asamk/signal/manager/api/UntrustedIdentityException.java similarity index 93% rename from lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java rename to lib/src/main/java/org/asamk/signal/manager/api/UntrustedIdentityException.java index 318afd26..7328cf64 100644 --- a/lib/src/main/java/org/asamk/signal/manager/UntrustedIdentityException.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/UntrustedIdentityException.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.api; import org.asamk.signal.manager.storage.recipients.RecipientAddress; diff --git a/lib/src/main/java/org/asamk/signal/manager/UserAlreadyExists.java b/lib/src/main/java/org/asamk/signal/manager/api/UserAlreadyExistsException.java similarity index 63% rename from lib/src/main/java/org/asamk/signal/manager/UserAlreadyExists.java rename to lib/src/main/java/org/asamk/signal/manager/api/UserAlreadyExistsException.java index 905392c5..a32cd57c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/UserAlreadyExists.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/UserAlreadyExistsException.java @@ -1,13 +1,13 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.api; import java.io.File; -public class UserAlreadyExists extends Exception { +public class UserAlreadyExistsException extends Exception { private final String number; private final File fileName; - public UserAlreadyExists(String number, File fileName) { + public UserAlreadyExistsException(String number, File fileName) { this.number = number; this.fileName = fileName; } 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 223226da..b7a34152 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 @@ -1,8 +1,8 @@ package org.asamk.signal.manager.helper; -import org.asamk.signal.manager.AttachmentInvalidException; import org.asamk.signal.manager.AttachmentStore; import org.asamk.signal.manager.SignalDependencies; +import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.IOUtils; diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/Context.java b/lib/src/main/java/org/asamk/signal/manager/helper/Context.java index 3ef0bf16..11de336e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/Context.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/Context.java @@ -4,7 +4,7 @@ import org.asamk.signal.manager.AttachmentStore; import org.asamk.signal.manager.AvatarStore; import org.asamk.signal.manager.JobExecutor; import org.asamk.signal.manager.SignalDependencies; -import org.asamk.signal.manager.StickerPackStore; +import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore; import org.asamk.signal.manager.storage.SignalAccount; import java.util.function.Supplier; 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 f293280b..5e668c15 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 @@ -1,7 +1,7 @@ package org.asamk.signal.manager.helper; -import org.asamk.signal.manager.AttachmentInvalidException; import org.asamk.signal.manager.SignalDependencies; +import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.api.InactiveGroupLinkException; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.SendGroupMessageResults; 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 025971a9..149171e6 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 @@ -1,6 +1,6 @@ package org.asamk.signal.manager.helper; -import org.asamk.signal.manager.TrustLevel; +import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.util.Utils; 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 eece81d3..ac06baab 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 @@ -2,8 +2,8 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.SignalDependencies; -import org.asamk.signal.manager.TrustLevel; -import org.asamk.signal.manager.UntrustedIdentityException; +import org.asamk.signal.manager.api.TrustLevel; +import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.actions.HandleAction; import org.asamk.signal.manager.actions.RefreshPreKeysAction; import org.asamk.signal.manager.actions.RenewSessionAction; 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 631e2715..8dfc7deb 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 @@ -2,7 +2,7 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.SignalDependencies; -import org.asamk.signal.manager.UntrustedIdentityException; +import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.actions.HandleAction; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.messageCache.CachedMessage; 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 2fdb2593..c432ad9a 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 @@ -1,10 +1,10 @@ package org.asamk.signal.manager.helper; -import org.asamk.signal.manager.JsonStickerPack; import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.api.InvalidStickerException; import org.asamk.signal.manager.api.StickerPackId; import org.asamk.signal.manager.storage.SignalAccount; +import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; import org.asamk.signal.manager.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 dcf86614..960ed1d5 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 @@ -1,7 +1,7 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.SignalDependencies; -import org.asamk.signal.manager.TrustLevel; +import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.api.PhoneNumberSharingMode; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.SignalAccount; 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 75bd296d..0833add7 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 @@ -1,6 +1,6 @@ package org.asamk.signal.manager.helper; -import org.asamk.signal.manager.TrustLevel; +import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.recipients.Contact; 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 16264546..d026b25a 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 @@ -3,7 +3,7 @@ package org.asamk.signal.manager.storage; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.asamk.signal.manager.TrustLevel; +import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.configuration.ConfigurationStore; 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 410b07f6..adcf6204 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 @@ -1,6 +1,6 @@ package org.asamk.signal.manager.storage.identities; -import org.asamk.signal.manager.TrustLevel; +import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.whispersystems.libsignal.IdentityKey; 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 2bc8307b..580194f3 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 @@ -2,7 +2,7 @@ package org.asamk.signal.manager.storage.identities; import com.fasterxml.jackson.databind.ObjectMapper; -import org.asamk.signal.manager.TrustLevel; +import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.util.IOUtils; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java index 2fa19f42..a424bef8 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java @@ -1,6 +1,6 @@ package org.asamk.signal.manager.storage.protocol; -import org.asamk.signal.manager.TrustLevel; +import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.whispersystems.libsignal.IdentityKey; 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 12f7d59a..f9453a30 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 @@ -5,7 +5,7 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; -import org.asamk.signal.manager.TrustLevel; +import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.Utils; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.slf4j.Logger; diff --git a/lib/src/main/java/org/asamk/signal/manager/JsonStickerPack.java b/lib/src/main/java/org/asamk/signal/manager/storage/stickerPacks/JsonStickerPack.java similarity index 89% rename from lib/src/main/java/org/asamk/signal/manager/JsonStickerPack.java rename to lib/src/main/java/org/asamk/signal/manager/storage/stickerPacks/JsonStickerPack.java index 75e65bcb..8a585f7d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/JsonStickerPack.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/stickerPacks/JsonStickerPack.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.storage.stickerPacks; import org.asamk.signal.manager.api.StickerPack; diff --git a/lib/src/main/java/org/asamk/signal/manager/StickerPackStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/stickerPacks/StickerPackStore.java similarity index 96% rename from lib/src/main/java/org/asamk/signal/manager/StickerPackStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/stickerPacks/StickerPackStore.java index 8d755215..2943847c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/StickerPackStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/stickerPacks/StickerPackStore.java @@ -1,8 +1,9 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.storage.stickerPacks; import com.fasterxml.jackson.databind.ObjectMapper; import org.asamk.signal.manager.api.StickerPackId; +import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.Utils; import org.whispersystems.signalservice.api.util.StreamDetails; 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 aadadf95..793e452e 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 @@ -1,6 +1,6 @@ package org.asamk.signal.manager.util; -import org.asamk.signal.manager.AttachmentInvalidException; +import org.asamk.signal.manager.api.AttachmentInvalidException; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; 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 bd393397..c914db47 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 @@ -2,9 +2,9 @@ package org.asamk.signal.manager.util; import com.fasterxml.jackson.databind.ObjectMapper; -import org.asamk.signal.manager.JsonStickerPack; -import org.asamk.signal.manager.StickerPackInvalidException; +import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifestUpload; diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index d06cd798..4e5b1e9f 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -22,10 +22,11 @@ import org.asamk.signal.dbus.DbusMultiAccountManagerImpl; import org.asamk.signal.dbus.DbusProvisioningManagerImpl; import org.asamk.signal.dbus.DbusRegistrationManagerImpl; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.MultiAccountManagerImpl; -import org.asamk.signal.manager.NotRegisteredException; +import org.asamk.signal.manager.MultiAccountManager; import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.RegistrationManager; +import org.asamk.signal.manager.api.AccountCheckException; +import org.asamk.signal.manager.api.NotRegisteredException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironment; import org.asamk.signal.manager.storage.identities.TrustNewIdentity; @@ -46,8 +47,6 @@ import java.io.File; import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; import static net.sourceforge.argparse4j.DefaultSettings.VERSION_0_9_0_DEFAULT_SETTINGS; @@ -143,18 +142,18 @@ public class App { return; } - final File dataPath; - var config = ns.getString("config"); - if (config != null) { - dataPath = new File(config); - } else { - dataPath = getDefaultDataPath(); - } - if (!ServiceConfig.isSignalClientAvailable()) { throw new UserErrorException("Missing required native library dependency: libsignal-client"); } + final File configPath; + var config = ns.getString("config"); + if (config != null) { + configPath = new File(config); + } else { + configPath = getDefaultConfigPath(); + } + final var serviceEnvironmentCli = ns.get("service-environment"); final var serviceEnvironment = serviceEnvironmentCli == ServiceEnvironmentCli.LIVE ? ServiceEnvironment.LIVE @@ -170,23 +169,21 @@ public class App { throw new UserErrorException("You cannot specify a account (phone number) when linking"); } - handleProvisioningCommand(provisioningCommand, dataPath, serviceEnvironment, outputWriter); + handleProvisioningCommand(provisioningCommand, configPath, serviceEnvironment, outputWriter); return; } if (account == null) { - var accounts = Manager.getAllLocalAccountNumbers(dataPath); - if (command instanceof MultiLocalCommand multiLocalCommand) { handleMultiLocalCommand(multiLocalCommand, - dataPath, + configPath, serviceEnvironment, - accounts, outputWriter, trustNewIdentity); return; } + var accounts = MultiAccountManager.getAllLocalAccountNumbers(configPath); if (accounts.size() == 0) { throw new UserErrorException("No local users found, you first need to register or link an account"); } else if (accounts.size() > 1) { @@ -200,7 +197,7 @@ public class App { } if (command instanceof RegistrationCommand registrationCommand) { - handleRegistrationCommand(registrationCommand, account, dataPath, serviceEnvironment); + handleRegistrationCommand(registrationCommand, account, configPath, serviceEnvironment); return; } @@ -210,7 +207,7 @@ public class App { handleLocalCommand((LocalCommand) command, account, - dataPath, + configPath, serviceEnvironment, outputWriter, trustNewIdentity); @@ -218,11 +215,11 @@ public class App { private void handleProvisioningCommand( final ProvisioningCommand command, - final File dataPath, + final File configPath, final ServiceEnvironment serviceEnvironment, final OutputWriter outputWriter ) throws CommandException { - var pm = ProvisioningManager.init(dataPath, serviceEnvironment, BaseConfig.USER_AGENT); + var pm = ProvisioningManager.init(configPath, serviceEnvironment, BaseConfig.USER_AGENT); command.handleCommand(ns, pm, outputWriter); } @@ -245,12 +242,12 @@ public class App { private void handleRegistrationCommand( final RegistrationCommand command, final String account, - final File dataPath, + final File configPath, final ServiceEnvironment serviceEnvironment ) throws CommandException { final RegistrationManager manager; try { - manager = RegistrationManager.init(account, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); + manager = RegistrationManager.init(account, configPath, serviceEnvironment, BaseConfig.USER_AGENT); } catch (Throwable e) { throw new UnexpectedErrorException("Error loading or creating state file: " + e.getMessage() @@ -283,12 +280,12 @@ public class App { private void handleLocalCommand( final LocalCommand command, final String account, - final File dataPath, + final File configPath, final ServiceEnvironment serviceEnvironment, final OutputWriter outputWriter, final TrustNewIdentity trustNewIdentity ) throws CommandException { - try (var m = loadManager(account, dataPath, serviceEnvironment, trustNewIdentity)) { + try (var m = loadManager(account, configPath, serviceEnvironment, trustNewIdentity)) { command.handleCommand(ns, m, outputWriter); } catch (IOException e) { logger.warn("Cleanup failed", e); @@ -313,25 +310,15 @@ public class App { private void handleMultiLocalCommand( final MultiLocalCommand command, - final File dataPath, + final File configPath, final ServiceEnvironment serviceEnvironment, - final List accounts, final OutputWriter outputWriter, final TrustNewIdentity trustNewIdentity ) throws CommandException { - final var managers = new ArrayList(); - for (String a : accounts) { - try { - managers.add(loadManager(a, dataPath, serviceEnvironment, trustNewIdentity)); - } catch (CommandException e) { - logger.warn("Ignoring {}: {}", a, e.getMessage()); - } - } - - try (var multiAccountManager = new MultiAccountManagerImpl(managers, - dataPath, + try (var multiAccountManager = MultiAccountManager.init(configPath, serviceEnvironment, - BaseConfig.USER_AGENT)) { + BaseConfig.USER_AGENT, + trustNewIdentity)) { command.handleCommand(ns, multiAccountManager, outputWriter); } } @@ -351,16 +338,22 @@ public class App { private Manager loadManager( final String account, - final File dataPath, + final File configPath, final ServiceEnvironment serviceEnvironment, final TrustNewIdentity trustNewIdentity ) throws CommandException { - Manager manager; logger.trace("Loading account file for {}", account); try { - manager = Manager.init(account, dataPath, serviceEnvironment, BaseConfig.USER_AGENT, trustNewIdentity); + return Manager.init(account, configPath, serviceEnvironment, BaseConfig.USER_AGENT, trustNewIdentity); } catch (NotRegisteredException e) { throw new UserErrorException("User " + account + " is not registered."); + } catch (AccountCheckException ace) { + if (ace.getCause() instanceof IOException e) { + throw new IOErrorException("Error while checking account " + account + ": " + e.getMessage(), e); + } else { + throw new UnexpectedErrorException("Error while checking account " + account + ": " + ace.getMessage(), + ace); + } } catch (Throwable e) { throw new UnexpectedErrorException("Error loading state file for user " + account @@ -370,20 +363,6 @@ public class App { + e.getClass().getSimpleName() + ")", e); } - - logger.trace("Checking account state"); - try { - manager.checkAccountState(); - } catch (IOException e) { - try { - manager.close(); - } catch (IOException ie) { - logger.warn("Failed to close broken account", ie); - } - throw new IOErrorException("Error while checking account " + account + ": " + e.getMessage(), e); - } - - return manager; } private void initDbusClient( @@ -457,7 +436,7 @@ public class App { /** * @return the default data directory to be used by signal-cli. */ - private static File getDefaultDataPath() { + private static File getDefaultConfigPath() { return new File(IOUtils.getDataHomeDir(), "signal-cli"); } } diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 314b0c68..f5fce554 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -1,7 +1,7 @@ package org.asamk.signal; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.UntrustedIdentityException; +import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.groups.GroupId; diff --git a/src/main/java/org/asamk/signal/commands/BlockCommand.java b/src/main/java/org/asamk/signal/commands/BlockCommand.java index cf2b1981..7d72b1c5 100644 --- a/src/main/java/org/asamk/signal/commands/BlockCommand.java +++ b/src/main/java/org/asamk/signal/commands/BlockCommand.java @@ -7,7 +7,7 @@ import org.asamk.signal.commands.exceptions.CommandException; 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.NotMasterDeviceException; +import org.asamk.signal.manager.api.NotMasterDeviceException; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.output.OutputWriter; diff --git a/src/main/java/org/asamk/signal/commands/FinishLinkCommand.java b/src/main/java/org/asamk/signal/commands/FinishLinkCommand.java index ce0de000..f49de103 100644 --- a/src/main/java/org/asamk/signal/commands/FinishLinkCommand.java +++ b/src/main/java/org/asamk/signal/commands/FinishLinkCommand.java @@ -6,7 +6,7 @@ import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.MultiAccountManager; -import org.asamk.signal.manager.UserAlreadyExists; +import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.asamk.signal.output.JsonWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,7 +56,7 @@ public class FinishLinkCommand implements JsonRpcMultiCommand> areUsersRegistered(final Set numbers) throws IOException { final var numbersList = new ArrayList<>(numbers); diff --git a/src/main/java/org/asamk/signal/dbus/DbusProvisioningManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusProvisioningManagerImpl.java index cba2b5e4..2e026c8a 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusProvisioningManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusProvisioningManagerImpl.java @@ -2,7 +2,7 @@ package org.asamk.signal.dbus; import org.asamk.SignalControl; import org.asamk.signal.manager.ProvisioningManager; -import org.asamk.signal.manager.UserAlreadyExists; +import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.freedesktop.dbus.connections.impl.DBusConnection; import java.io.IOException; @@ -27,9 +27,7 @@ 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; @@ -47,7 +45,7 @@ public class DbusProvisioningManagerImpl implements ProvisioningManager { } @Override - public String finishDeviceLink(final String deviceName) throws IOException, TimeoutException, UserAlreadyExists { + public String finishDeviceLink(final String deviceName) throws IOException, TimeoutException, UserAlreadyExistsException { return signalControl.finishLink(deviceLinkUri.toString(), deviceName); } } diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java index 2d211444..f80b91d6 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java @@ -7,7 +7,7 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.MultiAccountManager; import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.RegistrationManager; -import org.asamk.signal.manager.UserAlreadyExists; +import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.PinLockedException; @@ -100,7 +100,7 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { final ProvisioningManager provisioningManager = c.getProvisioningManagerFor(deviceLinkUri); try { provisioningManager.finishDeviceLink(newDeviceName); - } catch (IOException | TimeoutException | UserAlreadyExists e) { + } catch (IOException | TimeoutException | UserAlreadyExistsException e) { e.printStackTrace(); } }); @@ -127,7 +127,7 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { try { final var provisioningManager = c.getProvisioningManagerFor(new URI(deviceLinkUri)); return provisioningManager.finishDeviceLink(newDeviceName); - } catch (TimeoutException | IOException | UserAlreadyExists | URISyntaxException e) { + } catch (TimeoutException | IOException | UserAlreadyExistsException | URISyntaxException e) { throw new SignalControl.Error.Failure(e.getClass().getSimpleName() + " " + e.getMessage()); } } diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index ae9edfec..671966a0 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -2,16 +2,16 @@ package org.asamk.signal.dbus; import org.asamk.Signal; import org.asamk.signal.BaseConfig; -import org.asamk.signal.manager.AttachmentInvalidException; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.NotMasterDeviceException; -import org.asamk.signal.manager.StickerPackInvalidException; +import org.asamk.signal.manager.api.StickerPackInvalidException; +import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.api.Identity; import org.asamk.signal.manager.api.InactiveGroupLinkException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.InvalidNumberException; import org.asamk.signal.manager.api.InvalidStickerException; import org.asamk.signal.manager.api.Message; +import org.asamk.signal.manager.api.NotMasterDeviceException; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendMessageResult; diff --git a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java index ab5d73e1..264b7fce 100644 --- a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java +++ b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java @@ -3,7 +3,7 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonInclude; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.UntrustedIdentityException; +import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.storage.recipients.RecipientAddress; From 8e773d92c19533cf4a00bb2e21c30bd4a85e2feb Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 6 Feb 2022 19:33:50 +0100 Subject: [PATCH 024/938] Align receive timeout behavior for dbus client with cli and JSON-RPC Timeout is reset by every incoming message --- .../asamk/signal/dbus/DbusManagerImpl.java | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 33b149d9..a7152a52 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -3,7 +3,6 @@ package org.asamk.signal.dbus; import org.asamk.Signal; import org.asamk.signal.DbusConfig; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.api.Configuration; import org.asamk.signal.manager.api.Device; @@ -19,6 +18,7 @@ import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.manager.api.StickerPack; +import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UpdateGroup; @@ -53,6 +53,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -498,12 +499,24 @@ public class DbusManagerImpl implements Manager { public void receiveMessages( final Duration timeout, final ReceiveMessageHandler handler ) throws IOException { - addReceiveHandler(handler); - try { - Thread.sleep(timeout.toMillis()); - } catch (InterruptedException ignored) { + final var lastMessage = new AtomicLong(System.currentTimeMillis()); + + final ReceiveMessageHandler receiveHandler = (envelope, e) -> { + lastMessage.set(System.currentTimeMillis()); + handler.handleMessage(envelope, e); + }; + addReceiveHandler(receiveHandler); + while (true) { + try { + final var sleepTimeRemaining = timeout.toMillis() - (System.currentTimeMillis() - lastMessage.get()); + if (sleepTimeRemaining < 0) { + break; + } + Thread.sleep(sleepTimeRemaining); + } catch (InterruptedException ignored) { + } } - removeReceiveHandler(handler); + removeReceiveHandler(receiveHandler); } @Override From 38ade4e9851f33d057c1f999cdc54e8687703a2d Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 9 Feb 2022 19:08:12 +0100 Subject: [PATCH 025/938] Close database connection before sleeping --- .../signal/manager/storage/sendLog/MessageSendLogStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 98f4ac69..39ce64be 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 @@ -52,11 +52,11 @@ public class MessageSendLogStore implements AutoCloseable { while (true) { try (final var connection = database.getConnection()) { deleteOutdatedEntries(connection); - Thread.sleep(interval); } catch (SQLException e) { logger.warn("Deleting outdated entries failed"); break; } + Thread.sleep(interval); } } catch (InterruptedException e) { logger.debug("Stopping msl cleanup thread"); From 76f27449dfea58bcd9a8bafd2f8d3c167e355a7c Mon Sep 17 00:00:00 2001 From: Simon F Date: Thu, 10 Feb 2022 06:43:55 +0000 Subject: [PATCH 026/938] Add reference to alpine linux aport to README (#887) --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 942c6564..3632d08d 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,9 @@ the [documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service) f You can [build signal-cli](#building) yourself, or use the [provided binary files](https://github.com/AsamK/signal-cli/releases/latest), which should work on Linux, macOS and -Windows. For Arch Linux there is also a [package in AUR](https://aur.archlinux.org/packages/signal-cli/) and there is -a [FreeBSD port](https://www.freshports.org/net-im/signal-cli) available as well. +Windows. For Arch Linux there is also a [package in AUR](https://aur.archlinux.org/packages/signal-cli/), as well as +a [FreeBSD port](https://www.freshports.org/net-im/signal-cli) and +an [Alpine aport](https://pkgs.alpinelinux.org/packages?name=signal-cli). System requirements: From 8966535efe6e22a0755d27ac3e0b7d00163d2d95 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 9 Feb 2022 22:47:23 +0100 Subject: [PATCH 027/938] Update reflect-config.json --- graalvm-config-dir/reflect-config.json | 51 ++++++++++++-------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 56351226..ae98cd74 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -854,32 +854,6 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, -{ - "name":"org.asamk.signal.manager.JsonStickerPack", - "allDeclaredFields":true, - "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":["java.lang.String","java.lang.String","org.asamk.signal.manager.JsonStickerPack$JsonSticker","java.util.List"] }, - {"name":"author","parameterTypes":[] }, - {"name":"cover","parameterTypes":[] }, - {"name":"stickers","parameterTypes":[] }, - {"name":"title","parameterTypes":[] } - ] -}, -{ - "name":"org.asamk.signal.manager.JsonStickerPack$JsonSticker", - "allDeclaredFields":true, - "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":["java.lang.Integer","java.lang.String","java.lang.String","java.lang.String"] }, - {"name":"contentType","parameterTypes":[] }, - {"name":"emoji","parameterTypes":[] }, - {"name":"file","parameterTypes":[] }, - {"name":"id","parameterTypes":[] } - ] -}, { "name":"org.asamk.signal.manager.api.PhoneNumberSharingMode", "allDeclaredFields":true, @@ -1081,6 +1055,30 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"author","parameterTypes":[] }, + {"name":"cover","parameterTypes":[] }, + {"name":"stickers","parameterTypes":[] }, + {"name":"title","parameterTypes":[] } + ] +}, +{ + "name":"org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack$JsonSticker", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"contentType","parameterTypes":[] }, + {"name":"emoji","parameterTypes":[] }, + {"name":"file","parameterTypes":[] }, + {"name":"id","parameterTypes":[] } + ] +}, { "name":"org.asamk.signal.manager.storage.stickers.StickerStore", "allDeclaredFields":true, @@ -1806,7 +1804,6 @@ {"name":"isAnnouncementGroup_"}, {"name":"memberCount_"}, {"name":"pendingAdminApproval_"}, - {"name":"publicKey_"}, {"name":"revision_"}, {"name":"title_"} ] From fd8ba66509d5f11f7829b0f8ea8458a0008914b7 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 10 Feb 2022 15:40:04 +0100 Subject: [PATCH 028/938] Fix behavior when reading a v1 group from storage that has already been migrated locally Fixes #888 --- .../org/asamk/signal/manager/helper/StorageHelper.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 960ed1d5..dfed3d43 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 @@ -129,18 +129,18 @@ public class StorageHelper { final var groupV1Record = record.getGroupV1().get(); final var groupIdV1 = GroupId.v1(groupV1Record.getGroupId()); - final var group = account.getGroupStore().getGroup(groupIdV1); + var group = account.getGroupStore().getGroup(groupIdV1); if (group == null) { try { context.getGroupHelper().sendGroupInfoRequest(groupIdV1, account.getSelfRecipientId()); } catch (Throwable e) { logger.warn("Failed to send group request", e); } + group = account.getGroupStore().getOrCreateGroupV1(groupIdV1); } - final var groupV1 = account.getGroupStore().getOrCreateGroupV1(groupIdV1); - if (groupV1.isBlocked() != groupV1Record.isBlocked()) { - groupV1.setBlocked(groupV1Record.isBlocked()); - account.getGroupStore().updateGroup(groupV1); + if (group != null && group.isBlocked() != groupV1Record.isBlocked()) { + group.setBlocked(groupV1Record.isBlocked()); + account.getGroupStore().updateGroup(group); } } From 6dabf4550bf7cd710a33648b898bc21fe3772d35 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 11 Feb 2022 21:01:02 +0100 Subject: [PATCH 029/938] Fix type of device id to Integer Fixes #889 --- src/main/java/org/asamk/Signal.java | 2 +- src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 6e6d9ff1..fd91d8af 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -466,7 +466,7 @@ public interface Signal extends DBusInterface { } } - @DBusProperty(name = "Id", type = Long.class, access = DBusProperty.Access.READ) + @DBusProperty(name = "Id", type = Integer.class, access = DBusProperty.Access.READ) @DBusProperty(name = "Name", type = String.class) @DBusProperty(name = "Created", type = String.class, access = DBusProperty.Access.READ) @DBusProperty(name = "LastSeen", type = String.class, access = DBusProperty.Access.READ) diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index a7152a52..bee87d74 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -169,7 +169,7 @@ public class DbusManagerImpl implements Manager { return signal.listDevices().stream().map(d -> { final var device = getRemoteObject(d.getObjectPath(), Signal.Device.class).GetAll("org.asamk.Signal.Device"); - return new Device(((Long) device.get("Id").getValue()).intValue(), + return new Device((Integer) device.get("Id").getValue(), (String) device.get("Name").getValue(), (long) device.get("Created").getValue(), (long) device.get("LastSeen").getValue(), From 1db7f8d76eaefc7f3b2ff3d4de1883ee8b302d9e Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 11 Feb 2022 20:35:52 +0100 Subject: [PATCH 030/938] Add additional logging to message send log store --- .../signal/manager/storage/sendLog/MessageSendLogStore.java | 2 ++ 1 file changed, 2 insertions(+) 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 39ce64be..c0e7d429 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 @@ -354,6 +354,8 @@ public class MessageSendLogStore implements AutoCloseable { final var rowCount = statement.executeUpdate(); if (rowCount > 0) { logger.debug("Removed {} outdated entries from the message send log", rowCount); + } else { + logger.trace("No outdated entries to be removed from message send log."); } } } From ff6b733cd0448c05f4be5aad32895cc8c748ee79 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 9 Feb 2022 19:07:23 +0100 Subject: [PATCH 031/938] Add SignalAccountFiles as a central entry point --- .../asamk/signal/manager/LibSignalLogger.java | 2 +- .../org/asamk/signal/manager/Manager.java | 45 ------ .../org/asamk/signal/manager/ManagerImpl.java | 17 +- .../asamk/signal/manager/ManagerLogger.java | 8 + .../signal/manager/MultiAccountManager.java | 45 ------ .../manager/MultiAccountManagerImpl.java | 27 +--- .../signal/manager/ProvisioningManager.java | 23 --- .../manager/ProvisioningManagerImpl.java | 45 +++++- .../signal/manager/RegistrationManager.java | 48 ------ .../manager/RegistrationManagerImpl.java | 22 ++- .../signal/manager/SignalAccountFiles.java | 152 ++++++++++++++++++ .../manager/helper/AccountFileUpdater.java | 8 + .../signal/manager/helper/AccountHelper.java | 4 +- .../asamk/signal/manager/helper/Context.java | 9 +- .../signal/manager/helper/StorageHelper.java | 4 +- .../signal/manager/storage/SignalAccount.java | 96 +++++------ .../storage/accounts/AccountsStore.java | 52 ++++++ src/main/java/org/asamk/signal/App.java | 91 +++++------ src/main/java/org/asamk/signal/Main.java | 4 +- 19 files changed, 398 insertions(+), 304 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/ManagerLogger.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/helper/AccountFileUpdater.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java diff --git a/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java b/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java index c14b0f13..ac686fc5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java +++ b/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java @@ -9,7 +9,7 @@ public class LibSignalLogger implements SignalProtocolLogger { private final static Logger logger = LoggerFactory.getLogger("LibSignal"); - public static void initLogger() { + static void initLogger() { SignalProtocolLoggerProvider.setProvider(new LibSignalLogger()); } 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 b7a97c46..9db509ad 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -1,6 +1,5 @@ package org.asamk.signal.manager; -import org.asamk.signal.manager.api.AccountCheckException; import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.api.Configuration; import org.asamk.signal.manager.api.Device; @@ -12,7 +11,6 @@ import org.asamk.signal.manager.api.InvalidStickerException; import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.NotMasterDeviceException; -import org.asamk.signal.manager.api.NotRegisteredException; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendGroupMessageResults; @@ -23,16 +21,12 @@ import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; -import org.asamk.signal.manager.config.ServiceConfig; -import org.asamk.signal.manager.config.ServiceEnvironment; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.manager.storage.SignalAccount; -import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientAddress; @@ -51,45 +45,6 @@ import java.util.UUID; public interface Manager extends Closeable { - static Manager init( - String number, - File settingsPath, - ServiceEnvironment serviceEnvironment, - String userAgent, - TrustNewIdentity trustNewIdentity - ) throws IOException, NotRegisteredException, AccountCheckException { - var pathConfig = PathConfig.createDefault(settingsPath); - - if (!SignalAccount.userExists(pathConfig.dataPath(), number)) { - throw new NotRegisteredException(); - } - - var account = SignalAccount.load(pathConfig.dataPath(), number, true, trustNewIdentity); - - if (!account.isRegistered()) { - account.close(); - throw new NotRegisteredException(); - } - - account.initDatabase(); - final var serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); - - final var manager = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); - - try { - manager.checkAccountState(); - } catch (IOException e) { - manager.close(); - throw new AccountCheckException("Error while checking account " + account + ": " + e.getMessage(), e); - } - - return manager; - } - - static void initLogger() { - LibSignalLogger.initLogger(); - } - static boolean isValidNumber(final String e164Number, final String countryCode) { return PhoneNumberFormatter.isValidNumber(e164Number, countryCode); } diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index c15f1612..742be520 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -45,6 +45,7 @@ import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; +import org.asamk.signal.manager.helper.AccountFileUpdater; import org.asamk.signal.manager.helper.Context; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfo; @@ -99,6 +100,7 @@ class ManagerImpl implements Manager { private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class); private SignalAccount account; + private final AccountFileUpdater accountFileUpdater; private final SignalDependencies dependencies; private final Context context; @@ -114,13 +116,15 @@ class ManagerImpl implements Manager { ManagerImpl( SignalAccount account, PathConfig pathConfig, + AccountFileUpdater accountFileUpdater, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent ) { this.account = account; + this.accountFileUpdater = accountFileUpdater; final var credentialsProvider = new DynamicCredentialsProvider(account.getAci(), - account.getAccount(), + account.getNumber(), account.getPassword(), account.getDeviceId()); final var sessionLock = new SignalSessionLock() { @@ -142,7 +146,12 @@ class ManagerImpl implements Manager { final var attachmentStore = new AttachmentStore(pathConfig.attachmentsPath()); final var stickerPackStore = new StickerPackStore(pathConfig.stickerPacksPath()); - this.context = new Context(account, dependencies, avatarStore, attachmentStore, stickerPackStore); + this.context = new Context(account, + accountFileUpdater, + dependencies, + avatarStore, + attachmentStore, + stickerPackStore); this.context.getAccountHelper().setUnregisteredListener(this::close); this.context.getReceiveHelper().setAuthenticationFailureListener(this::close); this.context.getReceiveHelper().setCaughtUpWithOldMessagesListener(() -> { @@ -168,7 +177,7 @@ class ManagerImpl implements Manager { @Override public String getSelfNumber() { - return account.getAccount(); + return account.getNumber(); } void checkAccountState() throws IOException { @@ -179,7 +188,7 @@ class ManagerImpl implements Manager { public Map> areUsersRegistered(Set numbers) throws IOException { final var canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> { try { - final var canonicalizedNumber = PhoneNumberFormatter.formatNumber(n, account.getAccount()); + final var canonicalizedNumber = PhoneNumberFormatter.formatNumber(n, account.getNumber()); if (!canonicalizedNumber.equals(n)) { logger.debug("Normalized number {} to {}.", n, canonicalizedNumber); } diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerLogger.java b/lib/src/main/java/org/asamk/signal/manager/ManagerLogger.java new file mode 100644 index 00000000..e5dc2c0d --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerLogger.java @@ -0,0 +1,8 @@ +package org.asamk.signal.manager; + +public class ManagerLogger { + + public static void initLogger() { + LibSignalLogger.initLogger(); + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java index 9f1cf2fd..15b60594 100644 --- a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java @@ -1,58 +1,13 @@ package org.asamk.signal.manager; -import org.asamk.signal.manager.api.AccountCheckException; -import org.asamk.signal.manager.api.NotRegisteredException; -import org.asamk.signal.manager.config.ServiceEnvironment; -import org.asamk.signal.manager.storage.identities.TrustNewIdentity; -import org.slf4j.LoggerFactory; -import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; - -import java.io.File; import java.io.IOException; import java.net.URI; -import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; public interface MultiAccountManager extends AutoCloseable { - static MultiAccountManager init( - final File settingsPath, - final ServiceEnvironment serviceEnvironment, - final String userAgent, - final TrustNewIdentity trustNewIdentity - ) { - final var logger = LoggerFactory.getLogger(MultiAccountManager.class); - final var managers = getAllLocalAccountNumbers(settingsPath).stream().map(a -> { - try { - return Manager.init(a, settingsPath, serviceEnvironment, userAgent, trustNewIdentity); - } catch (NotRegisteredException | IOException | AccountCheckException e) { - logger.warn("Ignoring {}: {} ({})", a, e.getMessage(), e.getClass().getSimpleName()); - return null; - } - }).filter(Objects::nonNull).toList(); - - return new MultiAccountManagerImpl(managers, settingsPath, serviceEnvironment, userAgent); - } - - static List getAllLocalAccountNumbers(File settingsPath) { - var pathConfig = PathConfig.createDefault(settingsPath); - final var dataPath = pathConfig.dataPath(); - final var files = dataPath.listFiles(); - - if (files == null) { - return List.of(); - } - - return Arrays.stream(files) - .filter(File::isFile) - .map(File::getName) - .filter(file -> PhoneNumberFormatter.isValidNumber(file, null)) - .toList(); - } - List getAccountNumbers(); List getManagers(); diff --git a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java index 4c67fd12..072e965b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManagerImpl.java @@ -1,10 +1,8 @@ package org.asamk.signal.manager; -import org.asamk.signal.manager.config.ServiceEnvironment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; import java.net.URI; import java.util.ArrayList; @@ -25,21 +23,12 @@ class MultiAccountManagerImpl implements MultiAccountManager { private final Set> onManagerRemovedHandlers = new HashSet<>(); private final Set managers = new HashSet<>(); private final Map provisioningManagers = new HashMap<>(); - private final File settingsPath; - private final ServiceEnvironment serviceEnvironment; - private final String userAgent; + private final SignalAccountFiles signalAccountFiles; - public MultiAccountManagerImpl( - final Collection managers, - final File settingsPath, - final ServiceEnvironment serviceEnvironment, - final String userAgent - ) { + public MultiAccountManagerImpl(final Collection managers, final SignalAccountFiles signalAccountFiles) { + this.signalAccountFiles = signalAccountFiles; this.managers.addAll(managers); managers.forEach(m -> m.addClosedListener(() -> this.removeManager(m))); - this.settingsPath = settingsPath; - this.serviceEnvironment = serviceEnvironment; - this.userAgent = userAgent; } @Override @@ -99,9 +88,9 @@ class MultiAccountManagerImpl implements MultiAccountManager { } @Override - public Manager getManager(final String account) { + public Manager getManager(final String number) { synchronized (managers) { - return managers.stream().filter(m -> m.getSelfNumber().equals(account)).findFirst().orElse(null); + return managers.stream().filter(m -> m.getSelfNumber().equals(number)).findFirst().orElse(null); } } @@ -119,12 +108,12 @@ class MultiAccountManagerImpl implements MultiAccountManager { } private ProvisioningManager getNewProvisioningManager() { - return ProvisioningManager.init(settingsPath, serviceEnvironment, userAgent, this::addManager); + return signalAccountFiles.initProvisioningManager(this::addManager); } @Override - public RegistrationManager getNewRegistrationManager(String account) throws IOException { - return RegistrationManager.init(account, settingsPath, serviceEnvironment, userAgent, this::addManager); + public RegistrationManager getNewRegistrationManager(String number) throws IOException { + return signalAccountFiles.initRegistrationManager(number, this::addManager); } @Override diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 81834220..09a329f5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -1,36 +1,13 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.api.UserAlreadyExistsException; -import org.asamk.signal.manager.config.ServiceConfig; -import org.asamk.signal.manager.config.ServiceEnvironment; -import java.io.File; import java.io.IOException; import java.net.URI; import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; public interface ProvisioningManager { - static ProvisioningManager init( - File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent - ) { - return init(settingsPath, serviceEnvironment, userAgent, null); - } - - static ProvisioningManager init( - File settingsPath, - ServiceEnvironment serviceEnvironment, - String userAgent, - Consumer newManagerListener - ) { - var pathConfig = PathConfig.createDefault(settingsPath); - - final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); - - return new ProvisioningManagerImpl(pathConfig, serviceConfiguration, userAgent, newManagerListener); - } - URI getDeviceLinkUri() throws TimeoutException, IOException; String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExistsException; diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java index bfa51091..e0f1084b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java @@ -20,6 +20,7 @@ import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.storage.SignalAccount; +import org.asamk.signal.manager.storage.accounts.AccountsStore; import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.manager.util.KeyUtils; import org.slf4j.Logger; @@ -47,6 +48,7 @@ class ProvisioningManagerImpl implements ProvisioningManager { private final ServiceEnvironmentConfig serviceEnvironmentConfig; private final String userAgent; private final Consumer newManagerListener; + private final AccountsStore accountsStore; private final SignalServiceAccountManager accountManager; private final IdentityKeyPair tempIdentityKey; @@ -57,12 +59,14 @@ class ProvisioningManagerImpl implements ProvisioningManager { PathConfig pathConfig, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent, - final Consumer newManagerListener + final Consumer newManagerListener, + final AccountsStore accountsStore ) { this.pathConfig = pathConfig; this.serviceEnvironmentConfig = serviceEnvironmentConfig; this.userAgent = userAgent; this.newManagerListener = newManagerListener; + this.accountsStore = accountsStore; tempIdentityKey = KeyUtils.generateIdentityKeyPair(); registrationId = KeyHelper.generateRegistrationId(false); @@ -91,11 +95,23 @@ class ProvisioningManagerImpl implements ProvisioningManager { public String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExistsException { var ret = accountManager.getNewDeviceRegistration(tempIdentityKey); var number = ret.getNumber(); + var aci = ret.getAci(); logger.info("Received link information from {}, linking in progress ...", number); - if (SignalAccount.userExists(pathConfig.dataPath(), number) && !canRelinkExistingAccount(number)) { - throw new UserAlreadyExistsException(number, SignalAccount.getFileName(pathConfig.dataPath(), number)); + var accountPath = accountsStore.getPathByAci(aci); + if (accountPath == null) { + accountPath = accountsStore.getPathByNumber(number); + } + if (accountPath != null + && SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath) + && !canRelinkExistingAccount(accountPath)) { + throw new UserAlreadyExistsException(number, SignalAccount.getFileName(pathConfig.dataPath(), accountPath)); + } + if (accountPath == null) { + accountPath = accountsStore.addAccount(number, aci); + } else { + accountsStore.updateAccount(accountPath, number, aci); } var encryptedDeviceName = deviceName == null @@ -115,8 +131,9 @@ class ProvisioningManagerImpl implements ProvisioningManager { SignalAccount account = null; try { account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.dataPath(), + accountPath, number, - ret.getAci(), + aci, password, encryptedDeviceName, deviceId, @@ -127,7 +144,12 @@ class ProvisioningManagerImpl implements ProvisioningManager { ManagerImpl m = null; try { - m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); + final var accountPathFinal = accountPath; + m = new ManagerImpl(account, + pathConfig, + (newNumber, newAci) -> accountsStore.updateAccount(accountPathFinal, newNumber, newAci), + serviceEnvironmentConfig, + userAgent); account = null; logger.debug("Refreshing pre keys"); @@ -162,10 +184,13 @@ class ProvisioningManagerImpl implements ProvisioningManager { } } - private boolean canRelinkExistingAccount(final String number) throws IOException { + private boolean canRelinkExistingAccount(final String accountPath) throws IOException { final SignalAccount signalAccount; try { - signalAccount = SignalAccount.load(pathConfig.dataPath(), number, false, TrustNewIdentity.ON_FIRST_USE); + signalAccount = SignalAccount.load(pathConfig.dataPath(), + accountPath, + false, + TrustNewIdentity.ON_FIRST_USE); } catch (IOException e) { logger.debug("Account in use or failed to load.", e); return false; @@ -177,7 +202,11 @@ class ProvisioningManagerImpl implements ProvisioningManager { return false; } - final var m = new ManagerImpl(signalAccount, pathConfig, serviceEnvironmentConfig, userAgent); + final var m = new ManagerImpl(signalAccount, + pathConfig, + (newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci), + serviceEnvironmentConfig, + userAgent); try (m) { m.checkAccountState(); } catch (AuthorizationFailedException ignored) { 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 f094ed92..c1c318d9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -3,60 +3,12 @@ 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.PinLockedException; -import org.asamk.signal.manager.config.ServiceConfig; -import org.asamk.signal.manager.config.ServiceEnvironment; -import org.asamk.signal.manager.storage.SignalAccount; -import org.asamk.signal.manager.storage.identities.TrustNewIdentity; -import org.asamk.signal.manager.util.KeyUtils; -import org.whispersystems.libsignal.util.KeyHelper; import java.io.Closeable; -import java.io.File; import java.io.IOException; -import java.util.function.Consumer; public interface RegistrationManager extends Closeable { - static RegistrationManager init( - String number, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent - ) throws IOException { - return init(number, settingsPath, serviceEnvironment, userAgent, null); - } - - static RegistrationManager init( - String number, - File settingsPath, - ServiceEnvironment serviceEnvironment, - String userAgent, - Consumer newManagerListener - ) throws IOException { - var pathConfig = PathConfig.createDefault(settingsPath); - - final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); - if (!SignalAccount.userExists(pathConfig.dataPath(), number)) { - var identityKey = KeyUtils.generateIdentityKeyPair(); - var registrationId = KeyHelper.generateRegistrationId(false); - - var profileKey = KeyUtils.createProfileKey(); - var account = SignalAccount.create(pathConfig.dataPath(), - number, - identityKey, - registrationId, - profileKey, - TrustNewIdentity.ON_FIRST_USE); - - return new RegistrationManagerImpl(account, - pathConfig, - serviceConfiguration, - userAgent, - newManagerListener); - } - - var account = SignalAccount.load(pathConfig.dataPath(), number, true, TrustNewIdentity.ON_FIRST_USE); - - return new RegistrationManagerImpl(account, pathConfig, serviceConfiguration, userAgent, newManagerListener); - } - void register(boolean voiceVerification, String captcha) throws IOException, CaptchaRequiredException; void verifyAccount( diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index c48a2e99..81602122 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -21,6 +21,7 @@ import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; +import org.asamk.signal.manager.helper.AccountFileUpdater; import org.asamk.signal.manager.helper.PinHelper; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.util.Utils; @@ -59,16 +60,19 @@ class RegistrationManagerImpl implements RegistrationManager { private final SignalServiceAccountManager accountManager; private final PinHelper pinHelper; + private final AccountFileUpdater accountFileUpdater; RegistrationManagerImpl( SignalAccount account, PathConfig pathConfig, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent, - Consumer newManagerListener + Consumer newManagerListener, + AccountFileUpdater accountFileUpdater ) { this.account = account; this.pathConfig = pathConfig; + this.accountFileUpdater = accountFileUpdater; this.serviceEnvironmentConfig = serviceEnvironmentConfig; this.userAgent = userAgent; this.newManagerListener = newManagerListener; @@ -82,7 +86,7 @@ class RegistrationManagerImpl implements RegistrationManager { this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), new DynamicCredentialsProvider( // Using empty UUID, because registering doesn't work otherwise - null, account.getAccount(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID), + null, account.getNumber(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, ServiceConfig.AUTOMATIC_NETWORK_RETRY); @@ -101,7 +105,7 @@ class RegistrationManagerImpl implements RegistrationManager { try { final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), new DynamicCredentialsProvider(account.getAci(), - account.getAccount(), + account.getNumber(), account.getPassword(), account.getDeviceId()), userAgent, @@ -120,7 +124,11 @@ class RegistrationManagerImpl implements RegistrationManager { account.setRegistered(true); logger.info("Reactivated existing account, verify is not necessary."); if (newManagerListener != null) { - final var m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); + final var m = new ManagerImpl(account, + pathConfig, + accountFileUpdater, + serviceEnvironmentConfig, + userAgent); account = null; newManagerListener.accept(m); } @@ -187,11 +195,13 @@ class RegistrationManagerImpl implements RegistrationManager { } //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); - account.finishRegistration(ACI.parseOrNull(response.getUuid()), masterKey, pin); + final var aci = ACI.parseOrNull(response.getUuid()); + account.finishRegistration(aci, masterKey, pin); + accountFileUpdater.updateAccountIdentifiers(account.getNumber(), aci); ManagerImpl m = null; try { - m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); + m = new ManagerImpl(account, pathConfig, accountFileUpdater, serviceEnvironmentConfig, userAgent); account = null; m.refreshPreKeys(); diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java new file mode 100644 index 00000000..f1d37e81 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -0,0 +1,152 @@ +package org.asamk.signal.manager; + +import org.asamk.signal.manager.api.AccountCheckException; +import org.asamk.signal.manager.api.NotRegisteredException; +import org.asamk.signal.manager.config.ServiceConfig; +import org.asamk.signal.manager.config.ServiceEnvironment; +import org.asamk.signal.manager.config.ServiceEnvironmentConfig; +import org.asamk.signal.manager.storage.SignalAccount; +import org.asamk.signal.manager.storage.accounts.AccountsStore; +import org.asamk.signal.manager.storage.identities.TrustNewIdentity; +import org.asamk.signal.manager.util.KeyUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.whispersystems.libsignal.util.KeyHelper; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; + +public class SignalAccountFiles { + + private static final Logger logger = LoggerFactory.getLogger(MultiAccountManager.class); + + private final PathConfig pathConfig; + private final ServiceEnvironmentConfig serviceEnvironmentConfig; + private final String userAgent; + private final TrustNewIdentity trustNewIdentity; + private final AccountsStore accountsStore; + + public SignalAccountFiles( + final File settingsPath, + final ServiceEnvironment serviceEnvironment, + final String userAgent, + final TrustNewIdentity trustNewIdentity + ) { + this.pathConfig = PathConfig.createDefault(settingsPath); + this.serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); + this.userAgent = userAgent; + this.trustNewIdentity = trustNewIdentity; + this.accountsStore = new AccountsStore(pathConfig.dataPath()); + } + + public Set getAllLocalAccountNumbers() { + return accountsStore.getAllNumbers(); + } + + public MultiAccountManager initMultiAccountManager() { + final var managers = getAllLocalAccountNumbers().stream().map(a -> { + try { + return initManager(a); + } catch (NotRegisteredException | IOException | AccountCheckException e) { + logger.warn("Ignoring {}: {} ({})", a, e.getMessage(), e.getClass().getSimpleName()); + return null; + } + }).filter(Objects::nonNull).toList(); + + return new MultiAccountManagerImpl(managers, this); + } + + public Manager initManager(String number) throws IOException, NotRegisteredException, AccountCheckException { + final var accountPath = accountsStore.getPathByNumber(number); + if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) { + throw new NotRegisteredException(); + } + + var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity); + if (!number.equals(account.getNumber())) { + account.close(); + throw new IOException("Number in account file doesn't match expected number: " + account.getNumber()); + } + + if (!account.isRegistered()) { + account.close(); + throw new NotRegisteredException(); + } + + account.initDatabase(); + + final var manager = new ManagerImpl(account, + pathConfig, + (newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci), + serviceEnvironmentConfig, + userAgent); + + try { + manager.checkAccountState(); + } catch (IOException e) { + manager.close(); + throw new AccountCheckException("Error while checking account " + number + ": " + e.getMessage(), e); + } + + return manager; + } + + public ProvisioningManager initProvisioningManager() { + return initProvisioningManager(null); + } + + public ProvisioningManager initProvisioningManager(Consumer newManagerListener) { + return new ProvisioningManagerImpl(pathConfig, + serviceEnvironmentConfig, + userAgent, + newManagerListener, + accountsStore); + } + + public RegistrationManager initRegistrationManager(String number) throws IOException { + return initRegistrationManager(number, null); + } + + public RegistrationManager initRegistrationManager( + String number, Consumer newManagerListener + ) throws IOException { + final var accountPath = accountsStore.getPathByNumber(number); + if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) { + final var newAccountPath = accountPath == null ? accountsStore.addAccount(number, null) : accountPath; + var identityKey = KeyUtils.generateIdentityKeyPair(); + var registrationId = KeyHelper.generateRegistrationId(false); + + var profileKey = KeyUtils.createProfileKey(); + var account = SignalAccount.create(pathConfig.dataPath(), + newAccountPath, + number, + identityKey, + registrationId, + profileKey, + trustNewIdentity); + + return new RegistrationManagerImpl(account, + pathConfig, + serviceEnvironmentConfig, + userAgent, + newManagerListener, + (newNumber, newAci) -> accountsStore.updateAccount(newAccountPath, newNumber, newAci)); + } + + var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity); + if (!number.equals(account.getNumber())) { + account.close(); + throw new IOException("Number in account file doesn't match expected number: " + account.getNumber()); + } + + return new RegistrationManagerImpl(account, + pathConfig, + serviceEnvironmentConfig, + userAgent, + newManagerListener, + (newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci)); + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountFileUpdater.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountFileUpdater.java new file mode 100644 index 00000000..fb6e327c --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountFileUpdater.java @@ -0,0 +1,8 @@ +package org.asamk.signal.manager.helper; + +import org.whispersystems.signalservice.api.push.ACI; + +public interface AccountFileUpdater { + + void updateAccountIdentifiers(String number, ACI aci); +} 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 2b90a4fc..3e60ce74 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 @@ -52,7 +52,9 @@ public class AccountHelper { try { context.getPreKeyHelper().refreshPreKeysIfNecessary(); if (account.getAci() == null) { - account.setAci(ACI.parseOrNull(dependencies.getAccountManager().getWhoAmI().getAci())); + final var aci = ACI.parseOrNull(dependencies.getAccountManager().getWhoAmI().getAci()); + account.setAci(aci); + context.getAccountFileUpdater().updateAccountIdentifiers(account.getNumber(), aci); } updateAccountAttributes(); } catch (AuthorizationFailedException e) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/Context.java b/lib/src/main/java/org/asamk/signal/manager/helper/Context.java index 11de336e..522143ee 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/Context.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/Context.java @@ -4,8 +4,8 @@ import org.asamk.signal.manager.AttachmentStore; import org.asamk.signal.manager.AvatarStore; import org.asamk.signal.manager.JobExecutor; import org.asamk.signal.manager.SignalDependencies; -import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore; import org.asamk.signal.manager.storage.SignalAccount; +import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore; import java.util.function.Supplier; @@ -14,6 +14,7 @@ public class Context { private final Object LOCK = new Object(); private final SignalAccount account; + private final AccountFileUpdater accountFileUpdater; private final SignalDependencies dependencies; private final AvatarStore avatarStore; private final StickerPackStore stickerPackStore; @@ -40,12 +41,14 @@ public class Context { public Context( final SignalAccount account, + final AccountFileUpdater accountFileUpdater, final SignalDependencies dependencies, final AvatarStore avatarStore, final AttachmentStore attachmentStore, final StickerPackStore stickerPackStore ) { this.account = account; + this.accountFileUpdater = accountFileUpdater; this.dependencies = dependencies; this.avatarStore = avatarStore; this.stickerPackStore = stickerPackStore; @@ -57,6 +60,10 @@ public class Context { return account; } + public AccountFileUpdater getAccountFileUpdater() { + return accountFileUpdater; + } + public SignalDependencies getDependencies() { return dependencies; } 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 dfed3d43..6f136b3c 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 @@ -1,8 +1,8 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.SignalDependencies; -import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.api.PhoneNumberSharingMode; +import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.recipients.Contact; @@ -188,7 +188,7 @@ public class StorageHelper { return; } - if (!accountRecord.getE164().equals(account.getAccount())) { + if (!accountRecord.getE164().equals(account.getNumber())) { // TODO implement changed number handling } 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 d026b25a..d77d51a2 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 @@ -3,8 +3,8 @@ package org.asamk.signal.manager.storage; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.configuration.ConfigurationStore; import org.asamk.signal.manager.storage.contacts.ContactsStore; @@ -87,7 +87,8 @@ public class SignalAccount implements Closeable { private int previousStorageVersion; private File dataPath; - private String account; + private String accountPath; + private String number; private ACI aci; private String encryptedDeviceName; private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; @@ -132,23 +133,18 @@ public class SignalAccount implements Closeable { } public static SignalAccount load( - File dataPath, String account, boolean waitForLock, final TrustNewIdentity trustNewIdentity + File dataPath, String accountPath, boolean waitForLock, final TrustNewIdentity trustNewIdentity ) throws IOException { logger.trace("Opening account file"); - final var fileName = getFileName(dataPath, account); + final var fileName = getFileName(dataPath, accountPath); final var pair = openFileChannel(fileName, waitForLock); try { var signalAccount = new SignalAccount(pair.first(), pair.second()); logger.trace("Loading account file"); - signalAccount.load(dataPath, trustNewIdentity); + signalAccount.load(dataPath, accountPath, trustNewIdentity); logger.trace("Migrating legacy parts of account file"); signalAccount.migrateLegacyConfigs(); - if (!account.equals(signalAccount.getAccount())) { - throw new IOException("Number in account file doesn't match expected number: " - + signalAccount.getAccount()); - } - return signalAccount; } catch (Throwable e) { pair.second().close(); @@ -159,14 +155,15 @@ public class SignalAccount implements Closeable { public static SignalAccount create( File dataPath, - String account, + String accountPath, + String number, IdentityKeyPair identityKey, int registrationId, ProfileKey profileKey, final TrustNewIdentity trustNewIdentity ) throws IOException { IOUtils.createPrivateDirectories(dataPath); - var fileName = getFileName(dataPath, account); + var fileName = getFileName(dataPath, accountPath); if (!fileName.exists()) { IOUtils.createPrivateFile(fileName); } @@ -174,14 +171,15 @@ public class SignalAccount implements Closeable { final var pair = openFileChannel(fileName, true); var signalAccount = new SignalAccount(pair.first(), pair.second()); - signalAccount.account = account; + signalAccount.accountPath = accountPath; + signalAccount.number = number; signalAccount.profileKey = profileKey; signalAccount.dataPath = dataPath; signalAccount.identityKeyPair = identityKey; signalAccount.localRegistrationId = registrationId; signalAccount.trustNewIdentity = trustNewIdentity; - signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account), + signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), signalAccount.getRecipientStore(), signalAccount::saveGroupStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); @@ -198,7 +196,8 @@ public class SignalAccount implements Closeable { public static SignalAccount createOrUpdateLinkedAccount( File dataPath, - String account, + String accountPath, + String number, ACI aci, String password, String encryptedDeviceName, @@ -209,10 +208,11 @@ public class SignalAccount implements Closeable { final TrustNewIdentity trustNewIdentity ) throws IOException { IOUtils.createPrivateDirectories(dataPath); - var fileName = getFileName(dataPath, account); + var fileName = getFileName(dataPath, accountPath); if (!fileName.exists()) { return createLinkedAccount(dataPath, - account, + accountPath, + number, aci, password, encryptedDeviceName, @@ -223,8 +223,8 @@ public class SignalAccount implements Closeable { trustNewIdentity); } - final var signalAccount = load(dataPath, account, true, trustNewIdentity); - signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey); + final var signalAccount = load(dataPath, accountPath, true, trustNewIdentity); + signalAccount.setProvisioningData(number, aci, password, encryptedDeviceName, deviceId, profileKey); signalAccount.getRecipientStore().resolveRecipientTrusted(signalAccount.getSelfAddress()); signalAccount.getSessionStore().archiveAllSessions(); signalAccount.getSenderKeyStore().deleteAll(); @@ -246,7 +246,8 @@ public class SignalAccount implements Closeable { private static SignalAccount createLinkedAccount( File dataPath, - String account, + String accountPath, + String number, ACI aci, String password, String encryptedDeviceName, @@ -256,19 +257,20 @@ public class SignalAccount implements Closeable { ProfileKey profileKey, final TrustNewIdentity trustNewIdentity ) throws IOException { - var fileName = getFileName(dataPath, account); + var fileName = getFileName(dataPath, accountPath); IOUtils.createPrivateFile(fileName); final var pair = openFileChannel(fileName, true); var signalAccount = new SignalAccount(pair.first(), pair.second()); - signalAccount.setProvisioningData(account, aci, password, encryptedDeviceName, deviceId, profileKey); + signalAccount.setProvisioningData(number, aci, password, encryptedDeviceName, deviceId, profileKey); signalAccount.dataPath = dataPath; + signalAccount.accountPath = accountPath; signalAccount.identityKeyPair = identityKey; signalAccount.localRegistrationId = registrationId; signalAccount.trustNewIdentity = trustNewIdentity; - signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, account), + signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), signalAccount.getRecipientStore(), signalAccount::saveGroupStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); @@ -283,14 +285,14 @@ public class SignalAccount implements Closeable { } private void setProvisioningData( - final String account, + final String number, final ACI aci, final String password, final String encryptedDeviceName, final int deviceId, final ProfileKey profileKey ) { - this.account = account; + this.number = number; this.aci = aci; this.password = password; this.profileKey = profileKey; @@ -396,7 +398,7 @@ public class SignalAccount implements Closeable { return new File(getUserPath(dataPath, account), "account.db"); } - public static boolean userExists(File dataPath, String account) { + public static boolean accountFileExists(File dataPath, String account) { if (account == null) { return false; } @@ -405,9 +407,11 @@ public class SignalAccount implements Closeable { } private void load( - File dataPath, final TrustNewIdentity trustNewIdentity + File dataPath, String accountPath, final TrustNewIdentity trustNewIdentity ) throws IOException { - JsonNode rootNode; + this.dataPath = dataPath; + this.accountPath = accountPath; + final JsonNode rootNode; synchronized (fileChannel) { fileChannel.position(0); rootNode = jsonProcessor.readTree(Channels.newInputStream(fileChannel)); @@ -423,7 +427,7 @@ public class SignalAccount implements Closeable { previousStorageVersion = accountVersion; } - account = Utils.getNotNullNode(rootNode, "username").asText(); + number = Utils.getNotNullNode(rootNode, "username").asText(); if (rootNode.hasNonNull("password")) { password = rootNode.get("password").asText(); } @@ -501,7 +505,6 @@ public class SignalAccount implements Closeable { migratedLegacyConfig = true; } - this.dataPath = dataPath; this.identityKeyPair = identityKeyPair; this.localRegistrationId = registrationId; this.trustNewIdentity = trustNewIdentity; @@ -511,11 +514,11 @@ public class SignalAccount implements Closeable { if (rootNode.hasNonNull("groupStore")) { groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class); groupStore = GroupStore.fromStorage(groupStoreStorage, - getGroupCachePath(dataPath, account), + getGroupCachePath(dataPath, accountPath), getRecipientStore(), this::saveGroupStore); } else { - groupStore = new GroupStore(getGroupCachePath(dataPath, account), + groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), getRecipientStore(), this::saveGroupStore); } @@ -732,7 +735,7 @@ public class SignalAccount implements Closeable { synchronized (fileChannel) { var rootNode = jsonProcessor.createObjectNode(); rootNode.put("version", CURRENT_STORAGE_VERSION) - .put("username", account) + .put("username", number) .put("uuid", aci == null ? null : aci.toString()) .put("deviceName", encryptedDeviceName) .put("deviceId", deviceId) @@ -821,22 +824,23 @@ public class SignalAccount implements Closeable { } private PreKeyStore getPreKeyStore() { - return getOrCreate(() -> preKeyStore, () -> preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, account))); + return getOrCreate(() -> preKeyStore, + () -> preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, accountPath))); } private SignedPreKeyStore getSignedPreKeyStore() { return getOrCreate(() -> signedPreKeyStore, - () -> signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, account))); + () -> signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, accountPath))); } public SessionStore getSessionStore() { return getOrCreate(() -> sessionStore, - () -> sessionStore = new SessionStore(getSessionsPath(dataPath, account), getRecipientStore())); + () -> sessionStore = new SessionStore(getSessionsPath(dataPath, accountPath), getRecipientStore())); } public IdentityKeyStore getIdentityKeyStore() { return getOrCreate(() -> identityKeyStore, - () -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, account), + () -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath), getRecipientStore(), identityKeyPair, localRegistrationId, @@ -853,7 +857,7 @@ public class SignalAccount implements Closeable { public RecipientStore getRecipientStore() { return getOrCreate(() -> recipientStore, - () -> recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, account), + () -> recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, accountPath), this::mergeRecipients)); } @@ -867,8 +871,8 @@ public class SignalAccount implements Closeable { public SenderKeyStore getSenderKeyStore() { return getOrCreate(() -> senderKeyStore, - () -> senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, account), - getSenderKeysPath(dataPath, account), + () -> senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, accountPath), + getSenderKeysPath(dataPath, accountPath), getRecipientStore()::resolveRecipientAddress, getRecipientStore())); } @@ -879,13 +883,13 @@ public class SignalAccount implements Closeable { public MessageCache getMessageCache() { return getOrCreate(() -> messageCache, - () -> messageCache = new MessageCache(getMessageCachePath(dataPath, account))); + () -> messageCache = new MessageCache(getMessageCachePath(dataPath, accountPath))); } public AccountDatabase getAccountDatabase() { return getOrCreate(() -> accountDatabase, () -> { try { - accountDatabase = AccountDatabase.init(getDatabaseFile(dataPath, account)); + accountDatabase = AccountDatabase.init(getDatabaseFile(dataPath, accountPath)); } catch (SQLException e) { throw new RuntimeException(e); } @@ -897,8 +901,8 @@ public class SignalAccount implements Closeable { () -> messageSendLogStore = new MessageSendLogStore(getRecipientStore(), getAccountDatabase())); } - public String getAccount() { - return account; + public String getNumber() { + return number; } public ACI getAci() { @@ -911,11 +915,11 @@ public class SignalAccount implements Closeable { } public SignalServiceAddress getSelfAddress() { - return new SignalServiceAddress(aci, account); + return new SignalServiceAddress(aci, number); } public RecipientAddress getSelfRecipientAddress() { - return new RecipientAddress(aci == null ? null : aci.uuid(), account); + return new RecipientAddress(aci == null ? null : aci.uuid(), number); } public RecipientId getSelfRecipientId() { 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 new file mode 100644 index 00000000..ed6dec97 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStore.java @@ -0,0 +1,52 @@ +package org.asamk.signal.manager.storage.accounts; + +import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; + +import java.io.File; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +public class AccountsStore { + + private final File dataPath; + + public AccountsStore(final File dataPath) { + this.dataPath = dataPath; + } + + public Set getAllNumbers() { + final var files = dataPath.listFiles(); + + if (files == null) { + return Set.of(); + } + + return Arrays.stream(files) + .filter(File::isFile) + .map(File::getName) + .filter(file -> PhoneNumberFormatter.isValidNumber(file, null)) + .collect(Collectors.toSet()); + } + + public String getPathByNumber(String number) { + return number; + } + + public String getPathByAci(ACI aci) { + return null; + } + + public void updateAccount(String path, String number, ACI aci) { + // TODO remove number and uuid from all other accounts + if (!path.equals(number)) { + throw new UnsupportedOperationException("Updating number not supported yet"); + } + } + + public String addAccount(String number, ACI aci) { + // TODO remove number and uuid from all other accounts + return number; + } +} diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 4e5b1e9f..5853e4b8 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -22,9 +22,8 @@ import org.asamk.signal.dbus.DbusMultiAccountManagerImpl; import org.asamk.signal.dbus.DbusProvisioningManagerImpl; import org.asamk.signal.dbus.DbusRegistrationManagerImpl; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.MultiAccountManager; -import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.RegistrationManager; +import org.asamk.signal.manager.SignalAccountFiles; import org.asamk.signal.manager.api.AccountCheckException; import org.asamk.signal.manager.api.NotRegisteredException; import org.asamk.signal.manager.config.ServiceConfig; @@ -164,26 +163,27 @@ public class App { ? TrustNewIdentity.ON_FIRST_USE : trustNewIdentityCli == TrustNewIdentityCli.ALWAYS ? TrustNewIdentity.ALWAYS : TrustNewIdentity.NEVER; + final SignalAccountFiles signalAccountFiles = new SignalAccountFiles(configPath, + serviceEnvironment, + BaseConfig.USER_AGENT, + trustNewIdentity); + if (command instanceof ProvisioningCommand provisioningCommand) { if (account != null) { throw new UserErrorException("You cannot specify a account (phone number) when linking"); } - handleProvisioningCommand(provisioningCommand, configPath, serviceEnvironment, outputWriter); + handleProvisioningCommand(provisioningCommand, signalAccountFiles, outputWriter); return; } if (account == null) { if (command instanceof MultiLocalCommand multiLocalCommand) { - handleMultiLocalCommand(multiLocalCommand, - configPath, - serviceEnvironment, - outputWriter, - trustNewIdentity); + handleMultiLocalCommand(multiLocalCommand, signalAccountFiles, outputWriter); return; } - var accounts = MultiAccountManager.getAllLocalAccountNumbers(configPath); + var accounts = signalAccountFiles.getAllLocalAccountNumbers(); if (accounts.size() == 0) { throw new UserErrorException("No local users found, you first need to register or link an account"); } else if (accounts.size() > 1) { @@ -191,13 +191,13 @@ public class App { "Multiple users found, you need to specify an account (phone number) with -a"); } - account = accounts.get(0); + account = accounts.stream().findFirst().get(); } else if (!Manager.isValidNumber(account, null)) { throw new UserErrorException("Invalid account (phone number), make sure you include the country code."); } if (command instanceof RegistrationCommand registrationCommand) { - handleRegistrationCommand(registrationCommand, account, configPath, serviceEnvironment); + handleRegistrationCommand(registrationCommand, account, signalAccountFiles); return; } @@ -205,21 +205,15 @@ public class App { throw new UserErrorException("Command only works in multi-account mode"); } - handleLocalCommand((LocalCommand) command, - account, - configPath, - serviceEnvironment, - outputWriter, - trustNewIdentity); + handleLocalCommand((LocalCommand) command, account, signalAccountFiles, outputWriter); } private void handleProvisioningCommand( final ProvisioningCommand command, - final File configPath, - final ServiceEnvironment serviceEnvironment, + final SignalAccountFiles signalAccountFiles, final OutputWriter outputWriter ) throws CommandException { - var pm = ProvisioningManager.init(configPath, serviceEnvironment, BaseConfig.USER_AGENT); + var pm = signalAccountFiles.initProvisioningManager(); command.handleCommand(ns, pm, outputWriter); } @@ -240,22 +234,9 @@ public class App { } private void handleRegistrationCommand( - final RegistrationCommand command, - final String account, - final File configPath, - final ServiceEnvironment serviceEnvironment + final RegistrationCommand command, final String account, final SignalAccountFiles signalAccountFiles ) throws CommandException { - final RegistrationManager manager; - try { - manager = RegistrationManager.init(account, configPath, serviceEnvironment, BaseConfig.USER_AGENT); - } catch (Throwable e) { - throw new UnexpectedErrorException("Error loading or creating state file: " - + e.getMessage() - + " (" - + e.getClass().getSimpleName() - + ")", e); - } - try (manager) { + try (final var manager = loadRegistrationManager(account, signalAccountFiles)) { command.handleCommand(ns, manager); } catch (IOException e) { logger.warn("Cleanup failed", e); @@ -280,12 +261,10 @@ public class App { private void handleLocalCommand( final LocalCommand command, final String account, - final File configPath, - final ServiceEnvironment serviceEnvironment, - final OutputWriter outputWriter, - final TrustNewIdentity trustNewIdentity + final SignalAccountFiles signalAccountFiles, + final OutputWriter outputWriter ) throws CommandException { - try (var m = loadManager(account, configPath, serviceEnvironment, trustNewIdentity)) { + try (var m = loadManager(account, signalAccountFiles)) { command.handleCommand(ns, m, outputWriter); } catch (IOException e) { logger.warn("Cleanup failed", e); @@ -310,15 +289,10 @@ public class App { private void handleMultiLocalCommand( final MultiLocalCommand command, - final File configPath, - final ServiceEnvironment serviceEnvironment, - final OutputWriter outputWriter, - final TrustNewIdentity trustNewIdentity + final SignalAccountFiles signalAccountFiles, + final OutputWriter outputWriter ) throws CommandException { - try (var multiAccountManager = MultiAccountManager.init(configPath, - serviceEnvironment, - BaseConfig.USER_AGENT, - trustNewIdentity)) { + try (var multiAccountManager = signalAccountFiles.initMultiAccountManager()) { command.handleCommand(ns, multiAccountManager, outputWriter); } } @@ -336,15 +310,26 @@ public class App { } } + private RegistrationManager loadRegistrationManager( + final String account, final SignalAccountFiles signalAccountFiles + ) throws UnexpectedErrorException { + try { + return signalAccountFiles.initRegistrationManager(account); + } catch (Throwable e) { + throw new UnexpectedErrorException("Error loading or creating state file: " + + e.getMessage() + + " (" + + e.getClass().getSimpleName() + + ")", e); + } + } + private Manager loadManager( - final String account, - final File configPath, - final ServiceEnvironment serviceEnvironment, - final TrustNewIdentity trustNewIdentity + final String account, final SignalAccountFiles signalAccountFiles ) throws CommandException { logger.trace("Loading account file for {}", account); try { - return Manager.init(account, configPath, serviceEnvironment, BaseConfig.USER_AGENT, trustNewIdentity); + return signalAccountFiles.initManager(account); } catch (NotRegisteredException e) { throw new UserErrorException("User " + account + " is not registered."); } catch (AccountCheckException ace) { diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index f0b78590..14bdc4c9 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -27,7 +27,7 @@ import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.commands.exceptions.UntrustedKeyErrorException; import org.asamk.signal.commands.exceptions.UserErrorException; -import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.ManagerLogger; import org.asamk.signal.util.SecurityProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.bridge.SLF4JBridgeHandler; @@ -97,7 +97,7 @@ public class Main { if (verboseLevel > 0) { java.util.logging.Logger.getLogger("") .setLevel(verboseLevel > 2 ? java.util.logging.Level.FINEST : java.util.logging.Level.INFO); - Manager.initLogger(); + ManagerLogger.initLogger(); } SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install(); From 0476895c3d9e843e505ef768340d7969b084cada Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 10 Feb 2022 18:27:24 +0100 Subject: [PATCH 032/938] Store account list in accounts.json file --- graalvm-config-dir/reflect-config.json | 22 +++ .../signal/manager/SignalAccountFiles.java | 2 +- .../storage/accounts/AccountsStorage.java | 8 + .../storage/accounts/AccountsStore.java | 184 ++++++++++++++++-- src/main/java/org/asamk/signal/App.java | 13 +- 5 files changed, 208 insertions(+), 21 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index ae98cd74..158f9429 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -859,6 +859,28 @@ "allDeclaredFields":true, "queryAllDeclaredMethods":true }, +{ + "name":"org.asamk.signal.manager.storage.accounts.AccountsStorage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"","parameterTypes":["java.util.List"] }, + {"name":"accounts","parameterTypes":[] } + ] +}, +{ + "name":"org.asamk.signal.manager.storage.accounts.AccountsStorage$Account", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, + {"name":"number","parameterTypes":[] }, + {"name":"path","parameterTypes":[] }, + {"name":"uuid","parameterTypes":[] } + ] +}, { "name":"org.asamk.signal.manager.storage.configuration.ConfigurationStore$Storage", "allDeclaredFields":true, 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 f1d37e81..379aa1cb 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -34,7 +34,7 @@ public class SignalAccountFiles { final ServiceEnvironment serviceEnvironment, final String userAgent, final TrustNewIdentity trustNewIdentity - ) { + ) throws IOException { this.pathConfig = PathConfig.createDefault(settingsPath); this.serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); this.userAgent = userAgent; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java new file mode 100644 index 00000000..341d9572 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java @@ -0,0 +1,8 @@ +package org.asamk.signal.manager.storage.accounts; + +import java.util.List; + +record AccountsStorage(List accounts) { + + record Account(String path, String number, String uuid) {} +} 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 ed6dec97..07dfac3c 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,22 +1,136 @@ package org.asamk.signal.manager.storage.accounts; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.storage.Utils; +import org.asamk.signal.manager.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Random; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; public class AccountsStore { + private final static Logger logger = LoggerFactory.getLogger(AccountsStore.class); + private final ObjectMapper objectMapper = Utils.createStorageObjectMapper(); + private final File dataPath; - public AccountsStore(final File dataPath) { + public AccountsStore(final File dataPath) throws IOException { this.dataPath = dataPath; + if (!getAccountsFile().exists()) { + createInitialAccounts(); + } } public Set getAllNumbers() { + return readAccounts().stream() + .map(AccountsStorage.Account::number) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + public String getPathByNumber(String number) { + return readAccounts().stream() + .filter(a -> number.equals(a.number())) + .map(AccountsStorage.Account::path) + .findFirst() + .orElse(null); + } + + public String getPathByAci(ACI aci) { + return readAccounts().stream() + .filter(a -> aci.toString().equals(a.uuid())) + .map(AccountsStorage.Account::path) + .findFirst() + .orElse(null); + } + + public void updateAccount(String path, String number, ACI aci) { + updateAccounts(accounts -> accounts.stream().map(a -> { + if (path.equals(a.path())) { + return new AccountsStorage.Account(a.path(), number, aci == null ? null : aci.toString()); + } + + if (number != null && number.equals(a.number())) { + return new AccountsStorage.Account(a.path(), null, a.uuid()); + } + if (aci != null && aci.toString().equals(a.toString())) { + return new AccountsStorage.Account(a.path(), a.number(), null); + } + + return a; + }).toList()); + } + + public String addAccount(String number, ACI aci) { + final var accountPath = generateNewAccountPath(); + final var account = new AccountsStorage.Account(accountPath, number, aci == null ? null : aci.toString()); + updateAccounts(accounts -> { + final var existingAccounts = accounts.stream().map(a -> { + if (number != null && number.equals(a.number())) { + return new AccountsStorage.Account(a.path(), null, a.uuid()); + } + if (aci != null && aci.toString().equals(a.toString())) { + return new AccountsStorage.Account(a.path(), a.number(), null); + } + + return a; + }); + return Stream.concat(existingAccounts, Stream.of(account)).toList(); + }); + return accountPath; + } + + private String generateNewAccountPath() { + return new Random().ints(100000, 1000000) + .mapToObj(String::valueOf) + .filter(n -> !new File(dataPath, n).exists() && !new File(dataPath, n + ".d").exists()) + .findFirst() + .get(); + } + + private File getAccountsFile() { + return new File(dataPath, "accounts.json"); + } + + private void createInitialAccounts() throws IOException { + final var legacyAccountPaths = getLegacyAccountPaths(); + final var accountsStorage = new AccountsStorage(legacyAccountPaths.stream() + .map(number -> new AccountsStorage.Account(number, number, null)) + .toList()); + + IOUtils.createPrivateDirectories(dataPath); + var fileName = getAccountsFile(); + if (!fileName.exists()) { + IOUtils.createPrivateFile(fileName); + } + + final var pair = openFileChannel(getAccountsFile()); + try (final var fileChannel = pair.first(); final var lock = pair.second()) { + saveAccountsLocked(fileChannel, accountsStorage); + } + } + + private Set getLegacyAccountPaths() { final var files = dataPath.listFiles(); if (files == null) { @@ -30,23 +144,61 @@ public class AccountsStore { .collect(Collectors.toSet()); } - public String getPathByNumber(String number) { - return number; - } - - public String getPathByAci(ACI aci) { - return null; - } - - public void updateAccount(String path, String number, ACI aci) { - // TODO remove number and uuid from all other accounts - if (!path.equals(number)) { - throw new UnsupportedOperationException("Updating number not supported yet"); + private List readAccounts() { + try { + final var pair = openFileChannel(getAccountsFile()); + try (final var fileChannel = pair.first(); final var lock = pair.second()) { + return readAccountsLocked(fileChannel).accounts(); + } + } catch (IOException e) { + logger.error("Failed to read accounts list", e); + return List.of(); } } - public String addAccount(String number, ACI aci) { - // TODO remove number and uuid from all other accounts - return number; + private void updateAccounts(Function, List> updater) { + try { + final var pair = openFileChannel(getAccountsFile()); + try (final var fileChannel = pair.first(); final var lock = pair.second()) { + final var accountsStorage = readAccountsLocked(fileChannel); + final var newAccountsStorage = updater.apply(accountsStorage.accounts()); + saveAccountsLocked(fileChannel, new AccountsStorage(newAccountsStorage)); + } + } catch (IOException e) { + logger.error("Failed to update accounts list", e); + } + } + + private AccountsStorage readAccountsLocked(FileChannel fileChannel) throws IOException { + fileChannel.position(0); + final var inputStream = Channels.newInputStream(fileChannel); + return objectMapper.readValue(inputStream, AccountsStorage.class); + } + + private void saveAccountsLocked(FileChannel fileChannel, AccountsStorage accountsStorage) throws IOException { + try { + try (var output = new ByteArrayOutputStream()) { + // Write to memory first to prevent corrupting the file in case of serialization errors + objectMapper.writeValue(output, accountsStorage); + var input = new ByteArrayInputStream(output.toByteArray()); + fileChannel.position(0); + input.transferTo(Channels.newOutputStream(fileChannel)); + fileChannel.truncate(fileChannel.position()); + fileChannel.force(false); + } + } catch (Exception e) { + logger.error("Error saving accounts file: {}", e.getMessage(), e); + } + } + + private static Pair openFileChannel(File fileName) throws IOException { + var fileChannel = new RandomAccessFile(fileName, "rw").getChannel(); + var lock = fileChannel.tryLock(); + if (lock == null) { + logger.info("Config file is in use by another instance, waiting…"); + lock = fileChannel.lock(); + logger.info("Config file lock acquired."); + } + return new Pair<>(fileChannel, lock); } } diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 5853e4b8..7a9297a8 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -163,10 +163,15 @@ public class App { ? TrustNewIdentity.ON_FIRST_USE : trustNewIdentityCli == TrustNewIdentityCli.ALWAYS ? TrustNewIdentity.ALWAYS : TrustNewIdentity.NEVER; - final SignalAccountFiles signalAccountFiles = new SignalAccountFiles(configPath, - serviceEnvironment, - BaseConfig.USER_AGENT, - trustNewIdentity); + final SignalAccountFiles signalAccountFiles; + try { + signalAccountFiles = new SignalAccountFiles(configPath, + serviceEnvironment, + BaseConfig.USER_AGENT, + trustNewIdentity); + } catch (IOException e) { + throw new IOErrorException("Failed to read local accounts list", e); + } if (command instanceof ProvisioningCommand provisioningCommand) { if (account != null) { From 292ef0f2dad4188bc6f42725af9360c3d030b92a Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 11 Feb 2022 16:23:09 +0100 Subject: [PATCH 033/938] Move credentials provider to SignalAccount --- .../org/asamk/signal/manager/ManagerImpl.java | 9 +------ .../signal/manager/SignalDependencies.java | 6 ++--- .../signal/manager/storage/SignalAccount.java | 25 +++++++++++++++++++ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 742be520..992dfb8b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -70,7 +70,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; import org.whispersystems.signalservice.api.util.DeviceNameUtil; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; -import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.util.Hex; import org.whispersystems.signalservice.internal.util.Util; @@ -100,7 +99,6 @@ class ManagerImpl implements Manager { private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class); private SignalAccount account; - private final AccountFileUpdater accountFileUpdater; private final SignalDependencies dependencies; private final Context context; @@ -121,12 +119,7 @@ class ManagerImpl implements Manager { String userAgent ) { this.account = account; - this.accountFileUpdater = accountFileUpdater; - final var credentialsProvider = new DynamicCredentialsProvider(account.getAci(), - account.getNumber(), - account.getPassword(), - account.getDeviceId()); final var sessionLock = new SignalSessionLock() { private final ReentrantLock LEGACY_LOCK = new ReentrantLock(); @@ -138,7 +131,7 @@ class ManagerImpl implements Manager { }; this.dependencies = new SignalDependencies(serviceEnvironmentConfig, userAgent, - credentialsProvider, + account.getCredentialsProvider(), account.getSignalProtocolStore(), executor, sessionLock); diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java index cc894602..90bbbdc4 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java @@ -18,9 +18,9 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.services.ProfileService; +import org.whispersystems.signalservice.api.util.CredentialsProvider; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.websocket.WebSocketFactory; -import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; import java.util.concurrent.ExecutorService; @@ -34,7 +34,7 @@ public class SignalDependencies { private final ServiceEnvironmentConfig serviceEnvironmentConfig; private final String userAgent; - private final DynamicCredentialsProvider credentialsProvider; + private final CredentialsProvider credentialsProvider; private final SignalServiceDataStore dataStore; private final ExecutorService executor; private final SignalSessionLock sessionLock; @@ -55,7 +55,7 @@ public class SignalDependencies { SignalDependencies( final ServiceEnvironmentConfig serviceEnvironmentConfig, final String userAgent, - final DynamicCredentialsProvider credentialsProvider, + final CredentialsProvider credentialsProvider, final SignalServiceDataStore dataStore, final ExecutorService executor, final SignalSessionLock sessionLock 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 d77d51a2..cbe542a9 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 @@ -50,6 +50,7 @@ import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.storage.StorageKey; +import org.whispersystems.signalservice.api.util.CredentialsProvider; import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.ByteArrayInputStream; @@ -901,6 +902,30 @@ public class SignalAccount implements Closeable { () -> messageSendLogStore = new MessageSendLogStore(getRecipientStore(), getAccountDatabase())); } + public CredentialsProvider getCredentialsProvider() { + return new CredentialsProvider() { + @Override + public ACI getAci() { + return aci; + } + + @Override + public String getE164() { + return number; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public int getDeviceId() { + return deviceId; + } + }; + } + public String getNumber() { return number; } From 7a06d3959ee425253afa67a3891ea8bf8ee6ab3c Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 11 Feb 2022 16:25:35 +0100 Subject: [PATCH 034/938] Extract number verification code logic --- .../manager/RegistrationManagerImpl.java | 161 ++++++------------ .../signal/manager/helper/PinHelper.java | 11 +- .../manager/util/NumberVerificationUtils.java | 101 +++++++++++ 3 files changed, 161 insertions(+), 112 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index 81602122..97c0adf2 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -24,22 +24,15 @@ import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.helper.AccountFileUpdater; import org.asamk.signal.manager.helper.PinHelper; import org.asamk.signal.manager.storage.SignalAccount; -import org.asamk.signal.manager.util.Utils; +import org.asamk.signal.manager.util.NumberVerificationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.KbsPinData; -import org.whispersystems.signalservice.api.KeyBackupServicePinException; -import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; -import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.internal.ServiceResponse; -import org.whispersystems.signalservice.internal.push.LockedException; -import org.whispersystems.signalservice.internal.push.RequestVerificationCodeResponse; import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; @@ -100,98 +93,25 @@ class RegistrationManagerImpl implements RegistrationManager { @Override public void register(boolean voiceVerification, String captcha) throws IOException, CaptchaRequiredException { - captcha = captcha == null ? null : captcha.replace("signalcaptcha://", ""); - if (account.getAci() != null) { - try { - final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), - new DynamicCredentialsProvider(account.getAci(), - account.getNumber(), - account.getPassword(), - account.getDeviceId()), - userAgent, - null, - ServiceConfig.AUTOMATIC_NETWORK_RETRY); - accountManager.setAccountAttributes(account.getEncryptedDeviceName(), - null, - account.getLocalRegistrationId(), - true, - null, - account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), - account.getSelfUnidentifiedAccessKey(), - account.isUnrestrictedUnidentifiedAccess(), - capabilities, - account.isDiscoverableByPhoneNumber()); - account.setRegistered(true); - logger.info("Reactivated existing account, verify is not necessary."); - if (newManagerListener != null) { - final var m = new ManagerImpl(account, - pathConfig, - accountFileUpdater, - serviceEnvironmentConfig, - userAgent); - account = null; - newManagerListener.accept(m); - } - return; - } catch (IOException e) { - logger.debug("Failed to reactivate account"); - } - } - final ServiceResponse response; - if (voiceVerification) { - response = accountManager.requestVoiceVerificationCode(Utils.getDefaultLocale(null), - Optional.fromNullable(captcha), - Optional.absent(), - Optional.absent()); - } else { - response = accountManager.requestSmsVerificationCode(false, - Optional.fromNullable(captcha), - Optional.absent(), - Optional.absent()); - } - try { - handleResponseException(response); - } catch (org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException e) { - throw new CaptchaRequiredException(e.getMessage(), e); + if (account.getAci() != null && attemptReactivateAccount()) { + return; } + + NumberVerificationUtils.requestVerificationCode(accountManager, captcha, voiceVerification); } @Override public void verifyAccount( String verificationCode, String pin ) throws IOException, PinLockedException, IncorrectPinException { - verificationCode = verificationCode.replace("-", ""); - VerifyAccountResponse response; - MasterKey masterKey; - try { - response = verifyAccountWithCode(verificationCode, null); - - masterKey = null; + final var result = NumberVerificationUtils.verifyNumber(verificationCode, + pin, + pinHelper, + this::verifyAccountWithCode); + final var response = result.first(); + final var masterKey = result.second(); + if (masterKey == null) { pin = null; - } catch (LockedException e) { - if (pin == null) { - throw new PinLockedException(e.getTimeRemaining()); - } - - KbsPinData registrationLockData; - try { - registrationLockData = pinHelper.getRegistrationLockData(pin, e); - } catch (KeyBackupSystemNoDataException ex) { - throw new IOException(e); - } catch (KeyBackupServicePinException ex) { - throw new IncorrectPinException(ex.getTriesRemaining()); - } - if (registrationLockData == null) { - throw e; - } - - var registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock(); - try { - response = verifyAccountWithCode(verificationCode, registrationLock); - } catch (LockedException _e) { - throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!"); - } - masterKey = registrationLockData.getMasterKey(); } //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); @@ -226,12 +146,46 @@ class RegistrationManagerImpl implements RegistrationManager { } } - private VerifyAccountResponse verifyAccountWithCode( + private boolean attemptReactivateAccount() { + try { + final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), + account.getCredentialsProvider(), + userAgent, + null, + ServiceConfig.AUTOMATIC_NETWORK_RETRY); + accountManager.setAccountAttributes(account.getEncryptedDeviceName(), + null, + account.getLocalRegistrationId(), + true, + null, + account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), + account.getSelfUnidentifiedAccessKey(), + account.isUnrestrictedUnidentifiedAccess(), + capabilities, + account.isDiscoverableByPhoneNumber()); + account.setRegistered(true); + logger.info("Reactivated existing account, verify is not necessary."); + if (newManagerListener != null) { + final var m = new ManagerImpl(account, + pathConfig, + accountFileUpdater, + serviceEnvironmentConfig, + userAgent); + account = null; + newManagerListener.accept(m); + } + return true; + } catch (IOException e) { + logger.debug("Failed to reactivate account"); + } + return false; + } + + private ServiceResponse verifyAccountWithCode( final String verificationCode, final String registrationLock - ) throws IOException { - final ServiceResponse response; + ) { if (registrationLock == null) { - response = accountManager.verifyAccount(verificationCode, + return accountManager.verifyAccount(verificationCode, account.getLocalRegistrationId(), true, account.getSelfUnidentifiedAccessKey(), @@ -239,7 +193,7 @@ class RegistrationManagerImpl implements RegistrationManager { ServiceConfig.capabilities, account.isDiscoverableByPhoneNumber()); } else { - response = accountManager.verifyAccountWithRegistrationLockPin(verificationCode, + return accountManager.verifyAccountWithRegistrationLockPin(verificationCode, account.getLocalRegistrationId(), true, registrationLock, @@ -248,8 +202,6 @@ class RegistrationManagerImpl implements RegistrationManager { ServiceConfig.capabilities, account.isDiscoverableByPhoneNumber()); } - handleResponseException(response); - return response.getResult().get(); } @Override @@ -259,15 +211,4 @@ class RegistrationManagerImpl implements RegistrationManager { account = null; } } - - private void handleResponseException(final ServiceResponse response) throws IOException { - final var throwableOptional = response.getExecutionError().or(response.getApplicationError()); - if (throwableOptional.isPresent()) { - if (throwableOptional.get() instanceof IOException) { - throw (IOException) throwableOptional.get(); - } else { - throw new IOException(throwableOptional.get()); - } - } - } } 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 888b86ce..accdbc0f 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 @@ -1,5 +1,6 @@ package org.asamk.signal.manager.helper; +import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.util.PinHashing; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.signalservice.api.KbsPinData; @@ -47,13 +48,19 @@ public class PinHelper { public KbsPinData getRegistrationLockData( String pin, LockedException e - ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { + ) throws IOException, IncorrectPinException { var basicStorageCredentials = e.getBasicStorageCredentials(); if (basicStorageCredentials == null) { return null; } - return getRegistrationLockData(pin, basicStorageCredentials); + try { + return getRegistrationLockData(pin, basicStorageCredentials); + } catch (KeyBackupSystemNoDataException ex) { + throw new IOException(e); + } catch (KeyBackupServicePinException ex) { + throw new IncorrectPinException(ex.getTriesRemaining()); + } } private KbsPinData getRegistrationLockData( 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 new file mode 100644 index 00000000..1874bab0 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java @@ -0,0 +1,101 @@ +package org.asamk.signal.manager.util; + +import org.asamk.signal.manager.api.CaptchaRequiredException; +import org.asamk.signal.manager.api.IncorrectPinException; +import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.api.PinLockedException; +import org.asamk.signal.manager.helper.PinHelper; +import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.KbsPinData; +import org.whispersystems.signalservice.api.SignalServiceAccountManager; +import org.whispersystems.signalservice.api.kbs.MasterKey; +import org.whispersystems.signalservice.internal.ServiceResponse; +import org.whispersystems.signalservice.internal.push.LockedException; +import org.whispersystems.signalservice.internal.push.RequestVerificationCodeResponse; +import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; + +import java.io.IOException; + +public class NumberVerificationUtils { + + public static void requestVerificationCode( + SignalServiceAccountManager accountManager, String captcha, boolean voiceVerification + ) throws IOException, CaptchaRequiredException { + captcha = captcha == null ? null : captcha.replace("signalcaptcha://", ""); + + final ServiceResponse response; + if (voiceVerification) { + response = accountManager.requestVoiceVerificationCode(Utils.getDefaultLocale(null), + Optional.fromNullable(captcha), + Optional.absent(), + Optional.absent()); + } else { + response = accountManager.requestSmsVerificationCode(false, + Optional.fromNullable(captcha), + Optional.absent(), + Optional.absent()); + } + try { + handleResponseException(response); + } catch (org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException e) { + throw new CaptchaRequiredException(e.getMessage(), e); + } + } + + public static Pair verifyNumber( + String verificationCode, String pin, PinHelper pinHelper, Verifier verifier + ) throws IOException, PinLockedException, IncorrectPinException { + verificationCode = verificationCode.replace("-", ""); + try { + final var response = verifyAccountWithCode(verificationCode, null, verifier); + + return new Pair<>(response, null); + } catch (LockedException e) { + if (pin == null) { + throw new PinLockedException(e.getTimeRemaining()); + } + + KbsPinData registrationLockData; + registrationLockData = pinHelper.getRegistrationLockData(pin, e); + if (registrationLockData == null) { + throw e; + } + + var registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock(); + VerifyAccountResponse response; + try { + response = verifyAccountWithCode(verificationCode, registrationLock, verifier); + } catch (LockedException _e) { + throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!"); + } + + return new Pair<>(response, registrationLockData.getMasterKey()); + } + } + + private static VerifyAccountResponse verifyAccountWithCode( + final String verificationCode, final String registrationLock, final Verifier verifier + ) throws IOException { + final var response = verifier.verify(verificationCode, registrationLock); + handleResponseException(response); + return response.getResult().get(); + } + + private static void handleResponseException(final ServiceResponse response) throws IOException { + final var throwableOptional = response.getExecutionError().or(response.getApplicationError()); + if (throwableOptional.isPresent()) { + if (throwableOptional.get() instanceof IOException) { + throw (IOException) throwableOptional.get(); + } else { + throw new IOException(throwableOptional.get()); + } + } + } + + public interface Verifier { + + ServiceResponse verify( + String verificationCode, String registrationLock + ); + } +} From 8985cc26568884d9a365cf265f2761089fa7bd8b Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 11 Feb 2022 17:09:09 +0100 Subject: [PATCH 035/938] Update self identifiers after whoAmI request --- .../signal/manager/helper/AccountHelper.java | 25 ++++++++++++++++--- .../helper/UnidentifiedAccessHelper.java | 5 ++++ .../signal/manager/storage/SignalAccount.java | 5 ++++ 3 files changed, 32 insertions(+), 3 deletions(-) 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 3e60ce74..6b7f68f4 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 @@ -52,9 +52,7 @@ public class AccountHelper { try { context.getPreKeyHelper().refreshPreKeysIfNecessary(); if (account.getAci() == null) { - final var aci = ACI.parseOrNull(dependencies.getAccountManager().getWhoAmI().getAci()); - account.setAci(aci); - context.getAccountFileUpdater().updateAccountIdentifiers(account.getNumber(), aci); + checkWhoAmiI(); } updateAccountAttributes(); } catch (AuthorizationFailedException e) { @@ -63,6 +61,27 @@ public class AccountHelper { } } + public void checkWhoAmiI() throws IOException { + final var whoAmI = dependencies.getAccountManager().getWhoAmI(); + final var number = whoAmI.getNumber(); + final var aci = ACI.parseOrNull(whoAmI.getAci()); + if (number.equals(account.getNumber()) && aci.equals(account.getAci())) { + return; + } + + updateSelfIdentifiers(number, aci); + } + + private void updateSelfIdentifiers(final String number, final ACI aci) { + account.setNumber(number); + account.setAci(aci); + account.getRecipientStore().resolveRecipientTrusted(account.getSelfRecipientAddress()); + context.getAccountFileUpdater().updateAccountIdentifiers(account.getNumber(), account.getAci()); + // TODO check and update remote storage + context.getUnidentifiedAccessHelper().rotateSenderCertificates(); + dependencies.getSignalWebSocket().forceNewWebSockets(); + } + public void setDeviceName(String deviceName) { final var privateKey = account.getIdentityKeyPair().getPrivateKey(); final var encryptedDeviceName = DeviceNameUtil.encryptDeviceName(deviceName, privateKey); 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 cc419bea..36e18939 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 @@ -37,6 +37,11 @@ public class UnidentifiedAccessHelper { this.context = context; } + public void rotateSenderCertificates() { + privacySenderCertificate = null; + senderCertificate = null; + } + public List> getAccessFor(List recipients) { return recipients.stream().map(this::getAccessFor).toList(); } 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 cbe542a9..b95a087d 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 @@ -930,6 +930,11 @@ public class SignalAccount implements Closeable { return number; } + public void setNumber(final String number) { + this.number = number; + save(); + } + public ACI getAci() { return aci; } From 796f4d0d96c16398c7d0fc7ef15f22e511324785 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 11 Feb 2022 17:35:14 +0100 Subject: [PATCH 036/938] Prevent stripping number from self recipient in edge cases --- .../signal/manager/helper/AccountHelper.java | 2 +- .../signal/manager/storage/SignalAccount.java | 11 +++-- .../storage/recipients/RecipientStore.java | 46 +++++++++++++------ .../recipients/SelfAddressProvider.java | 6 +++ 4 files changed, 46 insertions(+), 19 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/recipients/SelfAddressProvider.java 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 6b7f68f4..2affa078 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 @@ -75,7 +75,7 @@ public class AccountHelper { private void updateSelfIdentifiers(final String number, final ACI aci) { account.setNumber(number); account.setAci(aci); - account.getRecipientStore().resolveRecipientTrusted(account.getSelfRecipientAddress()); + account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress()); context.getAccountFileUpdater().updateAccountIdentifiers(account.getNumber(), account.getAci()); // TODO check and update remote storage context.getUnidentifiedAccessHelper().rotateSenderCertificates(); 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 b95a087d..166855a9 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 @@ -226,7 +226,7 @@ public class SignalAccount implements Closeable { final var signalAccount = load(dataPath, accountPath, true, trustNewIdentity); signalAccount.setProvisioningData(number, aci, password, encryptedDeviceName, deviceId, profileKey); - signalAccount.getRecipientStore().resolveRecipientTrusted(signalAccount.getSelfAddress()); + signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); signalAccount.getSessionStore().archiveAllSessions(); signalAccount.getSenderKeyStore().deleteAll(); signalAccount.clearAllPreKeys(); @@ -277,7 +277,7 @@ public class SignalAccount implements Closeable { signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); - signalAccount.getRecipientStore().resolveRecipientTrusted(signalAccount.getSelfAddress()); + signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION; signalAccount.migrateLegacyConfigs(); signalAccount.save(); @@ -558,7 +558,7 @@ public class SignalAccount implements Closeable { if (legacyRecipientStore != null) { getRecipientStore().resolveRecipientsTrusted(legacyRecipientStore.getAddresses()); } - getRecipientStore().resolveRecipientTrusted(getSelfRecipientAddress()); + getRecipientStore().resolveSelfRecipientTrusted(getSelfRecipientAddress()); migrated = true; } @@ -859,7 +859,8 @@ public class SignalAccount implements Closeable { public RecipientStore getRecipientStore() { return getOrCreate(() -> recipientStore, () -> recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, accountPath), - this::mergeRecipients)); + this::mergeRecipients, + this::getSelfRecipientAddress)); } public ProfileStore getProfileStore() { @@ -1106,7 +1107,7 @@ public class SignalAccount implements Closeable { clearAllPreKeys(); getSessionStore().archiveAllSessions(); getSenderKeyStore().deleteAll(); - final var recipientId = getRecipientStore().resolveRecipientTrusted(getSelfAddress()); + final var recipientId = getRecipientStore().resolveSelfRecipientTrusted(getSelfRecipientAddress()); final var publicKey = getIdentityKeyPair().getPublicKey(); getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date()); getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED); 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 36f82481..f52b79b7 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 @@ -42,6 +42,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile private final ObjectMapper objectMapper; private final File file; private final RecipientMergeHandler recipientMergeHandler; + private final SelfAddressProvider selfAddressProvider; private final Map recipients; private final Map recipientsMerged = new HashMap<>(); @@ -49,7 +50,9 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile private long lastId; private boolean isBulkUpdating; - public static RecipientStore load(File file, RecipientMergeHandler recipientMergeHandler) { + public static RecipientStore load( + File file, RecipientMergeHandler recipientMergeHandler, SelfAddressProvider selfAddressProvider + ) { final var objectMapper = Utils.createStorageObjectMapper(); try (var inputStream = new FileInputStream(file)) { final var storage = objectMapper.readValue(inputStream, Storage.class); @@ -57,6 +60,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile final var recipientStore = new RecipientStore(objectMapper, file, recipientMergeHandler, + selfAddressProvider, new HashMap<>(), storage.lastId); final var recipients = storage.recipients.stream().map(r -> { @@ -113,7 +117,12 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile return recipientStore; } catch (FileNotFoundException e) { logger.trace("Creating new recipient store."); - return new RecipientStore(objectMapper, file, recipientMergeHandler, new HashMap<>(), 0); + return new RecipientStore(objectMapper, + file, + recipientMergeHandler, + selfAddressProvider, + new HashMap<>(), + 0); } catch (IOException e) { logger.warn("Failed to load recipient store", e); throw new RuntimeException(e); @@ -124,12 +133,14 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile final ObjectMapper objectMapper, final File file, final RecipientMergeHandler recipientMergeHandler, + final SelfAddressProvider selfAddressProvider, final Map recipients, final long lastId ) { this.objectMapper = objectMapper; this.file = file; this.recipientMergeHandler = recipientMergeHandler; + this.selfAddressProvider = selfAddressProvider; this.recipients = recipients; this.lastId = lastId; } @@ -161,7 +172,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile @Override public RecipientId resolveRecipient(ACI aci) { - return resolveRecipient(new RecipientAddress(aci.uuid()), false); + return resolveRecipient(new RecipientAddress(aci.uuid()), false, false); } @Override @@ -172,7 +183,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile @Override public RecipientId resolveRecipient(final String identifier) { - return resolveRecipient(Utils.getRecipientAddressFromIdentifier(identifier), false); + return resolveRecipient(Utils.getRecipientAddressFromIdentifier(identifier), false, false); } public RecipientId resolveRecipient( @@ -188,26 +199,30 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile throw new UnregisteredRecipientException(new RecipientAddress(null, number)); } - return resolveRecipient(new RecipientAddress(aci.uuid(), number), false); + return resolveRecipient(new RecipientAddress(aci.uuid(), number), false, false); } return byNumber.get().getRecipientId(); } public RecipientId resolveRecipient(RecipientAddress address) { - return resolveRecipient(address, false); + return resolveRecipient(address, false, false); } @Override public RecipientId resolveRecipient(final SignalServiceAddress address) { - return resolveRecipient(new RecipientAddress(address), false); + return resolveRecipient(new RecipientAddress(address), false, false); + } + + public RecipientId resolveSelfRecipientTrusted(RecipientAddress address) { + return resolveRecipient(address, true, true); } public RecipientId resolveRecipientTrusted(RecipientAddress address) { - return resolveRecipient(address, true); + return resolveRecipient(address, true, false); } public RecipientId resolveRecipientTrusted(SignalServiceAddress address) { - return resolveRecipient(new RecipientAddress(address), true); + return resolveRecipient(new RecipientAddress(address), true, false); } public List resolveRecipientsTrusted(List addresses) { @@ -215,7 +230,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile final List> toBeMerged = new ArrayList<>(); synchronized (recipients) { recipientIds = addresses.stream().map(address -> { - final var pair = resolveRecipientLocked(address, true); + final var pair = resolveRecipientLocked(address, true, false); if (pair.second().isPresent()) { toBeMerged.add(new Pair<>(pair.first(), pair.second().get())); } @@ -345,10 +360,10 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile * @param isHighTrust true, if the number/uuid connection was obtained from a trusted source. * Has no effect, if the address contains only a number or a uuid. */ - private RecipientId resolveRecipient(RecipientAddress address, boolean isHighTrust) { + private RecipientId resolveRecipient(RecipientAddress address, boolean isHighTrust, boolean isSelf) { final Pair> pair; synchronized (recipients) { - pair = resolveRecipientLocked(address, isHighTrust); + pair = resolveRecipientLocked(address, isHighTrust, isSelf); } if (pair.second().isPresent()) { @@ -358,8 +373,13 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile } private Pair> resolveRecipientLocked( - RecipientAddress address, boolean isHighTrust + RecipientAddress address, boolean isHighTrust, boolean isSelf ) { + if (isHighTrust && !isSelf) { + if (selfAddressProvider.getSelfAddress().matches(address)) { + isHighTrust = false; + } + } final var byNumber = address.number().isEmpty() ? Optional.empty() : findByNumberLocked(address.number().get()); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/SelfAddressProvider.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/SelfAddressProvider.java new file mode 100644 index 00000000..c99b43eb --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/SelfAddressProvider.java @@ -0,0 +1,6 @@ +package org.asamk.signal.manager.storage.recipients; + +public interface SelfAddressProvider { + + RecipientAddress getSelfAddress(); +} From f207c2abc390d1b6292de6e86379d1513dc358cf Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 11 Feb 2022 17:59:02 +0100 Subject: [PATCH 037/938] Reexport dbus objects when self number changes --- .../org/asamk/signal/manager/Manager.java | 2 ++ .../org/asamk/signal/manager/ManagerImpl.java | 20 +++++++++++++------ .../asamk/signal/commands/DaemonCommand.java | 2 -- .../asamk/signal/dbus/DbusManagerImpl.java | 4 ++++ .../org/asamk/signal/dbus/DbusSignalImpl.java | 17 +++++++++++++++- 5 files changed, 36 insertions(+), 9 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 9db509ad..99b76781 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -261,6 +261,8 @@ public interface Manager extends Closeable { */ boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient) throws UnregisteredRecipientException; + void addAddressChangedListener(Runnable listener); + void addClosedListener(Runnable listener); @Override diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 992dfb8b..fa500712 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -109,6 +109,7 @@ class ManagerImpl implements Manager { private final Set weakHandlers = new HashSet<>(); private final Set messageHandlers = new HashSet<>(); private final List closedListeners = new ArrayList<>(); + private final List addressChangedListeners = new ArrayList<>(); private final CompositeDisposable disposable = new CompositeDisposable(); ManagerImpl( @@ -139,12 +140,12 @@ class ManagerImpl implements Manager { final var attachmentStore = new AttachmentStore(pathConfig.attachmentsPath()); final var stickerPackStore = new StickerPackStore(pathConfig.stickerPacksPath()); - this.context = new Context(account, - accountFileUpdater, - dependencies, - avatarStore, - attachmentStore, - stickerPackStore); + this.context = new Context(account, (number, aci) -> { + accountFileUpdater.updateAccountIdentifiers(number, aci); + synchronized (addressChangedListeners) { + addressChangedListeners.forEach(Runnable::run); + } + }, dependencies, avatarStore, attachmentStore, stickerPackStore); this.context.getAccountHelper().setUnregisteredListener(this::close); this.context.getReceiveHelper().setAuthenticationFailureListener(this::close); this.context.getReceiveHelper().setCaughtUpWithOldMessagesListener(() -> { @@ -998,6 +999,13 @@ class ManagerImpl implements Manager { return updated; } + @Override + public void addAddressChangedListener(final Runnable listener) { + synchronized (addressChangedListeners) { + addressChangedListeners.add(listener); + } + } + @Override public void addClosedListener(final Runnable listener) { synchronized (closedListeners) { diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 043b19c2..79e0cd82 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -314,7 +314,6 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { } } catch (DBusException ignored) { } - connection.unExportObject(path); }); final var initThreads = c.getManagers() @@ -370,7 +369,6 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { final DBusConnection conn, final String objectPath, final Manager m, final boolean noReceiveOnStart ) throws DBusException { final var signal = new DbusSignalImpl(m, conn, objectPath, noReceiveOnStart); - conn.exportObject(signal); final var initThread = new Thread(signal::initObjects); initThread.setName("dbus-init"); initThread.start(); diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index bee87d74..51323395 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -621,6 +621,10 @@ public class DbusManagerImpl implements Manager { throw new UnsupportedOperationException(); } + @Override + public void addAddressChangedListener(final Runnable listener) { + } + @Override public void addClosedListener(final Runnable listener) { synchronized (closedListeners) { diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 671966a0..f3877974 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -3,7 +3,6 @@ package org.asamk.signal.dbus; import org.asamk.Signal; import org.asamk.signal.BaseConfig; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.api.Identity; import org.asamk.signal.manager.api.InactiveGroupLinkException; @@ -16,6 +15,7 @@ import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendMessageResult; import org.asamk.signal.manager.api.SendMessageResults; +import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; @@ -79,12 +79,22 @@ public class DbusSignalImpl implements Signal { this.connection = connection; this.objectPath = objectPath; this.noReceiveOnStart = noReceiveOnStart; + + m.addAddressChangedListener(() -> { + unExportObjects(); + exportObjects(); + }); } public void initObjects() { + exportObjects(); if (!noReceiveOnStart) { subscribeReceive(); } + } + + private void exportObjects() { + exportObject(this); updateDevices(); updateGroups(); @@ -96,9 +106,14 @@ public class DbusSignalImpl implements Signal { m.removeReceiveHandler(dbusMessageHandler); dbusMessageHandler = null; } + unExportObjects(); + } + + private void unExportObjects() { unExportDevices(); unExportGroups(); unExportConfiguration(); + connection.unExportObject(this.objectPath); } @Override From d690b35ed943b0210e3f483528b8c033fe2d667f Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 11 Feb 2022 18:11:13 +0100 Subject: [PATCH 038/938] Implement support for change number as linked device --- graalvm-config-dir/reflect-config.json | 18 +++++++++++++ .../signal/manager/SignalDependencies.java | 15 +++++++++++ .../signal/manager/config/ServiceConfig.java | 2 +- .../signal/manager/helper/AccountHelper.java | 26 ++++++++++++++++++- .../signal/manager/helper/StorageHelper.java | 2 +- 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 158f9429..18954ec8 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -1931,6 +1931,17 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"getCode","parameterTypes":[] }, + {"name":"getNumber","parameterTypes":[] }, + {"name":"getRegistrationLock","parameterTypes":[] } + ] +}, { "name":"org.whispersystems.signalservice.api.groupsv2.CredentialResponse", "allDeclaredFields":true, @@ -2937,6 +2948,13 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.whispersystems.signalservice.internal.push.WhoAmIResponse", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.serialize.protos.AddressProto", "fields":[ diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java index 90bbbdc4..4597d585 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java @@ -68,6 +68,11 @@ public class SignalDependencies { this.sessionLock = sessionLock; } + public void resetAfterAddressChange() { + this.messageSender = null; + this.cipher = null; + } + public ServiceEnvironmentConfig getServiceEnvironmentConfig() { return serviceEnvironmentConfig; } @@ -81,6 +86,16 @@ public class SignalDependencies { ServiceConfig.AUTOMATIC_NETWORK_RETRY)); } + public SignalServiceAccountManager createUnauthenticatedAccountManager(String number, String password) { + return new SignalServiceAccountManager(getServiceEnvironmentConfig().getSignalServiceConfiguration(), + null, + number, + password, + SignalServiceAddress.DEFAULT_DEVICE_ID, + userAgent, + ServiceConfig.AUTOMATIC_NETWORK_RETRY); + } + public GroupsV2Api getGroupsV2Api() { return getOrCreate(() -> groupsV2Api, () -> groupsV2Api = getAccountManager().getGroupsV2Api()); } 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 78decc81..dd85671b 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,7 @@ public class ServiceConfig { public static final AccountAttributes.Capabilities capabilities; static { - capabilities = new AccountAttributes.Capabilities(false, true, false, true, true, true, false); + capabilities = new AccountAttributes.Capabilities(false, true, false, true, true, true, true); try { TrustStore contactTrustStore = new IasTrustStore(); 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 2affa078..724c2c49 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 @@ -2,10 +2,14 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.DeviceLinkInfo; import org.asamk.signal.manager.SignalDependencies; +import org.asamk.signal.manager.api.CaptchaRequiredException; +import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; +import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.util.KeyUtils; +import org.asamk.signal.manager.util.NumberVerificationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidKeyException; @@ -76,10 +80,30 @@ public class AccountHelper { account.setNumber(number); account.setAci(aci); account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress()); - context.getAccountFileUpdater().updateAccountIdentifiers(account.getNumber(), account.getAci()); // TODO check and update remote storage context.getUnidentifiedAccessHelper().rotateSenderCertificates(); + dependencies.resetAfterAddressChange(); dependencies.getSignalWebSocket().forceNewWebSockets(); + context.getAccountFileUpdater().updateAccountIdentifiers(account.getNumber(), account.getAci()); + } + + public void startChangeNumber( + String newNumber, String captcha, boolean voiceVerification + ) throws IOException, CaptchaRequiredException { + final var accountManager = dependencies.createUnauthenticatedAccountManager(newNumber, account.getPassword()); + NumberVerificationUtils.requestVerificationCode(accountManager, captcha, voiceVerification); + } + + public void finishChangeNumber( + String newNumber, String verificationCode, String pin + ) throws IncorrectPinException, PinLockedException, IOException { + final var result = NumberVerificationUtils.verifyNumber(verificationCode, + pin, + context.getPinHelper(), + (verificationCode1, registrationLock) -> dependencies.getAccountManager() + .changeNumber(verificationCode1, newNumber, registrationLock)); + // TODO handle response + updateSelfIdentifiers(newNumber, account.getAci()); } public void setDeviceName(String deviceName) { 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 6f136b3c..b85d0050 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 @@ -189,7 +189,7 @@ public class StorageHelper { } if (!accountRecord.getE164().equals(account.getNumber())) { - // TODO implement changed number handling + context.getAccountHelper().checkWhoAmiI(); } account.getConfigurationStore().setReadReceipts(accountRecord.isReadReceiptsEnabled()); From b0bb602eb59eb6238a87d8a4ba942ac3bc0dc90d Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 11 Feb 2022 20:33:12 +0100 Subject: [PATCH 039/938] Update libsignal-service-java --- lib/build.gradle.kts | 2 +- .../org/asamk/signal/manager/ManagerImpl.java | 2 +- .../manager/RegistrationManagerImpl.java | 6 ++-- .../signal/manager/SignalDependencies.java | 4 +-- .../signal/manager/helper/AccountHelper.java | 6 ++-- .../signal/manager/storage/SignalAccount.java | 33 +++++++++++++++++-- .../storage/protocol/SignalProtocolStore.java | 12 ++----- 7 files changed, 43 insertions(+), 22 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 5b891b8b..5c3d8443 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_39") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_41") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.1") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index fa500712..c0643604 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -133,7 +133,7 @@ class ManagerImpl implements Manager { this.dependencies = new SignalDependencies(serviceEnvironmentConfig, userAgent, account.getCredentialsProvider(), - account.getSignalProtocolStore(), + account.getSignalServiceDataStore(), executor, sessionLock); final var avatarStore = new AvatarStore(pathConfig.avatarsPath()); diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index 97c0adf2..599c55a4 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -153,8 +153,7 @@ class RegistrationManagerImpl implements RegistrationManager { userAgent, null, ServiceConfig.AUTOMATIC_NETWORK_RETRY); - accountManager.setAccountAttributes(account.getEncryptedDeviceName(), - null, + accountManager.setAccountAttributes(null, account.getLocalRegistrationId(), true, null, @@ -162,7 +161,8 @@ class RegistrationManagerImpl implements RegistrationManager { account.getSelfUnidentifiedAccessKey(), account.isUnrestrictedUnidentifiedAccess(), capabilities, - account.isDiscoverableByPhoneNumber()); + account.isDiscoverableByPhoneNumber(), + account.getEncryptedDeviceName()); account.setRegistered(true); logger.info("Reactivated existing account, verify is not necessary."); if (newManagerListener != null) { diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java index 4597d585..450abaac 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java @@ -90,8 +90,8 @@ public class SignalDependencies { return new SignalServiceAccountManager(getServiceEnvironmentConfig().getSignalServiceConfiguration(), null, number, - password, SignalServiceAddress.DEFAULT_DEVICE_ID, + password, userAgent, ServiceConfig.AUTOMATIC_NETWORK_RETRY); } @@ -191,7 +191,7 @@ public class SignalDependencies { final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot()); final var address = new SignalServiceAddress(credentialsProvider.getAci(), credentialsProvider.getE164()); final var deviceId = credentialsProvider.getDeviceId(); - cipher = new SignalServiceCipher(address, deviceId, dataStore, sessionLock, certificateValidator); + cipher = new SignalServiceCipher(address, deviceId, dataStore.aci(), sessionLock, certificateValidator); }); } 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 724c2c49..90a26217 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 @@ -114,8 +114,7 @@ public class AccountHelper { public void updateAccountAttributes() throws IOException { dependencies.getAccountManager() - .setAccountAttributes(account.getEncryptedDeviceName(), - null, + .setAccountAttributes(null, account.getLocalRegistrationId(), true, null, @@ -123,7 +122,8 @@ public class AccountHelper { account.getSelfUnidentifiedAccessKey(), account.isUnrestrictedUnidentifiedAccess(), ServiceConfig.capabilities, - account.isDiscoverableByPhoneNumber()); + account.isDiscoverableByPhoneNumber(), + account.getEncryptedDeviceName()); } public void addDevice(DeviceLinkInfo deviceLinkInfo) throws IOException, InvalidDeviceLinkException { 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 166855a9..87be95b4 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 @@ -44,9 +44,12 @@ import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Medium; +import org.whispersystems.signalservice.api.SignalServiceAccountDataStore; +import org.whispersystems.signalservice.api.SignalServiceDataStore; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.AccountIdentifier; import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.storage.StorageKey; @@ -814,7 +817,31 @@ public class SignalAccount implements Closeable { save(); } - public SignalProtocolStore getSignalProtocolStore() { + public SignalServiceDataStore getSignalServiceDataStore() { + return new SignalServiceDataStore() { + @Override + public SignalServiceAccountDataStore get(final AccountIdentifier accountIdentifier) { + return getSignalServiceAccountDataStore(); + } + + @Override + public SignalServiceAccountDataStore aci() { + return getSignalServiceAccountDataStore(); + } + + @Override + public SignalServiceAccountDataStore pni() { + return getSignalServiceAccountDataStore(); + } + + @Override + public boolean isMultiDevice() { + return SignalAccount.this.isMultiDevice(); + } + }; + } + + public SignalServiceAccountDataStore getSignalServiceAccountDataStore() { return getOrCreate(() -> signalProtocolStore, () -> signalProtocolStore = new SignalProtocolStore(getPreKeyStore(), getSignedPreKeyStore(), @@ -957,8 +984,8 @@ public class SignalAccount implements Closeable { return getRecipientStore().resolveRecipient(getSelfRecipientAddress()); } - public String getEncryptedDeviceName() { - return encryptedDeviceName; + public byte[] getEncryptedDeviceName() { + return encryptedDeviceName == null ? null : Base64.getDecoder().decode(encryptedDeviceName); } public void setEncryptedDeviceName(final String encryptedDeviceName) { 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 7f200459..d7fb3b47 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalProtocolStore.java @@ -12,7 +12,7 @@ import org.whispersystems.libsignal.state.PreKeyStore; import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyStore; -import org.whispersystems.signalservice.api.SignalServiceDataStore; +import org.whispersystems.signalservice.api.SignalServiceAccountDataStore; import org.whispersystems.signalservice.api.SignalServiceSenderKeyStore; import org.whispersystems.signalservice.api.SignalServiceSessionStore; import org.whispersystems.signalservice.api.push.DistributionId; @@ -23,7 +23,7 @@ import java.util.Set; import java.util.UUID; import java.util.function.Supplier; -public class SignalProtocolStore implements SignalServiceDataStore { +public class SignalProtocolStore implements SignalServiceAccountDataStore { private final PreKeyStore preKeyStore; private final SignedPreKeyStore signedPreKeyStore; @@ -131,6 +131,7 @@ public class SignalProtocolStore implements SignalServiceDataStore { @Override public void archiveSession(final SignalProtocolAddress address) { sessionStore.archiveSession(address); + senderKeyStore.clearSenderKeySharedWith(List.of(address)); } @Override @@ -196,11 +197,4 @@ public class SignalProtocolStore implements SignalServiceDataStore { public boolean isMultiDevice() { return isMultiDevice.get(); } - - @Override - public Transaction beginTransaction() { - return () -> { - // No-op transaction should be safe, as it's only a performance improvement - }; - } } From 0e0ef402fc2446cd20227270c7e1eb4fa85f0a29 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Feb 2022 11:17:44 +0100 Subject: [PATCH 040/938] Improve decryption error logging --- .../signal/manager/helper/IncomingMessageHandler.java | 9 ++++++--- .../java/org/asamk/signal/manager/helper/SendHelper.java | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) 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 ac06baab..0b9cc80d 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 @@ -2,8 +2,6 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.SignalDependencies; -import org.asamk.signal.manager.api.TrustLevel; -import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.actions.HandleAction; import org.asamk.signal.manager.actions.RefreshPreKeysAction; import org.asamk.signal.manager.actions.RenewSessionAction; @@ -22,6 +20,8 @@ import org.asamk.signal.manager.actions.SendSyncKeysAction; import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.StickerPackId; +import org.asamk.signal.manager.api.TrustLevel; +import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupUtils; @@ -228,7 +228,10 @@ public final class IncomingMessageHandler { if (content.getDecryptionErrorMessage().isPresent()) { var message = content.getDecryptionErrorMessage().get(); - logger.debug("Received a decryption error message (resend request for {})", message.getTimestamp()); + logger.debug("Received a decryption error message from {}.{} (resend request for {})", + sender, + senderDeviceId, + message.getTimestamp()); if (message.getDeviceId() == account.getDeviceId()) { handleDecryptionErrorMessage(actions, sender, senderDeviceId, message); } else { 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 aedd29a2..712d5a31 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 @@ -213,6 +213,7 @@ public class SendHelper { public SendMessageResult resendMessage( final RecipientId recipientId, final long timestamp, final MessageSendLogEntry messageSendLogEntry ) { + logger.trace("Resending message {} to {}", timestamp, recipientId); if (messageSendLogEntry.groupId().isEmpty()) { return handleSendMessage(recipientId, (messageSender, address, unidentifiedAccess) -> messageSender.resendContent(address, From bb3b9692e3620863017be9a1c06dd3652d59ce1c Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Feb 2022 11:20:05 +0100 Subject: [PATCH 041/938] Implement basic listAccounts for dbus client mode Fixes #891 --- .../java/org/asamk/signal/dbus/DbusManagerImpl.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 51323395..8d8d333f 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -50,6 +50,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -540,7 +541,14 @@ public class DbusManagerImpl implements Manager { @Override public List> getContacts() { - throw new UnsupportedOperationException(); + return signal.listNumbers().stream().map(n -> { + final var contactName = signal.getContactName(n); + if (contactName.length() == 0) { + return null; + } + return new Pair<>(new RecipientAddress(null, n), + new Contact(contactName, null, 0, signal.isContactBlocked(n), false)); + }).filter(Objects::nonNull).toList(); } @Override From cf0cc50e328c0fc4fcbbecfa0ed21887613110a1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 12 Feb 2022 12:26:42 +0100 Subject: [PATCH 042/938] Improve stop receive handling Only interrupt the receive thread if it is currently waiting for new messages from the server, otherwise just set a stop flag. --- graalvm-config-dir/resource-config.json | 13 +++++++ .../org/asamk/signal/manager/ManagerImpl.java | 35 ++++++++---------- .../signal/manager/helper/ProfileHelper.java | 3 -- .../signal/manager/helper/ReceiveHelper.java | 37 +++++++++++++------ .../storage/sendLog/MessageSendLogStore.java | 2 +- 5 files changed, 55 insertions(+), 35 deletions(-) diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index c0866365..08a9dcc5 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -58,6 +58,12 @@ { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CH\\E" }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CI\\E" + }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CL\\E" + }, { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CN\\E" }, @@ -169,6 +175,9 @@ { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_TH\\E" }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_TR\\E" + }, { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_UA\\E" }, @@ -178,6 +187,9 @@ { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_US\\E" }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_VE\\E" + }, { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_XK\\E" }, @@ -210,6 +222,7 @@ "name":"net.sourceforge.argparse4j.internal.ArgumentParserImpl", "locales":[ "", + "en", "und" ] }] diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index c0643604..3f418de8 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -765,24 +765,17 @@ class ManagerImpl implements Manager { } receiveThread = new Thread(() -> { logger.debug("Starting receiving messages"); - while (!Thread.interrupted()) { - try { - context.getReceiveHelper().receiveMessages(Duration.ofMinutes(1), false, (envelope, e) -> { - synchronized (messageHandlers) { - Stream.concat(messageHandlers.stream(), weakHandlers.stream()).forEach(h -> { - try { - h.handleMessage(envelope, e); - } catch (Exception ex) { - logger.warn("Message handler failed, ignoring", ex); - } - }); + context.getReceiveHelper().receiveMessagesContinuously((envelope, e) -> { + synchronized (messageHandlers) { + Stream.concat(messageHandlers.stream(), weakHandlers.stream()).forEach(h -> { + try { + h.handleMessage(envelope, e); + } catch (Exception ex) { + logger.warn("Message handler failed, ignoring", ex); } }); - break; - } catch (IOException e) { - logger.warn("Receiving messages failed, retrying", e); } - } + }); logger.debug("Finished receiving messages"); synchronized (messageHandlers) { receiveThread = null; @@ -816,7 +809,10 @@ class ManagerImpl implements Manager { } private void stopReceiveThread(final Thread thread) { - thread.interrupt(); + if (context.getReceiveHelper().requestStopReceiveMessages()) { + logger.debug("Receive stop requested, interrupting read from server."); + thread.interrupt(); + } try { thread.join(); } catch (InterruptedException ignored) { @@ -1030,14 +1026,15 @@ class ManagerImpl implements Manager { dependencies.getSignalWebSocket().disconnect(); disposable.dispose(); + if (account != null) { + account.close(); + } + synchronized (closedListeners) { closedListeners.forEach(Runnable::run); closedListeners.clear(); } - if (account != null) { - account.close(); - } account = null; } } 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 ec4d385e..8e58e976 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 @@ -347,9 +347,6 @@ public final class ProfileHelper { .storeProfileAvatar(address, outputStream -> retrieveProfileAvatar(avatarPath, profileKey, outputStream)); } catch (Throwable e) { - if (e instanceof AssertionError && e.getCause() instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } logger.warn("Failed to download profile avatar, ignoring: {}", e.getMessage()); } } 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 8dfc7deb..7bc2c224 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 @@ -2,8 +2,8 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.SignalDependencies; -import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.actions.HandleAction; +import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.messageCache.CachedMessage; import org.slf4j.Logger; @@ -37,6 +37,8 @@ public class ReceiveHelper { private boolean ignoreAttachments = false; private boolean needsToRetryFailedMessages = false; private boolean hasCaughtUpWithOldMessages = false; + private boolean isWaitingForMessage = false; + private boolean shouldStop = false; private Callable authenticationFailureListener; private Callable caughtUpWithOldMessagesListener; @@ -66,6 +68,22 @@ public class ReceiveHelper { this.caughtUpWithOldMessagesListener = caughtUpWithOldMessagesListener; } + public boolean requestStopReceiveMessages() { + this.shouldStop = true; + return isWaitingForMessage; + } + + public void receiveMessagesContinuously(Manager.ReceiveMessageHandler handler) { + while (!shouldStop) { + try { + receiveMessages(Duration.ofMinutes(1), false, handler); + break; + } catch (IOException e) { + logger.warn("Receiving messages failed, retrying", e); + } + } + } + public void receiveMessages( Duration timeout, boolean returnOnTimeout, Manager.ReceiveMessageHandler handler ) throws IOException { @@ -92,6 +110,7 @@ public class ReceiveHelper { queuedActions.clear(); dependencies.getSignalWebSocket().disconnect(); webSocketStateDisposable.dispose(); + shouldStop = false; } } @@ -104,8 +123,9 @@ public class ReceiveHelper { final var signalWebSocket = dependencies.getSignalWebSocket(); var backOffCounter = 0; + isWaitingForMessage = false; - while (!Thread.interrupted()) { + while (!shouldStop) { if (needsToRetryFailedMessages) { retryFailedReceivedMessages(handler); needsToRetryFailedMessages = false; @@ -118,13 +138,16 @@ public class ReceiveHelper { } logger.debug("Checking for new message from server"); try { + isWaitingForMessage = true; var result = signalWebSocket.readOrEmpty(timeout.toMillis(), envelope1 -> { + isWaitingForMessage = false; final var recipientId = envelope1.hasSourceUuid() ? account.getRecipientStore() .resolveRecipient(envelope1.getSourceAddress()) : null; logger.trace("Storing new message from {}", recipientId); // store message on disk, before acknowledging receipt to the server cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId); }); + isWaitingForMessage = false; backOffCounter = 0; if (result.isPresent()) { @@ -143,7 +166,6 @@ public class ReceiveHelper { } } catch (AssertionError e) { if (e.getCause() instanceof InterruptedException) { - Thread.currentThread().interrupt(); break; } else { throw e; @@ -255,23 +277,14 @@ public class ReceiveHelper { private void handleQueuedActions(final Collection queuedActions) { logger.debug("Handling message actions"); - var interrupted = false; for (var action : queuedActions) { logger.debug("Executing action {}", action.getClass().getSimpleName()); try { action.execute(context); } catch (Throwable e) { - if ((e instanceof AssertionError || e instanceof RuntimeException) - && e.getCause() instanceof InterruptedException) { - interrupted = true; - continue; - } logger.warn("Message action failed.", e); } } - if (interrupted) { - Thread.currentThread().interrupt(); - } } private void onWebSocketStateChange(final WebSocketConnectionState s) { 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 c0e7d429..3b75de65 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 @@ -49,7 +49,7 @@ public class MessageSendLogStore implements AutoCloseable { this.cleanupThread = new Thread(() -> { try { final var interval = Duration.ofHours(1).toMillis(); - while (true) { + while (!Thread.interrupted()) { try (final var connection = database.getConnection()) { deleteOutdatedEntries(connection); } catch (SQLException e) { From 90c787f8e276db5db670fd9e5f0e15ad12438017 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 13 Feb 2022 20:07:08 +0100 Subject: [PATCH 043/938] Load multiple accounts in parallel --- graalvm-config-dir/reflect-config.json | 2 ++ .../signal/manager/SignalAccountFiles.java | 17 +++++++++++++---- .../storage/accounts/AccountsStorage.java | 4 ++-- .../manager/storage/accounts/AccountsStore.java | 14 +++++++++----- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 18954ec8..859fa0ea 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -1083,6 +1083,7 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ + {"name":"","parameterTypes":["java.lang.String","java.lang.String","org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack$JsonSticker","java.util.List"] }, {"name":"author","parameterTypes":[] }, {"name":"cover","parameterTypes":[] }, {"name":"stickers","parameterTypes":[] }, @@ -1095,6 +1096,7 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ + {"name":"","parameterTypes":["java.lang.Integer","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"contentType","parameterTypes":[] }, {"name":"emoji","parameterTypes":[] }, {"name":"file","parameterTypes":[] }, 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 379aa1cb..b3ed439d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -47,11 +47,11 @@ public class SignalAccountFiles { } public MultiAccountManager initMultiAccountManager() { - final var managers = getAllLocalAccountNumbers().stream().map(a -> { + final var managers = accountsStore.getAllAccounts().parallelStream().map(a -> { try { - return initManager(a); + return initManager(a.path()); } catch (NotRegisteredException | IOException | AccountCheckException e) { - logger.warn("Ignoring {}: {} ({})", a, e.getMessage(), e.getClass().getSimpleName()); + logger.warn("Ignoring {}: {} ({})", a.number(), e.getMessage(), e.getClass().getSimpleName()); return null; } }).filter(Objects::nonNull).toList(); @@ -61,7 +61,16 @@ public class SignalAccountFiles { public Manager initManager(String number) throws IOException, NotRegisteredException, AccountCheckException { final var accountPath = accountsStore.getPathByNumber(number); - if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) { + return this.initManager(number, accountPath); + } + + private Manager initManager( + String number, String accountPath + ) throws IOException, NotRegisteredException, AccountCheckException { + if (accountPath == null) { + throw new NotRegisteredException(); + } + if (!SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) { throw new NotRegisteredException(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java index 341d9572..7507c339 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java @@ -2,7 +2,7 @@ package org.asamk.signal.manager.storage.accounts; import java.util.List; -record AccountsStorage(List accounts) { +public record AccountsStorage(List accounts) { - record Account(String path, String number, String uuid) {} + public record Account(String path, String number, String uuid) {} } 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 07dfac3c..3c81c7d7 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,14 +41,18 @@ public class AccountsStore { } } - public Set getAllNumbers() { + public synchronized Set getAllNumbers() { return readAccounts().stream() .map(AccountsStorage.Account::number) .filter(Objects::nonNull) .collect(Collectors.toSet()); } - public String getPathByNumber(String number) { + public synchronized Set getAllAccounts() { + return readAccounts().stream().filter(a -> a.number() != null).collect(Collectors.toSet()); + } + + public synchronized String getPathByNumber(String number) { return readAccounts().stream() .filter(a -> number.equals(a.number())) .map(AccountsStorage.Account::path) @@ -56,7 +60,7 @@ public class AccountsStore { .orElse(null); } - public String getPathByAci(ACI aci) { + public synchronized String getPathByAci(ACI aci) { return readAccounts().stream() .filter(a -> aci.toString().equals(a.uuid())) .map(AccountsStorage.Account::path) @@ -64,7 +68,7 @@ public class AccountsStore { .orElse(null); } - public void updateAccount(String path, String number, ACI aci) { + public synchronized void updateAccount(String path, String number, ACI aci) { updateAccounts(accounts -> accounts.stream().map(a -> { if (path.equals(a.path())) { return new AccountsStorage.Account(a.path(), number, aci == null ? null : aci.toString()); @@ -81,7 +85,7 @@ public class AccountsStore { }).toList()); } - public String addAccount(String number, ACI aci) { + public synchronized String addAccount(String number, ACI aci) { final var accountPath = generateNewAccountPath(); final var account = new AccountsStorage.Account(accountPath, number, aci == null ? null : aci.toString()); updateAccounts(accounts -> { From ccce539843c8f61d4b34b5c608eb422760b0c48e Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 13 Feb 2022 20:08:02 +0100 Subject: [PATCH 044/938] Improve error messages when daemon is already running --- .../org/asamk/signal/commands/Commands.java | 2 +- .../asamk/signal/commands/DaemonCommand.java | 32 +++++++++---------- .../java/org/asamk/signal/util/IOUtils.java | 2 +- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/asamk/signal/commands/Commands.java b/src/main/java/org/asamk/signal/commands/Commands.java index aee9a7ba..29b28da0 100644 --- a/src/main/java/org/asamk/signal/commands/Commands.java +++ b/src/main/java/org/asamk/signal/commands/Commands.java @@ -29,8 +29,8 @@ public class Commands { addCommand(new RegisterCommand()); addCommand(new RemoveContactCommand()); addCommand(new RemoveDeviceCommand()); - addCommand(new RemoteDeleteCommand()); addCommand(new RemovePinCommand()); + addCommand(new RemoteDeleteCommand()); addCommand(new SendCommand()); addCommand(new SendContactsCommand()); addCommand(new SendReactionCommand()); diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 79e0cd82..e224bf1c 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -340,41 +340,39 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { } else { busType = DBusConnection.DBusBusType.SESSION; } + DBusConnection conn; try { - var conn = DBusConnection.getConnection(busType); + conn = DBusConnection.getConnection(busType); dbusRunner.run(conn, DbusConfig.getObjectPath()); - - conn.requestBusName(DbusConfig.getBusname()); - - logger.info("DBus daemon running on {} bus: {}", busType, DbusConfig.getBusname()); } catch (DBusException e) { - logger.error("Dbus command failed", e); - throw new UnexpectedErrorException("Dbus command failed", e); + throw new UnexpectedErrorException("Dbus command failed: " + e.getMessage(), e); } + + try { + conn.requestBusName(DbusConfig.getBusname()); + } catch (DBusException e) { + throw new UnexpectedErrorException("Dbus command failed, maybe signal-cli dbus daemon is already running: " + + e.getMessage(), e); + } + + logger.info("DBus daemon running on {} bus: {}", busType, DbusConfig.getBusname()); } private Thread exportMultiAccountManager( final DBusConnection conn, final Manager m, final boolean noReceiveOnStart ) { - try { - final var objectPath = DbusConfig.getObjectPath(m.getSelfNumber()); - return exportDbusObject(conn, objectPath, m, noReceiveOnStart); - } catch (DBusException e) { - logger.error("Failed to export object", e); - return null; - } + final var objectPath = DbusConfig.getObjectPath(m.getSelfNumber()); + return exportDbusObject(conn, objectPath, m, noReceiveOnStart); } private Thread exportDbusObject( final DBusConnection conn, final String objectPath, final Manager m, final boolean noReceiveOnStart - ) throws DBusException { + ) { final var signal = new DbusSignalImpl(m, conn, objectPath, noReceiveOnStart); final var initThread = new Thread(signal::initObjects); initThread.setName("dbus-init"); initThread.start(); - logger.debug("Exported dbus object: " + objectPath); - return initThread; } diff --git a/src/main/java/org/asamk/signal/util/IOUtils.java b/src/main/java/org/asamk/signal/util/IOUtils.java index b772887b..25850664 100644 --- a/src/main/java/org/asamk/signal/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/util/IOUtils.java @@ -136,7 +136,7 @@ public class IOUtils { logger.info("Listening on socket: " + address); postBind(address); } catch (IOException e) { - throw new IOErrorException("Failed to bind socket: " + e.getMessage(), e); + throw new IOErrorException("Failed to bind socket " + address + ": " + e.getMessage(), e); } return serverChannel; } From 832604e763a19df09ec4f3af6aa57a0f7e8439d6 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 19 Feb 2022 18:44:25 +0100 Subject: [PATCH 045/938] 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 ddd37d49..923eb843 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.9.9" + id("org.graalvm.buildtools.native") version "0.9.10" } version = "0.10.3" From 3f582e9c2e7db9adeb48dbc569f3c32509a63ad5 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 19 Feb 2022 18:46:45 +0100 Subject: [PATCH 046/938] Prevent a stale jsonrpc connection from interfering with message receiving Fixes #893 --- .../org/asamk/signal/manager/ManagerImpl.java | 2 +- .../asamk/signal/commands/DaemonCommand.java | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 3f418de8..941a6122 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -770,7 +770,7 @@ class ManagerImpl implements Manager { Stream.concat(messageHandlers.stream(), weakHandlers.stream()).forEach(h -> { try { h.handleMessage(envelope, e); - } catch (Exception ex) { + } catch (Throwable ex) { logger.warn("Message handler failed, ignoring", ex); } }); diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index e224bf1c..7c972496 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -35,7 +35,6 @@ import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -240,12 +239,13 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { private void runSocket(final ServerSocketChannel serverChannel, Consumer socketHandler) { final var thread = new Thread(() -> { while (true) { + final var connectionId = threadNumber.getAndIncrement(); final SocketChannel channel; final String clientString; try { channel = serverChannel.accept(); clientString = channel.getRemoteAddress() + " " + IOUtils.getUnixDomainPrincipal(channel); - logger.info("Accepted new client: " + clientString); + logger.info("Accepted new client connection {}: {}", connectionId, clientString); } catch (IOException e) { logger.error("Failed to accept new socket connection", e); synchronized (this) { @@ -256,12 +256,14 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { final var connectionThread = new Thread(() -> { try (final var c = channel) { socketHandler.accept(c); - logger.info("Connection closed: " + clientString); } catch (IOException e) { logger.warn("Failed to close channel", e); + } catch (Throwable e) { + logger.warn("Connection handler failed, closing connection", e); } + logger.info("Connection {} closed: {}", connectionId, clientString); }); - connectionThread.setName("daemon-connection-" + threadNumber.getAndIncrement()); + connectionThread.setName("daemon-connection-" + connectionId); connectionThread.start(); } }); @@ -298,11 +300,9 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { c.addOnManagerAddedHandler(m -> { final var thread = exportMultiAccountManager(connection, m, noReceiveOnStart); - if (thread != null) { - try { - thread.join(); - } catch (InterruptedException ignored) { - } + try { + thread.join(); + } catch (InterruptedException ignored) { } }); c.addOnManagerRemovedHandler(m -> { @@ -319,7 +319,6 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { final var initThreads = c.getManagers() .stream() .map(m -> exportMultiAccountManager(connection, m, noReceiveOnStart)) - .filter(Objects::nonNull) .toList(); for (var t : initThreads) { From 72611296095e8a91d39fa5929c8c61548d413c4f Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 4 Feb 2022 15:47:06 +0100 Subject: [PATCH 047/938] Rename error field in json receive response to exception --- .../java/org/asamk/signal/json/JsonReceiveMessageHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/json/JsonReceiveMessageHandler.java b/src/main/java/org/asamk/signal/json/JsonReceiveMessageHandler.java index 758c5f5a..da29ed6c 100644 --- a/src/main/java/org/asamk/signal/json/JsonReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/json/JsonReceiveMessageHandler.java @@ -25,7 +25,7 @@ public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler final var object = new HashMap(); object.put("account", m.getSelfNumber()); if (exception != null) { - object.put("error", JsonError.from(exception)); + object.put("exception", JsonError.from(exception)); } if (envelope != null) { From abde122a35d85f5db956d652300d7a995d460863 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 5 Feb 2022 16:41:22 +0100 Subject: [PATCH 048/938] Implement JSON-RPC client PoC --- client/.gitignore | 1 + client/Cargo.lock | 910 ++++++++++++++++++++++++++++++++++++++++++ client/Cargo.toml | 25 ++ client/src/cli.rs | 340 ++++++++++++++++ client/src/jsonrpc.rs | 288 +++++++++++++ client/src/main.rs | 355 ++++++++++++++++ client/src/tcp.rs | 29 ++ run_tests.sh | 47 ++- 8 files changed, 1982 insertions(+), 13 deletions(-) create mode 100644 client/.gitignore create mode 100644 client/Cargo.lock create mode 100644 client/Cargo.toml create mode 100644 client/src/cli.rs create mode 100644 client/src/jsonrpc.rs create mode 100644 client/src/main.rs create mode 100644 client/src/tcp.rs diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 00000000..b83d2226 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/client/Cargo.lock b/client/Cargo.lock new file mode 100644 index 00000000..77afe1dd --- /dev/null +++ b/client/Cargo.lock @@ -0,0 +1,910 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a99269dff3bc004caa411f38845c20303f1e393ca2bd6581576fa3a7f59577d" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f1fea81f183005ced9e59cdb01737ef2423956dac5a6d731b06b2ecfaa3467" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd1122e63869df2cb309f449da1ad54a7c6dfeb7c7e6ccd8e0825d9eb93bb72" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-executor" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures 0.1.31", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "globset" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jsonrpc-client-transports" +version = "18.0.0" +source = "git+https://github.com/AsamK/jsonrpc?branch=client_subscribe_named_params#8a68f95e202943fd338fd0c14da08c2dc8f6e6a6" +dependencies = [ + "derive_more", + "futures 0.3.21", + "jsonrpc-core 18.0.0 (git+https://github.com/AsamK/jsonrpc?branch=client_subscribe_named_params)", + "jsonrpc-pubsub", + "jsonrpc-server-utils 18.0.0 (git+https://github.com/AsamK/jsonrpc?branch=client_subscribe_named_params)", + "log", + "parity-tokio-ipc", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures 0.3.21", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "git+https://github.com/AsamK/jsonrpc?branch=client_subscribe_named_params#8a68f95e202943fd338fd0c14da08c2dc8f6e6a6" +dependencies = [ + "futures 0.3.21", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "jsonrpc-core-client" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0" +dependencies = [ + "futures 0.3.21", + "jsonrpc-client-transports", +] + +[[package]] +name = "jsonrpc-derive" +version = "18.0.0" +source = "git+https://github.com/AsamK/jsonrpc?branch=client_subscribe_named_params#8a68f95e202943fd338fd0c14da08c2dc8f6e6a6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jsonrpc-pubsub" +version = "18.0.0" +source = "git+https://github.com/AsamK/jsonrpc?branch=client_subscribe_named_params#8a68f95e202943fd338fd0c14da08c2dc8f6e6a6" +dependencies = [ + "futures 0.3.21", + "jsonrpc-core 18.0.0 (git+https://github.com/AsamK/jsonrpc?branch=client_subscribe_named_params)", + "lazy_static", + "log", + "parking_lot", + "rand", + "serde", +] + +[[package]] +name = "jsonrpc-server-utils" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" +dependencies = [ + "bytes", + "futures 0.3.21", + "globset", + "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "log", + "tokio", + "tokio-stream", + "tokio-util", + "unicase", +] + +[[package]] +name = "jsonrpc-server-utils" +version = "18.0.0" +source = "git+https://github.com/AsamK/jsonrpc?branch=client_subscribe_named_params#8a68f95e202943fd338fd0c14da08c2dc8f6e6a6" +dependencies = [ + "bytes", + "futures 0.3.21", + "globset", + "jsonrpc-core 18.0.0 (git+https://github.com/AsamK/jsonrpc?branch=client_subscribe_named_params)", + "lazy_static", + "log", + "tokio", + "tokio-stream", + "tokio-util", + "unicase", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "mio" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + +[[package]] +name = "parity-tokio-ipc" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" +dependencies = [ + "futures 0.3.21", + "libc", + "log", + "rand", + "tokio", + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-cli-client" +version = "0.0.1" +dependencies = [ + "anyhow", + "clap", + "jsonrpc-client-transports", + "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core-client", + "jsonrpc-derive", + "jsonrpc-server-utils 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" + +[[package]] +name = "tokio" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/client/Cargo.toml b/client/Cargo.toml new file mode 100644 index 00000000..e14d2d74 --- /dev/null +++ b/client/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "signal-cli-client" +version = "0.0.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +clap = { version = "3", features = ["cargo", "derive"] } +jsonrpc-core = "18" +jsonrpc-core-client = "18" +jsonrpc-client-transports = { version = "18", default-features = false, features = [ + "ipc", +] } +jsonrpc-derive = "18" +jsonrpc-server-utils = "18" +log = "0.4" +serde = "1" +serde_json = "1" +tokio = { version = "1", features = ["rt", "macros", "net"] } + +[patch.crates-io] +jsonrpc-client-transports = { git = "https://github.com/AsamK/jsonrpc", branch = "client_subscribe_named_params" } +jsonrpc-derive = { git = "https://github.com/AsamK/jsonrpc", branch = "client_subscribe_named_params" } diff --git a/client/src/cli.rs b/client/src/cli.rs new file mode 100644 index 00000000..bc6a3079 --- /dev/null +++ b/client/src/cli.rs @@ -0,0 +1,340 @@ +use clap::{crate_version, ArgEnum, Parser, Subcommand}; +use std::{ffi::OsString, net::SocketAddr}; + +/// JSON-RPC client for signal-cli +#[derive(Parser, Debug)] +#[clap(rename_all = "kebab-case", version=crate_version!())] +pub struct Cli { + /// Account to use (for daemon in multi-account mode) + #[clap(short = 'a', long)] + pub account: Option, + + /// TCP host and port of signal-cli daemon + #[clap(long)] + pub json_rpc_tcp: Option>, + + /// UNIX socket address and port of signal-cli daemon + #[clap(long)] + pub json_rpc_socket: Option>, + + #[clap(arg_enum, long, default_value_t = OutputTypes::Json)] + pub output: OutputTypes, + + #[clap(long)] + pub verbose: bool, + + #[clap(subcommand)] + pub command: CliCommands, +} + +#[derive(ArgEnum, Clone, Debug)] +#[clap(rename_all = "kebab-case")] +pub enum OutputTypes { + PlainText, + Json, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Subcommand, Debug)] +#[clap(rename_all = "camelCase", version=crate_version!())] +pub enum CliCommands { + AddDevice { + #[clap(long)] + uri: String, + }, + #[clap(rename_all = "kebab-case")] + Block { + recipient: Vec, + + #[clap(short = 'g', long)] + group_id: Vec, + }, + GetUserStatus { + recipient: Vec, + }, + JoinGroup { + #[clap(long)] + uri: String, + }, + Link { + #[clap(short = 'n', long)] + name: String, + }, + ListAccounts, + ListContacts, + ListDevices, + ListGroups { + #[clap(short = 'd', long)] + detailed: bool, + }, + ListIdentities { + #[clap(short = 'n', long)] + number: Option, + }, + ListStickerPacks, + QuitGroup { + #[clap(short = 'g', long = "group-id")] + group_id: String, + #[clap(long)] + delete: bool, + #[clap(long)] + admin: Vec, + }, + Receive { + #[clap(short = 't', long, default_value_t = 3.0)] + timeout: f64, + }, + Register { + #[clap(short = 'v', long)] + voice: bool, + #[clap(long)] + captcha: Option, + }, + RemoveContact { + recipient: String, + #[clap(long)] + forget: bool, + }, + RemoveDevice { + #[clap(short = 'd', long = "device-id")] + device_id: u32, + }, + RemovePin, + RemoteDelete { + #[clap(short = 't', long = "target-timestamp")] + target_timestamp: u64, + + recipient: Vec, + + #[clap(short = 'g', long = "group-id")] + group_id: Vec, + + #[clap(long = "note-to-self")] + note_to_self: bool, + }, + #[clap(rename_all = "kebab-case")] + Send { + recipient: Vec, + + #[clap(short = 'g', long)] + group_id: Vec, + + #[clap(long)] + note_to_self: bool, + + #[clap(short = 'e', long)] + end_session: bool, + + #[clap(short = 'm', long)] + message: Option, + + #[clap(short = 'a', long)] + attachment: Vec, + + #[clap(long)] + mention: Vec, + + #[clap(long)] + quote_timestamp: Option, + + #[clap(long)] + quote_author: Option, + + #[clap(long)] + quote_message: Option, + + #[clap(long)] + quote_mention: Vec, + + #[clap(long)] + sticker: Option, + }, + SendContacts, + SendReaction { + recipient: Vec, + + #[clap(short = 'g', long = "group-id")] + group_id: Vec, + + #[clap(long = "note-to-self")] + note_to_self: bool, + + #[clap(short = 'e', long)] + emoji: String, + + #[clap(short = 'a', long = "target-author")] + target_author: String, + + #[clap(short = 't', long = "target-timestamp")] + target_timestamp: u64, + + #[clap(short = 'r', long)] + remove: bool, + }, + SendReceipt { + recipient: String, + + #[clap(short = 't', long = "target-timestamp")] + target_timestamp: Vec, + + #[clap(arg_enum, long)] + r#type: ReceiptType, + }, + SendSyncRequest, + SendTyping { + recipient: Vec, + + #[clap(short = 'g', long = "group-id")] + group_id: Vec, + + #[clap(short = 's', long)] + stop: bool, + }, + SetPin { + pin: String, + }, + SubmitRateLimitChallenge { + challenge: String, + captcha: String, + }, + Trust { + recipient: String, + + #[clap(short = 'a', long = "trust-all-known-keys")] + trust_all_known_keys: bool, + + #[clap(short = 'v', long = "verified-safety-number")] + verified_safety_number: Option, + }, + #[clap(rename_all = "kebab-case")] + Unblock { + recipient: Vec, + + #[clap(short = 'g', long)] + group_id: Vec, + }, + Unregister { + #[clap(long = "delete-account")] + delete_account: bool, + }, + UpdateAccount { + #[clap(short = 'n', long = "device-name")] + device_name: Option, + }, + UpdateConfiguration { + #[clap(long = "read-receipts", parse(try_from_str))] + read_receipts: Option, + + #[clap(long = "unidentified-delivery-indicators")] + unidentified_delivery_indicators: Option, + + #[clap(long = "typing-indicators")] + typing_indicators: Option, + + #[clap(long = "link-previews")] + link_previews: Option, + }, + UpdateContact { + recipient: String, + + #[clap(short = 'e', long)] + expiration: Option, + + #[clap(short = 'n', long)] + name: Option, + }, + UpdateGroup { + #[clap(short = 'g', long = "group-id")] + group_id: Option, + + #[clap(short = 'n', long)] + name: Option, + + #[clap(short = 'd', long)] + description: Option, + + #[clap(short = 'a', long)] + avatar: Option, + + #[clap(short = 'm', long)] + member: Vec, + + #[clap(short = 'r', long = "remove-member")] + remove_member: Vec, + + #[clap(long)] + admin: Vec, + + #[clap(long = "remove-admin")] + remove_admin: Vec, + + #[clap(long = "reset-link")] + reset_link: bool, + + #[clap(arg_enum, long)] + link: Option, + + #[clap(arg_enum, long = "set-permission-add-member")] + set_permission_add_member: Option, + + #[clap(arg_enum, long = "set-permission-edit-details")] + set_permission_edit_details: Option, + + #[clap(arg_enum, long = "set-permission-send-messages")] + set_permission_send_messages: Option, + + #[clap(short = 'e', long)] + expiration: Option, + }, + UpdateProfile { + #[clap(long = "given-name")] + given_name: Option, + + #[clap(long = "family-name")] + family_name: Option, + + #[clap(long)] + about: Option, + + #[clap(long = "about-emoji")] + about_emoji: Option, + + #[clap(long)] + avatar: Option, + + #[clap(long = "remove-avatar")] + remove_avatar: bool, + }, + UploadStickerPack { + path: String, + }, + Verify { + verification_code: String, + + #[clap(short = 'p', long)] + pin: Option, + }, + Version, +} + +#[derive(ArgEnum, Clone, Debug)] +#[clap(rename_all = "kebab-case")] +pub enum ReceiptType { + Read, + Viewed, +} + +#[derive(ArgEnum, Clone, Debug)] +#[clap(rename_all = "kebab-case")] +pub enum LinkState { + Enabled, + EnabledWithApproval, + Disabled, +} + +#[derive(ArgEnum, Clone, Debug)] +#[clap(rename_all = "kebab-case")] +pub enum GroupPermission { + EveryMember, + OnlyAdmins, +} diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs new file mode 100644 index 00000000..c0feddd0 --- /dev/null +++ b/client/src/jsonrpc.rs @@ -0,0 +1,288 @@ +use std::path::Path; + +use jsonrpc_client_transports::{transports::ipc, RpcError}; +use jsonrpc_core::serde::Deserialize; +use jsonrpc_derive::rpc; +use tokio::net::ToSocketAddrs; + +pub type SignalCliClient = gen_client::Client; + +#[rpc(client, params = "named")] +pub trait Rpc { + #[rpc(name = "addDevice", params = "named")] + fn add_device(&self, account: Option, uri: String) -> Result; + + #[rpc(name = "block", params = "named")] + fn block( + &self, + account: Option, + recipients: Vec, + #[allow(non_snake_case)] groupIds: Vec, + ) -> Result; + + #[rpc(name = "getUserStatus", params = "named")] + fn get_user_status(&self, account: Option, recipients: Vec) -> Result; + + #[rpc(name = "joinGroup", params = "named")] + fn join_group(&self, account: Option, uri: String) -> Result; + + #[rpc(name = "finishLink", params = "named")] + fn finish_link( + &self, + #[allow(non_snake_case)] deviceLinkUri: String, + #[allow(non_snake_case)] deviceName: String, + ) -> Result; + + #[rpc(name = "listAccounts", params = "named")] + fn list_accounts(&self) -> Result; + + #[rpc(name = "listContacts", params = "named")] + fn list_contacts(&self, account: Option) -> Result; + + #[rpc(name = "listDevices", params = "named")] + fn list_devices(&self, account: Option) -> Result; + + #[rpc(name = "listGroups", params = "named")] + fn list_groups(&self, account: Option) -> Result; + + #[rpc(name = "listIdentities", params = "named")] + fn list_identities(&self, account: Option, number: Option) -> Result; + + #[rpc(name = "listStickerPacks", params = "named")] + fn list_sticker_packs(&self, account: Option) -> Result; + + #[rpc(name = "quitGroup", params = "named")] + fn quit_group( + &self, + account: Option, + #[allow(non_snake_case)] groupId: String, + delete: bool, + admins: Vec, + ) -> Result; + + #[rpc(name = "register", params = "named")] + fn register( + &self, + account: Option, + voice: bool, + captcha: Option, + ) -> Result; + + #[rpc(name = "removeContact", params = "named")] + fn remove_contact( + &self, + account: Option, + recipient: String, + forget: bool, + ) -> Result; + + #[rpc(name = "removeDevice", params = "named")] + fn remove_device( + &self, + account: Option, + #[allow(non_snake_case)] deviceId: u32, + ) -> Result; + + #[rpc(name = "removePin", params = "named")] + fn remove_pin(&self, account: Option) -> Result; + + #[rpc(name = "remoteDelete", params = "named")] + fn remote_delete( + &self, + account: Option, + #[allow(non_snake_case)] targetTimestamp: u64, + recipients: Vec, + #[allow(non_snake_case)] groupIds: Vec, + #[allow(non_snake_case)] noteToSelf: bool, + ) -> Result; + + #[rpc(name = "send", params = "named")] + fn send( + &self, + account: Option, + recipients: Vec, + #[allow(non_snake_case)] groupIds: Vec, + #[allow(non_snake_case)] noteToSelf: bool, + #[allow(non_snake_case)] endSession: bool, + message: String, + attachments: Vec, + mentions: Vec, + #[allow(non_snake_case)] quoteTimestamp: Option, + #[allow(non_snake_case)] quoteAuthor: Option, + #[allow(non_snake_case)] quoteMessage: Option, + #[allow(non_snake_case)] quoteMention: Vec, + sticker: Option, + ) -> Result; + + #[rpc(name = "sendContacts", params = "named")] + fn send_contacts(&self, account: Option) -> Result; + + #[rpc(name = "sendReaction", params = "named")] + fn send_reaction( + &self, + account: Option, + recipients: Vec, + #[allow(non_snake_case)] groupIds: Vec, + #[allow(non_snake_case)] noteToSelf: bool, + emoji: String, + #[allow(non_snake_case)] targetAuthor: String, + #[allow(non_snake_case)] targetTimestamp: u64, + remove: bool, + ) -> Result; + + #[rpc(name = "sendReceipt", params = "named")] + fn send_receipt( + &self, + account: Option, + recipient: String, + #[allow(non_snake_case)] targetTimestamps: Vec, + r#type: String, + ) -> Result; + + #[rpc(name = "sendSyncRequest", params = "named")] + fn send_sync_request(&self, account: Option) -> Result; + + #[rpc(name = "sendTyping", params = "named")] + fn send_typing( + &self, + account: Option, + recipients: Vec, + #[allow(non_snake_case)] groupIds: Vec, + stop: bool, + ) -> Result; + + #[rpc(name = "setPin", params = "named")] + fn set_pin(&self, account: Option, pin: String) -> Result; + + #[rpc(name = "submitRateLimitChallenge", params = "named")] + fn submit_rate_limit_challenge( + &self, + account: Option, + challenge: String, + captcha: String, + ) -> Result; + + #[rpc(name = "startLink", params = "named")] + fn start_link(&self, account: Option) -> Result; + + #[rpc(name = "trust", params = "named")] + fn trust( + &self, + account: Option, + recipient: String, + #[allow(non_snake_case)] trustAllKnownKeys: bool, + #[allow(non_snake_case)] verifiedSafetyNumber: Option, + ) -> Result; + + #[rpc(name = "unblock", params = "named")] + fn unblock( + &self, + account: Option, + recipients: Vec, + #[allow(non_snake_case)] groupIds: Vec, + ) -> Result; + + #[rpc(name = "unregister", params = "named")] + fn unregister( + &self, + account: Option, + #[allow(non_snake_case)] deleteAccount: bool, + ) -> Result; + + #[rpc(name = "updateAccount", params = "named")] + fn update_account( + &self, + account: Option, + #[allow(non_snake_case)] deviceName: Option, + ) -> Result; + + #[rpc(name = "updateConfiguration", params = "named")] + fn update_configuration( + &self, + account: Option, + #[allow(non_snake_case)] readReceiptes: Option, + #[allow(non_snake_case)] unidentifiedDeliveryIndicators: Option, + #[allow(non_snake_case)] typingIndicators: Option, + #[allow(non_snake_case)] linkPreviews: Option, + ) -> Result; + + #[rpc(name = "updateContact", params = "named")] + fn update_contact( + &self, + account: Option, + recipient: String, + name: Option, + expiration: Option, + ) -> Result; + + #[rpc(name = "updateGroup", params = "named")] + fn update_group( + &self, + account: Option, + #[allow(non_snake_case)] groupId: Option, + name: Option, + description: Option, + avatar: Option, + member: Vec, + #[allow(non_snake_case)] removeMember: Vec, + admin: Vec, + #[allow(non_snake_case)] removeAdmin: Vec, + #[allow(non_snake_case)] resetLink: bool, + #[allow(non_snake_case)] link: Option, + #[allow(non_snake_case)] setPermissionAddMember: Option, + #[allow(non_snake_case)] setPermissionEditDetails: Option, + #[allow(non_snake_case)] setPermissionSendMessages: Option, + expiration: Option, + ) -> Result; + + #[rpc(name = "updateProfile", params = "named")] + fn update_profile( + &self, + account: Option, + #[allow(non_snake_case)] givenName: Option, + #[allow(non_snake_case)] familyName: Option, + about: Option, + #[allow(non_snake_case)] aboutEmoji: Option, + avatar: Option, + #[allow(non_snake_case)] removeAvatar: bool, + ) -> Result; + + #[rpc(name = "uploadStickerPack", params = "named")] + fn upload_sticker_pack(&self, account: Option, path: String) -> Result; + + #[rpc(name = "verify", params = "named")] + fn verify( + &self, + account: Option, + #[allow(non_snake_case)] verificationCode: String, + pin: Option, + ) -> Result; + + #[pubsub( + subscription = "receive", + subscribe, + name = "subscribeReceive", + params = "named" + )] + fn subscribe_receive(&self, _: Self::Metadata, _: Subscriber, account: Option); + + #[pubsub(subscription = "receive", unsubscribe, name = "unsubscribeReceive")] + fn unsubscribe_receive(&self, _: Option, _: SubscriptionId) -> Result; + + #[rpc(name = "version")] + fn version(&self) -> Result; +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct JsonLink { + pub device_link_uri: String, +} + +pub async fn connect_tcp(tcp: impl ToSocketAddrs) -> Result { + super::tcp::connect::<_, SignalCliClient>(tcp).await +} + +pub async fn connect_unix(socket_path: impl AsRef) -> Result { + ipc::connect::<_, SignalCliClient>(socket_path).await +} diff --git a/client/src/main.rs b/client/src/main.rs new file mode 100644 index 00000000..1ad8dbcf --- /dev/null +++ b/client/src/main.rs @@ -0,0 +1,355 @@ +use clap::StructOpt; +use jsonrpc_client_transports::{RpcError, TypedSubscriptionStream}; +use jsonrpc_core::{futures_util::StreamExt, Value}; +use std::{path::PathBuf, time::Duration}; +use tokio::{select, time::sleep}; + +use crate::cli::{GroupPermission, LinkState}; + +mod cli; +#[allow(clippy::too_many_arguments)] +mod jsonrpc; +mod tcp; + +const DEFAULT_TCP: &str = "127.0.0.1:7583"; +const DEFAULT_SOCKET_SUFFIX: &str = "signal-cli/socket"; + +#[tokio::main] +async fn main() -> Result<(), anyhow::Error> { + let cli = cli::Cli::parse(); + + let client = connect(&cli) + .await + .map_err(|e| anyhow::anyhow!("Failed to connect to socket: {e}"))?; + + let result = match cli.command { + cli::CliCommands::Receive { timeout } => { + let mut stream = client + .subscribe_receive(cli.account) + .map_err(|e| anyhow::anyhow!("JSON-RPC command failed: {:?}", e))?; + + { + while let Some(v) = stream_next(timeout, &mut stream).await { + let v = v.map_err(|e| anyhow::anyhow!("JSON-RPC command failed: {:?}", e))?; + println!("{v}"); + } + } + return Ok(()); + } + cli::CliCommands::AddDevice { uri } => client.add_device(cli.account, uri).await, + cli::CliCommands::Block { + recipient, + group_id, + } => client.block(cli.account, recipient, group_id).await, + cli::CliCommands::GetUserStatus { recipient } => { + client.get_user_status(cli.account, recipient).await + } + cli::CliCommands::JoinGroup { uri } => client.join_group(cli.account, uri).await, + cli::CliCommands::Link { name } => { + let url = client + .start_link(cli.account) + .await + .map_err(|e| anyhow::anyhow!("JSON-RPC command startLink failed: {e:?}",))? + .device_link_uri; + println!("{}", url); + client.finish_link(url, name).await + } + cli::CliCommands::ListAccounts => client.list_accounts().await, + cli::CliCommands::ListContacts => client.list_contacts(cli.account).await, + cli::CliCommands::ListDevices => client.list_devices(cli.account).await, + cli::CliCommands::ListGroups { detailed: _ } => client.list_groups(cli.account).await, + cli::CliCommands::ListIdentities { number } => { + client.list_identities(cli.account, number).await + } + cli::CliCommands::ListStickerPacks => client.list_sticker_packs(cli.account).await, + cli::CliCommands::QuitGroup { + group_id, + delete, + admin, + } => { + client + .quit_group(cli.account, group_id, delete, admin) + .await + } + cli::CliCommands::Register { voice, captcha } => { + client.register(cli.account, voice, captcha).await + } + cli::CliCommands::RemoveContact { recipient, forget } => { + client.remove_contact(cli.account, recipient, forget).await + } + cli::CliCommands::RemoveDevice { device_id } => { + client.remove_device(cli.account, device_id).await + } + cli::CliCommands::RemovePin => client.remove_pin(cli.account).await, + cli::CliCommands::RemoteDelete { + target_timestamp, + recipient, + group_id, + note_to_self, + } => { + client + .remote_delete( + cli.account, + target_timestamp, + recipient, + group_id, + note_to_self, + ) + .await + } + cli::CliCommands::Send { + recipient, + group_id, + note_to_self, + end_session, + message, + attachment, + mention, + quote_timestamp, + quote_author, + quote_message, + quote_mention, + sticker, + } => { + client + .send( + cli.account, + recipient, + group_id, + note_to_self, + end_session, + message.unwrap_or_default(), + attachment, + mention, + quote_timestamp, + quote_author, + quote_message, + quote_mention, + sticker, + ) + .await + } + cli::CliCommands::SendContacts => client.send_contacts(cli.account).await, + cli::CliCommands::SendReaction { + recipient, + group_id, + note_to_self, + emoji, + target_author, + target_timestamp, + remove, + } => { + client + .send_reaction( + cli.account, + recipient, + group_id, + note_to_self, + emoji, + target_author, + target_timestamp, + remove, + ) + .await + } + cli::CliCommands::SendReceipt { + recipient, + target_timestamp, + r#type, + } => { + client + .send_receipt( + cli.account, + recipient, + target_timestamp, + match r#type { + cli::ReceiptType::Read => "read".to_owned(), + cli::ReceiptType::Viewed => "viewed".to_owned(), + }, + ) + .await + } + cli::CliCommands::SendSyncRequest => client.send_sync_request(cli.account).await, + cli::CliCommands::SendTyping { + recipient, + group_id, + stop, + } => { + client + .send_typing(cli.account, recipient, group_id, stop) + .await + } + cli::CliCommands::SetPin { pin } => client.set_pin(cli.account, pin).await, + cli::CliCommands::SubmitRateLimitChallenge { challenge, captcha } => { + client + .submit_rate_limit_challenge(cli.account, challenge, captcha) + .await + } + cli::CliCommands::Trust { + recipient, + trust_all_known_keys, + verified_safety_number, + } => { + client + .trust( + cli.account, + recipient, + trust_all_known_keys, + verified_safety_number, + ) + .await + } + cli::CliCommands::Unblock { + recipient, + group_id, + } => client.unblock(cli.account, recipient, group_id).await, + cli::CliCommands::Unregister { delete_account } => { + client.unregister(cli.account, delete_account).await + } + cli::CliCommands::UpdateAccount { device_name } => { + client.update_account(cli.account, device_name).await + } + cli::CliCommands::UpdateConfiguration { + read_receipts, + unidentified_delivery_indicators, + typing_indicators, + link_previews, + } => { + client + .update_configuration( + cli.account, + read_receipts, + unidentified_delivery_indicators, + typing_indicators, + link_previews, + ) + .await + } + cli::CliCommands::UpdateContact { + recipient, + expiration, + name, + } => { + client + .update_contact(cli.account, recipient, name, expiration) + .await + } + cli::CliCommands::UpdateGroup { + group_id, + name, + description, + avatar, + member, + remove_member, + admin, + remove_admin, + reset_link, + link, + set_permission_add_member, + set_permission_edit_details, + set_permission_send_messages, + expiration, + } => { + client + .update_group( + cli.account, + group_id, + name, + description, + avatar, + member, + remove_member, + admin, + remove_admin, + reset_link, + link.map(|link| match link { + LinkState::Enabled => "enabled".to_owned(), + LinkState::EnabledWithApproval => "enabledWithApproval".to_owned(), + LinkState::Disabled => "disabled".to_owned(), + }), + set_permission_add_member.map(|p| match p { + GroupPermission::EveryMember => "everyMember".to_owned(), + GroupPermission::OnlyAdmins => "onlyAdmins".to_owned(), + }), + set_permission_edit_details.map(|p| match p { + GroupPermission::EveryMember => "everyMember".to_owned(), + GroupPermission::OnlyAdmins => "onlyAdmins".to_owned(), + }), + set_permission_send_messages.map(|p| match p { + GroupPermission::EveryMember => "everyMember".to_owned(), + GroupPermission::OnlyAdmins => "onlyAdmins".to_owned(), + }), + expiration, + ) + .await + } + cli::CliCommands::UpdateProfile { + given_name, + family_name, + about, + about_emoji, + avatar, + remove_avatar, + } => { + client + .update_profile( + cli.account, + given_name, + family_name, + about, + about_emoji, + avatar, + remove_avatar, + ) + .await + } + cli::CliCommands::UploadStickerPack { path } => { + client.upload_sticker_pack(cli.account, path).await + } + cli::CliCommands::Verify { + verification_code, + pin, + } => client.verify(cli.account, verification_code, pin).await, + cli::CliCommands::Version => client.version().await, + }; + + result + .map(|v| println!("{v}")) + .map_err(|e| anyhow::anyhow!("JSON-RPC command failed: {e:?}",))?; + Ok(()) +} + +async fn connect(cli: &cli::Cli) -> Result { + if let Some(tcp) = cli.json_rpc_tcp { + let socket_addr = tcp.unwrap_or_else(|| DEFAULT_TCP.parse().unwrap()); + jsonrpc::connect_tcp(socket_addr).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() + }) + }) + .unwrap_or_else(|| ("/run".to_owned() + DEFAULT_SOCKET_SUFFIX).into()); + jsonrpc::connect_unix(socket_path).await + } +} + +async fn stream_next( + timeout: f64, + stream: &mut TypedSubscriptionStream, +) -> Option> { + if timeout < 0.0 { + stream.next().await + } else { + select! { + v = stream.next() => v, + _= sleep(Duration::from_millis((timeout * 1000.0) as u64)) => None, + } + } +} diff --git a/client/src/tcp.rs b/client/src/tcp.rs new file mode 100644 index 00000000..53650381 --- /dev/null +++ b/client/src/tcp.rs @@ -0,0 +1,29 @@ +use jsonrpc_client_transports::{transports::duplex, RpcChannel, RpcError}; +use jsonrpc_core::futures_util::{SinkExt, StreamExt, TryStreamExt}; +use jsonrpc_server_utils::{codecs::StreamCodec, tokio_util::codec::Decoder}; +use tokio::net::{TcpStream, ToSocketAddrs}; + +/// Connect to a JSON-RPC TCP server. +pub async fn connect>( + socket: S, +) -> Result { + let connection = TcpStream::connect(socket) + .await + .map_err(|e| RpcError::Other(Box::new(e)))?; + let (sink, stream) = StreamCodec::stream_incoming().framed(connection).split(); + let sink = sink.sink_map_err(|e| RpcError::Other(Box::new(e))); + let stream = stream.map_err(|e| log::error!("TCP stream error: {}", e)); + + let (client, sender) = duplex( + Box::pin(sink), + Box::pin( + stream + .take_while(|x| std::future::ready(x.is_ok())) + .map(|x| x.expect("Stream is closed upon first error.")), + ), + ); + + tokio::spawn(client); + + Ok(sender.into()) +} diff --git a/run_tests.sh b/run_tests.sh index 277739cb..341c825f 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -16,6 +16,7 @@ NUMBER_1="$1" NUMBER_2="$2" TEST_PIN_1=456test_pin_foo123 NATIVE=0 +JSON_RPC=0 PATH_TEST_CONFIG="$PWD/build/test-config" PATH_MAIN="$PATH_TEST_CONFIG/main" @@ -23,6 +24,12 @@ PATH_LINK="$PATH_TEST_CONFIG/link" if [ "$NATIVE" -eq 1 ]; then SIGNAL_CLI="$PWD/build/native/nativeCompile/signal-cli" +elif [ "$JSON_RPC" -eq 1 ]; then + (cd client && cargo build) + "$PWD/build/install/signal-cli/bin/signal-cli" --verbose --verbose --trust-new-identities=always --config="$PATH_MAIN" --service-environment="staging" daemon --socket --receive-mode=manual& + "$PWD/build/install/signal-cli/bin/signal-cli" --verbose --verbose --trust-new-identities=always --config="$PATH_LINK" --service-environment="staging" daemon --tcp --receive-mode=manual& + sleep 5 + SIGNAL_CLI="$PWD/client/target/debug/signal-cli-client" else ./gradlew installDist SIGNAL_CLI="$PWD/build/install/signal-cli/bin/signal-cli" @@ -30,16 +37,28 @@ fi run() { set -x - "$SIGNAL_CLI" --service-environment="staging" $@ + if [ "$JSON_RPC" -eq 1 ]; then + "$SIGNAL_CLI" $@ + else + "$SIGNAL_CLI" --service-environment="staging" $@ + fi set +x } run_main() { - run --config="$PATH_MAIN" $@ + if [ "$JSON_RPC" -eq 1 ]; then + run --json-rpc-socket="$XDG_RUNTIME_DIR/signal-cli/socket" $@ + else + run --config="$PATH_MAIN" $@ + fi } run_linked() { - run --config="$PATH_LINK" $@ + if [ "$JSON_RPC" -eq 1 ]; then + run --json-rpc-tcp="127.0.0.1:7583" $@ + else + run --config="$PATH_LINK" $@ + fi } register() { @@ -84,6 +103,7 @@ sleep 5 run_main listAccounts run_main --output=json listAccounts +if [ "$JSON_RPC" -eq 0 ]; then ## DBus #run_main -a "$NUMBER_1" --dbus send "$NUMBER_2" -m daemon_not_running || true #run_main daemon & @@ -127,6 +147,7 @@ exec 3<> "$FIFO_FILE" exec 3>&- wait +fi run_main -a "$NUMBER_1" setPin "$TEST_PIN_1" run_main -a "$NUMBER_2" removePin @@ -143,7 +164,7 @@ run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi run_main -a "$NUMBER_1" receive run_main -a "$NUMBER_2" receive ## Groups -GROUP_ID=$(run_main -a "$NUMBER_1" updateGroup -n GRUPPE -a LICENSE -m "$NUMBER_1" | grep -oP '(?<=").+(?=")') +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 run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" -n GRUPPE_UMB -m "$NUMBER_2" --admin "$NUMBER_2" --description DESCRIPTION --link=enabled-with-approval --set-permission-add-member=only-admins --set-permission-edit-details=only-admins --set-permission-send-messages=only-admins -e 42 run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" --remove-admin "$NUMBER_2" --reset-link --set-permission-send-messages=every-member @@ -171,15 +192,15 @@ run_main -a "$NUMBER_2" trust "$NUMBER_1" -a ## Basic send/receive for OUTPUT in "plain-text" "json"; do run_main -a "$NUMBER_1" --output="$OUTPUT" getUserStatus "$NUMBER_1" "$NUMBER_2" "+111111111" - run_main -a "$NUMBER_1" send "$NUMBER_2" -m hi - run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi - run_main -a "$NUMBER_1" send -g "$GROUP_ID" -m hi -a LICENSE --mention "1:1:$NUMBER_2" - TIMESTAMP=$(uname -a | run_main -a "$NUMBER_1" send "$NUMBER_2") - run_main -a "$NUMBER_2" sendReaction "$NUMBER_1" -e 🍀 -a "$NUMBER_1" -t "$TIMESTAMP" - run_main -a "$NUMBER_1" remoteDelete "$NUMBER_2" -t "$TIMESTAMP" + run_main -a "$NUMBER_1" --output="$OUTPUT" send "$NUMBER_2" -m hi + run_main -a "$NUMBER_2" --output="$OUTPUT" send "$NUMBER_1" -m hi + run_main -a "$NUMBER_1" --output="$OUTPUT" send -g "$GROUP_ID" -m hi -a LICENSE --mention "1:1:$NUMBER_2" + TIMESTAMP=$(uname -a | run_main -a "$NUMBER_1" --output=json send "$NUMBER_2" | jq '.timestamp') + run_main -a "$NUMBER_2" --output="$OUTPUT" sendReaction "$NUMBER_1" -e 🍀 -a "$NUMBER_1" -t "$TIMESTAMP" + run_main -a "$NUMBER_1" --output="$OUTPUT" remoteDelete "$NUMBER_2" -t "$TIMESTAMP" run_main -a "$NUMBER_2" --output="$OUTPUT" receive run_main -a "$NUMBER_1" --output="$OUTPUT" receive - run_main -a "$NUMBER_1" send -e "$NUMBER_2" + run_main -a "$NUMBER_1" --output="$OUTPUT" send -e "$NUMBER_2" run_main -a "$NUMBER_2" --output="$OUTPUT" receive done @@ -194,8 +215,8 @@ run_linked -a "$NUMBER_1" sendSyncRequest run_main -a "$NUMBER_1" sendContacts for OUTPUT in "plain-text" "json"; do - run_main -a "$NUMBER_1" send "$NUMBER_2" -m hi - run_main -a "$NUMBER_2" send "$NUMBER_1" -m hi + run_main -a "$NUMBER_1" --output="$OUTPUT" send "$NUMBER_2" -m hi + run_main -a "$NUMBER_2" --output="$OUTPUT" send "$NUMBER_1" -m hi run_main -a "$NUMBER_2" --output="$OUTPUT" receive run_main -a "$NUMBER_1" --output="$OUTPUT" receive run_linked -a "$NUMBER_1" --output="$OUTPUT" receive From 69fe3986cf0810bf712d0e7cee970a6a549514d3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 20 Feb 2022 10:01:29 +0100 Subject: [PATCH 049/938] Handle OverlappingFileLockException when linking --- .../org/asamk/signal/manager/ProvisioningManagerImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java index e0f1084b..9142d2e9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java @@ -37,6 +37,7 @@ import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider import java.io.IOException; import java.net.URI; +import java.nio.channels.OverlappingFileLockException; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; @@ -194,6 +195,9 @@ class ProvisioningManagerImpl implements ProvisioningManager { } catch (IOException e) { logger.debug("Account in use or failed to load.", e); return false; + } catch (OverlappingFileLockException e) { + logger.debug("Account in use.", e); + return false; } try (signalAccount) { From 1ffb6d69075ea4b02047d65b2b9637da70e7cfcc Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 20 Feb 2022 10:02:15 +0100 Subject: [PATCH 050/938] Handle UnsupportedOperationException in daemon command --- .../java/org/asamk/signal/commands/DaemonCommand.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 7c972496..4e43aa99 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -10,6 +10,7 @@ import org.asamk.signal.ReceiveMessageHandler; 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.dbus.DbusSignalControlImpl; import org.asamk.signal.dbus.DbusSignalImpl; import org.asamk.signal.json.JsonReceiveMessageHandler; @@ -282,7 +283,7 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { private void runDbusSingleAccount( final Manager m, final boolean isDbusSystem, final boolean noReceiveOnStart - ) throws UnexpectedErrorException { + ) throws CommandException { runDbus(isDbusSystem, (conn, objectPath) -> { try { exportDbusObject(conn, objectPath, m, noReceiveOnStart).join(); @@ -293,7 +294,7 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { private void runDbusMultiAccount( final MultiAccountManager c, final boolean noReceiveOnStart, final boolean isDbusSystem - ) throws UnexpectedErrorException { + ) throws CommandException { runDbus(isDbusSystem, (connection, objectPath) -> { final var signalControl = new DbusSignalControlImpl(c, objectPath); connection.exportObject(signalControl); @@ -332,7 +333,7 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { private void runDbus( final boolean isDbusSystem, DbusRunner dbusRunner - ) throws UnexpectedErrorException { + ) throws CommandException { DBusConnection.DBusBusType busType; if (isDbusSystem) { busType = DBusConnection.DBusBusType.SYSTEM; @@ -345,6 +346,8 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand { dbusRunner.run(conn, DbusConfig.getObjectPath()); } catch (DBusException e) { throw new UnexpectedErrorException("Dbus command failed: " + e.getMessage(), e); + } catch (UnsupportedOperationException e) { + throw new UserErrorException("Failed to connect to Dbus: " + e.getMessage(), e); } try { From 2c44b65e9fcbc91652a0c8de614698545f5c2722 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 20 Feb 2022 10:03:19 +0100 Subject: [PATCH 051/938] Prevent preKeyIds from starting at zero --- .../asamk/signal/manager/storage/SignalAccount.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 87be95b4..bbd9944c 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 @@ -103,8 +103,8 @@ public class SignalAccount implements Closeable { private StorageKey storageKey; private long storageManifestVersion = -1; private ProfileKey profileKey; - private int preKeyIdOffset; - private int nextSignedPreKeyId; + private int preKeyIdOffset = 1; + private int nextSignedPreKeyId = 1; private IdentityKeyPair identityKeyPair; private int localRegistrationId; private TrustNewIdentity trustNewIdentity; @@ -479,14 +479,14 @@ public class SignalAccount implements Closeable { storageManifestVersion = rootNode.get("storageManifestVersion").asLong(); } if (rootNode.hasNonNull("preKeyIdOffset")) { - preKeyIdOffset = rootNode.get("preKeyIdOffset").asInt(0); + preKeyIdOffset = rootNode.get("preKeyIdOffset").asInt(1); } else { - preKeyIdOffset = 0; + preKeyIdOffset = 1; } if (rootNode.hasNonNull("nextSignedPreKeyId")) { - nextSignedPreKeyId = rootNode.get("nextSignedPreKeyId").asInt(); + nextSignedPreKeyId = rootNode.get("nextSignedPreKeyId").asInt(1); } else { - nextSignedPreKeyId = 0; + nextSignedPreKeyId = 1; } if (rootNode.hasNonNull("profileKey")) { try { From f3b2df62dae51496c8c5c9c85ac5a3951ea27978 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 20 Feb 2022 12:22:37 +0100 Subject: [PATCH 052/938] Update libsignal-service-java --- graalvm-config-dir/jni-config.json | 11 ++ graalvm-config-dir/reflect-config.json | 25 ++-- lib/build.gradle.kts | 2 +- .../org/asamk/signal/manager/ManagerImpl.java | 4 +- .../manager/ProvisioningManagerImpl.java | 9 +- .../manager/RegistrationManagerImpl.java | 6 +- .../signal/manager/SignalAccountFiles.java | 6 +- .../signal/manager/SignalDependencies.java | 1 + .../actions/RetrieveStorageDataAction.java | 6 +- .../actions/SendPniIdentityKeyAction.java | 20 +++ .../signal/manager/helper/AccountHelper.java | 24 +-- .../signal/manager/helper/GroupV2Helper.java | 23 +-- .../signal/manager/helper/IdentityHelper.java | 2 +- .../helper/IncomingMessageHandler.java | 10 ++ .../signal/manager/helper/PreKeyHelper.java | 24 ++- .../signal/manager/helper/SyncHelper.java | 66 ++++----- .../signal/manager/storage/SignalAccount.java | 139 ++++++++++++++---- .../storage/recipients/RecipientAddress.java | 2 +- .../storage/recipients/RecipientResolver.java | 4 +- .../storage/recipients/RecipientStore.java | 5 +- .../org/asamk/signal/manager/util/Utils.java | 4 +- 21 files changed, 273 insertions(+), 120 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/actions/SendPniIdentityKeyAction.java diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index 18ba01fc..3ac74947 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -12,6 +12,10 @@ "name":"java.lang.Boolean", "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] }, +{ + "name":"java.lang.Class", + "methods":[{"name":"getCanonicalName","parameterTypes":[] }] +}, { "name":"java.lang.ClassLoader", "methods":[ @@ -30,6 +34,10 @@ { "name":"java.lang.NoSuchMethodError" }, +{ + "name":"java.lang.Throwable", + "methods":[{"name":"getMessage","parameterTypes":[] }] +}, { "name":"java.lang.UnsatisfiedLinkError", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] @@ -115,6 +123,9 @@ "name":"org.whispersystems.libsignal.InvalidKeyException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, +{ + "name":"org.whispersystems.libsignal.InvalidKeyIdException" +}, { "name":"org.whispersystems.libsignal.InvalidMessageException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 859fa0ea..1857e1b3 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2023,11 +2023,6 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, -{ - "name":"org.whispersystems.signalservice.api.push.AccountIdentifier", - "allDeclaredFields":true, - "queryAllDeclaredMethods":true -}, { "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity", "allDeclaredFields":true, @@ -2314,16 +2309,19 @@ { "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisionMessage", "fields":[ + {"name":"aciIdentityKeyPrivate_"}, + {"name":"aciIdentityKeyPublic_"}, + {"name":"aci_"}, {"name":"bitField0_"}, - {"name":"identityKeyPrivate_"}, - {"name":"identityKeyPublic_"}, {"name":"number_"}, + {"name":"pniIdentityKeyPrivate_"}, + {"name":"pniIdentityKeyPublic_"}, + {"name":"pni_"}, {"name":"profileKey_"}, {"name":"provisioningCode_"}, {"name":"provisioningVersion_"}, {"name":"readReceipts_"}, - {"name":"userAgent_"}, - {"name":"uuid_"} + {"name":"userAgent_"} ] }, { @@ -2686,6 +2684,7 @@ "fields":[ {"name":"bitField0_"}, {"name":"content_"}, + {"name":"destinationUuid_"}, {"name":"legacyMessage_"}, {"name":"relay_"}, {"name":"serverGuid_"}, @@ -2852,6 +2851,14 @@ {"name":"type_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$PniIdentity", + "fields":[ + {"name":"bitField0_"}, + {"name":"privateKey_"}, + {"name":"publicKey_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Read", "fields":[ diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 5c3d8443..c59e7f3e 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_41") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_42") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.1") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 941a6122..380b00ee 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -278,7 +278,7 @@ class ManagerImpl implements Manager { public List getLinkedDevices() throws IOException { var devices = dependencies.getAccountManager().getDevices(); account.setMultiDevice(devices.size() > 1); - var identityKey = account.getIdentityKeyPair().getPrivateKey(); + var identityKey = account.getAciIdentityKeyPair().getPrivateKey(); return devices.stream().map(d -> { String deviceName = d.getName(); if (deviceName != null) { @@ -568,7 +568,7 @@ class ManagerImpl implements Manager { final var recipientId = context.getRecipientHelper().resolveRecipient(m.recipient()); mentions.add(new SignalServiceDataMessage.Mention(context.getRecipientHelper() .resolveSignalServiceAddress(recipientId) - .getAci(), m.start(), m.length())); + .getServiceId(), m.start(), m.length())); } return mentions; } diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java index 9142d2e9..75163555 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java @@ -79,7 +79,7 @@ class ProvisioningManagerImpl implements ProvisioningManager { groupsV2Operations = null; } accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), - new DynamicCredentialsProvider(null, null, password, SignalServiceAddress.DEFAULT_DEVICE_ID), + new DynamicCredentialsProvider(null, null, null, password, SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, ServiceConfig.AUTOMATIC_NETWORK_RETRY); @@ -97,6 +97,7 @@ class ProvisioningManagerImpl implements ProvisioningManager { var ret = accountManager.getNewDeviceRegistration(tempIdentityKey); var number = ret.getNumber(); var aci = ret.getAci(); + var pni = ret.getPni(); logger.info("Received link information from {}, linking in progress ...", number); @@ -117,7 +118,7 @@ class ProvisioningManagerImpl implements ProvisioningManager { var encryptedDeviceName = deviceName == null ? null - : DeviceNameUtil.encryptDeviceName(deviceName, ret.getIdentity().getPrivateKey()); + : DeviceNameUtil.encryptDeviceName(deviceName, ret.getAciIdentity().getPrivateKey()); logger.debug("Finishing new device registration"); var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(), @@ -135,10 +136,12 @@ class ProvisioningManagerImpl implements ProvisioningManager { accountPath, number, aci, + pni, password, encryptedDeviceName, deviceId, - ret.getIdentity(), + ret.getAciIdentity(), + ret.getPniIdentity(), registrationId, profileKey, TrustNewIdentity.ON_FIRST_USE); diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index 599c55a4..bc543d8d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -31,6 +31,7 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.PNI; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.internal.ServiceResponse; import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; @@ -79,7 +80,7 @@ class RegistrationManagerImpl implements RegistrationManager { this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), new DynamicCredentialsProvider( // Using empty UUID, because registering doesn't work otherwise - null, account.getNumber(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID), + null, null, account.getNumber(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, ServiceConfig.AUTOMATIC_NETWORK_RETRY); @@ -116,7 +117,8 @@ class RegistrationManagerImpl implements RegistrationManager { //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); final var aci = ACI.parseOrNull(response.getUuid()); - account.finishRegistration(aci, masterKey, pin); + final var pni = PNI.parseOrNull(response.getPni()); + account.finishRegistration(aci, pni, masterKey, pin); accountFileUpdater.updateAccountIdentifiers(account.getNumber(), aci); ManagerImpl m = null; 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 b3ed439d..2785ec41 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -125,14 +125,16 @@ public class SignalAccountFiles { final var accountPath = accountsStore.getPathByNumber(number); if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) { final var newAccountPath = accountPath == null ? accountsStore.addAccount(number, null) : accountPath; - var identityKey = KeyUtils.generateIdentityKeyPair(); + var aciIdentityKey = KeyUtils.generateIdentityKeyPair(); + var pniIdentityKey = KeyUtils.generateIdentityKeyPair(); var registrationId = KeyHelper.generateRegistrationId(false); var profileKey = KeyUtils.createProfileKey(); var account = SignalAccount.create(pathConfig.dataPath(), newAccountPath, number, - identityKey, + aciIdentityKey, + pniIdentityKey, registrationId, profileKey, trustNewIdentity); diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java index 450abaac..244a110f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java @@ -88,6 +88,7 @@ public class SignalDependencies { public SignalServiceAccountManager createUnauthenticatedAccountManager(String number, String password) { return new SignalServiceAccountManager(getServiceEnvironmentConfig().getSignalServiceConfiguration(), + null, null, number, SignalServiceAddress.DEFAULT_DEVICE_ID, diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java index bdfd3188..b44c7e3a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java +++ b/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java @@ -17,10 +17,8 @@ public class RetrieveStorageDataAction implements HandleAction { public void execute(Context context) throws Throwable { if (context.getAccount().getStorageKey() != null) { context.getStorageHelper().readDataFromStorage(); - } else { - if (!context.getAccount().isMasterDevice()) { - context.getSyncHelper().requestAllSyncData(); - } + } else if (!context.getAccount().isMasterDevice()) { + context.getSyncHelper().requestSyncKeys(); } } } diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/SendPniIdentityKeyAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/SendPniIdentityKeyAction.java new file mode 100644 index 00000000..fe70a661 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/actions/SendPniIdentityKeyAction.java @@ -0,0 +1,20 @@ +package org.asamk.signal.manager.actions; + +import org.asamk.signal.manager.helper.Context; + +public class SendPniIdentityKeyAction implements HandleAction { + + private static final SendPniIdentityKeyAction INSTANCE = new SendPniIdentityKeyAction(); + + private SendPniIdentityKeyAction() { + } + + public static SendPniIdentityKeyAction create() { + return INSTANCE; + } + + @Override + public void execute(Context context) throws Throwable { + context.getSyncHelper().sendPniIdentity(); + } +} 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 90a26217..fc1bcfc4 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 @@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.PNI; import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; import org.whispersystems.signalservice.api.util.DeviceNameUtil; @@ -55,9 +56,12 @@ public class AccountHelper { } try { context.getPreKeyHelper().refreshPreKeysIfNecessary(); - if (account.getAci() == null) { + if (account.getAci() == null || account.getPni() == null) { checkWhoAmiI(); } + if (!account.isMasterDevice() && account.getPniIdentityKeyPair() == null) { + context.getSyncHelper().requestSyncPniIdentity(); + } updateAccountAttributes(); } catch (AuthorizationFailedException e) { account.setRegistered(false); @@ -69,16 +73,18 @@ public class AccountHelper { final var whoAmI = dependencies.getAccountManager().getWhoAmI(); final var number = whoAmI.getNumber(); final var aci = ACI.parseOrNull(whoAmI.getAci()); - if (number.equals(account.getNumber()) && aci.equals(account.getAci())) { + final var pni = PNI.parseOrNull(whoAmI.getPni()); + if (number.equals(account.getNumber()) && aci.equals(account.getAci()) && pni.equals(account.getPni())) { return; } - updateSelfIdentifiers(number, aci); + updateSelfIdentifiers(number, aci, pni); } - private void updateSelfIdentifiers(final String number, final ACI aci) { + private void updateSelfIdentifiers(final String number, final ACI aci, final PNI pni) { account.setNumber(number); account.setAci(aci); + account.setPni(pni); account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress()); // TODO check and update remote storage context.getUnidentifiedAccessHelper().rotateSenderCertificates(); @@ -103,11 +109,11 @@ public class AccountHelper { (verificationCode1, registrationLock) -> dependencies.getAccountManager() .changeNumber(verificationCode1, newNumber, registrationLock)); // TODO handle response - updateSelfIdentifiers(newNumber, account.getAci()); + updateSelfIdentifiers(newNumber, account.getAci(), PNI.parseOrThrow(result.first().getPni())); } public void setDeviceName(String deviceName) { - final var privateKey = account.getIdentityKeyPair().getPrivateKey(); + final var privateKey = account.getAciIdentityKeyPair().getPrivateKey(); final var encryptedDeviceName = DeviceNameUtil.encryptDeviceName(deviceName, privateKey); account.setEncryptedDeviceName(encryptedDeviceName); } @@ -127,15 +133,15 @@ public class AccountHelper { } public void addDevice(DeviceLinkInfo deviceLinkInfo) throws IOException, InvalidDeviceLinkException { - var identityKeyPair = account.getIdentityKeyPair(); var verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode(); try { dependencies.getAccountManager() .addDevice(deviceLinkInfo.deviceIdentifier(), deviceLinkInfo.deviceKey(), - identityKeyPair, - Optional.of(account.getProfileKey().serialize()), + account.getAciIdentityKeyPair(), + account.getPniIdentityKeyPair(), + account.getProfileKey(), verificationCode); } catch (InvalidKeyException e) { throw new InvalidDeviceLinkException("Invalid device link", e); 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 1291b143..a40c6223 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 @@ -38,6 +38,7 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException; import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException; import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; @@ -150,7 +151,7 @@ class GroupV2Helper { final var memberList = new ArrayList<>(members); final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream(); final var uuids = memberList.stream() - .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getAci().uuid()); + .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()); var candidates = Utils.zip(uuids, credentials, (uuid, credential) -> new GroupCandidate(uuid, Optional.fromNullable(credential))) @@ -218,7 +219,7 @@ class GroupV2Helper { final var memberList = new ArrayList<>(newMembers); final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream(); final var uuids = memberList.stream() - .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getAci().uuid()); + .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()); var candidates = Utils.zip(uuids, credentials, (uuid, credential) -> new GroupCandidate(uuid, Optional.fromNullable(credential))) @@ -245,8 +246,8 @@ class GroupV2Helper { final var adminUuids = membersToMakeAdmin.stream() .map(context.getRecipientHelper()::resolveSignalServiceAddress) - .map(SignalServiceAddress::getAci) - .map(ACI::uuid) + .map(SignalServiceAddress::getServiceId) + .map(ServiceId::uuid) .toList(); final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); return commitChange(groupInfoV2, @@ -258,8 +259,8 @@ class GroupV2Helper { ) throws IOException { final var memberUuids = members.stream() .map(context.getRecipientHelper()::resolveSignalServiceAddress) - .map(SignalServiceAddress::getAci) - .map(ACI::uuid) + .map(SignalServiceAddress::getServiceId) + .map(ServiceId::uuid) .collect(Collectors.toSet()); return ejectMembers(groupInfoV2, memberUuids); } @@ -270,8 +271,8 @@ class GroupV2Helper { var pendingMembersList = groupInfoV2.getGroup().getPendingMembersList(); final var memberUuids = members.stream() .map(context.getRecipientHelper()::resolveSignalServiceAddress) - .map(SignalServiceAddress::getAci) - .map(ACI::uuid) + .map(SignalServiceAddress::getServiceId) + .map(ServiceId::uuid) .map(uuid -> DecryptedGroupUtil.findPendingByUuid(pendingMembersList, uuid)) .filter(Optional::isPresent) .map(Optional::get) @@ -343,7 +344,7 @@ class GroupV2Helper { change.setSourceUuid(context.getRecipientHelper() .resolveSignalServiceAddress(selfRecipientId) - .getAci() + .getServiceId() .toByteString()); return commitChange(groupSecretParams, decryptedGroupJoinInfo.getRevision(), change, groupLinkPassword); @@ -360,7 +361,7 @@ class GroupV2Helper { final var change = groupOperations.createAcceptInviteChange(profileKeyCredential); - final var aci = context.getRecipientHelper().resolveSignalServiceAddress(selfRecipientId).getAci(); + final var aci = context.getRecipientHelper().resolveSignalServiceAddress(selfRecipientId).getServiceId(); change.setSourceUuid(aci.toByteString()); return commitChange(groupInfoV2, change); @@ -372,7 +373,7 @@ class GroupV2Helper { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId); final var newRole = admin ? Member.Role.ADMINISTRATOR : Member.Role.DEFAULT; - final var change = groupOperations.createChangeMemberRole(address.getAci().uuid(), newRole); + final var change = groupOperations.createChangeMemberRole(address.getServiceId().uuid(), newRole); return commitChange(groupInfoV2, change); } 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 149171e6..283e756f 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 @@ -77,7 +77,7 @@ public class IdentityHelper { ) { return Utils.computeSafetyNumber(capabilities.isUuid(), account.getSelfAddress(), - account.getIdentityKeyPair().getPublicKey(), + account.getAciIdentityKeyPair().getPublicKey(), theirAddress, theirIdentityKey); } 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 0b9cc80d..2400128c 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 @@ -10,6 +10,7 @@ import org.asamk.signal.manager.actions.RetrieveProfileAction; import org.asamk.signal.manager.actions.RetrieveStorageDataAction; import org.asamk.signal.manager.actions.SendGroupInfoAction; import org.asamk.signal.manager.actions.SendGroupInfoRequestAction; +import org.asamk.signal.manager.actions.SendPniIdentityKeyAction; import org.asamk.signal.manager.actions.SendReceiptAction; import org.asamk.signal.manager.actions.SendRetryMessageRequestAction; import org.asamk.signal.manager.actions.SendSyncBlockedListAction; @@ -31,6 +32,7 @@ import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.stickers.Sticker; +import org.asamk.signal.manager.util.KeyUtils; import org.signal.libsignal.metadata.ProtocolInvalidKeyException; import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException; import org.signal.libsignal.metadata.ProtocolInvalidMessageException; @@ -340,6 +342,9 @@ public final class IncomingMessageHandler { if (rm.isConfigurationRequest()) { actions.add(SendSyncConfigurationAction.create()); } + if (rm.isPniIdentityRequest()) { + actions.add(SendPniIdentityKeyAction.create()); + } } if (syncMessage.getGroups().isPresent()) { logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring."); @@ -440,6 +445,11 @@ public final class IncomingMessageHandler { .get()); } } + if (syncMessage.getPniIdentity().isPresent()) { + final var pniIdentity = syncMessage.getPniIdentity().get(); + account.setPniIdentityKeyPair(KeyUtils.getIdentityKeyPair(pniIdentity.getPublicKey().toByteArray(), + pniIdentity.getPrivateKey().toByteArray())); + } return actions; } 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 f56a7055..9a4d140e 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 @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; +import org.whispersystems.signalservice.api.push.ServiceIdType; import java.io.IOException; import java.util.List; @@ -28,17 +29,32 @@ public class PreKeyHelper { } public void refreshPreKeysIfNecessary() throws IOException { - if (dependencies.getAccountManager().getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) { - refreshPreKeys(); + refreshPreKeysIfNecessary(ServiceIdType.ACI); + refreshPreKeysIfNecessary(ServiceIdType.PNI); + } + + public void refreshPreKeysIfNecessary(ServiceIdType serviceIdType) throws IOException { + if (dependencies.getAccountManager().getPreKeysCount(serviceIdType) < ServiceConfig.PREKEY_MINIMUM_COUNT) { + refreshPreKeys(serviceIdType); } } public void refreshPreKeys() throws IOException { + refreshPreKeys(ServiceIdType.ACI); + refreshPreKeys(ServiceIdType.PNI); + } + + public void refreshPreKeys(ServiceIdType serviceIdType) throws IOException { + if (serviceIdType != ServiceIdType.ACI) { + // TODO implement + return; + } var oneTimePreKeys = generatePreKeys(); - final var identityKeyPair = account.getIdentityKeyPair(); + final var identityKeyPair = account.getAciIdentityKeyPair(); var signedPreKeyRecord = generateSignedPreKey(identityKeyPair); - dependencies.getAccountManager().setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys); + dependencies.getAccountManager() + .setPreKeys(serviceIdType, identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys); } private List generatePreKeys() { 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 0833add7..9d987984 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 @@ -1,5 +1,7 @@ package org.asamk.signal.manager.helper; +import com.google.protobuf.ByteString; + import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfoV1; @@ -49,11 +51,20 @@ public class SyncHelper { } public void requestAllSyncData() { - requestSyncGroups(); - requestSyncContacts(); - requestSyncBlocked(); - requestSyncConfiguration(); + requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.GROUPS); + requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS); + requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED); + requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION); requestSyncKeys(); + requestSyncPniIdentity(); + } + + public void requestSyncKeys() { + requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.KEYS); + } + + public void requestSyncPniIdentity() { + requestSyncData(SignalServiceProtos.SyncMessage.Request.Type.PNI_IDENTITY); } public void sendSyncFetchProfileMessage() { @@ -217,6 +228,15 @@ public class SyncHelper { context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forConfiguration(configurationMessage)); } + public void sendPniIdentity() { + final var pniIdentityKeyPair = account.getPniIdentityKeyPair(); + var pniIdentity = SignalServiceProtos.SyncMessage.PniIdentity.newBuilder() + .setPrivateKey(ByteString.copyFrom(pniIdentityKeyPair.getPrivateKey().serialize())) + .setPublicKey(ByteString.copyFrom(pniIdentityKeyPair.getPublicKey().serialize())) + .build(); + context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forPniIdentity(pniIdentity)); + } + public void handleSyncDeviceContacts(final InputStream input) throws IOException { final var s = new DeviceContactsInputStream(input); DeviceContact c; @@ -270,42 +290,8 @@ public class SyncHelper { } } - private void requestSyncGroups() { - var r = SignalServiceProtos.SyncMessage.Request.newBuilder() - .setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS) - .build(); - var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); - context.getSendHelper().sendSyncMessage(message); - } - - private void requestSyncContacts() { - var r = SignalServiceProtos.SyncMessage.Request.newBuilder() - .setType(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS) - .build(); - var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); - context.getSendHelper().sendSyncMessage(message); - } - - private void requestSyncBlocked() { - var r = SignalServiceProtos.SyncMessage.Request.newBuilder() - .setType(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED) - .build(); - var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); - context.getSendHelper().sendSyncMessage(message); - } - - private void requestSyncConfiguration() { - var r = SignalServiceProtos.SyncMessage.Request.newBuilder() - .setType(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION) - .build(); - var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); - context.getSendHelper().sendSyncMessage(message); - } - - private void requestSyncKeys() { - var r = SignalServiceProtos.SyncMessage.Request.newBuilder() - .setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS) - .build(); + private void requestSyncData(final SignalServiceProtos.SyncMessage.Request.Type type) { + var r = SignalServiceProtos.SyncMessage.Request.newBuilder().setType(type).build(); var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); context.getSendHelper().sendSyncMessage(message); } 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 bbd9944c..d2723e38 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 @@ -49,8 +49,9 @@ import org.whispersystems.signalservice.api.SignalServiceDataStore; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.push.ACI; -import org.whispersystems.signalservice.api.push.AccountIdentifier; import org.whispersystems.signalservice.api.push.DistributionId; +import org.whispersystems.signalservice.api.push.PNI; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.storage.StorageKey; import org.whispersystems.signalservice.api.util.CredentialsProvider; @@ -94,6 +95,7 @@ public class SignalAccount implements Closeable { private String accountPath; private String number; private ACI aci; + private PNI pni; private String encryptedDeviceName; private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; private boolean isMultiDevice = false; @@ -105,7 +107,8 @@ public class SignalAccount implements Closeable { private ProfileKey profileKey; private int preKeyIdOffset = 1; private int nextSignedPreKeyId = 1; - private IdentityKeyPair identityKeyPair; + private IdentityKeyPair aciIdentityKeyPair; + private IdentityKeyPair pniIdentityKeyPair; private int localRegistrationId; private TrustNewIdentity trustNewIdentity; private long lastReceiveTimestamp = 0; @@ -161,7 +164,8 @@ public class SignalAccount implements Closeable { File dataPath, String accountPath, String number, - IdentityKeyPair identityKey, + IdentityKeyPair aciIdentityKey, + IdentityKeyPair pniIdentityKey, int registrationId, ProfileKey profileKey, final TrustNewIdentity trustNewIdentity @@ -180,7 +184,8 @@ public class SignalAccount implements Closeable { signalAccount.profileKey = profileKey; signalAccount.dataPath = dataPath; - signalAccount.identityKeyPair = identityKey; + signalAccount.aciIdentityKeyPair = aciIdentityKey; + signalAccount.pniIdentityKeyPair = pniIdentityKey; signalAccount.localRegistrationId = registrationId; signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), @@ -203,10 +208,12 @@ public class SignalAccount implements Closeable { String accountPath, String number, ACI aci, + PNI pni, String password, String encryptedDeviceName, int deviceId, - IdentityKeyPair identityKey, + IdentityKeyPair aciIdentityKey, + IdentityKeyPair pniIdentityKey, int registrationId, ProfileKey profileKey, final TrustNewIdentity trustNewIdentity @@ -218,17 +225,27 @@ public class SignalAccount implements Closeable { accountPath, number, aci, + pni, password, encryptedDeviceName, deviceId, - identityKey, + aciIdentityKey, + pniIdentityKey, registrationId, profileKey, trustNewIdentity); } final var signalAccount = load(dataPath, accountPath, true, trustNewIdentity); - signalAccount.setProvisioningData(number, aci, password, encryptedDeviceName, deviceId, profileKey); + signalAccount.setProvisioningData(number, + aci, + pni, + password, + encryptedDeviceName, + deviceId, + aciIdentityKey, + pniIdentityKey, + profileKey); signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); signalAccount.getSessionStore().archiveAllSessions(); signalAccount.getSenderKeyStore().deleteAll(); @@ -253,10 +270,12 @@ public class SignalAccount implements Closeable { String accountPath, String number, ACI aci, + PNI pni, String password, String encryptedDeviceName, int deviceId, - IdentityKeyPair identityKey, + IdentityKeyPair aciIdentityKey, + IdentityKeyPair pniIdentityKey, int registrationId, ProfileKey profileKey, final TrustNewIdentity trustNewIdentity @@ -267,11 +286,18 @@ public class SignalAccount implements Closeable { final var pair = openFileChannel(fileName, true); var signalAccount = new SignalAccount(pair.first(), pair.second()); - signalAccount.setProvisioningData(number, aci, password, encryptedDeviceName, deviceId, profileKey); + signalAccount.setProvisioningData(number, + aci, + pni, + password, + encryptedDeviceName, + deviceId, + aciIdentityKey, + pniIdentityKey, + profileKey); signalAccount.dataPath = dataPath; signalAccount.accountPath = accountPath; - signalAccount.identityKeyPair = identityKey; signalAccount.localRegistrationId = registrationId; signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), @@ -283,6 +309,7 @@ public class SignalAccount implements Closeable { signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION; signalAccount.migrateLegacyConfigs(); + signalAccount.clearAllPreKeys(); signalAccount.save(); return signalAccount; @@ -291,17 +318,23 @@ public class SignalAccount implements Closeable { private void setProvisioningData( final String number, final ACI aci, + final PNI pni, final String password, final String encryptedDeviceName, final int deviceId, + final IdentityKeyPair aciIdentity, + final IdentityKeyPair pniIdentity, final ProfileKey profileKey ) { this.number = number; this.aci = aci; + this.pni = pni; this.password = password; this.profileKey = profileKey; this.encryptedDeviceName = encryptedDeviceName; this.deviceId = deviceId; + this.aciIdentityKeyPair = aciIdentity; + this.pniIdentityKeyPair = pniIdentity; this.registered = true; this.isMultiDevice = true; this.lastReceiveTimestamp = 0; @@ -330,6 +363,9 @@ public class SignalAccount implements Closeable { } save(); } + if (isMasterDevice() && getPniIdentityKeyPair() == null) { + setPniIdentityKeyPair(KeyUtils.generateIdentityKeyPair()); + } } private void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) { @@ -440,7 +476,14 @@ public class SignalAccount implements Closeable { try { aci = ACI.parseOrThrow(rootNode.get("uuid").asText()); } catch (IllegalArgumentException e) { - throw new IOException("Config file contains an invalid uuid, needs to be a valid UUID", e); + throw new IOException("Config file contains an invalid aci/uuid, needs to be a valid UUID", e); + } + } + if (rootNode.hasNonNull("pni")) { + try { + pni = PNI.parseOrThrow(rootNode.get("pni").asText()); + } catch (IllegalArgumentException e) { + throw new IOException("Config file contains an invalid pni, needs to be a valid UUID", e); } } if (rootNode.hasNonNull("deviceName")) { @@ -459,11 +502,16 @@ public class SignalAccount implements Closeable { if (rootNode.hasNonNull("registrationId")) { registrationId = rootNode.get("registrationId").asInt(); } - IdentityKeyPair identityKeyPair = null; + IdentityKeyPair aciIdentityKeyPair = null; if (rootNode.hasNonNull("identityPrivateKey") && rootNode.hasNonNull("identityKey")) { final var publicKeyBytes = Base64.getDecoder().decode(rootNode.get("identityKey").asText()); final var privateKeyBytes = Base64.getDecoder().decode(rootNode.get("identityPrivateKey").asText()); - identityKeyPair = KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes); + aciIdentityKeyPair = KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes); + } + if (rootNode.hasNonNull("pniIdentityPrivateKey") && rootNode.hasNonNull("pniIdentityKey")) { + final var publicKeyBytes = Base64.getDecoder().decode(rootNode.get("pniIdentityKey").asText()); + final var privateKeyBytes = Base64.getDecoder().decode(rootNode.get("pniIdentityPrivateKey").asText()); + pniIdentityKeyPair = KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes); } if (rootNode.hasNonNull("registrationLockPin")) { @@ -504,12 +552,12 @@ public class SignalAccount implements Closeable { LegacyJsonSignalProtocolStore.class) : null; if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) { - identityKeyPair = legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentityKeyPair(); + aciIdentityKeyPair = legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentityKeyPair(); registrationId = legacySignalProtocolStore.getLegacyIdentityKeyStore().getLocalRegistrationId(); migratedLegacyConfig = true; } - this.identityKeyPair = identityKeyPair; + this.aciIdentityKeyPair = aciIdentityKeyPair; this.localRegistrationId = registrationId; this.trustNewIdentity = trustNewIdentity; @@ -741,6 +789,7 @@ public class SignalAccount implements Closeable { rootNode.put("version", CURRENT_STORAGE_VERSION) .put("username", number) .put("uuid", aci == null ? null : aci.toString()) + .put("pni", pni == null ? null : pni.toString()) .put("deviceName", encryptedDeviceName) .put("deviceId", deviceId) .put("isMultiDevice", isMultiDevice) @@ -748,8 +797,18 @@ public class SignalAccount implements Closeable { .put("password", password) .put("registrationId", localRegistrationId) .put("identityPrivateKey", - Base64.getEncoder().encodeToString(identityKeyPair.getPrivateKey().serialize())) - .put("identityKey", Base64.getEncoder().encodeToString(identityKeyPair.getPublicKey().serialize())) + Base64.getEncoder().encodeToString(aciIdentityKeyPair.getPrivateKey().serialize())) + .put("identityKey", + Base64.getEncoder().encodeToString(aciIdentityKeyPair.getPublicKey().serialize())) + .put("pniIdentityPrivateKey", + pniIdentityKeyPair == null + ? null + : Base64.getEncoder() + .encodeToString(pniIdentityKeyPair.getPrivateKey().serialize())) + .put("pniIdentityKey", + pniIdentityKeyPair == null + ? null + : Base64.getEncoder().encodeToString(pniIdentityKeyPair.getPublicKey().serialize())) .put("registrationLockPin", registrationLockPin) .put("pinMasterKey", pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize())) @@ -820,8 +879,14 @@ public class SignalAccount implements Closeable { public SignalServiceDataStore getSignalServiceDataStore() { return new SignalServiceDataStore() { @Override - public SignalServiceAccountDataStore get(final AccountIdentifier accountIdentifier) { - return getSignalServiceAccountDataStore(); + public SignalServiceAccountDataStore get(final ServiceId accountIdentifier) { + if (accountIdentifier.equals(aci)) { + return getSignalServiceAccountDataStore(); + } else if (accountIdentifier.equals(pni)) { + throw new AssertionError("PNI not to be used yet!"); + } else { + throw new IllegalArgumentException("No matching store found for " + accountIdentifier); + } } @Override @@ -831,7 +896,7 @@ public class SignalAccount implements Closeable { @Override public SignalServiceAccountDataStore pni() { - return getSignalServiceAccountDataStore(); + throw new AssertionError("PNI not to be used yet!"); } @Override @@ -870,7 +935,7 @@ public class SignalAccount implements Closeable { return getOrCreate(() -> identityKeyStore, () -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath), getRecipientStore(), - identityKeyPair, + aciIdentityKeyPair, localRegistrationId, trustNewIdentity)); } @@ -937,6 +1002,11 @@ public class SignalAccount implements Closeable { return aci; } + @Override + public PNI getPni() { + return pni; + } + @Override public String getE164() { return number; @@ -972,6 +1042,15 @@ public class SignalAccount implements Closeable { save(); } + public PNI getPni() { + return pni; + } + + public void setPni(final PNI pni) { + this.pni = pni; + save(); + } + public SignalServiceAddress getSelfAddress() { return new SignalServiceAddress(aci, number); } @@ -1001,8 +1080,17 @@ public class SignalAccount implements Closeable { return deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID; } - public IdentityKeyPair getIdentityKeyPair() { - return identityKeyPair; + public IdentityKeyPair getAciIdentityKeyPair() { + return aciIdentityKeyPair; + } + + public IdentityKeyPair getPniIdentityKeyPair() { + return pniIdentityKeyPair; + } + + public void setPniIdentityKeyPair(final IdentityKeyPair identityKeyPair) { + pniIdentityKeyPair = identityKeyPair; + save(); } public int getLocalRegistrationId() { @@ -1118,7 +1206,7 @@ public class SignalAccount implements Closeable { return configurationStore.getPhoneNumberUnlisted() == null || !configurationStore.getPhoneNumberUnlisted(); } - public void finishRegistration(final ACI aci, final MasterKey masterKey, final String pin) { + public void finishRegistration(final ACI aci, final PNI pni, final MasterKey masterKey, final String pin) { this.pinMasterKey = masterKey; this.storageManifestVersion = -1; this.storageKey = null; @@ -1127,6 +1215,7 @@ public class SignalAccount implements Closeable { this.isMultiDevice = false; this.registered = true; this.aci = aci; + this.pni = pni; this.registrationLockPin = pin; this.lastReceiveTimestamp = 0; save(); @@ -1135,7 +1224,7 @@ public class SignalAccount implements Closeable { getSessionStore().archiveAllSessions(); getSenderKeyStore().deleteAll(); final var recipientId = getRecipientStore().resolveSelfRecipientTrusted(getSelfRecipientAddress()); - final var publicKey = getIdentityKeyPair().getPublicKey(); + final var publicKey = getAciIdentityKeyPair().getPublicKey(); getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date()); getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java index 2f762ff7..3bfdadc0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java @@ -28,7 +28,7 @@ public record RecipientAddress(Optional uuid, Optional number) { } public RecipientAddress(SignalServiceAddress address) { - this(Optional.of(address.getAci().uuid()), Optional.ofNullable(address.getNumber().orNull())); + this(Optional.of(address.getServiceId().uuid()), Optional.ofNullable(address.getNumber().orNull())); } public RecipientAddress(UUID uuid) { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java index 487001a0..8744eeb9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientResolver.java @@ -1,6 +1,6 @@ package org.asamk.signal.manager.storage.recipients; -import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; public interface RecipientResolver { @@ -11,7 +11,7 @@ public interface RecipientResolver { RecipientId resolveRecipient(SignalServiceAddress address); - RecipientId resolveRecipient(ACI aci); + RecipientId resolveRecipient(ServiceId aci); RecipientId resolveRecipient(long recipientId); } 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 f52b79b7..9b1108c0 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 @@ -13,6 +13,7 @@ import org.signal.zkgroup.profiles.ProfileKeyCredential; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; @@ -171,8 +172,8 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile } @Override - public RecipientId resolveRecipient(ACI aci) { - return resolveRecipient(new RecipientAddress(aci.uuid()), false, false); + public RecipientId resolveRecipient(ServiceId serviceId) { + return resolveRecipient(new RecipientAddress(serviceId.uuid()), false, false); } @Override 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 8cd114f8..535c157d 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 @@ -65,8 +65,8 @@ public class Utils { if (isUuidCapable) { // Version 2: UUID user version = 2; - ownId = ownAddress.getAci().toByteArray(); - theirId = theirAddress.getAci().toByteArray(); + ownId = ownAddress.getServiceId().toByteArray(); + theirId = theirAddress.getServiceId().toByteArray(); } else { // Version 1: E164 user version = 1; From ec945cd227935470817027b262fc1ddc648cc380 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 20 Feb 2022 14:18:20 +0100 Subject: [PATCH 053/938] Add --message-from-stdin flag for send command --- README.md | 5 ++-- man/signal-cli.1.adoc | 8 ++++-- .../asamk/signal/commands/SendCommand.java | 25 ++++++++++--------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3632d08d..e6f74c46 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,8 @@ System requirements: - at least Java Runtime Environment (JRE) 17 - native library: libsignal-client - The native libs are bundled for x86_64 Linux (with recent enough glibc, see #643), Windows and MacOS. For other systems/architectures + The native libs are bundled for x86_64 Linux (with recent enough glibc, see #643), Windows and MacOS. For other + systems/architectures see: [Provide native lib for libsignal](https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal) ### Install system-wide on Linux @@ -75,7 +76,7 @@ of all country codes.) * Pipe the message content from another process. - uname -a | signal-cli -a ACCOUNT send RECIPIENT + uname -a | signal-cli -a ACCOUNT send --message-from-stdin RECIPIENT * Receive messages diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 02ae4ca4..4754d4e5 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -212,7 +212,11 @@ Send the message to self without notification. Specify the recipient group ID in base64 encoding. *-m* MESSAGE, *--message* MESSAGE:: -Specify the message, if missing, standard input is used. +Specify the message. +Currently, signal-cli reads the message from stdin if `-m` is missing, but this will change in a future version and the explicit flag `--message-from-stdin` should be used instead. + +*--message-from-stdin*:: +Read the message from standard input. *-a* [ATTACHMENT [ATTACHMENT ...]], *--attachment* [ATTACHMENT [ATTACHMENT ...]]:: Add one or more files as attachment. @@ -578,7 +582,7 @@ Send a message to one or more recipients:: signal-cli -a ACCOUNT send -m "This is a message" [RECIPIENT [RECIPIENT ...]] [-a [ATTACHMENT [ATTACHMENT ...]]] Pipe the message content from another process:: -uname -a | signal-cli -a ACCOUNT send [RECIPIENT [RECIPIENT ...]] +uname -a | signal-cli -a ACCOUNT send --message-from-stdin [RECIPIENT [RECIPIENT ...]] Create a group:: signal-cli -a ACCOUNT updateGroup -n "Group name" -m [MEMBER [MEMBER ...]] diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index fda0c8e6..5e81047a 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -51,7 +51,11 @@ public class SendCommand implements JsonRpcLocalCommand { .help("Send the message to self without notification.") .action(Arguments.storeTrue()); - subparser.addArgument("-m", "--message").help("Specify the message, if missing standard input is used."); + var mut = subparser.addMutuallyExclusiveGroup(); + mut.addArgument("-m", "--message").help("Specify the message to be sent."); + mut.addArgument("--message-from-stdin") + .action(Arguments.storeTrue()) + .help("Read the message from standard input."); subparser.addArgument("-a", "--attachment").nargs("*").help("Add file as attachment"); subparser.addArgument("-e", "--end-session", "--endsession") .help("Clear session state and send end session message.") @@ -107,16 +111,13 @@ public class SendCommand implements JsonRpcLocalCommand { final var sticker = stickerString == null ? null : parseSticker(stickerString); var messageText = ns.getString("message"); - if (messageText == null) { - if (sticker != null) { - messageText = ""; - } else { - logger.debug("Reading message from stdin..."); - try { - messageText = IOUtils.readAll(System.in, Charset.defaultCharset()); - } catch (IOException e) { - throw new UserErrorException("Failed to read message from stdin: " + e.getMessage()); - } + final var readMessageFromStdin = ns.getBoolean("message-from-stdin") == Boolean.TRUE; + if (readMessageFromStdin || (messageText == null && sticker == null)) { + logger.debug("Reading message from stdin..."); + try { + messageText = IOUtils.readAll(System.in, Charset.defaultCharset()); + } catch (IOException e) { + throw new UserErrorException("Failed to read message from stdin: " + e.getMessage()); } } @@ -146,7 +147,7 @@ public class SendCommand implements JsonRpcLocalCommand { } try { - var results = m.sendMessage(new Message(messageText, + var results = m.sendMessage(new Message(messageText == null ? "" : messageText, attachments, mentions, Optional.ofNullable(quote), From 8b75504822c194695ba25c6004585b99ac138e8a Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 20 Feb 2022 16:57:49 +0100 Subject: [PATCH 054/938] Bump version --- CHANGELOG.md | 15 +++++++++++++++ build.gradle.kts | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c35f160a..5dc9f674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ ## [Unreleased] +## [0.10.4] - 2022-02-20 +### Added +- Implement support for change number as linked device +- Add `--message-from-stdin` flag for send command. The current behavior of + reading from stdin if the `-m` flag is not given, will be removed in a future + version. + +### Changed +- Align receive timeout behavior for dbus client with cli and JSON-RPC. + Timeout is reset by every incoming message +- Renamed `error` field in json receive response to `exception` + +### Fixed +- Prevent a stale jsonrpc connection from interfering with message receiving + ## [0.10.3] - 2022-02-01 ### Added - MessageSendLog to cache sent message for 24h. diff --git a/build.gradle.kts b/build.gradle.kts index 923eb843..a8a316e9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.10" } -version = "0.10.3" +version = "0.10.4" java { sourceCompatibility = JavaVersion.VERSION_17 From 6f8784a1c9c5bd3047a8e7088608df7b2f66a72d Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 24 Feb 2022 17:31:33 +0100 Subject: [PATCH 055/938] Update libsignal-service-java Fixes #897 --- lib/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index c59e7f3e..3590f960 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_42") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_43") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.1") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") From bf2a83755e33542a411d58701c5eb0eff3f1ebaf Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 2 Mar 2022 22:45:26 +0100 Subject: [PATCH 056/938] Mark --target-timestamp flag as required for sendReceipt command Fixes #901 --- src/main/java/org/asamk/signal/commands/SendReceiptCommand.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java b/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java index 6ba8a40c..1eec6c99 100644 --- a/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java @@ -28,6 +28,7 @@ public class SendReceiptCommand implements JsonRpcLocalCommand { subparser.addArgument("-t", "--target-timestamp") .type(long.class) .nargs("+") + .required(true) .help("Specify the timestamp of the messages for which a receipt should be sent."); subparser.addArgument("--type") .help("Specify the receipt type (default is read receipt).") From 90df362c35983399c086db25fa529dc739f7eb14 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 2 Mar 2022 22:58:22 +0100 Subject: [PATCH 057/938] Bump version --- CHANGELOG.md | 5 +++++ build.gradle.kts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dc9f674..b9989642 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +## [0.10.4.1] - 2022-03-02 +### Fixed +- Linking to current apps (which currently don't include a PNI identity yet) +- Show better error message when --target-timestamp is missing for sendReceipt + ## [0.10.4] - 2022-02-20 ### Added - Implement support for change number as linked device diff --git a/build.gradle.kts b/build.gradle.kts index a8a316e9..36313561 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.10" } -version = "0.10.4" +version = "0.10.4.1" java { sourceCompatibility = JavaVersion.VERSION_17 From 3c2fa65e05ee6e57e88edb947385e0858f205949 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 16 Mar 2022 19:37:58 +0100 Subject: [PATCH 058/938] Fix NoSuchElementException in json serialization for messages from an untrusted identity Fixes #910 --- src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java index 264b7fce..18207747 100644 --- a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java +++ b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java @@ -3,9 +3,9 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonInclude; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.RecipientIdentifier; +import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import java.util.UUID; @@ -48,8 +48,7 @@ public record JsonMessageEnvelope( source = sourceAddress.getLegacyIdentifier(); sourceNumber = sourceAddress.number().orElse(null); sourceUuid = sourceAddress.uuid().map(UUID::toString).orElse(null); - sourceName = m.getContactOrProfileName(RecipientIdentifier.Single.fromAddress(envelope.sourceAddress() - .get())); + sourceName = m.getContactOrProfileName(RecipientIdentifier.Single.fromAddress(sourceAddress)); } else { source = null; sourceNumber = null; From 942999b7b4beebf4519eed9b216587519b47e6c6 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 16 Mar 2022 21:04:14 +0100 Subject: [PATCH 059/938] Update libsignal-service-java --- graalvm-config-dir/reflect-config.json | 35 +++++++++---------- lib/build.gradle.kts | 2 +- .../signal/manager/api/MessageEnvelope.java | 5 +-- .../signal/manager/api/SendMessageResult.java | 14 ++++---- .../signal/manager/config/ServiceConfig.java | 2 +- .../signal/manager/helper/GroupHelper.java | 6 ++-- .../manager/helper/RecipientHelper.java | 3 +- .../signal/manager/helper/SendHelper.java | 7 ++-- .../manager/storage/groups/GroupInfoV2.java | 10 +++--- .../storage/recipients/RecipientAddress.java | 6 ++-- .../manager/util/MessageCacheUtils.java | 10 +++--- .../signal/manager/util/ProfileUtils.java | 2 +- man/signal-cli.1.adoc | 1 + .../signal/json/JsonSendMessageResult.java | 7 +++- .../signal/util/SendMessageResultUtils.java | 2 ++ 15 files changed, 60 insertions(+), 52 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 1857e1b3..f7c1a95b 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2484,6 +2484,7 @@ {"name":"nullMessage_"}, {"name":"receiptMessage_"}, {"name":"senderKeyDistributionMessage_"}, + {"name":"storyMessage_"}, {"name":"syncMessage_"}, {"name":"typingMessage_"} ] @@ -2626,22 +2627,10 @@ {"name":"receipt_"} ] }, -{ - "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Preview", - "fields":[ - {"name":"bitField0_"}, - {"name":"date_"}, - {"name":"description_"}, - {"name":"image_"}, - {"name":"title_"}, - {"name":"url_"} - ] -}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Quote", "fields":[ {"name":"attachments_"}, - {"name":"authorE164_"}, {"name":"authorUuid_"}, {"name":"bitField0_"}, {"name":"bodyRanges_"}, @@ -2763,6 +2752,17 @@ {"name":"padding_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Preview", + "fields":[ + {"name":"bitField0_"}, + {"name":"date_"}, + {"name":"description_"}, + {"name":"image_"}, + {"name":"title_"}, + {"name":"url_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ReceiptMessage", "fields":[ @@ -2846,7 +2846,6 @@ "fields":[ {"name":"bitField0_"}, {"name":"groupId_"}, - {"name":"threadE164_"}, {"name":"threadUuid_"}, {"name":"type_"} ] @@ -2863,7 +2862,6 @@ "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Read", "fields":[ {"name":"bitField0_"}, - {"name":"senderE164_"}, {"name":"senderUuid_"}, {"name":"timestamp_"} ] @@ -2879,7 +2877,6 @@ "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent", "fields":[ {"name":"bitField0_"}, - {"name":"destinationE164_"}, {"name":"destinationUuid_"}, {"name":"expirationStartTimestamp_"}, {"name":"isRecipientUpdate_"}, @@ -2892,7 +2889,6 @@ "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent$UnidentifiedDeliveryStatus", "fields":[ {"name":"bitField0_"}, - {"name":"destinationE164_"}, {"name":"destinationUuid_"}, {"name":"unidentified_"} ] @@ -2910,7 +2906,6 @@ "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$ViewOnceOpen", "fields":[ {"name":"bitField0_"}, - {"name":"senderE164_"}, {"name":"senderUuid_"}, {"name":"timestamp_"} ] @@ -2919,7 +2914,6 @@ "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Viewed", "fields":[ {"name":"bitField0_"}, - {"name":"senderE164_"}, {"name":"senderUuid_"}, {"name":"timestamp_"} ] @@ -2937,7 +2931,6 @@ "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Verified", "fields":[ {"name":"bitField0_"}, - {"name":"destinationE164_"}, {"name":"destinationUuid_"}, {"name":"identityKey_"}, {"name":"nullMessage_"}, @@ -3104,6 +3097,10 @@ "name":"org.whispersystems.signalservice.internal.util.JsonUtil$IdentityKeySerializer", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.whispersystems.signalservice.internal.util.JsonUtil$ServiceIdDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.util.JsonUtil$UuidDeserializer", "methods":[{"name":"","parameterTypes":[] }] diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 3590f960..81a804ff 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_43") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_44") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.1") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") 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 73e7fe19..2361c733 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 @@ -13,6 +13,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext; +import org.whispersystems.signalservice.api.messages.SignalServicePreview; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; @@ -242,7 +243,7 @@ public record MessageEnvelope( RecipientResolver recipientResolver, RecipientAddressResolver addressResolver ) { - return new Mention(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(mention.getAci())), + return new Mention(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(mention.getServiceId())), mention.getStart(), mention.getLength()); } @@ -481,7 +482,7 @@ public record MessageEnvelope( public record Preview(String title, String description, long date, String url, Optional image) { static Preview from( - SignalServiceDataMessage.Preview preview, final AttachmentFileProvider fileProvider + SignalServicePreview preview, final AttachmentFileProvider fileProvider ) { return new Preview(preview.getTitle(), preview.getDescription(), diff --git a/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java b/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java index c54e9f27..1d38e9d9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java @@ -11,29 +11,30 @@ public record SendMessageResult( boolean isNetworkFailure, boolean isUnregisteredFailure, boolean isIdentityFailure, + boolean isRateLimitFailure, ProofRequiredException proofRequiredFailure ) { public static SendMessageResult success(RecipientAddress address) { - return new SendMessageResult(address, true, false, false, false, null); + return new SendMessageResult(address, true, false, false, false, false, null); } public static SendMessageResult networkFailure(RecipientAddress address) { - return new SendMessageResult(address, false, true, false, false, null); + return new SendMessageResult(address, false, true, false, false, false, null); } public static SendMessageResult unregisteredFailure(RecipientAddress address) { - return new SendMessageResult(address, false, false, true, false, null); + return new SendMessageResult(address, false, false, true, false, false, null); } public static SendMessageResult identityFailure(RecipientAddress address, IdentityKey identityKey) { - return new SendMessageResult(address, false, false, false, true, null); + return new SendMessageResult(address, false, false, false, true, false, null); } public static SendMessageResult proofRequiredFailure( RecipientAddress address, ProofRequiredException proofRequiredException ) { - return new SendMessageResult(address, false, true, false, false, proofRequiredException); + return new SendMessageResult(address, false, true, false, false, false, proofRequiredException); } public static SendMessageResult from( @@ -47,10 +48,9 @@ public record SendMessageResult( sendMessageResult.isNetworkFailure(), sendMessageResult.isUnregisteredFailure(), sendMessageResult.getIdentityFailure() != null, + sendMessageResult.getRateLimitFailure() != null, sendMessageResult.getProofRequiredFailure() == null ? null : new ProofRequiredException(sendMessageResult.getProofRequiredFailure())); } - - public record IdentityFailure(IdentityKey identityKey) {} } 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 dd85671b..3a544318 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,7 @@ public class ServiceConfig { public static final AccountAttributes.Capabilities capabilities; static { - capabilities = new AccountAttributes.Capabilities(false, true, false, true, true, true, true); + capabilities = new AccountAttributes.Capabilities(false, true, false, true, true, true, true, false); try { TrustStore contactTrustStore = new IasTrustStore(); 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 5e668c15..140b5971 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 @@ -41,8 +41,8 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStre import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; -import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.DistributionId; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.exceptions.ConflictException; import java.io.File; @@ -391,8 +391,8 @@ public class GroupHelper { private void storeProfileKeysFromMembers(final DecryptedGroup group) { for (var member : group.getMembersList()) { - final var aci = ACI.fromByteString(member.getUuid()); - final var recipientId = account.getRecipientStore().resolveRecipient(aci); + final var serviceId = ServiceId.fromByteString(member.getUuid()); + final var recipientId = account.getRecipientStore().resolveRecipient(serviceId); try { account.getProfileStore() .storeProfileKey(recipientId, new ProfileKey(member.getProfileKey().toByteArray())); 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 38acd37b..278fb107 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 @@ -12,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.internal.contacts.crypto.Quote; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException; @@ -75,7 +76,7 @@ public class RecipientHelper { public RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException { if (recipient instanceof RecipientIdentifier.Uuid uuidRecipient) { - return account.getRecipientStore().resolveRecipient(ACI.from(uuidRecipient.uuid())); + return account.getRecipientStore().resolveRecipient(ServiceId.from(uuidRecipient.uuid())); } else { final var number = ((RecipientIdentifier.Number) recipient).number(); return account.getRecipientStore().resolveRecipient(number, () -> { 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 712d5a31..1fb7c35c 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 @@ -190,9 +190,10 @@ public class SendHelper { SignalServiceTypingMessage message, RecipientId recipientId ) { final var result = handleSendMessage(recipientId, - (messageSender, address, unidentifiedAccess) -> messageSender.sendTyping(address, - unidentifiedAccess, - message)); + (messageSender, address, unidentifiedAccess) -> messageSender.sendTyping(List.of(address), + List.of(unidentifiedAccess), + message, + null).get(0)); handleSendMessageResult(result); return result; } 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 eb6d5f29..218032f8 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 @@ -10,8 +10,8 @@ import org.signal.storageservice.protos.groups.Member; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.EnabledState; import org.signal.zkgroup.groups.GroupMasterKey; -import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.DistributionId; +import org.whispersystems.signalservice.api.push.ServiceId; import java.util.Set; import java.util.stream.Collectors; @@ -112,7 +112,7 @@ public final class GroupInfoV2 extends GroupInfo { } return group.getMembersList() .stream() - .map(m -> ACI.fromByteString(m.getUuid())) + .map(m -> ServiceId.fromByteString(m.getUuid())) .map(recipientResolver::resolveRecipient) .collect(Collectors.toSet()); } @@ -124,7 +124,7 @@ public final class GroupInfoV2 extends GroupInfo { } return group.getPendingMembersList() .stream() - .map(m -> ACI.fromByteString(m.getUuid())) + .map(m -> ServiceId.fromByteString(m.getUuid())) .map(recipientResolver::resolveRecipient) .collect(Collectors.toSet()); } @@ -136,7 +136,7 @@ public final class GroupInfoV2 extends GroupInfo { } return group.getRequestingMembersList() .stream() - .map(m -> ACI.fromByteString(m.getUuid())) + .map(m -> ServiceId.fromByteString(m.getUuid())) .map(recipientResolver::resolveRecipient) .collect(Collectors.toSet()); } @@ -149,7 +149,7 @@ public final class GroupInfoV2 extends GroupInfo { return group.getMembersList() .stream() .filter(m -> m.getRole() == Member.Role.ADMINISTRATOR) - .map(m -> ACI.fromByteString(m.getUuid())) + .map(m -> ServiceId.fromByteString(m.getUuid())) .map(recipientResolver::resolveRecipient) .collect(Collectors.toSet()); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java index 3bfdadc0..69e2537d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java @@ -1,6 +1,6 @@ package org.asamk.signal.manager.storage.recipients; -import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.Optional; @@ -8,7 +8,7 @@ import java.util.UUID; public record RecipientAddress(Optional uuid, Optional number) { - public static final UUID UNKNOWN_UUID = ACI.UNKNOWN.uuid(); + public static final UUID UNKNOWN_UUID = ServiceId.UNKNOWN.uuid(); /** * Construct a RecipientAddress. @@ -62,7 +62,7 @@ public record RecipientAddress(Optional uuid, Optional number) { } public SignalServiceAddress toSignalServiceAddress() { - return new SignalServiceAddress(ACI.from(uuid.orElse(UNKNOWN_UUID)), + return new SignalServiceAddress(ServiceId.from(uuid.orElse(UNKNOWN_UUID)), org.whispersystems.libsignal.util.guava.Optional.fromNullable(number.orElse(null))); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java index 7c754d55..9d7e0c50 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java @@ -2,7 +2,7 @@ package org.asamk.signal.manager.util; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.DataInputStream; @@ -23,9 +23,9 @@ public class MessageCacheUtils { } var type = in.readInt(); var source = in.readUTF(); - ACI sourceAci = null; + ServiceId sourceServiceId = null; if (version >= 3) { - sourceAci = ACI.parseOrNull(in.readUTF()); + sourceServiceId = ServiceId.parseOrNull(in.readUTF()); } var sourceDevice = in.readInt(); if (version == 1) { @@ -58,9 +58,9 @@ public class MessageCacheUtils { if (version >= 4) { serverDeliveredTimestamp = in.readLong(); } - Optional addressOptional = sourceAci == null + Optional addressOptional = sourceServiceId == null ? Optional.absent() - : Optional.of(new SignalServiceAddress(sourceAci, source)); + : Optional.of(new SignalServiceAddress(sourceServiceId, source)); return new SignalServiceEnvelope(type, addressOptional, sourceDevice, 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 df7ca9c8..c62c1d4c 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 @@ -35,7 +35,7 @@ public class ProfileUtils { getUnidentifiedAccessMode(encryptedProfile, profileCipher), getCapabilities(encryptedProfile)); } catch (InvalidCiphertextException e) { - logger.debug("Failed to decrypt profile for {}", encryptedProfile.getAci(), e); + logger.debug("Failed to decrypt profile for {}", encryptedProfile.getServiceId(), e); return null; } } diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 4754d4e5..1a30b2e0 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -93,6 +93,7 @@ The verification should be done over voice, not SMS. *--captcha*:: The captcha token, required if registration failed with a captcha required error. To get the token, go to https://signalcaptchas.org/registration/generate.html +For the staging environment, use: https://signalcaptchas.org/staging/registration/generate.html Check the developer tools for a redirect starting with signalcaptcha:// Everything after signalcaptcha:// is the captcha token. === verify diff --git a/src/main/java/org/asamk/signal/json/JsonSendMessageResult.java b/src/main/java/org/asamk/signal/json/JsonSendMessageResult.java index 9b80a3aa..f81f54c6 100644 --- a/src/main/java/org/asamk/signal/json/JsonSendMessageResult.java +++ b/src/main/java/org/asamk/signal/json/JsonSendMessageResult.java @@ -24,7 +24,11 @@ public record JsonSendMessageResult( ? Type.SUCCESS : result.isNetworkFailure() ? Type.NETWORK_FAILURE - : result.isUnregisteredFailure() ? Type.UNREGISTERED_FAILURE : Type.IDENTITY_FAILURE, + : result.isRateLimitFailure() + ? Type.RATE_LIMIT_FAILURE + : result.isUnregisteredFailure() + ? Type.UNREGISTERED_FAILURE + : Type.IDENTITY_FAILURE, result.proofRequiredFailure() != null ? result.proofRequiredFailure().getToken() : null, result.proofRequiredFailure() != null ? result.proofRequiredFailure().getRetryAfterSeconds() : null); } @@ -34,5 +38,6 @@ public record JsonSendMessageResult( NETWORK_FAILURE, UNREGISTERED_FAILURE, IDENTITY_FAILURE, + RATE_LIMIT_FAILURE, } } diff --git a/src/main/java/org/asamk/signal/util/SendMessageResultUtils.java b/src/main/java/org/asamk/signal/util/SendMessageResultUtils.java index 74f49534..a7739c23 100644 --- a/src/main/java/org/asamk/signal/util/SendMessageResultUtils.java +++ b/src/main/java/org/asamk/signal/util/SendMessageResultUtils.java @@ -100,6 +100,8 @@ public class SendMessageResultUtils { failure.getRetryAfterSeconds()); } else if (result.isNetworkFailure()) { return String.format("Network failure for \"%s\"", identifier); + } else if (result.isRateLimitFailure()) { + return String.format("Rate limit failure for \"%s\"", identifier); } else if (result.isUnregisteredFailure()) { return String.format("Unregistered user \"%s\"", identifier); } else if (result.isIdentityFailure()) { From 0686fd8e6807578a99e671473cf0d5222d2055ac Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 17 Mar 2022 21:45:30 +0100 Subject: [PATCH 060/938] Fix multi account commands for newly created accounts Fixes #913 --- .../main/java/org/asamk/signal/manager/SignalAccountFiles.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2785ec41..a6614460 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -49,7 +49,7 @@ public class SignalAccountFiles { public MultiAccountManager initMultiAccountManager() { final var managers = accountsStore.getAllAccounts().parallelStream().map(a -> { try { - return initManager(a.path()); + return initManager(a.number(), a.path()); } catch (NotRegisteredException | IOException | AccountCheckException e) { logger.warn("Ignoring {}: {} ({})", a.number(), e.getMessage(), e.getClass().getSimpleName()); return null; From 4f5d5afc4f526222002d1a2e4515aaf0e6c321ff Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 17 Mar 2022 21:49:29 +0100 Subject: [PATCH 061/938] Use correct captcha url in test script --- run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_tests.sh b/run_tests.sh index 341c825f..aa185c9d 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -64,7 +64,7 @@ run_linked() { register() { NUMBER=$1 PIN=$2 - echo -n "Enter a captcha token (https://signalcaptchas.org/staging/challenge/generate.html): " + echo -n "Enter a captcha token (https://signalcaptchas.org/staging/registration/generate.html): " read CAPTCHA run_main -a "$NUMBER" register --captcha "$CAPTCHA" echo -n "Enter validation code for ${NUMBER}: " From fd25ef539f6fa480e5f4cc7be0b7e8425b3375a2 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 17 Mar 2022 22:33:39 +0100 Subject: [PATCH 062/938] Bump version --- CHANGELOG.md | 5 +++++ build.gradle.kts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9989642..5fba3b39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +## [0.10.4.2] - 2022-03-17 +### Fixed +- Crash in json output when receiving message from untrusted identity +- Fix multi account commands for newly created accounts + ## [0.10.4.1] - 2022-03-02 ### Fixed - Linking to current apps (which currently don't include a PNI identity yet) diff --git a/build.gradle.kts b/build.gradle.kts index 36313561..99de4644 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.10" } -version = "0.10.4.1" +version = "0.10.4.2" java { sourceCompatibility = JavaVersion.VERSION_17 From 9eb97746c123575593d8cb3709a3413c0b22482d Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 17 Mar 2022 23:24:30 +0100 Subject: [PATCH 063/938] Update libsignal-service-java --- graalvm-config-dir/reflect-config.json | 22 ++ lib/build.gradle.kts | 2 +- .../org/asamk/signal/manager/ManagerImpl.java | 19 +- .../signal/manager/SignalDependencies.java | 6 +- .../manager/SignalWebSocketHealthMonitor.java | 2 +- .../SendRetryMessageRequestAction.java | 5 +- .../signal/manager/api/MessageEnvelope.java | 193 +++++++----------- .../signal/manager/config/LiveConfig.java | 6 +- .../signal/manager/config/StagingConfig.java | 6 +- .../signal/manager/helper/AccountHelper.java | 4 +- .../signal/manager/helper/GroupHelper.java | 10 +- .../signal/manager/helper/GroupV2Helper.java | 44 +--- .../helper/IncomingMessageHandler.java | 12 +- .../signal/manager/helper/ProfileHelper.java | 32 +-- .../manager/helper/RecipientHelper.java | 2 +- .../signal/manager/helper/SendHelper.java | 8 +- .../signal/manager/helper/StickerHelper.java | 8 +- .../signal/manager/helper/StorageHelper.java | 26 +-- .../signal/manager/helper/SyncHelper.java | 44 ++-- .../helper/UnidentifiedAccessHelper.java | 14 +- .../signal/manager/storage/SignalAccount.java | 3 - .../storage/profiles/SignalProfile.java | 3 - .../manager/storage/recipients/Profile.java | 1 - .../storage/recipients/RecipientAddress.java | 5 +- .../signal/manager/util/AttachmentUtils.java | 10 +- .../manager/util/MessageCacheUtils.java | 4 +- .../manager/util/NumberVerificationUtils.java | 16 +- .../signal/manager/util/ProfileUtils.java | 3 - .../signal/manager/util/StickerUtils.java | 8 +- .../org/asamk/signal/manager/util/Utils.java | 4 +- man/signal-cli-dbus.5.adoc | 5 +- .../org/asamk/signal/dbus/DbusProperties.java | 3 +- 32 files changed, 242 insertions(+), 288 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index f7c1a95b..58cf72ed 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -1573,12 +1573,17 @@ {"name":"signature_"} ] }, +{ + "name":"org.signal.storageservice.protos.groups.BannedMember", + "fields":[{"name":"userId_"}] +}, { "name":"org.signal.storageservice.protos.groups.Group", "fields":[ {"name":"accessControl_"}, {"name":"announcementsOnly_"}, {"name":"avatar_"}, + {"name":"bannedMembers_"}, {"name":"description_"}, {"name":"disappearingMessagesTimer_"}, {"name":"inviteLinkPassword_"}, @@ -1608,9 +1613,11 @@ { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions", "fields":[ + {"name":"addBannedMembers_"}, {"name":"addMembers_"}, {"name":"addPendingMembers_"}, {"name":"addRequestingMembers_"}, + {"name":"deleteBannedMembers_"}, {"name":"deleteMembers_"}, {"name":"deletePendingMembers_"}, {"name":"deleteRequestingMembers_"}, @@ -1631,6 +1638,10 @@ {"name":"sourceUuid_"} ] }, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddBannedMemberAction", + "fields":[{"name":"added_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddMemberAction", "fields":[ @@ -1646,6 +1657,10 @@ "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddRequestingMemberAction", "fields":[{"name":"added_"}] }, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteBannedMemberAction", + "fields":[{"name":"deletedUserId_"}] +}, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteMemberAction", "fields":[{"name":"deletedUserId_"}] @@ -1777,11 +1792,16 @@ {"name":"uuid_"} ] }, +{ + "name":"org.signal.storageservice.protos.groups.local.DecryptedBannedMember", + "fields":[{"name":"uuid_"}] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedGroup", "fields":[ {"name":"accessControl_"}, {"name":"avatar_"}, + {"name":"bannedMembers_"}, {"name":"description_"}, {"name":"disappearingMessagesTimer_"}, {"name":"inviteLinkPassword_"}, @@ -1796,6 +1816,7 @@ { "name":"org.signal.storageservice.protos.groups.local.DecryptedGroupChange", "fields":[ + {"name":"deleteBannedMembers_"}, {"name":"deleteMembers_"}, {"name":"deletePendingMembers_"}, {"name":"deleteRequestingMembers_"}, @@ -1804,6 +1825,7 @@ {"name":"modifyMemberRoles_"}, {"name":"newAttributeAccess_"}, {"name":"newAvatar_"}, + {"name":"newBannedMembers_"}, {"name":"newDescription_"}, {"name":"newInviteLinkAccess_"}, {"name":"newInviteLinkPassword_"}, diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 81a804ff..ee2ace73 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_44") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_45") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.1") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 380b00ee..aa4752a2 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -62,7 +62,6 @@ import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.StickerUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; @@ -82,6 +81,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; @@ -246,14 +246,9 @@ class ManagerImpl implements Manager { @Override public void setProfile( - String givenName, final String familyName, String about, String aboutEmoji, java.util.Optional avatar + String givenName, final String familyName, String about, String aboutEmoji, Optional avatar ) throws IOException { - context.getProfileHelper() - .setProfile(givenName, - familyName, - about, - aboutEmoji, - avatar == null ? null : Optional.fromNullable(avatar.orElse(null))); + context.getProfileHelper().setProfile(givenName, familyName, about, aboutEmoji, avatar); context.getSyncHelper().sendSyncFetchProfileMessage(); } @@ -308,7 +303,7 @@ class ManagerImpl implements Manager { } @Override - public void setRegistrationLockPin(java.util.Optional pin) throws IOException, NotMasterDeviceException { + public void setRegistrationLockPin(Optional pin) throws IOException, NotMasterDeviceException { if (!account.isMasterDevice()) { throw new NotMasterDeviceException(); } @@ -442,7 +437,7 @@ class ManagerImpl implements Manager { final var timestamp = System.currentTimeMillis(); for (var recipient : recipients) { if (recipient instanceof RecipientIdentifier.Single single) { - final var message = new SignalServiceTypingMessage(action, timestamp, Optional.absent()); + final var message = new SignalServiceTypingMessage(action, timestamp, Optional.empty()); try { final var recipientId = context.getRecipientHelper().resolveRecipient(single); final var result = context.getSendHelper().sendTypingMessage(message, recipientId); @@ -558,7 +553,7 @@ class ManagerImpl implements Manager { stickerPack.getPackKey(), stickerId, manifestSticker.emoji(), - AttachmentUtils.createAttachment(streamDetails, Optional.absent()))); + AttachmentUtils.createAttachment(streamDetails, Optional.empty()))); } } @@ -719,7 +714,7 @@ class ManagerImpl implements Manager { pack.isInstalled(), manifest.title(), manifest.author(), - java.util.Optional.ofNullable(manifest.cover() == null ? null : manifest.cover().toApi()), + Optional.ofNullable(manifest.cover() == null ? null : manifest.cover().toApi()), manifest.stickers().stream().map(JsonStickerPack.JsonSticker::toApi).toList()); } catch (Exception e) { logger.warn("Failed to read local sticker pack manifest: {}", e.getMessage(), e); diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java index 244a110f..4db6eddf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java @@ -4,7 +4,6 @@ import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.signal.libsignal.metadata.certificate.CertificateValidator; import org.signal.zkgroup.profiles.ClientZkProfileOperations; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceDataStore; @@ -23,6 +22,7 @@ import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.websocket.WebSocketFactory; import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; +import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.function.Supplier; @@ -137,7 +137,7 @@ public class SignalDependencies { public WebSocketConnection createUnidentifiedWebSocket() { return new WebSocketConnection("unidentified", serviceEnvironmentConfig.getSignalServiceConfiguration(), - Optional.absent(), + Optional.empty(), userAgent, healthMonitor); } @@ -164,7 +164,7 @@ public class SignalDependencies { sessionLock, userAgent, getSignalWebSocket(), - Optional.absent(), + Optional.empty(), getClientZkProfileOperations(), executor, ServiceConfig.MAX_ENVELOPE_SIZE, diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java b/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java index fd4db601..95d12674 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalWebSocketHealthMonitor.java @@ -2,8 +2,8 @@ package org.asamk.signal.manager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.util.guava.Preconditions; import org.whispersystems.signalservice.api.SignalWebSocket; +import org.whispersystems.signalservice.api.util.Preconditions; import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.websocket.HealthMonitor; import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState; 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 cb4cd246..144fba4a 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 @@ -6,10 +6,11 @@ import org.asamk.signal.manager.storage.recipients.RecipientId; import org.signal.libsignal.metadata.ProtocolException; import org.whispersystems.libsignal.protocol.CiphertextMessage; import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.internal.push.SignalServiceProtos; +import java.util.Optional; + public class SendRetryMessageRequestAction implements HandleAction { private final RecipientId recipientId; @@ -32,7 +33,7 @@ public class SendRetryMessageRequestAction implements HandleAction { int senderDevice = protocolException.getSenderDevice(); Optional groupId = protocolException.getGroupId().isPresent() ? Optional.of(GroupId.unknownVersion( - protocolException.getGroupId().get())) : Optional.absent(); + protocolException.getGroupId().get())) : Optional.empty(); byte[] originalContent; int envelopeType; 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 2361c733..7c6cd58c 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 @@ -82,7 +82,7 @@ public record MessageEnvelope( public static Typing from(final SignalServiceTypingMessage typingMessage) { return new Typing(typingMessage.getTimestamp(), typingMessage.isTypingStarted() ? Type.STARTED : Type.STOPPED, - Optional.ofNullable(typingMessage.getGroupId().transform(GroupId::unknownVersion).orNull())); + typingMessage.getGroupId().map(GroupId::unknownVersion)); } public enum Type { @@ -119,43 +119,33 @@ public record MessageEnvelope( final AttachmentFileProvider fileProvider ) { return new Data(dataMessage.getTimestamp(), - Optional.ofNullable(dataMessage.getGroupContext().transform(GroupContext::from).orNull()), - Optional.ofNullable(dataMessage.getGroupCallUpdate().transform(GroupCallUpdate::from).orNull()), - Optional.ofNullable(dataMessage.getBody().orNull()), + dataMessage.getGroupContext().map(GroupContext::from), + dataMessage.getGroupCallUpdate().map(GroupCallUpdate::from), + dataMessage.getBody(), dataMessage.getExpiresInSeconds(), dataMessage.isExpirationUpdate(), dataMessage.isViewOnce(), dataMessage.isEndSession(), dataMessage.getProfileKey().isPresent(), - Optional.ofNullable(dataMessage.getReaction() - .transform(r -> Reaction.from(r, recipientResolver, addressResolver)) - .orNull()), - Optional.ofNullable(dataMessage.getQuote() - .transform(q -> Quote.from(q, recipientResolver, addressResolver, fileProvider)) - .orNull()), - Optional.ofNullable(dataMessage.getPayment() - .transform(p -> p.getPaymentNotification().isPresent() ? Payment.from(p) : null) - .orNull()), + dataMessage.getReaction().map(r -> Reaction.from(r, recipientResolver, addressResolver)), + dataMessage.getQuote().map(q -> Quote.from(q, recipientResolver, addressResolver, fileProvider)), + dataMessage.getPayment().map(p -> p.getPaymentNotification().isPresent() ? Payment.from(p) : null), dataMessage.getAttachments() - .transform(a -> a.stream().map(as -> Attachment.from(as, fileProvider)).toList()) - .or(List.of()), - Optional.ofNullable(dataMessage.getRemoteDelete() - .transform(SignalServiceDataMessage.RemoteDelete::getTargetSentTimestamp) - .orNull()), - Optional.ofNullable(dataMessage.getSticker().transform(Sticker::from).orNull()), + .map(a -> a.stream().map(as -> Attachment.from(as, fileProvider)).toList()) + .orElse(List.of()), + dataMessage.getRemoteDelete().map(SignalServiceDataMessage.RemoteDelete::getTargetSentTimestamp), + dataMessage.getSticker().map(Sticker::from), dataMessage.getSharedContacts() - .transform(a -> a.stream() + .map(a -> a.stream() .map(sharedContact -> SharedContact.from(sharedContact, fileProvider)) .toList()) - .or(List.of()), + .orElse(List.of()), dataMessage.getMentions() - .transform(a -> a.stream() - .map(m -> Mention.from(m, recipientResolver, addressResolver)) - .toList()) - .or(List.of()), + .map(a -> a.stream().map(m -> Mention.from(m, recipientResolver, addressResolver)).toList()) + .orElse(List.of()), dataMessage.getPreviews() - .transform(a -> a.stream().map(preview -> Preview.from(preview, fileProvider)).toList()) - .or(List.of())); + .map(a -> a.stream().map(preview -> Preview.from(preview, fileProvider)).toList()) + .orElse(List.of())); } public record GroupContext(GroupId groupId, boolean isGroupUpdate, int revision) { @@ -271,13 +261,13 @@ public record MessageEnvelope( final var a = attachment.asPointer(); return new Attachment(Optional.of(a.getRemoteId().toString()), Optional.of(fileProvider.getFile(a.getRemoteId())), - Optional.ofNullable(a.getFileName().orNull()), + a.getFileName(), a.getContentType(), a.getUploadTimestamp() == 0 ? Optional.empty() : Optional.of(a.getUploadTimestamp()), - Optional.ofNullable(a.getSize().transform(Integer::longValue).orNull()), - Optional.ofNullable(a.getPreview().orNull()), + a.getSize().map(Integer::longValue), + a.getPreview(), Optional.empty(), - Optional.ofNullable(a.getCaption().orNull()), + a.getCaption(), a.getWidth() == 0 ? Optional.empty() : Optional.of(a.getWidth()), a.getHeight() == 0 ? Optional.empty() : Optional.of(a.getHeight()), a.getVoiceNote(), @@ -287,13 +277,13 @@ public record MessageEnvelope( final var a = attachment.asStream(); return new Attachment(Optional.empty(), Optional.empty(), - Optional.ofNullable(a.getFileName().orNull()), + a.getFileName(), a.getContentType(), a.getUploadTimestamp() == 0 ? Optional.empty() : Optional.of(a.getUploadTimestamp()), Optional.of(a.getLength()), - Optional.ofNullable(a.getPreview().orNull()), + a.getPreview(), Optional.empty(), - Optional.ofNullable(a.getCaption().orNull()), + a.getCaption(), a.getWidth() == 0 ? Optional.empty() : Optional.of(a.getWidth()), a.getHeight() == 0 ? Optional.empty() : Optional.of(a.getHeight()), a.getVoiceNote(), @@ -347,13 +337,11 @@ public record MessageEnvelope( final AttachmentFileProvider fileProvider ) { return new SharedContact(Name.from(sharedContact.getName()), - Optional.ofNullable(sharedContact.getAvatar() - .transform(avatar1 -> Avatar.from(avatar1, fileProvider)) - .orNull()), - sharedContact.getPhone().transform(p -> p.stream().map(Phone::from).toList()).or(List.of()), - sharedContact.getEmail().transform(p -> p.stream().map(Email::from).toList()).or(List.of()), - sharedContact.getAddress().transform(p -> p.stream().map(Address::from).toList()).or(List.of()), - Optional.ofNullable(sharedContact.getOrganization().orNull())); + sharedContact.getAvatar().map(avatar1 -> Avatar.from(avatar1, fileProvider)), + sharedContact.getPhone().map(p -> p.stream().map(Phone::from).toList()).orElse(List.of()), + sharedContact.getEmail().map(p -> p.stream().map(Email::from).toList()).orElse(List.of()), + sharedContact.getAddress().map(p -> p.stream().map(Address::from).toList()).orElse(List.of()), + sharedContact.getOrganization()); } public record Name( @@ -366,12 +354,12 @@ public record MessageEnvelope( ) { static Name from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Name name) { - return new Name(Optional.ofNullable(name.getDisplay().orNull()), - Optional.ofNullable(name.getGiven().orNull()), - Optional.ofNullable(name.getFamily().orNull()), - Optional.ofNullable(name.getPrefix().orNull()), - Optional.ofNullable(name.getSuffix().orNull()), - Optional.ofNullable(name.getMiddle().orNull())); + return new Name(name.getDisplay(), + name.getGiven(), + name.getFamily(), + name.getPrefix(), + name.getSuffix(), + name.getMiddle()); } } @@ -390,9 +378,7 @@ public record MessageEnvelope( ) { static Phone from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Phone phone) { - return new Phone(phone.getValue(), - Type.from(phone.getType()), - Optional.ofNullable(phone.getLabel().orNull())); + return new Phone(phone.getValue(), Type.from(phone.getType()), phone.getLabel()); } public enum Type { @@ -417,9 +403,7 @@ public record MessageEnvelope( ) { static Email from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Email email) { - return new Email(email.getValue(), - Type.from(email.getType()), - Optional.ofNullable(email.getLabel().orNull())); + return new Email(email.getValue(), Type.from(email.getType()), email.getLabel()); } public enum Type { @@ -453,14 +437,14 @@ public record MessageEnvelope( static Address from(org.whispersystems.signalservice.api.messages.shared.SharedContact.PostalAddress address) { return new Address(Address.Type.from(address.getType()), - Optional.ofNullable(address.getLabel().orNull()), - Optional.ofNullable(address.getLabel().orNull()), - Optional.ofNullable(address.getLabel().orNull()), - Optional.ofNullable(address.getLabel().orNull()), - Optional.ofNullable(address.getLabel().orNull()), - Optional.ofNullable(address.getLabel().orNull()), - Optional.ofNullable(address.getLabel().orNull()), - Optional.ofNullable(address.getLabel().orNull())); + address.getLabel(), + address.getLabel(), + address.getLabel(), + address.getLabel(), + address.getLabel(), + address.getLabel(), + address.getLabel(), + address.getLabel()); } public enum Type { @@ -488,9 +472,7 @@ public record MessageEnvelope( preview.getDescription(), preview.getDate(), preview.getUrl(), - Optional.ofNullable(preview.getImage() - .transform(as -> Attachment.from(as, fileProvider)) - .orNull())); + preview.getImage().map(as -> Attachment.from(as, fileProvider))); } } } @@ -512,30 +494,22 @@ public record MessageEnvelope( RecipientAddressResolver addressResolver, final AttachmentFileProvider fileProvider ) { - return new Sync(Optional.ofNullable(syncMessage.getSent() - .transform(s -> Sent.from(s, recipientResolver, addressResolver, fileProvider)) - .orNull()), - Optional.ofNullable(syncMessage.getBlockedList() - .transform(b -> Blocked.from(b, recipientResolver, addressResolver)) - .orNull()), + return new Sync(syncMessage.getSent() + .map(s -> Sent.from(s, recipientResolver, addressResolver, fileProvider)), + syncMessage.getBlockedList().map(b -> Blocked.from(b, recipientResolver, addressResolver)), syncMessage.getRead() - .transform(r -> r.stream() - .map(rm -> Read.from(rm, recipientResolver, addressResolver)) - .toList()) - .or(List.of()), + .map(r -> r.stream().map(rm -> Read.from(rm, recipientResolver, addressResolver)).toList()) + .orElse(List.of()), syncMessage.getViewed() - .transform(r -> r.stream() + .map(r -> r.stream() .map(rm -> Viewed.from(rm, recipientResolver, addressResolver)) .toList()) - .or(List.of()), - Optional.ofNullable(syncMessage.getViewOnceOpen() - .transform(rm -> ViewOnceOpen.from(rm, recipientResolver, addressResolver)) - .orNull()), - Optional.ofNullable(syncMessage.getContacts().transform(Contacts::from).orNull()), - Optional.ofNullable(syncMessage.getGroups().transform(Groups::from).orNull()), - Optional.ofNullable(syncMessage.getMessageRequestResponse() - .transform(m -> MessageRequestResponse.from(m, recipientResolver, addressResolver)) - .orNull())); + .orElse(List.of()), + syncMessage.getViewOnceOpen().map(rm -> ViewOnceOpen.from(rm, recipientResolver, addressResolver)), + syncMessage.getContacts().map(Contacts::from), + syncMessage.getGroups().map(Groups::from), + syncMessage.getMessageRequestResponse() + .map(m -> MessageRequestResponse.from(m, recipientResolver, addressResolver))); } public record Sent( @@ -554,10 +528,8 @@ public record MessageEnvelope( ) { return new Sent(sentMessage.getTimestamp(), sentMessage.getExpirationStartTimestamp(), - Optional.ofNullable(sentMessage.getDestination() - .transform(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient( - d))) - .orNull()), + sentMessage.getDestination() + .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))), sentMessage.getRecipients() .stream() .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))) @@ -638,13 +610,9 @@ public record MessageEnvelope( RecipientAddressResolver addressResolver ) { return new MessageRequestResponse(Type.from(messageRequestResponse.getType()), - Optional.ofNullable(messageRequestResponse.getGroupId() - .transform(GroupId::unknownVersion) - .orNull()), - Optional.ofNullable(messageRequestResponse.getPerson() - .transform(p -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient( - p))) - .orNull())); + messageRequestResponse.getGroupId().map(GroupId::unknownVersion), + messageRequestResponse.getPerson() + .map(p -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(p)))); } public enum Type { @@ -682,17 +650,17 @@ public record MessageEnvelope( ) { public static Call from(final SignalServiceCallMessage callMessage) { - return new Call(Optional.ofNullable(callMessage.getDestinationDeviceId().orNull()), - Optional.ofNullable(callMessage.getGroupId().transform(GroupId::unknownVersion).orNull()), - Optional.ofNullable(callMessage.getTimestamp().orNull()), - Optional.ofNullable(callMessage.getOfferMessage().transform(Offer::from).orNull()), - Optional.ofNullable(callMessage.getAnswerMessage().transform(Answer::from).orNull()), - Optional.ofNullable(callMessage.getHangupMessage().transform(Hangup::from).orNull()), - Optional.ofNullable(callMessage.getBusyMessage().transform(Busy::from).orNull()), + return new Call(callMessage.getDestinationDeviceId(), + callMessage.getGroupId().map(GroupId::unknownVersion), + callMessage.getTimestamp(), + callMessage.getOfferMessage().map(Offer::from), + callMessage.getAnswerMessage().map(Answer::from), + callMessage.getHangupMessage().map(Hangup::from), + callMessage.getBusyMessage().map(Busy::from), callMessage.getIceUpdateMessages() - .transform(m -> m.stream().map(IceUpdate::from).toList()) - .or(List.of()), - Optional.ofNullable(callMessage.getOpaqueMessage().transform(Opaque::from).orNull())); + .map(m -> m.stream().map(IceUpdate::from).toList()) + .orElse(List.of()), + callMessage.getOpaqueMessage().map(Opaque::from)); } public record Offer(long id, String sdp, Type type, byte[] opaque) { @@ -813,15 +781,12 @@ public record MessageEnvelope( Optional sync; Optional call; if (content != null) { - receipt = Optional.ofNullable(content.getReceiptMessage().transform(Receipt::from).orNull()); - typing = Optional.ofNullable(content.getTypingMessage().transform(Typing::from).orNull()); - data = Optional.ofNullable(content.getDataMessage() - .transform(dataMessage -> Data.from(dataMessage, recipientResolver, addressResolver, fileProvider)) - .orNull()); - sync = Optional.ofNullable(content.getSyncMessage() - .transform(s -> Sync.from(s, recipientResolver, addressResolver, fileProvider)) - .orNull()); - call = Optional.ofNullable(content.getCallMessage().transform(Call::from).orNull()); + receipt = content.getReceiptMessage().map(Receipt::from); + typing = content.getTypingMessage().map(Typing::from); + data = content.getDataMessage() + .map(dataMessage -> Data.from(dataMessage, recipientResolver, addressResolver, fileProvider)); + sync = content.getSyncMessage().map(s -> Sync.from(s, recipientResolver, addressResolver, fileProvider)); + call = content.getCallMessage().map(Call::from); } else { receipt = envelope.isReceipt() ? Optional.of(new Receipt(envelope.getServerReceivedTimestamp(), Receipt.Type.DELIVERY, 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 ec9099ef..5890b6a3 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 @@ -4,7 +4,6 @@ import org.bouncycastle.util.encoders.Hex; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.ecc.Curve; import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; import org.whispersystems.signalservice.internal.configuration.SignalCdshUrl; @@ -18,6 +17,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl; import java.util.Base64; import java.util.List; import java.util.Map; +import java.util.Optional; import okhttp3.Dns; import okhttp3.Interceptor; @@ -42,8 +42,8 @@ class LiveConfig { private final static String SIGNAL_CDSH_URL = ""; private final static TrustStore TRUST_STORE = new WhisperTrustStore(); - private final static Optional dns = Optional.absent(); - private final static Optional proxy = Optional.absent(); + private final static Optional dns = Optional.empty(); + private final static Optional proxy = Optional.empty(); private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXQ=="); 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 71b63e71..ed53d8d8 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 @@ -4,7 +4,6 @@ import org.bouncycastle.util.encoders.Hex; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.ecc.Curve; import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; import org.whispersystems.signalservice.internal.configuration.SignalCdshUrl; @@ -18,6 +17,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl; import java.util.Base64; import java.util.List; import java.util.Map; +import java.util.Optional; import okhttp3.Dns; import okhttp3.Interceptor; @@ -42,8 +42,8 @@ class StagingConfig { private final static String SIGNAL_CDSH_URL = "https://cdsh.staging.signal.org"; private final static TrustStore TRUST_STORE = new WhisperTrustStore(); - private final static Optional dns = Optional.absent(); - private final static Optional proxy = Optional.absent(); + private final static Optional dns = Optional.empty(); + private final static Optional proxy = Optional.empty(); private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() .decode("ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXQ=="); 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 fc1bcfc4..328074b7 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 @@ -13,13 +13,13 @@ import org.asamk.signal.manager.util.NumberVerificationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.PNI; import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; import org.whispersystems.signalservice.api.util.DeviceNameUtil; import java.io.IOException; +import java.util.Optional; import java.util.concurrent.TimeUnit; public class AccountHelper { @@ -176,7 +176,7 @@ public class AccountHelper { // When setting an empty GCM id, the Signal-Server also sets the fetchesMessages property to false. // If this is the master device, other users can't send messages to this number anymore. // If this is a linked device, other users can still send messages, but this device doesn't receive them anymore. - dependencies.getAccountManager().setGcmId(Optional.absent()); + dependencies.getAccountManager().setGcmId(Optional.empty()); account.setRegistered(false); unregisteredListener.call(); 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 140b5971..e40dbdfb 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 @@ -34,7 +34,6 @@ import org.signal.zkgroup.groups.GroupSecretParams; import org.signal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; @@ -53,6 +52,7 @@ import java.nio.file.Files; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; public class GroupHelper { @@ -91,10 +91,10 @@ public class GroupHelper { public Optional createGroupAvatarAttachment(GroupIdV1 groupId) throws IOException { final var streamDetails = context.getAvatarStore().retrieveGroupAvatar(groupId); if (streamDetails == null) { - return Optional.absent(); + return Optional.empty(); } - return Optional.of(AttachmentUtils.createAttachment(streamDetails, Optional.absent())); + return Optional.of(AttachmentUtils.createAttachment(streamDetails, Optional.empty())); } public GroupInfoV2 getOrMigrateGroup( @@ -625,9 +625,7 @@ public class GroupHelper { try { final var attachment = createGroupAvatarAttachment(g.getGroupId()); - if (attachment.isPresent()) { - group.withAvatar(attachment.get()); - } + attachment.ifPresent(group::withAvatar); } catch (IOException e) { throw new AttachmentInvalidException(g.getGroupId().toBase64(), e); } 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 a40c6223..92da383e 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 @@ -10,7 +10,6 @@ import org.asamk.signal.manager.groups.GroupPermission; import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.storage.groups.GroupInfoV2; -import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.Utils; @@ -29,7 +28,6 @@ import org.signal.zkgroup.groups.GroupSecretParams; import org.signal.zkgroup.groups.UuidCiphertext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil; import org.whispersystems.signalservice.api.groupsv2.GroupCandidate; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; @@ -48,6 +46,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -90,7 +89,7 @@ class GroupV2Helper { return dependencies.getGroupsV2Api() .getGroupJoinInfo(groupSecretParams, - Optional.fromNullable(password).transform(GroupLinkPassword::serialize), + Optional.ofNullable(password).map(GroupLinkPassword::serialize), getGroupAuthForToday(groupSecretParams)); } @@ -145,44 +144,27 @@ class GroupV2Helper { return null; } - if (!areMembersValid(members)) return null; - - final var self = new GroupCandidate(getSelfAci().uuid(), Optional.fromNullable(profileKeyCredential)); + final var self = new GroupCandidate(getSelfAci().uuid(), Optional.of(profileKeyCredential)); final var memberList = new ArrayList<>(members); final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream(); final var uuids = memberList.stream() .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()); var candidates = Utils.zip(uuids, credentials, - (uuid, credential) -> new GroupCandidate(uuid, Optional.fromNullable(credential))) + (uuid, credential) -> new GroupCandidate(uuid, Optional.ofNullable(credential))) .collect(Collectors.toSet()); final var groupSecretParams = GroupSecretParams.generate(); return dependencies.getGroupsV2Operations() .createNewGroup(groupSecretParams, name, - Optional.fromNullable(avatar), + Optional.ofNullable(avatar), self, candidates, Member.Role.DEFAULT, 0); } - private boolean areMembersValid(final Set members) { - final var noGv2Capability = context.getProfileHelper() - .getRecipientProfile(new ArrayList<>(members)) - .stream() - .filter(profile -> profile != null && !profile.getCapabilities().contains(Profile.Capability.gv2)) - .collect(Collectors.toSet()); - if (noGv2Capability.size() > 0) { - logger.warn("Cannot create a V2 group as some members don't support Groups V2: {}", - noGv2Capability.stream().map(Profile::getDisplayName).collect(Collectors.joining(", "))); - return false; - } - - return true; - } - Pair updateGroup( GroupInfoV2 groupInfoV2, String name, String description, File avatarFile ) throws IOException { @@ -212,21 +194,17 @@ class GroupV2Helper { ) throws IOException { GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); - if (!areMembersValid(newMembers)) { - throw new IOException("Failed to update group"); - } - final var memberList = new ArrayList<>(newMembers); final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream(); final var uuids = memberList.stream() .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()); var candidates = Utils.zip(uuids, credentials, - (uuid, credential) -> new GroupCandidate(uuid, Optional.fromNullable(credential))) + (uuid, credential) -> new GroupCandidate(uuid, Optional.ofNullable(credential))) .collect(Collectors.toSet()); final var aci = getSelfAci(); - final var change = groupOperations.createModifyGroupMembershipChange(candidates, aci.uuid()); + final var change = groupOperations.createModifyGroupMembershipChange(candidates, Set.of(), aci.uuid()); change.setSourceUuid(getSelfAci().toByteString()); @@ -431,7 +409,7 @@ class GroupV2Helper { GroupInfoV2 groupInfoV2, Set uuids ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); - return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(uuids)); + return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(uuids, false)); } private Pair commitChange( @@ -453,7 +431,7 @@ class GroupV2Helper { } var signedGroupChange = dependencies.getGroupsV2Api() - .patchGroup(changeActions, getGroupAuthForToday(groupSecretParams), Optional.absent()); + .patchGroup(changeActions, getGroupAuthForToday(groupSecretParams), Optional.empty()); return new Pair<>(decryptedGroupState, signedGroupChange); } @@ -470,7 +448,7 @@ class GroupV2Helper { return dependencies.getGroupsV2Api() .patchGroup(changeActions, getGroupAuthForToday(groupSecretParams), - Optional.fromNullable(password).transform(GroupLinkPassword::serialize)); + Optional.ofNullable(password).map(GroupLinkPassword::serialize)); } DecryptedGroup getUpdatedDecryptedGroup( @@ -493,7 +471,7 @@ class GroupV2Helper { .forGroup(GroupSecretParams.deriveFromMasterKey(groupMasterKey)); try { - return groupOperations.decryptChange(GroupChange.parseFrom(signedGroupChange), true).orNull(); + return groupOperations.decryptChange(GroupChange.parseFrom(signedGroupChange), true).orElse(null); } catch (VerificationFailedException | InvalidGroupStateException | InvalidProtocolBufferException e) { return 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 2400128c..fb56f262 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 @@ -270,7 +270,7 @@ public final class IncomingMessageHandler { final DecryptionErrorMessage message ) { final var logEntries = account.getMessageSendLogStore() - .findMessages(sender, senderDeviceId, message.getTimestamp(), !message.getRatchetKey().isPresent()); + .findMessages(sender, senderDeviceId, message.getTimestamp(), message.getRatchetKey().isEmpty()); for (final var logEntry : logEntries) { actions.add(new ResendMessageAction(sender, message.getTimestamp(), logEntry)); @@ -318,7 +318,7 @@ public final class IncomingMessageHandler { account.setMultiDevice(true); if (syncMessage.getSent().isPresent()) { var message = syncMessage.getSent().get(); - final var destination = message.getDestination().orNull(); + final var destination = message.getDestination().orElse(null); actions.addAll(handleSignalServiceDataMessage(message.getMessage(), true, sender, @@ -388,11 +388,11 @@ public final class IncomingMessageHandler { if (syncMessage.getStickerPackOperations().isPresent()) { final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get(); for (var m : stickerPackOperationMessages) { - if (!m.getPackId().isPresent()) { + if (m.getPackId().isEmpty()) { continue; } final var stickerPackId = StickerPackId.deserialize(m.getPackId().get()); - final var installed = !m.getType().isPresent() + final var installed = m.getType().isEmpty() || m.getType().get() == StickerPackOperationMessage.Type.INSTALL; var sticker = account.getStickerStore().getStickerPack(stickerPackId); @@ -488,12 +488,12 @@ public final class IncomingMessageHandler { return false; } - if (content == null || !content.getDataMessage().isPresent()) { + if (content == null || content.getDataMessage().isEmpty()) { return false; } var message = content.getDataMessage().get(); - if (!message.getGroupContext().isPresent()) { + if (message.getGroupContext().isEmpty()) { return false; } 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 8e58e976..43a5ddb7 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 @@ -15,8 +15,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; +import org.whispersystems.signalservice.api.profiles.AvatarUploadParams; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -33,6 +33,7 @@ import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.Optional; import java.util.Set; import io.reactivex.rxjava3.core.Flowable; @@ -130,20 +131,23 @@ public final class ProfileHelper { var newProfile = builder.build(); if (uploadProfile) { - try (final var streamDetails = avatar == null - ? context.getAvatarStore() - .retrieveProfileAvatar(account.getSelfRecipientAddress()) - : avatar.isPresent() ? Utils.createStreamDetailsFromFile(avatar.get()) : null) { + try (final var streamDetails = avatar != null && avatar.isPresent() ? Utils.createStreamDetailsFromFile( + avatar.get()) : null) { + final var avatarUploadParams = avatar == null + ? AvatarUploadParams.unchanged(true) + : avatar.isPresent() + ? AvatarUploadParams.forAvatar(streamDetails) + : AvatarUploadParams.unchanged(false); final var avatarPath = dependencies.getAccountManager() .setVersionedProfile(account.getAci(), account.getProfileKey(), newProfile.getInternalServiceName(), newProfile.getAbout() == null ? "" : newProfile.getAbout(), newProfile.getAboutEmoji() == null ? "" : newProfile.getAboutEmoji(), - Optional.absent(), - streamDetails, + Optional.empty(), + avatarUploadParams, List.of(/* TODO */)); - builder.withAvatarUrlPath(avatarPath.orNull()); + builder.withAvatarUrlPath(avatarPath.orElse(null)); newProfile = builder.build(); } } @@ -202,7 +206,7 @@ public final class ProfileHelper { private SignalServiceProfile retrieveProfileSync(String username) throws IOException { final var locale = Utils.getDefaultLocale(Locale.US); - return dependencies.getMessageReceiver().retrieveProfileByUsername(username, Optional.absent(), locale); + return dependencies.getMessageReceiver().retrieveProfileByUsername(username, Optional.empty(), locale); } private Profile decryptProfileAndDownloadAvatar( @@ -246,7 +250,7 @@ public final class ProfileHelper { RecipientId recipientId, SignalServiceProfile.RequestType requestType ) { var unidentifiedAccess = getUnidentifiedAccess(recipientId); - var profileKey = Optional.fromNullable(account.getProfileStore().getProfileKey(recipientId)); + var profileKey = Optional.ofNullable(account.getProfileStore().getProfileKey(recipientId)); logger.trace("Retrieving profile for {} {}", recipientId, @@ -259,7 +263,7 @@ public final class ProfileHelper { if (requestType == SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL || account.getProfileStore().getProfileKeyCredential(recipientId) == null) { logger.trace("Storing profile credential"); - final var profileKeyCredential = p.getProfileKeyCredential().orNull(); + final var profileKeyCredential = p.getProfileKeyCredential().orElse(null); account.getProfileStore().storeProfileKeyCredential(recipientId, profileKeyCredential); } @@ -324,8 +328,8 @@ public final class ProfileHelper { throw new NotFoundException("Profile not found"); } else { throw pair.getExecutionError() - .or(pair.getApplicationError()) - .or(new IOException("Unknown error while retrieving profile")); + .or(pair::getApplicationError) + .orElseThrow(() -> new IOException("Unknown error while retrieving profile")); } }); } @@ -380,6 +384,6 @@ public final class ProfileHelper { return unidentifiedAccess.get().getTargetUnidentifiedAccess(); } - return Optional.absent(); + return Optional.empty(); } } 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 278fb107..5a1942f9 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 @@ -91,7 +91,7 @@ public class RecipientHelper { public RecipientId refreshRegisteredUser(RecipientId recipientId) throws IOException, UnregisteredRecipientException { final var address = resolveSignalServiceAddress(recipientId); - if (!address.getNumber().isPresent()) { + if (address.getNumber().isEmpty()) { return recipientId; } final var number = address.getNumber().get(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index 1fb7c35c..375c2def 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 @@ -21,7 +21,6 @@ import org.whispersystems.libsignal.InvalidRegistrationIdException; import org.whispersystems.libsignal.NoSessionException; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.ContentHint; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; @@ -47,6 +46,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; @@ -138,7 +138,7 @@ public class SendHelper { final var result = handleSendMessage(recipientId, (messageSender, address, unidentifiedAccess) -> messageSender.sendRetryReceipt(address, unidentifiedAccess, - groupId.transform(GroupId::serialize), + groupId.map(GroupId::serialize), errorMessage)); handleSendMessageResult(result); return result; @@ -222,7 +222,7 @@ public class SendHelper { timestamp, messageSendLogEntry.content(), messageSendLogEntry.contentHint(), - Optional.absent())); + Optional.empty())); } final var groupId = messageSendLogEntry.groupId().get(); @@ -457,7 +457,7 @@ public class SendHelper { } final var access = context.getUnidentifiedAccessHelper().getAccessFor(recipientId); - if (!access.isPresent() || !access.get().getTargetUnidentifiedAccess().isPresent()) { + if (access.isEmpty() || access.get().getTargetUnidentifiedAccess().isEmpty()) { continue; } 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 c432ad9a..566c2b15 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 @@ -64,14 +64,14 @@ public class StickerHelper { context.getStickerPackStore().storeSticker(packId, id, o -> IOUtils.copyStream(inputStream, o)); } - final var jsonManifest = new JsonStickerPack(manifest.getTitle().orNull(), - manifest.getAuthor().orNull(), + final var jsonManifest = new JsonStickerPack(manifest.getTitle().orElse(null), + manifest.getAuthor().orElse(null), manifest.getCover() - .transform(c -> new JsonStickerPack.JsonSticker(c.getId(), + .map(c -> new JsonStickerPack.JsonSticker(c.getId(), c.getEmoji(), String.valueOf(c.getId()), c.getContentType())) - .orNull(), + .orElse(null), manifest.getStickers() .stream() .map(c -> new JsonStickerPack.JsonSticker(c.getId(), 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 b85d0050..c28eb613 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 @@ -13,7 +13,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.storage.SignalAccountRecord; import org.whispersystems.signalservice.api.storage.SignalStorageManifest; import org.whispersystems.signalservice.api.storage.SignalStorageRecord; @@ -25,6 +24,7 @@ import java.io.IOException; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Optional; public class StorageHelper { @@ -51,7 +51,7 @@ public class StorageHelper { return; } - if (!manifest.isPresent()) { + if (manifest.isEmpty()) { logger.debug("Manifest is up to date, does not exist or couldn't be decrypted, ignoring."); return; } @@ -79,7 +79,7 @@ public class StorageHelper { } private void readContactRecord(final SignalStorageRecord record) { - if (record == null || !record.getContact().isPresent()) { + if (record == null || record.getContact().isEmpty()) { return; } @@ -92,9 +92,9 @@ public class StorageHelper { (contact == null || !contact.isBlocked()) && contactRecord.isBlocked() )) { final var newContact = (contact == null ? Contact.newBuilder() : Contact.newBuilder(contact)).withBlocked( - contactRecord.isBlocked()) - .withName((contactRecord.getGivenName().or("") + " " + contactRecord.getFamilyName().or("")).trim()) - .build(); + contactRecord.isBlocked()).withName(( + contactRecord.getGivenName().orElse("") + " " + contactRecord.getFamilyName().orElse("") + ).trim()).build(); account.getContactStore().storeContact(recipientId, newContact); } @@ -122,7 +122,7 @@ public class StorageHelper { } private void readGroupV1Record(final SignalStorageRecord record) { - if (record == null || !record.getGroupV1().isPresent()) { + if (record == null || record.getGroupV1().isEmpty()) { return; } @@ -145,7 +145,7 @@ public class StorageHelper { } private void readGroupV2Record(final SignalStorageRecord record) { - if (record == null || !record.getGroupV2().isPresent()) { + if (record == null || record.getGroupV2().isEmpty()) { return; } @@ -171,7 +171,7 @@ public class StorageHelper { private void readAccountRecord(final SignalStorageManifest manifest) throws IOException { Optional accountId = manifest.getAccountStorageId(); - if (!accountId.isPresent()) { + if (accountId.isEmpty()) { logger.warn("Manifest has no account record, ignoring."); return; } @@ -182,7 +182,7 @@ public class StorageHelper { return; } - SignalAccountRecord accountRecord = record.getAccount().orNull(); + SignalAccountRecord accountRecord = record.getAccount().orElse(null); if (accountRecord == null) { logger.warn("The storage record didn't actually have an account, ignoring."); return; @@ -217,15 +217,15 @@ public class StorageHelper { } if (profileKey != null) { account.setProfileKey(profileKey); - final var avatarPath = accountRecord.getAvatarUrlPath().orNull(); + final var avatarPath = accountRecord.getAvatarUrlPath().orElse(null); context.getProfileHelper().downloadProfileAvatar(account.getSelfRecipientId(), avatarPath, profileKey); } } context.getProfileHelper() .setProfile(false, - accountRecord.getGivenName().orNull(), - accountRecord.getFamilyName().orNull(), + accountRecord.getGivenName().orElse(null), + accountRecord.getFamilyName().orElse(null), null, null, null); 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 9d987984..67afed1a 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 @@ -12,7 +12,6 @@ import org.asamk.signal.manager.util.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; @@ -37,6 +36,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.util.ArrayList; +import java.util.Optional; public class SyncHelper { @@ -81,7 +81,7 @@ public class SyncHelper { for (var record : account.getGroupStore().getGroups()) { if (record instanceof GroupInfoV1 groupInfo) { out.write(new DeviceGroup(groupInfo.getGroupId().serialize(), - Optional.fromNullable(groupInfo.name), + Optional.ofNullable(groupInfo.name), groupInfo.getMembers() .stream() .map(context.getRecipientHelper()::resolveSignalServiceAddress) @@ -89,9 +89,9 @@ public class SyncHelper { context.getGroupHelper().createGroupAvatarAttachment(groupInfo.getGroupId()), groupInfo.isMember(account.getSelfRecipientId()), Optional.of(groupInfo.messageExpirationTime), - Optional.fromNullable(groupInfo.color), + Optional.ofNullable(groupInfo.color), groupInfo.blocked, - Optional.absent(), + Optional.empty(), groupInfo.archived)); } } @@ -139,28 +139,28 @@ public class SyncHelper { var profileKey = account.getProfileStore().getProfileKey(recipientId); out.write(new DeviceContact(address, - Optional.fromNullable(contact.getName()), + Optional.ofNullable(contact.getName()), createContactAvatarAttachment(new RecipientAddress(address)), - Optional.fromNullable(contact.getColor()), - Optional.fromNullable(verifiedMessage), - Optional.fromNullable(profileKey), + Optional.ofNullable(contact.getColor()), + Optional.ofNullable(verifiedMessage), + Optional.ofNullable(profileKey), contact.isBlocked(), Optional.of(contact.getMessageExpirationTime()), - Optional.absent(), + Optional.empty(), contact.isArchived())); } if (account.getProfileKey() != null) { // Send our own profile key as well out.write(new DeviceContact(account.getSelfAddress(), - Optional.absent(), - Optional.absent(), - Optional.absent(), - Optional.absent(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), Optional.of(account.getProfileKey()), false, - Optional.absent(), - Optional.absent(), + Optional.empty(), + Optional.empty(), false)); } } @@ -215,16 +215,16 @@ public class SyncHelper { } public void sendKeysMessage() { - var keysMessage = new KeysMessage(Optional.fromNullable(account.getStorageKey())); + var keysMessage = new KeysMessage(Optional.ofNullable(account.getStorageKey())); context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forKeys(keysMessage)); } public void sendConfigurationMessage() { final var config = account.getConfigurationStore(); - var configurationMessage = new ConfigurationMessage(Optional.fromNullable(config.getReadReceipts()), - Optional.fromNullable(config.getUnidentifiedDeliveryIndicators()), - Optional.fromNullable(config.getTypingIndicators()), - Optional.fromNullable(config.getLinkPreviews())); + var configurationMessage = new ConfigurationMessage(Optional.ofNullable(config.getReadReceipts()), + Optional.ofNullable(config.getUnidentifiedDeliveryIndicators()), + Optional.ofNullable(config.getTypingIndicators()), + Optional.ofNullable(config.getLinkPreviews())); context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forConfiguration(configurationMessage)); } @@ -299,10 +299,10 @@ public class SyncHelper { private Optional createContactAvatarAttachment(RecipientAddress address) throws IOException { final var streamDetails = context.getAvatarStore().retrieveContactAvatar(address); if (streamDetails == null) { - return Optional.absent(); + return Optional.empty(); } - return Optional.of(AttachmentUtils.createAttachment(streamDetails, Optional.absent())); + return Optional.of(AttachmentUtils.createAttachment(streamDetails, Optional.empty())); } private void downloadContactAvatar(SignalServiceAttachment avatar, RecipientAddress address) { 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 36e18939..b2b5fc55 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 @@ -10,12 +10,12 @@ import org.signal.libsignal.metadata.certificate.SenderCertificate; import org.signal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import java.io.IOException; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; public class UnidentifiedAccessHelper { @@ -54,26 +54,26 @@ public class UnidentifiedAccessHelper { var recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipientId, noRefresh); if (recipientUnidentifiedAccessKey == null) { logger.trace("Unidentified access not available for {}", recipientId); - return Optional.absent(); + return Optional.empty(); } var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(noRefresh); if (selfUnidentifiedAccessKey == null) { logger.trace("Unidentified access not available for self"); - return Optional.absent(); + return Optional.empty(); } var senderCertificate = getSenderCertificateFor(recipientId); if (senderCertificate == null) { logger.trace("Unidentified access not available due to missing sender certificate"); - return Optional.absent(); + return Optional.empty(); } try { return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(recipientUnidentifiedAccessKey, senderCertificate), new UnidentifiedAccess(selfUnidentifiedAccessKey, senderCertificate))); } catch (InvalidCertificateException e) { - return Optional.absent(); + return Optional.empty(); } } @@ -82,7 +82,7 @@ public class UnidentifiedAccessHelper { var selfUnidentifiedAccessCertificate = getSenderCertificate(); if (selfUnidentifiedAccessKey == null || selfUnidentifiedAccessCertificate == null) { - return Optional.absent(); + return Optional.empty(); } try { @@ -90,7 +90,7 @@ public class UnidentifiedAccessHelper { selfUnidentifiedAccessCertificate), new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate))); } catch (InvalidCertificateException e) { - return Optional.absent(); + return Optional.empty(); } } 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 d2723e38..a6593fd9 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 @@ -705,9 +705,6 @@ public class SignalAccount implements Closeable { if (profile.getCapabilities().gv1Migration) { capabilities.add(Profile.Capability.gv1Migration); } - if (profile.getCapabilities().gv2) { - capabilities.add(Profile.Capability.gv2); - } if (profile.getCapabilities().storage) { capabilities.add(Profile.Capability.storage); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java index 60d9aefc..ed5f0160 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java @@ -88,9 +88,6 @@ public class SignalProfile { @JsonIgnore public boolean uuid; - @JsonProperty - public boolean gv2; - @JsonProperty public boolean storage; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java index 44ee5f6d..dc3aaf53 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java @@ -142,7 +142,6 @@ public class Profile { } public enum Capability { - gv2, storage, gv1Migration, senderKey, diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java index 69e2537d..0ab8c8f5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java @@ -28,7 +28,7 @@ public record RecipientAddress(Optional uuid, Optional number) { } public RecipientAddress(SignalServiceAddress address) { - this(Optional.of(address.getServiceId().uuid()), Optional.ofNullable(address.getNumber().orNull())); + this(Optional.of(address.getServiceId().uuid()), address.getNumber()); } public RecipientAddress(UUID uuid) { @@ -62,7 +62,6 @@ public record RecipientAddress(Optional uuid, Optional number) { } public SignalServiceAddress toSignalServiceAddress() { - return new SignalServiceAddress(ServiceId.from(uuid.orElse(UNKNOWN_UUID)), - org.whispersystems.libsignal.util.guava.Optional.fromNullable(number.orElse(null))); + return new SignalServiceAddress(ServiceId.from(uuid.orElse(UNKNOWN_UUID)), number); } } 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 793e452e..a19a6fcf 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 @@ -1,7 +1,6 @@ package org.asamk.signal.manager.util; import org.asamk.signal.manager.api.AttachmentInvalidException; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; import org.whispersystems.signalservice.api.util.StreamDetails; @@ -11,6 +10,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; public class AttachmentUtils { @@ -39,10 +39,10 @@ public class AttachmentUtils { ) { // TODO mabybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option final var uploadTimestamp = System.currentTimeMillis(); - Optional preview = Optional.absent(); - Optional caption = Optional.absent(); - Optional blurHash = Optional.absent(); - final Optional resumableUploadSpec = Optional.absent(); + Optional preview = Optional.empty(); + Optional caption = Optional.empty(); + Optional blurHash = Optional.empty(); + final Optional resumableUploadSpec = Optional.empty(); return new SignalServiceAttachmentStream(streamDetails.getStream(), streamDetails.getContentType(), streamDetails.getLength(), diff --git a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java index 9d7e0c50..a1f19274 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java @@ -1,6 +1,5 @@ package org.asamk.signal.manager.util; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -11,6 +10,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.util.Optional; public class MessageCacheUtils { @@ -59,7 +59,7 @@ public class MessageCacheUtils { serverDeliveredTimestamp = in.readLong(); } Optional addressOptional = sourceServiceId == null - ? Optional.absent() + ? Optional.empty() : Optional.of(new SignalServiceAddress(sourceServiceId, source)); return new SignalServiceEnvelope(type, addressOptional, 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 1874bab0..2f8981e3 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 @@ -5,7 +5,6 @@ import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.helper.PinHelper; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.KbsPinData; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.kbs.MasterKey; @@ -15,6 +14,7 @@ import org.whispersystems.signalservice.internal.push.RequestVerificationCodeRes import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; import java.io.IOException; +import java.util.Optional; public class NumberVerificationUtils { @@ -26,14 +26,14 @@ public class NumberVerificationUtils { final ServiceResponse response; if (voiceVerification) { response = accountManager.requestVoiceVerificationCode(Utils.getDefaultLocale(null), - Optional.fromNullable(captcha), - Optional.absent(), - Optional.absent()); + Optional.ofNullable(captcha), + Optional.empty(), + Optional.empty()); } else { response = accountManager.requestSmsVerificationCode(false, - Optional.fromNullable(captcha), - Optional.absent(), - Optional.absent()); + Optional.ofNullable(captcha), + Optional.empty(), + Optional.empty()); } try { handleResponseException(response); @@ -82,7 +82,7 @@ public class NumberVerificationUtils { } private static void handleResponseException(final ServiceResponse response) throws IOException { - final var throwableOptional = response.getExecutionError().or(response.getApplicationError()); + final var throwableOptional = response.getExecutionError().or(response::getApplicationError); if (throwableOptional.isPresent()) { if (throwableOptional.get() instanceof IOException) { throw (IOException) throwableOptional.get(); 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 c62c1d4c..5510eeeb 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 @@ -62,9 +62,6 @@ public class ProfileUtils { if (encryptedProfile.getCapabilities().isGv1Migration()) { capabilities.add(Profile.Capability.gv1Migration); } - if (encryptedProfile.getCapabilities().isGv2()) { - capabilities.add(Profile.Capability.gv2); - } if (encryptedProfile.getCapabilities().isStorage()) { capabilities.add(Profile.Capability.storage); } 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 c914db47..f3825cc4 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 @@ -2,10 +2,9 @@ package org.asamk.signal.manager.util; import com.fasterxml.jackson.databind.ObjectMapper; -import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; -import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifestUpload; import java.io.BufferedInputStream; @@ -15,6 +14,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URLConnection; import java.util.ArrayList; +import java.util.Optional; import java.util.zip.ZipFile; public class StickerUtils { @@ -61,7 +61,7 @@ public class StickerUtils { : getContentType(rootPath, zip, sticker.file()); var stickerInfo = new SignalServiceStickerManifestUpload.StickerInfo(data.first(), data.second(), - Optional.fromNullable(sticker.emoji()).or(""), + Optional.ofNullable(sticker.emoji()).orElse(""), contentType); stickers.add(stickerInfo); } @@ -83,7 +83,7 @@ public class StickerUtils { .contentType() : getContentType(rootPath, zip, pack.cover().file()); cover = new SignalServiceStickerManifestUpload.StickerInfo(data.first(), data.second(), - Optional.fromNullable(pack.cover().emoji()).or(""), + Optional.ofNullable(pack.cover().emoji()).orElse(""), contentType); } 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 535c157d..49f474ed 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 @@ -70,7 +70,7 @@ public class Utils { } else { // Version 1: E164 user version = 1; - if (!ownAddress.getNumber().isPresent() || !theirAddress.getNumber().isPresent()) { + if (ownAddress.getNumber().isEmpty() || theirAddress.getNumber().isEmpty()) { return null; } ownId = ownAddress.getNumber().get().getBytes(); @@ -102,7 +102,7 @@ public class Utils { public static Stream zip(Stream leftStream, Stream rightStream, BiFunction combiner) { Spliterator lefts = leftStream.spliterator(); Spliterator rights = rightStream.spliterator(); - return StreamSupport.stream(new Spliterators.AbstractSpliterator(Long.min(lefts.estimateSize(), + return StreamSupport.stream(new Spliterators.AbstractSpliterator<>(Long.min(lefts.estimateSize(), rights.estimateSize()), lefts.characteristics() & rights.characteristics()) { @Override public boolean tryAdvance(Consumer action) { diff --git a/man/signal-cli-dbus.5.adoc b/man/signal-cli-dbus.5.adoc index a475eadc..a2738337 100644 --- a/man/signal-cli-dbus.5.adoc +++ b/man/signal-cli-dbus.5.adoc @@ -67,9 +67,10 @@ This can be piped to a QR encoder to create a display that can be captured by a For example: -``` +[source] +---- dbus-send --session --dest=org.asamk.Signal --type=method_call --print-reply /org/asamk/Signal org.asamk.Signal.link string:"My secondary client" | tr '\n' '\0' | sed 's/.*string //g' | sed 's/\"//g' | qrencode -s10 -tANSI256 -``` +---- listAccounts() -> accountList:: * accountList : Array of all attached accounts in DBus object path form diff --git a/src/main/java/org/asamk/signal/dbus/DbusProperties.java b/src/main/java/org/asamk/signal/dbus/DbusProperties.java index bbe01d6b..256b858d 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusProperties.java +++ b/src/main/java/org/asamk/signal/dbus/DbusProperties.java @@ -7,6 +7,7 @@ import org.freedesktop.dbus.types.Variant; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; public abstract class DbusProperties implements Properties { @@ -25,7 +26,7 @@ public abstract class DbusProperties implements Properties { return handler.get(); } - private java.util.Optional getHandlerOptional(final String interfaceName) { + private Optional getHandlerOptional(final String interfaceName) { return handlers.stream().filter(h -> h.getInterfaceName().equals(interfaceName)).findFirst(); } From b5d578e483abeda7bb5683b674f3fba17fb279bc Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 27 Mar 2022 18:56:36 +0200 Subject: [PATCH 064/938] Update graalvm build tools plugin --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 99de4644..d4bb65e7 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.9.10" + id("org.graalvm.buildtools.native") version "0.9.11" } version = "0.10.4.2" From 86e5ef73827ba6d5a89f5f194dd4c45673d0ba76 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 27 Mar 2022 18:57:21 +0200 Subject: [PATCH 065/938] Fix plain text output of blocked group ids --- src/main/java/org/asamk/signal/ReceiveMessageHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index f5fce554..3351d666 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -1,9 +1,9 @@ package org.asamk.signal; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.RecipientIdentifier; +import org.asamk.signal.manager.api.UntrustedIdentityException; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.output.PlainTextWriter; @@ -309,7 +309,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { writer.println("- {}", address.getLegacyIdentifier()); } for (var groupId : blockedList.groupIds()) { - writer.println("- {}", groupId); + writer.println("- {}", groupId.toBase64()); } } if (syncMessage.viewOnceOpen().isPresent()) { From d27a12a6cfcf9afca362a8d359ee808b1507e0b3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 29 Mar 2022 21:37:06 +0200 Subject: [PATCH 066/938] Update libsignal-service-java --- graalvm-config-dir/jni-config.json | 208 +++++++++--------- graalvm-config-dir/reflect-config.json | 96 ++++---- lib/build.gradle.kts | 2 +- .../asamk/signal/manager/DeviceLinkInfo.java | 6 +- .../asamk/signal/manager/LibSignalLogger.java | 4 +- .../manager/ProvisioningManagerImpl.java | 7 +- .../manager/RegistrationManagerImpl.java | 3 +- .../signal/manager/SignalAccountFiles.java | 2 +- .../signal/manager/SignalDependencies.java | 11 +- .../SendRetryMessageRequestAction.java | 4 +- .../asamk/signal/manager/api/Identity.java | 2 +- .../signal/manager/api/SendMessageResult.java | 2 +- .../signal/manager/config/LiveConfig.java | 6 +- .../signal/manager/config/ServiceConfig.java | 3 +- .../config/ServiceEnvironmentConfig.java | 2 +- .../signal/manager/config/StagingConfig.java | 6 +- .../manager/groups/GroupInviteLinkUrl.java | 4 +- .../signal/manager/groups/GroupUtils.java | 8 +- .../signal/manager/helper/AccountHelper.java | 2 +- .../manager/helper/AttachmentHelper.java | 2 +- .../signal/manager/helper/GroupHelper.java | 8 +- .../signal/manager/helper/GroupV2Helper.java | 15 +- .../signal/manager/helper/IdentityHelper.java | 10 +- .../helper/IncomingMessageHandler.java | 8 +- .../signal/manager/helper/PinHelper.java | 2 +- .../signal/manager/helper/PreKeyHelper.java | 6 +- .../signal/manager/helper/ProfileHelper.java | 8 +- .../manager/helper/RecipientHelper.java | 2 +- .../signal/manager/helper/SendHelper.java | 10 +- .../signal/manager/helper/StickerHelper.java | 2 +- .../signal/manager/helper/StorageHelper.java | 10 +- .../signal/manager/helper/SyncHelper.java | 2 +- .../helper/UnidentifiedAccessHelper.java | 2 +- .../manager/jobs/RetrieveStickerPackJob.java | 2 +- .../signal/manager/storage/SignalAccount.java | 21 +- .../manager/storage/groups/GroupInfoV2.java | 2 +- .../manager/storage/groups/GroupStore.java | 4 +- .../storage/identities/IdentityInfo.java | 2 +- .../storage/identities/IdentityKeyStore.java | 10 +- .../manager/storage/prekeys/PreKeyStore.java | 9 +- .../storage/prekeys/SignedPreKeyStore.java | 9 +- .../storage/profiles/LegacyProfileStore.java | 6 +- .../profiles/LegacySignalProfileEntry.java | 4 +- .../storage/profiles/ProfileStore.java | 4 +- .../storage/protocol/LegacyIdentityInfo.java | 2 +- .../protocol/LegacyJsonIdentityKeyStore.java | 6 +- .../storage/protocol/SignalProtocolStore.java | 24 +- .../manager/storage/recipients/Recipient.java | 4 +- .../storage/recipients/RecipientStore.java | 6 +- .../storage/sendLog/MessageSendLogStore.java | 4 +- .../senderKeys/SenderKeyRecordStore.java | 9 +- .../senderKeys/SenderKeySharedStore.java | 2 +- .../storage/senderKeys/SenderKeyStore.java | 4 +- .../storage/sessions/SessionStore.java | 10 +- .../stickerPacks/StickerPackStore.java | 1 - .../asamk/signal/manager/util/KeyUtils.java | 20 +- .../signal/manager/util/ProfileUtils.java | 2 +- .../org/asamk/signal/manager/util/Utils.java | 6 +- .../signal/dbus/DbusSignalControlImpl.java | 2 +- 59 files changed, 334 insertions(+), 306 deletions(-) diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index 3ac74947..a852dc92 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -56,24 +56,121 @@ { "name":"org.asamk.signal.manager.storage.protocol.SignalProtocolStore", "methods":[ - {"name":"getIdentity","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress"] }, + {"name":"getIdentity","parameterTypes":["org.signal.libsignal.protocol.SignalProtocolAddress"] }, {"name":"getIdentityKeyPair","parameterTypes":[] }, {"name":"getLocalRegistrationId","parameterTypes":[] }, - {"name":"isTrustedIdentity","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.IdentityKey","org.whispersystems.libsignal.state.IdentityKeyStore$Direction"] }, + {"name":"isTrustedIdentity","parameterTypes":["org.signal.libsignal.protocol.SignalProtocolAddress","org.signal.libsignal.protocol.IdentityKey","org.signal.libsignal.protocol.state.IdentityKeyStore$Direction"] }, {"name":"loadPreKey","parameterTypes":["int"] }, - {"name":"loadSenderKey","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","java.util.UUID"] }, - {"name":"loadSession","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress"] }, + {"name":"loadSenderKey","parameterTypes":["org.signal.libsignal.protocol.SignalProtocolAddress","java.util.UUID"] }, + {"name":"loadSession","parameterTypes":["org.signal.libsignal.protocol.SignalProtocolAddress"] }, {"name":"loadSignedPreKey","parameterTypes":["int"] }, {"name":"removePreKey","parameterTypes":["int"] }, - {"name":"saveIdentity","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.IdentityKey"] }, - {"name":"storeSenderKey","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","java.util.UUID","org.whispersystems.libsignal.groups.state.SenderKeyRecord"] }, - {"name":"storeSession","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.state.SessionRecord"] } + {"name":"saveIdentity","parameterTypes":["org.signal.libsignal.protocol.SignalProtocolAddress","org.signal.libsignal.protocol.IdentityKey"] }, + {"name":"storeSenderKey","parameterTypes":["org.signal.libsignal.protocol.SignalProtocolAddress","java.util.UUID","org.signal.libsignal.protocol.groups.state.SenderKeyRecord"] }, + {"name":"storeSession","parameterTypes":["org.signal.libsignal.protocol.SignalProtocolAddress","org.signal.libsignal.protocol.state.SessionRecord"] } ] }, { "name":"org.graalvm.jniutils.JNIExceptionWrapperEntryPoints", "methods":[{"name":"getClassName","parameterTypes":["java.lang.Class"] }] }, +{ + "name":"org.signal.libsignal.protocol.DuplicateMessageException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"org.signal.libsignal.protocol.IdentityKey", + "methods":[ + {"name":"","parameterTypes":["byte[]"] }, + {"name":"serialize","parameterTypes":[] } + ] +}, +{ + "name":"org.signal.libsignal.protocol.IdentityKeyPair", + "methods":[{"name":"serialize","parameterTypes":[] }] +}, +{ + "name":"org.signal.libsignal.protocol.InvalidKeyException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"org.signal.libsignal.protocol.InvalidKeyIdException" +}, +{ + "name":"org.signal.libsignal.protocol.InvalidMessageException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"org.signal.libsignal.protocol.SignalProtocolAddress", + "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }] +}, +{ + "name":"org.signal.libsignal.protocol.UntrustedIdentityException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"org.signal.libsignal.protocol.groups.state.SenderKeyRecord", + "fields":[{"name":"unsafeHandle"}], + "methods":[{"name":"","parameterTypes":["long"] }] +}, +{ + "name":"org.signal.libsignal.protocol.groups.state.SenderKeyStore" +}, +{ + "name":"org.signal.libsignal.protocol.logging.Log", + "methods":[{"name":"log","parameterTypes":["int","java.lang.String","java.lang.String"] }] +}, +{ + "name":"org.signal.libsignal.protocol.message.PlaintextContent", + "fields":[{"name":"unsafeHandle"}] +}, +{ + "name":"org.signal.libsignal.protocol.message.PreKeySignalMessage", + "fields":[{"name":"unsafeHandle"}], + "methods":[{"name":"","parameterTypes":["long"] }] +}, +{ + "name":"org.signal.libsignal.protocol.message.SenderKeyMessage", + "fields":[{"name":"unsafeHandle"}], + "methods":[{"name":"","parameterTypes":["long"] }] +}, +{ + "name":"org.signal.libsignal.protocol.message.SignalMessage", + "fields":[{"name":"unsafeHandle"}], + "methods":[{"name":"","parameterTypes":["long"] }] +}, +{ + "name":"org.signal.libsignal.protocol.state.IdentityKeyStore" +}, +{ + "name":"org.signal.libsignal.protocol.state.IdentityKeyStore$Direction", + "fields":[ + {"name":"RECEIVING"}, + {"name":"SENDING"} + ] +}, +{ + "name":"org.signal.libsignal.protocol.state.PreKeyRecord", + "fields":[{"name":"unsafeHandle"}] +}, +{ + "name":"org.signal.libsignal.protocol.state.PreKeyStore" +}, +{ + "name":"org.signal.libsignal.protocol.state.SessionRecord", + "fields":[{"name":"unsafeHandle"}], + "methods":[{"name":"","parameterTypes":["byte[]"] }] +}, +{ + "name":"org.signal.libsignal.protocol.state.SessionStore" +}, +{ + "name":"org.signal.libsignal.protocol.state.SignedPreKeyRecord", + "fields":[{"name":"unsafeHandle"}] +}, +{ + "name":"org.signal.libsignal.protocol.state.SignedPreKeyStore" +}, { "name":"org.sqlite.Collation" }, @@ -103,102 +200,5 @@ {"name":"pointer"}, {"name":"udfdatalist"} ] -}, -{ - "name":"org.whispersystems.libsignal.DuplicateMessageException", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }] -}, -{ - "name":"org.whispersystems.libsignal.IdentityKey", - "methods":[ - {"name":"","parameterTypes":["byte[]"] }, - {"name":"serialize","parameterTypes":[] } - ] -}, -{ - "name":"org.whispersystems.libsignal.IdentityKeyPair", - "methods":[{"name":"serialize","parameterTypes":[] }] -}, -{ - "name":"org.whispersystems.libsignal.InvalidKeyException", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }] -}, -{ - "name":"org.whispersystems.libsignal.InvalidKeyIdException" -}, -{ - "name":"org.whispersystems.libsignal.InvalidMessageException", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }] -}, -{ - "name":"org.whispersystems.libsignal.SignalProtocolAddress", - "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }] -}, -{ - "name":"org.whispersystems.libsignal.UntrustedIdentityException", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }] -}, -{ - "name":"org.whispersystems.libsignal.groups.state.SenderKeyRecord", - "fields":[{"name":"unsafeHandle"}], - "methods":[{"name":"","parameterTypes":["long"] }] -}, -{ - "name":"org.whispersystems.libsignal.groups.state.SenderKeyStore" -}, -{ - "name":"org.whispersystems.libsignal.logging.Log", - "methods":[{"name":"log","parameterTypes":["int","java.lang.String","java.lang.String"] }] -}, -{ - "name":"org.whispersystems.libsignal.protocol.PlaintextContent", - "fields":[{"name":"unsafeHandle"}] -}, -{ - "name":"org.whispersystems.libsignal.protocol.PreKeySignalMessage", - "fields":[{"name":"unsafeHandle"}], - "methods":[{"name":"","parameterTypes":["long"] }] -}, -{ - "name":"org.whispersystems.libsignal.protocol.SenderKeyMessage", - "fields":[{"name":"unsafeHandle"}], - "methods":[{"name":"","parameterTypes":["long"] }] -}, -{ - "name":"org.whispersystems.libsignal.protocol.SignalMessage", - "fields":[{"name":"unsafeHandle"}], - "methods":[{"name":"","parameterTypes":["long"] }] -}, -{ - "name":"org.whispersystems.libsignal.state.IdentityKeyStore" -}, -{ - "name":"org.whispersystems.libsignal.state.IdentityKeyStore$Direction", - "fields":[ - {"name":"RECEIVING"}, - {"name":"SENDING"} - ] -}, -{ - "name":"org.whispersystems.libsignal.state.PreKeyRecord", - "fields":[{"name":"unsafeHandle"}] -}, -{ - "name":"org.whispersystems.libsignal.state.PreKeyStore" -}, -{ - "name":"org.whispersystems.libsignal.state.SessionRecord", - "fields":[{"name":"unsafeHandle"}], - "methods":[{"name":"","parameterTypes":["byte[]"] }] -}, -{ - "name":"org.whispersystems.libsignal.state.SessionStore" -}, -{ - "name":"org.whispersystems.libsignal.state.SignedPreKeyRecord", - "fields":[{"name":"unsafeHandle"}] -}, -{ - "name":"org.whispersystems.libsignal.state.SignedPreKeyStore" } ] diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 58cf72ed..f4c809ec 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -1553,6 +1553,43 @@ "name":"org.freedesktop.dbus.interfaces.Properties$PropertiesChanged", "allPublicConstructors":true }, +{ + "name":"org.signal.libsignal.protocol.state.IdentityKeyStore", + "allDeclaredMethods":true +}, +{ + "name":"org.signal.libsignal.protocol.state.PreKeyStore", + "allDeclaredMethods":true +}, +{ + "name":"org.signal.libsignal.protocol.state.SessionStore", + "allDeclaredMethods":true +}, +{ + "name":"org.signal.libsignal.protocol.state.SignalProtocolStore", + "allDeclaredMethods":true +}, +{ + "name":"org.signal.libsignal.protocol.state.SignedPreKeyStore", + "allDeclaredMethods":true +}, +{ + "name":"org.signal.libsignal.zkgroup.internal.ByteArray", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"org.signal.libsignal.zkgroup.profiles.ProfileKey", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, { "name":"org.signal.storageservice.protos.groups.AccessControl", "fields":[ @@ -1575,7 +1612,10 @@ }, { "name":"org.signal.storageservice.protos.groups.BannedMember", - "fields":[{"name":"userId_"}] + "fields":[ + {"name":"timestamp_"}, + {"name":"userId_"} + ] }, { "name":"org.signal.storageservice.protos.groups.Group", @@ -1794,7 +1834,10 @@ }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedBannedMember", - "fields":[{"name":"uuid_"}] + "fields":[ + {"name":"timestamp_"}, + {"name":"uuid_"} + ] }, { "name":"org.signal.storageservice.protos.groups.local.DecryptedGroup", @@ -1903,46 +1946,9 @@ "name":"org.signal.storageservice.protos.groups.local.DecryptedTimer", "fields":[{"name":"duration_"}] }, -{ - "name":"org.signal.zkgroup.internal.ByteArray", - "allDeclaredFields":true, - "queryAllDeclaredMethods":true -}, -{ - "name":"org.signal.zkgroup.profiles.ProfileKey", - "allDeclaredFields":true, - "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true -}, -{ - "name":"org.signal.zkgroup.profiles.ProfileKeyCredential", - "allDeclaredFields":true, - "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true -}, { "name":"org.sqlite.JDBC" }, -{ - "name":"org.whispersystems.libsignal.state.IdentityKeyStore", - "allDeclaredMethods":true -}, -{ - "name":"org.whispersystems.libsignal.state.PreKeyStore", - "allDeclaredMethods":true -}, -{ - "name":"org.whispersystems.libsignal.state.SessionStore", - "allDeclaredMethods":true -}, -{ - "name":"org.whispersystems.libsignal.state.SignalProtocolStore", - "allDeclaredMethods":true -}, -{ - "name":"org.whispersystems.libsignal.state.SignedPreKeyStore", - "allDeclaredMethods":true -}, { "name":"org.whispersystems.signalservice.api.account.AccountAttributes", "allDeclaredFields":true, @@ -2793,6 +2799,17 @@ {"name":"type_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$StoryMessage", + "fields":[ + {"name":"allowsReplies_"}, + {"name":"attachmentCase_"}, + {"name":"attachment_"}, + {"name":"bitField0_"}, + {"name":"group_"}, + {"name":"profileKey_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage", "fields":[ @@ -2806,6 +2823,7 @@ {"name":"messageRequestResponse_"}, {"name":"outgoingPayment_"}, {"name":"padding_"}, + {"name":"pniIdentity_"}, {"name":"read_"}, {"name":"request_"}, {"name":"sent_"}, diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index ee2ace73..36247362 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_45") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_46") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.1") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") diff --git a/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java b/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java index 7c15131e..356cb47d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java @@ -2,9 +2,9 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.util.Utils; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.signal.libsignal.protocol.InvalidKeyException; +import org.signal.libsignal.protocol.ecc.Curve; +import org.signal.libsignal.protocol.ecc.ECPublicKey; import java.net.URI; import java.net.URISyntaxException; diff --git a/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java b/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java index ac686fc5..7942c194 100644 --- a/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java +++ b/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java @@ -1,9 +1,9 @@ package org.asamk.signal.manager; +import org.signal.libsignal.protocol.logging.SignalProtocolLogger; +import org.signal.libsignal.protocol.logging.SignalProtocolLoggerProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.logging.SignalProtocolLogger; -import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider; public class LibSignalLogger implements SignalProtocolLogger { diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java index 75163555..7519950e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java @@ -23,10 +23,10 @@ import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.accounts.AccountsStore; import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.manager.util.KeyUtils; +import org.signal.libsignal.protocol.IdentityKeyPair; +import org.signal.libsignal.protocol.util.KeyHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.util.KeyHelper; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; @@ -74,7 +74,8 @@ class ProvisioningManagerImpl implements ProvisioningManager { password = KeyUtils.createPassword(); GroupsV2Operations groupsV2Operations; try { - groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())); + groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()), + ServiceConfig.GROUP_MAX_SIZE); } catch (Throwable ignored) { groupsV2Operations = null; } diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index bc543d8d..133bd8b8 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -73,7 +73,8 @@ class RegistrationManagerImpl implements RegistrationManager { GroupsV2Operations groupsV2Operations; try { - groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())); + groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()), + ServiceConfig.GROUP_MAX_SIZE); } catch (Throwable ignored) { groupsV2Operations = null; } 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 a6614460..0ca9b0ab 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -9,9 +9,9 @@ import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.accounts.AccountsStore; import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.manager.util.KeyUtils; +import org.signal.libsignal.protocol.util.KeyHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.util.KeyHelper; import java.io.File; import java.io.IOException; diff --git a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java index 4db6eddf..aeef7f15 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalDependencies.java @@ -3,7 +3,7 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.signal.libsignal.metadata.certificate.CertificateValidator; -import org.signal.zkgroup.profiles.ClientZkProfileOperations; +import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceDataStore; @@ -94,7 +94,8 @@ public class SignalDependencies { SignalServiceAddress.DEFAULT_DEVICE_ID, password, userAgent, - ServiceConfig.AUTOMATIC_NETWORK_RETRY); + ServiceConfig.AUTOMATIC_NETWORK_RETRY, + ServiceConfig.GROUP_MAX_SIZE); } public GroupsV2Api getGroupsV2Api() { @@ -103,8 +104,10 @@ public class SignalDependencies { public GroupsV2Operations getGroupsV2Operations() { return getOrCreate(() -> groupsV2Operations, - () -> groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( - serviceEnvironmentConfig.getSignalServiceConfiguration())) : null); + () -> groupsV2Operations = capabilities.isGv2() + ? new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()), + ServiceConfig.GROUP_MAX_SIZE) + : null); } private ClientZkOperations getClientZkOperations() { 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 144fba4a..4ebbe99e 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 @@ -4,8 +4,8 @@ import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.helper.Context; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.signal.libsignal.metadata.ProtocolException; -import org.whispersystems.libsignal.protocol.CiphertextMessage; -import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; +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.internal.push.SignalServiceProtos; diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Identity.java b/lib/src/main/java/org/asamk/signal/manager/api/Identity.java index 65d86d0a..8785df76 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/Identity.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/Identity.java @@ -1,7 +1,7 @@ package org.asamk.signal.manager.api; import org.asamk.signal.manager.storage.recipients.RecipientAddress; -import org.whispersystems.libsignal.IdentityKey; +import org.signal.libsignal.protocol.IdentityKey; import java.util.Date; diff --git a/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java b/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java index 1d38e9d9..78883860 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java @@ -3,7 +3,7 @@ package org.asamk.signal.manager.api; import org.asamk.signal.manager.helper.RecipientAddressResolver; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientResolver; -import org.whispersystems.libsignal.IdentityKey; +import org.signal.libsignal.protocol.IdentityKey; public record SendMessageResult( RecipientAddress address, 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 5890b6a3..9c15bc71 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 @@ -1,9 +1,9 @@ package org.asamk.signal.manager.config; import org.bouncycastle.util.encoders.Hex; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; +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.SignalCdnUrl; import org.whispersystems.signalservice.internal.configuration.SignalCdshUrl; 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 3a544318..de248755 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 @@ -24,6 +24,7 @@ public class ServiceConfig { public final static long MAX_ENVELOPE_SIZE = 0; public final static long AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE = 10 * 1024 * 1024; public final static boolean AUTOMATIC_NETWORK_RETRY = true; + public final static int GROUP_MAX_SIZE = 1001; private final static KeyStore iasKeyStore; @@ -48,7 +49,7 @@ public class ServiceConfig { public static boolean isSignalClientAvailable() { try { try { - org.signal.client.internal.Native.UuidCiphertext_CheckValidContents(new byte[0]); + org.signal.libsignal.internal.Native.UuidCiphertext_CheckValidContents(new byte[0]); } catch (IllegalArgumentException ignored) { } return true; diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java index e64472a0..9dac7edd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java @@ -1,6 +1,6 @@ package org.asamk.signal.manager.config; -import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; public class ServiceEnvironmentConfig { 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 ed53d8d8..bba5b333 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 @@ -1,9 +1,9 @@ package org.asamk.signal.manager.config; import org.bouncycastle.util.encoders.Hex; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; +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.SignalCdnUrl; import org.whispersystems.signalservice.internal.configuration.SignalCdshUrl; diff --git a/lib/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java index 53e12d8e..3425d95e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java +++ b/lib/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java @@ -2,10 +2,10 @@ package org.asamk.signal.manager.groups; import com.google.protobuf.ByteString; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.signal.storageservice.protos.groups.GroupInviteLink; import org.signal.storageservice.protos.groups.local.DecryptedGroup; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.groups.GroupMasterKey; import org.whispersystems.util.Base64UrlSafe; import java.io.IOException; 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 69e348b0..356fb984 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 @@ -3,10 +3,10 @@ package org.asamk.signal.manager.groups; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.GroupInfoV2; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.groups.GroupMasterKey; -import org.signal.zkgroup.groups.GroupSecretParams; -import org.whispersystems.libsignal.kdf.HKDF; +import org.signal.libsignal.protocol.kdf.HKDF; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.groups.GroupMasterKey; +import org.signal.libsignal.zkgroup.groups.GroupSecretParams; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext; 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 328074b7..26247b09 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 @@ -10,9 +10,9 @@ import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.NumberVerificationUtils; +import org.signal.libsignal.protocol.InvalidKeyException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.PNI; import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; 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 b7a34152..bad3d788 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 @@ -6,9 +6,9 @@ import org.asamk.signal.manager.api.AttachmentInvalidException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.IOUtils; +import org.signal.libsignal.protocol.InvalidMessageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; 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 e40dbdfb..b730fe2d 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 @@ -25,13 +25,13 @@ import org.asamk.signal.manager.storage.groups.GroupInfoV2; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.IOUtils; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.groups.GroupMasterKey; +import org.signal.libsignal.zkgroup.groups.GroupSecretParams; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.groups.GroupMasterKey; -import org.signal.zkgroup.groups.GroupSecretParams; -import org.signal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; 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 92da383e..49582ac9 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 @@ -13,6 +13,12 @@ import org.asamk.signal.manager.storage.groups.GroupInfoV2; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.Utils; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.VerificationFailedException; +import org.signal.libsignal.zkgroup.auth.AuthCredentialResponse; +import org.signal.libsignal.zkgroup.groups.GroupMasterKey; +import org.signal.libsignal.zkgroup.groups.GroupSecretParams; +import org.signal.libsignal.zkgroup.groups.UuidCiphertext; import org.signal.storageservice.protos.groups.AccessControl; import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.protos.groups.Member; @@ -20,12 +26,6 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.DecryptedGroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; import org.signal.storageservice.protos.groups.local.DecryptedPendingMember; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.VerificationFailedException; -import org.signal.zkgroup.auth.AuthCredentialResponse; -import org.signal.zkgroup.groups.GroupMasterKey; -import org.signal.zkgroup.groups.GroupSecretParams; -import org.signal.zkgroup.groups.UuidCiphertext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil; @@ -46,6 +46,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -409,7 +410,7 @@ class GroupV2Helper { GroupInfoV2 groupInfoV2, Set uuids ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); - return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(uuids, false)); + return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(uuids, false, List.of())); } private Pair commitChange( 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 283e756f..ee74338e 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 @@ -4,13 +4,13 @@ import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.util.Utils; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.fingerprint.Fingerprint; +import org.signal.libsignal.protocol.fingerprint.FingerprintParsingException; +import org.signal.libsignal.protocol.fingerprint.FingerprintVersionMismatchException; +import org.signal.libsignal.protocol.fingerprint.ScannableFingerprint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.fingerprint.Fingerprint; -import org.whispersystems.libsignal.fingerprint.FingerprintParsingException; -import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException; -import org.whispersystems.libsignal.fingerprint.ScannableFingerprint; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.push.SignalServiceAddress; 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 fb56f262..3da0c729 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 @@ -39,12 +39,12 @@ import org.signal.libsignal.metadata.ProtocolInvalidMessageException; import org.signal.libsignal.metadata.ProtocolNoSessionException; import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; import org.signal.libsignal.metadata.SelfSendException; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.protocol.SignalProtocolAddress; +import org.signal.libsignal.protocol.message.DecryptionErrorMessage; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; 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 accdbc0f..4ebc4d05 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 @@ -2,7 +2,7 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.util.PinHashing; -import org.whispersystems.libsignal.InvalidKeyException; +import org.signal.libsignal.protocol.InvalidKeyException; import org.whispersystems.signalservice.api.KbsPinData; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.KeyBackupServicePinException; 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 9a4d140e..52e443da 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 @@ -4,11 +4,11 @@ import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.util.KeyUtils; +import org.signal.libsignal.protocol.IdentityKeyPair; +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.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.signalservice.api.push.ServiceIdType; import java.io.IOException; 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 43a5ddb7..53a48466 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -9,12 +9,12 @@ import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.ProfileUtils; import org.asamk.signal.manager.util.Utils; -import org.signal.zkgroup.profiles.ProfileKey; -import org.signal.zkgroup.profiles.ProfileKeyCredential; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.InvalidKeyException; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.profiles.AvatarUploadParams; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; 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 5a1942f9..82368ca2 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 @@ -8,9 +8,9 @@ import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.signal.libsignal.protocol.InvalidKeyException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; 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 375c2def..859dc941 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 @@ -14,13 +14,13 @@ import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.sendLog.MessageSendLogEntry; +import org.signal.libsignal.protocol.InvalidKeyException; +import org.signal.libsignal.protocol.InvalidRegistrationIdException; +import org.signal.libsignal.protocol.NoSessionException; +import org.signal.libsignal.protocol.SignalProtocolAddress; +import org.signal.libsignal.protocol.message.DecryptionErrorMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.InvalidRegistrationIdException; -import org.whispersystems.libsignal.NoSessionException; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.ContentHint; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; 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 566c2b15..9552de58 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 @@ -6,9 +6,9 @@ import org.asamk.signal.manager.api.StickerPackId; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; import org.asamk.signal.manager.util.IOUtils; +import org.signal.libsignal.protocol.InvalidMessageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.signalservice.internal.util.Hex; import java.io.IOException; 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 c28eb613..4ca383e3 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 @@ -6,13 +6,13 @@ import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.recipients.Contact; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.groups.GroupMasterKey; -import org.signal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.InvalidKeyException; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.groups.GroupMasterKey; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.signalservice.api.storage.SignalAccountRecord; import org.whispersystems.signalservice.api.storage.SignalStorageManifest; import org.whispersystems.signalservice.api.storage.SignalStorageRecord; 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 67afed1a..86fb0e66 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 @@ -9,9 +9,9 @@ import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.IOUtils; +import org.signal.libsignal.protocol.IdentityKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; 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 b2b5fc55..7dd412a4 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 @@ -7,7 +7,7 @@ import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.signal.libsignal.metadata.certificate.InvalidCertificateException; import org.signal.libsignal.metadata.certificate.SenderCertificate; -import org.signal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; diff --git a/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java b/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java index 2d7633b8..54c0390b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java +++ b/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java @@ -2,9 +2,9 @@ package org.asamk.signal.manager.jobs; import org.asamk.signal.manager.api.StickerPackId; import org.asamk.signal.manager.helper.Context; +import org.signal.libsignal.protocol.InvalidMessageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.signalservice.internal.util.Hex; import java.io.IOException; 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 a6593fd9..1ab383df 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 @@ -34,16 +34,17 @@ import org.asamk.signal.manager.storage.stickers.StickerStore; import org.asamk.signal.manager.storage.threads.LegacyJsonThreadStore; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.KeyUtils; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.protocol.IdentityKeyPair; +import org.signal.libsignal.protocol.InvalidMessageException; +import org.signal.libsignal.protocol.SignalProtocolAddress; +import org.signal.libsignal.protocol.state.PreKeyRecord; +import org.signal.libsignal.protocol.state.SessionRecord; +import org.signal.libsignal.protocol.state.SignedPreKeyRecord; +import org.signal.libsignal.protocol.util.Medium; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.util.Medium; import org.whispersystems.signalservice.api.SignalServiceAccountDataStore; import org.whispersystems.signalservice.api.SignalServiceDataStore; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; @@ -618,7 +619,7 @@ public class SignalAccount implements Closeable { for (var entry : legacySignalProtocolStore.getLegacyPreKeyStore().getPreKeys().entrySet()) { try { getPreKeyStore().storePreKey(entry.getKey(), new PreKeyRecord(entry.getValue())); - } catch (IOException e) { + } catch (InvalidMessageException e) { logger.warn("Failed to migrate pre key, ignoring", e); } } @@ -630,7 +631,7 @@ public class SignalAccount implements Closeable { for (var entry : legacySignalProtocolStore.getLegacySignedPreKeyStore().getSignedPreKeys().entrySet()) { try { getSignedPreKeyStore().storeSignedPreKey(entry.getKey(), new SignedPreKeyRecord(entry.getValue())); - } catch (IOException e) { + } catch (InvalidMessageException e) { logger.warn("Failed to migrate signed pre key, ignoring", e); } } 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 218032f8..23c80ba9 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 @@ -5,11 +5,11 @@ import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.groups.GroupPermission; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; +import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.signal.storageservice.protos.groups.AccessControl; import org.signal.storageservice.protos.groups.Member; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.EnabledState; -import org.signal.zkgroup.groups.GroupMasterKey; import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.ServiceId; 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 49968b3a..a9affdd6 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 @@ -19,9 +19,9 @@ import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.util.IOUtils; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.signal.storageservice.protos.groups.local.DecryptedGroup; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.groups.GroupMasterKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.DistributionId; 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 adcf6204..5a7324e5 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 @@ -2,7 +2,7 @@ package org.asamk.signal.manager.storage.identities; import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.recipients.RecipientId; -import org.whispersystems.libsignal.IdentityKey; +import org.signal.libsignal.protocol.IdentityKey; import java.util.Date; 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 580194f3..983c59e1 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 @@ -6,12 +6,12 @@ import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.util.IOUtils; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.IdentityKeyPair; +import org.signal.libsignal.protocol.InvalidKeyException; +import org.signal.libsignal.protocol.SignalProtocolAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.SignalProtocolAddress; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -32,7 +32,7 @@ import java.util.regex.Pattern; import io.reactivex.rxjava3.subjects.PublishSubject; import io.reactivex.rxjava3.subjects.Subject; -public class IdentityKeyStore implements org.whispersystems.libsignal.state.IdentityKeyStore { +public class IdentityKeyStore implements org.signal.libsignal.protocol.state.IdentityKeyStore { private final static Logger logger = LoggerFactory.getLogger(IdentityKeyStore.class); private final ObjectMapper objectMapper = org.asamk.signal.manager.storage.Utils.createStorageObjectMapper(); 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 ccdea422..020c9109 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 @@ -1,10 +1,11 @@ package org.asamk.signal.manager.storage.prekeys; import org.asamk.signal.manager.util.IOUtils; +import org.signal.libsignal.protocol.InvalidKeyIdException; +import org.signal.libsignal.protocol.InvalidMessageException; +import org.signal.libsignal.protocol.state.PreKeyRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.state.PreKeyRecord; import java.io.File; import java.io.FileInputStream; @@ -12,7 +13,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; -public class PreKeyStore implements org.whispersystems.libsignal.state.PreKeyStore { +public class PreKeyStore implements org.signal.libsignal.protocol.state.PreKeyStore { private final static Logger logger = LoggerFactory.getLogger(PreKeyStore.class); @@ -31,7 +32,7 @@ public class PreKeyStore implements org.whispersystems.libsignal.state.PreKeySto } try (var inputStream = new FileInputStream(file)) { return new PreKeyRecord(inputStream.readAllBytes()); - } catch (IOException e) { + } catch (IOException | InvalidMessageException e) { logger.error("Failed to load pre key: {}", e.getMessage()); throw new AssertionError(e); } 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 074e5811..575e9256 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 @@ -1,10 +1,11 @@ package org.asamk.signal.manager.storage.prekeys; import org.asamk.signal.manager.util.IOUtils; +import org.signal.libsignal.protocol.InvalidKeyIdException; +import org.signal.libsignal.protocol.InvalidMessageException; +import org.signal.libsignal.protocol.state.SignedPreKeyRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; import java.io.File; import java.io.FileInputStream; @@ -15,7 +16,7 @@ import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; -public class SignedPreKeyStore implements org.whispersystems.libsignal.state.SignedPreKeyStore { +public class SignedPreKeyStore implements org.signal.libsignal.protocol.state.SignedPreKeyStore { private final static Logger logger = LoggerFactory.getLogger(SignedPreKeyStore.class); @@ -117,7 +118,7 @@ public class SignedPreKeyStore implements org.whispersystems.libsignal.state.Sig private SignedPreKeyRecord loadSignedPreKeyRecord(final File file) { try (var inputStream = new FileInputStream(file)) { return new SignedPreKeyRecord(inputStream.readAllBytes()); - } catch (IOException e) { + } catch (IOException | InvalidMessageException e) { logger.error("Failed to load signed pre key: {}", e.getMessage()); throw new AssertionError(e); } 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 1c6369f0..b3aa4f8c 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 @@ -9,9 +9,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.asamk.signal.manager.storage.recipients.RecipientAddress; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.profiles.ProfileKey; -import org.signal.zkgroup.profiles.ProfileKeyCredential; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.IOException; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java index 03b11bcb..7571146e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java @@ -1,8 +1,8 @@ package org.asamk.signal.manager.storage.profiles; import org.asamk.signal.manager.storage.recipients.RecipientAddress; -import org.signal.zkgroup.profiles.ProfileKey; -import org.signal.zkgroup.profiles.ProfileKeyCredential; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; public class LegacySignalProfileEntry { 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 c600f0fb..0ff20042 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 @@ -2,8 +2,8 @@ package org.asamk.signal.manager.storage.profiles; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientId; -import org.signal.zkgroup.profiles.ProfileKey; -import org.signal.zkgroup.profiles.ProfileKeyCredential; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; public interface ProfileStore { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java index a424bef8..85ebcd2b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/LegacyIdentityInfo.java @@ -2,7 +2,7 @@ package org.asamk.signal.manager.storage.protocol; import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.recipients.RecipientAddress; -import org.whispersystems.libsignal.IdentityKey; +import org.signal.libsignal.protocol.IdentityKey; import java.util.Date; 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 f9453a30..de5062e4 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 @@ -8,11 +8,11 @@ import com.fasterxml.jackson.databind.JsonNode; import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.storage.Utils; import org.asamk.signal.manager.storage.recipients.RecipientAddress; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.IdentityKeyPair; +import org.signal.libsignal.protocol.InvalidKeyException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.IOException; 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 d7fb3b47..f804bc6d 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 @@ -1,17 +1,17 @@ package org.asamk.signal.manager.storage.protocol; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyIdException; -import org.whispersystems.libsignal.NoSessionException; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.groups.state.SenderKeyRecord; -import org.whispersystems.libsignal.state.IdentityKeyStore; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.PreKeyStore; -import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyStore; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.IdentityKeyPair; +import org.signal.libsignal.protocol.InvalidKeyIdException; +import org.signal.libsignal.protocol.NoSessionException; +import org.signal.libsignal.protocol.SignalProtocolAddress; +import org.signal.libsignal.protocol.groups.state.SenderKeyRecord; +import org.signal.libsignal.protocol.state.IdentityKeyStore; +import org.signal.libsignal.protocol.state.PreKeyRecord; +import org.signal.libsignal.protocol.state.PreKeyStore; +import org.signal.libsignal.protocol.state.SessionRecord; +import org.signal.libsignal.protocol.state.SignedPreKeyRecord; +import org.signal.libsignal.protocol.state.SignedPreKeyStore; import org.whispersystems.signalservice.api.SignalServiceAccountDataStore; import org.whispersystems.signalservice.api.SignalServiceSenderKeyStore; import org.whispersystems.signalservice.api.SignalServiceSessionStore; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java index df7d10aa..d5c68315 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java @@ -1,7 +1,7 @@ package org.asamk.signal.manager.storage.recipients; -import org.signal.zkgroup.profiles.ProfileKey; -import org.signal.zkgroup.profiles.ProfileKeyCredential; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import java.util.Objects; 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 9b1108c0..04eeea91 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 @@ -7,9 +7,9 @@ import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.storage.Utils; import org.asamk.signal.manager.storage.contacts.ContactsStore; import org.asamk.signal.manager.storage.profiles.ProfileStore; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.profiles.ProfileKey; -import org.signal.zkgroup.profiles.ProfileKeyCredential; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.ACI; 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 3b75de65..bb313c70 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 @@ -5,8 +5,8 @@ import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.storage.Database; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.groups.GroupMasterKey; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.crypto.ContentHint; 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 ec5c5a70..f9a260de 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 @@ -3,10 +3,11 @@ package org.asamk.signal.manager.storage.senderKeys; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.util.IOUtils; +import org.signal.libsignal.protocol.InvalidMessageException; +import org.signal.libsignal.protocol.SignalProtocolAddress; +import org.signal.libsignal.protocol.groups.state.SenderKeyRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.groups.state.SenderKeyRecord; import java.io.File; import java.io.FileInputStream; @@ -22,7 +23,7 @@ import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups.state.SenderKeyStore { +public class SenderKeyRecordStore implements org.signal.libsignal.protocol.groups.state.SenderKeyStore { private final static Logger logger = LoggerFactory.getLogger(SenderKeyRecordStore.class); @@ -203,7 +204,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups final var senderKeyRecord = new SenderKeyRecord(inputStream.readAllBytes()); cachedSenderKeys.put(key, senderKeyRecord); return senderKeyRecord; - } catch (IOException e) { + } catch (IOException | InvalidMessageException e) { logger.warn("Failed to load sender key, resetting sender key: {}", e.getMessage()); return null; } 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 a5947ef2..f27f6139 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 @@ -6,9 +6,9 @@ import org.asamk.signal.manager.helper.RecipientAddressResolver; import org.asamk.signal.manager.storage.Utils; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; +import org.signal.libsignal.protocol.SignalProtocolAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.util.UuidUtil; 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 5318b3f2..a9504497 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 @@ -3,8 +3,8 @@ package org.asamk.signal.manager.storage.senderKeys; import org.asamk.signal.manager.helper.RecipientAddressResolver; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.groups.state.SenderKeyRecord; +import org.signal.libsignal.protocol.SignalProtocolAddress; +import org.signal.libsignal.protocol.groups.state.SenderKeyRecord; import org.whispersystems.signalservice.api.SignalServiceSenderKeyStore; import org.whispersystems.signalservice.api.push.DistributionId; 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 b1713e9b..24327eb0 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 @@ -3,13 +3,13 @@ package org.asamk.signal.manager.storage.sessions; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.util.IOUtils; +import org.signal.libsignal.protocol.NoSessionException; +import org.signal.libsignal.protocol.SignalProtocolAddress; +import org.signal.libsignal.protocol.ecc.ECPublicKey; +import org.signal.libsignal.protocol.message.CiphertextMessage; +import org.signal.libsignal.protocol.state.SessionRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.NoSessionException; -import org.whispersystems.libsignal.SignalProtocolAddress; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.protocol.CiphertextMessage; -import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.signalservice.api.SignalServiceSessionStore; import java.io.File; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/stickerPacks/StickerPackStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/stickerPacks/StickerPackStore.java index 2943847c..2f80ded0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/stickerPacks/StickerPackStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/stickerPacks/StickerPackStore.java @@ -3,7 +3,6 @@ package org.asamk.signal.manager.storage.stickerPacks; import com.fasterxml.jackson.databind.ObjectMapper; import org.asamk.signal.manager.api.StickerPackId; -import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.Utils; import org.whispersystems.signalservice.api.util.StreamDetails; 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 e6c97d44..d87868ee 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 @@ -1,15 +1,15 @@ package org.asamk.signal.manager.util; -import org.signal.zkgroup.InvalidInputException; -import org.signal.zkgroup.profiles.ProfileKey; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPrivateKey; -import org.whispersystems.libsignal.state.PreKeyRecord; -import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.util.Medium; +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.ECPrivateKey; +import org.signal.libsignal.protocol.state.PreKeyRecord; +import org.signal.libsignal.protocol.state.SignedPreKeyRecord; +import org.signal.libsignal.protocol.util.Medium; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.whispersystems.signalservice.api.kbs.MasterKey; import java.security.SecureRandom; 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 5510eeeb..c2bd1fa9 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 @@ -2,7 +2,7 @@ package org.asamk.signal.manager.util; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.storage.recipients.Profile; -import org.signal.zkgroup.profiles.ProfileKey; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; 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 49f474ed..3fc69801 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 @@ -1,10 +1,10 @@ package org.asamk.signal.manager.util; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.fingerprint.Fingerprint; +import org.signal.libsignal.protocol.fingerprint.NumericFingerprintGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.fingerprint.Fingerprint; -import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.StreamDetails; diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java index f80b91d6..e034d677 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java @@ -7,10 +7,10 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.MultiAccountManager; import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.RegistrationManager; -import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.IncorrectPinException; import org.asamk.signal.manager.api.PinLockedException; +import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.freedesktop.dbus.DBusPath; import java.io.IOException; From c8f819cb9427fa58c81bdd02829aab97a94b7f33 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 6 Apr 2022 22:09:22 +0200 Subject: [PATCH 067/938] Fix output of rate limit exception --- .../main/java/org/asamk/signal/manager/helper/SendHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 859dc941..46f34a4d 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 @@ -175,7 +175,7 @@ public class SendHelper { } catch (RateLimitException e) { var address = context.getRecipientHelper().resolveSignalServiceAddress(account.getSelfRecipientId()); logger.warn("Sending failed due to rate limiting from the signal server: {}", e.getMessage()); - return SendMessageResult.networkFailure(address); + return SendMessageResult.rateLimitFailure(address, e); } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { var address = context.getRecipientHelper().resolveSignalServiceAddress(account.getSelfRecipientId()); return SendMessageResult.identityFailure(address, e.getIdentityKey()); @@ -603,7 +603,7 @@ public class SendHelper { return SendMessageResult.proofRequiredFailure(address, e); } catch (RateLimitException e) { logger.warn("Sending failed due to rate limiting from the signal server: {}", e.getMessage()); - return SendMessageResult.networkFailure(address); + return SendMessageResult.rateLimitFailure(address, e); } catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) { return SendMessageResult.identityFailure(address, e.getIdentityKey()); } catch (IOException e) { From 8dc82a30af841802d9acc91f09b56d418478ce6b Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 6 Apr 2022 22:17:06 +0200 Subject: [PATCH 068/938] Fix check if there was a send error when creating a group without members Fixes #925 --- src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index f3877974..ad6f4546 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -874,7 +874,7 @@ public class DbusSignalImpl implements Signal { } var errors = SendMessageResultUtils.getErrorMessagesFromSendMessageResults(results); - if (errors.size() < results.size()) { + if (errors.size() == 0 || errors.size() < results.size()) { return; } From 02a018462bd6d30d8b20eb4acce81238333c3640 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 6 Apr 2022 22:28:07 +0200 Subject: [PATCH 069/938] Prevent deleting a group locally if the user is still a member Fixes #924 --- lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index aa4752a2..45ec09f9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -348,6 +348,11 @@ class ManagerImpl implements Manager { @Override public void deleteGroup(GroupId groupId) throws IOException { + final var group = context.getGroupHelper().getGroup(groupId); + if (group.isMember(account.getSelfRecipientId())) { + throw new IOException( + "The local group information cannot be removed, as the user is still a member of the group"); + } context.getGroupHelper().deleteGroup(groupId); } From af711157672d3328b7b4c9879ecc7ab4e40f7426 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 6 Apr 2022 22:34:34 +0200 Subject: [PATCH 070/938] Update reflect-config.json --- graalvm-config-dir/reflect-config.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index f4c809ec..98492ef8 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -419,6 +419,10 @@ "name":"org.asamk.Signal$Error$Failure", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, +{ + "name":"org.asamk.Signal$Error$LastGroupAdmin", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.asamk.Signal$Error$UntrustedIdentity", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] @@ -458,7 +462,9 @@ }, { "name":"org.asamk.Signal$StructGroup", - "allDeclaredFields":true + "allDeclaredFields":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":["org.freedesktop.dbus.DBusPath","byte[]","java.lang.String"] }] }, { "name":"org.asamk.Signal$SyncMessageReceived", From 2c159952893123b190f66bd10370a4b12c3471d2 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 6 Apr 2022 22:35:51 +0200 Subject: [PATCH 071/938] Output RATE_LIMIT_FAILURE type more reliably in json output Fixes #923 --- .../org/asamk/signal/manager/api/SendMessageResult.java | 2 +- .../java/org/asamk/signal/json/JsonSendMessageResult.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java b/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java index 78883860..be5bf801 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/SendMessageResult.java @@ -48,7 +48,7 @@ public record SendMessageResult( sendMessageResult.isNetworkFailure(), sendMessageResult.isUnregisteredFailure(), sendMessageResult.getIdentityFailure() != null, - sendMessageResult.getRateLimitFailure() != null, + sendMessageResult.getRateLimitFailure() != null || sendMessageResult.getProofRequiredFailure() != null, sendMessageResult.getProofRequiredFailure() == null ? null : new ProofRequiredException(sendMessageResult.getProofRequiredFailure())); diff --git a/src/main/java/org/asamk/signal/json/JsonSendMessageResult.java b/src/main/java/org/asamk/signal/json/JsonSendMessageResult.java index f81f54c6..dd7f235f 100644 --- a/src/main/java/org/asamk/signal/json/JsonSendMessageResult.java +++ b/src/main/java/org/asamk/signal/json/JsonSendMessageResult.java @@ -22,10 +22,10 @@ public record JsonSendMessageResult( groupId != null ? groupId.toBase64() : null, result.isSuccess() ? Type.SUCCESS - : result.isNetworkFailure() - ? Type.NETWORK_FAILURE - : result.isRateLimitFailure() - ? Type.RATE_LIMIT_FAILURE + : result.isRateLimitFailure() + ? Type.RATE_LIMIT_FAILURE + : result.isNetworkFailure() + ? Type.NETWORK_FAILURE : result.isUnregisteredFailure() ? Type.UNREGISTERED_FAILURE : Type.IDENTITY_FAILURE, From 69f1d0c213f36914bdc78d4a4270b283df4d95d1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 9 Apr 2022 19:02:51 +0200 Subject: [PATCH 072/938] Workaround adoc issue --- man/signal-cli-dbus.5.adoc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/man/signal-cli-dbus.5.adoc b/man/signal-cli-dbus.5.adoc index a2738337..a475eadc 100644 --- a/man/signal-cli-dbus.5.adoc +++ b/man/signal-cli-dbus.5.adoc @@ -67,10 +67,9 @@ This can be piped to a QR encoder to create a display that can be captured by a For example: -[source] ----- +``` dbus-send --session --dest=org.asamk.Signal --type=method_call --print-reply /org/asamk/Signal org.asamk.Signal.link string:"My secondary client" | tr '\n' '\0' | sed 's/.*string //g' | sed 's/\"//g' | qrencode -s10 -tANSI256 ----- +``` listAccounts() -> accountList:: * accountList : Array of all attached accounts in DBus object path form From 2a20e70aabaad0106774157a78eba60428604ac1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 9 Apr 2022 19:01:37 +0200 Subject: [PATCH 073/938] Add support for banning/unbanning group members --- client/src/cli.rs | 6 + client/src/jsonrpc.rs | 2 + client/src/main.rs | 4 + graalvm-config-dir/reflect-config.json | 6 + .../org/asamk/signal/manager/ManagerImpl.java | 6 + .../org/asamk/signal/manager/api/Group.java | 5 + .../asamk/signal/manager/api/UpdateGroup.java | 121 ++++++++++++++++-- .../signal/manager/helper/GroupHelper.java | 32 +++++ .../signal/manager/helper/GroupV2Helper.java | 40 +++++- .../manager/storage/groups/GroupInfo.java | 4 + .../manager/storage/groups/GroupInfoV2.java | 12 ++ man/signal-cli.1.adoc | 7 + src/main/java/org/asamk/Signal.java | 1 + .../signal/commands/ListGroupsCommand.java | 5 +- .../signal/commands/UpdateGroupCommand.java | 6 + .../asamk/signal/dbus/DbusManagerImpl.java | 3 + .../org/asamk/signal/dbus/DbusSignalImpl.java | 2 + 17 files changed, 246 insertions(+), 16 deletions(-) diff --git a/client/src/cli.rs b/client/src/cli.rs index bc6a3079..a8cee165 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -268,6 +268,12 @@ pub enum CliCommands { #[clap(long = "remove-admin")] remove_admin: Vec, + #[clap(long)] + ban: Vec, + + #[clap(long)] + unban: Vec, + #[clap(long = "reset-link")] reset_link: bool, diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index c0feddd0..d4ed4084 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -227,6 +227,8 @@ pub trait Rpc { #[allow(non_snake_case)] removeMember: Vec, admin: Vec, #[allow(non_snake_case)] removeAdmin: Vec, + ban: Vec, + unban: Vec, #[allow(non_snake_case)] resetLink: bool, #[allow(non_snake_case)] link: Option, #[allow(non_snake_case)] setPermissionAddMember: Option, diff --git a/client/src/main.rs b/client/src/main.rs index 1ad8dbcf..6266d224 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -243,6 +243,8 @@ async fn main() -> Result<(), anyhow::Error> { remove_member, admin, remove_admin, + ban, + unban, reset_link, link, set_permission_add_member, @@ -261,6 +263,8 @@ async fn main() -> Result<(), anyhow::Error> { remove_member, admin, remove_admin, + ban, + unban, reset_link, link.map(|link| match link { LinkState::Enabled => "enabled".to_owned(), diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 98492ef8..53f611b0 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2057,6 +2057,12 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, +{ + "name":"org.whispersystems.signalservice.api.push.ServiceId", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity", "allDeclaredFields":true, diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 45ec09f9..addc3bb7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -386,6 +386,12 @@ class ManagerImpl implements Manager { updateGroup.getRemoveAdmins() == null ? null : context.getRecipientHelper().resolveRecipients(updateGroup.getRemoveAdmins()), + updateGroup.getBanMembers() == null + ? null + : context.getRecipientHelper().resolveRecipients(updateGroup.getBanMembers()), + updateGroup.getUnbanMembers() == null + ? null + : context.getRecipientHelper().resolveRecipients(updateGroup.getUnbanMembers()), updateGroup.isResetGroupLink(), updateGroup.getGroupLinkState(), updateGroup.getAddMemberPermission(), 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 a8dfdff1..9b2d988a 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 @@ -20,6 +20,7 @@ public record Group( Set pendingMembers, Set requestingMembers, Set adminMembers, + Set bannedMembers, boolean isBlocked, int messageExpirationTimer, GroupPermission permissionAddMember, @@ -52,6 +53,10 @@ public record Group( .stream() .map(recipientStore::resolveRecipientAddress) .collect(Collectors.toSet()), + groupInfo.getBannedMembers() + .stream() + .map(recipientStore::resolveRecipientAddress) + .collect(Collectors.toSet()), groupInfo.isBlocked(), groupInfo.getMessageExpirationTimer(), groupInfo.getPermissionAddMember(), diff --git a/lib/src/main/java/org/asamk/signal/manager/api/UpdateGroup.java b/lib/src/main/java/org/asamk/signal/manager/api/UpdateGroup.java index b5877ae5..077542e1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/UpdateGroup.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/UpdateGroup.java @@ -14,6 +14,8 @@ public class UpdateGroup { private final Set removeMembers; private final Set admins; private final Set removeAdmins; + private final Set banMembers; + private final Set unbanMembers; private final boolean resetGroupLink; private final GroupLinkState groupLinkState; private final GroupPermission addMemberPermission; @@ -29,6 +31,8 @@ public class UpdateGroup { removeMembers = builder.removeMembers; admins = builder.admins; removeAdmins = builder.removeAdmins; + banMembers = builder.banMembers; + unbanMembers = builder.unbanMembers; resetGroupLink = builder.resetGroupLink; groupLinkState = builder.groupLinkState; addMemberPermission = builder.addMemberPermission; @@ -43,23 +47,58 @@ public class UpdateGroup { } public static Builder newBuilder(final UpdateGroup copy) { - Builder builder = new Builder(); - builder.name = copy.getName(); - builder.description = copy.getDescription(); - builder.members = copy.getMembers(); - builder.removeMembers = copy.getRemoveMembers(); - builder.admins = copy.getAdmins(); - builder.removeAdmins = copy.getRemoveAdmins(); - builder.resetGroupLink = copy.isResetGroupLink(); - builder.groupLinkState = copy.getGroupLinkState(); - builder.addMemberPermission = copy.getAddMemberPermission(); - builder.editDetailsPermission = copy.getEditDetailsPermission(); - builder.avatarFile = copy.getAvatarFile(); - builder.expirationTimer = copy.getExpirationTimer(); - builder.isAnnouncementGroup = copy.getIsAnnouncementGroup(); + Builder builder = new Builder(copy.name, + copy.description, + copy.members, + copy.removeMembers, + copy.admins, + copy.removeAdmins, + copy.banMembers, + copy.unbanMembers, + copy.resetGroupLink, + copy.groupLinkState, + copy.addMemberPermission, + copy.editDetailsPermission, + copy.avatarFile, + copy.expirationTimer, + copy.isAnnouncementGroup); return builder; } + public static Builder newBuilder( + final String name, + final String description, + final Set members, + final Set removeMembers, + final Set admins, + final Set removeAdmins, + final Set banMembers, + final Set unbanMembers, + final boolean resetGroupLink, + final GroupLinkState groupLinkState, + final GroupPermission addMemberPermission, + final GroupPermission editDetailsPermission, + final File avatarFile, + final Integer expirationTimer, + final Boolean isAnnouncementGroup + ) { + return new Builder(name, + description, + members, + removeMembers, + admins, + removeAdmins, + banMembers, + unbanMembers, + resetGroupLink, + groupLinkState, + addMemberPermission, + editDetailsPermission, + avatarFile, + expirationTimer, + isAnnouncementGroup); + } + public String getName() { return name; } @@ -84,6 +123,14 @@ public class UpdateGroup { return removeAdmins; } + public Set getBanMembers() { + return banMembers; + } + + public Set getUnbanMembers() { + return unbanMembers; + } + public boolean isResetGroupLink() { return resetGroupLink; } @@ -120,6 +167,8 @@ public class UpdateGroup { private Set removeMembers; private Set admins; private Set removeAdmins; + private Set banMembers; + private Set unbanMembers; private boolean resetGroupLink; private GroupLinkState groupLinkState; private GroupPermission addMemberPermission; @@ -131,6 +180,40 @@ public class UpdateGroup { private Builder() { } + private Builder( + final String name, + final String description, + final Set members, + final Set removeMembers, + final Set admins, + final Set removeAdmins, + final Set banMembers, + final Set unbanMembers, + final boolean resetGroupLink, + final GroupLinkState groupLinkState, + final GroupPermission addMemberPermission, + final GroupPermission editDetailsPermission, + final File avatarFile, + final Integer expirationTimer, + final Boolean isAnnouncementGroup + ) { + this.name = name; + this.description = description; + this.members = members; + this.removeMembers = removeMembers; + this.admins = admins; + this.removeAdmins = removeAdmins; + this.banMembers = banMembers; + this.unbanMembers = unbanMembers; + this.resetGroupLink = resetGroupLink; + this.groupLinkState = groupLinkState; + this.addMemberPermission = addMemberPermission; + this.editDetailsPermission = editDetailsPermission; + this.avatarFile = avatarFile; + this.expirationTimer = expirationTimer; + this.isAnnouncementGroup = isAnnouncementGroup; + } + public Builder withName(final String val) { name = val; return this; @@ -161,6 +244,16 @@ public class UpdateGroup { return this; } + public Builder withBanMembers(final Set val) { + banMembers = val; + return this; + } + + public Builder withUnbanMembers(final Set val) { + unbanMembers = val; + return this; + } + public Builder withResetGroupLink(final boolean val) { resetGroupLink = val; return this; 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 b730fe2d..57f9c924 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 @@ -194,6 +194,8 @@ public class GroupHelper { final Set removeMembers, final Set admins, final Set removeAdmins, + final Set banMembers, + final Set unbanMembers, final boolean resetGroupLink, final GroupLinkState groupLinkState, final GroupPermission addMemberPermission, @@ -213,6 +215,8 @@ public class GroupHelper { removeMembers, admins, removeAdmins, + banMembers, + unbanMembers, resetGroupLink, groupLinkState, addMemberPermission, @@ -230,6 +234,8 @@ public class GroupHelper { removeMembers, admins, removeAdmins, + banMembers, + unbanMembers, resetGroupLink, groupLinkState, addMemberPermission, @@ -467,6 +473,8 @@ public class GroupHelper { final Set removeMembers, final Set admins, final Set removeAdmins, + final Set banMembers, + final Set unbanMembers, final boolean resetGroupLink, final GroupLinkState groupLinkState, final GroupPermission addMemberPermission, @@ -493,7 +501,13 @@ public class GroupHelper { if (removeMembers != null) { var existingRemoveMembers = new HashSet<>(removeMembers); + if (banMembers != null) { + existingRemoveMembers.addAll(banMembers); + } existingRemoveMembers.retainAll(group.getMembers()); + if (members != null) { + existingRemoveMembers.removeAll(members); + } existingRemoveMembers.remove(account.getSelfRecipientId());// self can be removed with sendQuitGroupMessage if (existingRemoveMembers.size() > 0) { var groupGroupChangePair = groupV2Helper.removeMembers(group, existingRemoveMembers); @@ -535,6 +549,24 @@ public class GroupHelper { } } + if (banMembers != null) { + final var newlyBannedMembers = new HashSet<>(banMembers); + newlyBannedMembers.removeAll(group.getBannedMembers()); + if (newlyBannedMembers.size() > 0) { + var groupGroupChangePair = groupV2Helper.banMembers(group, newlyBannedMembers); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + } + + if (unbanMembers != null) { + var existingUnbanMembers = new HashSet<>(unbanMembers); + existingUnbanMembers.retainAll(group.getBannedMembers()); + if (existingUnbanMembers.size() > 0) { + var groupGroupChangePair = groupV2Helper.unbanMembers(group, existingUnbanMembers); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + } + if (resetGroupLink) { var groupGroupChangePair = groupV2Helper.resetGroupLinkPassword(group); result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); 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 49582ac9..d56ce98f 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 @@ -203,9 +203,13 @@ class GroupV2Helper { credentials, (uuid, credential) -> new GroupCandidate(uuid, Optional.ofNullable(credential))) .collect(Collectors.toSet()); + final var bannedUuids = groupInfoV2.getBannedMembers() + .stream() + .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()) + .collect(Collectors.toSet()); final var aci = getSelfAci(); - final var change = groupOperations.createModifyGroupMembershipChange(candidates, Set.of(), aci.uuid()); + final var change = groupOperations.createModifyGroupMembershipChange(candidates, bannedUuids, aci.uuid()); change.setSourceUuid(getSelfAci().toByteString()); @@ -259,6 +263,40 @@ class GroupV2Helper { return revokeInvites(groupInfoV2, memberUuids); } + Pair banMembers( + GroupInfoV2 groupInfoV2, Set block + ) throws IOException { + GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); + + final var uuids = block.stream() + .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()) + .collect(Collectors.toSet()); + + final var change = groupOperations.createBanUuidsChange(uuids, + false, + groupInfoV2.getGroup().getBannedMembersList()); + + change.setSourceUuid(getSelfAci().toByteString()); + + return commitChange(groupInfoV2, change); + } + + Pair unbanMembers( + GroupInfoV2 groupInfoV2, Set block + ) throws IOException { + GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); + + final var uuids = block.stream() + .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()) + .collect(Collectors.toSet()); + + final var change = groupOperations.createUnbanUuidsChange(uuids); + + change.setSourceUuid(getSelfAci().toByteString()); + + return commitChange(groupInfoV2, change); + } + Pair resetGroupLinkPassword(GroupInfoV2 groupInfoV2) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var newGroupLinkPassword = GroupLinkPassword.createNew().serialize(); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java index 4d94eb01..3816dcc6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java @@ -26,6 +26,10 @@ public sealed abstract class GroupInfo permits GroupInfoV1, GroupInfoV2 { public abstract Set getMembers(); + public Set getBannedMembers() { + return Set.of(); + } + public Set getPendingMembers() { return Set.of(); } 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 23c80ba9..dc803f0f 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 @@ -117,6 +117,18 @@ public final class GroupInfoV2 extends GroupInfo { .collect(Collectors.toSet()); } + @Override + public Set getBannedMembers() { + if (this.group == null) { + return Set.of(); + } + return group.getBannedMembersList() + .stream() + .map(m -> ServiceId.fromByteString(m.getUuid())) + .map(recipientResolver::resolveRecipient) + .collect(Collectors.toSet()); + } + @Override public Set getPendingMembers() { if (this.group == null) { diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 1a30b2e0..0562fcca 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -357,6 +357,13 @@ Specify one or more members to make a group admin *--remove-admin* [MEMBER [MEMBER ...]]:: Specify one or more members to remove group admin privileges +*--ban* [MEMBER [MEMBER ...]]:: +Specify one or more members to ban from joining the group. +Banned members cannot join or request to join via a group link. + +*--unban* [MEMBER [MEMBER ...]]:: +Specify one or more members to remove from the ban list + *--reset-link*:: Reset group link and create new link password diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index fd91d8af..caf55555 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -523,6 +523,7 @@ public interface Signal extends DBusInterface { @DBusProperty(name = "PendingMembers", type = String[].class, access = DBusProperty.Access.READ) @DBusProperty(name = "RequestingMembers", type = String[].class, access = DBusProperty.Access.READ) @DBusProperty(name = "Admins", type = String[].class, access = DBusProperty.Access.READ) + @DBusProperty(name = "Banned", type = String[].class, access = DBusProperty.Access.READ) @DBusProperty(name = "PermissionAddMember", type = String.class) @DBusProperty(name = "PermissionEditDetails", type = String.class) @DBusProperty(name = "PermissionSendMessage", type = String.class) diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 84e458b3..80e583af 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -53,7 +53,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { final var groupInviteLink = group.groupInviteLinkUrl(); writer.println( - "Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Message expiration: {} Link: {}", + "Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Banned: {} Message expiration: {} Link: {}", group.groupId().toBase64(), group.title(), group.description(), @@ -63,6 +63,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { resolveMembers(group.pendingMembers()), resolveMembers(group.requestingMembers()), resolveMembers(group.adminMembers()), + resolveMembers(group.bannedMembers()), group.messageExpirationTimer() == 0 ? "disabled" : group.messageExpirationTimer() + "s", groupInviteLink == null ? '-' : groupInviteLink.getUrl()); } else { @@ -95,6 +96,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { resolveJsonMembers(group.pendingMembers()), resolveJsonMembers(group.requestingMembers()), resolveJsonMembers(group.adminMembers()), + resolveJsonMembers(group.bannedMembers()), group.permissionAddMember().name(), group.permissionEditDetails().name(), group.permissionSendMessage().name(), @@ -122,6 +124,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { Set pendingMembers, Set requestingMembers, Set admins, + Set banned, String permissionAddMember, String permissionEditDetails, String permissionSendMessage, diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index 2a6d7d15..411ae747 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -55,6 +55,8 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand { subparser.addArgument("--remove-admin") .nargs("*") .help("Specify one or more members to remove group admin privileges"); + subparser.addArgument("--ban").nargs("*").help("Specify one or more members to ban from joining the group"); + subparser.addArgument("--unban").nargs("*").help("Specify one or more members to remove from the ban list"); subparser.addArgument("--reset-link") .action(Arguments.storeTrue()) @@ -114,6 +116,8 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand { var groupRemoveMembers = CommandUtil.getSingleRecipientIdentifiers(ns.getList("remove-member"), localNumber); var groupAdmins = CommandUtil.getSingleRecipientIdentifiers(ns.getList("admin"), localNumber); var groupRemoveAdmins = CommandUtil.getSingleRecipientIdentifiers(ns.getList("remove-admin"), localNumber); + var groupBan = CommandUtil.getSingleRecipientIdentifiers(ns.getList("ban"), localNumber); + var groupUnban = CommandUtil.getSingleRecipientIdentifiers(ns.getList("unban"), localNumber); var groupAvatar = ns.getString("avatar"); var groupResetLink = Boolean.TRUE.equals(ns.getBoolean("reset-link")); var groupLinkState = getGroupLinkState(ns.getString("link")); @@ -145,6 +149,8 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand { .withRemoveMembers(groupRemoveMembers) .withAdmins(groupAdmins) .withRemoveAdmins(groupRemoveAdmins) + .withBanMembers(groupBan) + .withUnbanMembers(groupUnban) .withResetGroupLink(groupResetLink) .withGroupLinkState(groupLinkState) .withAddMemberPermission(groupAddMemberPermission) diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 8d8d333f..ed27ad6d 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -583,6 +583,9 @@ public class DbusManagerImpl implements Manager { ((List) group.get("Admins").getValue()).stream() .map(m -> new RecipientAddress(null, m)) .collect(Collectors.toSet()), + ((List) group.get("Banned").getValue()).stream() + .map(m -> new RecipientAddress(null, m)) + .collect(Collectors.toSet()), (boolean) group.get("IsBlocked").getValue(), (int) group.get("MessageExpirationTimer").getValue(), GroupPermission.valueOf((String) group.get("PermissionAddMember").getValue()), diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index ad6f4546..850bcf1d 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -1162,6 +1162,8 @@ public class DbusSignalImpl implements Signal { () -> new Variant<>(getRecipientStrings(getGroup().requestingMembers()), "as")), new DbusProperty<>("Admins", () -> new Variant<>(getRecipientStrings(getGroup().adminMembers()), "as")), + new DbusProperty<>("Banned", + () -> new Variant<>(getRecipientStrings(getGroup().bannedMembers()), "as")), new DbusProperty<>("PermissionAddMember", () -> getGroup().permissionAddMember().name(), this::setGroupPermissionAddMember), From 945ff44de3df067545412c3e1c0fff8a5d95e810 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 11 Apr 2022 20:05:02 +0200 Subject: [PATCH 074/938] Refresh pre keys for PNI identity Fixes #930 --- .../signal/manager/helper/PreKeyHelper.java | 21 ++- .../signal/manager/storage/SignalAccount.java | 162 +++++++++++++----- 2 files changed, 132 insertions(+), 51 deletions(-) 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 52e443da..ea5ccdab 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 @@ -45,32 +45,31 @@ public class PreKeyHelper { } public void refreshPreKeys(ServiceIdType serviceIdType) throws IOException { - if (serviceIdType != ServiceIdType.ACI) { - // TODO implement + final var oneTimePreKeys = generatePreKeys(serviceIdType); + final var identityKeyPair = account.getIdentityKeyPair(serviceIdType); + if (identityKeyPair == null) { return; } - var oneTimePreKeys = generatePreKeys(); - final var identityKeyPair = account.getAciIdentityKeyPair(); - var signedPreKeyRecord = generateSignedPreKey(identityKeyPair); + final var signedPreKeyRecord = generateSignedPreKey(serviceIdType, identityKeyPair); dependencies.getAccountManager() .setPreKeys(serviceIdType, identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys); } - private List generatePreKeys() { - final var offset = account.getPreKeyIdOffset(); + private List generatePreKeys(ServiceIdType serviceIdType) { + final var offset = account.getPreKeyIdOffset(serviceIdType); var records = KeyUtils.generatePreKeyRecords(offset, ServiceConfig.PREKEY_BATCH_SIZE); - account.addPreKeys(records); + account.addPreKeys(serviceIdType, records); return records; } - private SignedPreKeyRecord generateSignedPreKey(IdentityKeyPair identityKeyPair) { - final var signedPreKeyId = account.getNextSignedPreKeyId(); + private SignedPreKeyRecord generateSignedPreKey(ServiceIdType serviceIdType, IdentityKeyPair identityKeyPair) { + final var signedPreKeyId = account.getNextSignedPreKeyId(serviceIdType); var record = KeyUtils.generateSignedPreKeyRecord(identityKeyPair, signedPreKeyId); - account.addSignedPreKey(record); + account.addSignedPreKey(serviceIdType, record); return record; } 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 1ab383df..66f515ae 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 @@ -53,6 +53,7 @@ import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.PNI; import org.whispersystems.signalservice.api.push.ServiceId; +import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.storage.StorageKey; import org.whispersystems.signalservice.api.util.CredentialsProvider; @@ -106,8 +107,10 @@ public class SignalAccount implements Closeable { private StorageKey storageKey; private long storageManifestVersion = -1; private ProfileKey profileKey; - private int preKeyIdOffset = 1; - private int nextSignedPreKeyId = 1; + private int aciPreKeyIdOffset = 1; + private int aciNextSignedPreKeyId = 1; + private int pniPreKeyIdOffset = 1; + private int pniNextSignedPreKeyId = 1; private IdentityKeyPair aciIdentityKeyPair; private IdentityKeyPair pniIdentityKeyPair; private int localRegistrationId; @@ -117,8 +120,10 @@ public class SignalAccount implements Closeable { private boolean registered = false; private SignalProtocolStore signalProtocolStore; - private PreKeyStore preKeyStore; - private SignedPreKeyStore signedPreKeyStore; + private PreKeyStore aciPreKeyStore; + private SignedPreKeyStore aciSignedPreKeyStore; + private PreKeyStore pniPreKeyStore; + private SignedPreKeyStore pniSignedPreKeyStore; private SessionStore sessionStore; private IdentityKeyStore identityKeyStore; private SenderKeyStore senderKeyStore; @@ -259,10 +264,14 @@ public class SignalAccount implements Closeable { } private void clearAllPreKeys() { - this.preKeyIdOffset = new SecureRandom().nextInt(Medium.MAX_VALUE); - this.nextSignedPreKeyId = new SecureRandom().nextInt(Medium.MAX_VALUE); - this.getPreKeyStore().removeAllPreKeys(); - this.getSignedPreKeyStore().removeAllSignedPreKeys(); + this.aciPreKeyIdOffset = new SecureRandom().nextInt(Medium.MAX_VALUE); + this.aciNextSignedPreKeyId = new SecureRandom().nextInt(Medium.MAX_VALUE); + this.pniPreKeyIdOffset = new SecureRandom().nextInt(Medium.MAX_VALUE); + this.pniNextSignedPreKeyId = new SecureRandom().nextInt(Medium.MAX_VALUE); + this.getAciPreKeyStore().removeAllPreKeys(); + this.getAciSignedPreKeyStore().removeAllSignedPreKeys(); + this.getPniPreKeyStore().removeAllPreKeys(); + this.getPniSignedPreKeyStore().removeAllSignedPreKeys(); save(); } @@ -407,14 +416,22 @@ public class SignalAccount implements Closeable { return new File(getUserPath(dataPath, account), "group-cache"); } - private static File getPreKeysPath(File dataPath, String account) { + private static File getAciPreKeysPath(File dataPath, String account) { return new File(getUserPath(dataPath, account), "pre-keys"); } - private static File getSignedPreKeysPath(File dataPath, String account) { + private static File getAciSignedPreKeysPath(File dataPath, String account) { return new File(getUserPath(dataPath, account), "signed-pre-keys"); } + private static File getPniPreKeysPath(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "pre-keys-pni"); + } + + private static File getPniSignedPreKeysPath(File dataPath, String account) { + return new File(getUserPath(dataPath, account), "signed-pre-keys-pni"); + } + private static File getIdentitiesPath(File dataPath, String account) { return new File(getUserPath(dataPath, account), "identities"); } @@ -528,14 +545,24 @@ public class SignalAccount implements Closeable { storageManifestVersion = rootNode.get("storageManifestVersion").asLong(); } if (rootNode.hasNonNull("preKeyIdOffset")) { - preKeyIdOffset = rootNode.get("preKeyIdOffset").asInt(1); + aciPreKeyIdOffset = rootNode.get("preKeyIdOffset").asInt(1); } else { - preKeyIdOffset = 1; + aciPreKeyIdOffset = 1; } if (rootNode.hasNonNull("nextSignedPreKeyId")) { - nextSignedPreKeyId = rootNode.get("nextSignedPreKeyId").asInt(1); + aciNextSignedPreKeyId = rootNode.get("nextSignedPreKeyId").asInt(1); } else { - nextSignedPreKeyId = 1; + aciNextSignedPreKeyId = 1; + } + if (rootNode.hasNonNull("pniPreKeyIdOffset")) { + pniPreKeyIdOffset = rootNode.get("pniPreKeyIdOffset").asInt(1); + } else { + pniPreKeyIdOffset = 1; + } + if (rootNode.hasNonNull("pniNextSignedPreKeyId")) { + pniNextSignedPreKeyId = rootNode.get("pniNextSignedPreKeyId").asInt(1); + } else { + pniNextSignedPreKeyId = 1; } if (rootNode.hasNonNull("profileKey")) { try { @@ -618,7 +645,7 @@ public class SignalAccount implements Closeable { logger.debug("Migrating legacy pre key store."); for (var entry : legacySignalProtocolStore.getLegacyPreKeyStore().getPreKeys().entrySet()) { try { - getPreKeyStore().storePreKey(entry.getKey(), new PreKeyRecord(entry.getValue())); + getAciPreKeyStore().storePreKey(entry.getKey(), new PreKeyRecord(entry.getValue())); } catch (InvalidMessageException e) { logger.warn("Failed to migrate pre key, ignoring", e); } @@ -630,7 +657,8 @@ public class SignalAccount implements Closeable { logger.debug("Migrating legacy signed pre key store."); for (var entry : legacySignalProtocolStore.getLegacySignedPreKeyStore().getSignedPreKeys().entrySet()) { try { - getSignedPreKeyStore().storeSignedPreKey(entry.getKey(), new SignedPreKeyRecord(entry.getValue())); + getAciSignedPreKeyStore().storeSignedPreKey(entry.getKey(), + new SignedPreKeyRecord(entry.getValue())); } catch (InvalidMessageException e) { logger.warn("Failed to migrate signed pre key, ignoring", e); } @@ -813,8 +841,10 @@ public class SignalAccount implements Closeable { .put("storageKey", storageKey == null ? null : Base64.getEncoder().encodeToString(storageKey.serialize())) .put("storageManifestVersion", storageManifestVersion == -1 ? null : storageManifestVersion) - .put("preKeyIdOffset", preKeyIdOffset) - .put("nextSignedPreKeyId", nextSignedPreKeyId) + .put("preKeyIdOffset", aciPreKeyIdOffset) + .put("nextSignedPreKeyId", aciNextSignedPreKeyId) + .put("pniPreKeyIdOffset", pniPreKeyIdOffset) + .put("pniNextSignedPreKeyId", pniNextSignedPreKeyId) .put("profileKey", profileKey == null ? null : Base64.getEncoder().encodeToString(profileKey.serialize())) .put("registered", registered) @@ -852,25 +882,63 @@ public class SignalAccount implements Closeable { return new Pair<>(fileChannel, lock); } - public void addPreKeys(List records) { + public void addPreKeys(ServiceIdType serviceIdType, List records) { + if (serviceIdType.equals(ServiceIdType.ACI)) { + addAciPreKeys(records); + } else { + addPniPreKeys(records); + } + } + + public void addAciPreKeys(List records) { for (var record : records) { - if (preKeyIdOffset != record.getId()) { - logger.error("Invalid pre key id {}, expected {}", record.getId(), preKeyIdOffset); + if (aciPreKeyIdOffset != record.getId()) { + logger.error("Invalid pre key id {}, expected {}", record.getId(), aciPreKeyIdOffset); throw new AssertionError("Invalid pre key id"); } - getPreKeyStore().storePreKey(record.getId(), record); - preKeyIdOffset = (preKeyIdOffset + 1) % Medium.MAX_VALUE; + getAciPreKeyStore().storePreKey(record.getId(), record); + aciPreKeyIdOffset = (aciPreKeyIdOffset + 1) % Medium.MAX_VALUE; } save(); } - public void addSignedPreKey(SignedPreKeyRecord record) { - if (nextSignedPreKeyId != record.getId()) { - logger.error("Invalid signed pre key id {}, expected {}", record.getId(), nextSignedPreKeyId); + public void addPniPreKeys(List records) { + for (var record : records) { + if (pniPreKeyIdOffset != record.getId()) { + logger.error("Invalid pre key id {}, expected {}", record.getId(), pniPreKeyIdOffset); + throw new AssertionError("Invalid pre key id"); + } + getPniPreKeyStore().storePreKey(record.getId(), record); + pniPreKeyIdOffset = (pniPreKeyIdOffset + 1) % Medium.MAX_VALUE; + } + save(); + } + + public void addSignedPreKey(ServiceIdType serviceIdType, SignedPreKeyRecord record) { + if (serviceIdType.equals(ServiceIdType.ACI)) { + addAciSignedPreKey(record); + } else { + addPniSignedPreKey(record); + } + } + + public void addAciSignedPreKey(SignedPreKeyRecord record) { + if (aciNextSignedPreKeyId != record.getId()) { + logger.error("Invalid signed pre key id {}, expected {}", record.getId(), aciNextSignedPreKeyId); throw new AssertionError("Invalid signed pre key id"); } - getSignedPreKeyStore().storeSignedPreKey(record.getId(), record); - nextSignedPreKeyId = (nextSignedPreKeyId + 1) % Medium.MAX_VALUE; + getAciSignedPreKeyStore().storeSignedPreKey(record.getId(), record); + aciNextSignedPreKeyId = (aciNextSignedPreKeyId + 1) % Medium.MAX_VALUE; + save(); + } + + public void addPniSignedPreKey(SignedPreKeyRecord record) { + if (pniNextSignedPreKeyId != record.getId()) { + logger.error("Invalid signed pre key id {}, expected {}", record.getId(), pniNextSignedPreKeyId); + throw new AssertionError("Invalid signed pre key id"); + } + getPniSignedPreKeyStore().storeSignedPreKey(record.getId(), record); + pniNextSignedPreKeyId = (pniNextSignedPreKeyId + 1) % Medium.MAX_VALUE; save(); } @@ -906,22 +974,32 @@ public class SignalAccount implements Closeable { public SignalServiceAccountDataStore getSignalServiceAccountDataStore() { return getOrCreate(() -> signalProtocolStore, - () -> signalProtocolStore = new SignalProtocolStore(getPreKeyStore(), - getSignedPreKeyStore(), + () -> signalProtocolStore = new SignalProtocolStore(getAciPreKeyStore(), + getAciSignedPreKeyStore(), getSessionStore(), getIdentityKeyStore(), getSenderKeyStore(), this::isMultiDevice)); } - private PreKeyStore getPreKeyStore() { - return getOrCreate(() -> preKeyStore, - () -> preKeyStore = new PreKeyStore(getPreKeysPath(dataPath, accountPath))); + private PreKeyStore getAciPreKeyStore() { + return getOrCreate(() -> aciPreKeyStore, + () -> aciPreKeyStore = new PreKeyStore(getAciPreKeysPath(dataPath, accountPath))); } - private SignedPreKeyStore getSignedPreKeyStore() { - return getOrCreate(() -> signedPreKeyStore, - () -> signedPreKeyStore = new SignedPreKeyStore(getSignedPreKeysPath(dataPath, accountPath))); + private SignedPreKeyStore getAciSignedPreKeyStore() { + return getOrCreate(() -> aciSignedPreKeyStore, + () -> aciSignedPreKeyStore = new SignedPreKeyStore(getAciSignedPreKeysPath(dataPath, accountPath))); + } + + private PreKeyStore getPniPreKeyStore() { + return getOrCreate(() -> pniPreKeyStore, + () -> pniPreKeyStore = new PreKeyStore(getPniPreKeysPath(dataPath, accountPath))); + } + + private SignedPreKeyStore getPniSignedPreKeyStore() { + return getOrCreate(() -> pniSignedPreKeyStore, + () -> pniSignedPreKeyStore = new SignedPreKeyStore(getPniSignedPreKeysPath(dataPath, accountPath))); } public SessionStore getSessionStore() { @@ -1078,6 +1156,10 @@ public class SignalAccount implements Closeable { return deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID; } + public IdentityKeyPair getIdentityKeyPair(ServiceIdType serviceIdType) { + return serviceIdType.equals(ServiceIdType.ACI) ? aciIdentityKeyPair : pniIdentityKeyPair; + } + public IdentityKeyPair getAciIdentityKeyPair() { return aciIdentityKeyPair; } @@ -1157,12 +1239,12 @@ public class SignalAccount implements Closeable { return UnidentifiedAccess.deriveAccessKeyFrom(getProfileKey()); } - public int getPreKeyIdOffset() { - return preKeyIdOffset; + public int getPreKeyIdOffset(ServiceIdType serviceIdType) { + return serviceIdType.equals(ServiceIdType.ACI) ? aciPreKeyIdOffset : pniPreKeyIdOffset; } - public int getNextSignedPreKeyId() { - return nextSignedPreKeyId; + public int getNextSignedPreKeyId(ServiceIdType serviceIdType) { + return serviceIdType.equals(ServiceIdType.ACI) ? aciNextSignedPreKeyId : pniNextSignedPreKeyId; } public boolean isRegistered() { From 6893e91190192779130488c2c0fa05605ebb1ee6 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 11 Apr 2022 22:24:48 +0200 Subject: [PATCH 075/938] Update dependencies --- build.gradle.kts | 6 +++--- lib/build.gradle.kts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d4bb65e7..fa0b5a7a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,12 +34,12 @@ repositories { dependencies { implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") - implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.1") + implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.2.2") implementation("net.sourceforge.argparse4j", "argparse4j", "0.9.0") implementation("com.github.hypfvieh", "dbus-java-transport-native-unixsocket", "4.0.0") - implementation("org.slf4j", "slf4j-api", "1.7.32") + implementation("org.slf4j", "slf4j-api", "1.7.36") implementation("ch.qos.logback", "logback-classic", "1.2.10") - implementation("org.slf4j", "jul-to-slf4j", "1.7.32") + implementation("org.slf4j", "jul-to-slf4j", "1.7.36") implementation(project(":lib")) } diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 36247362..00a949c2 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -15,10 +15,10 @@ repositories { dependencies { implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_46") - implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.1") + implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.2.2") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") - implementation("org.slf4j", "slf4j-api", "1.7.32") + implementation("org.slf4j", "slf4j-api", "1.7.36") implementation("org.xerial", "sqlite-jdbc", "3.36.0.3") implementation("com.zaxxer", "HikariCP", "5.0.1") } From 8f85d164ffbe745555aada96696c4764af638b6a Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 11 Apr 2022 22:30:25 +0200 Subject: [PATCH 076/938] Update reflect-config.json --- graalvm-config-dir/reflect-config.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 53f611b0..05e9ea7a 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -415,6 +415,10 @@ "allDeclaredMethods":true, "allDeclaredClasses":true }, +{ + "name":"org.asamk.Signal$Error$AttachmentInvalid", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.asamk.Signal$Error$Failure", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] From 406faa1bbb1e08e426b46c2256d59e0899e06109 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 11 Apr 2022 22:26:05 +0200 Subject: [PATCH 077/938] Bump version --- CHANGELOG.md | 12 ++++++++++++ build.gradle.kts | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fba3b39..de705aed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ ## [Unreleased] +## [0.10.5] - 2022-04-11 +**Attention**: Now requires native libsignal-client version 0.15 + +### Added +- New `--ban`, `--unban` flags for `updateGroup` command to ban users from joining by group link + +### Fixed +- Fix plain text output of blocked group ids +- Fix error output in case of rate limiting +- Fix error when creating a group with no members +- Fix adding recent Signal-Desktop versions as linked devices + ## [0.10.4.2] - 2022-03-17 ### Fixed - Crash in json output when receiving message from untrusted identity diff --git a/build.gradle.kts b/build.gradle.kts index fa0b5a7a..33a1a2bc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.11" } -version = "0.10.4.2" +version = "0.10.5" java { sourceCompatibility = JavaVersion.VERSION_17 From a48601b028db52652484d38f83cba97742c27608 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 11 Apr 2022 23:02:41 +0200 Subject: [PATCH 078/938] Update repackage-native-libs.yml --- .github/workflows/repackage-native-libs.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/repackage-native-libs.yml b/.github/workflows/repackage-native-libs.yml index 9f731bd7..34cc9d10 100644 --- a/.github/workflows/repackage-native-libs.yml +++ b/.github/workflows/repackage-native-libs.yml @@ -40,7 +40,7 @@ jobs: - name: Get signal-client jar version id: lib_ver run: | - JAR_PREFIX=signal-client-java- + JAR_PREFIX=libsignal-client- jar_file=$(find ./signal-cli-*/lib/ -name "$JAR_PREFIX*.jar") jar_version=$(echo "$jar_file" | xargs basename | sed "s/$JAR_PREFIX//; s/.jar//") echo "$jar_version" @@ -48,7 +48,7 @@ jobs: - name: Download signal-client builds env: - RELEASES_URL: https://github.com/signalapp/libsignal-client/releases/download/ + RELEASES_URL: https://github.com/signalapp/libsignal/releases/download/ FILE_NAMES: signal_jni.dll libsignal_jni.dylib SIGNAL_CLIENT_VER: ${{ steps.lib_ver.outputs.signal_client_version }} run: | @@ -63,7 +63,7 @@ jobs: SIGNAL_CLIENT_VER: ${{ steps.lib_ver.outputs.signal_client_version }} run: | mv signal_jni.dll libsignal_jni.so - zip -u ./signal-cli-${SIGNAL_CLI_VER}/lib/signal-client-java-${SIGNAL_CLIENT_VER}.jar ./libsignal_jni.so + zip -u ./signal-cli-${SIGNAL_CLI_VER}/lib/libsignal-client-${SIGNAL_CLIENT_VER}.jar ./libsignal_jni.so tar -czf signal-cli-${SIGNAL_CLI_VER}-Windows.tar.gz signal-cli-${SIGNAL_CLI_VER}/ - name: Replace macOS lib @@ -71,7 +71,7 @@ jobs: SIGNAL_CLI_VER: ${{ steps.cli_ver.outputs.signal_cli_version }} SIGNAL_CLIENT_VER: ${{ steps.lib_ver.outputs.signal_client_version }} run: | - jar_file=./signal-cli-${SIGNAL_CLI_VER}/lib/signal-client-java-${SIGNAL_CLIENT_VER}.jar + jar_file=./signal-cli-${SIGNAL_CLI_VER}/lib/libsignal-client-${SIGNAL_CLIENT_VER}.jar zip -d "$jar_file" libsignal_jni.so zip "$jar_file" libsignal_jni.dylib tar -czf signal-cli-${SIGNAL_CLI_VER}-macOS.tar.gz signal-cli-${SIGNAL_CLI_VER}/ From 59b2d2f5a193c0f8524c80b790c22d2c4899663c Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 12 Apr 2022 15:40:28 +0200 Subject: [PATCH 079/938] Add Java 18 to CI workflows --- .github/workflows/ci.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/repackage-native-libs.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17f23ddf..7a59d3cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '17' ] + java: [ '17', '18' ] steps: - uses: actions/checkout@v1 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0ef62f7e..b3fc9aba 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Java JDK uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 18 - name: Checkout repository uses: actions/checkout@v2 diff --git a/.github/workflows/repackage-native-libs.yml b/.github/workflows/repackage-native-libs.yml index 34cc9d10..9a87f723 100644 --- a/.github/workflows/repackage-native-libs.yml +++ b/.github/workflows/repackage-native-libs.yml @@ -135,7 +135,7 @@ jobs: shell: bash # Explicit for windows env: - JAVA_VERSION: 17 + JAVA_VERSION: 18 steps: From 0309c85c6a0307d7bb2db63c40f99c7c44a61468 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 12 Apr 2022 15:44:23 +0200 Subject: [PATCH 080/938] Update gradle wrapper --- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 59821 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..41d9927a4d4fb3f96a785543079b8df6723c946b 100644 GIT binary patch delta 8958 zcmY+KWl$VIlZIh&f(Hri?gR<$?iyT!TL`X;1^2~W7YVSq1qtqM!JWlDxLm%}UESUM zndj}Uny%^UnjhVhFb!8V3s(a#fIy>`VW15{5nuy;_V&a5O#0S&!a4dSkUMz_VHu3S zGA@p9Q$T|Sj}tYGWdjH;Mpp8m&yu&YURcrt{K;R|kM~(*{v%QwrBJIUF+K1kX5ZmF zty3i{d`y0;DgE+de>vN@yYqFPe1Ud{!&G*Q?iUc^V=|H%4~2|N zW+DM)W!`b&V2mQ0Y4u_)uB=P@-2`v|Wm{>CxER1P^ z>c}ZPZ)xxdOCDu59{X^~2id7+6l6x)U}C4Em?H~F`uOxS1?}xMxTV|5@}PlN%Cg$( zwY6c}r60=z5ZA1L zTMe;84rLtYvcm?M(H~ZqU;6F7Evo{P7!LGcdwO|qf1w+)MsnvK5^c@Uzj<{ zUoej1>95tuSvDJ|5K6k%&UF*uE6kBn47QJw^yE&#G;u^Z9oYWrK(+oL97hBsUMc_^ z;-lmxebwlB`Er_kXp2$`&o+rPJAN<`WX3ws2K{q@qUp}XTfV{t%KrsZ5vM!Q#4{V& zq>iO$MCiLq#%wXj%`W$_%FRg_WR*quv65TdHhdpV&jlq<=K^K`&!Kl5mA6p4n~p3u zWE{20^hYpn1M}}VmSHBXl1*-)2MP=0_k)EPr#>EoZukiXFDz?Di1I>2@Z^P$pvaF+ zN+qUy63jek2m59;YG)`r^F3-O)0RDIXPhf)XOOdkmu`3SMMSW(g+`Ajt{=h1dt~ks ztrhhP|L4G%5x79N#kwAHh5N){@{fzE7n&%dnisCm65Za<8r_hKvfx4Bg*`%-*-Mvn zFvn~)VP@}1sAyD+B{{8l{EjD10Av&Mz9^Xff*t`lU=q=S#(|>ls520;n3<}X#pyh& z*{CJf7$*&~!9jMnw_D~ikUKJ2+UnXmN6qak{xx%W;BKuXt7@ky!LPI1qk?gDwG@@o zkY+BkIie>{{q==5)kXw(*t#I?__Kwi>`=+s?Gq6X+vtSsaAO&Tf+Bl$vKnzc&%BHM z=loWOQq~n}>l=EL(5&6((ESsQC3^@4jlO5Od{qN#sWV)vqXw}aA>*uvwZopNN(|-T zRTF%5Y_k1R$;(d-)n;hWex{;7b6KgdAVE@&0pd(*qDzBO#YZV%kh%pYt1`hnQ(Fa& zYiDrOTDqk5M7hzp9kI2h!PxNnuJ&xl*zF8sx6!67bA49R1bmUF5bpK&&{eI0U~cH}PM z3aW1$lRb|ItkG5~_eBNu$|I|vYIdAA9a!pVq<+UTx*M}fG`23zxXp&E=FfnY- zEzKj;Cu_s4v>leO7M2-mE(UzKHL4c$c`3dS*19OpLV^4NI*hWWnJQ9lvzP4c;c?do zqrcsKT*i~eIHl0D3r4N{)+RsB6XhrC^;sp2cf_Eq#6*CV;t8v=V!ISe>>9kPgh}NI z=1UZutslxcT$Ad;_P^;Oouoa(cs!Ctpvi>%aQ+Zp=1d|h{W9Wmf7JWxa(~<#tSZ?C%wu4_5F!fc!<@PIBeJ)Nr^$bB6!_Gic_7}c3J{QI~Gg5g5jTp9}V6KYgrgaX>pJt}7$!wOht&KO|+z{Iw@YL|@~D zMww}+lG}rm2^peNx>58ME||ZQxFQeVSX8iogHLq_vXb`>RnoEKaTWBF-$JD#Q4BMv zt2(2Qb*x-?ur1Y(NsW8AdtX0#rDB?O(Vs4_xA(u-o!-tBG03OI!pQD+2UytbL5>lG z*(F)KacHqMa4?dxa(Vcrw>IIAeB$3cx#;;5r2X;HE8|}eYdAgCw#tpXNy7C3w1q`9 zGxZ6;@1G%8shz9e+!K2MO*{_RjO}Jo6eL3{TSZ>nY7)Qs`Dhi5><@oh0r)gT7H-?3 zLDsd^@m%JvrS8sta5`QiZNs^*GT}Hiy^zjK2^Ni%`Z|ma)D2 zuyumbvw$M8$haCTI~6M%d4+P)uX%u{Sfg4Al+F7c6;O-*)DKI7E8izSOKB#FcV{M+ zEvY0FBkq!$J0EW$Cxl}3{JwV^ki-T?q6C30Y5e&p@8Rd?$ST-Ghn*-`tB{k54W<>F z5I)TFpUC!E9298=sk>m#FI4sUDy_!8?51FqqW!9LN1(zuDnB3$!pEUjL>N>RNgAG~-9Xm|1lqHseW(%v&6K(DZ3Pano(1-Qe?3%J&>0`~w^Q-p&@ zg@HjvhJk?*hpF7$9P|gkzz`zBz_5Z!C4_-%fCcAgiSilzFQef!@amHDrW!YZS@?7C zs2Y9~>yqO+rkih?kXztzvnB^6W=f52*iyuZPv$c42$WK7>PHb z6%MYIr5D32KPdwL1hJf{_#jn?`k(taW?mwmZVvrr=y~fNcV$`}v(8};o9AjOJumS4 z`889O91^pkF+|@$d9wVoZ3;^j;^sUs&Ubo_qD&MTL%O z&*SE0ujG~zm;?x)8TLC&ft))nyI zcg44@*Q{cYT+qGrA=In_X{NNCD+B0w#;@g)jvBU;_8od6U>;7HIo@F*=g8CQUo(u^ z3r4FJ7#<@)MXO&5+DgKE&^>^`r!loe7CWE*1k0*0wLFzSOV8jvlX~WOQ?$1v zk$Or}!;ix0g78^6W;+<=J>z@CBs!<<)HvF(Ls-&`matpesJ5kkjC)6nGB@b{ii6-Uoho$BT%iJgugTOeZ$5Xo4D7Pd< zC*LJh5V@2#5%aBZCgzlQi3@<_!VfiL07ywc)ZbwKPfcR|ElQoS(8x|a7#IR}7#Io= zwg4$8S{egr-NffD)Fg&X9bJSoM25pF&%hf>(T&9bI}=#dPQyNYz;ZZ7EZ=u1n701SWKkZ9n(-qU ztN`sdWL1uxQ1mKS@x11;O|@^AD9!NeoPx}?EKIr!2>1Qq4gjfGU)tr6?Z5l7JAS3j zZeq{vG{rb%DFE4%$szK}d2UzB{4>L?Tv+NAlE*&Nq6g+XauaSI+N2Y8PJLw+aNg1p zbxr|hI8wcMP&&+(Cu|%+Jq|r>+BHk@{AvfBXKiVldN)@}TBS0LdIpnANCVE26WL-} zV}HJ^?m&$Rkq;Zf*i-hoasnpJVyTH__dbGWrB_R55d*>pTyl6(?$EO@>RCmTX1Hzr zT2)rOng?D4FfZ_C49hjMV*UonG2DlG$^+k=Y%|?Dqae4}JOU=8=fgY4Uh!pa9eEqf zFX&WLPu!jArN*^(>|H>dj~g`ONZhaaD%h_HHrHkk%d~TR_RrX{&eM#P@3x=S^%_6h zh=A)A{id16$zEFq@-D7La;kTuE!oopx^9{uA3y<}9 z^bQ@U<&pJV6kq7LRF47&!UAvgkBx=)KS_X!NY28^gQr27P=gKh0+E>$aCx&^vj2uc}ycsfSEP zedhTgUwPx%?;+dESs!g1z}5q9EC+fol}tAH9#fhZQ?q1GjyIaR@}lGCSpM-014T~l zEwriqt~ftwz=@2tn$xP&-rJt?nn5sy8sJ5Roy;pavj@O+tm}d_qmAlvhG(&k>(arz z;e|SiTr+0<&6(-An0*4{7akwUk~Yf4M!!YKj^swp9WOa%al`%R>V7mi z+5+UodFAaPdi4(8_FO&O!Ymb#@yxkuVMrog(7gkj$G@FLA#ENMxG)4f<}S%Fn?Up$+C%{02AgMKa^ z4SFGWp6U>{Q6VRJV}yjxXT*e`1XaX}(dW1F&RNhpTzvCtzuu;LMhMfJ2LBEy?{^GHG!OF!! zDvs64TG)?MX&9NCE#H3(M0K>O>`ca0WT2YR>PTe&tn?~0FV!MRtdb@v?MAUG&Ef7v zW%7>H(;Mm)RJkt18GXv!&np z?RUxOrCfs;m{fBz5MVlq59idhov21di5>WXWD-594L-X5;|@kyWi@N+(jLuh=o+5l zGGTi~)nflP_G}Yg5Pi%pl88U4+^*ihDoMP&zA*^xJE_X*Ah!jODrijCqQ^{=&hD7& z^)qv3;cu?olaT3pc{)Kcy9jA2E8I)#Kn8qO>70SQ5P8YSCN=_+_&)qg)OYBg|-k^d3*@jRAeB?;yd-O1A0wJ z?K*RDm|wE<(PBz~+C%2CTtzCTUohxP2*1kE8Of~{KRAvMrO_}NN&@P7SUO{;zx0iK z@or9R8ydYOFZf(cHASCAatL%;62IL27~SmASr(7F&NMr+#gNw@z1VM z_ALFwo3)SoANEwRerBdRV`>y`t72#aF2ConmWQp(Xy|msN9$yxhZ1jAQ67lq{vbC5 zujj|MlGo`6Bfn0TfKgi(k=gq0`K~W+X(@GzYlPI4g0M;owH3yG14rhK>lG8lS{`!K z+Nc@glT-DGz?Ym?v#Hq|_mEdPAlHH5jZuh*6glq!+>Lk$S%ED2@+ea6CE@&1-9a?s znglt|fmIK}fg<9@XgHe4*q!aO<-;Xj$T?IzB-{&2`#eA6rdtCi80mpP&vw(Uytxu$#YzNI_cB>LS zmim>ys;ir;*Dzbr22ZDxO2s;671&J0U<9(n1yj)J zHFNz=ufPcQVEG+ePjB<5C;=H0{>Mi*xD>hQq8`Vi7TjJ$V04$`h3EZGL|}a07oQdR z?{cR(z+d>arn^AUug&voOzzi$ZqaS)blz-z3zr;10x;oP2)|Cyb^WtN2*wNn`YX!Y z+$Pji<7|!XyMCEw4so}xXLU)p)BA~2fl>y2Tt}o9*BPm?AXA8UE8a;>rOgyCwZBFa zyl42y`bc3}+hiZL_|L_LY29vVerM+BVE@YxK>TGm@dHi@Uw*7AIq?QA9?THL603J% zIBJ4y3n8OFzsOI;NH%DZ!MDwMl<#$)d9eVVeqVl(5ZX$PPbt*p_(_9VSXhaUPa9Qu z7)q4vqYKX7ieVSjOmVEbLj4VYtnDpe*0Y&+>0dS^bJ<8s*eHq3tjRAw^+Mu4W^-E= z4;&namG4G;3pVDyPkUw#0kWEO1;HI6M51(1<0|*pa(I!sj}F^)avrE`ShVMKBz}nE zzKgOPMSEp6M>hJzyTHHcjV%W*;Tdb}1xJjCP#=iQuBk_Eho6yCRVp&e!}4IBJ&?ksVc&u#g3+G$oNlJ?mWfADjeBS-Ph3`DKk-~Z70XugH8sq2eba@4 zIC1H_J$`9b$K`J)sGX3d!&>OmC@@rx1TL~NinQOYy72Q_+^&Mg>Ku(fTgaXdr$p_V z#gav1o{k~c>#)u3r@~6v^o)Lf=C{rAlL@!s457pq)pO;Cojx7U{urO4cvXP|E>+dV zmr2?!-5)tk-&*ap^D^2x7NG6nOop2zNFQ9v8-EZ{WCz-h36C)<^|f{V#R_WE^@(T0+d-at5hXX{U?zak*ac-XnyINo+yBD~~3O1I=a z99|CI>502&s-Qi5bv>^2#cQ%ut<4d7KgQ^kE|=%6#VlGiY8$rdJUH{sra;P~cyb_i zeX(kS%w0C?mjhJl9TZp8RS;N~y3(EXEz13oPhOSE4WaTljGkVXWd~|#)vsG6_76I)Kb z8ro?;{j^lxNsaxE-cfP;g(e;mhh3)&ba}li?woV2#7ByioiD>s%L_D;?#;C#z;a(N z-_WY<=SH42m9bFQ>Nb z@4K$@4l8pD7AKxCR>t0%`Qoy9=hA?<<^Vcj8;-E+oBe3ReW1`el8np8E$k{LgFQ}2 z2t8a`wOXFdJ9!5$&mEfD1CnJ)TB+RJih88-Zos9@HZ# zL#{qfbF0ARTXkR@G{lwlOH~nnL)1jcyu!qv2`57S&%oKz0}r{~l9U_UHaJ5!8#nrs z?2FrL`mxnzu&{bweD&62)ilz*?pYIvt`T!XFVVA78})p1YEy7 z8fK#s?b~Yo$n7&_a?EBdXH-_W)Z44?!;DFx6pZ?~RArtBI*Qm4~6nX6Z_T*i$bQPE;Qz?DAPstpGSqr-AJ zo%m9cA`oDDm?&dTaoh_>@F>a?!y4qt_;NGN9Z<%SS;fX-cSu|>+Pba22`CRb#|HZa z;{)yHE>M-pc1C0mrnT~80!u&dvVTYFV8xTQ#g;6{c<9d!FDqU%TK5T6h*w*p980D~ zUyCb`y3{-?(mJFP)0*-Nt;mI$-gc4VQumh|rs&j_^R{sgTPF`1Xja2YWstsKFuQ(d zmZMxV$p$|qQUXchu&8%J(9|)B?`~rIx&)LqDS>ob5%gTeTP#Sbny#y*rnJ&?(l=!( zoV~}LJ1DPLnF8oyM(2ScrQ0{Q4m4-BWnS4wilgCW-~~;}pw=&<+HggRD_3c@3RQIr z9+-%!%}u_{`YS=&>h%kPO3ce}>y!d-zqiniNR-b5r97u;+K6HA2tS>Z#cV{+eFI`* zd8RMGAUtX1KWfPV;q<-5JAykS+2sY$2~UX+4461a(%{P#{rwFPu0xpIuYlbgD{C7C z=U{FUarVTYX6ZUq3wE@G^QT4H2Re;n$Fz9cJ>hABl)9T8pozqbA1)H-%1=WKm^QMu zjnUZ&Pu>q+X&6Co*y#@pxc-4waKMInEPGmE_>3@Ym3S*dedSradmc5mlJn`i0vMW6 zhBnGQD^Z;&S0lnS0curqDO@({J7kTtRE+Ra?nl^HP9<)W&C>~`!258f$XDbyQOQXG zP8hhySnarOpgu8xv8@WlXnm(Uk~)_3$Sg0vTbU3 z{W!5B(L3{Yy3K5PN<@jEarAtja`}@KYva&zFRF*s+_%jIXh$T(S=an8?=Ry3H*NRqWgsM`&!#|@kf1>=4q%bFw7^Rhz!z5I zyI^zU8_R1WN9`88Z=n>pIZQ`Ixr~_9G%Q}@A7rd#*%y7G zXl^Id=^ZL?Rx}}gWXCqzj9C6;x(~mAH|$JteXa1MH<6UQig@!Hf~t}B%tP0I|H&;y zO6N0}svOa1a^PyP9N5?4W6VF%=Bj{qHUgc8@siw4bafT=UPFSoQqKgyUX>sXTBZ=x zOh^Ad!{kOM9v{%5y}`-8u*T&C7Vq6mD%GR}UeU(*epO&qgC-CkD;%=l)ZuinSzHM` z{@`j&_vC6dDe{Yb9k@1zeV_K6!l(@=6ucoI=R^cH=6{i71%4W3$J-?<8Qn#$-DMtA z6Qqi)t?4ifrt%3jSA#6ji#{f(($KBL-iQh-xrC||3U3lq`9>r)>X%oLvtimuHW-)} zy}>9~|M>w4eES`g7;iBM%Se5-OP%1U6gNWp3AZqT8C6OlFFfQ$|7LL;tBV)(qlp4K zruar^K8FnJN3@_}B;G`a~H`t|3+6d>q3#`ctTkE-D^1#d9NalQ04lH*qUW2!V zhk7#z8OwHhSl8w14;KctfO8ubZJ4$dEdpXE78wABz=n5*=q9ex3S}`e7x~~V-jmHOhtX2*n+pBslo3uosdE7xABK=V#-t{1Hd~?i z{i~%Bw6NYF+F$aK$M`r#xe=NxhA5=p%i7!$);sd>Q}#`G?Q~fygrMXmZw?0#5#17W}6Tj+&kFexG{!mYl5FoA99}3G9l;3lVQ^ z48^~gsVppE*x91WheqI(A%F0Z#$#1UJP1R12Mj9r)y(A?a+iquX+d8WD4WAQJ_!oq z9rTISr7bPd(GTP57xm$}C}&kjMivi;zi^Y9g3&X0A;ovdJ?{%_wHgt%%9P&N4H z^XzV(uNA4 zAP`hgP6BEN5`YXh|DF~6Pud?~gWfhUKoPX4>z|}0aocC&K+AoV%|SX*N!wGq3|y< zg4lP(04XIPmt6}$N!dTk+pZv>u;MTB{L4hp9uXk7>aS!6jqM2lVr%{)H3$O127TSZ z0x9hi0k-P?nWFdQ0K`pykqUIT&jD~B0tHP{ffS(}fZ(aW$oBWTSfHO!A^><6vA?qar%tzN-5NQO zL&|F{nGiQyzNJ+bM$Y`n=Lx^3wTG^o2bGB@cwr1eb+6c-1tN=U+Db;bc~eJ!hwM{SbI=#g?$!PjDB+) zPgU_2EIxocr*EOJG52-~!gml&|D|C2OQ3Y(zAhL}iae4-Ut0F*!z!VEdfw8#`LAi# zhJ_EM*~;S|FMV6y%-SduHjPOI3cFM(GpH|HES<}*=vqY+64%dJYc|k?n6Br7)D#~# zEqO(xepfaf2F{>{E2`xb=AO%A<7RtUq6kU_Iu0m?@0K(+<}u3gVw5fy=Y4CC*{IE3 zLP3YBJ7x+U(os5=&NT%gKi23bbaZ`@;%ln)wp4GpDUT$J8NtFDHJzIe_-t}{!HAsh zJ4<^WovY};)9IKAskSebdQiXv$y5}THuJZ}ouoElIZRui=6lrupV|_Jz=9^&;@HwL;J#@23k?A;k`0Bgf;ioO>W`IQ+4? z7A)eKoY4%+g%=w;=Vm8}H>@U*=*AWNtPqgWRqib#5RTGA@Q=43FrQn3J`GkTUV5yp0U`EOTqjfp+-9;0F8!dMEwwcK%(6`8sDD^aR04 zd6O5vh|Xk?&3dy4f|1QK&Ulf{h6Iq;d-&*ti#Ck>wZFG;GHwc?b;X~eBITx49>2d8 z4HcK&1&DvEGT6kXdzAm4oO8%c}8OBt~8H956_;YP-ss*uMf==a+%w~F>Qkm7r)IAuxuoX}h92$gHqbFUun#8m zWHdy`Zrm#=Pa98x8cO0vd@Tgkr*lm0{dky+Gocr0P8y%HGEI#c3qLqIRc`Oq_C%*; zG+QTr(#Q|yHKv6R@!DmLlwJQ3FAB)Yor-I4zyDyqM4yp5n2TrQH>gRt*Zw0+WI-Sj`EgmYHh=t9! zF6lz^xpqGGpo6!5`sc0a^FVhy_Uxq|@~(1@IIzV)nTpY9sY`CV!?8e&bB8=M&sYEb z2i}fvKdhp9Hs68Y-!QJ<=wE(iQ5+49tqt;Rh|jhYrI5VW-mIz|UY{h8E=rC5sh#DU z?wGgk-Tn!I?+Zer7pHlF_Z^!Kd1qkS3&lv#%s6-<5Y%jQL${cge5=G5Ab?D&|9$Y~ zf%rJC2+=2vg;y0-SJb3<@3%}BO$T$C66q$L_H33a`VUbgW~N(4B=v5(<=My|#|J7q z*Ox4wL4kbJd_~EjLTABSu4U7Jk#`y(6O*U6(k6XxM}CtGZB(H@3~kh*zaGRXM}Iwp zQ%xFk2>@wiZrVCV_G4G~v;NebCQ%T7{SDyPpSv&dT@Cn)Mx@IK*IdNrj{*4pkV4wv z)y0J538h>cpB7iPSzA~x24T`{dzNkpvGIqvt1Dvdq@o-`B=$hkczX8$yFMhsWNK-X zxr$kR$tMD0@W)Vxe1^t9qVmsg&K^F@u84)(n2dttIEAZFN6VD$&tskpG%SI7whGL3 z)DeRiwe&?8m7U{G`oW8!SCi*dM>oYL%UKQnKxV_0RXAEBQg1kStExGEUVwLJ0orGGwb7uv+kPDl7_E2*iD|J*=8A@;XCvwq0aw5oJYN*Yh&o=l} z2z8YKb-fIAH5spql4eXqp*)o2*b>#1@DSt?zZi{GPj0gH&Nm+EI<3^z0w%YTEV4xw zI6$+=Faa|Y4o5i0zm5lOg|&tmnJ806DBovU@Ll6XsA;NRrTK~t*AAJIAS=v-UZ%Pr z$oddI@NRir&erzCwq|)ciJemr-E061j{0Vc@Ys7K(mW|JYj*$+i1Q8XlIK8T?TYS(AXu$`2U zQ@fHxc=AVHl_}cRZQ)w0anMEoqRKKIvS^`<-aMf*FM`NsG&Uowneo+Ji$7DUDYc7*Hjg;-&aHM%3 zXO6cz$$G};Uqh+iY7Wpme>PHG4cu(q;xyskNLs$^uRRMfEg?8Cj~aE-ajM%CXkx0F z>C?g3tIA#9sBQOpe`J+04{q7^TqhFk^F1jFtk4JDRO*`d-fx`GYHb=&(JiaM1b?Y^ zO3Kj3sj76ieol|N$;>j@t#tKj=@*gP+mv}KwlTcPYgR$+)2(gk)2JNE=jSauPq!$< z<|?Sb%W)wS)b>b6i{8!x!^!xIdU3{CJFVnTcw0j{M%DUCF=_>eYYEUWnA-|B(+KYL z_W_`JI&&u^@t0})@DH^1LDuT0s3dMpCHIbYBgOT4Zh_4yHbSqRbtIKndeT4Q*Jg91 z@>rO!^t-G~*AIW;FQ$3J=b;oGg8?CTa~qNCb>&cgp@e;?0AqA&paz~(%PYO+QBo4( zp?}ZdSMWx0iJm7HVNk9A#^9Osa#GPJ!_pYEW}($8>&2}fbr@&ygZ?${A7_9?X$(&5 z#~-hxdPQwCNEpf=^+WH-3`2LxrrBMTa}~qJC9S;VzhG!On^JLyW6WkF{8aAE$sM+( zxr8xLW(KIjI`Rm(24r3OJBk<3GF=G!uSP0-G&AY32mLm8q=#Xom&Pqv=1C{d3>1^ zAjsmV@XZ%BKq^eUfBpa8KvO8ob|F3hAjJv*yo2Bhl0)KUus{qA9m8jf)KnOGGTa6~4>3@J_VzkL|vYPl*uL+Ot*Q7W!f5rJw5+AsjP_IfL+-S*2p| zB7!FhjvkUTxQkGWGSg{X;h~dK>gAJivW?88Nu!3o>ySDaABn$rAYt086#27fbjPQS zhq>55ASvm*60qRdVOY9=bU^+{Pi#!OaZwENN;zy5?EztOHK-Q5;rCuiFl}BSc1YaQ zC-S{=KsGDz@Ji9O5W;XxE0xI|@3o6(2~i4b8Ii9VT;^G$*dRw(V?=br)D&q^XkeBX z+gl~+R@rVD-Hwv@7RHV?Bip5KMI)aV^&snt?H<$Nt=OPx#VxF&BGi?2A2+lNOYywNUGMeGL;|(=UjGDtLG0sN&LpGx;|U;xa13s z;W_|SPk^G}!M9_^pO zA3bt3-tca%^42sHeDtfcC0S3w3H1ny!Bxpa=*k?XRPpx9Bb-gx1J9Yvx)4J(8cG+q z(iCPZ9dsf3#QVyZgD_MW#G#qgV)olu$59&3(PzQfw@%4uZ~<5J=ABvdY43(Qnp{;G zHg3>@T#>DbTuhFl3)fb3TFqdh)V2aq7!;&JOHseTWukvA7}(iGUq;v-{2J0iHSNHq z;+)h!p6Ok^+Sp8-jgL($n6Qu47xyE`cFO5SdZR6;R!FET`tm#0D37z339Suxjpv+s z*=%2-N$N?X&0?x_uut3erF@aBGj;9$k9?3FlbDO{RQa1_qtxrh4!4#fjp4x~akvdTp@ zos?^Q&XE;3N93s4rHQGPrV7+au1$$aB6$hLy*Yz_kN$~dweb9PcB!eYVQTGjFuJP> zZCEwBtb>TIgIO^qAzq@Bv-qud_ZD-2W<_at&ml-gv`tPt$@DF5`HlA zM>DmmMkpv&Zm-8)Y#0bLQf4MpD4_-7M8eu6rh(tL8dq8onHs#R9J~dGd2IaXXMC~h z91pKhnQa%Fsn29nAA1;x(%oC zhca~qQDJaMf?wFrl-Pj;e$bZMYmMF!Y3Lv&Sb?Sjn#!NVx&NDyc^$b4uYyo2OmERa zRz;yDGd@JTykzFLe|Wk-y7#3x`6$wt$zR8r48mdUvfbeL+4D|Z``~7$PrE@qc7rZe zVsIoIbCwzjLZ@_M1*bD{HaYn();Z1-q*-I{tEnTZ(}Zmk&%MXSNBX>o| z-u*RNkAyKC-Srp7c-=@5f)xMWg>o2WWl}j6j9=8+D8;T z>0*0q#;qw8%U8i;6s0fu#I*%(g*@@a2Er@@nyI}{=@W{Z-;`=wN4N~>6Xrh&z#g}l zN1g5}0-#(nHUTv_rl2{yUZ;h#t&Fd?tY!7L%ClY)>uH-Ny2ET$lW$S)IQiN79H)D^ zb&0AXYkupy0~w8)*>Sj_p9}4L?lGTq%VG|2p`nWGhnM^!g|j-|O{%9Q%swOq63|*W zw$(N_laI}`ilB+o!a-wl?er~;;3+)$_akSQ!8YO_&-e*SI7n^(QQ;X0ZE`{4f!gAl z5$d+9CKVNonM!NO_frREICIAxOv)wm>}-k?iRisM`R7;=lyo|E_YR~FpS&PS`Lg0f zl-ON<0S%Uix8J%#yZdkCz4YNhcec<|7*P(JsM#>-L>+tYg_71q9~70FAc^6KW5jql zw!crdgVLH1G_eET=|SEc977;)ezVC|{PJZfra|}@rD;0s&@61mTEBJtILllg{%{vN zfhb&lq0yChaLhnJ-Qb62MB7`>M;|_ceHKZAeeh@#8tbrK!ArP6oXIhMK;dhEJTY`@ z0Tq>MIe0`7tGv)N*F0IGYSJv0vN?Az8g+4K9S!pW2~9F4W(_U_T=jCZrzuZ3*|__T zONp_UWmyePv8C~rckc?Xji;Z5OEqg zC*Um)i;Wh4TEwqReQdVVbUKT^2>Tpi6z_^-uF*adUFug4i@JhzpWT^Sk&E>CyP2?H zWf6x}ehuTs6wvzCnTU&gYzT029Nz19(In1WC z`(1IGmi!O%2AR|BjQa4Q0~u)kM%}?xQyjWuQ16^Gp++;`vr7!k--UZWM*~7Zl|ceO@I3`OpaRhD;YoCuo5IC0uHx>9 z478hu@H|e0Zlo)Zj@01#;8BDs@991xe~^9uG2}UXLM(m7fa}AMwX*tjioBeV&Q8Gx zSq$6wZFkRBK`cMI>R(@W@+lo2t)L+4q-negWRLWZBz*|%=W4v62JrmzNuOtA*x)QE z5L%=OH#@KMdB%Jp^r?0tE}5-*6oP`-lO7Sf)0)n*e<{HA=&qhLR)oD8-+V}Z4=md) z+k9lKf64DB2hAT)UaCP~di?-V3~JBH7itYyk~L6hrnxM%?RKntqd`=!b|e7eFnAcu z3*V;g{xr7TSTm$}DY%~SMpl>m{Sj!We+WfxSEor?YeiAxYUy25pn(?T()E>ByP^c@ zipwvWrhIK((R((VU+;@LmOnDu)ZXB3YArzzin!Z^0;PyJWnlfflo|q8(QY;o1*5CO z##hnkO{uynTMdk`~DOC#1 zdiYxQoy}=@7(ke#A8$YZZVtk4wo$8x28&I;cY3Ro-|kW=*yiiHgCLZeAr)UtVx>Tu z|LvL0hq|1-jC0I4x#>&QZCfrVB=zT!nR|~Uz`9%~2 znl{uZ{VEszW`Fad^q_HB!K9*|U-stK%?~;g?&&+12A}Rq$z($Bzuk^2X(Y=hF?-dQ ztc3DsQKI;qhWIV`99Q#R3xnU0AvY!i*BECj-z9l74|%O=V@nlv|qqC^r^-~C?E zGW%c|uYgnfJ(gjsTm_cIqcv*mYM{+i+&@F@+69ZQOK&u#v4oxUSQJ=tvqQ3W=*m;| z>SkBi8LYb-qRY7Sthh*0%3XAC%$z1rhOJzuX=PkTOa=DlocZUpE#KxVNH5)_4n=T( zGi3YrH7e~sPNYVBd~Grcq#CF~rN{p9Zza-Ntnwfma@TB)=3g36*0lSZg#ixEjFe%+ zX=&LDZ5zqculZ`=RYc^ln(~;nN|Qh6gN=!6f9-N2h+3NWbIxYud&;4SX*tWf5slk4 z{q@@l71UAZgj~*6edXb57fBUxvAS7s(RI=X868JM0+^DCn2yC>;v%S;qPOjB>YVsz(Zx9a>>BK&M zIQK>7_n)4ud0X5YM}^i*keH{ehLsiy9@NvOpsFeQjdI6anLGvVbBw_*fU1TzdVS$i z*4j7z!I5RF#rSz|8ibi$;qE{4`aqWYik7QB5U&F5C*;TO_x+gtzPGpzNt!7~nsBT7)Ckc(K~%uv&{{6A`mmBJVAk-{s~52Vu|HbCH7_W1~ZCX^RflOakGg=jo2Z z<*s;5-J+2@^LRDZ-7EV&Pq+FTErw@pfFqvx^i%E7Fx#^n(E`m2(c>K-O5`M`Yek9el zzTGs5qD6*G;y#~xu3>qWuO?-amKYtvRA}I9z#UspEeM;wOERYeot_n_EUMJf$4_u?E!6X~?q)tPoZb^_;8Y_Ox2h1m<+Le-fsRd|T8db<8#$bqez zua^Z|>h%zdnuU^ww$#-dZ9NTM`FN+!IlLkz*FqWb!x^Z|C{KyGjZ+>G;;7Mb@LY|H zc+Gp`L((Dw7pnDlHNm&;SfHedhx*kad$I^uGz{`0BYelq0yEUHpNKSkvj$|dpvY3{7*YGyhXA^LP0&wOw9oNoC=QoVx1<2Dne8qqZL zm>nFh5DX(-RnQwvHCZQwn^#Z=E!SPVlaRJ78Bo@}!!9dRt^qZy?-*`Pt4WSmgucJv zV1yFkcjlEM^uz-;b#Q7ZCP@Lk)m}uPX={R4B=56k7WNh11BN~0T*vr@!!ow^B0hOR zQ)4)&(e%>bNNL%bm<&8H{*l_L7s0$2GUgX2Vd;=4d9Dm2v3TaL+;L>{K7h7 zV#k?xDPm(NDE31$ z<}|X)pEY6myjK+^gaIMk&Yj2~F0rSKemNqlsVm4c|N7mp_C*L01s;GNx#D-*&gk!qQr}^?_r@q!8fuXw!)fA7xkd} zb>vHvdx~H$5qqAWrow7}+8zBM65-JOt5z za=T6f7MK`XJuQog8kIEboPdhcaVJeHy)5z7EBLK5NRr()E|#K0L0N^JD@pUA^Czb` zbUZ_558y+vqAGeyHCbrvOvLD67Ph}06959VzQ_|>RrXQAqE+AQ(-AaKdxoWaF8hdt z{O3W@b^*o#-f1VuU>YMV03ELF7zkCN4Q&b#prz%3Nne0lSbRo@@ z^ihv%oIl~Qyl6Q;a#$*jOC%x0_;eis*)J7=f@Ct*)xF5 zo}u~@-I}2|$b%5L7>@+Z?4o+1r&v6ceIy+vroK&jCQ<4q&45HP2wCol4hVm3pZtjf zHz1D7oyaSKJ~T{Gx}7ONLA)D5k(%%`WswrDyzX*rn}i}}TB4^y#@mAwPzoC)`?rYv zHgx|trUN#mu*VzUV~8TnJM2Qh*ZM5B{x&y>5An`(M7=Z*Q>TdiH@j*2=moNuOtvpz z+G`@~-`%~+AgPKgke@XiRPgndh@bp*-HRsh;HTtz@-y_uhb%7ylVOTqG0#u?Vn5c5 zEp*XRo|8hcgG^$#{$O9CJ&NE;TrfRpSnLmes&MO{m=N%zc`}gb!eQ7odl$oy1%PI} z#AIxx%oRVy&{O~9xnK4$EY>(eQj}!HKIV$Fz*H=-=Kn)N0D6u`(;iO|VraI4fu_W` z;b5{7;Lyx4za}DU#+U7}=H0dAS#YJJ&g2!P@Htu-AL&w=-)*%P9h2{wR|@?Ff9~)b z^+e_3Hetq7W%ls{!?<6&Y$Z;NNB41pvrv)|MET6AZXFXJeFqbFW5@i5WGzl?bP+~? z*&_puH;wKv2)9T_d+P`bLvJFqX#j&xa*-;0nGBbQf0DC>o~=J_Wmtf*2SZQr?{i~X z9-IbRH8{iy?<0v9Ir1?$66+igy|yDQ5J~A9sFX@Pe<*kCY8+MwH?I z`P}zfQ6l^AO8ehZ=l^ZR;R%uu4;BK*=?W9t|0{+-at(MQZ(CtG=EJFNaFMlKCMXu30(gJUqj5+ z`GM|!keqcj;FKTa_qq;{*dHRXAq157hlB@kL#8%yAm2AgfU|*rDKX@FLlp=HL8ddv zAWLCHe@DcDeB2}fl7#=0+#<05c3=VqM*O3bkr@9X4nO|)q0hU;Gye{L8ZN*NH8Id@mP-u;Fmb8YuorjLrW&ndip8CN%_qp982r w1WEnz9^$&s1hkp_3#lPJQ~!HI7WYYjA7>z!`?f%npAh2%rB@vD|Lau$2O)#1n*aa+ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..aa991fce 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From f968d9bf3b1793b9137a48219775a366c63c1fff Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 12 Apr 2022 15:45:42 +0200 Subject: [PATCH 081/938] Use Java 17 for codeql --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b3fc9aba..0ef62f7e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Java JDK uses: actions/setup-java@v1 with: - java-version: 18 + java-version: 17 - name: Checkout repository uses: actions/checkout@v2 From a0c304d3cab59073ea37949b9d580cd9fb20c3e1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 18 Apr 2022 11:02:29 +0200 Subject: [PATCH 082/938] Update dependencies --- build.gradle.kts | 2 +- graalvm-config-dir/reflect-config.json | 4 ++++ lib/build.gradle.kts | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 33a1a2bc..d0ef32f6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,7 +38,7 @@ dependencies { implementation("net.sourceforge.argparse4j", "argparse4j", "0.9.0") implementation("com.github.hypfvieh", "dbus-java-transport-native-unixsocket", "4.0.0") implementation("org.slf4j", "slf4j-api", "1.7.36") - implementation("ch.qos.logback", "logback-classic", "1.2.10") + implementation("ch.qos.logback", "logback-classic", "1.2.11") implementation("org.slf4j", "jul-to-slf4j", "1.7.36") implementation(project(":lib")) } diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 05e9ea7a..7827feef 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -3141,6 +3141,10 @@ {"name":"record_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.storage.protos.StoryDistributionListRecord", + "allDeclaredFields":true +}, { "name":"org.whispersystems.signalservice.internal.util.JsonUtil$AciDeserializer", "methods":[{"name":"","parameterTypes":[] }] diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 00a949c2..c0434285 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_46") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_47") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.2.2") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") From 275be59c252efa9affe5cc10fbfd4fd25c435c3f Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 26 Apr 2022 22:53:47 +0200 Subject: [PATCH 083/938] Fix graalvm native build Fixes #944 --- graalvm-config-dir/reflect-config.json | 24 ++++++++++++++++---- graalvm-config-dir/serialization-config.json | 8 +++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 7827feef..9f7f9526 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -495,7 +495,11 @@ "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] + "methods":[ + {"name":"","parameterTypes":["java.lang.String","java.lang.String"] }, + {"name":"deviceLinkUri","parameterTypes":[] }, + {"name":"deviceName","parameterTypes":[] } + ] }, { "name":"org.asamk.signal.commands.FinishLinkCommand$JsonFinishLink", @@ -578,7 +582,11 @@ "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.String"] }] + "methods":[ + {"name":"","parameterTypes":["java.lang.Boolean","java.lang.String"] }, + {"name":"captcha","parameterTypes":[] }, + {"name":"voice","parameterTypes":[] } + ] }, { "name":"org.asamk.signal.commands.StartLinkCommand$JsonLink", @@ -592,7 +600,11 @@ "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] + "methods":[ + {"name":"","parameterTypes":["java.lang.String","java.lang.String"] }, + {"name":"pin","parameterTypes":[] }, + {"name":"verificationCode","parameterTypes":[] } + ] }, { "name":"org.asamk.signal.json.JsonAttachment", @@ -1049,7 +1061,11 @@ "name":"org.asamk.signal.manager.storage.recipients.RecipientAddress", "allDeclaredFields":true, "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"number","parameterTypes":[] }, + {"name":"uuid","parameterTypes":[] } + ] }, { "name":"org.asamk.signal.manager.storage.recipients.RecipientStore$Storage", diff --git a/graalvm-config-dir/serialization-config.json b/graalvm-config-dir/serialization-config.json index 0d4f101c..bf554e06 100644 --- a/graalvm-config-dir/serialization-config.json +++ b/graalvm-config-dir/serialization-config.json @@ -1,2 +1,6 @@ -[ -] +{ + "types":[ + ], + "lambdaCapturingTypes":[ + ] +} From 5837a6982b1274952868b9ff68475a0ac9e512b2 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 2 May 2022 17:13:30 +0200 Subject: [PATCH 084/938] Fix deleting a recipient which has no uuid Fixes #946 --- .../manager/storage/recipients/RecipientStore.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 04eeea91..9caac144 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 @@ -279,11 +279,13 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile synchronized (recipients) { logger.debug("Deleting recipient data for {}", recipientId); final var recipient = recipients.get(recipientId); - storeRecipientLocked(recipientId, - Recipient.newBuilder() - .withRecipientId(recipientId) - .withAddress(new RecipientAddress(recipient.getAddress().uuid().orElse(null))) - .build()); + recipient.getAddress() + .uuid() + .ifPresent(uuid -> storeRecipientLocked(recipientId, + Recipient.newBuilder() + .withRecipientId(recipientId) + .withAddress(new RecipientAddress(uuid)) + .build())); } } From a80e18169a9cc68c59c5d7bc6dcdc7b747dfcf37 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 14 May 2022 15:08:19 +0200 Subject: [PATCH 085/938] Update libsignal-service-java --- graalvm-config-dir/jni-config.json | 15 ++++++- graalvm-config-dir/reflect-config.json | 4 ++ lib/build.gradle.kts | 2 +- .../org/asamk/signal/manager/ManagerImpl.java | 3 +- .../signal/manager/api/MessageEnvelope.java | 5 ++- .../signal/manager/config/ServiceConfig.java | 5 ++- .../signal/manager/helper/GroupV2Helper.java | 3 +- .../helper/IncomingMessageHandler.java | 15 ++++--- .../signal/manager/helper/SendHelper.java | 6 ++- .../manager/util/MessageCacheUtils.java | 13 ++++-- .../asamk/signal/ReceiveMessageHandler.java | 6 ++- .../asamk/signal/dbus/DbusManagerImpl.java | 4 +- .../dbus/DbusReceiveMessageHandler.java | 42 ++++++++++--------- .../signal/json/JsonSyncDataMessage.java | 7 +++- 14 files changed, 84 insertions(+), 46 deletions(-) diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index a852dc92..27abd5be 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -81,6 +81,7 @@ { "name":"org.signal.libsignal.protocol.IdentityKey", "methods":[ + {"name":"","parameterTypes":["long"] }, {"name":"","parameterTypes":["byte[]"] }, {"name":"serialize","parameterTypes":[] } ] @@ -102,7 +103,10 @@ }, { "name":"org.signal.libsignal.protocol.SignalProtocolAddress", - "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }] + "methods":[ + {"name":"","parameterTypes":["long"] }, + {"name":"","parameterTypes":["java.lang.String","int"] } + ] }, { "name":"org.signal.libsignal.protocol.UntrustedIdentityException", @@ -159,7 +163,10 @@ { "name":"org.signal.libsignal.protocol.state.SessionRecord", "fields":[{"name":"unsafeHandle"}], - "methods":[{"name":"","parameterTypes":["byte[]"] }] + "methods":[ + {"name":"","parameterTypes":["long"] }, + {"name":"","parameterTypes":["byte[]"] } + ] }, { "name":"org.signal.libsignal.protocol.state.SessionStore" @@ -171,6 +178,10 @@ { "name":"org.signal.libsignal.protocol.state.SignedPreKeyStore" }, +{ + "name":"org.signal.libsignal.zkgroup.InvalidInputException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.sqlite.Collation" }, diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 9f7f9526..b2e12b5e 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2560,6 +2560,7 @@ {"name":"delete_"}, {"name":"expireTimer_"}, {"name":"flags_"}, + {"name":"giftBadge_"}, {"name":"groupCallUpdate_"}, {"name":"groupV2_"}, {"name":"group_"}, @@ -2953,6 +2954,8 @@ {"name":"expirationStartTimestamp_"}, {"name":"isRecipientUpdate_"}, {"name":"message_"}, + {"name":"storyMessageRecipients_"}, + {"name":"storyMessage_"}, {"name":"timestamp_"}, {"name":"unidentifiedStatus_"} ] @@ -3042,6 +3045,7 @@ "fields":[ {"name":"address_"}, {"name":"bitField0_"}, + {"name":"destinationUuid_"}, {"name":"groupId_"}, {"name":"needsReceipt_"}, {"name":"senderDevice_"}, diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index c0434285..e1590dc2 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_47") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_48") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.2.2") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index addc3bb7..c7f1d22f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -540,7 +540,8 @@ class ManagerImpl implements Manager { .resolveSignalServiceAddress(context.getRecipientHelper().resolveRecipient(quote.author())), quote.message(), List.of(), - resolveMentions(quote.mentions()))); + resolveMentions(quote.mentions()), + SignalServiceDataMessage.Quote.Type.NORMAL)); } if (message.sticker().isPresent()) { final var sticker = message.sticker().get(); 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 7c6cd58c..67a33613 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 @@ -517,7 +517,7 @@ public record MessageEnvelope( long expirationStartTimestamp, Optional destination, Set recipients, - Data message + Optional message ) { static Sent from( @@ -534,7 +534,8 @@ public record MessageEnvelope( .stream() .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))) .collect(Collectors.toSet()), - Data.from(sentMessage.getMessage(), recipientResolver, addressResolver, fileProvider)); + sentMessage.getDataMessage() + .map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider))); } } 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 de248755..422d0db7 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 @@ -31,7 +31,7 @@ public class ServiceConfig { public static final AccountAttributes.Capabilities capabilities; static { - capabilities = new AccountAttributes.Capabilities(false, true, false, true, true, true, true, false); + capabilities = new AccountAttributes.Capabilities(false, true, false, true, true, true, true, false, false); try { TrustStore contactTrustStore = new IasTrustStore(); @@ -50,7 +50,8 @@ public class ServiceConfig { try { try { org.signal.libsignal.internal.Native.UuidCiphertext_CheckValidContents(new byte[0]); - } catch (IllegalArgumentException ignored) { + } catch (Exception e) { + logger.trace("Expected exception when checking libsignal-client: {}", e.getMessage()); } return true; } catch (UnsatisfiedLinkError e) { 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 d56ce98f..06a0b89a 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 @@ -529,7 +529,8 @@ class GroupV2Helper { final var today = currentTimeDays(); if (groupApiCredentials == null || !groupApiCredentials.containsKey(today)) { // Returns credentials for the next 7 days - groupApiCredentials = dependencies.getGroupsV2Api().getCredentials(today); + final var isAci = true; // TODO enable group handling with PNI + groupApiCredentials = dependencies.getGroupsV2Api().getCredentials(today, isAci); // TODO cache credentials on disk until they expire } var authCredentialResponse = groupApiCredentials.get(today); 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 3da0c729..c4b1524c 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 @@ -122,7 +122,8 @@ public final class IncomingMessageHandler { actions.add(new RetrieveProfileAction(recipientId)); exception = new UntrustedIdentityException(account.getRecipientStore() .resolveRecipientAddress(recipientId), e.getSenderDevice()); - } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException | ProtocolInvalidMessageException e) { + } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException | + ProtocolInvalidMessageException e) { logger.debug("Failed to decrypt incoming message", e); final var sender = account.getRecipientStore().resolveRecipient(e.getSender()); if (context.getContactHelper().isContactBlocked(sender)) { @@ -319,11 +320,13 @@ public final class IncomingMessageHandler { if (syncMessage.getSent().isPresent()) { var message = syncMessage.getSent().get(); final var destination = message.getDestination().orElse(null); - actions.addAll(handleSignalServiceDataMessage(message.getMessage(), - true, - sender, - destination == null ? null : context.getRecipientHelper().resolveRecipient(destination), - ignoreAttachments)); + if (message.getDataMessage().isPresent()) { + actions.addAll(handleSignalServiceDataMessage(message.getDataMessage().get(), + true, + sender, + destination == null ? null : context.getRecipientHelper().resolveRecipient(destination), + ignoreAttachments)); + } } if (syncMessage.getRequest().isPresent() && account.isMasterDevice()) { var rm = syncMessage.getRequest().get(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index 46f34a4d..652c4694 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 @@ -616,10 +616,12 @@ public class SendHelper { var address = account.getSelfAddress(); var transcript = new SentTranscriptMessage(Optional.of(address), message.getTimestamp(), - message, + Optional.of(message), message.getExpiresInSeconds(), Map.of(address, true), - false); + false, + Optional.empty(), + Set.of()); var syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript); return sendSyncMessage(syncMessage); diff --git a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java index a1f19274..347e3d81 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java @@ -3,6 +3,7 @@ package org.asamk.signal.manager.util; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -18,7 +19,7 @@ public class MessageCacheUtils { try (var f = new FileInputStream(file)) { var in = new DataInputStream(f); var version = in.readInt(); - if (version > 4) { + if (version > 5) { return null; } var type = in.readInt(); @@ -32,6 +33,10 @@ public class MessageCacheUtils { // read legacy relay field in.readUTF(); } + String destinationUuid = null; + if (version >= 5) { + destinationUuid = in.readUTF(); + } var timestamp = in.readLong(); byte[] content = null; var contentLen = in.readInt(); @@ -69,18 +74,20 @@ public class MessageCacheUtils { content, serverReceivedTimestamp, serverDeliveredTimestamp, - uuid); + uuid, + destinationUuid == null ? UuidUtil.UNKNOWN_UUID.toString() : destinationUuid); } } public static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException { try (var f = new FileOutputStream(file)) { try (var out = new DataOutputStream(f)) { - out.writeInt(4); // version + out.writeInt(5); // version out.writeInt(envelope.getType()); out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : ""); out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : ""); out.writeInt(envelope.getSourceDevice()); + out.writeUTF(envelope.getDestinationUuid() == null ? "" : envelope.getDestinationUuid()); out.writeLong(envelope.getTimestamp()); if (envelope.hasContent()) { out.writeInt(envelope.getContent().length); diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 3351d666..04a7be03 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -298,8 +298,10 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { .println("Expiration started at: {}", DateUtils.formatTimestamp(sentTranscriptMessage.expirationStartTimestamp())); } - var message = sentTranscriptMessage.message(); - printDataMessage(writer.indentedWriter(), message); + if (sentTranscriptMessage.message().isPresent()) { + var message = sentTranscriptMessage.message().get(); + printDataMessage(writer.indentedWriter(), message); + } } if (syncMessage.blocked().isPresent()) { writer.println("Received sync message with block list"); diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index ed27ad6d..d64abd0b 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -786,7 +786,7 @@ public class DbusManagerImpl implements Manager { ? Optional.empty() : Optional.of(new RecipientAddress(null, syncReceived.getDestination())), Set.of(), - new MessageEnvelope.Data(syncReceived.getTimestamp(), + Optional.of(new MessageEnvelope.Data(syncReceived.getTimestamp(), syncReceived.getGroupId().length > 0 ? Optional.of(new MessageEnvelope.Data.GroupContext(GroupId.unknownVersion( syncReceived.getGroupId()), false, 0)) @@ -806,7 +806,7 @@ public class DbusManagerImpl implements Manager { Optional.empty(), List.of(), List.of(), - List.of()))), + List.of())))), Optional.empty(), List.of(), List.of(), diff --git a/src/main/java/org/asamk/signal/dbus/DbusReceiveMessageHandler.java b/src/main/java/org/asamk/signal/dbus/DbusReceiveMessageHandler.java index 375fb0b1..dcea2bf1 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/dbus/DbusReceiveMessageHandler.java @@ -79,27 +79,29 @@ public class DbusReceiveMessageHandler implements Manager.ReceiveMessageHandler if (syncMessage.sent().isPresent()) { var transcript = syncMessage.sent().get(); - if (transcript.destination().isPresent() || transcript.message().groupContext().isPresent()) { - var message = transcript.message(); - var groupId = message.groupContext() - .map(MessageEnvelope.Data.GroupContext::groupId) - .map(GroupId::serialize) - .orElseGet(() -> new byte[0]); + if (transcript.message().isPresent()) { + final var dataMessage = transcript.message().get(); + if (transcript.destination().isPresent() || dataMessage.groupContext().isPresent()) { + var groupId = dataMessage.groupContext() + .map(MessageEnvelope.Data.GroupContext::groupId) + .map(GroupId::serialize) + .orElseGet(() -> new byte[0]); - conn.sendMessage(new Signal.SyncMessageReceived(objectPath, - transcript.message().timestamp(), - senderString, - transcript.destination().map(RecipientAddress::getLegacyIdentifier).orElse(""), - groupId, - message.body().orElse(""), - getAttachments(message))); - conn.sendMessage(new Signal.SyncMessageReceivedV2(objectPath, - transcript.message().timestamp(), - senderString, - transcript.destination().map(RecipientAddress::getLegacyIdentifier).orElse(""), - groupId, - message.body().orElse(""), - getMessageExtras(message))); + conn.sendMessage(new Signal.SyncMessageReceived(objectPath, + dataMessage.timestamp(), + senderString, + transcript.destination().map(RecipientAddress::getLegacyIdentifier).orElse(""), + groupId, + dataMessage.body().orElse(""), + getAttachments(dataMessage))); + conn.sendMessage(new Signal.SyncMessageReceivedV2(objectPath, + dataMessage.timestamp(), + senderString, + transcript.destination().map(RecipientAddress::getLegacyIdentifier).orElse(""), + groupId, + dataMessage.body().orElse(""), + getMessageExtras(dataMessage))); + } } } } diff --git a/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java index 0accddc1..fc84b088 100644 --- a/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java @@ -19,10 +19,13 @@ record JsonSyncDataMessage( return new JsonSyncDataMessage(address.getLegacyIdentifier(), address.number().orElse(null), address.uuid().map(UUID::toString).orElse(null), - JsonDataMessage.from(transcriptMessage.message())); + transcriptMessage.message().map(JsonDataMessage::from).orElse(null)); } else { - return new JsonSyncDataMessage(null, null, null, JsonDataMessage.from(transcriptMessage.message())); + return new JsonSyncDataMessage(null, + null, + null, + transcriptMessage.message().map(JsonDataMessage::from).orElse(null)); } } } From 35679216f701ce3281556471645c5fd11214d457 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 14 May 2022 15:19:00 +0200 Subject: [PATCH 086/938] Extract getSelfProfile method --- .../asamk/signal/manager/helper/IncomingMessageHandler.java | 3 +-- .../java/org/asamk/signal/manager/helper/ProfileHelper.java | 6 +++++- .../java/org/asamk/signal/manager/helper/SendHelper.java | 2 +- .../signal/manager/helper/UnidentifiedAccessHelper.java | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) 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 c4b1524c..489be834 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 @@ -130,8 +130,7 @@ public final class IncomingMessageHandler { logger.debug("Received invalid message from blocked contact, ignoring."); } else { final var senderProfile = context.getProfileHelper().getRecipientProfile(sender); - final var selfProfile = context.getProfileHelper() - .getRecipientProfile(account.getSelfRecipientId()); + final var selfProfile = context.getProfileHelper().getSelfProfile(); if ((!sender.equals(account.getSelfRecipientId()) || e.getSenderDevice() != account.getDeviceId()) && senderProfile != null && senderProfile.getCapabilities().contains(Profile.Capability.senderKey) 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 53a48466..7afa63fc 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 @@ -114,7 +114,7 @@ public final class ProfileHelper { String aboutEmoji, Optional avatar ) throws IOException { - var profile = getRecipientProfile(account.getSelfRecipientId()); + var profile = getSelfProfile(); var builder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile); if (givenName != null) { builder.withGivenName(givenName); @@ -164,6 +164,10 @@ public final class ProfileHelper { account.getProfileStore().storeProfile(account.getSelfRecipientId(), newProfile); } + public Profile getSelfProfile() { + return getRecipientProfile(account.getSelfRecipientId()); + } + public List getRecipientProfile(List recipientIds) { try { account.getRecipientStore().setBulkUpdating(true); 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 652c4694..0797f84c 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 @@ -441,7 +441,7 @@ public class SendHelper { } private Set getSenderKeyCapableRecipientIds(final Set recipientIds) { - final var selfProfile = context.getProfileHelper().getRecipientProfile(account.getSelfRecipientId()); + final var selfProfile = context.getProfileHelper().getSelfProfile(); if (selfProfile == null || !selfProfile.getCapabilities().contains(Profile.Capability.senderKey)) { logger.debug("Not all of our devices support sender key. Using legacy."); return Set.of(); 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 7dd412a4..bdd6221b 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 @@ -143,7 +143,7 @@ public class UnidentifiedAccessHelper { private byte[] getSelfUnidentifiedAccessKey(boolean noRefresh) { var selfProfile = noRefresh ? account.getProfileStore().getProfile(account.getSelfRecipientId()) - : context.getProfileHelper().getRecipientProfile(account.getSelfRecipientId()); + : context.getProfileHelper().getSelfProfile(); if (selfProfile != null && selfProfile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED) { return createUnrestrictedUnidentifiedAccess(); From 656282459ccb895b59fcc59e542740ffdf44458a Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 14 May 2022 15:43:25 +0200 Subject: [PATCH 087/938] Add warning if user has no profile name set --- lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index c7f1d22f..63a26b2b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -517,6 +517,11 @@ class ManagerImpl implements Manager { public SendMessageResults sendMessage( Message message, Set recipients ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException { + final var selfProfile = context.getProfileHelper().getSelfProfile(); + if (selfProfile == null || selfProfile.getDisplayName().isEmpty()) { + logger.warn( + "No profile name set. When sending a message it's recommended to set a profile name wit the updateProfile command. This may become mandatory in the future."); + } final var messageBuilder = SignalServiceDataMessage.newBuilder(); applyMessage(messageBuilder, message); return sendMessage(messageBuilder, recipients); From 55dde93811ac137741b49d3fab2483e689592038 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 15 May 2022 16:13:01 +0200 Subject: [PATCH 088/938] Store information if account is registerd on LIVE or STAGING env in account file Fixes #896 --- .../manager/ProvisioningManagerImpl.java | 8 +++++++ .../manager/RegistrationManagerImpl.java | 6 ++++++ .../signal/manager/SignalAccountFiles.java | 13 +++++++++++- .../signal/manager/config/ServiceConfig.java | 6 ++++-- .../config/ServiceEnvironmentConfig.java | 7 +++++++ .../signal/manager/storage/SignalAccount.java | 21 +++++++++++++++++++ 6 files changed, 58 insertions(+), 3 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java index 7519950e..7d396bb1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java @@ -136,6 +136,7 @@ class ProvisioningManagerImpl implements ProvisioningManager { account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.dataPath(), accountPath, number, + serviceEnvironmentConfig.getType(), aci, pni, password, @@ -209,6 +210,13 @@ class ProvisioningManagerImpl implements ProvisioningManager { logger.debug("Account is a master device."); return false; } + if (signalAccount.isRegistered() + && signalAccount.getServiceEnvironment() != null + && signalAccount.getServiceEnvironment() != serviceEnvironmentConfig.getType()) { + logger.debug("Account is registered in another environment: {}.", + signalAccount.getServiceEnvironment()); + return false; + } final var m = new ManagerImpl(signalAccount, pathConfig, diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index 133bd8b8..ebafe24b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -95,6 +95,12 @@ class RegistrationManagerImpl implements RegistrationManager { @Override public void register(boolean voiceVerification, String captcha) throws IOException, CaptchaRequiredException { + if (account.isRegistered() + && account.getServiceEnvironment() != null + && account.getServiceEnvironment() != serviceEnvironmentConfig.getType()) { + throw new IOException("Account is registered in another environment: " + account.getServiceEnvironment()); + } + if (account.getAci() != null && attemptReactivateAccount()) { return; } 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 0ca9b0ab..5f0f551c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -24,6 +24,7 @@ public class SignalAccountFiles { private static final Logger logger = LoggerFactory.getLogger(MultiAccountManager.class); private final PathConfig pathConfig; + private final ServiceEnvironment serviceEnvironment; private final ServiceEnvironmentConfig serviceEnvironmentConfig; private final String userAgent; private final TrustNewIdentity trustNewIdentity; @@ -36,7 +37,8 @@ public class SignalAccountFiles { final TrustNewIdentity trustNewIdentity ) throws IOException { this.pathConfig = PathConfig.createDefault(settingsPath); - this.serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); + this.serviceEnvironment = serviceEnvironment; + this.serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(this.serviceEnvironment, userAgent); this.userAgent = userAgent; this.trustNewIdentity = trustNewIdentity; this.accountsStore = new AccountsStore(pathConfig.dataPath()); @@ -85,6 +87,10 @@ public class SignalAccountFiles { throw new NotRegisteredException(); } + if (account.getServiceEnvironment() != null && account.getServiceEnvironment() != serviceEnvironment) { + throw new IOException("Account is registered in another environment: " + account.getServiceEnvironment()); + } + account.initDatabase(); final var manager = new ManagerImpl(account, @@ -100,6 +106,10 @@ public class SignalAccountFiles { throw new AccountCheckException("Error while checking account " + number + ": " + e.getMessage(), e); } + if (account.getServiceEnvironment() == null) { + account.setServiceEnvironment(serviceEnvironment); + } + return manager; } @@ -133,6 +143,7 @@ public class SignalAccountFiles { var account = SignalAccount.create(pathConfig.dataPath(), newAccountPath, number, + serviceEnvironment, aciIdentityKey, pniIdentityKey, registrationId, 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 422d0db7..91b07ce3 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 @@ -75,11 +75,13 @@ public class ServiceConfig { final var interceptors = List.of(userAgentInterceptor); return switch (serviceEnvironment) { - case LIVE -> new ServiceEnvironmentConfig(LiveConfig.createDefaultServiceConfiguration(interceptors), + case LIVE -> new ServiceEnvironmentConfig(serviceEnvironment, + LiveConfig.createDefaultServiceConfiguration(interceptors), LiveConfig.getUnidentifiedSenderTrustRoot(), LiveConfig.createKeyBackupConfig(), LiveConfig.getCdsMrenclave()); - case STAGING -> new ServiceEnvironmentConfig(StagingConfig.createDefaultServiceConfiguration(interceptors), + case STAGING -> new ServiceEnvironmentConfig(serviceEnvironment, + StagingConfig.createDefaultServiceConfiguration(interceptors), StagingConfig.getUnidentifiedSenderTrustRoot(), StagingConfig.createKeyBackupConfig(), StagingConfig.getCdsMrenclave()); diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java index 9dac7edd..b6a6af1d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java @@ -5,6 +5,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceConf public class ServiceEnvironmentConfig { + private final ServiceEnvironment type; private final SignalServiceConfiguration signalServiceConfiguration; private final ECPublicKey unidentifiedSenderTrustRoot; @@ -14,17 +15,23 @@ public class ServiceEnvironmentConfig { private final String cdsMrenclave; public ServiceEnvironmentConfig( + final ServiceEnvironment type, final SignalServiceConfiguration signalServiceConfiguration, final ECPublicKey unidentifiedSenderTrustRoot, final KeyBackupConfig keyBackupConfig, final String cdsMrenclave ) { + this.type = type; this.signalServiceConfiguration = signalServiceConfiguration; this.unidentifiedSenderTrustRoot = unidentifiedSenderTrustRoot; this.keyBackupConfig = keyBackupConfig; this.cdsMrenclave = cdsMrenclave; } + public ServiceEnvironment getType() { + return type; + } + public SignalServiceConfiguration getSignalServiceConfiguration() { return signalServiceConfiguration; } 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 66f515ae..ec8434cc 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 @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.TrustLevel; +import org.asamk.signal.manager.config.ServiceEnvironment; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.configuration.ConfigurationStore; import org.asamk.signal.manager.storage.contacts.ContactsStore; @@ -95,6 +96,7 @@ public class SignalAccount implements Closeable { private File dataPath; private String accountPath; + private ServiceEnvironment serviceEnvironment; private String number; private ACI aci; private PNI pni; @@ -170,6 +172,7 @@ public class SignalAccount implements Closeable { File dataPath, String accountPath, String number, + ServiceEnvironment serviceEnvironment, IdentityKeyPair aciIdentityKey, IdentityKeyPair pniIdentityKey, int registrationId, @@ -187,6 +190,7 @@ public class SignalAccount implements Closeable { signalAccount.accountPath = accountPath; signalAccount.number = number; + signalAccount.serviceEnvironment = serviceEnvironment; signalAccount.profileKey = profileKey; signalAccount.dataPath = dataPath; @@ -213,6 +217,7 @@ public class SignalAccount implements Closeable { File dataPath, String accountPath, String number, + ServiceEnvironment serviceEnvironment, ACI aci, PNI pni, String password, @@ -230,6 +235,7 @@ public class SignalAccount implements Closeable { return createLinkedAccount(dataPath, accountPath, number, + serviceEnvironment, aci, pni, password, @@ -279,6 +285,7 @@ public class SignalAccount implements Closeable { File dataPath, String accountPath, String number, + ServiceEnvironment serviceEnvironment, ACI aci, PNI pni, String password, @@ -308,6 +315,7 @@ public class SignalAccount implements Closeable { signalAccount.dataPath = dataPath; signalAccount.accountPath = accountPath; + signalAccount.serviceEnvironment = serviceEnvironment; signalAccount.localRegistrationId = registrationId; signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), @@ -489,6 +497,9 @@ public class SignalAccount implements Closeable { if (rootNode.hasNonNull("password")) { password = rootNode.get("password").asText(); } + if (rootNode.hasNonNull("serviceEnvironment")) { + serviceEnvironment = ServiceEnvironment.valueOf(rootNode.get("serviceEnvironment").asText()); + } registered = Utils.getNotNullNode(rootNode, "registered").asBoolean(); if (rootNode.hasNonNull("uuid")) { try { @@ -814,6 +825,7 @@ public class SignalAccount implements Closeable { var rootNode = jsonProcessor.createObjectNode(); rootNode.put("version", CURRENT_STORAGE_VERSION) .put("username", number) + .put("serviceEnvironment", serviceEnvironment == null ? null : serviceEnvironment.name()) .put("uuid", aci == null ? null : aci.toString()) .put("pni", pni == null ? null : pni.toString()) .put("deviceName", encryptedDeviceName) @@ -1109,6 +1121,15 @@ public class SignalAccount implements Closeable { save(); } + public ServiceEnvironment getServiceEnvironment() { + return serviceEnvironment; + } + + public void setServiceEnvironment(final ServiceEnvironment serviceEnvironment) { + this.serviceEnvironment = serviceEnvironment; + save(); + } + public ACI getAci() { return aci; } From 22add1cbee357e988ca9c85d9d161fc783fd5fa3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 16 May 2022 12:20:23 +0200 Subject: [PATCH 089/938] Add command to delete local account data Fixes #912 --- .../manager/AccountFileUpdaterImpl.java | 26 +++++++ .../org/asamk/signal/manager/ManagerImpl.java | 19 ++++-- .../manager/ProvisioningManagerImpl.java | 4 +- .../signal/manager/RegistrationManager.java | 4 ++ .../manager/RegistrationManagerImpl.java | 12 ++++ .../signal/manager/SignalAccountFiles.java | 6 +- .../manager/helper/AccountFileUpdater.java | 2 + .../signal/manager/storage/SignalAccount.java | 13 ++++ .../storage/accounts/AccountsStore.java | 4 ++ man/signal-cli.1.adoc | 10 +++ .../org/asamk/signal/commands/Commands.java | 1 + .../DeleteLocalAccountDataCommand.java | 68 +++++++++++++++++++ .../dbus/DbusRegistrationManagerImpl.java | 10 +++ 13 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/AccountFileUpdaterImpl.java create mode 100644 src/main/java/org/asamk/signal/commands/DeleteLocalAccountDataCommand.java diff --git a/lib/src/main/java/org/asamk/signal/manager/AccountFileUpdaterImpl.java b/lib/src/main/java/org/asamk/signal/manager/AccountFileUpdaterImpl.java new file mode 100644 index 00000000..af0008c3 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/AccountFileUpdaterImpl.java @@ -0,0 +1,26 @@ +package org.asamk.signal.manager; + +import org.asamk.signal.manager.helper.AccountFileUpdater; +import org.asamk.signal.manager.storage.accounts.AccountsStore; +import org.whispersystems.signalservice.api.push.ACI; + +class AccountFileUpdaterImpl implements AccountFileUpdater { + + private final AccountsStore accountsStore; + private final String accountPath; + + public AccountFileUpdaterImpl(final AccountsStore accountsStore, final String accountPath) { + this.accountsStore = accountsStore; + this.accountPath = accountPath; + } + + @Override + public void updateAccountIdentifiers(final String newNumber, final ACI newAci) { + accountsStore.updateAccount(accountPath, newNumber, newAci); + } + + @Override + public void removeAccount() { + accountsStore.removeAccount(accountPath); + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 63a26b2b..fa041c55 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -66,6 +66,7 @@ import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; +import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.util.DeviceNameUtil; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; @@ -99,6 +100,7 @@ class ManagerImpl implements Manager { private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class); private SignalAccount account; + private AccountFileUpdater accountFileUpdater; private final SignalDependencies dependencies; private final Context context; @@ -120,6 +122,7 @@ class ManagerImpl implements Manager { String userAgent ) { this.account = account; + this.accountFileUpdater = accountFileUpdater; final var sessionLock = new SignalSessionLock() { private final ReentrantLock LEGACY_LOCK = new ReentrantLock(); @@ -140,10 +143,18 @@ class ManagerImpl implements Manager { final var attachmentStore = new AttachmentStore(pathConfig.attachmentsPath()); final var stickerPackStore = new StickerPackStore(pathConfig.stickerPacksPath()); - this.context = new Context(account, (number, aci) -> { - accountFileUpdater.updateAccountIdentifiers(number, aci); - synchronized (addressChangedListeners) { - addressChangedListeners.forEach(Runnable::run); + this.context = new Context(account, new AccountFileUpdater() { + @Override + public void updateAccountIdentifiers(final String number, final ACI aci) { + accountFileUpdater.updateAccountIdentifiers(number, aci); + synchronized (addressChangedListeners) { + addressChangedListeners.forEach(Runnable::run); + } + } + + @Override + public void removeAccount() { + accountFileUpdater.removeAccount(); } }, dependencies, avatarStore, attachmentStore, stickerPackStore); this.context.getAccountHelper().setUnregisteredListener(this::close); diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java index 7d396bb1..d7c12337 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java @@ -153,7 +153,7 @@ class ProvisioningManagerImpl implements ProvisioningManager { final var accountPathFinal = accountPath; m = new ManagerImpl(account, pathConfig, - (newNumber, newAci) -> accountsStore.updateAccount(accountPathFinal, newNumber, newAci), + new AccountFileUpdaterImpl(accountsStore, accountPathFinal), serviceEnvironmentConfig, userAgent); account = null; @@ -220,7 +220,7 @@ class ProvisioningManagerImpl implements ProvisioningManager { final var m = new ManagerImpl(signalAccount, pathConfig, - (newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci), + new AccountFileUpdaterImpl(accountsStore, accountPath), serviceEnvironmentConfig, userAgent); try (m) { 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 c1c318d9..507690f4 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -14,4 +14,8 @@ public interface RegistrationManager extends Closeable { void verifyAccount( String verificationCode, String pin ) throws IOException, PinLockedException, IncorrectPinException; + + void deleteLocalAccountData() throws IOException; + + boolean isRegistered(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index ebafe24b..3529154a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -155,6 +155,18 @@ class RegistrationManagerImpl implements RegistrationManager { } } + @Override + public void deleteLocalAccountData() throws IOException { + account.deleteAccountData(); + accountFileUpdater.removeAccount(); + account = null; + } + + @Override + public boolean isRegistered() { + return account.isRegistered(); + } + private boolean attemptReactivateAccount() { try { final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), 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 5f0f551c..3821a306 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -95,7 +95,7 @@ public class SignalAccountFiles { final var manager = new ManagerImpl(account, pathConfig, - (newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci), + new AccountFileUpdaterImpl(accountsStore, accountPath), serviceEnvironmentConfig, userAgent); @@ -155,7 +155,7 @@ public class SignalAccountFiles { serviceEnvironmentConfig, userAgent, newManagerListener, - (newNumber, newAci) -> accountsStore.updateAccount(newAccountPath, newNumber, newAci)); + new AccountFileUpdaterImpl(accountsStore, newAccountPath)); } var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity); @@ -169,6 +169,6 @@ public class SignalAccountFiles { serviceEnvironmentConfig, userAgent, newManagerListener, - (newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci)); + new AccountFileUpdaterImpl(accountsStore, accountPath)); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountFileUpdater.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountFileUpdater.java index fb6e327c..a983e4b5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountFileUpdater.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountFileUpdater.java @@ -5,4 +5,6 @@ import org.whispersystems.signalservice.api.push.ACI; public interface AccountFileUpdater { void updateAccountIdentifiers(String number, ACI aci); + + void removeAccount(); } 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 ec8434cc..f09714cb 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 @@ -70,9 +70,11 @@ import java.nio.channels.Channels; import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; +import java.nio.file.Files; import java.security.SecureRandom; import java.sql.SQLException; import java.util.Base64; +import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; @@ -1330,6 +1332,17 @@ public class SignalAccount implements Closeable { getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED); } + public void deleteAccountData() throws IOException { + close(); + try (final var files = Files.walk(getUserPath(dataPath, accountPath).toPath()) + .sorted(Comparator.reverseOrder())) { + for (final var file = files.iterator(); file.hasNext(); ) { + Files.delete(file.next()); + } + } + Files.delete(getFileName(dataPath, accountPath).toPath()); + } + @Override public void close() { synchronized (fileChannel) { 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 3c81c7d7..ea2f0a1b 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 @@ -104,6 +104,10 @@ public class AccountsStore { return accountPath; } + public void removeAccount(final String accountPath) { + updateAccounts(accounts -> accounts.stream().filter(a -> !a.path().equals(accountPath)).toList()); + } + private String generateNewAccountPath() { return new Random().ints(100000, 1000000) .mapToObj(String::valueOf) diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 0562fcca..7bbdbf32 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -121,6 +121,16 @@ You will have to be readded to each group. CAUTION: Only delete your account if you won't use this number again! +=== deleteLocalAccountData + +Delete all local data for this account. +Data should only be deleted if the account is unregistered. + +CAUTION: This cannot be undone. + +*--ignore-registered*:: +Delete the account data even though the account is still registered on the Signal servers. + === updateAccount Update the account attributes on the signal server. diff --git a/src/main/java/org/asamk/signal/commands/Commands.java b/src/main/java/org/asamk/signal/commands/Commands.java index 29b28da0..15ab1724 100644 --- a/src/main/java/org/asamk/signal/commands/Commands.java +++ b/src/main/java/org/asamk/signal/commands/Commands.java @@ -13,6 +13,7 @@ public class Commands { addCommand(new AddDeviceCommand()); addCommand(new BlockCommand()); addCommand(new DaemonCommand()); + addCommand(new DeleteLocalAccountDataCommand()); addCommand(new FinishLinkCommand()); addCommand(new GetUserStatusCommand()); addCommand(new JoinGroupCommand()); diff --git a/src/main/java/org/asamk/signal/commands/DeleteLocalAccountDataCommand.java b/src/main/java/org/asamk/signal/commands/DeleteLocalAccountDataCommand.java new file mode 100644 index 00000000..51c8582d --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/DeleteLocalAccountDataCommand.java @@ -0,0 +1,68 @@ +package org.asamk.signal.commands; + +import com.fasterxml.jackson.core.type.TypeReference; + +import net.sourceforge.argparse4j.impl.Arguments; +import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; + +import org.asamk.signal.OutputType; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; +import org.asamk.signal.manager.RegistrationManager; +import org.asamk.signal.output.JsonWriter; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public class DeleteLocalAccountDataCommand implements RegistrationCommand, JsonRpcRegistrationCommand> { + + @Override + public String getName() { + return "deleteLocalAccountData"; + } + + @Override + public void attachToSubparser(final Subparser subparser) { + subparser.help( + "Delete all local data for this account. Data should only be deleted if the account is unregistered. CAUTION: This cannot be undone."); + subparser.addArgument("--ignore-registered") + .help("Delete the account data even though the account is still registered on the Signal servers.") + .action(Arguments.storeTrue()); + } + + @Override + public void handleCommand(final Namespace ns, final RegistrationManager m) throws CommandException { + try { + final var ignoreRegistered = Boolean.TRUE.equals(ns.getBoolean("ignore-registered")); + if (m.isRegistered() && !ignoreRegistered) { + throw new UserErrorException( + "Not deleting account, it is still registered. Use --ignore-registered to delete it anyway."); + } + + m.deleteLocalAccountData(); + } catch (IOException e) { + throw new IOErrorException("Deletion error: " + e.getMessage(), e); + } + } + + @Override + public TypeReference> getRequestType() { + return new TypeReference<>() {}; + } + + @Override + public List getSupportedOutputTypes() { + return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); + } + + @Override + public void handleCommand( + 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/dbus/DbusRegistrationManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusRegistrationManagerImpl.java index aa27fc27..4d43b98c 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusRegistrationManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusRegistrationManagerImpl.java @@ -47,6 +47,16 @@ public class DbusRegistrationManagerImpl implements RegistrationManager { } } + @Override + public void deleteLocalAccountData() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isRegistered() { + throw new UnsupportedOperationException(); + } + @Override public void close() { } From 477e6516ed7e13b2592a94d29a0f65ca60c3a78f Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 16 May 2022 12:23:45 +0200 Subject: [PATCH 090/938] Prevent printing stack trace for unregistered users --- .../org/asamk/signal/manager/helper/RecipientHelper.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 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 82368ca2..15508cd4 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 @@ -51,7 +51,11 @@ public class RecipientHelper { final ACI aci; try { aci = getRegisteredUser(number); - } catch (UnregisteredRecipientException | IOException e) { + } catch (UnregisteredRecipientException e) { + logger.warn("Failed to get uuid for e164 number: {}", number); + // Return SignalServiceAddress with unknown UUID + return address.toSignalServiceAddress(); + } catch (IOException e) { logger.warn("Failed to get uuid for e164 number: {}", number, e); // Return SignalServiceAddress with unknown UUID return address.toSignalServiceAddress(); @@ -106,7 +110,8 @@ public class RecipientHelper { .getRegisteredUsers(ServiceConfig.getIasKeyStore(), numbers, serviceEnvironmentConfig.getCdsMrenclave()); - } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException | NumberFormatException e) { + } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | + UnauthenticatedResponseException | InvalidKeyException | NumberFormatException e) { throw new IOException(e); } From f0fe1fac6640d7b2e39ebbc00369e18bc73d90e4 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 16 May 2022 13:00:23 +0200 Subject: [PATCH 091/938] Update native-image handling in tests --- graalvm-config-dir/reflect-config.json | 9 +++++++++ run_tests.sh | 22 ++++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index b2e12b5e..f9fe367f 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2960,6 +2960,15 @@ {"name":"unidentifiedStatus_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent$StoryMessageRecipient", + "fields":[ + {"name":"bitField0_"}, + {"name":"destinationUuid_"}, + {"name":"distributionListIds_"}, + {"name":"isAllowedToReply_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent$UnidentifiedDeliveryStatus", "fields":[ diff --git a/run_tests.sh b/run_tests.sh index aa185c9d..94eb98d9 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -5,12 +5,6 @@ if [ $# -ne 2 ]; then fi set -e -# To update graalvm config, set GRAALVM_HOME, e.g: -# export GRAALVM_HOME=/usr/lib/jvm/java-17-graalvm -if [ ! -z "$GRAALVM_HOME" ]; then - export JAVA_HOME=$GRAALVM_HOME - export SIGNAL_CLI_OPTS='-agentlib:native-image-agent=config-merge-dir=graalvm-config-dir/' -fi NUMBER_1="$1" NUMBER_2="$2" @@ -36,6 +30,13 @@ else fi run() { + # To update graalvm config, set GRAALVM_HOME, e.g: + # export GRAALVM_HOME=/usr/lib/jvm/java-17-graalvm + if [ ! -z "$GRAALVM_HOME" ]; then + export JAVA_HOME=$GRAALVM_HOME + export SIGNAL_CLI_OPTS="-agentlib:native-image-agent=config-merge-dir=graalvm-config-dir-${SIGNAL_CLI_AGENT_ID}/" + fi + set -x if [ "$JSON_RPC" -eq 1 ]; then "$SIGNAL_CLI" $@ @@ -46,19 +47,23 @@ run() { } run_main() { + export SIGNAL_CLI_AGENT_ID=main if [ "$JSON_RPC" -eq 1 ]; then run --json-rpc-socket="$XDG_RUNTIME_DIR/signal-cli/socket" $@ else run --config="$PATH_MAIN" $@ fi + unset SIGNAL_CLI_AGENT_ID } run_linked() { + export SIGNAL_CLI_AGENT_ID=linked if [ "$JSON_RPC" -eq 1 ]; then run --json-rpc-tcp="127.0.0.1:7583" $@ else run --config="$PATH_LINK" $@ fi + unset SIGNAL_CLI_AGENT_ID } register() { @@ -227,3 +232,8 @@ run_main -a "$NUMBER_1" removeDevice -d 2 ## Unregister run_main -a "$NUMBER_1" unregister run_main -a "$NUMBER_2" unregister --delete-account + +if [ ! -z "$GRAALVM_HOME" ]; then + "$GRAALVM_HOME"/lib/svm/bin/native-image-configure generate --input-dir=graalvm-config-dir/ --input-dir=graalvm-config-dir-linked/ --input-dir=graalvm-config-dir-main/ --output-dir=graalvm-config-dir// + rm -r graalvm-config-dir-main graalvm-config-dir-linked +fi From 68eb6b6bb457190290d78266ac6efc71eb118dfe Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 16 May 2022 14:35:34 +0200 Subject: [PATCH 092/938] Update graalvm config Fixes #942 --- graalvm-config-dir/jni-config.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index 27abd5be..5c774bed 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -101,6 +101,10 @@ "name":"org.signal.libsignal.protocol.InvalidMessageException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, +{ + "name":"org.signal.libsignal.protocol.NoSessionException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.signal.libsignal.protocol.SignalProtocolAddress", "methods":[ From c3425979dd4800a7416cb2dfc13523097bbc0955 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 16 May 2022 15:22:03 +0200 Subject: [PATCH 093/938] Added new parameter to filter the output of listGroups by groupId Fixes #953 --- man/signal-cli.1.adoc | 3 +++ .../org/asamk/signal/commands/ListGroupsCommand.java | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 7bbdbf32..4fdd684a 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -413,6 +413,9 @@ In json mode this is outputted as an list of objects and is always in detailed m *-d*, *--detailed*:: Include the list of members of each group and the group invite link. +*-g*, *--group-id*:: +Filter the group list by one or more group IDs. + === listContacts Show a list of known contacts with names. diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 80e583af..c08571f7 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -11,6 +11,7 @@ import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.output.JsonWriter; import org.asamk.signal.output.OutputWriter; import org.asamk.signal.output.PlainTextWriter; +import org.asamk.signal.util.CommandUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +34,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { subparser.addArgument("-d", "--detailed") .action(Arguments.storeTrue()) .help("List the members and group invite links of each group. If output=json, then this is always set"); + subparser.addArgument("-g", "--group-id").help("Specify one or more group IDs to show.").nargs("*"); } private static Set resolveMembers(Set addresses) { @@ -79,7 +81,13 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { public void handleCommand( final Namespace ns, final Manager m, final OutputWriter outputWriter ) throws CommandException { - final var groups = m.getGroups(); + var groups = m.getGroups(); + + final var groupIdStrings = ns.getList("group-id"); + final var groupIds = CommandUtil.getGroupIds(groupIdStrings); + if (groupIds.size() > 0) { + groups = groups.stream().filter(g -> groupIds.contains(g.groupId())).toList(); + } if (outputWriter instanceof JsonWriter jsonWriter) { From e844abcad1da16dab3f78baaafca2c16fe7b5d15 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 17 May 2022 20:09:26 +0200 Subject: [PATCH 094/938] Store payment address of profiles --- .../signal/manager/helper/ProfileHelper.java | 12 +++++- .../signal/manager/storage/SignalAccount.java | 1 + .../manager/storage/recipients/Profile.java | 16 +++++++ .../storage/recipients/RecipientStore.java | 43 +++++++++++-------- .../signal/manager/util/ProfileUtils.java | 1 + 5 files changed, 53 insertions(+), 20 deletions(-) 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 7afa63fc..982acddd 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 @@ -1,5 +1,7 @@ package org.asamk.signal.manager.helper; +import com.google.protobuf.InvalidProtocolBufferException; + import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.storage.SignalAccount; @@ -23,6 +25,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.services.ProfileService; +import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import java.io.File; import java.io.IOException; @@ -138,13 +141,20 @@ public final class ProfileHelper { : avatar.isPresent() ? AvatarUploadParams.forAvatar(streamDetails) : AvatarUploadParams.unchanged(false); + final var paymentsAddress = Optional.ofNullable(newProfile.getPaymentAddress()).map(data -> { + try { + return SignalServiceProtos.PaymentAddress.parseFrom(data); + } catch (InvalidProtocolBufferException e) { + return null; + } + }); final var avatarPath = dependencies.getAccountManager() .setVersionedProfile(account.getAci(), account.getProfileKey(), newProfile.getInternalServiceName(), newProfile.getAbout() == null ? "" : newProfile.getAbout(), newProfile.getAboutEmoji() == null ? "" : newProfile.getAboutEmoji(), - Optional.empty(), + paymentsAddress, avatarUploadParams, List.of(/* TODO */)); builder.withAvatarUrlPath(avatarPath.orElse(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 f09714cb..7b5411e6 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 @@ -757,6 +757,7 @@ public class SignalAccount implements Closeable { profile.getAbout(), profile.getAboutEmoji(), null, + null, profile.isUnrestrictedUnidentifiedAccess() ? Profile.UnidentifiedAccessMode.UNRESTRICTED : profile.getUnidentifiedAccess() != null diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java index dc3aaf53..3f14f645 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java @@ -20,6 +20,8 @@ public class Profile { private final String avatarUrlPath; + private final byte[] paymentAddress; + private final UnidentifiedAccessMode unidentifiedAccessMode; private final Set capabilities; @@ -31,6 +33,7 @@ public class Profile { final String about, final String aboutEmoji, final String avatarUrlPath, + final byte[] paymentAddress, final UnidentifiedAccessMode unidentifiedAccessMode, final Set capabilities ) { @@ -40,6 +43,7 @@ public class Profile { this.about = about; this.aboutEmoji = aboutEmoji; this.avatarUrlPath = avatarUrlPath; + this.paymentAddress = paymentAddress; this.unidentifiedAccessMode = unidentifiedAccessMode; this.capabilities = capabilities; } @@ -51,6 +55,7 @@ public class Profile { about = builder.about; aboutEmoji = builder.aboutEmoji; avatarUrlPath = builder.avatarUrlPath; + paymentAddress = builder.paymentAddress; unidentifiedAccessMode = builder.unidentifiedAccessMode; capabilities = builder.capabilities; } @@ -67,6 +72,7 @@ public class Profile { builder.about = copy.getAbout(); builder.aboutEmoji = copy.getAboutEmoji(); builder.avatarUrlPath = copy.getAvatarUrlPath(); + builder.paymentAddress = copy.getPaymentAddress(); builder.unidentifiedAccessMode = copy.getUnidentifiedAccessMode(); builder.capabilities = copy.getCapabilities(); return builder; @@ -118,6 +124,10 @@ public class Profile { return avatarUrlPath; } + public byte[] getPaymentAddress() { + return paymentAddress; + } + public UnidentifiedAccessMode getUnidentifiedAccessMode() { return unidentifiedAccessMode; } @@ -190,6 +200,7 @@ public class Profile { private String about; private String aboutEmoji; private String avatarUrlPath; + private byte[] paymentAddress; private UnidentifiedAccessMode unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN; private Set capabilities = Collections.emptySet(); private long lastUpdateTimestamp = 0; @@ -240,5 +251,10 @@ public class Profile { lastUpdateTimestamp = val; return this; } + + public Builder withPaymentAddress(final byte[] val) { + paymentAddress = val; + return this; + } } } 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 9caac144..619ec418 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 @@ -103,6 +103,9 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile r.profile.about, r.profile.aboutEmoji, r.profile.avatarUrlPath, + r.profile.paymentAddress == null + ? null + : Base64.getDecoder().decode(r.profile.paymentAddress), Profile.UnidentifiedAccessMode.valueOfOrUnknown(r.profile.unidentifiedAccessMode), r.profile.capabilities.stream() .map(Profile.Capability::valueOfOrNull) @@ -535,27 +538,28 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile final var base64 = Base64.getEncoder(); var storage = new Storage(recipients.entrySet().stream().map(pair -> { final var recipient = pair.getValue(); - final var contact = recipient.getContact() == null + final var recipientContact = recipient.getContact(); + final var contact = recipientContact == null ? null - : new Storage.Recipient.Contact(recipient.getContact().getName(), - recipient.getContact().getColor(), - recipient.getContact().getMessageExpirationTime(), - recipient.getContact().isBlocked(), - recipient.getContact().isArchived()); - final var profile = recipient.getProfile() == null + : new Storage.Recipient.Contact(recipientContact.getName(), + recipientContact.getColor(), + recipientContact.getMessageExpirationTime(), + recipientContact.isBlocked(), + recipientContact.isArchived()); + final var recipientProfile = recipient.getProfile(); + final var profile = recipientProfile == null ? null - : new Storage.Recipient.Profile(recipient.getProfile().getLastUpdateTimestamp(), - recipient.getProfile().getGivenName(), - recipient.getProfile().getFamilyName(), - recipient.getProfile().getAbout(), - recipient.getProfile().getAboutEmoji(), - recipient.getProfile().getAvatarUrlPath(), - recipient.getProfile().getUnidentifiedAccessMode().name(), - recipient.getProfile() - .getCapabilities() - .stream() - .map(Enum::name) - .collect(Collectors.toSet())); + : new Storage.Recipient.Profile(recipientProfile.getLastUpdateTimestamp(), + recipientProfile.getGivenName(), + recipientProfile.getFamilyName(), + recipientProfile.getAbout(), + recipientProfile.getAboutEmoji(), + recipientProfile.getAvatarUrlPath(), + recipientProfile.getPaymentAddress() == null + ? null + : base64.encodeToString(recipientProfile.getPaymentAddress()), + recipientProfile.getUnidentifiedAccessMode().name(), + recipientProfile.getCapabilities().stream().map(Enum::name).collect(Collectors.toSet())); return new Storage.Recipient(pair.getKey().id(), recipient.getAddress().number().orElse(null), recipient.getAddress().uuid().map(UUID::toString).orElse(null), @@ -605,6 +609,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile String about, String aboutEmoji, String avatarUrlPath, + String paymentAddress, String unidentifiedAccessMode, Set capabilities ) {} 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 c2bd1fa9..53a9cdb6 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 @@ -32,6 +32,7 @@ public class ProfileUtils { about, aboutEmoji, encryptedProfile.getAvatar(), + encryptedProfile.getPaymentAddress(), getUnidentifiedAccessMode(encryptedProfile, profileCipher), getCapabilities(encryptedProfile)); } catch (InvalidCiphertextException e) { From 376fcba9ec1213ec77f37359e0248cf625b2f927 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 17 May 2022 20:22:56 +0200 Subject: [PATCH 095/938] Include reason for inactive group link in error message --- .../main/java/org/asamk/signal/manager/helper/GroupHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 57f9c924..9d4f712c 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 @@ -262,7 +262,7 @@ public class GroupHelper { groupJoinInfo = context.getGroupV2Helper() .getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword()); } catch (GroupLinkNotActiveException e) { - throw new InactiveGroupLinkException("Group link inactive", e); + throw new InactiveGroupLinkException("Group link inactive (reason: " + e.getReason() + ")", e); } final var groupChange = context.getGroupV2Helper() .joinGroup(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword(), groupJoinInfo); From bb9d44811c511c7acef24e0e9047bdafef9b8f39 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 17 May 2022 20:39:06 +0200 Subject: [PATCH 096/938] Include unrestricted unidentified access state in getUserStatus response --- .../java/org/asamk/signal/manager/Manager.java | 4 ++-- .../org/asamk/signal/manager/ManagerImpl.java | 12 +++++++++--- .../asamk/signal/manager/api/UserStatus.java | 5 +++++ .../signal/commands/GetUserStatusCommand.java | 18 ++++++++++-------- .../org/asamk/signal/dbus/DbusManagerImpl.java | 10 ++++++---- .../org/asamk/signal/dbus/DbusSignalImpl.java | 11 ++++------- 6 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/api/UserStatus.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 99b76781..d14b0f44 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -21,6 +21,7 @@ import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.groups.GroupNotFoundException; @@ -41,7 +42,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.UUID; public interface Manager extends Closeable { @@ -58,7 +58,7 @@ public interface Manager extends Closeable { * @return A map of numbers to canonicalized number and uuid. If a number is not registered the uuid is null. * @throws IOException if it's unable to get the contacts to check if they're registered */ - Map> areUsersRegistered(Set numbers) throws IOException; + Map getUserStatus(Set numbers) throws IOException; void updateAccountAttributes(String deviceName) throws IOException; diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index fa041c55..db8744cd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -38,6 +38,7 @@ import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; @@ -84,7 +85,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; @@ -190,7 +190,7 @@ class ManagerImpl implements Manager { } @Override - public Map> areUsersRegistered(Set numbers) throws IOException { + public Map getUserStatus(Set numbers) throws IOException { final var canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> { try { final var canonicalizedNumber = PhoneNumberFormatter.formatNumber(n, account.getNumber()); @@ -213,7 +213,13 @@ class ManagerImpl implements Manager { return numbers.stream().collect(Collectors.toMap(n -> n, n -> { final var number = canonicalizedNumbers.get(n); final var aci = registeredUsers.get(number); - return new Pair<>(number.isEmpty() ? null : number, aci == null ? null : aci.uuid()); + final var profile = aci == null + ? null + : context.getProfileHelper().getRecipientProfile(account.getRecipientStore().resolveRecipient(aci)); + return new UserStatus(number.isEmpty() ? null : number, + aci == null ? null : aci.uuid(), + profile != null + && profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED); })); } diff --git a/lib/src/main/java/org/asamk/signal/manager/api/UserStatus.java b/lib/src/main/java/org/asamk/signal/manager/api/UserStatus.java new file mode 100644 index 00000000..737a2595 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/api/UserStatus.java @@ -0,0 +1,5 @@ +package org.asamk.signal.manager.api; + +import java.util.UUID; + +public record UserStatus(String number, UUID uuid, boolean unrestrictedUnidentifiedAccess) {} diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 5ab237f9..8c89dd4f 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -6,7 +6,7 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.output.JsonWriter; import org.asamk.signal.output.OutputWriter; import org.asamk.signal.output.PlainTextWriter; @@ -16,7 +16,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashSet; import java.util.Map; -import java.util.UUID; public class GetUserStatusCommand implements JsonRpcLocalCommand { @@ -38,9 +37,9 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand { final Namespace ns, final Manager m, final OutputWriter outputWriter ) throws CommandException { // Get a map of registration statuses - Map> registered; + Map registered; try { - registered = m.areUsersRegistered(new HashSet<>(ns.getList("recipient"))); + registered = m.getUserStatus(new HashSet<>(ns.getList("recipient"))); } catch (IOException e) { throw new IOErrorException("Unable to check if users are registered: " + e.getMessage() @@ -53,8 +52,8 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand { if (outputWriter instanceof JsonWriter jsonWriter) { var jsonUserStatuses = registered.entrySet().stream().map(entry -> { - final var number = entry.getValue().first(); - final var uuid = entry.getValue().second(); + final var number = entry.getValue().number(); + final var uuid = entry.getValue().uuid(); return new JsonUserStatus(entry.getKey(), number, uuid == null ? null : uuid.toString(), uuid != null); }).toList(); @@ -63,8 +62,11 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand { final var writer = (PlainTextWriter) outputWriter; for (var entry : registered.entrySet()) { - final var uuid = entry.getValue().second(); - writer.println("{}: {}", entry.getKey(), uuid != null); + final var userStatus = entry.getValue(); + writer.println("{}: {}{}", + entry.getKey(), + userStatus.uuid() != null, + userStatus.unrestrictedUnidentifiedAccess() ? " (unrestricted sealed sender)" : ""); } } } diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index d64abd0b..05ba6b99 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -22,6 +22,7 @@ import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.groups.GroupNotFoundException; @@ -53,7 +54,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.UUID; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.function.Supplier; @@ -87,14 +87,16 @@ public class DbusManagerImpl implements Manager { } @Override - public Map> areUsersRegistered(final Set numbers) throws IOException { + public Map getUserStatus(final Set numbers) throws IOException { final var numbersList = new ArrayList<>(numbers); final var registered = signal.isRegistered(numbersList); - final var result = new HashMap>(); + final var result = new HashMap(); for (var i = 0; i < numbersList.size(); i++) { result.put(numbersList.get(i), - new Pair<>(numbersList.get(i), registered.get(i) ? RecipientAddress.UNKNOWN_UUID : null)); + new UserStatus(numbersList.get(i), + registered.get(i) ? RecipientAddress.UNKNOWN_UUID : null, + false)); } return result; } diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 850bcf1d..746f8f52 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -19,6 +19,7 @@ import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.groups.GroupLinkState; @@ -53,7 +54,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -641,17 +641,14 @@ public class DbusSignalImpl implements Signal { return List.of(); } - Map> registered; + Map registered; try { - registered = m.areUsersRegistered(new HashSet<>(numbers)); + registered = m.getUserStatus(new HashSet<>(numbers)); } catch (IOException e) { throw new Error.Failure(e.getMessage()); } - return numbers.stream().map(number -> { - var uuid = registered.get(number).second(); - return uuid != null; - }).toList(); + return numbers.stream().map(number -> registered.get(number).uuid() != null).toList(); } @Override From 2d60f98e933843ad339ac42ae20a33014a76ee0e Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 18 May 2022 10:45:53 +0200 Subject: [PATCH 097/938] Keep unrestricted unidentified access if it was set before --- .../java/org/asamk/signal/manager/storage/SignalAccount.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7b5411e6..33b9e80c 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 @@ -1302,8 +1302,8 @@ public class SignalAccount implements Closeable { } public boolean isUnrestrictedUnidentifiedAccess() { - // TODO make configurable - return false; + final var profile = getProfileStore().getProfile(getSelfRecipientId()); + return profile != null && profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED; } public boolean isDiscoverableByPhoneNumber() { From 53f47d42fc30a86a9bb6cd08f4678a756f4a4aaf Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 18 May 2022 11:09:05 +0200 Subject: [PATCH 098/938] Refactor set blocked methods to accept multiple recipientIds/groupIds --- .../org/asamk/signal/manager/Manager.java | 9 +++-- .../org/asamk/signal/manager/ManagerImpl.java | 24 ++++++++--- .../asamk/signal/commands/BlockCommand.java | 40 +++++++++---------- .../asamk/signal/commands/UnblockCommand.java | 40 +++++++++---------- .../asamk/signal/dbus/DbusManagerImpl.java | 17 +++++--- .../org/asamk/signal/dbus/DbusSignalImpl.java | 6 +-- 6 files changed, 74 insertions(+), 62 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 d14b0f44..8f49126f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -38,6 +38,7 @@ import java.io.File; import java.io.IOException; import java.net.URI; import java.time.Duration; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -151,12 +152,12 @@ public interface Manager extends Closeable { RecipientIdentifier.Single recipient, String name ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException; - void setContactBlocked( - RecipientIdentifier.Single recipient, boolean blocked + void setContactsBlocked( + Collection recipient, boolean blocked ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException; - void setGroupBlocked( - GroupId groupId, boolean blocked + void setGroupsBlocked( + Collection groupId, boolean blocked ) throws GroupNotFoundException, IOException, NotMasterDeviceException; /** diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index db8744cd..f6205dba 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -79,6 +79,7 @@ import java.io.IOException; import java.net.URI; import java.time.Duration; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -683,25 +684,36 @@ class ManagerImpl implements Manager { } @Override - public void setContactBlocked( - RecipientIdentifier.Single recipient, boolean blocked + public void setContactsBlocked( + Collection recipients, boolean blocked ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException { if (!account.isMasterDevice()) { throw new NotMasterDeviceException(); } - context.getContactHelper().setContactBlocked(context.getRecipientHelper().resolveRecipient(recipient), blocked); + if (recipients.size() == 0) { + return; + } + final var recipientIds = context.getRecipientHelper().resolveRecipients(recipients); + for (final var recipientId : recipientIds) { + context.getContactHelper().setContactBlocked(recipientId, blocked); + } // TODO cycle our profile key, if we're not together in a group with recipient context.getSyncHelper().sendBlockedList(); } @Override - public void setGroupBlocked( - final GroupId groupId, final boolean blocked + public void setGroupsBlocked( + final Collection groupIds, final boolean blocked ) throws GroupNotFoundException, NotMasterDeviceException { if (!account.isMasterDevice()) { throw new NotMasterDeviceException(); } - context.getGroupHelper().setGroupBlocked(groupId, blocked); + if (groupIds.size() == 0) { + return; + } + for (final var groupId : groupIds) { + context.getGroupHelper().setGroupBlocked(groupId, blocked); + } // TODO cycle our profile key context.getSyncHelper().sendBlockedList(); } diff --git a/src/main/java/org/asamk/signal/commands/BlockCommand.java b/src/main/java/org/asamk/signal/commands/BlockCommand.java index 7d72b1c5..8d7f1a8b 100644 --- a/src/main/java/org/asamk/signal/commands/BlockCommand.java +++ b/src/main/java/org/asamk/signal/commands/BlockCommand.java @@ -38,31 +38,27 @@ public class BlockCommand implements JsonRpcLocalCommand { final Namespace ns, final Manager m, final OutputWriter outputWriter ) throws CommandException { final var contacts = ns.getList("recipient"); - for (var contact : CommandUtil.getSingleRecipientIdentifiers(contacts, m.getSelfNumber())) { - try { - m.setContactBlocked(contact, true); - } catch (NotMasterDeviceException e) { - throw new UserErrorException("This command doesn't work on linked devices."); - } catch (IOException e) { - throw new UnexpectedErrorException("Failed to sync block to linked devices: " + e.getMessage(), e); - } catch (UnregisteredRecipientException e) { - throw new UserErrorException("The user " + e.getSender().getIdentifier() + " is not registered."); - } + final var recipients = CommandUtil.getSingleRecipientIdentifiers(contacts, m.getSelfNumber()); + try { + m.setContactsBlocked(recipients, true); + } catch (NotMasterDeviceException e) { + throw new UserErrorException("This command doesn't work on linked devices."); + } catch (IOException e) { + throw new UnexpectedErrorException("Failed to sync block to linked devices: " + e.getMessage(), e); + } catch (UnregisteredRecipientException e) { + throw new UserErrorException("The user " + e.getSender().getIdentifier() + " is not registered."); } final var groupIdStrings = ns.getList("group-id"); - if (groupIdStrings != null) { - for (var groupId : CommandUtil.getGroupIds(groupIdStrings)) { - try { - m.setGroupBlocked(groupId, true); - } catch (NotMasterDeviceException e) { - throw new UserErrorException("This command doesn't work on linked devices."); - } catch (GroupNotFoundException e) { - logger.warn("Group not found {}: {}", groupId.toBase64(), e.getMessage()); - } catch (IOException e) { - throw new UnexpectedErrorException("Failed to sync block to linked devices: " + e.getMessage(), e); - } - } + final var groupIds = CommandUtil.getGroupIds(groupIdStrings); + try { + m.setGroupsBlocked(groupIds, true); + } catch (NotMasterDeviceException e) { + throw new UserErrorException("This command doesn't work on linked devices."); + } catch (GroupNotFoundException e) { + logger.warn("Unknown group id: {}", e.getMessage()); + } catch (IOException e) { + throw new UnexpectedErrorException("Failed to sync block to linked devices: " + e.getMessage(), e); } } } diff --git a/src/main/java/org/asamk/signal/commands/UnblockCommand.java b/src/main/java/org/asamk/signal/commands/UnblockCommand.java index af49236c..4cfe4647 100644 --- a/src/main/java/org/asamk/signal/commands/UnblockCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnblockCommand.java @@ -37,30 +37,28 @@ public class UnblockCommand implements JsonRpcLocalCommand { public void handleCommand( final Namespace ns, final Manager m, final OutputWriter outputWriter ) throws CommandException { - for (var contactNumber : CommandUtil.getSingleRecipientIdentifiers(ns.getList("recipient"), - m.getSelfNumber())) { - try { - m.setContactBlocked(contactNumber, false); - } catch (NotMasterDeviceException e) { - throw new UserErrorException("This command doesn't work on linked devices."); - } catch (IOException e) { - throw new UnexpectedErrorException("Failed to sync unblock to linked devices: " + e.getMessage(), e); - } catch (UnregisteredRecipientException e) { - throw new UserErrorException("The user " + e.getSender().getIdentifier() + " is not registered."); - } + final var contacts = ns.getList("recipient"); + final var recipients = CommandUtil.getSingleRecipientIdentifiers(contacts, m.getSelfNumber()); + try { + m.setContactsBlocked(recipients, false); + } catch (NotMasterDeviceException e) { + throw new UserErrorException("This command doesn't work on linked devices."); + } catch (IOException e) { + throw new UnexpectedErrorException("Failed to sync unblock to linked devices: " + e.getMessage(), e); + } catch (UnregisteredRecipientException e) { + throw new UserErrorException("The user " + e.getSender().getIdentifier() + " is not registered."); } final var groupIdStrings = ns.getList("group-id"); - for (var groupId : CommandUtil.getGroupIds(groupIdStrings)) { - try { - m.setGroupBlocked(groupId, false); - } catch (NotMasterDeviceException e) { - throw new UserErrorException("This command doesn't work on linked devices."); - } catch (GroupNotFoundException e) { - logger.warn("Unknown group id: {}", groupId); - } catch (IOException e) { - throw new UnexpectedErrorException("Failed to sync unblock to linked devices: " + e.getMessage(), e); - } + final var groupIds = CommandUtil.getGroupIds(groupIdStrings); + try { + m.setGroupsBlocked(groupIds, false); + } catch (NotMasterDeviceException e) { + throw new UserErrorException("This command doesn't work on linked devices."); + } catch (GroupNotFoundException e) { + logger.warn("Unknown group id: {}", e.getMessage()); + } catch (IOException e) { + throw new UnexpectedErrorException("Failed to sync unblock to linked devices: " + e.getMessage(), e); } } } diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 05ba6b99..29889c3d 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -47,6 +47,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.time.Duration; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -410,17 +411,21 @@ public class DbusManagerImpl implements Manager { } @Override - public void setContactBlocked( - final RecipientIdentifier.Single recipient, final boolean blocked + public void setContactsBlocked( + final Collection recipients, final boolean blocked ) throws NotMasterDeviceException, IOException { - signal.setContactBlocked(recipient.getIdentifier(), blocked); + for (final var recipient : recipients) { + signal.setContactBlocked(recipient.getIdentifier(), blocked); + } } @Override - public void setGroupBlocked( - final GroupId groupId, final boolean blocked + public void setGroupsBlocked( + final Collection groupIds, final boolean blocked ) throws GroupNotFoundException, IOException { - setGroupProperty(groupId, "IsBlocked", blocked); + for (final var groupId : groupIds) { + setGroupProperty(groupId, "IsBlocked", blocked); + } } private void setGroupProperty(final GroupId groupId, final String propertyName, final boolean blocked) { diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 746f8f52..b4485e32 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -516,7 +516,7 @@ public class DbusSignalImpl implements Signal { @Override public void setContactBlocked(final String number, final boolean blocked) { try { - m.setContactBlocked(getSingleRecipientIdentifier(number, m.getSelfNumber()), blocked); + m.setContactsBlocked(List.of(getSingleRecipientIdentifier(number, m.getSelfNumber())), blocked); } catch (NotMasterDeviceException e) { throw new Error.Failure("This command doesn't work on linked devices."); } catch (IOException e) { @@ -529,7 +529,7 @@ public class DbusSignalImpl implements Signal { @Override public void setGroupBlocked(final byte[] groupId, final boolean blocked) { try { - m.setGroupBlocked(getGroupId(groupId), blocked); + m.setGroupsBlocked(List.of(getGroupId(groupId)), blocked); } catch (NotMasterDeviceException e) { throw new Error.Failure("This command doesn't work on linked devices."); } catch (GroupNotFoundException e) { @@ -1287,7 +1287,7 @@ public class DbusSignalImpl implements Signal { private void setIsBlocked(final boolean isBlocked) { try { - m.setGroupBlocked(groupId, isBlocked); + m.setGroupsBlocked(List.of(groupId), isBlocked); } catch (NotMasterDeviceException e) { throw new Error.Failure("This command doesn't work on linked devices."); } catch (GroupNotFoundException e) { From 7ac6c9a170eba9dab7f9f0ebc6c3bffc017ab691 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 18 May 2022 15:27:02 +0200 Subject: [PATCH 099/938] Cleanup fileChannel if file locking fails --- .../signal/manager/storage/SignalAccount.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) 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 33b9e80c..79f43670 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 @@ -884,17 +884,25 @@ public class SignalAccount implements Closeable { private static Pair openFileChannel(File fileName, boolean waitForLock) throws IOException { var fileChannel = new RandomAccessFile(fileName, "rw").getChannel(); - var lock = fileChannel.tryLock(); - if (lock == null) { - if (!waitForLock) { - logger.debug("Config file is in use by another instance."); - throw new IOException("Config file is in use by another instance."); + try { + var lock = fileChannel.tryLock(); + if (lock == null) { + if (!waitForLock) { + logger.debug("Config file is in use by another instance."); + throw new IOException("Config file is in use by another instance."); + } + logger.info("Config file is in use by another instance, waiting…"); + lock = fileChannel.lock(); + logger.info("Config file lock acquired."); + } + final var result = new Pair<>(fileChannel, lock); + fileChannel = null; + return result; + } finally { + if (fileChannel != null) { + fileChannel.close(); } - logger.info("Config file is in use by another instance, waiting…"); - lock = fileChannel.lock(); - logger.info("Config file lock acquired."); } - return new Pair<>(fileChannel, lock); } public void addPreKeys(ServiceIdType serviceIdType, List records) { From 06a9884e99f620ca5c85015d4fa8ee23abbad48d Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 18 May 2022 19:12:20 +0200 Subject: [PATCH 100/938] Don't reset avatar url path when updating profile with same avatar --- .../java/org/asamk/signal/manager/helper/ProfileHelper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 982acddd..7d68124a 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 @@ -157,7 +157,9 @@ public final class ProfileHelper { paymentsAddress, avatarUploadParams, List.of(/* TODO */)); - builder.withAvatarUrlPath(avatarPath.orElse(null)); + if (!avatarUploadParams.keepTheSame) { + builder.withAvatarUrlPath(avatarPath.orElse(null)); + } newProfile = builder.build(); } } From b1e56faab2b2417af4f4a7f1033ffd752a9d16e4 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 18 May 2022 15:26:34 +0200 Subject: [PATCH 101/938] Store profile sharing enabled for contacts Automatically enable it when sending direct messages --- .../signal/manager/helper/ContactHelper.java | 3 +++ .../signal/manager/helper/SendHelper.java | 17 +++++++++--- .../signal/manager/helper/StorageHelper.java | 19 ++++++++----- .../signal/manager/storage/SignalAccount.java | 3 ++- .../manager/storage/recipients/Contact.java | 21 +++++++++++++-- .../storage/recipients/RecipientStore.java | 27 ++++++++++++++----- .../asamk/signal/dbus/DbusManagerImpl.java | 2 +- 7 files changed, 71 insertions(+), 21 deletions(-) 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 71b2ded8..164fea76 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 @@ -36,6 +36,9 @@ public class ContactHelper { public void setContactBlocked(RecipientId recipientId, boolean blocked) { var contact = account.getContactStore().getContact(recipientId); final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); + if (blocked) { + builder.withProfileSharingEnabled(false); + } account.getContactStore().storeContact(recipientId, builder.withBlocked(blocked).build()); } } 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 0797f84c..59a7a671 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 @@ -11,6 +11,7 @@ import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfo; +import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.sendLog.MessageSendLogEntry; @@ -73,10 +74,20 @@ public class SendHelper { public SendMessageResult sendMessage( final SignalServiceDataMessage.Builder messageBuilder, final RecipientId recipientId ) throws IOException { - final var contact = account.getContactStore().getContact(recipientId); - final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0; + var contact = account.getContactStore().getContact(recipientId); + if (contact == null || !contact.isProfileSharingEnabled()) { + final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); + contact = contactBuilder.withProfileSharingEnabled(true).build(); + account.getContactStore().storeContact(recipientId, contact); + } + + final var expirationTime = contact.getMessageExpirationTime(); messageBuilder.withExpiration(expirationTime); - messageBuilder.withProfileKey(account.getProfileKey().serialize()); + + if (!contact.isBlocked()) { + final var profileKey = account.getProfileKey().serialize(); + messageBuilder.withProfileKey(profileKey); + } final var message = messageBuilder.build(); return sendMessage(message, recipientId); 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 4ca383e3..de28638e 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 @@ -88,13 +88,18 @@ public class StorageHelper { final var recipientId = account.getRecipientStore().resolveRecipient(address); final var contact = account.getContactStore().getContact(recipientId); - if (contactRecord.getGivenName().isPresent() || contactRecord.getFamilyName().isPresent() || ( - (contact == null || !contact.isBlocked()) && contactRecord.isBlocked() - )) { - final var newContact = (contact == null ? Contact.newBuilder() : Contact.newBuilder(contact)).withBlocked( - contactRecord.isBlocked()).withName(( - contactRecord.getGivenName().orElse("") + " " + contactRecord.getFamilyName().orElse("") - ).trim()).build(); + final var blocked = contact != null && contact.isBlocked(); + final var profileShared = contact != null && contact.isProfileSharingEnabled(); + if (contactRecord.getGivenName().isPresent() + || contactRecord.getFamilyName().isPresent() + || blocked != contactRecord.isBlocked() + || profileShared != contactRecord.isProfileSharingEnabled()) { + final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); + final var name = contactRecord.getGivenName().orElse("") + " " + contactRecord.getFamilyName().orElse(""); + final var newContact = contactBuilder.withBlocked(contactRecord.isBlocked()) + .withName(name.trim()) + .withProfileSharingEnabled(contactRecord.isProfileSharingEnabled()) + .build(); account.getContactStore().storeContact(recipientId, newContact); } 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 79f43670..6b2d063b 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 @@ -715,7 +715,8 @@ public class SignalAccount implements Closeable { contact.color, contact.messageExpirationTime, contact.blocked, - contact.archived)); + contact.archived, + false)); // Store profile keys only in profile store var profileKeyString = contact.profileKey; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Contact.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Contact.java index c03f5c7f..25d6151f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Contact.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Contact.java @@ -14,18 +14,22 @@ public class Contact { private final boolean archived; + private final boolean profileSharingEnabled; + public Contact( final String name, final String color, final int messageExpirationTime, final boolean blocked, - final boolean archived + final boolean archived, + final boolean profileSharingEnabled ) { this.name = name; this.color = color; this.messageExpirationTime = messageExpirationTime; this.blocked = blocked; this.archived = archived; + this.profileSharingEnabled = profileSharingEnabled; } private Contact(final Builder builder) { @@ -34,6 +38,7 @@ public class Contact { messageExpirationTime = builder.messageExpirationTime; blocked = builder.blocked; archived = builder.archived; + profileSharingEnabled = builder.profileSharingEnabled; } public static Builder newBuilder() { @@ -47,6 +52,7 @@ public class Contact { builder.messageExpirationTime = copy.getMessageExpirationTime(); builder.blocked = copy.isBlocked(); builder.archived = copy.isArchived(); + builder.profileSharingEnabled = copy.isProfileSharingEnabled(); return builder; } @@ -70,6 +76,10 @@ public class Contact { return archived; } + public boolean isProfileSharingEnabled() { + return profileSharingEnabled; + } + @Override public boolean equals(final Object o) { if (this == o) return true; @@ -78,13 +88,14 @@ public class Contact { return messageExpirationTime == contact.messageExpirationTime && blocked == contact.blocked && archived == contact.archived + && profileSharingEnabled == contact.profileSharingEnabled && Objects.equals(name, contact.name) && Objects.equals(color, contact.color); } @Override public int hashCode() { - return Objects.hash(name, color, messageExpirationTime, blocked, archived); + return Objects.hash(name, color, messageExpirationTime, blocked, archived, profileSharingEnabled); } public static final class Builder { @@ -94,6 +105,7 @@ public class Contact { private int messageExpirationTime; private boolean blocked; private boolean archived; + private boolean profileSharingEnabled; private Builder() { } @@ -123,6 +135,11 @@ public class Contact { return this; } + public Builder withProfileSharingEnabled(final boolean val) { + profileSharingEnabled = val; + return this; + } + public Contact build() { return new Contact(this); } 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 619ec418..16ec9bed 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 @@ -26,6 +26,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Base64; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -75,7 +76,8 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile r.contact.color, r.contact.messageExpirationTime, r.contact.blocked, - r.contact.archived); + r.contact.archived, + r.contact.profileSharingEnabled); } ProfileKey profileKey = null; @@ -149,10 +151,6 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile this.lastId = lastId; } - public boolean isBulkUpdating() { - return isBulkUpdating; - } - public void setBulkUpdating(final boolean bulkUpdating) { isBulkUpdating = bulkUpdating; if (!bulkUpdating) { @@ -174,6 +172,15 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile } } + public Collection getRecipientIdsWithEnabledProfileSharing() { + synchronized (recipients) { + return recipients.values().stream().filter(r -> { + final var contact = r.getContact(); + return contact != null && !contact.isBlocked() && contact.isProfileSharingEnabled(); + }).map(Recipient::getRecipientId).toList(); + } + } + @Override public RecipientId resolveRecipient(ServiceId serviceId) { return resolveRecipient(new RecipientAddress(serviceId.uuid()), false, false); @@ -545,7 +552,8 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile recipientContact.getColor(), recipientContact.getMessageExpirationTime(), recipientContact.isBlocked(), - recipientContact.isArchived()); + recipientContact.isArchived(), + recipientContact.isProfileSharingEnabled()); final var recipientProfile = recipient.getProfile(); final var profile = recipientProfile == null ? null @@ -599,7 +607,12 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile ) { private record Contact( - String name, String color, int messageExpirationTime, boolean blocked, boolean archived + String name, + String color, + int messageExpirationTime, + boolean blocked, + boolean archived, + boolean profileSharingEnabled ) {} private record Profile( diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 29889c3d..5b33b039 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -554,7 +554,7 @@ public class DbusManagerImpl implements Manager { return null; } return new Pair<>(new RecipientAddress(null, n), - new Contact(contactName, null, 0, signal.isContactBlocked(n), false)); + new Contact(contactName, null, 0, signal.isContactBlocked(n), false, false)); }).filter(Objects::nonNull).toList(); } From cf1626ea315ba3d7d76fc8f9ec0acc1c7597d631 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 18 May 2022 12:19:06 +0200 Subject: [PATCH 102/938] Rotate profile key after blocking a contact/group --- .../org/asamk/signal/manager/ManagerImpl.java | 27 ++++++++-- .../manager/actions/SendProfileKeyAction.java | 33 ++++++++++++ .../UpdateAccountAttributesAction.java | 20 +++++++ .../signal/manager/helper/GroupHelper.java | 18 +++++++ .../signal/manager/helper/GroupV2Helper.java | 32 +++++++++++ .../helper/IncomingMessageHandler.java | 9 ++++ .../signal/manager/helper/ProfileHelper.java | 54 +++++++++++++++---- .../signal/manager/helper/SendHelper.java | 15 ++++++ .../signal/manager/helper/StorageHelper.java | 1 + .../signal/manager/storage/SignalAccount.java | 3 +- .../storage/profiles/ProfileStore.java | 2 + .../storage/recipients/RecipientStore.java | 23 +++++--- 12 files changed, 218 insertions(+), 19 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/actions/SendProfileKeyAction.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/actions/UpdateAccountAttributesAction.java diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index f6205dba..b3e6a0ae 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -694,27 +694,48 @@ class ManagerImpl implements Manager { return; } final var recipientIds = context.getRecipientHelper().resolveRecipients(recipients); + final var selfRecipientId = account.getSelfRecipientId(); + boolean shouldRotateProfileKey = false; for (final var recipientId : recipientIds) { + if (context.getContactHelper().isContactBlocked(recipientId) == blocked) { + continue; + } context.getContactHelper().setContactBlocked(recipientId, blocked); + // if we don't have a common group with the blocked contact we need to rotate the profile key + shouldRotateProfileKey = blocked && ( + shouldRotateProfileKey || account.getGroupStore() + .getGroups() + .stream() + .noneMatch(g -> g.isMember(selfRecipientId) && g.isMember(recipientId)) + ); + } + if (shouldRotateProfileKey) { + context.getProfileHelper().rotateProfileKey(); } - // TODO cycle our profile key, if we're not together in a group with recipient context.getSyncHelper().sendBlockedList(); } @Override public void setGroupsBlocked( final Collection groupIds, final boolean blocked - ) throws GroupNotFoundException, NotMasterDeviceException { + ) throws GroupNotFoundException, NotMasterDeviceException, IOException { if (!account.isMasterDevice()) { throw new NotMasterDeviceException(); } if (groupIds.size() == 0) { return; } + boolean shouldRotateProfileKey = false; for (final var groupId : groupIds) { + if (context.getGroupHelper().isGroupBlocked(groupId) == blocked) { + continue; + } context.getGroupHelper().setGroupBlocked(groupId, blocked); + shouldRotateProfileKey = blocked; + } + if (shouldRotateProfileKey) { + context.getProfileHelper().rotateProfileKey(); } - // TODO cycle our profile key context.getSyncHelper().sendBlockedList(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/SendProfileKeyAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/SendProfileKeyAction.java new file mode 100644 index 00000000..5695e458 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/actions/SendProfileKeyAction.java @@ -0,0 +1,33 @@ +package org.asamk.signal.manager.actions; + +import org.asamk.signal.manager.helper.Context; +import org.asamk.signal.manager.storage.recipients.RecipientId; + +import java.util.Objects; + +public class SendProfileKeyAction implements HandleAction { + + private final RecipientId recipientId; + + public SendProfileKeyAction(final RecipientId recipientId) { + this.recipientId = recipientId; + } + + @Override + public void execute(Context context) throws Throwable { + context.getSendHelper().sendProfileKey(recipientId); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final SendProfileKeyAction that = (SendProfileKeyAction) o; + return recipientId.equals(that.recipientId); + } + + @Override + public int hashCode() { + return Objects.hash(recipientId); + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/UpdateAccountAttributesAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/UpdateAccountAttributesAction.java new file mode 100644 index 00000000..da04dd18 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/actions/UpdateAccountAttributesAction.java @@ -0,0 +1,20 @@ +package org.asamk.signal.manager.actions; + +import org.asamk.signal.manager.helper.Context; + +public class UpdateAccountAttributesAction implements HandleAction { + + private static final UpdateAccountAttributesAction INSTANCE = new UpdateAccountAttributesAction(); + + private UpdateAccountAttributesAction() { + } + + public static UpdateAccountAttributesAction create() { + return INSTANCE; + } + + @Override + public void execute(Context context) throws Throwable { + context.getAccountHelper().updateAccountAttributes(); + } +} 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 9d4f712c..ab3e1264 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 @@ -254,6 +254,24 @@ public class GroupHelper { return result; } + public void updateGroupProfileKey(GroupIdV2 groupId) throws GroupNotFoundException, NotAGroupMemberException, IOException { + var group = getGroupForUpdating(groupId); + + if (group instanceof GroupInfoV2 groupInfoV2) { + Pair groupChangePair; + try { + groupChangePair = context.getGroupV2Helper().updateSelfProfileKey(groupInfoV2); + } catch (ConflictException e) { + // Detected conflicting update, refreshing group and trying again + groupInfoV2 = (GroupInfoV2) getGroup(groupId, true); + groupChangePair = context.getGroupV2Helper().updateSelfProfileKey(groupInfoV2); + } + if (groupChangePair != null) { + sendUpdateGroupV2Message(groupInfoV2, groupChangePair.first(), groupChangePair.second()); + } + } + } + public Pair joinGroup( GroupInviteLinkUrl inviteLinkUrl ) throws IOException, InactiveGroupLinkException { 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 06a0b89a..385acc59 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 @@ -25,6 +25,7 @@ import org.signal.storageservice.protos.groups.Member; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.DecryptedGroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; +import org.signal.storageservice.protos.groups.local.DecryptedMember; import org.signal.storageservice.protos.groups.local.DecryptedPendingMember; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,6 +46,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Optional; @@ -340,6 +342,36 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } + Pair updateSelfProfileKey(GroupInfoV2 groupInfoV2) throws IOException { + Optional selfInGroup = groupInfoV2.getGroup() == null + ? Optional.empty() + : DecryptedGroupUtil.findMemberByUuid(groupInfoV2.getGroup().getMembersList(), getSelfAci().uuid()); + if (selfInGroup.isEmpty()) { + logger.trace("Not updating group, self not in group " + groupInfoV2.getGroupId().toBase64()); + return null; + } + + final var profileKey = context.getAccount().getProfileKey(); + if (Arrays.equals(profileKey.serialize(), selfInGroup.get().getProfileKey().toByteArray())) { + logger.trace("Not updating group, own Profile Key is already up to date in group " + + groupInfoV2.getGroupId().toBase64()); + return null; + } + logger.debug("Updating own profile key in group " + groupInfoV2.getGroupId().toBase64()); + + final var selfRecipientId = context.getAccount().getSelfRecipientId(); + final var profileKeyCredential = context.getProfileHelper().getRecipientProfileKeyCredential(selfRecipientId); + if (profileKeyCredential == null) { + logger.trace("Cannot update profile key as self does not have a versioned profile"); + return null; + } + + final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); + final var change = groupOperations.createUpdateProfileKeyCredentialChange(profileKeyCredential); + change.setSourceUuid(getSelfAci().toByteString()); + return commitChange(groupInfoV2, change); + } + GroupChange joinGroup( GroupMasterKey groupMasterKey, GroupLinkPassword groupLinkPassword, 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 489be834..5e310a48 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 @@ -11,6 +11,7 @@ import org.asamk.signal.manager.actions.RetrieveStorageDataAction; import org.asamk.signal.manager.actions.SendGroupInfoAction; import org.asamk.signal.manager.actions.SendGroupInfoRequestAction; import org.asamk.signal.manager.actions.SendPniIdentityKeyAction; +import org.asamk.signal.manager.actions.SendProfileKeyAction; import org.asamk.signal.manager.actions.SendReceiptAction; import org.asamk.signal.manager.actions.SendRetryMessageRequestAction; import org.asamk.signal.manager.actions.SendSyncBlockedListAction; @@ -18,6 +19,7 @@ import org.asamk.signal.manager.actions.SendSyncConfigurationAction; import org.asamk.signal.manager.actions.SendSyncContactsAction; import org.asamk.signal.manager.actions.SendSyncGroupsAction; import org.asamk.signal.manager.actions.SendSyncKeysAction; +import org.asamk.signal.manager.actions.UpdateAccountAttributesAction; import org.asamk.signal.manager.api.MessageEnvelope; import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.StickerPackId; @@ -246,6 +248,13 @@ public final class IncomingMessageHandler { if (content.isNeedsReceipt()) { actions.add(new SendReceiptAction(sender, message.getTimestamp())); + } else { + // Message wasn't sent as unidentified sender message + final var contact = context.getAccount().getContactStore().getContact(sender); + if (contact != null && !contact.isBlocked() && contact.isProfileSharingEnabled()) { + actions.add(UpdateAccountAttributesAction.create()); + actions.add(new SendProfileKeyAction(sender)); + } } actions.addAll(handleSignalServiceDataMessage(message, 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 7d68124a..4bc00317 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 @@ -4,11 +4,15 @@ import com.google.protobuf.InvalidProtocolBufferException; import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.config.ServiceConfig; +import org.asamk.signal.manager.groups.GroupNotFoundException; +import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.storage.SignalAccount; +import org.asamk.signal.manager.storage.groups.GroupInfoV2; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.util.IOUtils; +import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.ProfileUtils; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.protocol.IdentityKey; @@ -57,6 +61,35 @@ public final class ProfileHelper { this.context = context; } + public void rotateProfileKey() throws IOException { + var profileKey = KeyUtils.createProfileKey(); + account.setProfileKey(profileKey); + context.getAccountHelper().updateAccountAttributes(); + setProfile(true, true, null, null, null, null, null); + // TODO update profile key in storage + + final var recipientIds = account.getRecipientStore().getRecipientIdsWithEnabledProfileSharing(); + for (final var recipientId : recipientIds) { + context.getSendHelper().sendProfileKey(recipientId); + } + + final var selfRecipientId = account.getSelfRecipientId(); + final var activeGroupIds = account.getGroupStore() + .getGroups() + .stream() + .filter(g -> g instanceof GroupInfoV2 && g.isMember(selfRecipientId)) + .map(g -> (GroupInfoV2) g) + .map(GroupInfoV2::getGroupId) + .toList(); + for (final var groupId : activeGroupIds) { + try { + context.getGroupHelper().updateGroupProfileKey(groupId); + } catch (GroupNotFoundException | NotAGroupMemberException | IOException e) { + logger.warn("Failed to update group profile key: {}", e.getMessage()); + } + } + } + public Profile getRecipientProfile(RecipientId recipientId) { return getRecipientProfile(recipientId, false); } @@ -106,11 +139,12 @@ public final class ProfileHelper { public void setProfile( String givenName, final String familyName, String about, String aboutEmoji, Optional avatar ) throws IOException { - setProfile(true, givenName, familyName, about, aboutEmoji, avatar); + setProfile(true, false, givenName, familyName, about, aboutEmoji, avatar); } public void setProfile( boolean uploadProfile, + boolean forceUploadAvatar, String givenName, final String familyName, String about, @@ -134,13 +168,14 @@ public final class ProfileHelper { var newProfile = builder.build(); if (uploadProfile) { - try (final var streamDetails = avatar != null && avatar.isPresent() ? Utils.createStreamDetailsFromFile( - avatar.get()) : null) { - final var avatarUploadParams = avatar == null - ? AvatarUploadParams.unchanged(true) - : avatar.isPresent() - ? AvatarUploadParams.forAvatar(streamDetails) - : AvatarUploadParams.unchanged(false); + final var streamDetails = avatar != null && avatar.isPresent() + ? Utils.createStreamDetailsFromFile(avatar.get()) + : forceUploadAvatar && avatar == null ? context.getAvatarStore() + .retrieveProfileAvatar(account.getSelfRecipientAddress()) : null; + try (streamDetails) { + final var avatarUploadParams = streamDetails != null + ? AvatarUploadParams.forAvatar(streamDetails) + : avatar == null ? AvatarUploadParams.unchanged(true) : AvatarUploadParams.unchanged(false); final var paymentsAddress = Optional.ofNullable(newProfile.getPaymentAddress()).map(data -> { try { return SignalServiceProtos.PaymentAddress.parseFrom(data); @@ -148,6 +183,7 @@ public final class ProfileHelper { return null; } }); + logger.debug("Uploading new profile"); final var avatarPath = dependencies.getAccountManager() .setVersionedProfile(account.getAci(), account.getProfileKey(), @@ -156,7 +192,7 @@ public final class ProfileHelper { newProfile.getAboutEmoji() == null ? "" : newProfile.getAboutEmoji(), paymentsAddress, avatarUploadParams, - List.of(/* TODO */)); + List.of(/* TODO implement support for badges */)); if (!avatarUploadParams.keepTheSame) { builder.withAvatarUrlPath(avatarPath.orElse(null)); } 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 59a7a671..a2e2379b 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 @@ -139,6 +139,21 @@ public class SendHelper { return result; } + public SendMessageResult sendProfileKey(RecipientId recipientId) { + logger.debug("Sending updated profile key to recipient: {}", recipientId); + final var profileKey = account.getProfileKey().serialize(); + final var message = SignalServiceDataMessage.newBuilder() + .asProfileKeyUpdate(true) + .withProfileKey(profileKey) + .build(); + return handleSendMessage(recipientId, + (messageSender, address, unidentifiedAccess) -> messageSender.sendDataMessage(address, + unidentifiedAccess, + ContentHint.IMPLICIT, + message, + SignalServiceMessageSender.IndividualSendEvents.EMPTY)); + } + public SendMessageResult sendRetryReceipt( DecryptionErrorMessage errorMessage, RecipientId recipientId, Optional groupId ) { 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 de28638e..469ca02e 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 @@ -229,6 +229,7 @@ public class StorageHelper { context.getProfileHelper() .setProfile(false, + false, accountRecord.getGivenName().orElse(null), accountRecord.getFamilyName().orElse(null), 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 6b2d063b..ac41d791 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 @@ -373,7 +373,7 @@ public class SignalAccount implements Closeable { setProfileKey(KeyUtils.createProfileKey()); } // Ensure our profile key is stored in profile store - getProfileStore().storeProfileKey(getSelfRecipientId(), getProfileKey()); + getProfileStore().storeSelfProfileKey(getSelfRecipientId(), getProfileKey()); if (previousStorageVersion < 3) { for (final var group : groupStore.getGroups()) { if (group instanceof GroupInfoV2 && group.getDistributionId() == null) { @@ -1266,6 +1266,7 @@ public class SignalAccount implements Closeable { } this.profileKey = profileKey; save(); + getProfileStore().storeSelfProfileKey(getSelfRecipientId(), getProfileKey()); } public byte[] getSelfUnidentifiedAccessKey() { 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 0ff20042..df65db0d 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 @@ -15,6 +15,8 @@ public interface ProfileStore { void storeProfile(RecipientId recipientId, Profile profile); + void storeSelfProfileKey(RecipientId recipientId, ProfileKey profileKey); + void storeProfileKey(RecipientId recipientId, ProfileKey profileKey); void storeProfileKeyCredential(RecipientId recipientId, ProfileKeyCredential profileKeyCredential); 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 16ec9bed..299a3980 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 @@ -325,8 +325,17 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile } } + @Override + public void storeSelfProfileKey(final RecipientId recipientId, final ProfileKey profileKey) { + storeProfileKey(recipientId, profileKey, false); + } + @Override public void storeProfileKey(RecipientId recipientId, final ProfileKey profileKey) { + storeProfileKey(recipientId, profileKey, true); + } + + private void storeProfileKey(RecipientId recipientId, final ProfileKey profileKey, boolean resetProfile) { synchronized (recipients) { final var recipient = recipients.get(recipientId); if (profileKey != null && profileKey.equals(recipient.getProfileKey()) && ( @@ -339,13 +348,15 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile return; } - final var newRecipient = Recipient.newBuilder(recipient) + final var builder = Recipient.newBuilder(recipient) .withProfileKey(profileKey) - .withProfileKeyCredential(null) - .withProfile(recipient.getProfile() == null - ? null - : Profile.newBuilder(recipient.getProfile()).withLastUpdateTimestamp(0).build()) - .build(); + .withProfileKeyCredential(null); + if (resetProfile) { + builder.withProfile(recipient.getProfile() == null + ? null + : Profile.newBuilder(recipient.getProfile()).withLastUpdateTimestamp(0).build()); + } + final var newRecipient = builder.build(); storeRecipientLocked(recipientId, newRecipient); } } From be28d13d0dfb46df57b8406d9c33651bce18eee9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 19 May 2022 10:32:57 +0200 Subject: [PATCH 103/938] Update libsignal-service-java --- build.gradle.kts | 3 +-- lib/build.gradle.kts | 4 ++-- .../java/org/asamk/signal/manager/config/LiveConfig.java | 6 +++--- .../java/org/asamk/signal/manager/config/StagingConfig.java | 6 +++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d0ef32f6..926ae182 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,7 +21,6 @@ graalvmNative { binaries { this["main"].run { configurationFileDirectories.from(file("graalvm-config-dir")) - buildArgs.add("--allow-incomplete-classpath") buildArgs.add("--report-unsupported-elements-at-runtime") } } @@ -34,7 +33,7 @@ repositories { dependencies { implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") - implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.2.2") + implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.3") implementation("net.sourceforge.argparse4j", "argparse4j", "0.9.0") implementation("com.github.hypfvieh", "dbus-java-transport-native-unixsocket", "4.0.0") implementation("org.slf4j", "slf4j-api", "1.7.36") diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index e1590dc2..cad9ca8b 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,8 +14,8 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_48") - implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.2.2") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_49") + implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.3") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") implementation("org.slf4j", "slf4j-api", "1.7.36") 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 9c15bc71..98e1f026 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 @@ -6,7 +6,7 @@ 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.SignalCdnUrl; -import org.whispersystems.signalservice.internal.configuration.SignalCdshUrl; +import org.whispersystems.signalservice.internal.configuration.SignalCdsiUrl; import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl; import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl; import org.whispersystems.signalservice.internal.configuration.SignalProxy; @@ -39,7 +39,7 @@ class LiveConfig { private final static String SIGNAL_CONTACT_DISCOVERY_URL = "https://api.directory.signal.org"; private final static String SIGNAL_KEY_BACKUP_URL = "https://api.backup.signal.org"; private final static String STORAGE_URL = "https://storage.signal.org"; - private final static String SIGNAL_CDSH_URL = ""; + private final static String SIGNAL_CDSI_URL = ""; private final static TrustStore TRUST_STORE = new WhisperTrustStore(); private final static Optional dns = Optional.empty(); @@ -60,7 +60,7 @@ class LiveConfig { TRUST_STORE)}, new SignalKeyBackupServiceUrl[]{new SignalKeyBackupServiceUrl(SIGNAL_KEY_BACKUP_URL, TRUST_STORE)}, new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)}, - new SignalCdshUrl[]{new SignalCdshUrl(SIGNAL_CDSH_URL, TRUST_STORE)}, + new SignalCdsiUrl[]{new SignalCdsiUrl(SIGNAL_CDSI_URL, TRUST_STORE)}, interceptors, dns, proxy, 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 bba5b333..8faec0eb 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 @@ -6,7 +6,7 @@ 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.SignalCdnUrl; -import org.whispersystems.signalservice.internal.configuration.SignalCdshUrl; +import org.whispersystems.signalservice.internal.configuration.SignalCdsiUrl; import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl; import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl; import org.whispersystems.signalservice.internal.configuration.SignalProxy; @@ -39,7 +39,7 @@ class StagingConfig { private final static String SIGNAL_CONTACT_DISCOVERY_URL = "https://api-staging.directory.signal.org"; private final static String SIGNAL_KEY_BACKUP_URL = "https://api-staging.backup.signal.org"; private final static String STORAGE_URL = "https://storage-staging.signal.org"; - private final static String SIGNAL_CDSH_URL = "https://cdsh.staging.signal.org"; + private final static String SIGNAL_CDSI_URL = "https://cdsi.staging.signal.org"; private final static TrustStore TRUST_STORE = new WhisperTrustStore(); private final static Optional dns = Optional.empty(); @@ -60,7 +60,7 @@ class StagingConfig { TRUST_STORE)}, new SignalKeyBackupServiceUrl[]{new SignalKeyBackupServiceUrl(SIGNAL_KEY_BACKUP_URL, TRUST_STORE)}, new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)}, - new SignalCdshUrl[]{new SignalCdshUrl(SIGNAL_CDSH_URL, TRUST_STORE)}, + new SignalCdsiUrl[]{new SignalCdsiUrl(SIGNAL_CDSI_URL, TRUST_STORE)}, interceptors, dns, proxy, From 06e281101223662c6ad4e1243a3d997b26cb8bc4 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 19 May 2022 12:23:35 +0200 Subject: [PATCH 104/938] Only update profile keys from authoritative group changes --- .../signal/manager/helper/GroupHelper.java | 82 ++++++++++++++++++- .../signal/manager/helper/GroupV2Helper.java | 74 +++++++++++++++-- 2 files changed, 144 insertions(+), 12 deletions(-) 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 ab3e1264..9346372c 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 @@ -31,9 +31,11 @@ import org.signal.libsignal.zkgroup.groups.GroupSecretParams; import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroup; +import org.signal.storageservice.protos.groups.local.DecryptedGroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupHistoryEntry; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; @@ -50,8 +52,10 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -123,12 +127,22 @@ public class GroupHelper { if (signedGroupChange != null && groupInfoV2.getGroup() != null && groupInfoV2.getGroup().getRevision() + 1 == revision) { - group = context.getGroupV2Helper() - .getUpdatedDecryptedGroup(groupInfoV2.getGroup(), signedGroupChange, groupMasterKey); + final var decryptedGroupChange = context.getGroupV2Helper() + .getDecryptedGroupChange(signedGroupChange, groupMasterKey); + + if (decryptedGroupChange != null) { + storeProfileKeyFromChange(decryptedGroupChange); + group = context.getGroupV2Helper() + .getUpdatedDecryptedGroup(groupInfoV2.getGroup(), decryptedGroupChange); + } } if (group == null) { try { group = context.getGroupV2Helper().getDecryptedGroup(groupSecretParams); + + if (group != null) { + storeProfileKeysFromHistory(groupSecretParams, groupInfoV2, group); + } } catch (NotAGroupMemberException ignored) { } } @@ -373,6 +387,17 @@ public class GroupHelper { groupInfoV2.setPermissionDenied(true); decryptedGroup = null; } + if (decryptedGroup != null) { + try { + storeProfileKeysFromHistory(groupSecretParams, groupInfoV2, decryptedGroup); + } catch (NotAGroupMemberException ignored) { + } + storeProfileKeysFromMembers(decryptedGroup); + final var avatar = decryptedGroup.getAvatar(); + if (avatar != null && !avatar.isEmpty()) { + downloadGroupAvatar(groupInfoV2.getGroupId(), groupSecretParams, avatar); + } + } groupInfoV2.setGroup(decryptedGroup, account.getRecipientStore()); account.getGroupStore().updateGroup(group); } @@ -417,14 +442,63 @@ public class GroupHelper { for (var member : group.getMembersList()) { final var serviceId = ServiceId.fromByteString(member.getUuid()); final var recipientId = account.getRecipientStore().resolveRecipient(serviceId); + final var profileStore = account.getProfileStore(); + if (profileStore.getProfileKey(recipientId) != null) { + // We already have a profile key, not updating it from a non-authoritative source + continue; + } try { - account.getProfileStore() - .storeProfileKey(recipientId, new ProfileKey(member.getProfileKey().toByteArray())); + profileStore.storeProfileKey(recipientId, new ProfileKey(member.getProfileKey().toByteArray())); } catch (InvalidInputException ignored) { } } } + private void storeProfileKeyFromChange(final DecryptedGroupChange decryptedGroupChange) { + final var profileKeyFromChange = context.getGroupV2Helper() + .getAuthoritativeProfileKeyFromChange(decryptedGroupChange); + + if (profileKeyFromChange != null) { + final var serviceId = profileKeyFromChange.first(); + final var profileKey = profileKeyFromChange.second(); + final var recipientId = account.getRecipientStore().resolveRecipient(serviceId); + account.getProfileStore().storeProfileKey(recipientId, profileKey); + } + } + + private void storeProfileKeysFromHistory( + final GroupSecretParams groupSecretParams, + final GroupInfoV2 localGroup, + final DecryptedGroup newDecryptedGroup + ) throws NotAGroupMemberException { + final var revisionWeWereAdded = context.getGroupV2Helper().findRevisionWeWereAdded(newDecryptedGroup); + final var localRevision = localGroup.getGroup() == null ? 0 : localGroup.getGroup().getRevision(); + var fromRevision = Math.max(revisionWeWereAdded, localRevision); + final var newProfileKeys = new HashMap(); + while (true) { + final var page = context.getGroupV2Helper().getDecryptedGroupHistoryPage(groupSecretParams, fromRevision); + page.getResults() + .stream() + .map(DecryptedGroupHistoryEntry::getChange) + .filter(Optional::isPresent) + .map(Optional::get) + .map(context.getGroupV2Helper()::getAuthoritativeProfileKeyFromChange) + .filter(Objects::nonNull) + .forEach(p -> { + final var serviceId = p.first(); + final var profileKey = p.second(); + final var recipientId = account.getRecipientStore().resolveRecipient(serviceId); + newProfileKeys.put(recipientId, profileKey); + }); + if (!page.getPagingData().hasMorePages()) { + break; + } + fromRevision = page.getPagingData().getNextPageRevision(); + } + + newProfileKeys.forEach(account.getProfileStore()::storeProfileKey); + } + private GroupInfo getGroupForUpdating(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { var g = getGroup(groupId); if (g == null) { 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 385acc59..9b934580 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 @@ -1,5 +1,6 @@ package org.asamk.signal.manager.helper; +import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import org.asamk.signal.manager.SignalDependencies; @@ -19,6 +20,7 @@ import org.signal.libsignal.zkgroup.auth.AuthCredentialResponse; import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.signal.libsignal.zkgroup.groups.GroupSecretParams; import org.signal.libsignal.zkgroup.groups.UuidCiphertext; +import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.signal.storageservice.protos.groups.AccessControl; import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.protos.groups.Member; @@ -27,10 +29,12 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; import org.signal.storageservice.protos.groups.local.DecryptedMember; import org.signal.storageservice.protos.groups.local.DecryptedPendingMember; +import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil; import org.whispersystems.signalservice.api.groupsv2.GroupCandidate; +import org.whispersystems.signalservice.api.groupsv2.GroupHistoryPage; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; @@ -40,6 +44,7 @@ import org.whispersystems.signalservice.api.push.ACI; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.File; import java.io.FileInputStream; @@ -53,7 +58,9 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; class GroupV2Helper { @@ -96,6 +103,35 @@ class GroupV2Helper { getGroupAuthForToday(groupSecretParams)); } + GroupHistoryPage getDecryptedGroupHistoryPage( + final GroupSecretParams groupSecretParams, int fromRevision + ) throws NotAGroupMemberException { + try { + final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams); + return dependencies.getGroupsV2Api() + .getGroupHistoryPage(groupSecretParams, fromRevision, groupsV2AuthorizationString, false); + } catch (NonSuccessfulResponseCodeException e) { + if (e.getCode() == 403) { + throw new NotAGroupMemberException(GroupUtils.getGroupIdV2(groupSecretParams), null); + } + logger.warn("Failed to retrieve Group V2 history, ignoring: {}", e.getMessage()); + return null; + } catch (IOException | VerificationFailedException | InvalidGroupStateException e) { + logger.warn("Failed to retrieve Group V2 history, ignoring: {}", e.getMessage()); + return null; + } + } + + int findRevisionWeWereAdded(DecryptedGroup partialDecryptedGroup) { + ByteString bytes = UuidUtil.toByteString(getSelfAci().uuid()); + for (DecryptedMember decryptedMember : partialDecryptedGroup.getMembersList()) { + if (decryptedMember.getUuid().equals(bytes)) { + return decryptedMember.getJoinedAtRevision(); + } + } + return partialDecryptedGroup.getRevision(); + } + Pair createGroup( String name, Set members, File avatarFile ) throws IOException { @@ -522,21 +558,43 @@ class GroupV2Helper { Optional.ofNullable(password).map(GroupLinkPassword::serialize)); } - DecryptedGroup getUpdatedDecryptedGroup( - DecryptedGroup group, byte[] signedGroupChange, GroupMasterKey groupMasterKey - ) { + Pair getAuthoritativeProfileKeyFromChange(final DecryptedGroupChange change) { + UUID editor = UuidUtil.fromByteStringOrNull(change.getEditor()); + final var editorProfileKeyBytes = Stream.concat(Stream.of(change.getNewMembersList().stream(), + change.getPromotePendingMembersList().stream(), + change.getModifiedProfileKeysList().stream()) + .flatMap(Function.identity()) + .filter(m -> UuidUtil.fromByteString(m.getUuid()).equals(editor)) + .map(DecryptedMember::getProfileKey), + change.getNewRequestingMembersList() + .stream() + .filter(m -> UuidUtil.fromByteString(m.getUuid()).equals(editor)) + .map(DecryptedRequestingMember::getProfileKey)).findFirst(); + + if (editorProfileKeyBytes.isEmpty()) { + return null; + } + + ProfileKey profileKey; + try { + profileKey = new ProfileKey(editorProfileKeyBytes.get().toByteArray()); + } catch (InvalidInputException e) { + logger.debug("Bad profile key in group"); + return null; + } + + return new Pair<>(ServiceId.from(editor), profileKey); + } + + DecryptedGroup getUpdatedDecryptedGroup(DecryptedGroup group, DecryptedGroupChange decryptedGroupChange) { try { - final var decryptedGroupChange = getDecryptedGroupChange(signedGroupChange, groupMasterKey); - if (decryptedGroupChange == null) { - return null; - } return DecryptedGroupUtil.apply(group, decryptedGroupChange); } catch (NotAbleToApplyGroupV2ChangeException e) { return null; } } - private DecryptedGroupChange getDecryptedGroupChange(byte[] signedGroupChange, GroupMasterKey groupMasterKey) { + DecryptedGroupChange getDecryptedGroupChange(byte[] signedGroupChange, GroupMasterKey groupMasterKey) { if (signedGroupChange != null) { var groupOperations = dependencies.getGroupsV2Operations() .forGroup(GroupSecretParams.deriveFromMasterKey(groupMasterKey)); From 470aeadbd93e4607312037f555e6004cb7d76402 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 19 May 2022 13:06:57 +0200 Subject: [PATCH 105/938] Bump version --- CHANGELOG.md | 17 +++++++++++++++++ build.gradle.kts | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de705aed..f88e83f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ ## [Unreleased] +## [0.10.6] - 2022-05-19 +**Attention**: Now requires native libsignal-client version 0.17 + +### Added +- Check if account is used on the environment it was registered (live or staging) +- New command `deleteLocalAccountData` to delete all local data of an unregistered account +- New parameter `-g` for `listGroups` command to filter for specific groups + +### Fixed +- Fix deleting a recipient which has no uuid + +### Changed +- Show warning when sending a message and no profile name has been set. + (A profile name may become mandatory in the future) +- After blocking a contact/group the profile key is now rotated +- Only update profile keys from authoritative group changes + ## [0.10.5] - 2022-04-11 **Attention**: Now requires native libsignal-client version 0.15 diff --git a/build.gradle.kts b/build.gradle.kts index 926ae182..22cc27f7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.11" } -version = "0.10.5" +version = "0.10.6" java { sourceCompatibility = JavaVersion.VERSION_17 From 496cd5e6219b54e01d0a3b175cb77457001ce7dc Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 19 May 2022 13:48:34 +0200 Subject: [PATCH 106/938] Fix repackage if building with multiple java versions --- .github/workflows/repackage-native-libs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/repackage-native-libs.yml b/.github/workflows/repackage-native-libs.yml index 9a87f723..fc5fa780 100644 --- a/.github/workflows/repackage-native-libs.yml +++ b/.github/workflows/repackage-native-libs.yml @@ -31,7 +31,7 @@ jobs: run: | #echo ${GITHUB_REF#refs/tag/} tree . - mv ./*/*.tar.gz . + mv ./$(ls */ -d | tail -n1)/*.tar.gz . ver=$(ls ./*.tar.gz | xargs basename | sed -E 's/signal-cli-(.*).tar.gz/\1/') echo $ver echo "::set-output name=signal_cli_version::${ver}" From 5f941004f5006b286690df2a93bc47ace3b59270 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 20 May 2022 11:46:03 +0200 Subject: [PATCH 107/938] Extend listContacts command with profiles and filtering --- graalvm-config-dir/reflect-config.json | 25 ++++++ graalvm-config-dir/resource-config.json | 3 + .../org/asamk/signal/manager/Manager.java | 10 ++- .../org/asamk/signal/manager/ManagerImpl.java | 53 +++++++------ .../signal/manager/helper/ProfileHelper.java | 16 +++- .../manager/helper/RecipientHelper.java | 4 +- .../signal/manager/helper/SendHelper.java | 2 +- .../storage/recipients/RecipientStore.java | 18 +++++ .../signal/commands/ListContactsCommand.java | 79 ++++++++++++++++--- .../asamk/signal/dbus/DbusManagerImpl.java | 31 ++++++-- .../org/asamk/signal/dbus/DbusSignalImpl.java | 38 ++------- 11 files changed, 196 insertions(+), 83 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index f9fe367f..69b19703 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -527,6 +527,20 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.commands.ListContactsCommand$JsonContact$JsonProfile", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"about","parameterTypes":[] }, + {"name":"aboutEmoji","parameterTypes":[] }, + {"name":"familyName","parameterTypes":[] }, + {"name":"givenName","parameterTypes":[] }, + {"name":"lastUpdateTimestamp","parameterTypes":[] }, + {"name":"paymentAddress","parameterTypes":[] } + ] +}, { "name":"org.asamk.signal.commands.ListDevicesCommand$JsonDevice", "allDeclaredFields":true, @@ -1797,6 +1811,17 @@ {"name":"userId_"} ] }, +{ + "name":"org.signal.storageservice.protos.groups.GroupChanges", + "fields":[{"name":"groupChanges_"}] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChanges$GroupChangeState", + "fields":[ + {"name":"groupChange_"}, + {"name":"groupState_"} + ] +}, { "name":"org.signal.storageservice.protos.groups.GroupInviteLink", "fields":[ diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index 08a9dcc5..542c8f31 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -73,6 +73,9 @@ { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_DE\\E" }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_DK\\E" + }, { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_EC\\E" }, 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 8f49126f..cc3ce0ce 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -28,9 +28,8 @@ import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; -import org.asamk.signal.manager.storage.recipients.RecipientAddress; +import org.asamk.signal.manager.storage.recipients.Recipient; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.Closeable; @@ -215,7 +214,12 @@ public interface Manager extends Closeable { void sendContacts() throws IOException; - List> getContacts(); + List getRecipients( + boolean onlyContacts, + Optional blocked, + Collection address, + Optional name + ); String getContactOrProfileName(RecipientIdentifier.Single recipient); diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index b3e6a0ae..f2d080fd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -51,9 +51,8 @@ import org.asamk.signal.manager.helper.Context; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.identities.IdentityInfo; -import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; -import org.asamk.signal.manager.storage.recipients.RecipientAddress; +import org.asamk.signal.manager.storage.recipients.Recipient; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack; import org.asamk.signal.manager.storage.stickerPacks.StickerPackStore; @@ -84,6 +83,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -101,7 +101,6 @@ class ManagerImpl implements Manager { private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class); private SignalAccount account; - private AccountFileUpdater accountFileUpdater; private final SignalDependencies dependencies; private final Context context; @@ -123,7 +122,6 @@ class ManagerImpl implements Manager { String userAgent ) { this.account = account; - this.accountFileUpdater = accountFileUpdater; final var sessionLock = new SignalSessionLock() { private final ReentrantLock LEGACY_LOCK = new ReentrantLock(); @@ -337,7 +335,7 @@ class ManagerImpl implements Manager { } @Override - public Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException { + public Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws UnregisteredRecipientException { return context.getProfileHelper().getRecipientProfile(context.getRecipientHelper().resolveRecipient(recipient)); } @@ -495,7 +493,7 @@ class ManagerImpl implements Manager { @Override public SendMessageResults sendReadReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException { + ) { final var timestamp = System.currentTimeMillis(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, @@ -507,7 +505,7 @@ class ManagerImpl implements Manager { @Override public SendMessageResults sendViewedReceipt( RecipientIdentifier.Single sender, List messageIds - ) throws IOException { + ) { final var timestamp = System.currentTimeMillis(); var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.VIEWED, messageIds, @@ -520,7 +518,7 @@ class ManagerImpl implements Manager { final RecipientIdentifier.Single sender, final long timestamp, final SignalServiceReceiptMessage receiptMessage - ) throws IOException { + ) { try { final var result = context.getSendHelper() .sendReceiptMessage(receiptMessage, context.getRecipientHelper().resolveRecipient(sender)); @@ -592,7 +590,7 @@ class ManagerImpl implements Manager { } } - private ArrayList resolveMentions(final List mentionList) throws IOException, UnregisteredRecipientException { + private ArrayList resolveMentions(final List mentionList) throws UnregisteredRecipientException { final var mentions = new ArrayList(); for (final var m : mentionList) { final var recipientId = context.getRecipientHelper().resolveRecipient(m.recipient()); @@ -676,7 +674,7 @@ class ManagerImpl implements Manager { @Override public void setContactName( RecipientIdentifier.Single recipient, String name - ) throws NotMasterDeviceException, IOException, UnregisteredRecipientException { + ) throws NotMasterDeviceException, UnregisteredRecipientException { if (!account.isMasterDevice()) { throw new NotMasterDeviceException(); } @@ -932,7 +930,7 @@ class ManagerImpl implements Manager { final RecipientId recipientId; try { recipientId = context.getRecipientHelper().resolveRecipient(recipient); - } catch (IOException | UnregisteredRecipientException e) { + } catch (UnregisteredRecipientException e) { return false; } return context.getContactHelper().isContactBlocked(recipientId); @@ -944,12 +942,22 @@ class ManagerImpl implements Manager { } @Override - public List> getContacts() { - return account.getContactStore() - .getContacts() - .stream() - .map(p -> new Pair<>(account.getRecipientStore().resolveRecipientAddress(p.first()), p.second())) - .toList(); + public List getRecipients( + boolean onlyContacts, + Optional blocked, + Collection recipients, + Optional name + ) { + final var recipientIds = recipients.stream().map(a -> { + try { + return context.getRecipientHelper().resolveRecipient(a); + } catch (UnregisteredRecipientException e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toSet()); + // refresh profiles of explicitly given recipients + context.getProfileHelper().refreshRecipientProfiles(recipientIds); + return account.getRecipientStore().getRecipients(onlyContacts, blocked, recipientIds, name); } @Override @@ -957,7 +965,7 @@ class ManagerImpl implements Manager { final RecipientId recipientId; try { recipientId = context.getRecipientHelper().resolveRecipient(recipient); - } catch (IOException | UnregisteredRecipientException e) { + } catch (UnregisteredRecipientException e) { return null; } @@ -1007,7 +1015,7 @@ class ManagerImpl implements Manager { try { identity = account.getIdentityKeyStore() .getIdentity(context.getRecipientHelper().resolveRecipient(recipient)); - } catch (IOException | UnregisteredRecipientException e) { + } catch (UnregisteredRecipientException e) { identity = null; } return identity == null ? List.of() : List.of(toIdentity(identity)); @@ -1044,12 +1052,7 @@ class ManagerImpl implements Manager { private boolean trustIdentity( RecipientIdentifier.Single recipient, Function trustMethod ) throws UnregisteredRecipientException { - RecipientId recipientId; - try { - recipientId = context.getRecipientHelper().resolveRecipient(recipient); - } catch (IOException e) { - return false; - } + final var recipientId = context.getRecipientHelper().resolveRecipient(recipient); final var updated = trustMethod.apply(recipientId); if (updated && this.isReceiving()) { context.getReceiveHelper().setNeedsToRetryFailedMessages(true); 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 4bc00317..8a64c8b7 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 @@ -36,6 +36,7 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.util.Base64; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Locale; @@ -94,10 +95,18 @@ public final class ProfileHelper { return getRecipientProfile(recipientId, false); } + public List getRecipientProfiles(Collection recipientIds) { + return getRecipientProfiles(recipientIds, false); + } + public void refreshRecipientProfile(RecipientId recipientId) { getRecipientProfile(recipientId, true); } + public void refreshRecipientProfiles(Collection recipientIds) { + getRecipientProfiles(recipientIds, true); + } + public List getRecipientProfileKeyCredential(List recipientIds) { try { account.getRecipientStore().setBulkUpdating(true); @@ -216,11 +225,12 @@ public final class ProfileHelper { return getRecipientProfile(account.getSelfRecipientId()); } - public List getRecipientProfile(List recipientIds) { + private List getRecipientProfiles(Collection recipientIds, boolean force) { + final var profileStore = account.getProfileStore(); try { account.getRecipientStore().setBulkUpdating(true); final var profileFetches = Flowable.fromIterable(recipientIds) - .filter(recipientId -> isProfileRefreshRequired(account.getProfileStore().getProfile(recipientId))) + .filter(recipientId -> force || isProfileRefreshRequired(profileStore.getProfile(recipientId))) .map(recipientId -> retrieveProfile(recipientId, SignalServiceProfile.RequestType.PROFILE).onErrorComplete()); Maybe.merge(profileFetches, 10).blockingSubscribe(); @@ -228,7 +238,7 @@ public final class ProfileHelper { account.getRecipientStore().setBulkUpdating(false); } - return recipientIds.stream().map(r -> account.getProfileStore().getProfile(r)).toList(); + return recipientIds.stream().map(profileStore::getProfile).toList(); } private Profile getRecipientProfile(RecipientId recipientId, boolean force) { 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 15508cd4..c253d602 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 @@ -69,7 +69,7 @@ public class RecipientHelper { return account.getRecipientStore().resolveRecipient(address); } - public Set resolveRecipients(Collection recipients) throws IOException, UnregisteredRecipientException { + public Set resolveRecipients(Collection recipients) throws UnregisteredRecipientException { final var recipientIds = new HashSet(recipients.size()); for (var number : recipients) { final var recipientId = resolveRecipient(number); @@ -78,7 +78,7 @@ public class RecipientHelper { return recipientIds; } - public RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException { + public RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws UnregisteredRecipientException { if (recipient instanceof RecipientIdentifier.Uuid uuidRecipient) { return account.getRecipientStore().resolveRecipient(ServiceId.from(uuidRecipient.uuid())); } else { 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 a2e2379b..8b2a054e 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 @@ -475,7 +475,7 @@ public class SendHelper { final var senderKeyTargets = new HashSet(); final var recipientList = new ArrayList<>(recipientIds); - final var profiles = context.getProfileHelper().getRecipientProfile(recipientList).iterator(); + final var profiles = context.getProfileHelper().getRecipientProfiles(recipientList).iterator(); for (final var recipientId : recipientList) { final var profile = profiles.next(); if (profile == null || !profile.getCapabilities().contains(Profile.Capability.senderKey)) { 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 299a3980..850b6270 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 @@ -277,6 +277,24 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile .toList(); } + public List getRecipients( + boolean onlyContacts, Optional blocked, Set recipientIds, Optional name + ) { + return recipients.values() + .stream() + .filter(r -> !onlyContacts || r.getContact() != null) + .filter(r -> blocked.isEmpty() || ( + blocked.get() == ( + r.getContact() != null && r.getContact().isBlocked() + ) + )) + .filter(r -> recipientIds.isEmpty() || (recipientIds.contains(r.getRecipientId()))) + .filter(r -> name.isEmpty() + || (r.getContact() != null && name.get().equals(r.getContact().getName())) + || (r.getProfile() != null && name.get().equals(r.getProfile().getDisplayName()))) + .toList(); + } + @Override public void deleteContact(RecipientId recipientId) { synchronized (recipients) { diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index b83440ec..0bfc7ab9 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -1,13 +1,20 @@ package org.asamk.signal.commands; +import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.storage.recipients.Contact; +import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.output.JsonWriter; import org.asamk.signal.output.OutputWriter; import org.asamk.signal.output.PlainTextWriter; +import org.asamk.signal.util.CommandUtil; +import java.util.Base64; +import java.util.Optional; import java.util.UUID; public class ListContactsCommand implements JsonRpcLocalCommand { @@ -19,19 +26,39 @@ public class ListContactsCommand implements JsonRpcLocalCommand { @Override public void attachToSubparser(final Subparser subparser) { - subparser.help("Show a list of known contacts with names."); + subparser.help("Show a list of known contacts with names and profiles."); + subparser.addArgument("recipient").help("Specify one ore more phone numbers to show.").nargs("*"); + subparser.addArgument("-a", "--all-recipients") + .action(Arguments.storeTrue()) + .help("Include all known recipients, not only contacts."); + subparser.addArgument("--blocked") + .type(Boolean.class) + .help("Specify if only blocked or unblocked contacts should be shown (default: all contacts)"); + subparser.addArgument("--name").help("Find contacts with the given contact or profile name."); } @Override - public void handleCommand(final Namespace ns, final Manager m, final OutputWriter outputWriter) { - var contacts = m.getContacts(); + public void handleCommand( + 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"); + final var recipientStrings = ns.getList("recipient"); + final var recipientIdentifiers = CommandUtil.getSingleRecipientIdentifiers(recipientStrings, m.getSelfNumber()); + final var name = ns.getString("name"); + final var recipients = m.getRecipients(!allRecipients, + Optional.ofNullable(blocked), + recipientIdentifiers, + Optional.ofNullable(name)); if (outputWriter instanceof PlainTextWriter writer) { - for (var c : contacts) { - final var contact = c.second(); - writer.println("Number: {} Name: {} Blocked: {} Message expiration: {}", - c.first().getLegacyIdentifier(), + for (var r : recipients) { + final var contact = r.getContact() == null ? Contact.newBuilder().build() : r.getContact(); + final var profile = r.getProfile() == null ? Profile.newBuilder().build() : r.getProfile(); + writer.println("Number: {} Name: {} Profile name: {} Blocked: {} Message expiration: {}", + r.getAddress().getLegacyIdentifier(), contact.getName(), + profile.getDisplayName(), contact.isBlocked(), contact.getMessageExpirationTime() == 0 ? "disabled" @@ -39,19 +66,47 @@ public class ListContactsCommand implements JsonRpcLocalCommand { } } else { final var writer = (JsonWriter) outputWriter; - final var jsonContacts = contacts.stream().map(contactPair -> { - final var address = contactPair.first(); - final var contact = contactPair.second(); + final var jsonContacts = recipients.stream().map(r -> { + final var address = r.getAddress(); + final var contact = r.getContact() == null ? Contact.newBuilder().build() : r.getContact(); return new JsonContact(address.number().orElse(null), address.uuid().map(UUID::toString).orElse(null), contact.getName(), contact.isBlocked(), - contact.getMessageExpirationTime()); + contact.getMessageExpirationTime(), + r.getProfile() == null + ? null + : new JsonContact.JsonProfile(r.getProfile().getLastUpdateTimestamp(), + r.getProfile().getGivenName(), + r.getProfile().getFamilyName(), + r.getProfile().getAbout(), + r.getProfile().getAboutEmoji(), + r.getProfile().getPaymentAddress() == null + ? null + : Base64.getEncoder() + .encodeToString(r.getProfile().getPaymentAddress()))); }).toList(); writer.write(jsonContacts); } } - private record JsonContact(String number, String uuid, String name, boolean isBlocked, int messageExpirationTime) {} + private record JsonContact( + String number, + String uuid, + String name, + boolean isBlocked, + int messageExpirationTime, + JsonProfile profile + ) { + + private record JsonProfile( + long lastUpdateTimestamp, + String givenName, + String familyName, + String about, + String aboutEmoji, + String paymentAddress + ) {} + } } diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 5b33b039..11800be2 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -32,6 +32,7 @@ import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Profile; +import org.asamk.signal.manager.storage.recipients.Recipient; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.freedesktop.dbus.DBusMap; import org.freedesktop.dbus.DBusPath; @@ -547,14 +548,32 @@ public class DbusManagerImpl implements Manager { } @Override - public List> getContacts() { - return signal.listNumbers().stream().map(n -> { - final var contactName = signal.getContactName(n); - if (contactName.length() == 0) { + public List getRecipients( + final boolean onlyContacts, + final Optional blocked, + final Collection addresses, + final Optional name + ) { + final var numbers = addresses.stream() + .filter(s -> s instanceof RecipientIdentifier.Number) + .map(s -> ((RecipientIdentifier.Number) s).number()) + .collect(Collectors.toSet()); + return signal.listNumbers().stream().filter(n -> addresses.isEmpty() || numbers.contains(n)).map(n -> { + final var contactBlocked = signal.isContactBlocked(n); + if (blocked.isPresent() && blocked.get() != contactBlocked) { return null; } - return new Pair<>(new RecipientAddress(null, n), - new Contact(contactName, null, 0, signal.isContactBlocked(n), false, false)); + final var contactName = signal.getContactName(n); + if (onlyContacts && contactName.length() == 0) { + return null; + } + if (name.isPresent() && !name.get().equals(contactName)) { + return null; + } + return Recipient.newBuilder() + .withAddress(new RecipientAddress(null, n)) + .withContact(new Contact(contactName, null, 0, contactBlocked, false, false)) + .build(); }).filter(Objects::nonNull).toList(); } diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index b4485e32..f4a9cda8 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -4,14 +4,12 @@ import org.asamk.Signal; import org.asamk.signal.BaseConfig; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.api.AttachmentInvalidException; -import org.asamk.signal.manager.api.Identity; import org.asamk.signal.manager.api.InactiveGroupLinkException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.InvalidNumberException; import org.asamk.signal.manager.api.InvalidStickerException; import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.NotMasterDeviceException; -import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendMessageResult; import org.asamk.signal.manager.api.SendMessageResults; @@ -28,7 +26,6 @@ import org.asamk.signal.manager.groups.GroupPermission; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.util.SendMessageResultUtils; import org.freedesktop.dbus.DBusPath; @@ -55,7 +52,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; public class DbusSignalImpl implements Signal { @@ -719,9 +715,9 @@ public class DbusSignalImpl implements Signal { // all numbers the system knows @Override public List listNumbers() { - return Stream.concat(m.getIdentities().stream().map(Identity::recipient), - m.getContacts().stream().map(Pair::first)) - .map(a -> a.number().orElse(null)) + return m.getRecipients(false, Optional.empty(), Set.of(), Optional.empty()) + .stream() + .map(r -> r.getAddress().number().orElse(null)) .filter(Objects::nonNull) .distinct() .toList(); @@ -729,30 +725,10 @@ public class DbusSignalImpl implements Signal { @Override public List getContactNumber(final String name) { - // Contact names have precedence. - var numbers = new ArrayList(); - var contacts = m.getContacts(); - for (var c : contacts) { - if (name.equals(c.second().getName())) { - numbers.add(c.first().getLegacyIdentifier()); - } - } - // Try profiles if no contact name was found - for (var identity : m.getIdentities()) { - final var address = identity.recipient(); - var number = address.number().orElse(null); - if (number != null) { - Profile profile = null; - try { - profile = m.getRecipientProfile(RecipientIdentifier.Single.fromAddress(address)); - } catch (IOException | UnregisteredRecipientException ignored) { - } - if (profile != null && profile.getDisplayName().equals(name)) { - numbers.add(number); - } - } - } - return numbers; + return m.getRecipients(false, Optional.empty(), Set.of(), Optional.of(name)) + .stream() + .map(r -> r.getAddress().getLegacyIdentifier()) + .toList(); } @Override From 2ecddba37509c1328980a2d59d1e96ca2cabab53 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 20 May 2022 11:57:18 +0200 Subject: [PATCH 108/938] Update json-rpc client --- client/Cargo.lock | 220 +++++++++++++++++++++++++----------------- client/src/cli.rs | 22 ++++- client/src/jsonrpc.rs | 22 ++++- client/src/main.rs | 21 +++- 4 files changed, 189 insertions(+), 96 deletions(-) diff --git a/client/Cargo.lock b/client/Cargo.lock index 77afe1dd..855d74bc 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.54" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a99269dff3bc004caa411f38845c20303f1e393ca2bd6581576fa3a7f59577d" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" [[package]] name = "atty" @@ -63,16 +63,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.1.0" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f1fea81f183005ced9e59cdb01737ef2423956dac5a6d731b06b2ecfaa3467" +checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", "bitflags", "clap_derive", + "clap_lex", "indexmap", "lazy_static", - "os_str_bytes", "strsim", "termcolor", "textwrap", @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.0" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd1122e63869df2cb309f449da1ad54a7c6dfeb7c7e6ccd8e0825d9eb93bb72" +checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" dependencies = [ "heck", "proc-macro-error", @@ -91,6 +91,15 @@ dependencies = [ "syn", ] +[[package]] +name = "clap_lex" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -221,7 +230,7 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -260,9 +269,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg", "hashbrown", @@ -279,9 +288,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "jsonrpc-client-transports" @@ -407,63 +416,45 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.119" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mio" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" +checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" dependencies = [ "libc", "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "ntapi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" -dependencies = [ - "winapi", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", ] [[package]] @@ -477,13 +468,16 @@ dependencies = [ ] [[package]] -name = "os_str_bytes" -version = "6.0.0" +name = "once_cell" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] +checksum = "7b10983b38c53aebdf33f542c6275b0f58a238129d00c4ae0e6fb59738d783ca" + +[[package]] +name = "os_str_bytes" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" [[package]] name = "parity-tokio-ipc" @@ -526,9 +520,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -577,18 +571,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] @@ -636,18 +630,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", @@ -671,9 +665,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] name = "scopeguard" @@ -683,24 +677,24 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" +checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" [[package]] name = "serde" -version = "1.0.136" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", @@ -709,9 +703,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ "itoa", "ryu", @@ -737,9 +731,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] name = "smallvec" @@ -765,41 +759,42 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "tokio" -version = "1.17.0" +version = "1.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" dependencies = [ "bytes", "libc", "memchr", "mio", "num_cpus", + "once_cell", "pin-project-lite", "socket2", "tokio-macros", @@ -830,9 +825,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ "bytes", "futures-core", @@ -844,9 +839,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] @@ -861,10 +856,10 @@ dependencies = [ ] [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "version_check" @@ -878,6 +873,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" @@ -908,3 +909,46 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/client/src/cli.rs b/client/src/cli.rs index a8cee165..01b59fed 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -49,6 +49,10 @@ pub enum CliCommands { #[clap(short = 'g', long)] group_id: Vec, }, + DeleteLocalAccountData { + #[clap(long = "ignore-registered")] + ignore_registered: Option, + }, GetUserStatus { recipient: Vec, }, @@ -61,11 +65,21 @@ pub enum CliCommands { name: String, }, ListAccounts, - ListContacts, + ListContacts { + recipient: Vec, + #[clap(short = 'a', long = "all-recipients")] + all_recipients: bool, + #[clap(long, parse(try_from_str))] + blocked: Option, + #[clap(long)] + name: Option, + }, ListDevices, ListGroups { #[clap(short = 'd', long)] detailed: bool, + #[clap(short = 'g', long = "group-id")] + group_id: Vec, }, ListIdentities { #[clap(short = 'n', long)] @@ -225,13 +239,13 @@ pub enum CliCommands { #[clap(long = "read-receipts", parse(try_from_str))] read_receipts: Option, - #[clap(long = "unidentified-delivery-indicators")] + #[clap(long = "unidentified-delivery-indicators", parse(try_from_str))] unidentified_delivery_indicators: Option, - #[clap(long = "typing-indicators")] + #[clap(long = "typing-indicators", parse(try_from_str))] typing_indicators: Option, - #[clap(long = "link-previews")] + #[clap(long = "link-previews", parse(try_from_str))] link_previews: Option, }, UpdateContact { diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index d4ed4084..f91be25e 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -20,6 +20,13 @@ pub trait Rpc { #[allow(non_snake_case)] groupIds: Vec, ) -> Result; + #[rpc(name = "deleteLocalAccountData", params = "named")] + fn delete_local_account_data( + &self, + account: Option, + #[allow(non_snake_case)] ignoreRegistered: Option, + ) -> Result; + #[rpc(name = "getUserStatus", params = "named")] fn get_user_status(&self, account: Option, recipients: Vec) -> Result; @@ -37,13 +44,24 @@ pub trait Rpc { fn list_accounts(&self) -> Result; #[rpc(name = "listContacts", params = "named")] - fn list_contacts(&self, account: Option) -> Result; + fn list_contacts( + &self, + account: Option, + recipients: Vec, + #[allow(non_snake_case)] allRecipients: bool, + blocked: Option, + name: Option, + ) -> Result; #[rpc(name = "listDevices", params = "named")] fn list_devices(&self, account: Option) -> Result; #[rpc(name = "listGroups", params = "named")] - fn list_groups(&self, account: Option) -> Result; + fn list_groups( + &self, + account: Option, + #[allow(non_snake_case)] groupIds: Vec, + ) -> Result; #[rpc(name = "listIdentities", params = "named")] fn list_identities(&self, account: Option, number: Option) -> Result; diff --git a/client/src/main.rs b/client/src/main.rs index 6266d224..622be806 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -41,6 +41,11 @@ async fn main() -> Result<(), anyhow::Error> { recipient, group_id, } => client.block(cli.account, recipient, group_id).await, + cli::CliCommands::DeleteLocalAccountData { ignore_registered } => { + client + .delete_local_account_data(cli.account, ignore_registered) + .await + } cli::CliCommands::GetUserStatus { recipient } => { client.get_user_status(cli.account, recipient).await } @@ -55,9 +60,21 @@ async fn main() -> Result<(), anyhow::Error> { client.finish_link(url, name).await } cli::CliCommands::ListAccounts => client.list_accounts().await, - cli::CliCommands::ListContacts => client.list_contacts(cli.account).await, + cli::CliCommands::ListContacts { + recipient, + all_recipients, + blocked, + name, + } => { + client + .list_contacts(cli.account, recipient, all_recipients, blocked, name) + .await + } cli::CliCommands::ListDevices => client.list_devices(cli.account).await, - cli::CliCommands::ListGroups { detailed: _ } => client.list_groups(cli.account).await, + cli::CliCommands::ListGroups { + detailed: _, + group_id, + } => client.list_groups(cli.account, group_id).await, cli::CliCommands::ListIdentities { number } => { client.list_identities(cli.account, number).await } From b18991b9fb3a68b8a3f4bd16a160ead90b4990a9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 21 May 2022 09:00:42 +0200 Subject: [PATCH 109/938] Update documentation --- graalvm-config-dir/resource-config.json | 9 +++++++++ man/signal-cli.1.adoc | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index 542c8f31..1de22f76 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -46,6 +46,9 @@ { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BM\\E" }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BO\\E" + }, { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BR\\E" }, @@ -67,6 +70,12 @@ { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CN\\E" }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CO\\E" + }, + { + "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CR\\E" + }, { "pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CZ\\E" }, diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 4fdd684a..7d1aa6f1 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -418,7 +418,20 @@ Filter the group list by one or more group IDs. === listContacts -Show a list of known contacts with names. +Show a list of known contacts with names and profiles. +When a specific recipient is given, its profile will be refreshed. + +RECIPIENT:: +Specify the recipients’ phone number. + +*-a*, *--all-recipients*:: +Include all known recipients, not only contacts. + +*--blocked*:: +Specify if only blocked or unblocked contacts should be shown (default: all contacts) + +*--name*:: +Find contacts with the given contact or profile name. === listIdentities From 7587a603872a337dd6be706854a0658ea131dbbe Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 19 May 2022 20:54:15 +0200 Subject: [PATCH 110/938] Implement sendPayment notification command --- .../org/asamk/signal/manager/Manager.java | 4 ++ .../org/asamk/signal/manager/ManagerImpl.java | 14 +++++ man/signal-cli.1.adoc | 13 +++++ .../org/asamk/signal/commands/Commands.java | 1 + .../SendPaymentNotificationCommand.java | 51 +++++++++++++++++++ .../asamk/signal/dbus/DbusManagerImpl.java | 7 +++ 6 files changed, 90 insertions(+) create mode 100644 src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.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 cc3ce0ce..e568816f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -141,6 +141,10 @@ public interface Manager extends Closeable { Set recipients ) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException; + SendMessageResults sendPaymentNotificationMessage( + byte[] receipt, String note, RecipientIdentifier.Single recipient + ) throws IOException; + SendMessageResults sendEndSessionMessage(Set recipients) throws IOException; void deleteRecipient(RecipientIdentifier.Single recipient); diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index f2d080fd..ed7ae86b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -638,6 +638,20 @@ class ManagerImpl implements Manager { return sendMessage(messageBuilder, recipients); } + @Override + public SendMessageResults sendPaymentNotificationMessage( + byte[] receipt, String note, RecipientIdentifier.Single recipient + ) throws IOException { + final var paymentNotification = new SignalServiceDataMessage.PaymentNotification(receipt, note); + final var payment = new SignalServiceDataMessage.Payment(paymentNotification); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withPayment(payment); + try { + return sendMessage(messageBuilder, Set.of(recipient)); + } catch (NotAGroupMemberException | GroupNotFoundException | GroupSendingNotAllowedException e) { + throw new AssertionError(e); + } + } + @Override public SendMessageResults sendEndSessionMessage(Set recipients) throws IOException { var messageBuilder = SignalServiceDataMessage.newBuilder().asEndSessionMessage(); diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 7d1aa6f1..1cebd044 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -256,6 +256,19 @@ Specify the mentions of the original message (same format as `--mention`). *-e*, *--end-session*:: Clear session state and send end session message. +=== sendPaymentNotification + +Send a payment notification. + +RECIPIENT:: +Specify the recipient’s phone number. + +*--receipt* RECEIPT:: +The base64 encoded receipt blob. + +*--note* NOTE:: +Specify a note for the payment notification. + === sendReaction Send reaction to a previously received or sent message. diff --git a/src/main/java/org/asamk/signal/commands/Commands.java b/src/main/java/org/asamk/signal/commands/Commands.java index 15ab1724..1c4251da 100644 --- a/src/main/java/org/asamk/signal/commands/Commands.java +++ b/src/main/java/org/asamk/signal/commands/Commands.java @@ -34,6 +34,7 @@ public class Commands { addCommand(new RemoteDeleteCommand()); addCommand(new SendCommand()); addCommand(new SendContactsCommand()); + addCommand(new SendPaymentNotificationCommand()); addCommand(new SendReactionCommand()); addCommand(new SendReceiptCommand()); addCommand(new SendSyncRequestCommand()); diff --git a/src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java b/src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java new file mode 100644 index 00000000..77181e3e --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/SendPaymentNotificationCommand.java @@ -0,0 +1,51 @@ +package org.asamk.signal.commands; + +import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; + +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.manager.Manager; +import org.asamk.signal.output.OutputWriter; +import org.asamk.signal.util.CommandUtil; + +import java.io.IOException; +import java.util.Base64; + +import static org.asamk.signal.util.SendMessageResultUtils.outputResult; + +public class SendPaymentNotificationCommand implements JsonRpcLocalCommand { + + @Override + public String getName() { + return "sendPaymentNotification"; + } + + @Override + public void attachToSubparser(final Subparser subparser) { + subparser.help("Send a payment notification."); + subparser.addArgument("recipient").help("Specify the recipient's phone number."); + subparser.addArgument("--receipt").required(true).help("The base64 encoded receipt blob."); + subparser.addArgument("--note").help("Specify a note for the payment notification."); + } + + @Override + public void handleCommand( + 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()); + + final var receiptString = ns.getString("receipt"); + final var receipt = Base64.getDecoder().decode(receiptString); + final var note = ns.getString("note"); + + try { + final var results = m.sendPaymentNotificationMessage(receipt, note, recipientIdentifier); + outputResult(outputWriter, results); + } catch (IOException e) { + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() + .getSimpleName() + ")", e); + } + } +} diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 11800be2..6bb93677 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -388,6 +388,13 @@ public class DbusManagerImpl implements Manager { groupId)); } + @Override + public SendMessageResults sendPaymentNotificationMessage( + final byte[] receipt, final String note, final RecipientIdentifier.Single recipient + ) throws IOException { + throw new UnsupportedOperationException(); + } + @Override public SendMessageResults sendEndSessionMessage(final Set recipients) throws IOException { signal.sendEndSessionMessage(recipients.stream().map(RecipientIdentifier.Single::getIdentifier).toList()); From 3666531f8bfe179bdaf5eaa20ffc2ed16e0fcf9b Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 21 May 2022 09:29:58 +0200 Subject: [PATCH 111/938] Refactor manager update profile method --- graalvm-config-dir/reflect-config.json | 8 ++ .../org/asamk/signal/manager/Manager.java | 12 +- .../org/asamk/signal/manager/ManagerImpl.java | 14 ++- .../manager/RegistrationManagerImpl.java | 3 +- .../signal/manager/api/UpdateProfile.java | 108 ++++++++++++++++++ .../signal/commands/UpdateProfileCommand.java | 15 ++- .../asamk/signal/dbus/DbusManagerImpl.java | 21 ++-- .../org/asamk/signal/dbus/DbusSignalImpl.java | 14 ++- 8 files changed, 160 insertions(+), 35 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 69b19703..f9deabc6 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2754,6 +2754,14 @@ {"name":"stickerId_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$StoryContext", + "fields":[ + {"name":"authorUuid_"}, + {"name":"bitField0_"}, + {"name":"sentTimestamp_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Envelope", "fields":[ 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 e568816f..4444e57a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -21,6 +21,7 @@ import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; @@ -67,15 +68,10 @@ public interface Manager extends Closeable { void updateConfiguration(Configuration configuration) throws IOException, NotMasterDeviceException; /** - * @param givenName if null, the previous givenName will be kept - * @param familyName if null, the previous familyName will be kept - * @param about if null, the previous about text will be kept - * @param aboutEmoji if null, the previous about emoji will be kept - * @param avatar if avatar is null the image from the local avatar store is used (if present), + * Update the user's profile. + * If a field is null, the previous value will be kept. */ - void setProfile( - String givenName, String familyName, String about, String aboutEmoji, Optional avatar - ) throws IOException; + void updateProfile(UpdateProfile updateProfile) throws IOException; void unregister() throws IOException; diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index ed7ae86b..3ca3c109 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -38,6 +38,7 @@ import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.groups.GroupId; @@ -261,10 +262,15 @@ class ManagerImpl implements Manager { } @Override - public void setProfile( - String givenName, final String familyName, String about, String aboutEmoji, Optional avatar - ) throws IOException { - context.getProfileHelper().setProfile(givenName, familyName, about, aboutEmoji, avatar); + public void updateProfile(UpdateProfile updateProfile) throws IOException { + context.getProfileHelper() + .setProfile(updateProfile.getGivenName(), + updateProfile.getFamilyName(), + updateProfile.getAbout(), + updateProfile.getAboutEmoji(), + updateProfile.isDeleteAvatar() + ? Optional.empty() + : updateProfile.getAvatar() == null ? null : Optional.of(updateProfile.getAvatar())); context.getSyncHelper().sendSyncFetchProfileMessage(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index 3529154a..509b8923 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -19,6 +19,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.PinLockedException; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.helper.AccountFileUpdater; @@ -139,7 +140,7 @@ class RegistrationManagerImpl implements RegistrationManager { } // Set an initial empty profile so user can be added to groups try { - m.setProfile(null, null, null, null, null); + m.updateProfile(UpdateProfile.newBuilder().build()); } catch (NoClassDefFoundError e) { logger.warn("Failed to set default profile: {}", e.getMessage()); } diff --git a/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java b/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java new file mode 100644 index 00000000..2b47f7d5 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java @@ -0,0 +1,108 @@ +package org.asamk.signal.manager.api; + +import java.io.File; + +public class UpdateProfile { + + private final String givenName; + private final String familyName; + private final String about; + private final String aboutEmoji; + private final File avatar; + private final boolean deleteAvatar; + + private UpdateProfile(final Builder builder) { + givenName = builder.givenName; + familyName = builder.familyName; + about = builder.about; + aboutEmoji = builder.aboutEmoji; + avatar = builder.avatar; + deleteAvatar = builder.deleteAvatar; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static Builder newBuilder(final UpdateProfile copy) { + Builder builder = new Builder(); + builder.givenName = copy.getGivenName(); + builder.familyName = copy.getFamilyName(); + builder.about = copy.getAbout(); + builder.aboutEmoji = copy.getAboutEmoji(); + builder.avatar = copy.getAvatar(); + builder.deleteAvatar = copy.isDeleteAvatar(); + return builder; + } + + public String getGivenName() { + return givenName; + } + + public String getFamilyName() { + return familyName; + } + + public String getAbout() { + return about; + } + + public String getAboutEmoji() { + return aboutEmoji; + } + + public File getAvatar() { + return avatar; + } + + public boolean isDeleteAvatar() { + return deleteAvatar; + } + + public static final class Builder { + + private String givenName; + private String familyName; + private String about; + private String aboutEmoji; + private File avatar; + private boolean deleteAvatar; + + private Builder() { + } + + public Builder withGivenName(final String val) { + givenName = val; + return this; + } + + public Builder withFamilyName(final String val) { + familyName = val; + return this; + } + + public Builder withAbout(final String val) { + about = val; + return this; + } + + public Builder withAboutEmoji(final String val) { + aboutEmoji = val; + return this; + } + + public Builder withAvatar(final File val) { + avatar = val; + return this; + } + + public Builder withDeleteAvatar(final boolean val) { + deleteAvatar = val; + return this; + } + + public UpdateProfile build() { + return new UpdateProfile(this); + } + } +} diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index e60edf23..1edb6df9 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -7,11 +7,11 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.output.OutputWriter; import java.io.File; import java.io.IOException; -import java.util.Optional; public class UpdateProfileCommand implements JsonRpcLocalCommand { @@ -44,12 +44,17 @@ public class UpdateProfileCommand implements JsonRpcLocalCommand { var avatarPath = ns.getString("avatar"); boolean removeAvatar = Boolean.TRUE.equals(ns.getBoolean("remove-avatar")); - Optional avatarFile = removeAvatar - ? Optional.empty() - : avatarPath == null ? null : Optional.of(new File(avatarPath)); + File avatarFile = removeAvatar || avatarPath == null ? null : new File(avatarPath); try { - m.setProfile(givenName, familyName, about, aboutEmoji, avatarFile); + m.updateProfile(UpdateProfile.newBuilder() + .withGivenName(givenName) + .withFamilyName(familyName) + .withAbout(about) + .withAboutEmoji(aboutEmoji) + .withAvatar(avatarFile) + .withDeleteAvatar(removeAvatar) + .build()); } catch (IOException e) { throw new IOErrorException("Update profile error: " + e.getMessage(), e); } diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 6bb93677..320a8533 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -22,6 +22,7 @@ import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.StickerPackUrl; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; @@ -138,19 +139,13 @@ public class DbusManagerImpl implements Manager { } @Override - public void setProfile( - final String givenName, - final String familyName, - final String about, - final String aboutEmoji, - final Optional avatar - ) throws IOException { - signal.updateProfile(emptyIfNull(givenName), - emptyIfNull(familyName), - emptyIfNull(about), - emptyIfNull(aboutEmoji), - avatar == null ? "" : avatar.map(File::getPath).orElse(""), - avatar != null && avatar.isEmpty()); + public void updateProfile(UpdateProfile updateProfile) throws IOException { + signal.updateProfile(emptyIfNull(updateProfile.getGivenName()), + emptyIfNull(updateProfile.getFamilyName()), + emptyIfNull(updateProfile.getAbout()), + emptyIfNull(updateProfile.getAboutEmoji()), + updateProfile.getAvatar() == null ? "" : updateProfile.getAvatar().getPath(), + updateProfile.isDeleteAvatar()); } @Override diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index f4a9cda8..ec7174b2 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -17,6 +17,7 @@ import org.asamk.signal.manager.api.StickerPackInvalidException; import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; +import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.api.UserStatus; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; @@ -662,10 +663,15 @@ public class DbusSignalImpl implements Signal { about = nullIfEmpty(about); aboutEmoji = nullIfEmpty(aboutEmoji); avatarPath = nullIfEmpty(avatarPath); - Optional avatarFile = removeAvatar - ? Optional.empty() - : avatarPath == null ? null : Optional.of(new File(avatarPath)); - m.setProfile(givenName, familyName, about, aboutEmoji, avatarFile); + File avatarFile = removeAvatar || avatarPath == null ? null : new File(avatarPath); + m.updateProfile(UpdateProfile.newBuilder() + .withGivenName(givenName) + .withFamilyName(familyName) + .withAbout(about) + .withAboutEmoji(aboutEmoji) + .withAvatar(avatarFile) + .withDeleteAvatar(removeAvatar) + .build()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); } From bf75d9b4e0385d8b6b86faef8860caa06368c447 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 21 May 2022 10:08:40 +0200 Subject: [PATCH 112/938] Decrypt and verify the profile payment address --- graalvm-config-dir/reflect-config.json | 17 +++++- .../signal/manager/helper/ProfileHelper.java | 14 ++--- .../manager/storage/recipients/Profile.java | 20 +++---- .../storage/recipients/RecipientStore.java | 10 ++-- .../signal/manager/util/PaymentUtils.java | 52 +++++++++++++++++++ .../signal/manager/util/ProfileUtils.java | 39 +++++++++++++- .../signal/commands/ListContactsCommand.java | 13 ++--- 7 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index f9deabc6..6d5df0d0 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -538,7 +538,7 @@ {"name":"familyName","parameterTypes":[] }, {"name":"givenName","parameterTypes":[] }, {"name":"lastUpdateTimestamp","parameterTypes":[] }, - {"name":"paymentAddress","parameterTypes":[] } + {"name":"mobileCoinAddress","parameterTypes":[] } ] }, { @@ -2846,6 +2846,21 @@ {"name":"padding_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$PaymentAddress", + "fields":[ + {"name":"addressCase_"}, + {"name":"address_"} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$PaymentAddress$MobileCoinAddress", + "fields":[ + {"name":"address_"}, + {"name":"bitField0_"}, + {"name":"signature_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Preview", "fields":[ 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 8a64c8b7..1a876d7e 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 @@ -1,7 +1,5 @@ package org.asamk.signal.manager.helper; -import com.google.protobuf.InvalidProtocolBufferException; - import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.groups.GroupNotFoundException; @@ -13,6 +11,7 @@ import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.KeyUtils; +import org.asamk.signal.manager.util.PaymentUtils; import org.asamk.signal.manager.util.ProfileUtils; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.protocol.IdentityKey; @@ -29,7 +28,6 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.services.ProfileService; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import java.io.File; import java.io.IOException; @@ -185,13 +183,9 @@ public final class ProfileHelper { final var avatarUploadParams = streamDetails != null ? AvatarUploadParams.forAvatar(streamDetails) : avatar == null ? AvatarUploadParams.unchanged(true) : AvatarUploadParams.unchanged(false); - final var paymentsAddress = Optional.ofNullable(newProfile.getPaymentAddress()).map(data -> { - try { - return SignalServiceProtos.PaymentAddress.parseFrom(data); - } catch (InvalidProtocolBufferException e) { - return null; - } - }); + final var paymentsAddress = Optional.ofNullable(newProfile.getMobileCoinAddress()) + .map(address -> PaymentUtils.signPaymentsAddress(address, + account.getAciIdentityKeyPair().getPrivateKey())); logger.debug("Uploading new profile"); final var avatarPath = dependencies.getAccountManager() .setVersionedProfile(account.getAci(), diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java index 3f14f645..909337b7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Profile.java @@ -20,7 +20,7 @@ public class Profile { private final String avatarUrlPath; - private final byte[] paymentAddress; + private final byte[] mobileCoinAddress; private final UnidentifiedAccessMode unidentifiedAccessMode; @@ -33,7 +33,7 @@ public class Profile { final String about, final String aboutEmoji, final String avatarUrlPath, - final byte[] paymentAddress, + final byte[] mobileCoinAddress, final UnidentifiedAccessMode unidentifiedAccessMode, final Set capabilities ) { @@ -43,7 +43,7 @@ public class Profile { this.about = about; this.aboutEmoji = aboutEmoji; this.avatarUrlPath = avatarUrlPath; - this.paymentAddress = paymentAddress; + this.mobileCoinAddress = mobileCoinAddress; this.unidentifiedAccessMode = unidentifiedAccessMode; this.capabilities = capabilities; } @@ -55,7 +55,7 @@ public class Profile { about = builder.about; aboutEmoji = builder.aboutEmoji; avatarUrlPath = builder.avatarUrlPath; - paymentAddress = builder.paymentAddress; + mobileCoinAddress = builder.mobileCoinAddress; unidentifiedAccessMode = builder.unidentifiedAccessMode; capabilities = builder.capabilities; } @@ -72,7 +72,7 @@ public class Profile { builder.about = copy.getAbout(); builder.aboutEmoji = copy.getAboutEmoji(); builder.avatarUrlPath = copy.getAvatarUrlPath(); - builder.paymentAddress = copy.getPaymentAddress(); + builder.mobileCoinAddress = copy.getMobileCoinAddress(); builder.unidentifiedAccessMode = copy.getUnidentifiedAccessMode(); builder.capabilities = copy.getCapabilities(); return builder; @@ -124,8 +124,8 @@ public class Profile { return avatarUrlPath; } - public byte[] getPaymentAddress() { - return paymentAddress; + public byte[] getMobileCoinAddress() { + return mobileCoinAddress; } public UnidentifiedAccessMode getUnidentifiedAccessMode() { @@ -200,7 +200,7 @@ public class Profile { private String about; private String aboutEmoji; private String avatarUrlPath; - private byte[] paymentAddress; + private byte[] mobileCoinAddress; private UnidentifiedAccessMode unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN; private Set capabilities = Collections.emptySet(); private long lastUpdateTimestamp = 0; @@ -252,8 +252,8 @@ public class Profile { return this; } - public Builder withPaymentAddress(final byte[] val) { - paymentAddress = val; + public Builder withMobileCoinAddress(final byte[] val) { + mobileCoinAddress = val; return this; } } 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 850b6270..0297f6d7 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 @@ -105,9 +105,9 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile r.profile.about, r.profile.aboutEmoji, r.profile.avatarUrlPath, - r.profile.paymentAddress == null + r.profile.mobileCoinAddress == null ? null - : Base64.getDecoder().decode(r.profile.paymentAddress), + : Base64.getDecoder().decode(r.profile.mobileCoinAddress), Profile.UnidentifiedAccessMode.valueOfOrUnknown(r.profile.unidentifiedAccessMode), r.profile.capabilities.stream() .map(Profile.Capability::valueOfOrNull) @@ -592,9 +592,9 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile recipientProfile.getAbout(), recipientProfile.getAboutEmoji(), recipientProfile.getAvatarUrlPath(), - recipientProfile.getPaymentAddress() == null + recipientProfile.getMobileCoinAddress() == null ? null - : base64.encodeToString(recipientProfile.getPaymentAddress()), + : base64.encodeToString(recipientProfile.getMobileCoinAddress()), recipientProfile.getUnidentifiedAccessMode().name(), recipientProfile.getCapabilities().stream().map(Enum::name).collect(Collectors.toSet())); return new Storage.Recipient(pair.getKey().id(), @@ -651,7 +651,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile String about, String aboutEmoji, String avatarUrlPath, - String paymentAddress, + String mobileCoinAddress, String unidentifiedAccessMode, Set capabilities ) {} 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 new file mode 100644 index 00000000..6c34d9c9 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/util/PaymentUtils.java @@ -0,0 +1,52 @@ +package org.asamk.signal.manager.util; + +import com.google.protobuf.ByteString; + +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.IdentityKeyPair; +import org.signal.libsignal.protocol.ecc.ECPrivateKey; +import org.signal.libsignal.protocol.ecc.ECPublicKey; +import org.whispersystems.signalservice.internal.push.SignalServiceProtos; + +public class PaymentUtils { + + private 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 SignalServiceProtos.PaymentAddress signPaymentsAddress( + byte[] publicAddressBytes, ECPrivateKey privateKey + ) { + byte[] signature = privateKey.calculateSignature(publicAddressBytes); + + return SignalServiceProtos.PaymentAddress.newBuilder() + .setMobileCoinAddress(SignalServiceProtos.PaymentAddress.MobileCoinAddress.newBuilder() + .setAddress(ByteString.copyFrom(publicAddressBytes)) + .setSignature(ByteString.copyFrom(signature))) + .build(); + } + + /** + * Verifies that the payments address is signed with the supplied {@link IdentityKey}. + *

+ * Returns the validated bytes if so, otherwise returns null. + */ + public static byte[] verifyPaymentsAddress( + SignalServiceProtos.PaymentAddress paymentAddress, ECPublicKey publicKey + ) { + if (!paymentAddress.hasMobileCoinAddress()) { + return null; + } + + byte[] bytes = paymentAddress.getMobileCoinAddress().getAddress().toByteArray(); + byte[] signature = paymentAddress.getMobileCoinAddress().getSignature().toByteArray(); + + if (signature.length != 64 || !publicKey.verifySignature(bytes, signature)) { + return null; + } + + return bytes; + } +} 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 53a9cdb6..e202f70a 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 @@ -1,14 +1,21 @@ package org.asamk.signal.manager.util; +import com.google.protobuf.InvalidProtocolBufferException; + import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.storage.recipients.Profile; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.InvalidKeyException; +import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; import org.whispersystems.signalservice.api.crypto.ProfileCipher; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; +import org.whispersystems.signalservice.internal.push.SignalServiceProtos; +import java.io.IOException; import java.util.Base64; import java.util.HashSet; @@ -20,6 +27,12 @@ public class ProfileUtils { final ProfileKey profileKey, final SignalServiceProfile encryptedProfile ) { var profileCipher = new ProfileCipher(profileKey); + IdentityKey identityKey = null; + try { + identityKey = new IdentityKey(Base64.getDecoder().decode(encryptedProfile.getIdentityKey()), 0); + } catch (InvalidKeyException ignored) { + } + try { var name = decrypt(encryptedProfile.getName(), profileCipher); var about = trimZeros(decrypt(encryptedProfile.getAbout(), profileCipher)); @@ -32,7 +45,11 @@ public class ProfileUtils { about, aboutEmoji, encryptedProfile.getAvatar(), - encryptedProfile.getPaymentAddress(), + identityKey == null || encryptedProfile.getPaymentAddress() == null + ? null + : decryptAndVerifyMobileCoinAddress(encryptedProfile.getPaymentAddress(), + profileCipher, + identityKey.getPublicKey()), getUnidentifiedAccessMode(encryptedProfile, profileCipher), getCapabilities(encryptedProfile)); } catch (InvalidCiphertextException e) { @@ -88,6 +105,26 @@ public class ProfileUtils { } } + private static byte[] decryptAndVerifyMobileCoinAddress( + final byte[] encryptedPaymentAddress, final ProfileCipher profileCipher, final ECPublicKey publicKey + ) throws InvalidCiphertextException { + byte[] decrypted; + try { + decrypted = profileCipher.decryptWithLength(encryptedPaymentAddress); + } catch (IOException e) { + return null; + } + + SignalServiceProtos.PaymentAddress paymentAddress; + try { + paymentAddress = SignalServiceProtos.PaymentAddress.parseFrom(decrypted); + } catch (InvalidProtocolBufferException e) { + return null; + } + + return PaymentUtils.verifyPaymentsAddress(paymentAddress, publicKey); + } + private static Pair splitName(String name) { if (name == null) { return new Pair<>(null, null); diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index 0bfc7ab9..8fc6f2d1 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -81,10 +81,10 @@ public class ListContactsCommand implements JsonRpcLocalCommand { r.getProfile().getFamilyName(), r.getProfile().getAbout(), r.getProfile().getAboutEmoji(), - r.getProfile().getPaymentAddress() == null + r.getProfile().getMobileCoinAddress() == null ? null : Base64.getEncoder() - .encodeToString(r.getProfile().getPaymentAddress()))); + .encodeToString(r.getProfile().getMobileCoinAddress()))); }).toList(); writer.write(jsonContacts); @@ -92,12 +92,7 @@ public class ListContactsCommand implements JsonRpcLocalCommand { } private record JsonContact( - String number, - String uuid, - String name, - boolean isBlocked, - int messageExpirationTime, - JsonProfile profile + String number, String uuid, String name, boolean isBlocked, int messageExpirationTime, JsonProfile profile ) { private record JsonProfile( @@ -106,7 +101,7 @@ public class ListContactsCommand implements JsonRpcLocalCommand { String familyName, String about, String aboutEmoji, - String paymentAddress + String mobileCoinAddress ) {} } } From 34c0968f5e4db6b01b9c5cfd0d9556632e0667ba Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 21 May 2022 10:42:56 +0200 Subject: [PATCH 113/938] Add mobile-coin-address to updateProfile command --- .../org/asamk/signal/manager/ManagerImpl.java | 3 ++- .../asamk/signal/manager/api/UpdateProfile.java | 13 +++++++++++++ .../signal/manager/helper/ProfileHelper.java | 17 +++++++++++++---- .../signal/manager/helper/StorageHelper.java | 1 + man/signal-cli.1.adoc | 3 +++ .../signal/commands/UpdateProfileCommand.java | 9 ++++++++- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 3ca3c109..96196f8f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -270,7 +270,8 @@ class ManagerImpl implements Manager { updateProfile.getAboutEmoji(), updateProfile.isDeleteAvatar() ? Optional.empty() - : updateProfile.getAvatar() == null ? null : Optional.of(updateProfile.getAvatar())); + : updateProfile.getAvatar() == null ? null : Optional.of(updateProfile.getAvatar()), + updateProfile.getMobileCoinAddress()); context.getSyncHelper().sendSyncFetchProfileMessage(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java b/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java index 2b47f7d5..d5de308e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/UpdateProfile.java @@ -10,6 +10,7 @@ public class UpdateProfile { private final String aboutEmoji; private final File avatar; private final boolean deleteAvatar; + private final byte[] mobileCoinAddress; private UpdateProfile(final Builder builder) { givenName = builder.givenName; @@ -18,6 +19,7 @@ public class UpdateProfile { aboutEmoji = builder.aboutEmoji; avatar = builder.avatar; deleteAvatar = builder.deleteAvatar; + mobileCoinAddress = builder.mobileCoinAddress; } public static Builder newBuilder() { @@ -32,6 +34,7 @@ public class UpdateProfile { builder.aboutEmoji = copy.getAboutEmoji(); builder.avatar = copy.getAvatar(); builder.deleteAvatar = copy.isDeleteAvatar(); + builder.mobileCoinAddress = copy.getMobileCoinAddress(); return builder; } @@ -59,6 +62,10 @@ public class UpdateProfile { return deleteAvatar; } + public byte[] getMobileCoinAddress() { + return mobileCoinAddress; + } + public static final class Builder { private String givenName; @@ -67,6 +74,7 @@ public class UpdateProfile { private String aboutEmoji; private File avatar; private boolean deleteAvatar; + private byte[] mobileCoinAddress; private Builder() { } @@ -101,6 +109,11 @@ public class UpdateProfile { return this; } + public Builder withMobileCoinAddress(final byte[] val) { + mobileCoinAddress = val; + return this; + } + public UpdateProfile build() { return new UpdateProfile(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 1a876d7e..cc01dc73 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 @@ -64,7 +64,7 @@ public final class ProfileHelper { var profileKey = KeyUtils.createProfileKey(); account.setProfileKey(profileKey); context.getAccountHelper().updateAccountAttributes(); - setProfile(true, true, null, null, null, null, null); + setProfile(true, true, null, null, null, null, null, null); // TODO update profile key in storage final var recipientIds = account.getRecipientStore().getRecipientIdsWithEnabledProfileSharing(); @@ -144,9 +144,14 @@ public final class ProfileHelper { * @param avatar if avatar is null the image from the local avatar store is used (if present), */ public void setProfile( - String givenName, final String familyName, String about, String aboutEmoji, Optional avatar + String givenName, + final String familyName, + String about, + String aboutEmoji, + Optional avatar, + byte[] mobileCoinAddress ) throws IOException { - setProfile(true, false, givenName, familyName, about, aboutEmoji, avatar); + setProfile(true, false, givenName, familyName, about, aboutEmoji, avatar, mobileCoinAddress); } public void setProfile( @@ -156,7 +161,8 @@ public final class ProfileHelper { final String familyName, String about, String aboutEmoji, - Optional avatar + Optional avatar, + byte[] mobileCoinAddress ) throws IOException { var profile = getSelfProfile(); var builder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile); @@ -172,6 +178,9 @@ public final class ProfileHelper { if (aboutEmoji != null) { builder.withAboutEmoji(aboutEmoji); } + if (mobileCoinAddress != null) { + builder.withMobileCoinAddress(mobileCoinAddress); + } var newProfile = builder.build(); if (uploadProfile) { 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 469ca02e..88fe84b9 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 @@ -234,6 +234,7 @@ public class StorageHelper { accountRecord.getFamilyName().orElse(null), null, null, + null, null); } diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 1cebd044..456c6f1a 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -493,6 +493,9 @@ Path to the new avatar image file. *--remove-avatar*:: Remove the avatar +*--mobile-coin-address*:: +New MobileCoin address (Base64 encoded public address) + === updateContact Update the info associated to a number on our contact list. diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index 1edb6df9..d8e87430 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -12,6 +12,7 @@ import org.asamk.signal.output.OutputWriter; import java.io.File; import java.io.IOException; +import java.util.Base64; public class UpdateProfileCommand implements JsonRpcLocalCommand { @@ -27,6 +28,7 @@ public class UpdateProfileCommand implements JsonRpcLocalCommand { subparser.addArgument("--family-name").help("New profile family name (optional)"); subparser.addArgument("--about").help("New profile about text"); subparser.addArgument("--about-emoji").help("New profile about emoji"); + subparser.addArgument("--mobile-coin-address").help("New MobileCoin address (Base64 encoded public address)"); final var avatarOptions = subparser.addMutuallyExclusiveGroup(); avatarOptions.addArgument("--avatar").help("Path to new profile avatar"); @@ -41,9 +43,13 @@ public class UpdateProfileCommand implements JsonRpcLocalCommand { var familyName = ns.getString("family-name"); var about = ns.getString("about"); var aboutEmoji = ns.getString("about-emoji"); + var mobileCoinAddressString = ns.getString("mobile-coin-address"); + var mobileCoinAddress = mobileCoinAddressString == null + ? null + : Base64.getDecoder().decode(mobileCoinAddressString); + var avatarPath = ns.getString("avatar"); boolean removeAvatar = Boolean.TRUE.equals(ns.getBoolean("remove-avatar")); - File avatarFile = removeAvatar || avatarPath == null ? null : new File(avatarPath); try { @@ -52,6 +58,7 @@ public class UpdateProfileCommand implements JsonRpcLocalCommand { .withFamilyName(familyName) .withAbout(about) .withAboutEmoji(aboutEmoji) + .withMobileCoinAddress(mobileCoinAddress) .withAvatar(avatarFile) .withDeleteAvatar(removeAvatar) .build()); From 5a63b5419fc109c8eede318dba096571161165a4 Mon Sep 17 00:00:00 2001 From: Sebastian Scheibner Date: Sat, 21 May 2022 11:47:46 +0200 Subject: [PATCH 114/938] Update README.md --- README.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e6f74c46..570451c2 100644 --- a/README.md +++ b/README.md @@ -6,26 +6,23 @@ verifying, sending and receiving messages. To be able to link to an existing Sig signal-cli uses a [patched libsignal-service-java](https://github.com/AsamK/libsignal-service-java), because libsignal-service-java does not yet support [provisioning as a linked device](https://github.com/WhisperSystems/libsignal-service-java/pull/21). For -registering you need a phone number where you can receive SMS or incoming calls. signal-cli is primarily intended to be -used on servers to notify admins of important events. For this use-case, it has a dbus -interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)), that can be used to -send messages from any programming language that has dbus bindings. It also has a JSON-RPC based interface, see -the [documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service) for more information. +registering you need a phone number where you can receive SMS or incoming calls. + +signal-cli is primarily intended to be used on servers to notify admins of important events. For this use-case, it has a daemon mode with D-BUS +interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)) and JSON-PRC interface ([documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service)). For the JSON-RPC interface there's also a simple [example client](https://github.com/AsamK/signal-cli/tree/master/client), written in Rust. ## Installation -You can [build signal-cli](#building) yourself, or use +You can [build signal-cli](#building) yourself or use the [provided binary files](https://github.com/AsamK/signal-cli/releases/latest), which should work on Linux, macOS and -Windows. For Arch Linux there is also a [package in AUR](https://aur.archlinux.org/packages/signal-cli/), as well as -a [FreeBSD port](https://www.freshports.org/net-im/signal-cli) and -an [Alpine aport](https://pkgs.alpinelinux.org/packages?name=signal-cli). +Windows. There's also a [docker image and some Linux packages](https://github.com/AsamK/signal-cli/wiki/Binary-distributions) provided by the community. System requirements: - at least Java Runtime Environment (JRE) 17 - native library: libsignal-client - The native libs are bundled for x86_64 Linux (with recent enough glibc, see #643), Windows and MacOS. For other + The native libs are bundled for x86_64 Linux (with recent enough glibc), Windows and MacOS. For other systems/architectures see: [Provide native lib for libsignal](https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal) From cf07512d2403cd428959f96840ee923a606d78d9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 21 May 2022 12:10:51 +0200 Subject: [PATCH 115/938] Update json-rpc client --- client/Cargo.lock | 8 ++++---- client/src/cli.rs | 12 ++++++++++++ client/src/jsonrpc.rs | 10 ++++++++++ client/src/main.rs | 11 +++++++++++ graalvm-config-dir/reflect-config.json | 24 ++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/client/Cargo.lock b/client/Cargo.lock index 855d74bc..607cf0d5 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -639,9 +639,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", @@ -650,9 +650,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "rustc_version" diff --git a/client/src/cli.rs b/client/src/cli.rs index 01b59fed..a98b16d3 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -164,6 +164,15 @@ pub enum CliCommands { sticker: Option, }, SendContacts, + SendPaymentNotification { + recipient: String, + + #[clap(long)] + receipt: String, + + #[clap(long)] + note: String, + }, SendReaction { recipient: Vec, @@ -319,6 +328,9 @@ pub enum CliCommands { #[clap(long = "about-emoji")] about_emoji: Option, + #[clap(long = "mobile-coin-address")] + mobile_coin_address: Option, + #[clap(long)] avatar: Option, diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index f91be25e..3c8abbbd 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -135,6 +135,15 @@ pub trait Rpc { #[rpc(name = "sendContacts", params = "named")] fn send_contacts(&self, account: Option) -> Result; + #[rpc(name = "sendPaymentNotification", params = "named")] + fn send_payment_notification( + &self, + account: Option, + recipient: String, + receipt: String, + note: String, + ) -> Result; + #[rpc(name = "sendReaction", params = "named")] fn send_reaction( &self, @@ -263,6 +272,7 @@ pub trait Rpc { #[allow(non_snake_case)] familyName: Option, about: Option, #[allow(non_snake_case)] aboutEmoji: Option, + #[allow(non_snake_case)] mobileCoinAddress: Option, avatar: Option, #[allow(non_snake_case)] removeAvatar: bool, ) -> Result; diff --git a/client/src/main.rs b/client/src/main.rs index 622be806..c0ef0de6 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -147,6 +147,15 @@ async fn main() -> Result<(), anyhow::Error> { .await } cli::CliCommands::SendContacts => client.send_contacts(cli.account).await, + cli::CliCommands::SendPaymentNotification { + recipient, + receipt, + note, + } => { + client + .send_payment_notification(cli.account, recipient, receipt, note) + .await + } cli::CliCommands::SendReaction { recipient, group_id, @@ -309,6 +318,7 @@ async fn main() -> Result<(), anyhow::Error> { family_name, about, about_emoji, + mobile_coin_address, avatar, remove_avatar, } => { @@ -319,6 +329,7 @@ async fn main() -> Result<(), anyhow::Error> { family_name, about, about_emoji, + mobile_coin_address, avatar, remove_avatar, ) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 6d5df0d0..503f17f5 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2971,6 +2971,30 @@ {"name":"type_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$OutgoingPayment", + "fields":[ + {"name":"bitField0_"}, + {"name":"note_"}, + {"name":"paymentDetailCase_"}, + {"name":"paymentDetail_"}, + {"name":"recipientUuid_"} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$OutgoingPayment$MobileCoin", + "fields":[ + {"name":"amountPicoMob_"}, + {"name":"bitField0_"}, + {"name":"feePicoMob_"}, + {"name":"ledgerBlockIndex_"}, + {"name":"ledgerBlockTimestamp_"}, + {"name":"outputPublicKeys_"}, + {"name":"receipt_"}, + {"name":"recipientAddress_"}, + {"name":"spentKeyImages_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$PniIdentity", "fields":[ From 995eaa6e7cb49b353e306adf08c0457e1aade618 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 22 May 2022 22:01:11 +0200 Subject: [PATCH 116/938] Print more detailed error message when registering with non-normalized number Fixes #958 --- .../asamk/signal/manager/RegistrationManager.java | 5 ++++- .../signal/manager/RegistrationManagerImpl.java | 5 ++++- .../api/NonNormalizedPhoneNumberException.java | 12 ++++++++++++ .../asamk/signal/manager/helper/AccountHelper.java | 3 ++- .../signal/manager/util/NumberVerificationUtils.java | 8 +++++++- .../org/asamk/signal/commands/RegisterCommand.java | 3 +++ .../org/asamk/signal/dbus/DbusSignalControlImpl.java | 3 +++ 7 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/api/NonNormalizedPhoneNumberException.java 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 507690f4..a92792c7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -2,6 +2,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.PinLockedException; import java.io.Closeable; @@ -9,7 +10,9 @@ import java.io.IOException; public interface RegistrationManager extends Closeable { - void register(boolean voiceVerification, String captcha) throws IOException, CaptchaRequiredException; + void register( + boolean voiceVerification, String captcha + ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException; void verifyAccount( String verificationCode, String pin diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index 509b8923..3ce77557 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -18,6 +18,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.PinLockedException; import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.config.ServiceConfig; @@ -95,7 +96,9 @@ class RegistrationManagerImpl implements RegistrationManager { } @Override - public void register(boolean voiceVerification, String captcha) throws IOException, CaptchaRequiredException { + public void register( + boolean voiceVerification, String captcha + ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException { if (account.isRegistered() && account.getServiceEnvironment() != null && account.getServiceEnvironment() != serviceEnvironmentConfig.getType()) { diff --git a/lib/src/main/java/org/asamk/signal/manager/api/NonNormalizedPhoneNumberException.java b/lib/src/main/java/org/asamk/signal/manager/api/NonNormalizedPhoneNumberException.java new file mode 100644 index 00000000..ae00f66b --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/api/NonNormalizedPhoneNumberException.java @@ -0,0 +1,12 @@ +package org.asamk.signal.manager.api; + +public class NonNormalizedPhoneNumberException extends Exception { + + public NonNormalizedPhoneNumberException(final String message) { + super(message); + } + + public NonNormalizedPhoneNumberException(final String message, final Throwable cause) { + super(message, cause); + } +} 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 26247b09..b75cce52 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 @@ -5,6 +5,7 @@ import org.asamk.signal.manager.SignalDependencies; import org.asamk.signal.manager.api.CaptchaRequiredException; 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.config.ServiceConfig; import org.asamk.signal.manager.storage.SignalAccount; @@ -95,7 +96,7 @@ public class AccountHelper { public void startChangeNumber( String newNumber, String captcha, boolean voiceVerification - ) throws IOException, CaptchaRequiredException { + ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException { final var accountManager = dependencies.createUnauthenticatedAccountManager(newNumber, account.getPassword()); NumberVerificationUtils.requestVerificationCode(accountManager, captcha, voiceVerification); } 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 2f8981e3..9dcee1fb 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 @@ -2,6 +2,7 @@ package org.asamk.signal.manager.util; 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.PinLockedException; import org.asamk.signal.manager.helper.PinHelper; @@ -20,7 +21,7 @@ public class NumberVerificationUtils { public static void requestVerificationCode( SignalServiceAccountManager accountManager, String captcha, boolean voiceVerification - ) throws IOException, CaptchaRequiredException { + ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException { captcha = captcha == null ? null : captcha.replace("signalcaptcha://", ""); final ServiceResponse response; @@ -39,6 +40,11 @@ public class NumberVerificationUtils { handleResponseException(response); } catch (org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException e) { throw new CaptchaRequiredException(e.getMessage(), e); + } catch (org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException e) { + throw new NonNormalizedPhoneNumberException("Phone number is not normalized (" + + e.getMessage() + + "). Expected normalized: " + + e.getNormalizedNumber(), e); } } diff --git a/src/main/java/org/asamk/signal/commands/RegisterCommand.java b/src/main/java/org/asamk/signal/commands/RegisterCommand.java index d0bc6f1b..b2d3581e 100644 --- a/src/main/java/org/asamk/signal/commands/RegisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegisterCommand.java @@ -12,6 +12,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.CaptchaRequiredException; +import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.output.JsonWriter; import java.io.IOException; @@ -76,6 +77,8 @@ public class RegisterCommand implements RegistrationCommand, JsonRpcRegistration message = "Invalid captcha given."; } throw new UserErrorException(message); + } catch (NonNormalizedPhoneNumberException e) { + throw new UserErrorException("Failed to register: " + e.getMessage(), e); } catch (IOException e) { throw new IOErrorException("Request 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 e034d677..59afebf8 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalControlImpl.java @@ -9,6 +9,7 @@ import org.asamk.signal.manager.ProvisioningManager; 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.PinLockedException; import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.freedesktop.dbus.DBusPath; @@ -61,6 +62,8 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl { } catch (CaptchaRequiredException e) { String message = captcha == null ? "Captcha required for verification." : "Invalid captcha given."; throw new SignalControl.Error.RequiresCaptcha(message); + } catch (NonNormalizedPhoneNumberException e) { + throw new Error.InvalidNumber(e.getMessage()); } catch (OverlappingFileLockException e) { throw new SignalControl.Error.Failure("Account is already in use"); } catch (IOException e) { From 3abb641c7c554bf8a719b0a47a494aee1f9d8eba Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 23 May 2022 13:18:33 +0200 Subject: [PATCH 117/938] Reduce direct use of recipient store --- .../org/asamk/signal/manager/ManagerImpl.java | 22 +++---- .../signal/manager/helper/AccountHelper.java | 2 +- .../signal/manager/helper/GroupHelper.java | 20 +++--- .../helper/IncomingMessageHandler.java | 20 +++--- .../signal/manager/helper/ProfileHelper.java | 2 +- .../signal/manager/helper/ReceiveHelper.java | 6 +- .../manager/helper/RecipientHelper.java | 14 ++--- .../signal/manager/helper/SendHelper.java | 8 +-- .../signal/manager/helper/StorageHelper.java | 2 +- .../signal/manager/helper/SyncHelper.java | 4 +- .../signal/manager/storage/SignalAccount.java | 61 ++++++++++++------- .../storage/recipients/RecipientStore.java | 4 +- .../recipients/RecipientTrustedResolver.java | 10 +++ 13 files changed, 101 insertions(+), 74 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientTrustedResolver.java diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 96196f8f..19ff8b54 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -168,9 +168,9 @@ class ManagerImpl implements Manager { logger.trace("Archiving old sessions for {}", recipientId); account.getSessionStore().archiveSessions(recipientId); account.getSenderKeyStore().deleteSharedWith(recipientId); - final var profile = account.getRecipientStore().getProfile(recipientId); + final var profile = account.getProfileStore().getProfile(recipientId); if (profile != null) { - account.getRecipientStore() + account.getProfileStore() .storeProfile(recipientId, Profile.newBuilder(profile) .withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN) @@ -215,7 +215,8 @@ class ManagerImpl implements Manager { final var aci = registeredUsers.get(number); final var profile = aci == null ? null - : context.getProfileHelper().getRecipientProfile(account.getRecipientStore().resolveRecipient(aci)); + : context.getProfileHelper() + .getRecipientProfile(account.getRecipientResolver().resolveRecipient(aci)); return new UserStatus(number.isEmpty() ? null : number, aci == null ? null : aci.uuid(), profile != null @@ -356,9 +357,7 @@ class ManagerImpl implements Manager { return null; } - return Group.from(groupInfo, - account.getRecipientStore()::resolveRecipientAddress, - account.getSelfRecipientId()); + return Group.from(groupInfo, account.getRecipientAddressResolver(), account.getSelfRecipientId()); } @Override @@ -459,9 +458,7 @@ class ManagerImpl implements Manager { } private SendMessageResult toSendMessageResult(final org.whispersystems.signalservice.api.messages.SendMessageResult result) { - return SendMessageResult.from(result, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress); + return SendMessageResult.from(result, account.getRecipientResolver(), account.getRecipientAddressResolver()); } private SendMessageResults sendTypingMessage( @@ -683,13 +680,13 @@ class ManagerImpl implements Manager { @Override public void deleteRecipient(final RecipientIdentifier.Single recipient) { - account.removeRecipient(account.getRecipientStore().resolveRecipient(recipient.toPartialRecipientAddress())); + account.removeRecipient(account.getRecipientResolver().resolveRecipient(recipient.toPartialRecipientAddress())); } @Override public void deleteContact(final RecipientIdentifier.Single recipient) { account.getContactStore() - .deleteContact(account.getRecipientStore().resolveRecipient(recipient.toPartialRecipientAddress())); + .deleteContact(account.getRecipientResolver().resolveRecipient(recipient.toPartialRecipientAddress())); } @Override @@ -1018,7 +1015,8 @@ class ManagerImpl implements Manager { return null; } - final var address = account.getRecipientStore().resolveRecipientAddress(identityInfo.getRecipientId()); + final var address = account.getRecipientAddressResolver() + .resolveRecipientAddress(identityInfo.getRecipientId()); final var scannableFingerprint = context.getIdentityHelper() .computeSafetyNumberForScanning(identityInfo.getRecipientId(), identityInfo.getIdentityKey()); return new Identity(address, 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 b75cce52..224ac4da 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 @@ -86,7 +86,7 @@ public class AccountHelper { account.setNumber(number); account.setAci(aci); account.setPni(pni); - account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress()); + account.getRecipientTrustedResolver().resolveSelfRecipientTrusted(account.getSelfRecipientAddress()); // TODO check and update remote storage context.getUnidentifiedAccessHelper().rotateSenderCertificates(); dependencies.resetAfterAddressChange(); 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 9346372c..f4e92320 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 @@ -153,7 +153,7 @@ public class GroupHelper { downloadGroupAvatar(groupId, groupSecretParams, avatar); } } - groupInfoV2.setGroup(group, account.getRecipientStore()); + groupInfoV2.setGroup(group, account.getRecipientResolver()); account.getGroupStore().updateGroup(groupInfoV2); } @@ -183,7 +183,7 @@ public class GroupHelper { final var gv2 = gv2Pair.first(); final var decryptedGroup = gv2Pair.second(); - gv2.setGroup(decryptedGroup, account.getRecipientStore()); + gv2.setGroup(decryptedGroup, account.getRecipientResolver()); if (avatarFile != null) { context.getAvatarStore() .storeGroupAvatar(gv2.getGroupId(), @@ -398,7 +398,7 @@ public class GroupHelper { downloadGroupAvatar(groupInfoV2.getGroupId(), groupSecretParams, avatar); } } - groupInfoV2.setGroup(decryptedGroup, account.getRecipientStore()); + groupInfoV2.setGroup(decryptedGroup, account.getRecipientResolver()); account.getGroupStore().updateGroup(group); } } @@ -441,7 +441,7 @@ public class GroupHelper { private void storeProfileKeysFromMembers(final DecryptedGroup group) { for (var member : group.getMembersList()) { final var serviceId = ServiceId.fromByteString(member.getUuid()); - final var recipientId = account.getRecipientStore().resolveRecipient(serviceId); + final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId); final var profileStore = account.getProfileStore(); if (profileStore.getProfileKey(recipientId) != null) { // We already have a profile key, not updating it from a non-authoritative source @@ -461,7 +461,7 @@ public class GroupHelper { if (profileKeyFromChange != null) { final var serviceId = profileKeyFromChange.first(); final var profileKey = profileKeyFromChange.second(); - final var recipientId = account.getRecipientStore().resolveRecipient(serviceId); + final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId); account.getProfileStore().storeProfileKey(recipientId, profileKey); } } @@ -487,7 +487,7 @@ public class GroupHelper { .forEach(p -> { final var serviceId = p.first(); final var profileKey = p.second(); - final var recipientId = account.getRecipientStore().resolveRecipient(serviceId); + final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId); newProfileKeys.put(recipientId, profileKey); }); if (!page.getPagingData().hasMorePages()) { @@ -729,7 +729,7 @@ public class GroupHelper { throw new LastGroupAdminException(groupInfoV2.getGroupId(), groupInfoV2.getTitle()); } final var groupGroupChangePair = context.getGroupV2Helper().leaveGroup(groupInfoV2, newAdmins); - groupInfoV2.setGroup(groupGroupChangePair.first(), account.getRecipientStore()); + groupInfoV2.setGroup(groupGroupChangePair.first(), account.getRecipientResolver()); account.getGroupStore().updateGroup(groupInfoV2); var messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().toByteArray()); @@ -773,7 +773,7 @@ public class GroupHelper { ) throws IOException { final var selfRecipientId = account.getSelfRecipientId(); final var members = group.getMembersIncludingPendingWithout(selfRecipientId); - group.setGroup(newDecryptedGroup, account.getRecipientStore()); + group.setGroup(newDecryptedGroup, account.getRecipientResolver()); members.addAll(group.getMembersIncludingPendingWithout(selfRecipientId)); account.getGroupStore().updateGroup(group); @@ -792,8 +792,8 @@ public class GroupHelper { return new SendGroupMessageResults(timestamp, results.stream() .map(sendMessageResult -> SendMessageResult.from(sendMessageResult, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress)) + account.getRecipientResolver(), + account.getRecipientAddressResolver())) .toList()); } } 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 5e310a48..b28c21e6 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 @@ -89,8 +89,8 @@ public final class IncomingMessageHandler { try { content = dependencies.getCipher().decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { - final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender()); - final var exception = new UntrustedIdentityException(account.getRecipientStore() + final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender()); + final var exception = new UntrustedIdentityException(account.getRecipientAddressResolver() .resolveRecipientAddress(recipientId), e.getSenderDevice()); return new Pair<>(List.of(), exception); } catch (Exception e) { @@ -112,7 +112,7 @@ public final class IncomingMessageHandler { if (envelope.hasSourceUuid()) { // Store uuid if we don't have it already // address/uuid in envelope is sent by server - account.getRecipientStore().resolveRecipientTrusted(envelope.getSourceAddress()); + account.getRecipientTrustedResolver().resolveRecipientTrusted(envelope.getSourceAddress()); } SignalServiceContent content = null; Exception exception = null; @@ -120,14 +120,14 @@ public final class IncomingMessageHandler { try { content = dependencies.getCipher().decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { - final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender()); + final var recipientId = account.getRecipientResolver().resolveRecipient(e.getSender()); actions.add(new RetrieveProfileAction(recipientId)); - exception = new UntrustedIdentityException(account.getRecipientStore() + exception = new UntrustedIdentityException(account.getRecipientAddressResolver() .resolveRecipientAddress(recipientId), e.getSenderDevice()); } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException | ProtocolInvalidMessageException e) { logger.debug("Failed to decrypt incoming message", e); - final var sender = account.getRecipientStore().resolveRecipient(e.getSender()); + final var sender = account.getRecipientResolver().resolveRecipient(e.getSender()); if (context.getContactHelper().isContactBlocked(sender)) { logger.debug("Received invalid message from blocked contact, ignoring."); } else { @@ -169,7 +169,7 @@ public final class IncomingMessageHandler { if (!envelope.hasSourceUuid() && content != null) { // Store uuid if we don't have it already // address/uuid is validated by unidentified sender certificate - account.getRecipientStore().resolveRecipientTrusted(content.getSender()); + account.getRecipientTrustedResolver().resolveRecipientTrusted(content.getSender()); } if (envelope.isReceipt()) { final var senderPair = getSender(envelope, content); @@ -195,8 +195,8 @@ public final class IncomingMessageHandler { } handler.handleMessage(MessageEnvelope.from(envelope, content, - account.getRecipientStore(), - account.getRecipientStore()::resolveRecipientAddress, + account.getRecipientResolver(), + account.getRecipientAddressResolver(), context.getAttachmentHelper()::getAttachmentFile, exception), exception); return actions; @@ -391,7 +391,7 @@ public final class IncomingMessageHandler { if (syncMessage.getVerified().isPresent()) { final var verifiedMessage = syncMessage.getVerified().get(); account.getIdentityKeyStore() - .setIdentityTrustLevel(account.getRecipientStore() + .setIdentityTrustLevel(account.getRecipientTrustedResolver() .resolveRecipientTrusted(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); 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 cc01dc73..38788278 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 @@ -289,7 +289,7 @@ public final class ProfileHelper { var profile = account.getProfileStore().getProfile(recipientId); if (profile == null || !Objects.equals(avatarPath, profile.getAvatarUrlPath())) { logger.trace("Downloading profile avatar for {}", recipientId); - downloadProfileAvatar(account.getRecipientStore().resolveRecipientAddress(recipientId), + downloadProfileAvatar(account.getRecipientAddressResolver().resolveRecipientAddress(recipientId), avatarPath, profileKey); var builder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile); 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 7bc2c224..7083caae 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 @@ -141,7 +141,7 @@ public class ReceiveHelper { isWaitingForMessage = true; var result = signalWebSocket.readOrEmpty(timeout.toMillis(), envelope1 -> { isWaitingForMessage = false; - final var recipientId = envelope1.hasSourceUuid() ? account.getRecipientStore() + final var recipientId = envelope1.hasSourceUuid() ? account.getRecipientResolver() .resolveRecipient(envelope1.getSourceAddress()) : null; logger.trace("Storing new message from {}", recipientId); // store message on disk, before acknowledging receipt to the server @@ -211,7 +211,7 @@ public class ReceiveHelper { if (exception instanceof UntrustedIdentityException) { logger.debug("Keeping message with untrusted identity in message cache"); final var address = ((UntrustedIdentityException) exception).getSender(); - final var recipientId = account.getRecipientStore().resolveRecipient(address); + final var recipientId = account.getRecipientResolver().resolveRecipient(address); if (!envelope.hasSourceUuid()) { try { cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId); @@ -260,7 +260,7 @@ public class ReceiveHelper { } if (!envelope.hasSourceUuid()) { final var identifier = ((UntrustedIdentityException) exception).getSender(); - final var recipientId = account.getRecipientStore().resolveRecipient(identifier); + final var recipientId = account.getRecipientResolver().resolveRecipient(identifier); try { account.getMessageCache().replaceSender(cachedMessage, recipientId); } catch (IOException ioException) { 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 c253d602..2f254b5d 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 @@ -40,7 +40,7 @@ public class RecipientHelper { } public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) { - final var address = account.getRecipientStore().resolveRecipientAddress(recipientId); + final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId); if (address.number().isEmpty() || address.uuid().isPresent()) { return address.toSignalServiceAddress(); } @@ -60,13 +60,13 @@ public class RecipientHelper { // Return SignalServiceAddress with unknown UUID return address.toSignalServiceAddress(); } - return account.getRecipientStore() - .resolveRecipientAddress(account.getRecipientStore().resolveRecipient(aci)) + return account.getRecipientAddressResolver() + .resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(aci)) .toSignalServiceAddress(); } public RecipientId resolveRecipient(final SignalServiceAddress address) { - return account.getRecipientStore().resolveRecipient(address); + return account.getRecipientResolver().resolveRecipient(address); } public Set resolveRecipients(Collection recipients) throws UnregisteredRecipientException { @@ -80,7 +80,7 @@ public class RecipientHelper { public RecipientId resolveRecipient(final RecipientIdentifier.Single recipient) throws UnregisteredRecipientException { if (recipient instanceof RecipientIdentifier.Uuid uuidRecipient) { - return account.getRecipientStore().resolveRecipient(ServiceId.from(uuidRecipient.uuid())); + return account.getRecipientResolver().resolveRecipient(ServiceId.from(uuidRecipient.uuid())); } else { final var number = ((RecipientIdentifier.Number) recipient).number(); return account.getRecipientStore().resolveRecipient(number, () -> { @@ -100,7 +100,7 @@ public class RecipientHelper { } final var number = address.getNumber().get(); final var uuid = getRegisteredUser(number); - return account.getRecipientStore().resolveRecipientTrusted(new SignalServiceAddress(uuid, number)); + return account.getRecipientTrustedResolver().resolveRecipientTrusted(new SignalServiceAddress(uuid, number)); } public Map getRegisteredUsers(final Set numbers) throws IOException { @@ -116,7 +116,7 @@ public class RecipientHelper { } // Store numbers as recipients, so we have the number/uuid association - registeredUsers.forEach((number, aci) -> account.getRecipientStore() + registeredUsers.forEach((number, aci) -> account.getRecipientTrustedResolver() .resolveRecipientTrusted(new SignalServiceAddress(aci, number))); return registeredUsers; 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 8b2a054e..85d5cc32 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 @@ -656,12 +656,12 @@ public class SendHelper { private void handleSendMessageResult(final SendMessageResult r) { if (r.isSuccess() && !r.getSuccess().isUnidentified()) { final var recipientId = context.getRecipientHelper().resolveRecipient(r.getAddress()); - final var profile = account.getRecipientStore().getProfile(recipientId); + final var profile = account.getProfileStore().getProfile(recipientId); if (profile != null && ( profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.ENABLED || profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED )) { - account.getRecipientStore() + account.getProfileStore() .storeProfile(recipientId, Profile.newBuilder(profile) .withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN) @@ -670,12 +670,12 @@ public class SendHelper { } if (r.isUnregisteredFailure()) { final var recipientId = context.getRecipientHelper().resolveRecipient(r.getAddress()); - final var profile = account.getRecipientStore().getProfile(recipientId); + final var profile = account.getProfileStore().getProfile(recipientId); if (profile != null && ( profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.ENABLED || profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED )) { - account.getRecipientStore() + account.getProfileStore() .storeProfile(recipientId, Profile.newBuilder(profile) .withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN) 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 88fe84b9..b1a653fd 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 @@ -86,7 +86,7 @@ public class StorageHelper { final var contactRecord = record.getContact().get(); final var address = contactRecord.getAddress(); - final var recipientId = account.getRecipientStore().resolveRecipient(address); + final var recipientId = account.getRecipientResolver().resolveRecipient(address); final var contact = account.getContactStore().getContact(recipientId); final var blocked = contact != null && contact.isBlocked(); final var profileShared = contact != null && contact.isProfileSharingEnabled(); 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 86fb0e66..d98f9eef 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 @@ -257,7 +257,7 @@ public class SyncHelper { if (c.getAddress().matches(account.getSelfAddress()) && c.getProfileKey().isPresent()) { account.setProfileKey(c.getProfileKey().get()); } - final var recipientId = account.getRecipientStore().resolveRecipientTrusted(c.getAddress()); + final var recipientId = account.getRecipientTrustedResolver().resolveRecipientTrusted(c.getAddress()); var contact = account.getContactStore().getContact(recipientId); final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); if (c.getName().isPresent()) { @@ -272,7 +272,7 @@ public class SyncHelper { if (c.getVerified().isPresent()) { final var verifiedMessage = c.getVerified().get(); account.getIdentityKeyStore() - .setIdentityTrustLevel(account.getRecipientStore() + .setIdentityTrustLevel(account.getRecipientTrustedResolver() .resolveRecipientTrusted(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); 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 ac41d791..521e21d9 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 @@ -7,6 +7,7 @@ import org.asamk.signal.manager.api.Pair; import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.config.ServiceEnvironment; import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.helper.RecipientAddressResolver; import org.asamk.signal.manager.storage.configuration.ConfigurationStore; import org.asamk.signal.manager.storage.contacts.ContactsStore; import org.asamk.signal.manager.storage.contacts.LegacyJsonContactsStore; @@ -27,7 +28,9 @@ import org.asamk.signal.manager.storage.recipients.LegacyRecipientStore; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.storage.recipients.RecipientStore; +import org.asamk.signal.manager.storage.recipients.RecipientTrustedResolver; import org.asamk.signal.manager.storage.sendLog.MessageSendLogStore; import org.asamk.signal.manager.storage.senderKeys.SenderKeyStore; import org.asamk.signal.manager.storage.sessions.SessionStore; @@ -201,7 +204,7 @@ public class SignalAccount implements Closeable { signalAccount.localRegistrationId = registrationId; signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), - signalAccount.getRecipientStore(), + signalAccount.getRecipientResolver(), signalAccount::saveGroupStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); @@ -260,7 +263,8 @@ public class SignalAccount implements Closeable { aciIdentityKey, pniIdentityKey, profileKey); - signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); + signalAccount.getRecipientTrustedResolver() + .resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); signalAccount.getSessionStore().archiveAllSessions(); signalAccount.getSenderKeyStore().deleteAll(); signalAccount.clearAllPreKeys(); @@ -321,12 +325,13 @@ public class SignalAccount implements Closeable { signalAccount.localRegistrationId = registrationId; signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), - signalAccount.getRecipientStore(), + signalAccount.getRecipientResolver(), signalAccount::saveGroupStore); signalAccount.stickerStore = new StickerStore(signalAccount::saveStickerStore); signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); - signalAccount.getRecipientStore().resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); + signalAccount.getRecipientTrustedResolver() + .resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION; signalAccount.migrateLegacyConfigs(); signalAccount.clearAllPreKeys(); @@ -608,11 +613,11 @@ public class SignalAccount implements Closeable { groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"), GroupStore.Storage.class); groupStore = GroupStore.fromStorage(groupStoreStorage, getGroupCachePath(dataPath, accountPath), - getRecipientStore(), + getRecipientResolver(), this::saveGroupStore); } else { groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), - getRecipientStore(), + getRecipientResolver(), this::saveGroupStore); } @@ -650,7 +655,7 @@ public class SignalAccount implements Closeable { if (legacyRecipientStore != null) { getRecipientStore().resolveRecipientsTrusted(legacyRecipientStore.getAddresses()); } - getRecipientStore().resolveSelfRecipientTrusted(getSelfRecipientAddress()); + getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress()); migrated = true; } @@ -710,7 +715,7 @@ public class SignalAccount implements Closeable { final var contactStore = jsonProcessor.convertValue(contactStoreNode, LegacyJsonContactsStore.class); for (var contact : contactStore.getContacts()) { final var recipientId = getRecipientStore().resolveRecipientTrusted(contact.getAddress()); - getRecipientStore().storeContact(recipientId, + getContactStore().storeContact(recipientId, new Contact(contact.name, contact.color, contact.messageExpirationTime, @@ -738,9 +743,9 @@ public class SignalAccount implements Closeable { var profileStoreNode = rootNode.get("profileStore"); final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class); for (var profileEntry : legacyProfileStore.getProfileEntries()) { - var recipientId = getRecipientStore().resolveRecipient(profileEntry.getAddress()); - getRecipientStore().storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); - getRecipientStore().storeProfileKey(recipientId, profileEntry.getProfileKey()); + var recipientId = getRecipientResolver().resolveRecipient(profileEntry.getAddress()); + getProfileStore().storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); + getProfileStore().storeProfileKey(recipientId, profileEntry.getProfileKey()); final var profile = profileEntry.getProfile(); if (profile != null) { final var capabilities = new HashSet(); @@ -765,7 +770,7 @@ public class SignalAccount implements Closeable { ? Profile.UnidentifiedAccessMode.ENABLED : Profile.UnidentifiedAccessMode.DISABLED, capabilities); - getRecipientStore().storeProfile(recipientId, newProfile); + getProfileStore().storeProfile(recipientId, newProfile); } } } @@ -784,10 +789,10 @@ public class SignalAccount implements Closeable { } try { if (UuidUtil.isUuid(thread.id) || thread.id.startsWith("+")) { - final var recipientId = getRecipientStore().resolveRecipient(thread.id); - var contact = getRecipientStore().getContact(recipientId); + final var recipientId = getRecipientResolver().resolveRecipient(thread.id); + var contact = getContactStore().getContact(recipientId); if (contact != null) { - getRecipientStore().storeContact(recipientId, + getContactStore().storeContact(recipientId, Contact.newBuilder(contact) .withMessageExpirationTime(thread.messageExpirationTime) .build()); @@ -1028,13 +1033,13 @@ public class SignalAccount implements Closeable { public SessionStore getSessionStore() { return getOrCreate(() -> sessionStore, - () -> sessionStore = new SessionStore(getSessionsPath(dataPath, accountPath), getRecipientStore())); + () -> sessionStore = new SessionStore(getSessionsPath(dataPath, accountPath), getRecipientResolver())); } public IdentityKeyStore getIdentityKeyStore() { return getOrCreate(() -> identityKeyStore, () -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath), - getRecipientStore(), + getRecipientResolver(), aciIdentityKeyPair, localRegistrationId, trustNewIdentity)); @@ -1048,6 +1053,18 @@ public class SignalAccount implements Closeable { return getRecipientStore(); } + public RecipientResolver getRecipientResolver() { + return getRecipientStore(); + } + + public RecipientTrustedResolver getRecipientTrustedResolver() { + return getRecipientStore(); + } + + public RecipientAddressResolver getRecipientAddressResolver() { + return getRecipientStore()::resolveRecipientAddress; + } + public RecipientStore getRecipientStore() { return getOrCreate(() -> recipientStore, () -> recipientStore = RecipientStore.load(getRecipientsStoreFile(dataPath, accountPath), @@ -1067,8 +1084,8 @@ public class SignalAccount implements Closeable { return getOrCreate(() -> senderKeyStore, () -> senderKeyStore = new SenderKeyStore(getSharedSenderKeysFile(dataPath, accountPath), getSenderKeysPath(dataPath, accountPath), - getRecipientStore()::resolveRecipientAddress, - getRecipientStore())); + getRecipientAddressResolver(), + getRecipientResolver())); } public ConfigurationStore getConfigurationStore() { @@ -1092,7 +1109,7 @@ public class SignalAccount implements Closeable { public MessageSendLogStore getMessageSendLogStore() { return getOrCreate(() -> messageSendLogStore, - () -> messageSendLogStore = new MessageSendLogStore(getRecipientStore(), getAccountDatabase())); + () -> messageSendLogStore = new MessageSendLogStore(getRecipientResolver(), getAccountDatabase())); } public CredentialsProvider getCredentialsProvider() { @@ -1169,7 +1186,7 @@ public class SignalAccount implements Closeable { } public RecipientId getSelfRecipientId() { - return getRecipientStore().resolveRecipient(getSelfRecipientAddress()); + return getRecipientResolver().resolveRecipient(getSelfRecipientAddress()); } public byte[] getEncryptedDeviceName() { @@ -1337,7 +1354,7 @@ public class SignalAccount implements Closeable { clearAllPreKeys(); getSessionStore().archiveAllSessions(); getSenderKeyStore().deleteAll(); - final var recipientId = getRecipientStore().resolveSelfRecipientTrusted(getSelfRecipientAddress()); + final var recipientId = getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress()); final var publicKey = getAciIdentityKeyPair().getPublicKey(); getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date()); getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED); 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 0297f6d7..d2cc94d6 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 @@ -37,7 +37,7 @@ import java.util.UUID; import java.util.function.Supplier; import java.util.stream.Collectors; -public class RecipientStore implements RecipientResolver, ContactsStore, ProfileStore { +public class RecipientStore implements RecipientResolver, RecipientTrustedResolver, ContactsStore, ProfileStore { private final static Logger logger = LoggerFactory.getLogger(RecipientStore.class); @@ -224,6 +224,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile return resolveRecipient(new RecipientAddress(address), false, false); } + @Override public RecipientId resolveSelfRecipientTrusted(RecipientAddress address) { return resolveRecipient(address, true, true); } @@ -232,6 +233,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile return resolveRecipient(address, true, false); } + @Override public RecipientId resolveRecipientTrusted(SignalServiceAddress address) { return resolveRecipient(new RecipientAddress(address), true, false); } 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 new file mode 100644 index 00000000..0bc522e3 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientTrustedResolver.java @@ -0,0 +1,10 @@ +package org.asamk.signal.manager.storage.recipients; + +import org.whispersystems.signalservice.api.push.SignalServiceAddress; + +public interface RecipientTrustedResolver { + + RecipientId resolveSelfRecipientTrusted(RecipientAddress address); + + RecipientId resolveRecipientTrusted(SignalServiceAddress address); +} From 621d822a6ce03cf52b0897935cf669d341603eca Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 23 May 2022 16:24:08 +0200 Subject: [PATCH 118/938] Refresh self profile before rotating profile key --- .../java/org/asamk/signal/manager/helper/ProfileHelper.java | 2 ++ 1 file changed, 2 insertions(+) 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 38788278..812e4b3c 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 @@ -61,6 +61,8 @@ public final class ProfileHelper { } public void rotateProfileKey() throws IOException { + // refresh our profile, before creating a new profile key + getSelfProfile(); var profileKey = KeyUtils.createProfileKey(); account.setProfileKey(profileKey); context.getAccountHelper().updateAccountAttributes(); From 69e952738bd6300940ac3845792f052fe57eb58d Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 23 May 2022 16:24:36 +0200 Subject: [PATCH 119/938] Update code style for text blocks --- .idea/codeStyles/Project.xml | 1 + .../storage/sendLog/MessageSendLogStore.java | 34 +++++++++---------- .../signal/commands/RegisterCommand.java | 8 ++--- .../signal/util/SendMessageResultUtils.java | 10 +++--- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 9cbd20b7..bb65b3e9 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -4,6 +4,7 @@

+ * The {@code dataURI} must be of the form: + *

+ * {@code + * data:[][;base64], + * } + *

+ * The {@code } is an Internet media type specification (with + * optional parameters.) The appearance of ";base64" means that the data + * is encoded as base64. Without ";base64", the data is represented using (ASCII) URL Escaped encoding. + * If {@code } is omitted, it defaults to {@link DataURI#DEFAULT_TYPE}. + * Parameter values should use the URL Escaped encoding. + * + * @param dataURI the data URI + * @return a data URI object + * @throws IllegalArgumentException if the given string is not a valid data URI + */ + public static DataURI of(final String dataURI) { + final var matcher = DATA_URI_PATTERN.matcher(dataURI); + + if (!matcher.find()) { + throw new IllegalArgumentException("The given string is not a valid data URI."); + } + + final Map parameters = new HashMap<>(); + final var params = matcher.group("parameters"); + if (params != null) { + final Matcher paramsMatcher = PARAMETER_PATTERN.matcher(params); + while (paramsMatcher.find()) { + final var key = paramsMatcher.group("key"); + final var value = URLDecoder.decode(paramsMatcher.group("value"), StandardCharsets.UTF_8); + parameters.put(key, value); + } + } + + final boolean isBase64 = matcher.group("base64") != null; + final byte[] data; + if (isBase64) { + data = Base64.getDecoder().decode(matcher.group("data").getBytes(StandardCharsets.UTF_8)); + } else { + data = URLDecoder.decode(matcher.group("data"), StandardCharsets.UTF_8).getBytes(StandardCharsets.UTF_8); + } + + return new DataURI(Optional.ofNullable(matcher.group("type")).orElse(DEFAULT_TYPE), parameters, data); + } +} 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 3fc69801..e71e6412 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 @@ -1,5 +1,6 @@ package org.asamk.signal.manager.util; +import org.asamk.signal.manager.api.Pair; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.fingerprint.Fingerprint; import org.signal.libsignal.protocol.fingerprint.NumericFingerprintGenerator; @@ -9,6 +10,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.StreamDetails; import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -17,9 +19,11 @@ import java.net.URLConnection; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.Base64; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.BiFunction; @@ -31,10 +35,10 @@ public class Utils { private final static Logger logger = LoggerFactory.getLogger(Utils.class); - public static String getFileMimeType(File file, String defaultMimeType) throws IOException { + public static String getFileMimeType(final File file, final String defaultMimeType) throws IOException { var mime = Files.probeContentType(file.toPath()); if (mime == null) { - try (InputStream bufferedStream = new BufferedInputStream(new FileInputStream(file))) { + try (final InputStream bufferedStream = new BufferedInputStream(new FileInputStream(file))) { mime = URLConnection.guessContentTypeFromStream(bufferedStream); } } @@ -44,13 +48,31 @@ public class Utils { return mime; } - public static StreamDetails createStreamDetailsFromFile(File file) throws IOException { - InputStream stream = new FileInputStream(file); + public static Pair> createStreamDetailsFromDataURI(final String dataURI) { + final DataURI uri = DataURI.of(dataURI); + + return new Pair<>(new StreamDetails( + new ByteArrayInputStream(uri.data()), uri.mediaType(), uri.data().length), + Optional.ofNullable(uri.parameter().get("filename"))); + } + + public static StreamDetails createStreamDetailsFromFile(final File file) throws IOException { + final InputStream stream = new FileInputStream(file); final var size = file.length(); final var mime = getFileMimeType(file, "application/octet-stream"); return new StreamDetails(stream, mime, size); } + public static Pair> createStreamDetails(final String value) throws IOException { + try { + return createStreamDetailsFromDataURI(value); + } catch (final IllegalArgumentException e) { + final File f = new File(value); + + return new Pair<>(createStreamDetailsFromFile(f), Optional.of(f.getName())); + } + } + public static Fingerprint computeSafetyNumber( boolean isUuidCapable, SignalServiceAddress ownAddress, diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 6258243a..d60ef3ad 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -231,6 +231,9 @@ Read the message from standard input. *-a* [ATTACHMENT [ATTACHMENT ...]], *--attachment* [ATTACHMENT [ATTACHMENT ...]]:: Add one or more files as 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,` *--sticker* STICKER:: Send a sticker of a locally known sticker pack (syntax: stickerPackId:stickerId). diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index ae324c84..411895dd 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -55,7 +55,9 @@ public class SendCommand implements JsonRpcLocalCommand { mut.addArgument("--message-from-stdin") .action(Arguments.storeTrue()) .help("Read the message from standard input."); - subparser.addArgument("-a", "--attachment").nargs("*").help("Add file as attachment"); + subparser.addArgument("-a", "--attachment").nargs("*").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("-e", "--end-session", "--endsession") .help("Clear session state and send end session message.") .action(Arguments.storeTrue()); From 3ad87e136231159f7439c7707605f3c4fe90d2f0 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 4 Jun 2022 11:52:42 +0200 Subject: [PATCH 138/938] Reformat --- .../org/asamk/signal/manager/util/AttachmentUtils.java | 2 -- .../main/java/org/asamk/signal/manager/util/Utils.java | 4 +--- src/main/java/org/asamk/signal/commands/SendCommand.java | 8 +++++--- 3 files changed, 6 insertions(+), 8 deletions(-) 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 4da78f68..a535d82b 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 @@ -5,8 +5,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStre import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; 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 e71e6412..cead93d9 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 @@ -19,7 +19,6 @@ import java.net.URLConnection; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.Base64; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -51,8 +50,7 @@ public class Utils { public static Pair> createStreamDetailsFromDataURI(final String dataURI) { final DataURI uri = DataURI.of(dataURI); - return new Pair<>(new StreamDetails( - new ByteArrayInputStream(uri.data()), uri.mediaType(), uri.data().length), + return new Pair<>(new StreamDetails(new ByteArrayInputStream(uri.data()), uri.mediaType(), uri.data().length), Optional.ofNullable(uri.parameter().get("filename"))); } diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 411895dd..23a7fb37 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -55,9 +55,11 @@ public class SendCommand implements JsonRpcLocalCommand { mut.addArgument("--message-from-stdin") .action(Arguments.storeTrue()) .help("Read the message from standard input."); - subparser.addArgument("-a", "--attachment").nargs("*").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("-a", "--attachment") + .nargs("*") + .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("-e", "--end-session", "--endsession") .help("Clear session state and send end session message.") .action(Arguments.storeTrue()); From 51c2352d676cce004844f704b4c71c3de5bde4cc Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 6 Jun 2022 17:14:17 +0200 Subject: [PATCH 139/938] Extend logging when determining default locale --- lib/src/main/java/org/asamk/signal/manager/util/Utils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 cead93d9..cb0cf95f 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 @@ -107,15 +107,18 @@ public class Utils { public static Locale getDefaultLocale(Locale fallback) { final var locale = Locale.getDefault(); if (locale == null) { + logger.debug("No default locale found, using fallback: {}", fallback); return fallback; } + final var localeString = locale.getLanguage() + "-" + locale.getCountry(); try { - Locale.LanguageRange.parse(locale.getLanguage() + "-" + locale.getCountry()); + Locale.LanguageRange.parse(localeString); } catch (IllegalArgumentException e) { - logger.debug("Invalid locale, ignoring: {}", locale); + logger.debug("Invalid locale '{}', using fallback: {}", locale, fallback); return fallback; } + logger.debug("Using default locale: {} ({})", locale, localeString); return locale; } From 5b5a1718e9c54f6e12eac47662257faa9337f366 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 7 Jun 2022 11:53:53 +0200 Subject: [PATCH 140/938] Update register error message --- graalvm-config-dir/reflect-config.json | 7 +++++++ .../java/org/asamk/signal/commands/RegisterCommand.java | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 62612d44..2c2c94f9 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2135,6 +2135,13 @@ "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity$ByteArraySerializer", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException$JsonResponse", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.api.storage.StorageAuthResponse", "allDeclaredFields":true, diff --git a/src/main/java/org/asamk/signal/commands/RegisterCommand.java b/src/main/java/org/asamk/signal/commands/RegisterCommand.java index 4415bb18..84287877 100644 --- a/src/main/java/org/asamk/signal/commands/RegisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegisterCommand.java @@ -80,7 +80,7 @@ public class RegisterCommand implements RegistrationCommand, JsonRpcRegistration } catch (NonNormalizedPhoneNumberException e) { throw new UserErrorException("Failed to register: " + e.getMessage(), e); } catch (IOException e) { - throw new IOErrorException("Request verify error: " + e.getMessage(), e); + throw new IOErrorException("Failed to register: " + e.getMessage(), e); } } From 5afe9c5337e16e89c2ce7bd4aa1781a9ab78704b Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 7 Jun 2022 14:25:28 +0200 Subject: [PATCH 141/938] Rename ThreadInfo to LegacyThreadInfo --- .../storage/threads/LegacyJsonThreadStore.java | 12 ++++++------ .../{ThreadInfo.java => LegacyThreadInfo.java} | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) rename lib/src/main/java/org/asamk/signal/manager/storage/threads/{ThreadInfo.java => LegacyThreadInfo.java} (86%) 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 81810713..7e5fd242 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 @@ -25,9 +25,9 @@ public class LegacyJsonThreadStore { @JsonProperty("threads") @JsonSerialize(using = MapToListSerializer.class) @JsonDeserialize(using = ThreadsDeserializer.class) - private Map threads = new HashMap<>(); + private Map threads = new HashMap<>(); - public List getThreads() { + public List getThreads() { return new ArrayList<>(threads.values()); } @@ -41,16 +41,16 @@ public class LegacyJsonThreadStore { } } - private static class ThreadsDeserializer extends JsonDeserializer> { + private static class ThreadsDeserializer extends JsonDeserializer> { @Override - public Map deserialize( + public Map deserialize( JsonParser jsonParser, DeserializationContext deserializationContext ) throws IOException { - var threads = new HashMap(); + var threads = new HashMap(); JsonNode node = jsonParser.getCodec().readTree(jsonParser); for (var n : node) { - var t = jsonProcessor.treeToValue(n, ThreadInfo.class); + var t = jsonProcessor.treeToValue(n, LegacyThreadInfo.class); threads.put(t.id, t); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyThreadInfo.java similarity index 86% rename from lib/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java rename to lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyThreadInfo.java index b81a0051..7a55c665 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyThreadInfo.java @@ -2,7 +2,7 @@ package org.asamk.signal.manager.storage.threads; import com.fasterxml.jackson.annotation.JsonProperty; -public class ThreadInfo { +public class LegacyThreadInfo { @JsonProperty public String id; From 1757e939e1f60e77744501d0bd6cd7dd762b5dfc Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 7 Jun 2022 15:26:42 +0200 Subject: [PATCH 142/938] Reformat sql statements in MessageSendLogStore --- .../storage/sendLog/MessageSendLogStore.java | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) 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 79fc28f0..c893f09c 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 @@ -93,13 +93,16 @@ public class MessageSendLogStore implements AutoCloseable { public List findMessages( final RecipientId recipientId, final int deviceId, final long timestamp, final boolean isSenderKey ) { + final var sql = """ + SELECT group_id, content, content_hint + FROM %s l + INNER JOIN %s lc ON l.content_id = lc._id + WHERE l.recipient_id = ? AND l.device_id = ? AND lc.timestamp = ? + """.formatted(TABLE_MESSAGE_SEND_LOG, TABLE_MESSAGE_SEND_LOG_CONTENT); try (final var connection = database.getConnection()) { deleteOutdatedEntries(connection); - try (final var statement = connection.prepareStatement( - "SELECT group_id, content, content_hint FROM %s l INNER JOIN %s lc ON l.content_id = lc._id WHERE l.recipient_id = ? AND l.device_id = ? AND lc.timestamp = ?".formatted( - TABLE_MESSAGE_SEND_LOG, - TABLE_MESSAGE_SEND_LOG_CONTENT))) { + try (final var statement = connection.prepareStatement(sql)) { statement.setLong(1, recipientId.id()); statement.setInt(2, deviceId); statement.setLong(3, timestamp); @@ -185,10 +188,12 @@ public class MessageSendLogStore implements AutoCloseable { } public void deleteEntryForGroup(long sentTimestamp, GroupId groupId) { + final var sql = """ + DELETE FROM %s AS lc + WHERE lc.timestamp = ? AND lc.group_id = ? + """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT); try (final var connection = database.getConnection()) { - try (final var statement = connection.prepareStatement( - "DELETE FROM %s AS lc WHERE lc.timestamp = ? AND lc.group_id = ?".formatted( - TABLE_MESSAGE_SEND_LOG_CONTENT))) { + try (final var statement = connection.prepareStatement(sql)) { statement.setLong(1, sentTimestamp); statement.setBytes(2, groupId.serialize()); statement.executeUpdate(); @@ -199,12 +204,13 @@ public class MessageSendLogStore implements AutoCloseable { } public void deleteEntryForRecipientNonGroup(long sentTimestamp, RecipientId recipientId) { + final var sql = """ + DELETE FROM %s AS lc + WHERE lc.timestamp = ? AND lc.group_id IS NULL AND lc._id IN (SELECT content_id FROM %s l WHERE l.recipient_id = ?) + """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT, TABLE_MESSAGE_SEND_LOG); try (final var connection = database.getConnection()) { connection.setAutoCommit(false); - try (final var statement = connection.prepareStatement( - "DELETE FROM %s AS lc WHERE lc.timestamp = ? AND lc.group_id IS NULL AND lc._id IN (SELECT content_id FROM %s l WHERE l.recipient_id = ?)".formatted( - TABLE_MESSAGE_SEND_LOG_CONTENT, - TABLE_MESSAGE_SEND_LOG))) { + try (final var statement = connection.prepareStatement(sql)) { statement.setLong(1, sentTimestamp); statement.setLong(2, recipientId.id()); statement.executeUpdate(); @@ -222,12 +228,13 @@ public class MessageSendLogStore implements AutoCloseable { } public void deleteEntriesForRecipient(List sentTimestamps, RecipientId recipientId, int deviceId) { + final var sql = """ + DELETE FROM %s AS l + WHERE l.content_id IN (SELECT _id FROM %s lc WHERE lc.timestamp = ?) AND l.recipient_id = ? AND l.device_id = ? + """.formatted(TABLE_MESSAGE_SEND_LOG, TABLE_MESSAGE_SEND_LOG_CONTENT); try (final var connection = database.getConnection()) { connection.setAutoCommit(false); - try (final var statement = connection.prepareStatement( - "DELETE FROM %s AS l WHERE l.content_id IN (SELECT _id FROM %s lc WHERE lc.timestamp = ?) AND l.recipient_id = ? AND l.device_id = ?".formatted( - TABLE_MESSAGE_SEND_LOG, - TABLE_MESSAGE_SEND_LOG_CONTENT))) { + try (final var statement = connection.prepareStatement(sql)) { for (final var sentTimestamp : sentTimestamps) { statement.setLong(1, sentTimestamp); statement.setLong(2, recipientId.id()); @@ -269,12 +276,14 @@ public class MessageSendLogStore implements AutoCloseable { ) { byte[] groupId = getGroupId(content); + final var sql = """ + INSERT INTO %s (timestamp, group_id, content, content_hint) + VALUES (?,?,?,?) + """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT); try (final var connection = database.getConnection()) { connection.setAutoCommit(false); final long contentId; - try (final var statement = connection.prepareStatement( - "INSERT INTO %s (timestamp, group_id, content, content_hint) VALUES (?,?,?,?)".formatted( - TABLE_MESSAGE_SEND_LOG_CONTENT))) { + try (final var statement = connection.prepareStatement(sql)) { statement.setLong(1, sentTimestamp); statement.setBytes(2, groupId); statement.setBytes(3, content.toByteArray()); @@ -334,8 +343,11 @@ public class MessageSendLogStore implements AutoCloseable { private void insertRecipientsForExistingContent( final long contentId, final List recipientDevices, final Connection connection ) throws SQLException { - try (final var statement = connection.prepareStatement( - "INSERT INTO %s (recipient_id, device_id, content_id) VALUES (?,?,?)".formatted(TABLE_MESSAGE_SEND_LOG))) { + final var sql = """ + INSERT INTO %s (recipient_id, device_id, content_id) + VALUES (?,?,?) + """.formatted(TABLE_MESSAGE_SEND_LOG); + try (final var statement = connection.prepareStatement(sql)) { for (final var recipientDevice : recipientDevices) { for (final var deviceId : recipientDevice.deviceIds()) { statement.setLong(1, recipientDevice.recipientId().id()); @@ -348,8 +360,11 @@ public class MessageSendLogStore implements AutoCloseable { } private void deleteOutdatedEntries(final Connection connection) throws SQLException { - try (final var statement = connection.prepareStatement("DELETE FROM %s WHERE timestamp < ?".formatted( - TABLE_MESSAGE_SEND_LOG_CONTENT))) { + final var sql = """ + DELETE FROM %s + WHERE timestamp < ? + """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT); + try (final var statement = connection.prepareStatement(sql)) { statement.setLong(1, System.currentTimeMillis() - LOG_DURATION.toMillis()); final var rowCount = statement.executeUpdate(); if (rowCount > 0) { @@ -361,9 +376,11 @@ public class MessageSendLogStore implements AutoCloseable { } private void deleteOrphanedLogContents(final Connection connection) throws SQLException { - try (final var statement = connection.prepareStatement( - "DELETE FROM %s WHERE _id NOT IN (SELECT content_id FROM %s)".formatted(TABLE_MESSAGE_SEND_LOG_CONTENT, - TABLE_MESSAGE_SEND_LOG))) { + final var sql = """ + DELETE FROM %s + WHERE _id NOT IN (SELECT content_id FROM %s) + """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT, TABLE_MESSAGE_SEND_LOG); + try (final var statement = connection.prepareStatement(sql)) { statement.executeUpdate(); } } From 8236696492cb29930c421e642d89d4bd48b7c3e5 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 7 Jun 2022 20:34:41 +0200 Subject: [PATCH 143/938] Fix typo --- .../manager/storage/senderKeys/SenderKeyRecordStore.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 0d9f6288..83302faf 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 @@ -6,6 +6,7 @@ import org.asamk.signal.manager.util.IOUtils; import org.signal.libsignal.protocol.InvalidMessageException; import org.signal.libsignal.protocol.SignalProtocolAddress; import org.signal.libsignal.protocol.groups.state.SenderKeyRecord; +import org.signal.libsignal.protocol.groups.state.SenderKeyStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,7 +24,7 @@ import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class SenderKeyRecordStore implements org.signal.libsignal.protocol.groups.state.SenderKeyStore { +public class SenderKeyRecordStore implements SenderKeyStore { private final static Logger logger = LoggerFactory.getLogger(SenderKeyRecordStore.class); @@ -138,7 +139,7 @@ public class SenderKeyRecordStore implements org.signal.libsignal.protocol.group } /** - * @param identifier can be either a serialized uuid or a e164 phone number + * @param identifier can be either a serialized uuid or an e164 phone number */ private RecipientId resolveRecipient(String identifier) { return resolver.resolveRecipient(identifier); From 936a68433def7a74d11a5fb17e0fac073dae77b9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 8 Jun 2022 15:21:24 +0200 Subject: [PATCH 144/938] Add additional logging for payment address parsing --- .../java/org/asamk/signal/manager/util/PaymentUtils.java | 6 ++++++ .../java/org/asamk/signal/manager/util/ProfileUtils.java | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) 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 6c34d9c9..1471d8fe 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 @@ -6,10 +6,14 @@ import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKeyPair; import org.signal.libsignal.protocol.ecc.ECPrivateKey; import org.signal.libsignal.protocol.ecc.ECPublicKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.internal.push.SignalServiceProtos; public class PaymentUtils { + private final static Logger logger = LoggerFactory.getLogger(PaymentUtils.class); + private PaymentUtils() { } @@ -37,6 +41,7 @@ public class PaymentUtils { SignalServiceProtos.PaymentAddress paymentAddress, ECPublicKey publicKey ) { if (!paymentAddress.hasMobileCoinAddress()) { + logger.debug("Got payment address without mobile coin address, ignoring."); return null; } @@ -44,6 +49,7 @@ public class PaymentUtils { byte[] signature = paymentAddress.getMobileCoinAddress().getSignature().toByteArray(); if (signature.length != 64 || !publicKey.verifySignature(bytes, signature)) { + logger.debug("Got mobile coin address with invalid signature, ignoring."); return null; } 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 e202f70a..5d61cab3 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 @@ -30,7 +30,8 @@ public class ProfileUtils { IdentityKey identityKey = null; try { identityKey = new IdentityKey(Base64.getDecoder().decode(encryptedProfile.getIdentityKey()), 0); - } catch (InvalidKeyException ignored) { + } catch (InvalidKeyException e) { + logger.debug("Failed to decode identity key in profile, can't verify payment address", e); } try { @@ -112,6 +113,7 @@ public class ProfileUtils { try { decrypted = profileCipher.decryptWithLength(encryptedPaymentAddress); } catch (IOException e) { + logger.debug("Failed to decrypt payment address", e); return null; } @@ -119,6 +121,7 @@ public class ProfileUtils { try { paymentAddress = SignalServiceProtos.PaymentAddress.parseFrom(decrypted); } catch (InvalidProtocolBufferException e) { + logger.debug("Failed to parse payment address", e); return null; } From 0e65e67077f36932fe9ed1471d41502ca6e1ffe3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 8 Jun 2022 17:26:22 +0200 Subject: [PATCH 145/938] Log more detailed provisioning errors --- .../org/asamk/signal/manager/ProvisioningManagerImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java index 0c617646..e405f54c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java @@ -161,7 +161,7 @@ class ProvisioningManagerImpl implements ProvisioningManager { try { m.refreshPreKeys(); } catch (Exception e) { - logger.error("Failed to refresh pre keys."); + logger.error("Failed to refresh pre keys.", e); } logger.debug("Requesting sync data"); @@ -169,7 +169,8 @@ class ProvisioningManagerImpl implements ProvisioningManager { m.requestAllSyncData(); } catch (Exception e) { logger.error( - "Failed to request sync messages from linked device, data can be requested again with `sendSyncRequest`."); + "Failed to request sync messages from linked device, data can be requested again with `sendSyncRequest`.", + e); } if (newManagerListener != null) { From 7bf06aef5ecea8856aeebc5c9d3a67d11cb73b92 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 8 Jun 2022 17:26:37 +0200 Subject: [PATCH 146/938] Store self profile key in profile store after linking --- .../asamk/signal/manager/storage/SignalAccount.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 f66d3e4a..6d636359 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 @@ -313,6 +313,11 @@ public class SignalAccount implements Closeable { final var pair = openFileChannel(fileName, true); var signalAccount = new SignalAccount(pair.first(), pair.second()); + signalAccount.dataPath = dataPath; + signalAccount.accountPath = accountPath; + signalAccount.serviceEnvironment = serviceEnvironment; + signalAccount.localRegistrationId = registrationId; + signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.setProvisioningData(number, aci, pni, @@ -323,11 +328,6 @@ public class SignalAccount implements Closeable { pniIdentityKey, profileKey); - signalAccount.dataPath = dataPath; - signalAccount.accountPath = accountPath; - signalAccount.serviceEnvironment = serviceEnvironment; - signalAccount.localRegistrationId = registrationId; - signalAccount.trustNewIdentity = trustNewIdentity; signalAccount.groupStore = new GroupStore(getGroupCachePath(dataPath, accountPath), signalAccount.getRecipientResolver(), signalAccount::saveGroupStore); @@ -360,6 +360,7 @@ public class SignalAccount implements Closeable { this.pni = pni; this.password = password; this.profileKey = profileKey; + getProfileStore().storeSelfProfileKey(getSelfRecipientId(), getProfileKey()); this.encryptedDeviceName = encryptedDeviceName; this.deviceId = deviceId; this.aciIdentityKeyPair = aciIdentity; From c8cd36bde884fe03004dfa05357eac34394b4b01 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 8 Jun 2022 17:50:20 +0200 Subject: [PATCH 147/938] Unsubscribe receive if jsonRpcSender channel is closed --- .../java/org/asamk/signal/manager/ManagerImpl.java | 3 ++- .../signal/jsonrpc/SignalJsonRpcDispatcherHandler.java | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index dc7e743e..fcc50e09 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -860,7 +860,8 @@ class ManagerImpl implements Manager { logger.debug("Starting receiving messages"); context.getReceiveHelper().receiveMessagesContinuously((envelope, e) -> { synchronized (messageHandlers) { - Stream.concat(messageHandlers.stream(), weakHandlers.stream()).forEach(h -> { + final var handlers = Stream.concat(messageHandlers.stream(), weakHandlers.stream()).toList(); + handlers.forEach(h -> { try { h.handleMessage(envelope, e); } catch (Throwable ex) { diff --git a/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java b/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java index 086681f7..181233bc 100644 --- a/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java +++ b/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java @@ -30,6 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.nio.channels.ClosedChannelException; import java.nio.channels.OverlappingFileLockException; import java.util.HashMap; import java.util.List; @@ -101,7 +102,14 @@ public class SignalJsonRpcDispatcherHandler { final var receiveMessageHandler = new JsonReceiveMessageHandler(m, s -> { final ContainerNode params = objectMapper.valueToTree(s); ((ObjectNode) params).set("subscription", IntNode.valueOf(subscriptionId)); - jsonRpcSender.sendRequest(JsonRpcRequest.forNotification("receive", params, null)); + final var jsonRpcRequest = JsonRpcRequest.forNotification("receive", params, null); + try { + jsonRpcSender.sendRequest(jsonRpcRequest); + } catch (AssertionError e) { + if (e.getCause() instanceof ClosedChannelException) { + unsubscribeReceive(subscriptionId); + } + } }); m.addReceiveHandler(receiveMessageHandler); return new Pair<>(m, (Manager.ReceiveMessageHandler) receiveMessageHandler); From 26620f3137bc7a3aa5a5e05510cfdac6c6fb3a32 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 9 Jun 2022 15:37:21 +0200 Subject: [PATCH 148/938] Refactor identity key store --- .../org/asamk/signal/manager/ManagerImpl.java | 2 +- .../signal/manager/helper/IdentityHelper.java | 5 +- .../signal/manager/helper/ProfileHelper.java | 3 +- .../signal/manager/helper/SendHelper.java | 2 +- .../signal/manager/helper/StorageHelper.java | 3 +- .../signal/manager/helper/SyncHelper.java | 2 +- .../signal/manager/storage/SignalAccount.java | 17 +++-- .../storage/identities/IdentityKeyStore.java | 60 ++++------------- .../identities/SignalIdentityKeyStore.java | 66 +++++++++++++++++++ 9 files changed, 99 insertions(+), 61 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index fcc50e09..201f0931 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -1049,7 +1049,7 @@ class ManagerImpl implements Manager { IdentityInfo identity; try { identity = account.getIdentityKeyStore() - .getIdentity(context.getRecipientHelper().resolveRecipient(recipient)); + .getIdentityInfo(context.getRecipientHelper().resolveRecipient(recipient)); } catch (UnregisteredRecipientException e) { identity = null; } 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 ee74338e..ce397353 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 @@ -16,7 +16,6 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.IOException; import java.util.Arrays; -import java.util.Date; import java.util.function.Function; import static org.asamk.signal.manager.config.ServiceConfig.capabilities; @@ -85,7 +84,7 @@ public class IdentityHelper { private boolean trustIdentity( RecipientId recipientId, Function verifier, TrustLevel trustLevel ) { - var identity = account.getIdentityKeyStore().getIdentity(recipientId); + var identity = account.getIdentityKeyStore().getIdentityInfo(recipientId); if (identity == null) { return false; } @@ -110,7 +109,7 @@ public class IdentityHelper { ) { final var identityKey = identityFailure.getIdentityKey(); if (identityKey != null) { - account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date()); + account.getIdentityKeyStore().saveIdentity(recipientId, identityKey); } else { // Retrieve profile to get the current identity key from the server context.getProfileHelper().refreshRecipientProfile(recipientId); 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 812e4b3c..a3f3b480 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 @@ -35,7 +35,6 @@ import java.io.OutputStream; import java.nio.file.Files; import java.util.Base64; import java.util.Collection; -import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -354,7 +353,7 @@ public final class ProfileHelper { try { logger.trace("Storing identity"); final var identityKey = new IdentityKey(Base64.getDecoder().decode(encryptedProfile.getIdentityKey())); - account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date()); + account.getIdentityKeyStore().saveIdentity(recipientId, identityKey); } catch (InvalidKeyException ignored) { logger.warn("Got invalid identity key in profile for {}", context.getRecipientHelper().resolveSignalServiceAddress(recipientId).getIdentifier()); 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 758fe1b9..e0f2f161 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 @@ -477,7 +477,7 @@ public class SendHelper { continue; } - final var identity = account.getIdentityKeyStore().getIdentity(recipientId); + final var identity = account.getIdentityKeyStore().getIdentityInfo(recipientId); if (identity == null || !identity.getTrustLevel().isTrusted()) { continue; } 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 281901c1..84d98db2 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 @@ -24,7 +24,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -127,7 +126,7 @@ public class StorageHelper { try { logger.trace("Storing identity key {}", recipientId); final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get()); - account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date()); + account.getIdentityKeyStore().saveIdentity(recipientId, identityKey); final var trustLevel = TrustLevel.fromIdentityState(contactRecord.getIdentityState()); if (trustLevel != null) { 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 4e77d738..3ad1fda8 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 @@ -128,7 +128,7 @@ public class SyncHelper { final var contact = contactPair.second(); final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId); - var currentIdentity = account.getIdentityKeyStore().getIdentity(recipientId); + var currentIdentity = account.getIdentityKeyStore().getIdentityInfo(recipientId); VerifiedMessage verifiedMessage = null; if (currentIdentity != null) { verifiedMessage = new VerifiedMessage(address, 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 6d636359..a66af784 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 @@ -15,6 +15,7 @@ import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.GroupInfoV2; import org.asamk.signal.manager.storage.groups.GroupStore; import org.asamk.signal.manager.storage.identities.IdentityKeyStore; +import org.asamk.signal.manager.storage.identities.SignalIdentityKeyStore; import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.manager.storage.messageCache.MessageCache; import org.asamk.signal.manager.storage.prekeys.PreKeyStore; @@ -81,7 +82,6 @@ import java.security.SecureRandom; import java.sql.SQLException; import java.util.Base64; import java.util.Comparator; -import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -137,6 +137,7 @@ public class SignalAccount implements Closeable { private SignedPreKeyStore pniSignedPreKeyStore; private SessionStore sessionStore; private IdentityKeyStore identityKeyStore; + private SignalIdentityKeyStore aciIdentityKeyStore; private SenderKeyStore senderKeyStore; private GroupStore groupStore; private GroupStore.Storage groupStoreStorage; @@ -1016,7 +1017,7 @@ public class SignalAccount implements Closeable { () -> signalProtocolStore = new SignalProtocolStore(getAciPreKeyStore(), getAciSignedPreKeyStore(), getSessionStore(), - getIdentityKeyStore(), + getAciIdentityKeyStore(), getSenderKeyStore(), this::isMultiDevice)); } @@ -1050,11 +1051,17 @@ public class SignalAccount implements Closeable { return getOrCreate(() -> identityKeyStore, () -> identityKeyStore = new IdentityKeyStore(getIdentitiesPath(dataPath, accountPath), getRecipientResolver(), - aciIdentityKeyPair, - localRegistrationId, trustNewIdentity)); } + public SignalIdentityKeyStore getAciIdentityKeyStore() { + return getOrCreate(() -> aciIdentityKeyStore, + () -> aciIdentityKeyStore = new SignalIdentityKeyStore(getRecipientResolver(), + () -> aciIdentityKeyPair, + localRegistrationId, + getIdentityKeyStore())); + } + public GroupStore getGroupStore() { return groupStore; } @@ -1390,7 +1397,7 @@ public class SignalAccount implements Closeable { getSenderKeyStore().deleteAll(); final var recipientId = getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress()); final var publicKey = getAciIdentityKeyPair().getPublicKey(); - getIdentityKeyStore().saveIdentity(recipientId, publicKey, new Date()); + getIdentityKeyStore().saveIdentity(recipientId, publicKey); getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED); } 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 d9c553b7..4d08f89f 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 @@ -7,9 +7,8 @@ import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientResolver; import org.asamk.signal.manager.util.IOUtils; import org.signal.libsignal.protocol.IdentityKey; -import org.signal.libsignal.protocol.IdentityKeyPair; import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.SignalProtocolAddress; +import org.signal.libsignal.protocol.state.IdentityKeyStore.Direction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,7 +31,7 @@ import java.util.regex.Pattern; import io.reactivex.rxjava3.subjects.PublishSubject; import io.reactivex.rxjava3.subjects.Subject; -public class IdentityKeyStore implements org.signal.libsignal.protocol.state.IdentityKeyStore { +public class IdentityKeyStore { private final static Logger logger = LoggerFactory.getLogger(IdentityKeyStore.class); private final ObjectMapper objectMapper = org.asamk.signal.manager.storage.Utils.createStorageObjectMapper(); @@ -42,24 +41,16 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide private final File identitiesPath; private final RecipientResolver resolver; - private final IdentityKeyPair identityKeyPair; - private final int localRegistrationId; private final TrustNewIdentity trustNewIdentity; private final PublishSubject identityChanges = PublishSubject.create(); private boolean isRetryingDecryption = false; public IdentityKeyStore( - final File identitiesPath, - final RecipientResolver resolver, - final IdentityKeyPair identityKeyPair, - final int localRegistrationId, - final TrustNewIdentity trustNewIdentity + final File identitiesPath, final RecipientResolver resolver, final TrustNewIdentity trustNewIdentity ) { this.identitiesPath = identitiesPath; this.resolver = resolver; - this.identityKeyPair = identityKeyPair; - this.localRegistrationId = localRegistrationId; this.trustNewIdentity = trustNewIdentity; } @@ -67,21 +58,8 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide return identityChanges; } - @Override - public IdentityKeyPair getIdentityKeyPair() { - return identityKeyPair; - } - - @Override - public int getLocalRegistrationId() { - return localRegistrationId; - } - - @Override - public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { - final var recipientId = resolveRecipient(address.getName()); - - return saveIdentity(recipientId, identityKey, new Date()); + public boolean saveIdentity(final RecipientId recipientId, final IdentityKey identityKey) { + return saveIdentity(recipientId, identityKey, null); } public boolean saveIdentity(final RecipientId recipientId, final IdentityKey identityKey, Date added) { @@ -100,7 +78,10 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide trustNewIdentity == TrustNewIdentity.ON_FIRST_USE && identityInfo == null ) ? TrustLevel.TRUSTED_UNVERIFIED : TrustLevel.UNTRUSTED; logger.debug("Storing new identity for recipient {} with trust {}", recipientId, trustLevel); - final var newIdentityInfo = new IdentityInfo(recipientId, identityKey, trustLevel, added); + final var newIdentityInfo = new IdentityInfo(recipientId, + identityKey, + trustLevel, + added == null ? new Date() : added); storeIdentityLocked(recipientId, newIdentityInfo); identityChanges.onNext(recipientId); return true; @@ -137,26 +118,23 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide } } - @Override - public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { + public boolean isTrustedIdentity(RecipientId recipientId, IdentityKey identityKey, Direction direction) { if (trustNewIdentity == TrustNewIdentity.ALWAYS) { return true; } - var recipientId = resolveRecipient(address.getName()); - synchronized (cachedIdentities) { // TODO implement possibility for different handling of incoming/outgoing trust decisions var identityInfo = loadIdentityLocked(recipientId); if (identityInfo == null) { logger.debug("Initial identity found for {}, saving.", recipientId); - saveIdentity(address, identityKey); + saveIdentity(recipientId, identityKey); identityInfo = loadIdentityLocked(recipientId); } else if (!identityInfo.getIdentityKey().equals(identityKey)) { // Identity found, but different if (direction == Direction.SENDING) { logger.debug("Changed identity found for {}, saving.", recipientId); - saveIdentity(address, identityKey); + saveIdentity(recipientId, identityKey); identityInfo = loadIdentityLocked(recipientId); } else { logger.trace("Trusting identity for {} for {}: {}", recipientId, direction, false); @@ -170,17 +148,14 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide } } - @Override - public IdentityKey getIdentity(SignalProtocolAddress address) { - var recipientId = resolveRecipient(address.getName()); - + public IdentityKey getIdentity(RecipientId recipientId) { synchronized (cachedIdentities) { var identity = loadIdentityLocked(recipientId); return identity == null ? null : identity.getIdentityKey(); } } - public IdentityInfo getIdentity(RecipientId recipientId) { + public IdentityInfo getIdentityInfo(RecipientId recipientId) { synchronized (cachedIdentities) { return loadIdentityLocked(recipientId); } @@ -214,13 +189,6 @@ public class IdentityKeyStore implements org.signal.libsignal.protocol.state.Ide } } - /** - * @param identifier can be either a serialized uuid or a e164 phone number - */ - private RecipientId resolveRecipient(String identifier) { - return resolver.resolveRecipient(identifier); - } - private File getIdentityFile(final RecipientId recipientId) { try { IOUtils.createPrivateDirectories(identitiesPath); 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 new file mode 100644 index 00000000..06050875 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java @@ -0,0 +1,66 @@ +package org.asamk.signal.manager.storage.identities; + +import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.asamk.signal.manager.storage.recipients.RecipientResolver; +import org.signal.libsignal.protocol.IdentityKey; +import org.signal.libsignal.protocol.IdentityKeyPair; +import org.signal.libsignal.protocol.SignalProtocolAddress; + +import java.util.function.Supplier; + +public class SignalIdentityKeyStore implements org.signal.libsignal.protocol.state.IdentityKeyStore { + + private final RecipientResolver resolver; + private final Supplier identityKeyPairSupplier; + private final int localRegistrationId; + private final IdentityKeyStore identityKeyStore; + + public SignalIdentityKeyStore( + final RecipientResolver resolver, + final Supplier identityKeyPairSupplier, + final int localRegistrationId, + final IdentityKeyStore identityKeyStore + ) { + this.resolver = resolver; + this.identityKeyPairSupplier = identityKeyPairSupplier; + this.localRegistrationId = localRegistrationId; + this.identityKeyStore = identityKeyStore; + } + + @Override + public IdentityKeyPair getIdentityKeyPair() { + return identityKeyPairSupplier.get(); + } + + @Override + public int getLocalRegistrationId() { + return localRegistrationId; + } + + @Override + public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) { + final var recipientId = resolveRecipient(address.getName()); + + return identityKeyStore.saveIdentity(recipientId, identityKey); + } + + @Override + public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { + var recipientId = resolveRecipient(address.getName()); + + return identityKeyStore.isTrustedIdentity(recipientId, identityKey, direction); + } + + @Override + public IdentityKey getIdentity(SignalProtocolAddress address) { + var recipientId = resolveRecipient(address.getName()); + return identityKeyStore.getIdentity(recipientId); + } + + /** + * @param identifier can be either a serialized uuid or an e164 phone number + */ + private RecipientId resolveRecipient(String identifier) { + return resolver.resolveRecipient(identifier); + } +} From 7bb690e58e56ab81ad85f811e438f8024c6cc495 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 11 Jun 2022 12:34:30 +0200 Subject: [PATCH 149/938] Update libsignal-service-java --- lib/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index cad9ca8b..567afd19 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_49") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_50") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.3") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") From e9e66e1005ce39684d9d128933bb7edaf99d8de1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 10 Jun 2022 10:34:37 +0200 Subject: [PATCH 150/938] Handle groups sync message again --- .../helper/IncomingMessageHandler.java | 8 +++- .../signal/manager/helper/SyncHelper.java | 46 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) 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 b6e3d8cb..4940e67c 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 @@ -367,7 +367,13 @@ public final class IncomingMessageHandler { } } if (syncMessage.getGroups().isPresent()) { - logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring."); + try { + final var groupsMessage = syncMessage.getGroups().get(); + context.getAttachmentHelper() + .retrieveAttachment(groupsMessage, context.getSyncHelper()::handleSyncDeviceGroups); + } catch (Exception e) { + logger.warn("Failed to handle received sync groups, ignoring: {}", e.getMessage()); + } } if (syncMessage.getBlockedList().isPresent()) { final var blockedListMessage = syncMessage.getBlockedList().get(); 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 3ad1fda8..9a5b5a7d 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 @@ -3,6 +3,7 @@ package org.asamk.signal.manager.helper; import com.google.protobuf.ByteString; import org.asamk.signal.manager.api.TrustLevel; +import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.recipients.Contact; @@ -21,6 +22,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact; import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup; +import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage; import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; @@ -36,7 +38,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; public class SyncHelper { @@ -237,6 +241,48 @@ public class SyncHelper { context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forPniIdentity(pniIdentity)); } + public void handleSyncDeviceGroups(final InputStream input) { + final var s = new DeviceGroupsInputStream(input); + DeviceGroup g; + while (true) { + try { + g = s.read(); + } catch (IOException e) { + logger.warn("Sync groups contained invalid group, ignoring: {}", e.getMessage()); + continue; + } + if (g == null) { + break; + } + var syncGroup = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(g.getId())); + if (syncGroup != null) { + if (g.getName().isPresent()) { + syncGroup.name = g.getName().get(); + } + syncGroup.addMembers(g.getMembers() + .stream() + .map(account.getRecipientResolver()::resolveRecipient) + .collect(Collectors.toSet())); + if (!g.isActive()) { + syncGroup.removeMember(account.getSelfRecipientId()); + } else { + // Add ourself to the member set as it's marked as active + syncGroup.addMembers(List.of(account.getSelfRecipientId())); + } + syncGroup.blocked = g.isBlocked(); + if (g.getColor().isPresent()) { + syncGroup.color = g.getColor().get(); + } + + if (g.getAvatar().isPresent()) { + context.getGroupHelper().downloadGroupAvatar(syncGroup.getGroupId(), g.getAvatar().get()); + } + syncGroup.archived = g.isArchived(); + account.getGroupStore().updateGroup(syncGroup); + } + } + } + public void handleSyncDeviceContacts(final InputStream input) throws IOException { final var s = new DeviceContactsInputStream(input); DeviceContact c; From 0b63e78d2b80cd96962152393c0130046d490b54 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 11 Jun 2022 18:17:15 +0200 Subject: [PATCH 151/938] Add logging to test script --- run_tests.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/run_tests.sh b/run_tests.sh index 94eb98d9..69a7d382 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -20,8 +20,8 @@ if [ "$NATIVE" -eq 1 ]; then SIGNAL_CLI="$PWD/build/native/nativeCompile/signal-cli" elif [ "$JSON_RPC" -eq 1 ]; then (cd client && cargo build) - "$PWD/build/install/signal-cli/bin/signal-cli" --verbose --verbose --trust-new-identities=always --config="$PATH_MAIN" --service-environment="staging" daemon --socket --receive-mode=manual& - "$PWD/build/install/signal-cli/bin/signal-cli" --verbose --verbose --trust-new-identities=always --config="$PATH_LINK" --service-environment="staging" daemon --tcp --receive-mode=manual& + "$PWD/build/install/signal-cli/bin/signal-cli" --verbose --verbose --trust-new-identities=always --config="$PATH_MAIN" --service-environment="staging" --log-file="$PATH_MAIN/log" daemon --socket --receive-mode=manual& + "$PWD/build/install/signal-cli/bin/signal-cli" --verbose --verbose --trust-new-identities=always --config="$PATH_LINK" --service-environment="staging" --log-file="$PATH_LINK/log" daemon --tcp --receive-mode=manual& sleep 5 SIGNAL_CLI="$PWD/client/target/debug/signal-cli-client" else @@ -41,7 +41,7 @@ run() { if [ "$JSON_RPC" -eq 1 ]; then "$SIGNAL_CLI" $@ else - "$SIGNAL_CLI" --service-environment="staging" $@ + "$SIGNAL_CLI" --service-environment="staging" --verbose --verbose $@ fi set +x } @@ -51,7 +51,7 @@ run_main() { if [ "$JSON_RPC" -eq 1 ]; then run --json-rpc-socket="$XDG_RUNTIME_DIR/signal-cli/socket" $@ else - run --config="$PATH_MAIN" $@ + run --config="$PATH_MAIN" --log-file="$PATH_MAIN/log" $@ fi unset SIGNAL_CLI_AGENT_ID } @@ -61,7 +61,7 @@ run_linked() { if [ "$JSON_RPC" -eq 1 ]; then run --json-rpc-tcp="127.0.0.1:7583" $@ else - run --config="$PATH_LINK" $@ + run --config="$PATH_LINK" --log-file="$PATH_LINK/log" $@ fi unset SIGNAL_CLI_AGENT_ID } @@ -177,14 +177,14 @@ run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" -r "$NUMBER_2" run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2" run_main -a "$NUMBER_1" listGroups -d run_main -a "$NUMBER_1" --output=json listGroups -d -run_main -a "$NUMBER_2" --verbose receive +run_main -a "$NUMBER_2" receive run_main -a "$NUMBER_2" quitGroup -g "$GROUP_ID" run_main -a "$NUMBER_2" listGroups -d run_main -a "$NUMBER_2" --output=json listGroups -d run_main -a "$NUMBER_1" receive run_main -a "$NUMBER_1" updateGroup -g "$GROUP_ID" -m "$NUMBER_2" -run_main -a "$NUMBER_1" --verbose block -g "$GROUP_ID" -run_main -a "$NUMBER_1" --verbose unblock -g "$GROUP_ID" +run_main -a "$NUMBER_1" block -g "$GROUP_ID" +run_main -a "$NUMBER_1" unblock -g "$GROUP_ID" ## Configuration run_main -a "$NUMBER_1" updateConfiguration --read-receipts=true From c487929bcd505a76b4ee445a0765079184865e8b Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 11 Jun 2022 18:36:53 +0200 Subject: [PATCH 152/938] Make version command work on the command line --- .../asamk/signal/commands/VersionCommand.java | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/asamk/signal/commands/VersionCommand.java b/src/main/java/org/asamk/signal/commands/VersionCommand.java index 95b41598..52099ff3 100644 --- a/src/main/java/org/asamk/signal/commands/VersionCommand.java +++ b/src/main/java/org/asamk/signal/commands/VersionCommand.java @@ -1,14 +1,23 @@ package org.asamk.signal.commands; +import com.fasterxml.jackson.core.type.TypeReference; + +import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; + import org.asamk.signal.BaseConfig; +import org.asamk.signal.OutputType; import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.MultiAccountManager; import org.asamk.signal.output.JsonWriter; +import org.asamk.signal.output.OutputWriter; +import org.asamk.signal.output.PlainTextWriter; +import java.util.List; import java.util.Map; -public class VersionCommand implements JsonRpcSingleCommand, JsonRpcMultiCommand { +public class VersionCommand implements JsonRpcLocalCommand, JsonRpcMultiLocalCommand { @Override public String getName() { @@ -16,21 +25,41 @@ public class VersionCommand implements JsonRpcSingleCommand, JsonRpcMultiC } @Override - public void handleCommand( - final Void request, final Manager m, final JsonWriter jsonWriter - ) throws CommandException { - outputVersion(jsonWriter); + public List getSupportedOutputTypes() { + return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); + } + + @Override + public void attachToSubparser(final Subparser subparser) { } @Override public void handleCommand( - final Void request, final MultiAccountManager c, final JsonWriter jsonWriter + final Namespace ns, final Manager m, final OutputWriter outputWriter ) throws CommandException { - outputVersion(jsonWriter); + outputVersion(outputWriter); } - private void outputVersion(final JsonWriter jsonWriter) { - jsonWriter.write(Map.of("version", - BaseConfig.PROJECT_VERSION == null ? "unknown" : BaseConfig.PROJECT_VERSION)); + @Override + public void handleCommand( + final Namespace ns, final MultiAccountManager c, final OutputWriter outputWriter + ) throws CommandException { + outputVersion(outputWriter); + } + + @Override + public TypeReference> getRequestType() { + return new TypeReference<>() {}; + } + + private void outputVersion(final OutputWriter outputWriter) { + final var projectName = BaseConfig.PROJECT_NAME == null ? "signal-cli" : BaseConfig.PROJECT_NAME; + final var version = BaseConfig.PROJECT_VERSION == null ? "unknown" : BaseConfig.PROJECT_VERSION; + + if (outputWriter instanceof JsonWriter jsonWriter) { + jsonWriter.write(Map.of("version", version)); + } else if (outputWriter instanceof PlainTextWriter plainTextWriter) { + plainTextWriter.println("{} {}", projectName, version); + } } } From aaa6412469c0af93a226ba805e6dbe74b4ea9773 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 11 Jun 2022 22:45:51 +0200 Subject: [PATCH 153/938] Allow registering new accounts on both live and staging environments in the same config directory --- graalvm-config-dir/reflect-config.json | 8 +- .../signal/manager/SignalAccountFiles.java | 17 ++- .../storage/accounts/AccountsStorage.java | 4 +- .../storage/accounts/AccountsStore.java | 132 ++++++++++++++---- src/main/java/org/asamk/signal/App.java | 10 +- 5 files changed, 136 insertions(+), 35 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 2c2c94f9..54ed49f6 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -914,8 +914,9 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ - {"name":"","parameterTypes":["java.util.List"] }, - {"name":"accounts","parameterTypes":[] } + {"name":"","parameterTypes":["java.util.List","java.lang.Integer"] }, + {"name":"accounts","parameterTypes":[] }, + {"name":"version","parameterTypes":[] } ] }, { @@ -924,7 +925,8 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ - {"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, + {"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, + {"name":"environment","parameterTypes":[] }, {"name":"number","parameterTypes":[] }, {"name":"path","parameterTypes":[] }, {"name":"uuid","parameterTypes":[] } 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 3821a306..f397b534 100644 --- a/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java +++ b/lib/src/main/java/org/asamk/signal/manager/SignalAccountFiles.java @@ -41,14 +41,24 @@ public class SignalAccountFiles { this.serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(this.serviceEnvironment, userAgent); this.userAgent = userAgent; this.trustNewIdentity = trustNewIdentity; - this.accountsStore = new AccountsStore(pathConfig.dataPath()); + this.accountsStore = new AccountsStore(pathConfig.dataPath(), serviceEnvironment, accountPath -> { + if (accountPath == null || !SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath)) { + return null; + } + + try { + return SignalAccount.load(pathConfig.dataPath(), accountPath, false, trustNewIdentity); + } catch (Exception e) { + return null; + } + }); } - public Set getAllLocalAccountNumbers() { + public Set getAllLocalAccountNumbers() throws IOException { return accountsStore.getAllNumbers(); } - public MultiAccountManager initMultiAccountManager() { + public MultiAccountManager initMultiAccountManager() throws IOException { final var managers = accountsStore.getAllAccounts().parallelStream().map(a -> { try { return initManager(a.number(), a.path()); @@ -108,6 +118,7 @@ public class SignalAccountFiles { if (account.getServiceEnvironment() == null) { account.setServiceEnvironment(serviceEnvironment); + accountsStore.updateAccount(accountPath, account.getNumber(), account.getAci()); } return manager; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java index 7507c339..40107986 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/accounts/AccountsStorage.java @@ -2,7 +2,7 @@ package org.asamk.signal.manager.storage.accounts; import java.util.List; -public record AccountsStorage(List accounts) { +public record AccountsStorage(List accounts, Integer version) { - public record Account(String path, String number, String uuid) {} + public record Account(String path, String environment, String number, String uuid) {} } 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 ea2f0a1b..d708a41c 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 @@ -3,6 +3,8 @@ package org.asamk.signal.manager.storage.accounts; import com.fasterxml.jackson.databind.ObjectMapper; import org.asamk.signal.manager.api.Pair; +import org.asamk.signal.manager.config.ServiceEnvironment; +import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.Utils; import org.asamk.signal.manager.util.IOUtils; import org.slf4j.Logger; @@ -29,39 +31,52 @@ import java.util.stream.Stream; public class AccountsStore { + private static final int MINIMUM_STORAGE_VERSION = 1; + private static final int CURRENT_STORAGE_VERSION = 2; private final static Logger logger = LoggerFactory.getLogger(AccountsStore.class); private final ObjectMapper objectMapper = Utils.createStorageObjectMapper(); private final File dataPath; + private final String serviceEnvironment; + private final AccountLoader accountLoader; - public AccountsStore(final File dataPath) throws IOException { + public AccountsStore( + final File dataPath, final ServiceEnvironment serviceEnvironment, final AccountLoader accountLoader + ) throws IOException { this.dataPath = dataPath; + this.serviceEnvironment = getServiceEnvironmentString(serviceEnvironment); + this.accountLoader = accountLoader; if (!getAccountsFile().exists()) { createInitialAccounts(); } } - public synchronized Set getAllNumbers() { + public synchronized Set getAllNumbers() throws IOException { return readAccounts().stream() .map(AccountsStorage.Account::number) .filter(Objects::nonNull) .collect(Collectors.toSet()); } - public synchronized Set getAllAccounts() { - return readAccounts().stream().filter(a -> a.number() != null).collect(Collectors.toSet()); + public synchronized Set getAllAccounts() throws IOException { + return readAccounts().stream() + .filter(a -> a.environment() == null || serviceEnvironment.equals(a.environment())) + .filter(a -> a.number() != null) + .collect(Collectors.toSet()); } - public synchronized String getPathByNumber(String number) { + public synchronized String getPathByNumber(String number) throws IOException { return readAccounts().stream() + .filter(a -> a.environment() == null || serviceEnvironment.equals(a.environment())) .filter(a -> number.equals(a.number())) .map(AccountsStorage.Account::path) .findFirst() .orElse(null); } - public synchronized String getPathByAci(ACI aci) { + public synchronized String getPathByAci(ACI aci) throws IOException { return readAccounts().stream() + .filter(a -> a.environment() == null || serviceEnvironment.equals(a.environment())) .filter(a -> aci.toString().equals(a.uuid())) .map(AccountsStorage.Account::path) .findFirst() @@ -70,15 +85,22 @@ public class AccountsStore { public synchronized void updateAccount(String path, String number, ACI aci) { updateAccounts(accounts -> accounts.stream().map(a -> { + if (a.environment() != null && !serviceEnvironment.equals(a.environment())) { + return a; + } + if (path.equals(a.path())) { - return new AccountsStorage.Account(a.path(), number, aci == null ? null : aci.toString()); + return new AccountsStorage.Account(a.path(), + serviceEnvironment, + number, + aci == null ? null : aci.toString()); } if (number != null && number.equals(a.number())) { - return new AccountsStorage.Account(a.path(), null, a.uuid()); + return new AccountsStorage.Account(a.path(), a.environment(), null, a.uuid()); } if (aci != null && aci.toString().equals(a.toString())) { - return new AccountsStorage.Account(a.path(), a.number(), null); + return new AccountsStorage.Account(a.path(), a.environment(), a.number(), null); } return a; @@ -87,14 +109,21 @@ public class AccountsStore { public synchronized String addAccount(String number, ACI aci) { final var accountPath = generateNewAccountPath(); - final var account = new AccountsStorage.Account(accountPath, number, aci == null ? null : aci.toString()); + final var account = new AccountsStorage.Account(accountPath, + serviceEnvironment, + number, + aci == null ? null : aci.toString()); updateAccounts(accounts -> { final var existingAccounts = accounts.stream().map(a -> { - if (number != null && number.equals(a.number())) { - return new AccountsStorage.Account(a.path(), null, a.uuid()); + if (a.environment() != null && !serviceEnvironment.equals(a.environment())) { + return a; } - if (aci != null && aci.toString().equals(a.toString())) { - return new AccountsStorage.Account(a.path(), a.number(), null); + + if (number != null && number.equals(a.number())) { + return new AccountsStorage.Account(a.path(), a.environment(), null, a.uuid()); + } + if (aci != null && aci.toString().equals(a.uuid())) { + return new AccountsStorage.Account(a.path(), a.environment(), a.number(), null); } return a; @@ -105,7 +134,9 @@ public class AccountsStore { } public void removeAccount(final String accountPath) { - updateAccounts(accounts -> accounts.stream().filter(a -> !a.path().equals(accountPath)).toList()); + updateAccounts(accounts -> accounts.stream().filter(a -> !( + (a.environment() == null || serviceEnvironment.equals(a.environment())) && a.path().equals(accountPath) + )).toList()); } private String generateNewAccountPath() { @@ -123,8 +154,8 @@ public class AccountsStore { private void createInitialAccounts() throws IOException { final var legacyAccountPaths = getLegacyAccountPaths(); final var accountsStorage = new AccountsStorage(legacyAccountPaths.stream() - .map(number -> new AccountsStorage.Account(number, number, null)) - .toList()); + .map(number -> new AccountsStorage.Account(number, null, number, null)) + .toList(), CURRENT_STORAGE_VERSION); IOUtils.createPrivateDirectories(dataPath); var fileName = getAccountsFile(); @@ -152,15 +183,52 @@ public class AccountsStore { .collect(Collectors.toSet()); } - private List readAccounts() { - try { - final var pair = openFileChannel(getAccountsFile()); - try (final var fileChannel = pair.first(); final var lock = pair.second()) { - return readAccountsLocked(fileChannel).accounts(); + private List readAccounts() throws IOException { + final var pair = openFileChannel(getAccountsFile()); + try (final var fileChannel = pair.first(); final var lock = pair.second()) { + final var storage = readAccountsLocked(fileChannel); + + var accountsVersion = storage.version() == null ? 1 : storage.version(); + if (accountsVersion > CURRENT_STORAGE_VERSION) { + throw new IOException("Accounts file was created by a more recent version: " + accountsVersion); + } else if (accountsVersion < MINIMUM_STORAGE_VERSION) { + throw new IOException("Accounts file was created by a no longer supported older version: " + + accountsVersion); + } else if (accountsVersion < CURRENT_STORAGE_VERSION) { + return upgradeAccountsFile(fileChannel, storage, accountsVersion).accounts(); } - } catch (IOException e) { - logger.error("Failed to read accounts list", e); - return List.of(); + return storage.accounts(); + } + } + + private AccountsStorage upgradeAccountsFile( + final FileChannel fileChannel, final AccountsStorage storage, final int accountsVersion + ) { + try { + List newAccounts = storage.accounts(); + if (accountsVersion < 2) { + // add environment field + newAccounts = newAccounts.stream().map(a -> { + if (a.environment() != null) { + return a; + } + try (final var account = accountLoader.loadAccountOrNull(a.path())) { + if (account == null || account.getServiceEnvironment() == null) { + return a; + } + return new AccountsStorage.Account(a.path(), + getServiceEnvironmentString(account.getServiceEnvironment()), + a.number(), + a.uuid()); + } + }).toList(); + } + final var newStorage = new AccountsStorage(newAccounts, CURRENT_STORAGE_VERSION); + saveAccountsLocked(fileChannel, newStorage); + return newStorage; + } catch (Exception e) { + logger.warn("Failed to upgrade accounts file", e); + return storage; } } @@ -170,7 +238,7 @@ public class AccountsStore { try (final var fileChannel = pair.first(); final var lock = pair.second()) { final var accountsStorage = readAccountsLocked(fileChannel); final var newAccountsStorage = updater.apply(accountsStorage.accounts()); - saveAccountsLocked(fileChannel, new AccountsStorage(newAccountsStorage)); + saveAccountsLocked(fileChannel, new AccountsStorage(newAccountsStorage, CURRENT_STORAGE_VERSION)); } } catch (IOException e) { logger.error("Failed to update accounts list", e); @@ -209,4 +277,16 @@ public class AccountsStore { } return new Pair<>(fileChannel, lock); } + + private String getServiceEnvironmentString(final ServiceEnvironment serviceEnvironment) { + return switch (serviceEnvironment) { + case LIVE -> "LIVE"; + case STAGING -> "STAGING"; + }; + } + + public interface AccountLoader { + + SignalAccount loadAccountOrNull(String accountPath); + } } diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 37f5feec..91af270d 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -46,6 +46,7 @@ import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.OutputStreamWriter; +import java.util.Set; import static net.sourceforge.argparse4j.DefaultSettings.VERSION_0_9_0_DEFAULT_SETTINGS; @@ -188,7 +189,12 @@ public class App { return; } - var accounts = signalAccountFiles.getAllLocalAccountNumbers(); + Set accounts = null; + try { + accounts = signalAccountFiles.getAllLocalAccountNumbers(); + } catch (IOException e) { + throw new IOErrorException("Failed to load local accounts file", e); + } if (accounts.size() == 0) { throw new UserErrorException("No local users found, you first need to register or link an account"); } else if (accounts.size() > 1) { @@ -299,6 +305,8 @@ public class App { ) throws CommandException { try (var multiAccountManager = signalAccountFiles.initMultiAccountManager()) { command.handleCommand(ns, multiAccountManager, outputWriter); + } catch (IOException e) { + throw new IOErrorException("Failed to load local accounts file", e); } } From 4ea3d94d0768f452899b45120df50a67fd1b8443 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 13 Jun 2022 15:35:25 +0200 Subject: [PATCH 154/938] Improve number filtering of listContacts command If the given number is not registered, don't output all recipients --- lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 201f0931..5548e5ac 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -989,6 +989,9 @@ class ManagerImpl implements Manager { return null; } }).filter(Objects::nonNull).collect(Collectors.toSet()); + if (!recipients.isEmpty() && recipientIds.isEmpty()) { + return List.of(); + } // refresh profiles of explicitly given recipients context.getProfileHelper().refreshRecipientProfiles(recipientIds); return account.getRecipientStore().getRecipients(onlyContacts, blocked, recipientIds, name); From adce64bc21cc57bcba2bd12a68105be5924a9f63 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 13 Jun 2022 15:46:03 +0200 Subject: [PATCH 155/938] Bump version --- CHANGELOG.md | 11 +++++++++++ build.gradle.kts | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a829372d..3d27afb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ## [Unreleased] +## [0.10.8] - 2022-06-13 + +### Added +- Attachments can now be given as data: URIs with base64 data instead of just file paths (Thanks @KevinRoebert) +- `version` command can now be used on the commandline, in addition to the `--version` flag. + In the next version the current short form `-v` will change its meaning to `--verbose`! + +### Improved +- An account can now be registered on both LIVE and STAGING environment in the same config directory. +- Logging output for registering has been extended. + ## [0.10.7] - 2022-05-29 ### Added diff --git a/build.gradle.kts b/build.gradle.kts index 8bdd9ca4..cbaa36c3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.11" } -version = "0.10.7" +version = "0.10.8" java { sourceCompatibility = JavaVersion.VERSION_17 From abebffb2cd99678bd17498fea2d1f0749b602c8b Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 18 Jun 2022 12:11:36 +0200 Subject: [PATCH 156/938] Improve output for profile key update messages --- .../java/org/asamk/signal/manager/api/MessageEnvelope.java | 2 ++ src/main/java/org/asamk/signal/ReceiveMessageHandler.java | 5 ++++- src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) 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 67a33613..d2164e91 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 @@ -100,6 +100,7 @@ public record MessageEnvelope( boolean isExpirationUpdate, boolean isViewOnce, boolean isEndSession, + boolean isProfileKeyUpdate, boolean hasProfileKey, Optional reaction, Optional quote, @@ -126,6 +127,7 @@ public record MessageEnvelope( dataMessage.isExpirationUpdate(), dataMessage.isViewOnce(), dataMessage.isEndSession(), + dataMessage.isProfileKeyUpdate(), dataMessage.getProfileKey().isPresent(), dataMessage.getReaction().map(r -> Reaction.from(r, recipientResolver, addressResolver)), dataMessage.getQuote().map(q -> Quote.from(q, recipientResolver, addressResolver, fileProvider)), diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 04a7be03..390087f9 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -141,9 +141,12 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (message.expiresInSeconds() > 0) { writer.println("Expires in: {} seconds", message.expiresInSeconds()); } - if (message.hasProfileKey()) { + if (message.isProfileKeyUpdate()) { writer.println("Profile key update"); } + if (message.hasProfileKey()) { + writer.println("With profile key"); + } if (message.reaction().isPresent()) { writer.println("Reaction:"); final var reaction = message.reaction().get(); diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 9ffb0795..90153147 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -757,6 +757,7 @@ public class DbusManagerImpl implements Manager { false, false, false, + false, Optional.empty(), Optional.empty(), Optional.empty(), @@ -827,6 +828,7 @@ public class DbusManagerImpl implements Manager { false, false, false, + false, Optional.empty(), Optional.empty(), Optional.empty(), From 280bdbefdcb9123a9bd7ee0cf7b7b3585a972dfc Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 18 Jun 2022 12:27:34 +0200 Subject: [PATCH 157/938] Only send profile key update message from the primary device --- .../asamk/signal/manager/helper/IncomingMessageHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 4940e67c..d18cc0c0 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 @@ -255,7 +255,10 @@ public final class IncomingMessageHandler { } else { // Message wasn't sent as unidentified sender message final var contact = context.getAccount().getContactStore().getContact(sender); - if (contact != null && !contact.isBlocked() && contact.isProfileSharingEnabled()) { + if (account.isPrimaryDevice() + && contact != null + && !contact.isBlocked() + && contact.isProfileSharingEnabled()) { actions.add(UpdateAccountAttributesAction.create()); actions.add(new SendProfileKeyAction(sender)); } From 6bbbccb9ac74d577d159fdabe9b6de27a8d99822 Mon Sep 17 00:00:00 2001 From: John Rush Date: Tue, 21 Jun 2022 01:11:30 -0700 Subject: [PATCH 158/938] Update README.md (#972) Fix typo from "JSON-PRC" to "JSON-RPC". --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 570451c2..50442c34 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ support [provisioning as a linked device](https://github.com/WhisperSystems/libs registering you need a phone number where you can receive SMS or incoming calls. signal-cli is primarily intended to be used on servers to notify admins of important events. For this use-case, it has a daemon mode with D-BUS -interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)) and JSON-PRC interface ([documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service)). For the JSON-RPC interface there's also a simple [example client](https://github.com/AsamK/signal-cli/tree/master/client), written in Rust. +interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)) and JSON-RPC interface ([documentation](https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service)). For the JSON-RPC interface there's also a simple [example client](https://github.com/AsamK/signal-cli/tree/master/client), written in Rust. ## Installation From c1dc44d4fd5cf1aba2a30f7c689cf9ea013e2d60 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 21 Jun 2022 16:58:15 +0200 Subject: [PATCH 159/938] Separate registrationLock attribute from master key --- .../manager/RegistrationManagerImpl.java | 2 +- .../signal/manager/helper/AccountHelper.java | 13 ++++------ .../signal/manager/storage/SignalAccount.java | 24 ++++++++++++++++--- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java index 3ce77557..5a99b827 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManagerImpl.java @@ -182,7 +182,7 @@ class RegistrationManagerImpl implements RegistrationManager { account.getLocalRegistrationId(), true, null, - account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), + account.getRegistrationLock(), account.getSelfUnidentifiedAccessKey(), account.isUnrestrictedUnidentifiedAccess(), capabilities, 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 bae049c5..0c66a182 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 @@ -9,7 +9,6 @@ import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException; import org.asamk.signal.manager.api.PinLockedException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.storage.SignalAccount; -import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.NumberVerificationUtils; import org.signal.libsignal.protocol.InvalidKeyException; import org.slf4j.Logger; @@ -125,7 +124,7 @@ public class AccountHelper { account.getLocalRegistrationId(), true, null, - account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), + account.getRegistrationLock(), account.getSelfUnidentifiedAccessKey(), account.isUnrestrictedUnidentifiedAccess(), ServiceConfig.capabilities, @@ -157,20 +156,18 @@ public class AccountHelper { } public void setRegistrationPin(String pin) throws IOException { - final var masterKey = account.getPinMasterKey() != null - ? account.getPinMasterKey() - : KeyUtils.createMasterKey(); + var masterKey = account.getOrCreatePinMasterKey(); context.getPinHelper().setRegistrationLockPin(pin, masterKey); - account.setRegistrationLockPin(pin, masterKey); + account.setRegistrationLockPin(pin); } public void removeRegistrationPin() throws IOException { // Remove KBS Pin context.getPinHelper().removeRegistrationLockPin(); - account.setRegistrationLockPin(null, null); + account.setRegistrationLockPin(null); } public void unregister() throws IOException { @@ -189,7 +186,7 @@ public class AccountHelper { } catch (IOException e) { logger.warn("Failed to remove registration lock pin"); } - account.setRegistrationLockPin(null, null); + account.setRegistrationLockPin(null); dependencies.getAccountManager().deleteAccount(); 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 a66af784..7268693a 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 @@ -1253,13 +1253,31 @@ public class SignalAccount implements Closeable { save(); } - public void setRegistrationLockPin(final String registrationLockPin, final MasterKey pinMasterKey) { + public void setRegistrationLockPin(final String registrationLockPin) { this.registrationLockPin = registrationLockPin; - this.pinMasterKey = pinMasterKey; save(); } - public MasterKey getPinMasterKey() { + public String getRegistrationLock() { + final var masterKey = getPinBackedMasterKey(); + if (masterKey == null) { + return null; + } + return masterKey.deriveRegistrationLock(); + } + + public MasterKey getPinBackedMasterKey() { + if (registrationLockPin == null) { + return null; + } + return pinMasterKey; + } + + public MasterKey getOrCreatePinMasterKey() { + if (pinMasterKey == null) { + pinMasterKey = KeyUtils.createMasterKey(); + save(); + } return pinMasterKey; } From a4db5d616a184b4ef442a37005a0738a276909f5 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 21 Jun 2022 17:17:25 +0200 Subject: [PATCH 160/938] Create master key before storage sync if it doesn't exist yet --- .../main/java/org/asamk/signal/manager/ManagerImpl.java | 4 +--- .../manager/actions/RetrieveStorageDataAction.java | 6 +----- .../org/asamk/signal/manager/helper/StorageHelper.java | 9 ++++++++- .../org/asamk/signal/manager/storage/SignalAccount.java | 7 +++++++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 5548e5ac..6f178e4e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -830,9 +830,7 @@ class ManagerImpl implements Manager { } void retrieveRemoteStorage() throws IOException { - if (account.getStorageKey() != null) { - context.getStorageHelper().readDataFromStorage(); - } + context.getStorageHelper().readDataFromStorage(); } @Override diff --git a/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java b/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java index 28b304ab..8b296006 100644 --- a/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java +++ b/lib/src/main/java/org/asamk/signal/manager/actions/RetrieveStorageDataAction.java @@ -15,10 +15,6 @@ public class RetrieveStorageDataAction implements HandleAction { @Override public void execute(Context context) throws Throwable { - if (context.getAccount().getStorageKey() != null) { - context.getStorageHelper().readDataFromStorage(); - } else if (!context.getAccount().isPrimaryDevice()) { - context.getSyncHelper().requestSyncKeys(); - } + context.getStorageHelper().readDataFromStorage(); } } 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 84d98db2..f849389d 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 @@ -43,11 +43,18 @@ public class StorageHelper { } public void readDataFromStorage() throws IOException { + final var storageKey = account.getOrCreateStorageKey(); + if (storageKey == null) { + logger.debug("Storage key unknown, requesting from primary device."); + context.getSyncHelper().requestSyncKeys(); + return; + } + logger.debug("Reading data from remote storage"); Optional manifest; try { manifest = dependencies.getAccountManager() - .getStorageManifestIfDifferentVersion(account.getStorageKey(), account.getStorageManifestVersion()); + .getStorageManifestIfDifferentVersion(storageKey, account.getStorageManifestVersion()); } catch (InvalidKeyException e) { logger.warn("Manifest couldn't be decrypted, ignoring."); return; 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 7268693a..72b759b6 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 @@ -1288,6 +1288,13 @@ public class SignalAccount implements Closeable { return storageKey; } + public StorageKey getOrCreateStorageKey() { + if (isPrimaryDevice()) { + return getOrCreatePinMasterKey().deriveStorageServiceKey(); + } + return storageKey; + } + public void setStorageKey(final StorageKey storageKey) { if (storageKey.equals(this.storageKey)) { return; From 9553b1ef00992430477717a5ceb8fd7aea920661 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 21 Jun 2022 17:18:16 +0200 Subject: [PATCH 161/938] Check account identifiers on the server before updating the account attributes --- lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 6f178e4e..cdb6f640 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -231,6 +231,7 @@ class ManagerImpl implements Manager { if (deviceName != null) { context.getAccountHelper().setDeviceName(deviceName); } + context.getAccountHelper().checkWhoAmiI(); context.getAccountHelper().updateAccountAttributes(); } From c586f58286fb3128d67c420be45b78c1905ed014 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 21 Jun 2022 20:34:38 +0200 Subject: [PATCH 162/938] Reset cached storage manifest after provisioning/registering --- .../signal/manager/storage/SignalAccount.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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 72b759b6..5e3e7d04 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 @@ -371,6 +371,7 @@ public class SignalAccount implements Closeable { this.lastReceiveTimestamp = 0; this.pinMasterKey = null; this.storageManifestVersion = -1; + this.setStorageManifest(null); this.storageKey = null; } @@ -1329,9 +1330,19 @@ public class SignalAccount implements Closeable { } public void setStorageManifest(SignalStorageManifest manifest) { - final var manifestBytes = manifest.serialize(); - final var storageManifestFile = getStorageManifestFile(dataPath, accountPath); + if (manifest == null) { + if (storageManifestFile.exists()) { + try { + Files.delete(storageManifestFile.toPath()); + } catch (IOException e) { + logger.error("Failed to delete local storage manifest.", e); + } + } + return; + } + + final var manifestBytes = manifest.serialize(); try (var outputStream = new FileOutputStream(storageManifestFile)) { outputStream.write(manifestBytes); } catch (IOException e) { @@ -1406,6 +1417,7 @@ public class SignalAccount implements Closeable { public void finishRegistration(final ACI aci, final PNI pni, final MasterKey masterKey, final String pin) { this.pinMasterKey = masterKey; this.storageManifestVersion = -1; + this.setStorageManifest(null); this.storageKey = null; this.encryptedDeviceName = null; this.deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; From dca1d479e84f7b4c8b3a91ef517438d8483a32a7 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 27 Jun 2022 15:39:33 +0200 Subject: [PATCH 163/938] Update graalvm buildtools --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index cbaa36c3..878ea08e 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.9.11" + id("org.graalvm.buildtools.native") version "0.9.12" } version = "0.10.8" From 9f7979314f2e3d5338b704f43e81da5a408d97b9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 27 Jun 2022 16:57:40 +0200 Subject: [PATCH 164/938] Prevent duplicate family name when handling contact sync message --- .../main/java/org/asamk/signal/manager/helper/SyncHelper.java | 1 + 1 file changed, 1 insertion(+) 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 9a5b5a7d..d192ae8f 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 @@ -308,6 +308,7 @@ public class SyncHelper { final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); if (c.getName().isPresent()) { builder.withGivenName(c.getName().get()); + builder.withFamilyName(null); } if (c.getColor().isPresent()) { builder.withColor(c.getColor().get()); From 1c7d1861d6b004c47b5a3264fbfec071f25a9e7b Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jul 2022 20:07:28 +0200 Subject: [PATCH 165/938] Update graalvm buildtools --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 878ea08e..bd257e5a 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.9.12" + id("org.graalvm.buildtools.native") version "0.9.13" } version = "0.10.8" From 9da2a0040358c57c27fbbf3c98d416b243070832 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jul 2022 20:13:36 +0200 Subject: [PATCH 166/938] Fix issue with loading legacy profile store Fixes #981 --- .../manager/storage/profiles/LegacyProfileStore.java | 2 +- .../{SignalProfile.java => LegacySignalProfile.java} | 7 +++++-- .../manager/storage/profiles/LegacySignalProfileEntry.java | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) rename lib/src/main/java/org/asamk/signal/manager/storage/profiles/{SignalProfile.java => LegacySignalProfile.java} (95%) 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 b3aa4f8c..be275116 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 @@ -60,7 +60,7 @@ public class LegacyProfileStore { } } var lastUpdateTimestamp = entry.get("lastUpdateTimestamp").asLong(); - var profile = jsonProcessor.treeToValue(entry.get("profile"), SignalProfile.class); + var profile = jsonProcessor.treeToValue(entry.get("profile"), LegacySignalProfile.class); profileEntries.add(new LegacySignalProfileEntry(address, profileKey, lastUpdateTimestamp, diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfile.java similarity index 95% rename from lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java rename to lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfile.java index ed5f0160..e3e18b1e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfile.java @@ -3,7 +3,7 @@ package org.asamk.signal.manager.storage.profiles; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -public class SignalProfile { +public class LegacySignalProfile { @JsonProperty @JsonIgnore @@ -27,7 +27,7 @@ public class SignalProfile { @JsonProperty private final Capabilities capabilities; - public SignalProfile( + public LegacySignalProfile( @JsonProperty("name") final String name, @JsonProperty("about") final String about, @JsonProperty("aboutEmoji") final String aboutEmoji, @@ -88,6 +88,9 @@ public class SignalProfile { @JsonIgnore public boolean uuid; + @JsonIgnore + public boolean gv2; + @JsonProperty public boolean storage; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java index 7571146e..609d2f54 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java @@ -12,7 +12,7 @@ public class LegacySignalProfileEntry { private final long lastUpdateTimestamp; - private final SignalProfile profile; + private final LegacySignalProfile profile; private final ProfileKeyCredential profileKeyCredential; @@ -20,7 +20,7 @@ public class LegacySignalProfileEntry { final RecipientAddress address, final ProfileKey profileKey, final long lastUpdateTimestamp, - final SignalProfile profile, + final LegacySignalProfile profile, final ProfileKeyCredential profileKeyCredential ) { this.address = address; @@ -42,7 +42,7 @@ public class LegacySignalProfileEntry { return lastUpdateTimestamp; } - public SignalProfile getProfile() { + public LegacySignalProfile getProfile() { return profile; } From f4346e3f0ad1507bc9ba350289b32b388f2b2814 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 7 Jul 2022 18:46:29 +0200 Subject: [PATCH 167/938] Update account attributes before checking whoAmI --- lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index cdb6f640..23423955 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -231,8 +231,8 @@ class ManagerImpl implements Manager { if (deviceName != null) { context.getAccountHelper().setDeviceName(deviceName); } - context.getAccountHelper().checkWhoAmiI(); context.getAccountHelper().updateAccountAttributes(); + context.getAccountHelper().checkWhoAmiI(); } @Override From dcde9fbe8ea884e7ae61b3c64a0cff28c09050ea Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 7 Jul 2022 18:22:09 +0200 Subject: [PATCH 168/938] Use toList() Stream method --- .../java/org/asamk/signal/manager/helper/SendHelper.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 e0f2f161..523df2c9 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 @@ -51,7 +51,6 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; public class SendHelper { @@ -275,7 +274,7 @@ public class SendHelper { .getDevices() .stream() .map(device -> new SignalProtocolAddress(address.getIdentifier(), device)) - .collect(Collectors.toList()); + .toList(); account.getSenderKeyStore().markSenderKeySharedWith(group.getDistributionId(), addresses); } @@ -536,14 +535,14 @@ public class SendHelper { List addresses = recipientIdList.stream() .map(context.getRecipientHelper()::resolveSignalServiceAddress) - .collect(Collectors.toList()); + .toList(); List unidentifiedAccesses = context.getUnidentifiedAccessHelper() .getAccessFor(recipientIdList) .stream() .map(Optional::get) .map(UnidentifiedAccessPair::getTargetUnidentifiedAccess) .map(Optional::get) - .collect(Collectors.toList()); + .toList(); try { List results = sender.send(distributionId, From 457b093dce9a63b22b773ac4502867fdc7b0ab2c Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 7 Jul 2022 18:43:49 +0200 Subject: [PATCH 169/938] Improve import of contact from storage --- .../signal/manager/helper/StorageHelper.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) 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 f849389d..bc785099 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 @@ -6,6 +6,7 @@ import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.recipients.Contact; +import org.asamk.signal.manager.storage.recipients.Profile; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.zkgroup.InvalidInputException; @@ -100,26 +101,36 @@ public class StorageHelper { final var contactRecord = record.getContact().get(); final var address = contactRecord.getAddress(); - final var recipientId = account.getRecipientResolver().resolveRecipient(address); + final var contact = account.getContactStore().getContact(recipientId); final var blocked = contact != null && contact.isBlocked(); final var profileShared = contact != null && contact.isProfileSharingEnabled(); - final var givenName = contact == null ? null : contact.getGivenName(); - final var familyName = contact == null ? null : contact.getFamilyName(); - if ((contactRecord.getGivenName().isPresent() && !contactRecord.getGivenName().get().equals(givenName)) || ( - contactRecord.getFamilyName().isPresent() && !contactRecord.getFamilyName().get().equals(familyName) - ) || blocked != contactRecord.isBlocked() || profileShared != contactRecord.isProfileSharingEnabled()) { + final var archived = contact != null && contact.isArchived(); + if (blocked != contactRecord.isBlocked() + || profileShared != contactRecord.isProfileSharingEnabled() + || archived != contactRecord.isArchived()) { logger.debug("Storing new or updated contact {}", recipientId); final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); final var newContact = contactBuilder.withBlocked(contactRecord.isBlocked()) - .withGivenName(contactRecord.getGivenName().orElse(null)) - .withFamilyName(contactRecord.getFamilyName().orElse(null)) .withProfileSharingEnabled(contactRecord.isProfileSharingEnabled()) + .withArchived(contactRecord.isArchived()) .build(); account.getContactStore().storeContact(recipientId, newContact); } + final var profile = account.getProfileStore().getProfile(recipientId); + final var givenName = profile == null ? null : profile.getGivenName(); + final var familyName = profile == null ? null : profile.getFamilyName(); + if ((contactRecord.getGivenName().isPresent() && !contactRecord.getGivenName().get().equals(givenName)) || ( + contactRecord.getFamilyName().isPresent() && !contactRecord.getFamilyName().get().equals(familyName) + )) { + final var profileBuilder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile); + final var newProfile = profileBuilder.withGivenName(contactRecord.getGivenName().orElse(null)) + .withFamilyName(contactRecord.getFamilyName().orElse(null)) + .build(); + account.getProfileStore().storeProfile(recipientId, newProfile); + } if (contactRecord.getProfileKey().isPresent()) { try { logger.trace("Storing profile key {}", recipientId); From ed7d023581ecbdf8cfebbef6a89e41921b281ee0 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jul 2022 11:06:10 +0200 Subject: [PATCH 170/938] Update gradle wrapper --- gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 6 ++++++ gradlew.bat | 14 ++++++++------ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4fb3f96a785543079b8df6723c946b..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 10197 zcmaKS1ymhDwk=#NxVyW%y9U<)A-Dv)xI0|j{UX8L-JRg>5ZnnKAh;%chM6~S-g^K4 z>eZ{yK4;gd>gwvXs=Id8Jk-J}R4pT911;+{Jp9@aiz6!p1Oz9z&_kGLA%J5%3Ih@0 zQ|U}%$)3u|G`jIfPzMVfcWs?jV2BO^*3+q2><~>3j+Z`^Z%=;19VWg0XndJ zwJ~;f4$;t6pBKaWn}UNO-wLCFHBd^1)^v%$P)fJk1PbK5<;Z1K&>k~MUod6d%@Bq9 z>(44uiaK&sdhwTTxFJvC$JDnl;f}*Q-^01T508(8{+!WyquuyB7R!d!J)8Ni0p!cV6$CHsLLy6}7C zYv_$eD;)@L)tLj0GkGpBoa727hs%wH$>EhfuFy{_8Q8@1HI%ZAjlpX$ob{=%g6`Ox zLzM!d^zy`VV1dT9U9(^}YvlTO9Bf8v^wMK37`4wFNFzW?HWDY(U(k6@tp(crHD)X5>8S-# zW1qgdaZa*Sh6i%60e1+hty}34dD%vKgb?QmQiZ=-j+isA4={V_*R$oGN#j|#ia@n6 zuZx4e2Xx?^lUwYFn2&Tmbx0qA3Z8;y+zKoeQu;~k~FZGy!FU_TFxYd!Ck;5QvMx9gj5fI2@BLNp~Ps@ zf@k<&Q2GS5Ia9?_D?v~$I%_CLA4x~eiKIZ>9w^c#r|vB?wXxZ(vXd*vH(Fd%Me8p( z=_0)k=iRh%8i`FYRF>E97uOFTBfajv{IOz(7CU zv0Gd84+o&ciHlVtY)wn6yhZTQQO*4Mvc#dxa>h}82mEKKy7arOqU$enb9sgh#E=Lq zU;_RVm{)30{bw+|056%jMVcZRGEBSJ+JZ@jH#~DvaDQm92^TyUq=bY*+AkEakpK>8 zB{)CkK48&nE5AzTqT;WysOG|!y}5fshxR8Ek(^H6i>|Fd&wu?c&Q@N9ZrJ=?ABHI! z`*z8D`w=~AJ!P-9M=T}f`;76$qZRllB&8#9WgbuO$P7lVqdX1=g*t=7z6!0AQ^ux_ z9rcfUv^t}o_l-ZE+TqvqFsA*~W<^78!k;~!i8(eS+(+@u8FxK+Q7;mHZ<1}|4m<}vh@p`t%|@eM_J(P% zI>M7C)Ir{l|J;$G_EGGEhbP4?6{sYzMqBv+x95N&YWFH6UcE@b}B?q)G*4<4mR@sy1#vPnLMK51tb#ED(8TA1nE zYfhK7bo1!R5WJF$5Y?zG21)6+_(_5oSX9sGIW;(O&S?Rh(nydNQYzKjjJ54aDJ-1F zrJ=np8LsN?%?Rt7f~3aAX!2E{`fh_pb?2(;HOB3W+I*~A>W%iY+v45+^e$cE10fA} zXPvw9=Bd+(;+!rl)pkYj0HGB}+3Z!Mr;zr%gz~c-hFMv8b2VRE2R$8V=_XE zq$3=|Yg05(fmwrJ)QK2ptB4no`Y8Dg_vK2QDc6-6sXRQ5k78-+cPi-fH}vpgs|Ive zE=m*XNVs?EWgiNI!5AcD*3QMW)R`EqT!f0e1%hERO&?AT7HWnSf5@#AR{OGuXG3Zb zCnVWg7h|61lGV3k+>L<#d>)InG>ETn1DbOHCfztqzQ_fBiaUt@q6VMy={Fe-w#~2- z0?*f|z$zgjI9>+JVICObBaK=pU}AEOd@q(8d?j7zQFD@=6t`|KmolTr2MfBI$;EGh zD%W0cA_d#V6Lb$us5yIG(|d>r-QleC4;%hEu5W9hyY zY#+ESY&v`8(&mC~?*|e5WEhC!YU2>m_}`K+q9)a(d$bsS<=YkyZGp}YA%TXw>@abA zS_poVPoN+?<6?DAuCNt&5SHV(hp56PJ})swwVFZFXM->F zc|0c8<$H_OV%DR|y7e+s$12@Ac8SUClPg8_O9sTUjpv%6Jsn5vsZCg>wL+db4c+{+ zsg<#wOuV4jeOq`veckdi-1`dz;gvL)bZeH|D*x=8UwRU5&8W1@l>3$)8WzET0%;1J zM3(X<7tKK&9~kWRI{&FmwY5Gg!b5f4kI_vSm)H1#>l6M+OiReDXC{kPy!`%Ecq-+3yZTk=<` zm)pE6xum5q0Qkd#iny0Q-S}@I0;mDhxf>sX)Oiv)FdsAMnpx%oe8OQ`m%Xeozdzx!C1rQR>m1c_}+J4x)K}k{G zo68;oGG&Ox7w^-m7{g4a7NJu-B|~M;oIH~~#`RyUNm##feZH;E?pf}nshmoiIY52n z%pc%lnU4Q#C=RUz)RU6}E_j4#)jh<&a%JyJj$Fufc#&COaxFHtl}zJUGNLBu3~_@1 zn9F^JO9);Duxo&i@>X(kbYga1i>6p1fca8FzQ0>((Lb-aPUbC*d~a03V$y;*RBY!R ziEJ2IF^FjrvO}0Uy{cMn%u<+P5U!UO>pm9#ZYL5i6|xSC+np7IH$GfXs&uI;y4as@ z&AzJh>(S2?3PKKgab3Z(`xbx(C#46XIvVcW8eG_DjT~}Yz_8PWZ`uf6^Xr=vkvL_` zqmvfgJL+Zc`;iq~iP?%@G7}~fal-zqxa0yNyHBJJ5M)9bI>7S_cg?Ya&p(I)C5Ef4 zZ>YAF6x|U=?ec?g*|f2g5Tw3PgxaM_bi_5Az9MO$;_Byw(2d}2%-|bg4ShdQ;)Z|M z4K|tFv)qx*kKGKoyh!DQY<{n&UmAChq@DJrQP>EY7g1JF(ih*D8wCVWyQ z5Jj^|-NVFSh5T0vd1>hUvPV6?=`90^_)t(L9)XOW7jeP45NyA2lzOn&QAPTl&d#6P zSv%36uaN(9i9WlpcH#}rmiP#=L0q(dfhdxvFVaOwM;pY;KvNQ9wMyUKs6{d}29DZQ z{H3&Sosr6)9Z+C>Q5)iHSW~gGoWGgK-0;k~&dyr-bA3O|3PCNzgC?UKS_B=^i8Ri^ zd_*_qI4B07Cayq|p4{`U_E_P=K`N_~{F|+-+`sCgcNxs`%X!$=(?l2aAW}0M=~COb zf19oe^iuAUuDEf)4tgv<=WRPpK@IjToNNC*#&Ykw!)aqWU4h#|U@(cG_=Qx+&xt~a zvCz~Ds3F71dsjNLkfM%TqdVNu=RNMOzh7?b+%hICbFlOAPphrYy>7D-e7{%o_kPFn z;T!?ilE-LcKM0P(GKMseEeW57Vs`=FF}(y@^pQl;rL3fHs8icmA+!6YJt&8 ztSF?%Un35qkv>drkks&BNTJv~xK?vD;aBkp7eIkDYqn+G0%;sT4FcwAoO+vke{8CO z0d76sgg$CannW5T#q`z~L4id)9BCKRU0A!Z-{HpXr)QJrd9@iJB+l32Ql)Z}*v(St zE)Vp=BB=DDB4Pr}B(UHNe31<@!6d{U?XDoxJ@S)9QM)2L%SA0x^~^fb=bdsBy!uh& zU?M_^kvnt%FZzm+>~bEH{2o?v&Iogs`1t-b+Ml`J!ZPS(46YQJKxWE81O$HE5w;** z|8zM%bp`M7J8)4;%DqH`wVTmM0V@D}xd%tRE3_6>ioMJxyi5Hkb>85muF81&EY!73ei zA3e<#ug||EZJ=1GLXNJ)A z791&ge#lF;GVX6IU?iw0jX^1bYaU?+x{zPlpyX6zijyn*nEdZ$fxxkl!a-~*P3bkf zPd*pzu~3GBYkR_>ET`5UM^>>zTV>5m>)f=az{d0sg6a8VzUtXy$ZS?h#Gk-CA?7)c zI%Vu9DN6XSDQn6;?n9`>l$q&>s?K)R8*OsmI+$L_m z_~E`}w694Z*`Xk3Ne=497Si~=RWRqCM?6=88smrxle#s*W znwhTRsMRmg?37GLJ-)%nDZA7r$YG849j8mJWir1bWBy& zZPneYojSbooC8U@tkO`bWx4%E5*;p#Q^1^S3lsfy7(6A{jL0`A__0vm?>xC%1y8_m z57FfWr^@YG2I1K7MGYuYd>JC}@sT2n^rkrY3w%~$J$Y~HSoOHn?zpR$ zjLj_bq@Yj8kd~DXHh30KVbz@K)0S;hPKm+S&-o%IG+@x@MEcrxW2KFh;z^4dJDZix zGRGe&lQD$p)0JVF4NRgGYuh0bYLy)BCy~sbS3^b3 zHixT<%-Vwbht|25T{3^Hk;qZ^3s!OOgljHs+EIf~C%=_>R5%vQI4mQR9qOXThMXlU zS|oSH>0PjnCakb*js2{ObN`}%HYsT6=%(xA| znpUtG_TJ08kHgm5l@G|t?4E3tG2fq?wNtIp*Vqrb{9@bo^~Rx7+J&OnayrX`LDcF~ zd@0m0ZJ#Z@=T>4kTa5e2FjI&5c(F7S{gnRPoGpu9eIqrtSvnT_tk$8T)r%YwZw!gK zj*k@cG)V&@t+mtDi37#>LhVGTfRA^p%x0d#_P|Mktz3*KOoLIqFm`~KGoDDD4OOxe z?}ag_c08u%vu=5Vx=~uoS8Q;}+R2~?Uh|m-+`-2kDo$d6T!nD*hc#dB(*R{LXV=zo z`PJP0V=O!@3l-bw+d`X6(=@fq=4O#ETa8M^fOvO4qja9o3e8ANc9$sI=A4$zUut~w z4+JryRkI{9qWxU1CCMM$@Aj=6)P+z?vqa=UCv_4XyVNoBD{Xb~Oi4cjjhm8fRD!*U z2)zaS;AI78^Wq+5mDInKiMz|z#K`2emQfNH*U;{9^{NqSMVoq?RSo43<8YpJM^+W$ zxy!A5>5Zl16Vi#?nAYywu3w_=KWnd3*QetocWt`3pK67>)ZVwnT3h zbPdD&MZkD?q=-N`MpCCwpM74L+Tr1aa)zJ)8G;(Pg51@U&5W>aNu9rA`bh{vgfE={ zdJ>aKc|2Ayw_bop+dK?Y5$q--WM*+$9&3Q9BBiwU8L<-`T6E?ZC`mT0b}%HR*LPK} z!MCd_Azd{36?Y_>yN{U1w5yrN8q`z(Vh^RnEF+;4b|2+~lfAvPT!`*{MPiDioiix8 zY*GdCwJ{S(5(HId*I%8XF=pHFz<9tAe;!D5$Z(iN#jzSql4sqX5!7Y?q4_%$lH zz8ehZuyl0K=E&gYhlfFWabnSiGty$>md|PpU1VfaC5~kskDnZX&Yu}?-h;OSav=8u z=e3Yq=mi$4A|sB-J00;1d{Sd1+!v0NtU((Nz2;PFFlC}V{@p&4wGcVhU&nI($RAS! zwXn7)?8~1J3*4+VccRSg5JS<(bBhBM&{ELMD4C_NTpvzboH!{Zr*%HP;{UqxI#g&7 zOAqPSW5Qus$8-xtTvD%h{Tw<2!XR(lU54LZG{)Cah*LZbpJkA=PMawg!O>X@&%+5XiyeIf91n2E*hl$k-Y(3iW*E}Mz-h~H~7S9I1I zR#-j`|Hk?$MqFhE4C@=n!hN*o5+M%NxRqP+aLxDdt=wS6rAu6ECK*;AB%Nyg0uyAv zO^DnbVZZo*|Ef{nsYN>cjZC$OHzR_*g%T#oF zCky9HJS;NCi=7(07tQXq?V8I&OA&kPlJ_dfSRdL2bRUt;tA3yKZRMHMXH&#W@$l%-{vQd7y@~i*^qnj^`Z{)V$6@l&!qP_y zg2oOd!Wit#)2A~w-eqw3*Mbe)U?N|q6sXw~E~&$!!@QYX4b@%;3=>)@Z#K^`8~Aki z+LYKJu~Y$;F5%_0aF9$MsbGS9Bz2~VUG@i@3Fi2q(hG^+Ia44LrfSfqtg$4{%qBDM z_9-O#3V+2~W$dW0G)R7l_R_vw(KSkC--u&%Rs^Io&*?R=`)6BN64>6>)`TxyT_(Rd zUn+aIl1mPa#Jse9B3`!T=|e!pIp$(8ZOe0ao?nS7o?oKlj zypC-fMj1DHIDrh1unUI1vp=-Fln;I9e7Jvs3wj*^_1&W|X} zZSL|S|Bb@CV*YC_-T&2!Ht3b6?)d`tHOP?rA;;t#zaXa0Sc;vGnV0BLIf8f-r{QHh z*Zp`4_ItlOR7{u(K+!p_oLDmaAkNag*l4#29F2b_A*0oz0T|#-&f*;c#<`^)(W@gm z#k9k=t%u8<+C1fNUA{Fh7~wgPrEZZ#(6aBI%6bR4RO(e1(ZocjoDek4#MTgZD>1NG zy9~yoZfWYfwe&S-(zk4o6q6o?2*~DOrJ(%5wSnEJMVOKCzHd z=Yhm+HLzoDl{P*Ybro7@sk1!Ez3`hE+&qr7Rw^2glw^M(b(NS2!F|Q!mi|l~lF94o z!QiV)Q{Z>GO5;l1y!$O)=)got;^)%@v#B!ZEVQy1(BJApHr5%Zh&W|gweD+%Ky%CO ztr45vR*y(@*Dg_Qw5v~PJtm^@Lyh*zRuT6~(K+^HWEF{;R#L$vL2!_ndBxCtUvZ(_ zauI7Qq}ERUWjr&XW9SwMbU>*@p)(cuWXCxRK&?ZoOy>2VESII53iPDP64S1pl{NsC zD;@EGPxs&}$W1;P6BB9THF%xfoLX|4?S;cu@$)9OdFst-!A7T{(LXtdNQSx!*GUSIS_lyI`da8>!y_tpJb3Zuf0O*;2y?HCfH z5QT6@nL|%l3&u4;F!~XG9E%1YwF*Fgs5V&uFsx52*iag(?6O|gYCBY3R{qhxT-Etb zq(E%V=MgQnuDGEKOGsmBj9T0-nmI%zys8NSO>gfJT4bP>tI>|ol@ zDt(&SUKrg%cz>AmqtJKEMUM;f47FEOFc%Bbmh~|*#E zDd!Tl(wa)ZZIFwe^*)4>{T+zuRykc3^-=P1aI%0Mh}*x7%SP6wD{_? zisraq`Las#y-6{`y@CU3Ta$tOl|@>4qXcB;1bb)oH9kD6 zKym@d$ zv&PZSSAV1Gwwzqrc?^_1+-ZGY+3_7~a(L+`-WdcJMo>EWZN3%z4y6JyF4NR^urk`c z?osO|J#V}k_6*9*n2?j+`F{B<%?9cdTQyVNm8D}H~T}?HOCXt%r7#2hz97Gx#X%62hyaLbU z_ZepP0<`<;eABrHrJAc!_m?kmu#7j}{empH@iUIEk^jk}^EFwO)vd7NZB=&uk6JG^ zC>xad8X$h|eCAOX&MaX<$tA1~r|hW?-0{t4PkVygTc`yh39c;&efwY(-#;$W)+4Xb z$XFsdG&;@^X`aynAMxsq)J#KZXX!sI@g~YiJdHI~r z$4mj_?S29sIa4c$z)19JmJ;Uj?>Kq=0XuH#k#};I&-6zZ_&>)j>UR0XetRO!-sjF< zd_6b1A2vfi++?>cf}s{@#BvTD|a%{9si7G}T+8ZnwuA z1k8c%lgE<-7f~H`cqgF;qZ|$>R-xNPA$25N1WI3#n%gj}4Ix}vj|e=x)B^roGQpB) zO+^#nO2 zjzJ9kHI6nI5ni&V_#5> z!?<7Qd9{|xwIf4b0bRc;zb}V4>snRg6*wl$Xz`hRDN8laL5tg&+@Dv>U^IjGQ}*=XBnXWrwTy;2nX?<1rkvOs#u(#qJ=A zBy>W`N!?%@Ay=upXFI}%LS9bjw?$h)7Dry0%d}=v0YcCSXf9nnp0tBKT1eqZ-4LU` zyiXglKRX)gtT0VbX1}w0f2ce8{$WH?BQm@$`ua%YP8G@<$n13D#*(Yd5-bHfI8!on zf5q4CPdgJLl;BqIo#>CIkX)G;rh|bzGuz1N%rr+5seP${mEg$;uQ3jC$;TsR&{IX< z;}7j3LnV+xNn^$F1;QarDf6rNYj7He+VsjJk6R@0MAkcwrsq4?(~`GKy|mgkfkd1msc2>%B!HpZ~HOzj}kl|ZF(IqB=D6ZTVcKe=I7)LlAI=!XU?J*i#9VXeKeaG zwx_l@Z(w`)5Cclw`6kQKlS<;_Knj)^Dh2pL`hQo!=GPOMR0iqEtx12ORLpN(KBOm5 zontAH5X5!9WHS_=tJfbACz@Dnkuw|^7t=l&x8yb2a~q|aqE_W&0M|tI7@ilGXqE)MONI8p67OiQGqKEQWw;LGga=ZM1;{pSw1jJK_y$vhY6 ztFrV7-xf>lbeKH1U)j3R=?w*>(Yh~NNEPVmeQ8n}0x01$-o z2Jyjn+sXhgOz>AzcZ zAbJZ@f}MBS0lLKR=IE{z;Fav%tcb+`Yi*!`HTDPqSCsFr>;yt^^&SI2mhKJ8f*%ji zz%JkZGvOn{JFn;)5jf^21AvO-9nRzsg0&CPz;OEn07`CfT@gK4abFBT$Mu?8fCcscmRkK+ zbAVJZ~#_a z{|(FFX}~8d3;DW8zuY9?r#Dt>!aD>} zlYw>D7y#eDy+PLZ&XKIY&Df0hsLDDi(Yrq8O==d30RchrUw8a=Eex>Dd?)3+k=}Q> z-b85lun-V$I}86Vg#l1S@1%=$2BQD5_waAZKQfJ${3{b2SZ#w1u+jMr{dJMvI|Og= zpQ9D={XK|ggbe04zTUd}iF{`GO1dV%zWK~?sM9OM(= zVK9&y4F^w1WFW{$qi|xQk0F`@HG8oLI5|5$j~ci9xTMT69v5KS-Yym--raU5kn2#C z<~5q^Bf0rTXVhctG2%&MG(cUGaz(gC(rcG~>qgO$W6>!#NOVQJ;pIYe-lLy(S=HgI zPh;lkL$l+FfMHItHnw_^bj8}CKM19t(C_2vSrhX2$K@-gFlH};#C?1;kk&U1L%4S~ zR^h%h+O1WE7DI$~dly?-_C7>(!E`~#REJ~Xa7lyrB$T!`&qYV5QreAa^aKr%toUJR zPWh)J3iD`(P6BI5k$oE$us#%!4$>`iH2p-88?WV0M$-K)JDibvA4 zpef%_*txN$Ei3=Lt(BBxZ&mhl|mUz-z*OD1=r9nfN zc5vOMFWpi>K=!$6f{eb?5Ru4M3o;t9xLpry|C%j~`@$f)OFB5+xo8XM8g&US@UU-sB|dAoc20y(F@=-2Ggp_`SWjEb#>IG^@j zuQK}e^>So#W2%|-)~K!+)wdU#6l>w5wnZt2pRL5Dz#~N`*UyC9tYechBTc2`@(OI# zNvcE*+zZZjU-H`QOITK^tZwOyLo)ZCLk>>Wm+flMsr5X{A<|m`Y281n?8H_2Fkz5}X?i%Rfm5s+n`J zDB&->=U+LtOIJ|jdYXjQWSQZFEs>Rm{`knop4Sq)(}O_@gk{14y51)iOcGQ5J=b#e z2Yx^6^*F^F7q_m-AGFFgx5uqyw6_4w?yKCJKDGGprWyekr;X(!4CnM5_5?KgN=3qCm03 z##6k%kIU5%g!cCL(+aK>`Wd;dZ4h$h_jb7n?nqx5&o9cUJfr%h#m4+Bh)>HodKcDcsXDXwzJ3jR(sSFqWV(OKHC*cV8;;&bH=ZI0YbW3PgIHwTjiWy z?2MXWO2u0RAEEq(zv9e%Rsz|0(OKB?_3*kkXwHxEuazIZ7=JhaNV*P~hv57q55LoebmJpfHXA@yuS{Esg+ z*C}0V-`x^=0nOa@SPUJek>td~tJ{U1T&m)~`FLp*4DF77S^{|0g%|JIqd-=5)p6a` zpJOsEkKT(FPS@t^80V!I-YJbLE@{5KmVXjEq{QbCnir%}3 zB)-J379=wrBNK6rbUL7Mh^tVmQYn-BJJP=n?P&m-7)P#OZjQoK0{5?}XqJScV6>QX zPR>G{xvU_P;q!;S9Y7*07=Z!=wxIUorMQP(m?te~6&Z0PXQ@I=EYhD*XomZ^z;`Os z4>Uh4)Cg2_##mUa>i1Dxi+R~g#!!i{?SMj%9rfaBPlWj_Yk)lCV--e^&3INB>I?lu z9YXCY5(9U`3o?w2Xa5ErMbl5+pDVpu8v+KJzI9{KFk1H?(1`_W>Cu903Hg81vEX32l{nP2vROa1Fi!Wou0+ZX7Rp`g;B$*Ni3MC-vZ`f zFTi7}c+D)!4hz6NH2e%%t_;tkA0nfkmhLtRW%){TpIqD_ev>}#mVc)<$-1GKO_oK8 zy$CF^aV#x7>F4-J;P@tqWKG0|D1+7h+{ZHU5OVjh>#aa8+V;6BQ)8L5k9t`>)>7zr zfIlv77^`Fvm<)_+^z@ac%D&hnlUAFt8!x=jdaUo{)M9Ar;Tz5Dcd_|~Hl6CaRnK3R zYn${wZe8_BZ0l0c%qbP}>($jsNDay>8+JG@F!uV4F;#zGsBP0f$f3HqEHDz_sCr^q z1;1}7KJ9&`AX2Qdav1(nNzz+GPdEk5K3;hGXe{Hq13{)c zZy%fFEEH#nlJoG{f*M^#8yXuW%!9svN8ry-Vi7AOFnN~r&D`%6d#lvMXBgZkX^vFj z;tkent^62jUr$Cc^@y31Lka6hS>F?1tE8JW$iXO*n9CQMk}D*At3U(-W1E~z>tG?> z5f`5R5LbrhRNR8kv&5d9SL7ke2a*Xr)Qp#75 z6?-p035n2<7hK;sb>t9GAwG4{9v~iEIG>}7B5zcCgZhu$M0-z8?eUO^E?g)md^XT_ z2^~-u$yak>LBy(=*GsTj6p<>b5PO&un@5hGCxpBQlOB3DpsItKZRC*oXq-r{u}Wb; z&ko>#fbnl2Z;o@KqS-d6DTeCG?m1 z&E>p}SEc*)SD&QjZbs!Csjx~0+$@ekuzV_wAalnQvX3a^n~3ui)|rDO+9HW|JPEeBGP4 z)?zcZ<8qv47`EWA*_X~H^vr(lP|f%=%cWFM;u)OFHruKT<~?>5Y8l?56>&;=WdZU# zZEK4-C8s-3zPMA^&y~e*9z)!ZJghr3N^pJa2A$??Xqx-BR*TytGYor&l8Q+^^r%Yq02xay^f#;;wO6K7G!v>wRd6531WnDI~h$PN( z+4#08uX?r&zVKsQ;?5eBX=FxsXaGyH4Gth4a&L|{8LnNCHFr1M{KjJ!BfBS_aiy-E zxtmNcXq3}WTwQ7Dq-9YS5o758sT(5b`Sg-NcH>M9OH1oW6&sZ@|GYk|cJI`vm zO<$~q!3_$&GfWetudRc*mp8)M)q7DEY-#@8w=ItkApfq3sa)*GRqofuL7)dafznKf zLuembr#8gm*lIqKH)KMxSDqbik*B(1bFt%3Vv|ypehXLCa&wc7#u!cJNlUfWs8iQ` z$66(F=1fkxwg745-8_eqV>nWGY3DjB9gE23$R5g&w|C{|xvT@7j*@aZNB199scGchI7pINb5iyqYn)O=yJJX)Ca3&Ca+{n<=1w|(|f0)h<9gs$pVSV<<9Og-V z8ki@nKwE)x)^wmHBMk?mpMT=g{S#^8W|>&rI#Ceh;9za}io0k@0JxiCqi-jHlxbt3 zjJA?RihhRvhk6%G5-D{ePh1jare*fQS<328P-DcVAxPTrw=n6k?C6EV75f}cnBRPT zMYDqqKu(ND&aOtc!QRV`vzJSVxx8i~WB#5Ml{b#eQqNnSi7l-bS-`ITW<^zyYQA(b zbj4SuRK>q9o`_v%+C=S?h>2e4!66Ij(P5{7Uz$3u6YJJC$W%EoBa{-(=tQ|y1vov%ZkXVOV z##_UVg4V^4ne#4~<-1DkJqkKqgT+E_=&4Ue&eQ-JC+gi?7G@d6= zximz{zE)WW{b@QCJ!7l&N5x=dXS?$5RBU-VvN4Uec-GHK&jPa&P2z+qDdLhIB+HU) zu0CW&uLvE^4I5xtK-$+oe|58)7m6*PO%Xt<+-XEA%jG_BEachkF3e@pn?tl!`8lOF zbi2QOuNXX)YT*MCYflILO{VZ*9GiC%R4FO20zMK?p+&aCMm2oeMK7(aW=UDzr=AO0 z$5mJ%=qRsR8rZ>_YsL+vi{3*J_9Kzq(;ZwRj+4_f0-*wbkSMPWahX#Fj_a8BnrhJ6 zo^ZZ?Vah1@&6#r=JkuaYDBdp;J3@ii+CHM&@9*er&#P}$@wI$bfrH)&c!*|nkvhf%^*Y6b%dKz%QBSIo@U z{?V^qEs4`q<8@n+u8YiB^sc@6g>TncG<|GsmC3egwE6aO=EwLr~3-2 zNr`+)`i+-83?|1Xy0^8ps&pb}YT?w1eWVnC9Ps1=KM;Rw)bH6O!7Did1NwpnqVPZc z*%Qo~qkDL>@^<^fmIBtx$WUWQiNtAB2x-LO^BB=|w~-zTnJNEdm1Ou(?8PF&U88X@ z#8rdaTd||)dG^uJw~N_-%!XNbuAyh4`>Shea=pSj0TqP+w4!`nxsmVSv02kb`DBr% zyX=e>5IJ3JYPtdbCHvKMdhXUO_*E9jc_?se7%VJF#&ZaBD;7+eFN3x+hER7!u&`Wz z7zMvBPR4y`*$a250KYjFhAKS%*XG&c;R-kS0wNY1=836wL6q02mqx;IPcH(6ThA@2 zXKQF|9H>6AW$KUF#^A%l6y5{fel77_+cR_zZ0(7=6bmNXABv}R!B-{(E^O6Y?ZS)n zs1QEmh_Fm7p}oRyT3zxUNr4UV8NGs+2b8|4shO$OGFj3D&7_e?#yDi=TTe%$2QbG5 zk<;q7aQ;p!M-Osm{vFdmXZ@!z9uWh!;*%>(vTRggufuUGP9Hols@vhx z73pn$3u2;vzRvnXuT&$Os7J@6y12*j!{ix%3B4YU1466ItmJs0NsU(4ZYRYh7wEA6q{b*Hs6@k~ zi7Yq@Ax!et0cUMTvk7P%ym){MHpcliHEI~e3HP0NV=}7;xFv#IC?a<=`>~j_sk{e> z7vg-tK*p83HZ0=QK@ zRIHo^r{D8&Ms-^WZp+6US_Quqjh$Q66W^1}=Uz&XJ8AQE9&2}P zY|FXZzZ|0IiaBd2qdt6dIjQr(ZMIOU%NG1F&fu6Po9m^?BvLhI6T0R!H2d8;U(&p2 zYA|MFscMqcO(ye~Jp?F;0>Ke+5hzVr?aBNe>GsGgr$XrpS9uajN2kNQ3o$V5rp0T( z0$6TJC;3)26SNG#XcX7l^MKTn$ga?6r4Jzfb%ZgA(Zbwit0$kY=avSnI$@Gk%+^pu zS5mHrcRS8LFPC*uVWH4DDD1pY$H8N>X?KIJZuZ2SvTqc5Nr0GHdD8TCJcd$zIhOdC zZX0ErnsozQh;t^==4zTfrZO421AL?)O)l#GSxU#|LTTg4#&yeK=^w#;q63!Nv~1(@ zs^-RNRuF&qgcr+bIzc@7$h9L;_yjdifE*$j0Q&Np=1AuHL--zdkv@}`1 zo~LlDl_YAq*z?vmr4M`GjDkl9?p|-tl(DtX76oZv25_DtZutLS9Ez!5~p?th@4 zyc_uax4W#<(#)LMkvo)yp|5tKsC2=p#6PyhpH|449T<9Zdk|%CAb5cw?fhvQtBO&7 zpQ9$24yLqPHP;$N&fe2wm%8qdctwIna<3SwGtQA3{C77s%CW%LYxtK(SBGustL0<( zu~U9r0UOkr(c{OJxZS0Ntu3+cJlF7R`7k-Bsa&q?9Ae5{{|o~?cM+T7{lB1^#vT8R z?>c9fNWey`1dKDY%F3d2O*8^qYhjlB8*7HMKE<*=(A`{>=1%s1}Pm&#_t1xy!FkPk@%SMEka2@*= zxDuM|vJJ5s+xgDls{>*o!7eOcs|xuVBPWX&+y5vEiADK%hi`#Dbd>;;Pbk2H4*-X&R?_-6ZEutSd8hC+sSjhIo z;D(j4P;2EVpEj#UF7IjM6PC+X$C5T&=nL`*!*hm9U)#O?>wqOgC>jXKN3Slk_yaQX zLf|4D8T4k|wHW`;#ZQVocNF|3izi0sOqXzi7@KlYC3CXBG`94wD;tMI1bj|8Vm zY}9`VI9!plSfhAal$M_HlaYOVNU?9Z#0<$o?lXXbX3O(l_?f)i3_~r+GcO-x#+x^X zfsZl0>Rj2iP1rsT;+b;Mr? z4Vu&O)Q5ru4j;qaSP5gA{az@XTS1NpT0d9Xhl_FkkRpcEGA0(QQ~YMh#&zwDUkNzm z6cgkdgl9W{iL6ArJ1TQHqnQ^SQ1WGu?FT|93$Ba}mPCH~!$3}0Y0g zcoG%bdTd$bmBx9Y<`Jc+=Cp4}c@EUfjiz;Rcz101p z=?#i$wo>gBE9|szaZMt-d4nUIhBnYRuBVyx+p?5#aZQgUe(!ah`J#l1$%bl5avL27 zU2~@V`3Ic&!?FhDX@Cw!R4%xtWark#p8DLT)HCZ?VJxf^yr@AD*!ERK3#L$E^*Yr? zzN&uF9Roh4rP+r`Z#7U$tzl6>k!b~HgM$C<_crP=vC>6=q{j?(I}!9>g3rJU(&){o z`R^E*9%+kEa8H_fkD9VT7(Fks&Y-RcHaUJYf-|B+eMXMaRM;{FKRiTB>1(=Iij4k1(X__|WqAd-~t#2@UQ}Z&<1Th0azdXfoll!dd)6>1miA z!&=6sDJm=e$?L&06+Q3`D-HNSkK-3$3DdZMX-6Xjn;wd#9A{~ur!2NcX>(qY_oZL0~H7dnQ9sgLe!W>~2|RSW7|hWn<({Pg*xF$%B-!rKe^_R_vc z(LO!0agxxP;FWPV({8#lEv$&&GVakGus=@!3YVG`y^AO1m{2%Np;>HNA1e{=?ra1C}H zAwT0sbwG|!am;fl?*_t^^#yLDXZ*Nx)_FqueZi0c-G~omtpHW0Cu)mEJ`Z1X8brq$ z%vK##b~o*^b&Hz!hgrD=^6P8}aW40lhzMLB5T5*v`1QH?+L~-@CDi3+C@nRf2{7UE zyDIe{@LKw`Eu=Z%6<<_=#V|yxJIKiq_N?ZJ_v0$c)N4l07ZV_mIXG}glfBSPivOhw z-~+9GdckSpMBNR9eR`Y|9_)sXS+u_OiQ%!9rE(2AFjoxN8lk16Sb~^Sq6kRoEp3yD(mm`HsYIXcag_EAB8MHc}nahxVVUTts~U9P|f;7Ul$_` zStR4v&P4q_$KXOEni$lkxy8=9w8G&47VY0oDb^+jT+>ARe3NHUg~St`$RDxY)?;_F znqTujR&chZd2qHF7y8D$4&E3+e@J~!X3&BW4BF(Ebp#TEjrd+9SU!)j;qH+ZkL@AW z?J6Mj}v0_+D zH0qlbzCkHf|EZ`6c>5ig5NAFF%|La%M-}g(7&}Vx8K)qg30YD;H!S!??{;YivzrH0 z(M%2*b_S-)yh&Aiqai)GF^c!<1Xemj|13>dZ_M#)41SrP;OEMaRJ)bCeX*ZT7W`4Y zQ|8L@NHpD@Tf(5>1U(s5iW~Zdf7$@pAL`a3X@YUv1J>q-uJ_(Dy5nYTCUHC}1(dlI zt;5>DLcHh&jbysqt?G01MhXI3!8wgf){Hv}=0N|L$t8M#L7d6WscO8Om2|NBz2Ga^ zs86y%x$H18)~akOWD7@em7)ldlWgb?_sRN>-EcYQO_}aX@+b$dR{146>{kXWP4$nN{V0_+|3{Lt|8uX_fhKh~i{(x%cj*PU$i{PO(5$uA? zQzO>a6oPj-TUk&{zq?JD2MNb6Mf~V3g$ra+PB;ujLJ2JM(a7N*b`y{MX--!fAd}5C zF$D_b8S;+Np(!cW)(hnv5b@@|EMt*RLKF*wy>ykFhEhlPN~n_Bj>LT9B^_yj>z#fx z3JuE4H&?Cc!;G@}E*3k`HK#8ag`yE3Z1)5JUlSua%qkF zkTu|<9{w9OSi$qr)WD#7EzITnch=xnR63E*d~WGvi*Co9BBE?ETHud;!Z)7&wz+l6 zuKODYG1>I1U#a%&(GNJ`AqRfg=H!BtSl+_;CEeufF-#+*2EMMz-22@>18=8PH{PHd z);mN=aR0MPF>eutLiS#-AOX>#2%+pTGEOj!j4L(m0~&xR=0+g#HNpno6@veLhJp}e zyNVC$a>4;!9&iGvU_dj&xbKt@^t6r%f^)+}eV^suRTLP52+BVs0kOLwg6n`=NUv50E7My8XQUh?y%mW62OT1pMrKI3Q(r`7vU&@93=G~A?b(^pvC-8x=bSk zZ60BQR96WB1Z@9Df(M1IQh+YrU8sEjB=Tc2;(zBn-pete*icZE|M&Uc+oHg`|1o`g zH~m+k=D$o);{Rs)b<9Zo|9_Z6L6QHLNki(N>Dw^^i1LITprZeeqIaT#+)fw)PlllU zldphHC)t!0Gf(i9zgVm>`*TbmITF zH1FZ4{wrjRCx{t^26VK_2srZuWuY*EMAsMrJYFFCH35Ky7bq8<0K|ey2wHnrFMZyr z&^yEgX{{3i@&iE5>xKZ{Ads36G3a!i50D!C4?^~cLB<<|fc1!XN(HJRM)H^21sEs%vv+Mu0h*HkLHaEffMwc0n6)JhNXY#M5w@iO@dfXY z0c6dM2a4Hd1SA*#qYj@jK}uVgAZdaBj8t6uuhUNe>)ne9vfd#C6qLV9+@Q7{MnF#0 zJ7fd-ivG_~u3bVvOzpcw1u~ZSp8-kl(sunnX>L~*K-ByWDM2E8>;Si6kn^58AZQxI xVa^It*?521mj4+UJO?7%w*+`EfEcU=@KhDx-s^WzP+ae~{CgHDE&XryzW}Nww%-5% diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fce..8049c684 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c7873..a69d9cb6 100755 --- a/gradlew +++ b/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f9..53a6b238 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From de583475f923fba15cf548369808ee06fba10a2d Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jul 2022 15:17:48 +0200 Subject: [PATCH 171/938] Update libsignal-service-java --- lib/build.gradle.kts | 2 +- lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java | 5 +++-- .../java/org/asamk/signal/manager/helper/SendHelper.java | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 567afd19..f475d567 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_50") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_51") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.3") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 23423955..7b09aa3e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -564,7 +564,8 @@ class ManagerImpl implements Manager { final var quote = message.quote().get(); messageBuilder.withQuote(new SignalServiceDataMessage.Quote(quote.timestamp(), context.getRecipientHelper() - .resolveSignalServiceAddress(context.getRecipientHelper().resolveRecipient(quote.author())), + .resolveSignalServiceAddress(context.getRecipientHelper().resolveRecipient(quote.author())) + .getServiceId(), quote.message(), List.of(), resolveMentions(quote.mentions()), @@ -651,7 +652,7 @@ class ManagerImpl implements Manager { var targetAuthorRecipientId = context.getRecipientHelper().resolveRecipient(targetAuthor); var reaction = new SignalServiceDataMessage.Reaction(emoji, remove, - context.getRecipientHelper().resolveSignalServiceAddress(targetAuthorRecipientId), + context.getRecipientHelper().resolveSignalServiceAddress(targetAuthorRecipientId).getServiceId(), targetSentTimestamp); final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction); return sendMessage(messageBuilder, recipients); 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 523df2c9..f71133ae 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 @@ -633,7 +633,7 @@ public class SendHelper { message.getTimestamp(), Optional.of(message), message.getExpiresInSeconds(), - Map.of(address, true), + Map.of(address.getServiceId(), true), false, Optional.empty(), Set.of()); From 72293d8d877fcbd9412d4a748db9de2b2a950748 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jul 2022 15:25:58 +0200 Subject: [PATCH 172/938] Bump version to 0.10.9 --- CHANGELOG.md | 9 +++++++++ build.gradle.kts | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d27afb3..8ff851c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +## [0.10.9] - 2022-07-16 + +### Changed +- updateAccount command checks self number and PNI after updating account attributes + +### Fixed +- Fixed small issue with syncing contacts from storage +- Fixed issue with opening older account files + ## [0.10.8] - 2022-06-13 ### Added diff --git a/build.gradle.kts b/build.gradle.kts index bd257e5a..0c5ca9b2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.13" } -version = "0.10.8" +version = "0.10.9" java { sourceCompatibility = JavaVersion.VERSION_17 From 956cb97331c2dfa5371d29aa8ac6fe58c2ef0623 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 24 Jul 2022 10:01:22 +0200 Subject: [PATCH 173/938] Update libsignal-service-java --- graalvm-config-dir/reflect-config.json | 43 +++++++++------ lib/build.gradle.kts | 2 +- .../signal/manager/config/LiveConfig.java | 2 +- .../signal/manager/config/StagingConfig.java | 2 +- .../signal/manager/helper/GroupV2Helper.java | 55 +++++++++++++------ .../signal/manager/helper/ProfileHelper.java | 25 +++++---- .../signal/manager/storage/SignalAccount.java | 2 +- .../storage/profiles/LegacyProfileStore.java | 15 +---- .../profiles/LegacySignalProfileEntry.java | 11 +--- .../storage/profiles/ProfileStore.java | 9 ++- .../manager/storage/recipients/Recipient.java | 26 ++++----- .../storage/recipients/RecipientStore.java | 38 +++++++------ 12 files changed, 122 insertions(+), 108 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 54ed49f6..4c41de46 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -761,7 +761,6 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ - {"name":"date","parameterTypes":[] }, {"name":"description","parameterTypes":[] }, {"name":"image","parameterTypes":[] }, {"name":"title","parameterTypes":[] }, @@ -1041,18 +1040,6 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, -{ - "name":"org.asamk.signal.manager.storage.profiles.SignalProfile", - "allDeclaredFields":true, - "allDeclaredMethods":true, - "allDeclaredConstructors":true -}, -{ - "name":"org.asamk.signal.manager.storage.profiles.SignalProfile$Capabilities", - "allDeclaredFields":true, - "allDeclaredMethods":true, - "allDeclaredConstructors":true -}, { "name":"org.asamk.signal.manager.storage.protocol.LegacyJsonIdentityKeyStore$JsonIdentityKeyStoreDeserializer", "methods":[{"name":"","parameterTypes":[] }] @@ -1634,13 +1621,13 @@ "queryAllDeclaredMethods":true }, { - "name":"org.signal.libsignal.zkgroup.profiles.ProfileKey", + "name":"org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { - "name":"org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential", + "name":"org.signal.libsignal.zkgroup.profiles.ProfileKey", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true @@ -1728,6 +1715,7 @@ {"name":"modifyMemberRoles_"}, {"name":"modifyTitle_"}, {"name":"promotePendingMembers_"}, + {"name":"promotePendingPniAciMembers_"}, {"name":"promoteRequestingMembers_"}, {"name":"revision_"}, {"name":"sourceUuid_"} @@ -1798,7 +1786,11 @@ }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberProfileKeyAction", - "fields":[{"name":"presentation_"}] + "fields":[ + {"name":"presentation_"}, + {"name":"profileKey_"}, + {"name":"userId_"} + ] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberRoleAction", @@ -1817,7 +1809,20 @@ }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromotePendingMemberAction", - "fields":[{"name":"presentation_"}] + "fields":[ + {"name":"presentation_"}, + {"name":"profileKey_"}, + {"name":"userId_"} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromotePendingPniAciMemberProfileKeyAction", + "fields":[ + {"name":"pni_"}, + {"name":"presentation_"}, + {"name":"profileKey_"}, + {"name":"userId_"} + ] }, { "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromoteRequestingMemberAction", @@ -1946,6 +1951,7 @@ {"name":"newTimer_"}, {"name":"newTitle_"}, {"name":"promotePendingMembers_"}, + {"name":"promotePendingPniAciMembers_"}, {"name":"promoteRequestingMembers_"}, {"name":"revision_"} ] @@ -1967,6 +1973,7 @@ "name":"org.signal.storageservice.protos.groups.local.DecryptedMember", "fields":[ {"name":"joinedAtRevision_"}, + {"name":"pni_"}, {"name":"profileKey_"}, {"name":"role_"}, {"name":"uuid_"} @@ -2142,7 +2149,7 @@ "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":[] }] + "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"org.whispersystems.signalservice.api.storage.StorageAuthResponse", diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index f475d567..eb68b368 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_51") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_52") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.3") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") 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 98e1f026..b219830e 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 @@ -46,7 +46,7 @@ class LiveConfig { private final static Optional proxy = Optional.empty(); private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() - .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXQ=="); + .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P"); static SignalServiceConfiguration createDefaultServiceConfiguration( final List interceptors 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 8faec0eb..ca2adc92 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 @@ -46,7 +46,7 @@ class StagingConfig { private final static Optional proxy = Optional.empty(); private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() - .decode("ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXQ=="); + .decode("ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXZSSsOZ6s7M1+rTJN0bI5CKY2PX29y5Ok3jSWufIKcgKOnWoP67d5b2du2ZVJjpjfibNIHbT/cegy/sBLoFwtHogVYUewANUAXIaMPyCLRArsKhfJ5wBtTminG/PAvuBdJ70Z/bXVPf8TVsR292zQ65xwvWTejROW6AZX6aqucUj"); static SignalServiceConfiguration createDefaultServiceConfiguration( final List interceptors 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 9b934580..967c70a2 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 @@ -16,7 +16,7 @@ import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.VerificationFailedException; -import org.signal.libsignal.zkgroup.auth.AuthCredentialResponse; +import org.signal.libsignal.zkgroup.auth.AuthCredentialWithPniResponse; import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.signal.libsignal.zkgroup.groups.GroupSecretParams; import org.signal.libsignal.zkgroup.groups.UuidCiphertext; @@ -41,6 +41,7 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException; import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException; import org.whispersystems.signalservice.api.push.ACI; +import org.whispersystems.signalservice.api.push.PNI; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; @@ -69,7 +70,7 @@ class GroupV2Helper { private final SignalDependencies dependencies; private final Context context; - private HashMap groupApiCredentials; + private HashMap groupApiCredentials; GroupV2Helper(final Context context) { this.dependencies = context.getDependencies(); @@ -177,7 +178,7 @@ class GroupV2Helper { String name, Set members, byte[] avatar ) { final var profileKeyCredential = context.getProfileHelper() - .getRecipientProfileKeyCredential(context.getAccount().getSelfRecipientId()); + .getExpiringProfileKeyCredential(context.getAccount().getSelfRecipientId()); if (profileKeyCredential == null) { logger.warn("Cannot create a V2 group as self does not have a versioned profile"); return null; @@ -185,7 +186,7 @@ class GroupV2Helper { final var self = new GroupCandidate(getSelfAci().uuid(), Optional.of(profileKeyCredential)); final var memberList = new ArrayList<>(members); - final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream(); + final var credentials = context.getProfileHelper().getExpiringProfileKeyCredential(memberList).stream(); final var uuids = memberList.stream() .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()); var candidates = Utils.zip(uuids, @@ -234,7 +235,7 @@ class GroupV2Helper { GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var memberList = new ArrayList<>(newMembers); - final var credentials = context.getProfileHelper().getRecipientProfileKeyCredential(memberList).stream(); + final var credentials = context.getProfileHelper().getExpiringProfileKeyCredential(memberList).stream(); final var uuids = memberList.stream() .map(member -> context.getRecipientHelper().resolveSignalServiceAddress(member).getServiceId().uuid()); var candidates = Utils.zip(uuids, @@ -396,7 +397,7 @@ class GroupV2Helper { logger.debug("Updating own profile key in group " + groupInfoV2.getGroupId().toBase64()); final var selfRecipientId = context.getAccount().getSelfRecipientId(); - final var profileKeyCredential = context.getProfileHelper().getRecipientProfileKeyCredential(selfRecipientId); + final var profileKeyCredential = context.getProfileHelper().getExpiringProfileKeyCredential(selfRecipientId); if (profileKeyCredential == null) { logger.trace("Cannot update profile key as self does not have a versioned profile"); return null; @@ -417,7 +418,7 @@ class GroupV2Helper { final var groupOperations = dependencies.getGroupsV2Operations().forGroup(groupSecretParams); final var selfRecipientId = context.getAccount().getSelfRecipientId(); - final var profileKeyCredential = context.getProfileHelper().getRecipientProfileKeyCredential(selfRecipientId); + final var profileKeyCredential = context.getProfileHelper().getExpiringProfileKeyCredential(selfRecipientId); if (profileKeyCredential == null) { throw new IOException("Cannot join a V2 group as self does not have a versioned profile"); } @@ -439,7 +440,7 @@ class GroupV2Helper { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var selfRecipientId = context.getAccount().getSelfRecipientId(); - final var profileKeyCredential = context.getProfileHelper().getRecipientProfileKeyCredential(selfRecipientId); + final var profileKeyCredential = context.getProfileHelper().getExpiringProfileKeyCredential(selfRecipientId); if (profileKeyCredential == null) { throw new IOException("Cannot join a V2 group as self does not have a versioned profile"); } @@ -609,31 +610,49 @@ class GroupV2Helper { return null; } - private static int currentTimeDays() { - return (int) TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()); + private static long currentDaySeconds() { + return TimeUnit.DAYS.toSeconds(TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis())); } private GroupsV2AuthorizationString getGroupAuthForToday( final GroupSecretParams groupSecretParams ) throws IOException { - final var today = currentTimeDays(); - if (groupApiCredentials == null || !groupApiCredentials.containsKey(today)) { + final var todaySeconds = currentDaySeconds(); + if (groupApiCredentials == null || !groupApiCredentials.containsKey(todaySeconds)) { // Returns credentials for the next 7 days - final var isAci = true; // TODO enable group handling with PNI - groupApiCredentials = dependencies.getGroupsV2Api().getCredentials(today, isAci); + groupApiCredentials = dependencies.getGroupsV2Api().getCredentials(todaySeconds); // TODO cache credentials on disk until they expire } - var authCredentialResponse = groupApiCredentials.get(today); - final var aci = getSelfAci(); try { - return dependencies.getGroupsV2Api() - .getGroupsV2AuthorizationString(aci, today, groupSecretParams, authCredentialResponse); + return getAuthorizationString(groupSecretParams, todaySeconds); + } catch (VerificationFailedException e) { + logger.debug("Group api credentials invalid, renewing and trying again."); + groupApiCredentials.clear(); + } + + groupApiCredentials = dependencies.getGroupsV2Api().getCredentials(todaySeconds); + try { + return getAuthorizationString(groupSecretParams, todaySeconds); } catch (VerificationFailedException e) { throw new IOException(e); } } + private GroupsV2AuthorizationString getAuthorizationString( + final GroupSecretParams groupSecretParams, final long todaySeconds + ) throws VerificationFailedException { + var authCredentialResponse = groupApiCredentials.get(todaySeconds); + final var aci = getSelfAci(); + final var pni = getSelfPni(); + return dependencies.getGroupsV2Api() + .getGroupsV2AuthorizationString(aci, pni, todaySeconds, groupSecretParams, authCredentialResponse); + } + private ACI getSelfAci() { return context.getAccount().getAci(); } + + private PNI getSelfPni() { + return context.getAccount().getPni(); + } } 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 a3f3b480..f4497e5d 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 @@ -16,8 +16,8 @@ import org.asamk.signal.manager.util.ProfileUtils; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.InvalidKeyException; +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; @@ -28,6 +28,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.services.ProfileService; +import org.whispersystems.signalservice.api.util.ExpiringProfileCredentialUtil; import java.io.File; import java.io.IOException; @@ -106,11 +107,12 @@ public final class ProfileHelper { getRecipientProfiles(recipientIds, true); } - public List getRecipientProfileKeyCredential(List recipientIds) { + public List getExpiringProfileKeyCredential(List recipientIds) { try { account.getRecipientStore().setBulkUpdating(true); final var profileFetches = Flowable.fromIterable(recipientIds) - .filter(recipientId -> account.getProfileStore().getProfileKeyCredential(recipientId) == null) + .filter(recipientId -> !ExpiringProfileCredentialUtil.isValid(account.getProfileStore() + .getExpiringProfileKeyCredential(recipientId))) .map(recipientId -> retrieveProfile(recipientId, SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL).onErrorComplete()); Maybe.merge(profileFetches, 10).blockingSubscribe(); @@ -118,12 +120,12 @@ public final class ProfileHelper { account.getRecipientStore().setBulkUpdating(false); } - return recipientIds.stream().map(r -> account.getProfileStore().getProfileKeyCredential(r)).toList(); + return recipientIds.stream().map(r -> account.getProfileStore().getExpiringProfileKeyCredential(r)).toList(); } - public ProfileKeyCredential getRecipientProfileKeyCredential(RecipientId recipientId) { - var profileKeyCredential = account.getProfileStore().getProfileKeyCredential(recipientId); - if (profileKeyCredential != null) { + public ExpiringProfileKeyCredential getExpiringProfileKeyCredential(RecipientId recipientId) { + var profileKeyCredential = account.getProfileStore().getExpiringProfileKeyCredential(recipientId); + if (ExpiringProfileCredentialUtil.isValid(profileKeyCredential)) { return profileKeyCredential; } @@ -134,7 +136,7 @@ public final class ProfileHelper { return null; } - return account.getProfileStore().getProfileKeyCredential(recipientId); + return account.getProfileStore().getExpiringProfileKeyCredential(recipientId); } /** @@ -327,10 +329,11 @@ public final class ProfileHelper { final var encryptedProfile = p.getProfile(); if (requestType == SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL - || account.getProfileStore().getProfileKeyCredential(recipientId) == null) { + || !ExpiringProfileCredentialUtil.isValid(account.getProfileStore() + .getExpiringProfileKeyCredential(recipientId))) { logger.trace("Storing profile credential"); - final var profileKeyCredential = p.getProfileKeyCredential().orElse(null); - account.getProfileStore().storeProfileKeyCredential(recipientId, profileKeyCredential); + final var profileKeyCredential = p.getExpiringProfileKeyCredential().orElse(null); + account.getProfileStore().storeExpiringProfileKeyCredential(recipientId, profileKeyCredential); } final var profile = account.getProfileStore().getProfile(recipientId); 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 5e3e7d04..b797290a 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 @@ -756,7 +756,7 @@ public class SignalAccount implements Closeable { final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class); for (var profileEntry : legacyProfileStore.getProfileEntries()) { var recipientId = getRecipientResolver().resolveRecipient(profileEntry.getAddress()); - getProfileStore().storeProfileKeyCredential(recipientId, profileEntry.getProfileKeyCredential()); + // Not migrating profile key credential here, it was changed to expiring profile key credentials getProfileStore().storeProfileKey(recipientId, profileEntry.getProfileKey()); final var profile = profileEntry.getProfile(); if (profile != 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 be275116..99e2980e 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 @@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.IOException; @@ -51,21 +50,9 @@ public class LegacyProfileStore { profileKey = new ProfileKey(Base64.getDecoder().decode(entry.get("profileKey").asText())); } catch (InvalidInputException ignored) { } - ProfileKeyCredential profileKeyCredential = null; - if (entry.hasNonNull("profileKeyCredential")) { - try { - profileKeyCredential = new ProfileKeyCredential(Base64.getDecoder() - .decode(entry.get("profileKeyCredential").asText())); - } catch (Throwable ignored) { - } - } var lastUpdateTimestamp = entry.get("lastUpdateTimestamp").asLong(); var profile = jsonProcessor.treeToValue(entry.get("profile"), LegacySignalProfile.class); - profileEntries.add(new LegacySignalProfileEntry(address, - profileKey, - lastUpdateTimestamp, - profile, - profileKeyCredential)); + profileEntries.add(new LegacySignalProfileEntry(address, profileKey, lastUpdateTimestamp, profile)); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java index 609d2f54..3acad2b1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/LegacySignalProfileEntry.java @@ -2,7 +2,6 @@ package org.asamk.signal.manager.storage.profiles; import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; public class LegacySignalProfileEntry { @@ -14,20 +13,16 @@ public class LegacySignalProfileEntry { private final LegacySignalProfile profile; - private final ProfileKeyCredential profileKeyCredential; - public LegacySignalProfileEntry( final RecipientAddress address, final ProfileKey profileKey, final long lastUpdateTimestamp, - final LegacySignalProfile profile, - final ProfileKeyCredential profileKeyCredential + final LegacySignalProfile profile ) { this.address = address; this.profileKey = profileKey; this.lastUpdateTimestamp = lastUpdateTimestamp; this.profile = profile; - this.profileKeyCredential = profileKeyCredential; } public RecipientAddress getAddress() { @@ -45,8 +40,4 @@ public class LegacySignalProfileEntry { public LegacySignalProfile getProfile() { return profile; } - - public ProfileKeyCredential getProfileKeyCredential() { - return profileKeyCredential; - } } 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 df65db0d..acdcbb18 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 @@ -2,8 +2,8 @@ package org.asamk.signal.manager.storage.profiles; import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; public interface ProfileStore { @@ -11,7 +11,7 @@ public interface ProfileStore { ProfileKey getProfileKey(RecipientId recipientId); - ProfileKeyCredential getProfileKeyCredential(RecipientId recipientId); + ExpiringProfileKeyCredential getExpiringProfileKeyCredential(RecipientId recipientId); void storeProfile(RecipientId recipientId, Profile profile); @@ -19,5 +19,8 @@ public interface ProfileStore { void storeProfileKey(RecipientId recipientId, ProfileKey profileKey); - void storeProfileKeyCredential(RecipientId recipientId, ProfileKeyCredential profileKeyCredential); + void storeExpiringProfileKeyCredential( + RecipientId recipientId, + ExpiringProfileKeyCredential expiringProfileKeyCredential + ); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java index d5c68315..2d3c8430 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/Recipient.java @@ -1,7 +1,7 @@ package org.asamk.signal.manager.storage.recipients; +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import java.util.Objects; @@ -15,7 +15,7 @@ public class Recipient { private final ProfileKey profileKey; - private final ProfileKeyCredential profileKeyCredential; + private final ExpiringProfileKeyCredential expiringProfileKeyCredential; private final Profile profile; @@ -24,14 +24,14 @@ public class Recipient { final RecipientAddress address, final Contact contact, final ProfileKey profileKey, - final ProfileKeyCredential profileKeyCredential, + final ExpiringProfileKeyCredential expiringProfileKeyCredential, final Profile profile ) { this.recipientId = recipientId; this.address = address; this.contact = contact; this.profileKey = profileKey; - this.profileKeyCredential = profileKeyCredential; + this.expiringProfileKeyCredential = expiringProfileKeyCredential; this.profile = profile; } @@ -40,7 +40,7 @@ public class Recipient { address = builder.address; contact = builder.contact; profileKey = builder.profileKey; - profileKeyCredential = builder.profileKeyCredential; + expiringProfileKeyCredential = builder.expiringProfileKeyCredential1; profile = builder.profile; } @@ -54,7 +54,7 @@ public class Recipient { builder.address = copy.getAddress(); builder.contact = copy.getContact(); builder.profileKey = copy.getProfileKey(); - builder.profileKeyCredential = copy.getProfileKeyCredential(); + builder.expiringProfileKeyCredential1 = copy.getExpiringProfileKeyCredential(); builder.profile = copy.getProfile(); return builder; } @@ -75,8 +75,8 @@ public class Recipient { return profileKey; } - public ProfileKeyCredential getProfileKeyCredential() { - return profileKeyCredential; + public ExpiringProfileKeyCredential getExpiringProfileKeyCredential() { + return expiringProfileKeyCredential; } public Profile getProfile() { @@ -92,13 +92,13 @@ public class Recipient { && Objects.equals(address, recipient.address) && Objects.equals(contact, recipient.contact) && Objects.equals(profileKey, recipient.profileKey) - && Objects.equals(profileKeyCredential, recipient.profileKeyCredential) + && Objects.equals(expiringProfileKeyCredential, recipient.expiringProfileKeyCredential) && Objects.equals(profile, recipient.profile); } @Override public int hashCode() { - return Objects.hash(recipientId, address, contact, profileKey, profileKeyCredential, profile); + return Objects.hash(recipientId, address, contact, profileKey, expiringProfileKeyCredential, profile); } public static final class Builder { @@ -107,7 +107,7 @@ public class Recipient { private RecipientAddress address; private Contact contact; private ProfileKey profileKey; - private ProfileKeyCredential profileKeyCredential; + private ExpiringProfileKeyCredential expiringProfileKeyCredential1; private Profile profile; private Builder() { @@ -133,8 +133,8 @@ public class Recipient { return this; } - public Builder withProfileKeyCredential(final ProfileKeyCredential val) { - profileKeyCredential = val; + public Builder withExpiringProfileKeyCredential(final ExpiringProfileKeyCredential val) { + expiringProfileKeyCredential1 = val; return this; } 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 cc9db862..7244a96c 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 @@ -8,8 +8,8 @@ import org.asamk.signal.manager.storage.Utils; import org.asamk.signal.manager.storage.contacts.ContactsStore; import org.asamk.signal.manager.storage.profiles.ProfileStore; import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.ACI; @@ -89,11 +89,11 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv } } - ProfileKeyCredential profileKeyCredential = null; - if (r.profileKeyCredential != null) { + ExpiringProfileKeyCredential expiringProfileKeyCredential = null; + if (r.expiringProfileKeyCredential != null) { try { - profileKeyCredential = new ProfileKeyCredential(Base64.getDecoder() - .decode(r.profileKeyCredential)); + expiringProfileKeyCredential = new ExpiringProfileKeyCredential(Base64.getDecoder() + .decode(r.expiringProfileKeyCredential)); } catch (Throwable ignored) { } } @@ -116,7 +116,7 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv .collect(Collectors.toSet())); } - return new Recipient(recipientId, address, contact, profileKey, profileKeyCredential, profile); + return new Recipient(recipientId, address, contact, profileKey, expiringProfileKeyCredential, profile); }).collect(Collectors.toMap(Recipient::getRecipientId, r -> r)); recipientStore.addRecipients(recipients); @@ -333,9 +333,9 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv } @Override - public ProfileKeyCredential getProfileKeyCredential(final RecipientId recipientId) { + public ExpiringProfileKeyCredential getExpiringProfileKeyCredential(final RecipientId recipientId) { final var recipient = getRecipient(recipientId); - return recipient == null ? null : recipient.getProfileKeyCredential(); + return recipient == null ? null : recipient.getExpiringProfileKeyCredential(); } @Override @@ -371,7 +371,7 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv final var builder = Recipient.newBuilder(recipient) .withProfileKey(profileKey) - .withProfileKeyCredential(null); + .withExpiringProfileKeyCredential(null); if (resetProfile) { builder.withProfile(recipient.getProfile() == null ? null @@ -383,11 +383,15 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv } @Override - public void storeProfileKeyCredential(RecipientId recipientId, final ProfileKeyCredential profileKeyCredential) { + public void storeExpiringProfileKeyCredential( + RecipientId recipientId, final ExpiringProfileKeyCredential expiringProfileKeyCredential + ) { synchronized (recipients) { final var recipient = recipients.get(recipientId); storeRecipientLocked(recipientId, - Recipient.newBuilder(recipient).withProfileKeyCredential(profileKeyCredential).build()); + Recipient.newBuilder(recipient) + .withExpiringProfileKeyCredential(expiringProfileKeyCredential) + .build()); } } @@ -535,9 +539,9 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv recipient.getProfileKey() != null ? recipient.getProfileKey() : toBeMergedRecipient.getProfileKey(), - recipient.getProfileKeyCredential() != null - ? recipient.getProfileKeyCredential() - : toBeMergedRecipient.getProfileKeyCredential(), + recipient.getExpiringProfileKeyCredential() != null + ? recipient.getExpiringProfileKeyCredential() + : toBeMergedRecipient.getExpiringProfileKeyCredential(), recipient.getProfile() != null ? recipient.getProfile() : toBeMergedRecipient.getProfile())); recipients.remove(toBeMergedRecipientId); recipientsMerged.put(toBeMergedRecipientId.id(), recipientId.id()); @@ -607,9 +611,9 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv recipient.getProfileKey() == null ? null : base64.encodeToString(recipient.getProfileKey().serialize()), - recipient.getProfileKeyCredential() == null + recipient.getExpiringProfileKeyCredential() == null ? null - : base64.encodeToString(recipient.getProfileKeyCredential().serialize()), + : base64.encodeToString(recipient.getExpiringProfileKeyCredential().serialize()), contact, profile); }).toList(), lastId); @@ -634,7 +638,7 @@ public class RecipientStore implements RecipientResolver, RecipientTrustedResolv String number, String uuid, String profileKey, - String profileKeyCredential, + String expiringProfileKeyCredential, Storage.Recipient.Contact contact, Storage.Recipient.Profile profile ) { From 1cc21834e2429a5efa27b1766dcff9d6b3151ddf Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 27 Jul 2022 18:20:07 +0200 Subject: [PATCH 174/938] Workaround possible GraalVM issue Fixes #987 --- src/main/java/org/asamk/signal/util/IOUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/util/IOUtils.java b/src/main/java/org/asamk/signal/util/IOUtils.java index 9f220946..2d4ffd8e 100644 --- a/src/main/java/org/asamk/signal/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/util/IOUtils.java @@ -125,7 +125,7 @@ public class IOUtils { UnixDomainPrincipal principal = null; try { principal = channel.getOption(ExtendedSocketOptions.SO_PEERCRED); - } catch (UnsupportedOperationException ignored) { + } catch (UnsupportedOperationException | NoClassDefFoundError ignored) { } return principal; } From 7a427372872580e6d4ce740e668101dbc2a1f536 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 27 Jul 2022 20:58:58 +0200 Subject: [PATCH 175/938] Workaround possible GraalVM issue --- src/main/java/org/asamk/signal/util/IOUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/asamk/signal/util/IOUtils.java b/src/main/java/org/asamk/signal/util/IOUtils.java index 2d4ffd8e..9e0d6c3e 100644 --- a/src/main/java/org/asamk/signal/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/util/IOUtils.java @@ -121,13 +121,13 @@ public class IOUtils { return socketAddress; } - public static UnixDomainPrincipal getUnixDomainPrincipal(final SocketChannel channel) throws IOException { + public static String getUnixDomainPrincipal(final SocketChannel channel) throws IOException { UnixDomainPrincipal principal = null; try { principal = channel.getOption(ExtendedSocketOptions.SO_PEERCRED); } catch (UnsupportedOperationException | NoClassDefFoundError ignored) { } - return principal; + return principal == null ? null : principal.toString(); } public static ServerSocketChannel bindSocket(final SocketAddress address) throws IOErrorException { From d69b9ea394dc4ac8298139480700611316bdb444 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 28 Jul 2022 16:57:49 +0200 Subject: [PATCH 176/938] Update KBS enclave Fixes #985 --- .../java/org/asamk/signal/manager/config/LiveConfig.java | 6 +++--- .../java/org/asamk/signal/manager/config/StagingConfig.java | 6 +++--- 2 files changed, 6 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 b219830e..41eab6cf 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,10 +28,10 @@ class LiveConfig { .decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"); private final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15"; - private final static String KEY_BACKUP_ENCLAVE_NAME = "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe"; + private final static String KEY_BACKUP_ENCLAVE_NAME = "0cedba03535b41b67729ce9924185f831d7767928a1d1689acb689bc079c375f"; private final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode( - "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe"); - private final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87"; + "187d2739d22be65e74b65f0055e74d31310e4267e5fac2b1246cc8beba81af39"); + private final static String KEY_BACKUP_MRENCLAVE = "ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba"; private final static String URL = "https://chat.signal.org"; private final static String CDN_URL = "https://cdn.signal.org"; 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 ca2adc92..771e9348 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,10 +28,10 @@ class StagingConfig { .decode("BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx"); private final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15"; - private final static String KEY_BACKUP_ENCLAVE_NAME = "823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9"; + private final static String KEY_BACKUP_ENCLAVE_NAME = "dd6f66d397d9e8cf6ec6db238e59a7be078dd50e9715427b9c89b409ffe53f99"; private final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode( - "16b94ac6d2b7f7b9d72928f36d798dbb35ed32e7bb14c42b4301ad0344b46f29"); - private final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87"; + "4200003414528c151e2dccafbc87aa6d3d66a5eb8f8c05979a6e97cb33cd493a"); + private final static String KEY_BACKUP_MRENCLAVE = "ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba"; private final static String URL = "https://chat.staging.signal.org"; private final static String CDN_URL = "https://cdn-staging.signal.org"; From d8a5244a8861cde08c954e128e3267e705e6ae5f Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 30 Jul 2022 14:56:17 +0200 Subject: [PATCH 177/938] Bump version to 0.10.10 --- CHANGELOG.md | 5 +++++ build.gradle.kts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ff851c8..5d70bb27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +## [0.10.10] - 2022-07-30 +### Fixed +- Fix setPin/removePin commands which broke due to server side changes +- Workaround GraalVM 22.2.0 issue with daemon connection + ## [0.10.9] - 2022-07-16 ### Changed diff --git a/build.gradle.kts b/build.gradle.kts index 0c5ca9b2..35960cbc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.13" } -version = "0.10.9" +version = "0.10.10" java { sourceCompatibility = JavaVersion.VERSION_17 From 525f04d44606cd11d0bf15b2bed7ec2dc1bff500 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 30 Jul 2022 16:52:13 +0200 Subject: [PATCH 178/938] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d70bb27..d2083b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## [Unreleased] ## [0.10.10] - 2022-07-30 +**Attention**: Now requires native libsignal-client version 0.18.1 + ### Fixed - Fix setPin/removePin commands which broke due to server side changes - Workaround GraalVM 22.2.0 issue with daemon connection From 597ac9b5042bec57c845b4ca08ce935d84ef63d4 Mon Sep 17 00:00:00 2001 From: Sebastian Scheibner Date: Sat, 30 Jul 2022 16:57:48 +0200 Subject: [PATCH 179/938] Update FUNDING.yml --- FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/FUNDING.yml b/FUNDING.yml index 909168ff..c2420609 100644 --- a/FUNDING.yml +++ b/FUNDING.yml @@ -1,3 +1,4 @@ +github: AsamK liberapay: asamk ko_fi: asamk #bitcoin: bc1qykae53fry8a8ycgdzgv0rlxfc959hmmllvz698 From 81e36d4f9b690df2c39d772f9b3e99422329ff27 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 2 Aug 2022 23:20:26 +0200 Subject: [PATCH 180/938] Update libsignal-service-java --- graalvm-config-dir/reflect-config.json | 4 ++- lib/build.gradle.kts | 2 +- .../signal/manager/helper/SendHelper.java | 32 +++++++++++++------ .../storage/sendLog/MessageSendLogEntry.java | 2 +- .../storage/sendLog/MessageSendLogStore.java | 16 ++++++---- .../manager/util/MessageCacheUtils.java | 13 ++++++-- 6 files changed, 47 insertions(+), 22 deletions(-) diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 4c41de46..c223851b 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -2806,7 +2806,8 @@ {"name":"sourceE164_"}, {"name":"sourceUuid_"}, {"name":"timestamp_"}, - {"name":"type_"} + {"name":"type_"}, + {"name":"urgent_"} ] }, { @@ -3052,6 +3053,7 @@ "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent", "fields":[ {"name":"bitField0_"}, + {"name":"destinationE164_"}, {"name":"destinationUuid_"}, {"name":"expirationStartTimestamp_"}, {"name":"isRecipientUpdate_"}, diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index eb68b368..70de8dd7 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_52") + implementation("com.github.turasa", "signal-service-java", "2.15.3_unofficial_53") implementation("com.fasterxml.jackson.core", "jackson-databind", "2.13.3") implementation("com.google.protobuf", "protobuf-javalite", "3.11.4") implementation("org.bouncycastle", "bcprov-jdk15on", "1.70") 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 f71133ae..717db812 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 @@ -123,7 +123,7 @@ public class SendHelper { (messageSender, address, unidentifiedAccess) -> messageSender.sendReceipt(address, unidentifiedAccess, receiptMessage)); - messageSendLogStore.insertIfPossible(receiptMessage.getWhen(), result, ContentHint.IMPLICIT); + messageSendLogStore.insertIfPossible(receiptMessage.getWhen(), result, ContentHint.IMPLICIT, false); handleSendMessageResult(result); return result; } @@ -140,7 +140,8 @@ public class SendHelper { unidentifiedAccess, ContentHint.IMPLICIT, message, - SignalServiceMessageSender.IndividualSendEvents.EMPTY)); + SignalServiceMessageSender.IndividualSendEvents.EMPTY, + false)); } public SendMessageResult sendRetryReceipt( @@ -237,7 +238,8 @@ public class SendHelper { timestamp, messageSendLogEntry.content(), messageSendLogEntry.contentHint(), - Optional.empty())); + Optional.empty(), + messageSendLogEntry.urgent())); } final var groupId = messageSendLogEntry.groupId().get(); @@ -266,7 +268,8 @@ public class SendHelper { timestamp, contentToSend, messageSendLogEntry.contentHint(), - Optional.of(group.getGroupId().serialize()))); + Optional.of(group.getGroupId().serialize()), + messageSendLogEntry.urgent())); if (result.isSuccess()) { final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId); @@ -315,6 +318,7 @@ public class SendHelper { final var messageSendLogStore = account.getMessageSendLogStore(); final AtomicLong entryId = new AtomicLong(-1); + final var urgent = true; final LegacySenderHandler legacySender = (recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendDataMessage( recipients, unidentifiedAccess, @@ -328,14 +332,16 @@ public class SendHelper { if (entryId.get() == -1) { final var newId = messageSendLogStore.insertIfPossible(message.getTimestamp(), sendResult, - contentHint); + contentHint, + urgent); entryId.set(newId); } else { messageSendLogStore.addRecipientToExistingEntryIfPossible(entryId.get(), sendResult); } } }, - () -> false); + () -> false, + urgent); final SenderKeySenderHandler senderKeySender = (distId, recipients, unidentifiedAccess, isRecipientUpdate) -> { final var res = messageSender.sendGroupDataMessage(distId, recipients, @@ -343,10 +349,14 @@ public class SendHelper { isRecipientUpdate, contentHint, message, - SignalServiceMessageSender.SenderKeyGroupEvents.EMPTY); + SignalServiceMessageSender.SenderKeyGroupEvents.EMPTY, + urgent); synchronized (entryId) { if (entryId.get() == -1) { - final var newId = messageSendLogStore.insertIfPossible(message.getTimestamp(), res, contentHint); + final var newId = messageSendLogStore.insertIfPossible(message.getTimestamp(), + res, + contentHint, + urgent); entryId.set(newId); } else { messageSendLogStore.addRecipientToExistingEntryIfPossible(entryId.get(), res); @@ -582,13 +592,15 @@ public class SendHelper { SignalServiceDataMessage message, RecipientId recipientId ) { final var messageSendLogStore = account.getMessageSendLogStore(); + final var urgent = true; final var result = handleSendMessage(recipientId, (messageSender, address, unidentifiedAccess) -> messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.RESENDABLE, message, - SignalServiceMessageSender.IndividualSendEvents.EMPTY)); - messageSendLogStore.insertIfPossible(message.getTimestamp(), result, ContentHint.RESENDABLE); + SignalServiceMessageSender.IndividualSendEvents.EMPTY, + urgent)); + messageSendLogStore.insertIfPossible(message.getTimestamp(), result, ContentHint.RESENDABLE, urgent); handleSendMessageResult(result); return result; } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java index 31a4252a..67ca5cd4 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogEntry.java @@ -7,5 +7,5 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import java.util.Optional; public record MessageSendLogEntry( - Optional groupId, SignalServiceProtos.Content content, ContentHint contentHint + Optional groupId, SignalServiceProtos.Content content, ContentHint contentHint, boolean urgent ) {} 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 c893f09c..b53c5ac0 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 @@ -117,7 +117,8 @@ public class MessageSendLogStore implements AutoCloseable { return null; } final var contentHint = ContentHint.fromType(resultSet.getInt("content_hint")); - return new MessageSendLogEntry(groupId, content, contentHint); + final var urgent = true; // TODO + return new MessageSendLogEntry(groupId, content, contentHint, urgent); })) { return result.filter(Objects::nonNull) .filter(e -> !isSenderKey || e.groupId().isPresent()) @@ -131,7 +132,7 @@ public class MessageSendLogStore implements AutoCloseable { } public long insertIfPossible( - long sentTimestamp, SendMessageResult sendMessageResult, ContentHint contentHint + long sentTimestamp, SendMessageResult sendMessageResult, ContentHint contentHint, boolean urgent ) { final RecipientDevices recipientDevice = getRecipientDevices(sendMessageResult); if (recipientDevice == null) { @@ -141,11 +142,12 @@ public class MessageSendLogStore implements AutoCloseable { return insert(List.of(recipientDevice), sentTimestamp, sendMessageResult.getSuccess().getContent().get(), - contentHint); + contentHint, + urgent); } public long insertIfPossible( - long sentTimestamp, List sendMessageResults, ContentHint contentHint + long sentTimestamp, List sendMessageResults, ContentHint contentHint, boolean urgent ) { final var recipientDevices = sendMessageResults.stream() .map(this::getRecipientDevices) @@ -161,7 +163,7 @@ public class MessageSendLogStore implements AutoCloseable { .findFirst() .get(); - return insert(recipientDevices, sentTimestamp, content, contentHint); + return insert(recipientDevices, sentTimestamp, content, contentHint, urgent); } public void addRecipientToExistingEntryIfPossible(final long contentId, final SendMessageResult sendMessageResult) { @@ -272,10 +274,12 @@ public class MessageSendLogStore implements AutoCloseable { final List recipientDevices, final long sentTimestamp, final SignalServiceProtos.Content content, - final ContentHint contentHint + final ContentHint contentHint, + final boolean urgent ) { byte[] groupId = getGroupId(content); + // TODO store urgent final var sql = """ INSERT INTO %s (timestamp, group_id, content, content_hint) VALUES (?,?,?,?) diff --git a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java index 347e3d81..43badfa6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java @@ -19,7 +19,8 @@ public class MessageCacheUtils { try (var f = new FileInputStream(file)) { var in = new DataInputStream(f); var version = in.readInt(); - if (version > 5) { + if (version > 6) { + // Unsupported envelope version return null; } var type = in.readInt(); @@ -63,6 +64,10 @@ public class MessageCacheUtils { if (version >= 4) { serverDeliveredTimestamp = in.readLong(); } + boolean isUrgent = true; + if (version >= 6) { + isUrgent = in.readBoolean(); + } Optional addressOptional = sourceServiceId == null ? Optional.empty() : Optional.of(new SignalServiceAddress(sourceServiceId, source)); @@ -75,14 +80,15 @@ public class MessageCacheUtils { serverReceivedTimestamp, serverDeliveredTimestamp, uuid, - destinationUuid == null ? UuidUtil.UNKNOWN_UUID.toString() : destinationUuid); + destinationUuid == null ? UuidUtil.UNKNOWN_UUID.toString() : destinationUuid, + isUrgent); } } public static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException { try (var f = new FileOutputStream(file)) { try (var out = new DataOutputStream(f)) { - out.writeInt(5); // version + out.writeInt(6); // version out.writeInt(envelope.getType()); out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : ""); out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : ""); @@ -105,6 +111,7 @@ public class MessageCacheUtils { var uuid = envelope.getServerGuid(); out.writeUTF(uuid == null ? "" : uuid); out.writeLong(envelope.getServerDeliveredTimestamp()); + out.writeBoolean(envelope.isUrgent()); } } } From a593051512b716ed3cc42a1a7b69d49a459352ed Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 8 Aug 2022 18:12:30 +0200 Subject: [PATCH 181/938] Implement receive handling for story messages --- graalvm-config-dir/reflect-config.json | 82 ++++++++++++++ .../org/asamk/signal/manager/api/Color.java | 24 ++++ .../signal/manager/api/MessageEnvelope.java | 103 +++++++++++++++++- .../helper/IncomingMessageHandler.java | 103 ++++++++++++++---- .../asamk/signal/ReceiveMessageHandler.java | 42 +++++++ .../asamk/signal/dbus/DbusManagerImpl.java | 8 +- .../asamk/signal/json/JsonDataMessage.java | 9 +- .../signal/json/JsonMessageEnvelope.java | 3 + .../asamk/signal/json/JsonStoryContext.java | 16 +++ .../asamk/signal/json/JsonStoryMessage.java | 52 +++++++++ .../asamk/signal/json/JsonSyncMessage.java | 22 +--- .../signal/json/JsonSyncStoryMessage.java | 26 +++++ 12 files changed, 443 insertions(+), 47 deletions(-) create mode 100644 lib/src/main/java/org/asamk/signal/manager/api/Color.java create mode 100644 src/main/java/org/asamk/signal/json/JsonStoryContext.java create mode 100644 src/main/java/org/asamk/signal/json/JsonStoryMessage.java create mode 100644 src/main/java/org/asamk/signal/json/JsonSyncStoryMessage.java diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index c223851b..389c947c 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -837,6 +837,55 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.json.JsonStoryContext", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"authorNumber","parameterTypes":[] }, + {"name":"authorUuid","parameterTypes":[] }, + {"name":"sentTimestamp","parameterTypes":[] } + ] +}, +{ + "name":"org.asamk.signal.json.JsonStoryMessage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"allowsReplies","parameterTypes":[] }, + {"name":"fileAttachment","parameterTypes":[] }, + {"name":"groupId","parameterTypes":[] }, + {"name":"textAttachment","parameterTypes":[] } + ] +}, +{ + "name":"org.asamk.signal.json.JsonStoryMessage$TextAttachment", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"backgroundColor","parameterTypes":[] }, + {"name":"backgroundGradient","parameterTypes":[] }, + {"name":"preview","parameterTypes":[] }, + {"name":"style","parameterTypes":[] }, + {"name":"text","parameterTypes":[] }, + {"name":"textBackgroundColor","parameterTypes":[] }, + {"name":"textForegroundColor","parameterTypes":[] } + ] +}, +{ + "name":"org.asamk.signal.json.JsonStoryMessage$TextAttachment$Gradient", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"angle","parameterTypes":[] }, + {"name":"endColor","parameterTypes":[] }, + {"name":"startColor","parameterTypes":[] } + ] +}, { "name":"org.asamk.signal.json.JsonSyncDataMessage", "allDeclaredFields":true, @@ -860,6 +909,17 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.json.JsonSyncStoryMessage", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"dataMessage","parameterTypes":[] }, + {"name":"destinationNumber","parameterTypes":[] }, + {"name":"destinationUuid","parameterTypes":[] } + ] +}, { "name":"org.asamk.signal.json.JsonTypingMessage", "allDeclaredFields":true, @@ -3106,6 +3166,28 @@ {"name":"timestamp_"} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TextAttachment", + "fields":[ + {"name":"backgroundCase_"}, + {"name":"background_"}, + {"name":"bitField0_"}, + {"name":"preview_"}, + {"name":"textBackgroundColor_"}, + {"name":"textForegroundColor_"}, + {"name":"textStyle_"}, + {"name":"text_"} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TextAttachment$Gradient", + "fields":[ + {"name":"angle_"}, + {"name":"bitField0_"}, + {"name":"endColor_"}, + {"name":"startColor_"} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TypingMessage", "fields":[ diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Color.java b/lib/src/main/java/org/asamk/signal/manager/api/Color.java new file mode 100644 index 00000000..11f7deb0 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/api/Color.java @@ -0,0 +1,24 @@ +package org.asamk.signal.manager.api; + +public record Color(int color) { + + public int alpha() { + return color >>> 24; + } + + public int red() { + return (color >> 16) & 0xFF; + } + + public int green() { + return (color >> 8) & 0xFF; + } + + public int blue() { + return color & 0xFF; + } + + public String toHexColor() { + return String.format("#%08x", color); + } +} 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 d2164e91..a3546a4b 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 @@ -15,6 +15,8 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext; import org.whispersystems.signalservice.api.messages.SignalServicePreview; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; +import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage; +import org.whispersystems.signalservice.api.messages.SignalServiceTextAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; import org.whispersystems.signalservice.api.messages.calls.BusyMessage; @@ -49,7 +51,8 @@ public record MessageEnvelope( Optional typing, Optional data, Optional sync, - Optional call + Optional call, + Optional story ) { public record Receipt(long when, Type type, List timestamps) { @@ -94,6 +97,7 @@ public record MessageEnvelope( public record Data( long timestamp, Optional groupContext, + Optional storyContext, Optional groupCallUpdate, Optional body, int expiresInSeconds, @@ -121,6 +125,10 @@ public record MessageEnvelope( ) { return new Data(dataMessage.getTimestamp(), dataMessage.getGroupContext().map(GroupContext::from), + dataMessage.getStoryContext() + .map((SignalServiceDataMessage.StoryContext storyContext) -> StoryContext.from(storyContext, + recipientResolver, + addressResolver)), dataMessage.getGroupCallUpdate().map(GroupCallUpdate::from), dataMessage.getBody(), dataMessage.getExpiresInSeconds(), @@ -168,6 +176,18 @@ public record MessageEnvelope( } } + public record StoryContext(RecipientAddress author, long sentTimestamp) { + + static StoryContext from( + SignalServiceDataMessage.StoryContext storyContext, + RecipientResolver recipientResolver, + RecipientAddressResolver addressResolver + ) { + return new StoryContext(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient( + storyContext.getAuthorServiceId())), storyContext.getSentTimestamp()); + } + } + public record GroupCallUpdate(String eraId) { static GroupCallUpdate from(SignalServiceDataMessage.GroupCallUpdate groupCallUpdate) { @@ -519,7 +539,8 @@ public record MessageEnvelope( long expirationStartTimestamp, Optional destination, Set recipients, - Optional message + Optional message, + Optional story ) { static Sent from( @@ -537,7 +558,8 @@ public record MessageEnvelope( .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d))) .collect(Collectors.toSet()), sentMessage.getDataMessage() - .map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider))); + .map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider)), + sentMessage.getStoryMessage().map(s -> Story.from(s, fileProvider))); } } @@ -757,6 +779,75 @@ public record MessageEnvelope( } } + public record Story( + boolean allowsReplies, + Optional groupId, + Optional fileAttachment, + Optional textAttachment + ) { + + 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)), + storyMessage.getTextAttachment().map(t -> TextAttachment.from(t, fileProvider))); + } + + public record TextAttachment( + Optional text, + Optional