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 }