From 46a91277851c03f29fa8194dafd9917fb2dfa4b2 Mon Sep 17 00:00:00 2001 From: M66B Date: Sat, 19 Mar 2022 14:32:48 +0100 Subject: [PATCH] Continue after multipart error --- app/src/main/java/eu/faircode/email/Core.java | 18 -- .../java/eu/faircode/email/MessageHelper.java | 285 +++++++++--------- 2 files changed, 140 insertions(+), 163 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/Core.java b/app/src/main/java/eu/faircode/email/Core.java index 9d4db334ed..b11a95769e 100644 --- a/app/src/main/java/eu/faircode/email/Core.java +++ b/app/src/main/java/eu/faircode/email/Core.java @@ -3728,24 +3728,6 @@ class Core { IMAPStore istore, IMAPFolder ifolder, MimeMessage imessage, boolean browsed, boolean download, List rules, State state, SyncStats stats) throws MessagingException, IOException { - try { - return _synchronizeMessage(context, account, folder, - istore, ifolder, imessage, - browsed, download, rules, state, stats); - } catch (MessageHelper.MessagingStructureException ex) { - Log.e(ex); - long uid = ifolder.getUID(imessage); - EntityOperation.queue(context, folder, EntityOperation.FETCH, uid); - return null; - } - } - - private static EntityMessage _synchronizeMessage( - Context context, - EntityAccount account, EntityFolder folder, - IMAPStore istore, IMAPFolder ifolder, MimeMessage imessage, - boolean browsed, boolean download, - List rules, State state, SyncStats stats) throws MessagingException, IOException { DB db = DB.getInstance(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean download_headers = prefs.getBoolean("download_headers", false); diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java index 87b68b4a40..9a7b7d2ce4 100644 --- a/app/src/main/java/eu/faircode/email/MessageHelper.java +++ b/app/src/main/java/eu/faircode/email/MessageHelper.java @@ -3806,8 +3806,11 @@ public class MessageHelper { break; } } - } else - throw new MessagingStructureException(content, "multipart/mixed"); + } else { + String msg = "Expected multipart/mixed got " + content.getClass().getName(); + Log.e(msg); + parts.warnings.add(msg); + } } if (part.isMimeType("multipart/signed")) { @@ -3851,8 +3854,11 @@ public class MessageHelper { sb.append(' ').append(i).append('=').append(multipart.getBodyPart(i).getContentType()); Log.e(sb.toString()); } - } else - throw new MessagingStructureException(content, "multipart/signed"); + } else { + String msg = "Expected multipart/signed got " + content.getClass().getName(); + Log.e(msg); + parts.warnings.add(msg); + } } else Log.e(ct.toString()); } else if (part.isMimeType("multipart/encrypted")) { @@ -3873,8 +3879,12 @@ public class MessageHelper { sb.append(' ').append(i).append('=').append(multipart.getBodyPart(i).getContentType()); Log.e(sb.toString()); } - } else - throw new MessagingStructureException(content, "multipart/encrypted"); + } else { + String msg = "Expected multipart/encrypted got " + content.getClass().getName(); + Log.e(msg); + parts.warnings.add(msg); + + } } else Log.e(ct.toString()); } else if (part.isMimeType("application/pkcs7-mime") || @@ -3954,145 +3964,148 @@ public class MessageHelper { if (part.isMimeType("multipart/*")) { Multipart multipart; Object content = part.getContent(); // Should always be Multipart - if (content instanceof Multipart) + if (content instanceof Multipart) { multipart = (Multipart) part.getContent(); - else - throw new MessagingStructureException(content, "multipart/*"); + int count = multipart.getCount(); + for (int i = 0; i < count; i++) + try { + BodyPart child = multipart.getBodyPart(i); + getMessageParts(part, child, parts, encrypt); + } catch (ParseException ex) { + // Nested body: try to continue + // ParseException: In parameter list boundary="...">, expected parameter name, got ";" + Log.w(ex); + parts.warnings.add(Log.formatThrowable(ex, false)); + } + return; + } else { + String msg = "Expected multipart/* got " + content.getClass().getName(); + Log.e(msg); + parts.warnings.add(msg); + } + } - int count = multipart.getCount(); - for (int i = 0; i < count; i++) - try { - BodyPart child = multipart.getBodyPart(i); - getMessageParts(part, child, parts, encrypt); - } catch (ParseException ex) { - // Nested body: try to continue - // ParseException: In parameter list boundary="...">, expected parameter name, got ";" - Log.w(ex); - parts.warnings.add(Log.formatThrowable(ex, false)); + // https://www.iana.org/assignments/cont-disp/cont-disp.xhtml + String disposition; + try { + // From the body structure + disposition = part.getDisposition(); + if (disposition != null) + disposition = disposition.toLowerCase(Locale.ROOT); + } catch (MessagingException ex) { + Log.w(ex); + parts.warnings.add(Log.formatThrowable(ex, false)); + disposition = null; + } + + String filename; + try { + // From the body structure: + // 1. disposition filename + // 2. content type name + filename = part.getFileName(); // IMAPBodyPart/BODYSTRUCTURE + if (filename != null) { + // https://tools.ietf.org/html/rfc2231 + // http://kb.mozillazine.org/Attachments_renamed + // https://blog.nodemailer.com/2017/01/27/the-mess-that-is-attachment-filenames/ + int q1 = filename.indexOf('\''); + int q2 = filename.indexOf('\'', q1 + 1); + if (q1 >= 0 && q2 > 0) { + try { + String charset = filename.substring(0, q1); + String language = filename.substring(q1 + 1, q2); + String name = filename.substring(q2 + 1) + .replace("+", "%2B"); + + if (!TextUtils.isEmpty(charset)) + filename = URLDecoder.decode(name, charset); + } catch (Throwable ex) { + Log.e(ex); + } } - } else { - // https://www.iana.org/assignments/cont-disp/cont-disp.xhtml - String disposition; - try { - // From the body structure - disposition = part.getDisposition(); - if (disposition != null) - disposition = disposition.toLowerCase(Locale.ROOT); - } catch (MessagingException ex) { - Log.w(ex); - parts.warnings.add(Log.formatThrowable(ex, false)); - disposition = null; + + filename = decodeMime(filename); } + } catch (MessagingException ex) { + Log.w(ex); + parts.warnings.add(Log.formatThrowable(ex, false)); + filename = null; + } - String filename; - try { - // From the body structure: - // 1. disposition filename - // 2. content type name - filename = part.getFileName(); // IMAPBodyPart/BODYSTRUCTURE - if (filename != null) { - // https://tools.ietf.org/html/rfc2231 - // http://kb.mozillazine.org/Attachments_renamed - // https://blog.nodemailer.com/2017/01/27/the-mess-that-is-attachment-filenames/ - int q1 = filename.indexOf('\''); - int q2 = filename.indexOf('\'', q1 + 1); - if (q1 >= 0 && q2 > 0) { - try { - String charset = filename.substring(0, q1); - String language = filename.substring(q1 + 1, q2); - String name = filename.substring(q2 + 1) - .replace("+", "%2B"); + ContentType contentType; + try { + // From the body structure + contentType = new ContentType(part.getContentType()); + } catch (ParseException ex) { + if (part instanceof MimeMessage) + Log.w("MimeMessage content type=" + ex.getMessage()); + else + Log.w(ex); + contentType = new ContentType(Helper.guessMimeType(filename)); + } - if (!TextUtils.isEmpty(charset)) - filename = URLDecoder.decode(name, charset); - } catch (Throwable ex) { - Log.e(ex); - } - } + String ct = contentType.getBaseType(); + if (("text/plain".equalsIgnoreCase(ct) || "text/html".equalsIgnoreCase(ct)) && + !Part.ATTACHMENT.equalsIgnoreCase(disposition) && TextUtils.isEmpty(filename)) { + parts.text.add(new PartHolder(part, contentType)); + } else { + if (Report.isDeliveryStatus(ct) || Report.isDispositionNotification(ct)) + parts.extra.add(new PartHolder(part, contentType)); + + AttachmentPart apart = new AttachmentPart(); + apart.disposition = disposition; + apart.filename = filename; + apart.encrypt = encrypt; + apart.part = part; - filename = decodeMime(filename); + String cid = null; + try { + if (apart.part instanceof IMAPBodyPart) + cid = ((IMAPBodyPart) apart.part).getContentID(); + if (TextUtils.isEmpty(cid)) { + String[] cids = apart.part.getHeader("Content-ID"); + if (cids != null && cids.length > 0) + cid = MimeUtility.unfold(cids[0]); } } catch (MessagingException ex) { Log.w(ex); - parts.warnings.add(Log.formatThrowable(ex, false)); - filename = null; - } - - ContentType contentType; - try { - // From the body structure - contentType = new ContentType(part.getContentType()); - } catch (ParseException ex) { - if (part instanceof MimeMessage) - Log.w("MimeMessage content type=" + ex.getMessage()); - else - Log.w(ex); - contentType = new ContentType(Helper.guessMimeType(filename)); + if (!"Failed to fetch headers".equals(ex.getMessage())) + parts.warnings.add(Log.formatThrowable(ex, false)); } - String ct = contentType.getBaseType(); - if (("text/plain".equalsIgnoreCase(ct) || "text/html".equalsIgnoreCase(ct)) && - !Part.ATTACHMENT.equalsIgnoreCase(disposition) && TextUtils.isEmpty(filename)) { - parts.text.add(new PartHolder(part, contentType)); - } else { - if (Report.isDeliveryStatus(ct) || Report.isDispositionNotification(ct)) - parts.extra.add(new PartHolder(part, contentType)); - - AttachmentPart apart = new AttachmentPart(); - apart.disposition = disposition; - apart.filename = filename; - apart.encrypt = encrypt; - apart.part = part; - - String cid = null; + Boolean related = null; + if (parent != null) try { - if (apart.part instanceof IMAPBodyPart) - cid = ((IMAPBodyPart) apart.part).getContentID(); - if (TextUtils.isEmpty(cid)) { - String[] cids = apart.part.getHeader("Content-ID"); - if (cids != null && cids.length > 0) - cid = MimeUtility.unfold(cids[0]); - } + related = parent.isMimeType("multipart/related"); } catch (MessagingException ex) { Log.w(ex); - if (!"Failed to fetch headers".equals(ex.getMessage())) - parts.warnings.add(Log.formatThrowable(ex, false)); } - Boolean related = null; - if (parent != null) - try { - related = parent.isMimeType("multipart/related"); - } catch (MessagingException ex) { - Log.w(ex); - } - - apart.attachment = new EntityAttachment(); - apart.attachment.disposition = apart.disposition; - apart.attachment.name = apart.filename; - apart.attachment.type = contentType.getBaseType().toLowerCase(Locale.ROOT); - apart.attachment.size = (long) apart.part.getSize(); - apart.attachment.cid = cid; - apart.attachment.related = related; - apart.attachment.encryption = apart.encrypt; - - if ("text/calendar".equalsIgnoreCase(apart.attachment.type) && - TextUtils.isEmpty(apart.attachment.name)) - apart.attachment.name = "invite.ics"; - - if (apart.attachment.size <= 0) - apart.attachment.size = null; - - // https://tools.ietf.org/html/rfc2392 - if (apart.attachment.cid != null) { - if (!apart.attachment.cid.startsWith("<")) - apart.attachment.cid = "<" + apart.attachment.cid; - if (!apart.attachment.cid.endsWith(">")) - apart.attachment.cid += ">"; - } - - parts.attachments.add(apart); + apart.attachment = new EntityAttachment(); + apart.attachment.disposition = apart.disposition; + apart.attachment.name = apart.filename; + apart.attachment.type = contentType.getBaseType().toLowerCase(Locale.ROOT); + apart.attachment.size = (long) apart.part.getSize(); + apart.attachment.cid = cid; + apart.attachment.related = related; + apart.attachment.encryption = apart.encrypt; + + if ("text/calendar".equalsIgnoreCase(apart.attachment.type) && + TextUtils.isEmpty(apart.attachment.name)) + apart.attachment.name = "invite.ics"; + + if (apart.attachment.size <= 0) + apart.attachment.size = null; + + // https://tools.ietf.org/html/rfc2392 + if (apart.attachment.cid != null) { + if (!apart.attachment.cid.startsWith("<")) + apart.attachment.cid = "<" + apart.attachment.cid; + if (!apart.attachment.cid.endsWith(">")) + apart.attachment.cid += ">"; } + + parts.attachments.add(apart); } } catch (FolderClosedException ex) { throw ex; @@ -4435,24 +4448,6 @@ public class MessageHelper { return values; } - static class MessagingStructureException extends MessagingException { - private String className; - private String expected; - - MessagingStructureException(Object content, String expected) { - super(); - if (content != null) - this.className = content.getClass().getName(); - this.expected = expected; - } - - @Nullable - @Override - public String getMessage() { - return className + " expected: " + expected; - } - } - static class Report { String type; String reporter;