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 2008/04/08 20:01:43 UTC

svn commit: r646017 - in /geronimo/specs/trunk/geronimo-javamail_1.4_spec/src: main/java/javax/mail/internet/ test/java/javax/mail/internet/ test/resources/

Author: rickmcguire
Date: Tue Apr  8 11:01:42 2008
New Revision: 646017

URL: http://svn.apache.org/viewvc?rev=646017&view=rev
Log:
GERONIMO-3953 ArrayIndexOutOfBoundsException reading MimeMultipart from a file with multiple attachments.


Added:
    geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/resources/wmtom.bin   (with props)
Modified:
    geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/InternetHeaders.java
    geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/MimeMultipart.java
    geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/internet/MimeMultipartTest.java

Modified: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/InternetHeaders.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/InternetHeaders.java?rev=646017&r1=646016&r2=646017&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/InternetHeaders.java (original)
+++ geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/InternetHeaders.java Tue Apr  8 11:01:42 2008
@@ -104,6 +104,7 @@
         try {
             StringBuffer name = new StringBuffer(32);
             StringBuffer value = new StringBuffer(128);
+            boolean foundColon = false; 
             done: while (true) {
                 int c = in.read();
                 char ch = (char) c;
@@ -128,22 +129,34 @@
                 } else {
                     // new header
                     if (name.length() > 0) {
-                        addHeader(name.toString().trim(), value.toString().trim());
+                        if (foundColon) {
+                            addHeader(name.toString().trim(), value.toString().trim());
+                        }
+                        else {
+                            addHeader(name.toString().trim(), name.toString().trim());
+                        }
                     }
                     name.setLength(0);
                     value.setLength(0);
+                    foundColon = false; 
                     while (true) {
                         name.append((char) c);
                         c = in.read();
                         if (c == -1) {
                             break done;
                         } else if (c == ':') {
+                            foundColon = true; 
+                            break;
+                        } else if (c == 13 || c == 10) {
                             break;
                         }
                     }
-                    c = in.read();
-                    if (c == -1) {
-                        break done;
+                    // only read this if we found a separator 
+                    if (foundColon) {
+                        c = in.read();
+                        if (c == -1) {
+                            break done;
+                        }
                     }
                 }
 
@@ -165,7 +178,12 @@
                 }
             }
             if (name.length() > 0) {
-                addHeader(name.toString().trim(), value.toString().trim());
+                if (foundColon) {
+                    addHeader(name.toString().trim(), value.toString().trim());
+                }
+                else {
+                    addHeader(name.toString().trim(), name.toString().trim());
+                }
             }
         } catch (IOException e) {
             throw new MessagingException("Error loading headers", e);

Modified: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/MimeMultipart.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/MimeMultipart.java?rev=646017&r1=646016&r2=646017&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/MimeMultipart.java (original)
+++ geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/MimeMultipart.java Tue Apr  8 11:01:42 2008
@@ -27,6 +27,8 @@
 import java.io.OutputStream;
 import java.io.PushbackInputStream;
 
+import java.util.Arrays; 
+
 import javax.activation.DataSource;
 import javax.mail.BodyPart;
 import javax.mail.MessagingException;
@@ -165,11 +167,21 @@
         }
         try {
             ContentType cType = new ContentType(contentType);
-            byte[] boundary = ("--" + cType.getParameter("boundary")).getBytes();
             InputStream is = new BufferedInputStream(ds.getInputStream());
-            PushbackInputStream pushbackInStream = new PushbackInputStream(is,
-                    (boundary.length + 2));
-            readTillFirstBoundary(pushbackInStream, boundary);
+            PushbackInputStream pushbackInStream = null;
+            String boundaryString = cType.getParameter("boundary"); 
+            byte[] boundary = null; 
+            if (boundaryString == null) {
+                pushbackInStream = new PushbackInputStream(is, 128);  
+                // read until we find something that looks like a boundary string 
+                boundary = readTillFirstBoundary(pushbackInStream); 
+            }
+            else {
+                boundary = ("--" + boundaryString).getBytes();
+                pushbackInStream = new PushbackInputStream(is, (boundary.length + 2));
+                readTillFirstBoundary(pushbackInStream, boundary);
+            }
+            
             while (pushbackInStream.available()>0){
                 MimeBodyPartInputStream partStream;
                 partStream = new MimeBodyPartInputStream(pushbackInStream,
@@ -199,49 +211,120 @@
      * @param boundary
      * @throws MessagingException
      */
-    private boolean readTillFirstBoundary(PushbackInputStream pushbackInStream, byte[] boundary) throws MessagingException {
+    private byte[] readTillFirstBoundary(PushbackInputStream pushbackInStream) throws MessagingException {
         ByteArrayOutputStream preambleStream = new ByteArrayOutputStream();
 
         try {
             while (pushbackInStream.available() > 0) {
-                int value = pushbackInStream.read();
-                if ((byte) value == boundary[0]) {
-                    int boundaryIndex = 0;
-                    while (pushbackInStream.available() > 0 && (boundaryIndex < boundary.length)
-                            && ((byte) value == boundary[boundaryIndex])) {
-                        value = pushbackInStream.read();
-                        if (value == -1)
-                            throw new MessagingException(
-                                    "Unexpected End of Stream while searching for first Mime Boundary");
-                        boundaryIndex++;
+                // read the next line 
+                byte[] line = readLine(pushbackInStream); 
+                // if this looks like a boundary, then make it so 
+                if (line.length > 2 && line[0] == '-' && line[1] == '-') {
+                    // save the preamble, if there is one.
+                    byte[] preambleBytes = preambleStream.toByteArray();
+                    if (preambleBytes.length > 0) {
+                        preamble = new String(preambleBytes);
                     }
-                    if (boundaryIndex == boundary.length) { // boundary found
-                        pushbackInStream.read();
+                    return line;        
+                }
+                else {
+                    // this is part of the preamble.
+                    preambleStream.write(line);
+                    preambleStream.write('\r'); 
+                    preambleStream.write('\n'); 
+                }
+            }
+            throw new MessagingException("Unexpected End of Stream while searching for first Mime Boundary");
+        } catch (IOException ioe) {
+            throw new MessagingException(ioe.toString(), ioe);
+        }
+    }
 
-                        // save the preamble, if there is one.
-                        byte[] preambleBytes = preambleStream.toByteArray();
-                        if (preambleBytes.length > 0) {
-                            preamble = new String(preambleBytes);
-                        }
-                        return true;
-                    }
-                    else {
-                        // we need to add this to the preamble.  We write the part of the boundary that
-                        // actually matched, followed by the character that was the mismatch.
-                        preambleStream.write(boundary, 0, boundaryIndex);
-                        preambleStream.write((byte)value);
+    /**
+     * Move the read pointer to the begining of the first part
+     * read till the end of first boundary.  Any data read before this point are
+     * saved as the preamble.
+     *
+     * @param pushbackInStream
+     * @param boundary
+     * @throws MessagingException
+     */
+    private void readTillFirstBoundary(PushbackInputStream pushbackInStream, byte[] boundary) throws MessagingException {
+        ByteArrayOutputStream preambleStream = new ByteArrayOutputStream();
+
+        try {
+            while (pushbackInStream.available() > 0) {
+                // read the next line 
+                byte[] line = readLine(pushbackInStream); 
+                
+                // if this is the same length as our target boundary, then compare the two. 
+                if (Arrays.equals(line, boundary)) {
+                    // save the preamble, if there is one.
+                    byte[] preambleBytes = preambleStream.toByteArray();
+                    if (preambleBytes.length > 0) {
+                        preamble = new String(preambleBytes);
                     }
+                    return;        
                 }
                 else {
                     // this is part of the preamble.
-                    preambleStream.write((byte)value);
+                    preambleStream.write(line);
+                    preambleStream.write('\r'); 
+                    preambleStream.write('\n'); 
                 }
             }
+            throw new MessagingException("Unexpected End of Stream while searching for first Mime Boundary");
         } catch (IOException ioe) {
             throw new MessagingException(ioe.toString(), ioe);
         }
-        return false;
     }
+    
+    /**
+     * Read a single line of data from the input stream, 
+     * returning it as an array of bytes. 
+     * 
+     * @param in     The source input stream.
+     * 
+     * @return A byte array containing the line data.  Returns 
+     *         null if there's nothing left in the stream.
+     * @exception MessagingException
+     */
+    private byte[] readLine(PushbackInputStream in) throws IOException 
+    {
+        ByteArrayOutputStream line = new ByteArrayOutputStream();
+        
+        while (in.available() > 0) {
+            int value = in.read(); 
+            if (value == -1) {
+                // if we have nothing in the accumulator, signal an EOF back 
+                if (line.size() == 0) {
+                    return null; 
+                }
+                break; 
+            }
+            else if (value == '\r') {
+                value = in.read(); 
+                // we expect to find a linefeed after the carriage return, but 
+                // some things play loose with the rules. 
+                if (value != '\n') {
+                    in.unread(value); 
+                }
+                break; 
+            }
+            else if (value == '\n') {
+                // naked linefeed, allow that 
+                break; 
+            }
+            else {
+                // write this to the line 
+                line.write((byte)value); 
+            }
+        }
+        // return this as an array of bytes 
+        return line.toByteArray(); 
+    }
+    
+    
 
     protected InternetHeaders createInternetHeaders(InputStream in) throws MessagingException {
         return new InternetHeaders(in);

Modified: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/internet/MimeMultipartTest.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/internet/MimeMultipartTest.java?rev=646017&r1=646016&r2=646017&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/internet/MimeMultipartTest.java (original)
+++ geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/internet/MimeMultipartTest.java Tue Apr  8 11:01:42 2008
@@ -26,6 +26,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.util.Properties;
 import javax.mail.BodyPart;
 import javax.mail.MessagingException;
@@ -93,6 +94,25 @@
 
         MimeMessage newMessage = new MimeMessage(session, in);
         assertEquals("This is a preamble\r\n", ((MimeMultipart)newMessage.getContent()).getPreamble());
+    }
+
+    public void testMIMEWriting() throws IOException, MessagingException {
+        File basedir = new File(System.getProperty("basedir", "."));
+        File testInput = new File(basedir, "src/test/resources/wmtom.bin");
+        FileInputStream inStream = new FileInputStream(testInput);
+        Properties props = new Properties();
+        javax.mail.Session session = javax.mail.Session
+                .getInstance(props, null);
+        MimeMessage mimeMessage = new MimeMessage(session, inStream);
+        DataHandler dh = mimeMessage.getDataHandler();
+        MimeMultipart multiPart = new MimeMultipart(dh.getDataSource());
+        MimeBodyPart mimeBodyPart0 = (MimeBodyPart) multiPart.getBodyPart(0);
+        Object object0 = mimeBodyPart0.getContent();
+        assertNotNull(object0);
+        MimeBodyPart mimeBodyPart1 = (MimeBodyPart) multiPart.getBodyPart(1);
+        Object object1 = mimeBodyPart1.getContent();
+        assertNotNull(object1);
+        assertEquals(multiPart.getCount(), 2);
     }
 
     protected void writeToSetUp() throws Exception {

Added: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/resources/wmtom.bin
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/resources/wmtom.bin?rev=646017&view=auto
==============================================================================
Binary file - no diff available.

Propchange: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/resources/wmtom.bin
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream