mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-07-08 15:04:21 +01:00
Skip sends to users with prekey failures.
This commit is contained in:
committed by
Cody Henthorne
parent
6b91e525db
commit
3e21fb77c7
@@ -421,12 +421,15 @@ public final class PushGroupSendJob extends PushSendJob {
|
||||
List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
|
||||
List<Pair<RecipientId, Boolean>> successUnidentifiedStatus = Stream.of(successes).map(result -> new Pair<>(accessList.requireIdByAddress(result.getAddress()), result.getSuccess().isUnidentified())).toList();
|
||||
Set<RecipientId> successIds = Stream.of(successUnidentifiedStatus).map(Pair::first).collect(Collectors.toSet());
|
||||
List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
|
||||
List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
|
||||
Set<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId(context))).collect(Collectors.toSet());
|
||||
Set<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId(context))).collect(Collectors.toSet());
|
||||
List<RecipientId> unregisteredRecipients = Stream.of(results).filter(SendMessageResult::isUnregisteredFailure).map(result -> RecipientId.from(result.getAddress())).toList();
|
||||
List<RecipientId> skippedRecipients = new ArrayList<>(unregisteredRecipients);
|
||||
List<RecipientId> invalidPreKeyRecipients = Stream.of(results).filter(SendMessageResult::isInvalidPreKeyFailure).map(result -> RecipientId.from(result.getAddress())).toList();
|
||||
Set<RecipientId> skippedRecipients = new HashSet<>();
|
||||
|
||||
skippedRecipients.addAll(skipped);
|
||||
skippedRecipients.addAll(unregisteredRecipients);
|
||||
skippedRecipients.addAll(invalidPreKeyRecipients);
|
||||
|
||||
if (networkFailures.size() > 0 || identityMismatches.size() > 0 || proofRequired != null || unregisteredRecipients.size() > 0) {
|
||||
Log.w(TAG, String.format(Locale.US, "Failed to send to some recipients. Network: %d, Identity: %d, ProofRequired: %s, Unregistered: %d",
|
||||
@@ -439,10 +442,12 @@ public final class PushGroupSendJob extends PushSendJob {
|
||||
}
|
||||
|
||||
existingNetworkFailures.removeAll(resolvedNetworkFailures);
|
||||
existingNetworkFailures.removeIf(it -> skippedRecipients.contains(it.getRecipientId(context)));
|
||||
existingNetworkFailures.addAll(networkFailures);
|
||||
database.setNetworkFailures(messageId, existingNetworkFailures);
|
||||
|
||||
existingIdentityMismatches.removeAll(resolvedIdentityFailures);
|
||||
existingIdentityMismatches.removeIf(it -> skippedRecipients.contains(it.getRecipientId(context)));
|
||||
existingIdentityMismatches.addAll(identityMismatches);
|
||||
database.setMismatchedIdentities(messageId, existingIdentityMismatches);
|
||||
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api
|
||||
|
||||
import org.signal.libsignal.protocol.InvalidKeyException
|
||||
import org.signal.libsignal.protocol.SignalProtocolAddress
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Wraps an [InvalidKeyException] in an [IOException] with a nicer message.
|
||||
*/
|
||||
class InvalidPreKeyException(
|
||||
address: SignalProtocolAddress,
|
||||
invalidKeyException: InvalidKeyException
|
||||
) : IOException("Invalid prekey for $address", invalidKeyException)
|
||||
+17
-11
@@ -1849,24 +1849,28 @@ public class SignalServiceMessageSender {
|
||||
results.add(futureResult.get());
|
||||
} catch (ExecutionException e) {
|
||||
if (e.getCause() instanceof UntrustedIdentityException) {
|
||||
Log.w(TAG, e);
|
||||
Log.w(TAG, "[" + timestamp + "] Hit identity mismatch: " + recipient.getIdentifier(), e);
|
||||
results.add(SendMessageResult.identityFailure(recipient, ((UntrustedIdentityException) e.getCause()).getIdentityKey()));
|
||||
} else if (e.getCause() instanceof UnregisteredUserException) {
|
||||
Log.w(TAG, "[" + timestamp + "] Found unregistered user.");
|
||||
Log.w(TAG, "[" + timestamp + "] Hit unregistered user: " + recipient.getIdentifier());
|
||||
results.add(SendMessageResult.unregisteredFailure(recipient));
|
||||
} else if (e.getCause() instanceof PushNetworkException) {
|
||||
Log.w(TAG, e);
|
||||
Log.w(TAG, "[" + timestamp + "] Hit network failure: " + recipient.getIdentifier(), e);
|
||||
results.add(SendMessageResult.networkFailure(recipient));
|
||||
} else if (e.getCause() instanceof ServerRejectedException) {
|
||||
Log.w(TAG, e);
|
||||
Log.w(TAG, "[" + timestamp + "] Hit server rejection: " + recipient.getIdentifier(), e);
|
||||
throw ((ServerRejectedException) e.getCause());
|
||||
} else if (e.getCause() instanceof ProofRequiredException) {
|
||||
Log.w(TAG, e);
|
||||
Log.w(TAG, "[" + timestamp + "] Hit proof required: " + recipient.getIdentifier(), e);
|
||||
results.add(SendMessageResult.proofRequiredFailure(recipient, (ProofRequiredException) e.getCause()));
|
||||
} else if (e.getCause() instanceof RateLimitException) {
|
||||
Log.w(TAG, e);
|
||||
Log.w(TAG, "[" + timestamp + "] Hit rate limit: " + recipient.getIdentifier(), e);
|
||||
results.add(SendMessageResult.rateLimitFailure(recipient, (RateLimitException) e.getCause()));
|
||||
} else if (e.getCause() instanceof InvalidPreKeyException) {
|
||||
Log.w(TAG, "[" + timestamp + "] Hit invalid prekey: " + recipient.getIdentifier(), e);
|
||||
results.add(SendMessageResult.invalidPreKeyFailure(recipient));
|
||||
} else {
|
||||
Log.w(TAG, "[" + timestamp + "] Hit unknown exception: " + recipient.getIdentifier(), e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
@@ -2317,10 +2321,10 @@ public class SignalServiceMessageSender {
|
||||
|
||||
// Visible for testing only
|
||||
public OutgoingPushMessage getEncryptedMessage(SignalServiceAddress recipient,
|
||||
Optional<UnidentifiedAccess> unidentifiedAccess,
|
||||
int deviceId,
|
||||
EnvelopeContent plaintext,
|
||||
boolean story)
|
||||
Optional<UnidentifiedAccess> unidentifiedAccess,
|
||||
int deviceId,
|
||||
EnvelopeContent plaintext,
|
||||
boolean story)
|
||||
throws IOException, InvalidKeyException, UntrustedIdentityException
|
||||
{
|
||||
SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(recipient.getIdentifier(), deviceId);
|
||||
@@ -2331,6 +2335,8 @@ public class SignalServiceMessageSender {
|
||||
List<PreKeyBundle> preKeys = getPreKeys(recipient, unidentifiedAccess, deviceId, story);
|
||||
|
||||
for (PreKeyBundle preKey : preKeys) {
|
||||
Log.d(TAG, "Initializing prekey session for " + signalProtocolAddress);
|
||||
|
||||
try {
|
||||
SignalProtocolAddress preKeyAddress = new SignalProtocolAddress(recipient.getIdentifier(), preKey.getDeviceId());
|
||||
SignalSessionBuilder sessionBuilder = new SignalSessionBuilder(sessionLock, new SessionBuilder(aciStore, preKeyAddress));
|
||||
@@ -2344,7 +2350,7 @@ public class SignalServiceMessageSender {
|
||||
eventListener.get().onSecurityEvent(recipient);
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new IOException(e);
|
||||
throw new InvalidPreKeyException(signalProtocolAddress, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-7
@@ -19,29 +19,34 @@ public class SendMessageResult {
|
||||
private final IdentityFailure identityFailure;
|
||||
private final ProofRequiredException proofRequiredFailure;
|
||||
private final RateLimitException rateLimitFailure;
|
||||
private final boolean invalidPreKeyFailure;
|
||||
|
||||
public static SendMessageResult success(SignalServiceAddress address, List<Integer> devices, boolean unidentified, boolean needsSync, long duration, Optional<Content> content) {
|
||||
return new SendMessageResult(address, new Success(unidentified, needsSync, duration, content, devices), false, false, null, null, null);
|
||||
return new SendMessageResult(address, new Success(unidentified, needsSync, duration, content, devices), false, false, null, null, null, false);
|
||||
}
|
||||
|
||||
public static SendMessageResult networkFailure(SignalServiceAddress address) {
|
||||
return new SendMessageResult(address, null, true, false, null, null, null);
|
||||
return new SendMessageResult(address, null, true, false, null, null, null, false);
|
||||
}
|
||||
|
||||
public static SendMessageResult unregisteredFailure(SignalServiceAddress address) {
|
||||
return new SendMessageResult(address, null, false, true, null, null, null);
|
||||
return new SendMessageResult(address, null, false, true, null, null, null, false);
|
||||
}
|
||||
|
||||
public static SendMessageResult identityFailure(SignalServiceAddress address, IdentityKey identityKey) {
|
||||
return new SendMessageResult(address, null, false, false, new IdentityFailure(identityKey), null, null);
|
||||
return new SendMessageResult(address, null, false, false, new IdentityFailure(identityKey), null, null, false);
|
||||
}
|
||||
|
||||
public static SendMessageResult proofRequiredFailure(SignalServiceAddress address, ProofRequiredException proofRequiredException) {
|
||||
return new SendMessageResult(address, null, false, false, null, proofRequiredException, null);
|
||||
return new SendMessageResult(address, null, false, false, null, proofRequiredException, null, false);
|
||||
}
|
||||
|
||||
public static SendMessageResult rateLimitFailure(SignalServiceAddress address, RateLimitException rateLimitException) {
|
||||
return new SendMessageResult(address, null, false, false, null, null, rateLimitException);
|
||||
return new SendMessageResult(address, null, false, false, null, null, rateLimitException, false);
|
||||
}
|
||||
|
||||
public static SendMessageResult invalidPreKeyFailure(SignalServiceAddress address) {
|
||||
return new SendMessageResult(address, null, false, false, null, null, null, true);
|
||||
}
|
||||
|
||||
public SignalServiceAddress getAddress() {
|
||||
@@ -76,13 +81,18 @@ public class SendMessageResult {
|
||||
return rateLimitFailure;
|
||||
}
|
||||
|
||||
public boolean isInvalidPreKeyFailure() {
|
||||
return invalidPreKeyFailure;
|
||||
}
|
||||
|
||||
private SendMessageResult(SignalServiceAddress address,
|
||||
Success success,
|
||||
boolean networkFailure,
|
||||
boolean unregisteredFailure,
|
||||
IdentityFailure identityFailure,
|
||||
ProofRequiredException proofRequiredFailure,
|
||||
RateLimitException rateLimitFailure)
|
||||
RateLimitException rateLimitFailure,
|
||||
boolean invalidPreKeyFailure)
|
||||
{
|
||||
this.address = address;
|
||||
this.success = success;
|
||||
@@ -91,6 +101,7 @@ public class SendMessageResult {
|
||||
this.identityFailure = identityFailure;
|
||||
this.proofRequiredFailure = proofRequiredFailure;
|
||||
this.rateLimitFailure = rateLimitFailure;
|
||||
this.invalidPreKeyFailure = invalidPreKeyFailure;
|
||||
}
|
||||
|
||||
public static class Success {
|
||||
|
||||
Reference in New Issue
Block a user