| /******************************************************************************* |
| * Copyright (c) 2004, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.ws.internal.explorer.platform.util; |
| |
| import java.io.BufferedReader; |
| import java.io.InputStreamReader; |
| import java.util.Enumeration; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Vector; |
| import javax.servlet.http.HttpServletRequest; |
| |
| public final class MultipartFormDataParser |
| { |
| private Hashtable paramTable_; |
| |
| private static final String HEADER_CONTENT_TYPE = "Content-Type"; |
| private static final String HEADER_MULTIPART = "multipart"; |
| private static final String HEADER_BOUNDARY = "boundary="; |
| private static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition: form-data"; |
| private static final String HEADER_NAME = "name=\""; |
| |
| private static final byte PARSER_STATE_INITIAL = 0; |
| private static final byte PARSER_STATE_BOUNDARY = 1; |
| private static final byte PARSER_STATE_PARAMETER = 2; |
| private static final byte PARSER_STATE_BLANK = 3; |
| private static final byte PARSER_STATE_DATA = 4; |
| |
| private static String parserStates[] = {"initial","boundary","parameter name","blank line","data"}; |
| |
| public MultipartFormDataParser() |
| { |
| } |
| |
| public MultipartFormDataParser(Hashtable parameters) |
| { |
| paramTable_ = new Hashtable(); |
| for (Iterator it = parameters.keySet().iterator(); it.hasNext();) |
| { |
| Object key = it.next(); |
| Object value = parameters.get(key); |
| if (value instanceof List) |
| { |
| List list = (List)value; |
| for (Iterator it2 = list.iterator(); it2.hasNext();) |
| saveData(key.toString(), it2.next().toString()); |
| } |
| else if (value.getClass().isArray()) |
| { |
| Object[] array = (Object[])value; |
| for (int i = 0; i < array.length; i++) |
| saveData(key.toString(), array[i].toString()); |
| } |
| else |
| { |
| saveData(key.toString(), value.toString()); |
| } |
| } |
| } |
| |
| /** |
| * Parse a multipart/form-data encoded post request with a given encoding. |
| * If the encoding is null, use the system default encoding. utf-8 is not a |
| * bad choice for the encoding. |
| */ |
| public final void parseRequest(HttpServletRequest request,String encoding) throws MultipartFormDataException |
| { |
| // Content-Type header should have the form: |
| // multipart/form-data; boundary=... |
| // |
| // RFC2046 5.1.1 page 19, paragraph 2: |
| // The Content-Type field for multipart entities requires one parameter, "boundary" (no quotes) |
| String contentType = request.getHeader(HEADER_CONTENT_TYPE); |
| if (contentType == null || !contentType.startsWith(HEADER_MULTIPART) || contentType.indexOf(HEADER_BOUNDARY) == -1) |
| throw new MultipartFormDataException("Content-Type is not multipart/form-data"); |
| |
| // RFC2046 5.1.1 page 19, paragraph 4: |
| // The boundary value may be enclosed in double quotes. Strip these if they are present. |
| String boundary = contentType.substring(contentType.indexOf(HEADER_BOUNDARY)+HEADER_BOUNDARY.length(),contentType.length()); |
| if (boundary.charAt(0) == '\"' && boundary.charAt(boundary.length()-1) == '\"') |
| boundary = boundary.substring(1,boundary.length()-1); |
| |
| // RFC2046 5.1.1 page 19, paragraph 2: |
| // The boundary delimiter line is then defined as a line consisting entirely |
| // of two hyphen characters. |
| String delimiter = "--"+boundary; |
| |
| if (paramTable_ == null) |
| paramTable_ = new Hashtable(); |
| else |
| paramTable_.clear(); |
| |
| try |
| { |
| BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream(),encoding)); |
| String line = null; |
| String parameterName = null; |
| StringBuffer parameterValue = new StringBuffer(); |
| byte currentParserState = PARSER_STATE_INITIAL; |
| while ((line = br.readLine()) != null) |
| { |
| // Lines appear in the following sequence. |
| // 1) boundary indicating the start of a new parameter and end of a data segment. |
| // 2) Content-Disposition: form-data; name="..." - the name of a new parameter. |
| // 3) a blank line |
| // 4) data |
| // |
| // For each parameter, the sequence is repeated. |
| if (line.startsWith(delimiter)) |
| { |
| byte[] expectedParserStates = {PARSER_STATE_INITIAL,PARSER_STATE_DATA}; |
| if (isValidParserState(currentParserState,expectedParserStates)) |
| { |
| // Save any current data and prepare for a new parameter name. |
| if (parameterName != null) |
| { |
| saveData(parameterName,parameterValue.toString()); |
| parameterName = null; |
| parameterValue.setLength(0); |
| } |
| currentParserState = PARSER_STATE_BOUNDARY; |
| } |
| else |
| throw new MultipartFormDataException(getParserExceptionMessage(currentParserState,expectedParserStates)); |
| } |
| else if (line.startsWith(HEADER_CONTENT_DISPOSITION)) |
| { |
| byte[] expectedParserStates = {PARSER_STATE_BOUNDARY}; |
| if (isValidParserState(currentParserState,expectedParserStates)) |
| { |
| // Obtain the parameter name without the surrounding double quotes. Accounts for RFC 1867 too. |
| int parameterNameStartingPosition = line.indexOf(HEADER_NAME)+HEADER_NAME.length(); |
| parameterName = line.substring(parameterNameStartingPosition,+parameterNameStartingPosition+line.substring(parameterNameStartingPosition).indexOf('\"')); |
| currentParserState = PARSER_STATE_PARAMETER; |
| } |
| } |
| else if (currentParserState == PARSER_STATE_PARAMETER) |
| { |
| // A blank line should follow the PARAMETER. Discard the line and move on. |
| currentParserState = PARSER_STATE_BLANK; |
| } |
| else |
| { |
| // Expect the line to contain data. |
| if (parameterValue.length() > 0) |
| parameterValue.append('\n'); |
| parameterValue.append(line); |
| currentParserState = PARSER_STATE_DATA; |
| } |
| } |
| } |
| catch (Throwable t) |
| { |
| throw new MultipartFormDataException(t.getMessage()); |
| } |
| //dumpParamTable(); |
| } |
| |
| /** |
| * Returns the value of a request parameter as a String, or null if the parameter does not exist. |
| * If the parameter has multiple values, only the first value is returned. Use getParameterValues() |
| * for parameters with multiple values. |
| */ |
| public final String getParameter(String parameter) throws MultipartFormDataException |
| { |
| if (paramTable_ == null) |
| throw new MultipartFormDataException("Parser contains no parsed data"); |
| Vector values = (Vector)paramTable_.get(parameter); |
| return ((values != null)?((String)values.elementAt(0)):null); |
| } |
| |
| public final String[] getParameterValues(String parameter) throws MultipartFormDataException |
| { |
| if (paramTable_ == null) |
| throw new MultipartFormDataException("Parser contains no parsed data"); |
| Vector valuesVector = (Vector)paramTable_.get(parameter); |
| if (valuesVector == null) |
| return null; |
| String[] valuesArray = new String[valuesVector.size()]; |
| for (int i=0;i<valuesArray.length;i++) |
| valuesArray[i] = (String)valuesVector.elementAt(i); |
| return valuesArray; |
| } |
| |
| public final String[] getParameterNames() throws MultipartFormDataException |
| { |
| if (paramTable_ == null) |
| throw new MultipartFormDataException("Parser contains no parsed data"); |
| |
| int size = paramTable_.size(); |
| if (size == 0) |
| return null; |
| String[] names = new String[size]; |
| Enumeration keys = paramTable_.keys(); |
| for (int i=0;i<size;i++) |
| names[i] = (String)keys.nextElement(); |
| return names; |
| } |
| |
| private final void saveData(String parameterName,String parameterValue) |
| { |
| Vector values = (Vector)paramTable_.get(parameterName); |
| if (values == null) |
| values = new Vector(); |
| values.addElement(parameterValue); |
| paramTable_.put(parameterName,values); |
| } |
| |
| private final boolean isValidParserState(byte currentState,byte[] expectedStates) |
| { |
| boolean validity = false; |
| for (int i=0;i<expectedStates.length;i++) |
| { |
| if (currentState == expectedStates[i]) |
| { |
| validity = true; |
| break; |
| } |
| } |
| return validity; |
| } |
| |
| private final String getParserExceptionMessage(byte currentState,byte[] expectedStates) |
| { |
| StringBuffer msg = new StringBuffer("Parser state inconsistency!"); |
| msg.append('\n'); |
| msg.append("Current state : ").append(parserStates[currentState]).append('\n'); |
| msg.append("Expected state(s): "); |
| for (int i=0;i<expectedStates.length;i++) |
| { |
| msg.append(parserStates[expectedStates[i]]); |
| if (i != expectedStates.length-1) |
| msg.append(", "); |
| } |
| msg.append('\n'); |
| return msg.toString(); |
| } |
| } |