package com.fsck.k9.message.extractors; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.net.Uri; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import timber.log.Timber; import android.support.annotation.WorkerThread; import com.fsck.k9.Globals; import com.fsck.k9.K9; import com.fsck.k9.mail.Body; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mailstore.AttachmentViewInfo; import com.fsck.k9.mailstore.DeferredFileBody; import com.fsck.k9.mailstore.LocalMessage; import com.fsck.k9.mailstore.LocalPart; import com.fsck.k9.provider.AttachmentProvider; import com.fsck.k9.provider.DecryptedFileProvider; public class AttachmentInfoExtractor { private final Context context; public static AttachmentInfoExtractor getInstance() { Context context = Globals.getContext(); return new AttachmentInfoExtractor(context); } @VisibleForTesting AttachmentInfoExtractor(Context context) { this.context = context; } @WorkerThread public List<AttachmentViewInfo> extractAttachmentInfoForView(List<Part> attachmentParts) throws MessagingException { List<AttachmentViewInfo> attachments = new ArrayList<>(); for (Part part : attachmentParts) { AttachmentViewInfo attachmentViewInfo = extractAttachmentInfo(part); attachments.add(attachmentViewInfo); } return attachments; } @WorkerThread public AttachmentViewInfo extractAttachmentInfo(Part part) throws MessagingException { Uri uri; long size; boolean isContentAvailable; if (part instanceof LocalPart) { LocalPart localPart = (LocalPart) part; String accountUuid = localPart.getAccountUuid(); long messagePartId = localPart.getId(); size = localPart.getSize(); isContentAvailable = part.getBody() != null; uri = AttachmentProvider.getAttachmentUri(accountUuid, messagePartId); } else if (part instanceof LocalMessage) { LocalMessage localMessage = (LocalMessage) part; String accountUuid = localMessage.getAccount().getUuid(); long messagePartId = localMessage.getMessagePartId(); size = localMessage.getSize(); isContentAvailable = part.getBody() != null; uri = AttachmentProvider.getAttachmentUri(accountUuid, messagePartId); } else { Body body = part.getBody(); if (body instanceof DeferredFileBody) { DeferredFileBody decryptedTempFileBody = (DeferredFileBody) body; size = decryptedTempFileBody.getSize(); uri = getDecryptedFileProviderUri(decryptedTempFileBody, part.getMimeType()); isContentAvailable = true; } else { throw new IllegalArgumentException("Unsupported part type provided"); } } return extractAttachmentInfo(part, uri, size, isContentAvailable); } @Nullable @VisibleForTesting protected Uri getDecryptedFileProviderUri(DeferredFileBody decryptedTempFileBody, String mimeType) { Uri uri; try { File file = decryptedTempFileBody.getFile(); uri = DecryptedFileProvider.getUriForProvidedFile( context, file, decryptedTempFileBody.getEncoding(), mimeType); } catch (IOException e) { Timber.e(e, "Decrypted temp file (no longer?) exists!"); uri = null; } return uri; } public AttachmentViewInfo extractAttachmentInfoForDatabase(Part part) throws MessagingException { boolean isContentAvailable = part.getBody() != null; return extractAttachmentInfo(part, Uri.EMPTY, AttachmentViewInfo.UNKNOWN_SIZE, isContentAvailable); } @WorkerThread private AttachmentViewInfo extractAttachmentInfo(Part part, Uri uri, long size, boolean isContentAvailable) throws MessagingException { boolean inlineAttachment = false; String mimeType = part.getMimeType(); String contentTypeHeader = MimeUtility.unfoldAndDecode(part.getContentType()); String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition()); String name = MimeUtility.getHeaderParameter(contentDisposition, "filename"); if (name == null) { name = MimeUtility.getHeaderParameter(contentTypeHeader, "name"); } if (name == null) { String extension = null; if (mimeType != null) { extension = MimeUtility.getExtensionByMimeType(mimeType); } name = "noname" + ((extension != null) ? "." + extension : ""); } // Inline parts with a content-id are almost certainly components of an HTML message // not attachments. Only show them if the user pressed the button to show more // attachments. if (contentDisposition != null && MimeUtility.getHeaderParameter(contentDisposition, null).matches("^(?i:inline)") && part.getHeader(MimeHeader.HEADER_CONTENT_ID).length > 0) { inlineAttachment = true; } long attachmentSize = extractAttachmentSize(contentDisposition, size); return new AttachmentViewInfo(mimeType, name, attachmentSize, uri, inlineAttachment, part, isContentAvailable); } @WorkerThread private long extractAttachmentSize(String contentDisposition, long size) { if (size != AttachmentViewInfo.UNKNOWN_SIZE) { return size; } long result = AttachmentViewInfo.UNKNOWN_SIZE; String sizeParam = MimeUtility.getHeaderParameter(contentDisposition, "size"); if (sizeParam != null) { try { result = Integer.parseInt(sizeParam); } catch (NumberFormatException e) { /* ignore */ } } return result; } }