mirror of
https://github.com/AsamK/signal-cli.git
synced 2026-05-18 13:14:14 +00:00
Compare commits
12 Commits
c1c827d2d3
...
8a8c495865
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a8c495865 | ||
|
|
2cbff3a4c1 | ||
|
|
540cfbd977 | ||
|
|
97f77b1f69 | ||
|
|
4601e60118 | ||
|
|
fcf82b9318 | ||
|
|
9c8137fafa | ||
|
|
0a1531dcce | ||
|
|
c10f618a3e | ||
|
|
4a3d9d90a6 | ||
|
|
b4275414e1 | ||
|
|
5f94b7b6d1 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
# java="25" is the LTS Java version used in reproducible builds script (default in Containerfile).
|
# java="25" is the LTS Java version used in reproducible builds script (default in Containerfile).
|
||||||
# More Java versions can be added to test compatibility, eg. "26".
|
# More Java versions can be added to test compatibility, eg. "26".
|
||||||
java: ["25"]
|
java: ["25", "26"]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
[versions]
|
[versions]
|
||||||
slf4j = "2.0.17"
|
slf4j = "2.0.18"
|
||||||
junit = "6.0.3"
|
junit = "6.0.3"
|
||||||
micronaut-json-schema = "2.0.0-M8"
|
micronaut-json-schema = "2.0.0"
|
||||||
micronaut-core = "4.9.3"
|
micronaut-core = "5.0.0"
|
||||||
signal-service = "2.15.3_unofficial_145"
|
signal-service = "2.15.3_unofficial_146"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.84"
|
bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.84"
|
||||||
@ -20,7 +20,7 @@ slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" }
|
|||||||
logback = "ch.qos.logback:logback-classic:1.5.32"
|
logback = "ch.qos.logback:logback-classic:1.5.32"
|
||||||
|
|
||||||
signalnetwork = { module = "com.github.turasa:signal-network", version.ref = "signal-service" }
|
signalnetwork = { module = "com.github.turasa:signal-network", version.ref = "signal-service" }
|
||||||
sqlite = "org.xerial:sqlite-jdbc:3.53.0.0"
|
sqlite = "org.xerial:sqlite-jdbc:3.53.1.0"
|
||||||
hikari = "com.zaxxer:HikariCP:7.0.2"
|
hikari = "com.zaxxer:HikariCP:7.0.2"
|
||||||
junit-jupiter-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
|
junit-jupiter-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
|
||||||
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
|
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,7 +1,9 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
|
retries=0
|
||||||
|
retryBackOffMs=500
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
31
gradlew.bat
vendored
31
gradlew.bat
vendored
@ -23,8 +23,8 @@
|
|||||||
@rem
|
@rem
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
setlocal EnableExtensions
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
@ -51,7 +51,7 @@ echo. 1>&2
|
|||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
"%COMSPEC%" /c exit 1
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
:findJavaFromJavaHome
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
@ -65,7 +65,7 @@ echo. 1>&2
|
|||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
"%COMSPEC%" /c exit 1
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
@ -73,21 +73,10 @@ goto fail
|
|||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||||
|
@rem which allows us to clear the local environment before executing the java command
|
||||||
|
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
||||||
|
|
||||||
:end
|
:exitWithErrorLevel
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||||
|
|
||||||
:fail
|
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
|
||||||
rem the _cmd.exe /c_ return code!
|
|
||||||
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
|
|
||||||
|
|
||||||
:omega
|
|
||||||
|
|||||||
@ -259,7 +259,7 @@ public interface Manager extends Closeable {
|
|||||||
RecipientIdentifier.Single recipient
|
RecipientIdentifier.Single recipient
|
||||||
) throws IOException;
|
) throws IOException;
|
||||||
|
|
||||||
SendMessageResults sendEndSessionMessage(Set<RecipientIdentifier.Single> recipients) throws IOException;
|
void sendEndSessionMessage(Set<RecipientIdentifier.Single> recipients) throws IOException;
|
||||||
|
|
||||||
SendMessageResults sendMessageRequestResponse(
|
SendMessageResults sendMessageRequestResponse(
|
||||||
MessageEnvelope.Sync.MessageRequestResponse.Type type,
|
MessageEnvelope.Sync.MessageRequestResponse.Type type,
|
||||||
@ -407,6 +407,10 @@ public interface Manager extends Closeable {
|
|||||||
|
|
||||||
void addClosedListener(Runnable listener);
|
void addClosedListener(Runnable listener);
|
||||||
|
|
||||||
|
void addUnidentifiedKeepAlive(String token);
|
||||||
|
|
||||||
|
void removeUnidentifiedKeepAlive(String token);
|
||||||
|
|
||||||
InputStream retrieveAttachment(final String id) throws IOException;
|
InputStream retrieveAttachment(final String id) throws IOException;
|
||||||
|
|
||||||
InputStream retrieveContactAvatar(final RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException;
|
InputStream retrieveContactAvatar(final RecipientIdentifier.Single recipient) throws IOException, UnregisteredRecipientException;
|
||||||
|
|||||||
@ -157,7 +157,7 @@ public record MessageEnvelope(
|
|||||||
dataMessage.getExpiresInSeconds(),
|
dataMessage.getExpiresInSeconds(),
|
||||||
dataMessage.isExpirationUpdate(),
|
dataMessage.isExpirationUpdate(),
|
||||||
dataMessage.isViewOnce(),
|
dataMessage.isViewOnce(),
|
||||||
dataMessage.isEndSession(),
|
false,
|
||||||
dataMessage.isProfileKeyUpdate(),
|
dataMessage.isProfileKeyUpdate(),
|
||||||
dataMessage.getProfileKey().isPresent(),
|
dataMessage.getProfileKey().isPresent(),
|
||||||
dataMessage.getReaction().map(r -> Reaction.from(r, recipientResolver, addressResolver)),
|
dataMessage.getReaction().map(r -> Reaction.from(r, recipientResolver, addressResolver)),
|
||||||
@ -1028,7 +1028,7 @@ public record MessageEnvelope(
|
|||||||
final AttachmentFileProvider fileProvider,
|
final AttachmentFileProvider fileProvider,
|
||||||
Exception exception
|
Exception exception
|
||||||
) {
|
) {
|
||||||
final var serviceId = envelope.getSourceServiceId().map(ServiceId::parseOrNull).orElse(null);
|
final var serviceId = envelope.getSourceServiceId();
|
||||||
final var source = !envelope.isUnidentifiedSender() && serviceId != null
|
final var source = !envelope.isUnidentifiedSender() && serviceId != null
|
||||||
? recipientResolver.resolveRecipient(serviceId)
|
? recipientResolver.resolveRecipient(serviceId)
|
||||||
: envelope.isUnidentifiedSender() && content != null
|
: envelope.isUnidentifiedSender() && content != null
|
||||||
|
|||||||
@ -109,8 +109,8 @@ public final class IncomingMessageHandler {
|
|||||||
SignalServiceContent content = null;
|
SignalServiceContent content = null;
|
||||||
if (!envelope.isReceipt()) {
|
if (!envelope.isReceipt()) {
|
||||||
account.getIdentityKeyStore().setRetryingDecryption(true);
|
account.getIdentityKeyStore().setRetryingDecryption(true);
|
||||||
final var destination = getDestination(envelope).serviceId();
|
|
||||||
try {
|
try {
|
||||||
|
final var destination = getDestination(envelope).serviceId();
|
||||||
final var cipherResult = dependencies.getCipher(destination == null
|
final var cipherResult = dependencies.getCipher(destination == null
|
||||||
|| destination.equals(account.getAci()) ? ServiceIdType.ACI : ServiceIdType.PNI)
|
|| destination.equals(account.getAci()) ? ServiceIdType.ACI : ServiceIdType.PNI)
|
||||||
.decrypt(envelope.getProto(), envelope.getServerDeliveredTimestamp());
|
.decrypt(envelope.getProto(), envelope.getServerDeliveredTimestamp());
|
||||||
@ -140,15 +140,30 @@ public final class IncomingMessageHandler {
|
|||||||
final Manager.ReceiveMessageHandler handler
|
final Manager.ReceiveMessageHandler handler
|
||||||
) {
|
) {
|
||||||
final var actions = new ArrayList<HandleAction>();
|
final var actions = new ArrayList<HandleAction>();
|
||||||
|
if (envelope.isPreKeySignalMessage()) {
|
||||||
|
actions.add(RefreshPreKeysAction.create());
|
||||||
|
}
|
||||||
SignalServiceContent content = null;
|
SignalServiceContent content = null;
|
||||||
Exception exception = null;
|
Exception exception = null;
|
||||||
envelope.getSourceServiceId().map(ServiceId::parseOrNull)
|
if (envelope.getSourceServiceId() != null) {
|
||||||
// Store uuid if we don't have it already
|
// Store uuid if we don't have it already
|
||||||
// uuid in envelope is sent by server
|
// uuid in envelope is sent by server
|
||||||
.ifPresent(serviceId -> account.getRecipientResolver().resolveRecipient(serviceId));
|
account.getRecipientResolver().resolveRecipient(envelope.getSourceServiceId());
|
||||||
|
}
|
||||||
if (!envelope.isReceipt()) {
|
if (!envelope.isReceipt()) {
|
||||||
final var destination = getDestination(envelope).serviceId();
|
|
||||||
try {
|
try {
|
||||||
|
final var destination = getDestination(envelope).serviceId();
|
||||||
|
|
||||||
|
if (destination == account.getPni() && envelope.getSourceServiceId() == null) {
|
||||||
|
throw new InvalidMessageException(
|
||||||
|
"Got a sealed sender message to our PNI? Invalid message, ignoring.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (envelope.getSourceServiceId() instanceof ServiceId.PNI
|
||||||
|
&& envelope.getProto().type != Envelope.Type.SERVER_DELIVERY_RECEIPT) {
|
||||||
|
throw new InvalidMessageException("Got a message from a PNI that was not a SERVER_DELIVERY_RECEIPT.");
|
||||||
|
}
|
||||||
|
|
||||||
final var cipherResult = dependencies.getCipher(destination == null
|
final var cipherResult = dependencies.getCipher(destination == null
|
||||||
|| destination.equals(account.getAci()) ? ServiceIdType.ACI : ServiceIdType.PNI)
|
|| destination.equals(account.getAci()) ? ServiceIdType.ACI : ServiceIdType.PNI)
|
||||||
.decrypt(envelope.getProto(), envelope.getServerDeliveredTimestamp());
|
.decrypt(envelope.getProto(), envelope.getServerDeliveredTimestamp());
|
||||||
@ -173,7 +188,13 @@ public final class IncomingMessageHandler {
|
|||||||
logger.debug("Received invalid message from blocked contact, ignoring.");
|
logger.debug("Received invalid message from blocked contact, ignoring.");
|
||||||
} else {
|
} else {
|
||||||
var serviceId = ServiceId.parseOrNull(e.getSender());
|
var serviceId = ServiceId.parseOrNull(e.getSender());
|
||||||
if (serviceId != null) {
|
ServiceId destination;
|
||||||
|
try {
|
||||||
|
destination = getDestination(envelope).serviceId();
|
||||||
|
} catch (InvalidMessageException ex) {
|
||||||
|
destination = null;
|
||||||
|
}
|
||||||
|
if (serviceId != null && destination != null) {
|
||||||
final var isSelf = sender.equals(account.getSelfRecipientId())
|
final var isSelf = sender.equals(account.getSelfRecipientId())
|
||||||
&& e.getSenderDevice() == account.getDeviceId();
|
&& e.getSenderDevice() == account.getDeviceId();
|
||||||
logger.debug("Received invalid message, queuing renew session action.");
|
logger.debug("Received invalid message, queuing renew session action.");
|
||||||
@ -311,7 +332,12 @@ public final class IncomingMessageHandler {
|
|||||||
final var sender = senderDeviceAddress.recipientId();
|
final var sender = senderDeviceAddress.recipientId();
|
||||||
final var senderServiceId = senderDeviceAddress.serviceId();
|
final var senderServiceId = senderDeviceAddress.serviceId();
|
||||||
final var senderDeviceId = senderDeviceAddress.deviceId();
|
final var senderDeviceId = senderDeviceAddress.deviceId();
|
||||||
final var destination = getDestination(envelope);
|
final DeviceAddress destination;
|
||||||
|
try {
|
||||||
|
destination = getDestination(envelope);
|
||||||
|
} catch (InvalidMessageException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
|
||||||
if (account.getPni().equals(destination.serviceId)) {
|
if (account.getPni().equals(destination.serviceId)) {
|
||||||
account.getRecipientStore().markNeedsPniSignature(destination.recipientId, true);
|
account.getRecipientStore().markNeedsPniSignature(destination.recipientId, true);
|
||||||
@ -874,11 +900,6 @@ public final class IncomingMessageHandler {
|
|||||||
|
|
||||||
final var selfAddress = isSync ? source : destination;
|
final var selfAddress = isSync ? source : destination;
|
||||||
final var conversationPartnerAddress = isSync ? destination : source;
|
final var conversationPartnerAddress = isSync ? destination : source;
|
||||||
if (conversationPartnerAddress != null && message.isEndSession()) {
|
|
||||||
account.getAccountData(selfAddress.serviceId())
|
|
||||||
.getSessionStore()
|
|
||||||
.deleteAllSessions(conversationPartnerAddress.serviceId());
|
|
||||||
}
|
|
||||||
if (message.isExpirationUpdate() || message.getBody().isPresent()) {
|
if (message.isExpirationUpdate() || message.getBody().isPresent()) {
|
||||||
if (message.getGroupContext().isPresent()) {
|
if (message.getGroupContext().isPresent()) {
|
||||||
final var groupContext = message.getGroupContext().get();
|
final var groupContext = message.getGroupContext().get();
|
||||||
@ -1047,7 +1068,7 @@ public final class IncomingMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceAddress getSenderAddress(SignalServiceEnvelope envelope, SignalServiceContent content) {
|
private SignalServiceAddress getSenderAddress(SignalServiceEnvelope envelope, SignalServiceContent content) {
|
||||||
final var serviceId = envelope.getSourceServiceId().map(ServiceId::parseOrNull).orElse(null);
|
final var serviceId = envelope.getSourceServiceId();
|
||||||
if (!envelope.isUnidentifiedSender() && serviceId != null) {
|
if (!envelope.isUnidentifiedSender() && serviceId != null) {
|
||||||
return new SignalServiceAddress(serviceId);
|
return new SignalServiceAddress(serviceId);
|
||||||
} else if (content != null) {
|
} else if (content != null) {
|
||||||
@ -1058,7 +1079,7 @@ public final class IncomingMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private DeviceAddress getSender(SignalServiceEnvelope envelope, SignalServiceContent content) {
|
private DeviceAddress getSender(SignalServiceEnvelope envelope, SignalServiceContent content) {
|
||||||
final var serviceId = envelope.getSourceServiceId().map(ServiceId::parseOrNull).orElse(null);
|
final var serviceId = envelope.getSourceServiceId();
|
||||||
if (!envelope.isUnidentifiedSender() && serviceId != null) {
|
if (!envelope.isUnidentifiedSender() && serviceId != null) {
|
||||||
return new DeviceAddress(account.getRecipientResolver().resolveRecipient(serviceId),
|
return new DeviceAddress(account.getRecipientResolver().resolveRecipient(serviceId),
|
||||||
serviceId,
|
serviceId,
|
||||||
@ -1070,10 +1091,13 @@ public final class IncomingMessageHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DeviceAddress getDestination(SignalServiceEnvelope envelope) {
|
private DeviceAddress getDestination(SignalServiceEnvelope envelope) throws InvalidMessageException {
|
||||||
final var destination = envelope.getDestinationServiceId();
|
final var destination = envelope.getDestinationServiceId();
|
||||||
if (destination == null || destination.isUnknown()) {
|
if (destination == null || destination.isUnknown()) {
|
||||||
return new DeviceAddress(account.getSelfRecipientId(), account.getAci(), account.getDeviceId());
|
throw new InvalidMessageException("Missing destination");
|
||||||
|
}
|
||||||
|
if (!account.getAci().equals(destination) && !account.getPni().equals(destination)) {
|
||||||
|
throw new InvalidMessageException("Message not intended for this account");
|
||||||
}
|
}
|
||||||
return new DeviceAddress(account.getRecipientResolver().resolveRecipient(destination),
|
return new DeviceAddress(account.getRecipientResolver().resolveRecipient(destination),
|
||||||
destination,
|
destination,
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import org.asamk.signal.manager.jobs.CleanOldPreKeysJob;
|
|||||||
import org.asamk.signal.manager.storage.SignalAccount;
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
import org.asamk.signal.manager.storage.messageCache.CachedMessage;
|
import org.asamk.signal.manager.storage.messageCache.CachedMessage;
|
||||||
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
|
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
|
||||||
import org.signal.core.models.ServiceId;
|
|
||||||
import org.signal.core.models.ServiceId.ACI;
|
import org.signal.core.models.ServiceId.ACI;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -150,10 +149,10 @@ public class ReceiveHelper {
|
|||||||
for (final var it : batch) {
|
for (final var it : batch) {
|
||||||
SignalServiceEnvelope envelope1 = new SignalServiceEnvelope(it.getEnvelope(),
|
SignalServiceEnvelope envelope1 = new SignalServiceEnvelope(it.getEnvelope(),
|
||||||
it.getServerDeliveredTimestamp());
|
it.getServerDeliveredTimestamp());
|
||||||
final var recipientId = envelope1.getSourceServiceId()
|
final var sourceServiceId = envelope1.getSourceServiceId();
|
||||||
.map(ServiceId::parseOrNull)
|
final var recipientId = sourceServiceId == null
|
||||||
.map(s -> account.getRecipientResolver().resolveRecipient(s))
|
? null
|
||||||
.orElse(null);
|
: account.getRecipientResolver().resolveRecipient(sourceServiceId);
|
||||||
logger.trace("Storing new message from {}", recipientId);
|
logger.trace("Storing new message from {}", recipientId);
|
||||||
// store message on disk, before acknowledging receipt to the server
|
// store message on disk, before acknowledging receipt to the server
|
||||||
cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId);
|
cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId);
|
||||||
@ -238,7 +237,7 @@ public class ReceiveHelper {
|
|||||||
if (exception instanceof UntrustedIdentityException) {
|
if (exception instanceof UntrustedIdentityException) {
|
||||||
logger.debug("Keeping message with untrusted identity in message cache");
|
logger.debug("Keeping message with untrusted identity in message cache");
|
||||||
final var address = ((UntrustedIdentityException) exception).getSender();
|
final var address = ((UntrustedIdentityException) exception).getSender();
|
||||||
if (envelope.getSourceServiceId().isEmpty() && address.aci().isPresent()) {
|
if (envelope.getSourceServiceId() == null && address.aci().isPresent()) {
|
||||||
final var recipientId = account.getRecipientResolver()
|
final var recipientId = account.getRecipientResolver()
|
||||||
.resolveRecipient(ACI.parseOrThrow(address.aci().get()));
|
.resolveRecipient(ACI.parseOrThrow(address.aci().get()));
|
||||||
try {
|
try {
|
||||||
@ -292,7 +291,7 @@ public class ReceiveHelper {
|
|||||||
cachedMessage.delete();
|
cachedMessage.delete();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (envelope.getSourceServiceId().isEmpty()) {
|
if (envelope.getSourceServiceId() == null) {
|
||||||
final var identifier = ((UntrustedIdentityException) exception).getSender();
|
final var identifier = ((UntrustedIdentityException) exception).getSender();
|
||||||
final var recipientId = account.getRecipientResolver()
|
final var recipientId = account.getRecipientResolver()
|
||||||
.resolveRecipient(new RecipientAddress(identifier));
|
.resolveRecipient(new RecipientAddress(identifier));
|
||||||
|
|||||||
@ -1091,30 +1091,26 @@ public class ManagerImpl implements Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SendMessageResults sendEndSessionMessage(Set<RecipientIdentifier.Single> recipients) throws IOException {
|
public void sendEndSessionMessage(Set<RecipientIdentifier.Single> recipients) throws IOException {
|
||||||
var messageBuilder = SignalServiceDataMessage.newBuilder().asEndSessionMessage();
|
for (var recipient : recipients) {
|
||||||
|
final RecipientId recipientId;
|
||||||
try {
|
try {
|
||||||
return sendMessage(messageBuilder,
|
recipientId = context.getRecipientHelper().resolveRecipient(recipient);
|
||||||
recipients.stream().map(RecipientIdentifier.class::cast).collect(Collectors.toSet()),
|
} catch (UnregisteredRecipientException e) {
|
||||||
false);
|
continue;
|
||||||
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
|
}
|
||||||
throw new AssertionError(e);
|
final var recipientAddress = context.getAccount()
|
||||||
} finally {
|
.getRecipientAddressResolver()
|
||||||
for (var recipient : recipients) {
|
.resolveRecipientAddress(recipientId);
|
||||||
final RecipientId recipientId;
|
final var aciSessionStore = account.getAccountData(ServiceIdType.ACI).getSessionStore();
|
||||||
try {
|
final var pniSessionStore = account.getAccountData(ServiceIdType.PNI).getSessionStore();
|
||||||
recipientId = context.getRecipientHelper().resolveRecipient(recipient);
|
if (recipientAddress.aci().isPresent()) {
|
||||||
} catch (UnregisteredRecipientException e) {
|
aciSessionStore.archiveSessions(recipientAddress.aci().get());
|
||||||
continue;
|
pniSessionStore.archiveSessions(recipientAddress.aci().get());
|
||||||
}
|
}
|
||||||
final var serviceId = context.getAccount()
|
if (recipientAddress.pni().isPresent()) {
|
||||||
.getRecipientAddressResolver()
|
aciSessionStore.archiveSessions(recipientAddress.pni().get());
|
||||||
.resolveRecipientAddress(recipientId)
|
pniSessionStore.archiveSessions(recipientAddress.pni().get());
|
||||||
.serviceId();
|
|
||||||
if (serviceId.isPresent()) {
|
|
||||||
account.getAccountData(ServiceIdType.ACI).getSessionStore().deleteAllSessions(serviceId.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1712,6 +1708,16 @@ public class ManagerImpl implements Manager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addUnidentifiedKeepAlive(final String token) {
|
||||||
|
dependencies.getUnauthenticatedSignalWebSocket().registerKeepAliveToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUnidentifiedKeepAlive(final String token) {
|
||||||
|
dependencies.getUnauthenticatedSignalWebSocket().removeKeepAliveToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCallEventListener(final CallEventListener listener) {
|
public void addCallEventListener(final CallEventListener listener) {
|
||||||
context.getCallManager().addCallEventListener(listener);
|
context.getCallManager().addCallEventListener(listener);
|
||||||
|
|||||||
@ -368,7 +368,8 @@ public class SignalDependencies {
|
|||||||
|
|
||||||
public SignalServiceCipher getCipher(ServiceIdType serviceIdType) {
|
public SignalServiceCipher getCipher(ServiceIdType serviceIdType) {
|
||||||
final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.unidentifiedSenderTrustRoots());
|
final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.unidentifiedSenderTrustRoots());
|
||||||
final var address = new SignalServiceAddress(credentialsProvider.getAci(), credentialsProvider.getE164());
|
final var serviceId = serviceIdType == ServiceIdType.ACI ? credentialsProvider.getAci() : credentialsProvider.getPni();
|
||||||
|
final var address = new SignalServiceAddress(serviceId, credentialsProvider.getE164());
|
||||||
final var deviceId = credentialsProvider.getDeviceId();
|
final var deviceId = credentialsProvider.getDeviceId();
|
||||||
return new SignalServiceCipher(address,
|
return new SignalServiceCipher(address,
|
||||||
deviceId,
|
deviceId,
|
||||||
|
|||||||
@ -6,7 +6,7 @@ ENV SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH
|
|||||||
ENV LANG=C.UTF-8
|
ENV LANG=C.UTF-8
|
||||||
ENV LC_CTYPE=en_US.UTF-8
|
ENV LC_CTYPE=en_US.UTF-8
|
||||||
RUN SNAPSHOT="$(date -u -d "@$SOURCE_DATE_EPOCH" +%Y%m%dT%H%M%SZ)" \
|
RUN SNAPSHOT="$(date -u -d "@$SOURCE_DATE_EPOCH" +%Y%m%dT%H%M%SZ)" \
|
||||||
&& apt install -y make asciidoc-base --update --snapshot "$SNAPSHOT" --no-install-recommends --no-install-suggests
|
&& sed -i 's/^deb /deb [snapshot=yes] /' /etc/apt/sources.list && apt update --snapshot "$SNAPSHOT" && apt install -y make asciidoc-base --snapshot "$SNAPSHOT" --no-install-recommends --no-install-suggests
|
||||||
COPY --chmod=0700 reproducible-builds/entrypoint.sh /usr/local/bin/entrypoint.sh
|
COPY --chmod=0700 reproducible-builds/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
WORKDIR /signal-cli
|
WORKDIR /signal-cli
|
||||||
ENTRYPOINT [ "/usr/local/bin/entrypoint.sh", "build" ]
|
ENTRYPOINT [ "/usr/local/bin/entrypoint.sh", "build" ]
|
||||||
|
|||||||
@ -144,8 +144,7 @@ public class SendCommand implements JsonRpcLocalCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final var results = m.sendEndSessionMessage(singleRecipients);
|
m.sendEndSessionMessage(singleRecipients);
|
||||||
outputResult(outputWriter, results);
|
|
||||||
return;
|
return;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
|
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
|
||||||
|
|||||||
@ -543,9 +543,8 @@ public class DbusManagerImpl implements Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SendMessageResults sendEndSessionMessage(final Set<RecipientIdentifier.Single> recipients) throws IOException {
|
public void sendEndSessionMessage(final Set<RecipientIdentifier.Single> recipients) throws IOException {
|
||||||
signal.sendEndSessionMessage(recipients.stream().map(RecipientIdentifier.Single::getIdentifier).toList());
|
signal.sendEndSessionMessage(recipients.stream().map(RecipientIdentifier.Single::getIdentifier).toList());
|
||||||
return new SendMessageResults(0, Map.of());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -916,6 +915,14 @@ public class DbusManagerImpl implements Manager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addUnidentifiedKeepAlive(final String token) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUnidentifiedKeepAlive(final String token) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCallEventListener(final CallEventListener listener) {
|
public void addCallEventListener(final CallEventListener listener) {
|
||||||
// Not supported over DBus
|
// Not supported over DBus
|
||||||
|
|||||||
@ -100,6 +100,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable {
|
|||||||
|
|
||||||
public void initObjects() {
|
public void initObjects() {
|
||||||
exportObjects();
|
exportObjects();
|
||||||
|
m.addUnidentifiedKeepAlive("dbus");
|
||||||
if (!noReceiveOnStart) {
|
if (!noReceiveOnStart) {
|
||||||
subscribeReceive();
|
subscribeReceive();
|
||||||
}
|
}
|
||||||
@ -116,6 +117,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
m.removeUnidentifiedKeepAlive("dbus");
|
||||||
if (dbusMessageHandler != null) {
|
if (dbusMessageHandler != null) {
|
||||||
m.removeReceiveHandler(dbusMessageHandler);
|
m.removeReceiveHandler(dbusMessageHandler);
|
||||||
dbusMessageHandler = null;
|
dbusMessageHandler = null;
|
||||||
@ -430,8 +432,7 @@ public class DbusSignalImpl implements Signal, AutoCloseable {
|
|||||||
@Override
|
@Override
|
||||||
public void sendEndSessionMessage(final List<String> recipients) {
|
public void sendEndSessionMessage(final List<String> recipients) {
|
||||||
try {
|
try {
|
||||||
final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getSelfNumber()));
|
m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getSelfNumber()));
|
||||||
checkSendMessageResults(results);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new Error.Failure(e.getMessage());
|
throw new Error.Failure(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,12 +25,15 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
import java.nio.channels.ClosedChannelException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class SignalJsonRpcDispatcherHandler {
|
public class SignalJsonRpcDispatcherHandler {
|
||||||
|
|
||||||
@ -43,6 +46,9 @@ public class SignalJsonRpcDispatcherHandler {
|
|||||||
|
|
||||||
private final Map<Integer, List<Pair<Manager, Manager.ReceiveMessageHandler>>> receiveHandlers = new HashMap<>();
|
private final Map<Integer, List<Pair<Manager, Manager.ReceiveMessageHandler>>> receiveHandlers = new HashMap<>();
|
||||||
private final Map<Integer, List<Pair<Manager, Manager.CallEventListener>>> callEventHandlers = new HashMap<>();
|
private final Map<Integer, List<Pair<Manager, Manager.CallEventListener>>> callEventHandlers = new HashMap<>();
|
||||||
|
private final String connectionKeepAliveToken = "jsonrpc-" + UUID.randomUUID();
|
||||||
|
private final List<Manager> keepAliveManagers = new ArrayList<>();
|
||||||
|
private boolean connectionActive = true;
|
||||||
private SignalJsonRpcCommandHandler commandHandler;
|
private SignalJsonRpcCommandHandler commandHandler;
|
||||||
|
|
||||||
public SignalJsonRpcDispatcherHandler(
|
public SignalJsonRpcDispatcherHandler(
|
||||||
@ -69,6 +75,10 @@ public class SignalJsonRpcDispatcherHandler {
|
|||||||
c.addOnManagerAddedHandler(m -> callEventHandlers.forEach((subscriptionId, handlers) -> handlers.add(
|
c.addOnManagerAddedHandler(m -> callEventHandlers.forEach((subscriptionId, handlers) -> handlers.add(
|
||||||
createCallEventHandler(m, subscriptionId))));
|
createCallEventHandler(m, subscriptionId))));
|
||||||
|
|
||||||
|
c.getManagers().forEach(this::registerKeepAlive);
|
||||||
|
c.addOnManagerAddedHandler(this::registerKeepAlive);
|
||||||
|
c.addOnManagerRemovedHandler(this::unregisterKeepAlive);
|
||||||
|
|
||||||
handleConnection();
|
handleConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +92,8 @@ public class SignalJsonRpcDispatcherHandler {
|
|||||||
final var currentThread = Thread.currentThread();
|
final var currentThread = Thread.currentThread();
|
||||||
m.addClosedListener(currentThread::interrupt);
|
m.addClosedListener(currentThread::interrupt);
|
||||||
|
|
||||||
|
registerKeepAlive(m);
|
||||||
|
|
||||||
handleConnection();
|
handleConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +103,9 @@ public class SignalJsonRpcDispatcherHandler {
|
|||||||
|
|
||||||
private int subscribeCallEvents(final Collection<Manager> managers) {
|
private int subscribeCallEvents(final Collection<Manager> managers) {
|
||||||
final var subscriptionId = nextSubscriptionId.getAndIncrement();
|
final var subscriptionId = nextSubscriptionId.getAndIncrement();
|
||||||
final var listeners = managers.stream().map(m -> createCallEventHandler(m, subscriptionId)).toList();
|
final var listeners = managers.stream()
|
||||||
|
.map(m -> createCallEventHandler(m, subscriptionId))
|
||||||
|
.collect(Collectors.toCollection(ArrayList::new));
|
||||||
callEventHandlers.put(subscriptionId, listeners);
|
callEventHandlers.put(subscriptionId, listeners);
|
||||||
return subscriptionId;
|
return subscriptionId;
|
||||||
}
|
}
|
||||||
@ -146,7 +160,7 @@ public class SignalJsonRpcDispatcherHandler {
|
|||||||
final var subscriptionId = nextSubscriptionId.getAndIncrement();
|
final var subscriptionId = nextSubscriptionId.getAndIncrement();
|
||||||
final var handlers = managers.stream()
|
final var handlers = managers.stream()
|
||||||
.map(m -> createReceiveHandler(m, subscriptionId, internalSubscription))
|
.map(m -> createReceiveHandler(m, subscriptionId, internalSubscription))
|
||||||
.toList();
|
.collect(Collectors.toCollection(ArrayList::new));
|
||||||
receiveHandlers.put(subscriptionId, handlers);
|
receiveHandlers.put(subscriptionId, handlers);
|
||||||
|
|
||||||
return subscriptionId;
|
return subscriptionId;
|
||||||
@ -200,14 +214,29 @@ public class SignalJsonRpcDispatcherHandler {
|
|||||||
subscriptionId.ifPresent(this::unsubscribeReceive);
|
subscriptionId.ifPresent(this::unsubscribeReceive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerKeepAlive(final Manager m) {
|
||||||
|
if (!connectionActive) return;
|
||||||
|
m.addUnidentifiedKeepAlive(connectionKeepAliveToken);
|
||||||
|
keepAliveManagers.add(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregisterKeepAlive(final Manager m) {
|
||||||
|
if (!connectionActive) return;
|
||||||
|
m.removeUnidentifiedKeepAlive(connectionKeepAliveToken);
|
||||||
|
keepAliveManagers.remove(m);
|
||||||
|
}
|
||||||
|
|
||||||
private void handleConnection() {
|
private void handleConnection() {
|
||||||
try {
|
try {
|
||||||
jsonRpcReader.readMessages((method, params) -> commandHandler.handleRequest(objectMapper, method, params),
|
jsonRpcReader.readMessages((method, params) -> commandHandler.handleRequest(objectMapper, method, params),
|
||||||
response -> logger.debug("Received unexpected response for id {}", response.getId()));
|
response -> logger.debug("Received unexpected response for id {}", response.getId()));
|
||||||
} finally {
|
} finally {
|
||||||
|
connectionActive = false;
|
||||||
receiveHandlers.forEach((_subscriptionId, handlers) -> handlers.forEach(this::unsubscribeReceiveHandler));
|
receiveHandlers.forEach((_subscriptionId, handlers) -> handlers.forEach(this::unsubscribeReceiveHandler));
|
||||||
receiveHandlers.clear();
|
receiveHandlers.clear();
|
||||||
unsubscribeAllCallEvents();
|
unsubscribeAllCallEvents();
|
||||||
|
keepAliveManagers.forEach(m -> m.removeUnidentifiedKeepAlive(connectionKeepAliveToken));
|
||||||
|
keepAliveManagers.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -326,8 +326,7 @@ class SseInitialFlushTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SendMessageResults sendEndSessionMessage(Set<RecipientIdentifier.Single> recipients) {
|
public void sendEndSessionMessage(Set<RecipientIdentifier.Single> recipients) {
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -448,6 +447,14 @@ class SseInitialFlushTest {
|
|||||||
public void addClosedListener(Runnable listener) {
|
public void addClosedListener(Runnable listener) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addUnidentifiedKeepAlive(String token) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUnidentifiedKeepAlive(String token) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream retrieveAttachment(String id) {
|
public InputStream retrieveAttachment(String id) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -328,8 +328,7 @@ class SubscribeCallEventsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SendMessageResults sendEndSessionMessage(Set<RecipientIdentifier.Single> r) {
|
public void sendEndSessionMessage(Set<RecipientIdentifier.Single> r) {
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -496,6 +495,14 @@ class SubscribeCallEventsTest {
|
|||||||
public void addClosedListener(Runnable l) {
|
public void addClosedListener(Runnable l) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addUnidentifiedKeepAlive(String token) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUnidentifiedKeepAlive(String token) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream retrieveAttachment(String id) {
|
public InputStream retrieveAttachment(String id) {
|
||||||
return null;
|
return null;
|
||||||
@ -741,8 +748,8 @@ class SubscribeCallEventsTest {
|
|||||||
|
|
||||||
assertEquals(1, manager1.addCount.get(), "manager1 should have one listener");
|
assertEquals(1, manager1.addCount.get(), "manager1 should have one listener");
|
||||||
assertEquals(1, manager2.addCount.get(), "manager2 should have one listener");
|
assertEquals(1, manager2.addCount.get(), "manager2 should have one listener");
|
||||||
// Also registers an onManagerAdded handler for receive and one for call events
|
// Registers onManagerAdded handlers for receive, call events, and keep-alive
|
||||||
assertEquals(2, multi.addedHandlers.size(), "should register onManagerAdded handlers");
|
assertEquals(3, multi.addedHandlers.size(), "should register onManagerAdded handlers");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user