You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rd...@apache.org on 2008/05/26 17:02:48 UTC
svn commit: r660206 - in /james/mime4j/trunk/src:
main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java
main/java/org/apache/james/mime4j/util/CodecUtil.java
test/java/org/apache/james/mime4j/util/QuotedPrintableEncodeTest.java
Author: rdonkin
Date: Mon May 26 08:02:46 2008
New Revision: 660206
URL: http://svn.apache.org/viewvc?rev=660206&view=rev
Log:
Added quoted printable encoding support for binaries. Completes fixes and test for MIME4J-36 (Wrong implementation of TempFileBinaryBody.writeTo) https://issues.apache.org/jira/browse/MIME4J-37
Added:
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/QuotedPrintableEncodeTest.java
Modified:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/CodecUtil.java
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java?rev=660206&r1=660205&r2=660206&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java Mon May 26 08:02:46 2008
@@ -100,7 +100,8 @@
CodecUtil.encodeBase64(inputStream, out);
out.write(CodecUtil.CRLF_CRLF);
} else if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(transferEncoding)) {
- IOUtils.copy(inputStream,out);
+ CodecUtil.encodeQuotedPrintableBinary(inputStream,out);
+ out.write(CodecUtil.CRLF_CRLF);
} else {
IOUtils.copy(inputStream,out);
}
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/CodecUtil.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/CodecUtil.java?rev=660206&r1=660205&r2=660206&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/CodecUtil.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/CodecUtil.java Mon May 26 08:02:46 2008
@@ -35,6 +35,128 @@
private static final int DEFAULT_ENCODING_BUFFER_SIZE = 1024;
+ private static final byte SPACE = 0x20;
+ private static final byte EQUALS = 0x3D;
+ private static final byte CR = 0x0D;
+ private static final byte LF = 0x0A;
+ private static final byte QUOTED_PRINTABLE_LAST_PLAIN = 0x7E;
+ private static final int QUOTED_PRINTABLE_MAX_LINE_LENGTH = 76;
+ private static final int QUOTED_PRINTABLE_OCTETS_PER_ESCAPE = 3;
+ private static final byte[] HEX_DIGITS = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+
+ /**
+ * Encodes the given stream using Quoted-Printable.
+ * This assumes that text is binary and therefore escapes
+ * all line endings.
+ * @param in not null
+ * @param out not null
+ * @throws IOException
+ */
+ public static void encodeQuotedPrintableBinary(final InputStream in, final OutputStream out) throws IOException {
+
+ BinaryQuotedPrintableEncoder encoder = new BinaryQuotedPrintableEncoder(DEFAULT_ENCODING_BUFFER_SIZE);
+ encoder.encode(in, out);
+ }
+
+ private static final class BinaryQuotedPrintableEncoder {
+ private final byte[] inBuffer;
+ private final byte[] outBuffer;
+
+ private int nextSoftBreak;
+ private int inputIndex;
+ private int outputIndex;
+ private int inputLength;
+ private InputStream in;
+ private OutputStream out;
+
+
+ public BinaryQuotedPrintableEncoder(int bufferSize) {
+ inBuffer = new byte[bufferSize];
+ outBuffer = new byte[3*bufferSize];
+ inputLength = 0;
+ outputIndex = 0;
+ nextSoftBreak = QUOTED_PRINTABLE_MAX_LINE_LENGTH + 1;
+ in = null;
+ out = null;
+ }
+
+ public void encode(final InputStream in, final OutputStream out) throws IOException {
+ this.in = in;
+ this.out = out;
+ nextSoftBreak = QUOTED_PRINTABLE_MAX_LINE_LENGTH + 1;
+ read();
+ while(inputLength > -1) {
+ while (inputIndex < inputLength) {
+ final byte next = inBuffer[inputIndex];
+ encode(next);
+ inputIndex++;
+ }
+ read();
+ }
+ flushOutput();
+ }
+
+ private void read() throws IOException {
+ inputLength = in.read(inBuffer);
+ inputIndex = 0;
+ }
+
+ private void encode(byte next) throws IOException {
+ if (next <= SPACE) {
+ escape(next);
+ } else if (next > QUOTED_PRINTABLE_LAST_PLAIN) {
+ escape(next);
+ } else if (next == EQUALS) {
+ escape(next);
+ } else {
+ plain(next);
+ }
+ }
+
+ private void plain(byte next) throws IOException {
+ if (--nextSoftBreak <= 1) {
+ softBreak();
+ }
+ write(next);
+ }
+
+ private void escape(byte next) throws IOException {
+ if (--nextSoftBreak <= QUOTED_PRINTABLE_OCTETS_PER_ESCAPE) {
+ softBreak();
+ }
+ write(EQUALS);
+ --nextSoftBreak;
+ write(HEX_DIGITS[next >> 4]);
+ --nextSoftBreak;
+ write(HEX_DIGITS[next % 0x10]);
+ }
+
+ private void write(byte next) throws IOException {
+ outBuffer[outputIndex] = next;
+ if (outputIndex >= outBuffer.length) {
+ flushOutput();
+ } else {
+ outputIndex++;
+ }
+ }
+
+ private void softBreak() throws IOException {
+ write(EQUALS);
+ write(CR);
+ write(LF);
+ nextSoftBreak = QUOTED_PRINTABLE_MAX_LINE_LENGTH;
+ }
+
+ private void flushOutput() throws IOException {
+ if (outputIndex < outBuffer.length) {
+ out.write(outBuffer, 0, outputIndex);
+ } else {
+ out.write(outBuffer);
+ }
+ outputIndex = 0;
+ }
+ }
+
/**
* Encodes the given stream using Base64.
* @param in not null
Added: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/QuotedPrintableEncodeTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/QuotedPrintableEncodeTest.java?rev=660206&view=auto
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/QuotedPrintableEncodeTest.java (added)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/QuotedPrintableEncodeTest.java Mon May 26 08:02:46 2008
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.james.mime4j.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
+
+import junit.framework.TestCase;
+
+public class QuotedPrintableEncodeTest extends TestCase {
+
+ private static final Charset US_ASCII = Charset.forName("US-ASCII");
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testEscapedSoftBreak() throws Exception {
+ byte[] content = new byte[500];
+ Arrays.fill(content, (byte)0x20);
+ byte[] expected = new byte[1557];
+ int index = 0;
+ for (int l=0;l<20;l++) {
+ for (int i=0;i<25;i++) {
+ expected[index++] = '=';
+ expected[index++] = '2';
+ expected[index++] = '0';
+ }
+ if (l<19) {
+ expected[index++] = '=';
+ expected[index++] = '\r';
+ expected[index++] = '\n';
+ }
+ }
+ check(content, expected);
+ }
+
+ public void testPlainAsciiSoftBreak() throws Exception {
+ byte[] content = new byte[500];
+ Arrays.fill(content, (byte)0x29);
+ byte[] expected = new byte[518];
+ Arrays.fill(expected, (byte)0x29);
+ expected[75] = '=';
+ expected[76] = '\r';
+ expected[77] = '\n';
+ expected[153] = '=';
+ expected[154] = '\r';
+ expected[155] = '\n';
+ expected[231] = '=';
+ expected[232] = '\r';
+ expected[233] = '\n';
+ expected[309] = '=';
+ expected[310] = '\r';
+ expected[311] = '\n';
+ expected[387] = '=';
+ expected[388] = '\r';
+ expected[389] = '\n';
+ expected[465] = '=';
+ expected[466] = '\r';
+ expected[467] = '\n';
+ check(content, expected);
+ }
+
+ public void testPlainASCII() throws Exception {
+ checkRoundtrip("Thisisaverysimplemessage.Thisisaverysimplemessage.Thisisaverysimplemessage." +
+ "Thisisaverysimplemessage.Thisisaverysimplemessage.Thisisaverysimplemessage." +
+ "Thisisaverysimplemessage.Thisisaverysimplemessage.Thisisaverysimplemessage." +
+ "Thisisaverysimplemessage.Thisisaverysimplemessage.Thisisaverysimplemessage.");
+ }
+
+ public void testEncodeSpace() throws Exception {
+ checkRoundtrip(" ");
+ }
+
+ public void testLetterEncoding() throws Exception {
+ for (byte b=0;b<Byte.MAX_VALUE;b++) {
+ byte[] content = {b};
+ checkRoundtrip(content);
+ }
+ }
+
+ private void checkRoundtrip(String content) throws Exception {
+ checkRoundtrip(content, US_ASCII);
+ }
+
+ private void checkRoundtrip(String content, Charset charset) throws Exception {
+ checkRoundtrip(charset.encode(content).array());
+ }
+
+ private void checkRoundtrip(byte[] content) throws Exception {
+ InputStream in = new ByteArrayInputStream(content);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ CodecUtil.encodeQuotedPrintableBinary(in, out);
+ // read back through decoder
+ in = new QuotedPrintableInputStream(new ByteArrayInputStream(out.toByteArray()));
+ out = new ByteArrayOutputStream();
+ IOUtils.copy(in, out);
+ assertEquals(content, out.toByteArray());
+ }
+
+ private void check(byte[] content, byte[] expected) throws Exception {
+ ByteArrayInputStream in = new ByteArrayInputStream(content);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ CodecUtil.encodeQuotedPrintableBinary(in, out);
+ assertEquals(expected, out.toByteArray());
+ }
+
+ private void assertEquals(byte[] expected, byte[] actual) {
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < actual.length; i++) {
+ assertEquals("Mismatch@" + i, expected[i], actual[i]);
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org