1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package com.xpn.xwiki.web

File Utils.java

 

Coverage histogram

../../../../img/srcFileCovDistChart6.png
72% of files have more coverage

Code metrics

90
247
35
1
922
501
108
0.44
7.06
35
3.09

Classes

Class Line # Actions
Utils 57 247 0% 108 176
0.526881752.7%
 

Contributing tests

This file is covered by 488 tests. .

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 com.xpn.xwiki.web;
21   
22    import java.io.IOException;
23    import java.io.InputStream;
24    import java.io.UnsupportedEncodingException;
25    import java.lang.reflect.Type;
26    import java.net.URL;
27    import java.util.Collections;
28    import java.util.Date;
29    import java.util.HashMap;
30    import java.util.List;
31    import java.util.Map;
32    import java.util.Map.Entry;
33   
34    import javax.inject.Provider;
35    import javax.servlet.http.HttpServletRequest;
36    import javax.servlet.http.HttpServletResponse;
37   
38    import org.apache.commons.fileupload.FileItem;
39    import org.apache.commons.io.IOUtils;
40    import org.apache.commons.lang3.BooleanUtils;
41    import org.apache.commons.lang3.RandomStringUtils;
42    import org.apache.commons.lang3.StringUtils;
43    import org.apache.commons.lang3.exception.ExceptionUtils;
44    import org.apache.struts.upload.MultipartRequestWrapper;
45    import org.slf4j.Logger;
46    import org.slf4j.LoggerFactory;
47    import org.xwiki.component.manager.ComponentLookupException;
48    import org.xwiki.component.manager.ComponentManager;
49    import org.xwiki.xml.XMLUtils;
50   
51    import com.xpn.xwiki.XWiki;
52    import com.xpn.xwiki.XWikiContext;
53    import com.xpn.xwiki.XWikiException;
54    import com.xpn.xwiki.plugin.fileupload.FileUploadPlugin;
55    import com.xpn.xwiki.util.Util;
56   
 
57    public class Utils
58    {
59    protected static final Logger LOGGER = LoggerFactory.getLogger(Utils.class);
60   
61    /** A key that is used for placing a map of replaced (for protection) strings in the context. */
62    private static final String PLACEHOLDERS_CONTEXT_KEY = Utils.class.getCanonicalName() + "_placeholders";
63   
64    /** Whether placeholders are enabled or not. */
65    private static final String PLACEHOLDERS_ENABLED_CONTEXT_KEY = Utils.class.getCanonicalName()
66    + "_placeholders_enabled";
67   
68    /**
69    * The component manager used by {@link #getComponent(Class)} and {@link #getComponent(Class, String)}. It is useful
70    * for any non component code that need to initialize/access components.
71    */
72    private static ComponentManager rootComponentManager;
73   
74    /**
75    * Generate the response by parsing a velocity template and printing the result to the {@link XWikiResponse
76    * Response}. This is the main entry point to the View part of the XWiki MVC architecture.
77    *
78    * @param template The name of the template to parse, without the {@code .vm} prefix. The template will be searched
79    * in the usual places: current XWikiSkins object, attachment of the current skin document, current skin
80    * folder, baseskin folder, /templates/ folder.
81    * @param context the current context
82    * @throws XWikiException when the response cannot be written to the client (for example when the client canceled
83    * the request, thus closing the socket)
84    * @see XWiki#parseTemplate(String, XWikiContext)
85    */
 
86  4707 toggle public static void parseTemplate(String template, XWikiContext context) throws XWikiException
87    {
88  4707 parseTemplate(template, true, context);
89    }
90   
91    /**
92    * Generate the response by parsing a velocity template and (optionally) printing the result to the
93    * {@link XWikiResponse Response}.
94    *
95    * @param template The name of the template to parse, without the {@code .vm} prefix. The template will be searched
96    * in the usual places: current XWikiSkins object, attachment of the current skin document, current skin
97    * folder, baseskin folder, /templates/ folder.
98    * @param write Whether the generated response should be written to the client or not. If {@code false}, only the
99    * needed headers are generated, suitable for implementing a HEAD response.
100    * @param context the current context
101    * @throws XWikiException when the response cannot be written to the client (for example when the client canceled
102    * the request, thus closing the socket)
103    * @see XWiki#parseTemplate(String, XWikiContext)
104    */
 
105  30065 toggle public static void parseTemplate(String template, boolean write, XWikiContext context) throws XWikiException
106    {
107  30071 XWikiResponse response = context.getResponse();
108   
109    // If a Redirect has already been sent then don't process the template since it means the output has been
110    // committed and we shouldn't write anymore to the servlet output stream!
111    // See: http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html#sendRedirect(String)
112    // "After using this method, the response should be considered to be committed and should not be written
113    // to."
114  30068 if (response.getStatus() == HttpServletResponse.SC_FOUND) {
115  476 return;
116    }
117   
118    // Set content-type and encoding (this can be changed later by pages themselves)
119  29588 response.setContentType("text/html; charset=" + context.getWiki().getEncoding());
120   
121  29593 String action = context.getAction();
122  29593 long cacheSetting = context.getWiki().getXWikiPreferenceAsLong("headers_nocache", -1, context);
123  29585 if (cacheSetting == -1) {
124  29596 cacheSetting = context.getWiki().ParamAsLong("xwiki.httpheaders.cache", -1);
125    }
126  29597 if (cacheSetting == -1) {
127  29594 cacheSetting = 1;
128    }
129  29593 if ((!"download".equals(action)) && (!"skin".equals(action))) {
130  29592 if (context.getResponse() instanceof XWikiServletResponse) {
131    // Add a last modified to tell when the page was last updated
132  29588 if (context.getWiki().getXWikiPreferenceAsLong("headers_lastmodified", 0, context) != 0) {
133  0 if (context.getDoc() != null) {
134  0 response.setDateHeader("Last-Modified", context.getDoc().getDate().getTime());
135    }
136    }
137    // Set a nocache to make sure the page is reloaded after an edit
138  29589 if (cacheSetting == 1) {
139  29592 response.setHeader("Pragma", "no-cache");
140  29594 response.setHeader("Cache-Control", "no-cache");
141  0 } else if (cacheSetting == 2) {
142  0 response.setHeader("Pragma", "no-cache");
143  0 response.setHeader("Cache-Control", "max-age=0, no-cache, no-store");
144  0 } else if (cacheSetting == 3) {
145  0 response.setHeader("Cache-Control", "private");
146  0 } else if (cacheSetting == 4) {
147  0 response.setHeader("Cache-Control", "public");
148    }
149   
150    // Set an expires in one month
151  29583 long expires = context.getWiki().getXWikiPreferenceAsLong("headers_expires", -1, context);
152  29593 if (expires == -1) {
153  29589 response.setDateHeader("Expires", -1);
154  0 } else if (expires != 0) {
155  0 response.setDateHeader("Expires", (new Date()).getTime() + 30 * 24 * 3600 * 1000L);
156    }
157    }
158    }
159   
160  29593 if (("download".equals(action)) || ("skin".equals(action))) {
161    // Set a nocache to make sure these files are not cached by proxies
162  5 if (cacheSetting == 1 || cacheSetting == 2) {
163  5 response.setHeader("Cache-Control", "no-cache");
164    }
165    }
166   
167  29596 context.getWiki().getPluginManager().beginParsing(context);
168    // This class allows various components in the rendering chain to use placeholders for some fragile data. For
169    // example, the URL generated for the image macro should not be further rendered, as it might get broken by wiki
170    // filters. For this to work, keep a map of [used placeholders -> values] in the context, and replace them when
171    // the content is fully rendered. The rendering code can use Utils.createPlaceholder.
172    // Initialize the placeholder map
173  29601 enablePlaceholders(context);
174  29595 String content = "";
175  29593 try {
176    // Note: This line below can change the state of the response. For example a vm file can have a call to
177    // sendRedirect. In this case we need to be careful to not write to the output stream since it's already
178    // been committed. This is why we do a check below before calling response.getOutputStream().write().
179  29596 content = context.getWiki().evaluateTemplate(template + ".vm", context);
180    // Replace all placeholders with the protected values
181  29599 content = replacePlaceholders(content, context);
182  29601 disablePlaceholders(context);
183  29601 content = context.getWiki().getPluginManager().endParsing(content.trim(), context);
184    } catch (IOException e) {
185  0 LOGGER.debug("IOException while evaluating template [{}] from /templates/", template, e);
186   
187    // get Error template "This template does not exist
188  0 try {
189  0 content = context.getWiki().evaluateTemplate("templatedoesnotexist.vm", context);
190  0 content = content.trim();
191    } catch (IOException ex) {
192    // Cannot write output, can't do anything else
193    }
194    }
195   
196  29600 if (!context.isFinished()) {
197  26172 if (context.getResponse() instanceof XWikiServletResponse) {
198    // Set the content length to the number of bytes, not the
199    // string length, so as to handle multi-byte encodings
200  26174 try {
201  26174 response.setContentLength(content.getBytes(context.getWiki().getEncoding()).length);
202    } catch (UnsupportedEncodingException e) {
203  0 e.printStackTrace();
204    }
205    }
206   
207    // We only write if the caller has asked.
208    // We also make sure to verify that there hasn't been a call to sendRedirect before since it would mean the
209    // response has already been written to and we shouldn't try to write in it.
210  26173 if (write && response.getStatus() != HttpServletResponse.SC_FOUND)
211    {
212  25876 try {
213  25876 try {
214  25873 response.getOutputStream().write(content.getBytes(context.getWiki().getEncoding()));
215    } catch (IllegalStateException ex) {
216  2 response.getWriter().write(content);
217    }
218    } catch (IOException e) {
219  10 throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
220    XWikiException.ERROR_XWIKI_APP_SEND_RESPONSE_EXCEPTION, "Exception while sending response", e);
221    }
222    }
223    }
224   
225  29590 try {
226  29591 response.getOutputStream().flush();
227    } catch (Throwable ex) {
228  3429 try {
229  3429 response.getWriter().flush();
230    } catch (Throwable ex2) {
231    }
232    }
233    }
234   
235    /**
236    * Retrieve the URL to which the client should be redirected after the successful completion of the requested
237    * action. This is taken from the {@code xredirect} parameter in the query string. If this parameter is not set, or
238    * is set to an empty value, return the default redirect specified as the second argument.
239    *
240    * @param request the current request
241    * @param defaultRedirect the default value to use if no {@code xredirect} parameter is present
242    * @return the destination URL, as specified in the {@code xredirect} parameter, or the specified default URL
243    */
 
244  145 toggle public static String getRedirect(XWikiRequest request, String defaultRedirect)
245    {
246  145 String redirect = request.getParameter("xredirect");
247  145 if (StringUtils.isBlank(redirect)) {
248  110 redirect = defaultRedirect;
249    }
250   
251  145 return redirect;
252    }
253   
254    /**
255    * Retrieve the URL to which the client should be redirected after the successful completion of the requested
256    * action. This is taken from the {@code xredirect} parameter in the query string. If this parameter is not set, or
257    * is set to an empty value, compose an URL back to the current document, using the specified action and query
258    * string, and return it.
259    *
260    * @param action the XWiki action to use for composing the default redirect URL ({@code view}, {@code edit}, etc)
261    * @param queryString the query parameters to append to the fallback URL
262    * @param context the current context
263    * @return the destination URL, as specified in the {@code xredirect} parameter, or computed using the current
264    * document and the specified action and query string
265    */
 
266  663 toggle public static String getRedirect(String action, String queryString, XWikiContext context)
267    {
268  663 return getRedirect(action, queryString, "xredirect");
269    }
270   
271    /**
272    * Retrieve the URL to which the client should be redirected after the successful completion of the requested
273    * action. If any of the specified {@code redirectParameters} (in order) is present in the query string, it is
274    * returned as the redirect destination. If none of the parameters is set, compose an URL back to the current
275    * document using the specified action and query string, and return it.
276    *
277    * @param action the XWiki action to use for composing the default redirect URL ({@code view}, {@code edit}, etc)
278    * @param queryString the query parameters to append to the fallback URL
279    * @param redirectParameters list of request parameters to look for as the redirect destination; each of the
280    * parameters is tried in the order they are passed, and the first one set to a non-empty value is
281    * returned, if any
282    * @return the destination URL, as specified in one of the {@code redirectParameters}, or computed using the current
283    * document and the specified action and query string
284    */
 
285  676 toggle public static String getRedirect(String action, String queryString, String... redirectParameters)
286    {
287  676 XWikiContext context = getContext();
288  676 XWikiRequest request = context.getRequest();
289  676 String redirect = null;
290  676 for (String p : redirectParameters) {
291  689 redirect = request.getParameter(p);
292  689 if (StringUtils.isNotEmpty(redirect)) {
293  69 break;
294    }
295    }
296   
297  676 if (StringUtils.isEmpty(redirect)) {
298  607 redirect = context.getDoc().getURL(action, queryString, true, context);
299    }
300   
301  676 return redirect;
302    }
303   
304    /**
305    * Retrieve the URL to which the client should be redirected after the successful completion of the requested
306    * action. This is taken from the {@code xredirect} parameter in the query string. If this parameter is not set, or
307    * is set to an empty value, compose an URL back to the current document, using the specified action, and return it.
308    *
309    * @param action the XWiki action to use for composing the default redirect URL ({@code view}, {@code edit}, etc)
310    * @param context the current context
311    * @return the destination URL, as specified in the {@code xredirect} parameter, or computed using the current
312    * document and the specified action
313    */
 
314  600 toggle public static String getRedirect(String action, XWikiContext context)
315    {
316  600 return getRedirect(action, null, context);
317    }
318   
319    /**
320    * Retrieve the name of the velocity template which should be used to generate the response. This is taken from the
321    * {@code xpage} parameter in the query string. If this parameter is not set, or is set to an empty value, return
322    * the provided default name.
323    *
324    * @param request the current request
325    * @param defaultpage the default value to use if no {@code xpage} parameter is set
326    * @return the name of the requested template, as specified in the {@code xpage} parameter, or the specified default
327    * template
328    */
 
329  30027 toggle public static String getPage(XWikiRequest request, String defaultpage)
330    {
331  30028 String page = request.getParameter("xpage");
332  30039 if (StringUtils.isEmpty(page)) {
333  26472 page = defaultpage;
334    }
335   
336  30032 return page;
337    }
338   
339    /**
340    * Get the name of an uploaded file, corresponding to the specified form field.
341    *
342    * @param filelist the list of uploaded files, computed by the FileUpload plugin
343    * @param name the name of the form field
344    * @return the original name of the file, if the specified field name does correspond to an uploaded file, or
345    * {@code null} otherwise
346    */
 
347  0 toggle public static String getFileName(List<FileItem> filelist, String name)
348    {
349  0 for (FileItem item : filelist) {
350  0 if (name.equals(item.getFieldName())) {
351  0 return item.getName();
352    }
353    }
354   
355  0 return null;
356    }
357   
358    /**
359    * Get the content of an uploaded file, corresponding to the specified form field.
360    *
361    * @param filelist the list of uploaded files, computed by the FileUpload plugin
362    * @param name the name of the form field
363    * @return the content of the file, if the specified field name does correspond to an uploaded file, or {@code null}
364    * otherwise
365    * @throws XWikiException if the file cannot be read due to an underlying I/O exception
366    */
 
367  0 toggle public static byte[] getContent(List<FileItem> filelist, String name) throws XWikiException
368    {
369  0 for (FileItem item : filelist) {
370  0 if (name.equals(item.getFieldName())) {
371  0 byte[] data = new byte[(int) item.getSize()];
372  0 InputStream fileis = null;
373  0 try {
374  0 fileis = item.getInputStream();
375  0 fileis.read(data);
376    } catch (IOException e) {
377  0 throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
378    XWikiException.ERROR_XWIKI_APP_UPLOAD_FILE_EXCEPTION,
379    "Exception while reading uploaded parsed file", e);
380    } finally {
381  0 IOUtils.closeQuietly(fileis);
382    }
383   
384  0 return data;
385    }
386    }
387   
388  0 return null;
389    }
390   
 
391  43016 toggle public static XWikiContext prepareContext(String action, XWikiRequest request, XWikiResponse response,
392    XWikiEngineContext engine_context) throws XWikiException
393    {
394  43013 XWikiContext context = new XWikiContext();
395  43016 String dbname = "xwiki";
396  43007 URL url = XWiki.getRequestURL(request);
397  43037 context.setURL(url);
398   
399  43033 context.setEngineContext(engine_context);
400  43011 context.setRequest(request);
401  42991 context.setResponse(response);
402  43006 context.setAction(action);
403  43011 context.setWikiId(dbname);
404   
405  43004 int mode = 0;
406  43015 if (request instanceof XWikiServletRequest) {
407  43009 mode = XWikiContext.MODE_SERVLET;
408    }
409  42999 context.setMode(mode);
410   
411  43005 return context;
412    }
413   
414    /**
415    * Parse the request parameters from the specified String using the specified encoding. <strong>IMPLEMENTATION
416    * NOTE</strong>: URL decoding is performed individually on the parsed name and value elements, rather than on the
417    * entire query string ahead of time, to properly deal with the case where the name or value includes an encoded
418    * {@code =} or {@code &} character that would otherwise be interpreted as a delimiter.
419    * <p>
420    * Code borrowed from Apache Tomcat 5.0
421    *
422    * @param data input string containing request parameters
423    * @param encoding the encoding to use for transforming bytes into characters
424    * @throws IllegalArgumentException if the data is malformed
425    */
 
426  0 toggle public static Map<String, String[]> parseParameters(String data, String encoding)
427    throws UnsupportedEncodingException
428    {
429  0 if (!StringUtils.isEmpty(data)) {
430    // use the specified encoding to extract bytes out of the
431    // given string so that the encoding is not lost. If an
432    // encoding is not specified, let it use platform default
433  0 byte[] bytes = null;
434  0 try {
435  0 if (encoding == null) {
436  0 bytes = data.getBytes();
437    } else {
438