001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.myfaces.tobago.webapp;
021    
022    import org.apache.commons.fileupload.FileItem;
023    import org.apache.commons.fileupload.FileUploadException;
024    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
025    import org.apache.commons.fileupload.servlet.ServletFileUpload;
026    import org.apache.commons.logging.Log;
027    import org.apache.commons.logging.LogFactory;
028    
029    import javax.faces.FacesException;
030    import javax.servlet.http.HttpServletRequest;
031    import javax.servlet.http.HttpServletRequestWrapper;
032    import java.io.File;
033    import java.io.UnsupportedEncodingException;
034    import java.util.Arrays;
035    import java.util.Collections;
036    import java.util.Enumeration;
037    import java.util.HashMap;
038    import java.util.List;
039    import java.util.Locale;
040    import java.util.Map;
041    
042    import static org.apache.myfaces.tobago.TobagoConstants.FORM_ACCEPT_CHARSET;
043    
044    public class TobagoMultipartFormdataRequest extends HttpServletRequestWrapper {
045    
046      private static final Log LOG = LogFactory.getLog(TobagoMultipartFormdataRequest.class);
047    
048      public static final long ONE_KB = 1024;
049      public static final long ONE_MB = ONE_KB * ONE_KB;
050      public static final long ONE_GB = ONE_KB * ONE_MB;
051    
052      private Map<String, String[]> parameters;
053    
054      private Map<String, FileItem> fileItems;
055    
056      public TobagoMultipartFormdataRequest(HttpServletRequest request) {
057        this(request, System.getProperty("java.io.tmpdir"), ONE_MB);
058      }
059    
060      public TobagoMultipartFormdataRequest(HttpServletRequest request, String repositoryPath, long maxSize) {
061        super(request);
062        init(request, repositoryPath, maxSize);
063      }
064    
065      private void init(HttpServletRequest request, String repositoryPath, long maxSize) {
066        if (!ServletFileUpload.isMultipartContent(request)) {
067          String errorText = "contentType is not multipart/form-data but '" + request.getContentType() + "'";
068          LOG.error(errorText);
069          throw new FacesException(errorText);
070        } else {
071          parameters = new HashMap<String, String[]>();
072          fileItems = new HashMap<String, FileItem>();
073          DiskFileItemFactory factory = new DiskFileItemFactory();
074    
075          factory.setRepository(new File(repositoryPath));
076    
077          ServletFileUpload upload = new ServletFileUpload(factory);
078    
079          upload.setSizeMax(maxSize);
080    
081          if (upload.getHeaderEncoding() != null) {
082            // TODO: enable configuration of  'accept-charset'
083            upload.setHeaderEncoding(FORM_ACCEPT_CHARSET);
084          }
085          List<FileItem> itemList;
086          try {
087            itemList = (List<FileItem>) upload.parseRequest(request);
088          } catch (FileUploadException e) {
089            //LOG.error(e);
090            throw new FacesException(e);
091          }
092          if (LOG.isDebugEnabled()) {
093            LOG.debug("parametercount = " + itemList.size() + " + " + request.getParameterMap().size());
094          }
095          for (FileItem item : itemList) {
096            String key = item.getFieldName();
097            if (LOG.isDebugEnabled()) {
098              String value = item.getString();
099              if (value.length() > 100) {
100                value = value.substring(0, 100) + " [...]";
101              }
102              LOG.debug("Parameter: '" + key + "'='" + value + "' isFormField=" + item.isFormField()
103                  + " contentType='" + item.getContentType() + "'");
104            }
105            if (item.isFormField()) {
106              String newValue;
107              try {
108                // TODO: enable configuration of 'accept-charset'
109                newValue = item.getString(FORM_ACCEPT_CHARSET);
110              } catch (UnsupportedEncodingException e) {
111                LOG.error("Caught: " + e.getMessage(), e);
112                newValue = item.getString();
113              }
114    
115              addParameter(key, newValue);
116            } else {
117              fileItems.put(key, item);
118            }
119          }
120    
121          // merging the GET parameters:
122          Enumeration e = request.getParameterNames();
123          while(e.hasMoreElements()) {
124            final String name = (String) e.nextElement();
125            final String[] newValues = request.getParameterValues(name);
126            if (LOG.isDebugEnabled()) {
127              LOG.debug("Parameter: '" + name + "'='" + Arrays.toString(newValues) + "' (GET)");
128            }
129            for (String newValue : newValues) {
130              addParameter(name, newValue);
131            }
132          }
133        }
134      }
135    
136      private void addParameter(String key, String newValue) {
137        final String[] inStock = parameters.get(key);
138        final String[] values;
139        if (inStock == null) {
140          values = new String[]{newValue};
141        } else {
142          values = new String[inStock.length + 1];
143          System.arraycopy(inStock, 0, values, 0, inStock.length);
144          values[inStock.length] = newValue;
145        }
146        parameters.put(key, values);
147      }
148    
149      public FileItem getFileItem(String key) {
150        if (fileItems != null) {
151          return fileItems.get(key);
152        }
153        return null;
154      }
155    
156      public String getParameter(String key) {
157        String parameter = null;
158        String[] values = (String[]) parameters.get(key);
159        if (values != null) {
160          parameter = values[0];
161        }
162        return parameter;
163      }
164    
165      public Enumeration getParameterNames() {
166        return Collections.enumeration(parameters.keySet());
167      }
168    
169      public String[] getParameterValues(String key) {
170        return (String[]) parameters.get(key);
171      }
172    
173      public Map getParameterMap() {
174        return parameters;
175      }
176    
177      public static long getMaxSize(String param) {
178        if (param != null) {
179          String number = param.toLowerCase(Locale.ENGLISH);
180          long factor = 1;
181          if (number.endsWith("g")) {
182            factor = ONE_GB;
183            number = number.substring(0, number.length() - 1);
184          } else if (number.endsWith("m")) {
185            factor = ONE_MB;
186            number = number.substring(0, number.length() - 1);
187          } else if (number.endsWith("k")) {
188            factor = ONE_KB;
189            number = number.substring(0, number.length() - 1);
190          }
191          try {
192            return Long.parseLong(number.trim()) * factor;
193          } catch (NumberFormatException e) {
194            LOG.error("Given max file size for "
195                + TobagoMultipartFormdataRequest.class.getName() + " " + param + " couldn't parsed to a number");
196          }
197        }
198        return ONE_MB;
199      }
200    }