Source code: com/xpn/xwiki/web/Utils.java
1 /**
2 * ===================================================================
3 *
4 * Copyright (c) 2003,2004 Ludovic Dubost, All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details, published at
15 * http://www.gnu.org/copyleft/lesser.html or in lesser.txt in the
16 * root folder of this distribution.
17
18 * Created by
19 * User: Ludovic Dubost
20 * Date: 19 mai 2004
21 * Time: 13:36:16
22 */
23 package com.xpn.xwiki.web;
24
25 import com.xpn.xwiki.XWiki;
26 import com.xpn.xwiki.XWikiContext;
27 import com.xpn.xwiki.XWikiException;
28 import com.xpn.xwiki.xmlrpc.XWikiXMLRPCRequest;
29 import com.xpn.xwiki.xmlrpc.XWikiXMLRPCURLFactory;
30 import com.novell.ldap.util.Base64;
31 import org.apache.commons.fileupload.DefaultFileItem;
32 import org.apache.log4j.MDC;
33 import org.apache.ecs.Filter;
34 import org.apache.ecs.filter.CharacterFilter;
35
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.UnsupportedEncodingException;
39 import java.net.URL;
40 import java.net.URLEncoder;
41 import java.net.URLDecoder;
42 import java.util.Date;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Map;
46
47 public class Utils {
48
49 public static void parseTemplate(String template, XWikiContext context) throws XWikiException {
50 parseTemplate(template, true, context);
51 }
52
53 public static void parseTemplate(String template, boolean write, XWikiContext context) throws XWikiException {
54 XWikiResponse response = context.getResponse();
55
56 // Set content-type and encoding (this can be changed in the future by pages themselves)
57 if (context.getResponse() instanceof XWikiPortletResponse) {
58 response.setContentType("text/html");
59 }
60 else {
61 response.setContentType("text/html; charset=" + context.getWiki().getEncoding());
62 }
63
64 if ("view".equals(context.getAction())) {
65 if (context.getResponse() instanceof XWikiServletResponse) {
66 // Add a last modified to tell when the page was last updated
67 if (context.getWiki().getXWikiPreferenceAsLong("headers_lastmodified", 1, context)!=0) {
68 if (context.getDoc()!=null)
69 response.setDateHeader("Last-Modified", context.getDoc().getDate().getTime());
70 }
71 // Set a nocache to make sure the page is reloaded after an edit
72 if (context.getWiki().getXWikiPreferenceAsLong("headers_nocache", 1, context)!=0) {
73 response.setHeader("Pragma", "no-cache");
74 response.setHeader("Cache-Control","no-cache");
75 }
76 // Set an expires in one month
77 long expires = context.getWiki().getXWikiPreferenceAsLong("headers_expires", -1, context);
78 if (expires==-1) {
79 response.setDateHeader("Expires", -1);
80 } else if (expires!=0) {
81 response.setDateHeader("Expires", (new Date()).getTime() + 30*24*3600*1000L);
82 }
83
84 }
85 }
86
87 String content = context.getWiki().parseTemplate(template + ".vm", context);
88 content = content.trim();
89
90 if (!context.isFinished()) {
91 if (context.getResponse() instanceof XWikiServletResponse) {
92 response.setContentLength(content.length());
93 }
94
95 try {
96 if (write)
97 response.getWriter().write(content);
98 } catch (IOException e) {
99 throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
100 XWikiException.ERROR_XWIKI_APP_SEND_RESPONSE_EXCEPTION,
101 "Exception while sending response", e);
102 }
103 }
104
105 try {
106 response.getWriter().flush();
107 } catch (Throwable e) {
108 }
109 }
110
111 public static String getRedirect(XWikiRequest request, String defaultRedirect) {
112 String redirect;
113 redirect = request.getParameter("xredirect");
114 if ((redirect == null)||(redirect.equals("")))
115 redirect = defaultRedirect;
116 return redirect;
117 }
118
119 public static String getRedirect(String action, XWikiContext context) {
120 String redirect;
121 redirect = context.getRequest().getParameter("xredirect");
122 if ((redirect == null)||(redirect.equals("")))
123 redirect = context.getDoc().getURL(action, true, context);
124 return redirect;
125 }
126
127 public static String getPage(XWikiRequest request, String defaultpage) {
128 String page;
129 page = request.getParameter("xpage");
130 if ((page == null)||(page.equals("")))
131 page = defaultpage;
132 return page;
133 }
134
135
136 public static String getFileName(List filelist, String name) {
137 DefaultFileItem fileitem = null;
138 for (int i=0;i<filelist.size();i++) {
139 DefaultFileItem item = (DefaultFileItem) filelist.get(i);
140 if (name.equals(item.getFieldName())) {
141 fileitem = item;
142 break;
143 }
144 }
145
146 if (fileitem==null)
147 return null;
148
149 return fileitem.getName();
150 }
151
152 public static byte[] getContent(List filelist, String name) throws XWikiException {
153 DefaultFileItem fileitem = null;
154 for (int i=0;i<filelist.size();i++) {
155 DefaultFileItem item = (DefaultFileItem) filelist.get(i);
156 if (name.equals(item.getFieldName())) {
157 fileitem = item;
158 break;
159 }
160 }
161
162 if (fileitem==null)
163 return null;
164
165 byte[] data = new byte[(int)fileitem.getSize()];
166 InputStream fileis = null;
167 try {
168 fileis = fileitem.getInputStream();
169 fileis.read(data);
170 fileis.close();
171 } catch (IOException e) {
172 throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
173 XWikiException.ERROR_XWIKI_APP_UPLOAD_FILE_EXCEPTION,
174 "Exception while reading uploaded parsed file", e);
175 }
176 return data;
177 }
178
179 public static XWikiContext prepareContext(String action, XWikiRequest request, XWikiResponse response,
180 XWikiEngineContext engine_context) throws XWikiException {
181 // Test works with xwiki-test.cfg instead of xwiki.cfg
182 XWikiContext context = new XWikiContext();
183 String dbname = "xwiki";
184
185 URL url = XWiki.getRequestURL(request);
186 context.setURL(url);
187 // Push the URL into the Log4j NDC context
188 MDC.put("url", url);
189
190 context.setEngineContext(engine_context);
191 context.setRequest(request);
192 context.setResponse(response);
193 context.setAction(action);
194 context.setDatabase(dbname);
195
196
197 if (request instanceof XWikiXMLRPCRequest) {
198 context.setMode(XWikiContext.MODE_XMLRPC);
199 XWikiURLFactory urlf = new XWikiXMLRPCURLFactory(context);
200 context.setURLFactory(urlf);
201 }
202 else if (request instanceof XWikiServletRequest) {
203 context.setMode(XWikiContext.MODE_SERVLET);
204 XWikiURLFactory urlf = new XWikiServletURLFactory(context);
205 context.setURLFactory(urlf);
206 }
207 else if (request instanceof XWikiPortletRequest) {
208 context.setMode(XWikiContext.MODE_PORTLET);
209 XWikiURLFactory urlf = new XWikiPortletURLFactory(context);
210 context.setURLFactory(urlf);
211 }
212
213 return context;
214 }
215
216 /**
217 * Append request parameters from the specified String to the specified
218 * Map. It is presumed that the specified Map is not accessed from any
219 * other thread, so no synchronization is performed.
220 * <p>
221 * <strong>IMPLEMENTATION NOTE</strong>: URL decoding is performed
222 * individually on the parsed name and value elements, rather than on
223 * the entire query string ahead of time, to properly deal with the case
224 * where the name or value includes an encoded "=" or "&" character
225 * that would otherwise be interpreted as a delimiter.
226 *
227 * @param data Input string containing request parameters
228 *
229 * @exception IllegalArgumentException if the data is malformed
230 *
231 * Code borrowed from Apache Tomcat 5.0
232 */
233 public static Map parseParameters(String data, String encoding)
234 throws UnsupportedEncodingException {
235
236 if ((data != null) && (data.length() > 0)) {
237
238 // use the specified encoding to extract bytes out of the
239 // given string so that the encoding is not lost. If an
240 // encoding is not specified, let it use platform default
241 byte[] bytes = null;
242 try {
243 if (encoding == null) {
244 bytes = data.getBytes();
245 } else {
246 bytes = data.getBytes(encoding);
247 }
248 } catch (UnsupportedEncodingException uee) {
249 }
250
251 return parseParameters(bytes, encoding);
252 }
253
254 return new HashMap();
255 }
256
257 /**
258 * Append request parameters from the specified String to the specified
259 * Map. It is presumed that the specified Map is not accessed from any
260 * other thread, so no synchronization is performed.
261 * <p>
262 * <strong>IMPLEMENTATION NOTE</strong>: URL decoding is performed
263 * individually on the parsed name and value elements, rather than on
264 * the entire query string ahead of time, to properly deal with the case
265 * where the name or value includes an encoded "=" or "&" character
266 * that would otherwise be interpreted as a delimiter.
267 *
268 * NOTE: byte array data is modified by this method. Caller beware.
269 *
270 * @param data Input string containing request parameters
271 * @param encoding Encoding to use for converting hex
272 *
273 * @exception UnsupportedEncodingException if the data is malformed
274 *
275 * Code borrowed from Apache Tomcat 5.0
276 */
277 public static Map parseParameters(byte[] data, String encoding)
278 throws UnsupportedEncodingException {
279
280 Map map = new HashMap();
281
282 if (data != null && data.length > 0) {
283 int pos = 0;
284 int ix = 0;
285 int ox = 0;
286 String key = null;
287 String value = null;
288 while (ix < data.length) {
289 byte c = data[ix++];
290 switch ((char) c) {
291 case '&':
292 value = new String(data, 0, ox, encoding);
293 if (key != null) {
294 putMapEntry(map, key, value);
295 key = null;
296 }
297 ox = 0;
298 break;
299 case '=':
300 if (key == null) {
301 key = new String(data, 0, ox, encoding);
302 ox = 0;
303 } else {
304 data[ox++] = c;
305 }
306 break;
307 case '+':
308 data[ox++] = (byte)' ';
309 break;
310 case '%':
311 data[ox++] = (byte)((convertHexDigit(data[ix++]) << 4)
312 + convertHexDigit(data[ix++]));
313 break;
314 default:
315 data[ox++] = c;
316 }
317 }
318 //The last value does not end in '&'. So save it now.
319 if (key != null) {
320 value = new String(data, 0, ox, encoding);
321 putMapEntry(map, key, value);
322 }
323 }
324 return map;
325 }
326
327 /**
328 * Convert a byte character value to hexidecimal digit value.
329 *
330 * @param b the character value byte
331 *
332 * Code borrowed from Apache Tomcat 5.0
333 */
334 private static byte convertHexDigit( byte b ) {
335 if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
336 if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
337 if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
338 return 0;
339 }
340
341 /**
342 * Put name value pair in map.
343 *
344 * Put name and value pair in map. When name already exist, add value
345 * to array of values.
346 *
347 * Code borrowed from Apache Tomcat 5.0
348 */
349 private static void putMapEntry( Map map, String name, String value) {
350 String[] newValues = null;
351 String[] oldValues = (String[]) map.get(name);
352 if (oldValues == null) {
353 newValues = new String[1];
354 newValues[0] = value;
355 } else {
356 newValues = new String[oldValues.length + 1];
357 System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
358 newValues[oldValues.length] = value;
359 }
360 map.put(name, newValues);
361 }
362
363 public static String formEncode(String value) {
364 Filter filter = new CharacterFilter();
365 filter.removeAttribute("'");
366 String svalue = filter.process(value);
367 return svalue;
368 }
369
370 public static String SQLFilter(String text) {
371 try {
372 return text.replaceAll("'","''");
373 } catch (Exception e) {
374 return text;
375 }
376 }
377
378 public static String encode(String name, XWikiContext context) {
379 try {
380 //byte[] bytes = name.getBytes("UTF-8");
381 ///String result = new String(bytes);
382 return URLEncoder.encode(name, context.getWiki().getEncoding());
383 } catch (Exception e) {
384 return name;
385 }
386 }
387
388 public static String decode(String name, XWikiContext context) {
389 try {
390 // Make sure + is considered as a space
391 String result = name.replace('+',' ');
392
393 // It seems Internet Explorer can send us back UTF-8
394 // instead of ISO-8859-1 for URLs
395 if (Base64.isValidUTF8(result.getBytes(), false))
396 result = new String(result.getBytes(), "UTF-8");
397
398 // Still need to decode URLs
399 return URLDecoder.decode(result, context.getWiki().getEncoding());
400 } catch (Exception e) {
401 return name;
402 }
403 }
404
405 }