diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/Part.java b/k9mail-library/src/main/java/com/fsck/k9/mail/Part.java index 8531960ec..d78132f21 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/Part.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/Part.java @@ -20,7 +20,7 @@ public interface Part { String getDisposition() throws MessagingException; - String getContentId() throws MessagingException; + String getContentId(); String[] getHeader(String name) throws MessagingException; diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeBodyPart.java b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeBodyPart.java index 3c0103541..2a099db15 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeBodyPart.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeBodyPart.java @@ -96,7 +96,7 @@ public class MimeBodyPart extends BodyPart { } @Override - public String getContentId() throws MessagingException { + public String getContentId() { String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID); if (contentId == null) { return null; diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeMessage.java b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeMessage.java index 91ab8c812..7b0af70bb 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeMessage.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeMessage.java @@ -174,7 +174,7 @@ public class MimeMessage extends Message { } @Override - public String getContentId() throws MessagingException { + public String getContentId() { return null; } diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java index f79fdd004..4b9021a1d 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java @@ -448,18 +448,20 @@ public class LocalMessageExtractor { attachments); List attachmentInfos = extractAttachmentInfos(context, attachments); - if (pgpAnnotation != NO_ANNOTATIONS) { + MessageViewContainer messageViewContainer; + if (pgpAnnotation == NO_ANNOTATIONS) { + messageViewContainer = new MessageViewContainer(viewable.html, part, attachmentInfos); + } else { OpenPgpSignatureResult pgpResult = pgpAnnotation.getSignatureResult(); OpenPgpError pgpError = pgpAnnotation.getError(); boolean wasEncrypted = pgpAnnotation.wasEncrypted(); PendingIntent pendingIntent = pgpAnnotation.getPendingIntent(); - containers.add(new MessageViewContainer( - viewable.html, attachmentInfos, pgpResult, pgpError, wasEncrypted, pendingIntent)); - } else { - containers.add(new MessageViewContainer(viewable.html, attachmentInfos)); + messageViewContainer = new MessageViewContainer(viewable.html, part, attachmentInfos, pgpResult, + pgpError, wasEncrypted, pendingIntent); } + containers.add(messageViewContainer); } return new MessageViewInfo(containers, message); @@ -517,7 +519,7 @@ public class LocalMessageExtractor { return attachments; } - private static AttachmentViewInfo extractAttachmentInfo(Context context, Part part) throws MessagingException { + public static AttachmentViewInfo extractAttachmentInfo(Context context, Part part) throws MessagingException { if (part instanceof LocalPart) { LocalPart localPart = (LocalPart) part; String accountUuid = localPart.getAccountUuid(); diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java index 522119b03..857f42e2c 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java @@ -1,62 +1,50 @@ package com.fsck.k9.mailstore; -import android.app.PendingIntent; - -import java.util.ArrayList; import java.util.List; +import android.app.PendingIntent; + import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.Part; import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpSignatureResult; public class MessageViewInfo { - public final Message message; public final List containers; - @Deprecated - public MessageViewInfo(String text, List attachments, Message message) { - containers = new ArrayList(); - containers.add(new MessageViewContainer(text, attachments)); - this.message = message; - } public MessageViewInfo(List containers, Message message) { this.containers = containers; this.message = message; } + public static class MessageViewContainer { + public final String text; + public final Part rootPart; + public final List attachments; + public final boolean encrypted; + public final OpenPgpSignatureResult signatureResult; + public final OpenPgpError pgpError; + public final PendingIntent pgpPendingIntent; - final public String text; - final public List attachments; - final public boolean encrypted; - final public OpenPgpSignatureResult signatureResult; - final public OpenPgpError pgpError; - final public PendingIntent pgpPendingIntent; - - MessageViewContainer(String text, List attachments) { - this.text = text; - this.attachments = attachments; - this.signatureResult = null; - this.pgpError = null; - this.encrypted = false; - this.pgpPendingIntent = null; + MessageViewContainer(String text, Part rootPart, List attachments) { + this(text, rootPart, attachments, null, null, false, null); } - MessageViewContainer(String text, List attachments, - OpenPgpSignatureResult signatureResult, OpenPgpError pgpError, - boolean encrypted, PendingIntent pgpPendingIntent) { + MessageViewContainer(String text, Part rootPart, List attachments, + OpenPgpSignatureResult signatureResult, OpenPgpError pgpError, boolean encrypted, + PendingIntent pgpPendingIntent) { this.text = text; + this.rootPart = rootPart; this.attachments = attachments; this.signatureResult = signatureResult; this.pgpError = pgpError; this.encrypted = encrypted; this.pgpPendingIntent = pgpPendingIntent; } - } - } diff --git a/k9mail/src/main/java/com/fsck/k9/ui/messageview/DownloadImageTask.java b/k9mail/src/main/java/com/fsck/k9/ui/messageview/DownloadImageTask.java new file mode 100644 index 000000000..827bf33d3 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/ui/messageview/DownloadImageTask.java @@ -0,0 +1,193 @@ +package com.fsck.k9.ui.messageview; + + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; +import android.util.Log; +import android.widget.Toast; + +import com.fsck.k9.K9; +import com.fsck.k9.R; +import com.fsck.k9.helper.FileHelper; +import com.fsck.k9.helper.UrlEncodingHelper; +import com.fsck.k9.mail.internet.MimeUtility; +import com.fsck.k9.provider.AttachmentProvider.AttachmentProviderColumns; +import org.apache.commons.io.IOUtils; + + +@Deprecated +class DownloadImageTask extends AsyncTask { + private static final String[] ATTACHMENT_PROJECTION = new String[] { + AttachmentProviderColumns._ID, + AttachmentProviderColumns.DISPLAY_NAME + }; + private static final int DISPLAY_NAME_INDEX = 1; + + private static final String DEFAULT_FILE_NAME = "saved_image"; + + + private final Context context; + + public DownloadImageTask(Context context) { + this.context = context.getApplicationContext(); + } + + @Override + protected String doInBackground(String... params) { + String url = params[0]; + try { + boolean isExternalImage = url.startsWith("http"); + + String fileName; + if (isExternalImage) { + fileName = downloadAndStoreImage(url); + } else { + fileName = fetchAndStoreImage(url); + } + + return fileName; + } catch (Exception e) { + Log.e(K9.LOG_TAG, "Error while downloading image", e); + return null; + } + } + + @Override + protected void onPostExecute(String fileName) { + boolean errorSavingFile = (fileName == null); + + String text; + if (errorSavingFile) { + text = context.getString(R.string.image_saving_failed); + } else { + text = context.getString(R.string.image_saved_as, fileName); + } + + Toast.makeText(context, text, Toast.LENGTH_LONG).show(); + } + + private String downloadAndStoreImage(String urlString) throws IOException { + URL url = new URL(urlString); + URLConnection conn = url.openConnection(); + + InputStream in = conn.getInputStream(); + try { + String fileName = getFileNameFromUrl(url); + String mimeType = getMimeType(conn, fileName); + + String fileNameWithExtension = getFileNameWithExtension(fileName, mimeType); + return writeFileToStorage(fileNameWithExtension, in); + } finally { + in.close(); + } + } + + private String getFileNameFromUrl(URL url) { + String fileName; + + String path = url.getPath(); + int start = path.lastIndexOf("/"); + if (start != -1 && start + 1 < path.length()) { + fileName = UrlEncodingHelper.decodeUtf8(path.substring(start + 1)); + } else { + fileName = DEFAULT_FILE_NAME; + } + + return fileName; + } + + private String getMimeType(URLConnection conn, String fileName) { + String mimeType = null; + if (fileName.indexOf('.') == -1) { + mimeType = conn.getContentType(); + } + + return mimeType; + } + + private String fetchAndStoreImage(String urlString) throws IOException { + ContentResolver contentResolver = context.getContentResolver(); + Uri uri = Uri.parse(urlString); + + String fileName = getFileNameFromContentProvider(contentResolver, uri); + String mimeType = getMimeType(contentResolver, uri, fileName); + + InputStream in = contentResolver.openInputStream(uri); + try { + String fileNameWithExtension = getFileNameWithExtension(fileName, mimeType); + return writeFileToStorage(fileNameWithExtension, in); + } finally { + in.close(); + } + } + + private String getMimeType(ContentResolver contentResolver, Uri uri, String fileName) { + String mimeType = null; + if (fileName.indexOf('.') == -1) { + mimeType = contentResolver.getType(uri); + } + + return mimeType; + } + + private String getFileNameFromContentProvider(ContentResolver contentResolver, Uri uri) { + String displayName = DEFAULT_FILE_NAME; + + Cursor cursor = contentResolver.query(uri, ATTACHMENT_PROJECTION, null, null, null); + if (cursor != null) { + try { + if (cursor.moveToNext() && !cursor.isNull(DISPLAY_NAME_INDEX)) { + displayName = cursor.getString(DISPLAY_NAME_INDEX); + } + } finally { + cursor.close(); + } + } + + return displayName; + } + + private String getFileNameWithExtension(String fileName, String mimeType) { + if (fileName.indexOf('.') != -1) { + return fileName; + } + + // Use JPEG as fallback + String extension = "jpeg"; + if (mimeType != null) { + String extensionFromMimeType = MimeUtility.getExtensionByMimeType(mimeType); + if (extensionFromMimeType != null) { + extension = extensionFromMimeType; + } + } + + return fileName + "." + extension; + } + + private String writeFileToStorage(String fileName, InputStream in) throws IOException { + String sanitized = FileHelper.sanitizeFilename(fileName); + + File directory = new File(K9.getAttachmentDefaultPath()); + File file = FileHelper.createUniqueFile(directory, sanitized); + + FileOutputStream out = new FileOutputStream(file); + try { + IOUtils.copy(in, out); + out.flush(); + } finally { + out.close(); + } + + return file.getName(); + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageContainerView.java b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageContainerView.java index 1a38ed98b..a038366e1 100644 --- a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageContainerView.java +++ b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageContainerView.java @@ -1,23 +1,14 @@ package com.fsck.k9.ui.messageview; -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; import java.util.HashMap; import java.util.Map; import android.app.Activity; import android.app.Fragment; import android.content.ActivityNotFoundException; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.database.Cursor; -import android.graphics.Color; import android.net.Uri; -import android.os.AsyncTask; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -35,27 +26,22 @@ import android.view.View.OnCreateContextMenuListener; import android.view.ViewStub; import android.webkit.WebView; import android.webkit.WebView.HitTestResult; +import android.webkit.WebViewClient; import android.widget.Button; import android.widget.LinearLayout; -import android.widget.RelativeLayout; import android.widget.Toast; -import com.fsck.k9.K9; import com.fsck.k9.R; import com.fsck.k9.helper.ClipboardManager; import com.fsck.k9.helper.Contacts; -import com.fsck.k9.helper.FileHelper; -import com.fsck.k9.helper.UrlEncodingHelper; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mailstore.AttachmentViewInfo; import com.fsck.k9.mailstore.MessageViewInfo.MessageViewContainer; -import com.fsck.k9.provider.AttachmentProvider.AttachmentProviderColumns; +import com.fsck.k9.view.K9WebViewClient; import com.fsck.k9.view.MessageHeader.OnLayoutChangedListener; import com.fsck.k9.view.MessageWebView; -import org.apache.commons.io.IOUtils; import org.openintents.openpgp.OpenPgpError; @@ -77,12 +63,6 @@ public class MessageContainerView extends LinearLayout implements OnClickListene private static final int MENU_ITEM_EMAIL_SAVE = Menu.FIRST + 1; private static final int MENU_ITEM_EMAIL_COPY = Menu.FIRST + 2; - private static final String[] ATTACHMENT_PROJECTION = new String[] { - AttachmentProviderColumns._ID, - AttachmentProviderColumns.DISPLAY_NAME - }; - private static final int DISPLAY_NAME_INDEX = 1; - private ViewStub mOpenPgpHeaderStub; private View mSidebar; private MessageWebView mMessageContentView; @@ -242,7 +222,8 @@ public class MessageContainerView extends LinearLayout implements OnClickListene break; } case MENU_ITEM_IMAGE_SAVE: { - new DownloadImageTask().execute(url); + //TODO: Use download manager + new DownloadImageTask(getContext()).execute(url); break; } case MENU_ITEM_IMAGE_COPY: { @@ -442,10 +423,12 @@ public class MessageContainerView extends LinearLayout implements OnClickListene } } - public void setMessage(MessageViewContainer messageViewContainer) - throws MessagingException { + public void setMessageViewContainer(MessageViewContainer messageViewContainer) throws MessagingException { resetView(); + WebViewClient webViewClient = K9WebViewClient.newInstance(messageViewContainer.rootPart); + mMessageContentView.setWebViewClient(webViewClient); + // Save the text so we can reset the WebView when the user clicks the "Show pictures" button OpenPgpError error = messageViewContainer.pgpError; if (error != null) { @@ -661,117 +644,4 @@ public class MessageContainerView extends LinearLayout implements OnClickListene out.writeInt((this.showPictures) ? 1 : 0); } } - - class DownloadImageTask extends AsyncTask { - @Override - protected String doInBackground(String... params) { - String urlString = params[0]; - try { - boolean externalImage = urlString.startsWith("http"); - - String filename = null; - String mimeType = null; - InputStream in = null; - - try { - if (externalImage) { - URL url = new URL(urlString); - URLConnection conn = url.openConnection(); - in = conn.getInputStream(); - - String path = url.getPath(); - - // Try to get the filename from the URL - int start = path.lastIndexOf("/"); - if (start != -1 && start + 1 < path.length()) { - filename = UrlEncodingHelper.decodeUtf8(path.substring(start + 1)); - } else { - // Use a dummy filename if necessary - filename = "saved_image"; - } - - // Get the MIME type if we couldn't find a file extension - if (filename.indexOf('.') == -1) { - mimeType = conn.getContentType(); - } - } else { - ContentResolver contentResolver = getContext().getContentResolver(); - Uri uri = Uri.parse(urlString); - - // Get the filename from AttachmentProvider - Cursor cursor = contentResolver.query(uri, ATTACHMENT_PROJECTION, null, null, null); - if (cursor != null) { - try { - if (cursor.moveToNext()) { - filename = cursor.getString(DISPLAY_NAME_INDEX); - } - } finally { - cursor.close(); - } - } - - // Use a dummy filename if necessary - if (filename == null) { - filename = "saved_image"; - } - - // Get the MIME type if we couldn't find a file extension - if (filename.indexOf('.') == -1) { - mimeType = contentResolver.getType(uri); - } - - in = contentResolver.openInputStream(uri); - } - - // Do we still need an extension? - if (filename.indexOf('.') == -1) { - // Use JPEG as fallback - String extension = "jpeg"; - if (mimeType != null) { - // Try to find an extension for the given MIME type - String ext = MimeUtility.getExtensionByMimeType(mimeType); - if (ext != null) { - extension = ext; - } - } - filename += "." + extension; - } - - String sanitized = FileHelper.sanitizeFilename(filename); - - File directory = new File(K9.getAttachmentDefaultPath()); - File file = FileHelper.createUniqueFile(directory, sanitized); - FileOutputStream out = new FileOutputStream(file); - try { - IOUtils.copy(in, out); - out.flush(); - } finally { - out.close(); - } - - return file.getName(); - - } finally { - if (in != null) { - in.close(); - } - } - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - @Override - protected void onPostExecute(String filename) { - String text; - if (filename == null) { - text = getContext().getString(R.string.image_saving_failed); - } else { - text = getContext().getString(R.string.image_saved_as, filename); - } - - Toast.makeText(getContext(), text, Toast.LENGTH_LONG).show(); - } - } } diff --git a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageCryptoHelper.java b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageCryptoHelper.java index d976b8ee2..50f50a5ac 100644 --- a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageCryptoHelper.java +++ b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageCryptoHelper.java @@ -17,6 +17,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentSender.SendIntentException; import android.os.AsyncTask; +import android.text.TextUtils; import android.util.Log; import com.fsck.k9.Account; @@ -55,7 +56,7 @@ public class MessageCryptoHelper { private Deque partsToDecryptOrVerify; private OpenPgpApi openPgpApi; - private Part currentlyDecrypringOrVerifyingPart; + private Part currentlyDecryptingOrVerifyingPart; private Intent currentCryptoResult; private MessageCryptoAnnotations messageAnnotations; @@ -73,6 +74,11 @@ public class MessageCryptoHelper { public void decryptOrVerifyMessagePartsIfNecessary(LocalMessage message) { this.message = message; + if (!isCryptoProviderConfigured()) { + returnResultToFragment(); + return; + } + List encryptedParts = MessageDecryptVerifier.findEncryptedParts(message); List signedParts = MessageDecryptVerifier.findSignedParts(message); List inlineParts = MessageDecryptVerifier.findPgpInlineParts(message); @@ -87,6 +93,10 @@ public class MessageCryptoHelper { } } + private boolean isCryptoProviderConfigured() { + return !TextUtils.isEmpty(account.getCryptoApp()); + } + private void decryptOrVerifyNextPartOrStartExtractingTextAndAttachments() { if (!partsToDecryptOrVerify.isEmpty()) { @@ -143,7 +153,7 @@ public class MessageCryptoHelper { } private void decryptOrVerifyPart(Part part) { - currentlyDecrypringOrVerifyingPart = part; + currentlyDecryptingOrVerifyingPart = part; decryptVerify(new Intent()); } @@ -155,9 +165,9 @@ public class MessageCryptoHelper { intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accountName); try { - if (MessageDecryptVerifier.isPgpMimeSignedPart(currentlyDecrypringOrVerifyingPart)) { + if (MessageDecryptVerifier.isPgpMimeSignedPart(currentlyDecryptingOrVerifyingPart)) { callAsyncDetachedVerify(intent); - } else if (MessageDecryptVerifier.isPgpInlinePart(currentlyDecrypringOrVerifyingPart)) { + } else if (MessageDecryptVerifier.isPgpInlinePart(currentlyDecryptingOrVerifyingPart)) { callAsyncInlineOperation(intent); } else { callAsyncDecrypt(intent); @@ -208,7 +218,7 @@ public class MessageCryptoHelper { private void callAsyncDetachedVerify(Intent intent) throws IOException, MessagingException { PipedInputStream pipedInputStream = getPipedInputStreamForSignedData(); - byte[] signatureData = MessageDecryptVerifier.getSignatureData(currentlyDecrypringOrVerifyingPart); + byte[] signatureData = MessageDecryptVerifier.getSignatureData(currentlyDecryptingOrVerifyingPart); intent.putExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE, signatureData); openPgpApi.executeApiAsync(intent, pipedInputStream, null, new IOpenPgpCallback() { @@ -228,7 +238,7 @@ public class MessageCryptoHelper { @Override public void run() { try { - Multipart multipartSignedMultipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody(); + Multipart multipartSignedMultipart = (Multipart) currentlyDecryptingOrVerifyingPart.getBody(); BodyPart signatureBodyPart = multipartSignedMultipart.getBodyPart(0); Log.d(K9.LOG_TAG, "signed data type: " + signatureBodyPart.getMimeType()); signatureBodyPart.writeTo(out); @@ -255,14 +265,14 @@ public class MessageCryptoHelper { @Override public void run() { try { - if (MessageDecryptVerifier.isPgpMimePart(currentlyDecrypringOrVerifyingPart)) { + if (MessageDecryptVerifier.isPgpMimePart(currentlyDecryptingOrVerifyingPart)) { Multipart multipartEncryptedMultipart = - (Multipart) currentlyDecrypringOrVerifyingPart.getBody(); + (Multipart) currentlyDecryptingOrVerifyingPart.getBody(); BodyPart encryptionPayloadPart = multipartEncryptedMultipart.getBodyPart(1); Body encryptionPayloadBody = encryptionPayloadPart.getBody(); encryptionPayloadBody.writeTo(out); - } else if (MessageDecryptVerifier.isPgpInlinePart(currentlyDecrypringOrVerifyingPart)) { - String text = MessageExtractor.getTextFromPart(currentlyDecrypringOrVerifyingPart); + } else if (MessageDecryptVerifier.isPgpInlinePart(currentlyDecryptingOrVerifyingPart)) { + String text = MessageExtractor.getTextFromPart(currentlyDecryptingOrVerifyingPart); out.write(text.getBytes()); } else { Log.wtf(K9.LOG_TAG, "No suitable data to stream found!"); @@ -392,7 +402,7 @@ public class MessageCryptoHelper { } private void addOpenPgpResultPartToMessage(OpenPgpResultAnnotation resultAnnotation) { - messageAnnotations.put(currentlyDecrypringOrVerifyingPart, resultAnnotation); + messageAnnotations.put(currentlyDecryptingOrVerifyingPart, resultAnnotation); } private void onCryptoFailed(OpenPgpError error) { diff --git a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageView.java b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageView.java index ead2a2846..fda04a946 100644 --- a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageView.java +++ b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageView.java @@ -70,7 +70,8 @@ public class MessageView extends LinearLayout { MessageContainerView view = (MessageContainerView) mInflater.inflate(R.layout.message_container, null); view.initialize(fragment, attachmentCallback, openPgpHeaderViewCallback, !Account.NO_OPENPGP_PROVIDER.equals(account.getOpenPgpProvider())); - view.setMessage(container); + view.setMessageViewContainer(container); + containerViews.addView(view); } diff --git a/k9mail/src/main/java/com/fsck/k9/view/K9WebViewClient.java b/k9mail/src/main/java/com/fsck/k9/view/K9WebViewClient.java new file mode 100644 index 000000000..a52c64f5d --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/view/K9WebViewClient.java @@ -0,0 +1,125 @@ +package com.fsck.k9.view; + + +import java.io.InputStream; +import java.util.Stack; + +import android.annotation.TargetApi; +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.os.Build.VERSION_CODES; +import android.text.TextUtils; +import android.util.Log; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import com.fsck.k9.K9; +import com.fsck.k9.mail.Body; +import com.fsck.k9.mail.Multipart; +import com.fsck.k9.mail.Part; +import com.fsck.k9.mailstore.AttachmentViewInfo; +import com.fsck.k9.mailstore.LocalMessageExtractor; + + +/** + * {@link WebViewClient} that intercepts requests for {@code cid:} URIs to load the respective body part. + */ +public abstract class K9WebViewClient extends WebViewClient { + private static final String CID_SCHEME = "cid"; + private static final WebResourceResponse RESULT_DO_NOT_INTERCEPT = null; + private static final WebResourceResponse RESULT_DUMMY_RESPONSE = new WebResourceResponse(null, null, null); + + public static WebViewClient newInstance(Part part) { + if (Build.VERSION.SDK_INT < 21) { + return new PreLollipopWebViewClient(part); + } + + return new LollipopWebViewClient(part); + } + + + private final Part part; + + private K9WebViewClient(Part part) { + this.part = part; + } + + protected WebResourceResponse shouldInterceptRequest(WebView webView, Uri uri) { + if (!CID_SCHEME.equals(uri.getScheme())) { + return RESULT_DO_NOT_INTERCEPT; + } + + String cid = uri.getSchemeSpecificPart(); + if (TextUtils.isEmpty(cid)) { + return RESULT_DUMMY_RESPONSE; + } + + Part part = getPartForContentId(cid); + if (part == null) { + return RESULT_DUMMY_RESPONSE; + } + + Context context = webView.getContext(); + ContentResolver contentResolver = context.getContentResolver(); + try { + AttachmentViewInfo attachmentInfo = LocalMessageExtractor.extractAttachmentInfo(context, part); + String mimeType = attachmentInfo.mimeType; + InputStream inputStream = contentResolver.openInputStream(attachmentInfo.uri); + + return new WebResourceResponse(mimeType, null, inputStream); + } catch (Exception e) { + Log.e(K9.LOG_TAG, "Error while intercepting URI: " + uri, e); + return RESULT_DUMMY_RESPONSE; + } + } + + private Part getPartForContentId(String cid) { + Stack partsToCheck = new Stack(); + partsToCheck.push(part); + + while (!partsToCheck.isEmpty()) { + Part part = partsToCheck.pop(); + + Body body = part.getBody(); + if (body instanceof Multipart) { + Multipart multipart = (Multipart) body; + for (Part bodyPart : multipart.getBodyParts()) { + partsToCheck.push(bodyPart); + } + } else if (cid.equals(part.getContentId())) { + return part; + } + } + + return null; + } + + + private static class PreLollipopWebViewClient extends K9WebViewClient { + protected PreLollipopWebViewClient(Part part) { + super(part); + } + + @SuppressWarnings("deprecation") + @Override + public WebResourceResponse shouldInterceptRequest(WebView webView, String url) { + return shouldInterceptRequest(webView, Uri.parse(url)); + } + } + + @TargetApi(VERSION_CODES.LOLLIPOP) + private static class LollipopWebViewClient extends K9WebViewClient { + protected LollipopWebViewClient(Part part) { + super(part); + } + + @Override + public WebResourceResponse shouldInterceptRequest(WebView webView, WebResourceRequest request) { + return shouldInterceptRequest(webView, request.getUrl()); + } + } +}