Coverage Report - org.apache.commons.fileupload.FileUploadBase
 
Classes in this File Line Coverage Branch Coverage Complexity
FileUploadBase
75%
126/166
62%
36/58
2,591
FileUploadBase$FileItemIteratorImpl
60%
60/100
62%
31/50
2,591
FileUploadBase$FileItemIteratorImpl$1
100%
5/5
N/A
2,591
FileUploadBase$FileItemIteratorImpl$FileItemStreamImpl
94%
33/35
80%
8/10
2,591
FileUploadBase$FileItemIteratorImpl$FileItemStreamImpl$1
100%
8/8
N/A
2,591
FileUploadBase$FileSizeLimitExceededException
77%
7/9
N/A
2,591
FileUploadBase$FileUploadIOException
100%
4/4
N/A
2,591
FileUploadBase$IOFileUploadException
100%
4/4
N/A
2,591
FileUploadBase$InvalidContentTypeException
33%
2/6
N/A
2,591
FileUploadBase$SizeException
83%
5/6
N/A
2,591
FileUploadBase$SizeLimitExceededException
42%
3/7
N/A
2,591
FileUploadBase$UnknownSizeException
0%
0/4
N/A
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  
 }