##### Coverage Report - org.apache.commons.fileupload.FileUploadBase

Classes in this File | Line Coverage | Branch Coverage | Complexity
FileUploadBase |
75% | 126/166
62% | 36/58
| 2.590909090909091;2,591
FileUploadBase$FileItemIteratorImpl |
60% | 60/100
62% | 31/50
| 2.590909090909091;2,591
FileUploadBase$FileItemIteratorImpl$1 |
100% | 5/5
N/A | N/A
| 2.590909090909091;2,591
FileUploadBase$FileItemIteratorImpl$FileItemStreamImpl |
94% | 33/35
80% | 8/10
| 2.590909090909091;2,591
FileUploadBase$FileItemIteratorImpl$FileItemStreamImpl$1 |
100% | 8/8
N/A | N/A
| 2.590909090909091;2,591
FileUploadBase$FileSizeLimitExceededException |
77% | 7/9
N/A | N/A
| 2.590909090909091;2,591
FileUploadBase$FileUploadIOException |
100% | 4/4
N/A | N/A
| 2.590909090909091;2,591
FileUploadBase$IOFileUploadException |
100% | 4/4
N/A | N/A
| 2.590909090909091;2,591
FileUploadBase$InvalidContentTypeException |
33% | 2/6
N/A | N/A
| 2.590909090909091;2,591
FileUploadBase$SizeException |
83% | 5/6
N/A | N/A
| 2.590909090909091;2,591
FileUploadBase$SizeLimitExceededException |
42% | 3/7
N/A | N/A
| 2.590909090909091;2,591
FileUploadBase$UnknownSizeException |
0% | 0/4
N/A | N/A
| 2.590909090909091;2,591

1 |  |
     /*
2 |  |
      * Licensed to the Apache Software Foundation (ASF) under one or more
3 |  |
      * contributor license agreements.  See the NOTICE file distributed with
4 |  |
      * this work for additional information regarding copyright ownership.
5 |  |
      * The ASF licenses this file to You under the Apache License, Version 2.0
6 |  |
      * (the "License"); you may not use this file except in compliance with
7 |  |
      * the License.  You may obtain a copy of the License at
8 |  |
      *
9 |  |
      *      http://www.apache.org/licenses/LICENSE-2.0
10 |  |
      *
11 |  |
      * Unless required by applicable law or agreed to in writing, software
12 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
13 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 |  |
      * See the License for the specific language governing permissions and
15 |  |
      * limitations under the License.
16 |  |
      */
17 |  |
     package org.apache.commons.fileupload;
18 |  |
19 |  |
     import static java.lang.String.format;
20 |  |
21 |  |
     import java.io.IOException;
22 |  |
     import java.io.InputStream;
23 |  |
     import java.io.UnsupportedEncodingException;
24 |  |
     import java.util.ArrayList;
25 |  |
     import java.util.HashMap;
26 |  |
     import java.util.Iterator;
27 |  |
     import java.util.List;
28 |  |
     import java.util.Locale;
29 |  |
     import java.util.Map;
30 |  |
     import java.util.NoSuchElementException;
31 |  |
32 |  |
     import javax.servlet.http.HttpServletRequest;
33 |  |
34 |  |
     import org.apache.commons.fileupload.MultipartStream.ItemInputStream;
35 |  |
     import org.apache.commons.fileupload.servlet.ServletFileUpload;
36 |  |
     import org.apache.commons.fileupload.servlet.ServletRequestContext;
37 |  |
     import org.apache.commons.fileupload.util.Closeable;
38 |  |
     import org.apache.commons.fileupload.util.FileItemHeadersImpl;
39 |  |
     import org.apache.commons.fileupload.util.LimitedInputStream;
40 |  |
     import org.apache.commons.fileupload.util.Streams;
41 |  |
     import org.apache.commons.io.IOUtils;
42 |  |
43 |  |
     /**
44 |  |
      * <p>High level API for processing file uploads.</p>
45 |  |
      *
46 |  |
      * <p>This class handles multiple files per single HTML widget, sent using
47 |  |
      * <code>multipart/mixed</code> encoding type, as specified by
48 |  |
      * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>.  Use {@link
49 |  |
      * #parseRequest(RequestContext)} to acquire a list of {@link
50 |  |
      * org.apache.commons.fileupload.FileItem}s associated with a given HTML
51 |  |
      * widget.</p>
52 |  |
      *
53 |  |
      * <p>How the data for individual parts is stored is determined by the factory
54 |  |
      * used to create them; a given part may be in memory, on disk, or somewhere
55 |  |
      * else.</p>
56 |  |
      */
57 | 3335 |
     public abstract class FileUploadBase {
58 |  |
59 |  |
         // ---------------------------------------------------------- Class methods
60 |  |
61 |  |
         /**
62 |  |
          * <p>Utility method that determines whether the request contains multipart
63 |  |
          * content.</p>
64 |  |
          *
65 |  |
          * <p><strong>NOTE:</strong>This method will be moved to the
66 |  |
          * <code>ServletFileUpload</code> class after the FileUpload 1.1 release.
67 |  |
          * Unfortunately, since this method is static, it is not possible to
68 |  |
          * provide its replacement until this method is removed.</p>
69 |  |
          *
70 |  |
          * @param ctx The request context to be evaluated. Must be non-null.
71 |  |
          *
72 |  |
          * @return <code>true</code> if the request is multipart;
73 |  |
          *         <code>false</code> otherwise.
74 |  |
          */
75 |  |
         public static final boolean isMultipartContent(RequestContext ctx) {
76 | 0 |
             String contentType = ctx.getContentType();
77 | 0 |
             if (contentType == null) {
78 | 0 |
                 return false;
79 |  |
             }
80 | 0 |
             if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
81 | 0 |
                 return true;
82 |  |
             }
83 | 0 |
             return false;
84 |  |
         }
85 |  |
86 |  |
         /**
87 |  |
          * Utility method that determines whether the request contains multipart
88 |  |
          * content.
89 |  |
          *
90 |  |
          * @param req The servlet request to be evaluated. Must be non-null.
91 |  |
          *
92 |  |
          * @return <code>true</code> if the request is multipart;
93 |  |
          *         <code>false</code> otherwise.
94 |  |
          *
95 |  |
          * @deprecated 1.1 Use the method on <code>ServletFileUpload</code> instead.
96 |  |
          */
97 |  |
         @Deprecated
98 |  |
         public static boolean isMultipartContent(HttpServletRequest req) {
99 | 0 |
             return ServletFileUpload.isMultipartContent(req);
100 |  |
         }
101 |  |
102 |  |
         // ----------------------------------------------------- Manifest constants
103 |  |
104 |  |
         /**
105 |  |
          * HTTP content type header name.
106 |  |
          */
107 |  |
         public static final String CONTENT_TYPE = "Content-type";
108 |  |
109 |  |
         /**
110 |  |
          * HTTP content disposition header name.
111 |  |
          */
112 |  |
         public static final String CONTENT_DISPOSITION = "Content-disposition";
113 |  |
114 |  |
         /**
115 |  |
          * HTTP content length header name.
116 |  |
          */
117 |  |
         public static final String CONTENT_LENGTH = "Content-length";
118 |  |
119 |  |
         /**
120 |  |
          * Content-disposition value for form data.
121 |  |
          */
122 |  |
         public static final String FORM_DATA = "form-data";
123 |  |
124 |  |
         /**
125 |  |
          * Content-disposition value for file attachment.
126 |  |
          */
127 |  |
         public static final String ATTACHMENT = "attachment";
128 |  |
129 |  |
         /**
130 |  |
          * Part of HTTP content type header.
131 |  |
          */
132 |  |
         public static final String MULTIPART = "multipart/";
133 |  |
134 |  |
         /**
135 |  |
          * HTTP content type header for multipart forms.
136 |  |
          */
137 |  |
         public static final String MULTIPART_FORM_DATA = "multipart/form-data";
138 |  |
139 |  |
         /**
140 |  |
          * HTTP content type header for multiple uploads.
141 |  |
          */
142 |  |
         public static final String MULTIPART_MIXED = "multipart/mixed";
143 |  |
144 |  |
         /**
145 |  |
          * The maximum length of a single header line that will be parsed
146 |  |
          * (1024 bytes).
147 |  |
          * @deprecated This constant is no longer used. As of commons-fileupload
148 |  |
          *   1.2, the only applicable limit is the total size of a parts headers,
149 |  |
          *   {@link MultipartStream#HEADER_PART_SIZE_MAX}.
150 |  |
          */
151 |  |
         @Deprecated
152 |  |
         public static final int MAX_HEADER_SIZE = 1024;
153 |  |
154 |  |
         // ----------------------------------------------------------- Data members
155 |  |
156 |  |
         /**
157 |  |
          * The maximum size permitted for the complete request, as opposed to
158 |  |
          * {@link #fileSizeMax}. A value of -1 indicates no maximum.
159 |  |
          */
160 | 24 |
         private long sizeMax = -1;
161 |  |
162 |  |
         /**
163 |  |
          * The maximum size permitted for a single uploaded file, as opposed
164 |  |
          * to {@link #sizeMax}. A value of -1 indicates no maximum.
165 |  |
          */
166 | 24 |
         private long fileSizeMax = -1;
167 |  |
168 |  |
         /**
169 |  |
          * The content encoding to use when reading part headers.
170 |  |
          */
171 |  |
         private String headerEncoding;
172 |  |
173 |  |
         /**
174 |  |
          * The progress listener.
175 |  |
          */
176 |  |
         private ProgressListener listener;
177 |  |
178 |  |
         // ----------------------------------------------------- Property accessors
179 |  |
180 |  |
         /**
181 |  |
          * Returns the factory class used when creating file items.
182 |  |
          *
183 |  |
          * @return The factory class for new file items.
184 |  |
          */
185 |  |
         public abstract FileItemFactory getFileItemFactory();
186 |  |
187 |  |
         /**
188 |  |
          * Sets the factory class to use when creating file items.
189 |  |
          *
190 |  |
          * @param factory The factory class for new file items.
191 |  |
          */
192 |  |
         public abstract void setFileItemFactory(FileItemFactory factory);
193 |  |
194 |  |
         /**
195 |  |
          * Returns the maximum allowed size of a complete request, as opposed
196 |  |
          * to {@link #getFileSizeMax()}.
197 |  |
          *
198 |  |
          * @return The maximum allowed size, in bytes. The default value of
199 |  |
          *   -1 indicates, that there is no limit.
200 |  |
          *
201 |  |
          * @see #setSizeMax(long)
202 |  |
          *
203 |  |
          */
204 |  |
         public long getSizeMax() {
205 | 0 |
             return sizeMax;
206 |  |
         }
207 |  |
208 |  |
         /**
209 |  |
          * Sets the maximum allowed size of a complete request, as opposed
210 |  |
          * to {@link #setFileSizeMax(long)}.
211 |  |
          *
212 |  |
          * @param sizeMax The maximum allowed size, in bytes. The default value of
213 |  |
          *   -1 indicates, that there is no limit.
214 |  |
          *
215 |  |
          * @see #getSizeMax()
216 |  |
          *
217 |  |
          */
218 |  |
         public void setSizeMax(long sizeMax) {
219 | 2 |
             this.sizeMax = sizeMax;
220 | 2 |
         }
221 |  |
222 |  |
         /**
223 |  |
          * Returns the maximum allowed size of a single uploaded file,
224 |  |
          * as opposed to {@link #getSizeMax()}.
225 |  |
          *
226 |  |
          * @see #setFileSizeMax(long)
227 |  |
          * @return Maximum size of a single uploaded file.
228 |  |
          */
229 |  |
         public long getFileSizeMax() {
230 | 0 |
             return fileSizeMax;
231 |  |
         }
232 |  |
233 |  |
         /**
234 |  |
          * Sets the maximum allowed size of a single uploaded file,
235 |  |
          * as opposed to {@link #getSizeMax()}.
236 |  |
          *
237 |  |
          * @see #getFileSizeMax()
238 |  |
          * @param fileSizeMax Maximum size of a single uploaded file.
239 |  |
          */
240 |  |
         public void setFileSizeMax(long fileSizeMax) {
241 | 9 |
             this.fileSizeMax = fileSizeMax;
242 | 9 |
         }
243 |  |
244 |  |
         /**
245 |  |
          * Retrieves the character encoding used when reading the headers of an
246 |  |
          * individual part. When not specified, or <code>null</code>, the request
247 |  |
          * encoding is used. If that is also not specified, or <code>null</code>,
248 |  |
          * the platform default encoding is used.
249 |  |
          *
250 |  |
          * @return The encoding used to read part headers.
251 |  |
          */
252 |  |
         public String getHeaderEncoding() {
253 | 0 |
             return headerEncoding;
254 |  |
         }
255 |  |
256 |  |
         /**
257 |  |
          * Specifies the character encoding to be used when reading the headers of
258 |  |
          * individual part. When not specified, or <code>null</code>, the request
259 |  |
          * encoding is used. If that is also not specified, or <code>null</code>,
260 |  |
          * the platform default encoding is used.
261 |  |
          *
262 |  |
          * @param encoding The encoding used to read part headers.
263 |  |
          */
264 |  |
         public void setHeaderEncoding(String encoding) {
265 | 0 |
             headerEncoding = encoding;
266 | 0 |
         }
267 |  |
268 |  |
         // --------------------------------------------------------- Public methods
269 |  |
270 |  |
         /**
271 |  |
          * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
272 |  |
          * compliant <code>multipart/form-data</code> stream.
273 |  |
          *
274 |  |
          * @param req The servlet request to be parsed.
275 |  |
          *
276 |  |
          * @return A list of <code>FileItem</code> instances parsed from the
277 |  |
          *         request, in the order that they were transmitted.
278 |  |
          *
279 |  |
          * @throws FileUploadException if there are problems reading/parsing
280 |  |
          *                             the request or storing files.
281 |  |
          *
282 |  |
          * @deprecated 1.1 Use {@link ServletFileUpload#parseRequest(HttpServletRequest)} instead.
283 |  |
          */
284 |  |
         @Deprecated
285 |  |
         public List<FileItem> parseRequest(HttpServletRequest req)
286 |  |
         throws FileUploadException {
287 | 2 |
             return parseRequest(new ServletRequestContext(req));
288 |  |
         }
289 |  |
290 |  |
         /**
291 |  |
          * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
292 |  |
          * compliant <code>multipart/form-data</code> stream.
293 |  |
          *
294 |  |
          * @param ctx The context for the request to be parsed.
295 |  |
          *
296 |  |
          * @return An iterator to instances of <code>FileItemStream</code>
297 |  |
          *         parsed from the request, in the order that they were
298 |  |
          *         transmitted.
299 |  |
          *
300 |  |
          * @throws FileUploadException if there are problems reading/parsing
301 |  |
          *                             the request or storing files.
302 |  |
          * @throws IOException An I/O error occurred. This may be a network
303 |  |
          *   error while communicating with the client or a problem while
304 |  |
          *   storing the uploaded content.
305 |  |
          */
306 |  |
         public FileItemIterator getItemIterator(RequestContext ctx)
307 |  |
         throws FileUploadException, IOException {
308 |  |
             try {
309 | 38 |
                 return new FileItemIteratorImpl(ctx);
310 | 1 |
             } catch (FileUploadIOException e) {
311 |  |
                 // unwrap encapsulated SizeException
312 | 1 |
                 throw (FileUploadException) e.getCause();
313 |  |
             }
314 |  |
         }
315 |  |
316 |  |
         /**
317 |  |
          * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
318 |  |
          * compliant <code>multipart/form-data</code> stream.
319 |  |
          *
320 |  |
          * @param ctx The context for the request to be parsed.
321 |  |
          *
322 |  |
          * @return A list of <code>FileItem</code> instances parsed from the
323 |  |
          *         request, in the order that they were transmitted.
324 |  |
          *
325 |  |
          * @throws FileUploadException if there are problems reading/parsing
326 |  |
          *                             the request or storing files.
327 |  |
          */
328 |  |
         public List<FileItem> parseRequest(RequestContext ctx)
329 |  |
                 throws FileUploadException {
330 | 34 |
             List<FileItem> items = new ArrayList<FileItem>();
331 | 34 |
             boolean successful = false;
332 |  |
             try {
333 | 34 |
                 FileItemIterator iter = getItemIterator(ctx);
334 | 29 |
                 FileItemFactory fac = getFileItemFactory();
335 | 29 |
                 if (fac == null) {
336 | 0 |
                     throw new NullPointerException("No FileItemFactory has been set.");
337 |  |
                 }
338 | 2188 |
                 while (iter.hasNext()) {
339 | 2162 |
                     final FileItemStream item = iter.next();
340 |  |
                     // Don't use getName() here to prevent an InvalidFileNameException.
341 | 2162 |
                     final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;
342 | 4324 |
                     FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
343 | 2162 |
                                                        item.isFormField(), fileName);
344 | 2162 |
                     items.add(fileItem);
345 |  |
                     try {
346 | 2162 |
                         Streams.copy(item.openStream(), fileItem.getOutputStream(), true);
347 | 2 |
                     } catch (FileUploadIOException e) {
348 | 2 |
                         throw (FileUploadException) e.getCause();
349 | 1 |
                     } catch (IOException e) {
350 | 2 |
                         throw new IOFileUploadException(format("Processing of %s request failed. %s",
351 | 1 |
                                                                MULTIPART_FORM_DATA, e.getMessage()), e);
352 | 2159 |
                     }
353 | 2159 |
                     final FileItemHeaders fih = item.getHeaders();
354 | 2159 |
                     fileItem.setHeaders(fih);
355 | 2159 |
                 }
356 | 26 |
                 successful = true;
357 | 52 |
                 return items;
358 | 0 |
             } catch (FileUploadIOException e) {
359 | 0 |
                 throw (FileUploadException) e.getCause();
360 | 1 |
             } catch (IOException e) {
361 | 1 |
                 throw new FileUploadException(e.getMessage(), e);
362 |  |
             } finally {
363 | 34 |
                 if (!successful) {
364 | 8 |
                     for (FileItem fileItem : items) {
365 |  |
                         try {
366 | 701 |
                             fileItem.delete();
367 | 0 |
                         } catch (Exception ignored) {
368 |  |
                             // ignored TODO perhaps add to tracker delete failure list somehow?
369 | 701 |
                         }
370 | 701 |
                     }
371 |  |
                 }
372 |  |
             }
373 |  |
         }
374 |  |
375 |  |
         /**
376 |  |
          * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
377 |  |
          * compliant <code>multipart/form-data</code> stream.
378 |  |
          *
379 |  |
          * @param ctx The context for the request to be parsed.
380 |  |
          *
381 |  |
          * @return A map of <code>FileItem</code> instances parsed from the request.
382 |  |
          *
383 |  |
          * @throws FileUploadException if there are problems reading/parsing
384 |  |
          *                             the request or storing files.
385 |  |
          *
386 |  |
          * @since 1.3
387 |  |
          */
388 |  |
         public Map<String, List<FileItem>> parseParameterMap(RequestContext ctx)
389 |  |
                 throws FileUploadException {
390 | 2 |
             final List<FileItem> items = parseRequest(ctx);
391 | 2 |
             final Map<String, List<FileItem>> itemsMap = new HashMap<String, List<FileItem>>(items.size());
392 |  |
393 | 2 |
             for (FileItem fileItem : items) {
394 | 8 |
                 String fieldName = fileItem.getFieldName();
395 | 8 |
                 List<FileItem> mappedItems = itemsMap.get(fieldName);
396 |  |
397 | 8 |
                 if (mappedItems == null) {
398 | 6 |
                     mappedItems = new ArrayList<FileItem>();
399 | 6 |
                     itemsMap.put(fieldName, mappedItems);
400 |  |
                 }
401 |  |
402 | 8 |
                 mappedItems.add(fileItem);
403 | 8 |
             }
404 |  |
405 | 2 |
             return itemsMap;
406 |  |
         }
407 |  |
408 |  |
         // ------------------------------------------------------ Protected methods
409 |  |
410 |  |
         /**
411 |  |
          * Retrieves the boundary from the <code>Content-type</code> header.
412 |  |
          *
413 |  |
          * @param contentType The value of the content type header from which to
414 |  |
          *                    extract the boundary value.
415 |  |
          *
416 |  |
          * @return The boundary, as a byte array.
417 |  |
          */
418 |  |
         protected byte[] getBoundary(String contentType) {
419 | 40 |
             ParameterParser parser = new ParameterParser();
420 | 40 |
             parser.setLowerCaseNames(true);
421 |  |
             // Parameter parser can handle null input
422 | 40 |
             Map<String, String> params = parser.parse(contentType, new char[] {';', ','});
423 | 40 |
             String boundaryStr = params.get("boundary");
424 |  |
425 | 40 |
             if (boundaryStr == null) {
426 | 1 |
                 return null;
427 |  |
             }
428 |  |
             byte[] boundary;
429 |  |
             try {
430 | 39 |
                 boundary = boundaryStr.getBytes("ISO-8859-1");
431 | 0 |
             } catch (UnsupportedEncodingException e) {
432 | 0 |
                 boundary = boundaryStr.getBytes(); // Intentionally falls back to default charset
433 | 39 |
             }
434 | 39 |
             return boundary;
435 |  |
         }
436 |  |
437 |  |
         /**
438 |  |
          * Retrieves the file name from the <code>Content-disposition</code>
439 |  |
          * header.
440 |  |
          *
441 |  |
          * @param headers A <code>Map</code> containing the HTTP request headers.
442 |  |
          *
443 |  |
          * @return The file name for the current <code>encapsulation</code>.
444 |  |
          * @deprecated 1.2.1 Use {@link #getFileName(FileItemHeaders)}.
445 |  |
          */
446 |  |
         @Deprecated
447 |  |
         protected String getFileName(Map<String, String> headers) {
448 | 0 |
             return getFileName(getHeader(headers, CONTENT_DISPOSITION));
449 |  |
         }
450 |  |
451 |  |
         /**
452 |  |
          * Retrieves the file name from the <code>Content-disposition</code>
453 |  |
          * header.
454 |  |
          *
455 |  |
          * @param headers The HTTP headers object.
456 |  |
          *
457 |  |
          * @return The file name for the current <code>encapsulation</code>.
458 |  |
          */
459 |  |
         protected String getFileName(FileItemHeaders headers) {
460 | 3190 |
             return getFileName(headers.getHeader(CONTENT_DISPOSITION));
461 |  |
         }
462 |  |
463 |  |
         /**
464 |  |
          * Returns the given content-disposition headers file name.
465 |  |
          * @param pContentDisposition The content-disposition headers value.
466 |  |
          * @return The file name
467 |  |
          */
468 |  |
         private String getFileName(String pContentDisposition) {
469 | 3190 |
             String fileName = null;
470 | 3190 |
             if (pContentDisposition != null) {
471 | 3190 |
                 String cdl = pContentDisposition.toLowerCase(Locale.ENGLISH);
472 | 3190 |
                 if (cdl.startsWith(FORM_DATA) || cdl.startsWith(ATTACHMENT)) {
473 | 3190 |
                     ParameterParser parser = new ParameterParser();
474 | 3190 |
                     parser.setLowerCaseNames(true);
475 |  |
                     // Parameter parser can handle null input
476 | 3190 |
                     Map<String, String> params = parser.parse(pContentDisposition, ';');
477 | 3190 |
                     if (params.containsKey("filename")) {
478 | 29 |
                         fileName = params.get("filename");
479 | 29 |
                         if (fileName != null) {
480 | 27 |
                             fileName = fileName.trim();
481 |  |
                         } else {
482 |  |
                             // Even if there is no value, the parameter is present,
483 |  |
                             // so we return an empty file name rather than no file
484 |  |
                             // name.
485 | 2 |
                             fileName = "";
486 |  |
                         }
487 |  |
                     }
488 |  |
                 }
489 |  |
             }
490 | 3190 |
             return fileName;
491 |  |
         }
492 |  |
493 |  |
         /**
494 |  |
          * Retrieves the field name from the <code>Content-disposition</code>
495 |  |
          * header.
496 |  |
          *
497 |  |
          * @param headers A <code>Map</code> containing the HTTP request headers.
498 |  |
          *
499 |  |
          * @return The field name for the current <code>encapsulation</code>.
500 |  |
          */
501 |  |
         protected String getFieldName(FileItemHeaders headers) {
502 | 3188 |
             return getFieldName(headers.getHeader(CONTENT_DISPOSITION));
503 |  |
         }
504 |  |
505 |  |
         /**
506 |  |
          * Returns the field name, which is given by the content-disposition
507 |  |
          * header.
508 |  |
          * @param pContentDisposition The content-dispositions header value.
509 |  |
          * @return The field jake
510 |  |
          */
511 |  |
         private String getFieldName(String pContentDisposition) {
512 | 3188 |
             String fieldName = null;
513 | 3188 |
             if (pContentDisposition != null
514 | 3188 |
                     && pContentDisposition.toLowerCase(Locale.ENGLISH).startsWith(FORM_DATA)) {
515 | 3188 |
                 ParameterParser parser = new ParameterParser();
516 | 3188 |
                 parser.setLowerCaseNames(true);
517 |  |
                 // Parameter parser can handle null input
518 | 3188 |
                 Map<String, String> params = parser.parse(pContentDisposition, ';');
519 | 3188 |
                 fieldName = params.get("name");
520 | 3188 |
                 if (fieldName != null) {
521 | 3188 |
                     fieldName = fieldName.trim();
522 |  |
                 }
523 |  |
             }
524 | 3188 |
             return fieldName;
525 |  |
         }
526 |  |
527 |  |
         /**
528 |  |
          * Retrieves the field name from the <code>Content-disposition</code>
529 |  |
          * header.
530 |  |
          *
531 |  |
          * @param headers A <code>Map</code> containing the HTTP request headers.
532 |  |
          *
533 |  |
          * @return The field name for the current <code>encapsulation</code>.
534 |  |
          * @deprecated 1.2.1 Use {@link #getFieldName(FileItemHeaders)}.
535 |  |
          */
536 |  |
         @Deprecated
537 |  |
         protected String getFieldName(Map<String, String> headers) {
538 | 0 |
             return getFieldName(getHeader(headers, CONTENT_DISPOSITION));
539 |  |
         }
540 |  |
541 |  |
         /**
542 |  |
          * Creates a new {@link FileItem} instance.
543 |  |
          *
544 |  |
          * @param headers       A <code>Map</code> containing the HTTP request
545 |  |
          *                      headers.
546 |  |
          * @param isFormField   Whether or not this item is a form field, as
547 |  |
          *                      opposed to a file.
548 |  |
          *
549 |  |
          * @return A newly created <code>FileItem</code> instance.
550 |  |
          *
551 |  |
          * @throws FileUploadException if an error occurs.
552 |  |
          * @deprecated 1.2 This method is no longer used in favour of
553 |  |
          *   internally created instances of {@link FileItem}.
554 |  |
          */
555 |  |
         @Deprecated
556 |  |
         protected FileItem createItem(Map<String, String> headers,
557 |  |
                                       boolean isFormField)
558 |  |
             throws FileUploadException {
559 | 0 |
             return getFileItemFactory().createItem(getFieldName(headers),
560 | 0 |
                     getHeader(headers, CONTENT_TYPE),
561 |  |
                     isFormField,
562 | 0 |
                     getFileName(headers));
563 |  |
         }
564 |  |
565 |  |
         /**
566 |  |
          * <p> Parses the <code>header-part</code> and returns as key/value
567 |  |
          * pairs.
568 |  |
          *
569 |  |
          * <p> If there are multiple headers of the same names, the name
570 |  |
          * will map to a comma-separated list containing the values.
571 |  |
          *
572 |  |
          * @param headerPart The <code>header-part</code> of the current
573 |  |
          *                   <code>encapsulation</code>.
574 |  |
          *
575 |  |
          * @return A <code>Map</code> containing the parsed HTTP request headers.
576 |  |
          */
577 |  |
         protected FileItemHeaders getParsedHeaders(String headerPart) {
578 | 3194 |
             final int len = headerPart.length();
579 | 3194 |
             FileItemHeadersImpl headers = newFileItemHeaders();
580 | 3194 |
             int start = 0;
581 |  |
             for (;;) {
582 | 6434 |
                 int end = parseEndOfLine(headerPart, start);
583 | 6434 |
                 if (start == end) {
584 | 3194 |
                     break;
585 |  |
                 }
586 | 3240 |
                 StringBuilder header = new StringBuilder(headerPart.substring(start, end));
587 | 3240 |
                 start = end + 2;
588 | 3248 |
                 while (start < len) {
589 | 3248 |
                     int nonWs = start;
590 | 3272 |
                     while (nonWs < len) {
591 | 3272 |
                         char c = headerPart.charAt(nonWs);
592 | 3272 |
                         if (c != ' '  &&  c != '\t') {
593 | 3248 |
                             break;
594 |  |
                         }
595 | 24 |
                         ++nonWs;
596 | 24 |
                     }
597 | 3248 |
                     if (nonWs == start) {
598 | 3240 |
                         break;
599 |  |
                     }
600 |  |
                     // Continuation line found
601 | 8 |
                     end = parseEndOfLine(headerPart, nonWs);
602 | 8 |
                     header.append(" ").append(headerPart.substring(nonWs, end));
603 | 8 |
                     start = end + 2;
604 | 8 |
                 }
605 | 3240 |
                 parseHeaderLine(headers, header.toString());
606 | 3240 |
             }
607 | 3194 |
             return headers;
608 |  |
         }
609 |  |
610 |  |
         /**
611 |  |
          * Creates a new instance of {@link FileItemHeaders}.
612 |  |
          * @return The new instance.
613 |  |
          */
614 |  |
         protected FileItemHeadersImpl newFileItemHeaders() {
615 | 3194 |
             return new FileItemHeadersImpl();
616 |  |
         }
617 |  |
618 |  |
         /**
619 |  |
          * <p> Parses the <code>header-part</code> and returns as key/value
620 |  |
          * pairs.
621 |  |
          *
622 |  |
          * <p> If there are multiple headers of the same names, the name
623 |  |
          * will map to a comma-separated list containing the values.
624 |  |
          *
625 |  |
          * @param headerPart The <code>header-part</code> of the current
626 |  |
          *                   <code>encapsulation</code>.
627 |  |
          *
628 |  |
          * @return A <code>Map</code> containing the parsed HTTP request headers.
629 |  |
          * @deprecated 1.2.1 Use {@link #getParsedHeaders(String)}
630 |  |
          */
631 |  |
         @Deprecated
632 |  |
         protected Map<String, String> parseHeaders(String headerPart) {
633 | 0 |
             FileItemHeaders headers = getParsedHeaders(headerPart);
634 | 0 |
             Map<String, String> result = new HashMap<String, String>();
635 | 0 |
             for (Iterator<String> iter = headers.getHeaderNames();  iter.hasNext();) {
636 | 0 |
                 String headerName = iter.next();
637 | 0 |
                 Iterator<String> iter2 = headers.getHeaders(headerName);
638 | 0 |
                 StringBuilder headerValue = new StringBuilder(iter2.next());
639 | 0 |
                 while (iter2.hasNext()) {
640 | 0 |
                     headerValue.append(",").append(iter2.next());
641 |  |
                 }
642 | 0 |
                 result.put(headerName, headerValue.toString());
643 | 0 |
             }
644 | 0 |
             return result;
645 |  |
         }
646 |  |
647 |  |
         /**
648 |  |
          * Skips bytes until the end of the current line.
649 |  |
          * @param headerPart The headers, which are being parsed.
650 |  |
          * @param end Index of the last byte, which has yet been
651 |  |
          *   processed.
652 |  |
          * @return Index of the \r\n sequence, which indicates
653 |  |
          *   end of line.
654 |  |
          */
655 |  |
         private int parseEndOfLine(String headerPart, int end) {
656 | 6442 |
             int index = end;
657 |  |
             for (;;) {
658 | 6442 |
                 int offset = headerPart.indexOf('\r', index);
659 | 6442 |
                 if (offset == -1  ||  offset + 1 >= headerPart.length()) {
660 | 0 |
                     throw new IllegalStateException(
661 |  |
                         "Expected headers to be terminated by an empty line.");
662 |  |
                 }
663 | 6442 |
                 if (headerPart.charAt(offset + 1) == '\n') {
664 | 6442 |
                     return offset;
665 |  |
                 }
666 | 0 |
                 index = offset + 1;
667 | 0 |
             }
668 |  |
         }
669 |  |
670 |  |
         /**
671 |  |
          * Reads the next header line.
672 |  |
          * @param headers String with all headers.
673 |  |
          * @param header Map where to store the current header.
674 |  |
          */
675 |  |
         private void parseHeaderLine(FileItemHeadersImpl headers, String header) {
676 | 3240 |
             final int colonOffset = header.indexOf(':');
677 | 3240 |
             if (colonOffset == -1) {
678 |  |
                 // This header line is malformed, skip it.
679 | 0 |
                 return;
680 |  |
             }
681 | 3240 |
             String headerName = header.substring(0, colonOffset).trim();
682 | 3240 |
             String headerValue =
683 | 3240 |
                 header.substring(header.indexOf(':') + 1).trim();
684 | 3240 |
             headers.addHeader(headerName, headerValue);
685 | 3240 |
         }
686 |  |
687 |  |
         /**
688 |  |
          * Returns the header with the specified name from the supplied map. The
689 |  |
          * header lookup is case-insensitive.
690 |  |
          *
691 |  |
          * @param headers A <code>Map</code> containing the HTTP request headers.
692 |  |
          * @param name    The name of the header to return.
693 |  |
          *
694 |  |
          * @return The value of specified header, or a comma-separated list if
695 |  |
          *         there were multiple headers of that name.
696 |  |
          * @deprecated 1.2.1 Use {@link FileItemHeaders#getHeader(String)}.
697 |  |
          */
698 |  |
         @Deprecated
699 |  |
         protected final String getHeader(Map<String, String> headers,
700 |  |
                 String name) {
701 | 0 |
             return headers.get(name.toLowerCase(Locale.ENGLISH));
702 |  |
         }
703 |  |
704 |  |
         /**
705 |  |
          * The iterator, which is returned by
706 |  |
          * {@link FileUploadBase#getItemIterator(RequestContext)}.
707 |  |
          */
708 | 0 |
         private class FileItemIteratorImpl implements FileItemIterator {
709 |  |
710 |  |
             /**
711 |  |
              * Default implementation of {@link FileItemStream}.
712 |  |
              */
713 | 2168 |
             class FileItemStreamImpl implements FileItemStream {
714 |  |
715 |  |
                 /**
716 |  |
                  * The file items content type.
717 |  |
                  */
718 |  |
                 private final String contentType;
719 |  |
720 |  |
                 /**
721 |  |
                  * The file items field name.
722 |  |
                  */
723 |  |
                 private final String fieldName;
724 |  |
725 |  |
                 /**
726 |  |
                  * The file items file name.
727 |  |
                  */
728 |  |
                 private final String name;
729 |  |
730 |  |
                 /**
731 |  |
                  * Whether the file item is a form field.
732 |  |
                  */
733 |  |
                 private final boolean formField;
734 |  |
735 |  |
                 /**
736 |  |
                  * The file items input stream.
737 |  |
                  */
738 |  |
                 private final InputStream stream;
739 |  |
740 |  |
                 /**
741 |  |
                  * Whether the file item was already opened.
742 |  |
                  */
743 |  |
                 private boolean opened;
744 |  |
745 |  |
                 /**
746 |  |
                  * The headers, if any.
747 |  |
                  */
748 |  |
                 private FileItemHeaders headers;
749 |  |
750 |  |
                 /**
751 |  |
                  * Creates a new instance.
752 |  |
                  *
753 |  |
                  * @param pName The items file name, or null.
754 |  |
                  * @param pFieldName The items field name.
755 |  |
                  * @param pContentType The items content type, or null.
756 |  |
                  * @param pFormField Whether the item is a form field.
757 |  |
                  * @param pContentLength The items content length, if known, or -1
758 |  |
                  * @throws IOException Creating the file item failed.
759 |  |
                  */
760 |  |
                 FileItemStreamImpl(String pName, String pFieldName,
761 |  |
                         String pContentType, boolean pFormField,
762 | 3190 |
                         long pContentLength) throws IOException {
763 | 3190 |
                     name = pName;
764 | 3190 |
                     fieldName = pFieldName;
765 | 3190 |
                     contentType = pContentType;
766 | 3190 |
                     formField = pFormField;
767 | 3190 |
                     final ItemInputStream itemStream = multi.newInputStream();
768 | 3190 |
                     InputStream istream = itemStream;
769 | 3190 |
                     if (fileSizeMax != -1) {
770 | 5 |
                         if (pContentLength != -1
771 | 3 |
                                 &&  pContentLength > fileSizeMax) {
772 | 1 |
                             FileSizeLimitExceededException e =
773 |  |
                                 new FileSizeLimitExceededException(
774 | 1 |
                                     format("The field %s exceeds its maximum permitted size of %s bytes.",
775 | 1 |
                                            fieldName, Long.valueOf(fileSizeMax)),
776 | 1 |
                                     pContentLength, fileSizeMax);
777 | 1 |
                             e.setFileName(pName);
778 | 1 |
                             e.setFieldName(pFieldName);
779 | 1 |
                             throw new FileUploadIOException(e);
780 |  |
                         }
781 | 4 |
                         istream = new LimitedInputStream(istream, fileSizeMax) {
782 |  |
                             @Override
783 |  |
                             protected void raiseError(long pSizeMax, long pCount)
784 |  |
                                     throws IOException {
785 | 2 |
                                 itemStream.close(true);
786 | 2 |
                                 FileSizeLimitExceededException e =
787 |  |
                                     new FileSizeLimitExceededException(
788 | 2 |
                                         format("The field %s exceeds its maximum permitted size of %s bytes.",
789 | 2 |
                                                fieldName, Long.valueOf(pSizeMax)),
790 |  |
                                         pCount, pSizeMax);
791 | 2 |
                                 e.setFieldName(fieldName);
792 | 2 |
                                 e.setFileName(name);
793 | 2 |
                                 throw new FileUploadIOException(e);
794 |  |
                             }
795 |  |
                         };
796 |  |
                     }
797 | 3189 |
                     stream = istream;
798 | 3189 |
                 }
799 |  |
800 |  |
                 /**
801 |  |
                  * Returns the items content type, or null.
802 |  |
                  *
803 |  |
                  * @return Content type, if known, or null.
804 |  |
                  */
805 |  |
                 public String getContentType() {
806 | 2162 |
                     return contentType;
807 |  |
                 }
808 |  |
809 |  |
                 /**
810 |  |
                  * Returns the items field name.
811 |  |
                  *
812 |  |
                  * @return Field name.
813 |  |
                  */
814 |  |
                 public String getFieldName() {
815 | 2163 |
                     return fieldName;
816 |  |
                 }
817 |  |
818 |  |
                 /**
819 |  |
                  * Returns the items file name.
820 |  |
                  *
821 |  |
                  * @return File name, if known, or null.
822 |  |
                  * @throws InvalidFileNameException The file name contains a NUL character,
823 |  |
                  *   which might be an indicator of a security attack. If you intend to
824 |  |
                  *   use the file name anyways, catch the exception and use
825 |  |
                  *   InvalidFileNameException#getName().
826 |  |
                  */
827 |  |
                 public String getName() {
828 | 2 |
                     return Streams.checkFileName(name);
829 |  |
                 }
830 |  |
831 |  |
                 /**
832 |  |
                  * Returns, whether this is a form field.
833 |  |
                  *
834 |  |
                  * @return True, if the item is a form field,
835 |  |
                  *   otherwise false.
836 |  |
                  */
837 |  |
                 public boolean isFormField() {
838 | 2163 |
                     return formField;
839 |  |
                 }
840 |  |
841 |  |
                 /**
842 |  |
                  * Returns an input stream, which may be used to
843 |  |
                  * read the items contents.
844 |  |
                  *
845 |  |
                  * @return Opened input stream.
846 |  |
                  * @throws IOException An I/O error occurred.
847 |  |
                  */
848 |  |
                 public InputStream openStream() throws IOException {
849 | 3188 |
                     if (opened) {
850 | 0 |
                         throw new IllegalStateException(
851 |  |
                                 "The stream was already opened.");
852 |  |
                     }
853 | 3188 |
                     if (((Closeable) stream).isClosed()) {
854 | 0 |
                         throw new FileItemStream.ItemSkippedException();
855 |  |
                     }
856 | 3188 |
                     return stream;
857 |  |
                 }
858 |  |
859 |  |
                 /**
860 |  |
                  * Closes the file item.
861 |  |
                  *
862 |  |
                  * @throws IOException An I/O error occurred.
863 |  |
                  */
864 |  |
                 void close() throws IOException {
865 | 3184 |
                     stream.close();
866 | 3184 |
                 }
867 |  |
868 |  |
                 /**
869 |  |
                  * Returns the file item headers.
870 |  |
                  *
871 |  |
                  * @return The items header object
872 |  |
                  */
873 |  |
                 public FileItemHeaders getHeaders() {
874 | 2159 |
                     return headers;
875 |  |
                 }
876 |  |
877 |  |
                 /**
878 |  |
                  * Sets the file item headers.
879 |  |
                  *
880 |  |
                  * @param pHeaders The items header object
881 |  |
                  */
882 |  |
                 public void setHeaders(FileItemHeaders pHeaders) {
883 | 3189 |
                     headers = pHeaders;
884 | 3189 |
                 }
885 |  |
886 |  |
             }
887 |  |
888 |  |
             /**
889 |  |
              * The multi part stream to process.
890 |  |
              */
891 |  |
             private final MultipartStream multi;
892 |  |
893 |  |
             /**
894 |  |
              * The notifier, which used for triggering the
895 |  |
              * {@link ProgressListener}.
896 |  |
              */
897 |  |
             private final MultipartStream.ProgressNotifier notifier;
898 |  |
899 |  |
             /**
900 |  |
              * The boundary, which separates the various parts.
901 |  |
              */
902 |  |
             private final byte[] boundary;
903 |  |
904 |  |
             /**
905 |  |
              * The item, which we currently process.
906 |  |
              */
907 |  |
             private FileItemStreamImpl currentItem;
908 |  |
909 |  |
             /**
910 |  |
              * The current items field name.
911 |  |
              */
912 |  |
             private String currentFieldName;
913 |  |
914 |  |
             /**
915 |  |
              * Whether we are currently skipping the preamble.
916 |  |
              */
917 |  |
             private boolean skipPreamble;
918 |  |
919 |  |
             /**
920 |  |
              * Whether the current item may still be read.
921 |  |
              */
922 |  |
             private boolean itemValid;
923 |  |
924 |  |
             /**
925 |  |
              * Whether we have seen the end of the file.
926 |  |
              */
927 |  |
             private boolean eof;
928 |  |
929 |  |
             /**
930 |  |
              * Creates a new instance.
931 |  |
              *
932 |  |
              * @param ctx The request context.
933 |  |
              * @throws FileUploadException An error occurred while
934 |  |
              *   parsing the request.
935 |  |
              * @throws IOException An I/O error occurred.
936 |  |
              */
937 |  |
             FileItemIteratorImpl(RequestContext ctx)
938 | 38 |
                     throws FileUploadException, IOException {
939 | 38 |
                 if (ctx == null) {
940 | 0 |
                     throw new NullPointerException("ctx parameter");
941 |  |
                 }
942 |  |
943 | 38 |
                 String contentType = ctx.getContentType();
944 | 38 |
                 if ((null == contentType)
945 | 37 |
                         || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) {
946 | 1 |
                     throw new InvalidContentTypeException(
947 | 1 |
                             format("the request doesn't contain a %s or %s stream, content type header is %s",
948 |  |
                                    MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType));
949 |  |
                 }
950 |  |
951 |  |
952 |  |
                 @SuppressWarnings("deprecation") // still has to be backward compatible
953 | 37 |
                 final int contentLengthInt = ctx.getContentLength();
954 |  |
955 | 37 |
                 final long requestSize = UploadContext.class.isAssignableFrom(ctx.getClass())
956 |  |
                                          // Inline conditional is OK here CHECKSTYLE:OFF
957 | 37 |
                                          ? ((UploadContext) ctx).contentLength()
958 |  |
                                          : contentLengthInt;
959 |  |
                                          // CHECKSTYLE:ON
960 |  |
961 |  |
                 InputStream input; // N.B. this is eventually closed in MultipartStream processing
962 | 37 |
                 if (sizeMax >= 0) {
963 | 2 |
                     if (requestSize != -1 && requestSize > sizeMax) {
964 | 1 |
                         throw new SizeLimitExceededException(
965 | 1 |
                             format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
966 | 1 |
                                     Long.valueOf(requestSize), Long.valueOf(sizeMax)),
967 | 1 |
                                    requestSize, sizeMax);
968 |  |
                     }
969 |  |
                     // N.B. this is eventually closed in MultipartStream processing
970 | 1 |
                     input = new LimitedInputStream(ctx.getInputStream(), sizeMax) {
971 |  |
                         @Override
972 |  |
                         protected void raiseError(long pSizeMax, long pCount)
973 |  |
                                 throws IOException {
974 | 2 |
                             FileUploadException ex = new SizeLimitExceededException(
975 | 2 |
                             format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
976 | 2 |
                                     Long.valueOf(pCount), Long.valueOf(pSizeMax)),
977 |  |
                                    pCount, pSizeMax);
978 | 2 |
                             throw new FileUploadIOException(ex);
979 |  |
                         }
980 |  |
                     };
981 |  |
                 } else {
982 | 35 |
                     input = ctx.getInputStream();
983 |  |
                 }
984 |  |
985 | 36 |
                 String charEncoding = headerEncoding;
986 | 36 |
                 if (charEncoding == null) {
987 | 36 |
                     charEncoding = ctx.getCharacterEncoding();
988 |  |
                 }
989 |  |
990 | 36 |
                 boundary = getBoundary(contentType);
991 | 36 |
                 if (boundary == null) {
992 | 1 |
                     IOUtils.closeQuietly(input); // avoid possible resource leak
993 | 1 |
                     throw new FileUploadException("the request was rejected because no multipart boundary was found");
994 |  |
                 }
995 |  |
996 | 35 |
                 notifier = new MultipartStream.ProgressNotifier(listener, requestSize);
997 |  |
                 try {
998 | 35 |
                     multi = new MultipartStream(input, boundary, notifier);
999 | 0 |
                 } catch (IllegalArgumentException iae) {
1000 | 0 |
                     IOUtils.closeQuietly(input); // avoid possible resource leak
1001 | 0 |
                     throw new InvalidContentTypeException(
1002 | 0 |
                             format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae);
1003 | 35 |
                 }
1004 | 35 |
                 multi.setHeaderEncoding(charEncoding);
1005 |  |
1006 | 35 |
                 skipPreamble = true;
1007 | 35 |
                 findNextItem();
1008 | 33 |
             }
1009 |  |
1010 |  |
             /**
1011 |  |
              * Called for finding the next item, if any.
1012 |  |
              *
1013 |  |
              * @return True, if an next item was found, otherwise false.
1014 |  |
              * @throws IOException An I/O error occurred.
1015 |  |
              */
1016 |  |
             private boolean findNextItem() throws IOException {
1017 | 3219 |
                 if (eof) {
1018 | 0 |
                     return false;
1019 |  |
                 }
1020 | 3219 |
                 if (currentItem != null) {
1021 | 3184 |
                     currentItem.close();
1022 | 3184 |
                     currentItem = null;
1023 |  |
                 }
1024 |  |
                 for (;;) {
1025 |  |
                     boolean nextPart;
1026 | 3227 |
                     if (skipPreamble) {
1027 | 3227 |
                         nextPart = multi.skipPreamble();
1028 |  |
                     } else {
1029 | 0 |
                         nextPart = multi.readBoundary();
1030 |  |
                     }
1031 | 3226 |
                     if (!nextPart) {
1032 | 32 |
                         if (currentFieldName == null) {
1033 |  |
                             // Outer multipart terminated -> No more data
1034 | 28 |
                             eof = true;
1035 | 28 |
                             return false;
1036 |  |
                         }
1037 |  |
                         // Inner multipart terminated -> Return to parsing the outer
1038 | 4 |
                         multi.setBoundary(boundary);
1039 | 4 |
                         currentFieldName = null;
1040 | 4 |
                         continue;
1041 |  |
                     }
1042 | 3194 |
                     FileItemHeaders headers = getParsedHeaders(multi.readHeaders());
1043 | 3194 |
                     if (currentFieldName == null) {
1044 |  |
                         // We're parsing the outer multipart
1045 | 3188 |
                         String fieldName = getFieldName(headers);
1046 | 3188 |
                         if (fieldName != null) {
1047 | 3188 |
                             String subContentType = headers.getHeader(CONTENT_TYPE);
1048 | 3188 |
                             if (subContentType != null
1049 | 25 |
                                     &&  subContentType.toLowerCase(Locale.ENGLISH)
1050 | 25 |
                                             .startsWith(MULTIPART_MIXED)) {
1051 | 4 |
                                 currentFieldName = fieldName;
1052 |  |
                                 // Multiple files associated with this field name
1053 | 4 |
                                 byte[] subBoundary = getBoundary(subContentType);
1054 | 4 |
                                 multi.setBoundary(subBoundary);
1055 | 4 |
                                 skipPreamble = true;
1056 | 4 |
                                 continue;
1057 |  |
                             }
1058 | 3184 |
                             String fileName = getFileName(headers);
1059 | 3184 |
                             currentItem = new FileItemStreamImpl(fileName,
1060 | 0 |
                                     fieldName, headers.getHeader(CONTENT_TYPE),
1061 | 0 |
                                     fileName == null, getContentLength(headers));
1062 | 0 |
                             currentItem.setHeaders(headers);
1063 | 0 |
                             notifier.noteItem();
1064 | 0 |
                             itemValid = true;
1065 | 0 |
                             return true;
1066 |  |
                         }
1067 | 0 |
                     } else {
1068 | 0 |
                         String fileName = getFileName(headers);
1069 | 0 |
                         if (fileName != null) {
1070 | 0 |
                             currentItem = new FileItemStreamImpl(fileName,
1071 |  |
                                     currentFieldName,
1072 | 0 |
                                     headers.getHeader(CONTENT_TYPE),
1073 | 0 |
                                     false, getContentLength(headers));
1074 | 0 |
                             currentItem.setHeaders(headers);
1075 | 0 |
                             notifier.noteItem();
1076 | 0 |
                             itemValid = true;
1077 | 0 |
                             return true;
1078 |  |
                         }
1079 |  |
                     }
1080 | 0 |
                     multi.discardBodyData();
1081 | 0 |
                 }
1082 |  |
             }
1083 |  |
1084 |  |
             private long getContentLength(FileItemHeaders pHeaders) {
1085 |  |
                 try {
1086 | 0 |
                     return Long.parseLong(pHeaders.getHeader(CONTENT_LENGTH));
1087 | 0 |
                 } catch (Exception e) {
1088 | 0 |
                     return -1;
1089 |  |
                 }
1090 |  |
             }
1091 |  |
1092 |  |
             /**
1093 |  |
              * Returns, whether another instance of {@link FileItemStream}
1094 |  |
              * is available.
1095 |  |
              *
1096 |  |
              * @throws FileUploadException Parsing or processing the
1097 |  |
              *   file item failed.
1098 |  |
              * @throws IOException Reading the file item failed.
1099 |  |
              * @return True, if one or more additional file items
1100 |  |
              *   are available, otherwise false.
1101 |  |
              */
1102 |  |
             public boolean hasNext() throws FileUploadException, IOException {
1103 | 0 |
                 if (eof) {
1104 | 0 |
                     return false;
1105 |  |
                 }
1106 | 0 |
                 if (itemValid) {
1107 | 0 |
                     return true;
1108 |  |
                 }
1109 |  |
                 try {
1110 | 0 |
                     return findNextItem();
1111 | 0 |
                 } catch (FileUploadIOException e) {
1112 |  |
                     // unwrap encapsulated SizeException
1113 | 0 |
                     throw (FileUploadException) e.getCause();
1114 |  |
                 }
1115 |  |
             }
1116 |  |
1117 |  |
             /**
1118 |  |
              * Returns the next available {@link FileItemStream}.
1119 |  |
              *
1120 |  |
              * @throws java.util.NoSuchElementException No more items are
1121 |  |
              *   available. Use {@link #hasNext()} to prevent this exception.
1122 |  |
              * @throws FileUploadException Parsing or processing the
1123 |  |
              *   file item failed.
1124 |  |
              * @throws IOException Reading the file item failed.
1125 |  |
              * @return FileItemStream instance, which provides
1126 |  |
              *   access to the next file item.
1127 |  |
              */
1128 |  |
             public FileItemStream next() throws FileUploadException, IOException {
1129 | 0 |
                 if (eof  ||  (!itemValid && !hasNext())) {
1130 | 0 |
                     throw new NoSuchElementException();
1131 |  |
                 }
1132 | 0 |
                 itemValid = false;
1133 | 0 |
                 return currentItem;
1134 |  |
             }
1135 |  |
1136 |  |
         }
1137 |  |
1138 |  |
         /**
1139 |  |
          * This exception is thrown for hiding an inner
1140 |  |
          * {@link FileUploadException} in an {@link IOException}.
1141 |  |
          */
1142 |  |
         public static class FileUploadIOException extends IOException {
1143 |  |
1144 |  |
             /**
1145 |  |
              * The exceptions UID, for serializing an instance.
1146 |  |
              */
1147 |  |
             private static final long serialVersionUID = -7047616958165584154L;
1148 |  |
1149 |  |
             /**
1150 |  |
              * The exceptions cause; we overwrite the parent
1151 |  |
              * classes field, which is available since Java
1152 |  |
              * 1.4 only.
1153 |  |
              */
1154 |  |
             private final FileUploadException cause;
1155 |  |
1156 |  |
             /**
1157 |  |
              * Creates a <code>FileUploadIOException</code> with the
1158 |  |
              * given cause.
1159 |  |
              *
1160 |  |
              * @param pCause The exceptions cause, if any, or null.
1161 |  |
              */
1162 | 5 |
             public FileUploadIOException(FileUploadException pCause) {
1163 |  |
                 // We're not doing super(pCause) cause of 1.3 compatibility.
1164 | 5 |
                 cause = pCause;
1165 | 5 |
             }
1166 |  |
1167 |  |
             /**
1168 |  |
              * Returns the exceptions cause.
1169 |  |
              *
1170 |  |
              * @return The exceptions cause, if any, or null.
1171 |  |
              */
1172 |  |
             @Override
1173 |  |
             public Throwable getCause() {
1174 | 3 |
                 return cause;
1175 |  |
             }
1176 |  |
1177 |  |
         }
1178 |  |
1179 |  |
         /**
1180 |  |
          * Thrown to indicate that the request is not a multipart request.
1181 |  |
          */
1182 |  |
         public static class InvalidContentTypeException
1183 |  |
                 extends FileUploadException {
1184 |  |
1185 |  |
             /**
1186 |  |
              * The exceptions UID, for serializing an instance.
1187 |  |
              */
1188 |  |
             private static final long serialVersionUID = -9073026332015646668L;
1189 |  |
1190 |  |
             /**
1191 |  |
              * Constructs a <code>InvalidContentTypeException</code> with no
1192 |  |
              * detail message.
1193 |  |
              */
1194 |  |
             public InvalidContentTypeException() {
1195 | 0 |
                 super();
1196 | 0 |
             }
1197 |  |
1198 |  |
             /**
1199 |  |
              * Constructs an <code>InvalidContentTypeException</code> with
1200 |  |
              * the specified detail message.
1201 |  |
              *
1202 |  |
              * @param message The detail message.
1203 |  |
              */
1204 |  |
             public InvalidContentTypeException(String message) {
1205 | 1 |
                 super(message);
1206 | 1 |
             }
1207 |  |
1208 |  |
             /**
1209 |  |
              * Constructs an <code>InvalidContentTypeException</code> with
1210 |  |
              * the specified detail message and cause.
1211 |  |
              *
1212 |  |
              * @param msg The detail message.
1213 |  |
              * @param cause the original cause
1214 |  |
              *
1215 |  |
              * @since 1.3.1
1216 |  |
              */
1217 |  |
             public InvalidContentTypeException(String msg, Throwable cause) {
1218 | 0 |
                 super(msg, cause);
1219 | 0 |
             }
1220 |  |
         }
1221 |  |
1222 |  |
         /**
1223 |  |
          * Thrown to indicate an IOException.
1224 |  |
          */
1225 |  |
         public static class IOFileUploadException extends FileUploadException {
1226 |  |
1227 |  |
             /**
1228 |  |
              * The exceptions UID, for serializing an instance.
1229 |  |
              */
1230 |  |
             private static final long serialVersionUID = 1749796615868477269L;
1231 |  |
1232 |  |
             /**
1233 |  |
              * The exceptions cause; we overwrite the parent
1234 |  |
              * classes field, which is available since Java
1235 |  |
              * 1.4 only.
1236 |  |
              */
1237 |  |
             private final IOException cause;
1238 |  |
1239 |  |
             /**
1240 |  |
              * Creates a new instance with the given cause.
1241 |  |
              *
1242 |  |
              * @param pMsg The detail message.
1243 |  |
              * @param pException The exceptions cause.
1244 |  |
              */
1245 |  |
             public IOFileUploadException(String pMsg, IOException pException) {
1246 | 1 |
                 super(pMsg);
1247 | 1 |
                 cause = pException;
1248 | 1 |
             }
1249 |  |
1250 |  |
             /**
1251 |  |
              * Returns the exceptions cause.
1252 |  |
              *
1253 |  |
              * @return The exceptions cause, if any, or null.
1254 |  |
              */
1255 |  |
             @Override
1256 |  |
             public Throwable getCause() {
1257 | 1 |
                 return cause;
1258 |  |
             }
1259 |  |
1260 |  |
         }
1261 |  |
1262 |  |
         /**
1263 |  |
          * This exception is thrown, if a requests permitted size
1264 |  |
          * is exceeded.
1265 |  |
          */
1266 |  |
         protected abstract static class SizeException extends FileUploadException {
1267 |  |
1268 |  |
             /**
1269 |  |
              * Serial version UID, being used, if serialized.
1270 |  |
              */
1271 |  |
             private static final long serialVersionUID = -8776225574705254126L;
1272 |  |
1273 |  |
             /**
1274 |  |
              * The actual size of the request.
1275 |  |
              */
1276 |  |
             private final long actual;
1277 |  |
1278 |  |
             /**
1279 |  |
              * The maximum permitted size of the request.
1280 |  |
              */
1281 |  |
             private final long permitted;
1282 |  |
1283 |  |
             /**
1284 |  |
              * Creates a new instance.
1285 |  |
              *
1286 |  |
              * @param message The detail message.
1287 |  |
              * @param actual The actual number of bytes in the request.
1288 |  |
              * @param permitted The requests size limit, in bytes.
1289 |  |
              */
1290 |  |
             protected SizeException(String message, long actual, long permitted) {
1291 | 6 |
                 super(message);
1292 | 6 |
                 this.actual = actual;
1293 | 6 |
                 this.permitted = permitted;
1294 | 6 |
             }
1295 |  |
1296 |  |
             /**
1297 |  |
              * Retrieves the actual size of the request.
1298 |  |
              *
1299 |  |
              * @return The actual size of the request.
1300 |  |
              * @since 1.3
1301 |  |
              */
1302 |  |
             public long getActualSize() {
1303 | 0 |
                 return actual;
1304 |  |
             }
1305 |  |
1306 |  |
             /**
1307 |  |
              * Retrieves the permitted size of the request.
1308 |  |
              *
1309 |  |
              * @return The permitted size of the request.
1310 |  |
              * @since 1.3
1311 |  |
              */
1312 |  |
             public long getPermittedSize() {
1313 | 4 |
                 return permitted;
1314 |  |
             }
1315 |  |
1316 |  |
         }
1317 |  |
1318 |  |
         /**
1319 |  |
          * Thrown to indicate that the request size is not specified. In other
1320 |  |
          * words, it is thrown, if the content-length header is missing or
1321 |  |
          * contains the value -1.
1322 |  |
          *
1323 |  |
          * @deprecated 1.2 As of commons-fileupload 1.2, the presence of a
1324 |  |
          *   content-length header is no longer required.
1325 |  |
          */
1326 |  |
         @Deprecated
1327 |  |
         public static class UnknownSizeException
1328 |  |
             extends FileUploadException {
1329 |  |
1330 |  |
             /**
1331 |  |
              * The exceptions UID, for serializing an instance.
1332 |  |
              */
1333 |  |
             private static final long serialVersionUID = 7062279004812015273L;
1334 |  |
1335 |  |
             /**
1336 |  |
              * Constructs a <code>UnknownSizeException</code> with no
1337 |  |
              * detail message.
1338 |  |
              */
1339 |  |
             public UnknownSizeException() {
1340 | 0 |
                 super();
1341 | 0 |
             }
1342 |  |
1343 |  |
             /**
1344 |  |
              * Constructs an <code>UnknownSizeException</code> with
1345 |  |
              * the specified detail message.
1346 |  |
              *
1347 |  |
              * @param message The detail message.
1348 |  |
              */
1349 |  |
             public UnknownSizeException(String message) {
1350 | 0 |
                 super(message);
1351 | 0 |
             }
1352 |  |
1353 |  |
         }
1354 |  |
1355 |  |
         /**
1356 |  |
          * Thrown to indicate that the request size exceeds the configured maximum.
1357 |  |
          */
1358 | 1 |
         public static class SizeLimitExceededException
1359 |  |
                 extends SizeException {
1360 |  |
1361 |  |
             /**
1362 |  |
              * The exceptions UID, for serializing an instance.
1363 |  |
              */
1364 |  |
             private static final long serialVersionUID = -2474893167098052828L;
1365 |  |
1366 |  |
             /**
1367 |  |
              * @deprecated 1.2 Replaced by
1368 |  |
              * {@link #SizeLimitExceededException(String, long, long)}
1369 |  |
              */
1370 |  |
             @Deprecated
1371 |  |
             public SizeLimitExceededException() {
1372 | 0 |
                 this(null, 0, 0);
1373 | 0 |
             }
1374 |  |
1375 |  |
             /**
1376 |  |
              * @deprecated 1.2 Replaced by
1377 |  |
              * {@link #SizeLimitExceededException(String, long, long)}
1378 |  |
              * @param message The exceptions detail message.
1379 |  |
              */
1380 |  |
             @Deprecated
1381 |  |
             public SizeLimitExceededException(String message) {
1382 | 0 |
                 this(message, 0, 0);
1383 | 0 |
             }
1384 |  |
1385 |  |
             /**
1386 |  |
              * Constructs a <code>SizeExceededException</code> with
1387 |  |
              * the specified detail message, and actual and permitted sizes.
1388 |  |
              *
1389 |  |
              * @param message   The detail message.
1390 |  |
              * @param actual    The actual request size.
1391 |  |
              * @param permitted The maximum permitted request size.
1392 |  |
              */
1393 |  |
             public SizeLimitExceededException(String message, long actual,
1394 |  |
                     long permitted) {
1395 | 3 |
                 super(message, actual, permitted);
1396 | 3 |
             }
1397 |  |
1398 |  |
         }
1399 |  |
1400 |  |
         /**
1401 |  |
          * Thrown to indicate that A files size exceeds the configured maximum.
1402 |  |
          */
1403 | 3 |
         public static class FileSizeLimitExceededException
1404 |  |
                 extends SizeException {
1405 |  |
1406 |  |
             /**
1407 |  |
              * The exceptions UID, for serializing an instance.
1408 |  |
              */
1409 |  |
             private static final long serialVersionUID = 8150776562029630058L;
1410 |  |
1411 |  |
             /**
1412 |  |
              * File name of the item, which caused the exception.
1413 |  |
              */
1414 |  |
             private String fileName;
1415 |  |
1416 |  |
             /**
1417 |  |
              * Field name of the item, which caused the exception.
1418 |  |
              */
1419 |  |
             private String fieldName;
1420 |  |
1421 |  |
             /**
1422 |  |
              * Constructs a <code>SizeExceededException</code> with
1423 |  |
              * the specified detail message, and actual and permitted sizes.
1424 |  |
              *
1425 |  |
              * @param message   The detail message.
1426 |  |
              * @param actual    The actual request size.
1427 |  |
              * @param permitted The maximum permitted request size.
1428 |  |
              */
1429 |  |
             public FileSizeLimitExceededException(String message, long actual,
1430 |  |
                     long permitted) {
1431 | 3 |
                 super(message, actual, permitted);
1432 | 3 |
             }
1433 |  |
1434 |  |
             /**
1435 |  |
              * Returns the file name of the item, which caused the
1436 |  |
              * exception.
1437 |  |
              *
1438 |  |
              * @return File name, if known, or null.
1439 |  |
              */
1440 |  |
             public String getFileName() {
1441 | 0 |
                 return fileName;
1442 |  |
             }
1443 |  |
1444 |  |
             /**
1445 |  |
              * Sets the file name of the item, which caused the
1446 |  |
              * exception.
1447 |  |
              *
1448 |  |
              * @param pFileName the file name of the item, which caused the exception.
1449 |  |
              */
1450 |  |
             public void setFileName(String pFileName) {
1451 | 3 |
                 fileName = pFileName;
1452 | 3 |
             }
1453 |  |
1454 |  |
             /**
1455 |  |
              * Returns the field name of the item, which caused the
1456 |  |
              * exception.
1457 |  |
              *
1458 |  |
              * @return Field name, if known, or null.
1459 |  |
              */
1460 |  |
             public String getFieldName() {
1461 | 0 |
                 return fieldName;
1462 |  |
             }
1463 |  |
1464 |  |
             /**
1465 |  |
              * Sets the field name of the item, which caused the
1466 |  |
              * exception.
1467 |  |
              *
1468 |  |
              * @param pFieldName the field name of the item,
1469 |  |
              *        which caused the exception.
1470 |  |
              */
1471 |  |
             public void setFieldName(String pFieldName) {
1472 | 3 |
                 fieldName = pFieldName;
1473 | 3 |
             }
1474 |  |
1475 |  |
         }
1476 |  |
1477 |  |
         /**
1478 |  |
          * Returns the progress listener.
1479 |  |
          *
1480 |  |
          * @return The progress listener, if any, or null.
1481 |  |
          */
1482 |  |
         public ProgressListener getProgressListener() {
1483 | 0 |
             return listener;
1484 |  |
         }
1485 |  |
1486 |  |
         /**
1487 |  |
          * Sets the progress listener.
1488 |  |
          *
1489 |  |
          * @param pListener The progress listener, if any. Defaults to null.
1490 |  |
          */
1491 |  |
         public void setProgressListener(ProgressListener pListener) {
1492 | 2 |
             listener = pListener;
1493 | 2 |
         }
1494 |  |
1495 |  |
     }