/* * The contents of this file are subject to the Mozilla Public License Version 1.1 * (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at . * * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT * WARRANTY OF ANY KIND, either express or implied. See the License for the specific * language governing rights and limitations under the License. * * The Original Code is the Venice Web Communities System. * * The Initial Developer of the Original Code is Eric J. Bowersox , * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. * * Contributor(s): */ package com.silverwrist.util; import java.io.*; import java.sql.Blob; import java.sql.SQLException; import java.util.*; import javax.activation.DataSource; import javax.mail.*; import javax.mail.internet.*; import javax.servlet.*; import org.apache.log4j.*; // This class was built in a process I call "Java Junkyard Wars," in which I put together // a bunch of APIs in ways that their designers would never have anticipated. It's // absolutely bodge-tastic! public class ServletMultipartHandler { /*-------------------------------------------------------------------------------- * Internal wrapper around the ServletRequest that implements DataSource *-------------------------------------------------------------------------------- */ class ServletDataSource implements DataSource { private ServletRequest request; private InputStream istm = null; public ServletDataSource(ServletRequest request) { this.request = request; } // end constructor public InputStream getInputStream() throws IOException { if (istm==null) istm = request.getInputStream(); return istm; } // end getInputStream public OutputStream getOutputStream() throws IOException { throw new IOException("tried to get OutputStream on servlet input?!?!?"); } // end getOutputStream public String getContentType() { return request.getContentType(); } // end getContentType public String getName() { return "servlet"; } // end getName } // end class ServletDataSource /*-------------------------------------------------------------------------------- * Internal class representing a data value *-------------------------------------------------------------------------------- */ static class MultipartDataValue implements Blob { private static final int BLKSIZE = 4096; private byte[] actual_data; // the actual data we contain public MultipartDataValue(MimeBodyPart part) throws MessagingException, IOException { if (logger.isDebugEnabled()) logger.debug("creating new MultipartDataValue"); // set up the streams to copy between InputStream in = part.getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] copybuf = new byte[BLKSIZE]; // begin the copy loop int ct = in.read(copybuf,0,BLKSIZE); while (ct>=0) { // do a simple read and write if (logger.isDebugEnabled()) logger.debug("read blksize = " + ct); if (ct>0) out.write(copybuf,0,ct); ct = in.read(copybuf,0,BLKSIZE); } // end while in.close(); actual_data = out.toByteArray(); out.close(); if (logger.isDebugEnabled()) logger.debug("finished copying, " + actual_data.length + " bytes transferred"); } // end constructor public long length() { return actual_data.length; } // end length public byte[] getBytes(long pos, int length) { byte[] rc = new byte[length]; System.arraycopy(actual_data,(int)pos,rc,0,length); return rc; } // end getBytes public InputStream getBinaryStream() { return new ByteArrayInputStream(actual_data); } // end getBinaryStream public long position(byte[] pattern, long start) throws SQLException { logger.warn("position() function is not implemented for MultipartDataValue"); throw new SQLException("function not implemented"); } // end position public long position(Blob pattern, long start) throws SQLException { return position(pattern.getBytes(0,(int)(pattern.length())),start); } // end position } // end class MultipartDataValue /*-------------------------------------------------------------------------------- * Internal class representing a request parameter *-------------------------------------------------------------------------------- */ class MultipartParameter { private MimeBodyPart part; // the actual body part data private String name; // the parameter name private String filename; // the filename private MultipartDataValue cached_value = null; public MultipartParameter(MimeBodyPart part) throws MessagingException { this.part = part; // save part reference // Parse the Content-Disposition header. String[] cdstr = part.getHeader("Content-Disposition"); ContentDisposition cdisp = new ContentDisposition(cdstr[0]); name = cdisp.getParameter("name"); filename = cdisp.getParameter("filename"); if (filename!=null) { // Strip off everything but the base filename, if the browser happened to pass that. int sep = filename.lastIndexOf('\\'); if (sep>=0) filename = filename.substring(sep+1); sep = filename.lastIndexOf('/'); if (sep>=0) filename = filename.substring(sep+1); } // end if if (logger.isDebugEnabled()) { // tell us what kind of parameter we have if (filename!=null) logger.debug("new file parameter \"" + name + "\" defined (filename = " + filename + ")"); else logger.debug("new text parameter \"" + name + "\" defined"); } // end if } // end constructor public String getName() { return name; } // end getName public boolean isFile() { return (filename!=null); } // end isFile public String getValue() { if (filename!=null) return filename; // "value" for file parts is the filename try { // Retrieve the part's actual content and convert it to a String. (Since non-file // fields are of type text/plain, the Object "val" should actually be a String, in // which case the toString() call is actually a no-op. But this is safe.) Object val = part.getContent(); return val.toString(); } // end try catch (Exception e) { // turn any exception returns here into null returns logger.warn("parameter getValue() method threw a " + e.getClass().getName(),e); return null; } // end catch } // end getValue public String getContentType() { try { // pass through to the interior part return part.getContentType(); } // end try catch (Exception e) { // just dump a null on error logger.warn("parameter getContentType() method threw a " + e.getClass().getName(),e); return null; } // end catch } // end getContentType public int getSize() { try { // pass through to the interior part return part.getSize(); } // end try catch (Exception e) { // just dump a -1 on error logger.warn("parameter getSize() method threw a " + e.getClass().getName(),e); return -1; } // end catch } // end getSize public MultipartDataValue getContent() throws ServletMultipartException { if (filename==null) return null; // not a file parameter if (cached_value==null) { // we don't have the value cached yet if (logger.isDebugEnabled()) logger.debug("getting MultipartDataValue for parameter \"" + name + "\""); try { // extract the value cached_value = new MultipartDataValue(part); } // end try catch (MessagingException me) { // translate exception here logger.error("MIME parse error getting data content: " + me.getMessage(),me); throw new ServletMultipartException("Error getting data content: " + me.getMessage(),me); } // end catch catch (IOException ioe) { // translate exception here logger.error("IO error getting data: " + ioe.getMessage(),ioe); throw new ServletMultipartException("Error getting data content: " + ioe.getMessage(),ioe); } // end catch } // end if return cached_value; } // end getContent } // end class MultipartParameter /*-------------------------------------------------------------------------------- * Static data members *-------------------------------------------------------------------------------- */ private static Category logger = Category.getInstance(ServletMultipartHandler.class.getName()); /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- */ private MimeMultipart multipart; // holds all the multipart data private Hashtable param_byname; // parameters by name private Vector param_order; // parameters in order /*-------------------------------------------------------------------------------- * Constructor *-------------------------------------------------------------------------------- */ public ServletMultipartHandler(ServletRequest request) throws ServletMultipartException { if (logger.isDebugEnabled()) logger.debug("creating new ServletMultipartHandler"); if (!canHandle(request)) { // we can't handle ordinary requests, just multipart/form-data ones logger.error("this request is not of type multipart/form-data"); throw new ServletMultipartException("not a multipart/form-data request"); } // end if try { // build the MimeMultipart based on the ServletDataSource multipart = new MimeMultipart(new ServletDataSource(request)); int count = multipart.getCount(); if (logger.isDebugEnabled()) logger.debug("retrieved " + count + " parameter(s) from request"); // allocate the multipart parameters and slot them into our arrays param_byname = new Hashtable(); param_order = new Vector(); for (int i=0; itrue if the given ServletRequest can be handled by * the ServletMultipartHandler, false if not. * * @param request The ServletRequest to be checked. * @return true if the given ServletRequest can be handled by * the ServletMultipartHandler, false if not. */ public static boolean canHandle(ServletRequest request) { String ctype = request.getContentType(); return (ctype.startsWith("multipart/form-data")); } // end canHandle /*-------------------------------------------------------------------------------- * External operations *-------------------------------------------------------------------------------- */ public Enumeration getNames() { Vector tmp_vector = new Vector(); Enumeration enum = param_order.elements(); while (enum.hasMoreElements()) { // add each name to the temporary vector MultipartParameter parm = (MultipartParameter)(enum.nextElement()); tmp_vector.addElement(parm.getName()); } // end while return tmp_vector.elements(); // and enumerate it } // end getNames public boolean isFileParam(String name) { MultipartParameter parm = (MultipartParameter)(param_byname.get(name)); if (parm==null) { // no such parameter! logger.warn("parameter \"" + name + "\" not found"); return false; } // end if else return parm.isFile(); } // end isFileParam public String getValue(String name) { MultipartParameter parm = (MultipartParameter)(param_byname.get(name)); if (parm==null) { // no such parameter! logger.warn("parameter \"" + name + "\" not found"); return null; } // end if else return parm.getValue(); } // end getValue public String getContentType(String name) { MultipartParameter parm = (MultipartParameter)(param_byname.get(name)); if (parm==null) { // no such parameter! logger.warn("parameter \"" + name + "\" not found"); return null; } // end if else return parm.getContentType(); } // end getContentType public int getContentSize(String name) { MultipartParameter parm = (MultipartParameter)(param_byname.get(name)); if (parm==null) { // no such parameter! logger.warn("parameter \"" + name + "\" not found"); return -1; } // end if else return parm.getSize(); } // end getContentSize public Blob getFileContentBlob(String name) throws ServletMultipartException { MultipartParameter parm = (MultipartParameter)(param_byname.get(name)); if (parm==null) { // no such parameter! logger.warn("parameter \"" + name + "\" not found"); return null; } // end if else return parm.getContent(); } // end getFileContentBlob public InputStream getFileContentStream(String name) throws ServletMultipartException { MultipartParameter parm = (MultipartParameter)(param_byname.get(name)); if (parm==null) { // no such parameter! logger.warn("parameter \"" + name + "\" not found"); return null; } // end if MultipartDataValue val = parm.getContent(); return val.getBinaryStream(); } // end getFileContentStream } // end class ServletMultipartHandler