use of org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException in project Signal-Android by WhisperSystems.
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);
}
}
use of org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException in project Signal-Android by WhisperSystems.
the class PushTextSendJob method onPushSend.
@Override
public void onPushSend() throws IOException, NoSuchMessageException, UndeliverableMessageException, RetryLaterException {
SignalLocalMetrics.IndividualMessageSend.onJobStarted(messageId);
ExpiringMessageManager expirationManager = ApplicationDependencies.getExpiringMessageManager();
MessageDatabase database = SignalDatabase.sms();
SmsMessageRecord record = database.getSmsMessage(messageId);
if (!record.isPending() && !record.isFailed()) {
warn(TAG, String.valueOf(record.getDateSent()), "Message " + messageId + " was already sent. Ignoring.");
return;
}
try {
log(TAG, String.valueOf(record.getDateSent()), "Sending message: " + messageId + ", Recipient: " + record.getRecipient().getId() + ", Thread: " + record.getThreadId());
RecipientUtil.shareProfileIfFirstSecureMessage(context, record.getRecipient());
Recipient recipient = record.getRecipient().resolve();
byte[] profileKey = recipient.getProfileKey();
UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode();
boolean unidentified = deliver(record);
database.markAsSent(messageId, true);
database.markUnidentified(messageId, unidentified);
if (recipient.isSelf()) {
SyncMessageId id = new SyncMessageId(recipient.getId(), record.getDateSent());
SignalDatabase.mmsSms().incrementDeliveryReceiptCount(id, System.currentTimeMillis());
SignalDatabase.mmsSms().incrementReadReceiptCount(id, System.currentTimeMillis());
}
if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) {
log(TAG, String.valueOf(record.getDateSent()), "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(record.getDateSent()), "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(record.getDateSent()), "Marking recipient as UD-disabled following a non-UD send.");
SignalDatabase.recipients().setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.DISABLED);
}
if (record.getExpiresIn() > 0) {
database.markExpireStarted(messageId);
expirationManager.scheduleDeletion(record.getId(), record.isMms(), record.getExpiresIn());
}
log(TAG, String.valueOf(record.getDateSent()), "Sent message: " + messageId);
} catch (InsecureFallbackApprovalException e) {
warn(TAG, String.valueOf(record.getDateSent()), "Failure", e);
database.markAsPendingInsecureSmsFallback(record.getId());
ApplicationDependencies.getMessageNotifier().notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
} catch (UntrustedIdentityException e) {
warn(TAG, String.valueOf(record.getDateSent()), "Failure", e);
RecipientId recipientId = Recipient.external(context, e.getIdentifier()).getId();
database.addMismatchedIdentity(record.getId(), recipientId, e.getIdentityKey());
database.markAsSentFailed(record.getId());
database.markAsPush(record.getId());
RetrieveProfileJob.enqueue(recipientId);
} catch (ProofRequiredException e) {
handleProofRequiredException(e, record.getRecipient(), record.getThreadId(), messageId, false);
}
SignalLocalMetrics.IndividualMessageSend.onJobFinished(messageId);
}
use of org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException in project Signal-Android by WhisperSystems.
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;
}
use of org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException in project Signal-Android by WhisperSystems.
the class PushServiceSocket method validateServiceResponse.
private Response validateServiceResponse(Response response) throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException {
int responseCode = response.code();
String responseMessage = response.message();
switch(responseCode) {
case 413:
throw new RateLimitException("Rate limit exceeded: " + responseCode);
case 401:
case 403:
throw new AuthorizationFailedException(responseCode, "Authorization failed!");
case 404:
throw new NotFoundException("Not found");
case 409:
MismatchedDevices mismatchedDevices = readResponseJson(response, MismatchedDevices.class);
throw new MismatchedDevicesException(mismatchedDevices);
case 410:
StaleDevices staleDevices = readResponseJson(response, StaleDevices.class);
throw new StaleDevicesException(staleDevices);
case 411:
DeviceLimit deviceLimit = readResponseJson(response, DeviceLimit.class);
throw new DeviceLimitExceededException(deviceLimit);
case 417:
throw new ExpectationFailedException();
case 423:
RegistrationLockFailure accountLockFailure = readResponseJson(response, RegistrationLockFailure.class);
AuthCredentials credentials = accountLockFailure.backupCredentials;
String basicStorageCredentials = credentials != null ? credentials.asBasic() : null;
throw new LockedException(accountLockFailure.length, accountLockFailure.timeRemaining, basicStorageCredentials);
case 428:
ProofRequiredResponse proofRequiredResponse = readResponseJson(response, ProofRequiredResponse.class);
String retryAfterRaw = response.header("Retry-After");
long retryAfter = Util.parseInt(retryAfterRaw, -1);
throw new ProofRequiredException(proofRequiredResponse, retryAfter);
case 499:
throw new DeprecatedVersionException();
case 508:
throw new ServerRejectedException();
}
if (responseCode != 200 && responseCode != 202 && responseCode != 204) {
throw new NonSuccessfulResponseCodeException(responseCode, "Bad response: " + responseCode + " " + responseMessage);
}
return response;
}
use of org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException in project Signal-Android by WhisperSystems.
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;
}
Aggregations