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