diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab9890a019..598255fecd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,10 +6,11 @@
### Next version
-* Enabled [sqlite analyze](https://sqlite.org/lang_analyze.html)
-* Added [linear back-off scheme](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq123)
+* Added option to show contents of zipped attachments
* Added option to sort reply templates by frequency of use
* Added basic [DMARC](https://en.wikipedia.org/wiki/DMARC) report viewer
+* Added [linear back-off scheme](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq123)
+* Enabled [sqlite analyze](https://sqlite.org/lang_analyze.html)
* Small improvements and minor bug fixes
* Updated translations
diff --git a/app/src/main/assets/CHANGELOG.md b/app/src/main/assets/CHANGELOG.md
index ab9890a019..598255fecd 100644
--- a/app/src/main/assets/CHANGELOG.md
+++ b/app/src/main/assets/CHANGELOG.md
@@ -6,10 +6,11 @@
### Next version
-* Enabled [sqlite analyze](https://sqlite.org/lang_analyze.html)
-* Added [linear back-off scheme](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq123)
+* Added option to show contents of zipped attachments
* Added option to sort reply templates by frequency of use
* Added basic [DMARC](https://en.wikipedia.org/wiki/DMARC) report viewer
+* Added [linear back-off scheme](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq123)
+* Enabled [sqlite analyze](https://sqlite.org/lang_analyze.html)
* Small improvements and minor bug fixes
* Updated translations
diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java b/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java
index c874ee39d0..c30c887971 100644
--- a/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java
+++ b/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java
@@ -166,6 +166,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
private SwitchCompat swImagesPlaceholders;
private SwitchCompat swImagesInline;
private SwitchCompat swButtonExtra;
+ private SwitchCompat swUnzip;
private SwitchCompat swAttachmentsAlt;
private SwitchCompat swThumbnails;
@@ -197,7 +198,8 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
"message_zoom", "overview_mode", "override_width",
"display_font", "contrast", "monospaced_pre",
"background_color", "text_color", "text_size", "text_font", "text_align", "text_separators",
- "collapse_quotes", "image_placeholders", "inline_images", "button_extra", "attachments_alt", "thumbnails",
+ "collapse_quotes", "image_placeholders", "inline_images", "button_extra",
+ "unzip", "attachments_alt", "thumbnails",
"bundled_fonts", "parse_classes",
"authentication", "authentication_indicator"
};
@@ -314,6 +316,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swImagesPlaceholders = view.findViewById(R.id.swImagesPlaceholders);
swImagesInline = view.findViewById(R.id.swImagesInline);
swButtonExtra = view.findViewById(R.id.swButtonExtra);
+ swUnzip = view.findViewById(R.id.swUnzip);
swAttachmentsAlt = view.findViewById(R.id.swAttachmentsAlt);
swThumbnails = view.findViewById(R.id.swThumbnails);
swBundledFonts = view.findViewById(R.id.swBundledFonts);
@@ -1165,6 +1168,13 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
}
});
+ swUnzip.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
+ prefs.edit().putBoolean("unzip", checked).apply();
+ }
+ });
+
swAttachmentsAlt.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@@ -1430,6 +1440,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swImagesPlaceholders.setChecked(prefs.getBoolean("image_placeholders", true));
swImagesInline.setChecked(prefs.getBoolean("inline_images", false));
swButtonExtra.setChecked(prefs.getBoolean("button_extra", false));
+ swUnzip.setChecked(prefs.getBoolean("unzip", false));
swAttachmentsAlt.setChecked(prefs.getBoolean("attachments_alt", false));
swThumbnails.setChecked(prefs.getBoolean("thumbnails", true));
diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java
index 26a966fda9..73193b71a0 100644
--- a/app/src/main/java/eu/faircode/email/MessageHelper.java
+++ b/app/src/main/java/eu/faircode/email/MessageHelper.java
@@ -94,6 +94,8 @@ import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
@@ -3372,6 +3374,77 @@ public class MessageHelper {
} catch (Throwable ex) {
Log.e(ex);
}
+
+ else if ("application/zip".equals(local.type)) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ boolean unzip = prefs.getBoolean("unzip", false);
+
+ if (unzip) {
+ // https://developer.android.com/reference/java/util/zip/ZipInputStream
+ try (ZipInputStream zis = new ZipInputStream(
+ new BufferedInputStream(new FileInputStream(local.getFile(context))))) {
+
+ int subsequence = 1;
+
+ ZipEntry ze;
+ while ((ze = zis.getNextEntry()) != null)
+ try {
+ String name = ze.getName();
+ long total = ze.getSize();
+
+ // isDirectory:
+ // A directory entry is defined to be one whose name ends with a '/'.
+ if (ze.isDirectory() ||
+ (name != null && name.endsWith("\\"))) {
+ Log.i("Zipped folder=" + name);
+ continue;
+ }
+
+ Log.i("Zipped attachment seq=" + local.sequence + ":" + subsequence +
+ " " + name + ":" + total);
+
+ EntityAttachment entry = new EntityAttachment();
+ entry.message = local.message;
+ entry.sequence = local.sequence;
+ entry.subsequence = subsequence++;
+ entry.name = name;
+ entry.type = Helper.guessMimeType(entry.name);
+ entry.size = total;
+ entry.id = db.attachment().insertAttachment(entry);
+
+ File efile = entry.getFile(context);
+ Log.i("Unzipping to " + efile);
+
+ int last = 0;
+ long size = 0;
+ try (OutputStream os = new FileOutputStream(efile)) {
+ byte[] buffer = new byte[Helper.BUFFER_SIZE];
+ for (int len = zis.read(buffer); len != -1; len = zis.read(buffer)) {
+ size += len;
+ os.write(buffer, 0, len);
+
+ int progress = (int) (size * 100 / total);
+ if (progress / 20 > last / 20) {
+ last = progress;
+ db.attachment().setProgress(entry.id, progress);
+ }
+ }
+ } catch (Throwable ex) {
+ Log.e(ex);
+ db.attachment().setError(entry.id, Log.formatThrowable(ex));
+ db.attachment().setAvailable(entry.id, true); // unrecoverable
+ }
+
+ db.attachment().setDownloaded(entry.id, efile.length());
+ } finally {
+ zis.closeEntry();
+ }
+ } catch (Throwable ex) {
+ Log.e(ex);
+ db.attachment().setError(local.id, Log.formatThrowable(ex));
+ }
+ }
+ }
}
}
diff --git a/app/src/main/res/layout/fragment_options_display.xml b/app/src/main/res/layout/fragment_options_display.xml
index d71d44302e..768ee049fc 100644
--- a/app/src/main/res/layout/fragment_options_display.xml
+++ b/app/src/main/res/layout/fragment_options_display.xml
@@ -1750,6 +1750,17 @@
app:layout_constraintTop_toBottomOf="@id/tvImagesInlineHint"
app:switchPadding="12dp" />
+
+
Show message preview in italics
Number of preview lines
Expand address details by default
+ Show contents of zipped attachments
Show attachments after the message text
Show image thumbnails after the message text
Default message text zoom: %1$s %%
diff --git a/metadata/en-US/changelogs/1854.txt b/metadata/en-US/changelogs/1854.txt
index ab9890a019..598255fecd 100644
--- a/metadata/en-US/changelogs/1854.txt
+++ b/metadata/en-US/changelogs/1854.txt
@@ -6,10 +6,11 @@
### Next version
-* Enabled [sqlite analyze](https://sqlite.org/lang_analyze.html)
-* Added [linear back-off scheme](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq123)
+* Added option to show contents of zipped attachments
* Added option to sort reply templates by frequency of use
* Added basic [DMARC](https://en.wikipedia.org/wiki/DMARC) report viewer
+* Added [linear back-off scheme](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq123)
+* Enabled [sqlite analyze](https://sqlite.org/lang_analyze.html)
* Small improvements and minor bug fixes
* Updated translations