1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-12-02 05:52:18 -05:00

Merge remote-tracking branch 'origin/pgp_mime_preparations' into pgp_crypto_header

This commit is contained in:
Vincent Breitmoser 2015-02-09 16:53:16 +01:00
commit d437314dc3
10 changed files with 377 additions and 188 deletions

View File

@ -20,7 +20,7 @@ public interface Part {
String getDisposition() throws MessagingException; String getDisposition() throws MessagingException;
String getContentId() throws MessagingException; String getContentId();
String[] getHeader(String name) throws MessagingException; String[] getHeader(String name) throws MessagingException;

View File

@ -96,7 +96,7 @@ public class MimeBodyPart extends BodyPart {
} }
@Override @Override
public String getContentId() throws MessagingException { public String getContentId() {
String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID); String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
if (contentId == null) { if (contentId == null) {
return null; return null;

View File

@ -174,7 +174,7 @@ public class MimeMessage extends Message {
} }
@Override @Override
public String getContentId() throws MessagingException { public String getContentId() {
return null; return null;
} }

View File

@ -448,18 +448,20 @@ public class LocalMessageExtractor {
attachments); attachments);
List<AttachmentViewInfo> attachmentInfos = extractAttachmentInfos(context, attachments); List<AttachmentViewInfo> attachmentInfos = extractAttachmentInfos(context, attachments);
if (pgpAnnotation != NO_ANNOTATIONS) { MessageViewContainer messageViewContainer;
if (pgpAnnotation == NO_ANNOTATIONS) {
messageViewContainer = new MessageViewContainer(viewable.html, part, attachmentInfos);
} else {
OpenPgpSignatureResult pgpResult = pgpAnnotation.getSignatureResult(); OpenPgpSignatureResult pgpResult = pgpAnnotation.getSignatureResult();
OpenPgpError pgpError = pgpAnnotation.getError(); OpenPgpError pgpError = pgpAnnotation.getError();
boolean wasEncrypted = pgpAnnotation.wasEncrypted(); boolean wasEncrypted = pgpAnnotation.wasEncrypted();
PendingIntent pendingIntent = pgpAnnotation.getPendingIntent(); PendingIntent pendingIntent = pgpAnnotation.getPendingIntent();
containers.add(new MessageViewContainer( messageViewContainer = new MessageViewContainer(viewable.html, part, attachmentInfos, pgpResult,
viewable.html, attachmentInfos, pgpResult, pgpError, wasEncrypted, pendingIntent)); pgpError, wasEncrypted, pendingIntent);
} else {
containers.add(new MessageViewContainer(viewable.html, attachmentInfos));
} }
containers.add(messageViewContainer);
} }
return new MessageViewInfo(containers, message); return new MessageViewInfo(containers, message);
@ -517,7 +519,7 @@ public class LocalMessageExtractor {
return attachments; return attachments;
} }
private static AttachmentViewInfo extractAttachmentInfo(Context context, Part part) throws MessagingException { public static AttachmentViewInfo extractAttachmentInfo(Context context, Part part) throws MessagingException {
if (part instanceof LocalPart) { if (part instanceof LocalPart) {
LocalPart localPart = (LocalPart) part; LocalPart localPart = (LocalPart) part;
String accountUuid = localPart.getAccountUuid(); String accountUuid = localPart.getAccountUuid();

View File

@ -1,62 +1,50 @@
package com.fsck.k9.mailstore; package com.fsck.k9.mailstore;
import android.app.PendingIntent;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import android.app.PendingIntent;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.Part;
import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
public class MessageViewInfo { public class MessageViewInfo {
public final Message message; public final Message message;
public final List<MessageViewContainer> containers; public final List<MessageViewContainer> containers;
@Deprecated
public MessageViewInfo(String text, List<AttachmentViewInfo> attachments, Message message) {
containers = new ArrayList<MessageViewContainer>();
containers.add(new MessageViewContainer(text, attachments));
this.message = message;
}
public MessageViewInfo(List<MessageViewContainer> containers, Message message) { public MessageViewInfo(List<MessageViewContainer> containers, Message message) {
this.containers = containers; this.containers = containers;
this.message = message; this.message = message;
} }
public static class MessageViewContainer { public static class MessageViewContainer {
public final String text;
public final Part rootPart;
public final List<AttachmentViewInfo> attachments;
public final boolean encrypted;
public final OpenPgpSignatureResult signatureResult;
public final OpenPgpError pgpError;
public final PendingIntent pgpPendingIntent;
final public String text; MessageViewContainer(String text, Part rootPart, List<AttachmentViewInfo> attachments) {
final public List<AttachmentViewInfo> attachments; this(text, rootPart, attachments, null, null, false, null);
final public boolean encrypted;
final public OpenPgpSignatureResult signatureResult;
final public OpenPgpError pgpError;
final public PendingIntent pgpPendingIntent;
MessageViewContainer(String text, List<AttachmentViewInfo> attachments) {
this.text = text;
this.attachments = attachments;
this.signatureResult = null;
this.pgpError = null;
this.encrypted = false;
this.pgpPendingIntent = null;
} }
MessageViewContainer(String text, List<AttachmentViewInfo> attachments, MessageViewContainer(String text, Part rootPart, List<AttachmentViewInfo> attachments,
OpenPgpSignatureResult signatureResult, OpenPgpError pgpError, OpenPgpSignatureResult signatureResult, OpenPgpError pgpError, boolean encrypted,
boolean encrypted, PendingIntent pgpPendingIntent) { PendingIntent pgpPendingIntent) {
this.text = text; this.text = text;
this.rootPart = rootPart;
this.attachments = attachments; this.attachments = attachments;
this.signatureResult = signatureResult; this.signatureResult = signatureResult;
this.pgpError = pgpError; this.pgpError = pgpError;
this.encrypted = encrypted; this.encrypted = encrypted;
this.pgpPendingIntent = pgpPendingIntent; this.pgpPendingIntent = pgpPendingIntent;
} }
} }
} }

View File

@ -0,0 +1,193 @@
package com.fsck.k9.ui.messageview;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.helper.FileHelper;
import com.fsck.k9.helper.UrlEncodingHelper;
import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.provider.AttachmentProvider.AttachmentProviderColumns;
import org.apache.commons.io.IOUtils;
@Deprecated
class DownloadImageTask extends AsyncTask<String, Void, String> {
private static final String[] ATTACHMENT_PROJECTION = new String[] {
AttachmentProviderColumns._ID,
AttachmentProviderColumns.DISPLAY_NAME
};
private static final int DISPLAY_NAME_INDEX = 1;
private static final String DEFAULT_FILE_NAME = "saved_image";
private final Context context;
public DownloadImageTask(Context context) {
this.context = context.getApplicationContext();
}
@Override
protected String doInBackground(String... params) {
String url = params[0];
try {
boolean isExternalImage = url.startsWith("http");
String fileName;
if (isExternalImage) {
fileName = downloadAndStoreImage(url);
} else {
fileName = fetchAndStoreImage(url);
}
return fileName;
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Error while downloading image", e);
return null;
}
}
@Override
protected void onPostExecute(String fileName) {
boolean errorSavingFile = (fileName == null);
String text;
if (errorSavingFile) {
text = context.getString(R.string.image_saving_failed);
} else {
text = context.getString(R.string.image_saved_as, fileName);
}
Toast.makeText(context, text, Toast.LENGTH_LONG).show();
}
private String downloadAndStoreImage(String urlString) throws IOException {
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream();
try {
String fileName = getFileNameFromUrl(url);
String mimeType = getMimeType(conn, fileName);
String fileNameWithExtension = getFileNameWithExtension(fileName, mimeType);
return writeFileToStorage(fileNameWithExtension, in);
} finally {
in.close();
}
}
private String getFileNameFromUrl(URL url) {
String fileName;
String path = url.getPath();
int start = path.lastIndexOf("/");
if (start != -1 && start + 1 < path.length()) {
fileName = UrlEncodingHelper.decodeUtf8(path.substring(start + 1));
} else {
fileName = DEFAULT_FILE_NAME;
}
return fileName;
}
private String getMimeType(URLConnection conn, String fileName) {
String mimeType = null;
if (fileName.indexOf('.') == -1) {
mimeType = conn.getContentType();
}
return mimeType;
}
private String fetchAndStoreImage(String urlString) throws IOException {
ContentResolver contentResolver = context.getContentResolver();
Uri uri = Uri.parse(urlString);
String fileName = getFileNameFromContentProvider(contentResolver, uri);
String mimeType = getMimeType(contentResolver, uri, fileName);
InputStream in = contentResolver.openInputStream(uri);
try {
String fileNameWithExtension = getFileNameWithExtension(fileName, mimeType);
return writeFileToStorage(fileNameWithExtension, in);
} finally {
in.close();
}
}
private String getMimeType(ContentResolver contentResolver, Uri uri, String fileName) {
String mimeType = null;
if (fileName.indexOf('.') == -1) {
mimeType = contentResolver.getType(uri);
}
return mimeType;
}
private String getFileNameFromContentProvider(ContentResolver contentResolver, Uri uri) {
String displayName = DEFAULT_FILE_NAME;
Cursor cursor = contentResolver.query(uri, ATTACHMENT_PROJECTION, null, null, null);
if (cursor != null) {
try {
if (cursor.moveToNext() && !cursor.isNull(DISPLAY_NAME_INDEX)) {
displayName = cursor.getString(DISPLAY_NAME_INDEX);
}
} finally {
cursor.close();
}
}
return displayName;
}
private String getFileNameWithExtension(String fileName, String mimeType) {
if (fileName.indexOf('.') != -1) {
return fileName;
}
// Use JPEG as fallback
String extension = "jpeg";
if (mimeType != null) {
String extensionFromMimeType = MimeUtility.getExtensionByMimeType(mimeType);
if (extensionFromMimeType != null) {
extension = extensionFromMimeType;
}
}
return fileName + "." + extension;
}
private String writeFileToStorage(String fileName, InputStream in) throws IOException {
String sanitized = FileHelper.sanitizeFilename(fileName);
File directory = new File(K9.getAttachmentDefaultPath());
File file = FileHelper.createUniqueFile(directory, sanitized);
FileOutputStream out = new FileOutputStream(file);
try {
IOUtils.copy(in, out);
out.flush();
} finally {
out.close();
}
return file.getName();
}
}

View File

@ -1,23 +1,14 @@
package com.fsck.k9.ui.messageview; package com.fsck.k9.ui.messageview;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import android.app.Activity; import android.app.Activity;
import android.app.Fragment; import android.app.Fragment;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.util.AttributeSet; import android.util.AttributeSet;
@ -35,27 +26,22 @@ import android.view.View.OnCreateContextMenuListener;
import android.view.ViewStub; import android.view.ViewStub;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebView.HitTestResult; import android.webkit.WebView.HitTestResult;
import android.webkit.WebViewClient;
import android.widget.Button; import android.widget.Button;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast; import android.widget.Toast;
import com.fsck.k9.K9;
import com.fsck.k9.R; import com.fsck.k9.R;
import com.fsck.k9.helper.ClipboardManager; import com.fsck.k9.helper.ClipboardManager;
import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.Contacts;
import com.fsck.k9.helper.FileHelper;
import com.fsck.k9.helper.UrlEncodingHelper;
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.internet.MimeUtility;
import com.fsck.k9.mailstore.AttachmentViewInfo; import com.fsck.k9.mailstore.AttachmentViewInfo;
import com.fsck.k9.mailstore.MessageViewInfo.MessageViewContainer; import com.fsck.k9.mailstore.MessageViewInfo.MessageViewContainer;
import com.fsck.k9.provider.AttachmentProvider.AttachmentProviderColumns;
import com.fsck.k9.view.K9WebViewClient;
import com.fsck.k9.view.MessageHeader.OnLayoutChangedListener; import com.fsck.k9.view.MessageHeader.OnLayoutChangedListener;
import com.fsck.k9.view.MessageWebView; import com.fsck.k9.view.MessageWebView;
import org.apache.commons.io.IOUtils;
import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpError;
@ -77,12 +63,6 @@ public class MessageContainerView extends LinearLayout implements OnClickListene
private static final int MENU_ITEM_EMAIL_SAVE = Menu.FIRST + 1; private static final int MENU_ITEM_EMAIL_SAVE = Menu.FIRST + 1;
private static final int MENU_ITEM_EMAIL_COPY = Menu.FIRST + 2; private static final int MENU_ITEM_EMAIL_COPY = Menu.FIRST + 2;
private static final String[] ATTACHMENT_PROJECTION = new String[] {
AttachmentProviderColumns._ID,
AttachmentProviderColumns.DISPLAY_NAME
};
private static final int DISPLAY_NAME_INDEX = 1;
private ViewStub mOpenPgpHeaderStub; private ViewStub mOpenPgpHeaderStub;
private View mSidebar; private View mSidebar;
private MessageWebView mMessageContentView; private MessageWebView mMessageContentView;
@ -242,7 +222,8 @@ public class MessageContainerView extends LinearLayout implements OnClickListene
break; break;
} }
case MENU_ITEM_IMAGE_SAVE: { case MENU_ITEM_IMAGE_SAVE: {
new DownloadImageTask().execute(url); //TODO: Use download manager
new DownloadImageTask(getContext()).execute(url);
break; break;
} }
case MENU_ITEM_IMAGE_COPY: { case MENU_ITEM_IMAGE_COPY: {
@ -442,10 +423,12 @@ public class MessageContainerView extends LinearLayout implements OnClickListene
} }
} }
public void setMessage(MessageViewContainer messageViewContainer) public void setMessageViewContainer(MessageViewContainer messageViewContainer) throws MessagingException {
throws MessagingException {
resetView(); resetView();
WebViewClient webViewClient = K9WebViewClient.newInstance(messageViewContainer.rootPart);
mMessageContentView.setWebViewClient(webViewClient);
// Save the text so we can reset the WebView when the user clicks the "Show pictures" button // Save the text so we can reset the WebView when the user clicks the "Show pictures" button
OpenPgpError error = messageViewContainer.pgpError; OpenPgpError error = messageViewContainer.pgpError;
if (error != null) { if (error != null) {
@ -661,117 +644,4 @@ public class MessageContainerView extends LinearLayout implements OnClickListene
out.writeInt((this.showPictures) ? 1 : 0); out.writeInt((this.showPictures) ? 1 : 0);
} }
} }
class DownloadImageTask extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
String urlString = params[0];
try {
boolean externalImage = urlString.startsWith("http");
String filename = null;
String mimeType = null;
InputStream in = null;
try {
if (externalImage) {
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
in = conn.getInputStream();
String path = url.getPath();
// Try to get the filename from the URL
int start = path.lastIndexOf("/");
if (start != -1 && start + 1 < path.length()) {
filename = UrlEncodingHelper.decodeUtf8(path.substring(start + 1));
} else {
// Use a dummy filename if necessary
filename = "saved_image";
}
// Get the MIME type if we couldn't find a file extension
if (filename.indexOf('.') == -1) {
mimeType = conn.getContentType();
}
} else {
ContentResolver contentResolver = getContext().getContentResolver();
Uri uri = Uri.parse(urlString);
// Get the filename from AttachmentProvider
Cursor cursor = contentResolver.query(uri, ATTACHMENT_PROJECTION, null, null, null);
if (cursor != null) {
try {
if (cursor.moveToNext()) {
filename = cursor.getString(DISPLAY_NAME_INDEX);
}
} finally {
cursor.close();
}
}
// Use a dummy filename if necessary
if (filename == null) {
filename = "saved_image";
}
// Get the MIME type if we couldn't find a file extension
if (filename.indexOf('.') == -1) {
mimeType = contentResolver.getType(uri);
}
in = contentResolver.openInputStream(uri);
}
// Do we still need an extension?
if (filename.indexOf('.') == -1) {
// Use JPEG as fallback
String extension = "jpeg";
if (mimeType != null) {
// Try to find an extension for the given MIME type
String ext = MimeUtility.getExtensionByMimeType(mimeType);
if (ext != null) {
extension = ext;
}
}
filename += "." + extension;
}
String sanitized = FileHelper.sanitizeFilename(filename);
File directory = new File(K9.getAttachmentDefaultPath());
File file = FileHelper.createUniqueFile(directory, sanitized);
FileOutputStream out = new FileOutputStream(file);
try {
IOUtils.copy(in, out);
out.flush();
} finally {
out.close();
}
return file.getName();
} finally {
if (in != null) {
in.close();
}
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onPostExecute(String filename) {
String text;
if (filename == null) {
text = getContext().getString(R.string.image_saving_failed);
} else {
text = getContext().getString(R.string.image_saved_as, filename);
}
Toast.makeText(getContext(), text, Toast.LENGTH_LONG).show();
}
}
} }

View File

@ -17,6 +17,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender.SendIntentException; import android.content.IntentSender.SendIntentException;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.fsck.k9.Account; import com.fsck.k9.Account;
@ -55,7 +56,7 @@ public class MessageCryptoHelper {
private Deque<Part> partsToDecryptOrVerify; private Deque<Part> partsToDecryptOrVerify;
private OpenPgpApi openPgpApi; private OpenPgpApi openPgpApi;
private Part currentlyDecrypringOrVerifyingPart; private Part currentlyDecryptingOrVerifyingPart;
private Intent currentCryptoResult; private Intent currentCryptoResult;
private MessageCryptoAnnotations messageAnnotations; private MessageCryptoAnnotations messageAnnotations;
@ -73,6 +74,11 @@ public class MessageCryptoHelper {
public void decryptOrVerifyMessagePartsIfNecessary(LocalMessage message) { public void decryptOrVerifyMessagePartsIfNecessary(LocalMessage message) {
this.message = message; this.message = message;
if (!isCryptoProviderConfigured()) {
returnResultToFragment();
return;
}
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message); List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message);
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message);
List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(message); List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(message);
@ -87,6 +93,10 @@ public class MessageCryptoHelper {
} }
} }
private boolean isCryptoProviderConfigured() {
return !TextUtils.isEmpty(account.getCryptoApp());
}
private void decryptOrVerifyNextPartOrStartExtractingTextAndAttachments() { private void decryptOrVerifyNextPartOrStartExtractingTextAndAttachments() {
if (!partsToDecryptOrVerify.isEmpty()) { if (!partsToDecryptOrVerify.isEmpty()) {
@ -143,7 +153,7 @@ public class MessageCryptoHelper {
} }
private void decryptOrVerifyPart(Part part) { private void decryptOrVerifyPart(Part part) {
currentlyDecrypringOrVerifyingPart = part; currentlyDecryptingOrVerifyingPart = part;
decryptVerify(new Intent()); decryptVerify(new Intent());
} }
@ -155,9 +165,9 @@ public class MessageCryptoHelper {
intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accountName); intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accountName);
try { try {
if (MessageDecryptVerifier.isPgpMimeSignedPart(currentlyDecrypringOrVerifyingPart)) { if (MessageDecryptVerifier.isPgpMimeSignedPart(currentlyDecryptingOrVerifyingPart)) {
callAsyncDetachedVerify(intent); callAsyncDetachedVerify(intent);
} else if (MessageDecryptVerifier.isPgpInlinePart(currentlyDecrypringOrVerifyingPart)) { } else if (MessageDecryptVerifier.isPgpInlinePart(currentlyDecryptingOrVerifyingPart)) {
callAsyncInlineOperation(intent); callAsyncInlineOperation(intent);
} else { } else {
callAsyncDecrypt(intent); callAsyncDecrypt(intent);
@ -208,7 +218,7 @@ public class MessageCryptoHelper {
private void callAsyncDetachedVerify(Intent intent) throws IOException, MessagingException { private void callAsyncDetachedVerify(Intent intent) throws IOException, MessagingException {
PipedInputStream pipedInputStream = getPipedInputStreamForSignedData(); PipedInputStream pipedInputStream = getPipedInputStreamForSignedData();
byte[] signatureData = MessageDecryptVerifier.getSignatureData(currentlyDecrypringOrVerifyingPart); byte[] signatureData = MessageDecryptVerifier.getSignatureData(currentlyDecryptingOrVerifyingPart);
intent.putExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE, signatureData); intent.putExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE, signatureData);
openPgpApi.executeApiAsync(intent, pipedInputStream, null, new IOpenPgpCallback() { openPgpApi.executeApiAsync(intent, pipedInputStream, null, new IOpenPgpCallback() {
@ -228,7 +238,7 @@ public class MessageCryptoHelper {
@Override @Override
public void run() { public void run() {
try { try {
Multipart multipartSignedMultipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody(); Multipart multipartSignedMultipart = (Multipart) currentlyDecryptingOrVerifyingPart.getBody();
BodyPart signatureBodyPart = multipartSignedMultipart.getBodyPart(0); BodyPart signatureBodyPart = multipartSignedMultipart.getBodyPart(0);
Log.d(K9.LOG_TAG, "signed data type: " + signatureBodyPart.getMimeType()); Log.d(K9.LOG_TAG, "signed data type: " + signatureBodyPart.getMimeType());
signatureBodyPart.writeTo(out); signatureBodyPart.writeTo(out);
@ -255,14 +265,14 @@ public class MessageCryptoHelper {
@Override @Override
public void run() { public void run() {
try { try {
if (MessageDecryptVerifier.isPgpMimePart(currentlyDecrypringOrVerifyingPart)) { if (MessageDecryptVerifier.isPgpMimePart(currentlyDecryptingOrVerifyingPart)) {
Multipart multipartEncryptedMultipart = Multipart multipartEncryptedMultipart =
(Multipart) currentlyDecrypringOrVerifyingPart.getBody(); (Multipart) currentlyDecryptingOrVerifyingPart.getBody();
BodyPart encryptionPayloadPart = multipartEncryptedMultipart.getBodyPart(1); BodyPart encryptionPayloadPart = multipartEncryptedMultipart.getBodyPart(1);
Body encryptionPayloadBody = encryptionPayloadPart.getBody(); Body encryptionPayloadBody = encryptionPayloadPart.getBody();
encryptionPayloadBody.writeTo(out); encryptionPayloadBody.writeTo(out);
} else if (MessageDecryptVerifier.isPgpInlinePart(currentlyDecrypringOrVerifyingPart)) { } else if (MessageDecryptVerifier.isPgpInlinePart(currentlyDecryptingOrVerifyingPart)) {
String text = MessageExtractor.getTextFromPart(currentlyDecrypringOrVerifyingPart); String text = MessageExtractor.getTextFromPart(currentlyDecryptingOrVerifyingPart);
out.write(text.getBytes()); out.write(text.getBytes());
} else { } else {
Log.wtf(K9.LOG_TAG, "No suitable data to stream found!"); Log.wtf(K9.LOG_TAG, "No suitable data to stream found!");
@ -392,7 +402,7 @@ public class MessageCryptoHelper {
} }
private void addOpenPgpResultPartToMessage(OpenPgpResultAnnotation resultAnnotation) { private void addOpenPgpResultPartToMessage(OpenPgpResultAnnotation resultAnnotation) {
messageAnnotations.put(currentlyDecrypringOrVerifyingPart, resultAnnotation); messageAnnotations.put(currentlyDecryptingOrVerifyingPart, resultAnnotation);
} }
private void onCryptoFailed(OpenPgpError error) { private void onCryptoFailed(OpenPgpError error) {

View File

@ -70,7 +70,8 @@ public class MessageView extends LinearLayout {
MessageContainerView view = (MessageContainerView) mInflater.inflate(R.layout.message_container, null); MessageContainerView view = (MessageContainerView) mInflater.inflate(R.layout.message_container, null);
view.initialize(fragment, attachmentCallback, openPgpHeaderViewCallback, view.initialize(fragment, attachmentCallback, openPgpHeaderViewCallback,
!Account.NO_OPENPGP_PROVIDER.equals(account.getOpenPgpProvider())); !Account.NO_OPENPGP_PROVIDER.equals(account.getOpenPgpProvider()));
view.setMessage(container); view.setMessageViewContainer(container);
containerViews.addView(view); containerViews.addView(view);
} }

View File

@ -0,0 +1,125 @@
package com.fsck.k9.view;
import java.io.InputStream;
import java.util.Stack;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.fsck.k9.K9;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.Multipart;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mailstore.AttachmentViewInfo;
import com.fsck.k9.mailstore.LocalMessageExtractor;
/**
* {@link WebViewClient} that intercepts requests for {@code cid:} URIs to load the respective body part.
*/
public abstract class K9WebViewClient extends WebViewClient {
private static final String CID_SCHEME = "cid";
private static final WebResourceResponse RESULT_DO_NOT_INTERCEPT = null;
private static final WebResourceResponse RESULT_DUMMY_RESPONSE = new WebResourceResponse(null, null, null);
public static WebViewClient newInstance(Part part) {
if (Build.VERSION.SDK_INT < 21) {
return new PreLollipopWebViewClient(part);
}
return new LollipopWebViewClient(part);
}
private final Part part;
private K9WebViewClient(Part part) {
this.part = part;
}
protected WebResourceResponse shouldInterceptRequest(WebView webView, Uri uri) {
if (!CID_SCHEME.equals(uri.getScheme())) {
return RESULT_DO_NOT_INTERCEPT;
}
String cid = uri.getSchemeSpecificPart();
if (TextUtils.isEmpty(cid)) {
return RESULT_DUMMY_RESPONSE;
}
Part part = getPartForContentId(cid);
if (part == null) {
return RESULT_DUMMY_RESPONSE;
}
Context context = webView.getContext();
ContentResolver contentResolver = context.getContentResolver();
try {
AttachmentViewInfo attachmentInfo = LocalMessageExtractor.extractAttachmentInfo(context, part);
String mimeType = attachmentInfo.mimeType;
InputStream inputStream = contentResolver.openInputStream(attachmentInfo.uri);
return new WebResourceResponse(mimeType, null, inputStream);
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Error while intercepting URI: " + uri, e);
return RESULT_DUMMY_RESPONSE;
}
}
private Part getPartForContentId(String cid) {
Stack<Part> partsToCheck = new Stack<Part>();
partsToCheck.push(part);
while (!partsToCheck.isEmpty()) {
Part part = partsToCheck.pop();
Body body = part.getBody();
if (body instanceof Multipart) {
Multipart multipart = (Multipart) body;
for (Part bodyPart : multipart.getBodyParts()) {
partsToCheck.push(bodyPart);
}
} else if (cid.equals(part.getContentId())) {
return part;
}
}
return null;
}
private static class PreLollipopWebViewClient extends K9WebViewClient {
protected PreLollipopWebViewClient(Part part) {
super(part);
}
@SuppressWarnings("deprecation")
@Override
public WebResourceResponse shouldInterceptRequest(WebView webView, String url) {
return shouldInterceptRequest(webView, Uri.parse(url));
}
}
@TargetApi(VERSION_CODES.LOLLIPOP)
private static class LollipopWebViewClient extends K9WebViewClient {
protected LollipopWebViewClient(Part part) {
super(part);
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView webView, WebResourceRequest request) {
return shouldInterceptRequest(webView, request.getUrl());
}
}
}