diff options
author | Dominik Schürmann <dominik@dominikschuermann.de> | 2015-08-10 14:35:15 +0200 |
---|---|---|
committer | Dominik Schürmann <dominik@dominikschuermann.de> | 2015-08-17 09:31:34 +0200 |
commit | dbaf7070ead596f5c70ad48fc55aada2f77a856a (patch) | |
tree | c3c5c32478d1aaba490f4ff206d051fdb24f728a /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations | |
parent | 7004d129a8546977c450bd40931b0b774cd2025e (diff) | |
download | open-keychain-dbaf7070ead596f5c70ad48fc55aada2f77a856a.tar.gz open-keychain-dbaf7070ead596f5c70ad48fc55aada2f77a856a.tar.bz2 open-keychain-dbaf7070ead596f5c70ad48fc55aada2f77a856a.zip |
WIP mime parsing
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations')
3 files changed, 430 insertions, 0 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 d498bd9a1..3c15a2e7b 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 @@ -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_WRITING(LogLevel.ERROR,R.string.msg_export_log_error_writing), 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; |