490 lines
14 KiB
Java
490 lines
14 KiB
Java
/*
|
|
* 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 <http://www.mozilla.org/MPL/>.
|
|
*
|
|
* 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 <erbo@silcom.com>,
|
|
* 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; i<count; i++)
|
|
{ // create the MultipartParameter structures and stash them for later use
|
|
MultipartParameter parm = new MultipartParameter((MimeBodyPart)(multipart.getBodyPart(i)));
|
|
param_byname.put(parm.getName(),parm);
|
|
param_order.addElement(parm);
|
|
|
|
} // end count
|
|
|
|
} // end try
|
|
catch (MessagingException me)
|
|
{ // translate a MessagingException into the nicer ServletMultipartException
|
|
logger.error("multipart parsing failed: " + me.getMessage(),me);
|
|
throw new ServletMultipartException("Error parsing request data: " + me.getMessage(),me);
|
|
|
|
} // end catch
|
|
|
|
} // end constructor
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* External static operations
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* Returns <CODE>true</CODE> if the given <CODE>ServletRequest</CODE> can be handled by
|
|
* the <CODE>ServletMultipartHandler</CODE>, <CODE>false</CODE> if not.
|
|
*
|
|
* @param request The <CODE>ServletRequest</CODE> to be checked.
|
|
* @return <CODE>true</CODE> if the given <CODE>ServletRequest</CODE> can be handled by
|
|
* the <CODE>ServletMultipartHandler</CODE>, <CODE>false</CODE> 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
|