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

Commit 773600c7 authored by Vincent Breitmoser's avatar Vincent Breitmoser
Browse files

sanitize html in MessageViewInfoExtractor

also, inject HtmlSanitizer to keep MessageViewInfoExtractor testable and
pass Context through Globals
parent 65f2539e
Loading
Loading
Loading
Loading
+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;
+2 −5
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.widget.Toast;
import com.fsck.k9.R;
import com.fsck.k9.helper.ClipboardManager;
import com.fsck.k9.helper.Contacts;
import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mailstore.AttachmentResolver;
@@ -437,7 +438,7 @@ public class MessageContainerView extends LinearLayout implements OnClickListene
        }

        if (textToDisplay == null) {
            textToDisplay = wrapStatusMessage(getContext().getString(R.string.webview_empty_message));
            textToDisplay = HtmlConverter.wrapStatusMessage(getContext().getString(R.string.webview_empty_message));
        }

        OnPageFinishedListener onPageFinishedListener = new OnPageFinishedListener() {
@@ -460,10 +461,6 @@ public class MessageContainerView extends LinearLayout implements OnClickListene
        return hasHiddenExternalImages;
    }

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

    private void displayHtmlContentWithInlineAttachments(String htmlText, AttachmentResolver attachmentResolver,
            OnPageFinishedListener onPageFinishedListener) {
        currentHtmlText = htmlText;
Loading