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 1f2c0f108..c6c70ba2c 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java @@ -518,7 +518,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/ui/messageview/MessageContainerView.java b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageContainerView.java index 06a79bcb4..d1fe458bc 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 @@ -26,6 +26,7 @@ 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.Toast; @@ -34,10 +35,12 @@ import com.fsck.k9.R; import com.fsck.k9.helper.ClipboardManager; import com.fsck.k9.helper.Contacts; import com.fsck.k9.mail.Address; +import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mailstore.AttachmentViewInfo; import com.fsck.k9.mailstore.MessageViewInfo.MessageViewContainer; +import com.fsck.k9.view.K9WebViewClient; import com.fsck.k9.view.MessageHeader.OnLayoutChangedListener; import com.fsck.k9.view.MessageWebView; import org.openintents.openpgp.OpenPgpError; @@ -421,10 +424,13 @@ public class MessageContainerView extends LinearLayout implements OnClickListene } } - public void setMessage(MessageViewContainer messageViewContainer) + public void setMessageViewContainer(Message message, MessageViewContainer messageViewContainer) throws MessagingException { resetView(); + WebViewClient webViewClient = K9WebViewClient.newInstance(message); + 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) { diff --git a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageTopView.java b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageTopView.java index 9b9829494..9a3e49941 100644 --- a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageTopView.java +++ b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageTopView.java @@ -1,6 +1,5 @@ package com.fsck.k9.ui.messageview; -import android.app.Activity; import android.app.Fragment; import android.content.Context; import android.util.AttributeSet; @@ -14,7 +13,6 @@ import android.widget.LinearLayout; import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.R; -import com.fsck.k9.crypto.PgpData; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; @@ -67,11 +65,13 @@ public class MessageTopView extends LinearLayout { throws MessagingException { resetView(); + Message message = messageViewInfo.message; for (MessageViewContainer container : messageViewInfo.containers) { 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(message, 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..7f5dbc206 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/view/K9WebViewClient.java @@ -0,0 +1,126 @@ +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.Message; +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(Message message) { + if (Build.VERSION.SDK_INT < 21) { + return new PreLollipopWebViewClient(message); + } + + return new LollipopWebViewClient(message); + } + + + private final Message message; + + private K9WebViewClient(Message message) { + this.message = message; + } + + 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(message); + + 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(Message message) { + super(message); + } + + @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(Message message) { + super(message); + } + + @Override + public WebResourceResponse shouldInterceptRequest(WebView webView, WebResourceRequest request) { + return shouldInterceptRequest(webView, request.getUrl()); + } + } +}