Merge pull request #2159 from k9mail/jsoup_preparation
Code refactoring in preparation of HtmlCleaner replacement
This commit is contained in:
commit
2d84b41e5c
31 changed files with 519 additions and 265 deletions
|
@ -39,6 +39,7 @@ dependencies {
|
||||||
testCompile "org.robolectric:robolectric:${robolectricVersion}"
|
testCompile "org.robolectric:robolectric:${robolectricVersion}"
|
||||||
testCompile "junit:junit:${junitVersion}"
|
testCompile "junit:junit:${junitVersion}"
|
||||||
testCompile "org.mockito:mockito-core:${mockitoVersion}"
|
testCompile "org.mockito:mockito-core:${mockitoVersion}"
|
||||||
|
testCompile 'org.jsoup:jsoup:1.10.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
|
@ -6,13 +6,12 @@ import android.os.Bundle;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.activity.Accounts;
|
import com.fsck.k9.activity.Accounts;
|
||||||
import com.fsck.k9.activity.K9Activity;
|
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.
|
* Displays a welcome message when no accounts have been created yet.
|
||||||
|
|
|
@ -15,8 +15,8 @@ import android.util.Log;
|
||||||
import com.fsck.k9.Globals;
|
import com.fsck.k9.Globals;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.helper.HtmlConverter;
|
import com.fsck.k9.message.html.HtmlConverter;
|
||||||
import com.fsck.k9.helper.HtmlSanitizer;
|
import com.fsck.k9.message.html.HtmlSanitizer;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mail.Message;
|
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.
|
* Use the contents of a {@link com.fsck.k9.mail.internet.Viewable} to create the HTML to be displayed.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 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.
|
* to HTML if necessary.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.fsck.k9.Identity;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.activity.MessageReference;
|
import com.fsck.k9.activity.MessageReference;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
import com.fsck.k9.message.quote.InsertableHtmlContent;
|
||||||
|
|
||||||
|
|
||||||
public class IdentityHeaderBuilder {
|
public class IdentityHeaderBuilder {
|
||||||
|
|
|
@ -33,6 +33,7 @@ import com.fsck.k9.mail.internet.MimeMultipart;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
import com.fsck.k9.mailstore.TempFileBody;
|
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.codec.EncoderUtil;
|
||||||
import org.apache.james.mime4j.util.MimeUtil;
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@ import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
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.Body;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
import com.fsck.k9.message.quote.InsertableHtmlContent;
|
||||||
|
|
||||||
|
|
||||||
class TextBodyBuilder {
|
class TextBodyBuilder {
|
||||||
|
|
|
@ -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 "";
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ package com.fsck.k9.message.extractors;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
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.Message;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import com.fsck.k9.mail.internet.MessageExtractor;
|
import com.fsck.k9.mail.internet.MessageExtractor;
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.fsck.k9.message.extractors;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
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.Part;
|
||||||
import com.fsck.k9.mail.internet.MessageExtractor;
|
import com.fsck.k9.mail.internet.MessageExtractor;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.fsck.k9.helper;
|
package com.fsck.k9.message.html;
|
||||||
|
|
||||||
import android.text.*;
|
import android.text.*;
|
||||||
import android.text.Html.TagHandler;
|
import android.text.Html.TagHandler;
|
|
@ -1,4 +1,4 @@
|
||||||
package com.fsck.k9.helper;
|
package com.fsck.k9.message.html;
|
||||||
|
|
||||||
|
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
|
@ -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.Matcher;
|
||||||
import java.util.regex.Pattern;
|
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;
|
||||||
import com.fsck.k9.mail.Message.RecipientType;
|
import com.fsck.k9.mail.Message.RecipientType;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.message.html.HtmlConverter;
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
public class QuotedMessageHelper {
|
public class HtmlQuoteCreator {
|
||||||
// amount of extra buffer to allocate to accommodate quoting headers or prefixes
|
|
||||||
private static final int QUOTE_BUFFER_LENGTH = 512;
|
|
||||||
|
|
||||||
// Regular expressions to look for various HTML tags. This is no HTML::Parser, but hopefully it's good enough for
|
// Regular expressions to look for various HTML tags. This is no HTML::Parser, but hopefully it's good enough for
|
||||||
// our purposes.
|
// our purposes.
|
||||||
private static final Pattern FIND_INSERTION_POINT_HTML = Pattern.compile("(?si:.*?(<html(?:>|\\s+[^>]*>)).*)");
|
private static final Pattern FIND_INSERTION_POINT_HTML = Pattern.compile("(?si:.*?(<html(?:>|\\s+[^>]*>)).*)");
|
||||||
|
@ -41,13 +25,6 @@ public class QuotedMessageHelper {
|
||||||
private static final Pattern FIND_INSERTION_POINT_BODY = Pattern.compile("(?si:.*?(<body(?:>|\\s+[^>]*>)).*)");
|
private static final Pattern FIND_INSERTION_POINT_BODY = Pattern.compile("(?si:.*?(<body(?:>|\\s+[^>]*>)).*)");
|
||||||
private static final Pattern FIND_INSERTION_POINT_HTML_END = Pattern.compile("(?si:.*(</html>).*?)");
|
private static final Pattern FIND_INSERTION_POINT_HTML_END = Pattern.compile("(?si:.*(</html>).*?)");
|
||||||
private static final Pattern FIND_INSERTION_POINT_BODY_END = Pattern.compile("(?si:.*(</body>).*?)");
|
private static final Pattern FIND_INSERTION_POINT_BODY_END = Pattern.compile("(?si:.*(</body>).*?)");
|
||||||
|
|
||||||
// Regexes to check for signature.
|
|
||||||
private static final Pattern DASH_SIGNATURE_HTML = Pattern.compile("(<br( /)?>|\r?\n)-- <br( /)?>", Pattern.CASE_INSENSITIVE);
|
|
||||||
private static final Pattern BLOCKQUOTE_START = Pattern.compile("<blockquote", Pattern.CASE_INSENSITIVE);
|
|
||||||
private static final Pattern BLOCKQUOTE_END = Pattern.compile("</blockquote>", 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
|
// 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.
|
// we can locate the *end* of that tag.
|
||||||
private static final int FIND_INSERTION_POINT_FIRST_GROUP = 1;
|
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.
|
// 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 FIND_INSERTION_POINT_START_OF_STRING = 0;
|
||||||
|
|
||||||
private static final int REPLY_WRAP_LINE_WIDTH = 72;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add quoting markup to a HTML message.
|
* Add quoting markup to a HTML message.
|
||||||
* @param originalMessage Metadata for message being quoted.
|
* @param originalMessage Metadata for message being quoted.
|
||||||
|
@ -74,10 +49,10 @@ public class QuotedMessageHelper {
|
||||||
String messageBody, QuoteStyle quoteStyle) throws MessagingException {
|
String messageBody, QuoteStyle quoteStyle) throws MessagingException {
|
||||||
InsertableHtmlContent insertable = findInsertionPoints(messageBody);
|
InsertableHtmlContent insertable = findInsertionPoints(messageBody);
|
||||||
|
|
||||||
String sentDate = getSentDateText(resources, originalMessage);
|
String sentDate = QuoteHelper.getSentDateText(resources, originalMessage);
|
||||||
String fromAddress = Address.toString(originalMessage.getFrom());
|
String fromAddress = Address.toString(originalMessage.getFrom());
|
||||||
if (quoteStyle == QuoteStyle.PREFIX) {
|
if (quoteStyle == QuoteStyle.PREFIX) {
|
||||||
StringBuilder header = new StringBuilder(QUOTE_BUFFER_LENGTH);
|
StringBuilder header = new StringBuilder(QuoteHelper.QUOTE_BUFFER_LENGTH);
|
||||||
header.append("<div class=\"gmail_quote\">");
|
header.append("<div class=\"gmail_quote\">");
|
||||||
if (sentDate.length() != 0) {
|
if (sentDate.length() != 0) {
|
||||||
header.append(HtmlConverter.textToHtmlFragment(String.format(
|
header.append(HtmlConverter.textToHtmlFragment(String.format(
|
||||||
|
@ -134,25 +109,6 @@ public class QuotedMessageHelper {
|
||||||
return insertable;
|
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 "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Find the start and end positions of the HTML in the string. This should be the very top
|
* <p>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
|
* and bottom of the displayable message. It returns a {@link InsertableHtmlContent}, which
|
||||||
|
@ -265,189 +221,4 @@ public class QuotedMessageHelper {
|
||||||
|
|
||||||
return insertable;
|
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<Integer> start = new ArrayList<>();
|
|
||||||
List<Integer> 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() + " <blockquote> tags, but " +
|
|
||||||
end.size() + " </blockquote> 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 <blockquote>.
|
|
||||||
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 </blockquote>.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.fsck.k9.message;
|
package com.fsck.k9.message.quote;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
|
@ -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 "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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("(<br( /)?>|\r?\n)-- <br( /)?>", Pattern.CASE_INSENSITIVE);
|
||||||
|
private static final Pattern BLOCKQUOTE_START = Pattern.compile("<blockquote", Pattern.CASE_INSENSITIVE);
|
||||||
|
private static final Pattern BLOCKQUOTE_END = Pattern.compile("</blockquote>", 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<Integer> start = new ArrayList<>();
|
||||||
|
List<Integer> 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() + " <blockquote> tags, but " +
|
||||||
|
end.size() + " </blockquote> 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 <blockquote>.
|
||||||
|
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 </blockquote>.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ import android.widget.ImageButton;
|
||||||
import com.fsck.k9.FontSizes;
|
import com.fsck.k9.FontSizes;
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.activity.MessageCompose;
|
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.mailstore.AttachmentResolver;
|
||||||
import com.fsck.k9.message.QuotedTextMode;
|
import com.fsck.k9.message.QuotedTextMode;
|
||||||
import com.fsck.k9.message.SimpleMessageFormat;
|
import com.fsck.k9.message.SimpleMessageFormat;
|
||||||
|
|
|
@ -13,8 +13,11 @@ import com.fsck.k9.Account.QuoteStyle;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.activity.MessageCompose;
|
import com.fsck.k9.activity.MessageCompose;
|
||||||
import com.fsck.k9.activity.MessageCompose.Action;
|
import com.fsck.k9.activity.MessageCompose.Action;
|
||||||
import com.fsck.k9.helper.HtmlConverter;
|
import com.fsck.k9.message.extractors.BodyTextExtractor;
|
||||||
import com.fsck.k9.helper.QuotedMessageHelper;
|
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.MessagingException;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import com.fsck.k9.mail.internet.MessageExtractor;
|
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.AttachmentResolver;
|
||||||
import com.fsck.k9.mailstore.MessageViewInfo;
|
import com.fsck.k9.mailstore.MessageViewInfo;
|
||||||
import com.fsck.k9.message.IdentityField;
|
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.MessageBuilder;
|
||||||
import com.fsck.k9.message.QuotedTextMode;
|
import com.fsck.k9.message.QuotedTextMode;
|
||||||
import com.fsck.k9.message.SimpleMessageFormat;
|
import com.fsck.k9.message.SimpleMessageFormat;
|
||||||
|
import com.fsck.k9.message.signature.TextSignatureRemover;
|
||||||
|
|
||||||
|
|
||||||
public class QuotedMessagePresenter {
|
public class QuotedMessagePresenter {
|
||||||
|
@ -98,17 +102,17 @@ public class QuotedMessagePresenter {
|
||||||
|
|
||||||
// Handle the original message in the reply
|
// 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.
|
// 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) {
|
if (quotedTextFormat == SimpleMessageFormat.HTML) {
|
||||||
// Strip signature.
|
// Strip signature.
|
||||||
// closing tags such as </div>, </span>, </table>, </pre> will be cut off.
|
// closing tags such as </div>, </span>, </table>, </pre> will be cut off.
|
||||||
if (account.isStripSignature() && (action == Action.REPLY || action == Action.REPLY_ALL)) {
|
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.
|
// Add the HTML reply header to the top of the content.
|
||||||
quotedHtmlContent = QuotedMessageHelper.quoteOriginalHtmlMessage(
|
quotedHtmlContent = HtmlQuoteCreator.quoteOriginalHtmlMessage(
|
||||||
resources, messageViewInfo.message, content, quoteStyle);
|
resources, messageViewInfo.message, content, quoteStyle);
|
||||||
|
|
||||||
// Load the message with the reply header. TODO replace with MessageViewInfo data
|
// Load the message with the reply header. TODO replace with MessageViewInfo data
|
||||||
|
@ -116,16 +120,16 @@ public class QuotedMessagePresenter {
|
||||||
AttachmentResolver.createFromPart(messageViewInfo.rootPart));
|
AttachmentResolver.createFromPart(messageViewInfo.rootPart));
|
||||||
|
|
||||||
// TODO: Also strip the signature from the text/plain part
|
// TODO: Also strip the signature from the text/plain part
|
||||||
view.setQuotedText(QuotedMessageHelper.quoteOriginalTextMessage(resources, messageViewInfo.message,
|
view.setQuotedText(TextQuoteCreator.quoteOriginalTextMessage(resources, messageViewInfo.message,
|
||||||
QuotedMessageHelper.getBodyTextFromMessage(messageViewInfo.rootPart, SimpleMessageFormat.TEXT),
|
BodyTextExtractor.getBodyTextFromMessage(messageViewInfo.rootPart, SimpleMessageFormat.TEXT),
|
||||||
quoteStyle, account.getQuotePrefix()));
|
quoteStyle, account.getQuotePrefix()));
|
||||||
|
|
||||||
} else if (quotedTextFormat == SimpleMessageFormat.TEXT) {
|
} else if (quotedTextFormat == SimpleMessageFormat.TEXT) {
|
||||||
if (account.isStripSignature() && (action == Action.REPLY || action == Action.REPLY_ALL)) {
|
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()));
|
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
|
// composition window. If that's the case, try and convert it to text to
|
||||||
// match the behavior in text mode.
|
// match the behavior in text mode.
|
||||||
view.setMessageContentCharacters(
|
view.setMessageContentCharacters(
|
||||||
QuotedMessageHelper.getBodyTextFromMessage(messageViewInfo.message, SimpleMessageFormat.TEXT));
|
BodyTextExtractor.getBodyTextFromMessage(messageViewInfo.message, SimpleMessageFormat.TEXT));
|
||||||
forcePlainText = true;
|
forcePlainText = true;
|
||||||
|
|
||||||
showOrHideQuotedText(quotedMode);
|
showOrHideQuotedText(quotedMode);
|
||||||
|
|
|
@ -30,7 +30,7 @@ import android.widget.Toast;
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.helper.ClipboardManager;
|
import com.fsck.k9.helper.ClipboardManager;
|
||||||
import com.fsck.k9.helper.Contacts;
|
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.helper.Utility;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mailstore.AttachmentResolver;
|
import com.fsck.k9.mailstore.AttachmentResolver;
|
||||||
|
|
|
@ -17,7 +17,6 @@ import android.widget.Toast;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.K9.Theme;
|
import com.fsck.k9.K9.Theme;
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.helper.HtmlConverter;
|
|
||||||
import com.fsck.k9.mailstore.AttachmentResolver;
|
import com.fsck.k9.mailstore.AttachmentResolver;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ import android.app.Application;
|
||||||
|
|
||||||
import com.fsck.k9.GlobalsHelper;
|
import com.fsck.k9.GlobalsHelper;
|
||||||
import com.fsck.k9.activity.K9ActivityCommon;
|
import com.fsck.k9.activity.K9ActivityCommon;
|
||||||
import com.fsck.k9.helper.HtmlSanitizer;
|
import com.fsck.k9.message.html.HtmlSanitizer;
|
||||||
import com.fsck.k9.helper.HtmlSanitizerHelper;
|
import com.fsck.k9.message.html.HtmlSanitizerHelper;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Message.RecipientType;
|
import com.fsck.k9.mail.Message.RecipientType;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
|
|
@ -26,6 +26,7 @@ import com.fsck.k9.mail.internet.MessageIdGenerator;
|
||||||
import com.fsck.k9.mail.internet.MimeMessage;
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
import com.fsck.k9.mail.internet.MimeMultipart;
|
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||||
import com.fsck.k9.message.MessageBuilder.Callback;
|
import com.fsck.k9.message.MessageBuilder.Callback;
|
||||||
|
import com.fsck.k9.message.quote.InsertableHtmlContent;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
|
@ -33,6 +33,7 @@ import com.fsck.k9.mail.internet.MimeMultipart;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
import com.fsck.k9.message.MessageBuilder.Callback;
|
import com.fsck.k9.message.MessageBuilder.Callback;
|
||||||
|
import com.fsck.k9.message.quote.InsertableHtmlContent;
|
||||||
import com.fsck.k9.view.RecipientSelectView.Recipient;
|
import com.fsck.k9.view.RecipientSelectView.Recipient;
|
||||||
|
|
||||||
import org.apache.commons.io.Charsets;
|
import org.apache.commons.io.Charsets;
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.fsck.k9.message;
|
||||||
import com.fsck.k9.Account.QuoteStyle;
|
import com.fsck.k9.Account.QuoteStyle;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
|
import com.fsck.k9.message.quote.InsertableHtmlContent;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.experimental.theories.DataPoints;
|
import org.junit.experimental.theories.DataPoints;
|
||||||
import org.junit.experimental.theories.Theories;
|
import org.junit.experimental.theories.Theories;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.fsck.k9.helper;
|
package com.fsck.k9.message.html;
|
||||||
|
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
|
@ -6,6 +6,7 @@ import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.fsck.k9.message.html.HtmlConverter;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
public class HtmlSanitizerHelper {
|
|
@ -1,4 +1,4 @@
|
||||||
package com.fsck.k9.helper;
|
package com.fsck.k9.message.html;
|
||||||
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
|
@ -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" +
|
||||||
|
"<br>" +
|
||||||
|
"-- <br>" +
|
||||||
|
"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 = "<html>\r\n" +
|
||||||
|
" <head>\r\n" +
|
||||||
|
" <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\r\n" +
|
||||||
|
" </head>\r\n" +
|
||||||
|
" <body bgcolor=\"#FFFFFF\" text=\"#000000\">\r\n" +
|
||||||
|
" <p>This is the body text<br>\r\n" +
|
||||||
|
" </p>\r\n" +
|
||||||
|
" -- <br>\r\n" +
|
||||||
|
" <div class=\"moz-signature\">Sent from my Android device with K-9 Mail." +
|
||||||
|
" Please excuse my brevity.</div>\r\n" +
|
||||||
|
" </body>\r\n" +
|
||||||
|
"</html>";
|
||||||
|
|
||||||
|
String withoutSignature = HtmlSignatureRemover.stripSignature(html);
|
||||||
|
|
||||||
|
assertEquals("This is the body text", extractText(withoutSignature));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldStripSignatureBeforeBlockquoteTag() throws Exception {
|
||||||
|
String html = "<html><head></head><body>" +
|
||||||
|
"<div>" +
|
||||||
|
"This is the body text" +
|
||||||
|
"<br>" +
|
||||||
|
"-- <br>" +
|
||||||
|
"<blockquote>" +
|
||||||
|
"Sent from my Android device with K-9 Mail. Please excuse my brevity." +
|
||||||
|
"</blockquote>" +
|
||||||
|
"</div>" +
|
||||||
|
"</body></html>";
|
||||||
|
|
||||||
|
String withoutSignature = HtmlSignatureRemover.stripSignature(html);
|
||||||
|
|
||||||
|
assertEquals("<html><head></head><body>" +
|
||||||
|
"<div>This is the body text</div>" +
|
||||||
|
"</body></html>",
|
||||||
|
withoutSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldNotStripSignatureInsideBlockquoteTags() throws Exception {
|
||||||
|
String html = "<html><head></head><body>" +
|
||||||
|
"<blockquote>" +
|
||||||
|
"This is some quoted text" +
|
||||||
|
"<br>" +
|
||||||
|
"-- <br>" +
|
||||||
|
"Inner signature" +
|
||||||
|
"</blockquote>" +
|
||||||
|
"<div>" +
|
||||||
|
"This is the body text" +
|
||||||
|
"</div>" +
|
||||||
|
"</body></html>";
|
||||||
|
|
||||||
|
String withoutSignature = HtmlSignatureRemover.stripSignature(html);
|
||||||
|
|
||||||
|
assertEquals("<html><head></head><body>" +
|
||||||
|
"<blockquote>" +
|
||||||
|
"This is some quoted text" +
|
||||||
|
"<br />" +
|
||||||
|
"-- <br />" +
|
||||||
|
"Inner signature" +
|
||||||
|
"</blockquote>" +
|
||||||
|
"<div>This is the body text</div>" +
|
||||||
|
"</body></html>",
|
||||||
|
withoutSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldStripSignatureBetweenBlockquoteTags() throws Exception {
|
||||||
|
String html = "<html><head></head><body>" +
|
||||||
|
"<blockquote>" +
|
||||||
|
"Some quote" +
|
||||||
|
"</blockquote>" +
|
||||||
|
"<div>" +
|
||||||
|
"This is the body text" +
|
||||||
|
"<br>" +
|
||||||
|
"-- <br>" +
|
||||||
|
"<blockquote>" +
|
||||||
|
"Sent from my Android device with K-9 Mail. Please excuse my brevity." +
|
||||||
|
"</blockquote>" +
|
||||||
|
"<br>" +
|
||||||
|
"-- <br>" +
|
||||||
|
"Signature inside signature" +
|
||||||
|
"</div>" +
|
||||||
|
"</body></html>";
|
||||||
|
|
||||||
|
String withoutSignature = HtmlSignatureRemover.stripSignature(html);
|
||||||
|
|
||||||
|
assertEquals("<html><head></head><body>" +
|
||||||
|
"<blockquote>Some quote</blockquote>" +
|
||||||
|
"<div>This is the body text</div>" +
|
||||||
|
"</body></html>",
|
||||||
|
withoutSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldStripSignatureAfterLastBlockquoteTags() throws Exception {
|
||||||
|
String html = "<html><head></head><body>" +
|
||||||
|
"This is the body text" +
|
||||||
|
"<br>" +
|
||||||
|
"<blockquote>" +
|
||||||
|
"Some quote" +
|
||||||
|
"</blockquote>" +
|
||||||
|
"<br>" +
|
||||||
|
"-- <br>" +
|
||||||
|
"Sent from my Android device with K-9 Mail. Please excuse my brevity." +
|
||||||
|
"</body></html>";
|
||||||
|
|
||||||
|
String withoutSignature = HtmlSignatureRemover.stripSignature(html);
|
||||||
|
|
||||||
|
assertEquals("<html><head></head><body>" +
|
||||||
|
"This is the body text<br />" +
|
||||||
|
"<blockquote>Some quote</blockquote>" +
|
||||||
|
"</body></html>",
|
||||||
|
withoutSignature);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue