WIP mime parsing
This commit is contained in:
@@ -56,6 +56,8 @@ dependencies {
|
|||||||
compile 'com.mikepenz.iconics:community-material-typeface:1.0.0@aar'
|
compile 'com.mikepenz.iconics:community-material-typeface:1.0.0@aar'
|
||||||
compile 'com.nispok:snackbar:2.11.0'
|
compile 'com.nispok:snackbar:2.11.0'
|
||||||
compile 'com.squareup.okhttp:okhttp:2.4.0'
|
compile 'com.squareup.okhttp:okhttp:2.4.0'
|
||||||
|
compile 'org.apache.james:apache-mime4j-core:0.7.2'
|
||||||
|
compile 'org.apache.james:apache-mime4j-dom:0.7.2'
|
||||||
|
|
||||||
// libs as submodules
|
// libs as submodules
|
||||||
compile project(':extern:openpgp-api-lib:openpgp-api')
|
compile project(':extern:openpgp-api-lib:openpgp-api')
|
||||||
@@ -199,15 +201,21 @@ android {
|
|||||||
htmlOutput file('lint-report.html')
|
htmlOutput file('lint-report.html')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable preDexing, causes com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000) on some systems
|
|
||||||
dexOptions {
|
dexOptions {
|
||||||
|
// Disable preDexing, causes com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000) on some systems
|
||||||
preDexLibraries = false
|
preDexLibraries = false
|
||||||
|
// faster with incremental?
|
||||||
|
// incremental true
|
||||||
|
javaMaxHeapSize "4g"
|
||||||
}
|
}
|
||||||
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
exclude 'LICENSE.txt'
|
exclude 'LICENSE.txt'
|
||||||
exclude 'META-INF/LICENSE.txt'
|
exclude 'META-INF/LICENSE.txt'
|
||||||
exclude 'META-INF/NOTICE.txt'
|
exclude 'META-INF/NOTICE.txt'
|
||||||
|
exclude 'META-INF/DEPENDENCIES'
|
||||||
|
exclude 'META-INF/LICENSE'
|
||||||
|
exclude 'META-INF/NOTICE'
|
||||||
exclude '.readme'
|
exclude '.readme'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,360 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.operations;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.apache.james.mime4j.dom.BinaryBody;
|
||||||
|
import org.apache.james.mime4j.dom.Body;
|
||||||
|
import org.apache.james.mime4j.dom.Entity;
|
||||||
|
import org.apache.james.mime4j.dom.Message;
|
||||||
|
import org.apache.james.mime4j.dom.MessageBuilder;
|
||||||
|
import org.apache.james.mime4j.dom.Multipart;
|
||||||
|
import org.apache.james.mime4j.dom.TextBody;
|
||||||
|
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||||
|
import org.apache.james.mime4j.dom.address.MailboxList;
|
||||||
|
import org.apache.james.mime4j.dom.field.AddressListField;
|
||||||
|
import org.apache.james.mime4j.dom.field.ContentTypeField;
|
||||||
|
import org.apache.james.mime4j.dom.field.DateTimeField;
|
||||||
|
import org.apache.james.mime4j.dom.field.UnstructuredField;
|
||||||
|
import org.apache.james.mime4j.field.address.AddressFormatter;
|
||||||
|
import org.apache.james.mime4j.message.BodyPart;
|
||||||
|
import org.apache.james.mime4j.message.DefaultMessageBuilder;
|
||||||
|
import org.apache.james.mime4j.message.MessageImpl;
|
||||||
|
import org.apache.james.mime4j.stream.Field;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.MimeParsingResult;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
|
||||||
|
import org.sufficientlysecure.keychain.service.MimeParsingParcel;
|
||||||
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class MimeParsingOperation extends BaseOperation<MimeParsingParcel> {
|
||||||
|
|
||||||
|
public ArrayList<Uri> mTempUris;
|
||||||
|
|
||||||
|
public MimeParsingOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
|
||||||
|
super(context, providerHelper, progressable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public MimeParsingResult execute(MimeParsingParcel parcel,
|
||||||
|
CryptoInputParcel cryptoInputParcel) {
|
||||||
|
OperationResult.OperationLog log = new OperationResult.OperationLog();
|
||||||
|
|
||||||
|
log.add(OperationResult.LogType.MSG_MIME_PARSING, 0);
|
||||||
|
|
||||||
|
mTempUris = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream in = mContext.getContentResolver().openInputStream(parcel.getInputUri());
|
||||||
|
|
||||||
|
final MessageBuilder builder = new DefaultMessageBuilder();
|
||||||
|
final Message message = builder.parseMessage(in);
|
||||||
|
|
||||||
|
SimpleTreeNode root = createNode(message);
|
||||||
|
|
||||||
|
traverseTree(root);
|
||||||
|
|
||||||
|
log.add(OperationResult.LogType.MSG_MIME_PARSING_SUCCESS, 1);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(Constants.TAG, "Mime parsing error", e);
|
||||||
|
log.add(OperationResult.LogType.MSG_MIME_PARSING_ERROR, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MimeParsingResult(MimeParsingResult.RESULT_OK, log,
|
||||||
|
mTempUris);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void traverseTree(SimpleTreeNode node) {
|
||||||
|
if (node.isLeaf()) {
|
||||||
|
parseAndSaveAsUris(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SimpleTreeNode child : node.children) {
|
||||||
|
traverseTree(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps an Object and associates it with a text. All message parts
|
||||||
|
* (headers, bodies, multiparts, body parts) will be wrapped in
|
||||||
|
* ObjectWrapper instances before they are added to the JTree instance.
|
||||||
|
*/
|
||||||
|
public static class ObjectWrapper {
|
||||||
|
private String text = "";
|
||||||
|
private Object object = null;
|
||||||
|
|
||||||
|
public ObjectWrapper(String text, Object object) {
|
||||||
|
this.text = text;
|
||||||
|
this.object = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getObject() {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Create a node given a Multipart body.
|
||||||
|
// * Add the Preamble, all Body parts and the Epilogue to the node.
|
||||||
|
// *
|
||||||
|
// * @return the root node of the tree.
|
||||||
|
// */
|
||||||
|
// private DefaultMutableTreeNode createNode(Header header) {
|
||||||
|
// DefaultMutableTreeNode node = new DefaultMutableTreeNode(
|
||||||
|
// new ObjectWrapper("Header", header));
|
||||||
|
//
|
||||||
|
// for (Field field : header.getFields()) {
|
||||||
|
// String name = field.getName();
|
||||||
|
//
|
||||||
|
// node.add(new DefaultMutableTreeNode(new ObjectWrapper(name, field)));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return node;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a node given a Multipart body.
|
||||||
|
* Add the Preamble, all Body parts and the Epilogue to the node.
|
||||||
|
*
|
||||||
|
* @param multipart the Multipart.
|
||||||
|
* @return the root node of the tree.
|
||||||
|
*/
|
||||||
|
private SimpleTreeNode createNode(Multipart multipart) {
|
||||||
|
SimpleTreeNode node = new SimpleTreeNode(
|
||||||
|
new ObjectWrapper("Multipart", multipart));
|
||||||
|
|
||||||
|
// node.add(new DefaultMutableTreeNode(
|
||||||
|
// new ObjectWrapper("Preamble", multipart.getPreamble())));
|
||||||
|
for (Entity part : multipart.getBodyParts()) {
|
||||||
|
node.add(createNode(part));
|
||||||
|
}
|
||||||
|
// node.add(new DefaultMutableTreeNode(
|
||||||
|
// new ObjectWrapper("Epilogue", multipart.getEpilogue())));
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the tree nodes given a MIME entity (either a Message or
|
||||||
|
* a BodyPart).
|
||||||
|
*
|
||||||
|
* @param entity the entity.
|
||||||
|
* @return the root node of the tree displaying the specified entity and
|
||||||
|
* its children.
|
||||||
|
*/
|
||||||
|
private SimpleTreeNode createNode(Entity entity) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the root node for the entity. It's either a
|
||||||
|
* Message or a Body part.
|
||||||
|
*/
|
||||||
|
String type = "Message";
|
||||||
|
if (entity instanceof BodyPart) {
|
||||||
|
type = "Body part";
|
||||||
|
}
|
||||||
|
SimpleTreeNode node = new SimpleTreeNode(
|
||||||
|
new ObjectWrapper(type, entity));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the node encapsulating the entity Header.
|
||||||
|
*/
|
||||||
|
// node.add(createNode(entity.getHeader()));
|
||||||
|
|
||||||
|
Body body = entity.getBody();
|
||||||
|
|
||||||
|
if (body instanceof Multipart) {
|
||||||
|
/*
|
||||||
|
* The body of the entity is a Multipart.
|
||||||
|
*/
|
||||||
|
|
||||||
|
node.add(createNode((Multipart) body));
|
||||||
|
} else if (body instanceof MessageImpl) {
|
||||||
|
/*
|
||||||
|
* The body is another Message.
|
||||||
|
*/
|
||||||
|
|
||||||
|
node.add(createNode((MessageImpl) body));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Discrete Body (either of type TextBody or BinaryBody).
|
||||||
|
*/
|
||||||
|
type = "Text body";
|
||||||
|
if (body instanceof BinaryBody) {
|
||||||
|
type = "Binary body";
|
||||||
|
}
|
||||||
|
|
||||||
|
type += " (" + entity.getMimeType() + ")";
|
||||||
|
node.add(new SimpleTreeNode(new ObjectWrapper(type, body)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void parseAndSaveAsUris(SimpleTreeNode node) {
|
||||||
|
Object o = ((ObjectWrapper) node.getUserObject()).getObject();
|
||||||
|
|
||||||
|
if (o instanceof TextBody) {
|
||||||
|
/*
|
||||||
|
* A text body. Display its contents.
|
||||||
|
*/
|
||||||
|
TextBody body = (TextBody) o;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
try {
|
||||||
|
Reader r = body.getReader();
|
||||||
|
int c;
|
||||||
|
while ((c = r.read()) != -1) {
|
||||||
|
sb.append((char) c);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
Log.d(Constants.TAG, "text: " + sb.toString());
|
||||||
|
// textView.setText(sb.toString());
|
||||||
|
|
||||||
|
Uri tempUri = null;
|
||||||
|
try {
|
||||||
|
tempUri = TemporaryStorageProvider.createFile(mContext, "text", "text/plain");
|
||||||
|
OutputStream outStream = mContext.getContentResolver().openOutputStream(tempUri);
|
||||||
|
body.writeTo(outStream);
|
||||||
|
outStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(Constants.TAG, "error mime parsing", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
mTempUris.add(tempUri);
|
||||||
|
|
||||||
|
} else if (o instanceof BinaryBody) {
|
||||||
|
/*
|
||||||
|
* A binary body. Display its MIME type and length in bytes.
|
||||||
|
*/
|
||||||
|
BinaryBody body = (BinaryBody) o;
|
||||||
|
int size = 0;
|
||||||
|
try {
|
||||||
|
InputStream is = body.getInputStream();
|
||||||
|
while ((is.read()) != -1) {
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
Log.d(Constants.TAG, "Binary body\n"
|
||||||
|
+ "MIME type: "
|
||||||
|
+ body.getParent().getMimeType() + "\n"
|
||||||
|
+ "Size of decoded data: " + size + " bytes");
|
||||||
|
|
||||||
|
} else if (o instanceof ContentTypeField) {
|
||||||
|
/*
|
||||||
|
* Content-Type field.
|
||||||
|
*/
|
||||||
|
ContentTypeField field = (ContentTypeField) o;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("MIME type: ").append(field.getMimeType()).append("\n");
|
||||||
|
for (Map.Entry<String, String> entry : field.getParameters().entrySet()) {
|
||||||
|
sb.append(entry.getKey()).append(" = ").append(entry.getValue()).append("\n");
|
||||||
|
}
|
||||||
|
Log.d(Constants.TAG, sb.toString());
|
||||||
|
|
||||||
|
} else if (o instanceof AddressListField) {
|
||||||
|
/*
|
||||||
|
* An address field (From, To, Cc, etc)
|
||||||
|
*/
|
||||||
|
AddressListField field = (AddressListField) o;
|
||||||
|
MailboxList list = field.getAddressList().flatten();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Mailbox mailbox : list) {
|
||||||
|
sb.append(AddressFormatter.DEFAULT.format(mailbox, false)).append("\n");
|
||||||
|
}
|
||||||
|
Log.d(Constants.TAG, sb.toString());
|
||||||
|
|
||||||
|
} else if (o instanceof DateTimeField) {
|
||||||
|
Date date = ((DateTimeField) o).getDate();
|
||||||
|
Log.d(Constants.TAG, date.toString());
|
||||||
|
} else if (o instanceof UnstructuredField) {
|
||||||
|
Log.d(Constants.TAG, ((UnstructuredField) o).getValue());
|
||||||
|
} else if (o instanceof Field) {
|
||||||
|
Log.d(Constants.TAG, ((Field) o).getBody());
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* The Object should be a Header or a String containing a
|
||||||
|
* Preamble or Epilogue.
|
||||||
|
*/
|
||||||
|
Log.d(Constants.TAG, o.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SimpleTreeNode {
|
||||||
|
private SimpleTreeNode parent;
|
||||||
|
private Object userObject;
|
||||||
|
private ArrayList<SimpleTreeNode> children;
|
||||||
|
|
||||||
|
protected SimpleTreeNode(Object userObject) {
|
||||||
|
this.parent = null;
|
||||||
|
this.userObject = userObject;
|
||||||
|
this.children = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getUserObject() {
|
||||||
|
return userObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUserObject(Object userObject) {
|
||||||
|
this.userObject = userObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(SimpleTreeNode newChild) {
|
||||||
|
newChild.parent = this;
|
||||||
|
children.add(newChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleTreeNode getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return children.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.operations.results;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Parcel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class MimeParsingResult extends OperationResult {
|
||||||
|
|
||||||
|
public final ArrayList<Uri> mTemporaryUris;
|
||||||
|
|
||||||
|
public ArrayList<Uri> getTemporaryUris() {
|
||||||
|
return mTemporaryUris;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MimeParsingResult(int result, OperationLog log, ArrayList<Uri> temporaryUris) {
|
||||||
|
super(result, log);
|
||||||
|
mTemporaryUris = temporaryUris;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MimeParsingResult(Parcel in) {
|
||||||
|
super(in);
|
||||||
|
mTemporaryUris = in.createTypedArrayList(Uri.CREATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
super.writeToParcel(dest, flags);
|
||||||
|
dest.writeTypedList(mTemporaryUris);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<MimeParsingResult> CREATOR = new Creator<MimeParsingResult>() {
|
||||||
|
@Override
|
||||||
|
public MimeParsingResult createFromParcel(Parcel in) {
|
||||||
|
return new MimeParsingResult(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MimeParsingResult[] newArray(int size) {
|
||||||
|
return new MimeParsingResult[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -787,6 +787,11 @@ public abstract class OperationResult implements Parcelable {
|
|||||||
MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN(LogLevel.ERROR,R.string.msg_export_log_error_fopen),
|
MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN(LogLevel.ERROR,R.string.msg_export_log_error_fopen),
|
||||||
MSG_EXPORT_LOG_EXPORT_ERROR_WRITING(LogLevel.ERROR,R.string.msg_export_log_error_writing),
|
MSG_EXPORT_LOG_EXPORT_ERROR_WRITING(LogLevel.ERROR,R.string.msg_export_log_error_writing),
|
||||||
MSG_EXPORT_LOG_EXPORT_SUCCESS (LogLevel.OK, R.string.msg_export_log_success),
|
MSG_EXPORT_LOG_EXPORT_SUCCESS (LogLevel.OK, R.string.msg_export_log_success),
|
||||||
|
|
||||||
|
// mim parsing
|
||||||
|
MSG_MIME_PARSING(LogLevel.START,R.string.msg_mime_parsing_start),
|
||||||
|
MSG_MIME_PARSING_ERROR(LogLevel.ERROR,R.string.msg_mime_parsing_error),
|
||||||
|
MSG_MIME_PARSING_SUCCESS(LogLevel.OK,R.string.msg_mime_parsing_success),
|
||||||
;
|
;
|
||||||
|
|
||||||
public final int mMsgId;
|
public final int mMsgId;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.operations.EditKeyOperation;
|
|||||||
import org.sufficientlysecure.keychain.operations.ExportOperation;
|
import org.sufficientlysecure.keychain.operations.ExportOperation;
|
||||||
import org.sufficientlysecure.keychain.operations.ImportOperation;
|
import org.sufficientlysecure.keychain.operations.ImportOperation;
|
||||||
import org.sufficientlysecure.keychain.operations.KeybaseVerificationOperation;
|
import org.sufficientlysecure.keychain.operations.KeybaseVerificationOperation;
|
||||||
|
import org.sufficientlysecure.keychain.operations.MimeParsingOperation;
|
||||||
import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
|
import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
|
||||||
import org.sufficientlysecure.keychain.operations.RevokeOperation;
|
import org.sufficientlysecure.keychain.operations.RevokeOperation;
|
||||||
import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
|
import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
|
||||||
@@ -137,6 +138,9 @@ public class KeychainService extends Service implements Progressable {
|
|||||||
} else if (inputParcel instanceof KeybaseVerificationParcel) {
|
} else if (inputParcel instanceof KeybaseVerificationParcel) {
|
||||||
op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis),
|
op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis),
|
||||||
outerThis);
|
outerThis);
|
||||||
|
} else if (inputParcel instanceof MimeParsingParcel) {
|
||||||
|
op = new MimeParsingOperation(outerThis, new ProviderHelper(outerThis),
|
||||||
|
outerThis);
|
||||||
} else {
|
} else {
|
||||||
throw new AssertionError("Unrecognized input parcel in KeychainService!");
|
throw new AssertionError("Unrecognized input parcel in KeychainService!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.service;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
public class MimeParsingParcel implements Parcelable {
|
||||||
|
|
||||||
|
private Uri mInputUri;
|
||||||
|
private Uri mOutputUri;
|
||||||
|
|
||||||
|
public MimeParsingParcel() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MimeParsingParcel(Uri inputUri, Uri outputUri) {
|
||||||
|
mInputUri = inputUri;
|
||||||
|
mOutputUri = outputUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
MimeParsingParcel(Parcel source) {
|
||||||
|
// we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
|
||||||
|
mInputUri = source.readParcelable(getClass().getClassLoader());
|
||||||
|
mOutputUri = source.readParcelable(getClass().getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uri getInputUri() {
|
||||||
|
return mInputUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uri getOutputUri() {
|
||||||
|
return mOutputUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeParcelable(mInputUri, 0);
|
||||||
|
dest.writeParcelable(mOutputUri, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<MimeParsingParcel> CREATOR = new Creator<MimeParsingParcel>() {
|
||||||
|
public MimeParsingParcel createFromParcel(final Parcel source) {
|
||||||
|
return new MimeParsingParcel(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MimeParsingParcel[] newArray(final int size) {
|
||||||
|
return new MimeParsingParcel[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -28,7 +28,6 @@ import android.app.Activity;
|
|||||||
import android.content.ClipDescription;
|
import android.content.ClipDescription;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.LabeledIntent;
|
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
@@ -38,7 +37,6 @@ import android.net.Uri;
|
|||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.support.v7.widget.DefaultItemAnimator;
|
import android.support.v7.widget.DefaultItemAnimator;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
@@ -58,14 +56,16 @@ import android.widget.ViewAnimator;
|
|||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpMetadata;
|
import org.openintents.openpgp.OpenPgpMetadata;
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.sufficientlysecure.keychain.BuildConfig;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.MimeParsingResult;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
|
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
|
||||||
// this import NEEDS to be above the ViewModel one, or it won't compile! (as of 06/06/15)
|
// this import NEEDS to be above the ViewModel one, or it won't compile! (as of 06/06/15)
|
||||||
|
import org.sufficientlysecure.keychain.service.MimeParsingParcel;
|
||||||
|
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
|
import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder;
|
||||||
import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel;
|
import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel;
|
||||||
@@ -432,45 +432,47 @@ public class DecryptListFragment
|
|||||||
// OpenKeychain's internal viewer
|
// OpenKeychain's internal viewer
|
||||||
if ("text/plain".equals(metadata.getMimeType())) {
|
if ("text/plain".equals(metadata.getMimeType())) {
|
||||||
|
|
||||||
|
parseMime(outputUri);
|
||||||
|
|
||||||
// this is a significant i/o operation, use an asynctask
|
// this is a significant i/o operation, use an asynctask
|
||||||
new AsyncTask<Void,Void,Intent>() {
|
// new AsyncTask<Void,Void,Intent>() {
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
protected Intent doInBackground(Void... params) {
|
// protected Intent doInBackground(Void... params) {
|
||||||
|
//
|
||||||
Activity activity = getActivity();
|
// Activity activity = getActivity();
|
||||||
if (activity == null) {
|
// if (activity == null) {
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
// Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setDataAndType(outputUri, "text/plain");
|
// intent.setDataAndType(outputUri, "text/plain");
|
||||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
// intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
return intent;
|
// return intent;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
protected void onPostExecute(Intent intent) {
|
// protected void onPostExecute(Intent intent) {
|
||||||
// for result so we can possibly get a snackbar error from internal viewer
|
// // for result so we can possibly get a snackbar error from internal viewer
|
||||||
Activity activity = getActivity();
|
// Activity activity = getActivity();
|
||||||
if (intent == null || activity == null) {
|
// if (intent == null || activity == null) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
LabeledIntent internalIntent = new LabeledIntent(
|
// LabeledIntent internalIntent = new LabeledIntent(
|
||||||
new Intent(intent)
|
// new Intent(intent)
|
||||||
.setClass(activity, DisplayTextActivity.class)
|
// .setClass(activity, DisplayTextActivity.class)
|
||||||
.putExtra(DisplayTextActivity.EXTRA_METADATA, result),
|
// .putExtra(DisplayTextActivity.EXTRA_METADATA, result),
|
||||||
BuildConfig.APPLICATION_ID, R.string.view_internal, R.drawable.ic_launcher);
|
// BuildConfig.APPLICATION_ID, R.string.view_internal, R.drawable.ic_launcher);
|
||||||
|
//
|
||||||
Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show));
|
// Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show));
|
||||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
|
// chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
|
||||||
new Parcelable[] { internalIntent });
|
// new Parcelable[] { internalIntent });
|
||||||
|
//
|
||||||
activity.startActivity(chooserIntent);
|
// activity.startActivity(chooserIntent);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}.execute();
|
// }.execute();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
@@ -484,6 +486,52 @@ public class DecryptListFragment
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parseMime(final Uri inputUri) {
|
||||||
|
|
||||||
|
CryptoOperationHelper.Callback<MimeParsingParcel, MimeParsingResult> callback
|
||||||
|
= new CryptoOperationHelper.Callback<MimeParsingParcel, MimeParsingResult>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MimeParsingParcel createOperationInput() {
|
||||||
|
return new MimeParsingParcel(inputUri, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCryptoOperationSuccess(MimeParsingResult result) {
|
||||||
|
handleResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCryptoOperationCancelled() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCryptoOperationError(MimeParsingResult result) {
|
||||||
|
handleResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleResult(MimeParsingResult result) {
|
||||||
|
// TODO: merge with other log
|
||||||
|
// saveKeyResult.getLog().add(result, 0);
|
||||||
|
|
||||||
|
mOutputUris = new HashMap<>(result.getTemporaryUris().size());
|
||||||
|
for (Uri tempUri : result.getTemporaryUris()) {
|
||||||
|
// TODO: use same inputUri for all?
|
||||||
|
mOutputUris.put(inputUri, tempUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CryptoOperationHelper mimeParsingHelper = new CryptoOperationHelper<>(3, this, callback, R.string.progress_uploading);
|
||||||
|
mimeParsingHelper.cryptoOperation();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PgpDecryptVerifyInputParcel createOperationInput() {
|
public PgpDecryptVerifyInputParcel createOperationInput() {
|
||||||
|
|
||||||
|
|||||||
@@ -1341,6 +1341,11 @@
|
|||||||
<string name="msg_export_log_error_writing">"I/O error writing to file!"</string>
|
<string name="msg_export_log_error_writing">"I/O error writing to file!"</string>
|
||||||
<string name="msg_export_log_success">"Log exported successfully!"</string>
|
<string name="msg_export_log_success">"Log exported successfully!"</string>
|
||||||
|
|
||||||
|
<!-- Messages for Mime parsing operation -->
|
||||||
|
<string name="msg_mime_parsing_start">"Parsing the MIME structure"</string>
|
||||||
|
<string name="msg_mime_parsing_error">"MIME parsing failed"</string>
|
||||||
|
<string name="msg_mime_parsing_success">"MIME parsing successfully!"</string>
|
||||||
|
|
||||||
<!-- PassphraseCache -->
|
<!-- PassphraseCache -->
|
||||||
<string name="passp_cache_notif_click_to_clear">"Touch to clear passwords."</string>
|
<string name="passp_cache_notif_click_to_clear">"Touch to clear passwords."</string>
|
||||||
<plurals name="passp_cache_notif_n_keys">
|
<plurals name="passp_cache_notif_n_keys">
|
||||||
|
|||||||
Reference in New Issue
Block a user