/*
* 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