mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-27 11:42:16 -05:00
MimeUtility / Message refactor
* break MimeUtility class into manageable pieces (MessageExtractor/CharsetSupport) * move HTML related code out of the mail package
This commit is contained in:
parent
476cb1d4ce
commit
7d6e6b8abe
@ -1,4 +1,4 @@
|
|||||||
package com.fsck.k9.mail.internet;
|
package com.fsck.k9.activity;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ import java.io.Serializable;
|
|||||||
*
|
*
|
||||||
* TODO: This container should also have a text part, along with its insertion point. Or maybe a generic InsertableContent and maintain one each for Html and Text?
|
* TODO: This container should also have a text part, along with its insertion point. Or maybe a generic InsertableContent and maintain one each for Html and Text?
|
||||||
*/
|
*/
|
||||||
public class InsertableHtmlContent implements Serializable {
|
class InsertableHtmlContent implements Serializable {
|
||||||
private static final long serialVersionUID = 2397327034L;
|
private static final long serialVersionUID = 2397327034L;
|
||||||
// Default to a headerInsertionPoint at the beginning of the message.
|
// Default to a headerInsertionPoint at the beginning of the message.
|
||||||
private int headerInsertionPoint = 0;
|
private int headerInsertionPoint = 0;
|
@ -90,13 +90,12 @@ import com.fsck.k9.fragment.ProgressDialogFragment;
|
|||||||
import com.fsck.k9.helper.ContactItem;
|
import com.fsck.k9.helper.ContactItem;
|
||||||
import com.fsck.k9.helper.Contacts;
|
import com.fsck.k9.helper.Contacts;
|
||||||
import com.fsck.k9.mail.filter.Base64;
|
import com.fsck.k9.mail.filter.Base64;
|
||||||
import com.fsck.k9.mail.internet.HtmlConverter;
|
import com.fsck.k9.helper.HtmlConverter;
|
||||||
import com.fsck.k9.helper.IdentityHelper;
|
import com.fsck.k9.helper.IdentityHelper;
|
||||||
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.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mail.internet.InsertableHtmlContent;
|
|
||||||
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;
|
||||||
@ -108,7 +107,6 @@ 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.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.mail.internet.TextBodyBuilder;
|
|
||||||
import com.fsck.k9.mailstore.LocalAttachmentBody;
|
import com.fsck.k9.mailstore.LocalAttachmentBody;
|
||||||
import com.fsck.k9.mailstore.LocalMessage;
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
import com.fsck.k9.mailstore.TempFileBody;
|
import com.fsck.k9.mailstore.TempFileBody;
|
||||||
@ -2957,10 +2955,10 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
|
|
||||||
|
|
||||||
if (messageFormat == MessageFormat.HTML) {
|
if (messageFormat == MessageFormat.HTML) {
|
||||||
Part part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
Part part = message.findFirstPartByMimeType("text/html");
|
||||||
if (part != null) { // Shouldn't happen if we were the one who saved it.
|
if (part != null) { // Shouldn't happen if we were the one who saved it.
|
||||||
mQuotedTextFormat = SimpleMessageFormat.HTML;
|
mQuotedTextFormat = SimpleMessageFormat.HTML;
|
||||||
String text = MimeUtility.getTextFromPart(part);
|
String text = part.getText();
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + ".");
|
Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + ".");
|
||||||
}
|
}
|
||||||
@ -3023,9 +3021,9 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
*/
|
*/
|
||||||
private void processSourceMessageText(Message message, Integer bodyOffset, Integer bodyLength,
|
private void processSourceMessageText(Message message, Integer bodyOffset, Integer bodyLength,
|
||||||
boolean viewMessageContent) throws MessagingException {
|
boolean viewMessageContent) throws MessagingException {
|
||||||
Part textPart = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
Part textPart = message.findFirstPartByMimeType("text/plain");
|
||||||
if (textPart != null) {
|
if (textPart != null) {
|
||||||
String text = MimeUtility.getTextFromPart(textPart);
|
String text = textPart.getText();
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + ".");
|
Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + ".");
|
||||||
}
|
}
|
||||||
@ -3093,7 +3091,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
// Figure out which message format to use for the quoted text by looking if the source
|
// Figure out which message format to use for the quoted text by looking if the source
|
||||||
// message contains a text/html part. If it does, we use that.
|
// message contains a text/html part. If it does, we use that.
|
||||||
mQuotedTextFormat =
|
mQuotedTextFormat =
|
||||||
(MimeUtility.findFirstPartByMimeType(mSourceMessage, "text/html") == null) ?
|
(mSourceMessage.findFirstPartByMimeType("text/html") == null) ?
|
||||||
SimpleMessageFormat.TEXT : SimpleMessageFormat.HTML;
|
SimpleMessageFormat.TEXT : SimpleMessageFormat.HTML;
|
||||||
} else {
|
} else {
|
||||||
mQuotedTextFormat = SimpleMessageFormat.HTML;
|
mQuotedTextFormat = SimpleMessageFormat.HTML;
|
||||||
@ -3223,37 +3221,37 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
Part part;
|
Part part;
|
||||||
if (format == SimpleMessageFormat.HTML) {
|
if (format == SimpleMessageFormat.HTML) {
|
||||||
// HTML takes precedence, then text.
|
// HTML takes precedence, then text.
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
part = message.findFirstPartByMimeType("text/html");
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, HTML found.");
|
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, HTML found.");
|
||||||
}
|
}
|
||||||
return MimeUtility.getTextFromPart(part);
|
return part.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
part = message.findFirstPartByMimeType("text/plain");
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found.");
|
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found.");
|
||||||
}
|
}
|
||||||
return HtmlConverter.textToHtml(MimeUtility.getTextFromPart(part));
|
return HtmlConverter.textToHtml(part.getText());
|
||||||
}
|
}
|
||||||
} else if (format == SimpleMessageFormat.TEXT) {
|
} else if (format == SimpleMessageFormat.TEXT) {
|
||||||
// Text takes precedence, then html.
|
// Text takes precedence, then html.
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
part = message.findFirstPartByMimeType("text/plain");
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, text found.");
|
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, text found.");
|
||||||
}
|
}
|
||||||
return MimeUtility.getTextFromPart(part);
|
return part.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
part = message.findFirstPartByMimeType("text/html");
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, HTML found.");
|
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, HTML found.");
|
||||||
}
|
}
|
||||||
return HtmlConverter.htmlToText(MimeUtility.getTextFromPart(part));
|
return HtmlConverter.htmlToText(part.getText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package com.fsck.k9.mail.internet;
|
package com.fsck.k9.activity;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
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.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
|
import com.fsck.k9.helper.HtmlConverter;
|
||||||
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
public class TextBodyBuilder {
|
class TextBodyBuilder {
|
||||||
private boolean mIncludeQuotedText = true;
|
private boolean mIncludeQuotedText = true;
|
||||||
private boolean mReplyAfterQuote = false;
|
private boolean mReplyAfterQuote = false;
|
||||||
private boolean mSignatureBeforeQuotedText = false;
|
private boolean mSignatureBeforeQuotedText = false;
|
@ -12,7 +12,7 @@ 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.mail.internet.HtmlConverter;
|
import com.fsck.k9.helper.HtmlConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a welcome message when no accounts have been created yet.
|
* Displays a welcome message when no accounts have been created yet.
|
||||||
|
@ -1744,7 +1744,7 @@ public class MessagingController implements Runnable {
|
|||||||
* right now, attachments will be left for later.
|
* right now, attachments will be left for later.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Set<Part> viewables = MimeUtility.collectTextParts(message);
|
Set<Part> viewables = message.collectTextParts();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now download the parts we're interested in storing.
|
* Now download the parts we're interested in storing.
|
||||||
@ -3197,7 +3197,7 @@ public class MessagingController implements Runnable {
|
|||||||
try {
|
try {
|
||||||
LocalStore localStore = account.getLocalStore();
|
LocalStore localStore = account.getLocalStore();
|
||||||
|
|
||||||
List<Part> attachments = MimeUtility.collectAttachments(message);
|
List<Part> attachments = message.collectAttachments();
|
||||||
for (Part attachment : attachments) {
|
for (Part attachment : attachments) {
|
||||||
attachment.setBody(null);
|
attachment.setBody(null);
|
||||||
}
|
}
|
||||||
@ -4244,13 +4244,12 @@ public class MessagingController implements Runnable {
|
|||||||
try {
|
try {
|
||||||
Intent msg = new Intent(Intent.ACTION_SEND);
|
Intent msg = new Intent(Intent.ACTION_SEND);
|
||||||
String quotedText = null;
|
String quotedText = null;
|
||||||
Part part = MimeUtility.findFirstPartByMimeType(message,
|
Part part = message.findFirstPartByMimeType("text/plain");
|
||||||
"text/plain");
|
|
||||||
if (part == null) {
|
if (part == null) {
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
part = message.findFirstPartByMimeType("text/html");
|
||||||
}
|
}
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
quotedText = MimeUtility.getTextFromPart(part);
|
quotedText = part.getText();
|
||||||
}
|
}
|
||||||
if (quotedText != null) {
|
if (quotedText != null) {
|
||||||
msg.putExtra(Intent.EXTRA_TEXT, quotedText);
|
msg.putExtra(Intent.EXTRA_TEXT, quotedText);
|
||||||
|
@ -32,12 +32,12 @@ public class CryptoHelper {
|
|||||||
public boolean isEncrypted(Message message) {
|
public boolean isEncrypted(Message message) {
|
||||||
String data = null;
|
String data = null;
|
||||||
try {
|
try {
|
||||||
Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
Part part = message.findFirstPartByMimeType("text/plain");
|
||||||
if (part == null) {
|
if (part == null) {
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
part = message.findFirstPartByMimeType("text/html");
|
||||||
}
|
}
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
data = MimeUtility.getTextFromPart(part);
|
data = part.getText();
|
||||||
}
|
}
|
||||||
} catch (MessagingException e) {
|
} catch (MessagingException e) {
|
||||||
// guess not...
|
// guess not...
|
||||||
@ -55,12 +55,12 @@ public class CryptoHelper {
|
|||||||
public boolean isSigned(Message message) {
|
public boolean isSigned(Message message) {
|
||||||
String data = null;
|
String data = null;
|
||||||
try {
|
try {
|
||||||
Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
Part part = message.findFirstPartByMimeType("text/plain");
|
||||||
if (part == null) {
|
if (part == null) {
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
part = message.findFirstPartByMimeType("text/html");
|
||||||
}
|
}
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
data = MimeUtility.getTextFromPart(part);
|
data = part.getText();
|
||||||
}
|
}
|
||||||
} catch (MessagingException e) {
|
} catch (MessagingException e) {
|
||||||
// guess not...
|
// guess not...
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.fsck.k9.mail.internet;
|
package com.fsck.k9.helper;
|
||||||
|
|
||||||
import android.text.*;
|
import android.text.*;
|
||||||
import android.text.Html.TagHandler;
|
import android.text.Html.TagHandler;
|
@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
package com.fsck.k9.mail;
|
package com.fsck.k9.mail;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.internet.MessageExtractor;
|
||||||
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
|
|
||||||
public abstract class BodyPart implements Part {
|
public abstract class BodyPart implements Part {
|
||||||
private Multipart mParent;
|
private Multipart mParent;
|
||||||
|
|
||||||
@ -15,5 +18,23 @@ public abstract class BodyPart implements Part {
|
|||||||
public abstract void setEncoding(String encoding) throws MessagingException;
|
public abstract void setEncoding(String encoding) throws MessagingException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract void setUsing7bitTransport() throws MessagingException;
|
public String getContentDisposition() {
|
||||||
|
try {
|
||||||
|
String disposition = getDisposition();
|
||||||
|
if (disposition != null) {
|
||||||
|
return MimeUtility.getHeaderParameter(disposition, null);
|
||||||
|
}
|
||||||
|
} catch (MessagingException e) { /* ignore */ }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return MessageExtractor.getTextFromPart(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Part findFirstPartByMimeType(String mimeType) throws MessagingException {
|
||||||
|
return MimeUtility.findFirstPartByMimeType(this, mimeType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
package com.fsck.k9.mail;
|
package com.fsck.k9.mail;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -12,6 +14,8 @@ import android.util.Log;
|
|||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.mail.filter.CountingOutputStream;
|
import com.fsck.k9.mail.filter.CountingOutputStream;
|
||||||
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
||||||
|
import com.fsck.k9.mail.internet.MessageExtractor;
|
||||||
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
|
|
||||||
|
|
||||||
public abstract class Message implements Part, CompositeBody {
|
public abstract class Message implements Part, CompositeBody {
|
||||||
@ -265,4 +269,69 @@ public abstract class Message implements Part, CompositeBody {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public abstract Message clone();
|
public abstract Message clone();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the {@code Content-Disposition} header.
|
||||||
|
* @return The value of the {@code Content-Disposition} header if available. {@code null}, otherwise.
|
||||||
|
*/
|
||||||
|
public String getContentDisposition() {
|
||||||
|
try {
|
||||||
|
String disposition = getDisposition();
|
||||||
|
if (disposition != null) {
|
||||||
|
return MimeUtility.getHeaderParameter(disposition, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MessagingException e) { /* ignore */ }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return MessageExtractor.getTextFromPart(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Part findFirstPartByMimeType(String mimeType) throws MessagingException {
|
||||||
|
return MimeUtility.findFirstPartByMimeType(this, mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect attachment parts of a message.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* The message to collect the attachment parts from.
|
||||||
|
*
|
||||||
|
* @return A list of parts regarded as attachments.
|
||||||
|
*
|
||||||
|
* @throws MessagingException
|
||||||
|
* In case of an error.
|
||||||
|
*/
|
||||||
|
public List<Part> collectAttachments() throws MessagingException {
|
||||||
|
try {
|
||||||
|
List<Part> attachments = new ArrayList<Part>();
|
||||||
|
MessageExtractor.getViewables(this, attachments);
|
||||||
|
return attachments;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MessagingException("Couldn't collect attachment parts", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect the viewable textual parts of a message.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* The message to extract the viewable parts from.
|
||||||
|
*
|
||||||
|
* @return A set of viewable parts of the message.
|
||||||
|
*
|
||||||
|
* @throws MessagingException
|
||||||
|
* In case of an error.
|
||||||
|
*/
|
||||||
|
public Set<Part> collectTextParts() throws MessagingException {
|
||||||
|
try {
|
||||||
|
return MessageExtractor.getTextParts(this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MessagingException("Couldn't extract viewable parts", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.apache.james.mime4j.util.MimeUtil;
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.CharsetSupport;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
public abstract class Multipart implements CompositeBody {
|
public abstract class Multipart implements CompositeBody {
|
||||||
@ -64,7 +64,7 @@ public abstract class Multipart implements CompositeBody {
|
|||||||
BodyPart part = mParts.get(0);
|
BodyPart part = mParts.get(0);
|
||||||
Body body = part.getBody();
|
Body body = part.getBody();
|
||||||
if (body instanceof TextBody) {
|
if (body instanceof TextBody) {
|
||||||
MimeUtility.setCharset(charset, part);
|
CharsetSupport.setCharset(charset, part);
|
||||||
((TextBody)body).setCharset(charset);
|
((TextBody)body).setCharset(charset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,29 +5,41 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public interface Part {
|
public interface Part {
|
||||||
public void addHeader(String name, String value) throws MessagingException;
|
void addHeader(String name, String value) throws MessagingException;
|
||||||
|
|
||||||
public void removeHeader(String name) throws MessagingException;
|
void removeHeader(String name) throws MessagingException;
|
||||||
|
|
||||||
public void setHeader(String name, String value) throws MessagingException;
|
void setHeader(String name, String value) throws MessagingException;
|
||||||
|
|
||||||
public Body getBody();
|
Body getBody();
|
||||||
|
|
||||||
public String getContentType() throws MessagingException;
|
String getContentType() throws MessagingException;
|
||||||
|
|
||||||
public String getDisposition() throws MessagingException;
|
String getDisposition() throws MessagingException;
|
||||||
|
|
||||||
public String getContentId() throws MessagingException;
|
String getContentDisposition();
|
||||||
|
|
||||||
public String[] getHeader(String name) throws MessagingException;
|
String getContentId() throws MessagingException;
|
||||||
|
|
||||||
public boolean isMimeType(String mimeType) throws MessagingException;
|
String[] getHeader(String name) throws MessagingException;
|
||||||
|
|
||||||
public String getMimeType() throws MessagingException;
|
boolean isMimeType(String mimeType) throws MessagingException;
|
||||||
|
|
||||||
public void setBody(Body body) throws MessagingException;
|
String getMimeType() throws MessagingException;
|
||||||
|
|
||||||
public void writeTo(OutputStream out) throws IOException, MessagingException;
|
void setBody(Body body) throws MessagingException;
|
||||||
|
|
||||||
|
void writeTo(OutputStream out) throws IOException, MessagingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the Part's body and returns a String based on any charset conversion that needed
|
||||||
|
* to be done. Note, this <b>does not</b> return a text representation of HTML.
|
||||||
|
* @return a String containing the converted text in the body, or null if there was no text
|
||||||
|
* or an error during conversion.
|
||||||
|
*/
|
||||||
|
String getText();
|
||||||
|
|
||||||
|
Part findFirstPartByMimeType(String mimeType) throws MessagingException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called just prior to transmission, once the type of transport is known to
|
* Called just prior to transmission, once the type of transport is known to
|
||||||
@ -41,5 +53,5 @@ public interface Part {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
//TODO perhaps it would be clearer to use a flag "force7bit" in writeTo
|
//TODO perhaps it would be clearer to use a flag "force7bit" in writeTo
|
||||||
public abstract void setUsing7bitTransport() throws MessagingException;
|
void setUsing7bitTransport() throws MessagingException;
|
||||||
}
|
}
|
||||||
|
1121
src/com/fsck/k9/mail/internet/CharsetSupport.java
Normal file
1121
src/com/fsck/k9/mail/internet/CharsetSupport.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,7 +35,7 @@ class DecoderUtil {
|
|||||||
|
|
||||||
Base64InputStream is = new Base64InputStream(new ByteArrayInputStream(bytes));
|
Base64InputStream is = new Base64InputStream(new ByteArrayInputStream(bytes));
|
||||||
try {
|
try {
|
||||||
return MimeUtility.readToString(is, charset);
|
return CharsetSupport.readToString(is, charset);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ class DecoderUtil {
|
|||||||
|
|
||||||
QuotedPrintableInputStream is = new QuotedPrintableInputStream(new ByteArrayInputStream(bytes));
|
QuotedPrintableInputStream is = new QuotedPrintableInputStream(new ByteArrayInputStream(bytes));
|
||||||
try {
|
try {
|
||||||
return MimeUtility.readToString(is, charset);
|
return CharsetSupport.readToString(is, charset);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -162,7 +162,7 @@ class DecoderUtil {
|
|||||||
|
|
||||||
String charset;
|
String charset;
|
||||||
try {
|
try {
|
||||||
charset = MimeUtility.fixupCharset(mimeCharset, message);
|
charset = CharsetSupport.fixupCharset(mimeCharset, message);
|
||||||
} catch (MessagingException e) {
|
} catch (MessagingException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ class EncoderUtil {
|
|||||||
if (charset == null)
|
if (charset == null)
|
||||||
charset = determineCharset(text);
|
charset = determineCharset(text);
|
||||||
|
|
||||||
String mimeCharset = MimeUtility.getExternalCharset(charset.name());
|
String mimeCharset = CharsetSupport.getExternalCharset(charset.name());
|
||||||
|
|
||||||
byte[] bytes = encode(text, charset);
|
byte[] bytes = encode(text, charset);
|
||||||
|
|
||||||
|
103
src/com/fsck/k9/mail/internet/JisSupport.java
Normal file
103
src/com/fsck/k9/mail/internet/JisSupport.java
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package com.fsck.k9.mail.internet;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.Address;
|
||||||
|
import com.fsck.k9.mail.Message;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.Part;
|
||||||
|
|
||||||
|
class JisSupport {
|
||||||
|
public static final String SHIFT_JIS = "shift_jis";
|
||||||
|
|
||||||
|
public static String getJisVariantFromMessage(Message message) throws MessagingException {
|
||||||
|
if (message == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// If a receiver is known to use a JIS variant, the sender transfers the message after converting the
|
||||||
|
// charset as a convention.
|
||||||
|
String variant = getJisVariantFromReceivedHeaders(message);
|
||||||
|
if (variant != null)
|
||||||
|
return variant;
|
||||||
|
|
||||||
|
// If a receiver is not known to use any JIS variants, the sender transfers the message without converting
|
||||||
|
// the charset.
|
||||||
|
variant = getJisVariantFromFromHeaders(message);
|
||||||
|
if (variant != null)
|
||||||
|
return variant;
|
||||||
|
|
||||||
|
return getJisVariantFromMailerHeaders(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isShiftJis(String charset) {
|
||||||
|
return charset.length() > 17 && charset.startsWith("x-")
|
||||||
|
&& charset.endsWith("-shift_jis-2007");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getJisVariantFromAddress(String address) {
|
||||||
|
if (address == null)
|
||||||
|
return null;
|
||||||
|
if (isInDomain(address, "docomo.ne.jp") || isInDomain(address, "dwmail.jp") ||
|
||||||
|
isInDomain(address, "pdx.ne.jp") || isInDomain(address, "willcom.com") ||
|
||||||
|
isInDomain(address, "emnet.ne.jp") || isInDomain(address, "emobile.ne.jp"))
|
||||||
|
return "docomo";
|
||||||
|
else if (isInDomain(address, "softbank.ne.jp") || isInDomain(address, "vodafone.ne.jp") ||
|
||||||
|
isInDomain(address, "disney.ne.jp") || isInDomain(address, "vertuclub.ne.jp"))
|
||||||
|
return "softbank";
|
||||||
|
else if (isInDomain(address, "ezweb.ne.jp") || isInDomain(address, "ido.ne.jp"))
|
||||||
|
return "kddi";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String getJisVariantFromMailerHeaders(Message message) throws MessagingException {
|
||||||
|
String mailerHeaders[] = message.getHeader("X-Mailer");
|
||||||
|
if (mailerHeaders == null || mailerHeaders.length == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (mailerHeaders[0].startsWith("iPhone Mail ") || mailerHeaders[0].startsWith("iPad Mail "))
|
||||||
|
return "iphone";
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String getJisVariantFromReceivedHeaders(Part message) throws MessagingException {
|
||||||
|
String receivedHeaders[] = message.getHeader("Received");
|
||||||
|
if (receivedHeaders == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for (String receivedHeader : receivedHeaders) {
|
||||||
|
String address = getAddressFromReceivedHeader(receivedHeader);
|
||||||
|
if (address == null)
|
||||||
|
continue;
|
||||||
|
String variant = getJisVariantFromAddress(address);
|
||||||
|
if (variant != null)
|
||||||
|
return variant;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getAddressFromReceivedHeader(String receivedHeader) {
|
||||||
|
// Not implemented yet! Extract an address from the FOR clause of the given Received header.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getJisVariantFromFromHeaders(Message message) throws MessagingException {
|
||||||
|
Address addresses[] = message.getFrom();
|
||||||
|
if (addresses == null || addresses.length == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return getJisVariantFromAddress(addresses[0].getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isInDomain(String address, String domain) {
|
||||||
|
int index = address.length() - domain.length() - 1;
|
||||||
|
if (index < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char c = address.charAt(index);
|
||||||
|
if (c != '@' && c != '.')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return address.endsWith(domain);
|
||||||
|
}
|
||||||
|
}
|
410
src/com/fsck/k9/mail/internet/MessageExtractor.java
Normal file
410
src/com/fsck/k9/mail/internet/MessageExtractor.java
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
package com.fsck.k9.mail.internet;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.K9;
|
||||||
|
import com.fsck.k9.mail.Body;
|
||||||
|
import com.fsck.k9.mail.BodyPart;
|
||||||
|
import com.fsck.k9.mail.Message;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.Multipart;
|
||||||
|
import com.fsck.k9.mail.Part;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static com.fsck.k9.mail.internet.CharsetSupport.fixupCharset;
|
||||||
|
import static com.fsck.k9.mail.internet.MimeUtility.getHeaderParameter;
|
||||||
|
import static com.fsck.k9.mail.internet.Viewable.Alternative;
|
||||||
|
import static com.fsck.k9.mail.internet.Viewable.Textual;
|
||||||
|
|
||||||
|
public class MessageExtractor {
|
||||||
|
public static String getTextFromPart(Part part) {
|
||||||
|
try {
|
||||||
|
if ((part != null) && (part.getBody() != null)) {
|
||||||
|
final Body body = part.getBody();
|
||||||
|
if (body instanceof TextBody) {
|
||||||
|
return ((TextBody)body).getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
final String mimeType = part.getMimeType();
|
||||||
|
if ((mimeType != null) && MimeUtility.mimeTypeMatches(mimeType, "text/*")) {
|
||||||
|
/*
|
||||||
|
* We've got a text part, so let's see if it needs to be processed further.
|
||||||
|
*/
|
||||||
|
String charset = getHeaderParameter(part.getContentType(), "charset");
|
||||||
|
/*
|
||||||
|
* determine the charset from HTML message.
|
||||||
|
*/
|
||||||
|
if (mimeType.equalsIgnoreCase("text/html") && charset == null) {
|
||||||
|
InputStream in = part.getBody().getInputStream();
|
||||||
|
try {
|
||||||
|
byte[] buf = new byte[256];
|
||||||
|
in.read(buf, 0, buf.length);
|
||||||
|
String str = new String(buf, "US-ASCII");
|
||||||
|
|
||||||
|
if (str.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
Pattern p = Pattern.compile("<meta http-equiv=\"?Content-Type\"? content=\"text/html; charset=(.+?)\">", Pattern.CASE_INSENSITIVE);
|
||||||
|
Matcher m = p.matcher(str);
|
||||||
|
if (m.find()) {
|
||||||
|
charset = m.group(1);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (in instanceof BinaryTempFileBody.BinaryTempFileBodyInputStream) {
|
||||||
|
/*
|
||||||
|
* If this is a BinaryTempFileBodyInputStream, calling close()
|
||||||
|
* will delete the file. But we can't let that happen because
|
||||||
|
* the file needs to be opened again by the code a few lines
|
||||||
|
* down.
|
||||||
|
*/
|
||||||
|
((BinaryTempFileBody.BinaryTempFileBodyInputStream) in).closeWithoutDeleting();
|
||||||
|
} else {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
} catch (Exception e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
charset = fixupCharset(charset, getMessageFromPart(part));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we read the part into a buffer for further processing. Because
|
||||||
|
* the stream is now wrapped we'll remove any transfer encoding at this point.
|
||||||
|
*/
|
||||||
|
InputStream in = part.getBody().getInputStream();
|
||||||
|
try {
|
||||||
|
String text = CharsetSupport.readToString(in, charset);
|
||||||
|
|
||||||
|
// Replace the body with a TextBody that already contains the decoded text
|
||||||
|
part.setBody(new TextBody(text));
|
||||||
|
|
||||||
|
return text;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* This time we don't care if it's a BinaryTempFileBodyInputStream. We
|
||||||
|
* replaced the body with a TextBody instance and hence don't need the
|
||||||
|
* file anymore.
|
||||||
|
*/
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) { /* Ignore */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (OutOfMemoryError oom) {
|
||||||
|
/*
|
||||||
|
* If we are not able to process the body there's nothing we can do about it. Return
|
||||||
|
* null and let the upper layers handle the missing content.
|
||||||
|
*/
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to getTextFromPart " + oom.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
/*
|
||||||
|
* If we are not able to process the body there's nothing we can do about it. Return
|
||||||
|
* null and let the upper layers handle the missing content.
|
||||||
|
*/
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to getTextFromPart", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverse the MIME tree of a message an extract viewable parts.
|
||||||
|
*
|
||||||
|
* @param part
|
||||||
|
* The message part to start from.
|
||||||
|
* @param attachments
|
||||||
|
* A list that will receive the parts that are considered attachments.
|
||||||
|
*
|
||||||
|
* @return A list of {@link Viewable}s.
|
||||||
|
*
|
||||||
|
* @throws MessagingException
|
||||||
|
* In case of an error.
|
||||||
|
*/
|
||||||
|
public static List<Viewable> getViewables(Part part, List<Part> attachments) throws MessagingException {
|
||||||
|
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||||
|
|
||||||
|
Body body = part.getBody();
|
||||||
|
if (body instanceof Multipart) {
|
||||||
|
Multipart multipart = (Multipart) body;
|
||||||
|
if (part.getMimeType().equalsIgnoreCase("multipart/alternative")) {
|
||||||
|
/*
|
||||||
|
* For multipart/alternative parts we try to find a text/plain and a text/html
|
||||||
|
* child. Everything else we find is put into 'attachments'.
|
||||||
|
*/
|
||||||
|
List<Viewable> text = findTextPart(multipart, true);
|
||||||
|
|
||||||
|
Set<Part> knownTextParts = getParts(text);
|
||||||
|
List<Viewable> html = findHtmlPart(multipart, knownTextParts, attachments, true);
|
||||||
|
|
||||||
|
if (!text.isEmpty() || !html.isEmpty()) {
|
||||||
|
Alternative alternative = new Alternative(text, html);
|
||||||
|
viewables.add(alternative);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For all other multipart parts we recurse to grab all viewable children.
|
||||||
|
for (Part bodyPart : multipart.getBodyParts()) {
|
||||||
|
viewables.addAll(getViewables(bodyPart, attachments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (body instanceof Message &&
|
||||||
|
!("attachment".equalsIgnoreCase(part.getContentDisposition()))) {
|
||||||
|
/*
|
||||||
|
* We only care about message/rfc822 parts whose Content-Disposition header has a value
|
||||||
|
* other than "attachment".
|
||||||
|
*/
|
||||||
|
Message message = (Message) body;
|
||||||
|
|
||||||
|
// We add the Message object so we can extract the filename later.
|
||||||
|
viewables.add(new Viewable.MessageHeader(part, message));
|
||||||
|
|
||||||
|
// Recurse to grab all viewable parts and attachments from that message.
|
||||||
|
viewables.addAll(getViewables(message, attachments));
|
||||||
|
} else if (isPartTextualBody(part)) {
|
||||||
|
/*
|
||||||
|
* Save text/plain and text/html
|
||||||
|
*/
|
||||||
|
String mimeType = part.getMimeType();
|
||||||
|
if (mimeType.equalsIgnoreCase("text/plain")) {
|
||||||
|
Viewable.Text text = new Viewable.Text(part);
|
||||||
|
viewables.add(text);
|
||||||
|
} else {
|
||||||
|
Viewable.Html html = new Viewable.Html(part);
|
||||||
|
viewables.add(html);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Everything else is treated as attachment.
|
||||||
|
attachments.add(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Part> getTextParts(Part part) throws MessagingException {
|
||||||
|
List<Part> attachments = new ArrayList<Part>();
|
||||||
|
return getParts(getViewables(part, attachments));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Message getMessageFromPart(Part part) {
|
||||||
|
while (part != null) {
|
||||||
|
if (part instanceof Message)
|
||||||
|
return (Message)part;
|
||||||
|
|
||||||
|
if (!(part instanceof BodyPart))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Multipart multipart = ((BodyPart)part).getParent();
|
||||||
|
if (multipart == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
part = multipart.getParent();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the children of a {@link Multipart} for {@code text/plain} parts.
|
||||||
|
*
|
||||||
|
* @param multipart The {@code Multipart} to search through.
|
||||||
|
* @param directChild If {@code true}, this method will return after the first {@code text/plain} was
|
||||||
|
* found.
|
||||||
|
*
|
||||||
|
* @return A list of {@link Viewable.Text} viewables.
|
||||||
|
*
|
||||||
|
* @throws MessagingException
|
||||||
|
* In case of an error.
|
||||||
|
*/
|
||||||
|
private static List<Viewable> findTextPart(Multipart multipart, boolean directChild)
|
||||||
|
throws MessagingException {
|
||||||
|
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||||
|
|
||||||
|
for (Part part : multipart.getBodyParts()) {
|
||||||
|
Body body = part.getBody();
|
||||||
|
if (body instanceof Multipart) {
|
||||||
|
Multipart innerMultipart = (Multipart) body;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recurse to find text parts. Since this is a multipart that is a child of a
|
||||||
|
* multipart/alternative we don't want to stop after the first text/plain part
|
||||||
|
* we find. This will allow to get all text parts for constructions like this:
|
||||||
|
*
|
||||||
|
* 1. multipart/alternative
|
||||||
|
* 1.1. multipart/mixed
|
||||||
|
* 1.1.1. text/plain
|
||||||
|
* 1.1.2. text/plain
|
||||||
|
* 1.2. text/html
|
||||||
|
*/
|
||||||
|
List<Viewable> textViewables = findTextPart(innerMultipart, false);
|
||||||
|
|
||||||
|
if (!textViewables.isEmpty()) {
|
||||||
|
viewables.addAll(textViewables);
|
||||||
|
if (directChild) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (isPartTextualBody(part) && part.getMimeType().equalsIgnoreCase("text/plain")) {
|
||||||
|
Viewable.Text text = new Viewable.Text(part);
|
||||||
|
viewables.add(text);
|
||||||
|
if (directChild) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return viewables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the children of a {@link Multipart} for {@code text/html} parts.
|
||||||
|
* Every part that is not a {@code text/html} we want to display, we add to 'attachments'.
|
||||||
|
*
|
||||||
|
* @param multipart The {@code Multipart} to search through.
|
||||||
|
* @param knownTextParts A set of {@code text/plain} parts that shouldn't be added to 'attachments'.
|
||||||
|
* @param attachments A list that will receive the parts that are considered attachments.
|
||||||
|
* @param directChild If {@code true}, this method will add all {@code text/html} parts except the first
|
||||||
|
* found to 'attachments'.
|
||||||
|
*
|
||||||
|
* @return A list of {@link Viewable.Text} viewables.
|
||||||
|
*
|
||||||
|
* @throws MessagingException In case of an error.
|
||||||
|
*/
|
||||||
|
private static List<Viewable> findHtmlPart(Multipart multipart, Set<Part> knownTextParts,
|
||||||
|
List<Part> attachments, boolean directChild) throws MessagingException {
|
||||||
|
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||||
|
|
||||||
|
boolean partFound = false;
|
||||||
|
for (Part part : multipart.getBodyParts()) {
|
||||||
|
Body body = part.getBody();
|
||||||
|
if (body instanceof Multipart) {
|
||||||
|
Multipart innerMultipart = (Multipart) body;
|
||||||
|
|
||||||
|
if (directChild && partFound) {
|
||||||
|
// We already found our text/html part. Now we're only looking for attachments.
|
||||||
|
findAttachments(innerMultipart, knownTextParts, attachments);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Recurse to find HTML parts. Since this is a multipart that is a child of a
|
||||||
|
* multipart/alternative we don't want to stop after the first text/html part
|
||||||
|
* we find. This will allow to get all text parts for constructions like this:
|
||||||
|
*
|
||||||
|
* 1. multipart/alternative
|
||||||
|
* 1.1. text/plain
|
||||||
|
* 1.2. multipart/mixed
|
||||||
|
* 1.2.1. text/html
|
||||||
|
* 1.2.2. text/html
|
||||||
|
* 1.3. image/jpeg
|
||||||
|
*/
|
||||||
|
List<Viewable> htmlViewables = findHtmlPart(innerMultipart, knownTextParts,
|
||||||
|
attachments, false);
|
||||||
|
|
||||||
|
if (!htmlViewables.isEmpty()) {
|
||||||
|
partFound = true;
|
||||||
|
viewables.addAll(htmlViewables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!(directChild && partFound) && isPartTextualBody(part) &&
|
||||||
|
part.getMimeType().equalsIgnoreCase("text/html")) {
|
||||||
|
Viewable.Html html = new Viewable.Html(part);
|
||||||
|
viewables.add(html);
|
||||||
|
partFound = true;
|
||||||
|
} else if (!knownTextParts.contains(part)) {
|
||||||
|
// Only add this part as attachment if it's not a viewable text/plain part found
|
||||||
|
// earlier.
|
||||||
|
attachments.add(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverse the MIME tree and add everything that's not a known text part to 'attachments'.
|
||||||
|
*
|
||||||
|
* @param multipart
|
||||||
|
* The {@link Multipart} to start from.
|
||||||
|
* @param knownTextParts
|
||||||
|
* A set of known text parts we don't want to end up in 'attachments'.
|
||||||
|
* @param attachments
|
||||||
|
* A list that will receive the parts that are considered attachments.
|
||||||
|
*/
|
||||||
|
private static void findAttachments(Multipart multipart, Set<Part> knownTextParts,
|
||||||
|
List<Part> attachments) {
|
||||||
|
for (Part part : multipart.getBodyParts()) {
|
||||||
|
Body body = part.getBody();
|
||||||
|
if (body instanceof Multipart) {
|
||||||
|
Multipart innerMultipart = (Multipart) body;
|
||||||
|
findAttachments(innerMultipart, knownTextParts, attachments);
|
||||||
|
} else if (!knownTextParts.contains(part)) {
|
||||||
|
attachments.add(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a set of message parts for fast lookups.
|
||||||
|
*
|
||||||
|
* @param viewables
|
||||||
|
* A list of {@link Viewable}s containing references to the message parts to include in
|
||||||
|
* the set.
|
||||||
|
*
|
||||||
|
* @return The set of viewable {@code Part}s.
|
||||||
|
*
|
||||||
|
* @see MimeUtility#findHtmlPart(Multipart, Set, List, boolean)
|
||||||
|
* @see MimeUtility#findAttachments(Multipart, Set, List)
|
||||||
|
*/
|
||||||
|
private static Set<Part> getParts(List<Viewable> viewables) {
|
||||||
|
Set<Part> parts = new HashSet<Part>();
|
||||||
|
|
||||||
|
for (Viewable viewable : viewables) {
|
||||||
|
if (viewable instanceof Textual) {
|
||||||
|
parts.add(((Textual) viewable).getPart());
|
||||||
|
} else if (viewable instanceof Alternative) {
|
||||||
|
Alternative alternative = (Alternative) viewable;
|
||||||
|
parts.addAll(getParts(alternative.getText()));
|
||||||
|
parts.addAll(getParts(alternative.getHtml()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Boolean isPartTextualBody(Part part) throws MessagingException {
|
||||||
|
String disposition = part.getDisposition();
|
||||||
|
String dispositionType = null;
|
||||||
|
String dispositionFilename = null;
|
||||||
|
if (disposition != null) {
|
||||||
|
dispositionType = MimeUtility.getHeaderParameter(disposition, null);
|
||||||
|
dispositionFilename = MimeUtility.getHeaderParameter(disposition, "filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A best guess that this part is intended to be an attachment and not inline.
|
||||||
|
*/
|
||||||
|
boolean attachment = ("attachment".equalsIgnoreCase(dispositionType) || (dispositionFilename != null));
|
||||||
|
|
||||||
|
if ((!attachment) && (part.getMimeType().equalsIgnoreCase("text/html"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If the part is plain text and it got this far it's part of a
|
||||||
|
* mixed (et al) and should be rendered inline.
|
||||||
|
*/
|
||||||
|
else if ((!attachment) && (part.getMimeType().equalsIgnoreCase("text/plain"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Finally, if it's nothing else we will include it as an attachment.
|
||||||
|
*/
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -473,7 +473,7 @@ public class MimeMessage extends Message {
|
|||||||
if (mBody instanceof Multipart) {
|
if (mBody instanceof Multipart) {
|
||||||
((Multipart)mBody).setCharset(charset);
|
((Multipart)mBody).setCharset(charset);
|
||||||
} else if (mBody instanceof TextBody) {
|
} else if (mBody instanceof TextBody) {
|
||||||
MimeUtility.setCharset(charset, this);
|
CharsetSupport.setCharset(charset, this);
|
||||||
((TextBody)mBody).setCharset(charset);
|
((TextBody)mBody).setCharset(charset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,11 @@ package com.fsck.k9.mail.internet;
|
|||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
|
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
|
||||||
import org.apache.james.mime4j.util.MimeUtil;
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
105
src/com/fsck/k9/mail/internet/Viewable.java
Normal file
105
src/com/fsck/k9/mail/internet/Viewable.java
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package com.fsck.k9.mail.internet;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.Message;
|
||||||
|
import com.fsck.k9.mail.Part;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty marker class interface the class hierarchy used by
|
||||||
|
* {@link com.fsck.k9.mailstore.LocalMessageExtractor#extractTextAndAttachments(android.content.Context, com.fsck.k9.mail.Message)}.
|
||||||
|
*
|
||||||
|
* @see Viewable.Text
|
||||||
|
* @see Viewable.Html
|
||||||
|
* @see Viewable.MessageHeader
|
||||||
|
* @see Viewable.Alternative
|
||||||
|
*/
|
||||||
|
public interface Viewable {
|
||||||
|
/**
|
||||||
|
* Class representing textual parts of a message that aren't marked as attachments.
|
||||||
|
*
|
||||||
|
* @see com.fsck.k9.mail.internet.MessageExtractor#isPartTextualBody(com.fsck.k9.mail.Part)
|
||||||
|
*/
|
||||||
|
static abstract class Textual implements Viewable {
|
||||||
|
private Part mPart;
|
||||||
|
|
||||||
|
public Textual(Part part) {
|
||||||
|
mPart = part;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Part getPart() {
|
||||||
|
return mPart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a {@code text/plain} part of a message.
|
||||||
|
*/
|
||||||
|
static class Text extends Textual {
|
||||||
|
public Text(Part part) {
|
||||||
|
super(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a {@code text/html} part of a message.
|
||||||
|
*/
|
||||||
|
static class Html extends Textual {
|
||||||
|
public Html(Part part) {
|
||||||
|
super(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a {@code message/rfc822} part of a message.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is used to extract basic header information when the message contents are displayed
|
||||||
|
* inline.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
static class MessageHeader implements Viewable {
|
||||||
|
private Part mContainerPart;
|
||||||
|
private Message mMessage;
|
||||||
|
|
||||||
|
public MessageHeader(Part containerPart, Message message) {
|
||||||
|
mContainerPart = containerPart;
|
||||||
|
mMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Part getContainerPart() {
|
||||||
|
return mContainerPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Message getMessage() {
|
||||||
|
return mMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a {@code multipart/alternative} part of a message.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Only relevant {@code text/plain} and {@code text/html} children are stored in this container
|
||||||
|
* class.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
static class Alternative implements Viewable {
|
||||||
|
private List<Viewable> mText;
|
||||||
|
private List<Viewable> mHtml;
|
||||||
|
|
||||||
|
public Alternative(List<Viewable> text, List<Viewable> html) {
|
||||||
|
mText = text;
|
||||||
|
mHtml = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Viewable> getText() {
|
||||||
|
return mText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Viewable> getHtml() {
|
||||||
|
return mHtml;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -12,7 +12,7 @@ import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
|||||||
import com.fsck.k9.mail.filter.LineWrapOutputStream;
|
import com.fsck.k9.mail.filter.LineWrapOutputStream;
|
||||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
import com.fsck.k9.mail.filter.PeekableInputStream;
|
||||||
import com.fsck.k9.mail.filter.SmtpDataStuffing;
|
import com.fsck.k9.mail.filter.SmtpDataStuffing;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.CharsetSupport;
|
||||||
import com.fsck.k9.mail.store.StoreConfig;
|
import com.fsck.k9.mail.store.StoreConfig;
|
||||||
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
|
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
|
||||||
|
|
||||||
@ -463,7 +463,7 @@ public class SmtpTransport extends Transport {
|
|||||||
new HashMap<String, List<String>>();
|
new HashMap<String, List<String>>();
|
||||||
for (Address address : addresses) {
|
for (Address address : addresses) {
|
||||||
String addressString = address.getAddress();
|
String addressString = address.getAddress();
|
||||||
String charset = MimeUtility.getCharsetFromAddress(addressString);
|
String charset = CharsetSupport.getCharsetFromAddress(addressString);
|
||||||
List<String> addressesOfCharset = charsetAddressesMap.get(charset);
|
List<String> addressesOfCharset = charsetAddressesMap.get(charset);
|
||||||
if (addressesOfCharset == null) {
|
if (addressesOfCharset == null) {
|
||||||
addressesOfCharset = new ArrayList<String>();
|
addressesOfCharset = new ArrayList<String>();
|
||||||
|
@ -33,7 +33,7 @@ import com.fsck.k9.K9;
|
|||||||
import com.fsck.k9.Account.MessageFormat;
|
import com.fsck.k9.Account.MessageFormat;
|
||||||
import com.fsck.k9.activity.Search;
|
import com.fsck.k9.activity.Search;
|
||||||
import com.fsck.k9.mail.MessageRetrievalListener;
|
import com.fsck.k9.mail.MessageRetrievalListener;
|
||||||
import com.fsck.k9.mail.internet.HtmlConverter;
|
import com.fsck.k9.helper.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.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
@ -51,11 +51,11 @@ 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.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.mail.internet.MimeUtility.ViewableContainer;
|
|
||||||
import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
|
import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
|
||||||
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
|
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
|
||||||
import com.fsck.k9.provider.AttachmentProvider;
|
import com.fsck.k9.provider.AttachmentProvider;
|
||||||
|
|
||||||
|
|
||||||
public class LocalFolder extends Folder<LocalMessage> implements Serializable {
|
public class LocalFolder extends Folder<LocalMessage> implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = -1973296520918624767L;
|
private static final long serialVersionUID = -1973296520918624767L;
|
||||||
@ -1299,14 +1299,14 @@ public class LocalFolder extends Folder<LocalMessage> implements Serializable {
|
|||||||
// draft messages because this will cause the values stored in
|
// draft messages because this will cause the values stored in
|
||||||
// the identity header to be wrong.
|
// the identity header to be wrong.
|
||||||
ViewableContainer container =
|
ViewableContainer container =
|
||||||
MimeUtility.extractPartsFromDraft(message);
|
LocalMessageExtractor.extractPartsFromDraft(message);
|
||||||
|
|
||||||
text = container.text;
|
text = container.text;
|
||||||
html = container.html;
|
html = container.html;
|
||||||
attachments = container.attachments;
|
attachments = container.attachments;
|
||||||
} else {
|
} else {
|
||||||
ViewableContainer container =
|
ViewableContainer container =
|
||||||
MimeUtility.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message);
|
LocalMessageExtractor.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message);
|
||||||
|
|
||||||
attachments = container.attachments;
|
attachments = container.attachments;
|
||||||
text = container.text;
|
text = container.text;
|
||||||
@ -1412,7 +1412,7 @@ public class LocalFolder extends Folder<LocalMessage> implements Serializable {
|
|||||||
message.buildMimeRepresentation();
|
message.buildMimeRepresentation();
|
||||||
|
|
||||||
ViewableContainer container =
|
ViewableContainer container =
|
||||||
MimeUtility.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message);
|
LocalMessageExtractor.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message);
|
||||||
|
|
||||||
List<Part> attachments = container.attachments;
|
List<Part> attachments = container.attachments;
|
||||||
String text = container.text;
|
String text = container.text;
|
||||||
|
@ -120,16 +120,16 @@ public class LocalMessage extends MimeMessage {
|
|||||||
*/
|
*/
|
||||||
public String getTextForDisplay() throws MessagingException {
|
public String getTextForDisplay() throws MessagingException {
|
||||||
String text = null; // First try and fetch an HTML part.
|
String text = null; // First try and fetch an HTML part.
|
||||||
Part part = MimeUtility.findFirstPartByMimeType(this, "text/html");
|
Part part = findFirstPartByMimeType("text/html");
|
||||||
if (part == null) {
|
if (part == null) {
|
||||||
// If that fails, try and get a text part.
|
// If that fails, try and get a text part.
|
||||||
part = MimeUtility.findFirstPartByMimeType(this, "text/plain");
|
part = findFirstPartByMimeType("text/plain");
|
||||||
if (part != null && part.getBody() instanceof LocalTextBody) {
|
if (part != null && part.getBody() instanceof LocalTextBody) {
|
||||||
text = ((LocalTextBody) part.getBody()).getBodyForDisplay();
|
text = ((LocalTextBody) part.getBody()).getBodyForDisplay();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We successfully found an HTML part; do the necessary character set decoding.
|
// We successfully found an HTML part; do the necessary character set decoding.
|
||||||
text = MimeUtility.getTextFromPart(part);
|
text = part.getText();
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
464
src/com/fsck/k9/mailstore/LocalMessageExtractor.java
Normal file
464
src/com/fsck/k9/mailstore/LocalMessageExtractor.java
Normal file
@ -0,0 +1,464 @@
|
|||||||
|
package com.fsck.k9.mailstore;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.fsck.k9.R;
|
||||||
|
import com.fsck.k9.mail.Address;
|
||||||
|
import com.fsck.k9.mail.Body;
|
||||||
|
import com.fsck.k9.mail.BodyPart;
|
||||||
|
import com.fsck.k9.mail.Message;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.Part;
|
||||||
|
import com.fsck.k9.helper.HtmlConverter;
|
||||||
|
import com.fsck.k9.mail.internet.MessageExtractor;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||||
|
import com.fsck.k9.mail.internet.Viewable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.fsck.k9.mail.internet.MimeUtility.getHeaderParameter;
|
||||||
|
|
||||||
|
class LocalMessageExtractor {
|
||||||
|
private static final String TEXT_DIVIDER =
|
||||||
|
"------------------------------------------------------------------------";
|
||||||
|
private static final int TEXT_DIVIDER_LENGTH = TEXT_DIVIDER.length();
|
||||||
|
private static final String FILENAME_PREFIX = "----- ";
|
||||||
|
private static final int FILENAME_PREFIX_LENGTH = FILENAME_PREFIX.length();
|
||||||
|
private static final String FILENAME_SUFFIX = " ";
|
||||||
|
private static final int FILENAME_SUFFIX_LENGTH = FILENAME_SUFFIX.length();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the viewable textual parts of a message and return the rest as attachments.
|
||||||
|
*
|
||||||
|
* @param context A {@link android.content.Context} instance that will be used to get localized strings.
|
||||||
|
* @return A {@link ViewableContainer} instance containing the textual parts of the message as
|
||||||
|
* plain text and HTML, and a list of message parts considered attachments.
|
||||||
|
*
|
||||||
|
* @throws com.fsck.k9.mail.MessagingException
|
||||||
|
* In case of an error.
|
||||||
|
*/
|
||||||
|
public static ViewableContainer extractTextAndAttachments(Context context, Message message) throws MessagingException {
|
||||||
|
try {
|
||||||
|
List<Part> attachments = new ArrayList<Part>();
|
||||||
|
|
||||||
|
// Collect all viewable parts
|
||||||
|
List<Viewable> viewables = MessageExtractor.getViewables(message, attachments);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert the tree of viewable parts into text and HTML
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Used to suppress the divider for the first viewable part
|
||||||
|
boolean hideDivider = true;
|
||||||
|
|
||||||
|
StringBuilder text = new StringBuilder();
|
||||||
|
StringBuilder html = new StringBuilder();
|
||||||
|
|
||||||
|
for (Viewable viewable : viewables) {
|
||||||
|
if (viewable instanceof Viewable.Textual) {
|
||||||
|
// This is either a text/plain or text/html part. Fill the variables 'text' and
|
||||||
|
// 'html', converting between plain text and HTML as necessary.
|
||||||
|
text.append(buildText(viewable, !hideDivider));
|
||||||
|
html.append(buildHtml(viewable, !hideDivider));
|
||||||
|
hideDivider = false;
|
||||||
|
} else if (viewable instanceof Viewable.MessageHeader) {
|
||||||
|
Viewable.MessageHeader header = (Viewable.MessageHeader) viewable;
|
||||||
|
Part containerPart = header.getContainerPart();
|
||||||
|
Message innerMessage = header.getMessage();
|
||||||
|
|
||||||
|
addTextDivider(text, containerPart, !hideDivider);
|
||||||
|
addMessageHeaderText(context, text, innerMessage);
|
||||||
|
|
||||||
|
addHtmlDivider(html, containerPart, !hideDivider);
|
||||||
|
addMessageHeaderHtml(context, html, innerMessage);
|
||||||
|
|
||||||
|
hideDivider = true;
|
||||||
|
} else if (viewable instanceof Viewable.Alternative) {
|
||||||
|
// Handle multipart/alternative contents
|
||||||
|
Viewable.Alternative alternative = (Viewable.Alternative) viewable;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We made sure at least one of text/plain or text/html is present when
|
||||||
|
* creating the Alternative object. If one part is not present we convert the
|
||||||
|
* other one to make sure 'text' and 'html' always contain the same text.
|
||||||
|
*/
|
||||||
|
List<Viewable> textAlternative = alternative.getText().isEmpty() ?
|
||||||
|
alternative.getHtml() : alternative.getText();
|
||||||
|
List<Viewable> htmlAlternative = alternative.getHtml().isEmpty() ?
|
||||||
|
alternative.getText() : alternative.getHtml();
|
||||||
|
|
||||||
|
// Fill the 'text' variable
|
||||||
|
boolean divider = !hideDivider;
|
||||||
|
for (Viewable textViewable : textAlternative) {
|
||||||
|
text.append(buildText(textViewable, divider));
|
||||||
|
divider = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the 'html' variable
|
||||||
|
divider = !hideDivider;
|
||||||
|
for (Viewable htmlViewable : htmlAlternative) {
|
||||||
|
html.append(buildHtml(htmlViewable, divider));
|
||||||
|
divider = true;
|
||||||
|
}
|
||||||
|
hideDivider = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ViewableContainer(text.toString(), html.toString(), attachments);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MessagingException("Couldn't extract viewable parts", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ViewableContainer extractPartsFromDraft(Message message)
|
||||||
|
throws MessagingException {
|
||||||
|
|
||||||
|
Body body = message.getBody();
|
||||||
|
if (message.isMimeType("multipart/mixed") && body instanceof MimeMultipart) {
|
||||||
|
MimeMultipart multipart = (MimeMultipart) body;
|
||||||
|
|
||||||
|
ViewableContainer container;
|
||||||
|
int count = multipart.getCount();
|
||||||
|
if (count >= 1) {
|
||||||
|
// The first part is either a text/plain or a multipart/alternative
|
||||||
|
BodyPart firstPart = multipart.getBodyPart(0);
|
||||||
|
container = extractTextual(firstPart);
|
||||||
|
|
||||||
|
// The rest should be attachments
|
||||||
|
for (int i = 1; i < count; i++) {
|
||||||
|
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||||
|
container.attachments.add(bodyPart);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
container = new ViewableContainer("", "", new ArrayList<Part>());
|
||||||
|
}
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractTextual(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the contents of a {@link com.fsck.k9.mail.internet.Viewable} to create the HTML to be displayed.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This will use {@link com.fsck.k9.helper.HtmlConverter#textToHtml(String)} to convert plain text parts
|
||||||
|
* to HTML if necessary.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param viewable
|
||||||
|
* The viewable part to build the HTML from.
|
||||||
|
* @param prependDivider
|
||||||
|
* {@code true}, if the HTML divider should be inserted as first element.
|
||||||
|
* {@code false}, otherwise.
|
||||||
|
*
|
||||||
|
* @return The contents of the supplied viewable instance as HTML.
|
||||||
|
*/
|
||||||
|
private static StringBuilder buildHtml(Viewable viewable, boolean prependDivider)
|
||||||
|
{
|
||||||
|
StringBuilder html = new StringBuilder();
|
||||||
|
if (viewable instanceof Viewable.Textual) {
|
||||||
|
Part part = ((Viewable.Textual)viewable).getPart();
|
||||||
|
addHtmlDivider(html, part, prependDivider);
|
||||||
|
|
||||||
|
String t = part.getText();
|
||||||
|
if (t == null) {
|
||||||
|
t = "";
|
||||||
|
} else if (viewable instanceof Viewable.Text) {
|
||||||
|
t = HtmlConverter.textToHtml(t);
|
||||||
|
}
|
||||||
|
html.append(t);
|
||||||
|
} else if (viewable instanceof Viewable.Alternative) {
|
||||||
|
// That's odd - an Alternative as child of an Alternative; go ahead and try to use the
|
||||||
|
// text/html child; fall-back to the text/plain part.
|
||||||
|
Viewable.Alternative alternative = (Viewable.Alternative) viewable;
|
||||||
|
|
||||||
|
List<Viewable> htmlAlternative = alternative.getHtml().isEmpty() ?
|
||||||
|
alternative.getText() : alternative.getHtml();
|
||||||
|
|
||||||
|
boolean divider = prependDivider;
|
||||||
|
for (Viewable htmlViewable : htmlAlternative) {
|
||||||
|
html.append(buildHtml(htmlViewable, divider));
|
||||||
|
divider = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StringBuilder buildText(Viewable viewable, boolean prependDivider)
|
||||||
|
{
|
||||||
|
StringBuilder text = new StringBuilder();
|
||||||
|
if (viewable instanceof Viewable.Textual) {
|
||||||
|
Part part = ((Viewable.Textual)viewable).getPart();
|
||||||
|
addTextDivider(text, part, prependDivider);
|
||||||
|
|
||||||
|
String t = part.getText();
|
||||||
|
if (t == null) {
|
||||||
|
t = "";
|
||||||
|
} else if (viewable instanceof Viewable.Html) {
|
||||||
|
t = HtmlConverter.htmlToText(t);
|
||||||
|
}
|
||||||
|
text.append(t);
|
||||||
|
} else if (viewable instanceof Viewable.Alternative) {
|
||||||
|
// That's odd - an Alternative as child of an Alternative; go ahead and try to use the
|
||||||
|
// text/plain child; fall-back to the text/html part.
|
||||||
|
Viewable.Alternative alternative = (Viewable.Alternative) viewable;
|
||||||
|
|
||||||
|
List<Viewable> textAlternative = alternative.getText().isEmpty() ?
|
||||||
|
alternative.getHtml() : alternative.getText();
|
||||||
|
|
||||||
|
boolean divider = prependDivider;
|
||||||
|
for (Viewable textViewable : textAlternative) {
|
||||||
|
text.append(buildText(textViewable, divider));
|
||||||
|
divider = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an HTML divider between two HTML message parts.
|
||||||
|
*
|
||||||
|
* @param html
|
||||||
|
* The {@link StringBuilder} to append the divider to.
|
||||||
|
* @param part
|
||||||
|
* The message part that will follow after the divider. This is used to extract the
|
||||||
|
* part's name.
|
||||||
|
* @param prependDivider
|
||||||
|
* {@code true}, if the divider should be appended. {@code false}, otherwise.
|
||||||
|
*/
|
||||||
|
private static void addHtmlDivider(StringBuilder html, Part part, boolean prependDivider) {
|
||||||
|
if (prependDivider) {
|
||||||
|
String filename = getPartName(part);
|
||||||
|
|
||||||
|
html.append("<p style=\"margin-top: 2.5em; margin-bottom: 1em; border-bottom: 1px solid #000\">");
|
||||||
|
html.append(filename);
|
||||||
|
html.append("</p>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the message part.
|
||||||
|
*
|
||||||
|
* @param part
|
||||||
|
* The part to get the name for.
|
||||||
|
*
|
||||||
|
* @return The (file)name of the part if available. An empty string, otherwise.
|
||||||
|
*/
|
||||||
|
private static String getPartName(Part part) {
|
||||||
|
try {
|
||||||
|
String disposition = part.getDisposition();
|
||||||
|
if (disposition != null) {
|
||||||
|
String name = getHeaderParameter(disposition, "filename");
|
||||||
|
return (name == null) ? "" : name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MessagingException e) { /* ignore */ }
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a plain text divider between two plain text message parts.
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
* The {@link StringBuilder} to append the divider to.
|
||||||
|
* @param part
|
||||||
|
* The message part that will follow after the divider. This is used to extract the
|
||||||
|
* part's name.
|
||||||
|
* @param prependDivider
|
||||||
|
* {@code true}, if the divider should be appended. {@code false}, otherwise.
|
||||||
|
*/
|
||||||
|
private static void addTextDivider(StringBuilder text, Part part, boolean prependDivider) {
|
||||||
|
if (prependDivider) {
|
||||||
|
String filename = getPartName(part);
|
||||||
|
|
||||||
|
text.append("\r\n\r\n");
|
||||||
|
int len = filename.length();
|
||||||
|
if (len > 0) {
|
||||||
|
if (len > TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH - FILENAME_SUFFIX_LENGTH) {
|
||||||
|
filename = filename.substring(0, TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH -
|
||||||
|
FILENAME_SUFFIX_LENGTH - 3) + "...";
|
||||||
|
}
|
||||||
|
text.append(FILENAME_PREFIX);
|
||||||
|
text.append(filename);
|
||||||
|
text.append(FILENAME_SUFFIX);
|
||||||
|
text.append(TEXT_DIVIDER.substring(0, TEXT_DIVIDER_LENGTH -
|
||||||
|
FILENAME_PREFIX_LENGTH - filename.length() - FILENAME_SUFFIX_LENGTH));
|
||||||
|
} else {
|
||||||
|
text.append(TEXT_DIVIDER);
|
||||||
|
}
|
||||||
|
text.append("\r\n\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract important header values from a message to display inline (plain text version).
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* A {@link android.content.Context} instance that will be used to get localized strings.
|
||||||
|
* @param text
|
||||||
|
* The {@link StringBuilder} that will receive the (plain text) output.
|
||||||
|
* @param message
|
||||||
|
* The message to extract the header values from.
|
||||||
|
*
|
||||||
|
* @throws com.fsck.k9.mail.MessagingException
|
||||||
|
* In case of an error.
|
||||||
|
*/
|
||||||
|
private static void addMessageHeaderText(Context context, StringBuilder text, Message message)
|
||||||
|
throws MessagingException {
|
||||||
|
// From: <sender>
|
||||||
|
Address[] from = message.getFrom();
|
||||||
|
if (from != null && from.length > 0) {
|
||||||
|
text.append(context.getString(R.string.message_compose_quote_header_from));
|
||||||
|
text.append(' ');
|
||||||
|
text.append(Address.toString(from));
|
||||||
|
text.append("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// To: <recipients>
|
||||||
|
Address[] to = message.getRecipients(Message.RecipientType.TO);
|
||||||
|
if (to != null && to.length > 0) {
|
||||||
|
text.append(context.getString(R.string.message_compose_quote_header_to));
|
||||||
|
text.append(' ');
|
||||||
|
text.append(Address.toString(to));
|
||||||
|
text.append("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cc: <recipients>
|
||||||
|
Address[] cc = message.getRecipients(Message.RecipientType.CC);
|
||||||
|
if (cc != null && cc.length > 0) {
|
||||||
|
text.append(context.getString(R.string.message_compose_quote_header_cc));
|
||||||
|
text.append(' ');
|
||||||
|
text.append(Address.toString(cc));
|
||||||
|
text.append("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Date: <date>
|
||||||
|
Date date = message.getSentDate();
|
||||||
|
if (date != null) {
|
||||||
|
text.append(context.getString(R.string.message_compose_quote_header_send_date));
|
||||||
|
text.append(' ');
|
||||||
|
text.append(date.toString());
|
||||||
|
text.append("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subject: <subject>
|
||||||
|
String subject = message.getSubject();
|
||||||
|
text.append(context.getString(R.string.message_compose_quote_header_subject));
|
||||||
|
text.append(' ');
|
||||||
|
if (subject == null) {
|
||||||
|
text.append(context.getString(R.string.general_no_subject));
|
||||||
|
} else {
|
||||||
|
text.append(subject);
|
||||||
|
}
|
||||||
|
text.append("\r\n\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract important header values from a message to display inline (HTML version).
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* A {@link android.content.Context} instance that will be used to get localized strings.
|
||||||
|
* @param html
|
||||||
|
* The {@link StringBuilder} that will receive the (HTML) output.
|
||||||
|
* @param message
|
||||||
|
* The message to extract the header values from.
|
||||||
|
*
|
||||||
|
* @throws com.fsck.k9.mail.MessagingException
|
||||||
|
* In case of an error.
|
||||||
|
*/
|
||||||
|
private static void addMessageHeaderHtml(Context context, StringBuilder html, Message message)
|
||||||
|
throws MessagingException {
|
||||||
|
|
||||||
|
html.append("<table style=\"border: 0\">");
|
||||||
|
|
||||||
|
// From: <sender>
|
||||||
|
Address[] from = message.getFrom();
|
||||||
|
if (from != null && from.length > 0) {
|
||||||
|
addTableRow(html, context.getString(R.string.message_compose_quote_header_from),
|
||||||
|
Address.toString(from));
|
||||||
|
}
|
||||||
|
|
||||||
|
// To: <recipients>
|
||||||
|
Address[] to = message.getRecipients(Message.RecipientType.TO);
|
||||||
|
if (to != null && to.length > 0) {
|
||||||
|
addTableRow(html, context.getString(R.string.message_compose_quote_header_to),
|
||||||
|
Address.toString(to));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cc: <recipients>
|
||||||
|
Address[] cc = message.getRecipients(Message.RecipientType.CC);
|
||||||
|
if (cc != null && cc.length > 0) {
|
||||||
|
addTableRow(html, context.getString(R.string.message_compose_quote_header_cc),
|
||||||
|
Address.toString(cc));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Date: <date>
|
||||||
|
Date date = message.getSentDate();
|
||||||
|
if (date != null) {
|
||||||
|
addTableRow(html, context.getString(R.string.message_compose_quote_header_send_date),
|
||||||
|
date.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subject: <subject>
|
||||||
|
String subject = message.getSubject();
|
||||||
|
addTableRow(html, context.getString(R.string.message_compose_quote_header_subject),
|
||||||
|
(subject == null) ? context.getString(R.string.general_no_subject) : subject);
|
||||||
|
|
||||||
|
html.append("</table>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output an HTML table two column row with some hardcoded style.
|
||||||
|
*
|
||||||
|
* @param html
|
||||||
|
* The {@link StringBuilder} that will receive the output.
|
||||||
|
* @param header
|
||||||
|
* The string to be put in the {@code TH} element.
|
||||||
|
* @param value
|
||||||
|
* The string to be put in the {@code TD} element.
|
||||||
|
*/
|
||||||
|
private static void addTableRow(StringBuilder html, String header, String value) {
|
||||||
|
html.append("<tr><th style=\"text-align: left; vertical-align: top;\">");
|
||||||
|
html.append(header);
|
||||||
|
html.append("</th>");
|
||||||
|
html.append("<td>");
|
||||||
|
html.append(value);
|
||||||
|
html.append("</td></tr>");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ViewableContainer extractTextual(Part part) throws MessagingException {
|
||||||
|
String text = "";
|
||||||
|
String html = "";
|
||||||
|
List<Part> attachments = new ArrayList<Part>();
|
||||||
|
|
||||||
|
Body firstBody = part.getBody();
|
||||||
|
if (part.isMimeType("text/plain")) {
|
||||||
|
String bodyText = part.getText();
|
||||||
|
if (bodyText != null) {
|
||||||
|
text = bodyText;
|
||||||
|
html = HtmlConverter.textToHtml(text);
|
||||||
|
}
|
||||||
|
} else if (part.isMimeType("multipart/alternative") &&
|
||||||
|
firstBody instanceof MimeMultipart) {
|
||||||
|
MimeMultipart multipart = (MimeMultipart) firstBody;
|
||||||
|
for (BodyPart bodyPart : multipart.getBodyParts()) {
|
||||||
|
String bodyText = bodyPart.getText();
|
||||||
|
if (bodyText != null) {
|
||||||
|
if (text.isEmpty() && bodyPart.isMimeType("text/plain")) {
|
||||||
|
text = bodyText;
|
||||||
|
} else if (html.isEmpty() && bodyPart.isMimeType("text/html")) {
|
||||||
|
html = bodyText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ViewableContainer(text, html, attachments);
|
||||||
|
}
|
||||||
|
}
|
34
src/com/fsck/k9/mailstore/ViewableContainer.java
Normal file
34
src/com/fsck/k9/mailstore/ViewableContainer.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package com.fsck.k9.mailstore;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.Part;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store viewable text of a message as plain text and HTML, and the parts considered
|
||||||
|
* attachments.
|
||||||
|
*
|
||||||
|
* @see LocalMessageExtractor#extractTextAndAttachments(android.content.Context, com.fsck.k9.mail.Message)
|
||||||
|
*/
|
||||||
|
class ViewableContainer {
|
||||||
|
/**
|
||||||
|
* The viewable text of the message in plain text.
|
||||||
|
*/
|
||||||
|
public final String text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The viewable text of the message in HTML.
|
||||||
|
*/
|
||||||
|
public final String html;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parts of the message considered attachments (everything not viewable).
|
||||||
|
*/
|
||||||
|
public final List<Part> attachments;
|
||||||
|
|
||||||
|
public ViewableContainer(String text, String html, List<Part> attachments) {
|
||||||
|
this.text = text;
|
||||||
|
this.html = html;
|
||||||
|
this.attachments = attachments;
|
||||||
|
}
|
||||||
|
}
|
@ -215,8 +215,7 @@ public class MessageOpenPgpView extends LinearLayout {
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
// check for PGP/MIME encryption
|
// check for PGP/MIME encryption
|
||||||
Part pgp = MimeUtility
|
Part pgp = message.findFirstPartByMimeType("application/pgp-encrypted");
|
||||||
.findFirstPartByMimeType(message, "application/pgp-encrypted");
|
|
||||||
if (pgp != null) {
|
if (pgp != null) {
|
||||||
Toast.makeText(mContext, R.string.pgp_mime_unsupported, Toast.LENGTH_LONG)
|
Toast.makeText(mContext, R.string.pgp_mime_unsupported, Toast.LENGTH_LONG)
|
||||||
.show();
|
.show();
|
||||||
@ -241,12 +240,12 @@ public class MessageOpenPgpView extends LinearLayout {
|
|||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
// get data String
|
// get data String
|
||||||
Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
Part part = message.findFirstPartByMimeType("text/plain");
|
||||||
if (part == null) {
|
if (part == null) {
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
part = message.findFirstPartByMimeType("text/html");
|
||||||
}
|
}
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
mData = MimeUtility.getTextFromPart(part);
|
mData = part.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for service to be bound
|
// wait for service to be bound
|
||||||
|
@ -10,7 +10,7 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.mail.internet.HtmlConverter;
|
import com.fsck.k9.helper.HtmlConverter;
|
||||||
|
|
||||||
public class MessageWebView extends RigidWebView {
|
public class MessageWebView extends RigidWebView {
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ import com.fsck.k9.fragment.MessageViewFragment;
|
|||||||
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.FileHelper;
|
import com.fsck.k9.helper.FileHelper;
|
||||||
import com.fsck.k9.mail.internet.HtmlConverter;
|
import com.fsck.k9.helper.HtmlConverter;
|
||||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
import com.fsck.k9.helper.UrlEncodingHelper;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.fsck.k9.mail.internet;
|
package com.fsck.k9.activity;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
@ -8,6 +8,7 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
import com.fsck.k9.Account.QuoteStyle;
|
import com.fsck.k9.Account.QuoteStyle;
|
||||||
import com.fsck.k9.activity.TextBodyBuilder;
|
import com.fsck.k9.activity.TextBodyBuilder;
|
||||||
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
class TestingTextBodyBuilder extends TextBodyBuilder {
|
class TestingTextBodyBuilder extends TextBodyBuilder {
|
||||||
|
|
@ -0,0 +1,94 @@
|
|||||||
|
package com.fsck.k9.mail.internet;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class CharsetSupportTest extends TestCase {
|
||||||
|
|
||||||
|
public void testFixupCharset() throws Exception {
|
||||||
|
String charsetOnMail;
|
||||||
|
String expect;
|
||||||
|
|
||||||
|
charsetOnMail = "CP932";
|
||||||
|
expect = "shift_jis";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, new MimeMessage()));
|
||||||
|
|
||||||
|
// charsetOnMail = "koi8-u";
|
||||||
|
// expect = "koi8-r";
|
||||||
|
// assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, new MimeMessage()));
|
||||||
|
|
||||||
|
MimeMessage message;
|
||||||
|
|
||||||
|
message= new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@docomo.ne.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-docomo-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@dwmail.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-docomo-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@pdx.ne.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-docomo-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@willcom.com");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-docomo-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@emnet.ne.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-docomo-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@emobile.ne.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-docomo-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@softbank.ne.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-softbank-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@vodafone.ne.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-softbank-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@disney.ne.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-softbank-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@vertuclub.ne.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-softbank-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@ezweb.ne.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-kddi-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
message = new MimeMessage();
|
||||||
|
message.setHeader("From", "aaa@ido.ne.jp");
|
||||||
|
charsetOnMail = "shift_jis";
|
||||||
|
expect = "x-kddi-shift_jis-2007";
|
||||||
|
assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -8,15 +8,6 @@ import com.fsck.k9.mail.MessagingException;
|
|||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
public class MimeUtilityTest extends TestCase {
|
public class MimeUtilityTest extends TestCase {
|
||||||
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void tearDown() throws Exception {
|
|
||||||
super.tearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testGetHeaderParameter() {
|
public void testGetHeaderParameter() {
|
||||||
String result;
|
String result;
|
||||||
|
|
||||||
@ -55,93 +46,4 @@ public class MimeUtilityTest extends TestCase {
|
|||||||
result = MimeUtility.getHeaderParameter("text/HTML ; charset=\"windows-1251\"", null);
|
result = MimeUtility.getHeaderParameter("text/HTML ; charset=\"windows-1251\"", null);
|
||||||
assertEquals("text/HTML", result);
|
assertEquals("text/HTML", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFixupCharset() throws MessagingException {
|
|
||||||
String charsetOnMail;
|
|
||||||
String expect;
|
|
||||||
|
|
||||||
charsetOnMail = "CP932";
|
|
||||||
expect = "shift_jis";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, new MimeMessage()));
|
|
||||||
|
|
||||||
// charsetOnMail = "koi8-u";
|
|
||||||
// expect = "koi8-r";
|
|
||||||
// assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, new MimeMessage()));
|
|
||||||
|
|
||||||
MimeMessage message;
|
|
||||||
|
|
||||||
message= new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@docomo.ne.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-docomo-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@dwmail.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-docomo-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@pdx.ne.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-docomo-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@willcom.com");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-docomo-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@emnet.ne.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-docomo-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@emobile.ne.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-docomo-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@softbank.ne.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-softbank-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@vodafone.ne.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-softbank-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@disney.ne.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-softbank-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@vertuclub.ne.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-softbank-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@ezweb.ne.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-kddi-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
message = new MimeMessage();
|
|
||||||
message.setHeader("From", "aaa@ido.ne.jp");
|
|
||||||
charsetOnMail = "shift_jis";
|
|
||||||
expect = "x-kddi-shift_jis-2007";
|
|
||||||
assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
package com.fsck.k9.mail.internet;
|
package com.fsck.k9.helper;
|
||||||
|
|
||||||
|
import com.fsck.k9.helper.HtmlConverter;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
@ -14,11 +14,11 @@ import android.test.AndroidTestCase;
|
|||||||
import com.fsck.k9.mail.Message.RecipientType;
|
import com.fsck.k9.mail.Message.RecipientType;
|
||||||
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
||||||
import com.fsck.k9.mail.internet.BinaryTempFileMessageBody;
|
import com.fsck.k9.mail.internet.BinaryTempFileMessageBody;
|
||||||
|
import com.fsck.k9.mail.internet.CharsetSupport;
|
||||||
import com.fsck.k9.mail.internet.MimeBodyPart;
|
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||||
import com.fsck.k9.mail.internet.MimeHeader;
|
import com.fsck.k9.mail.internet.MimeHeader;
|
||||||
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.mail.internet.MimeUtility;
|
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
public class MessageTest extends AndroidTestCase {
|
public class MessageTest extends AndroidTestCase {
|
||||||
@ -356,7 +356,7 @@ public class MessageTest extends AndroidTestCase {
|
|||||||
+ "End of test.\r\n");
|
+ "End of test.\r\n");
|
||||||
textBody.setCharset("utf-8");
|
textBody.setCharset("utf-8");
|
||||||
MimeBodyPart bodyPart = new MimeBodyPart(textBody, "text/plain");
|
MimeBodyPart bodyPart = new MimeBodyPart(textBody, "text/plain");
|
||||||
MimeUtility.setCharset("utf-8", bodyPart);
|
CharsetSupport.setCharset("utf-8", bodyPart);
|
||||||
bodyPart.setEncoding(encoding);
|
bodyPart.setEncoding(encoding);
|
||||||
return bodyPart;
|
return bodyPart;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.fsck.k9.mail.internet;
|
package com.fsck.k9.mailstore;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -9,9 +9,14 @@ import com.fsck.k9.activity.K9ActivityCommon;
|
|||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Message.RecipientType;
|
import com.fsck.k9.mail.Message.RecipientType;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility.ViewableContainer;
|
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||||
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
public class ViewablesTest extends AndroidTestCase {
|
import static com.fsck.k9.mailstore.LocalMessageExtractor.extractTextAndAttachments;
|
||||||
|
|
||||||
|
public class LocalMessageExtractorTest extends AndroidTestCase {
|
||||||
|
|
||||||
public void testSimplePlainTextMessage() throws MessagingException {
|
public void testSimplePlainTextMessage() throws MessagingException {
|
||||||
String bodyText = "K-9 Mail rocks :>";
|
String bodyText = "K-9 Mail rocks :>";
|
||||||
@ -24,7 +29,7 @@ public class ViewablesTest extends AndroidTestCase {
|
|||||||
message.setBody(body);
|
message.setBody(body);
|
||||||
|
|
||||||
// Extract text
|
// Extract text
|
||||||
ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message);
|
ViewableContainer container = extractTextAndAttachments(getContext(), message);
|
||||||
|
|
||||||
String expectedText = bodyText;
|
String expectedText = bodyText;
|
||||||
String expectedHtml =
|
String expectedHtml =
|
||||||
@ -48,7 +53,7 @@ public class ViewablesTest extends AndroidTestCase {
|
|||||||
message.setBody(body);
|
message.setBody(body);
|
||||||
|
|
||||||
// Extract text
|
// Extract text
|
||||||
ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message);
|
ViewableContainer container = extractTextAndAttachments(getContext(), message);
|
||||||
|
|
||||||
String expectedText = "K-9 Mail rocks :>";
|
String expectedText = "K-9 Mail rocks :>";
|
||||||
String expectedHtml =
|
String expectedHtml =
|
||||||
@ -78,7 +83,7 @@ public class ViewablesTest extends AndroidTestCase {
|
|||||||
message.setBody(multipart);
|
message.setBody(multipart);
|
||||||
|
|
||||||
// Extract text
|
// Extract text
|
||||||
ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message);
|
ViewableContainer container = extractTextAndAttachments(getContext(), message);
|
||||||
|
|
||||||
String expectedText =
|
String expectedText =
|
||||||
bodyText1 + "\r\n\r\n" +
|
bodyText1 + "\r\n\r\n" +
|
||||||
@ -134,7 +139,7 @@ public class ViewablesTest extends AndroidTestCase {
|
|||||||
message.setBody(multipart);
|
message.setBody(multipart);
|
||||||
|
|
||||||
// Extract text
|
// Extract text
|
||||||
ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message);
|
ViewableContainer container = extractTextAndAttachments(getContext(), message);
|
||||||
|
|
||||||
String expectedText =
|
String expectedText =
|
||||||
bodyText +
|
bodyText +
|
Loading…
Reference in New Issue
Block a user