From 4f4e1386cb3c0a84152f992a99ee30be504da8a8 Mon Sep 17 00:00:00 2001 From: "Thomas E. Horner" Date: Tue, 13 Dec 2016 17:31:59 +0100 Subject: [PATCH] Forward as Attachment --- .../com/fsck/k9/activity/MessageCompose.java | 36 +++++++++++--- .../com/fsck/k9/activity/MessageList.java | 14 ++++++ .../activity/compose/AttachmentPresenter.java | 34 ++++++++++++- .../k9/activity/compose/MessageActions.java | 11 +++++ .../fsck/k9/fragment/MessageListFragment.java | 9 ++++ .../com/fsck/k9/message/MessageBuilder.java | 49 +++++++++---------- .../ui/messageview/MessageViewFragment.java | 7 +++ .../res/menu/message_list_item_context.xml | 3 ++ .../src/main/res/menu/message_list_option.xml | 3 ++ k9mail/src/main/res/values/strings.xml | 3 ++ .../fsck/k9/message/MessageBuilderTest.java | 10 ++-- 11 files changed, 136 insertions(+), 43 deletions(-) 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));