View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.webapp.filter.servlet;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import javax.servlet.http.HttpServletRequest;
27  
28  import org.apache.commons.fileupload.FileItem;
29  import org.apache.commons.fileupload.FileItemFactory;
30  import org.apache.commons.fileupload.FileItemHeaders;
31  import org.apache.commons.fileupload.FileItemHeadersSupport;
32  import org.apache.commons.fileupload.FileItemIterator;
33  import org.apache.commons.fileupload.FileItemStream;
34  import org.apache.commons.fileupload.FileUpload;
35  import org.apache.commons.fileupload.FileUploadBase;
36  import org.apache.commons.fileupload.FileUploadException;
37  import org.apache.commons.fileupload.RequestContext;
38  import org.apache.commons.fileupload.servlet.ServletFileUpload;
39  import org.apache.commons.fileupload.servlet.ServletRequestContext;
40  import org.apache.commons.fileupload.util.LimitedInputStream;
41  import org.apache.commons.fileupload.util.Streams;
42  
43  /**
44   * Custom implementation of ServletFileUpload intended to parse request but it
45   * catch and swallow FileSizeLimitExceededExceptions in order to return as
46   * many usable items as possible.
47   * 
48   * <p>
49   * NOTE: This class should be used(instantiated) only by 
50   * ServletMultipartRequestWrapper. By that reason, it could be changed
51   * or removed in the future.
52   * </p>
53   * 
54   * @since 1.1.9
55   * @author Phillip Webb
56   * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
57   * @version $Revision: 703744 $ $Date: 2008-10-11 17:28:20 -0500 (Sat, 11 Oct 2008) $
58   *
59   */
60  public class ServletChacheFileSizeErrorsFileUpload extends ServletFileUpload
61  {
62     
63      public ServletChacheFileSizeErrorsFileUpload()
64      {
65          super();
66      }
67      
68      public ServletChacheFileSizeErrorsFileUpload(FileItemFactory fileItemFactory)
69      {
70          super(fileItemFactory);
71      }
72          
73      /**
74       * Determine the length of an uploaded file as indicated by the header.
75       * 
76       * @param pHeaders
77       * @return length or -1
78       */
79      private long getContentLength(FileItemHeaders pHeaders) {
80          try {
81              return Long.parseLong(pHeaders.getHeader(FileUploadBase.CONTENT_LENGTH));
82          } catch (Exception e) {
83              return -1;
84          }
85      }    
86  
87      /**
88       * Similar to {@link ServletFileUpload#parseRequest(RequestContext)} but will
89       * catch and swallow FileSizeLimitExceededExceptions in order to return as
90       * many usable items as possible.
91       * 
92       * @param fileUpload
93       * @return List of {@link FileItem} excluding any that exceed max size.  
94       * @throws FileUploadException
95       */
96      public List parseRequestCatchingFileSizeErrors(HttpServletRequest request, FileUpload fileUpload)
97              throws FileUploadException
98      {
99          try
100         {
101             List items = new ArrayList();
102             
103             // The line below throws a SizeLimitExceededException (wrapped by a
104             // FileUploadIOException) if the request is longer than the max size
105             // allowed by fileupload requests (FileUpload.getSizeMax)
106             // But note that if the request does not send proper headers this check
107             // just will not do anything and we still have to check it again.
108             FileItemIterator iter = fileUpload
109                     .getItemIterator(new ServletRequestContext(request));
110 
111             FileItemFactory fac = fileUpload.getFileItemFactory();
112             if (fac == null)
113             {
114                 throw new NullPointerException(
115                         "No FileItemFactory has been set.");
116             }
117             
118             long maxFileSize = this.getFileSizeMax();
119             long maxSize = this.getSizeMax();
120             boolean checkMaxSize = false;
121             
122             if (maxFileSize == -1L)
123             {
124                 //The max allowed file size should be approximate to the maxSize
125                 maxFileSize = maxSize;
126             }
127             if (maxSize != -1L)
128             {
129                 checkMaxSize = true;
130             }
131             
132             while (iter.hasNext())
133             {
134                 final FileItemStream item = iter.next();
135                 FileItem fileItem = fac.createItem(item.getFieldName(), item
136                         .getContentType(), item.isFormField(), item.getName());
137 
138                 long allowedLimit = 0L;
139                 try
140                 {
141                     if (maxFileSize != -1L || checkMaxSize)
142                     {
143                         if (checkMaxSize)
144                         {
145                             allowedLimit = maxSize > maxFileSize ? maxFileSize : maxSize;
146                         }
147                         else
148                         {
149                             //Just put the limit
150                             allowedLimit = maxFileSize;
151                         }
152                         
153                         long contentLength = getContentLength(item.getHeaders());
154 
155                         //If we have a content length in the header we can use it
156                         if (contentLength != -1L && contentLength > allowedLimit)
157                         {
158                             throw new FileUploadIOException(
159                                     new FileSizeLimitExceededException(
160                                             "The field "
161                                                     + item.getFieldName()
162                                                     + " exceeds its maximum permitted "
163                                                     + " size of " + allowedLimit
164                                                     + " characters.",
165                                             contentLength, allowedLimit));
166                         }
167 
168                         //Otherwise we must limit the input as it arrives (NOTE: we cannot rely
169                         //on commons upload to throw this exception as it will close the 
170                         //underlying stream
171                         final InputStream itemInputStream = item.openStream();
172                         
173                         InputStream limitedInputStream = new LimitedInputStream(
174                                 itemInputStream, allowedLimit)
175                         {
176                             protected void raiseError(long pSizeMax, long pCount)
177                                     throws IOException
178                             {
179                                 throw new FileUploadIOException(
180                                         new FileSizeLimitExceededException(
181                                                 "The field "
182                                                         + item.getFieldName()
183                                                         + " exceeds its maximum permitted "
184                                                         + " size of "
185                                                         + pSizeMax
186                                                         + " characters.",
187                                                 pCount, pSizeMax));
188                             }
189                         };
190 
191                         //Copy from the limited stream
192                         long bytesCopied = Streams.copy(limitedInputStream, fileItem
193                                 .getOutputStream(), true);
194                         
195                         // Decrement the bytesCopied values from maxSize, so the next file copied 
196                         // takes into account this value when allowedLimit var is calculated
197                         // Note the invariant before the line is maxSize >= bytesCopied, since if this
198                         // is not true a FileUploadIOException is thrown first.
199                         maxSize -= bytesCopied;
200                     }
201                     else
202                     {
203                         //We can just copy the data
204                         Streams.copy(item.openStream(), fileItem
205                                 .getOutputStream(), true);
206                     }
207                 }
208                 catch (FileUploadIOException e)
209                 {
210                     try
211                     {
212                         throw (FileUploadException) e.getCause();
213                     }
214                     catch (FileUploadBase.FileSizeLimitExceededException se)
215                     {
216                         request
217                                 .setAttribute(
218                                         "org.apache.myfaces.custom.fileupload.exception",
219                                         "fileSizeLimitExceeded");
220                         String fieldName = fileItem.getFieldName();
221                         request.setAttribute(
222                                 "org.apache.myfaces.custom.fileupload."+fieldName+".maxSize",
223                                 new Integer((int)allowedLimit));
224                     }
225                 }
226                 catch (IOException e)
227                 {
228                     throw new IOFileUploadException("Processing of "
229                             + FileUploadBase.MULTIPART_FORM_DATA
230                             + " request failed. " + e.getMessage(), e);
231                 }
232                 if (fileItem instanceof FileItemHeadersSupport)
233                 {
234                     final FileItemHeaders fih = item.getHeaders();
235                     ((FileItemHeadersSupport) fileItem).setHeaders(fih);
236                 }
237                 if (fileItem != null)
238                 {
239                     items.add(fileItem);
240                 }
241             }
242             return items;
243         }
244         catch (FileUploadIOException e)
245         {
246             throw (FileUploadException) e.getCause();
247         }
248         catch (IOException e)
249         {
250             throw new FileUploadException(e.getMessage(), e);
251         }
252     }
253 }