Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7826febe authored by Vincent Breitmoser's avatar Vincent Breitmoser Committed by GitHub
Browse files

Merge pull request #1511 from k9mail/sanitize-in-extract

Sanitize HTML in MessageViewExtractor instead of MessageWebView
parents 8df6a56a 526448d1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ package com.fsck.k9;


import android.content.Context;
import android.support.annotation.VisibleForTesting;


public class Globals {
+26 −1
Original line number Diff line number Diff line
@@ -1323,6 +1323,31 @@ public class HtmlConverter {
        return "</pre>";
    }

    public static String wrapStatusMessage(CharSequence status) {
        return wrapMessageContent("<div style=\"text-align:center; color: grey;\">" + status + "</div>");
    }

    public static String wrapMessageContent(CharSequence messageContent) {
        // Include a meta tag so the WebView will not use a fixed viewport width of 980 px
        return "<html><head><meta name=\"viewport\" content=\"width=device-width\"/>" +
                HtmlConverter.cssStyleTheme() +
                HtmlConverter.cssStylePre() +
                "</head><body>" +
                messageContent +
                "</body></html>";
    }

    private static String cssStyleTheme() {
        if (K9.getK9MessageViewTheme() == K9.Theme.DARK)  {
            return "<style type=\"text/css\">" +
                    "* { background: black ! important; color: #F3F3F3 !important }" +
                    ":link, :link * { color: #CCFF33 !important }" +
                    ":visited, :visited * { color: #551A8B !important }</style> ";
        } else {
            return "";
        }
    }

    /**
     * Dynamically generate a CSS style for {@code <pre>} elements.
     *
@@ -1335,7 +1360,7 @@ public class HtmlConverter {
     *      A {@code <style>} element that can be dynamically included in the HTML
     *      {@code <head>} element when messages are displayed.
     */
    public static String cssStylePre() {
    private static String cssStylePre() {
        final String font = K9.messageViewFixedWidthFont()
                ? "monospace"
                : "sans-serif";
+11 −3
Original line number Diff line number Diff line
package com.fsck.k9.helper;


import android.support.annotation.VisibleForTesting;

import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.HtmlSerializer;
@@ -19,9 +21,15 @@ public class HtmlSanitizer {
    }


    private HtmlSanitizer() {}
    public static HtmlSanitizer getInstance() {
        return new HtmlSanitizer();
    }

    @VisibleForTesting
    HtmlSanitizer() {}


    public static String sanitize(String html) {
    public String sanitize(String html) {
        TagNode rootNode = HTML_CLEANER.clean(html);

        removeMetaRefresh(rootNode);
@@ -43,7 +51,7 @@ public class HtmlSanitizer {
        return properties;
    }

    private static void removeMetaRefresh(TagNode rootNode) {
    private void removeMetaRefresh(TagNode rootNode) {
        for (TagNode element : rootNode.getElementListByName("meta", true)) {
            String httpEquiv = element.getAttributeByName("http-equiv");
            if (httpEquiv != null && httpEquiv.trim().equalsIgnoreCase("refresh")) {
+38 −24
Original line number Diff line number Diff line
@@ -10,8 +10,10 @@ import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;

import com.fsck.k9.Globals;
import com.fsck.k9.R;
import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.helper.HtmlSanitizer;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
@@ -40,15 +42,29 @@ public class MessageViewInfoExtractor {
    private static final int FILENAME_SUFFIX_LENGTH = FILENAME_SUFFIX.length();


    private static final AttachmentInfoExtractor attachmentInfoExtractor = AttachmentInfoExtractor.getInstance();
    private final Context context;
    private final AttachmentInfoExtractor attachmentInfoExtractor;
    private final HtmlSanitizer htmlSanitizer;


    private MessageViewInfoExtractor() { }
    public static MessageViewInfoExtractor getInstance() {
        Context context = Globals.getContext();
        AttachmentInfoExtractor attachmentInfoExtractor = AttachmentInfoExtractor.getInstance();
        HtmlSanitizer htmlSanitizer = HtmlSanitizer.getInstance();
        return new MessageViewInfoExtractor(context, attachmentInfoExtractor, htmlSanitizer);
    }

    @WorkerThread
    public static MessageViewInfo extractMessageForView(Context context,
            Message message, MessageCryptoAnnotations annotations) throws MessagingException {
    @VisibleForTesting
    MessageViewInfoExtractor(Context context, AttachmentInfoExtractor attachmentInfoExtractor,
            HtmlSanitizer htmlSanitizer) {
        this.context = context;
        this.attachmentInfoExtractor = attachmentInfoExtractor;
        this.htmlSanitizer = htmlSanitizer;
    }

    @WorkerThread
    public MessageViewInfo extractMessageForView(Message message, MessageCryptoAnnotations annotations)
            throws MessagingException {
        Part rootPart;
        CryptoResultAnnotation cryptoResultAnnotation;
        List<Part> extraParts;
@@ -66,13 +82,13 @@ public class MessageViewInfoExtractor {

        List<AttachmentViewInfo> attachmentInfos = new ArrayList<>();
        ViewableExtractedText viewable = extractViewableAndAttachments(
                context, Collections.singletonList(rootPart), attachmentInfos);
                Collections.singletonList(rootPart), attachmentInfos);

        List<AttachmentViewInfo> extraAttachmentInfos = new ArrayList<>();
        String extraViewableText = null;
        if (extraParts != null) {
            ViewableExtractedText extraViewable =
                    extractViewableAndAttachments(context, extraParts, extraAttachmentInfos);
                    extractViewableAndAttachments(extraParts, extraAttachmentInfos);
            extraViewableText = extraViewable.text;
        }

@@ -82,7 +98,7 @@ public class MessageViewInfoExtractor {
                attachmentInfos, cryptoResultAnnotation, extraViewableText, extraAttachmentInfos, attachmentResolver);
    }

    private static ViewableExtractedText extractViewableAndAttachments(Context context, List<Part> parts,
    private ViewableExtractedText extractViewableAndAttachments(List<Part> parts,
            List<AttachmentViewInfo> attachmentInfos) throws MessagingException {
        ArrayList<Viewable> viewableParts = new ArrayList<>();
        ArrayList<Part> attachments = new ArrayList<>();
@@ -92,13 +108,12 @@ public class MessageViewInfoExtractor {
        }

        attachmentInfos.addAll(attachmentInfoExtractor.extractAttachmentInfos(attachments));
        return MessageViewInfoExtractor.extractTextFromViewables(context, viewableParts);
        return extractTextFromViewables(viewableParts);
    }

    /**
     * Extract the viewable textual parts of a message and return the rest as attachments.
     *
     * @param context A {@link android.content.Context} instance that will be used to get localized strings.
     * @return A {@link ViewableExtractedText} instance containing the textual parts of the message as
     *         plain text and HTML, and a list of message parts considered attachments.
     *
@@ -106,7 +121,7 @@ public class MessageViewInfoExtractor {
     *          In case of an error.
     */
    @VisibleForTesting
    static ViewableExtractedText extractTextFromViewables(Context context, List<Viewable> viewables)
    ViewableExtractedText extractTextFromViewables(List<Viewable> viewables)
            throws MessagingException {
        try {
            // Collect all viewable parts
@@ -134,10 +149,10 @@ public class MessageViewInfoExtractor {
                    Message innerMessage =  header.getMessage();

                    addTextDivider(text, containerPart, !hideDivider);
                    addMessageHeaderText(context, text, innerMessage);
                    addMessageHeaderText(text, innerMessage);

                    addHtmlDivider(html, containerPart, !hideDivider);
                    addMessageHeaderHtml(context, html, innerMessage);
                    addMessageHeaderHtml(html, innerMessage);

                    hideDivider = true;
                } else if (viewable instanceof Alternative) {
@@ -171,7 +186,10 @@ public class MessageViewInfoExtractor {
                }
            }

            return new ViewableExtractedText(text.toString(), html.toString());
            String content = HtmlConverter.wrapMessageContent(html);
            String sanitizedHtml = htmlSanitizer.sanitize(content);

            return new ViewableExtractedText(text.toString(), sanitizedHtml);
        } catch (Exception e) {
            throw new MessagingException("Couldn't extract viewable parts", e);
        }
@@ -193,7 +211,7 @@ public class MessageViewInfoExtractor {
     *
     * @return The contents of the supplied viewable instance as HTML.
     */
    private static StringBuilder buildHtml(Viewable viewable, boolean prependDivider) {
    private StringBuilder buildHtml(Viewable viewable, boolean prependDivider) {
        StringBuilder html = new StringBuilder();
        if (viewable instanceof Textual) {
            Part part = ((Textual)viewable).getPart();
@@ -224,7 +242,7 @@ public class MessageViewInfoExtractor {
        return html;
    }

    private static StringBuilder buildText(Viewable viewable, boolean prependDivider) {
    private StringBuilder buildText(Viewable viewable, boolean prependDivider) {
        StringBuilder text = new StringBuilder();
        if (viewable instanceof Textual) {
            Part part = ((Textual)viewable).getPart();
@@ -266,7 +284,7 @@ public class MessageViewInfoExtractor {
     * @param prependDivider
     *         {@code true}, if the divider should be appended. {@code false}, otherwise.
     */
    private static void addHtmlDivider(StringBuilder html, Part part, boolean prependDivider) {
    private void addHtmlDivider(StringBuilder html, Part part, boolean prependDivider) {
        if (prependDivider) {
            String filename = getPartName(part);

@@ -305,7 +323,7 @@ public class MessageViewInfoExtractor {
     * @param prependDivider
     *         {@code true}, if the divider should be appended. {@code false}, otherwise.
     */
    private static void addTextDivider(StringBuilder text, Part part, boolean prependDivider) {
    private void addTextDivider(StringBuilder text, Part part, boolean prependDivider) {
        if (prependDivider) {
            String filename = getPartName(part);

@@ -331,8 +349,6 @@ public class MessageViewInfoExtractor {
    /**
     * Extract important header values from a message to display inline (plain text version).
     *
     * @param context
     *         A {@link android.content.Context} instance that will be used to get localized strings.
     * @param text
     *         The {@link StringBuilder} that will receive the (plain text) output.
     * @param message
@@ -341,7 +357,7 @@ public class MessageViewInfoExtractor {
     * @throws com.fsck.k9.mail.MessagingException
     *          In case of an error.
     */
    private static void addMessageHeaderText(Context context, StringBuilder text, Message message)
    private void addMessageHeaderText(StringBuilder text, Message message)
            throws MessagingException {
        // From: <sender>
        Address[] from = message.getFrom();
@@ -394,8 +410,6 @@ public class MessageViewInfoExtractor {
    /**
     * Extract important header values from a message to display inline (HTML version).
     *
     * @param context
     *         A {@link android.content.Context} instance that will be used to get localized strings.
     * @param html
     *         The {@link StringBuilder} that will receive the (HTML) output.
     * @param message
@@ -404,7 +418,7 @@ public class MessageViewInfoExtractor {
     * @throws com.fsck.k9.mail.MessagingException
     *          In case of an error.
     */
    private static void addMessageHeaderHtml(Context context, StringBuilder html, Message message)
    private void addMessageHeaderHtml(StringBuilder html, Message message)
            throws MessagingException {

        html.append("<table style=\"border: 0\">");
+4 −1
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@ import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;


public class LocalMessageExtractorLoader extends AsyncTaskLoader<MessageViewInfo> {
    private static final MessageViewInfoExtractor messageViewInfoExtractor = MessageViewInfoExtractor.getInstance();


    private final Message message;
    private MessageViewInfo messageViewInfo;
    @Nullable
@@ -49,7 +52,7 @@ public class LocalMessageExtractorLoader extends AsyncTaskLoader<MessageViewInfo
    @WorkerThread
    public MessageViewInfo loadInBackground() {
        try {
            return MessageViewInfoExtractor.extractMessageForView(getContext(), message, annotations);
            return messageViewInfoExtractor.extractMessageForView(message, annotations);
        } catch (Exception e) {
            Log.e(K9.LOG_TAG, "Error while decoding message", e);
            return null;
Loading