* Switch to Jakarta Commons FileUpload for handling file uploads.
* Introduce uploadSoftfail setting in apps.properties that allows file upload errors to be cought by checking req.data.helma_upload_error
This commit is contained in:
parent
56c1973ca5
commit
878b7ee06d
13 changed files with 77 additions and 1586 deletions
|
@ -27,6 +27,11 @@ import java.util.*;
|
|||
import javax.servlet.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
import org.apache.commons.fileupload.FileUpload;
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.DiskFileUpload;
|
||||
import org.apache.commons.fileupload.FileUploadBase;
|
||||
|
||||
/**
|
||||
* This is an abstract Hop servlet adapter. This class communicates with hop applications
|
||||
* via RMI. Subclasses are either one servlet per app, or one servlet that handles multiple apps
|
||||
|
@ -43,7 +48,7 @@ public abstract class AbstractServletClient extends HttpServlet {
|
|||
String hopUrl;
|
||||
|
||||
// limit to HTTP uploads in kB
|
||||
int uploadLimit = 4096;
|
||||
int uploadLimit = 1024;
|
||||
|
||||
// cookie domain to use
|
||||
String cookieDomain;
|
||||
|
@ -61,6 +66,10 @@ public abstract class AbstractServletClient extends HttpServlet {
|
|||
// enable debug output
|
||||
boolean debug;
|
||||
|
||||
// soft fail on file upload errors by setting flag "helma_upload_error" in RequestTrans
|
||||
// if fals, an error response is written to the client immediately without entering helma
|
||||
boolean uploadSoftfail = false;
|
||||
|
||||
/**
|
||||
* Init this servlet.
|
||||
*
|
||||
|
@ -76,10 +85,13 @@ public abstract class AbstractServletClient extends HttpServlet {
|
|||
try {
|
||||
uploadLimit = (upstr == null) ? 1024 : Integer.parseInt(upstr);
|
||||
} catch (NumberFormatException x) {
|
||||
System.err.println("Bad format for uploadLimit: " + upstr);
|
||||
System.err.println("Bad number format for uploadLimit: " + upstr);
|
||||
uploadLimit = 1024;
|
||||
}
|
||||
|
||||
// soft fail mode for upload errors
|
||||
uploadSoftfail = ("true".equalsIgnoreCase(init.getInitParameter("uploadSoftfail")));
|
||||
|
||||
// get cookie domain
|
||||
cookieDomain = init.getInitParameter("cookieDomain");
|
||||
if (cookieDomain != null) {
|
||||
|
@ -150,36 +162,55 @@ public abstract class AbstractServletClient extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
// check for MIME file uploads
|
||||
String contentType = request.getContentType();
|
||||
|
||||
if ((contentType != null) &&
|
||||
(contentType.indexOf("multipart/form-data") == 0)) {
|
||||
if (FileUpload.isMultipartContent(request)) {
|
||||
// File Upload
|
||||
try {
|
||||
FileUpload upload = getUpload(request, encoding);
|
||||
DiskFileUpload upload = new DiskFileUpload();
|
||||
|
||||
if (upload != null) {
|
||||
Hashtable parts = upload.getParts();
|
||||
upload.setSizeMax(uploadLimit * 1024);
|
||||
upload.setHeaderEncoding(encoding);
|
||||
|
||||
for (Enumeration e = parts.keys(); e.hasMoreElements();) {
|
||||
String nextKey = (String) e.nextElement();
|
||||
Object nextPart = parts.get(nextKey);
|
||||
List uploads = upload.parseRequest(request);
|
||||
Iterator it = uploads.iterator();
|
||||
|
||||
if (nextPart instanceof List) {
|
||||
reqtrans.set(nextKey, ((List) nextPart).get(0));
|
||||
reqtrans.set(nextKey+"_array", ((List) nextPart).toArray());
|
||||
} else {
|
||||
reqtrans.set(nextKey, nextPart);
|
||||
}
|
||||
while (it.hasNext()) {
|
||||
FileItem item = (FileItem) it.next();
|
||||
// TODO: set fieldname_array if multiple values for one fieldname
|
||||
String name = item.getFieldName();
|
||||
Object value = null;
|
||||
// check if this is an ordinary HTML form element or a file upload
|
||||
if (item.isFormField()) {
|
||||
value = item.getString(encoding);
|
||||
} else {
|
||||
value = new MimePart(item.getName(),
|
||||
item.get(),
|
||||
item.getContentType());
|
||||
}
|
||||
// if multiple values exist for this name, append to _array
|
||||
if (reqtrans.get(name) != null) {
|
||||
appendFormValue(reqtrans, name, value);
|
||||
} else {
|
||||
reqtrans.set(name, value);
|
||||
}
|
||||
}
|
||||
} catch (Exception upx) {
|
||||
sendError(response, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE,
|
||||
"Sorry, upload size exceeds limit of " + uploadLimit +
|
||||
"kB.");
|
||||
|
||||
return;
|
||||
} catch (Exception upx) {
|
||||
System.err.println("Error in file upload: " + upx);
|
||||
if (uploadSoftfail) {
|
||||
String msg = upx.getMessage();
|
||||
if (msg == null || msg.length() == 0) {
|
||||
msg = upx.toString();
|
||||
}
|
||||
reqtrans.set("helma_upload_error", msg);
|
||||
} else if (upx instanceof FileUploadBase.SizeLimitExceededException) {
|
||||
sendError(response, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE,
|
||||
"File upload size exceeds limit of " + uploadLimit + "kB");
|
||||
return;
|
||||
} else {
|
||||
sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
"Error in file upload: " + upx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,24 +518,29 @@ public abstract class AbstractServletClient extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
FileUpload getUpload(HttpServletRequest request, String encoding) throws Exception {
|
||||
int contentLength = request.getContentLength();
|
||||
BufferedInputStream in = new BufferedInputStream(request.getInputStream());
|
||||
|
||||
if (contentLength > (uploadLimit * 1024)) {
|
||||
throw new RuntimeException("Upload exceeds limit of " + uploadLimit + " kb.");
|
||||
/**
|
||||
* Used to build the form value array when a multipart (file upload) form has
|
||||
* multiple values for one form element name.
|
||||
*
|
||||
* @param reqtrans
|
||||
* @param name
|
||||
* @param value
|
||||
*/
|
||||
private void appendFormValue(RequestTrans reqtrans, String name, Object value) {
|
||||
String arrayName = name + "_array";
|
||||
try {
|
||||
Object[] values = (Object[]) reqtrans.get(arrayName);
|
||||
if (values == null) {
|
||||
reqtrans.set(arrayName, new Object[] {reqtrans.get(name), value});
|
||||
} else {
|
||||
Object[] newValues = new Object[values.length + 1];
|
||||
System.arraycopy(values, 0, newValues, 0, values.length);
|
||||
newValues[values.length] = value;
|
||||
reqtrans.set(arrayName, newValues);
|
||||
}
|
||||
} catch (ClassCastException x) {
|
||||
// name_array is defined as something else in the form - don't overwrite it
|
||||
}
|
||||
|
||||
String contentType = request.getContentType();
|
||||
FileUpload upload = new FileUpload(uploadLimit);
|
||||
|
||||
upload.load(in, contentType, contentLength, encoding);
|
||||
|
||||
return upload;
|
||||
}
|
||||
|
||||
Object getUploadPart(FileUpload upload, String name) {
|
||||
return upload.getParts().get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* Helma License Notice
|
||||
*
|
||||
* The contents of this file are subject to the Helma License
|
||||
* Version 2.0 (the "License"). You may not use this file except in
|
||||
* compliance with the License. A copy of the License is available at
|
||||
* http://adele.helma.org/download/helma/license.txt
|
||||
*
|
||||
* Copyright 1998-2003 Helma Software. All Rights Reserved.
|
||||
*
|
||||
* $RCSfile$
|
||||
* $Author$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*/
|
||||
|
||||
package helma.util;
|
||||
|
||||
import helma.util.mime.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Utility class for MIME file uploads via HTTP POST.
|
||||
*/
|
||||
public class FileUpload {
|
||||
public Hashtable parts;
|
||||
int maxKbytes;
|
||||
|
||||
/**
|
||||
* Creates a new FileUpload object.
|
||||
*/
|
||||
public FileUpload() {
|
||||
maxKbytes = 4096;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FileUpload object.
|
||||
*
|
||||
* @param max ...
|
||||
*/
|
||||
public FileUpload(int max) {
|
||||
maxKbytes = max;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @return ...
|
||||
*/
|
||||
public Hashtable getParts() {
|
||||
return parts;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param is ...
|
||||
* @param contentType ...
|
||||
* @param contentLength ...
|
||||
*
|
||||
* @throws Exception ...
|
||||
* @throws MimeParserException ...
|
||||
* @throws IOException ...
|
||||
*/
|
||||
public void load(InputStream is, String contentType, int contentLength, String encoding)
|
||||
throws Exception {
|
||||
parts = new Hashtable();
|
||||
|
||||
String boundary = MimePart.getSubHeader(contentType, "boundary");
|
||||
|
||||
if (boundary == null) {
|
||||
throw new MimeParserException("Error parsing MIME input stream.");
|
||||
}
|
||||
|
||||
if ((maxKbytes > -1) && (contentLength > (maxKbytes * 1024))) {
|
||||
throw new IOException("Size of upload exceeds limit of " + maxKbytes +
|
||||
" kB.");
|
||||
}
|
||||
|
||||
byte[] b = new byte[contentLength];
|
||||
MultipartInputStream in = new MultipartInputStream(new BufferedInputStream(is),
|
||||
boundary.getBytes());
|
||||
|
||||
while (in.nextInputStream()) {
|
||||
MimeParser parser = new MimeParser(in, new MimeHeadersFactory());
|
||||
MimeHeaders headers = (MimeHeaders) parser.parse();
|
||||
|
||||
InputStream bodystream = parser.getInputStream();
|
||||
int read;
|
||||
int count = 0;
|
||||
|
||||
while ((read = bodystream.read(b, count, 4096)) > -1) {
|
||||
count += read;
|
||||
|
||||
if (count == b.length) {
|
||||
byte[] newb = new byte[count + 4096];
|
||||
|
||||
System.arraycopy(b, 0, newb, 0, count);
|
||||
b = newb;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] newb = new byte[count];
|
||||
|
||||
System.arraycopy(b, 0, newb, 0, count);
|
||||
|
||||
String type = headers.getValue("Content-Type");
|
||||
String disposition = headers.getValue("Content-Disposition");
|
||||
String name = MimePart.getSubHeader(disposition, "name");
|
||||
String filename = MimePart.getSubHeader(disposition, "filename");
|
||||
|
||||
if (filename != null) {
|
||||
int sep = filename.lastIndexOf("\\");
|
||||
|
||||
if (sep > -1) {
|
||||
filename = filename.substring(sep + 1);
|
||||
}
|
||||
|
||||
sep = filename.lastIndexOf("/");
|
||||
|
||||
if (sep > -1) {
|
||||
filename = filename.substring(sep + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Object existingValue = parts.get(name);
|
||||
Object newValue = null;
|
||||
|
||||
if (filename != null) {
|
||||
newValue = new MimePart(filename, newb, type);
|
||||
} else {
|
||||
newValue = new String(newb, encoding);
|
||||
}
|
||||
|
||||
if (existingValue == null) {
|
||||
// no previous value, just add new object
|
||||
parts.put(name, newValue);
|
||||
} else if (existingValue instanceof ArrayList) {
|
||||
// already multiple values, add to list
|
||||
((ArrayList) existingValue).add(newValue);
|
||||
} else {
|
||||
// already one value, convert to list
|
||||
ArrayList list = new ArrayList();
|
||||
list.add(existingValue);
|
||||
list.add(newValue);
|
||||
parts.put(name, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
// LanguageTag.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT, INRIA and Keio, 1999
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* This class is used to represent parsed Language tags,
|
||||
* It creates a representation from a string based representation
|
||||
* of the Language tag, as defined in RFC 1766
|
||||
* NOTE, we don't check that languages are defined according to ISO 639
|
||||
*/
|
||||
|
||||
public class LanguageTag implements Serializable, Cloneable {
|
||||
public static int NO_MATCH = -1;
|
||||
public static int MATCH_LANGUAGE = 1;
|
||||
public static int MATCH_SPECIFIC_LANGUAGE = 2;
|
||||
// subtag is not dialect as subtype can be
|
||||
// dialect or country identification or script variation, etc...
|
||||
public static int MATCH_SUBTAG = 3;
|
||||
public static int MATCH_SPECIFIC_SUBTAG = 4;
|
||||
|
||||
/**
|
||||
* String representation of the language
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String language = null ;
|
||||
/**
|
||||
* String representation of subtag
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String subtag = null ;
|
||||
|
||||
/**
|
||||
* external form of this language tag
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String external = null ;
|
||||
|
||||
/**
|
||||
* How good the given LanguageTag matches the receiver of the method ?
|
||||
* This method returns a matching level among:
|
||||
* <dl>
|
||||
* <dt>NO_MATCH<dd>Language not matching,</dd>
|
||||
* <dt>MATCH_LANGUAGE<dd>Languages match roughly (with *),</dd>
|
||||
* <dt>MATCH_SPECIFIC_LANGUAGE<dd>Languages match exactly,</dd>
|
||||
* <dt>MATCH_SUBTAG<dd>Languages match, subtags matches roughly</dd>
|
||||
* <dt>MATCH_SPECIFIC_SUBAG<dd>Languages match, subtag matches exactly</dd>
|
||||
* </dl>
|
||||
* The matches are ranked from worst match to best match, a simple
|
||||
* Max ( match[i], matched) will give the best match.
|
||||
* @param other The other LanguageTag to match against ourself.
|
||||
*/
|
||||
|
||||
public int match (LanguageTag other) {
|
||||
int match = NO_MATCH;
|
||||
// match types:
|
||||
if ( language.equals("*") || other.language.equals("*") ) {
|
||||
match = MATCH_LANGUAGE;
|
||||
} else if ( ! language.equalsIgnoreCase(other.language) ) {
|
||||
return NO_MATCH ;
|
||||
} else {
|
||||
match = MATCH_SPECIFIC_LANGUAGE;
|
||||
}
|
||||
// match subtypes:
|
||||
if ((subtag == null) || (other.subtag == null))
|
||||
return match;
|
||||
if ( subtag.equals("*") || other.subtag.equals("*") ) {
|
||||
match = MATCH_SUBTAG ;
|
||||
} else if ( ! subtag.equalsIgnoreCase(other.subtag) ) {
|
||||
return NO_MATCH;
|
||||
} else {
|
||||
match = MATCH_SPECIFIC_SUBTAG;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* A printable representation of this LanguageTag.
|
||||
* The printed representation is guaranteed to be parseable by the
|
||||
* String constructor.
|
||||
*/
|
||||
|
||||
public String toString () {
|
||||
if ( external == null ) {
|
||||
if (subtag != null) {
|
||||
external = language + "-" + subtag;
|
||||
} else {
|
||||
external = language;
|
||||
}
|
||||
}
|
||||
return external ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language
|
||||
* @return The language, encoded as a String.
|
||||
*/
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subtag
|
||||
* @return The subtag, encoded as a string
|
||||
*/
|
||||
|
||||
public String getSubtag() {
|
||||
return language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a Language tag from a spec
|
||||
* @param spec A string representing a LangateTag
|
||||
*/
|
||||
public LanguageTag(String spec) {
|
||||
int strl = spec.length() ;
|
||||
int start = 0, look = -1 ;
|
||||
// skip leading/trailing blanks:
|
||||
while ((start < strl) && (spec.charAt (start)) <= ' ')
|
||||
start++ ;
|
||||
while ((strl > start) && (spec.charAt (strl-1) <= ' '))
|
||||
strl-- ;
|
||||
// get the type:
|
||||
StringBuffer sb = new StringBuffer () ;
|
||||
while ((start < strl) && ((look = spec.charAt(start)) != '-')
|
||||
&& ((look = spec.charAt(start)) != ';')) {
|
||||
sb.append ((char) look) ;
|
||||
start++ ;
|
||||
}
|
||||
this.language = sb.toString() ;
|
||||
if ( look == '-' ) {
|
||||
start++ ;
|
||||
sb.setLength(0) ;
|
||||
while ((start < strl)
|
||||
&& ((look = spec.charAt(start)) > ' ') && (look != ';')) {
|
||||
sb.append ((char) look) ;
|
||||
start++ ;
|
||||
}
|
||||
this.subtag = sb.toString() ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* construct directly a language tag
|
||||
* it NEEDS both language and subtype parameters
|
||||
*/
|
||||
|
||||
public LanguageTag(String language, String subtag) {
|
||||
this.language = language;
|
||||
this.subtag = subtag;
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
// MimeHeaderHolder.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface MimeHeaderHolder {
|
||||
|
||||
/**
|
||||
* A new header has been parsed.
|
||||
* @param name The name of the encountered header.
|
||||
* @param buf The byte buffer containing the value.
|
||||
* @param off Offset of the header value in the above buffer.
|
||||
* @param len Length of the value in the above header.
|
||||
* @exception MimeParserException if the parsing failed
|
||||
*/
|
||||
|
||||
public void notifyHeader(String name, byte buf[], int off, int len)
|
||||
throws MimeParserException;
|
||||
|
||||
/**
|
||||
* The parsing is now about to start, take any appropriate action.
|
||||
* This hook can return a <strong>true</strong> boolean value to enforce
|
||||
* the MIME parser into transparent mode (eg the parser will <em>not</em>
|
||||
* try to parse any headers.
|
||||
* <p>This hack is primarily defined for HTTP/0.9 support, it might
|
||||
* also be usefull for other hacks.
|
||||
* @param parser The Mime parser.
|
||||
* @return A boolean <strong>true</strong> if the MimeParser shouldn't
|
||||
* continue the parsing, <strong>false</strong> otherwise.
|
||||
* @exception MimeParserException if the parsing failed
|
||||
* @exception IOException if an IO error occurs.
|
||||
*/
|
||||
|
||||
public boolean notifyBeginParsing(MimeParser parser)
|
||||
throws MimeParserException, IOException;
|
||||
|
||||
/**
|
||||
* All the headers have been parsed, take any appropriate actions.
|
||||
* @param parser The Mime parser.
|
||||
* @exception MimeParserException if the parsing failed
|
||||
* @exception IOException if an IO error occurs.
|
||||
*/
|
||||
|
||||
public void notifyEndParsing(MimeParser parser)
|
||||
throws MimeParserException, IOException;
|
||||
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
// MimeHeaders.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The most stupid MIME header holder.
|
||||
* This class uses a hashtable mapping header names (as String), to header
|
||||
* values (as String). Header names are lowered before entering the hashtable.
|
||||
*/
|
||||
|
||||
public class MimeHeaders implements MimeHeaderHolder {
|
||||
Hashtable headers = null;
|
||||
MimeParser parser = null;
|
||||
|
||||
/**
|
||||
* A new header has been parsed.
|
||||
* @param name The name of the encountered header.
|
||||
* @param buf The byte buffer containing the value.
|
||||
* @param off Offset of the header value in the above buffer.
|
||||
* @param len Length of the value in the above header.
|
||||
* @exception MimeParserException if the parsing failed
|
||||
*/
|
||||
|
||||
public void notifyHeader(String name, byte buf[], int off, int len)
|
||||
throws MimeParserException
|
||||
{
|
||||
String lname = name.toLowerCase();
|
||||
String oldval = null;
|
||||
if ( headers == null ) {
|
||||
headers = new Hashtable(5);
|
||||
} else {
|
||||
oldval = (String) headers.get(lname);
|
||||
}
|
||||
String newval = ((oldval != null)
|
||||
? oldval + "," + new String(buf, 0, off, len)
|
||||
: new String(buf, 0, off, len));
|
||||
headers.put(lname, newval);
|
||||
}
|
||||
|
||||
/**
|
||||
* The parsing is now about to start, take any appropriate action.
|
||||
* This hook can return a <strong>true</strong> boolean value to enforce
|
||||
* the MIME parser into transparent mode (eg the parser will <em>not</em>
|
||||
* try to parse any headers.
|
||||
* <p>This hack is primarily defined for HTTP/0.9 support, it might
|
||||
* also be usefull for other hacks.
|
||||
* @param parser The Mime parser.
|
||||
* @return A boolean <strong>true</strong> if the MimeParser shouldn't
|
||||
* continue the parsing, <strong>false</strong> otherwise.
|
||||
* @exception IOException if an IO error occurs.
|
||||
*/
|
||||
|
||||
public boolean notifyBeginParsing(MimeParser parser)
|
||||
throws IOException
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* All the headers have been parsed, take any appropriate actions.
|
||||
* @param parser The Mime parser.
|
||||
* @exception IOException if an IO error occurs.
|
||||
*/
|
||||
|
||||
public void notifyEndParsing(MimeParser parser)
|
||||
throws IOException
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a header value.
|
||||
* @param name The header name.
|
||||
* @param value The header value.
|
||||
*/
|
||||
|
||||
public void setValue(String name, String value) {
|
||||
if ( headers == null )
|
||||
headers = new Hashtable(5);
|
||||
headers.put(name.toLowerCase(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreive a header value.
|
||||
* @param name The name of the header.
|
||||
* @return The value for this header, or <strong>null</strong> if
|
||||
* undefined.
|
||||
*/
|
||||
|
||||
public String getValue(String name) {
|
||||
return ((headers != null)
|
||||
? (String) headers.get(name.toLowerCase())
|
||||
: null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate the headers defined by the holder.
|
||||
* @return A enumeration of header names, or <strong>null</strong> if no
|
||||
* header is defined.
|
||||
*/
|
||||
|
||||
public Enumeration enumerateHeaders() {
|
||||
if ( headers == null )
|
||||
return null;
|
||||
return headers.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entity stream attached to these headers, if any.
|
||||
* @return An InputStream instance, or <strong>null</strong> if no
|
||||
* entity available.
|
||||
*/
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return ((parser != null) ? parser.getInputStream() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump all headers to the given stream.
|
||||
* @param out The stream to dump to.
|
||||
*/
|
||||
|
||||
public void dump(PrintStream out) {
|
||||
Enumeration names = enumerateHeaders();
|
||||
if ( names != null ) {
|
||||
while (names.hasMoreElements()) {
|
||||
String name = (String) names.nextElement();
|
||||
out.println(name+": "+headers.get(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MimeHeaders(MimeParser parser) {
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
public MimeHeaders() {
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// MimeHeadersFactory.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
/**
|
||||
* A Mime header factory, that will build instances of the MimeHeaders class
|
||||
* to hold MIME headers.
|
||||
*/
|
||||
|
||||
public class MimeHeadersFactory implements MimeParserFactory {
|
||||
|
||||
/**
|
||||
* Create a new header holder to hold the parser's result.
|
||||
* @param parser The parser that has something to parse.
|
||||
* @return A MimeParserHandler compliant object.
|
||||
*/
|
||||
|
||||
public MimeHeaderHolder createHeaderHolder(MimeParser parser) {
|
||||
return new MimeHeaders(parser);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,228 +0,0 @@
|
|||
// MimeParser.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.* ;
|
||||
|
||||
/**
|
||||
* The MimeParser class parses an input MIME stream.
|
||||
*/
|
||||
|
||||
public class MimeParser {
|
||||
protected int ch = -1 ;
|
||||
protected InputStream input = null ;
|
||||
protected byte buffer[] = new byte[128] ;
|
||||
protected int bsize = 0 ;
|
||||
|
||||
/**
|
||||
* The factory used to create new MIME header holders.
|
||||
*/
|
||||
protected MimeParserFactory factory = null ;
|
||||
|
||||
protected void expect (int car)
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
if ( car != ch ) {
|
||||
String sc = (new Character((char) car)).toString() ;
|
||||
String se = (new Character((char) ch)).toString() ;
|
||||
throw new MimeParserException ("expecting "
|
||||
+ sc + "("+car+")"
|
||||
+ " got "
|
||||
+ se + "("+ch+")\n"
|
||||
+ "context: "
|
||||
+ new String (buffer, 0, 0, bsize)
|
||||
+ "\n") ;
|
||||
}
|
||||
ch = input.read() ;
|
||||
}
|
||||
|
||||
protected void skipSpaces ()
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
while ( (ch == ' ') || (ch == '\t') )
|
||||
ch = input.read() ;
|
||||
}
|
||||
|
||||
protected final void append (int c) {
|
||||
if ( bsize+1 >= buffer.length ) {
|
||||
byte nb[] = new byte[buffer.length*2] ;
|
||||
System.arraycopy (buffer, 0, nb, 0, buffer.length) ;
|
||||
buffer = nb ;
|
||||
}
|
||||
buffer[bsize++] = (byte) c ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the header name:
|
||||
*/
|
||||
|
||||
protected String parse822HeaderName ()
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
bsize = 0 ;
|
||||
while ( (ch >= 32) && (ch != ':') ) {
|
||||
append ((char) ch) ;
|
||||
ch = input.read() ;
|
||||
}
|
||||
expect (':') ;
|
||||
if ( bsize <= 0 )
|
||||
throw new MimeParserException ("expected a header name.") ;
|
||||
return new String (buffer, 0, 0, bsize) ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the header body, still trying to be 822 compliant *and* HTTP
|
||||
* robust, which is unfortunatelly a contrdiction.
|
||||
*/
|
||||
|
||||
protected void parse822HeaderBody ()
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
bsize = 0 ;
|
||||
skipSpaces () ;
|
||||
loop:
|
||||
while ( true ) {
|
||||
switch (ch) {
|
||||
case -1:
|
||||
break loop ;
|
||||
case '\r':
|
||||
if ( (ch = input.read()) != '\n' ) {
|
||||
append ('\r') ;
|
||||
continue ;
|
||||
}
|
||||
// no break intentional
|
||||
case '\n':
|
||||
// do as if '\r' had been received. This defeats 822, but
|
||||
// makes HTTP more "robust". I wish HTTP were a binary
|
||||
// protocol.
|
||||
switch (ch = input.read()) {
|
||||
case ' ': case '\t':
|
||||
do {
|
||||
ch = input.read () ;
|
||||
} while ((ch == ' ') || (ch == '\t')) ;
|
||||
append(ch);
|
||||
break ;
|
||||
default:
|
||||
break loop ;
|
||||
}
|
||||
break ;
|
||||
default:
|
||||
append ((char) ch) ;
|
||||
break ;
|
||||
}
|
||||
ch = input.read() ;
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the given input stream for an HTTP 1.1 token.
|
||||
*/
|
||||
|
||||
protected String parseToken (boolean lower)
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
bsize = 0 ;
|
||||
while ( true ) {
|
||||
switch ( ch ) {
|
||||
// CTLs
|
||||
case -1:
|
||||
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
|
||||
case 8: case 9: case 10: case 11: case 12: case 13: case 14:
|
||||
case 15: case 16: case 17: case 18: case 19: case 20: case 21:
|
||||
case 22: case 23: case 24: case 25: case 26: case 27: case 28:
|
||||
case 29: case 30: case 31:
|
||||
// tspecials
|
||||
case '(': case ')': case '<': case '>': case '@':
|
||||
case ',': case ';': case ':': case '\\': case '\"':
|
||||
case '/': case '[': case ']': case '?': case '=':
|
||||
case '{': case '}': case ' ':
|
||||
return new String (buffer, 0, 0, bsize) ;
|
||||
default:
|
||||
append ((char) (lower
|
||||
? Character.toLowerCase((char) ch)
|
||||
: ch)) ;
|
||||
}
|
||||
ch = input.read() ;
|
||||
}
|
||||
}
|
||||
|
||||
protected void parse822Headers(MimeHeaderHolder msg)
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
while ( true ) {
|
||||
if ( ch == '\r' ) {
|
||||
if ( (ch = input.read()) == '\n' )
|
||||
return ;
|
||||
} else if ( ch == '\n' ) {
|
||||
return ;
|
||||
}
|
||||
String name = parse822HeaderName () ;
|
||||
skipSpaces() ;
|
||||
parse822HeaderBody () ;
|
||||
msg.notifyHeader(name, buffer, 0, bsize);
|
||||
}
|
||||
}
|
||||
|
||||
public MimeHeaderHolder parse()
|
||||
throws MimeParserException, IOException
|
||||
{
|
||||
MimeHeaderHolder msg = factory.createHeaderHolder(this);
|
||||
ch = input.read() ;
|
||||
cached = true ;
|
||||
if ( ! msg.notifyBeginParsing(this) ) {
|
||||
if ( ! cached )
|
||||
ch = input.read();
|
||||
parse822Headers (msg) ;
|
||||
}
|
||||
msg.notifyEndParsing(this);
|
||||
return msg;
|
||||
}
|
||||
|
||||
boolean cached = false ;
|
||||
|
||||
public int read()
|
||||
throws IOException
|
||||
{
|
||||
if ( cached )
|
||||
cached = false;
|
||||
else
|
||||
ch = input.read();
|
||||
return ch;
|
||||
}
|
||||
|
||||
public void unread(int ch) {
|
||||
if ( cached )
|
||||
throw new RuntimeException("cannot unread more then once !");
|
||||
this.ch = ch;
|
||||
cached = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message body, as an input stream.
|
||||
* @return The input stream used by the parser to get data, after
|
||||
* a call to <code>parse</code>, this input stream contains exactly
|
||||
* the body of the message.
|
||||
*/
|
||||
|
||||
public InputStream getInputStream () {
|
||||
return input ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the MIMEParser class.
|
||||
* @param input The input stream to be parsed as a MIME stream.
|
||||
* @param factory The factory used to create MIME header holders.
|
||||
*/
|
||||
|
||||
public MimeParser (InputStream input, MimeParserFactory factory) {
|
||||
this.input = input ;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// MimeParserException.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
public class MimeParserException extends Exception {
|
||||
|
||||
public MimeParserException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// MimeParserFactory.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
/**
|
||||
* This class is used by the MimeParser, to create new MIME message holders.
|
||||
* Each MIME parse instances is custmozied wit hits own factory, which it
|
||||
* will use to create MIME header holders.
|
||||
*/
|
||||
|
||||
public interface MimeParserFactory {
|
||||
|
||||
/**
|
||||
* Create a new header holder to hold the parser's result.
|
||||
* @param parser The parser that has something to parse.
|
||||
* @return A MimeParserHandler compliant object.
|
||||
*/
|
||||
|
||||
abstract public MimeHeaderHolder createHeaderHolder(MimeParser parser);
|
||||
|
||||
}
|
|
@ -1,373 +0,0 @@
|
|||
// MimeType.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* This class is used to represent parsed MIME types.
|
||||
* It creates this representation from a string based representation of
|
||||
* the MIME type, as defined in the RFC 1345.
|
||||
*/
|
||||
|
||||
public class MimeType implements Serializable, Cloneable {
|
||||
/**
|
||||
* List of well known MIME types:
|
||||
*/
|
||||
public static MimeType TEXT_HTML = null ;
|
||||
public static MimeType APPLICATION_POSTSCRIPT = null ;
|
||||
public static MimeType TEXT_PLAIN = null ;
|
||||
public static MimeType APPLICATION_X_WWW_FORM_URLENCODED = null ;
|
||||
public static MimeType MULTIPART_FORM_DATA = null ;
|
||||
public static MimeType APPLICATION_X_JAVA_AGENT = null ;
|
||||
public static MimeType MESSAGE_HTTP = null ;
|
||||
public static MimeType TEXT_CSS = null ;
|
||||
public static MimeType TEXT = null ;
|
||||
|
||||
static {
|
||||
try {
|
||||
TEXT_HTML
|
||||
= new MimeType("text/html");
|
||||
APPLICATION_POSTSCRIPT
|
||||
= new MimeType ("application/postscript") ;
|
||||
TEXT_PLAIN
|
||||
= new MimeType("text/plain") ;
|
||||
APPLICATION_X_WWW_FORM_URLENCODED
|
||||
= new MimeType("application/x-www-form-urlencoded") ;
|
||||
MULTIPART_FORM_DATA
|
||||
= new MimeType("multipart/form-data") ;
|
||||
APPLICATION_X_JAVA_AGENT
|
||||
= new MimeType("application/x-java-agent") ;
|
||||
MESSAGE_HTTP
|
||||
= new MimeType("message/http");
|
||||
TEXT_CSS
|
||||
= new MimeType("text/css");
|
||||
TEXT
|
||||
= new MimeType("text/*");
|
||||
} catch (MimeTypeFormatException e) {
|
||||
System.out.println ("httpd.MimeType: invalid static init.") ;
|
||||
System.exit (1) ;
|
||||
}
|
||||
}
|
||||
|
||||
public final static int NO_MATCH = -1 ;
|
||||
public final static int MATCH_TYPE = 1 ;
|
||||
public final static int MATCH_SPECIFIC_TYPE = 2 ;
|
||||
public final static int MATCH_SUBTYPE = 3 ;
|
||||
public final static int MATCH_SPECIFIC_SUBTYPE = 4 ;
|
||||
|
||||
/**
|
||||
* String representation of type
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String type = null ;
|
||||
/**
|
||||
* String representation of subtype
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String subtype = null ;
|
||||
/**
|
||||
* parameter names
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String pnames[] = null;
|
||||
/**
|
||||
* parameter values
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String pvalues[] = null;
|
||||
/**
|
||||
* external form of this mime type
|
||||
*
|
||||
* @serial
|
||||
*/
|
||||
protected String external = null ;
|
||||
|
||||
/**
|
||||
* How good the given MimeType matches the receiver of the method ?
|
||||
* This method returns a matching level among:
|
||||
* <dl>
|
||||
* <dt>NO_MATCH<dd>Types not matching,</dd>
|
||||
* <dt>MATCH_TYPE<dd>Types match,</dd>
|
||||
* <dt>MATCH_SPECIFIC_TYPE<dd>Types match exactly,</dd>
|
||||
* <dt>MATCH_SUBTYPE<dd>Types match, subtypes matches too</dd>
|
||||
* <dt>MATCH_SPECIFIC_SUBTYPE<dd>Types match, subtypes matches exactly</dd>
|
||||
* </dl>
|
||||
* The matches are ranked from worst match to best match, a simple
|
||||
* Max ( match[i], matched) will give the best match.
|
||||
* @param other The other MimeType to match against ourself.
|
||||
*/
|
||||
|
||||
public int match (MimeType other) {
|
||||
int match = NO_MATCH;
|
||||
// match types:
|
||||
if ( type.equals("*") || other.type.equals("*") ) {
|
||||
return MATCH_TYPE;
|
||||
} else if ( ! type.equals (other.type) ) {
|
||||
return NO_MATCH ;
|
||||
} else {
|
||||
match = MATCH_SPECIFIC_TYPE;
|
||||
}
|
||||
// match subtypes:
|
||||
if ( subtype.equals("*") || other.subtype.equals("*") ) {
|
||||
match = MATCH_SUBTYPE ;
|
||||
} else if ( ! subtype.equals (other.subtype) ) {
|
||||
return NO_MATCH;
|
||||
} else {
|
||||
match = MATCH_SPECIFIC_SUBTYPE;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* A printable representation of this MimeType.
|
||||
* The printed representation is guaranteed to be parseable by the
|
||||
* String constructor.
|
||||
*/
|
||||
|
||||
public String toString () {
|
||||
if ( external == null ) {
|
||||
StringBuffer sb = new StringBuffer (type) ;
|
||||
sb.append((char) '/') ;
|
||||
sb.append (subtype) ;
|
||||
if ( pnames != null ) {
|
||||
for (int i = 0 ; i < pnames.length; i++) {
|
||||
sb.append(';');
|
||||
sb.append(pnames[i]);
|
||||
if ( pvalues[i] != null ) {
|
||||
sb.append('=');
|
||||
sb.append(pvalues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
external = sb.toString() ;
|
||||
}
|
||||
return external ;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does this MIME type has some value for the given parameter ?
|
||||
* @param name The parameter to check.
|
||||
* @return <strong>True</strong> if this parameter has a value, false
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean hasParameter (String name) {
|
||||
if ( pnames != null ) {
|
||||
for (int i = 0 ; i < pnames.length ; i++) {
|
||||
if ( pnames[i].equals(name) )
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
return false ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the major type of this mime type.
|
||||
* @return The major type, encoded as a String.
|
||||
*/
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minor type (subtype) of this mime type.
|
||||
* @return The minor or subtype encoded as a String.
|
||||
*/
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a mime type parameter value.
|
||||
* @param name The parameter whose value is to be returned.
|
||||
* @return The parameter value, or <b>null</b> if not found.
|
||||
*/
|
||||
public String getParameterValue (String name) {
|
||||
if ( pnames != null ) {
|
||||
for (int i = 0 ; i < pnames.length ; i++) {
|
||||
if ( pnames[i].equals(name) )
|
||||
return pvalues[i];
|
||||
}
|
||||
}
|
||||
return null ;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds some parameters to a MimeType
|
||||
* @param param a String array of parameter names
|
||||
* @param values the corresponding String array of values
|
||||
*/
|
||||
public void addParameters(String[] param, String[] values) {
|
||||
// sanity check
|
||||
if ((param == null) || (values == null) ||
|
||||
(values.length != param.length))
|
||||
return;
|
||||
if (pnames == null) {
|
||||
pnames = param;
|
||||
pvalues = values;
|
||||
} else {
|
||||
String[] nparam = new String[pnames.length+param.length];
|
||||
String[] nvalues = new String[pvalues.length+values.length];
|
||||
System.arraycopy(pnames, 0, nparam, 0, pnames.length);
|
||||
System.arraycopy(param, 0, nparam, pnames.length, param.length);
|
||||
System.arraycopy(pvalues, 0, nvalues, 0, pvalues.length);
|
||||
System.arraycopy(values,0, nvalues, pvalues.length, values.length);
|
||||
pnames = nparam;
|
||||
pvalues = nvalues;
|
||||
}
|
||||
external = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a clone of this object
|
||||
* @return another cloned instance of MimeType
|
||||
*/
|
||||
public MimeType getClone() {
|
||||
try {
|
||||
return (MimeType) clone();
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
// should never happen as we are Cloneable!
|
||||
}
|
||||
// never reached
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a parameterto a MimeType
|
||||
* @param param the parameter name, as a String
|
||||
* @param value the parameter value, as a Sting
|
||||
*/
|
||||
public void addParameter(String param, String value) {
|
||||
String[] p = new String[1];
|
||||
String[] v = new String[1];
|
||||
p[0] = param;
|
||||
v[0] = value;
|
||||
addParameters(p, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct MimeType object for the given string.
|
||||
* The string should be the representation of the type. This methods
|
||||
* tries to be compliant with HTTP1.1, p 15, although it is not
|
||||
* (because of quoted-text not being accepted).
|
||||
* FIXME
|
||||
* @param spec A string representing a MimeType
|
||||
* @exception MimeTypeFormatException if the string couldn't be parsed.
|
||||
*/
|
||||
public MimeType (String spec)
|
||||
throws MimeTypeFormatException
|
||||
{
|
||||
int strl = spec.length() ;
|
||||
int start = 0, look = -1 ;
|
||||
// skip leading/trailing blanks:
|
||||
while ((start < strl) && (spec.charAt (start)) <= ' ')
|
||||
start++ ;
|
||||
while ((strl > start) && (spec.charAt (strl-1) <= ' '))
|
||||
strl-- ;
|
||||
// get the type:
|
||||
StringBuffer sb = new StringBuffer () ;
|
||||
while ((start < strl) && ((look = spec.charAt(start)) != '/')) {
|
||||
sb.append ((char) look) ;
|
||||
start++ ;
|
||||
}
|
||||
if ( look != '/' )
|
||||
throw new MimeTypeFormatException (spec) ;
|
||||
this.type = sb.toString() ;
|
||||
// get the subtype:
|
||||
start++ ;
|
||||
sb.setLength(0) ;
|
||||
while ((start < strl)
|
||||
&& ((look = spec.charAt(start)) > ' ') && (look != ';')) {
|
||||
sb.append ((char) look) ;
|
||||
start++ ;
|
||||
}
|
||||
this.subtype = sb.toString() ;
|
||||
// get parameters, if any:
|
||||
while ((start < strl) && ((look = spec.charAt(start)) <= ' '))
|
||||
start++ ;
|
||||
if ( start < strl ) {
|
||||
if (spec.charAt(start) != ';')
|
||||
throw new MimeTypeFormatException (spec) ;
|
||||
start++ ;
|
||||
Vector vp = new Vector(4) ;
|
||||
Vector vv = new Vector(4) ;
|
||||
while ( start < strl ) {
|
||||
while ((start < strl) && (spec.charAt(start) <= ' ')) start++ ;
|
||||
// get parameter name:
|
||||
sb.setLength (0) ;
|
||||
while ((start < strl)
|
||||
&& ((look=spec.charAt(start)) > ' ') && (look != '=')) {
|
||||
sb.append (Character.toLowerCase((char) look)) ;
|
||||
start++ ;
|
||||
}
|
||||
String name = sb.toString() ;
|
||||
// get the value:
|
||||
while ((start < strl) && (spec.charAt(start) <= ' ')) start++ ;
|
||||
if (spec.charAt(start) != '=')
|
||||
throw new MimeTypeFormatException (spec) ;
|
||||
start++ ;
|
||||
while ((start < strl) &&
|
||||
((spec.charAt(start) == '"') ||
|
||||
(spec.charAt(start) <= ' '))) start++ ;
|
||||
sb.setLength(0) ;
|
||||
while ((start < strl)
|
||||
&& ((look=spec.charAt(start)) > ' ')
|
||||
&& (look != ';')
|
||||
&& (look != '"')) {
|
||||
sb.append ((char) look) ;
|
||||
start++ ;
|
||||
}
|
||||
while ((start < strl) && (spec.charAt(start) != ';')) start++ ;
|
||||
start++ ;
|
||||
String value = sb.toString() ;
|
||||
vp.addElement(name);
|
||||
vv.addElement(value);
|
||||
}
|
||||
this.pnames = new String[vp.size()];
|
||||
vp.copyInto(pnames);
|
||||
this.pvalues = new String[vv.size()];
|
||||
vv.copyInto(pvalues);
|
||||
}
|
||||
}
|
||||
|
||||
public MimeType (String type, String subtype
|
||||
, String pnames[], String pvalues[]) {
|
||||
this.type = type ;
|
||||
this.subtype = subtype ;
|
||||
this.pnames = pnames;
|
||||
this.pvalues = pvalues;
|
||||
}
|
||||
|
||||
public MimeType (String type, String subtype) {
|
||||
this.type = type;
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
public static void main (String args[]) {
|
||||
if ( args.length == 1) {
|
||||
MimeType type = null ;
|
||||
try {
|
||||
type = new MimeType (args[0]) ;
|
||||
} catch (MimeTypeFormatException e) {
|
||||
}
|
||||
if ( type != null )
|
||||
System.out.println (type) ;
|
||||
else
|
||||
System.out.println ("Invalid mime type specification.") ;
|
||||
} else {
|
||||
System.out.println ("Usage: java MimeType <type-to-parse>") ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// MimeType.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
public class MimeTypeFormatException extends Exception {
|
||||
|
||||
public MimeTypeFormatException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
// MultipartInputStream.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1996.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
import java.io.* ;
|
||||
|
||||
/**
|
||||
* A class to handle multipart MIME input streams. See RC 1521.
|
||||
* This class handles multipart input streams, as defined by the RFC 1521.
|
||||
* It prvides a sequential interface to all MIME parts, and for each part
|
||||
* it delivers a suitable InputStream for getting its body.
|
||||
*/
|
||||
|
||||
public class MultipartInputStream extends InputStream {
|
||||
InputStream in = null;
|
||||
byte boundary[] = null ;
|
||||
byte buffer[] = null ;
|
||||
boolean partEnd = false ;
|
||||
boolean fileEnd = false ;
|
||||
|
||||
// Read boundary bytes of input in buffer
|
||||
// Return true if enough bytes available, false otherwise.
|
||||
|
||||
private final boolean readBoundaryBytes()
|
||||
throws IOException
|
||||
{
|
||||
int pos = 0;
|
||||
while ( pos < buffer.length ) {
|
||||
int got = in.read(buffer, pos, buffer.length-pos);
|
||||
if ( got < 0 )
|
||||
return false;
|
||||
pos += got;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip to next input boundary, set stream at begining of content:
|
||||
// Returns true if boundary was found, false otherwise.
|
||||
|
||||
protected boolean skipToBoundary()
|
||||
throws IOException
|
||||
{
|
||||
int ch = in.read() ;
|
||||
skip:
|
||||
while ( ch != -1 ) {
|
||||
if ( ch != '-' ) {
|
||||
ch = in.read() ;
|
||||
continue ;
|
||||
}
|
||||
if ((ch = in.read()) != '-')
|
||||
continue ;
|
||||
in.mark(boundary.length) ;
|
||||
if ( ! readBoundaryBytes() ) {
|
||||
in.reset();
|
||||
ch = in.read();
|
||||
continue skip;
|
||||
}
|
||||
for (int i = 0 ; i < boundary.length ; i++) {
|
||||
if ( buffer[i] != boundary[i] ) {
|
||||
in.reset() ;
|
||||
ch = in.read() ;
|
||||
continue skip ;
|
||||
}
|
||||
}
|
||||
// FIXME: should we check for a properly syntaxed part, which
|
||||
// means that we should expect '\r\n'. For now, we just skip
|
||||
// as much as we can.
|
||||
if ( (ch = in.read()) == '\r' ) {
|
||||
ch = in.read() ;
|
||||
}
|
||||
in.mark(3);
|
||||
if( in.read() == '-' ) { // check fileEnd!
|
||||
if( in.read() == '\r' && in.read() == '\n' ) {
|
||||
fileEnd = true ;
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
in.reset();
|
||||
return true ;
|
||||
}
|
||||
fileEnd = true ;
|
||||
return false ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one byte of data from the current part.
|
||||
* @return A byte of data, or <strong>-1</strong> if end of file.
|
||||
* @exception IOException If some IO error occured.
|
||||
*/
|
||||
|
||||
public int read()
|
||||
throws IOException
|
||||
{
|
||||
int ch ;
|
||||
if ( partEnd )
|
||||
return -1 ;
|
||||
switch (ch = in.read()) {
|
||||
case '\r':
|
||||
// check for a boundary
|
||||
in.mark(boundary.length+3) ;
|
||||
int c1 = in.read() ;
|
||||
int c2 = in.read() ;
|
||||
int c3 = in.read() ;
|
||||
if ((c1 == '\n') && (c2 == '-') && (c3 == '-')) {
|
||||
if ( ! readBoundaryBytes() ) {
|
||||
in.reset();
|
||||
return ch;
|
||||
}
|
||||
for (int i = 0 ; i < boundary.length ; i++) {
|
||||
if ( buffer[i] != boundary[i] ) {
|
||||
in.reset() ;
|
||||
return ch ;
|
||||
}
|
||||
}
|
||||
partEnd = true ;
|
||||
if ( (ch = in.read()) == '\r' ) {
|
||||
in.read() ;
|
||||
} else if (ch == '-') {
|
||||
// FIXME, check the second hyphen
|
||||
if (in.read() == '-')
|
||||
fileEnd = true ;
|
||||
} else {
|
||||
fileEnd = (ch == -1);
|
||||
}
|
||||
return -1 ;
|
||||
} else {
|
||||
in.reset () ;
|
||||
return ch ;
|
||||
}
|
||||
// not reached
|
||||
case -1:
|
||||
fileEnd = true ;
|
||||
return -1 ;
|
||||
default:
|
||||
return ch ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read n bytes of data from the current part.
|
||||
* @return the number of bytes data, read or <strong>-1</strong>
|
||||
* if end of file.
|
||||
* @exception IOException If some IO error occured.
|
||||
*/
|
||||
public int read (byte b[], int off, int len)
|
||||
throws IOException
|
||||
{
|
||||
int got = 0 ;
|
||||
int ch ;
|
||||
|
||||
while ( got < len ) {
|
||||
if ((ch = read()) == -1)
|
||||
return (got == 0) ? -1 : got ;
|
||||
b[off+(got++)] = (byte) (ch & 0xFF) ;
|
||||
}
|
||||
return got ;
|
||||
}
|
||||
|
||||
public long skip (long n)
|
||||
throws IOException
|
||||
{
|
||||
while ((--n >= 0) && (read() != -1))
|
||||
;
|
||||
return n ;
|
||||
}
|
||||
|
||||
public int available ()
|
||||
throws IOException
|
||||
{
|
||||
return in.available();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to the next available part of data.
|
||||
* One can interrupt the current part, and use this method to switch
|
||||
* to next part before current part was totally read.
|
||||
* @return A boolean <strong>true</strong> if there next partis ready,
|
||||
* or <strong>false</strong> if this was the last part.
|
||||
*/
|
||||
|
||||
public boolean nextInputStream()
|
||||
throws IOException
|
||||
{
|
||||
if ( fileEnd ) {
|
||||
return false ;
|
||||
}
|
||||
if ( ! partEnd ) {
|
||||
return skipToBoundary() ;
|
||||
} else {
|
||||
partEnd = false ;
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new multipart input stream.
|
||||
* @param in The initial (multipart) input stream.
|
||||
* @param boundary The input stream MIME boundary.
|
||||
*/
|
||||
|
||||
public MultipartInputStream (InputStream in, byte boundary[]) {
|
||||
this.in = (in.markSupported()
|
||||
? in
|
||||
: new BufferedInputStream(in, boundary.length+4));
|
||||
this.boundary = boundary ;
|
||||
this.buffer = new byte[boundary.length] ;
|
||||
this.partEnd = false ;
|
||||
this.fileEnd = false ;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
// Utils.java
|
||||
// $Id$
|
||||
// (c) COPYRIGHT MIT and INRIA, 1998.
|
||||
// Please first read the full copyright statement in file COPYRIGHT.html
|
||||
|
||||
package helma.util.mime;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* @version $Revision$
|
||||
* @author Benoit Mahe (bmahe@w3.org)
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
private static Hashtable extension_map = new Hashtable();
|
||||
|
||||
private static void setSuffix(String ext, String ct) {
|
||||
extension_map.put(ext, ct);
|
||||
}
|
||||
|
||||
static {
|
||||
setSuffix("", "content/unknown");
|
||||
setSuffix(".uu", "application/octet-stream");
|
||||
setSuffix(".saveme", "application/octet-stream");
|
||||
setSuffix(".dump", "application/octet-stream");
|
||||
setSuffix(".hqx", "application/octet-stream");
|
||||
setSuffix(".arc", "application/octet-stream");
|
||||
setSuffix(".o", "application/octet-stream");
|
||||
setSuffix(".a", "application/octet-stream");
|
||||
setSuffix(".bin", "application/octet-stream");
|
||||
setSuffix(".exe", "application/octet-stream");
|
||||
setSuffix(".z", "application/octet-stream");
|
||||
setSuffix(".gz", "application/octet-stream");
|
||||
setSuffix(".oda", "application/oda");
|
||||
setSuffix(".pdf", "application/pdf");
|
||||
setSuffix(".eps", "application/postscript");
|
||||
setSuffix(".ai", "application/postscript");
|
||||
setSuffix(".ps", "application/postscript");
|
||||
setSuffix(".rtf", "application/rtf");
|
||||
setSuffix(".dvi", "application/x-dvi");
|
||||
setSuffix(".hdf", "application/x-hdf");
|
||||
setSuffix(".latex", "application/x-latex");
|
||||
setSuffix(".cdf", "application/x-netcdf");
|
||||
setSuffix(".nc", "application/x-netcdf");
|
||||
setSuffix(".tex", "application/x-tex");
|
||||
setSuffix(".texinfo", "application/x-texinfo");
|
||||
setSuffix(".texi", "application/x-texinfo");
|
||||
setSuffix(".t", "application/x-troff");
|
||||
setSuffix(".tr", "application/x-troff");
|
||||
setSuffix(".roff", "application/x-troff");
|
||||
setSuffix(".man", "application/x-troff-man");
|
||||
setSuffix(".me", "application/x-troff-me");
|
||||
setSuffix(".ms", "application/x-troff-ms");
|
||||
setSuffix(".src", "application/x-wais-source");
|
||||
setSuffix(".wsrc", "application/x-wais-source");
|
||||
setSuffix(".zip", "application/zip");
|
||||
setSuffix(".bcpio", "application/x-bcpio");
|
||||
setSuffix(".cpio", "application/x-cpio");
|
||||
setSuffix(".gtar", "application/x-gtar");
|
||||
setSuffix(".shar", "application/x-shar");
|
||||
setSuffix(".sh", "application/x-shar");
|
||||
setSuffix(".sv4cpio", "application/x-sv4cpio");
|
||||
setSuffix(".sv4crc", "application/x-sv4crc");
|
||||
setSuffix(".tar", "application/x-tar");
|
||||
setSuffix(".ustar", "application/x-ustar");
|
||||
setSuffix(".snd", "audio/basic");
|
||||
setSuffix(".au", "audio/basic");
|
||||
setSuffix(".aifc", "audio/x-aiff");
|
||||
setSuffix(".aif", "audio/x-aiff");
|
||||
setSuffix(".aiff", "audio/x-aiff");
|
||||
setSuffix(".wav", "audio/x-wav");
|
||||
setSuffix(".gif", "image/gif");
|
||||
setSuffix(".ief", "image/ief");
|
||||
setSuffix(".jfif", "image/jpeg");
|
||||
setSuffix(".jfif-tbnl", "image/jpeg");
|
||||
setSuffix(".jpe", "image/jpeg");
|
||||
setSuffix(".jpg", "image/jpeg");
|
||||
setSuffix(".jpeg", "image/jpeg");
|
||||
setSuffix(".tif", "image/tiff");
|
||||
setSuffix(".tiff", "image/tiff");
|
||||
setSuffix(".ras", "image/x-cmu-rast");
|
||||
setSuffix(".pnm", "image/x-portable-anymap");
|
||||
setSuffix(".pbm", "image/x-portable-bitmap");
|
||||
setSuffix(".pgm", "image/x-portable-graymap");
|
||||
setSuffix(".ppm", "image/x-portable-pixmap");
|
||||
setSuffix(".rgb", "image/x-rgb");
|
||||
setSuffix(".xbm", "image/x-xbitmap");
|
||||
setSuffix(".xpm", "image/x-xpixmap");
|
||||
setSuffix(".xwd", "image/x-xwindowdump");
|
||||
setSuffix(".htm", "text/html");
|
||||
setSuffix(".html", "text/html");
|
||||
setSuffix(".text", "text/plain");
|
||||
setSuffix(".c", "text/plain");
|
||||
setSuffix(".cc", "text/plain");
|
||||
setSuffix(".c++", "text/plain");
|
||||
setSuffix(".h", "text/plain");
|
||||
setSuffix(".pl", "text/plain");
|
||||
setSuffix(".txt", "text/plain");
|
||||
setSuffix(".java", "text/plain");
|
||||
setSuffix(".rtx", "application/rtf");
|
||||
setSuffix(".tsv", "texyt/tab-separated-values");
|
||||
setSuffix(".etx", "text/x-setext");
|
||||
setSuffix(".mpg", "video/mpeg");
|
||||
setSuffix(".mpe", "video/mpeg");
|
||||
setSuffix(".mpeg", "video/mpeg");
|
||||
setSuffix(".mov", "video/quicktime");
|
||||
setSuffix(".qt", "video/quicktime");
|
||||
setSuffix(".avi", "application/x-troff-msvideo");
|
||||
setSuffix(".movie", "video/x-sgi-movie");
|
||||
setSuffix(".mv", "video/x-sgi-movie");
|
||||
setSuffix(".mime", "message/rfc822");
|
||||
}
|
||||
|
||||
/**
|
||||
* A useful utility routine that tries to guess the content-type
|
||||
* of an object based upon its extension.
|
||||
*/
|
||||
public static String guessContentTypeFromName(String fname) {
|
||||
String ext = "";
|
||||
int i = fname.lastIndexOf('#');
|
||||
|
||||
if (i != -1)
|
||||
fname = fname.substring(0, i - 1);
|
||||
i = fname.lastIndexOf('.');
|
||||
i = Math.max(i, fname.lastIndexOf('/'));
|
||||
i = Math.max(i, fname.lastIndexOf('?'));
|
||||
|
||||
if (i != -1 && fname.charAt(i) == '.') {
|
||||
ext = fname.substring(i).toLowerCase();
|
||||
}
|
||||
return (String) extension_map.get(ext);
|
||||
}
|
||||
|
||||
public static MimeType getMimeType(String filename) {
|
||||
try {
|
||||
return new MimeType(guessContentTypeFromName(filename));
|
||||
} catch (MimeTypeFormatException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue