View Javadoc
1 package org.apache.turbine.services.upload; 2 3 /* ==================================================================== 4 * The Apache Software License, Version 1.1 5 * 6 * Copyright (c) 2001 The Apache Software Foundation. All rights 7 * reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The end-user documentation included with the redistribution, 22 * if any, must include the following acknowledgment: 23 * "This product includes software developed by the 24 * Apache Software Foundation (http://www.apache.org/)." 25 * Alternately, this acknowledgment may appear in the software itself, 26 * if and wherever such third-party acknowledgments normally appear. 27 * 28 * 4. The names "Apache" and "Apache Software Foundation" and 29 * "Apache Turbine" must not be used to endorse or promote products 30 * derived from this software without prior written permission. For 31 * written permission, please contact apache@apache.org. 32 * 33 * 5. Products derived from this software may not be called "Apache", 34 * "Apache Turbine", nor may "Apache" appear in their name, without 35 * prior written permission of the Apache Software Foundation. 36 * 37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 * ==================================================================== 50 * 51 * This software consists of voluntary contributions made by many 52 * individuals on behalf of the Apache Software Foundation. For more 53 * information on the Apache Software Foundation, please see 54 * <http://www.apache.org/>;. 55 */ 56 57 import java.io.IOException; 58 import java.io.InputStream; 59 import java.io.OutputStream; 60 import java.util.HashMap; 61 import java.util.Map; 62 import javax.servlet.http.HttpServletRequest; 63 import org.apache.turbine.util.ParameterParser; 64 import org.apache.turbine.util.TurbineException; 65 import org.apache.turbine.util.upload.FileItem; 66 import org.apache.turbine.util.upload.MultipartStream; 67 68 /*** 69 * <p> This class is an implementation of {@link UploadService}. 70 * 71 * <p> Files will be stored in temporary disk storage on in memory, 72 * depending on request size, and will be available from the {@link 73 * org.apache.turbine.util.ParameterParser} as {@link 74 * org.apache.turbine.util.upload.FileItem}s. 75 * 76 * <p>This implementation of {@link UploadService} handles multiple 77 * files per single html widget, sent using multipar/mixed encoding 78 * type, as specified by RFC 1867. Use {@link 79 * org.apache.turbine.util.ParameterParser#getFileItems(String)} to 80 * acquire an array of {@link 81 * org.apache.turbine.util.upload.FileItem}s associated with given 82 * html widget. 83 * 84 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a> 85 * @author <a href="mailto:dlr@collab.net">Daniel Rall</a> 86 * @version $Id: TurbineUploadService.java,v 1.4 2002/07/11 16:53:23 mpoeschl Exp $ 87 */ 88 public class TurbineUploadService 89 extends BaseUploadService 90 { 91 /*** 92 * <p> Processes an <a href="http://rf.cx/rfc1867.html">RFC 93 * 1867</a> compliant <code>multipart/form-data</code> stream. 94 * 95 * @param req The servlet request to be parsed. 96 * @param params The ParameterParser instance to insert form 97 * fields into. 98 * @param path The location where the files should be stored. 99 * @exception TurbineException If there are problems reading/parsing 100 * the request or storing files. 101 */ 102 public void parseRequest( HttpServletRequest req, 103 ParameterParser params, 104 String path ) 105 throws TurbineException 106 { 107 String contentType = req.getHeader(CONTENT_TYPE); 108 if(!contentType.startsWith(MULTIPART_FORM_DATA)) 109 { 110 throw new TurbineException("the request doesn't contain a " + 111 MULTIPART_FORM_DATA + " stream"); 112 } 113 int requestSize = req.getContentLength(); 114 if(requestSize == -1) 115 { 116 throw new TurbineException("the request was rejected because " + 117 "it's size is unknown"); 118 } 119 if(requestSize > TurbineUpload.getSizeMax()) 120 { 121 throw new TurbineException("the request was rejected because " + 122 "it's size exceeds allowed range"); 123 } 124 125 try 126 { 127 byte[] boundary = contentType.substring( 128 contentType.indexOf("boundary=")+9).getBytes(); 129 InputStream input = (InputStream)req.getInputStream(); 130 131 MultipartStream multi = new MultipartStream(input, boundary); 132 boolean nextPart = multi.skipPreamble(); 133 while(nextPart) 134 { 135 Map headers = parseHeaders(multi.readHeaders()); 136 String fieldName = getFieldName(headers); 137 if (fieldName != null) 138 { 139 String subContentType = getHeader(headers, CONTENT_TYPE); 140 if (subContentType != null && subContentType 141 .startsWith(MULTIPART_MIXED)) 142 { 143 // Multiple files. 144 byte[] subBoundary = 145 subContentType.substring( 146 subContentType 147 .indexOf("boundary=")+9).getBytes(); 148 multi.setBoundary(subBoundary); 149 boolean nextSubPart = multi.skipPreamble(); 150 while (nextSubPart) 151 { 152 headers = parseHeaders(multi.readHeaders()); 153 if (getFileName(headers) != null) 154 { 155 FileItem item = createItem(path, headers, 156 requestSize); 157 OutputStream os = item.getOutputStream(); 158 try 159 { 160 multi.readBodyData(os); 161 } 162 finally 163 { 164 os.close(); 165 } 166 params.append(getFieldName(headers), item); 167 } 168 else 169 { 170 // Ignore anything but files inside 171 // multipart/mixed. 172 multi.discardBodyData(); 173 } 174 nextSubPart = multi.readBoundary(); 175 } 176 multi.setBoundary(boundary); 177 } 178 else 179 { 180 if (getFileName(headers) != null) 181 { 182 // A single file. 183 FileItem item = createItem(path, headers, 184 requestSize); 185 OutputStream os = item.getOutputStream(); 186 try 187 { 188 multi.readBodyData(os); 189 } 190 finally 191 { 192 os.close(); 193 } 194 params.append(getFieldName(headers), item); 195 } 196 else 197 { 198 // A form field. 199 FileItem item = createItem(path, headers, 200 requestSize); 201 OutputStream os = item.getOutputStream(); 202 try 203 { 204 multi.readBodyData(os); 205 } 206 finally 207 { 208 os.close(); 209 } 210 params.append(getFieldName(headers), 211 new String(item.get())); 212 } 213 } 214 } 215 else 216 { 217 // Skip this part. 218 multi.discardBodyData(); 219 } 220 nextPart = multi.readBoundary(); 221 } 222 } 223 catch(IOException e) 224 { 225 throw new TurbineException("Processing of " + MULTIPART_FORM_DATA 226 + " request failed", e); 227 } 228 } 229 230 /*** 231 * <p> Retrieves file name from <code>Content-disposition</code> header. 232 * 233 * @param headers The HTTP request headers. 234 * @return A the file name for the current <code>encapsulation</code>. 235 */ 236 protected String getFileName(Map headers) 237 { 238 String fileName = null; 239 String cd = getHeader(headers, CONTENT_DISPOSITION); 240 if(cd.startsWith(FORM_DATA) || cd.startsWith("attachment")) 241 { 242 int start = cd.indexOf("filename=\""); 243 int end = cd.indexOf('"', start + 10); 244 if(start != -1 && end != -1 && (start + 10) != end) 245 { 246 String str = cd.substring(start + 10, end).trim(); 247 if (str.length() > 0) 248 { 249 fileName = str; 250 } 251 } 252 } 253 return fileName; 254 } 255 256 /*** 257 * <p> Retrieves field name from <code>Content-disposition</code> header. 258 * 259 * @param headers The HTTP request headers. 260 * @return The field name for the current <code>encapsulation</code>. 261 */ 262 protected String getFieldName(Map headers) 263 { 264 String fieldName = null; 265 String cd = getHeader(headers, CONTENT_DISPOSITION); 266 if(cd != null && cd.startsWith(FORM_DATA)) 267 { 268 int start = cd.indexOf("name=\""); 269 int end = cd.indexOf('"', start + 6); 270 if(start != -1 && end != -1) 271 { 272 fieldName = cd.substring(start + 6, end); 273 } 274 } 275 return fieldName; 276 } 277 278 /*** 279 * <p> Creates a new instance of {@link 280 * org.apache.turbine.util.upload.FileItem}. 281 * 282 * @param path The path for the FileItem. 283 * @param headers The HTTP request headers. 284 * @param requestSize The size of the request. 285 * @return A newly created <code>FileItem</code>. 286 */ 287 protected FileItem createItem( String path, 288 Map headers, 289 int requestSize ) 290 { 291 return FileItem.newInstance(path, 292 getFileName(headers), 293 getHeader(headers, CONTENT_TYPE), 294 requestSize); 295 } 296 297 /*** 298 * <p> Parses the <code>header-part</code> and returns as key/value 299 * pairs. 300 * 301 * <p> If there are multiple headers of the same names, the name 302 * will map to a comma-separated list containing the values. 303 * 304 * @param headerPart The <code>header-part</code> of the current 305 * <code>encapsulation</code>. 306 * @return The parsed HTTP request headers. 307 */ 308 protected Map parseHeaders( String headerPart ) 309 { 310 Map headers = new HashMap(); 311 char buffer[] = new char[MAX_HEADER_SIZE]; 312 boolean done = false; 313 int j = 0; 314 int i; 315 String header, headerName, headerValue; 316 try 317 { 318 while (!done) 319 { 320 i=0; 321 // Copy a single line of characters into the buffer, 322 // omitting trailing CRLF. 323 while (i<2 || buffer[i-2] != '\r' || buffer[i-1] != '\n') 324 { 325 buffer[i++] = headerPart.charAt(j++); 326 } 327 header = new String(buffer, 0, i-2); 328 if (header.equals("")) 329 { 330 done = true; 331 } 332 else 333 { 334 if (header.indexOf(':') == -1) 335 { 336 // This header line is malformed, skip it. 337 continue; 338 } 339 headerName = header.substring(0, header.indexOf(':')) 340 .trim().toLowerCase(); 341 headerValue = 342 header.substring(header.indexOf(':') + 1).trim(); 343 if (getHeader(headers, headerName) != null) 344 { 345 // More that one heder of that name exists, 346 // append to the list. 347 headers.put(headerName, 348 getHeader(headers, headerName) + ',' + 349 headerValue); 350 } 351 else 352 { 353 headers.put(headerName, headerValue); 354 } 355 } 356 } 357 } 358 catch(IndexOutOfBoundsException e) 359 { 360 // Headers were malformed. continue with all that was 361 // parsed. 362 } 363 return headers; 364 } 365 366 /*** 367 * <p> Returns a header with specified name. 368 * 369 * @param headers The HTTP request headers. 370 * @param name The name of the header to fetch. 371 * @return The value of specified header, or a comma-separated 372 * list if there were multiple headers of that name. 373 */ 374 protected final String getHeader( Map headers, String name ) 375 { 376 return (String)headers.get(name.toLowerCase()); 377 } 378 }

This page was automatically generated by Maven