1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.wysiwyg.server.filter

File ConversionFilter.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

18
53
5
1
211
118
18
0.34
10.6
5
3.6

Classes

Class Line # Actions
ConversionFilter 50 53 0% 18 6
0.9210526392.1%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.wysiwyg.server.filter;
21   
22    import java.io.IOException;
23    import java.lang.reflect.Type;
24    import java.util.HashMap;
25    import java.util.Map;
26   
27    import javax.servlet.Filter;
28    import javax.servlet.FilterChain;
29    import javax.servlet.FilterConfig;
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.lang3.RandomStringUtils;
37    import org.apache.commons.lang3.StringUtils;
38    import org.slf4j.Logger;
39    import org.slf4j.LoggerFactory;
40    import org.xwiki.gwt.wysiwyg.client.converter.HTMLConverter;
41   
42    import com.xpn.xwiki.web.Utils;
43   
44    /**
45    * This filter is used to convert the values of request parameters that require HTML conversion before being processed.
46    * A HTML editor can use this filter to convert its output to a specific syntax before it is saved.
47    *
48    * @version $Id: b6540ff6b850beca2f4d404bbcdc0f0b59fced5c $
49    */
 
50    public class ConversionFilter implements Filter
51    {
52    /**
53    * The logger instance.
54    */
55    private static final Logger LOGGER = LoggerFactory.getLogger(ConversionFilter.class);
56   
57    /**
58    * The name of the request parameter whose multiple values indicate the request parameters that require HTML
59    * conversion. For instance, if this parameter's value is {@code [description, content]} then the request has two
60    * parameters, {@code description} and {@code content}, requiring HTML conversion. The syntax these parameters must
61    * be converted to is found also on the request, under {@code description_syntax} and {@code content_syntax}
62    * parameters.
63    */
64    private static final String REQUIRES_HTML_CONVERSION = "RequiresHTMLConversion";
65   
66    /**
67    * The name of the session attribute holding the conversion output. The conversion output is stored in a {@link Map}
68    * of {@link Map}s. The first key identifies the request and the second key is the name of the request parameter
69    * that required HTML conversion.
70    */
71    private static final String CONVERSION_OUTPUT = "com.xpn.xwiki.wysiwyg.server.converter.output";
72   
73    /**
74    * The name of the session attribute holding the conversion exceptions. The conversion exceptions are stored in a
75    * {@link Map} of {@link Map}s. The first key identifies the request and the second key is the name of the request
76    * parameter that required HTML conversion.
77    */
78    private static final String CONVERSION_ERRORS = "com.xpn.xwiki.wysiwyg.server.converter.errors";
79   
 
80  32 toggle @Override
81    public void destroy()
82    {
83    }
84   
 
85  9699 toggle @Override
86    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
87    ServletException
88    {
89    // Take the list of request parameters that require HTML conversion.
90  9701 String[] parametersRequiringHTMLConversion = req.getParameterValues(REQUIRES_HTML_CONVERSION);
91  9694 if (parametersRequiringHTMLConversion != null) {
92  5 MutableServletRequestFactory mreqFactory =
93    Utils.getComponent((Type) MutableServletRequestFactory.class, req.getProtocol());
94    // Wrap the current request in order to be able to change request parameters.
95  5 MutableServletRequest mreq = mreqFactory.newInstance(req);
96    // Remove the list of request parameters that require HTML conversion to avoid recurrency.
97  5 mreq.removeParameter(REQUIRES_HTML_CONVERSION);
98    // Try to convert each parameter from the list and save caught exceptions.
99  5 Map<String, Throwable> errors = new HashMap<String, Throwable>();
100    // Save also the output to prevent loosing data in case of conversion exceptions.
101  5 Map<String, String> output = new HashMap<String, String>();
102  12 for (int i = 0; i < parametersRequiringHTMLConversion.length; i++) {
103  7 String parameterName = parametersRequiringHTMLConversion[i];
104  7 String html = req.getParameter(parameterName);
105    // Remove the syntax parameter from the request to avoid interference with further request processing.
106  7 String syntax = mreq.removeParameter(parameterName + "_syntax");
107  7 if (html == null || syntax == null) {
108  0 continue;
109    }
110  7 try {
111  7 HTMLConverter converter = Utils.getComponent((Type) HTMLConverter.class);
112  7 mreq.setParameter(parameterName, converter.fromHTML(html, syntax));
113    } catch (Exception e) {
114  2 LOGGER.error(e.getLocalizedMessage(), e);
115  2 errors.put(parameterName, e);
116    }
117    // If the conversion fails the output contains the value before the conversion.
118  7 output.put(parameterName, mreq.getParameter(parameterName));
119    }
120   
121  5 if (!errors.isEmpty()) {
122  2 handleConversionErrors(errors, output, mreq, res);
123    } else {
124  3 chain.doFilter(mreq, res);
125    }
126    } else {
127  9702 chain.doFilter(req, res);
128    }
129    }
130   
 
131  32 toggle @Override
132    public void init(FilterConfig config) throws ServletException
133    {
134    }
135   
 
136  2 toggle private void handleConversionErrors(Map<String, Throwable> errors, Map<String, String> output,
137    MutableServletRequest mreq, ServletResponse res) throws IOException
138    {
139  2 ServletRequest req = mreq.getRequest();
140  2 if (req instanceof HttpServletRequest
141    && "XMLHttpRequest".equals(((HttpServletRequest) req).getHeader("X-Requested-With"))) {
142    // If this is an AJAX request then we should simply send back the error.
143  1 StringBuilder errorMessage = new StringBuilder();
144    // Aggregate all error messages (for all fields that have conversion errors).
145  1 for (Map.Entry<String, Throwable> entry : errors.entrySet()) {
146  1 errorMessage.append(entry.getKey()).append(": ");
147  1 errorMessage.append(entry.getValue().getLocalizedMessage()).append('\n');
148    }
149  1 ((HttpServletResponse) res).sendError(400, errorMessage.substring(0, errorMessage.length() - 1));
150  1 return;
151    }
152    // Otherwise, if this is a normal request, we have to redirect the request back and provide a key to
153    // access the exception and the value before the conversion from the session.
154    // Redirect to the error page specified on the request.
155  1 String redirectURL = mreq.getParameter("xerror");
156  1 if (redirectURL == null) {
157    // Redirect to the referrer page.
158  1 redirectURL = mreq.getReferer();
159    }
160    // Extract the query string.
161  1 String queryString = StringUtils.substringAfterLast(redirectURL, String.valueOf('?'));
162    // Remove the query string.
163  1 redirectURL = StringUtils.substringBeforeLast(redirectURL, String.valueOf('?'));
164    // Remove the previous key from the query string. We have to do this since this might not be the first
165    // time the conversion fails for this redirect URL.
166  1 queryString = queryString.replaceAll("key=.*&?", "");
167  1 if (queryString.length() > 0 && !queryString.endsWith(String.valueOf('&'))) {
168  1 queryString += '&';
169    }
170    // Save the output and the caught exceptions on the session.
171  1 queryString += "key=" + save(mreq, output, errors);
172  1 mreq.sendRedirect(res, redirectURL + '?' + queryString);
173    }
174   
175    /**
176    * Saves on the session the conversion output and the caught conversion exceptions, after a conversion failure.
177    *
178    * @param mreq the request used to access the session
179    * @param output the conversion output for the given request
180    * @param errors the conversion exceptions for the given request
181    * @return a key that can be used along with the name of the request parameters that required HTML conversion to
182    * extract the conversion output and the conversion exceptions from the {@link #CONVERSION_OUTPUT} and
183    * {@value #CONVERSION_ERRORS} session attributes
184    */
 
185  1 toggle @SuppressWarnings("unchecked")
186    private String save(MutableServletRequest mreq, Map<String, String> output, Map<String, Throwable> errors)
187    {
188    // Generate a random key to identify the request.
189  1 String key = RandomStringUtils.randomAlphanumeric(4);
190   
191    // Save the output on the session.
192  1 Map<String, Map<String, String>> conversionOutput =
193    (Map<String, Map<String, String>>) mreq.getSessionAttribute(CONVERSION_OUTPUT);
194  1 if (conversionOutput == null) {
195  1 conversionOutput = new HashMap<String, Map<String, String>>();
196  1 mreq.setSessionAttribute(CONVERSION_OUTPUT, conversionOutput);
197    }
198  1 conversionOutput.put(key, output);
199   
200    // Save the errors on the session.
201  1 Map<String, Map<String, Throwable>> conversionErrors =
202    (Map<String, Map<String, Throwable>>) mreq.getSessionAttribute(CONVERSION_ERRORS);
203  1 if (conversionErrors == null) {
204  1 conversionErrors = new HashMap<String, Map<String, Throwable>>();
205  1 mreq.setSessionAttribute(CONVERSION_ERRORS, conversionErrors);
206    }
207  1 conversionErrors.put(key, errors);
208   
209  1 return key;
210    }
211    }