diff --git a/k9mail/src/main/java/com/fsck/k9/activity/MessageCompose.java b/k9mail/src/main/java/com/fsck/k9/activity/MessageCompose.java
index 8e9dc290d..6390a34f5 100644
--- a/k9mail/src/main/java/com/fsck/k9/activity/MessageCompose.java
+++ b/k9mail/src/main/java/com/fsck/k9/activity/MessageCompose.java
@@ -1,6 +1,7 @@
package com.fsck.k9.activity;
+import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -121,6 +122,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
public static final String ACTION_REPLY = "com.fsck.k9.intent.action.REPLY";
public static final String ACTION_REPLY_ALL = "com.fsck.k9.intent.action.REPLY_ALL";
public static final String ACTION_FORWARD = "com.fsck.k9.intent.action.FORWARD";
+ public static final String ACTION_FORWARD_AS_ATTACHMENT = "com.fsck.k9.intent.action.FORWARD_AS_ATTACHMENT";
public static final String ACTION_EDIT_DRAFT = "com.fsck.k9.intent.action.EDIT_DRAFT";
private static final String ACTION_AUTOCRYPT_PEER = "org.autocrypt.PEER_ACTION";
@@ -355,6 +357,8 @@ public class MessageCompose extends K9Activity implements OnClickListener,
this.action = Action.REPLY_ALL;
} else if (ACTION_FORWARD.equals(action)) {
this.action = Action.FORWARD;
+ } else if (ACTION_FORWARD_AS_ATTACHMENT.equals(action)) {
+ this.action = Action.FORWARD_AS_ATTACHMENT;
} else if (ACTION_EDIT_DRAFT.equals(action)) {
this.action = Action.EDIT_DRAFT;
} else {
@@ -388,7 +392,8 @@ public class MessageCompose extends K9Activity implements OnClickListener,
if (!relatedMessageProcessed) {
if (action == Action.REPLY || action == Action.REPLY_ALL ||
- action == Action.FORWARD || action == Action.EDIT_DRAFT) {
+ action == Action.FORWARD || action == Action.FORWARD_AS_ATTACHMENT ||
+ action == Action.EDIT_DRAFT) {
messageLoaderHelper = new MessageLoaderHelper(this, getLoaderManager(), getFragmentManager(),
messageLoaderCallbacks);
internalMessageHandler.sendEmptyMessage(MSG_PROGRESS_ON);
@@ -418,7 +423,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
recipientMvpView.requestFocusOnToField();
}
- if (action == Action.FORWARD) {
+ if (action == Action.FORWARD || action == Action.FORWARD_AS_ATTACHMENT) {
relatedMessageReference = relatedMessageReference.withModifiedFlag(Flag.FORWARDED);
}
@@ -1193,7 +1198,11 @@ public class MessageCompose extends K9Activity implements OnClickListener,
break;
}
case FORWARD: {
- processMessageToForward(messageViewInfo);
+ processMessageToForward(messageViewInfo, false);
+ break;
+ }
+ case FORWARD_AS_ATTACHMENT: {
+ processMessageToForward(messageViewInfo, true);
break;
}
case EDIT_DRAFT: {
@@ -1205,12 +1214,12 @@ public class MessageCompose extends K9Activity implements OnClickListener,
break;
}
}
- } catch (MessagingException me) {
+ } catch (MessagingException | IOException e) {
/*
* Let the user continue composing their message even if we have a problem processing
* the source message. Log it as an error, though.
*/
- Timber.e(me, "Error while processing source message: ");
+ Timber.e(e, "Error while processing source message: ");
} finally {
relatedMessageProcessed = true;
changesMadeSinceLastSave = false;
@@ -1268,7 +1277,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
}
- private void processMessageToForward(MessageViewInfo messageViewInfo) throws MessagingException {
+ private void processMessageToForward(MessageViewInfo messageViewInfo, boolean asAttachment) throws IOException, MessagingException {
Message message = messageViewInfo.message;
String subject = message.getSubject();
@@ -1290,8 +1299,12 @@ public class MessageCompose extends K9Activity implements OnClickListener,
}
// Quote the message and setup the UI.
- quotedMessagePresenter.processMessageToForward(messageViewInfo);
- attachmentPresenter.processMessageToForward(messageViewInfo);
+ if (asAttachment) {
+ attachmentPresenter.processMessageToForwardAsAttachment(messageViewInfo);
+ } else {
+ quotedMessagePresenter.processMessageToForward(messageViewInfo);
+ attachmentPresenter.processMessageToForward(messageViewInfo);
+ }
}
private void processDraftMessage(MessageViewInfo messageViewInfo) {
@@ -1772,6 +1785,12 @@ public class MessageCompose extends K9Activity implements OnClickListener,
Toast.makeText(MessageCompose.this,
getString(R.string.message_compose_attachments_skipped_toast), Toast.LENGTH_LONG).show();
}
+
+ @Override
+ public void showMissingAttachmentsPartialMessageForwardWarning() {
+ Toast.makeText(MessageCompose.this,
+ getString(R.string.message_compose_attachments_forward_toast), Toast.LENGTH_LONG).show();
+ }
};
private Handler internalMessageHandler = new Handler() {
@@ -1809,6 +1828,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
REPLY(R.string.compose_title_reply),
REPLY_ALL(R.string.compose_title_reply_all),
FORWARD(R.string.compose_title_forward),
+ FORWARD_AS_ATTACHMENT(R.string.compose_title_forward_as_attachment),
EDIT_DRAFT(R.string.compose_title_compose);
private final int titleResource;
diff --git a/k9mail/src/main/java/com/fsck/k9/activity/MessageList.java b/k9mail/src/main/java/com/fsck/k9/activity/MessageList.java
index 245690f14..33b2ccc9a 100644
--- a/k9mail/src/main/java/com/fsck/k9/activity/MessageList.java
+++ b/k9mail/src/main/java/com/fsck/k9/activity/MessageList.java
@@ -895,6 +895,10 @@ public class MessageList extends K9Activity implements MessageListFragmentListen
mMessageViewFragment.onForward();
return true;
}
+ case R.id.forward_as_attachment: {
+ mMessageViewFragment.onForwardAsAttachment();
+ return true;
+ }
case R.id.share: {
mMessageViewFragment.onSendAlternate();
return true;
@@ -1245,6 +1249,16 @@ public class MessageList extends K9Activity implements MessageListFragmentListen
MessageActions.actionForward(this, messageReference, decryptionResultForReply);
}
+ @Override
+ public void onForwardAsAttachment(MessageReference messageReference) {
+ onForwardAsAttachment(messageReference, null);
+ }
+
+ @Override
+ public void onForwardAsAttachment(MessageReference messageReference, Parcelable decryptionResultForReply) {
+ MessageActions.actionForwardAsAttachment(this, messageReference, decryptionResultForReply);
+ }
+
@Override
public void onReply(MessageReference messageReference) {
onReply(messageReference, null);
diff --git a/k9mail/src/main/java/com/fsck/k9/activity/compose/AttachmentPresenter.java b/k9mail/src/main/java/com/fsck/k9/activity/compose/AttachmentPresenter.java
index 46e1d4fd4..1462e5935 100644
--- a/k9mail/src/main/java/com/fsck/k9/activity/compose/AttachmentPresenter.java
+++ b/k9mail/src/main/java/com/fsck/k9/activity/compose/AttachmentPresenter.java
@@ -1,6 +1,9 @@
package com.fsck.k9.activity.compose;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -21,6 +24,7 @@ import com.fsck.k9.activity.loader.AttachmentContentLoader;
import com.fsck.k9.activity.loader.AttachmentInfoLoader;
import com.fsck.k9.activity.misc.Attachment;
import com.fsck.k9.activity.misc.Attachment.LoadingState;
+import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mailstore.AttachmentViewInfo;
import com.fsck.k9.mailstore.MessageViewInfo;
@@ -160,6 +164,10 @@ public class AttachmentPresenter {
}
public boolean loadNonInlineAttachments(MessageViewInfo messageViewInfo) {
+ return allPartsAvailable(messageViewInfo, true);
+ }
+
+ private boolean allPartsAvailable(MessageViewInfo messageViewInfo, boolean loadNonInlineAttachments) {
boolean allPartsAvailable = true;
for (AttachmentViewInfo attachmentViewInfo : messageViewInfo.attachments) {
@@ -170,7 +178,9 @@ public class AttachmentPresenter {
allPartsAvailable = false;
continue;
}
- addAttachment(attachmentViewInfo);
+ if (loadNonInlineAttachments) {
+ addAttachment(attachmentViewInfo);
+ }
}
return allPartsAvailable;
@@ -183,6 +193,27 @@ public class AttachmentPresenter {
}
}
+ public void processMessageToForwardAsAttachment(MessageViewInfo messageViewInfo) throws IOException, MessagingException {
+ boolean isMissingParts = !allPartsAvailable(messageViewInfo, false);
+ if (isMissingParts) {
+ attachmentMvpView.showMissingAttachmentsPartialMessageForwardWarning();
+ } else {
+ File tempFile = File.createTempFile("pre", ".tmp");
+ tempFile.deleteOnExit();
+ FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
+ messageViewInfo.message.writeTo(fileOutputStream);
+ fileOutputStream.close();
+
+ int loaderId = getNextFreeLoaderId();
+ Attachment attachment = Attachment.createAttachment(null, loaderId, "message/rfc822")
+ .deriveWithMetadataLoaded("message/rfc822", messageViewInfo.message.getSubject(), tempFile.length())
+ .deriveWithLoadComplete(tempFile.getAbsolutePath());
+
+ attachments.put(attachment.uri, attachment);
+ attachmentMvpView.addAttachmentView(attachment);
+ }
+ }
+
private void addAttachmentAndStartLoader(Attachment attachment) {
attachments.put(attachment.uri, attachment);
listener.onAttachmentAdded();
@@ -383,6 +414,7 @@ public class AttachmentPresenter {
void performSaveAfterChecks();
void showMissingAttachmentsPartialMessageWarning();
+ void showMissingAttachmentsPartialMessageForwardWarning();
}
public interface AttachmentsChangedListener {
diff --git a/k9mail/src/main/java/com/fsck/k9/activity/compose/MessageActions.java b/k9mail/src/main/java/com/fsck/k9/activity/compose/MessageActions.java
index e67bf5db6..31b93c959 100644
--- a/k9mail/src/main/java/com/fsck/k9/activity/compose/MessageActions.java
+++ b/k9mail/src/main/java/com/fsck/k9/activity/compose/MessageActions.java
@@ -71,6 +71,17 @@ public class MessageActions {
context.startActivity(i);
}
+ /**
+ * Compose a new message as a forward of the given message.
+ */
+ public static void actionForwardAsAttachment(Context context, MessageReference messageReference, Parcelable decryptionResult) {
+ Intent i = new Intent(context, MessageCompose.class);
+ i.putExtra(MessageCompose.EXTRA_MESSAGE_REFERENCE, messageReference.toIdentityString());
+ i.putExtra(MessageCompose.EXTRA_MESSAGE_DECRYPTION_RESULT, decryptionResult);
+ i.setAction(MessageCompose.ACTION_FORWARD_AS_ATTACHMENT);
+ context.startActivity(i);
+ }
+
/**
* Continue composition of the given message. This action modifies the way this Activity
* handles certain actions.
diff --git a/k9mail/src/main/java/com/fsck/k9/fragment/MessageListFragment.java b/k9mail/src/main/java/com/fsck/k9/fragment/MessageListFragment.java
index 20bc304e8..749db4c08 100644
--- a/k9mail/src/main/java/com/fsck/k9/fragment/MessageListFragment.java
+++ b/k9mail/src/main/java/com/fsck/k9/fragment/MessageListFragment.java
@@ -788,6 +788,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
fragmentListener.onForward(messageReference);
}
+ public void onForwardAsAttachment(MessageReference messageReference) {
+ fragmentListener.onForwardAsAttachment(messageReference);
+ }
+
private void onResendMessage(MessageReference messageReference) {
fragmentListener.onResendMessage(messageReference);
}
@@ -1116,6 +1120,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
onForward(getMessageAtPosition(adapterPosition));
break;
}
+ case R.id.forward_as_attachment: {
+ onForwardAsAttachment(getMessageAtPosition(adapterPosition));
+ break;
+ }
case R.id.send_again: {
onResendMessage(getMessageAtPosition(adapterPosition));
selectedCount = 0;
@@ -2376,6 +2384,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
void showMoreFromSameSender(String senderAddress);
void onResendMessage(MessageReference message);
void onForward(MessageReference message);
+ void onForwardAsAttachment(MessageReference message);
void onReply(MessageReference message);
void onReplyAll(MessageReference message);
void openMessage(MessageReference messageReference);
diff --git a/k9mail/src/main/java/com/fsck/k9/message/MessageBuilder.java b/k9mail/src/main/java/com/fsck/k9/message/MessageBuilder.java
index b8f1f0053..f7c117567 100644
--- a/k9mail/src/main/java/com/fsck/k9/message/MessageBuilder.java
+++ b/k9mail/src/main/java/com/fsck/k9/message/MessageBuilder.java
@@ -220,13 +220,6 @@ public abstract class MessageBuilder {
continue;
}
- String contentType = attachment.contentType;
- if (MimeUtil.isMessage(contentType)) {
- contentType = "application/octet-stream";
- // TODO reencode message body to 7 bit
- // body = new TempFileMessageBody(attachment.filename);
- }
-
Body body = new TempFileBody(attachment.filename);
MimeBodyPart bp = new MimeBodyPart(body);
@@ -236,30 +229,32 @@ public abstract class MessageBuilder {
* MimeHeader.writeTo().
*/
bp.addHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\r\n name=\"%s\"",
- contentType,
+ attachment.contentType,
EncoderUtil.encodeIfNecessary(attachment.name,
EncoderUtil.Usage.WORD_ENTITY, 7)));
- bp.setEncoding(MimeUtility.getEncodingforType(contentType));
+ if (!MimeUtil.isMessage(attachment.contentType)) {
+ bp.setEncoding(MimeUtility.getEncodingforType(attachment.contentType));
- /*
- * TODO: Oh the joys of MIME...
- *
- * From RFC 2183 (The Content-Disposition Header Field):
- * "Parameter values longer than 78 characters, or which
- * contain non-ASCII characters, MUST be encoded as specified
- * in [RFC 2184]."
- *
- * Example:
- *
- * Content-Type: application/x-stuff
- * title*1*=us-ascii'en'This%20is%20even%20more%20
- * title*2*=%2A%2A%2Afun%2A%2A%2A%20
- * title*3="isn't it!"
- */
- bp.addHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, String.format(Locale.US,
- "attachment;\r\n filename=\"%s\";\r\n size=%d",
- attachment.name, attachment.size));
+ /*
+ * TODO: Oh the joys of MIME...
+ *
+ * From RFC 2183 (The Content-Disposition Header Field):
+ * "Parameter values longer than 78 characters, or which
+ * contain non-ASCII characters, MUST be encoded as specified
+ * in [RFC 2184]."
+ *
+ * Example:
+ *
+ * Content-Type: application/x-stuff
+ * title*1*=us-ascii'en'This%20is%20even%20more%20
+ * title*2*=%2A%2A%2Afun%2A%2A%2A%20
+ * title*3="isn't it!"
+ */
+ bp.addHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, String.format(Locale.US,
+ "attachment;\r\n filename=\"%s\";\r\n size=%d",
+ attachment.name, attachment.size));
+ }
mp.addBodyPart(bp);
}
diff --git a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java
index f43490ec1..da52ab507 100644
--- a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java
+++ b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java
@@ -338,6 +338,12 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
}
}
+ public void onForwardAsAttachment() {
+ if (mMessage != null) {
+ mFragmentListener.onForwardAsAttachment(mMessage.makeMessageReference(), messageCryptoPresenter.getDecryptionResultForReply());
+ }
+ }
+
public void onToggleFlagged() {
if (mMessage != null) {
boolean newState = !mMessage.isSet(Flag.FLAGGED);
@@ -723,6 +729,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
public interface MessageViewFragmentListener {
void onForward(MessageReference messageReference, Parcelable decryptionResultForReply);
+ void onForwardAsAttachment(MessageReference messageReference, Parcelable decryptionResultForReply);
void disableDeleteAction();
void onReplyAll(MessageReference messageReference, Parcelable decryptionResultForReply);
void onReply(MessageReference messageReference, Parcelable decryptionResultForReply);
diff --git a/k9mail/src/main/res/menu/message_list_item_context.xml b/k9mail/src/main/res/menu/message_list_item_context.xml
index 40701cd2b..9cb661ad1 100644
--- a/k9mail/src/main/res/menu/message_list_item_context.xml
+++ b/k9mail/src/main/res/menu/message_list_item_context.xml
@@ -17,6 +17,9 @@
+
+
diff --git a/k9mail/src/main/res/values/strings.xml b/k9mail/src/main/res/values/strings.xml
index 590cd7561..004d495e9 100644
--- a/k9mail/src/main/res/values/strings.xml
+++ b/k9mail/src/main/res/values/strings.xml
@@ -97,6 +97,7 @@ Please submit bug reports, contribute new features and ask questions at
Reply
Reply all
Forward
+ Forward as attachment
Choose Account
Choose Folder
@@ -131,6 +132,7 @@ Please submit bug reports, contribute new features and ask questions at
Archive
Spam
Forward
+ Forward as attachment
Move
Send…
Refile…
@@ -276,6 +278,7 @@ Please submit bug reports, contribute new features and ask questions at
Recipient field contains incomplete input!
No email address could be found for this contact.
Some attachments cannot be forwarded because they have not been downloaded.
+ The message cannot be forwarded because some attachments have not been downloaded.
Quote message
Remove quoted text
Edit quoted text
diff --git a/k9mail/src/test/java/com/fsck/k9/message/MessageBuilderTest.java b/k9mail/src/test/java/com/fsck/k9/message/MessageBuilderTest.java
index 7bed92933..4242ed4d1 100644
--- a/k9mail/src/test/java/com/fsck/k9/message/MessageBuilderTest.java
+++ b/k9mail/src/test/java/com/fsck/k9/message/MessageBuilderTest.java
@@ -119,14 +119,10 @@ public class MessageBuilderTest {
"soviet message\r\n" +
"text =E2=98=AD\r\n" +
"--" + BOUNDARY_1 + "\r\n" +
- "Content-Type: application/octet-stream;\r\n" +
+ "Content-Type: message/rfc822;\r\n" +
" name=\"attach.txt\"\r\n" +
- "Content-Transfer-Encoding: base64\r\n" +
- "Content-Disposition: attachment;\r\n" +
- " filename=\"attach.txt\";\r\n" +
- " size=23\r\n" +
"\r\n" +
- "dGV4dCBkYXRhIGluIGF0dGFjaG1lbnQ=\r\n" +
+ "text data in attachment" +
"\r\n" +
"--" + BOUNDARY_1 + "--\r\n";
@@ -193,7 +189,7 @@ public class MessageBuilderTest {
}
@Test
- public void build_withMessageAttachment_shouldAttachAsApplicationOctetStream() throws Exception {
+ public void build_withMessageAttachment_shouldAttachAsMessageRfc822() throws Exception {
MessageBuilder messageBuilder = createSimpleMessageBuilder();
Attachment attachment = createAttachmentWithContent("message/rfc822", "attach.txt", TEST_ATTACHMENT_TEXT);
messageBuilder.setAttachments(Collections.singletonList(attachment));