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