View Javadoc
1   /*
2    * Copyright 2012-2021 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.filter;
17  
18  import java.io.IOException;
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.concurrent.ConcurrentHashMap;
25  
26  import javax.servlet.Filter;
27  import javax.servlet.FilterChain;
28  import javax.servlet.FilterConfig;
29  import javax.servlet.ServletContext;
30  import javax.servlet.ServletException;
31  import javax.servlet.ServletRequest;
32  import javax.servlet.ServletResponse;
33  import javax.servlet.http.HttpServletRequest;
34  import javax.servlet.http.HttpServletResponse;
35  
36  import org.apache.commons.codec.DecoderException;
37  import org.apache.commons.codec.net.URLCodec;
38  import org.codelibs.core.lang.StringUtil;
39  import org.lastaflute.web.servlet.filter.LastaPrepareFilter;
40  
41  public class EncodingFilter implements Filter {
42      public static final String ENCODING_MAP = "encodingRules";
43  
44      protected Map<String, String> encodingMap = new ConcurrentHashMap<>();
45  
46      protected String encoding;
47  
48      protected ServletContext servletContext;
49  
50      protected URLCodec urlCodec = new URLCodec();
51  
52      @Override
53      public void init(final FilterConfig config) throws ServletException {
54          servletContext = config.getServletContext();
55  
56          encoding = config.getInitParameter(LastaPrepareFilter.ENCODING_KEY);
57          if (encoding == null) {
58              encoding = LastaPrepareFilter.DEFAULT_ENCODING;
59          }
60  
61          // ex. sjis:Shift_JIS,eucjp:EUC-JP
62          final String value = config.getInitParameter(ENCODING_MAP);
63          if (StringUtil.isNotBlank(value)) {
64              final String[] encodingPairs = value.split(",");
65              for (final String pair : encodingPairs) {
66                  final String[] encInfos = pair.trim().split(":");
67                  if (encInfos.length == 2) {
68                      encodingMap.put("/" + encInfos[0] + "/", encInfos[1]);
69                  }
70              }
71          }
72      }
73  
74      @Override
75      public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
76              throws IOException, ServletException {
77          final HttpServletRequest req = (HttpServletRequest) request;
78          final String servletPath = req.getServletPath();
79          for (final Map.Entry<String, String> entry : encodingMap.entrySet()) {
80              final String path = entry.getKey();
81              if (servletPath.startsWith(path)) {
82                  req.setCharacterEncoding(entry.getValue());
83                  final StringBuilder locationBuf = new StringBuilder(1000);
84                  final String contextPath = servletContext.getContextPath();
85                  if (StringUtil.isNotBlank(contextPath) && !"/".equals(contextPath)) {
86                      locationBuf.append(contextPath);
87                  }
88                  locationBuf.append('/');
89                  locationBuf.append(servletPath.substring(path.length()));
90                  boolean append = false;
91                  final Map<String, String[]> parameterMap = new HashMap<>(req.getParameterMap());
92                  parameterMap.putAll(getParameterMapFromQueryString(req, entry.getValue()));
93                  for (final Map.Entry<String, String[]> paramEntry : parameterMap.entrySet()) {
94                      final String[] values = paramEntry.getValue();
95                      if (values == null) {
96                          continue;
97                      }
98                      final String key = paramEntry.getKey();
99                      for (final String value : values) {
100                         if (append) {
101                             locationBuf.append('&');
102                         } else {
103                             locationBuf.append('?');
104                             append = true;
105                         }
106                         locationBuf.append(urlCodec.encode(key, encoding));
107                         locationBuf.append('=');
108                         locationBuf.append(urlCodec.encode(value, encoding));
109                     }
110 
111                 }
112                 final HttpServletResponse res = (HttpServletResponse) response;
113                 res.sendRedirect(locationBuf.toString());
114                 return;
115             }
116         }
117 
118         chain.doFilter(request, response);
119     }
120 
121     protected Map<String, String[]> getParameterMapFromQueryString(final HttpServletRequest request, final String enc) throws IOException {
122         final String queryString = request.getQueryString();
123         if (StringUtil.isNotBlank(queryString)) {
124             return parseQueryString(queryString, enc);
125         }
126         return Collections.emptyMap();
127     }
128 
129     protected Map<String, String[]> parseQueryString(final String queryString, final String enc) throws IOException {
130         final Map<String, List<String>> paramListMap = new HashMap<>();
131         final String[] pairs = queryString.split("&");
132         try {
133             for (final String pair : pairs) {
134                 final int pos = pair.indexOf('=');
135                 if (pos >= 0) {
136                     final String key = urlCodec.decode(pair.substring(0, pos), enc);
137                     List<String> list = paramListMap.get(key);
138                     if (list == null) {
139                         list = new ArrayList<>();
140                         paramListMap.put(key, list);
141                     }
142                     if (pos + 1 < pair.length()) {
143                         list.add(urlCodec.decode(pair.substring(pos + 1), enc));
144                     } else {
145                         list.add(StringUtil.EMPTY);
146                     }
147                 } else {
148                     final String key = urlCodec.decode(pair, enc);
149                     List<String> list = paramListMap.get(key);
150                     if (list == null) {
151                         list = new ArrayList<>();
152                         paramListMap.put(key, list);
153                     }
154                     list.add(StringUtil.EMPTY);
155                 }
156             }
157         } catch (final DecoderException e) {
158             throw new IOException(e);
159         }
160 
161         final Map<String, String[]> paramMap = new HashMap<>(paramListMap.size());
162         for (final Map.Entry<String, List<String>> entry : paramListMap.entrySet()) {
163             final List<String> list = entry.getValue();
164             paramMap.put(entry.getKey(), list.toArray(new String[list.size()]));
165         }
166         return paramMap;
167     }
168 
169     @Override
170     public void destroy() {
171         // nothing
172     }
173 }