Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ARG SIGNAL_CLI_NATIVE_PACKAGE_VERSION

COPY ext/libraries/libsignal-client/v${LIBSIGNAL_CLIENT_VERSION} /tmp/libsignal-client-libraries
COPY ext/libraries/libsignal-client/signal-cli-native.patch /tmp/signal-cli-native.patch
COPY ext/patches/fix-binary-aci.patch /tmp/fix-binary-aci.patch

# use architecture specific libsignal_jni.so
RUN arch="$(uname -m)"; \
Expand Down Expand Up @@ -75,17 +76,22 @@ RUN if [ "$(uname -m)" = "x86_64" ]; then \
&& git clone https://github.com/AsamK/signal-cli.git signal-cli-${SIGNAL_CLI_VERSION}-source \
&& cd signal-cli-${SIGNAL_CLI_VERSION}-source \
&& git checkout -q v${SIGNAL_CLI_VERSION} \
&& git apply /tmp/fix-binary-aci.patch \
&& cd /tmp && mkdir -p /tmp/graalvm && tar xf gvm.tar.gz -C /tmp/graalvm --strip-components=1 \
&& export GRAALVM_HOME=/tmp/graalvm \
&& export PATH=/tmp/graalvm/bin:$PATH \
&& cd /tmp/signal-cli-${SIGNAL_CLI_VERSION}-source \
&& sed -i 's/Signal-Android\/5.22.3/Signal-Android\/5.51.7/g' src/main/java/org/asamk/signal/BaseConfig.java \
&& ./gradlew build \
&& ./gradlew installDist \
&& ls build/install/signal-cli/lib/libsignal-client-${LIBSIGNAL_CLIENT_VERSION}.jar || (echo "\n\nsignal-client jar file with version ${LIBSIGNAL_CLIENT_VERSION} not found. Maybe the version needs to be bumped in the signal-cli-rest-api Dockerfile?\n\n" && echo "Available version: \n" && ls build/install/signal-cli/lib/libsignal-client-* && echo "\n\n" && exit 1) \
&& BUILT_LIBSIGNAL_JAR_NAME=$(ls build/install/signal-cli/lib/ | grep 'libsignal-client-.*\.jar' | head -1) \
&& echo "Built libsignal-client jar: ${BUILT_LIBSIGNAL_JAR_NAME}" \
&& rm -rf /tmp/signal-cli-${SIGNAL_CLI_VERSION} \
&& cp -a build/install/signal-cli /tmp/signal-cli-${SIGNAL_CLI_VERSION} \
&& cd /tmp \
&& cp signal-cli-${SIGNAL_CLI_VERSION}-source/build/install/signal-cli/lib/libsignal-client-${LIBSIGNAL_CLIENT_VERSION}.jar libsignal-client.jar \
&& cp /tmp/signal-cli-${SIGNAL_CLI_VERSION}/lib/${BUILT_LIBSIGNAL_JAR_NAME} libsignal-client.jar \
&& zip -qu libsignal-client.jar libsignal_jni.so \
&& cp libsignal-client.jar /tmp/signal-cli-${SIGNAL_CLI_VERSION}/lib/${BUILT_LIBSIGNAL_JAR_NAME} \
&& cd /tmp/signal-cli-${SIGNAL_CLI_VERSION}-source \
&& git apply /tmp/signal-cli-native.patch \
&& ./gradlew -q nativeCompile; \
Expand Down Expand Up @@ -113,20 +119,27 @@ RUN if [ "$(uname -m)" = "x86_64" ]; then \
echo "Unknown architecture"; \
fi;

# replace libsignal-client

RUN ls /tmp/signal-cli-${SIGNAL_CLI_VERSION}/lib/libsignal-client-${LIBSIGNAL_CLIENT_VERSION}.jar || (echo "\n\nsignal-client jar file with version ${LIBSIGNAL_CLIENT_VERSION} not found. Maybe the version needs to be bumped in the signal-cli-rest-api Dockerfile?\n\n" && echo "Available version: \n" && ls /tmp/signal-cli-${SIGNAL_CLI_VERSION}/lib/libsignal-client-* && echo "\n\n" && exit 1)

# workaround until upstream is fixed
RUN cd /tmp/signal-cli-${SIGNAL_CLI_VERSION}/lib \
&& unzip signal-cli-${SIGNAL_CLI_VERSION}.jar \
&& sed -i 's/Signal-Android\/5.22.3/Signal-Android\/5.51.7/g' org/asamk/signal/BaseConfig.class \
&& zip -r signal-cli-${SIGNAL_CLI_VERSION}.jar org/ META-INF/ \
&& rm -rf META-INF \
&& rm -rf org
# Post-processing: inject native libsignal_jni.so and apply BaseConfig workaround.
# On x86_64 the source build (above) already produced a patched installDist with
# the native lib injected, so we only need to package it. On other architectures
# the release tarball is still unpatched — apply the BaseConfig sed workaround and
# inject the native lib there.

RUN if [ "$(uname -m)" != "x86_64" ]; then \
ls /tmp/signal-cli-${SIGNAL_CLI_VERSION}/lib/libsignal-client-${LIBSIGNAL_CLIENT_VERSION}.jar \
|| (echo "\n\nsignal-client jar file with version ${LIBSIGNAL_CLIENT_VERSION} not found.\n\n" \
&& ls /tmp/signal-cli-${SIGNAL_CLI_VERSION}/lib/libsignal-client-* && exit 1) \
&& cd /tmp/signal-cli-${SIGNAL_CLI_VERSION}/lib \
&& unzip signal-cli-${SIGNAL_CLI_VERSION}.jar \
&& sed -i 's/Signal-Android\/5.22.3/Signal-Android\/5.51.7/g' org/asamk/signal/BaseConfig.class \
&& zip -r signal-cli-${SIGNAL_CLI_VERSION}.jar org/ META-INF/ \
&& rm -rf META-INF \
&& rm -rf org \
&& cd /tmp/ \
&& zip -qu /tmp/signal-cli-${SIGNAL_CLI_VERSION}/lib/libsignal-client-${LIBSIGNAL_CLIENT_VERSION}.jar libsignal_jni.so; \
fi

RUN cd /tmp/ \
&& zip -qu /tmp/signal-cli-${SIGNAL_CLI_VERSION}/lib/libsignal-client-${LIBSIGNAL_CLIENT_VERSION}.jar libsignal_jni.so \
&& zip -qr signal-cli-${SIGNAL_CLI_VERSION}.zip signal-cli-${SIGNAL_CLI_VERSION}/* \
&& unzip -q /tmp/signal-cli-${SIGNAL_CLI_VERSION}.zip -d /opt \
&& rm -f /tmp/signal-cli-${SIGNAL_CLI_VERSION}.zip
Expand Down
89 changes: 89 additions & 0 deletions ext/patches/fix-binary-aci.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
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()