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

File TempResourceAction.java

 

Coverage histogram

../../../../img/srcFileCovDistChart9.png
41% of files have more coverage

Code metrics

10
45
5
1
206
109
14
0.31
9
5
2.8

Classes

Class Line # Actions
TempResourceAction 65 45 0% 14 6
0.990%
 

Contributing tests

This file is covered by 8 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.File;
23    import java.io.IOException;
24    import java.io.UnsupportedEncodingException;
25    import java.net.URI;
26    import java.net.URLDecoder;
27    import java.net.URLEncoder;
28    import java.util.ArrayList;
29    import java.util.List;
30    import java.util.regex.Matcher;
31    import java.util.regex.Pattern;
32   
33    import org.apache.commons.io.FileUtils;
34    import org.apache.commons.io.IOUtils;
35    import org.apache.commons.lang3.StringUtils;
36    import org.apache.tika.mime.MimeTypes;
37    import org.slf4j.Logger;
38    import org.slf4j.LoggerFactory;
39    import org.xwiki.environment.Environment;
40    import org.xwiki.tika.internal.TikaUtils;
41   
42    import com.xpn.xwiki.XWikiContext;
43    import com.xpn.xwiki.XWikiException;
44    import com.xpn.xwiki.util.Util;
45   
46    /**
47    * Action responsible for downloading temporary resources created by various modules. The temporary resource is put in
48    * the temporary directory in a directory named "temp" and in subdirectories "(module)/(wiki)/(space)/(page)/(file)"
49    * where:
50    * <ul>
51    * <li>(module): it's the 3rd path segment in the request URL (format: {code .../temp/1/2/3/4})</li>
52    * <li>(wiki): the name of the current wiki (extracted from the URL too)</li>
53    * <li>(space): it's the 1st path segment in the request URL (format: {code .../temp/1/2/3/4})</li>
54    * <li>(page): it's the 2nd path segment in the request URL (format: {code .../temp/1/2/3/4})</li>
55    * <li>(file): it's the 4th path segment in the request URL (format: {code .../temp/1/2/3/4})</li>
56    * </ul>
57    * For example if the URL is {@code http://localhost:8080/xwiki/bin/temp/Main/WebHome/test/test.png} then the resource
58    * will be fetched from {@code TMPDIR/temp/test/xwiki/Main/WebHome/test.png}.
59    *
60    * @version $Id: e44bcd3b7399165cdc840ae9a8bf174ea2e461f2 $
61    * @since 2.4M1
62    * @deprecated Use the "tmp" resource reference handler instead since 8.3
63    */
64    @Deprecated
 
65    public class TempResourceAction extends XWikiAction
66    {
67    /**
68    * URI pattern for this action.
69    */
70    public static final Pattern URI_PATTERN = Pattern.compile(".*?/temp/([^/]*+)/([^/]*+)/([^/]*+)/(.*+)");
71   
72    /**
73    * The path separator.
74    */
75    private static final String PATH_SEPARATOR = "/";
76   
77    /**
78    * The URL encoding.
79    */
80    private static final String URL_ENCODING = "UTF-8";
81   
82    /**
83    * Logging support.
84    */
85    private static final Logger LOGGER = LoggerFactory.getLogger(TempResourceAction.class);
86   
87    /**
88    * Used to find the temporary dir.
89    */
90    private Environment environment = Utils.getComponent(Environment.class);
91   
 
92  8 toggle @Override
93    public String render(XWikiContext context) throws XWikiException
94    {
95  9 XWikiRequest request = context.getRequest();
96  9 XWikiResponse response = context.getResponse();
97  8 String uri = request.getRequestURI();
98   
99    // Locate the temporary file.
100  8 File tempFile = getTemporaryFile(uri, context);
101  9 if (null == tempFile) {
102  0 throw new XWikiException(XWikiException.MODULE_XWIKI_APP, XWikiException.ERROR_XWIKI_APP_URL_EXCEPTION,
103    "Invalid temporary resource URL");
104    }
105   
106    // Write temporary file into response.
107  8 response.setDateHeader("Last-Modified", tempFile.lastModified());
108  9 String contentType = MimeTypes.OCTET_STREAM;
109  9 try {
110  9 contentType = TikaUtils.detect(tempFile);
111    } catch (IOException ex) {
112  0 LOGGER.warn(
113    String.format("Unable to determine mime type for temporary resource [%s]", tempFile.getAbsolutePath()),
114    ex);
115    }
116  9 response.setContentType(contentType);
117  9 if ("1".equals(request.getParameter("force-download"))) {
118  1 String fileName = StringUtils.defaultIfBlank(request.getParameter("force-filename"), tempFile.getName());
119  1 fileName = Util.encodeURI(fileName, context).replaceAll("\\+", "%20");
120  1 response.addHeader("Content-disposition", "attachment; filename*=utf-8''" + fileName);
121    }
122  9 try {
123  9 response.setContentLength((int) tempFile.length());
124  9 IOUtils.copy(FileUtils.openInputStream(tempFile), response.getOutputStream());
125    } catch (IOException e) {
126  0 throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
127    XWikiException.ERROR_XWIKI_APP_SEND_RESPONSE_EXCEPTION, "Exception while sending response", e);
128    }
129  9 return null;
130    }
131   
132    /**
133    * Returns the temporary file corresponding to the specified URI.
134    *
135    * @param uri request URI.
136    * @param context xwiki context.
137    * @return temporary file corresponding to the specified URI or null if no such file can be located.
138    */
 
139  14 toggle protected File getTemporaryFile(String uri, XWikiContext context)
140    {
141  14 Matcher matcher = URI_PATTERN.matcher(uri);
142  14 File result = null;
143  14 if (matcher.find()) {
144  14 List<String> pathSegments = new ArrayList<String>();
145    // Add all the path segments.
146  14 pathSegments.add("temp");
147    // temp/module
148  14 pathSegments.add(withMinimalURLEncoding(matcher.group(3)));
149    // temp/module/wiki
150  13 pathSegments.add(encodeURLPathSegment(context.getWikiId()));
151    // temp/module/wiki/space
152  14 pathSegments.add(withMinimalURLEncoding(matcher.group(1)));
153    // temp/module/wiki/space/page
154  14 pathSegments.add(withMinimalURLEncoding(matcher.group(2)));
155    // Save the path prefix before adding the file path to be able to check if the file path tries to get out of
156    // the parent folder (e.g. using '/../').
157  14 String prefix = StringUtils.join(pathSegments, PATH_SEPARATOR);
158    // temp/module/wiki/space/page/path/to/file.tmp
159  13 for (String filePathSegment : matcher.group(4).split(PATH_SEPARATOR)) {
160  17 pathSegments.add(withMinimalURLEncoding(filePathSegment));
161    }
162  14 String path = URI.create(StringUtils.join(pathSegments, PATH_SEPARATOR)).normalize().toString();
163  14 if (path.startsWith(prefix)) {
164  12 result = new File(this.environment.getTemporaryDirectory(), path);
165  12 result = result.exists() ? result : null;
166    }
167    }
168  15 return result;
169    }
170   
171    /**
172    * Keeps only minimal URL encoding. Currently, XWiki's URL factory over encodes the URLs in order to protect them
173    * from XWiki 1.0 syntax parser.
174    * <p>
175    * This method also ensures that the path to the temporary file is fully encoded (has the canonical form) even if
176    * the URL used to access the file is partially decoded (which can happen for instance when XWiki is behind Apache's
177    * {@code mode_proxy} with {@code nocanon} option disabled).
178    *
179    * @param encodedPathSegment an encoded URL path segment
180    * @return the given string with minimal URL encoding
181    */
 
182  57 toggle private String withMinimalURLEncoding(String encodedPathSegment)
183    {
184  58 return encodeURLPathSegment(decodeURLPathSegment(encodedPathSegment));
185    }
186   
 
187  69 toggle private String encodeURLPathSegment(String segment)
188    {
189  73 try {
190  72 return URLEncoder.encode(segment, URL_ENCODING);
191    } catch (UnsupportedEncodingException e) {
192    // This should never happen.
193  0 return segment;
194    }
195    }
196   
 
197  56 toggle private String decodeURLPathSegment(String encodedSegment)
198    {
199  57 try {
200  59 return URLDecoder.decode(encodedSegment, URL_ENCODING);
201    } catch (UnsupportedEncodingException e) {
202    // This should never happen.
203  0 return encodedSegment;
204    }
205    }
206    }