From 500fc5486ea27d0f33acec5cac6edb2df50389bf Mon Sep 17 00:00:00 2001 From: M66B Date: Wed, 8 Apr 2020 11:05:43 +0200 Subject: [PATCH] Relaxed QP decoder --- .../java/eu/faircode/email/MessageHelper.java | 19 +++---- .../eu/faircode/email/QDecoderStreamEx.java | 54 +++++++++++++++++++ 2 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/eu/faircode/email/QDecoderStreamEx.java diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java index f3818531fe..eee0402c6e 100644 --- a/app/src/main/java/eu/faircode/email/MessageHelper.java +++ b/app/src/main/java/eu/faircode/email/MessageHelper.java @@ -32,7 +32,6 @@ import com.sun.mail.util.ASCIIUtility; import com.sun.mail.util.BASE64DecoderStream; import com.sun.mail.util.FolderClosedIOException; import com.sun.mail.util.MessageRemovedIOException; -import com.sun.mail.util.QDecoderStream; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -1315,8 +1314,8 @@ public class MessageHelper { if (p1.charset != null && p1.charset.equalsIgnoreCase(p2.charset) && p1.encoding != null && p1.encoding.equalsIgnoreCase(p2.encoding)) { try { - byte[] b1 = decodeWord(p1.text, p1.encoding); - byte[] b2 = decodeWord(p2.text, p2.encoding); + byte[] b1 = decodeWord(p1.text, p1.encoding, p1.charset); + byte[] b2 = decodeWord(p2.text, p2.encoding, p2.charset); byte[] b = new byte[b1.length + b2.length]; System.arraycopy(b1, 0, b, 0, b1.length); System.arraycopy(b2, 0, b, b1.length, b2.length); @@ -1340,16 +1339,18 @@ public class MessageHelper { return sb.toString(); } - static byte[] decodeWord(String word, String encoding) throws IOException { + static byte[] decodeWord(String word, String encoding, String charset) throws IOException { ByteArrayInputStream bis = new ByteArrayInputStream(ASCIIUtility.getBytes(word)); InputStream is; if (encoding.equalsIgnoreCase("B")) is = new BASE64DecoderStream(bis); else if (encoding.equalsIgnoreCase("Q")) - is = new QDecoderStream(bis); - else - throw new UnsupportedEncodingException("Encoding=" + encoding); + is = new QDecoderStreamEx(bis); + else { + Log.e(new UnsupportedEncodingException("Encoding=" + encoding)); + return word.getBytes(charset); + } int count = bis.available(); byte[] bytes = new byte[count]; @@ -1379,10 +1380,10 @@ public class MessageHelper { return text; try { - return decodeMime(new String(decodeWord(text, encoding), charset)); + return decodeMime(new String(decodeWord(text, encoding, charset), charset)); } catch (Throwable ex) { String word = "=?" + charset + "?" + encoding + "?" + text + "?="; - Log.w(new IllegalArgumentException(word, ex)); + Log.e(new IllegalArgumentException(word, ex)); return word; } } diff --git a/app/src/main/java/eu/faircode/email/QDecoderStreamEx.java b/app/src/main/java/eu/faircode/email/QDecoderStreamEx.java new file mode 100644 index 0000000000..8850b8dbe6 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/QDecoderStreamEx.java @@ -0,0 +1,54 @@ +package eu.faircode.email; + +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright 2018-2020 by Marcel Bokhorst (M66B) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.util.QPDecoderStream; + +import java.io.IOException; +import java.io.InputStream; + +public class QDecoderStreamEx extends QPDecoderStream { + private Byte pushed = null; + + public QDecoderStreamEx(InputStream in) { + super(in); + } + + @Override + public int read() throws IOException { + int c = (pushed == null ? in.read() : pushed); + pushed = null; + + if (c == '_') + return ' '; + else if (c == '=') { + ba[0] = (byte) in.read(); + ba[1] = (byte) in.read(); + try { + return ASCIIUtility.parseInt(ba, 0, 2, 16); + } catch (NumberFormatException ex) { + Log.w(ex); + pushed = ba[1]; + return ba[0]; + } + } else + return c; + } +} +