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 ba...@apache.org on 2008/08/21 17:38:15 UTC
svn commit: r687784 - in /james/mime4j/trunk: ./
src/main/java/org/apache/james/mime4j/decoder/
src/main/java/org/apache/james/mime4j/message/
src/test/java/org/apache/james/mime4j/decoder/
Author: bago
Date: Thu Aug 21 08:38:15 2008
New Revision: 687784
URL: http://svn.apache.org/viewvc?rev=687784&view=rev
Log:
Remove code copied from Apache MyFaces Trinidad (Base64OutputStream & Base64OutputStreamTest).
Removed NOTICE credits to Oracle no more needed because of this removal.
Introduced a new Base64OutputStream fixed according to our test suite requirement (MIME4J-71).
Fixed Entity.writeTo to not append CRLF_CRLF in the base64 specific case (so at least this works like the qp code.
Added:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/decoder/Base64OutputStream.java
Removed:
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/decoder/Base64OutputStreamTest.java
Modified:
james/mime4j/trunk/NOTICE.txt
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/decoder/CodecUtil.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/decoder/CodecUtilTest.java
Modified: james/mime4j/trunk/NOTICE.txt
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/NOTICE.txt?rev=687784&r1=687783&r2=687784&view=diff
==============================================================================
--- james/mime4j/trunk/NOTICE.txt (original)
+++ james/mime4j/trunk/NOTICE.txt Thu Aug 21 08:38:15 2008
@@ -12,10 +12,3 @@
developed by Kent Beck, Erich Gamma, and David Saff
License: Common Public License Version 1.0
(http://www.opensource.org/licenses/cpl.php)
-
- The Base64OutputStream and Base64OutputStreamTest classes included in this
- package is based on code from Apache MyFaces Trinidad.
- Portions of Apache MyFaces Trinidad were originally based on the following:
- software copyright (c) 2000-2006, Oracle Corp, <http://www.oracle.com/>.
- and are licensed to the Apache Software Foundation under the
- "Software Grant and Corporate Contribution License Agreement"
\ No newline at end of file
Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/decoder/Base64OutputStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/decoder/Base64OutputStream.java?rev=687784&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/decoder/Base64OutputStream.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/decoder/Base64OutputStream.java Thu Aug 21 08:38:15 2008
@@ -0,0 +1,396 @@
+/****************************************************************
+ * 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.decoder;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * This class is based on Base64 and Base64OutputStream code from Commons-Codec 1.4
+ *
+ * Provides Base64 encoding and decoding as defined by RFC 2045.
+ *
+ * <p>
+ * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose
+ * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.
+ * </p>
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
+ * @author Apache Software Foundation
+ * @since 1.0-dev
+ * @version $Id$
+ */
+public class Base64OutputStream extends FilterOutputStream {
+
+ /**
+ * Chunk size per RFC 2045 section 6.8.
+ *
+ * <p>
+ * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
+ * equal signs.
+ * </p>
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
+ */
+ static final int CHUNK_SIZE = 76;
+
+ /**
+ * Chunk separator per RFC 2045 section 2.1.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
+ */
+ static final byte[] CHUNK_SEPARATOR = {'\r','\n'};
+
+ /**
+ * This array is a lookup table that translates 6-bit positive integer
+ * index values into their "Base64 Alphabet" equivalents as specified
+ * in Table 1 of RFC 2045.
+ *
+ * Thanks to "commons" project in ws.apache.org for this code.
+ * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
+ */
+ private static final byte[] intToBase64 = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+ };
+
+ /**
+ * Byte used to pad output.
+ */
+ private static final byte PAD = '=';
+
+ /**
+ * This array is a lookup table that translates unicode characters
+ * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
+ * into their 6-bit positive integer equivalents. Characters that
+ * are not in the Base64 alphabet but fall within the bounds of the
+ * array are translated to -1.
+ *
+ * Thanks to "commons" project in ws.apache.org for this code.
+ * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
+ */
+ private static final byte[] base64ToInt = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+ };
+
+ /** Mask used to extract 6 bits, used when encoding */
+ private static final int MASK_6BITS = 0x3f;
+
+ // The static final fields above are used for the original static byte[] methods on Base64.
+ // The private member fields below are used with the new streaming approach, which requires
+ // some state be preserved between calls of encode() and decode().
+
+
+ /**
+ * Line length for encoding. Not used when decoding. A value of zero or less implies
+ * no chunking of the base64 encoded data.
+ */
+ private final int lineLength;
+
+ /**
+ * Line separator for encoding. Not used when decoding. Only used if lineLength > 0.
+ */
+ private final byte[] lineSeparator;
+
+ /**
+ * Convenience variable to help us determine when our buffer is going to run out of
+ * room and needs resizing. <code>encodeSize = 4 + lineSeparator.length;</code>
+ */
+ private final int encodeSize;
+
+ /**
+ * Buffer for streaming.
+ */
+ private byte[] buf = new byte[1024];
+
+ /**
+ * Position where next character should be written in the buffer.
+ */
+ private int pos;
+
+ /**
+ * Variable tracks how many characters have been written to the current line.
+ * Only used when encoding. We use it to make sure each encoded line never
+ * goes beyond lineLength (if lineLength > 0).
+ */
+ private int currentLinePos;
+
+ /**
+ * Writes to the buffer only occur after every 3 reads when encoding, an
+ * every 4 reads when decoding. This variable helps track that.
+ */
+ private int modulus;
+
+ /**
+ * Boolean flag to indicate the EOF has been reached. Once EOF has been
+ * reached, this Base64 object becomes useless, and must be thrown away.
+ */
+ private boolean eof;
+
+ /**
+ * Place holder for the 3 bytes we're dealing with for our base64 logic.
+ * Bitwise operations store and extract the base64 encoding or decoding from
+ * this variable.
+ */
+ private int x;
+
+ /**
+ * Default constructor: lineLength is 76, and the lineSeparator is CRLF
+ * when encoding, and all forms can be decoded.
+ */
+ public Base64OutputStream(OutputStream os) {
+ this(os, CHUNK_SIZE, CHUNK_SEPARATOR);
+ }
+
+ /**
+ * <p>
+ * Consumer can use this constructor to choose a different lineLength
+ * when encoding (lineSeparator is still CRLF). All forms of data can
+ * be decoded.
+ * </p><p>
+ * Note: lineLengths that aren't multiples of 4 will still essentially
+ * end up being multiples of 4 in the encoded data.
+ * </p>
+ *
+ * @param lineLength each line of encoded data will be at most this long
+ * (rounded up to nearest multiple of 4).
+ * If lineLength <= 0, then the output will not be divided into lines (chunks).
+ * Ignored when decoding.
+ */
+ public Base64OutputStream(OutputStream os, int lineLength) {
+ this(os, lineLength, CHUNK_SEPARATOR);
+ }
+
+ /**
+ * <p>
+ * Consumer can use this constructor to choose a different lineLength
+ * and lineSeparator when encoding. All forms of data can
+ * be decoded.
+ * </p><p>
+ * Note: lineLengths that aren't multiples of 4 will still essentially
+ * end up being multiples of 4 in the encoded data.
+ * </p>
+ * @param lineLength Each line of encoded data will be at most this long
+ * (rounded up to nearest multiple of 4). Ignored when decoding.
+ * If <= 0, then output will not be divided into lines (chunks).
+ * @param lineSeparator Each line of encoded data will end with this
+ * sequence of bytes.
+ * If lineLength <= 0, then the lineSeparator is not used.
+ * @throws IllegalArgumentException The provided lineSeparator included
+ * some base64 characters. That's not going to work!
+ */
+ public Base64OutputStream(OutputStream os, int lineLength, byte[] lineSeparator) {
+ super(os);
+ this.lineLength = lineLength;
+ this.lineSeparator = new byte[lineSeparator.length];
+ System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
+ if (lineLength > 0) {
+ this.encodeSize = 4 + lineSeparator.length;
+ } else {
+ this.encodeSize = 4;
+ }
+ if (containsBase64Byte(lineSeparator)) {
+ String sep;
+ try {
+ sep = new String(lineSeparator, "UTF-8");
+ } catch (UnsupportedEncodingException uee) {
+ sep = new String(lineSeparator);
+ }
+ throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");
+ }
+ }
+
+ /** Doubles our buffer. */
+ private void resizeBuf() {
+ byte[] b = new byte[buf.length * 2];
+ System.arraycopy(buf, 0, b, 0, buf.length);
+ buf = b;
+ }
+
+ /**
+ * Returns whether or not the <code>octet</code> is in the base 64 alphabet.
+ *
+ * @param octet
+ * The value to test
+ * @return <code>true</code> if the value is defined in the the base 64 alphabet, <code>false</code> otherwise.
+ */
+ public static boolean isBase64(byte octet) {
+ return octet == PAD || (octet >= 0 && octet < base64ToInt.length && base64ToInt[octet] != -1);
+ }
+
+ /*
+ * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
+ *
+ * @param arrayOctet
+ * byte array to test
+ * @return <code>true</code> if any byte is a valid character in the Base64 alphabet; false herwise
+ */
+ private static boolean containsBase64Byte(byte[] arrayOctet) {
+ for (int i = 0; i < arrayOctet.length; i++) {
+ if (isBase64(arrayOctet[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Implementation of the Encoder Interface
+
+ private final byte[] singleByte = new byte[1];
+
+ /**
+ * Writes the specified <code>byte</code> to this output stream.
+ */
+ public void write(int i) throws IOException {
+ singleByte[0] = (byte) i;
+ write(singleByte, 0, 1);
+ }
+
+
+ /**
+ * Writes <code>len</code> bytes from the specified
+ * <code>b</code> array starting at <code>offset</code> to
+ * this output stream.
+ *
+ * @param b source byte array
+ * @param offset where to start reading the bytes
+ * @param len maximum number of bytes to write
+ *
+ * @throws IOException if an I/O error occurs.
+ * @throws NullPointerException if the byte array parameter is null
+ * @throws IndexOutOfBoundsException if offset, len or buffer size are invalid
+ */
+ public void write(byte b[], int offset, int len) throws IOException {
+ if (eof) {
+ return;
+ }
+ if (b == null) {
+ throw new NullPointerException();
+ } else if (len < 0) {
+ // inAvail < 0 is how we're informed of EOF in the underlying data we're
+ // encoding.
+ eof = true;
+ if (buf.length - pos < encodeSize) {
+ resizeBuf();
+ }
+ switch (modulus) {
+ case 1:
+ buf[pos++] = intToBase64[(x >> 2) & MASK_6BITS];
+ buf[pos++] = intToBase64[(x << 4) & MASK_6BITS];
+ buf[pos++] = PAD;
+ buf[pos++] = PAD;
+ break;
+
+ case 2:
+ buf[pos++] = intToBase64[(x >> 10) & MASK_6BITS];
+ buf[pos++] = intToBase64[(x >> 4) & MASK_6BITS];
+ buf[pos++] = intToBase64[(x << 2) & MASK_6BITS];
+ buf[pos++] = PAD;
+ break;
+ }
+ if (lineLength > 0) {
+ System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
+ pos += lineSeparator.length;
+ // TODO I had to add this to make it work as the quoted printable encoder.
+ // not sure this is generally speaking ok.
+ System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
+ pos += lineSeparator.length;
+ }
+ } else if (offset < 0 || len < 0 || offset + len < 0) {
+ throw new IndexOutOfBoundsException();
+ } else if (offset > b.length || offset + len > b.length) {
+ throw new IndexOutOfBoundsException();
+ } else if (len > 0) {
+ for (int i = 0; i < len; i++) {
+ if (buf.length - pos < encodeSize) {
+ resizeBuf();
+ }
+ modulus = (++modulus) % 3;
+ int bc = b[offset++];
+ if (bc < 0) { bc += 256; }
+ x = (x << 8) + bc;
+ if (0 == modulus) {
+ buf[pos++] = intToBase64[(x >> 18) & MASK_6BITS];
+ buf[pos++] = intToBase64[(x >> 12) & MASK_6BITS];
+ buf[pos++] = intToBase64[(x >> 6) & MASK_6BITS];
+ buf[pos++] = intToBase64[x & MASK_6BITS];
+ currentLinePos += 4;
+ if (lineLength > 0 && lineLength <= currentLinePos) {
+ System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
+ pos += lineSeparator.length;
+ currentLinePos = 0;
+ }
+ }
+ }
+ }
+ flushBuffer();
+ }
+
+ /**
+ * Flushes this output stream and forces any buffered output bytes
+ * to be written out to the stream. If propogate is true, the wrapped
+ * stream will also be flushed.
+ *
+ * @param propogate boolean flag to indicate whether the wrapped
+ * OutputStream should also be flushed.
+ * @throws IOException if an I/O error occurs.
+ */
+ private void flushBuffer() throws IOException {
+ if (pos > 0) {
+ out.write(buf, 0, pos);
+ // buf = null;
+ pos = 0;
+ }
+ }
+
+ /**
+ * Flushes this output stream and forces any buffered output bytes
+ * to be written out to the stream.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ public void flush() throws IOException {
+ flushBuffer();
+ out.flush();
+ }
+
+ /**
+ * Closes this output stream and releases any system resources
+ * associated with the stream.
+ */
+ public void close() throws IOException {
+ // Notify encoder of EOF (-1).
+ write(singleByte, 0, -1);
+ flush();
+ out.close();
+ }
+
+}
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/decoder/CodecUtil.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/decoder/CodecUtil.java?rev=687784&r1=687783&r2=687784&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/decoder/CodecUtil.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/decoder/CodecUtil.java Thu Aug 21 08:38:15 2008
@@ -24,7 +24,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.OutputStreamWriter;
/**
* Utility methods related to codecs.
@@ -314,14 +313,7 @@
* @throws IOException
*/
public static OutputStream wrapBase64(final OutputStream out) throws IOException {
- return new Base64OutputStream(new OutputStreamWriter(new LineBreakingOutputStream(out, 76)) {
-
- public void close() throws IOException {
- // do not close wrapped stream but flush them
- flush();
- }
-
- });
+ return new Base64OutputStream(out);
}
private static final class Base64Encoder {
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java?rev=687784&r1=687783&r2=687784&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java Thu Aug 21 08:38:15 2008
@@ -192,8 +192,5 @@
// we don't want it to close the inner stream so we override the behaviour
// for the wrapping stream writer.
if (encOut != out) encOut.close();
- if (MimeUtil.ENC_BASE64.equals(getContentTransferEncoding())) {
- out.write(CodecUtil.CRLF_CRLF);
- }
}
}
Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/decoder/CodecUtilTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/decoder/CodecUtilTest.java?rev=687784&r1=687783&r2=687784&view=diff
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/decoder/CodecUtilTest.java (original)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/decoder/CodecUtilTest.java Thu Aug 21 08:38:15 2008
@@ -20,14 +20,11 @@
package org.apache.james.mime4j.decoder;
import org.apache.james.mime4j.ExampleMail;
-import org.apache.james.mime4j.decoder.Base64InputStream;
-import org.apache.james.mime4j.decoder.CodecUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStreamWriter;
import junit.framework.TestCase;
@@ -84,7 +81,7 @@
private String roundtripUsingOutputStream(String input) throws IOException {
ByteArrayOutputStream out2 = new ByteArrayOutputStream();
- Base64OutputStream outb64 = new Base64OutputStream(new OutputStreamWriter(new LineBreakingOutputStream(out2, 76)));
+ Base64OutputStream outb64 = new Base64OutputStream(out2, 76);
CodecUtil.copy(new ByteArrayInputStream(input.getBytes()), outb64);
outb64.flush();
outb64.close();
@@ -96,7 +93,6 @@
return output;
}
-
/**
* This test is a proof for MIME4J-67
*/
@@ -123,9 +119,10 @@
}
*/
- /* performance test, not a unit test
+ /* performance test, not a unit test */
+ /*
public void testPerformance() throws Exception {
- if (true) return;
+ // if (true) return;
byte[] bytes = new byte[10000];
Random r = new Random(432875623874L);
r.nextBytes(bytes);
@@ -143,7 +140,6 @@
long time3 = System.currentTimeMillis();
roundtripUsingEncoder(input);
long time4 = System.currentTimeMillis();
- roundtripUsingOutputStream(input);
totalEncoder1 += time2-time1;
totalStream1 += time3-time2;
@@ -151,7 +147,7 @@
}
System.out.println("Encoder 1st: "+totalEncoder1);
- System.out.println("Encoder 2st: "+totalEncoder2);
+ System.out.println("Encoder 2nd: "+totalEncoder2);
System.out.println("Stream 1st: "+totalStream1);
}
*/
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org