diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md index 749e85be33..ce4d598ee5 100644 --- a/ATTRIBUTION.md +++ b/ATTRIBUTION.md @@ -34,3 +34,4 @@ FairEmail uses: * [OpenDyslexic](https://github.com/antijingoist/opendyslexic). Copyright (c) 12/2012 - 2019. Copyright (c) 2019-07-29, Abbie Gonzalez. [SIL Open Font License 1.1](https://github.com/antijingoist/opendyslexic/blob/master/OFL.txt). * [AndroidSVG](https://github.com/BigBadaboom/androidsvg). Copyright 2013 Paul LeBeau, Cave Rock Software Ltd. [Apache License 2.0](https://github.com/BigBadaboom/androidsvg/blob/master/LICENSE). * [Public Suffix List](https://publicsuffix.org/). Copyright © 2007–20 Mozilla Foundation. [Mozilla Public License, v. 2.0](https://mozilla.org/MPL/2.0/). +* [Outlook Message Parser](https://github.com/bbottema/outlook-message-parser). Copyright (C) 2017 Benny Bottema. [Apache License 2.0](https://github.com/bbottema/outlook-message-parser/blob/master/LICENSE-2.0.txt). diff --git a/app/build.gradle b/app/build.gradle index 4b84bac8eb..b0e1aa184d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -85,6 +85,8 @@ android { exclude 'META-INF/LICENSE.gpl.txt' exclude 'META-INF/LICENSE.commercial.txt' exclude 'org/apache/**' + exclude 'LICENSE-2.0.txt' + exclude 'RELEASE.txt' } // https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.SigningConfig.html @@ -262,6 +264,14 @@ configurations.all { // paging-runtime: AsyncPagedListDiffer, LivePagedListBuilder, PagedListAdapter, PagedStorageDiffHelper } +configurations.all { + resolutionStrategy.eachDependency { details -> + if (details.requested.group == "org.apache.poi") { + details.useVersion "3.17" + } + } +} + dependencies { //implementation fileTree(dir: 'libs', include: ['*.jar']) @@ -491,6 +501,9 @@ dependencies { // https://mvnrepository.com/artifact/org.apache.poi/poi-scratchpad implementation "org.apache.poi:poi-scratchpad:$apache_poi" + // https://github.com/bbottema/outlook-message-parser + implementation "org.simplejavamail:outlook-message-parser:1.7.12" + // https://mvnrepository.com/artifact/org.reactivestreams/reactive-streams // https://mvnrepository.com/artifact/io.reactivex.rxjava2/rxjava implementation "org.reactivestreams:reactive-streams:$reactivestreams_version" diff --git a/app/src/main/assets/ATTRIBUTION.md b/app/src/main/assets/ATTRIBUTION.md index 749e85be33..ce4d598ee5 100644 --- a/app/src/main/assets/ATTRIBUTION.md +++ b/app/src/main/assets/ATTRIBUTION.md @@ -34,3 +34,4 @@ FairEmail uses: * [OpenDyslexic](https://github.com/antijingoist/opendyslexic). Copyright (c) 12/2012 - 2019. Copyright (c) 2019-07-29, Abbie Gonzalez. [SIL Open Font License 1.1](https://github.com/antijingoist/opendyslexic/blob/master/OFL.txt). * [AndroidSVG](https://github.com/BigBadaboom/androidsvg). Copyright 2013 Paul LeBeau, Cave Rock Software Ltd. [Apache License 2.0](https://github.com/BigBadaboom/androidsvg/blob/master/LICENSE). * [Public Suffix List](https://publicsuffix.org/). Copyright © 2007–20 Mozilla Foundation. [Mozilla Public License, v. 2.0](https://mozilla.org/MPL/2.0/). +* [Outlook Message Parser](https://github.com/bbottema/outlook-message-parser). Copyright (C) 2017 Benny Bottema. [Apache License 2.0](https://github.com/bbottema/outlook-message-parser/blob/master/LICENSE-2.0.txt). diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java index 8b36824d8f..824bb01dfe 100644 --- a/app/src/main/java/eu/faircode/email/MessageHelper.java +++ b/app/src/main/java/eu/faircode/email/MessageHelper.java @@ -45,6 +45,10 @@ import com.sun.mail.util.MessageRemovedIOException; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import org.simplejavamail.outlookmessageparser.OutlookMessageParser; +import org.simplejavamail.outlookmessageparser.model.OutlookAttachment; +import org.simplejavamail.outlookmessageparser.model.OutlookFileAttachment; +import org.simplejavamail.outlookmessageparser.model.OutlookMessage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -2298,6 +2302,9 @@ public class MessageHelper { if (Helper.isTnef(local.type, local.name)) decodeTNEF(context, local); + + if ("msg".equalsIgnoreCase(Helper.getExtension(local.name))) + decodeOutlook(context, local); } void downloadAttachment(Context context, int index, EntityAttachment local) throws MessagingException, IOException { @@ -2535,6 +2542,110 @@ public class MessageHelper { } } + private void decodeOutlook(Context context, EntityAttachment local) { + try { + DB db = DB.getInstance(context); + int subsequence = 0; + + // https://poi.apache.org/components/hmef/index.html + File file = local.getFile(context); + OutlookMessage msg = new OutlookMessageParser().parseMsg(file); + + String headers = msg.getHeaders(); + if (!TextUtils.isEmpty(headers)) { + EntityAttachment attachment = new EntityAttachment(); + attachment.message = local.message; + attachment.sequence = local.sequence; + attachment.subsequence = ++subsequence; + attachment.name = "headers.txt"; + attachment.type = "text/rfc822-headers"; + attachment.disposition = Part.ATTACHMENT; + attachment.id = db.attachment().insertAttachment(attachment); + + Helper.writeText(attachment.getFile(context), headers); + db.attachment().setDownloaded(attachment.id, (long) headers.length()); + } + + String html = msg.getBodyHTML(); + if (!TextUtils.isEmpty(html)) { + EntityAttachment attachment = new EntityAttachment(); + attachment.message = local.message; + attachment.sequence = local.sequence; + attachment.subsequence = ++subsequence; + attachment.name = "body.html"; + attachment.type = "text/html"; + attachment.disposition = Part.ATTACHMENT; + attachment.id = db.attachment().insertAttachment(attachment); + + File a = attachment.getFile(context); + Helper.writeText(a, html); + db.attachment().setDownloaded(attachment.id, a.length()); + } + + if (TextUtils.isEmpty(html)) { + String text = msg.getBodyText(); + if (!TextUtils.isEmpty(text)) { + EntityAttachment attachment = new EntityAttachment(); + attachment.message = local.message; + attachment.sequence = local.sequence; + attachment.subsequence = ++subsequence; + attachment.name = "body.txt"; + attachment.type = "text/plain"; + attachment.disposition = Part.ATTACHMENT; + attachment.id = db.attachment().insertAttachment(attachment); + + File a = attachment.getFile(context); + Helper.writeText(a, text); + db.attachment().setDownloaded(attachment.id, a.length()); + } + } + + String rtf = msg.getBodyRTF(); + if (!TextUtils.isEmpty(rtf)) { + EntityAttachment attachment = new EntityAttachment(); + attachment.message = local.message; + attachment.sequence = local.sequence; + attachment.subsequence = ++subsequence; + attachment.name = "body.rtf"; + attachment.type = "application/rtf"; + attachment.disposition = Part.ATTACHMENT; + attachment.id = db.attachment().insertAttachment(attachment); + + File a = attachment.getFile(context); + Helper.writeText(a, rtf); + db.attachment().setDownloaded(attachment.id, a.length()); + } + + List attachments = msg.getOutlookAttachments(); + for (OutlookAttachment oa : attachments) + if (oa instanceof OutlookFileAttachment) { + OutlookFileAttachment ofa = (OutlookFileAttachment) oa; + + EntityAttachment attachment = new EntityAttachment(); + attachment.message = local.message; + attachment.sequence = local.sequence; + attachment.subsequence = ++subsequence; + attachment.name = ofa.getFilename(); + attachment.type = ofa.getMimeTag(); + attachment.disposition = Part.ATTACHMENT; + attachment.id = db.attachment().insertAttachment(attachment); + + if (TextUtils.isEmpty(attachment.type)) + attachment.type = Helper.guessMimeType(attachment.name); + + byte[] data = ofa.getData(); + try (OutputStream os = new FileOutputStream(attachment.getFile(context))) { + os.write(data); + } + + db.attachment().setDownloaded(attachment.id, (long) data.length); + } + + } catch (Throwable ex) { + Log.w(ex); + } + } + String getWarnings(String existing) { if (existing != null) warnings.add(0, existing);