/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.ws.core.soap.attachment; // $Id: MultipartRelatedDecoder.java 2250 2007-02-02 13:13:28Z thomas.diesler@jboss.com $ import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.Enumeration; import java.util.LinkedList; import javax.activation.DataHandler; import javax.mail.Header; import javax.mail.MessagingException; import javax.mail.internet.ContentType; import javax.mail.internet.InternetHeaders; import javax.mail.internet.ParseException; import javax.xml.soap.AttachmentPart; import org.jboss.ws.WSException; /** * Abstract MutilPartRelatedDecoder decodes a mime multipart/related stream. * * @author Jason T. Greene * @author Thomas.Diesler@jboss.org * @since 18-Jan-2006 */ public class MultipartRelatedDecoder { private ContentType contentType; private String rootType; private AttachmentPartImpl rootPart; private LinkedList relatedParts = new LinkedList(); /** * Constructs a MultipartRelatedDecoder. This will block until the message * has been decoded. * * @param contentType the mime Content-Type header provided by the transport * @param stream The stream to pull the multipart message from */ public MultipartRelatedDecoder(ContentType contentType) throws IOException, MessagingException { this.contentType = contentType; if (MimeConstants.TYPE_MULTIPART_RELATED.equalsIgnoreCase(contentType.getBaseType()) == false) throw new IllegalArgumentException("Multipart related decoder called with a non-multipart/related type"); rootType = contentType.getParameter("type"); if (rootType == null) throw new IllegalArgumentException("multipart/related type is invalid, it is missing the root type parameter"); } private boolean isValidRootType(String type) throws ParseException { // The type multipart/related parameter can not have parameters itself ContentType contentType = new ContentType(type); type = contentType.getBaseType(); return rootType.equals(type); } public void decodeMultipartRelatedMessage(InputStream stream) throws IOException, MessagingException { String boundaryParameter = contentType.getParameter("boundary"); String start = contentType.getParameter("start"); byte[] boundary; byte[] crlf; if (boundaryParameter == null) throw new IllegalArgumentException("multipart/related content type did not contain a boundary"); try { // [JBWS-1393] - Problem interpreting messages with attachment when confronted with no header if (start == null) boundary = ("--" + boundaryParameter).getBytes("US-ASCII"); else boundary = ("\r\n--" + boundaryParameter).getBytes("US-ASCII"); crlf = ("\r\n").getBytes("US-ASCII"); } catch (UnsupportedEncodingException e) { throw new WSException("US-ASCII not supported, this should never happen"); } // [JBWS-1620] - Incorrect handling of MIME boundaries in MultipartRelatedDecoder PushbackInputStream pushBackStream = new PushbackInputStream(stream,2); pushBackStream.unread(crlf); BoundaryDelimitedInputStream delimitedStream = new BoundaryDelimitedInputStream(pushBackStream, boundary); // Eat first inner stream since its empty byte[] buffer = new byte[256]; while (delimitedStream.read(buffer) != -1) { } while (!delimitedStream.isOuterStreamClosed()) { // If the stream is empty or an end marker is reached, skip to the next // one if (!advanceToHeaders(delimitedStream)) continue; InternetHeaders headers = new InternetHeaders(delimitedStream); String typeHeader[] = headers.getHeader(MimeConstants.CONTENT_TYPE); if (typeHeader == null) throw new IllegalArgumentException("multipart/related stream invalid, component Content-type missing."); SwapableMemoryDataSource source = new SwapableMemoryDataSource(delimitedStream, typeHeader[0]); AttachmentPartImpl part = new AttachmentPartImpl(new DataHandler(source)); Enumeration enumeration = headers.getAllHeaders(); while (enumeration.hasMoreElements()) { Header header = (Header)enumeration.nextElement(); part.addMimeHeader(header.getName(), header.getValue()); } // The root part is either the one pointed to by the start parameter, or // the first occuring part if start is not defined. if (rootPart == null && (start == null || start.equals(part.getContentId()))) { if (isValidRootType(part.getContentType()) == false) throw new IllegalArgumentException("multipart/related type specified a root type other than the one" + " that was found."); rootPart = part; } else { relatedParts.add(part); } } if (rootPart == null) throw new IllegalArgumentException("multipart/related stream invalid, no root part was found"); } private boolean advanceToHeaders(InputStream stream) throws IOException { boolean dash = false, cr = false; while (true) { int b = stream.read(); switch (b) { case -1: return false; case '\r': cr = true; dash = false; break; case '-': if (dash == true) { // Two dashes indicate no further content stream.close(); return false; } dash = true; cr = false; break; case '\n': if (cr == true) return true; dash = false; break; default: dash = false; cr = false; } } } /** * Returns an AttachmentPart representing the root part of this multipart/related message. * * @return the root part of this multipart/related message */ public AttachmentPart getRootPart() { return rootPart; } /** * Returns a collection of AttachmentPart objects that represent the attachments on this message. * If there are no attachments, an empty collection is returned. */ public Collection getRelatedParts() { return relatedParts; } }