You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ri...@apache.org on 2006/07/14 12:02:29 UTC

svn commit: r421852 [10/15] - in /geronimo/specs/trunk: ./ geronimo-spec-j2ee/ geronimo-spec-javamail-1.3.1/ geronimo-spec-javamail-1.3.1/src/ geronimo-spec-javamail-1.4/ geronimo-spec-javamail-1.4/src/ geronimo-spec-javamail-1.4/src/main/ geronimo-spe...

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/util/SharedFileInputStream.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/util/SharedFileInputStream.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/util/SharedFileInputStream.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/util/SharedFileInputStream.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,581 @@
+/**
+ *
+ * Copyright 2006 The Apache Software Foundation
+ *
+ *  Licensed 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 javax.mail.util;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+
+import javax.mail.internet.SharedInputStream;
+
+public class SharedFileInputStream extends BufferedInputStream implements SharedInputStream {
+
+
+    // This initial size isn't documented, but bufsize is 2048 after initialization for the
+    // Sun implementation.
+    private static final int DEFAULT_BUFFER_SIZE = 2048;
+
+    // the shared file information, used to synchronize opens/closes of the base file.
+    private SharedFileSource source;
+
+    /**
+     * The file offset that is the first byte in the read buffer.
+     */
+    protected long bufpos;
+
+    /**
+     * The normal size of the read buffer.
+     */
+    protected int bufsize;
+
+    /**
+     * The size of the file subset represented by this stream instance.
+     */
+    protected long datalen;
+
+    /**
+     * The source of the file data.  This is shared across multiple
+     * instances.
+     */
+    protected RandomAccessFile in;
+
+    /**
+     * The starting position of data represented by this stream relative
+     * to the start of the file data.  This stream instance represents
+     * data in the range start to (start + datalen - 1).
+     */
+    protected long start;
+
+
+    /**
+     * Construct a SharedFileInputStream from a file name, using the default buffer size.
+     *
+     * @param file   The name of the file.
+     *
+     * @exception IOException
+     */
+    public SharedFileInputStream(String file) throws IOException {
+        this(file, DEFAULT_BUFFER_SIZE);
+    }
+
+
+    /**
+     * Construct a SharedFileInputStream from a File object, using the default buffer size.
+     *
+     * @param file   The name of the file.
+     *
+     * @exception IOException
+     */
+    public SharedFileInputStream(File file) throws IOException {
+        this(file, DEFAULT_BUFFER_SIZE);
+    }
+
+
+    /**
+     * Construct a SharedFileInputStream from a file name, with a given initial buffer size.
+     *
+     * @param file       The name of the file.
+     * @param bufferSize The initial buffer size.
+     *
+     * @exception IOException
+     */
+    public SharedFileInputStream(String file, int bufferSize) throws IOException {
+        // I'm not sure this is correct or not.  The SharedFileInputStream spec requires this
+        // be a subclass of BufferedInputStream.  The BufferedInputStream constructor takes a stream,
+        // which we're not really working from at this point.  Using null seems to work so far.
+        super(null);
+        init(new File(file), bufferSize);
+    }
+
+
+    /**
+     * Construct a SharedFileInputStream from a File object, with a given initial buffer size.
+     *
+     * @param file   The name of the file.
+     * @param bufferSize The initial buffer size.
+     *
+     * @exception IOException
+     */
+    public SharedFileInputStream(File file, int bufferSize) throws IOException {
+        // I'm not sure this is correct or not.  The SharedFileInputStream spec requires this
+        // be a subclass of BufferedInputStream.  The BufferedInputStream constructor takes a stream,
+        // which we're not really working from at this point.  Using null seems to work so far.
+        super(null);
+        init(file, bufferSize);
+    }
+
+
+    /**
+     * Private constructor used to spawn off a shared instance
+     * of this stream.
+     *
+     * @param source  The internal class object that manages the shared resources of
+     *                the stream.
+     * @param start   The starting offset relative to the beginning of the file.
+     * @param len     The length of file data in this shared instance.
+     * @param bufsize The initial buffer size (same as the spawning parent.
+     */
+    private SharedFileInputStream(SharedFileSource source, long start, long len, int bufsize) {
+        super(null);
+        this.source = source;
+        in = source.open();
+        this.start = start;
+        bufpos = start;
+        datalen = len;
+        this.bufsize = bufsize;
+        buf = new byte[bufsize];
+        // other fields such as pos and count initialized by the super class constructor.
+    }
+
+
+    /**
+     * Shared initializtion routine for the constructors.
+     *
+     * @param file       The file we're accessing.
+     * @param bufferSize The initial buffer size to use.
+     *
+     * @exception IOException
+     */
+    private void init(File file, int bufferSize) throws IOException {
+        if (bufferSize <= 0) {
+            throw new IllegalArgumentException("Buffer size must be positive");
+        }
+        // create a random access file for accessing the data, then create an object that's used to share
+        // instances of the same stream.
+        source = new SharedFileSource(file);
+        // we're opening the first one.
+        in = source.open();
+        // this represents the entire file, for now.
+        start = 0;
+        // use the current file length for the bounds
+        datalen = in.length();
+        // now create our buffer version
+        bufsize = bufferSize;
+        bufpos = 0;
+        // NB:  this is using the super class protected variable.
+        buf = new byte[bufferSize];
+    }
+
+
+    /**
+     * Check to see if we need to read more data into our buffer.
+     *
+     * @return False if there's not valid data in the buffer (generally means
+     *         an EOF condition).
+     * @exception IOException
+     */
+    private boolean checkFill() throws IOException {
+        // if we have data in the buffer currently, just return
+        if (pos < count) {
+            return true;
+        }
+
+        // ugh, extending BufferedInputStream also means supporting mark positions.  That complicates everything.
+        // life is so much easier if marks are not used....
+        if (markpos < 0) {
+            // reset back to the buffer position
+            pos = 0;
+            // this will be the new position within the file once we're read some data.
+            bufpos += count;
+        }
+        else {
+
+
+            // we have marks to worry about....damn.
+            // if we have room in the buffer to read more data, then we will.  Otherwise, we need to see
+            // if it's possible to shift the data in the buffer or extend the buffer (up to the mark limit).
+            if (pos >= buf.length) {
+                // the mark position is not at the beginning of the buffer, so just shuffle the bytes, leaving
+                // us room to read more data.
+                if (markpos > 0) {
+                    // this is the size of the data we need to keep.
+                    int validSize = pos - markpos;
+                    // perform the shift operation.
+                    System.arraycopy(buf, markpos, buf, 0, validSize);
+                    // now adjust the positional markers for this shift.
+                    pos = validSize;
+                    bufpos += markpos;
+                    markpos = 0;
+                }
+                // the mark is at the beginning, and we've used up the buffer.  See if we're allowed to
+                // extend this.
+                else if (buf.length < marklimit) {
+                    // try to double this, but throttle to the mark limit
+                    int newSize = Math.min(buf.length * 2, marklimit);
+
+                    byte[] newBuffer = new byte[newSize];
+                    System.arraycopy(buf, 0, newBuffer, 0, buf.length);
+
+                    // replace the old buffer.  Note that all other positional markers remain the same here.
+                    buf = newBuffer;
+                }
+                // we've got further than allowed, so invalidate the mark, and just reset the buffer
+                else {
+                    markpos = -1;
+                    pos = 0;
+                    bufpos += count;
+                }
+            }
+        }
+
+        // if we're past our designated end, force an eof.
+        if (bufpos + pos >= start + datalen) {
+            return false;
+        }
+
+        // seek to the read location start.  Note this is a shared file, so this assumes all of the methods
+        // doing buffer fills will be synchronized.
+        int fillLength = buf.length - pos;
+
+        // we might be working with a subset of the file data, so normal eof processing might not apply.
+        // we need to limit how much we read to the data length.
+        if (bufpos - start + pos + fillLength > datalen) {
+            fillLength = (int)(datalen - (bufpos - start + pos));
+        }
+
+        // finally, try to read more data into the buffer.
+        fillLength = source.read(bufpos + pos, buf, pos, fillLength);
+
+        // we weren't able to read anything, count this as an eof failure.
+        if (fillLength <= 0) {
+            return false;
+        }
+
+        // set the new buffer count
+        count = fillLength + pos;
+
+        // we have data in the buffer.
+        return true;
+    }
+
+
+    /**
+     * Return the number of bytes available for reading without
+     * blocking for a long period.
+     *
+     * @return For this stream, this is the number of bytes between the
+     *         current read position and the indicated end of the file.
+     * @exception IOException
+     */
+    public synchronized int available() throws IOException {
+        checkOpen();
+
+        // this is backed by a file, which doesn't really block.  We can return all the way to the
+        // marked data end, if necessary
+        long endMarker = start + datalen;
+        return (int)(endMarker - (bufpos + pos));
+    }
+
+
+    /**
+     * Return the current read position of the stream.
+     *
+     * @return The current position relative to the beginning of the stream.
+     *         This is not the position relative to the start of the file, since
+     *         the stream starting position may be other than the beginning.
+     */
+    public long getPosition() {
+        checkOpenRuntime();
+
+        return bufpos + pos - start;
+    }
+
+
+    /**
+     * Mark the current position for retracing.
+     *
+     * @param readlimit The limit for the distance the read position can move from
+     *                  the mark position before the mark is reset.
+     */
+    public synchronized void mark(int readlimit) {
+        checkOpenRuntime();
+        marklimit = readlimit;
+        markpos = pos;
+    }
+
+
+    /**
+     * Read a single byte of data from the input stream.
+     *
+     * @return The read byte.  Returns -1 if an eof condition has been hit.
+     * @exception IOException
+     */
+    public synchronized int read() throws IOException {
+        checkOpen();
+
+        // check to see if we can fill more data
+        if (!checkFill()) {
+            return -1;
+        }
+
+        // return the current byte...anded to prevent sign extension.
+        return buf[pos++] & 0xff;
+    }
+
+
+    /**
+     * Read multiple bytes of data and place them directly into
+     * a byte-array buffer.
+     *
+     * @param buffer The target buffer.
+     * @param offset The offset within the buffer to place the data.
+     * @param length The length to attempt to read.
+     *
+     * @return The number of bytes actually read.  Returns -1 for an EOF
+     *         condition.
+     * @exception IOException
+     */
+    public synchronized int read(byte buffer[], int offset, int length) throws IOException {
+        checkOpen();
+
+        // asked to read nothing?  That's what we'll do.
+        if (length == 0) {
+            return 0;
+        }
+
+
+        int returnCount = 0;
+        while (length > 0) {
+            // check to see if we can/must fill more data
+            if (!checkFill()) {
+                // we've hit the end, but if we've read data, then return that.
+                if (returnCount > 0) {
+                    return returnCount;
+                }
+                // trun eof.
+                return -1;
+            }
+
+            int available = count - pos;
+            int given = Math.min(available, length);
+
+            System.arraycopy(buf, pos, buffer, offset, given);
+
+            // now adjust all of our positions and counters
+            pos += given;
+            length -= given;
+            returnCount += given;
+            offset += given;
+        }
+        // return the accumulated count.
+        return returnCount;
+    }
+
+
+    /**
+     * Skip the read pointer ahead a given number of bytes.
+     *
+     * @param n      The number of bytes to skip.
+     *
+     * @return The number of bytes actually skipped.
+     * @exception IOException
+     */
+    public synchronized long skip(long n) throws IOException {
+        checkOpen();
+
+        // nothing to skip, so don't skip
+        if (n <= 0) {
+            return 0;
+        }
+
+        // see if we need to fill more data, and potentially shift the mark positions
+        if (!checkFill()) {
+            return 0;
+        }
+
+        long available = count - pos;
+
+        // the skipped contract allows skipping within the current buffer bounds, so cap it there.
+        long skipped = available < n ? available : n;
+        pos += skipped;
+        return skipped;
+    }
+
+    /**
+     * Reset the mark position.
+     *
+     * @exception IOException
+     */
+    public synchronized void reset() throws IOException {
+        checkOpen();
+        if (markpos < 0) {
+            throw new IOException("Resetting to invalid mark position");
+        }
+        // if we have a markpos, it will still be in the buffer bounds.
+        pos = markpos;
+    }
+
+
+    /**
+     * Indicates the mark() operation is supported.
+     *
+     * @return Always returns true.
+     */
+    public boolean markSupported() {
+        return true;
+    }
+
+
+    /**
+     * Close the stream.  This does not close the source file until
+     * the last shared instance is closed.
+     *
+     * @exception IOException
+     */
+    public void close() throws IOException {
+        // already closed?  This is not an error
+        if (in == null) {
+            return;
+        }
+
+        try {
+            // perform a close on the source version.
+            source.close();
+        } finally {
+            in = null;
+        }
+    }
+
+
+    /**
+     * Create a new stream from this stream, using the given
+     * start offset and length.
+     *
+     * @param offset The offset relative to the start of this stream instance.
+     * @param end    The end offset of the substream.  If -1, the end of the parent stream is used.
+     *
+     * @return A new SharedFileInputStream object sharing the same source
+     *         input file.
+     */
+    public InputStream newStream(long offset, long end) {
+        checkOpenRuntime();
+
+        if (offset < 0) {
+            throw new IllegalArgumentException("Start position is less than 0");
+        }
+        // the default end position is the datalen of the one we're spawning from.
+        if (end == -1) {
+            end = datalen;
+        }
+
+        // create a new one using the private constructor
+        return new SharedFileInputStream(source, start + (int)offset, (int)(end - offset), bufsize);
+    }
+
+
+    /**
+     * Check if the file is open and throw an IOException if not.
+     *
+     * @exception IOException
+     */
+    private void checkOpen() throws IOException {
+        if (in == null) {
+            throw new IOException("Stream has been closed");
+        }
+    }
+
+
+    /**
+     * Check if the file is open and throw an IOException if not.  This version is
+     * used because several API methods are not defined as throwing IOException, so
+     * checkOpen() can't be used.  The Sun implementation just throws RuntimeExceptions
+     * in those methods, hence 2 versions.
+     *
+     * @exception RuntimeException
+     */
+    private void checkOpenRuntime() {
+        if (in == null) {
+            throw new RuntimeException("Stream has been closed");
+        }
+    }
+
+
+    /**
+     * Internal class used to manage resources shared between the
+     * ShareFileInputStream instances.
+     */
+    class SharedFileSource {
+        // the file source
+        public RandomAccessFile source;
+        // the shared instance count for this file (open instances)
+        public int instanceCount = 0;
+
+        public SharedFileSource(File file) throws IOException {
+            source = new RandomAccessFile(file, "r");
+        }
+
+        /**
+         * Open the shared stream to keep track of open instances.
+         */
+        public synchronized RandomAccessFile open() {
+            instanceCount++;
+            return source;
+        }
+
+        /**
+         * Process a close request for this stream.  If there are multiple
+         * instances using this underlying stream, the stream will not
+         * be closed.
+         *
+         * @exception IOException
+         */
+        public synchronized void close() throws IOException {
+            if (instanceCount > 0) {
+                instanceCount--;
+                // if the last open instance, close the real source file.
+                if (instanceCount == 0) {
+                    source.close();
+                }
+            }
+        }
+
+        /**
+         * Read a buffer of data from the shared file.
+         *
+         * @param position The position to read from.
+         * @param buf      The target buffer for storing the read data.
+         * @param offset   The starting offset within the buffer.
+         * @param length   The length to attempt to read.
+         *
+         * @return The number of bytes actually read.
+         * @exception IOException
+         */
+        public synchronized int read(long position, byte[] buf, int offset, int length) throws IOException {
+            // seek to the read location start.  Note this is a shared file, so this assumes all of the methods
+            // doing buffer fills will be synchronized.
+            source.seek(position);
+            return source.read(buf, offset, length);
+        }
+
+
+        /**
+         * Ensure the stream is closed when this shared object is finalized.
+         *
+         * @exception Throwable
+         */
+        protected void finalize() throws Throwable {
+            super.finalize();
+            if (instanceCount > 0) {
+                source.close();
+            }
+        }
+    }
+}
+

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/util/SharedFileInputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/util/SharedFileInputStream.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/javax/mail/util/SharedFileInputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/HtmlHandler.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/HtmlHandler.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/HtmlHandler.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/HtmlHandler.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * Licensed 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.geronimo.mail.handlers;
+
+import javax.activation.ActivationDataFlavor;
+
+public class HtmlHandler extends TextHandler {
+    public HtmlHandler() {
+        super(new ActivationDataFlavor(java.lang.String.class, "text/html", "HTML String"));
+    }
+}
+

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/HtmlHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/HtmlHandler.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/HtmlHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MessageHandler.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MessageHandler.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MessageHandler.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MessageHandler.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * Licensed 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.geronimo.mail.handlers;
+
+import javax.activation.ActivationDataFlavor;
+import javax.activation.DataContentHandler;
+import javax.activation.DataSource;
+import javax.mail.internet.ContentType;
+import javax.mail.Message;
+import javax.mail.MessageAware;
+import javax.mail.MessageContext;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeUtility;
+import javax.mail.internet.ParseException;
+import java.awt.datatransfer.DataFlavor;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+
+public class MessageHandler implements DataContentHandler {
+    /**
+     * Field dataFlavor
+     */
+    ActivationDataFlavor dataFlavor;
+
+    public MessageHandler(){
+        dataFlavor = new ActivationDataFlavor(java.lang.String.class, "message/rfc822", "Text");
+    }
+
+
+    /**
+     * Method getDF
+     *
+     * @return dataflavor
+     */
+    protected ActivationDataFlavor getDF() {
+        return dataFlavor;
+    }
+
+    /**
+     * Method getTransferDataFlavors
+     *
+     * @return dataflavors
+     */
+    public DataFlavor[] getTransferDataFlavors() {
+        return (new DataFlavor[]{dataFlavor});
+    }
+
+    /**
+     * Method getTransferData
+     *
+     * @param dataflavor
+     * @param datasource
+     * @return
+     * @throws IOException
+     */
+    public Object getTransferData(DataFlavor dataflavor, DataSource datasource)
+            throws IOException {
+        if (getDF().equals(dataflavor)) {
+            return getContent(datasource);
+        }
+        return null;
+    }
+
+    /**
+     * Method getContent
+     *
+     * @param datasource
+     * @return
+     * @throws IOException
+     */
+    public Object getContent(DataSource datasource) throws IOException {
+
+        try {
+            // if this is a proper message, it implements the MessageAware interface.  We need this to
+            // get the associated session.
+            if (datasource instanceof MessageAware) {
+                MessageContext context = ((MessageAware)datasource).getMessageContext();
+                // construct a mime message instance from the stream, associating it with the
+                // data source session.
+                return new MimeMessage(context.getSession(), datasource.getInputStream());
+            }
+        } catch (MessagingException e) {
+            // we need to transform any exceptions into an IOException.
+            throw new IOException("Exception writing MimeMultipart: " + e.toString());
+        }
+        return null;
+    }
+
+    /**
+     * Method writeTo
+     *
+     * @param object
+     * @param s
+     * @param outputstream
+     * @throws IOException
+     */
+    public void writeTo(Object object, String s, OutputStream outputstream) throws IOException {
+        // proper message type?
+        if (object instanceof Message) {
+            try {
+                ((Message)object).writeTo(outputstream);
+            } catch (MessagingException e) {
+                throw new IOException("Error parsing message: " + e.toString());
+            }
+        }
+    }
+}
+

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MessageHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MessageHandler.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MessageHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MultipartHandler.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MultipartHandler.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MultipartHandler.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MultipartHandler.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * Licensed 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.geronimo.mail.handlers;
+
+import javax.activation.ActivationDataFlavor;
+import javax.activation.DataContentHandler;
+import javax.activation.DataSource;
+import java.awt.datatransfer.DataFlavor;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.mail.internet.MimeMultipart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.MessagingException;
+
+public class MultipartHandler implements DataContentHandler {
+    /**
+     * Field dataFlavor
+     */
+    ActivationDataFlavor dataFlavor;
+
+    public MultipartHandler(){
+        dataFlavor = new ActivationDataFlavor(javax.mail.internet.MimeMultipart.class, "multipart/mixed", "Multipart");
+    }
+
+    /**
+     * Constructor TextHandler
+     *
+     * @param dataFlavor
+     */
+    public MultipartHandler(ActivationDataFlavor dataFlavor) {
+        this.dataFlavor = dataFlavor;
+    }
+
+    /**
+     * Method getDF
+     *
+     * @return dataflavor
+     */
+    protected ActivationDataFlavor getDF() {
+        return dataFlavor;
+    }
+
+    /**
+     * Method getTransferDataFlavors
+     *
+     * @return dataflavors
+     */
+    public DataFlavor[] getTransferDataFlavors() {
+        return (new DataFlavor[]{dataFlavor});
+    }
+
+    /**
+     * Method getTransferData
+     *
+     * @param dataflavor
+     * @param datasource
+     * @return
+     * @throws IOException
+     */
+    public Object getTransferData(DataFlavor dataflavor, DataSource datasource)
+            throws IOException {
+        if (getDF().equals(dataflavor)) {
+            return getContent(datasource);
+        }
+        return null;
+    }
+
+    /**
+     * Method getContent
+     *
+     * @param datasource
+     * @return
+     * @throws IOException
+     */
+    public Object getContent(DataSource datasource) throws IOException {
+        try {
+            return new MimeMultipart(datasource);
+        } catch (MessagingException e) {
+            // if there is a syntax error from the datasource parsing, the content is
+            // just null.
+            return null;
+        }
+    }
+
+    /**
+     * Method writeTo
+     *
+     * @param object
+     * @param s
+     * @param outputstream
+     * @throws IOException
+     */
+    public void writeTo(Object object, String s, OutputStream outputstream) throws IOException {
+        // if this object is a MimeMultipart, then delegate to the part.
+        if (object instanceof MimeMultipart) {
+            try {
+                ((MimeMultipart)object).writeTo(outputstream);
+            } catch (MessagingException e) {
+                // we need to transform any exceptions into an IOException.
+                throw new IOException("Exception writing MimeMultipart: " + e.toString());
+            }
+        }
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MultipartHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MultipartHandler.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/MultipartHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/TextHandler.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/TextHandler.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/TextHandler.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/TextHandler.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * Licensed 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.geronimo.mail.handlers;
+
+import java.awt.datatransfer.DataFlavor;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+
+import javax.activation.ActivationDataFlavor;
+import javax.activation.DataContentHandler;
+import javax.activation.DataSource;
+import javax.mail.internet.ContentType;
+import javax.mail.internet.MimeUtility;
+import javax.mail.internet.ParseException;
+
+public class TextHandler implements DataContentHandler {
+    /**
+     * Field dataFlavor
+     */
+    ActivationDataFlavor dataFlavor;
+
+    public TextHandler(){
+        dataFlavor = new ActivationDataFlavor(java.lang.String.class, "text/plain", "Text String");
+    }
+
+    /**
+     * Constructor TextHandler
+     *
+     * @param dataFlavor
+     */
+    public TextHandler(ActivationDataFlavor dataFlavor) {
+        this.dataFlavor = dataFlavor;
+    }
+
+    /**
+     * Method getDF
+     *
+     * @return dataflavor
+     */
+    protected ActivationDataFlavor getDF() {
+        return dataFlavor;
+    }
+
+    /**
+     * Method getTransferDataFlavors
+     *
+     * @return dataflavors
+     */
+    public DataFlavor[] getTransferDataFlavors() {
+        return (new DataFlavor[]{dataFlavor});
+    }
+
+    /**
+     * Method getTransferData
+     *
+     * @param dataflavor
+     * @param datasource
+     * @return
+     * @throws IOException
+     */
+    public Object getTransferData(DataFlavor dataflavor, DataSource datasource)
+            throws IOException {
+        if (getDF().equals(dataflavor)) {
+            return getContent(datasource);
+        }
+        return null;
+    }
+
+    /**
+     * Method getContent
+     *
+     * @param datasource
+     * @return
+     * @throws IOException
+     */
+    public Object getContent(DataSource datasource) throws IOException {
+        InputStreamReader reader;
+        try {
+            String s = getCharSet(datasource.getContentType());
+            reader = new InputStreamReader(datasource.getInputStream(), s);
+        } catch (Exception ex) {
+            throw new UnsupportedEncodingException(ex.toString());
+        }
+        StringWriter writer = new StringWriter();
+        int ch;
+        while ((ch = reader.read()) != -1) {
+            writer.write(ch);
+        }
+        return writer.getBuffer().toString();
+    }
+
+    /**
+     * Method writeTo
+     *
+     * @param object
+     * @param s
+     * @param outputstream
+     * @throws IOException
+     */
+    public void writeTo(Object object, String s, OutputStream outputstream)
+            throws IOException {
+        OutputStreamWriter os;
+        try {
+            String charset = getCharSet(s);
+            os = new OutputStreamWriter(outputstream, charset);
+        } catch (Exception ex) {
+            throw new UnsupportedEncodingException(ex.toString());
+        }
+        String content = (String) object;
+        os.write(content, 0, content.length());
+        os.flush();
+    }
+
+    /**
+     * get the character set from content type
+     * @param contentType
+     * @return
+     * @throws ParseException
+     */
+    protected String getCharSet(String contentType) throws ParseException {
+        ContentType type = new ContentType(contentType);
+        String charset = type.getParameter("charset");
+        if (charset == null) {
+            charset = "us-ascii";
+        }
+        return MimeUtility.javaCharset(charset);
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/TextHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/TextHandler.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/TextHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/XMLHandler.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/XMLHandler.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/XMLHandler.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/XMLHandler.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * Licensed 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.geronimo.mail.handlers;
+
+import javax.activation.ActivationDataFlavor;
+
+public class XMLHandler extends TextHandler {
+    public XMLHandler() {
+        super(new ActivationDataFlavor(java.lang.String.class, "text/xml", "XML String"));
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/XMLHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/XMLHandler.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/handlers/XMLHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/ASCIIUtil.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/ASCIIUtil.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/ASCIIUtil.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/ASCIIUtil.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,245 @@
+/**
+ *
+ * Copyright 2003-2006 The Apache Software Foundation
+ *
+ *  Licensed 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.geronimo.mail.util;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+
+/**
+ * Set of utility classes for handling common encoding-related
+ * manipulations.
+ */
+public class ASCIIUtil {
+
+    /**
+     * Test to see if this string contains only US-ASCII (i.e., 7-bit
+     * ASCII) charactes.
+     *
+     * @param s      The test string.
+     *
+     * @return true if this is a valid 7-bit ASCII encoding, false if it
+     *         contains any non-US ASCII characters.
+     */
+    static public boolean isAscii(String s) {
+        for (int i = 0; i < s.length(); i++) {
+            if (!isAscii(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Test to see if a given character can be considered "valid" ASCII.
+     * The excluded characters are the control characters less than
+     * 32, 8-bit characters greater than 127, EXCEPT the CR, LF and
+     * tab characters ARE considered value (all less than 32).
+     *
+     * @param ch     The test character.
+     *
+     * @return true if this character meets the "ascii-ness" criteria, false
+     *         otherwise.
+     */
+    static public boolean isAscii(int ch) {
+        // these are explicitly considered valid.
+        if (ch == '\r' || ch == '\n' || ch == '\t') {
+            return true;
+        }
+
+        // anything else outside the range is just plain wrong.
+        if (ch >= 127 || ch < 32) {
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * Examine a stream of text and make a judgement on what encoding
+     * type should be used for the text.  Ideally, we want to use 7bit
+     * encoding to determine this, but we may need to use either quoted-printable
+     * or base64.  The choice is made on the ratio of 7-bit characters to non-7bit.
+     *
+     * @param content     An input stream for the content we're examining.
+     *
+     * @exception IOException
+     */
+    public static String getTextTransferEncoding(InputStream content) throws IOException {
+
+        // for efficiency, we'll read in blocks.
+        BufferedInputStream in = new BufferedInputStream(content, 4096);
+
+        int span = 0;            // span of characters without a line break.
+        boolean containsLongLines = false;
+        int asciiChars = 0;
+        int nonAsciiChars = 0;
+
+        while (true) {
+            int ch = in.read();
+            // if we hit an EOF here, go decide what type we've actually found.
+            if (ch == -1) {
+                break;
+            }
+
+            // we found a linebreak.  Reset the line length counters on either one.  We don't
+            // really need to validate here.
+            if (ch == '\n' || ch == '\r') {
+                // hit a line end, reset our line length counter
+                span = 0;
+            }
+            else {
+                span++;
+                // the text has long lines, we can't transfer this as unencoded text.
+                if (span > 998) {
+                    containsLongLines = true;
+                }
+
+                // non-ascii character, we have to transfer this in binary.
+                if (!isAscii(ch)) {
+                    nonAsciiChars++;
+                }
+                else {
+                    asciiChars++;
+                }
+            }
+        }
+
+        // looking good so far, only valid chars here.
+        if (nonAsciiChars == 0) {
+            // does this contain long text lines?  We need to use a Q-P encoding which will
+            // be only slightly longer, but handles folding the longer lines.
+            if (containsLongLines) {
+                return "quoted-printable";
+            }
+            else {
+                // ideal!  Easiest one to handle.
+                return "7bit";
+            }
+        }
+        else {
+            // mostly characters requiring encoding?  Base64 is our best bet.
+            if (nonAsciiChars > asciiChars) {
+                return "base64";
+            }
+            else {
+                // Q-P encoding will use fewer bytes than the full Base64.
+                return "quoted-printable";
+            }
+        }
+    }
+
+
+    /**
+     * Examine a stream of text and make a judgement on what encoding
+     * type should be used for the text.  Ideally, we want to use 7bit
+     * encoding to determine this, but we may need to use either quoted-printable
+     * or base64.  The choice is made on the ratio of 7-bit characters to non-7bit.
+     *
+     * @param content     A string for the content we're examining.
+     */
+    public static String getTextTransferEncoding(String content) {
+
+        int asciiChars = 0;
+        int nonAsciiChars = 0;
+
+        for (int i = 0; i < content.length(); i++) {
+            int ch = content.charAt(i);
+
+            // non-ascii character, we have to transfer this in binary.
+            if (!isAscii(ch)) {
+                nonAsciiChars++;
+            }
+            else {
+                asciiChars++;
+            }
+        }
+
+        // looking good so far, only valid chars here.
+        if (nonAsciiChars == 0) {
+            // ideal!  Easiest one to handle.
+            return "7bit";
+        }
+        else {
+            // mostly characters requiring encoding?  Base64 is our best bet.
+            if (nonAsciiChars > asciiChars) {
+                return "base64";
+            }
+            else {
+                // Q-P encoding will use fewer bytes than the full Base64.
+                return "quoted-printable";
+            }
+        }
+    }
+
+
+    /**
+     * Determine if the transfer encoding looks like it might be
+     * valid ascii text, and thus transferable as 7bit code.  In
+     * order for this to be true, all characters must be valid
+     * 7-bit ASCII code AND all line breaks must be properly formed
+     * (JUST '\r\n' sequences).  7-bit transfers also
+     * typically have a line limit of 1000 bytes (998 + the CRLF), so any
+     * stretch of charactes longer than that will also force Base64 encoding.
+     *
+     * @param content     An input stream for the content we're examining.
+     *
+     * @exception IOException
+     */
+    public static String getBinaryTransferEncoding(InputStream content) throws IOException {
+
+        // for efficiency, we'll read in blocks.
+        BufferedInputStream in = new BufferedInputStream(content, 4096);
+
+        int previousChar = 0;
+        int span = 0;            // span of characters without a line break.
+
+        while (true) {
+            int ch = in.read();
+            // if we hit an EOF here, we've only found valid text so far, so we can transfer this as
+            // 7-bit ascii.
+            if (ch == -1) {
+                return "7bit";
+            }
+
+            // we found a newline, this is only valid if the previous char was the '\r'
+            if (ch == '\n') {
+                // malformed linebreak?  force this to base64 encoding.
+                if (previousChar != '\r') {
+                    return "base64";
+                }
+                // hit a line end, reset our line length counter
+                span = 0;
+            }
+            else {
+                span++;
+                // the text has long lines, we can't transfer this as unencoded text.
+                if (span > 998) {
+                    return "base64";
+                }
+
+                // non-ascii character, we have to transfer this in binary.
+                if (!isAscii(ch)) {
+                    return "base64";
+                }
+            }
+            previousChar = ch;
+        }
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/ASCIIUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/ASCIIUtil.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/ASCIIUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,150 @@
+/**
+ *
+ * Copyright 2003-2006 The Apache Software Foundation
+ *
+ *  Licensed 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.geronimo.mail.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class Base64
+{
+    private static final Encoder encoder = new Base64Encoder();
+
+    /**
+     * encode the input data producing a base 64 encoded byte array.
+     *
+     * @return a byte array containing the base 64 encoded data.
+     */
+    public static byte[] encode(
+        byte[]    data)
+    {
+        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
+
+        try
+        {
+            encoder.encode(data, 0, data.length, bOut);
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("exception encoding base64 string: " + e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * Encode the byte data to base 64 writing it to the given output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int encode(
+        byte[]                data,
+        OutputStream    out)
+        throws IOException
+    {
+        return encoder.encode(data, 0, data.length, out);
+    }
+
+    /**
+     * Encode the byte data to base 64 writing it to the given output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int encode(
+        byte[]                data,
+        int                    off,
+        int                    length,
+        OutputStream    out)
+        throws IOException
+    {
+        return encoder.encode(data, off, length, out);
+    }
+
+    /**
+     * decode the base 64 encoded input data. It is assumed the input data is valid.
+     *
+     * @return a byte array representing the decoded data.
+     */
+    public static byte[] decode(
+        byte[]    data)
+    {
+        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
+
+        try
+        {
+            encoder.decode(data, 0, data.length, bOut);
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("exception decoding base64 string: " + e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * decode the base 64 encoded String data - whitespace will be ignored.
+     *
+     * @return a byte array representing the decoded data.
+     */
+    public static byte[] decode(
+        String    data)
+    {
+        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
+
+        try
+        {
+            encoder.decode(data, bOut);
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("exception decoding base64 string: " + e);
+        }
+
+        return bOut.toByteArray();
+    }
+
+    /**
+     * decode the base 64 encoded String data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public static int decode(
+        String                data,
+        OutputStream    out)
+        throws IOException
+    {
+        return encoder.decode(data, out);
+    }
+
+    /**
+     * decode the base 64 encoded String data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @param data   The array data to decode.
+     * @param out    The output stream for the data.
+     *
+     * @return the number of bytes produced.
+     * @exception IOException
+     */
+    public static int decode(byte [] data, OutputStream out) throws IOException
+    {
+        return encoder.decode(data, 0, data.length, out);
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64DecoderStream.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64DecoderStream.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64DecoderStream.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64DecoderStream.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,208 @@
+/**
+ *
+ * Copyright 2003-2006 The Apache Software Foundation
+ *
+ *  Licensed 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.geronimo.mail.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FilterInputStream;
+
+/**
+ * An implementation of a FilterInputStream that decodes the
+ * stream data in BASE64 encoding format.  This version does the
+ * decoding "on the fly" rather than decoding a single block of
+ * data.  Since this version is intended for use by the MimeUtilty class,
+ * it also handles line breaks in the encoded data.
+ */
+public class Base64DecoderStream extends FilterInputStream {
+
+    static protected final String MAIL_BASE64_IGNOREERRORS = "mail.mime.base64.ignoreerrors";
+
+    // number of decodeable units we'll try to process at one time.  We'll attempt to read that much
+    // data from the input stream and decode in blocks.
+    static protected final int BUFFERED_UNITS = 2000;
+
+    // our decoder for processing the data
+    protected Base64Encoder decoder = new Base64Encoder();
+
+    // can be overridden by a system property.
+    protected boolean ignoreErrors = false;
+
+    // buffer for reading in chars for decoding (which can support larger bulk reads)
+    protected byte[] encodedChars = new byte[BUFFERED_UNITS * 4];
+    // a buffer for one decoding unit's worth of data (3 bytes).  This is the minimum amount we
+    // can read at one time.
+    protected byte[] decodedChars = new byte[BUFFERED_UNITS * 3];
+    // count of characters in the buffer
+    protected int decodedCount = 0;
+    // index of the next decoded character
+    protected int decodedIndex = 0;
+
+
+    public Base64DecoderStream(InputStream in) {
+        super(in);
+        // make sure we get the ignore errors flag
+        ignoreErrors = SessionUtil.getBooleanProperty(MAIL_BASE64_IGNOREERRORS, false);
+    }
+
+    /**
+     * Test for the existance of decoded characters in our buffer
+     * of decoded data.
+     *
+     * @return True if we currently have buffered characters.
+     */
+    private boolean dataAvailable() {
+        return decodedCount != 0;
+    }
+
+    /**
+     * Get the next buffered decoded character.
+     *
+     * @return The next decoded character in the buffer.
+     */
+    private byte getBufferedChar() {
+        decodedCount--;
+        return decodedChars[decodedIndex++];
+    }
+
+    /**
+     * Decode a requested number of bytes of data into a buffer.
+     *
+     * @return true if we were able to obtain more data, false otherwise.
+     */
+    private boolean decodeStreamData() throws IOException {
+        decodedIndex = 0;
+
+        // fill up a data buffer with input data
+        int readCharacters = fillEncodedBuffer();
+
+        if (readCharacters > 0) {
+            decodedCount =  decoder.decode(encodedChars, 0, readCharacters, decodedChars);
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Retrieve a single byte from the decoded characters buffer.
+     *
+     * @return The decoded character or -1 if there was an EOF condition.
+     */
+    private int getByte() throws IOException {
+        if (!dataAvailable()) {
+            if (!decodeStreamData()) {
+                return -1;
+            }
+        }
+        decodedCount--;
+        return decodedChars[decodedIndex++];
+    }
+
+    private int getBytes(byte[] data, int offset, int length) throws IOException {
+
+        int readCharacters = 0;
+        while (length > 0) {
+            // need data?  Try to get some
+            if (!dataAvailable()) {
+                // if we can't get this, return a count of how much we did get (which may be -1).
+                if (!decodeStreamData()) {
+                    return readCharacters > 0 ? readCharacters : -1;
+                }
+            }
+
+            // now copy some of the data from the decoded buffer to the target buffer
+            int copyCount = Math.min(decodedCount, length);
+            System.arraycopy(decodedChars, decodedIndex, data, offset, copyCount);
+            decodedIndex += copyCount;
+            decodedCount -= copyCount;
+            offset += copyCount;
+            length -= copyCount;
+            readCharacters += copyCount;
+        }
+        return readCharacters;
+    }
+
+
+    /**
+     * Fill our buffer of input characters for decoding from the
+     * stream.  This will attempt read a full buffer, but will
+     * terminate on an EOF or read error.  This will filter out
+     * non-Base64 encoding chars and will only return a valid
+     * multiple of 4 number of bytes.
+     *
+     * @return The count of characters read.
+     */
+    private int fillEncodedBuffer() throws IOException
+    {
+        int readCharacters = 0;
+
+        while (true) {
+            // get the next character from the stream
+            int ch = in.read();
+            // did we hit an EOF condition?
+            if (ch == -1) {
+                // now check to see if this is normal, or potentially an error
+                // if we didn't get characters as a multiple of 4, we may need to complain about this.
+                if ((readCharacters % 4) != 0) {
+                    // the error checking can be turned off...normally it isn't
+                    if (!ignoreErrors) {
+                        throw new IOException("Base64 encoding error, data truncated");
+                    }
+                    // we're ignoring errors, so round down to a multiple and return that.
+                    return (readCharacters / 4) * 4;
+                }
+                // return the count.
+                return readCharacters;
+            }
+            // if this character is valid in a Base64 stream, copy it to the buffer.
+            else if (decoder.isValidBase64(ch)) {
+                encodedChars[readCharacters++] = (byte)ch;
+                // if we've filled up the buffer, time to quit.
+                if (readCharacters >= encodedChars.length) {
+                    return readCharacters;
+                }
+            }
+
+            // we're filtering out whitespace and CRLF characters, so just ignore these
+        }
+    }
+
+
+    // in order to function as a filter, these streams need to override the different
+    // read() signature.
+
+    public int read() throws IOException
+    {
+        return getByte();
+    }
+
+
+    public int read(byte [] buffer, int offset, int length) throws IOException {
+        return getBytes(buffer, offset, length);
+    }
+
+
+    public boolean markSupported() {
+        return false;
+    }
+
+
+    public int available() throws IOException {
+        return ((in.available() / 4) * 3) + decodedCount;
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64DecoderStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64DecoderStream.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64DecoderStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64Encoder.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64Encoder.java?rev=421852&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64Encoder.java (added)
+++ geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64Encoder.java Fri Jul 14 03:02:19 2006
@@ -0,0 +1,552 @@
+/**
+ *
+ * Copyright 2003-2006 The Apache Software Foundation
+ *
+ *  Licensed 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.geronimo.mail.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+public class Base64Encoder
+    implements Encoder
+{
+    protected final byte[] encodingTable =
+        {
+            (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+            (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+            (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+            (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+            (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+            (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+            (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+            (byte)'v',
+            (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+            (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
+            (byte)'7', (byte)'8', (byte)'9',
+            (byte)'+', (byte)'/'
+        };
+
+    protected byte    padding = (byte)'=';
+
+    /*
+     * set up the decoding table.
+     */
+    protected final byte[] decodingTable = new byte[256];
+
+    protected void initialiseDecodingTable()
+    {
+        for (int i = 0; i < encodingTable.length; i++)
+        {
+            decodingTable[encodingTable[i]] = (byte)i;
+        }
+    }
+
+    public Base64Encoder()
+    {
+        initialiseDecodingTable();
+    }
+
+    /**
+     * encode the input data producing a base 64 output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public int encode(
+        byte[]                data,
+        int                    off,
+        int                    length,
+        OutputStream    out)
+        throws IOException
+    {
+        int modulus = length % 3;
+        int dataLength = (length - modulus);
+        int a1, a2, a3;
+
+        for (int i = off; i < off + dataLength; i += 3)
+        {
+            a1 = data[i] & 0xff;
+            a2 = data[i + 1] & 0xff;
+            a3 = data[i + 2] & 0xff;
+
+            out.write(encodingTable[(a1 >>> 2) & 0x3f]);
+            out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
+            out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
+            out.write(encodingTable[a3 & 0x3f]);
+        }
+
+        /*
+         * process the tail end.
+         */
+        int    b1, b2, b3;
+        int    d1, d2;
+
+        switch (modulus)
+        {
+        case 0:        /* nothing left to do */
+            break;
+        case 1:
+            d1 = data[off + dataLength] & 0xff;
+            b1 = (d1 >>> 2) & 0x3f;
+            b2 = (d1 << 4) & 0x3f;
+
+            out.write(encodingTable[b1]);
+            out.write(encodingTable[b2]);
+            out.write(padding);
+            out.write(padding);
+            break;
+        case 2:
+            d1 = data[off + dataLength] & 0xff;
+            d2 = data[off + dataLength + 1] & 0xff;
+
+            b1 = (d1 >>> 2) & 0x3f;
+            b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
+            b3 = (d2 << 2) & 0x3f;
+
+            out.write(encodingTable[b1]);
+            out.write(encodingTable[b2]);
+            out.write(encodingTable[b3]);
+            out.write(padding);
+            break;
+        }
+
+        return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4);
+    }
+
+    private boolean ignore(
+        char    c)
+    {
+        return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
+    }
+
+    /**
+     * decode the base 64 encoded byte data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public int decode(
+        byte[]                data,
+        int                    off,
+        int                    length,
+        OutputStream    out)
+        throws IOException
+    {
+        byte[]    bytes;
+        byte    b1, b2, b3, b4;
+        int        outLen = 0;
+
+        int        end = off + length;
+
+        while (end > 0)
+        {
+            if (!ignore((char)data[end - 1]))
+            {
+                break;
+            }
+
+            end--;
+        }
+
+        int  i = off;
+        int  finish = end - 4;
+
+        while (i < finish)
+        {
+            while ((i < finish) && ignore((char)data[i]))
+            {
+                i++;
+            }
+
+            b1 = decodingTable[data[i++]];
+
+            while ((i < finish) && ignore((char)data[i]))
+            {
+                i++;
+            }
+
+            b2 = decodingTable[data[i++]];
+
+            while ((i < finish) && ignore((char)data[i]))
+            {
+                i++;
+            }
+
+            b3 = decodingTable[data[i++]];
+
+            while ((i < finish) && ignore((char)data[i]))
+            {
+                i++;
+            }
+
+            b4 = decodingTable[data[i++]];
+
+            out.write((b1 << 2) | (b2 >> 4));
+            out.write((b2 << 4) | (b3 >> 2));
+            out.write((b3 << 6) | b4);
+
+            outLen += 3;
+        }
+
+        if (data[end - 2] == padding)
+        {
+            b1 = decodingTable[data[end - 4]];
+            b2 = decodingTable[data[end - 3]];
+
+            out.write((b1 << 2) | (b2 >> 4));
+
+            outLen += 1;
+        }
+        else if (data[end - 1] == padding)
+        {
+            b1 = decodingTable[data[end - 4]];
+            b2 = decodingTable[data[end - 3]];
+            b3 = decodingTable[data[end - 2]];
+
+            out.write((b1 << 2) | (b2 >> 4));
+            out.write((b2 << 4) | (b3 >> 2));
+
+            outLen += 2;
+        }
+        else
+        {
+            b1 = decodingTable[data[end - 4]];
+            b2 = decodingTable[data[end - 3]];
+            b3 = decodingTable[data[end - 2]];
+            b4 = decodingTable[data[end - 1]];
+
+            out.write((b1 << 2) | (b2 >> 4));
+            out.write((b2 << 4) | (b3 >> 2));
+            out.write((b3 << 6) | b4);
+
+            outLen += 3;
+        }
+
+        return outLen;
+    }
+
+    /**
+     * decode the base 64 encoded String data writing it to the given output stream,
+     * whitespace characters will be ignored.
+     *
+     * @return the number of bytes produced.
+     */
+    public int decode(
+        String                data,
+        OutputStream    out)
+        throws IOException
+    {
+        byte[]    bytes;
+        byte    b1, b2, b3, b4;
+        int        length = 0;
+
+        int        end = data.length();
+
+        while (end > 0)
+        {
+            if (!ignore(data.charAt(end - 1)))
+            {
+                break;
+            }
+
+            end--;
+        }
+
+        int    i = 0;
+        int   finish = end - 4;
+
+        while (i < finish)
+        {
+            while ((i < finish) && ignore(data.charAt(i)))
+            {
+                i++;
+            }
+
+            b1 = decodingTable[data.charAt(i++)];
+
+            while ((i < finish) && ignore(data.charAt(i)))
+            {
+                i++;
+            }
+            b2 = decodingTable[data.charAt(i++)];
+
+            while ((i < finish) && ignore(data.charAt(i)))
+            {
+                i++;
+            }
+            b3 = decodingTable[data.charAt(i++)];
+
+            while ((i < finish) && ignore(data.charAt(i)))
+            {
+                i++;
+            }
+            b4 = decodingTable[data.charAt(i++)];
+
+            out.write((b1 << 2) | (b2 >> 4));
+            out.write((b2 << 4) | (b3 >> 2));
+            out.write((b3 << 6) | b4);
+
+            length += 3;
+        }
+
+        if (data.charAt(end - 2) == padding)
+        {
+            b1 = decodingTable[data.charAt(end - 4)];
+            b2 = decodingTable[data.charAt(end - 3)];
+
+            out.write((b1 << 2) | (b2 >> 4));
+
+            length += 1;
+        }
+        else if (data.charAt(end - 1) == padding)
+        {
+            b1 = decodingTable[data.charAt(end - 4)];
+            b2 = decodingTable[data.charAt(end - 3)];
+            b3 = decodingTable[data.charAt(end - 2)];
+
+            out.write((b1 << 2) | (b2 >> 4));
+            out.write((b2 << 4) | (b3 >> 2));
+
+            length += 2;
+        }
+        else
+        {
+            b1 = decodingTable[data.charAt(end - 4)];
+            b2 = decodingTable[data.charAt(end - 3)];
+            b3 = decodingTable[data.charAt(end - 2)];
+            b4 = decodingTable[data.charAt(end - 1)];
+
+            out.write((b1 << 2) | (b2 >> 4));
+            out.write((b2 << 4) | (b3 >> 2));
+            out.write((b3 << 6) | b4);
+
+            length += 3;
+        }
+
+        return length;
+    }
+
+    /**
+     * decode the base 64 encoded byte data writing it to the provided byte array buffer.
+     *
+     * @return the number of bytes produced.
+     */
+    public int decode(byte[] data, int off, int length, byte[] out) throws IOException
+    {
+        byte[]    bytes;
+        byte    b1, b2, b3, b4;
+        int        outLen = 0;
+
+        int        end = off + length;
+
+        while (end > 0)
+        {
+            if (!ignore((char)data[end - 1]))
+            {
+                break;
+            }
+
+            end--;
+        }
+
+        int  i = off;
+        int  finish = end - 4;
+
+        while (i < finish)
+        {
+            while ((i < finish) && ignore((char)data[i]))
+            {
+                i++;
+            }
+
+            b1 = decodingTable[data[i++]];
+
+            while ((i < finish) && ignore((char)data[i]))
+            {
+                i++;
+            }
+
+            b2 = decodingTable[data[i++]];
+
+            while ((i < finish) && ignore((char)data[i]))
+            {
+                i++;
+            }
+
+            b3 = decodingTable[data[i++]];
+
+            while ((i < finish) && ignore((char)data[i]))
+            {
+                i++;
+            }
+
+            b4 = decodingTable[data[i++]];
+
+            out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
+            out[outLen++] = (byte)((b2 << 4) | (b3 >> 2));
+            out[outLen++] = (byte)((b3 << 6) | b4);
+        }
+
+        if (data[end - 2] == padding)
+        {
+            b1 = decodingTable[data[end - 4]];
+            b2 = decodingTable[data[end - 3]];
+
+            out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
+        }
+        else if (data[end - 1] == padding)
+        {
+            b1 = decodingTable[data[end - 4]];
+            b2 = decodingTable[data[end - 3]];
+            b3 = decodingTable[data[end - 2]];
+
+            out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
+            out[outLen++] = (byte)((b2 << 4) | (b3 >> 2));
+        }
+        else
+        {
+            b1 = decodingTable[data[end - 4]];
+            b2 = decodingTable[data[end - 3]];
+            b3 = decodingTable[data[end - 2]];
+            b4 = decodingTable[data[end - 1]];
+
+            out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
+            out[outLen++] = (byte)((b2 << 4) | (b3 >> 2));
+            out[outLen++] = (byte)((b3 << 6) | b4);
+        }
+
+        return outLen;
+    }
+
+    /**
+     * Test if a character is a valid Base64 encoding character.  This
+     * must be either a valid digit or the padding character ("=").
+     *
+     * @param ch     The test character.
+     *
+     * @return true if this is valid in Base64 encoded data, false otherwise.
+     */
+    public boolean isValidBase64(int ch) {
+        // 'A' has the value 0 in the decoding table, so we need a special one for that
+        return ch == padding || ch == 'A' || decodingTable[ch] != 0;
+    }
+
+
+    /**
+     * Perform RFC-2047 word encoding using Base64 data encoding.
+     *
+     * @param in      The source for the encoded data.
+     * @param charset The charset tag to be added to each encoded data section.
+     * @param out     The output stream where the encoded data is to be written.
+     * @param fold    Controls whether separate sections of encoded data are separated by
+     *                linebreaks or whitespace.
+     *
+     * @exception IOException
+     */
+    public void encodeWord(InputStream in, String charset, OutputStream out, boolean fold) throws IOException
+    {
+        PrintStream writer = new PrintStream(out);
+
+        // encoded words are restricted to 76 bytes, including the control adornments.
+        int limit = 76 - 7 - charset.length();
+        boolean firstLine = true;
+        StringBuffer encodedString = new StringBuffer(76);
+
+        while (true) {
+            // encode the next segment.
+            encode(in, encodedString, limit);
+            // if we're out of data, nothing will be encoded.
+            if (encodedString.length() == 0) {
+                break;
+            }
+
+            // if we have more than one segment, we need to insert separators.  Depending on whether folding
+            // was requested, this is either a blank or a linebreak.
+            if (!firstLine) {
+                if (fold) {
+                    writer.print("\r\n");
+                }
+                else {
+                    writer.print(" ");
+                }
+            }
+
+            // add the encoded word header
+            writer.print("=?");
+            writer.print(charset);
+            writer.print("?B?");
+            // the data
+            writer.print(encodedString.toString());
+            // and the word terminator.
+            writer.print("?=");
+            writer.flush();
+
+            // reset our string buffer for the next segment.
+            encodedString.setLength(0);
+        }
+    }
+
+    /**
+     * encode the input data producing a base 64 output stream.
+     *
+     * @return the number of bytes produced.
+     */
+    public void encode(InputStream in, StringBuffer out, int limit) throws IOException
+    {
+        int count = limit / 4;
+        byte [] inBuffer = new byte[3];
+
+        while (count-- > 0) {
+
+            int readCount = in.read(inBuffer);
+            // did we get a full triplet?  that's an easy encoding.
+            if (readCount == 3) {
+                int  a1 = inBuffer[0] & 0xff;
+                int  a2 = inBuffer[1] & 0xff;
+                int  a3 = inBuffer[2] & 0xff;
+
+                out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
+                out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
+                out.append((char)encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
+                out.append((char)encodingTable[a3 & 0x3f]);
+
+            }
+            else if (readCount <= 0) {
+                // eof condition, don'e entirely.
+                return;
+            }
+            else if (readCount == 1) {
+                int  a1 = inBuffer[0] & 0xff;
+                out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
+                out.append((char)encodingTable[(a1 << 4) & 0x3f]);
+                out.append((char)padding);
+                out.append((char)padding);
+                return;
+            }
+            else if (readCount == 2) {
+                int  a1 = inBuffer[0] & 0xff;
+                int  a2 = inBuffer[1] & 0xff;
+
+                out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
+                out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
+                out.append((char)encodingTable[(a2 << 2) & 0x3f]);
+                out.append((char)padding);
+                return;
+            }
+        }
+    }
+}

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64Encoder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64Encoder.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/specs/trunk/geronimo-spec-javamail-1.4/src/main/java/org/apache/geronimo/mail/util/Base64Encoder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain