Search in sources :

Example 6 with SignalServiceAttachmentPointer

use of org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer in project Signal-Android by WhisperSystems.

the class AttachmentDownloadJob method retrieveAttachment.

private void retrieveAttachment(long messageId, final AttachmentId attachmentId, final Attachment attachment) throws IOException, RetryLaterException {
    AttachmentDatabase database = SignalDatabase.attachments();
    File attachmentFile = database.getOrCreateTransferFile(attachmentId);
    try {
        SignalServiceMessageReceiver messageReceiver = ApplicationDependencies.getSignalServiceMessageReceiver();
        SignalServiceAttachmentPointer pointer = createAttachmentPointer(attachment);
        InputStream stream = messageReceiver.retrieveAttachment(pointer, attachmentFile, MAX_ATTACHMENT_SIZE, (total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.NETWORK, total, progress)));
        database.insertAttachmentsForPlaceholder(messageId, attachmentId, stream);
    } catch (RangeException e) {
        Log.w(TAG, "Range exception, file size " + attachmentFile.length(), e);
        if (attachmentFile.delete()) {
            Log.i(TAG, "Deleted temp download file to recover");
            throw new RetryLaterException(e);
        } else {
            throw new IOException("Failed to delete temp download file following range exception");
        }
    } catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException | MissingConfigurationException e) {
        Log.w(TAG, "Experienced exception while trying to download an attachment.", e);
        markFailed(messageId, attachmentId);
    }
}
Also used : InvalidMessageException(org.whispersystems.libsignal.InvalidMessageException) InputStream(java.io.InputStream) SignalServiceAttachmentPointer(org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer) NonSuccessfulResponseCodeException(org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException) PartProgressEvent(org.thoughtcrime.securesms.events.PartProgressEvent) IOException(java.io.IOException) AttachmentDatabase(org.thoughtcrime.securesms.database.AttachmentDatabase) MmsException(org.thoughtcrime.securesms.mms.MmsException) MissingConfigurationException(org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException) SignalServiceMessageReceiver(org.whispersystems.signalservice.api.SignalServiceMessageReceiver) RangeException(org.whispersystems.signalservice.api.push.exceptions.RangeException) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) File(java.io.File)

Example 7 with SignalServiceAttachmentPointer

use of org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer in project Signal-Android by WhisperSystems.

the class AttachmentUploadJob method onRun.

@Override
public void onRun() throws Exception {
    if (!Recipient.self().isRegistered()) {
        throw new NotPushRegisteredException();
    }
    Data inputData = getInputData();
    ResumableUploadSpec resumableUploadSpec;
    if (forceV2) {
        Log.d(TAG, "Forcing utilization of V2");
        resumableUploadSpec = null;
    } else if (inputData != null && inputData.hasString(ResumableUploadSpecJob.KEY_RESUME_SPEC)) {
        Log.d(TAG, "Using attachments V3");
        resumableUploadSpec = ResumableUploadSpec.deserialize(inputData.getString(ResumableUploadSpecJob.KEY_RESUME_SPEC));
    } else {
        Log.d(TAG, "Using attachments V2");
        resumableUploadSpec = null;
    }
    SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
    AttachmentDatabase database = SignalDatabase.attachments();
    DatabaseAttachment databaseAttachment = database.getAttachment(attachmentId);
    if (databaseAttachment == null) {
        throw new InvalidAttachmentException("Cannot find the specified attachment.");
    }
    long timeSinceUpload = System.currentTimeMillis() - databaseAttachment.getUploadTimestamp();
    if (timeSinceUpload < UPLOAD_REUSE_THRESHOLD && !TextUtils.isEmpty(databaseAttachment.getLocation())) {
        Log.i(TAG, "We can re-use an already-uploaded file. It was uploaded " + timeSinceUpload + " ms ago. Skipping.");
        return;
    } else if (databaseAttachment.getUploadTimestamp() > 0) {
        Log.i(TAG, "This file was previously-uploaded, but too long ago to be re-used. Age: " + timeSinceUpload + " ms");
    }
    Log.i(TAG, "Uploading attachment for message " + databaseAttachment.getMmsId() + " with ID " + databaseAttachment.getAttachmentId());
    try (NotificationController notification = getNotificationForAttachment(databaseAttachment)) {
        SignalServiceAttachment localAttachment = getAttachmentFor(databaseAttachment, notification, resumableUploadSpec);
        SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment.asStream());
        Attachment attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get();
        database.updateAttachmentAfterUpload(databaseAttachment.getAttachmentId(), attachment, remoteAttachment.getUploadTimestamp());
    } catch (NonSuccessfulResumableUploadResponseCodeException e) {
        if (e.getCode() == 400) {
            Log.w(TAG, "Failed to upload due to a 400 when getting resumable upload information. Downgrading to attachments v2", e);
            forceV2 = true;
        }
    }
}
Also used : NotPushRegisteredException(org.thoughtcrime.securesms.net.NotPushRegisteredException) DatabaseAttachment(org.thoughtcrime.securesms.attachments.DatabaseAttachment) SignalServiceMessageSender(org.whispersystems.signalservice.api.SignalServiceMessageSender) SignalServiceAttachmentPointer(org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer) Data(org.thoughtcrime.securesms.jobmanager.Data) PointerAttachment(org.thoughtcrime.securesms.attachments.PointerAttachment) DatabaseAttachment(org.thoughtcrime.securesms.attachments.DatabaseAttachment) SignalServiceAttachment(org.whispersystems.signalservice.api.messages.SignalServiceAttachment) Attachment(org.thoughtcrime.securesms.attachments.Attachment) AttachmentDatabase(org.thoughtcrime.securesms.database.AttachmentDatabase) NonSuccessfulResumableUploadResponseCodeException(org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResumableUploadResponseCodeException) SignalServiceAttachment(org.whispersystems.signalservice.api.messages.SignalServiceAttachment) ResumableUploadSpec(org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec) NotificationController(org.thoughtcrime.securesms.service.NotificationController)

Example 8 with SignalServiceAttachmentPointer

use of org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer in project Signal-Android by WhisperSystems.

the class SignalServiceMessageSender method uploadAttachmentV2.

private SignalServiceAttachmentPointer uploadAttachmentV2(SignalServiceAttachmentStream attachment, byte[] attachmentKey, PushAttachmentData attachmentData) throws NonSuccessfulResponseCodeException, PushNetworkException, MalformedResponseException {
    AttachmentV2UploadAttributes v2UploadAttributes = null;
    Log.d(TAG, "Using pipe to retrieve attachment upload attributes...");
    try {
        v2UploadAttributes = new AttachmentService.AttachmentAttributesResponseProcessor<>(attachmentService.getAttachmentV2UploadAttributes().blockingGet()).getResultOrThrow();
    } catch (WebSocketUnavailableException e) {
        Log.w(TAG, "[uploadAttachmentV2] Pipe unavailable, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")");
    } catch (IOException e) {
        Log.w(TAG, "Failed to retrieve attachment upload attributes using pipe. Falling back...");
    }
    if (v2UploadAttributes == null) {
        Log.d(TAG, "Not using pipe to retrieve attachment upload attributes...");
        v2UploadAttributes = socket.getAttachmentV2UploadAttributes();
    }
    Pair<Long, byte[]> attachmentIdAndDigest = socket.uploadAttachment(attachmentData, v2UploadAttributes);
    return new SignalServiceAttachmentPointer(0, new SignalServiceAttachmentRemoteId(attachmentIdAndDigest.first()), attachment.getContentType(), attachmentKey, Optional.of(Util.toIntExact(attachment.getLength())), attachment.getPreview(), attachment.getWidth(), attachment.getHeight(), Optional.of(attachmentIdAndDigest.second()), attachment.getFileName(), attachment.getVoiceNote(), attachment.isBorderless(), attachment.isGif(), attachment.getCaption(), attachment.getBlurHash(), attachment.getUploadTimestamp());
}
Also used : AttachmentV2UploadAttributes(org.whispersystems.signalservice.internal.push.AttachmentV2UploadAttributes) SignalServiceAttachmentRemoteId(org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId) SignalServiceAttachmentPointer(org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer) WebSocketUnavailableException(org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException) IOException(java.io.IOException)

Example 9 with SignalServiceAttachmentPointer

use of org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer in project libsignal-service-java by signalapp.

the class SignalServiceMessageSender method uploadAttachment.

public SignalServiceAttachmentPointer uploadAttachment(SignalServiceAttachmentStream attachment) throws IOException {
    byte[] attachmentKey = Util.getSecretBytes(64);
    long paddedLength = PaddingInputStream.getPaddedSize(attachment.getLength());
    InputStream dataStream = new PaddingInputStream(attachment.getInputStream(), attachment.getLength());
    long ciphertextLength = AttachmentCipherOutputStream.getCiphertextLength(paddedLength);
    PushAttachmentData attachmentData = new PushAttachmentData(attachment.getContentType(), dataStream, ciphertextLength, new AttachmentCipherOutputStreamFactory(attachmentKey), attachment.getListener());
    AttachmentUploadAttributes uploadAttributes = null;
    if (pipe.get().isPresent()) {
        Log.d(TAG, "Using pipe to retrieve attachment upload attributes...");
        try {
            uploadAttributes = pipe.get().get().getAttachmentUploadAttributes();
        } catch (IOException e) {
            Log.w(TAG, "Failed to retrieve attachment upload attributes using pipe. Falling back...");
        }
    }
    if (uploadAttributes == null) {
        Log.d(TAG, "Not using pipe to retrieve attachment upload attributes...");
        uploadAttributes = socket.getAttachmentUploadAttributes();
    }
    Pair<Long, byte[]> attachmentIdAndDigest = socket.uploadAttachment(attachmentData, uploadAttributes);
    return new SignalServiceAttachmentPointer(attachmentIdAndDigest.first(), attachment.getContentType(), attachmentKey, Optional.of(Util.toIntExact(attachment.getLength())), attachment.getPreview(), attachment.getWidth(), attachment.getHeight(), Optional.of(attachmentIdAndDigest.second()), attachment.getFileName(), attachment.getVoiceNote(), attachment.getCaption(), attachment.getBlurHash());
}
Also used : PaddingInputStream(org.whispersystems.signalservice.internal.crypto.PaddingInputStream) InputStream(java.io.InputStream) PaddingInputStream(org.whispersystems.signalservice.internal.crypto.PaddingInputStream) SignalServiceAttachmentPointer(org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer) IOException(java.io.IOException) AttachmentUploadAttributes(org.whispersystems.signalservice.internal.push.AttachmentUploadAttributes) PushAttachmentData(org.whispersystems.signalservice.internal.push.PushAttachmentData) AttachmentCipherOutputStreamFactory(org.whispersystems.signalservice.internal.push.http.AttachmentCipherOutputStreamFactory)

Example 10 with SignalServiceAttachmentPointer

use of org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer in project Signal-Android by signalapp.

the class AttachmentUploadJob method onRun.

@Override
public void onRun() throws Exception {
    if (!Recipient.self().isRegistered()) {
        throw new NotPushRegisteredException();
    }
    Data inputData = getInputData();
    ResumableUploadSpec resumableUploadSpec;
    if (forceV2) {
        Log.d(TAG, "Forcing utilization of V2");
        resumableUploadSpec = null;
    } else if (inputData != null && inputData.hasString(ResumableUploadSpecJob.KEY_RESUME_SPEC)) {
        Log.d(TAG, "Using attachments V3");
        resumableUploadSpec = ResumableUploadSpec.deserialize(inputData.getString(ResumableUploadSpecJob.KEY_RESUME_SPEC));
    } else {
        Log.d(TAG, "Using attachments V2");
        resumableUploadSpec = null;
    }
    SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
    AttachmentDatabase database = SignalDatabase.attachments();
    DatabaseAttachment databaseAttachment = database.getAttachment(attachmentId);
    if (databaseAttachment == null) {
        throw new InvalidAttachmentException("Cannot find the specified attachment.");
    }
    long timeSinceUpload = System.currentTimeMillis() - databaseAttachment.getUploadTimestamp();
    if (timeSinceUpload < UPLOAD_REUSE_THRESHOLD && !TextUtils.isEmpty(databaseAttachment.getLocation())) {
        Log.i(TAG, "We can re-use an already-uploaded file. It was uploaded " + timeSinceUpload + " ms ago. Skipping.");
        return;
    } else if (databaseAttachment.getUploadTimestamp() > 0) {
        Log.i(TAG, "This file was previously-uploaded, but too long ago to be re-used. Age: " + timeSinceUpload + " ms");
    }
    Log.i(TAG, "Uploading attachment for message " + databaseAttachment.getMmsId() + " with ID " + databaseAttachment.getAttachmentId());
    try (NotificationController notification = getNotificationForAttachment(databaseAttachment)) {
        SignalServiceAttachment localAttachment = getAttachmentFor(databaseAttachment, notification, resumableUploadSpec);
        SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment.asStream());
        Attachment attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get();
        database.updateAttachmentAfterUpload(databaseAttachment.getAttachmentId(), attachment, remoteAttachment.getUploadTimestamp());
    } catch (NonSuccessfulResumableUploadResponseCodeException e) {
        if (e.getCode() == 400) {
            Log.w(TAG, "Failed to upload due to a 400 when getting resumable upload information. Downgrading to attachments v2", e);
            forceV2 = true;
        }
    }
}
Also used : NotPushRegisteredException(org.thoughtcrime.securesms.net.NotPushRegisteredException) DatabaseAttachment(org.thoughtcrime.securesms.attachments.DatabaseAttachment) SignalServiceMessageSender(org.whispersystems.signalservice.api.SignalServiceMessageSender) SignalServiceAttachmentPointer(org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer) Data(org.thoughtcrime.securesms.jobmanager.Data) PointerAttachment(org.thoughtcrime.securesms.attachments.PointerAttachment) DatabaseAttachment(org.thoughtcrime.securesms.attachments.DatabaseAttachment) SignalServiceAttachment(org.whispersystems.signalservice.api.messages.SignalServiceAttachment) Attachment(org.thoughtcrime.securesms.attachments.Attachment) AttachmentDatabase(org.thoughtcrime.securesms.database.AttachmentDatabase) NonSuccessfulResumableUploadResponseCodeException(org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResumableUploadResponseCodeException) SignalServiceAttachment(org.whispersystems.signalservice.api.messages.SignalServiceAttachment) ResumableUploadSpec(org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec) NotificationController(org.thoughtcrime.securesms.service.NotificationController)

Aggregations

SignalServiceAttachmentPointer (org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer)22 IOException (java.io.IOException)10 InvalidMessageException (org.whispersystems.libsignal.InvalidMessageException)9 File (java.io.File)8 InputStream (java.io.InputStream)8 SignalServiceAttachmentRemoteId (org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId)8 NonSuccessfulResponseCodeException (org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException)7 AttachmentDatabase (org.thoughtcrime.securesms.database.AttachmentDatabase)5 Bitmap (android.graphics.Bitmap)4 GroupDatabase (org.thoughtcrime.securesms.database.GroupDatabase)4 SignalServiceMessageReceiver (org.whispersystems.signalservice.api.SignalServiceMessageReceiver)4 MissingConfigurationException (org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException)4 GroupRecord (org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord)3 PartProgressEvent (org.thoughtcrime.securesms.events.PartProgressEvent)3 Nullable (androidx.annotation.Nullable)2 Attachment (org.thoughtcrime.securesms.attachments.Attachment)2 DatabaseAttachment (org.thoughtcrime.securesms.attachments.DatabaseAttachment)2 PointerAttachment (org.thoughtcrime.securesms.attachments.PointerAttachment)2 Data (org.thoughtcrime.securesms.jobmanager.Data)2 NetworkConstraint (org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint)2