diff options
Diffstat (limited to 'OpenKeychain/src/main/java')
6 files changed, 512 insertions, 2 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/MimeParsingOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/MimeParsingOperation.java new file mode 100644 index 000000000..c7ebbf5fd --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/MimeParsingOperation.java @@ -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(); + } + + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/MimeParsingResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/MimeParsingResult.java new file mode 100644 index 000000000..05f5125cb --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/MimeParsingResult.java @@ -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]; + } + }; +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 41691933e..3856209b3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -818,7 +818,11 @@ public abstract class OperationResult implements Parcelable { MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH(LogLevel.ERROR, R.string.msg_keybase_error_msg_payload_mismatch), - // export log + // mime 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), + MSG_LV (LogLevel.START, R.string.msg_lv), MSG_LV_MATCH (LogLevel.DEBUG, R.string.msg_lv_match), MSG_LV_MATCH_ERROR (LogLevel.ERROR, R.string.msg_lv_match_error), @@ -838,7 +842,8 @@ public abstract class OperationResult implements Parcelable { MSG_LV_FETCH_ERROR_URL (LogLevel.ERROR, R.string.msg_lv_fetch_error_url), MSG_LV_FETCH_ERROR_IO (LogLevel.ERROR, R.string.msg_lv_fetch_error_io), MSG_LV_FETCH_ERROR_FORMAT(LogLevel.ERROR, R.string.msg_lv_fetch_error_format), - MSG_LV_FETCH_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_lv_fetch_error_nothing); + MSG_LV_FETCH_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_lv_fetch_error_nothing), + ; public final int mMsgId; public final LogLevel mLevel; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java index eff27f112..d2128cd77 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.operations.EditKeyOperation; import org.sufficientlysecure.keychain.operations.ExportOperation; import org.sufficientlysecure.keychain.operations.ImportOperation; import org.sufficientlysecure.keychain.operations.KeybaseVerificationOperation; +import org.sufficientlysecure.keychain.operations.MimeParsingOperation; import org.sufficientlysecure.keychain.operations.PromoteKeyOperation; import org.sufficientlysecure.keychain.operations.RevokeOperation; import org.sufficientlysecure.keychain.operations.SignEncryptOperation; @@ -137,6 +138,9 @@ public class KeychainService extends Service implements Progressable { } else if (inputParcel instanceof KeybaseVerificationParcel) { op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis), outerThis); + } else if (inputParcel instanceof MimeParsingParcel) { + op = new MimeParsingOperation(outerThis, new ProviderHelper(outerThis), + outerThis); } else { throw new AssertionError("Unrecognized input parcel in KeychainService!"); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/MimeParsingParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/MimeParsingParcel.java new file mode 100644 index 000000000..ccc817c21 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/MimeParsingParcel.java @@ -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]; + } + }; + +} + diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java index ddaf40010..3dda47ac5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -58,10 +58,13 @@ import org.openintents.openpgp.OpenPgpSignatureResult; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.operations.results.MimeParsingResult; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; 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) +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.util.KeyFormattingUtils.StatusHolder; import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel; |