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.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 }