Coverage Report - org.apache.commons.fileupload.FileUploadBase
 
Classes in this File Line Coverage Branch Coverage Complexity
FileUploadBase
76%
127/167
62%
36/58
2.606
FileUploadBase$FileItemIteratorImpl
60%
60/100
62%
31/50
2.606
FileUploadBase$FileItemIteratorImpl$1
100%
5/5
N/A
2.606
FileUploadBase$FileItemIteratorImpl$FileItemStreamImpl
94%
34/36
83%
10/12
2.606
FileUploadBase$FileItemIteratorImpl$FileItemStreamImpl$1
100%
8/8
N/A
2.606
FileUploadBase$FileSizeLimitExceededException
77%
7/9
N/A
2.606
FileUploadBase$FileUploadIOException
100%
4/4
N/A
2.606
FileUploadBase$IOFileUploadException
100%
4/4
N/A
2.606
FileUploadBase$InvalidContentTypeException
33%
2/6
N/A
2.606
FileUploadBase$SizeException
83%
5/6
N/A
2.606
FileUploadBase$SizeLimitExceededException
42%
3/7
N/A
2.606
FileUploadBase$UnknownSizeException
0%
0/4
N/A
2.606
 
 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  6530
 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  25
     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  25
     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  39
             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  35
         List<FileItem> items = new ArrayList<FileItem>();
 331  35
         boolean successful = false;
 332  
         try {
 333  35
             FileItemIterator iter = getItemIterator(ctx);
 334  30
             FileItemFactory fac = getFileItemFactory();
 335  30
             if (fac == null) {
 336  0
                 throw new NullPointerException("No FileItemFactory has been set.");
 337  
             }
 338  2190
             while (iter.hasNext()) {
 339  2163
                 final FileItemStream item = iter.next();
 340  
                 // Don't use getName() here to prevent an InvalidFileNameException.
 341  2163
                 final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;
 342  4326
                 FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
 343  2163
                                                    item.isFormField(), fileName);
 344  2163
                 items.add(fileItem);
 345  
                 try {
 346  2163
                     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  2160
                 }
 353  2160
                 final FileItemHeaders fih = item.getHeaders();
 354  2160
                 fileItem.setHeaders(fih);
 355  2160
             }
 356  27
             successful = true;
 357  54
             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  35
             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  8
         }
 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  41
         ParameterParser parser = new ParameterParser();
 420  41
         parser.setLowerCaseNames(true);
 421  
         // Parameter parser can handle null input
 422  41
         Map<String, String> params = parser.parse(contentType, new char[] {';', ','});
 423  41
         String boundaryStr = params.get("boundary");
 424  
 
 425  41
         if (boundaryStr == null) {
 426  1
             return null;
 427  
         }
 428  
         byte[] boundary;
 429  
         try {
 430  40
             boundary = boundaryStr.getBytes("ISO-8859-1");
 431  0
         } catch (UnsupportedEncodingException e) {
 432  0
             boundary = boundaryStr.getBytes(); // Intentionally falls back to default charset
 433  40
         }
 434  40
         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  3191
         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  3191
         String fileName = null;
 470  3191
         if (pContentDisposition != null) {
 471  3191
             String cdl = pContentDisposition.toLowerCase(Locale.ENGLISH);
 472  3191
             if (cdl.startsWith(FORM_DATA) || cdl.startsWith(ATTACHMENT)) {
 473  3191
                 ParameterParser parser = new ParameterParser();
 474  3191
                 parser.setLowerCaseNames(true);
 475  
                 // Parameter parser can handle null input
 476  3191
                 Map<String, String> params = parser.parse(pContentDisposition, ';');
 477  3191
                 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  3191
         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  3189
         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  3189
         String fieldName = null;
 513  3189
         if (pContentDisposition != null
 514  3189
                 && pContentDisposition.toLowerCase(Locale.ENGLISH).startsWith(FORM_DATA)) {
 515  3189
             ParameterParser parser = new ParameterParser();
 516  3189
             parser.setLowerCaseNames(true);
 517  
             // Parameter parser can handle null input
 518  3189
             Map<String, String> params = parser.parse(pContentDisposition, ';');
 519  3189
             fieldName = params.get("name");
 520  3189
             if (fieldName != null) {
 521  3189
                 fieldName = fieldName.trim();
 522  
             }
 523  
         }
 524  3189
         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  3195
         final int len = headerPart.length();
 579  3195
         FileItemHeadersImpl headers = newFileItemHeaders();
 580  3195
         int start = 0;
 581  
         for (;;) {
 582  6436
             int end = parseEndOfLine(headerPart, start);
 583  6436
             if (start == end) {
 584  3195
                 break;
 585  
             }
 586  3241
             StringBuilder header = new StringBuilder(headerPart.substring(start, end));
 587  3241
             start = end + 2;
 588  3249
             while (start < len) {
 589  3249
                 int nonWs = start;
 590  3273
                 while (nonWs < len) {
 591  3273
                     char c = headerPart.charAt(nonWs);
 592  3273
                     if (c != ' '  &&  c != '\t') {
 593  3249
                         break;
 594  
                     }
 595  24
                     ++nonWs;
 596  24
                 }
 597  3249
                 if (nonWs == start) {
 598  3241
                     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  3241
             parseHeaderLine(headers, header.toString());
 606  3241
         }
 607  3195
         return headers;
 608  
     }
 609  
 
 610  
     /**
 611  
      * Creates a new instance of {@link FileItemHeaders}.
 612  
      * @return The new instance.
 613  
      */
 614  
     protected FileItemHeadersImpl newFileItemHeaders() {
 615  3195
         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  6444
         int index = end;
 657  
         for (;;) {
 658  6444
             int offset = headerPart.indexOf('\r', index);
 659  6444
             if (offset == -1  ||  offset + 1 >= headerPart.length()) {
 660  0
                 throw new IllegalStateException(
 661  
                     "Expected headers to be terminated by an empty line.");
 662  
             }
 663  6444
             if (headerPart.charAt(offset + 1) == '\n') {
 664  6444
                 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  3241
         final int colonOffset = header.indexOf(':');
 677  3241
         if (colonOffset == -1) {
 678  
             // This header line is malformed, skip it.
 679  0
             return;
 680  
         }
 681  3241
         String headerName = header.substring(0, colonOffset).trim();
 682  3241
         String headerValue =
 683  3241
             header.substring(header.indexOf(':') + 1).trim();
 684  3241
         headers.addHeader(headerName, headerValue);
 685  3241
     }
 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  2169
         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  3191
                     long pContentLength) throws IOException {
 763  3191
                 name = pName;
 764  3191
                 fieldName = pFieldName;
 765  3191
                 contentType = pContentType;
 766  3191
                 formField = pFormField;
 767  3191
                 if (fileSizeMax != -1) { // Check if limit is already exceeded
 768  5
                     if (pContentLength != -1
 769  3
                             && pContentLength > fileSizeMax) {
 770  1
                         FileSizeLimitExceededException e =
 771  
                                 new FileSizeLimitExceededException(
 772  1
                                         format("The field %s exceeds its maximum permitted size of %s bytes.",
 773  1
                                                 fieldName, Long.valueOf(fileSizeMax)),
 774  1
                                         pContentLength, fileSizeMax);
 775  1
                         e.setFileName(pName);
 776  1
                         e.setFieldName(pFieldName);
 777  1
                         throw new FileUploadIOException(e);
 778  
                     }
 779  
                 }
 780  
                 // OK to construct stream now
 781  3190
                 final ItemInputStream itemStream = multi.newInputStream();
 782  3190
                 InputStream istream = itemStream;
 783  3190
                 if (fileSizeMax != -1) {
 784  4
                     istream = new LimitedInputStream(istream, fileSizeMax) {
 785  
                         @Override
 786  
                         protected void raiseError(long pSizeMax, long pCount)
 787  
                                 throws IOException {
 788  2
                             itemStream.close(true);
 789  2
                             FileSizeLimitExceededException e =
 790  
                                 new FileSizeLimitExceededException(
 791  2
                                     format("The field %s exceeds its maximum permitted size of %s bytes.",
 792  2
                                            fieldName, Long.valueOf(pSizeMax)),
 793  
                                     pCount, pSizeMax);
 794  2
                             e.setFieldName(fieldName);
 795  2
                             e.setFileName(name);
 796  2
                             throw new FileUploadIOException(e);
 797  
                         }
 798  
                     };
 799  
                 }
 800  3190
                 stream = istream;
 801  3190
             }
 802  
 
 803  
             /**
 804  
              * Returns the items content type, or null.
 805  
              *
 806  
              * @return Content type, if known, or null.
 807  
              */
 808  
             @Override
 809  
             public String getContentType() {
 810  2163
                 return contentType;
 811  
             }
 812  
 
 813  
             /**
 814  
              * Returns the items field name.
 815  
              *
 816  
              * @return Field name.
 817  
              */
 818  
             @Override
 819  
             public String getFieldName() {
 820  2164
                 return fieldName;
 821  
             }
 822  
 
 823  
             /**
 824  
              * Returns the items file name.
 825  
              *
 826  
              * @return File name, if known, or null.
 827  
              * @throws InvalidFileNameException The file name contains a NUL character,
 828  
              *   which might be an indicator of a security attack. If you intend to
 829  
              *   use the file name anyways, catch the exception and use
 830  
              *   InvalidFileNameException#getName().
 831  
              */
 832  
             @Override
 833  
             public String getName() {
 834  2
                 return Streams.checkFileName(name);
 835  
             }
 836  
 
 837  
             /**
 838  
              * Returns, whether this is a form field.
 839  
              *
 840  
              * @return True, if the item is a form field,
 841  
              *   otherwise false.
 842  
              */
 843  
             @Override
 844  
             public boolean isFormField() {
 845  2164
                 return formField;
 846  
             }
 847  
 
 848  
             /**
 849  
              * Returns an input stream, which may be used to
 850  
              * read the items contents.
 851  
              *
 852  
              * @return Opened input stream.
 853  
              * @throws IOException An I/O error occurred.
 854  
              */
 855  
             @Override
 856  
             public InputStream openStream() throws IOException {
 857  3189
                 if (opened) {
 858  0
                     throw new IllegalStateException(
 859  
                             "The stream was already opened.");
 860  
                 }
 861  3189
                 if (((Closeable) stream).isClosed()) {
 862  0
                     throw new FileItemStream.ItemSkippedException();
 863  
                 }
 864  3189
                 return stream;
 865  
             }
 866  
 
 867  
             /**
 868  
              * Closes the file item.
 869  
              *
 870  
              * @throws IOException An I/O error occurred.
 871  
              */
 872  
             void close() throws IOException {
 873  3185
                 stream.close();
 874  3185
             }
 875  
 
 876  
             /**
 877  
              * Returns the file item headers.
 878  
              *
 879  
              * @return The items header object
 880  
              */
 881  
             @Override
 882  
             public FileItemHeaders getHeaders() {
 883  2160
                 return headers;
 884  
             }
 885  
 
 886  
             /**
 887  
              * Sets the file item headers.
 888  
              *
 889  
              * @param pHeaders The items header object
 890  
              */
 891  
             @Override
 892  
             public void setHeaders(FileItemHeaders pHeaders) {
 893  3190
                 headers = pHeaders;
 894  3190
             }
 895  
 
 896  
         }
 897  
 
 898  
         /**
 899  
          * The multi part stream to process.
 900  
          */
 901  
         private final MultipartStream multi;
 902  
 
 903  
         /**
 904  
          * The notifier, which used for triggering the
 905  
          * {@link ProgressListener}.
 906  
          */
 907  
         private final MultipartStream.ProgressNotifier notifier;
 908  
 
 909  
         /**
 910  
          * The boundary, which separates the various parts.
 911  
          */
 912  
         private final byte[] boundary;
 913  
 
 914  
         /**
 915  
          * The item, which we currently process.
 916  
          */
 917  
         private FileItemStreamImpl currentItem;
 918  
 
 919  
         /**
 920  
          * The current items field name.
 921  
          */
 922  
         private String currentFieldName;
 923  
 
 924  
         /**
 925  
          * Whether we are currently skipping the preamble.
 926  
          */
 927  
         private boolean skipPreamble;
 928  
 
 929  
         /**
 930  
          * Whether the current item may still be read.
 931  
          */
 932  
         private boolean itemValid;
 933  
 
 934  
         /**
 935  
          * Whether we have seen the end of the file.
 936  
          */
 937  
         private boolean eof;
 938  
 
 939  
         /**
 940  
          * Creates a new instance.
 941  
          *
 942  
          * @param ctx The request context.
 943  
          * @throws FileUploadException An error occurred while
 944  
          *   parsing the request.
 945  
          * @throws IOException An I/O error occurred.
 946  
          */
 947  
         FileItemIteratorImpl(RequestContext ctx)
 948  39
                 throws FileUploadException, IOException {
 949  39
             if (ctx == null) {
 950  0
                 throw new NullPointerException("ctx parameter");
 951  
             }
 952  
 
 953  39
             String contentType = ctx.getContentType();
 954  39
             if ((null == contentType)
 955  38
                     || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) {
 956  1
                 throw new InvalidContentTypeException(
 957  1
                         format("the request doesn't contain a %s or %s stream, content type header is %s",
 958  
                                MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType));
 959  
             }
 960  
 
 961  
 
 962  
             @SuppressWarnings("deprecation") // still has to be backward compatible
 963  38
             final int contentLengthInt = ctx.getContentLength();
 964  
 
 965  38
             final long requestSize = UploadContext.class.isAssignableFrom(ctx.getClass())
 966  
                                      // Inline conditional is OK here CHECKSTYLE:OFF
 967  38
                                      ? ((UploadContext) ctx).contentLength()
 968  
                                      : contentLengthInt;
 969  
                                      // CHECKSTYLE:ON
 970  
 
 971  
             InputStream input; // N.B. this is eventually closed in MultipartStream processing
 972  38
             if (sizeMax >= 0) {
 973  2
                 if (requestSize != -1 && requestSize > sizeMax) {
 974  1
                     throw new SizeLimitExceededException(
 975  1
                         format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
 976  1
                                 Long.valueOf(requestSize), Long.valueOf(sizeMax)),
 977  1
                                requestSize, sizeMax);
 978  
                 }
 979  
                 // N.B. this is eventually closed in MultipartStream processing
 980  1
                 input = new LimitedInputStream(ctx.getInputStream(), sizeMax) {
 981  
                     @Override
 982  
                     protected void raiseError(long pSizeMax, long pCount)
 983  
                             throws IOException {
 984  2
                         FileUploadException ex = new SizeLimitExceededException(
 985  2
                         format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
 986  2
                                 Long.valueOf(pCount), Long.valueOf(pSizeMax)),
 987  
                                pCount, pSizeMax);
 988  2
                         throw new FileUploadIOException(ex);
 989  
                     }
 990  
                 };
 991  
             } else {
 992  36
                 input = ctx.getInputStream();
 993  
             }
 994  
 
 995  37
             String charEncoding = headerEncoding;
 996  37
             if (charEncoding == null) {
 997  37
                 charEncoding = ctx.getCharacterEncoding();
 998  
             }
 999  
 
 1000  37
             boundary = getBoundary(contentType);
 1001  37
             if (boundary == null) {
 1002  1
                 IOUtils.closeQuietly(input); // avoid possible resource leak
 1003  1
                 throw new FileUploadException("the request was rejected because no multipart boundary was found");
 1004  
             }
 1005  
 
 1006  36
             notifier = new MultipartStream.ProgressNotifier(listener, requestSize);
 1007  
             try {
 1008  36
                 multi = new MultipartStream(input, boundary, notifier);
 1009  0
             } catch (IllegalArgumentException iae) {
 1010  0
                 IOUtils.closeQuietly(input); // avoid possible resource leak
 1011  0
                 throw new InvalidContentTypeException(
 1012  0
                         format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae);
 1013  36
             }
 1014  36
             multi.setHeaderEncoding(charEncoding);
 1015  
 
 1016  36
             skipPreamble = true;
 1017  36
             findNextItem();
 1018  34
         }
 1019  
 
 1020  
         /**
 1021  
          * Called for finding the next item, if any.
 1022  
          *
 1023  
          * @return True, if an next item was found, otherwise false.
 1024  
          * @throws IOException An I/O error occurred.
 1025  
          */
 1026  
         private boolean findNextItem() throws IOException {
 1027  3221
             if (eof) {
 1028  0
                 return false;
 1029  
             }
 1030  3221
             if (currentItem != null) {
 1031  3185
                 currentItem.close();
 1032  3185
                 currentItem = null;
 1033  
             }
 1034  
             for (;;) {
 1035  
                 boolean nextPart;
 1036  3229
                 if (skipPreamble) {
 1037  3229
                     nextPart = multi.skipPreamble();
 1038  
                 } else {
 1039  0
                     nextPart = multi.readBoundary();
 1040  
                 }
 1041  3228
                 if (!nextPart) {
 1042  33
                     if (currentFieldName == null) {
 1043  
                         // Outer multipart terminated -> No more data
 1044  29
                         eof = true;
 1045  29
                         return false;
 1046  
                     }
 1047  
                     // Inner multipart terminated -> Return to parsing the outer
 1048  4
                     multi.setBoundary(boundary);
 1049  4
                     currentFieldName = null;
 1050  4
                     continue;
 1051  
                 }
 1052  3195
                 FileItemHeaders headers = getParsedHeaders(multi.readHeaders());
 1053  3195
                 if (currentFieldName == null) {
 1054  
                     // We're parsing the outer multipart
 1055  3189
                     String fieldName = getFieldName(headers);
 1056  3189
                     if (fieldName != null) {
 1057  3189
                         String subContentType = headers.getHeader(CONTENT_TYPE);
 1058  3189
                         if (subContentType != null
 1059  25
                                 &&  subContentType.toLowerCase(Locale.ENGLISH)
 1060  25
                                         .startsWith(MULTIPART_MIXED)) {
 1061  4
                             currentFieldName = fieldName;
 1062  
                             // Multiple files associated with this field name
 1063  4
                             byte[] subBoundary = getBoundary(subContentType);
 1064  4
                             multi.setBoundary(subBoundary);
 1065  4
                             skipPreamble = true;
 1066  4
                             continue;
 1067  
                         }
 1068  3185
                         String fileName = getFileName(headers);
 1069  3185
                         currentItem = new FileItemStreamImpl(fileName,
 1070  0
                                 fieldName, headers.getHeader(CONTENT_TYPE),
 1071  0
                                 fileName == null, getContentLength(headers));
 1072  0
                         currentItem.setHeaders(headers);
 1073  0
                         notifier.noteItem();
 1074  0
                         itemValid = true;
 1075  0
                         return true;
 1076  
                     }
 1077  0
                 } else {
 1078  0
                     String fileName = getFileName(headers);
 1079  0
                     if (fileName != null) {
 1080  0
                         currentItem = new FileItemStreamImpl(fileName,
 1081  
                                 currentFieldName,
 1082  0
                                 headers.getHeader(CONTENT_TYPE),
 1083  0
                                 false, getContentLength(headers));
 1084  0
                         currentItem.setHeaders(headers);
 1085  0
                         notifier.noteItem();
 1086  0
                         itemValid = true;
 1087  0
                         return true;
 1088  
                     }
 1089  
                 }
 1090  0
                 multi.discardBodyData();
 1091  0
             }
 1092  
         }
 1093  
 
 1094  
         private long getContentLength(FileItemHeaders pHeaders) {
 1095  
             try {
 1096  0
                 return Long.parseLong(pHeaders.getHeader(CONTENT_LENGTH));
 1097  0
             } catch (Exception e) {
 1098  0
                 return -1;
 1099  
             }
 1100  
         }
 1101  
 
 1102  
         /**
 1103  
          * Returns, whether another instance of {@link FileItemStream}
 1104  
          * is available.
 1105  
          *
 1106  
          * @throws FileUploadException Parsing or processing the
 1107  
          *   file item failed.
 1108  
          * @throws IOException Reading the file item failed.
 1109  
          * @return True, if one or more additional file items
 1110  
          *   are available, otherwise false.
 1111  
          */
 1112  
         @Override
 1113  
         public boolean hasNext() throws FileUploadException, IOException {
 1114  0
             if (eof) {
 1115  0
                 return false;
 1116  
             }
 1117  0
             if (itemValid) {
 1118  0
                 return true;
 1119  
             }
 1120  
             try {
 1121  0
                 return findNextItem();
 1122  0
             } catch (FileUploadIOException e) {
 1123  
                 // unwrap encapsulated SizeException
 1124  0
                 throw (FileUploadException) e.getCause();
 1125  
             }
 1126  
         }
 1127  
 
 1128  
         /**
 1129  
          * Returns the next available {@link FileItemStream}.
 1130  
          *
 1131  
          * @throws java.util.NoSuchElementException No more items are
 1132  
          *   available. Use {@link #hasNext()} to prevent this exception.
 1133  
          * @throws FileUploadException Parsing or processing the
 1134  
          *   file item failed.
 1135  
          * @throws IOException Reading the file item failed.
 1136  
          * @return FileItemStream instance, which provides
 1137  
          *   access to the next file item.
 1138  
          */
 1139  
         @Override
 1140  
         public FileItemStream next() throws FileUploadException, IOException {
 1141  0
             if (eof  ||  (!itemValid && !hasNext())) {
 1142  0
                 throw new NoSuchElementException();
 1143  
             }
 1144  0
             itemValid = false;
 1145  0
             return currentItem;
 1146  
         }
 1147  
 
 1148  
     }
 1149  
 
 1150  
     /**
 1151  
      * This exception is thrown for hiding an inner
 1152  
      * {@link FileUploadException} in an {@link IOException}.
 1153  
      */
 1154  
     public static class FileUploadIOException extends IOException {
 1155  
 
 1156  
         /**
 1157  
          * The exceptions UID, for serializing an instance.
 1158  
          */
 1159  
         private static final long serialVersionUID = -7047616958165584154L;
 1160  
 
 1161  
         /**
 1162  
          * The exceptions cause; we overwrite the parent
 1163  
          * classes field, which is available since Java
 1164  
          * 1.4 only.
 1165  
          */
 1166  
         private final FileUploadException cause;
 1167  
 
 1168  
         /**
 1169  
          * Creates a <code>FileUploadIOException</code> with the
 1170  
          * given cause.
 1171  
          *
 1172  
          * @param pCause The exceptions cause, if any, or null.
 1173  
          */
 1174  5
         public FileUploadIOException(FileUploadException pCause) {
 1175  
             // We're not doing super(pCause) cause of 1.3 compatibility.
 1176  5
             cause = pCause;
 1177  5
         }
 1178  
 
 1179  
         /**
 1180  
          * Returns the exceptions cause.
 1181  
          *
 1182  
          * @return The exceptions cause, if any, or null.
 1183  
          */
 1184  
         @Override
 1185  
         public Throwable getCause() {
 1186  3
             return cause;
 1187  
         }
 1188  
 
 1189  
     }
 1190  
 
 1191  
     /**
 1192  
      * Thrown to indicate that the request is not a multipart request.
 1193  
      */
 1194  
     public static class InvalidContentTypeException
 1195  
             extends FileUploadException {
 1196  
 
 1197  
         /**
 1198  
          * The exceptions UID, for serializing an instance.
 1199  
          */
 1200  
         private static final long serialVersionUID = -9073026332015646668L;
 1201  
 
 1202  
         /**
 1203  
          * Constructs a <code>InvalidContentTypeException</code> with no
 1204  
          * detail message.
 1205  
          */
 1206  
         public InvalidContentTypeException() {
 1207  0
             super();
 1208  0
         }
 1209  
 
 1210  
         /**
 1211  
          * Constructs an <code>InvalidContentTypeException</code> with
 1212  
          * the specified detail message.
 1213  
          *
 1214  
          * @param message The detail message.
 1215  
          */
 1216  
         public InvalidContentTypeException(String message) {
 1217  1
             super(message);
 1218  1
         }
 1219  
 
 1220  
         /**
 1221  
          * Constructs an <code>InvalidContentTypeException</code> with
 1222  
          * the specified detail message and cause.
 1223  
          *
 1224  
          * @param msg The detail message.
 1225  
          * @param cause the original cause
 1226  
          *
 1227  
          * @since 1.3.1
 1228  
          */
 1229  
         public InvalidContentTypeException(String msg, Throwable cause) {
 1230  0
             super(msg, cause);
 1231  0
         }
 1232  
     }
 1233  
 
 1234  
     /**
 1235  
      * Thrown to indicate an IOException.
 1236  
      */
 1237  
     public static class IOFileUploadException extends FileUploadException {
 1238  
 
 1239  
         /**
 1240  
          * The exceptions UID, for serializing an instance.
 1241  
          */
 1242  
         private static final long serialVersionUID = 1749796615868477269L;
 1243  
 
 1244  
         /**
 1245  
          * The exceptions cause; we overwrite the parent
 1246  
          * classes field, which is available since Java
 1247  
          * 1.4 only.
 1248  
          */
 1249  
         private final IOException cause;
 1250  
 
 1251  
         /**
 1252  
          * Creates a new instance with the given cause.
 1253  
          *
 1254  
          * @param pMsg The detail message.
 1255  
          * @param pException The exceptions cause.
 1256  
          */
 1257  
         public IOFileUploadException(String pMsg, IOException pException) {
 1258  1
             super(pMsg);
 1259  1
             cause = pException;
 1260  1
         }
 1261  
 
 1262  
         /**
 1263  
          * Returns the exceptions cause.
 1264  
          *
 1265  
          * @return The exceptions cause, if any, or null.
 1266  
          */
 1267  
         @Override
 1268  
         public Throwable getCause() {
 1269  1
             return cause;
 1270  
         }
 1271  
 
 1272  
     }
 1273  
 
 1274  
     /**
 1275  
      * This exception is thrown, if a requests permitted size
 1276  
      * is exceeded.
 1277  
      */
 1278  
     protected abstract static class SizeException extends FileUploadException {
 1279  
 
 1280  
         /**
 1281  
          * Serial version UID, being used, if serialized.
 1282  
          */
 1283  
         private static final long serialVersionUID = -8776225574705254126L;
 1284  
 
 1285  
         /**
 1286  
          * The actual size of the request.
 1287  
          */
 1288  
         private final long actual;
 1289  
 
 1290  
         /**
 1291  
          * The maximum permitted size of the request.
 1292  
          */
 1293  
         private final long permitted;
 1294  
 
 1295  
         /**
 1296  
          * Creates a new instance.
 1297  
          *
 1298  
          * @param message The detail message.
 1299  
          * @param actual The actual number of bytes in the request.
 1300  
          * @param permitted The requests size limit, in bytes.
 1301  
          */
 1302  
         protected SizeException(String message, long actual, long permitted) {
 1303  6
             super(message);
 1304  6
             this.actual = actual;
 1305  6
             this.permitted = permitted;
 1306  6
         }
 1307  
 
 1308  
         /**
 1309  
          * Retrieves the actual size of the request.
 1310  
          *
 1311  
          * @return The actual size of the request.
 1312  
          * @since 1.3
 1313  
          */
 1314  
         public long getActualSize() {
 1315  0
             return actual;
 1316  
         }
 1317  
 
 1318  
         /**
 1319  
          * Retrieves the permitted size of the request.
 1320  
          *
 1321  
          * @return The permitted size of the request.
 1322  
          * @since 1.3
 1323  
          */
 1324  
         public long getPermittedSize() {
 1325  4
             return permitted;
 1326  
         }
 1327  
 
 1328  
     }
 1329  
 
 1330  
     /**
 1331  
      * Thrown to indicate that the request size is not specified. In other
 1332  
      * words, it is thrown, if the content-length header is missing or
 1333  
      * contains the value -1.
 1334  
      *
 1335  
      * @deprecated 1.2 As of commons-fileupload 1.2, the presence of a
 1336  
      *   content-length header is no longer required.
 1337  
      */
 1338  
     @Deprecated
 1339  
     public static class UnknownSizeException
 1340  
         extends FileUploadException {
 1341  
 
 1342  
         /**
 1343  
          * The exceptions UID, for serializing an instance.
 1344  
          */
 1345  
         private static final long serialVersionUID = 7062279004812015273L;
 1346  
 
 1347  
         /**
 1348  
          * Constructs a <code>UnknownSizeException</code> with no
 1349  
          * detail message.
 1350  
          */
 1351  
         public UnknownSizeException() {
 1352  0
             super();
 1353  0
         }
 1354  
 
 1355  
         /**
 1356  
          * Constructs an <code>UnknownSizeException</code> with
 1357  
          * the specified detail message.
 1358  
          *
 1359  
          * @param message The detail message.
 1360  
          */
 1361  
         public UnknownSizeException(String message) {
 1362  0
             super(message);
 1363  0
         }
 1364  
 
 1365  
     }
 1366  
 
 1367  
     /**
 1368  
      * Thrown to indicate that the request size exceeds the configured maximum.
 1369  
      */
 1370  1
     public static class SizeLimitExceededException
 1371  
             extends SizeException {
 1372  
 
 1373  
         /**
 1374  
          * The exceptions UID, for serializing an instance.
 1375  
          */
 1376  
         private static final long serialVersionUID = -2474893167098052828L;
 1377  
 
 1378  
         /**
 1379  
          * @deprecated 1.2 Replaced by
 1380  
          * {@link #SizeLimitExceededException(String, long, long)}
 1381  
          */
 1382  
         @Deprecated
 1383  
         public SizeLimitExceededException() {
 1384  0
             this(null, 0, 0);
 1385  0
         }
 1386  
 
 1387  
         /**
 1388  
          * @deprecated 1.2 Replaced by
 1389  
          * {@link #SizeLimitExceededException(String, long, long)}
 1390  
          * @param message The exceptions detail message.
 1391  
          */
 1392  
         @Deprecated
 1393  
         public SizeLimitExceededException(String message) {
 1394  0
             this(message, 0, 0);
 1395  0
         }
 1396  
 
 1397  
         /**
 1398  
          * Constructs a <code>SizeExceededException</code> with
 1399  
          * the specified detail message, and actual and permitted sizes.
 1400  
          *
 1401  
          * @param message   The detail message.
 1402  
          * @param actual    The actual request size.
 1403  
          * @param permitted The maximum permitted request size.
 1404  
          */
 1405  
         public SizeLimitExceededException(String message, long actual,
 1406  
                 long permitted) {
 1407  3
             super(message, actual, permitted);
 1408  3
         }
 1409  
 
 1410  
     }
 1411  
 
 1412  
     /**
 1413  
      * Thrown to indicate that A files size exceeds the configured maximum.
 1414  
      */
 1415  3
     public static class FileSizeLimitExceededException
 1416  
             extends SizeException {
 1417  
 
 1418  
         /**
 1419  
          * The exceptions UID, for serializing an instance.
 1420  
          */
 1421  
         private static final long serialVersionUID = 8150776562029630058L;
 1422  
 
 1423  
         /**
 1424  
          * File name of the item, which caused the exception.
 1425  
          */
 1426  
         private String fileName;
 1427  
 
 1428  
         /**
 1429  
          * Field name of the item, which caused the exception.
 1430  
          */
 1431  
         private String fieldName;
 1432  
 
 1433  
         /**
 1434  
          * Constructs a <code>SizeExceededException</code> with
 1435  
          * the specified detail message, and actual and permitted sizes.
 1436  
          *
 1437  
          * @param message   The detail message.
 1438  
          * @param actual    The actual request size.
 1439  
          * @param permitted The maximum permitted request size.
 1440  
          */
 1441  
         public FileSizeLimitExceededException(String message, long actual,
 1442  
                 long permitted) {
 1443  3
             super(message, actual, permitted);
 1444  3
         }
 1445  
 
 1446  
         /**
 1447  
          * Returns the file name of the item, which caused the
 1448  
          * exception.
 1449  
          *
 1450  
          * @return File name, if known, or null.
 1451  
          */
 1452  
         public String getFileName() {
 1453  0
             return fileName;
 1454  
         }
 1455  
 
 1456  
         /**
 1457  
          * Sets the file name of the item, which caused the
 1458  
          * exception.
 1459  
          *
 1460  
          * @param pFileName the file name of the item, which caused the exception.
 1461  
          */
 1462  
         public void setFileName(String pFileName) {
 1463  3
             fileName = pFileName;
 1464  3
         }
 1465  
 
 1466  
         /**
 1467  
          * Returns the field name of the item, which caused the
 1468  
          * exception.
 1469  
          *
 1470  
          * @return Field name, if known, or null.
 1471  
          */
 1472  
         public String getFieldName() {
 1473  0
             return fieldName;
 1474  
         }
 1475  
 
 1476  
         /**
 1477  
          * Sets the field name of the item, which caused the
 1478  
          * exception.
 1479  
          *
 1480  
          * @param pFieldName the field name of the item,
 1481  
          *        which caused the exception.
 1482  
          */
 1483  
         public void setFieldName(String pFieldName) {
 1484  3
             fieldName = pFieldName;
 1485  3
         }
 1486  
 
 1487  
     }
 1488  
 
 1489  
     /**
 1490  
      * Returns the progress listener.
 1491  
      *
 1492  
      * @return The progress listener, if any, or null.
 1493  
      */
 1494  
     public ProgressListener getProgressListener() {
 1495  0
         return listener;
 1496  
     }
 1497  
 
 1498  
     /**
 1499  
      * Sets the progress listener.
 1500  
      *
 1501  
      * @param pListener The progress listener, if any. Defaults to null.
 1502  
      */
 1503  
     public void setProgressListener(ProgressListener pListener) {
 1504  2
         listener = pListener;
 1505  2
     }
 1506  
 
 1507  
 }