001 package org.apache.fulcrum.parser; 002 003 004 /* 005 * Licensed to the Apache Software Foundation (ASF) under one 006 * or more contributor license agreements. See the NOTICE file 007 * distributed with this work for additional information 008 * regarding copyright ownership. The ASF licenses this file 009 * to you under the Apache License, Version 2.0 (the 010 * "License"); you may not use this file except in compliance 011 * with the License. You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, 016 * software distributed under the License is distributed on an 017 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 018 * KIND, either express or implied. See the License for the 019 * specific language governing permissions and limitations 020 * under the License. 021 */ 022 023 024 import java.io.UnsupportedEncodingException; 025 import java.net.URLDecoder; 026 import java.util.Enumeration; 027 import java.util.Iterator; 028 import java.util.List; 029 import java.util.StringTokenizer; 030 031 import javax.servlet.http.HttpServletRequest; 032 033 import org.apache.avalon.framework.service.ServiceException; 034 import org.apache.commons.fileupload.FileItem; 035 import org.apache.commons.lang.ArrayUtils; 036 037 /** 038 * DefaultParameterParser is a utility object to handle parsing and 039 * retrieving the data passed via the GET/POST/PATH_INFO arguments. 040 * 041 * <p>NOTE: The name= portion of a name=value pair may be converted 042 * to lowercase or uppercase when the object is initialized and when 043 * new data is added. This behaviour is determined by the url.case.folding 044 * property in TurbineResources.properties. Adding a name/value pair may 045 * overwrite existing name=value pairs if the names match: 046 * 047 * <pre> 048 * ParameterParser pp = data.getParameters(); 049 * pp.add("ERROR",1); 050 * pp.add("eRrOr",2); 051 * int result = pp.getInt("ERROR"); 052 * </pre> 053 * 054 * In the above example, result is 2. 055 * 056 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a> 057 * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a> 058 * @author <a href="mailto:sean@informage.net">Sean Legassick</a> 059 * @author <a href="mailto:jh@byteaction.de">Jürgen Hoffmann</a> 060 * @version $Id: DefaultParameterParser.java 812786 2009-09-09 07:01:49Z tv $ 061 */ 062 public class DefaultParameterParser 063 extends BaseValueParser 064 implements ParameterParser 065 { 066 /** 067 * The servlet request to parse. 068 */ 069 private HttpServletRequest request = null; 070 071 /** 072 * The raw data of a file upload. 073 */ 074 private byte[] uploadData = null; 075 076 /** 077 * Create a new empty instance of ParameterParser. Uses the 078 * default character encoding (US-ASCII). 079 * 080 * <p>To add name/value pairs to this set of parameters, use the 081 * <code>add()</code> methods. 082 * 083 */ 084 public DefaultParameterParser() 085 { 086 super(); 087 } 088 089 /** 090 * Create a new empty instance of ParameterParser. Takes a 091 * character encoding name to use when converting strings to 092 * bytes. 093 * 094 * <p>To add name/value pairs to this set of parameters, use the 095 * <code>add()</code> methods. 096 * 097 * @param characterEncoding The character encoding of strings. 098 */ 099 public DefaultParameterParser(String characterEncoding) 100 { 101 super (characterEncoding); 102 } 103 104 /** 105 * Disposes the parser. 106 */ 107 public void dispose() 108 { 109 this.request = null; 110 this.uploadData = null; 111 super.dispose(); 112 } 113 114 /** 115 * Gets the parsed servlet request. 116 * 117 * @return the parsed servlet request or null. 118 */ 119 public HttpServletRequest getRequest() 120 { 121 return request; 122 } 123 124 /** 125 * Sets the servlet request to the parser. This requires a 126 * valid HttpServletRequest object. It will attempt to parse out 127 * the GET/POST/PATH_INFO data and store the data into a Map. 128 * There are convenience methods for retrieving the data as a 129 * number of different datatypes. The PATH_INFO data must be a 130 * URLEncoded() string. 131 * <p> 132 * To add name/value pairs to this set of parameters, use the 133 * <code>add()</code> methods. 134 * 135 * @param request An HttpServletRequest. 136 */ 137 public void setRequest(HttpServletRequest request) 138 { 139 clear(); 140 141 uploadData = null; 142 143 String enc = request.getCharacterEncoding(); 144 setCharacterEncoding(enc != null 145 ? enc 146 : parserService.getParameterEncoding()); 147 148 String contentType = request.getHeader("Content-type"); 149 150 if (parserService.getAutomaticUpload() 151 && contentType != null 152 && contentType.startsWith("multipart/form-data")) 153 { 154 if (getLogger().isDebugEnabled()) 155 { 156 getLogger().debug("Running the Fulcrum Upload Service"); 157 } 158 159 try 160 { 161 List fileItems = parserService.parseUpload(request); 162 163 if (fileItems != null) 164 { 165 for (Iterator it = fileItems.iterator(); it.hasNext();) 166 { 167 FileItem fi = (FileItem) it.next(); 168 if (fi.isFormField()) 169 { 170 getLogger().debug("Found an simple form field: " + fi.getFieldName() +", adding value " + fi.getString()); 171 172 String value = null; 173 try 174 { 175 value = fi.getString(getCharacterEncoding()); 176 } 177 catch (UnsupportedEncodingException e) 178 { 179 getLogger().error(getCharacterEncoding() 180 + " encoding is not supported." 181 + "Used the default when reading form data."); 182 value = fi.getString(); 183 } 184 add(fi.getFieldName(), value); 185 } 186 else 187 { 188 getLogger().debug("Found an uploaded file: " + fi.getFieldName()); 189 getLogger().debug("It has " + fi.getSize() + " Bytes and is " + (fi.isInMemory() ? "" : "not ") + "in Memory"); 190 getLogger().debug("Adding FileItem as " + fi.getFieldName() + " to the params"); 191 add(fi.getFieldName(), fi); 192 } 193 } 194 } 195 } 196 catch (ServiceException e) 197 { 198 getLogger().error("File upload failed", e); 199 } 200 } 201 202 for (Enumeration names = request.getParameterNames(); 203 names.hasMoreElements();) 204 { 205 String paramName = (String) names.nextElement(); 206 add(paramName, 207 request.getParameterValues(paramName)); 208 } 209 210 // Also cache any pathinfo variables that are passed around as 211 // if they are query string data. 212 try 213 { 214 boolean isNameTok = true; 215 String paramName = null; 216 String paramValue = null; 217 218 for ( StringTokenizer st = 219 new StringTokenizer(request.getPathInfo(), "/"); 220 st.hasMoreTokens();) 221 { 222 if (isNameTok) 223 { 224 paramName = URLDecoder.decode(st.nextToken(), getCharacterEncoding()); 225 isNameTok = false; 226 } 227 else 228 { 229 paramValue = URLDecoder.decode(st.nextToken(), getCharacterEncoding()); 230 if (paramName != null && paramName.length() > 0) 231 { 232 add(paramName, paramValue); 233 } 234 isNameTok = true; 235 } 236 } 237 } 238 catch (Exception e) 239 { 240 // If anything goes wrong above, don't worry about it. 241 // Chances are that the path info was wrong anyways and 242 // things that depend on it being right will fail later 243 // and should be caught later. 244 } 245 246 this.request = request; 247 248 if (getLogger().isDebugEnabled()) 249 { 250 getLogger().debug("Parameters found in the Request:"); 251 for (Iterator it = keySet().iterator(); it.hasNext();) 252 { 253 String key = (String) it.next(); 254 getLogger().debug("Key: " + key + " -> " + getString(key)); 255 } 256 } 257 } 258 259 /** 260 * Sets the uploadData byte[] 261 * 262 * @param uploadData A byte[] with data. 263 */ 264 public void setUploadData ( byte[] uploadData ) 265 { 266 this.uploadData = uploadData; 267 } 268 269 /** 270 * Gets the uploadData byte[] 271 * 272 * @return uploadData A byte[] with data. 273 */ 274 public byte[] getUploadData () 275 { 276 return this.uploadData; 277 } 278 279 280 /** 281 * Add a FileItem object as a parameters. If there are any 282 * FileItems already associated with the name, append to the 283 * array. The reason for this is that RFC 1867 allows multiple 284 * files to be associated with single HTML input element. 285 * 286 * @param name A String with the name. 287 * @param value A FileItem with the value. 288 * @deprecated Use add(String name, FileItem item) 289 */ 290 public void append(String name, FileItem value) 291 { 292 add(name, value); 293 } 294 295 296 /** 297 * Add a FileItem object as a parameters. If there are any 298 * FileItems already associated with the name, append to the 299 * array. The reason for this is that RFC 1867 allows multiple 300 * files to be associated with single HTML input element. 301 * 302 * @param name A String with the name. 303 * @param value A FileItem with the value. 304 */ 305 public void add(String name, FileItem value) 306 { 307 FileItem[] items = this.getFileItems(name); 308 items = (FileItem []) ArrayUtils.add(items, value); 309 parameters.put(convert(name), items); 310 } 311 312 313 /** 314 * Return a FileItem object for the given name. If the name does 315 * not exist or the object stored is not a FileItem, return null. 316 * 317 * @param name A String with the name. 318 * @return A FileItem. 319 */ 320 public FileItem getFileItem(String name) 321 { 322 try 323 { 324 FileItem value = null; 325 Object object = parameters.get(convert(name)); 326 if (object != null) 327 value = ((FileItem[])object)[0]; 328 return value; 329 } 330 catch ( ClassCastException e ) 331 { 332 return null; 333 } 334 } 335 336 /** 337 * Return an array of FileItem objects for the given name. If the 338 * name does not exist or the object stored is not a FileItem 339 * array, return null. 340 * 341 * @param name A String with the name. 342 * @return A FileItem[]. 343 */ 344 public FileItem[] getFileItems(String name) 345 { 346 try 347 { 348 return (FileItem[])parameters.get(convert(name)); 349 } 350 catch ( ClassCastException e ) 351 { 352 return null; 353 } 354 } 355 }