Search in sources :

Example 6 with ProofRequiredException

use of org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException in project Signal-Android by signalapp.

the class PushGroupSendJob method onPushSend.

@Override
public void onPushSend() throws IOException, MmsException, NoSuchMessageException, RetryLaterException {
    SignalLocalMetrics.GroupMessageSend.onJobStarted(messageId);
    MessageDatabase database = SignalDatabase.mms();
    OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
    long threadId = database.getMessageRecord(messageId).getThreadId();
    Set<NetworkFailure> existingNetworkFailures = message.getNetworkFailures();
    Set<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches();
    ApplicationDependencies.getJobManager().cancelAllInQueue(TypingSendJob.getQueue(threadId));
    if (database.isSent(messageId)) {
        log(TAG, String.valueOf(message.getSentTimeMillis()), "Message " + messageId + " was already sent. Ignoring.");
        return;
    }
    Recipient groupRecipient = message.getRecipient().resolve();
    if (!groupRecipient.isPushGroup()) {
        throw new MmsException("Message recipient isn't a group!");
    }
    if (groupRecipient.isPushV1Group()) {
        throw new MmsException("No GV1 messages can be sent anymore!");
    }
    try {
        log(TAG, String.valueOf(message.getSentTimeMillis()), "Sending message: " + messageId + ", Recipient: " + message.getRecipient().getId() + ", Thread: " + threadId + ", Attachments: " + buildAttachmentString(message.getAttachments()));
        if (!groupRecipient.resolve().isProfileSharing() && !database.isGroupQuitMessage(messageId)) {
            RecipientUtil.shareProfileIfFirstSecureMessage(context, groupRecipient);
        }
        List<Recipient> target;
        if (filterRecipient != null)
            target = Collections.singletonList(Recipient.resolved(filterRecipient));
        else if (!existingNetworkFailures.isEmpty())
            target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList();
        else
            target = Stream.of(getGroupMessageRecipients(groupRecipient.requireGroupId(), messageId)).distinctBy(Recipient::getId).toList();
        RecipientAccessList accessList = new RecipientAccessList(target);
        List<SendMessageResult> results = deliver(message, groupRecipient, target);
        Log.i(TAG, JobLogger.format(this, "Finished send."));
        List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(accessList.requireIdByAddress(result.getAddress()))).toList();
        List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(accessList.requireIdByAddress(result.getAddress()), result.getIdentityFailure().getIdentityKey())).toList();
        ProofRequiredException proofRequired = Stream.of(results).filter(r -> r.getProofRequiredFailure() != null).findLast().map(SendMessageResult::getProofRequiredFailure).orElse(null);
        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();
        List<RecipientId> unregisteredRecipients = Stream.of(results).filter(SendMessageResult::isUnregisteredFailure).map(result -> RecipientId.from(result.getAddress())).toList();
        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", networkFailures.size(), identityMismatches.size(), proofRequired != null, unregisteredRecipients.size()));
        }
        RecipientDatabase recipientDatabase = SignalDatabase.recipients();
        for (RecipientId unregistered : unregisteredRecipients) {
            recipientDatabase.markUnregistered(unregistered);
        }
        existingNetworkFailures.removeAll(resolvedNetworkFailures);
        existingNetworkFailures.addAll(networkFailures);
        database.setNetworkFailures(messageId, existingNetworkFailures);
        existingIdentityMismatches.removeAll(resolvedIdentityFailures);
        existingIdentityMismatches.addAll(identityMismatches);
        database.setMismatchedIdentities(messageId, existingIdentityMismatches);
        SignalDatabase.groupReceipts().setUnidentified(successUnidentifiedStatus, messageId);
        if (proofRequired != null) {
            handleProofRequiredException(proofRequired, groupRecipient, threadId, messageId, true);
        }
        if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) {
            database.markAsSent(messageId, true);
            markAttachmentsUploaded(messageId, message);
            if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
                database.markExpireStarted(messageId);
                ApplicationDependencies.getExpiringMessageManager().scheduleDeletion(messageId, true, message.getExpiresIn());
            }
            if (message.isViewOnce()) {
                SignalDatabase.attachments().deleteAttachmentFilesForViewOnceMessage(messageId);
            }
        } else if (!identityMismatches.isEmpty()) {
            Log.w(TAG, "Failing because there were " + identityMismatches.size() + " identity mismatches.");
            database.markAsSentFailed(messageId);
            notifyMediaMessageDeliveryFailed(context, messageId);
            Set<RecipientId> mismatchRecipientIds = Stream.of(identityMismatches).map(mismatch -> mismatch.getRecipientId(context)).collect(Collectors.toSet());
            RetrieveProfileJob.enqueue(mismatchRecipientIds);
        } else if (!networkFailures.isEmpty()) {
            Log.w(TAG, "Retrying because there were " + networkFailures.size() + " network failures.");
            throw new RetryLaterException();
        }
    } catch (UntrustedIdentityException | UndeliverableMessageException e) {
        warn(TAG, String.valueOf(message.getSentTimeMillis()), e);
        database.markAsSentFailed(messageId);
        notifyMediaMessageDeliveryFailed(context, messageId);
    }
    SignalLocalMetrics.GroupMessageSend.onJobFinished(messageId);
}
Also used : ServerRejectedException(org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException) SignalServiceDataMessage(org.whispersystems.signalservice.api.messages.SignalServiceDataMessage) SendMessageResult(org.whispersystems.signalservice.api.messages.SendMessageResult) NonNull(androidx.annotation.NonNull) Data(org.thoughtcrime.securesms.jobmanager.Data) JobManager(org.thoughtcrime.securesms.jobmanager.JobManager) RecipientUtil(org.thoughtcrime.securesms.recipients.RecipientUtil) Quote(org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Quote) MessageGroupContext(org.thoughtcrime.securesms.mms.MessageGroupContext) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) Locale(java.util.Locale) Recipient(org.thoughtcrime.securesms.recipients.Recipient) OutgoingGroupUpdateMessage(org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) SignalServiceAttachment(org.whispersystems.signalservice.api.messages.SignalServiceAttachment) Set(java.util.Set) GroupDatabase(org.thoughtcrime.securesms.database.GroupDatabase) UndeliverableMessageException(org.thoughtcrime.securesms.transport.UndeliverableMessageException) ByteString(com.google.protobuf.ByteString) Log(org.signal.core.util.logging.Log) GroupContextV2(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2) List(java.util.List) Nullable(androidx.annotation.Nullable) SignalServiceGroupV2(org.whispersystems.signalservice.api.messages.SignalServiceGroupV2) GroupId(org.thoughtcrime.securesms.groups.GroupId) Job(org.thoughtcrime.securesms.jobmanager.Job) SharedContact(org.whispersystems.signalservice.api.messages.shared.SharedContact) MessageDatabase(org.thoughtcrime.securesms.database.MessageDatabase) GroupUtil(org.thoughtcrime.securesms.util.GroupUtil) NoSuchMessageException(org.thoughtcrime.securesms.database.NoSuchMessageException) Attachment(org.thoughtcrime.securesms.attachments.Attachment) OutgoingMediaMessage(org.thoughtcrime.securesms.mms.OutgoingMediaMessage) Preview(org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview) Context(android.content.Context) RecipientAccessList(org.thoughtcrime.securesms.util.RecipientAccessList) SignalDatabase(org.thoughtcrime.securesms.database.SignalDatabase) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) ContentHint(org.whispersystems.signalservice.api.crypto.ContentHint) Stream(com.annimon.stream.Stream) JobLogger(org.thoughtcrime.securesms.jobmanager.JobLogger) SignalLocalMetrics(org.thoughtcrime.securesms.util.SignalLocalMetrics) WorkerThread(androidx.annotation.WorkerThread) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) Pair(org.whispersystems.libsignal.util.Pair) GroupSendUtil(org.thoughtcrime.securesms.messages.GroupSendUtil) GroupReceiptInfo(org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo) MessageId(org.thoughtcrime.securesms.database.model.MessageId) Collectors(com.annimon.stream.Collectors) GroupReceiptDatabase(org.thoughtcrime.securesms.database.GroupReceiptDatabase) IdentityKeyMismatch(org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch) MmsException(org.thoughtcrime.securesms.mms.MmsException) NetworkConstraint(org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint) IOException(java.io.IOException) Optional(org.whispersystems.libsignal.util.guava.Optional) TimeUnit(java.util.concurrent.TimeUnit) ProofRequiredException(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException) UntrustedIdentityException(org.whispersystems.signalservice.api.crypto.UntrustedIdentityException) NetworkFailure(org.thoughtcrime.securesms.database.documents.NetworkFailure) Collections(java.util.Collections) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) Set(java.util.Set) UntrustedIdentityException(org.whispersystems.signalservice.api.crypto.UntrustedIdentityException) OutgoingMediaMessage(org.thoughtcrime.securesms.mms.OutgoingMediaMessage) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) MmsException(org.thoughtcrime.securesms.mms.MmsException) UndeliverableMessageException(org.thoughtcrime.securesms.transport.UndeliverableMessageException) Pair(org.whispersystems.libsignal.util.Pair) MessageDatabase(org.thoughtcrime.securesms.database.MessageDatabase) Recipient(org.thoughtcrime.securesms.recipients.Recipient) ProofRequiredException(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException) NetworkFailure(org.thoughtcrime.securesms.database.documents.NetworkFailure) IdentityKeyMismatch(org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch) SendMessageResult(org.whispersystems.signalservice.api.messages.SendMessageResult) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) RecipientAccessList(org.thoughtcrime.securesms.util.RecipientAccessList)

Example 7 with ProofRequiredException

use of org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException in project Signal-Android by signalapp.

the class SignalServiceMessageSender method sendMessage.

private List<SendMessageResult> sendMessage(List<SignalServiceAddress> recipients, List<Optional<UnidentifiedAccess>> unidentifiedAccess, long timestamp, EnvelopeContent content, boolean online, PartialSendCompleteListener partialListener, CancelationSignal cancelationSignal) throws IOException {
    Log.d(TAG, "[" + timestamp + "] Sending to " + recipients.size() + " recipients.");
    enforceMaxContentSize(content);
    long startTime = System.currentTimeMillis();
    List<Future<SendMessageResult>> futureResults = new LinkedList<>();
    Iterator<SignalServiceAddress> recipientIterator = recipients.iterator();
    Iterator<Optional<UnidentifiedAccess>> unidentifiedAccessIterator = unidentifiedAccess.iterator();
    while (recipientIterator.hasNext()) {
        SignalServiceAddress recipient = recipientIterator.next();
        Optional<UnidentifiedAccess> access = unidentifiedAccessIterator.next();
        futureResults.add(executor.submit(() -> {
            SendMessageResult result = sendMessage(recipient, access, timestamp, content, online, cancelationSignal);
            if (partialListener != null) {
                partialListener.onPartialSendComplete(result);
            }
            return result;
        }));
    }
    List<SendMessageResult> results = new ArrayList<>(futureResults.size());
    recipientIterator = recipients.iterator();
    for (Future<SendMessageResult> futureResult : futureResults) {
        SignalServiceAddress recipient = recipientIterator.next();
        try {
            results.add(futureResult.get());
        } catch (ExecutionException e) {
            if (e.getCause() instanceof UntrustedIdentityException) {
                Log.w(TAG, e);
                results.add(SendMessageResult.identityFailure(recipient, ((UntrustedIdentityException) e.getCause()).getIdentityKey()));
            } else if (e.getCause() instanceof UnregisteredUserException) {
                Log.w(TAG, "[" + timestamp + "] Found unregistered user.");
                results.add(SendMessageResult.unregisteredFailure(recipient));
            } else if (e.getCause() instanceof PushNetworkException) {
                Log.w(TAG, e);
                results.add(SendMessageResult.networkFailure(recipient));
            } else if (e.getCause() instanceof ServerRejectedException) {
                Log.w(TAG, e);
                throw ((ServerRejectedException) e.getCause());
            } else if (e.getCause() instanceof ProofRequiredException) {
                Log.w(TAG, e);
                results.add(SendMessageResult.proofRequiredFailure(recipient, (ProofRequiredException) e.getCause()));
            } else {
                throw new IOException(e);
            }
        } catch (InterruptedException e) {
            throw new IOException(e);
        }
    }
    double sendsForAverage = 0;
    for (SendMessageResult result : results) {
        if (result.getSuccess() != null && result.getSuccess().getDuration() != -1) {
            sendsForAverage++;
        }
    }
    double average = 0;
    if (sendsForAverage > 0) {
        for (SendMessageResult result : results) {
            if (result.getSuccess() != null && result.getSuccess().getDuration() != -1) {
                average += result.getSuccess().getDuration() / sendsForAverage;
            }
        }
    }
    Log.d(TAG, "[" + timestamp + "] Completed send to " + recipients.size() + " recipients in " + (System.currentTimeMillis() - startTime) + " ms, with an average time of " + Math.round(average) + " ms per send.");
    return results;
}
Also used : UntrustedIdentityException(org.whispersystems.signalservice.api.crypto.UntrustedIdentityException) UnregisteredUserException(org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException) PushNetworkException(org.whispersystems.signalservice.api.push.exceptions.PushNetworkException) Optional(org.whispersystems.libsignal.util.guava.Optional) ArrayList(java.util.ArrayList) ServerRejectedException(org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException) ProofRequiredException(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException) IOException(java.io.IOException) LinkedList(java.util.LinkedList) SendMessageResult(org.whispersystems.signalservice.api.messages.SendMessageResult) UnidentifiedAccess(org.whispersystems.signalservice.api.crypto.UnidentifiedAccess) Future(java.util.concurrent.Future) SignalServiceAddress(org.whispersystems.signalservice.api.push.SignalServiceAddress) ExecutionException(java.util.concurrent.ExecutionException)

Example 8 with ProofRequiredException

use of org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException in project Signal-Android by signalapp.

the class DefaultErrorMapper method parseError.

@Override
public Throwable parseError(int status, String body, Function<String, String> getHeader) {
    if (customErrorMappers.containsKey(status)) {
        try {
            return customErrorMappers.get(status).parseError(status, body, getHeader);
        } catch (MalformedResponseException e) {
            return e;
        }
    }
    switch(status) {
        case 401:
        case 403:
            return new AuthorizationFailedException(status, "Authorization failed!");
        case 402:
            return new CaptchaRequiredException();
        case 404:
            return new NotFoundException("Not found");
        case 409:
            try {
                return new MismatchedDevicesException(JsonUtil.fromJsonResponse(body, MismatchedDevices.class));
            } catch (MalformedResponseException e) {
                return e;
            }
        case 410:
            try {
                return new StaleDevicesException(JsonUtil.fromJsonResponse(body, StaleDevices.class));
            } catch (MalformedResponseException e) {
                return e;
            }
        case 411:
            try {
                return new DeviceLimitExceededException(JsonUtil.fromJsonResponse(body, DeviceLimit.class));
            } catch (MalformedResponseException e) {
                return e;
            }
        case 413:
            return new RateLimitException("Rate limit exceeded: " + status);
        case 417:
            return new ExpectationFailedException();
        case 423:
            PushServiceSocket.RegistrationLockFailure accountLockFailure;
            try {
                accountLockFailure = JsonUtil.fromJsonResponse(body, PushServiceSocket.RegistrationLockFailure.class);
            } catch (MalformedResponseException e) {
                return e;
            }
            AuthCredentials credentials = accountLockFailure.backupCredentials;
            String basicStorageCredentials = credentials != null ? credentials.asBasic() : null;
            return new LockedException(accountLockFailure.length, accountLockFailure.timeRemaining, basicStorageCredentials);
        case 428:
            ProofRequiredResponse proofRequiredResponse;
            try {
                proofRequiredResponse = JsonUtil.fromJsonResponse(body, ProofRequiredResponse.class);
            } catch (MalformedResponseException e) {
                return e;
            }
            String retryAfterRaw = getHeader.apply("Retry-After");
            long retryAfter = Util.parseInt(retryAfterRaw, -1);
            return new ProofRequiredException(proofRequiredResponse, retryAfter);
        case 499:
            return new DeprecatedVersionException();
        case 508:
            return new ServerRejectedException();
    }
    if (status != 200 && status != 202 && status != 204) {
        return new NonSuccessfulResponseCodeException(status, "Bad response: " + status);
    }
    return null;
}
Also used : LockedException(org.whispersystems.signalservice.internal.push.LockedException) RateLimitException(org.whispersystems.signalservice.api.push.exceptions.RateLimitException) DeprecatedVersionException(org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException) ExpectationFailedException(org.whispersystems.signalservice.api.push.exceptions.ExpectationFailedException) DeviceLimit(org.whispersystems.signalservice.internal.push.DeviceLimit) NotFoundException(org.whispersystems.signalservice.api.push.exceptions.NotFoundException) NonSuccessfulResponseCodeException(org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException) StaleDevicesException(org.whispersystems.signalservice.internal.push.exceptions.StaleDevicesException) ProofRequiredException(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException) ServerRejectedException(org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException) MalformedResponseException(org.whispersystems.signalservice.api.push.exceptions.MalformedResponseException) MismatchedDevices(org.whispersystems.signalservice.internal.push.MismatchedDevices) StaleDevices(org.whispersystems.signalservice.internal.push.StaleDevices) DeviceLimitExceededException(org.whispersystems.signalservice.internal.push.DeviceLimitExceededException) AuthCredentials(org.whispersystems.signalservice.internal.push.AuthCredentials) AuthorizationFailedException(org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException) MismatchedDevicesException(org.whispersystems.signalservice.internal.push.exceptions.MismatchedDevicesException) PushServiceSocket(org.whispersystems.signalservice.internal.push.PushServiceSocket) CaptchaRequiredException(org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException) ProofRequiredResponse(org.whispersystems.signalservice.internal.push.ProofRequiredResponse)

Example 9 with ProofRequiredException

use of org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException in project Signal-Android by WhisperSystems.

the class PushGroupSendJob method onPushSend.

@Override
public void onPushSend() throws IOException, MmsException, NoSuchMessageException, RetryLaterException {
    SignalLocalMetrics.GroupMessageSend.onJobStarted(messageId);
    MessageDatabase database = SignalDatabase.mms();
    OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
    long threadId = database.getMessageRecord(messageId).getThreadId();
    Set<NetworkFailure> existingNetworkFailures = message.getNetworkFailures();
    Set<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches();
    ApplicationDependencies.getJobManager().cancelAllInQueue(TypingSendJob.getQueue(threadId));
    if (database.isSent(messageId)) {
        log(TAG, String.valueOf(message.getSentTimeMillis()), "Message " + messageId + " was already sent. Ignoring.");
        return;
    }
    Recipient groupRecipient = message.getRecipient().resolve();
    if (!groupRecipient.isPushGroup()) {
        throw new MmsException("Message recipient isn't a group!");
    }
    if (groupRecipient.isPushV1Group()) {
        throw new MmsException("No GV1 messages can be sent anymore!");
    }
    try {
        log(TAG, String.valueOf(message.getSentTimeMillis()), "Sending message: " + messageId + ", Recipient: " + message.getRecipient().getId() + ", Thread: " + threadId + ", Attachments: " + buildAttachmentString(message.getAttachments()));
        if (!groupRecipient.resolve().isProfileSharing() && !database.isGroupQuitMessage(messageId)) {
            RecipientUtil.shareProfileIfFirstSecureMessage(context, groupRecipient);
        }
        List<Recipient> target;
        if (filterRecipient != null)
            target = Collections.singletonList(Recipient.resolved(filterRecipient));
        else if (!existingNetworkFailures.isEmpty())
            target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList();
        else
            target = Stream.of(getGroupMessageRecipients(groupRecipient.requireGroupId(), messageId)).distinctBy(Recipient::getId).toList();
        RecipientAccessList accessList = new RecipientAccessList(target);
        List<SendMessageResult> results = deliver(message, groupRecipient, target);
        Log.i(TAG, JobLogger.format(this, "Finished send."));
        List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(accessList.requireIdByAddress(result.getAddress()))).toList();
        List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(accessList.requireIdByAddress(result.getAddress()), result.getIdentityFailure().getIdentityKey())).toList();
        ProofRequiredException proofRequired = Stream.of(results).filter(r -> r.getProofRequiredFailure() != null).findLast().map(SendMessageResult::getProofRequiredFailure).orElse(null);
        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();
        List<RecipientId> unregisteredRecipients = Stream.of(results).filter(SendMessageResult::isUnregisteredFailure).map(result -> RecipientId.from(result.getAddress())).toList();
        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", networkFailures.size(), identityMismatches.size(), proofRequired != null, unregisteredRecipients.size()));
        }
        RecipientDatabase recipientDatabase = SignalDatabase.recipients();
        for (RecipientId unregistered : unregisteredRecipients) {
            recipientDatabase.markUnregistered(unregistered);
        }
        existingNetworkFailures.removeAll(resolvedNetworkFailures);
        existingNetworkFailures.addAll(networkFailures);
        database.setNetworkFailures(messageId, existingNetworkFailures);
        existingIdentityMismatches.removeAll(resolvedIdentityFailures);
        existingIdentityMismatches.addAll(identityMismatches);
        database.setMismatchedIdentities(messageId, existingIdentityMismatches);
        SignalDatabase.groupReceipts().setUnidentified(successUnidentifiedStatus, messageId);
        if (proofRequired != null) {
            handleProofRequiredException(proofRequired, groupRecipient, threadId, messageId, true);
        }
        if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) {
            database.markAsSent(messageId, true);
            markAttachmentsUploaded(messageId, message);
            if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
                database.markExpireStarted(messageId);
                ApplicationDependencies.getExpiringMessageManager().scheduleDeletion(messageId, true, message.getExpiresIn());
            }
            if (message.isViewOnce()) {
                SignalDatabase.attachments().deleteAttachmentFilesForViewOnceMessage(messageId);
            }
        } else if (!identityMismatches.isEmpty()) {
            Log.w(TAG, "Failing because there were " + identityMismatches.size() + " identity mismatches.");
            database.markAsSentFailed(messageId);
            notifyMediaMessageDeliveryFailed(context, messageId);
            Set<RecipientId> mismatchRecipientIds = Stream.of(identityMismatches).map(mismatch -> mismatch.getRecipientId(context)).collect(Collectors.toSet());
            RetrieveProfileJob.enqueue(mismatchRecipientIds);
        } else if (!networkFailures.isEmpty()) {
            Log.w(TAG, "Retrying because there were " + networkFailures.size() + " network failures.");
            throw new RetryLaterException();
        }
    } catch (UntrustedIdentityException | UndeliverableMessageException e) {
        warn(TAG, String.valueOf(message.getSentTimeMillis()), e);
        database.markAsSentFailed(messageId);
        notifyMediaMessageDeliveryFailed(context, messageId);
    }
    SignalLocalMetrics.GroupMessageSend.onJobFinished(messageId);
}
Also used : ServerRejectedException(org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException) SignalServiceDataMessage(org.whispersystems.signalservice.api.messages.SignalServiceDataMessage) SendMessageResult(org.whispersystems.signalservice.api.messages.SendMessageResult) NonNull(androidx.annotation.NonNull) Data(org.thoughtcrime.securesms.jobmanager.Data) JobManager(org.thoughtcrime.securesms.jobmanager.JobManager) RecipientUtil(org.thoughtcrime.securesms.recipients.RecipientUtil) Quote(org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Quote) MessageGroupContext(org.thoughtcrime.securesms.mms.MessageGroupContext) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) Locale(java.util.Locale) Recipient(org.thoughtcrime.securesms.recipients.Recipient) OutgoingGroupUpdateMessage(org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) SignalServiceAttachment(org.whispersystems.signalservice.api.messages.SignalServiceAttachment) Set(java.util.Set) GroupDatabase(org.thoughtcrime.securesms.database.GroupDatabase) UndeliverableMessageException(org.thoughtcrime.securesms.transport.UndeliverableMessageException) ByteString(com.google.protobuf.ByteString) Log(org.signal.core.util.logging.Log) GroupContextV2(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2) List(java.util.List) Nullable(androidx.annotation.Nullable) SignalServiceGroupV2(org.whispersystems.signalservice.api.messages.SignalServiceGroupV2) GroupId(org.thoughtcrime.securesms.groups.GroupId) Job(org.thoughtcrime.securesms.jobmanager.Job) SharedContact(org.whispersystems.signalservice.api.messages.shared.SharedContact) MessageDatabase(org.thoughtcrime.securesms.database.MessageDatabase) GroupUtil(org.thoughtcrime.securesms.util.GroupUtil) NoSuchMessageException(org.thoughtcrime.securesms.database.NoSuchMessageException) Attachment(org.thoughtcrime.securesms.attachments.Attachment) OutgoingMediaMessage(org.thoughtcrime.securesms.mms.OutgoingMediaMessage) Preview(org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview) Context(android.content.Context) RecipientAccessList(org.thoughtcrime.securesms.util.RecipientAccessList) SignalDatabase(org.thoughtcrime.securesms.database.SignalDatabase) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) ContentHint(org.whispersystems.signalservice.api.crypto.ContentHint) Stream(com.annimon.stream.Stream) JobLogger(org.thoughtcrime.securesms.jobmanager.JobLogger) SignalLocalMetrics(org.thoughtcrime.securesms.util.SignalLocalMetrics) WorkerThread(androidx.annotation.WorkerThread) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) Pair(org.whispersystems.libsignal.util.Pair) GroupSendUtil(org.thoughtcrime.securesms.messages.GroupSendUtil) GroupReceiptInfo(org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo) MessageId(org.thoughtcrime.securesms.database.model.MessageId) Collectors(com.annimon.stream.Collectors) GroupReceiptDatabase(org.thoughtcrime.securesms.database.GroupReceiptDatabase) IdentityKeyMismatch(org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch) MmsException(org.thoughtcrime.securesms.mms.MmsException) NetworkConstraint(org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint) IOException(java.io.IOException) Optional(org.whispersystems.libsignal.util.guava.Optional) TimeUnit(java.util.concurrent.TimeUnit) ProofRequiredException(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException) UntrustedIdentityException(org.whispersystems.signalservice.api.crypto.UntrustedIdentityException) NetworkFailure(org.thoughtcrime.securesms.database.documents.NetworkFailure) Collections(java.util.Collections) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) Set(java.util.Set) UntrustedIdentityException(org.whispersystems.signalservice.api.crypto.UntrustedIdentityException) OutgoingMediaMessage(org.thoughtcrime.securesms.mms.OutgoingMediaMessage) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) MmsException(org.thoughtcrime.securesms.mms.MmsException) UndeliverableMessageException(org.thoughtcrime.securesms.transport.UndeliverableMessageException) Pair(org.whispersystems.libsignal.util.Pair) MessageDatabase(org.thoughtcrime.securesms.database.MessageDatabase) Recipient(org.thoughtcrime.securesms.recipients.Recipient) ProofRequiredException(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException) NetworkFailure(org.thoughtcrime.securesms.database.documents.NetworkFailure) IdentityKeyMismatch(org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch) SendMessageResult(org.whispersystems.signalservice.api.messages.SendMessageResult) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) RecipientAccessList(org.thoughtcrime.securesms.util.RecipientAccessList)

Example 10 with ProofRequiredException

use of org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException in project Signal-Android by signalapp.

the class PushMediaSendJob method onPushSend.

@Override
public void onPushSend() throws IOException, MmsException, NoSuchMessageException, UndeliverableMessageException, RetryLaterException {
    ExpiringMessageManager expirationManager = ApplicationDependencies.getExpiringMessageManager();
    MessageDatabase database = SignalDatabase.mms();
    OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
    long threadId = database.getMessageRecord(messageId).getThreadId();
    if (database.isSent(messageId)) {
        warn(TAG, String.valueOf(message.getSentTimeMillis()), "Message " + messageId + " was already sent. Ignoring.");
        return;
    }
    try {
        log(TAG, String.valueOf(message.getSentTimeMillis()), "Sending message: " + messageId + ", Recipient: " + message.getRecipient().getId() + ", Thread: " + threadId + ", Attachments: " + buildAttachmentString(message.getAttachments()));
        RecipientUtil.shareProfileIfFirstSecureMessage(context, message.getRecipient());
        Recipient recipient = message.getRecipient().fresh();
        byte[] profileKey = recipient.getProfileKey();
        UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode();
        boolean unidentified = deliver(message);
        database.markAsSent(messageId, true);
        markAttachmentsUploaded(messageId, message);
        database.markUnidentified(messageId, unidentified);
        if (recipient.isSelf()) {
            SyncMessageId id = new SyncMessageId(recipient.getId(), message.getSentTimeMillis());
            SignalDatabase.mmsSms().incrementDeliveryReceiptCount(id, System.currentTimeMillis());
            SignalDatabase.mmsSms().incrementReadReceiptCount(id, System.currentTimeMillis());
            SignalDatabase.mmsSms().incrementViewedReceiptCount(id, System.currentTimeMillis());
        }
        if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) {
            log(TAG, String.valueOf(message.getSentTimeMillis()), "Marking recipient as UD-unrestricted following a UD send.");
            SignalDatabase.recipients().setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.UNRESTRICTED);
        } else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) {
            log(TAG, String.valueOf(message.getSentTimeMillis()), "Marking recipient as UD-enabled following a UD send.");
            SignalDatabase.recipients().setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.ENABLED);
        } else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
            log(TAG, String.valueOf(message.getSentTimeMillis()), "Marking recipient as UD-disabled following a non-UD send.");
            SignalDatabase.recipients().setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.DISABLED);
        }
        if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
            database.markExpireStarted(messageId);
            expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn());
        }
        if (message.isViewOnce()) {
            SignalDatabase.attachments().deleteAttachmentFilesForViewOnceMessage(messageId);
        }
        log(TAG, String.valueOf(message.getSentTimeMillis()), "Sent message: " + messageId);
    } catch (InsecureFallbackApprovalException ifae) {
        warn(TAG, "Failure", ifae);
        database.markAsPendingInsecureSmsFallback(messageId);
        notifyMediaMessageDeliveryFailed(context, messageId);
        ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
    } catch (UntrustedIdentityException uie) {
        warn(TAG, "Failure", uie);
        RecipientId recipientId = Recipient.external(context, uie.getIdentifier()).getId();
        database.addMismatchedIdentity(messageId, recipientId, uie.getIdentityKey());
        database.markAsSentFailed(messageId);
        RetrieveProfileJob.enqueue(recipientId);
    } catch (ProofRequiredException e) {
        handleProofRequiredException(e, SignalDatabase.threads().getRecipientForThreadId(threadId), threadId, messageId, true);
    }
}
Also used : MessageDatabase(org.thoughtcrime.securesms.database.MessageDatabase) UntrustedIdentityException(org.whispersystems.signalservice.api.crypto.UntrustedIdentityException) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) ExpiringMessageManager(org.thoughtcrime.securesms.service.ExpiringMessageManager) OutgoingMediaMessage(org.thoughtcrime.securesms.mms.OutgoingMediaMessage) Recipient(org.thoughtcrime.securesms.recipients.Recipient) ProofRequiredException(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException) UnidentifiedAccessMode(org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode) InsecureFallbackApprovalException(org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException) SyncMessageId(org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId)

Aggregations

ProofRequiredException (org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException)12 UntrustedIdentityException (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException)8 ServerRejectedException (org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException)8 MessageDatabase (org.thoughtcrime.securesms.database.MessageDatabase)6 Recipient (org.thoughtcrime.securesms.recipients.Recipient)6 RecipientId (org.thoughtcrime.securesms.recipients.RecipientId)6 IOException (java.io.IOException)4 SyncMessageId (org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId)4 UnidentifiedAccessMode (org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode)4 OutgoingMediaMessage (org.thoughtcrime.securesms.mms.OutgoingMediaMessage)4 ExpiringMessageManager (org.thoughtcrime.securesms.service.ExpiringMessageManager)4 InsecureFallbackApprovalException (org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException)4 Optional (org.whispersystems.libsignal.util.guava.Optional)3 SendMessageResult (org.whispersystems.signalservice.api.messages.SendMessageResult)3 AuthorizationFailedException (org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException)3 DeprecatedVersionException (org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException)3 ExpectationFailedException (org.whispersystems.signalservice.api.push.exceptions.ExpectationFailedException)3 NonSuccessfulResponseCodeException (org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException)3 NotFoundException (org.whispersystems.signalservice.api.push.exceptions.NotFoundException)3 RateLimitException (org.whispersystems.signalservice.api.push.exceptions.RateLimitException)3