diff --git a/CHANGELOG.md b/CHANGELOG.md index 97c59d1b..7567a29d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## [Unreleased] + ## [0.14.1] - 2026-03-08 ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c18cd8a8..7162e881 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ If you have a question you can ask it in the [GitHub discussions page](https://g - Be sure to include a **title and clear description**, as much relevant information as possible. - Specify the versions of signal-cli, libsignal-client (if self-compiled), JDK and OS you're using - Specify if it's the normal java or the graalvm native version. - - Run the failing command with `--verbose` flag to get a more detailed log output and include that in the bug report + - Run the failing command with `-vv --scrub-log` flags to get a more detailed log output and include that in the bug report # Pull request diff --git a/build.gradle.kts b/build.gradle.kts index 94d74118..a033279f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ plugins { allprojects { group = "org.asamk" - version = "0.14.1" + version = "0.14.2-SNAPSHOT" } java { diff --git a/client/Cargo.lock b/client/Cargo.lock index 045b10c1..bd3dfd02 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -897,9 +897,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "ring", "rustls-pki-types", diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff560be1..d54afdf7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.32" -signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_140" +signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_141" sqlite = "org.xerial:sqlite-jdbc:3.51.2.0" hikari = "com.zaxxer:HikariCP:7.0.2" junit-jupiter-bom = { module = "org.junit:junit-bom", version.ref = "junit" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 61285a65..d997cfc6 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f78a6a..dbc3ce4a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Message.java b/lib/src/main/java/org/asamk/signal/manager/api/Message.java index 3080b8fb..b9d60b00 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/Message.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/Message.java @@ -7,6 +7,7 @@ public record Message( String messageText, List attachments, boolean viewOnce, + boolean voiceNote, List mentions, Optional quote, Optional sticker, 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 ace962d2..c2109f86 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 @@ -20,6 +20,7 @@ public class ServiceConfig { public static final int MAX_ATTACHMENT_SIZE = 150 * 1024 * 1024; public static final long MAX_ENVELOPE_SIZE = 0; + public static final int MAX_INCREMENTAL_MACS_PER_ENVELOPE = 10; public static final int MAX_MESSAGE_SIZE_BYTES = 2000; public static final long AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE = 10 * 1024 * 1024; public static final boolean AUTOMATIC_NETWORK_RETRY = true; 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 08526c72..cc2ba7d0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java @@ -44,8 +44,8 @@ public class AttachmentHelper { return attachmentStore.retrieveAttachment(id); } - public List uploadAttachments(final List attachments) throws AttachmentInvalidException, IOException { - final var attachmentStreams = createAttachmentStreams(attachments); + public List uploadAttachments(final List attachments, boolean voiceNote) throws AttachmentInvalidException, IOException { + final var attachmentStreams = createAttachmentStreams(attachments, voiceNote); try { // Upload attachments here, so we only upload once even for multiple recipients @@ -61,14 +61,18 @@ public class AttachmentHelper { } } - private List createAttachmentStreams(List attachments) throws AttachmentInvalidException, IOException { + public List uploadAttachments(final List attachments) throws AttachmentInvalidException, IOException { + return uploadAttachments(attachments, false); + } + + private List createAttachmentStreams(List attachments, boolean voiceNote) throws AttachmentInvalidException, IOException { if (attachments == null) { return null; } final var signalServiceAttachments = new ArrayList(attachments.size()); for (var attachment : attachments) { final var uploadSpec = dependencies.getMessageSender().getResumableUploadSpec(); - signalServiceAttachments.add(AttachmentUtils.createAttachmentStream(attachment, uploadSpec)); + signalServiceAttachments.add(AttachmentUtils.createAttachmentStream(attachment, voiceNote, uploadSpec)); } return signalServiceAttachments; } diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java index 8e5ba747..7a6785e9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java @@ -843,7 +843,7 @@ public class ManagerImpl implements Manager { messageBuilder.withBody(message.messageText()); } if (!message.attachments().isEmpty()) { - final var uploadedAttachments = context.getAttachmentHelper().uploadAttachments(message.attachments()); + final var uploadedAttachments = context.getAttachmentHelper().uploadAttachments(message.attachments(), message.voiceNote()); if (!additionalAttachments.isEmpty()) { additionalAttachments.addAll(uploadedAttachments); messageBuilder.withAttachments(additionalAttachments); diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java index 47eec0c0..4f543d88 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java @@ -328,6 +328,7 @@ public class SignalDependencies { Optional.empty(), executor, ServiceConfig.MAX_ENVELOPE_SIZE, + ServiceConfig.MAX_INCREMENTAL_MACS_PER_ENVELOPE, () -> true, true, true)); 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 bab4c433..5e2a3c6b 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 @@ -14,32 +14,49 @@ public class AttachmentUtils { public static SignalServiceAttachmentStream createAttachmentStream( String attachment, + boolean voiceNote, ResumableUploadSpec resumableUploadSpec ) throws AttachmentInvalidException { try { final var streamDetails = Utils.createStreamDetails(attachment); - return createAttachmentStream(streamDetails.first(), streamDetails.second(), resumableUploadSpec); + return createAttachmentStream(streamDetails.first(), streamDetails.second(), voiceNote, resumableUploadSpec); } catch (IOException e) { throw new AttachmentInvalidException(attachment, e); } } + public static SignalServiceAttachmentStream createAttachmentStream( + String attachment, + ResumableUploadSpec resumableUploadSpec + ) throws AttachmentInvalidException { + return createAttachmentStream(attachment, false, resumableUploadSpec); + } + + public static SignalServiceAttachmentStream createAttachmentStream( + StreamDetails streamDetails, + Optional name, + boolean voiceNote, + ResumableUploadSpec resumableUploadSpec + ) throws ResumeLocationInvalidException { + final var uploadTimestamp = System.currentTimeMillis(); + return SignalServiceAttachmentStream.newStreamBuilder() + .withStream(streamDetails.getStream()) + .withContentType(streamDetails.getContentType()) + .withLength(streamDetails.getLength()) + .withFileName(name.orElse(null)) + .withVoiceNote(voiceNote) + .withUploadTimestamp(uploadTimestamp) + .withResumableUploadSpec(resumableUploadSpec) + .withUuid(UUID.randomUUID()) + .build(); + } + public static SignalServiceAttachmentStream createAttachmentStream( StreamDetails streamDetails, Optional name, ResumableUploadSpec resumableUploadSpec ) throws ResumeLocationInvalidException { - // TODO maybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option - final var uploadTimestamp = System.currentTimeMillis(); - return SignalServiceAttachmentStream.newStreamBuilder() - .withStream(streamDetails.getStream()) - .withContentType(streamDetails.getContentType()) - .withLength(streamDetails.getLength()) - .withFileName(name.orElse(null)) - .withUploadTimestamp(uploadTimestamp) - .withResumableUploadSpec(resumableUploadSpec) - .withUuid(UUID.randomUUID()) - .build(); + return createAttachmentStream(streamDetails, name, false, resumableUploadSpec); } } diff --git a/run_tests.sh b/run_tests.sh index 4ca0ad21..81d2c007 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -50,9 +50,9 @@ run() { if [ "$JSON_RPC" -eq 1 ]; then "$SIGNAL_CLI" $@ elif [ "$DBUS" -eq 1 ]; then - "$SIGNAL_CLI" --dbus --verbose --verbose $@ | grep -v '^Warning:' | grep -v 'at org' + "$SIGNAL_CLI" --dbus --verbose --verbose $@ | grep -v 'Warning:' | grep -v 'at org' else - "$SIGNAL_CLI" --service-environment="staging" --verbose --verbose $@ | grep -v '^Warning:' | grep -v 'at org' + "$SIGNAL_CLI" --service-environment="staging" --verbose --verbose $@ | grep -v 'Warning:' | grep -v 'at org' fi set +x } diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 21e39d3f..aa4bb957 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -109,6 +109,9 @@ public class SendCommand implements JsonRpcLocalCommand { .action(Arguments.storeTrue()) .help("Send the message without the urgent flag, so no push notification is triggered for the recipient. " + "The message will still be delivered in real-time if the recipient's app is active."); + subparser.addArgument("--voice-note") + .action(Arguments.storeTrue()) + .help("Mark audio attachments as voice notes. Voice notes are displayed inline in Signal clients."); } @Override @@ -171,6 +174,7 @@ public class SendCommand implements JsonRpcLocalCommand { attachments = List.of(); } final var viewOnce = Boolean.TRUE.equals(ns.getBoolean("view-once")); + final var voiceNote = Boolean.TRUE.equals(ns.getBoolean("voice-note")); final var selfNumber = m.getSelfNumber(); @@ -247,6 +251,7 @@ public class SendCommand implements JsonRpcLocalCommand { final var message = new Message(messageText, attachments, viewOnce, + voiceNote, mentions, Optional.ofNullable(quote), Optional.ofNullable(sticker), diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index f60cc42b..2f4f881a 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -238,6 +238,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable { final var message = new Message(messageText, attachments, false, + false, List.of(), Optional.empty(), Optional.empty(), @@ -404,6 +405,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable { final var message = new Message(messageText, attachments, false, + false, List.of(), Optional.empty(), Optional.empty(), @@ -451,6 +453,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable { final var message = new Message(messageText, attachments, false, + false, List.of(), Optional.empty(), Optional.empty(), diff --git a/src/main/resources/META-INF/native-image/org.asamk/signal-cli/reachability-metadata.json b/src/main/resources/META-INF/native-image/org.asamk/signal-cli/reachability-metadata.json index 39e337d5..2bc14de8 100644 --- a/src/main/resources/META-INF/native-image/org.asamk/signal-cli/reachability-metadata.json +++ b/src/main/resources/META-INF/native-image/org.asamk/signal-cli/reachability-metadata.json @@ -1476,6 +1476,20 @@ } ] }, + { + "type": "kotlin.Pair", + "jniAccessible": true, + "methods": [ + { + "name": "getFirst", + "parameterTypes": [] + }, + { + "name": "getSecond", + "parameterTypes": [] + } + ] + }, { "type": "kotlin.SafePublicationLazyImpl", "fields": [ @@ -5382,6 +5396,18 @@ { "type": "org.signal.core.models.ServiceId$PNI" }, + { + "type": "org.signal.libsignal.attest.AttestationDataException", + "jniAccessible": true, + "methods": [ + { + "name": "", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, { "type": "org.signal.libsignal.internal.CompletableFuture", "jniAccessible": true, @@ -5693,6 +5719,78 @@ { "type": "org.signal.libsignal.protocol.SessionCipher$1", "jniAccessible": true, + "methods": [ + { + "name": "getIdentityKey", + "parameterTypes": [ + "long" + ] + }, + { + "name": "getLocalIdentityKeyPair", + "parameterTypes": [] + }, + { + "name": "getLocalRegistrationId", + "parameterTypes": [] + }, + { + "name": "isTrustedIdentity", + "parameterTypes": [ + "long", + "long", + "int" + ] + }, + { + "name": "loadPreKey", + "parameterTypes": [ + "int" + ] + }, + { + "name": "removePreKey", + "parameterTypes": [ + "int" + ] + }, + { + "name": "saveIdentityKey", + "parameterTypes": [ + "long", + "long" + ] + } + ] + }, + { + "type": "org.signal.libsignal.protocol.SessionCipher$2", + "jniAccessible": true, + "methods": [ + { + "name": "loadSession", + "parameterTypes": [ + "long" + ] + }, + { + "name": "loadSignedPreKey", + "parameterTypes": [ + "int" + ] + }, + { + "name": "storeSession", + "parameterTypes": [ + "long", + "long" + ] + } + ] + }, + { + "type": "org.signal.libsignal.protocol.SessionCipher$3", + "jniAccessible": true, "methods": [ { "name": "loadPreKey", @@ -5709,7 +5807,7 @@ ] }, { - "type": "org.signal.libsignal.protocol.SessionCipher$2", + "type": "org.signal.libsignal.protocol.SessionCipher$4", "jniAccessible": true, "methods": [ { @@ -5720,6 +5818,26 @@ } ] }, + { + "type": "org.signal.libsignal.protocol.SessionCipher$5", + "jniAccessible": true, + "methods": [ + { + "name": "loadKyberPreKey", + "parameterTypes": [ + "int" + ] + }, + { + "name": "markKyberPreKeyUsed", + "parameterTypes": [ + "int", + "int", + "long" + ] + } + ] + }, { "type": "org.signal.libsignal.protocol.SignalProtocolAddress", "jniAccessible": true, @@ -5751,6 +5869,9 @@ } ] }, + { + "type": "org.signal.libsignal.protocol.ecc.ECPrivateKey" + }, { "type": "org.signal.libsignal.protocol.ecc.ECPublicKey", "jniAccessible": true, @@ -5775,6 +5896,27 @@ } ] }, + { + "type": "org.signal.libsignal.protocol.groups.GroupCipher$1", + "jniAccessible": true, + "methods": [ + { + "name": "loadSenderKey", + "parameterTypes": [ + "long", + "java.util.UUID" + ] + }, + { + "name": "storeSenderKey", + "parameterTypes": [ + "long", + "java.util.UUID", + "long" + ] + } + ] + }, { "type": "org.signal.libsignal.protocol.groups.state.SenderKeyRecord", "jniAccessible": true, @@ -5966,10 +6108,26 @@ "allDeclaredMethods": true, "jniAccessible": true }, + { + "type": "org.signal.libsignal.protocol.state.internal.IdentityKeyStore", + "jniAccessible": true + }, + { + "type": "org.signal.libsignal.protocol.state.internal.KyberPreKeyStore", + "jniAccessible": true + }, { "type": "org.signal.libsignal.protocol.state.internal.PreKeyStore", "jniAccessible": true }, + { + "type": "org.signal.libsignal.protocol.state.internal.SenderKeyStore", + "jniAccessible": true + }, + { + "type": "org.signal.libsignal.protocol.state.internal.SessionStore", + "jniAccessible": true + }, { "type": "org.signal.libsignal.protocol.state.internal.SignedPreKeyStore", "jniAccessible": true @@ -9811,4 +9969,4 @@ "bundle": "net.sourceforge.argparse4j.internal.ArgumentParserImpl" } ] -} +} \ No newline at end of file