diff --git a/k9mail/build.gradle b/k9mail/build.gradle index c94a3275e..fd3838501 100644 --- a/k9mail/build.gradle +++ b/k9mail/build.gradle @@ -39,6 +39,7 @@ dependencies { testCompile "org.robolectric:robolectric:${robolectricVersion}" testCompile "junit:junit:${junitVersion}" testCompile "org.mockito:mockito-core:${mockitoVersion}" + testCompile 'org.jsoup:jsoup:1.10.2' } android { diff --git a/k9mail/src/main/java/com/fsck/k9/activity/setup/WelcomeMessage.java b/k9mail/src/main/java/com/fsck/k9/activity/setup/WelcomeMessage.java index afc4aabd2..34c92cc1a 100644 --- a/k9mail/src/main/java/com/fsck/k9/activity/setup/WelcomeMessage.java +++ b/k9mail/src/main/java/com/fsck/k9/activity/setup/WelcomeMessage.java @@ -6,13 +6,12 @@ import android.os.Bundle; import android.text.method.LinkMovementMethod; import android.view.View; import android.view.View.OnClickListener; -import android.widget.Button; import android.widget.TextView; import com.fsck.k9.R; import com.fsck.k9.activity.Accounts; import com.fsck.k9.activity.K9Activity; -import com.fsck.k9.helper.HtmlConverter; +import com.fsck.k9.message.html.HtmlConverter; /** * Displays a welcome message when no accounts have been created yet. diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java index e070f221d..88c2bb5bb 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java @@ -15,8 +15,8 @@ import android.util.Log; import com.fsck.k9.Globals; import com.fsck.k9.K9; import com.fsck.k9.R; -import com.fsck.k9.helper.HtmlConverter; -import com.fsck.k9.helper.HtmlSanitizer; +import com.fsck.k9.message.html.HtmlConverter; +import com.fsck.k9.message.html.HtmlSanitizer; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Message; @@ -211,7 +211,7 @@ public class MessageViewInfoExtractor { * Use the contents of a {@link com.fsck.k9.mail.internet.Viewable} to create the HTML to be displayed. * *

- * This will use {@link com.fsck.k9.helper.HtmlConverter#textToHtml(String)} to convert plain text parts + * This will use {@link HtmlConverter#textToHtml(String)} to convert plain text parts * to HTML if necessary. *

* diff --git a/k9mail/src/main/java/com/fsck/k9/message/IdentityHeaderBuilder.java b/k9mail/src/main/java/com/fsck/k9/message/IdentityHeaderBuilder.java index 8b587e237..256a3595b 100644 --- a/k9mail/src/main/java/com/fsck/k9/message/IdentityHeaderBuilder.java +++ b/k9mail/src/main/java/com/fsck/k9/message/IdentityHeaderBuilder.java @@ -10,6 +10,7 @@ import com.fsck.k9.Identity; import com.fsck.k9.K9; import com.fsck.k9.activity.MessageReference; import com.fsck.k9.mail.internet.TextBody; +import com.fsck.k9.message.quote.InsertableHtmlContent; public class IdentityHeaderBuilder { diff --git a/k9mail/src/main/java/com/fsck/k9/message/MessageBuilder.java b/k9mail/src/main/java/com/fsck/k9/message/MessageBuilder.java index 3c365fcd1..36412ab3e 100644 --- a/k9mail/src/main/java/com/fsck/k9/message/MessageBuilder.java +++ b/k9mail/src/main/java/com/fsck/k9/message/MessageBuilder.java @@ -33,6 +33,7 @@ import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mailstore.TempFileBody; +import com.fsck.k9.message.quote.InsertableHtmlContent; import org.apache.james.mime4j.codec.EncoderUtil; import org.apache.james.mime4j.util.MimeUtil; diff --git a/k9mail/src/main/java/com/fsck/k9/message/TextBodyBuilder.java b/k9mail/src/main/java/com/fsck/k9/message/TextBodyBuilder.java index a90b4d2ad..99c6d341c 100644 --- a/k9mail/src/main/java/com/fsck/k9/message/TextBodyBuilder.java +++ b/k9mail/src/main/java/com/fsck/k9/message/TextBodyBuilder.java @@ -5,9 +5,10 @@ import android.text.TextUtils; import android.util.Log; import com.fsck.k9.K9; -import com.fsck.k9.helper.HtmlConverter; +import com.fsck.k9.message.html.HtmlConverter; import com.fsck.k9.mail.Body; import com.fsck.k9.mail.internet.TextBody; +import com.fsck.k9.message.quote.InsertableHtmlContent; class TextBodyBuilder { diff --git a/k9mail/src/main/java/com/fsck/k9/message/extractors/BodyTextExtractor.java b/k9mail/src/main/java/com/fsck/k9/message/extractors/BodyTextExtractor.java new file mode 100644 index 000000000..f0f147bcf --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/message/extractors/BodyTextExtractor.java @@ -0,0 +1,62 @@ +package com.fsck.k9.message.extractors; + + +import android.util.Log; + +import com.fsck.k9.K9; +import com.fsck.k9.mail.Part; +import com.fsck.k9.mail.internet.MessageExtractor; +import com.fsck.k9.mail.internet.MimeUtility; +import com.fsck.k9.message.SimpleMessageFormat; +import com.fsck.k9.message.html.HtmlConverter; + + +//TODO: Get rid of this class and use MessageViewInfoExtractor instead +public class BodyTextExtractor { + /** Fetch the body text from a messagePart in the desired messagePart format. This method handles + * conversions between formats (html to text and vice versa) if necessary. + */ + public static String getBodyTextFromMessage(Part messagePart, SimpleMessageFormat format) { + Part part; + if (format == SimpleMessageFormat.HTML) { + // HTML takes precedence, then text. + part = MimeUtility.findFirstPartByMimeType(messagePart, "text/html"); + if (part != null) { + if (K9.DEBUG) { + Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, HTML found."); + } + return MessageExtractor.getTextFromPart(part); + } + + part = MimeUtility.findFirstPartByMimeType(messagePart, "text/plain"); + if (part != null) { + if (K9.DEBUG) { + Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found."); + } + String text = MessageExtractor.getTextFromPart(part); + return HtmlConverter.textToHtml(text); + } + } else if (format == SimpleMessageFormat.TEXT) { + // Text takes precedence, then html. + part = MimeUtility.findFirstPartByMimeType(messagePart, "text/plain"); + if (part != null) { + if (K9.DEBUG) { + Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, text found."); + } + return MessageExtractor.getTextFromPart(part); + } + + part = MimeUtility.findFirstPartByMimeType(messagePart, "text/html"); + if (part != null) { + if (K9.DEBUG) { + Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, HTML found."); + } + String text = MessageExtractor.getTextFromPart(part); + return HtmlConverter.htmlToText(text); + } + } + + // If we had nothing interesting, return an empty string. + return ""; + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/message/extractors/MessageFulltextCreator.java b/k9mail/src/main/java/com/fsck/k9/message/extractors/MessageFulltextCreator.java index 8c46468b7..82dd8e4ed 100644 --- a/k9mail/src/main/java/com/fsck/k9/message/extractors/MessageFulltextCreator.java +++ b/k9mail/src/main/java/com/fsck/k9/message/extractors/MessageFulltextCreator.java @@ -3,7 +3,7 @@ package com.fsck.k9.message.extractors; import android.support.annotation.NonNull; -import com.fsck.k9.helper.HtmlConverter; +import com.fsck.k9.message.html.HtmlConverter; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MessageExtractor; diff --git a/k9mail/src/main/java/com/fsck/k9/message/extractors/PreviewTextExtractor.java b/k9mail/src/main/java/com/fsck/k9/message/extractors/PreviewTextExtractor.java index 1a4065571..7e51ab5ab 100644 --- a/k9mail/src/main/java/com/fsck/k9/message/extractors/PreviewTextExtractor.java +++ b/k9mail/src/main/java/com/fsck/k9/message/extractors/PreviewTextExtractor.java @@ -3,7 +3,7 @@ package com.fsck.k9.message.extractors; import android.support.annotation.NonNull; -import com.fsck.k9.helper.HtmlConverter; +import com.fsck.k9.message.html.HtmlConverter; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MessageExtractor; diff --git a/k9mail/src/main/java/com/fsck/k9/helper/HtmlConverter.java b/k9mail/src/main/java/com/fsck/k9/message/html/HtmlConverter.java similarity index 99% rename from k9mail/src/main/java/com/fsck/k9/helper/HtmlConverter.java rename to k9mail/src/main/java/com/fsck/k9/message/html/HtmlConverter.java index 4fe87d411..02b4a1f04 100644 --- a/k9mail/src/main/java/com/fsck/k9/helper/HtmlConverter.java +++ b/k9mail/src/main/java/com/fsck/k9/message/html/HtmlConverter.java @@ -1,4 +1,4 @@ -package com.fsck.k9.helper; +package com.fsck.k9.message.html; import android.text.*; import android.text.Html.TagHandler; diff --git a/k9mail/src/main/java/com/fsck/k9/helper/HtmlSanitizer.java b/k9mail/src/main/java/com/fsck/k9/message/html/HtmlSanitizer.java similarity index 98% rename from k9mail/src/main/java/com/fsck/k9/helper/HtmlSanitizer.java rename to k9mail/src/main/java/com/fsck/k9/message/html/HtmlSanitizer.java index c74701888..78029e056 100644 --- a/k9mail/src/main/java/com/fsck/k9/helper/HtmlSanitizer.java +++ b/k9mail/src/main/java/com/fsck/k9/message/html/HtmlSanitizer.java @@ -1,4 +1,4 @@ -package com.fsck.k9.helper; +package com.fsck.k9.message.html; import android.support.annotation.VisibleForTesting; diff --git a/k9mail/src/main/java/com/fsck/k9/helper/QuotedMessageHelper.java b/k9mail/src/main/java/com/fsck/k9/message/quote/HtmlQuoteCreator.java similarity index 51% rename from k9mail/src/main/java/com/fsck/k9/helper/QuotedMessageHelper.java rename to k9mail/src/main/java/com/fsck/k9/message/quote/HtmlQuoteCreator.java index 57613e5c2..971d87ff6 100644 --- a/k9mail/src/main/java/com/fsck/k9/helper/QuotedMessageHelper.java +++ b/k9mail/src/main/java/com/fsck/k9/message/quote/HtmlQuoteCreator.java @@ -1,11 +1,6 @@ -package com.fsck.k9.helper; +package com.fsck.k9.message.quote; -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -19,21 +14,10 @@ import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.Part; -import com.fsck.k9.mail.internet.MessageExtractor; -import com.fsck.k9.mail.internet.MimeUtility; -import com.fsck.k9.message.InsertableHtmlContent; -import com.fsck.k9.message.SimpleMessageFormat; -import org.htmlcleaner.CleanerProperties; -import org.htmlcleaner.HtmlCleaner; -import org.htmlcleaner.SimpleHtmlSerializer; -import org.htmlcleaner.TagNode; +import com.fsck.k9.message.html.HtmlConverter; -public class QuotedMessageHelper { - // amount of extra buffer to allocate to accommodate quoting headers or prefixes - private static final int QUOTE_BUFFER_LENGTH = 512; - +public class HtmlQuoteCreator { // Regular expressions to look for various HTML tags. This is no HTML::Parser, but hopefully it's good enough for // our purposes. private static final Pattern FIND_INSERTION_POINT_HTML = Pattern.compile("(?si:.*?(|\\s+[^>]*>)).*)"); @@ -41,13 +25,6 @@ public class QuotedMessageHelper { private static final Pattern FIND_INSERTION_POINT_BODY = Pattern.compile("(?si:.*?(|\\s+[^>]*>)).*)"); private static final Pattern FIND_INSERTION_POINT_HTML_END = Pattern.compile("(?si:.*().*?)"); private static final Pattern FIND_INSERTION_POINT_BODY_END = Pattern.compile("(?si:.*().*?)"); - - // Regexes to check for signature. - private static final Pattern DASH_SIGNATURE_HTML = Pattern.compile("(|\r?\n)-- ", Pattern.CASE_INSENSITIVE); - private static final Pattern BLOCKQUOTE_START = Pattern.compile("", Pattern.CASE_INSENSITIVE); - private static final Pattern DASH_SIGNATURE_PLAIN = Pattern.compile("\r\n-- \r\n.*", Pattern.DOTALL); - // The first group in a Matcher contains the first capture group. We capture the tag found in the above REs so that // we can locate the *end* of that tag. private static final int FIND_INSERTION_POINT_FIRST_GROUP = 1; @@ -59,9 +36,7 @@ public class QuotedMessageHelper { // Index of the start of the beginning of a String. private static final int FIND_INSERTION_POINT_START_OF_STRING = 0; - private static final int REPLY_WRAP_LINE_WIDTH = 72; - - + /** * Add quoting markup to a HTML message. * @param originalMessage Metadata for message being quoted. @@ -74,10 +49,10 @@ public class QuotedMessageHelper { String messageBody, QuoteStyle quoteStyle) throws MessagingException { InsertableHtmlContent insertable = findInsertionPoints(messageBody); - String sentDate = getSentDateText(resources, originalMessage); + String sentDate = QuoteHelper.getSentDateText(resources, originalMessage); String fromAddress = Address.toString(originalMessage.getFrom()); if (quoteStyle == QuoteStyle.PREFIX) { - StringBuilder header = new StringBuilder(QUOTE_BUFFER_LENGTH); + StringBuilder header = new StringBuilder(QuoteHelper.QUOTE_BUFFER_LENGTH); header.append("
"); if (sentDate.length() != 0) { header.append(HtmlConverter.textToHtmlFragment(String.format( @@ -134,25 +109,6 @@ public class QuotedMessageHelper { return insertable; } - /** - * Extract the date from a message and convert it into a locale-specific - * date string suitable for use in a header for a quoted message. - * - * @return A string with the formatted date/time - */ - private static String getSentDateText(Resources resources, Message message) { - try { - final int dateStyle = DateFormat.LONG; - final int timeStyle = DateFormat.LONG; - Date date = message.getSentDate(); - Locale locale = resources.getConfiguration().locale; - return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale) - .format(date); - } catch (Exception e) { - return ""; - } - } - /** *

Find the start and end positions of the HTML in the string. This should be the very top * and bottom of the displayable message. It returns a {@link InsertableHtmlContent}, which @@ -265,189 +221,4 @@ public class QuotedMessageHelper { return insertable; } - - /** - * Add quoting markup to a text message. - * @param originalMessage Metadata for message being quoted. - * @param messageBody Text of the message to be quoted. - * @param quoteStyle Style of quoting. - * @return Quoted text. - * @throws MessagingException - */ - public static String quoteOriginalTextMessage(Resources resources, Message originalMessage, String messageBody, QuoteStyle quoteStyle, String prefix) throws MessagingException { - String body = messageBody == null ? "" : messageBody; - String sentDate = QuotedMessageHelper.getSentDateText(resources, originalMessage); - if (quoteStyle == QuoteStyle.PREFIX) { - StringBuilder quotedText = new StringBuilder(body.length() + QuotedMessageHelper.QUOTE_BUFFER_LENGTH); - if (sentDate.length() != 0) { - quotedText.append(String.format( - resources.getString(R.string.message_compose_reply_header_fmt_with_date) + "\r\n", - sentDate, - Address.toString(originalMessage.getFrom()))); - } else { - quotedText.append(String.format( - resources.getString(R.string.message_compose_reply_header_fmt) + "\r\n", - Address.toString(originalMessage.getFrom())) - ); - } - - final String wrappedText = Utility.wrap(body, REPLY_WRAP_LINE_WIDTH - prefix.length()); - - // "$" and "\" in the quote prefix have to be escaped for - // the replaceAll() invocation. - final String escapedPrefix = prefix.replaceAll("(\\\\|\\$)", "\\\\$1"); - quotedText.append(wrappedText.replaceAll("(?m)^", escapedPrefix)); - - // TODO is this correct? - return quotedText.toString().replaceAll("\\\r", ""); - } else if (quoteStyle == QuoteStyle.HEADER) { - StringBuilder quotedText = new StringBuilder(body.length() + QuotedMessageHelper.QUOTE_BUFFER_LENGTH); - quotedText.append("\r\n"); - quotedText.append(resources.getString(R.string.message_compose_quote_header_separator)).append("\r\n"); - if (originalMessage.getFrom() != null && Address.toString(originalMessage.getFrom()).length() != 0) { - quotedText.append(resources.getString(R.string.message_compose_quote_header_from)).append(" ").append(Address.toString(originalMessage.getFrom())).append("\r\n"); - } - if (sentDate.length() != 0) { - quotedText.append(resources.getString(R.string.message_compose_quote_header_send_date)).append(" ").append(sentDate).append("\r\n"); - } - if (originalMessage.getRecipients(RecipientType.TO) != null && originalMessage.getRecipients(RecipientType.TO).length != 0) { - quotedText.append(resources.getString(R.string.message_compose_quote_header_to)).append(" ").append(Address.toString(originalMessage.getRecipients(RecipientType.TO))).append("\r\n"); - } - if (originalMessage.getRecipients(RecipientType.CC) != null && originalMessage.getRecipients(RecipientType.CC).length != 0) { - quotedText.append(resources.getString(R.string.message_compose_quote_header_cc)).append(" ").append(Address.toString(originalMessage.getRecipients(RecipientType.CC))).append("\r\n"); - } - if (originalMessage.getSubject() != null) { - quotedText.append(resources.getString(R.string.message_compose_quote_header_subject)).append(" ").append(originalMessage.getSubject()).append("\r\n"); - } - quotedText.append("\r\n"); - - quotedText.append(body); - - return quotedText.toString(); - } else { - // Shouldn't ever happen. - return body; - } - } - - /** Fetch the body text from a messagePart in the desired messagePart format. This method handles - * conversions between formats (html to text and vice versa) if necessary. - */ - public static String getBodyTextFromMessage(Part messagePart, SimpleMessageFormat format) { - Part part; - if (format == SimpleMessageFormat.HTML) { - // HTML takes precedence, then text. - part = MimeUtility.findFirstPartByMimeType(messagePart, "text/html"); - if (part != null) { - if (K9.DEBUG) { - Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, HTML found."); - } - return MessageExtractor.getTextFromPart(part); - } - - part = MimeUtility.findFirstPartByMimeType(messagePart, "text/plain"); - if (part != null) { - if (K9.DEBUG) { - Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found."); - } - String text = MessageExtractor.getTextFromPart(part); - return HtmlConverter.textToHtml(text); - } - } else if (format == SimpleMessageFormat.TEXT) { - // Text takes precedence, then html. - part = MimeUtility.findFirstPartByMimeType(messagePart, "text/plain"); - if (part != null) { - if (K9.DEBUG) { - Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, text found."); - } - return MessageExtractor.getTextFromPart(part); - } - - part = MimeUtility.findFirstPartByMimeType(messagePart, "text/html"); - if (part != null) { - if (K9.DEBUG) { - Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, HTML found."); - } - String text = MessageExtractor.getTextFromPart(part); - return HtmlConverter.htmlToText(text); - } - } - - // If we had nothing interesting, return an empty string. - return ""; - } - - public static String stripSignatureForHtmlMessage(String content) { - Matcher dashSignatureHtml = DASH_SIGNATURE_HTML.matcher(content); - if (dashSignatureHtml.find()) { - Matcher blockquoteStart = BLOCKQUOTE_START.matcher(content); - Matcher blockquoteEnd = BLOCKQUOTE_END.matcher(content); - List start = new ArrayList<>(); - List end = new ArrayList<>(); - - while (blockquoteStart.find()) { - start.add(blockquoteStart.start()); - } - while (blockquoteEnd.find()) { - end.add(blockquoteEnd.start()); - } - if (start.size() != end.size()) { - Log.d(K9.LOG_TAG, "There are " + start.size() + "

tags, but " + - end.size() + "
tags. Refusing to strip."); - } else if (start.size() > 0) { - // Ignore quoted signatures in blockquotes. - dashSignatureHtml.region(0, start.get(0)); - if (dashSignatureHtml.find()) { - // before first
. - content = content.substring(0, dashSignatureHtml.start()); - } else { - for (int i = 0; i < start.size() - 1; i++) { - // within blockquotes. - if (end.get(i) < start.get(i + 1)) { - dashSignatureHtml.region(end.get(i), start.get(i + 1)); - if (dashSignatureHtml.find()) { - content = content.substring(0, dashSignatureHtml.start()); - break; - } - } - } - if (end.get(end.size() - 1) < content.length()) { - // after last
. - dashSignatureHtml.region(end.get(end.size() - 1), content.length()); - if (dashSignatureHtml.find()) { - content = content.substring(0, dashSignatureHtml.start()); - } - } - } - } else { - // No blockquotes found. - content = content.substring(0, dashSignatureHtml.start()); - } - } - - // Fix the stripping off of closing tags if a signature was stripped, - // as well as clean up the HTML of the quoted message. - HtmlCleaner cleaner = new HtmlCleaner(); - CleanerProperties properties = cleaner.getProperties(); - - // see http://htmlcleaner.sourceforge.net/parameters.php for descriptions - properties.setNamespacesAware(false); - properties.setAdvancedXmlEscape(false); - properties.setOmitXmlDeclaration(true); - properties.setOmitDoctypeDeclaration(false); - properties.setTranslateSpecialEntities(false); - properties.setRecognizeUnicodeChars(false); - - TagNode node = cleaner.clean(content); - SimpleHtmlSerializer htmlSerialized = new SimpleHtmlSerializer(properties); - content = htmlSerialized.getAsString(node, "UTF8"); - return content; - } - - public static String stripSignatureForTextMessage(String content) { - if (DASH_SIGNATURE_PLAIN.matcher(content).find()) { - content = DASH_SIGNATURE_PLAIN.matcher(content).replaceFirst("\r\n"); - } - return content; - } } diff --git a/k9mail/src/main/java/com/fsck/k9/message/InsertableHtmlContent.java b/k9mail/src/main/java/com/fsck/k9/message/quote/InsertableHtmlContent.java similarity index 99% rename from k9mail/src/main/java/com/fsck/k9/message/InsertableHtmlContent.java rename to k9mail/src/main/java/com/fsck/k9/message/quote/InsertableHtmlContent.java index f984fa7cb..ee5bffdcc 100644 --- a/k9mail/src/main/java/com/fsck/k9/message/InsertableHtmlContent.java +++ b/k9mail/src/main/java/com/fsck/k9/message/quote/InsertableHtmlContent.java @@ -1,4 +1,4 @@ -package com.fsck.k9.message; +package com.fsck.k9.message.quote; import java.io.Serializable; diff --git a/k9mail/src/main/java/com/fsck/k9/message/quote/QuoteHelper.java b/k9mail/src/main/java/com/fsck/k9/message/quote/QuoteHelper.java new file mode 100644 index 000000000..8ce47c9b1 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/message/quote/QuoteHelper.java @@ -0,0 +1,36 @@ +package com.fsck.k9.message.quote; + + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; + +import android.content.res.Resources; + +import com.fsck.k9.mail.Message; + + +class QuoteHelper { + // amount of extra buffer to allocate to accommodate quoting headers or prefixes + static final int QUOTE_BUFFER_LENGTH = 512; + + + /** + * Extract the date from a message and convert it into a locale-specific + * date string suitable for use in a header for a quoted message. + * + * @return A string with the formatted date/time + */ + static String getSentDateText(Resources resources, Message message) { + try { + final int dateStyle = DateFormat.LONG; + final int timeStyle = DateFormat.LONG; + Date date = message.getSentDate(); + Locale locale = resources.getConfiguration().locale; + return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale) + .format(date); + } catch (Exception e) { + return ""; + } + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/message/quote/TextQuoteCreator.java b/k9mail/src/main/java/com/fsck/k9/message/quote/TextQuoteCreator.java new file mode 100644 index 000000000..fdb53b3da --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/message/quote/TextQuoteCreator.java @@ -0,0 +1,83 @@ +package com.fsck.k9.message.quote; + + +import android.content.res.Resources; + +import com.fsck.k9.Account.QuoteStyle; +import com.fsck.k9.R; +import com.fsck.k9.helper.Utility; +import com.fsck.k9.mail.Address; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.Message.RecipientType; +import com.fsck.k9.mail.MessagingException; + +import static com.fsck.k9.message.quote.QuoteHelper.QUOTE_BUFFER_LENGTH; + + +public class TextQuoteCreator { + private static final int REPLY_WRAP_LINE_WIDTH = 72; + + /** + * Add quoting markup to a text message. + * @param originalMessage Metadata for message being quoted. + * @param messageBody Text of the message to be quoted. + * @param quoteStyle Style of quoting. + * @return Quoted text. + * @throws MessagingException + */ + public static String quoteOriginalTextMessage(Resources resources, Message originalMessage, String messageBody, QuoteStyle quoteStyle, String prefix) throws MessagingException { + String body = messageBody == null ? "" : messageBody; + String sentDate = QuoteHelper.getSentDateText(resources, originalMessage); + if (quoteStyle == QuoteStyle.PREFIX) { + StringBuilder quotedText = new StringBuilder(body.length() + QUOTE_BUFFER_LENGTH); + if (sentDate.length() != 0) { + quotedText.append(String.format( + resources.getString(R.string.message_compose_reply_header_fmt_with_date) + "\r\n", + sentDate, + Address.toString(originalMessage.getFrom()))); + } else { + quotedText.append(String.format( + resources.getString(R.string.message_compose_reply_header_fmt) + "\r\n", + Address.toString(originalMessage.getFrom())) + ); + } + + final String wrappedText = Utility.wrap(body, REPLY_WRAP_LINE_WIDTH - prefix.length()); + + // "$" and "\" in the quote prefix have to be escaped for + // the replaceAll() invocation. + final String escapedPrefix = prefix.replaceAll("(\\\\|\\$)", "\\\\$1"); + quotedText.append(wrappedText.replaceAll("(?m)^", escapedPrefix)); + + // TODO is this correct? + return quotedText.toString().replaceAll("\\\r", ""); + } else if (quoteStyle == QuoteStyle.HEADER) { + StringBuilder quotedText = new StringBuilder(body.length() + QUOTE_BUFFER_LENGTH); + quotedText.append("\r\n"); + quotedText.append(resources.getString(R.string.message_compose_quote_header_separator)).append("\r\n"); + if (originalMessage.getFrom() != null && Address.toString(originalMessage.getFrom()).length() != 0) { + quotedText.append(resources.getString(R.string.message_compose_quote_header_from)).append(" ").append(Address.toString(originalMessage.getFrom())).append("\r\n"); + } + if (sentDate.length() != 0) { + quotedText.append(resources.getString(R.string.message_compose_quote_header_send_date)).append(" ").append(sentDate).append("\r\n"); + } + if (originalMessage.getRecipients(RecipientType.TO) != null && originalMessage.getRecipients(RecipientType.TO).length != 0) { + quotedText.append(resources.getString(R.string.message_compose_quote_header_to)).append(" ").append(Address.toString(originalMessage.getRecipients(RecipientType.TO))).append("\r\n"); + } + if (originalMessage.getRecipients(RecipientType.CC) != null && originalMessage.getRecipients(RecipientType.CC).length != 0) { + quotedText.append(resources.getString(R.string.message_compose_quote_header_cc)).append(" ").append(Address.toString(originalMessage.getRecipients(RecipientType.CC))).append("\r\n"); + } + if (originalMessage.getSubject() != null) { + quotedText.append(resources.getString(R.string.message_compose_quote_header_subject)).append(" ").append(originalMessage.getSubject()).append("\r\n"); + } + quotedText.append("\r\n"); + + quotedText.append(body); + + return quotedText.toString(); + } else { + // Shouldn't ever happen. + return body; + } + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/message/signature/HtmlSignatureRemover.java b/k9mail/src/main/java/com/fsck/k9/message/signature/HtmlSignatureRemover.java new file mode 100644 index 000000000..86145b2ea --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/message/signature/HtmlSignatureRemover.java @@ -0,0 +1,90 @@ +package com.fsck.k9.message.signature; + + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.util.Log; + +import com.fsck.k9.K9; +import org.htmlcleaner.CleanerProperties; +import org.htmlcleaner.HtmlCleaner; +import org.htmlcleaner.SimpleHtmlSerializer; +import org.htmlcleaner.TagNode; + + +public class HtmlSignatureRemover { + private static final Pattern DASH_SIGNATURE_HTML = Pattern.compile("(|\r?\n)-- ", Pattern.CASE_INSENSITIVE); + private static final Pattern BLOCKQUOTE_START = Pattern.compile("", Pattern.CASE_INSENSITIVE); + + + public static String stripSignature(String content) { + Matcher dashSignatureHtml = DASH_SIGNATURE_HTML.matcher(content); + if (dashSignatureHtml.find()) { + Matcher blockquoteStart = BLOCKQUOTE_START.matcher(content); + Matcher blockquoteEnd = BLOCKQUOTE_END.matcher(content); + List start = new ArrayList<>(); + List end = new ArrayList<>(); + + while (blockquoteStart.find()) { + start.add(blockquoteStart.start()); + } + while (blockquoteEnd.find()) { + end.add(blockquoteEnd.start()); + } + if (start.size() != end.size()) { + Log.d(K9.LOG_TAG, "There are " + start.size() + "
tags, but " + + end.size() + "
tags. Refusing to strip."); + } else if (start.size() > 0) { + // Ignore quoted signatures in blockquotes. + dashSignatureHtml.region(0, start.get(0)); + if (dashSignatureHtml.find()) { + // before first
. + content = content.substring(0, dashSignatureHtml.start()); + } else { + for (int i = 0; i < start.size() - 1; i++) { + // within blockquotes. + if (end.get(i) < start.get(i + 1)) { + dashSignatureHtml.region(end.get(i), start.get(i + 1)); + if (dashSignatureHtml.find()) { + content = content.substring(0, dashSignatureHtml.start()); + break; + } + } + } + if (end.get(end.size() - 1) < content.length()) { + // after last
. + dashSignatureHtml.region(end.get(end.size() - 1), content.length()); + if (dashSignatureHtml.find()) { + content = content.substring(0, dashSignatureHtml.start()); + } + } + } + } else { + // No blockquotes found. + content = content.substring(0, dashSignatureHtml.start()); + } + } + + // Fix the stripping off of closing tags if a signature was stripped, + // as well as clean up the HTML of the quoted message. + HtmlCleaner cleaner = new HtmlCleaner(); + CleanerProperties properties = cleaner.getProperties(); + + // see http://htmlcleaner.sourceforge.net/parameters.php for descriptions + properties.setNamespacesAware(false); + properties.setAdvancedXmlEscape(false); + properties.setOmitXmlDeclaration(true); + properties.setOmitDoctypeDeclaration(false); + properties.setTranslateSpecialEntities(false); + properties.setRecognizeUnicodeChars(false); + + TagNode node = cleaner.clean(content); + SimpleHtmlSerializer htmlSerialized = new SimpleHtmlSerializer(properties); + content = htmlSerialized.getAsString(node, "UTF8"); + return content; + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/message/signature/TextSignatureRemover.java b/k9mail/src/main/java/com/fsck/k9/message/signature/TextSignatureRemover.java new file mode 100644 index 000000000..2e610ff41 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/message/signature/TextSignatureRemover.java @@ -0,0 +1,17 @@ +package com.fsck.k9.message.signature; + + +import java.util.regex.Pattern; + + +public class TextSignatureRemover { + private static final Pattern DASH_SIGNATURE_PLAIN = Pattern.compile("\r\n-- \r\n.*", Pattern.DOTALL); + + + public static String stripSignature(String content) { + if (DASH_SIGNATURE_PLAIN.matcher(content).find()) { + content = DASH_SIGNATURE_PLAIN.matcher(content).replaceFirst("\r\n"); + } + return content; + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/ui/compose/QuotedMessageMvpView.java b/k9mail/src/main/java/com/fsck/k9/ui/compose/QuotedMessageMvpView.java index ac3e70a37..cbc2ac095 100644 --- a/k9mail/src/main/java/com/fsck/k9/ui/compose/QuotedMessageMvpView.java +++ b/k9mail/src/main/java/com/fsck/k9/ui/compose/QuotedMessageMvpView.java @@ -12,7 +12,7 @@ import android.widget.ImageButton; import com.fsck.k9.FontSizes; import com.fsck.k9.R; import com.fsck.k9.activity.MessageCompose; -import com.fsck.k9.helper.HtmlConverter; +import com.fsck.k9.message.html.HtmlConverter; import com.fsck.k9.mailstore.AttachmentResolver; import com.fsck.k9.message.QuotedTextMode; import com.fsck.k9.message.SimpleMessageFormat; diff --git a/k9mail/src/main/java/com/fsck/k9/ui/compose/QuotedMessagePresenter.java b/k9mail/src/main/java/com/fsck/k9/ui/compose/QuotedMessagePresenter.java index a38423dfd..53882b65c 100644 --- a/k9mail/src/main/java/com/fsck/k9/ui/compose/QuotedMessagePresenter.java +++ b/k9mail/src/main/java/com/fsck/k9/ui/compose/QuotedMessagePresenter.java @@ -13,8 +13,11 @@ import com.fsck.k9.Account.QuoteStyle; import com.fsck.k9.K9; import com.fsck.k9.activity.MessageCompose; import com.fsck.k9.activity.MessageCompose.Action; -import com.fsck.k9.helper.HtmlConverter; -import com.fsck.k9.helper.QuotedMessageHelper; +import com.fsck.k9.message.extractors.BodyTextExtractor; +import com.fsck.k9.message.html.HtmlConverter; +import com.fsck.k9.message.quote.HtmlQuoteCreator; +import com.fsck.k9.message.quote.TextQuoteCreator; +import com.fsck.k9.message.signature.HtmlSignatureRemover; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MessageExtractor; @@ -22,10 +25,11 @@ import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mailstore.AttachmentResolver; import com.fsck.k9.mailstore.MessageViewInfo; import com.fsck.k9.message.IdentityField; -import com.fsck.k9.message.InsertableHtmlContent; +import com.fsck.k9.message.quote.InsertableHtmlContent; import com.fsck.k9.message.MessageBuilder; import com.fsck.k9.message.QuotedTextMode; import com.fsck.k9.message.SimpleMessageFormat; +import com.fsck.k9.message.signature.TextSignatureRemover; public class QuotedMessagePresenter { @@ -98,17 +102,17 @@ public class QuotedMessagePresenter { // Handle the original message in the reply // If we already have sourceMessageBody, use that. It's pre-populated if we've got crypto going on. - String content = QuotedMessageHelper.getBodyTextFromMessage(messageViewInfo.rootPart, quotedTextFormat); + String content = BodyTextExtractor.getBodyTextFromMessage(messageViewInfo.rootPart, quotedTextFormat); if (quotedTextFormat == SimpleMessageFormat.HTML) { // Strip signature. // closing tags such as
, , , will be cut off. if (account.isStripSignature() && (action == Action.REPLY || action == Action.REPLY_ALL)) { - content = QuotedMessageHelper.stripSignatureForHtmlMessage(content); + content = HtmlSignatureRemover.stripSignature(content); } // Add the HTML reply header to the top of the content. - quotedHtmlContent = QuotedMessageHelper.quoteOriginalHtmlMessage( + quotedHtmlContent = HtmlQuoteCreator.quoteOriginalHtmlMessage( resources, messageViewInfo.message, content, quoteStyle); // Load the message with the reply header. TODO replace with MessageViewInfo data @@ -116,16 +120,16 @@ public class QuotedMessagePresenter { AttachmentResolver.createFromPart(messageViewInfo.rootPart)); // TODO: Also strip the signature from the text/plain part - view.setQuotedText(QuotedMessageHelper.quoteOriginalTextMessage(resources, messageViewInfo.message, - QuotedMessageHelper.getBodyTextFromMessage(messageViewInfo.rootPart, SimpleMessageFormat.TEXT), + view.setQuotedText(TextQuoteCreator.quoteOriginalTextMessage(resources, messageViewInfo.message, + BodyTextExtractor.getBodyTextFromMessage(messageViewInfo.rootPart, SimpleMessageFormat.TEXT), quoteStyle, account.getQuotePrefix())); } else if (quotedTextFormat == SimpleMessageFormat.TEXT) { if (account.isStripSignature() && (action == Action.REPLY || action == Action.REPLY_ALL)) { - content = QuotedMessageHelper.stripSignatureForTextMessage(content); + content = TextSignatureRemover.stripSignature(content); } - view.setQuotedText(QuotedMessageHelper.quoteOriginalTextMessage( + view.setQuotedText(TextQuoteCreator.quoteOriginalTextMessage( resources, messageViewInfo.message, content, quoteStyle, account.getQuotePrefix())); } @@ -234,7 +238,7 @@ public class QuotedMessagePresenter { // composition window. If that's the case, try and convert it to text to // match the behavior in text mode. view.setMessageContentCharacters( - QuotedMessageHelper.getBodyTextFromMessage(messageViewInfo.message, SimpleMessageFormat.TEXT)); + BodyTextExtractor.getBodyTextFromMessage(messageViewInfo.message, SimpleMessageFormat.TEXT)); forcePlainText = true; showOrHideQuotedText(quotedMode); 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 d2d707b05..3041db3a3 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 @@ -30,7 +30,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.message.html.HtmlConverter; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Address; import com.fsck.k9.mailstore.AttachmentResolver; diff --git a/k9mail/src/main/java/com/fsck/k9/view/MessageWebView.java b/k9mail/src/main/java/com/fsck/k9/view/MessageWebView.java index 0b09131a7..dc6b93995 100644 --- a/k9mail/src/main/java/com/fsck/k9/view/MessageWebView.java +++ b/k9mail/src/main/java/com/fsck/k9/view/MessageWebView.java @@ -17,7 +17,6 @@ import android.widget.Toast; import com.fsck.k9.K9; import com.fsck.k9.K9.Theme; import com.fsck.k9.R; -import com.fsck.k9.helper.HtmlConverter; import com.fsck.k9.mailstore.AttachmentResolver; diff --git a/k9mail/src/test/java/com/fsck/k9/mailstore/MessageViewInfoExtractorTest.java b/k9mail/src/test/java/com/fsck/k9/mailstore/MessageViewInfoExtractorTest.java index 3bf589967..be083023b 100644 --- a/k9mail/src/test/java/com/fsck/k9/mailstore/MessageViewInfoExtractorTest.java +++ b/k9mail/src/test/java/com/fsck/k9/mailstore/MessageViewInfoExtractorTest.java @@ -12,8 +12,8 @@ import android.app.Application; import com.fsck.k9.GlobalsHelper; import com.fsck.k9.activity.K9ActivityCommon; -import com.fsck.k9.helper.HtmlSanitizer; -import com.fsck.k9.helper.HtmlSanitizerHelper; +import com.fsck.k9.message.html.HtmlSanitizer; +import com.fsck.k9.message.html.HtmlSanitizerHelper; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.MessagingException; diff --git a/k9mail/src/test/java/com/fsck/k9/message/MessageBuilderTest.java b/k9mail/src/test/java/com/fsck/k9/message/MessageBuilderTest.java index 2004434ea..757c4a7a1 100644 --- a/k9mail/src/test/java/com/fsck/k9/message/MessageBuilderTest.java +++ b/k9mail/src/test/java/com/fsck/k9/message/MessageBuilderTest.java @@ -26,6 +26,7 @@ import com.fsck.k9.mail.internet.MessageIdGenerator; import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.message.MessageBuilder.Callback; +import com.fsck.k9.message.quote.InsertableHtmlContent; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/k9mail/src/test/java/com/fsck/k9/message/PgpMessageBuilderTest.java b/k9mail/src/test/java/com/fsck/k9/message/PgpMessageBuilderTest.java index 57d09165c..19f4cd2b8 100644 --- a/k9mail/src/test/java/com/fsck/k9/message/PgpMessageBuilderTest.java +++ b/k9mail/src/test/java/com/fsck/k9/message/PgpMessageBuilderTest.java @@ -33,6 +33,7 @@ import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.message.MessageBuilder.Callback; +import com.fsck.k9.message.quote.InsertableHtmlContent; import com.fsck.k9.view.RecipientSelectView.Recipient; import org.apache.commons.io.Charsets; diff --git a/k9mail/src/test/java/com/fsck/k9/message/TextBodyBuilderTest.java b/k9mail/src/test/java/com/fsck/k9/message/TextBodyBuilderTest.java index 2c1c5d64b..1aba706cf 100644 --- a/k9mail/src/test/java/com/fsck/k9/message/TextBodyBuilderTest.java +++ b/k9mail/src/test/java/com/fsck/k9/message/TextBodyBuilderTest.java @@ -3,6 +3,7 @@ package com.fsck.k9.message; import com.fsck.k9.Account.QuoteStyle; import com.fsck.k9.mail.internet.TextBody; +import com.fsck.k9.message.quote.InsertableHtmlContent; import org.junit.Ignore; import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.Theories; diff --git a/k9mail/src/test/java/com/fsck/k9/helper/HtmlConverterTest.java b/k9mail/src/test/java/com/fsck/k9/message/html/HtmlConverterTest.java similarity index 99% rename from k9mail/src/test/java/com/fsck/k9/helper/HtmlConverterTest.java rename to k9mail/src/test/java/com/fsck/k9/message/html/HtmlConverterTest.java index 404a0a8f5..9db158533 100644 --- a/k9mail/src/test/java/com/fsck/k9/helper/HtmlConverterTest.java +++ b/k9mail/src/test/java/com/fsck/k9/message/html/HtmlConverterTest.java @@ -1,4 +1,4 @@ -package com.fsck.k9.helper; +package com.fsck.k9.message.html; import java.io.BufferedWriter; @@ -6,6 +6,7 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import com.fsck.k9.message.html.HtmlConverter; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/k9mail/src/test/java/com/fsck/k9/message/html/HtmlHelper.java b/k9mail/src/test/java/com/fsck/k9/message/html/HtmlHelper.java new file mode 100644 index 000000000..840c2de7f --- /dev/null +++ b/k9mail/src/test/java/com/fsck/k9/message/html/HtmlHelper.java @@ -0,0 +1,12 @@ +package com.fsck.k9.message.html; + + +import org.jsoup.Jsoup; +import org.jsoup.safety.Whitelist; + + +public class HtmlHelper { + public static String extractText(String html) { + return Jsoup.clean(html, Whitelist.none()); + } +} diff --git a/k9mail/src/test/java/com/fsck/k9/helper/HtmlSanitizerHelper.java b/k9mail/src/test/java/com/fsck/k9/message/html/HtmlSanitizerHelper.java similarity index 76% rename from k9mail/src/test/java/com/fsck/k9/helper/HtmlSanitizerHelper.java rename to k9mail/src/test/java/com/fsck/k9/message/html/HtmlSanitizerHelper.java index 085f24996..92fbde070 100644 --- a/k9mail/src/test/java/com/fsck/k9/helper/HtmlSanitizerHelper.java +++ b/k9mail/src/test/java/com/fsck/k9/message/html/HtmlSanitizerHelper.java @@ -1,4 +1,7 @@ -package com.fsck.k9.helper; +package com.fsck.k9.message.html; + + +import com.fsck.k9.message.html.HtmlSanitizer; public class HtmlSanitizerHelper { diff --git a/k9mail/src/test/java/com/fsck/k9/helper/HtmlSanitizerTest.java b/k9mail/src/test/java/com/fsck/k9/message/html/HtmlSanitizerTest.java similarity index 99% rename from k9mail/src/test/java/com/fsck/k9/helper/HtmlSanitizerTest.java rename to k9mail/src/test/java/com/fsck/k9/message/html/HtmlSanitizerTest.java index 69f1717bf..ff4e9a1a3 100644 --- a/k9mail/src/test/java/com/fsck/k9/helper/HtmlSanitizerTest.java +++ b/k9mail/src/test/java/com/fsck/k9/message/html/HtmlSanitizerTest.java @@ -1,4 +1,4 @@ -package com.fsck.k9.helper; +package com.fsck.k9.message.html; import org.junit.Before; diff --git a/k9mail/src/test/java/com/fsck/k9/message/signature/HtmlSignatureRemoverTest.java b/k9mail/src/test/java/com/fsck/k9/message/signature/HtmlSignatureRemoverTest.java new file mode 100644 index 000000000..b7d0c361c --- /dev/null +++ b/k9mail/src/test/java/com/fsck/k9/message/signature/HtmlSignatureRemoverTest.java @@ -0,0 +1,148 @@ +package com.fsck.k9.message.signature; + + +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import static com.fsck.k9.message.html.HtmlHelper.extractText; +import static org.junit.Assert.assertEquals; + + +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE, sdk = 21) +public class HtmlSignatureRemoverTest { + @Test + public void shouldStripSignatureFromK9StyleHtml() throws Exception { + String html = "This is the body text" + + "
" + + "--
" + + "Sent from my Android device with K-9 Mail. Please excuse my brevity."; + + String withoutSignature = HtmlSignatureRemover.stripSignature(html); + + assertEquals("This is the body text", extractText(withoutSignature)); + } + + @Ignore + @Test + public void shouldStripSignatureFromThunderbirdStyleHtml() throws Exception { + String html = "\r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + "

This is the body text
\r\n" + + "

\r\n" + + " --
\r\n" + + "
Sent from my Android device with K-9 Mail." + + " Please excuse my brevity.
\r\n" + + " \r\n" + + ""; + + String withoutSignature = HtmlSignatureRemover.stripSignature(html); + + assertEquals("This is the body text", extractText(withoutSignature)); + } + + @Test + public void shouldStripSignatureBeforeBlockquoteTag() throws Exception { + String html = "" + + "
" + + "This is the body text" + + "
" + + "--
" + + "
" + + "Sent from my Android device with K-9 Mail. Please excuse my brevity." + + "
" + + "
" + + ""; + + String withoutSignature = HtmlSignatureRemover.stripSignature(html); + + assertEquals("" + + "
This is the body text
" + + "", + withoutSignature); + } + + @Test + public void shouldNotStripSignatureInsideBlockquoteTags() throws Exception { + String html = "" + + "
" + + "This is some quoted text" + + "
" + + "--
" + + "Inner signature" + + "
" + + "
" + + "This is the body text" + + "
" + + ""; + + String withoutSignature = HtmlSignatureRemover.stripSignature(html); + + assertEquals("" + + "
" + + "This is some quoted text" + + "
" + + "--
" + + "Inner signature" + + "
" + + "
This is the body text
" + + "", + withoutSignature); + } + + @Test + public void shouldStripSignatureBetweenBlockquoteTags() throws Exception { + String html = "" + + "
" + + "Some quote" + + "
" + + "
" + + "This is the body text" + + "
" + + "--
" + + "
" + + "Sent from my Android device with K-9 Mail. Please excuse my brevity." + + "
" + + "
" + + "--
" + + "Signature inside signature" + + "
" + + ""; + + String withoutSignature = HtmlSignatureRemover.stripSignature(html); + + assertEquals("" + + "
Some quote
" + + "
This is the body text
" + + "", + withoutSignature); + } + + @Test + public void shouldStripSignatureAfterLastBlockquoteTags() throws Exception { + String html = "" + + "This is the body text" + + "
" + + "
" + + "Some quote" + + "
" + + "
" + + "--
" + + "Sent from my Android device with K-9 Mail. Please excuse my brevity." + + ""; + + String withoutSignature = HtmlSignatureRemover.stripSignature(html); + + assertEquals("" + + "This is the body text
" + + "
Some quote
" + + "", + withoutSignature); + } +} diff --git a/k9mail/src/test/java/com/fsck/k9/message/signature/TextSignatureRemoverTest.java b/k9mail/src/test/java/com/fsck/k9/message/signature/TextSignatureRemoverTest.java new file mode 100644 index 000000000..e9594c2ec --- /dev/null +++ b/k9mail/src/test/java/com/fsck/k9/message/signature/TextSignatureRemoverTest.java @@ -0,0 +1,22 @@ +package com.fsck.k9.message.signature; + + +import com.fsck.k9.message.signature.TextSignatureRemover; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + + +public class TextSignatureRemoverTest { + @Test + public void shouldStripSignature() throws Exception { + String text = "This is the body text\r\n" + + "\r\n" + + "-- \r\n" + + "Sent from my Android device with K-9 Mail. Please excuse my brevity."; + + String withoutSignature = TextSignatureRemover.stripSignature(text); + + assertEquals("This is the body text\r\n\r\n", withoutSignature); + } +}