2008-11-01 17:32:06 -04:00
|
|
|
|
2009-12-14 21:50:53 -05:00
|
|
|
package com.fsck.k9.mail;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2012-03-05 15:04:34 -05:00
|
|
|
import java.io.IOException;
|
2008-11-01 17:32:06 -04:00
|
|
|
import java.util.Date;
|
|
|
|
import java.util.HashSet;
|
2010-05-21 11:34:29 -04:00
|
|
|
import java.util.Set;
|
2011-03-27 11:06:08 -04:00
|
|
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
2012-03-05 15:04:34 -05:00
|
|
|
import com.fsck.k9.K9;
|
2010-05-01 16:06:52 -04:00
|
|
|
import com.fsck.k9.activity.MessageReference;
|
2011-03-27 11:06:08 -04:00
|
|
|
import com.fsck.k9.mail.filter.CountingOutputStream;
|
|
|
|
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
2010-11-13 16:40:56 -05:00
|
|
|
import com.fsck.k9.mail.store.UnavailableStorageException;
|
2011-03-27 11:06:08 -04:00
|
|
|
|
2010-05-01 16:06:52 -04:00
|
|
|
|
Recursively convert attachments of type message/rfc822 to 7bit if necessary.
The preceding commit resulted in attachments of type message/rfc822 being
sent with 8bit encoding even when the SMTP server did not support
8BITMIME. This commit assures that messages will be converted to 7bit
when necessary.
A new interface CompositeBody was created that extends Body, and classes
Message and Multipart were changed from implementing Body to
CompositeBody. Additional classes BinaryTempFileMessageBody and
LocalAttachmentMessageBody were created (by extending BinaryTempFileBody
and LocalAttachmentBody, respectively), and they too implement
CompositeBody.
A CompositeBody is a Body containing a composite-type that can contain
subparts that may require recursive processing when converting from 8bit
to 7bit. The Part to which a CompositeBody belongs is only permitted to
use 8bit or 7bit encoding for the CompositeBody.
Previously, a Message was created so that it was 7bit clean by default
(even though that meant base64 encoding all attachments, including
messages). Then, if the SMTP server supported 8BITMIME,
Message.setEncoding("8bit") was called so that bodies of type TextBody
would been transmitted using 8bit encoding rather than quoted-printable.
Now, messages are created with 8bit encoding by default. Then, if the
SMTP server does not support 8BITMIME, Message.setUsing7bitTransport is
called to recursively convert the message and its subparts to 7bit. The
method setUsing7bitTransport was added to the interfaces Part and
CompositeBody.
setEncoding no longer iterates over parts in Multipart. That task belongs
to setUsing7bitTransport, which may in turn call setEncoding on the parts.
MimeUtility.getEncodingforType was created as a helper function for
choosing a default encoding that should be used for a given MIME type when
an attachment is added to a message (either while composing or when
retrieving from LocalStore).
setEncoding was implemented in MimeBodyPart to assure that the encoding
set in the Part's headers was the same as set for the Part's Body. (The
method already existed in MimeMessage, which has similarities with
MimeBodyPart.)
MimeMessage.parse(InputStream in, boolean recurse) was implemented so that
the parser could be told to recursively process nested messages read from
the InputStream, thus giving access to all subparts at any level that may
need to be converted from 8bit to 7bit.
2013-09-02 23:49:28 -04:00
|
|
|
public abstract class Message implements Part, CompositeBody {
|
2010-08-07 11:10:07 -04:00
|
|
|
private static final Flag[] EMPTY_FLAG_ARRAY = new Flag[0];
|
|
|
|
|
2010-05-01 16:06:52 -04:00
|
|
|
private MessageReference mReference = null;
|
2010-05-11 22:51:59 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public enum RecipientType {
|
2008-11-01 17:32:06 -04:00
|
|
|
TO, CC, BCC,
|
|
|
|
}
|
|
|
|
|
|
|
|
protected String mUid;
|
|
|
|
|
2014-02-15 17:48:35 -05:00
|
|
|
protected Set<Flag> mFlags = new HashSet<Flag>();
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
protected Date mInternalDate;
|
|
|
|
|
|
|
|
protected Folder mFolder;
|
2010-07-06 06:29:26 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean olderThan(Date earliestDate) {
|
|
|
|
if (earliestDate == null) {
|
2010-05-30 17:20:47 -04:00
|
|
|
return false;
|
|
|
|
}
|
2010-07-13 17:16:49 -04:00
|
|
|
Date myDate = getSentDate();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (myDate == null) {
|
2010-07-13 17:16:49 -04:00
|
|
|
myDate = getInternalDate();
|
2010-05-30 17:20:47 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (myDate != null) {
|
2010-05-30 17:20:47 -04:00
|
|
|
return myDate.before(earliestDate);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2010-05-03 09:46:55 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean equals(Object o) {
|
|
|
|
if (o == null || !(o instanceof Message)) {
|
2010-05-01 16:06:52 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Message other = (Message)o;
|
2012-09-12 22:56:17 -04:00
|
|
|
return (mUid.equals(other.getUid())
|
|
|
|
&& mFolder.getName().equals(other.getFolder().getName())
|
|
|
|
&& mFolder.getAccount().getUuid().equals(other.getFolder().getAccount().getUuid()));
|
2010-05-01 16:06:52 -04:00
|
|
|
}
|
2010-05-11 22:51:59 -04:00
|
|
|
|
2010-05-03 09:46:55 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public int hashCode() {
|
2010-05-03 09:46:55 -04:00
|
|
|
final int MULTIPLIER = 31;
|
|
|
|
|
|
|
|
int result = 1;
|
|
|
|
result = MULTIPLIER * result + mFolder.getName().hashCode();
|
|
|
|
result = MULTIPLIER * result + mFolder.getAccount().getUuid().hashCode();
|
|
|
|
result = MULTIPLIER * result + mUid.hashCode();
|
|
|
|
return result;
|
|
|
|
}
|
2010-05-11 22:51:59 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getUid() {
|
2008-11-01 17:32:06 -04:00
|
|
|
return mUid;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setUid(String uid) {
|
2010-05-01 16:06:52 -04:00
|
|
|
mReference = null;
|
2008-11-01 17:32:06 -04:00
|
|
|
this.mUid = uid;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public Folder getFolder() {
|
2008-11-01 17:32:06 -04:00
|
|
|
return mFolder;
|
|
|
|
}
|
|
|
|
|
2010-11-30 22:02:13 -05:00
|
|
|
public abstract String getSubject();
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
public abstract void setSubject(String subject) throws MessagingException;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public Date getInternalDate() {
|
2008-11-01 17:32:06 -04:00
|
|
|
return mInternalDate;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setInternalDate(Date internalDate) {
|
2008-11-01 17:32:06 -04:00
|
|
|
this.mInternalDate = internalDate;
|
|
|
|
}
|
|
|
|
|
2010-05-30 17:20:47 -04:00
|
|
|
public abstract Date getSentDate();
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
public abstract void setSentDate(Date sentDate) throws MessagingException;
|
|
|
|
|
|
|
|
public abstract Address[] getRecipients(RecipientType type) throws MessagingException;
|
|
|
|
|
|
|
|
public abstract void setRecipients(RecipientType type, Address[] addresses)
|
2009-11-24 19:40:29 -05:00
|
|
|
throws MessagingException;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setRecipient(RecipientType type, Address address) throws MessagingException {
|
|
|
|
setRecipients(type, new Address[] {
|
2009-12-06 19:56:06 -05:00
|
|
|
address
|
|
|
|
});
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2010-11-30 22:02:13 -05:00
|
|
|
public abstract Address[] getFrom();
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
public abstract void setFrom(Address from) throws MessagingException;
|
|
|
|
|
2010-11-30 22:02:13 -05:00
|
|
|
public abstract Address[] getReplyTo();
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
public abstract void setReplyTo(Address[] from) throws MessagingException;
|
|
|
|
|
2009-11-17 16:13:29 -05:00
|
|
|
public abstract String getMessageId() throws MessagingException;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-11-17 16:13:29 -05:00
|
|
|
public abstract void setInReplyTo(String inReplyTo) throws MessagingException;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-11-17 16:13:29 -05:00
|
|
|
public abstract String[] getReferences() throws MessagingException;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-11-17 16:13:29 -05:00
|
|
|
public abstract void setReferences(String references) throws MessagingException;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-11-30 22:02:13 -05:00
|
|
|
public abstract Body getBody();
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
public abstract String getContentType() throws MessagingException;
|
|
|
|
|
|
|
|
public abstract void addHeader(String name, String value) throws MessagingException;
|
|
|
|
|
|
|
|
public abstract void setHeader(String name, String value) throws MessagingException;
|
|
|
|
|
|
|
|
public abstract String[] getHeader(String name) throws MessagingException;
|
|
|
|
|
2010-11-13 16:40:56 -05:00
|
|
|
public abstract Set<String> getHeaderNames() throws UnavailableStorageException;
|
2010-05-21 11:34:29 -04:00
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
public abstract void removeHeader(String name) throws MessagingException;
|
|
|
|
|
|
|
|
public abstract void setBody(Body body) throws MessagingException;
|
|
|
|
|
2012-03-05 15:04:34 -05:00
|
|
|
public abstract long getId();
|
|
|
|
|
|
|
|
public abstract String getPreview();
|
|
|
|
public abstract boolean hasAttachments();
|
|
|
|
|
2013-01-05 07:20:46 -05:00
|
|
|
/*
|
|
|
|
* calculateContentPreview
|
|
|
|
* Takes a plain text message body as a string.
|
|
|
|
* Returns a message summary as a string suitable for showing in a message list
|
|
|
|
*
|
|
|
|
* A message summary should be about the first 160 characters
|
|
|
|
* of unique text written by the message sender
|
|
|
|
* Quoted text, "On $date" and so on will be stripped out.
|
|
|
|
* All newlines and whitespace will be compressed.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public static String calculateContentPreview(String text) {
|
|
|
|
if (text == null) {
|
|
|
|
return null;
|
|
|
|
}
|
2012-03-05 15:04:34 -05:00
|
|
|
|
2013-01-05 07:20:46 -05:00
|
|
|
// Only look at the first 8k of a message when calculating
|
|
|
|
// the preview. This should avoid unnecessary
|
|
|
|
// memory usage on large messages
|
|
|
|
if (text.length() > 8192) {
|
|
|
|
text = text.substring(0, 8192);
|
|
|
|
}
|
|
|
|
|
2013-01-07 04:39:08 -05:00
|
|
|
// Remove (correctly delimited by '-- \n') signatures
|
|
|
|
text = text.replaceAll("(?ms)^-- [\\r\\n]+.*", "");
|
2013-01-05 07:20:46 -05:00
|
|
|
// try to remove lines of dashes in the preview
|
|
|
|
text = text.replaceAll("(?m)^----.*?$", "");
|
|
|
|
// remove quoted text from the preview
|
|
|
|
text = text.replaceAll("(?m)^[#>].*$", "");
|
|
|
|
// Remove a common quote header from the preview
|
|
|
|
text = text.replaceAll("(?m)^On .*wrote.?$", "");
|
|
|
|
// Remove a more generic quote header from the preview
|
|
|
|
text = text.replaceAll("(?m)^.*\\w+:$", "");
|
|
|
|
// Remove horizontal rules.
|
|
|
|
text = text.replaceAll("\\s*([-=_]{30,}+)\\s*", " ");
|
|
|
|
|
|
|
|
// URLs in the preview should just be shown as "..." - They're not
|
|
|
|
// clickable and they usually overwhelm the preview
|
|
|
|
text = text.replaceAll("https?://\\S+", "...");
|
|
|
|
// Don't show newlines in the preview
|
|
|
|
text = text.replaceAll("(\\r|\\n)+", " ");
|
|
|
|
// Collapse whitespace in the preview
|
|
|
|
text = text.replaceAll("\\s+", " ");
|
|
|
|
// Remove any whitespace at the beginning and end of the string.
|
|
|
|
text = text.trim();
|
|
|
|
|
|
|
|
return (text.length() <= 512) ? text : text.substring(0, 512);
|
|
|
|
}
|
2012-03-05 15:04:34 -05:00
|
|
|
|
2010-11-30 22:07:28 -05:00
|
|
|
public void delete(String trashFolderName) throws MessagingException {}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
/*
|
2009-11-24 19:40:29 -05:00
|
|
|
* TODO Refactor Flags at some point to be able to store user defined flags.
|
2008-11-01 17:32:06 -04:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public Flag[] getFlags() {
|
2010-08-07 11:10:07 -04:00
|
|
|
return mFlags.toArray(EMPTY_FLAG_ARRAY);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2010-09-12 02:11:08 -04:00
|
|
|
/**
|
|
|
|
* @param flag
|
|
|
|
* Flag to set. Never <code>null</code>.
|
|
|
|
* @param set
|
|
|
|
* If <code>true</code>, the flag is added. If <code>false</code>
|
|
|
|
* , the flag is removed.
|
|
|
|
* @throws MessagingException
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setFlag(Flag flag, boolean set) throws MessagingException {
|
|
|
|
if (set) {
|
2008-11-01 17:32:06 -04:00
|
|
|
mFlags.add(flag);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
mFlags.remove(flag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method calls setFlag(Flag, boolean)
|
|
|
|
* @param flags
|
|
|
|
* @param set
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setFlags(Flag[] flags, boolean set) throws MessagingException {
|
|
|
|
for (Flag flag : flags) {
|
2008-11-01 17:32:06 -04:00
|
|
|
setFlag(flag, set);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isSet(Flag flag) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return mFlags.contains(flag);
|
|
|
|
}
|
|
|
|
|
2010-11-12 16:38:02 -05:00
|
|
|
|
|
|
|
public void destroy() throws MessagingException {}
|
|
|
|
|
Recursively convert attachments of type message/rfc822 to 7bit if necessary.
The preceding commit resulted in attachments of type message/rfc822 being
sent with 8bit encoding even when the SMTP server did not support
8BITMIME. This commit assures that messages will be converted to 7bit
when necessary.
A new interface CompositeBody was created that extends Body, and classes
Message and Multipart were changed from implementing Body to
CompositeBody. Additional classes BinaryTempFileMessageBody and
LocalAttachmentMessageBody were created (by extending BinaryTempFileBody
and LocalAttachmentBody, respectively), and they too implement
CompositeBody.
A CompositeBody is a Body containing a composite-type that can contain
subparts that may require recursive processing when converting from 8bit
to 7bit. The Part to which a CompositeBody belongs is only permitted to
use 8bit or 7bit encoding for the CompositeBody.
Previously, a Message was created so that it was 7bit clean by default
(even though that meant base64 encoding all attachments, including
messages). Then, if the SMTP server supported 8BITMIME,
Message.setEncoding("8bit") was called so that bodies of type TextBody
would been transmitted using 8bit encoding rather than quoted-printable.
Now, messages are created with 8bit encoding by default. Then, if the
SMTP server does not support 8BITMIME, Message.setUsing7bitTransport is
called to recursively convert the message and its subparts to 7bit. The
method setUsing7bitTransport was added to the interfaces Part and
CompositeBody.
setEncoding no longer iterates over parts in Multipart. That task belongs
to setUsing7bitTransport, which may in turn call setEncoding on the parts.
MimeUtility.getEncodingforType was created as a helper function for
choosing a default encoding that should be used for a given MIME type when
an attachment is added to a message (either while composing or when
retrieving from LocalStore).
setEncoding was implemented in MimeBodyPart to assure that the encoding
set in the Part's headers was the same as set for the Part's Body. (The
method already existed in MimeMessage, which has similarities with
MimeBodyPart.)
MimeMessage.parse(InputStream in, boolean recurse) was implemented so that
the parser could be told to recursively process nested messages read from
the InputStream, thus giving access to all subparts at any level that may
need to be converted from 8bit to 7bit.
2013-09-02 23:49:28 -04:00
|
|
|
public abstract void setEncoding(String encoding) throws UnavailableStorageException, MessagingException;
|
2010-05-11 22:51:59 -04:00
|
|
|
|
2011-01-04 08:25:59 -05:00
|
|
|
public abstract void setCharset(String charset) throws MessagingException;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public MessageReference makeMessageReference() {
|
|
|
|
if (mReference == null) {
|
2010-05-01 16:06:52 -04:00
|
|
|
mReference = new MessageReference();
|
|
|
|
mReference.accountUuid = getFolder().getAccount().getUuid();
|
|
|
|
mReference.folderName = getFolder().getName();
|
|
|
|
mReference.uid = mUid;
|
|
|
|
}
|
|
|
|
return mReference;
|
|
|
|
}
|
2010-05-11 22:51:59 -04:00
|
|
|
|
2011-03-27 11:06:08 -04:00
|
|
|
public long calculateSize() {
|
|
|
|
try {
|
|
|
|
|
|
|
|
CountingOutputStream out = new CountingOutputStream();
|
|
|
|
EOLConvertingOutputStream eolOut = new EOLConvertingOutputStream(out);
|
|
|
|
writeTo(eolOut);
|
|
|
|
eolOut.flush();
|
|
|
|
return out.getCount();
|
|
|
|
} catch (IOException e) {
|
2011-11-03 01:18:30 -04:00
|
|
|
Log.e(K9.LOG_TAG, "Failed to calculate a message size", e);
|
2011-03-27 11:06:08 -04:00
|
|
|
} catch (MessagingException e) {
|
2011-11-03 01:18:30 -04:00
|
|
|
Log.e(K9.LOG_TAG, "Failed to calculate a message size", e);
|
2011-03-27 11:06:08 -04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-03 20:27:51 -05:00
|
|
|
/**
|
|
|
|
* Copy the contents of this object into another {@code Message} object.
|
|
|
|
*
|
|
|
|
* @param destination
|
|
|
|
* The {@code Message} object to receive the contents of this instance.
|
|
|
|
*/
|
|
|
|
protected void copy(Message destination) {
|
|
|
|
destination.mUid = mUid;
|
|
|
|
destination.mInternalDate = mInternalDate;
|
|
|
|
destination.mFolder = mFolder;
|
|
|
|
destination.mReference = mReference;
|
|
|
|
|
|
|
|
// mFlags contents can change during the object lifetime, so copy the Set
|
|
|
|
destination.mFlags = new HashSet<Flag>(mFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new {@code Message} object with the same content as this object.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* <strong>Note:</strong>
|
|
|
|
* This method was introduced as a hack to prevent {@code ConcurrentModificationException}s. It
|
|
|
|
* shouldn't be used unless absolutely necessary. See the comment in
|
|
|
|
* {@link com.fsck.k9.activity.MessageView.Listener#loadMessageForViewHeadersAvailable(com.fsck.k9.Account, String, String, Message)}
|
|
|
|
* for more information.
|
|
|
|
* </p>
|
|
|
|
*/
|
|
|
|
public abstract Message clone();
|
Recursively convert attachments of type message/rfc822 to 7bit if necessary.
The preceding commit resulted in attachments of type message/rfc822 being
sent with 8bit encoding even when the SMTP server did not support
8BITMIME. This commit assures that messages will be converted to 7bit
when necessary.
A new interface CompositeBody was created that extends Body, and classes
Message and Multipart were changed from implementing Body to
CompositeBody. Additional classes BinaryTempFileMessageBody and
LocalAttachmentMessageBody were created (by extending BinaryTempFileBody
and LocalAttachmentBody, respectively), and they too implement
CompositeBody.
A CompositeBody is a Body containing a composite-type that can contain
subparts that may require recursive processing when converting from 8bit
to 7bit. The Part to which a CompositeBody belongs is only permitted to
use 8bit or 7bit encoding for the CompositeBody.
Previously, a Message was created so that it was 7bit clean by default
(even though that meant base64 encoding all attachments, including
messages). Then, if the SMTP server supported 8BITMIME,
Message.setEncoding("8bit") was called so that bodies of type TextBody
would been transmitted using 8bit encoding rather than quoted-printable.
Now, messages are created with 8bit encoding by default. Then, if the
SMTP server does not support 8BITMIME, Message.setUsing7bitTransport is
called to recursively convert the message and its subparts to 7bit. The
method setUsing7bitTransport was added to the interfaces Part and
CompositeBody.
setEncoding no longer iterates over parts in Multipart. That task belongs
to setUsing7bitTransport, which may in turn call setEncoding on the parts.
MimeUtility.getEncodingforType was created as a helper function for
choosing a default encoding that should be used for a given MIME type when
an attachment is added to a message (either while composing or when
retrieving from LocalStore).
setEncoding was implemented in MimeBodyPart to assure that the encoding
set in the Part's headers was the same as set for the Part's Body. (The
method already existed in MimeMessage, which has similarities with
MimeBodyPart.)
MimeMessage.parse(InputStream in, boolean recurse) was implemented so that
the parser could be told to recursively process nested messages read from
the InputStream, thus giving access to all subparts at any level that may
need to be converted from 8bit to 7bit.
2013-09-02 23:49:28 -04:00
|
|
|
public abstract void setUsing7bitTransport() throws MessagingException;
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|