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:
commit
d437314dc3
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -174,7 +174,7 @@ public class MimeMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getContentId() throws MessagingException {
|
public String getContentId() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
125
k9mail/src/main/java/com/fsck/k9/view/K9WebViewClient.java
Normal file
125
k9mail/src/main/java/com/fsck/k9/view/K9WebViewClient.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user