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/07/30 13:01:33 UTC

svn commit: r680972 - in /james/mime4j/trunk/src: main/java/org/apache/james/mime4j/message/ main/java/org/apache/james/mime4j/util/ test/java/org/apache/james/mime4j/util/

Author: bago
Date: Wed Jul 30 04:01:23 2008
New Revision: 680972

URL: http://svn.apache.org/viewvc?rev=680972&view=rev
Log:
Message.writeTo for an rfc822 part was ignoring the message encoding (MIME4J-65)
The patch remove the usage of the Base64Encoder (because of MIME4J-66 and MIME4J-67) in favor of a Base64OutputStream cloned by ALv2/ASF licensed MyFaces Trinidad 1.2.8. 

Added:
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/Base64OutputStream.java   (with props)
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/LineBreakingOutputStream.java   (with props)
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/Base64OutputStreamTest.java   (with props)
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/LineBreakingOutputStreamTest.java   (with props)
Modified:
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/BodyPart.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Entity.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Message.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileBinaryBody.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileTextBody.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/CodecUtil.java
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/CodecUtilTest.java

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/BodyPart.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/BodyPart.java?rev=680972&r1=680971&r2=680972&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/BodyPart.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/BodyPart.java Wed Jul 30 04:01:23 2008
@@ -19,10 +19,6 @@
 
 package org.apache.james.mime4j.message;
 
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.apache.james.mime4j.MimeException;
 
 
 /**
@@ -33,12 +29,4 @@
  */
 public class BodyPart extends Entity {
 
-    /**
-     * 
-     * @see org.apache.james.mime4j.message.Entity#writeTo(java.io.OutputStream, int)
-     */
-    public void writeTo(OutputStream out, int mode) throws IOException, MimeException {
-        getHeader().writeTo(out, mode);
-        getBody().writeTo(out, mode);
-    }
 }

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=680972&r1=680971&r2=680972&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 Wed Jul 30 04:01:23 2008
@@ -19,13 +19,16 @@
 
 package org.apache.james.mime4j.message;
 
-import java.io.IOException;
-import java.io.OutputStream;
-
 import org.apache.james.mime4j.MimeException;
 import org.apache.james.mime4j.field.ContentTransferEncodingField;
 import org.apache.james.mime4j.field.ContentTypeField;
 import org.apache.james.mime4j.field.Field;
+import org.apache.james.mime4j.util.CodecUtil;
+import org.apache.james.mime4j.util.MessageUtils;
+import org.apache.james.mime4j.util.MimeUtil;
+
+import java.io.IOException;
+import java.io.OutputStream;
 
 /**
  * MIME entity. An entity has a header and a body (see RFC 2045).
@@ -168,5 +171,29 @@
      * @param mode output mode {@link MessageUtils}
      * @throws IOException 
      */
-    public abstract void writeTo(OutputStream out, int mode) throws IOException, MimeException;
+    public void writeTo(OutputStream out, int mode) throws IOException, MimeException {
+        getHeader().writeTo(out, mode);
+        
+        out.flush();
+        
+        final Body body = getBody();
+
+        OutputStream encOut;
+        if (MimeUtil.ENC_BASE64.equals(getContentTransferEncoding())) {
+            encOut = CodecUtil.wrapBase64(out);
+        } else if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(getContentTransferEncoding())) {
+            encOut = CodecUtil.wrapQuotedPrintable(out, (body instanceof BinaryBody));
+        } else {
+            encOut = out;
+        }
+        body.writeTo(encOut, mode);
+        encOut.flush();
+        // the Base64 output streams requires closing of the stream but
+        // 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/main/java/org/apache/james/mime4j/message/Message.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Message.java?rev=680972&r1=680971&r2=680972&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Message.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/Message.java Wed Jul 30 04:01:23 2008
@@ -19,19 +19,20 @@
 
 package org.apache.james.mime4j.message;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Stack;
-
 import org.apache.james.mime4j.BodyDescriptor;
 import org.apache.james.mime4j.CharArrayBuffer;
 import org.apache.james.mime4j.ContentHandler;
 import org.apache.james.mime4j.MimeException;
 import org.apache.james.mime4j.MimeStreamParser;
+import org.apache.james.mime4j.decoder.Base64InputStream;
+import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
 import org.apache.james.mime4j.field.Field;
 import org.apache.james.mime4j.field.UnstructuredField;
-import org.apache.james.mime4j.util.MessageUtils;
+import org.apache.james.mime4j.util.MimeUtil;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Stack;
 
 
 /**
@@ -86,20 +87,6 @@
         return (UnstructuredField) getHeader().getField(Field.SUBJECT);
     }
     
-    /**
-     * Writes out the content of this message..
-     * @param out not null
-     * @param mode header out validation mode {@link MessageUtils}
-     * @throws MimeException 
-     * @see org.apache.james.mime4j.message.Entity#writeTo(java.io.OutputStream, int)
-     */
-    public void writeTo(OutputStream out, int mode) throws IOException, MimeException {
-        getHeader().writeTo(out, mode);
-
-        final Body body = getBody();
-        body.writeTo(out, mode);
-    }
-    
     private class MessageBuilder implements ContentHandler {
         private Stack stack = new Stack();
         
@@ -183,13 +170,24 @@
             final String enc = bd.getTransferEncoding();
             
             final Body body;
+            
+            final InputStream decodedStream;
+            if (MimeUtil.ENC_BASE64.equals(enc)) {
+                decodedStream = new Base64InputStream(is);
+            } else if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(enc)) {
+                decodedStream = new QuotedPrintableInputStream(is);
+            } else {
+                decodedStream = is;
+            }
+            
             if (bd.getMimeType().startsWith("text/")) {
-                body = new TempFileTextBody(is, bd.getCharset(), enc);
+                body = new TempFileTextBody(decodedStream, bd.getCharset());
             } else {
-                body = new TempFileBinaryBody(is, enc);
+                body = new TempFileBinaryBody(decodedStream);
             }
             
-            ((Entity) stack.peek()).setBody(body);
+            Entity entity = ((Entity) stack.peek());
+            entity.setBody(body);
         }
         
         /**

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=680972&r1=680971&r2=680972&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 Wed Jul 30 04:01:23 2008
@@ -19,18 +19,15 @@
 
 package org.apache.james.mime4j.message;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import org.apache.james.mime4j.decoder.Base64InputStream;
-import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
 import org.apache.james.mime4j.util.CodecUtil;
-import org.apache.james.mime4j.util.MimeUtil;
 import org.apache.james.mime4j.util.TempFile;
 import org.apache.james.mime4j.util.TempPath;
 import org.apache.james.mime4j.util.TempStorage;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
 
 /**
  * Binary body backed by a {@link org.apache.james.mime4j.util.TempFile}.
@@ -42,7 +39,6 @@
     
     private Entity parent = null;
     private TempFile tempFile = null;
-    private final String transferEncoding;
 
     /**
      * Use the given InputStream to build the TemporyFileBinaryBody
@@ -50,22 +46,13 @@
      * @param is the InputStream to use as source
      * @throws IOException
      */
-    public TempFileBinaryBody(final InputStream is, final String transferEncoding) throws IOException {
+    public TempFileBinaryBody(final InputStream is) throws IOException {
         
-        this.transferEncoding = transferEncoding;
         TempPath tempPath = TempStorage.getInstance().getRootTempPath();
         tempFile = tempPath.createTempFile("attachment", ".bin");
         
         OutputStream out = tempFile.getOutputStream();
-        final InputStream decodedStream;
-        if (MimeUtil.ENC_BASE64.equals(transferEncoding)) {
-            decodedStream = new Base64InputStream(is);
-        } else if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(transferEncoding)) {
-            decodedStream = new QuotedPrintableInputStream(is);
-        } else {
-            decodedStream = is;
-        }
-        CodecUtil.copy(decodedStream, out);
+        CodecUtil.copy(is, out);
         out.close();
     }
     
@@ -95,13 +82,6 @@
      */
     public void writeTo(OutputStream out, int mode) throws IOException {
         final InputStream inputStream = getInputStream();
-        if (MimeUtil.ENC_BASE64.equals(transferEncoding)) {
-            CodecUtil.encodeBase64(inputStream, out);
-            out.write(CodecUtil.CRLF_CRLF);
-        } else if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(transferEncoding)) {
-            CodecUtil.encodeQuotedPrintableBinary(inputStream,out);
-        } else {
-            CodecUtil.copy(inputStream,out);
-        }
+        CodecUtil.copy(inputStream,out);
     }
 }

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileTextBody.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileTextBody.java?rev=680972&r1=680971&r2=680972&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileTextBody.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/message/TempFileTextBody.java Wed Jul 30 04:01:23 2008
@@ -19,24 +19,21 @@
 
 package org.apache.james.mime4j.message;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.io.UnsupportedEncodingException;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.james.mime4j.decoder.Base64InputStream;
-import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
 import org.apache.james.mime4j.util.CharsetUtil;
 import org.apache.james.mime4j.util.CodecUtil;
-import org.apache.james.mime4j.util.MimeUtil;
 import org.apache.james.mime4j.util.TempFile;
 import org.apache.james.mime4j.util.TempPath;
 import org.apache.james.mime4j.util.TempStorage;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
 
 /**
  * Text body backed by a {@link org.apache.james.mime4j.util.TempFile}.
@@ -49,30 +46,15 @@
     
     private String mimeCharset = null;
     private TempFile tempFile = null;
-    private final String transferEncoding;
 
-    public TempFileTextBody(InputStream is) throws IOException {
-        this(is, null, null);
-    }
-    
-    public TempFileTextBody(final InputStream is, final String mimeCharset, 
-            final String transferEncoding) throws IOException {
+    public TempFileTextBody(final InputStream is, final String mimeCharset) throws IOException {
         
         this.mimeCharset = mimeCharset;
-        this.transferEncoding = transferEncoding;        
         TempPath tempPath = TempStorage.getInstance().getRootTempPath();
         tempFile = tempPath.createTempFile("attachment", ".txt");
         
         OutputStream out = tempFile.getOutputStream();
-        final InputStream decodedStream;
-        if (MimeUtil.ENC_BASE64.equals(transferEncoding)) {
-            decodedStream = new Base64InputStream(is);
-        } else if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(transferEncoding)) {
-            decodedStream = new QuotedPrintableInputStream(is);
-        } else {
-            decodedStream = is;
-        }
-        CodecUtil.copy(decodedStream, out);
+        CodecUtil.copy(is, out);
         out.close();
     }
     
@@ -123,13 +105,6 @@
      */
     public void writeTo(OutputStream out, int mode) throws IOException {
         final InputStream inputStream = tempFile.getInputStream();
-        if (MimeUtil.ENC_BASE64.equals(transferEncoding)) {
-            CodecUtil.encodeBase64(inputStream, out);
-            out.write(CodecUtil.CRLF_CRLF);
-        } else if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(transferEncoding)) {
-            CodecUtil.encodeQuotedPrintable(inputStream,out);
-        } else {
-            CodecUtil.copy(inputStream,out);
-        }
+        CodecUtil.copy(inputStream,out);
     }
 }

Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/Base64OutputStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/Base64OutputStream.java?rev=680972&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/Base64OutputStream.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/Base64OutputStream.java Wed Jul 30 04:01:23 2008
@@ -0,0 +1,355 @@
+/****************************************************************
+ * 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.OutputStream;
+import java.io.Writer;
+import java.io.IOException;
+
+/**
+ * This class has been copied from Apache MyFaces Trinidad 1.2.8
+ * 
+ * An OutputStream that encodes data in a base64 representation.
+ * It takes a Writer as its single argument to its constructor and all
+ * bytes written to the stream are correspondingly converted into Base64
+ * and written out to the provided writer.
+ */
+public class Base64OutputStream extends OutputStream
+{
+  public Base64OutputStream(Writer out)
+  {
+    _out = out;
+    _numLeftoverBytes = 0;
+    _leftoverBytes = new byte[2];
+  }
+  
+  /**
+   *  Takes a byte writes it out to the writer
+   * 
+   * @param b   a byte
+   */
+  public final void write(final int b) throws IOException
+  {
+    _single[0] = (byte) b;
+    this.write(_single, 0, 1);
+  } 
+  
+  /**
+   * Writes len bytes from the specified byte array starting at offset off 
+   * to this output stream. The general contract for write(b, off, len) is 
+   * that some of the bytes in the array b are written to the output stream 
+   * in order; element b[off] is the first byte written and b[off+len-1] is 
+   * the last byte written by this operation.
+   * 
+   * The write method of OutputStream calls the write method of one argument 
+   * on each of the bytes to be written out. Subclasses are encouraged to 
+   * override this method and provide a more efficient implementation.
+   * 
+   * If b is null, a NullPointerException is thrown.
+   * 
+   * If off is negative, or len is negative, or off+len is greater than the 
+   * length of the array b, then an IndexOutOfBoundsException is thrown. 
+   * 
+   * 
+   * @param b        the data
+   * @param off      the start offset in the data
+   * @param len      the number of bytes to read
+   */
+  public final void write(final byte[] b, final int off, final int len) 
+                                      throws IOException, NullPointerException
+  {
+    if (b==null) 
+    {
+      throw new NullPointerException("BYTE_ARRAY_CANNOT_BE_NULL");
+    }
+      
+    if (off<0 || len<0 || off+len>b.length) 
+    {
+      throw new IndexOutOfBoundsException("ACTUAL_LENGTH_OFFSET: " + b.length + " | " + off + " | " + len);
+    }
+    
+    int lengthToProcess = len;
+    int index = off;
+    
+    // base case 1: if only processing one byte from byte array
+    if (lengthToProcess==1) 
+    {
+      if (_numLeftoverBytes==0) 
+      {
+        // remember this byte for next call to write
+        _numLeftoverBytes = 1;
+        _leftoverBytes[0] = b[index];
+      }
+      else if (_numLeftoverBytes==1) 
+      {
+        // remember this byte for next call to write
+        _numLeftoverBytes = 2;
+        _leftoverBytes[1] = b[index];
+      }
+      else if (_numLeftoverBytes==2) 
+      {
+        // this one byte is enough to complete a triplet
+        // so convert triplet into Base64
+        _writeBase64(_leftoverBytes[0], _leftoverBytes[1], b[index]);
+        _numLeftoverBytes=0;
+      }
+      return;
+    }  //end if (lengthToProcess==1)
+    
+    // base case 2: if only processing two bytes from byte array
+    if (lengthToProcess==2) 
+    {
+      if (_numLeftoverBytes==0) 
+      {
+        // not enough to process a triplet, so remember these two bytes 
+        // for next call to write
+        _numLeftoverBytes = 2;
+        _leftoverBytes[0] = b[index];
+        _leftoverBytes[1] = b[index+1];
+      }
+      else if (_numLeftoverBytes==1) 
+      {
+        // these two bytes form triplet combined with the leftover byte
+        _writeBase64(_leftoverBytes[0], b[index], b[index+1]);
+        _numLeftoverBytes = 0;
+      }
+      else if (_numLeftoverBytes==2) 
+      {
+        // two leftover bytes and one new byte form a triplet
+        // the second new byte is remembered for next call to write
+        _writeBase64(_leftoverBytes[0], _leftoverBytes[1], b[index]);
+        _leftoverBytes[0] = b[index+1];
+        _numLeftoverBytes = 1;
+      }
+      return;
+    }  // end if (lengthToProcess==2)
+    
+    // case involving looping
+    if (lengthToProcess>2) 
+    {
+      if (_numLeftoverBytes==1) 
+      {
+        _writeBase64(_leftoverBytes[0], b[index], b[index+1]);
+        _numLeftoverBytes = 0;
+        lengthToProcess -= 2;
+        index += 2;
+        // proceed with loop
+      }      
+      else if (_numLeftoverBytes==2) 
+      {
+        _writeBase64(_leftoverBytes[0], _leftoverBytes[1], b[index]);
+        _numLeftoverBytes = 0;
+        lengthToProcess -= 1;
+        index += 1;
+        // proceed with loop
+      }
+
+      _processArray(b, index, lengthToProcess);
+    }
+
+  } //end write(byte[], int off, int len)
+  
+  public final void flush() throws IOException 
+  {
+    _out.flush();
+  }
+
+  /**
+   * Call this method to indicate end of data stream 
+   * and to append any padding characters if necessary.  This method should be 
+   * called only if there will be no subsequent calls to a write method.  
+   * Subsequent calls to the write method will result in incorrect encoding.
+   * 
+   * @deprecated use the close() method instead.
+   */
+  public void finish() throws IOException
+  {
+    close();
+  }
+  
+  /**
+   * Call this method to indicate end of data stream 
+   * and to append any padding characters if necessary.  This method should be 
+   * called only if there will be no subsequent calls to a write method.  
+   * Subsequent calls to the write method will result in incorrect encoding.
+   * 
+   */
+  public final void close() throws IOException
+  {
+    if (_numLeftoverBytes==1) 
+    {
+      // grab the one byte from the leftover array
+      byte b1 = _leftoverBytes[0];
+      
+      // convert to two base 64 chars
+      int c1, c2;
+      c1 = (b1>>2)&0x3f;
+      c2 = (b1<<4)&0x3f;
+      
+      char[] encodedChars = _fourChars;
+      
+      encodedChars[0] = _encode(c1);
+      encodedChars[1] = _encode(c2);
+      // append two padding characters
+      encodedChars[2] = '=';
+      encodedChars[3] = '=';
+      
+      _out.write(encodedChars);
+    } 
+    else if (_numLeftoverBytes==2)
+    {
+      // grab the two bytes from the leftovers array
+      byte b1, b2;
+      b1 = _leftoverBytes[0];
+      b2 = _leftoverBytes[1];
+      
+      // convert the two bytes into three base64 chars
+      int c1, c2, c3;
+      c1 = (b1>>2)&0x3f;
+      c2 = (b1<<4 | ((b2>>4)&0x0f))&0x3f;
+      c3 = (b2<<2)&0x3f;
+      
+      char[] encodedChars = _fourChars;
+      
+      encodedChars[0] = _encode(c1);
+      encodedChars[1] = _encode(c2);
+      encodedChars[2] = _encode(c3);  
+      //append one padding character
+      encodedChars[3] = '=';
+      
+      _out.write(encodedChars);
+    } 
+    _out.close();
+  }
+  
+  
+  /**
+   * Encodes three bytes in base64 representation and writes the corresponding 
+   * four base64 characters to the output writer.
+   * 
+   * @param b1  the first byte
+   * @param b2  the second byte
+   * @param b3  the third byte
+   */
+  private final void _writeBase64(final byte b1, final byte b2, final byte b3) throws IOException
+  {
+    int c1, c2, c3, c4;
+    char[] encodedChars = _fourChars;
+    
+    c1 = (b1>>2)&0x3f;          // b1.high6
+    c2 = (b1<<4 | ((b2>>4)&0x0f) )&0x3f;  // b1.low2 + b2.high4
+    c3 = (b2<<2 | ((b3>>6)&0x03) )&0x3f;  // b2.low4 + b3.high2
+    c4 = b3&0x3f;               // b3.low6
+    
+    encodedChars[0] = _encode(c1);
+    encodedChars[1] = _encode(c2);
+    encodedChars[2] = _encode(c3);
+    encodedChars[3] = _encode(c4);
+    
+    // write array of chars to writer
+    _out.write(encodedChars);    
+  }
+  
+  /**
+   * Writes lengthToProcess number of bytes from byte array to writer 
+   * in base64 beginning with startIndex. Assumes all leftover bytes from 
+   * previous calls to write have been dealt with.  
+   * 
+   * @param b               the data
+   * @param startIndex      the start offset in the data
+   * @param lengthToProcess the number of bytes to read
+   */
+  private final void _processArray(byte[] b, int startIndex, int lengthToProcess) 
+                                                        throws IOException
+  {
+    int index = startIndex;
+  
+    // loop through remaining length of array
+    while(lengthToProcess>0) {
+      // base case: only one byte
+      if (lengthToProcess==1) 
+      {        
+        // save this byte for next call to write
+        _numLeftoverBytes = 1;
+        _leftoverBytes[0] = b[index];
+        return;
+      }
+      // base case: only two bytes
+      else if (lengthToProcess==2) 
+      {     
+        // save these two bytes for next call to write
+        _numLeftoverBytes = 2;
+        _leftoverBytes[0] = b[index];
+        _leftoverBytes[1] = b[index+1]; 
+        return;
+      } 
+      else 
+      {
+        // encode three bytes (24 bits) from input array
+        _writeBase64(b[index],b[index+1],b[index+2]);
+        
+        // finally, make some progress in loop
+        lengthToProcess -= 3;
+        index +=3;
+       }
+    } //end while
+  }
+  
+  
+
+  /**
+   * Encodes a six-bit pattern into a base-64 character.
+   * 
+   * @param c an integer whose lower 6 bits contain the base64 representation
+   *          all other bits should be zero
+   */
+  private static final char _encode(final int c) 
+  {
+    if (c < 26)
+      return (char)('A' + c);
+    if (c < 52)
+      return (char)('a' + (c-26));
+    if (c < 62)
+      return (char)('0' + (c-52));
+    if (c == 62)
+      return '+';
+    if (c == 63)
+      return '/';
+      
+    throw new AssertionError("Invalid B64 character code:"+c);
+  }
+
+  
+  /** stores leftover bytes from previous call to write method **/
+  private final byte[]      _leftoverBytes;
+  
+  /** indicates the number of bytes that were leftover after the last triplet 
+   * was formed in the last call to write method  **/
+  private int         _numLeftoverBytes;
+  
+  // cached four-character array
+  private final char[]      _fourChars = new char[4];
+  // cached single-byte array
+  private final byte[]      _single = new byte[1];
+
+  // Writer that will receive all completed character output
+  private final Writer      _out;  
+  
+}

Propchange: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/Base64OutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/Base64OutputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

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=680972&r1=680971&r2=680972&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 Wed Jul 30 04:01:23 2008
@@ -19,9 +19,11 @@
 
 package org.apache.james.mime4j.util;
 
+import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 
 /**
  * Utility methods related to codecs.
@@ -82,20 +84,15 @@
         private boolean pendingTab;
         private boolean pendingCR;
         private int nextSoftBreak;
-        private int inputIndex;
         private int outputIndex;
-        private int inputLength;
-        private InputStream in;
         private OutputStream out;
         
         
         public QuotedPrintableEncoder(int bufferSize, boolean binary) {
             inBuffer = new byte[bufferSize];
             outBuffer = new byte[3*bufferSize];
-            inputLength = 0;
             outputIndex = 0;
             nextSoftBreak = QUOTED_PRINTABLE_MAX_LINE_LENGTH + 1;
-            in = null;
             out = null;
             this.binary = binary;
             pendingSpace = false;
@@ -103,29 +100,32 @@
             pendingCR = false;
         }
         
-        public void encode(final InputStream in, final OutputStream out) throws IOException {
-            this.in = in;
+        void initEncoding(final OutputStream out) {
             this.out = out;
             pendingSpace = false;
             pendingTab = false;
             pendingCR = false;
             nextSoftBreak = QUOTED_PRINTABLE_MAX_LINE_LENGTH + 1;
-            read();
-            while(inputLength > -1) {
-                while (inputIndex < inputLength) { 
-                    final byte next = inBuffer[inputIndex];
-                    encode(next);
-                    inputIndex++;
-                }
-                read();
+        }
+        
+        void encodeChunk(byte[] buffer, int off, int len) throws IOException {
+            for (int inputIndex = off; inputIndex < len + off; inputIndex++) {
+                encode(buffer[inputIndex]);
             }
+        }
+        
+        void completeEncoding() throws IOException {
             writePending();
             flushOutput();
         }
-
-        private void read() throws IOException {
-            inputLength = in.read(inBuffer);
-            inputIndex = 0;
+        
+        public void encode(final InputStream in, final OutputStream out) throws IOException {
+            initEncoding(out);
+            int inputLength;
+            while((inputLength = in.read(inBuffer)) > -1) {
+                encodeChunk(inBuffer, 0, inputLength);
+            }
+            completeEncoding();
         }
         
         private void writePending() throws IOException {
@@ -210,7 +210,7 @@
                 softBreak();
             }
             
-            int nextUnsigned = (int) next & 0xff;
+            int nextUnsigned = next & 0xff;
             
             write(EQUALS);
             --nextSoftBreak;
@@ -247,6 +247,36 @@
         }
     }
     
+    static class QuotedPrintableOutputStream extends FilterOutputStream {
+        
+        QuotedPrintableEncoder encoder;
+
+        public QuotedPrintableOutputStream(OutputStream arg0, boolean binary) {
+            super(arg0);
+            encoder = new QuotedPrintableEncoder(DEFAULT_ENCODING_BUFFER_SIZE, binary);
+            encoder.initEncoding(out);
+        }
+
+        public void close() throws IOException {
+            encoder.completeEncoding();
+            // do not close the wrapped stream
+        }
+
+        public void flush() throws IOException {
+            encoder.flushOutput();
+        }
+
+        public void write(byte[] b, int off, int len) throws IOException {
+            encoder.encodeChunk(b, off, len);
+        }
+
+        public void write(int b) throws IOException {
+            if (true) throw new UnsupportedOperationException("QuotedPrintableOutputStream filter does not support byte per byte writes");
+            encoder.encodeChunk(new byte[] { (byte) b }, 0, 1);
+        }
+        
+    }
+    
     /**
      * Encodes the given stream using Quoted-Printable.
      * This assumes that stream is text and therefore does not escape
@@ -260,15 +290,37 @@
         encoder.encode(in, out);
     }
     
+    public static void encodeBase64(final InputStream in, final OutputStream out) throws IOException {
+        final Base64Encoder encoder = new Base64Encoder(DEFAULT_ENCODING_BUFFER_SIZE);
+        encoder.encode(in, out);
+    }
+    
+    
     /**
-     * Encodes the given stream using Base64.
-     * @param in not null
-     * @param out not null 
+     * Wraps the given stream in a Quoted-Printable encoder.
+     * @param out not null
+     * @return encoding outputstream 
      * @throws IOException
      */
-    public static void encodeBase64(final InputStream in, final OutputStream out) throws IOException {
-        Base64Encoder encoder = new Base64Encoder(DEFAULT_ENCODING_BUFFER_SIZE);
-        encoder.encode(in, out);
+    public static OutputStream wrapQuotedPrintable(final OutputStream out, boolean binary) throws IOException {
+        return new QuotedPrintableOutputStream(out, binary);
+    }
+    
+    /**
+     * Wraps the given stream in a Base64 encoder.
+     * @param out not null
+     * @return encoding outputstream 
+     * @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();
+            }
+            
+        });
     }
     
     private static final class Base64Encoder {
@@ -294,23 +346,23 @@
         }
         
         public void encode(final InputStream inStream, final OutputStream outStream) throws IOException {
-            int inputLength = inStream.read(in);
-            while (inputLength > -1) {
-                int outputLength = encodeInputBuffer(inputLength);
+            int inputLength;
+            while ((inputLength = inStream.read(in)) > -1) {
+                int outputLength = encodeInputBuffer(in, 0, inputLength);
                 if (outputLength > 0) {
                     outStream.write(out, 0, outputLength);
                 }
-                inputLength = inStream.read(in);
             }
         }
         
-        private int encodeInputBuffer(final int inputLength) {
+        private int encodeInputBuffer(byte[] in, final int pos, final int inputLength) {
             if (inputLength == 0) {
                 return 0;
             }
-            int inputIndex = 0;
+            int inputEnd = pos + inputLength;
+            int inputIndex = pos;
             int outputIndex = 0;
-            while (inputLength - inputIndex > 2) {
+            while (inputEnd - inputIndex > 2) {
                 int one = (toInt(in[inputIndex++]) << 16);
                 int two = (toInt(in[inputIndex++]) << 8);
                 int three = toInt(in[inputIndex++]);
@@ -325,7 +377,7 @@
                 outputIndex = setResult(out, outputIndex, ENCODING[index]);
             }
             
-            switch (inputLength - inputIndex) {
+            switch (inputEnd - inputIndex) {
                 case 1:
                     int quantum = in[inputIndex++] << 16;
                     int index = (quantum & FIRST_MASK) >> 18;
@@ -369,4 +421,5 @@
             return outputIndex;
         }
     }
+
 }

Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/LineBreakingOutputStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/LineBreakingOutputStream.java?rev=680972&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/LineBreakingOutputStream.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/LineBreakingOutputStream.java Wed Jul 30 04:01:23 2008
@@ -0,0 +1,70 @@
+/****************************************************************
+ * 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.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class take care of inserting a CRLF every lineLength bytes
+ * Default to 76 bytes lines.
+ * Please note that this does not check for existing newlines and
+ * simply adds CRLF every 76 bytes. 
+ */
+public class LineBreakingOutputStream extends FilterOutputStream {
+
+    private int linepos = 0;
+    private int lineLength = 76;
+    
+    public LineBreakingOutputStream(OutputStream out, int lineLength) {
+        super(out);
+        this.lineLength  = lineLength;
+    }
+
+    public final void write(final byte[] b, int off, int len) throws IOException {
+        while (len > 0) {
+            if (len + linepos > lineLength) {
+                int count = lineLength - linepos;
+                if (count > 0) {
+                    out.write(b, off, count);
+                    off += count;
+                    len -= count;
+                }
+                out.write(CodecUtil.CRLF);
+                linepos = 0;
+            } else {
+                out.write(b, off, len);
+                linepos += len;
+                len = 0;
+            }
+        }
+    }
+
+    public final void write(final int b) throws IOException {
+        if (linepos >= lineLength) {
+            out.write(CodecUtil.CRLF);
+            linepos = 0;
+        }
+        out.write(b);
+        linepos++;
+    }
+
+}

Propchange: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/LineBreakingOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/LineBreakingOutputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/Base64OutputStreamTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/Base64OutputStreamTest.java?rev=680972&view=auto
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/Base64OutputStreamTest.java (added)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/Base64OutputStreamTest.java Wed Jul 30 04:01:23 2008
@@ -0,0 +1,239 @@
+/****************************************************************
+ * 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.IOException;
+import java.io.StringWriter;
+import java.io.BufferedWriter;
+import java.util.zip.GZIPOutputStream;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * This class has been copied from Apache MyFaces Trinidad 1.2.8
+ *
+ * Unit tests for Base64OutputStream.
+ */
+public class Base64OutputStreamTest extends TestCase 
+{   
+ /**
+   * Creates a new Base64OutputStreamTest.
+   * 
+   * @param testName  the unit test name
+   */
+  public Base64OutputStreamTest(String testName)
+  {
+    super(testName);
+  }
+  
+  protected void setUp() throws Exception
+  {
+    super.setUp();
+  }
+  
+  protected void tearDown() throws Exception
+  {
+    super.tearDown();
+  }
+  
+  public static Test suite()
+  {
+    return new TestSuite(Base64OutputStreamTest.class);
+  }
+  
+  /**
+   * Tests decoding of stream that contains no trailing padding characters.
+   */ 	
+  public void testNoPaddingChar() throws IOException
+  {
+	 	
+    String	str = "abcdefghijklmnopqrstuvwxBase64 Encoding is a popular way to convert the 8bit and the binary to the 7bit for network trans using Socket, and a security method to handle text or file, often used in Authentical Login and Mail Attachment, also stored in text file or database. Most SMTP server will handle the login UserName and Password in this way. 1";
+    String str_encoded = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4QmFzZTY0IEVuY29kaW5nIGlzIGEgcG9wdWxhciB3YXkgdG8gY29udmVydCB0aGUgOGJpdCBhbmQgdGhlIGJpbmFyeSB0byB0aGUgN2JpdCBmb3IgbmV0d29yayB0cmFucyB1c2luZyBTb2NrZXQsIGFuZCBhIHNlY3VyaXR5IG1ldGhvZCB0byBoYW5kbGUgdGV4dCBvciBmaWxlLCBvZnRlbiB1c2VkIGluIEF1dGhlbnRpY2FsIExvZ2luIGFuZCBNYWlsIEF0dGFjaG1lbnQsIGFsc28gc3RvcmVkIGluIHRleHQgZmlsZSBvciBkYXRhYmFzZS4gTW9zdCBTTVRQIHNlcnZlciB3aWxsIGhhbmRsZSB0aGUgbG9naW4gVXNlck5hbWUgYW5kIFBhc3N3b3JkIGluIHRoaXMgd2F5LiAx";
+   
+    _testWriteChar(str, str_encoded);
+    
+    _testWriteArray(str, str_encoded);    	
+  }
+ 	
+  /**
+   * Tests decoding of stream that contains exactly one trailing padding character.
+   */ 	
+  public void testOnePaddingChar() throws IOException
+  {
+    String str = "Base64 Encoding is a popular way to convert the 8bit and the binary to the 7bit for network trans using Socket, and a security method to handle text or file, often used in Authentical Login and Mail Attachment, also stored in text file or database. Most SMTP server will handle the login UserName and Password in this way.9";
+    String str_encoded = "QmFzZTY0IEVuY29kaW5nIGlzIGEgcG9wdWxhciB3YXkgdG8gY29udmVydCB0aGUgOGJpdCBhbmQgdGhlIGJpbmFyeSB0byB0aGUgN2JpdCBmb3IgbmV0d29yayB0cmFucyB1c2luZyBTb2NrZXQsIGFuZCBhIHNlY3VyaXR5IG1ldGhvZCB0byBoYW5kbGUgdGV4dCBvciBmaWxlLCBvZnRlbiB1c2VkIGluIEF1dGhlbnRpY2FsIExvZ2luIGFuZCBNYWlsIEF0dGFjaG1lbnQsIGFsc28gc3RvcmVkIGluIHRleHQgZmlsZSBvciBkYXRhYmFzZS4gTW9zdCBTTVRQIHNlcnZlciB3aWxsIGhhbmRsZSB0aGUgbG9naW4gVXNlck5hbWUgYW5kIFBhc3N3b3JkIGluIHRoaXMgd2F5Ljk="; 
+    
+    _testWriteChar(str, str_encoded);
+    
+    _testWriteArray(str, str_encoded);    	
+    
+  }
+ 	
+  /**
+   * Tests decoding of stream that contains exactly two trailing padding characters.
+   */ 	 	
+  public void testTwoPaddingChars() throws IOException
+  {
+    String str = "Base64 Encoding is a popular way to convert the 8bit and the binary to the 7bit for network trans using Socket, and a security method to handle text or file, often used in Authentical Login and Mail Attachment, also stored in text file or database. Most SMTP server will handle the login UserName and Password in this way.";
+    String str_encoded = "QmFzZTY0IEVuY29kaW5nIGlzIGEgcG9wdWxhciB3YXkgdG8gY29udmVydCB0aGUgOGJpdCBhbmQgdGhlIGJpbmFyeSB0byB0aGUgN2JpdCBmb3IgbmV0d29yayB0cmFucyB1c2luZyBTb2NrZXQsIGFuZCBhIHNlY3VyaXR5IG1ldGhvZCB0byBoYW5kbGUgdGV4dCBvciBmaWxlLCBvZnRlbiB1c2VkIGluIEF1dGhlbnRpY2FsIExvZ2luIGFuZCBNYWlsIEF0dGFjaG1lbnQsIGFsc28gc3RvcmVkIGluIHRleHQgZmlsZSBvciBkYXRhYmFzZS4gTW9zdCBTTVRQIHNlcnZlciB3aWxsIGhhbmRsZSB0aGUgbG9naW4gVXNlck5hbWUgYW5kIFBhc3N3b3JkIGluIHRoaXMgd2F5Lg==";
+    
+    _testWriteChar(str, str_encoded);
+    
+    _testWriteArray(str, str_encoded);    	
+    
+  }	
+  
+  /**
+   * Tests decoding of stream by writing out to stream in different intervals to test whether
+   * whehther leftover data is persisted across calls to write
+   */ 	 	
+  public void testMultipleWrites() throws IOException
+  {
+    String str = "Base64 Encoding is a popular way to convert the 8bit and the binary to the 7bit for network trans using Socket, and a security method to handle text or file, often used in Authentical Login and Mail Attachment, also stored in text file or database. Most SMTP server will handle the login UserName and Password in this way.";
+    String str_encoded = "QmFzZTY0IEVuY29kaW5nIGlzIGEgcG9wdWxhciB3YXkgdG8gY29udmVydCB0aGUgOGJpdCBhbmQgdGhlIGJpbmFyeSB0byB0aGUgN2JpdCBmb3IgbmV0d29yayB0cmFucyB1c2luZyBTb2NrZXQsIGFuZCBhIHNlY3VyaXR5IG1ldGhvZCB0byBoYW5kbGUgdGV4dCBvciBmaWxlLCBvZnRlbiB1c2VkIGluIEF1dGhlbnRpY2FsIExvZ2luIGFuZCBNYWlsIEF0dGFjaG1lbnQsIGFsc28gc3RvcmVkIGluIHRleHQgZmlsZSBvciBkYXRhYmFzZS4gTW9zdCBTTVRQIHNlcnZlciB3aWxsIGhhbmRsZSB0aGUgbG9naW4gVXNlck5hbWUgYW5kIFBhc3N3b3JkIGluIHRoaXMgd2F5Lg==";
+    
+    // create a Base64OutputStream 
+    StringWriter strwriter = new StringWriter(); 
+    BufferedWriter buffwriter = new BufferedWriter(strwriter);
+    Base64OutputStream b64_out = new Base64OutputStream(buffwriter);
+    
+    byte[] b = str.getBytes();
+    
+    // write the bytes in different lengths to test whether leftover
+    // data is persisted across calls to write
+    b64_out.write(b, 0, 33);
+    b64_out.write(b, 33, 1);
+    b64_out.write(b, 34, 1);
+    b64_out.write(b, 35, 2);
+    b64_out.write(b, 37, 3);
+    b64_out.write(b, 40, 3);
+    b64_out.write(b, 43, 3);
+    b64_out.write(b, 46, 5);
+    b64_out.write(b, 51, 4);
+    b64_out.write(b, 55, 5);
+    b64_out.write(b, 60, 6);
+    b64_out.write(b, 66, 10);
+    b64_out.write(b, 76, 51);
+    b64_out.write(b, 127, 150);
+    b64_out.write(b, 277, 22);
+    b64_out.write(b, 299, 21);
+    b64_out.write(b, 320, 2);
+    
+    // remember to add padding characters (if necessary)
+    b64_out.close();
+    
+    // compare the contents of the outputstream with the expected encoded string
+    assertEquals(strwriter.toString(), str_encoded);	
+  }	
+  
+  
+  /**
+   * Testing writing binary data whose byte values range from -128 to 128.
+   * We create such data by GZIP-ing the string before writing out to the Base64OutputStream.
+   * 
+   */ 	 	
+  public void testWriteBinaryData() throws IOException
+  {
+    String str = "Base64 Encoding is a popular way to convert the 8bit and the binary to the 7bit for network trans using Socket, and a security method to handle text or file, often used in Authentical Login and Mail Attachment, also stored in text file or database. Most SMTP server will handle the login UserName and Password in this way.";
+    String str_encoded = "H4sIAAAAAAAAAD2QQW7DMAwEv7IPCHIK2l4ToLe6CJD2AbREx0JkMpDouvl9KQXokVxylssTVX454F2CxiRXpArCXe9rpoKNHjBFUPnhYrCZ8TYmA0nsxZiESh9p1WuTJi0Qtk3LDVZIKtbauBcNN7ZdXyVUDmtJ9sDCNmtshNmVzDD+NThjSpl30MlYnMARSXBc3UYsBcr40Kt3Gm2glHE0ozAvrrpFropqWp5bndhwDRvJaPTIewxaDZfh6+zHFI+HLeX8f4XHyd3h29VPWrhbnalWT/bEzv4qf9D+DzA2UNlCAQAA";
+    
+    byte[] bytes = str.getBytes();
+    
+    StringWriter strwriter = new StringWriter(); 
+    BufferedWriter buffwriter = new BufferedWriter(strwriter);
+    Base64OutputStream b64_out = new Base64OutputStream(buffwriter);
+    
+    GZIPOutputStream zip = new GZIPOutputStream(b64_out);
+    
+    zip.write(bytes, 0, bytes.length);
+    zip.finish();
+    buffwriter.flush();
+    b64_out.close();
+    
+    assertEquals(str_encoded, strwriter.toString());
+    
+  }
+  
+  
+  ////////// private	methods ////////////
+  
+  /**
+   *
+   *	Writes each individual char from str to a Base64OutputStream and compares
+   *	the resulting output stream contents with the expected encoded string.  
+   *
+   *	@param	str			the decoded string
+   *	@param	str_encoded	the encoded string
+   *
+   **/ 	
+  
+  private void _testWriteChar(String str, String str_encoded) throws IOException
+  {
+    // create a Base64OutputStream 
+    StringWriter strwriter = new StringWriter(); 
+    BufferedWriter buffwriter = new BufferedWriter(strwriter);
+    Base64OutputStream b64_out = new Base64OutputStream(buffwriter);
+    
+    // write out each char in str to the stream
+    for (int i = 0; i<str.length(); i++) 
+    {
+      b64_out.write(str.charAt(i));
+    }
+    // remember to add padding characters (if necessary)
+    b64_out.close();
+        
+    // compare the contents of the outputstream with the expected encoded string
+    assertEquals(strwriter.toString(), str_encoded);	
+  }
+  
+  /**
+   *
+   *	Writes str (after converting to an array of bytes) to a Base64OutputStream 
+   *	and compares the resulting output stream contents with the expected encoded 
+   *	string.  
+   *
+   *	@param	str			the decoded string
+   *	@param	str_encoded	the encoded string
+   *
+   **/ 	
+  private void _testWriteArray(String str, String str_encoded) throws IOException
+  {	
+    // create a Base64OutputStream 
+    StringWriter strwriter = new StringWriter(); 
+    BufferedWriter buffwriter = new BufferedWriter(strwriter);
+    Base64OutputStream b64_out = new Base64OutputStream(buffwriter);
+    
+    // convert str into an array of bytes
+    byte[] b = str.getBytes();
+    
+    // write out the array to the output stream
+    b64_out.write(b, 0, b.length);
+    // append padding chars if necessary
+    b64_out.close();
+    
+    // 		System.out.println("testwriteArray,  expected encoding:" + str_encoded);    
+    //       	System.out.println("testwriteArray, output of encoding:" + strwriter.toString());
+    
+    // compare the contents of the outputstream with the expected encoded string
+    assertEquals(strwriter.toString(), str_encoded);
+  }
+} // end Base64OutputStreamTest class

Propchange: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/Base64OutputStreamTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/Base64OutputStreamTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/CodecUtilTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/CodecUtilTest.java?rev=680972&r1=680971&r2=680972&view=diff
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/CodecUtilTest.java (original)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/CodecUtilTest.java Wed Jul 30 04:01:23 2008
@@ -19,11 +19,14 @@
 
 package org.apache.james.mime4j.util;
 
+import org.apache.james.mime4j.ExampleMail;
+import org.apache.james.mime4j.decoder.Base64InputStream;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
-
-import org.apache.james.mime4j.ExampleMail;
+import java.io.OutputStreamWriter;
 
 import junit.framework.TestCase;
 
@@ -68,6 +71,90 @@
         assertEquals("7bit=20content=20with=20euro=20=A4=20symbol", actual);
     }
     
+    public void testBase64OutputStream() throws Exception {
+        StringBuffer sb = new StringBuffer(2048);
+        for (int i = 0; i < 128; i++) {
+            sb.append("0123456789ABCDEF");
+        }
+        String input = sb.toString();
+        String output = roundtripUsingOutputStream(input);
+        assertEquals(input, output);
+    }
+
+    private String roundtripUsingOutputStream(String input) throws IOException {
+        ByteArrayOutputStream out2 = new ByteArrayOutputStream();
+        Base64OutputStream outb64 = new Base64OutputStream(new OutputStreamWriter(new LineBreakingOutputStream(out2, 76)));
+        CodecUtil.copy(new ByteArrayInputStream(input.getBytes()), outb64);
+        outb64.flush();
+        outb64.close();
+        
+        InputStream is = new Base64InputStream(new ByteArrayInputStream(out2.toByteArray()));
+        ByteArrayOutputStream outRoundtrip = new ByteArrayOutputStream();
+        CodecUtil.copy(is, outRoundtrip);
+        String output = new String(outRoundtrip.toByteArray());
+        return output;
+    }
+    
+    
+    /**
+     * This test is a proof for MIME4J-67
+     */
+    /* Currently commented because we don't want failing tests.
+    public void testBase64Encoder() throws Exception {
+        StringBuffer sb = new StringBuffer(2048);
+        for (int i = 0; i < 128; i++) {
+            sb.append("0123456789ABCDEF");
+        }
+        String input = sb.toString();
+        String output = roundtripUsingEncoder(input);
+        assertEquals(input, output);
+    }
+
+    private String roundtripUsingEncoder(String input) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        CodecUtil.encodeBase64(new ByteArrayInputStream(input.getBytes()), out);
+        
+        InputStream is = new Base64InputStream(new ByteArrayInputStream(out.toByteArray()));
+        ByteArrayOutputStream outRoundtrip = new ByteArrayOutputStream();
+        CodecUtil.copy(is, outRoundtrip);
+        String output = new String(outRoundtrip.toByteArray());
+        return output;
+    } 
+    */
+    
+    /* performance test, not a unit test
+    public void testPerformance() throws Exception {
+        if (true) return;
+        byte[] bytes = new byte[10000];
+        Random r = new Random(432875623874L);
+        r.nextBytes(bytes);
+        long totalEncoder1 = 0;
+        long totalStream1 = 0;
+        long totalEncoder2 = 0;
+        for (int i = 0; i < 10000; i++) {
+            int length = r.nextInt(1000);
+            int pos = r.nextInt(9000);
+            String input = new String(bytes, pos, length);
+            long time1 = System.currentTimeMillis();
+            roundtripUsingEncoder(input);
+            long time2 = System.currentTimeMillis();
+            roundtripUsingOutputStream(input);
+            long time3 = System.currentTimeMillis();
+            roundtripUsingEncoder(input);
+            long time4 = System.currentTimeMillis();
+            roundtripUsingOutputStream(input);
+            
+            totalEncoder1 += time2-time1;
+            totalStream1 += time3-time2;
+            totalEncoder2 += time4-time3;
+        }
+        
+        System.out.println("Encoder 1st: "+totalEncoder1);
+        System.out.println("Encoder 2st: "+totalEncoder2);
+        System.out.println("Stream 1st: "+totalStream1);
+    }
+    */
+    
     private void assertEquals(byte[] expected, byte[] actual) {
         StringBuffer buffer = new StringBuffer(expected.length);
         assertEquals(expected.length, actual.length);

Added: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/LineBreakingOutputStreamTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/LineBreakingOutputStreamTest.java?rev=680972&view=auto
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/LineBreakingOutputStreamTest.java (added)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/LineBreakingOutputStreamTest.java Wed Jul 30 04:01:23 2008
@@ -0,0 +1,34 @@
+/****************************************************************
+ * 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.IOException;
+import java.io.OutputStream;
+
+import junit.framework.TestCase;
+
+public class LineBreakingOutputStreamTest extends TestCase {
+    
+    public void testLongLine() throws IOException {
+        OutputStream os = new LineBreakingOutputStream(System.out, 4);
+        os.write("gsdfklgsjdhflkgjhsdfklgjhsdlkfjghksdljfhgslkdjfhgklsjdhf".getBytes());
+    }
+
+}

Propchange: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/LineBreakingOutputStreamTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/util/LineBreakingOutputStreamTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org