signal-cli-rest-api/ext/patches/fix-binary-aci.patch
Stephan Richter 40dd9a21b2 fix: Signal Desktop v8.0.0 binary ACI compatibility
Signal Desktop v8.0.0 switched from string ACI fields to binary ACI
encoding in protobuf messages. This breaks reactions, mentions, quotes,
and other message features when the library cannot parse the new format.

Two-part fix applied via patch to signal-cli v0.13.24 source build:

1. Bump signal-service-java from unofficial_137 to unofficial_138, which
   adds dual-format ACI parsing (string + binary fallback via
   ServiceId.parseOrNull).

2. Add defensive null guards in MessageEnvelope.java for cases where
   ServiceId resolution still fails (e.g. ACI.UNKNOWN). Preserves
   message content with UNKNOWN_UUID fallback rather than dropping
   entire message components (quotes, reactions, mentions, etc.).

The patch is applied during the x86_64 source build. The source-built
installDist output replaces the release tarball, so both the JVM and
native (GraalVM) paths get the fix.

Non-x86_64 architectures continue using the unpatched release tarball
until signal-cli cuts a new release with unofficial_138.

See: https://github.com/AsamK/signal-cli/pull/1944

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 19:24:30 -05:00

90 lines
5.5 KiB
Diff

Fix Signal Desktop v8.0.0 binary ACI encoding compatibility.
Signal Desktop v8.0.0 switched from string ACI fields to binary ACI encoding
in protobuf messages. This causes null ServiceId values when the library
cannot parse the new format, breaking reactions, mentions, quotes, and other
message features.
Two-part fix:
1. Bump signal-service-java from unofficial_137 to unofficial_138 which adds
dual-format ACI parsing (string + binary fallback).
2. Add defensive null guards in MessageEnvelope.java for cases where ServiceId
resolution still fails (e.g. ACI.UNKNOWN), preserving message content with
UNKNOWN_UUID fallback rather than dropping entire message components.
See: https://github.com/AsamK/signal-cli/pull/1944
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 9b1bd5f4..0000001 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -11,7 +11,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.25"
-signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_137"
+signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_138"
sqlite = "org.xerial:sqlite-jdbc:3.51.1.0"
hikari = "com.zaxxer:HikariCP:7.0.2"
junit-jupiter-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
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 37946057..57a5a0f4 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
@@ -132,6 +132,7 @@ public record MessageEnvelope(
return new Data(dataMessage.getTimestamp(),
dataMessage.getGroupContext().map(GroupContext::from),
dataMessage.getStoryContext()
+ .filter(s -> s.getAuthorServiceId() != null)
.map((SignalServiceDataMessage.StoryContext storyContext) -> StoryContext.from(storyContext,
recipientResolver,
addressResolver)),
@@ -143,9 +144,10 @@ public record MessageEnvelope(
dataMessage.isEndSession(),
dataMessage.isProfileKeyUpdate(),
dataMessage.getProfileKey().isPresent(),
- dataMessage.getReaction().map(r -> Reaction.from(r, recipientResolver, addressResolver)),
+ dataMessage.getReaction()
+ .filter(r -> r.getTargetAuthor() != null)
+ .map(r -> Reaction.from(r, recipientResolver, addressResolver)),
dataMessage.getQuote()
- .filter(q -> q.getAuthor() != null && q.getAuthor().isValid())
.map(q -> Quote.from(q, recipientResolver, addressResolver, fileProvider)),
dataMessage.getPayment().map(p -> p.getPaymentNotification().isPresent() ? Payment.from(p) : null),
dataMessage.getAttachments()
@@ -159,10 +161,15 @@ public record MessageEnvelope(
.toList())
.orElse(List.of()),
dataMessage.getPollCreate().map(PollCreate::from),
- dataMessage.getPollVote().map(p -> PollVote.from(p, recipientResolver, addressResolver)),
+ dataMessage.getPollVote()
+ .filter(p -> p.getTargetAuthor() != null)
+ .map(p -> PollVote.from(p, recipientResolver, addressResolver)),
dataMessage.getPollTerminate().map(PollTerminate::from),
dataMessage.getMentions()
- .map(a -> a.stream().map(m -> Mention.from(m, recipientResolver, addressResolver)).toList())
+ .map(a -> a.stream()
+ .filter(m -> m.getServiceId() != null)
+ .map(m -> Mention.from(m, recipientResolver, addressResolver))
+ .toList())
.orElse(List.of()),
dataMessage.getPreviews()
.map(a -> a.stream().map(preview -> Preview.from(preview, fileProvider)).toList())
@@ -241,10 +248,13 @@ public record MessageEnvelope(
RecipientAddressResolver addressResolver,
final AttachmentFileProvider fileProvider
) {
+ final var author = quote.getAuthor() != null && quote.getAuthor().isValid()
+ ? addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(quote.getAuthor()))
+ .toApiRecipientAddress()
+ : new RecipientAddress(RecipientAddress.UNKNOWN_UUID);
return new Quote(quote.getId(),
- addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(quote.getAuthor()))
- .toApiRecipientAddress(),
- Optional.of(quote.getText()),
+ author,
+ Optional.ofNullable(quote.getText()),
quote.getMentions() == null
? List.of()
: quote.getMentions()