001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.util; 029 030import org.opencms.flex.CmsFlexRequest; 031import org.opencms.flex.CmsFlexResponse; 032import org.opencms.i18n.CmsEncoder; 033import org.opencms.json.JSONArray; 034import org.opencms.json.JSONException; 035import org.opencms.json.JSONObject; 036import org.opencms.jsp.CmsJspActionElement; 037import org.opencms.main.CmsLog; 038import org.opencms.main.OpenCms; 039import org.opencms.site.CmsSiteMatcher; 040 041import java.io.File; 042import java.io.IOException; 043import java.io.UnsupportedEncodingException; 044import java.net.URI; 045import java.net.URLDecoder; 046import java.util.ArrayList; 047import java.util.Enumeration; 048import java.util.HashMap; 049import java.util.Iterator; 050import java.util.List; 051import java.util.Map; 052import java.util.Map.Entry; 053 054import javax.servlet.ServletException; 055import javax.servlet.ServletRequest; 056import javax.servlet.http.Cookie; 057import javax.servlet.http.HttpServletRequest; 058import javax.servlet.http.HttpServletResponse; 059import javax.servlet.http.HttpSession; 060 061import org.apache.commons.fileupload.FileItem; 062import org.apache.commons.fileupload.FileUploadException; 063import org.apache.commons.fileupload.disk.DiskFileItemFactory; 064import org.apache.commons.fileupload.servlet.ServletFileUpload; 065import org.apache.commons.logging.Log; 066 067import com.google.common.collect.ArrayListMultimap; 068import com.google.common.collect.Multimap; 069 070/** 071 * Provides utility functions for dealing with values a <code>{@link HttpServletRequest}</code>.<p> 072 * 073 * @since 6.0.0 074 */ 075public final class CmsRequestUtil { 076 077 /** Request attribute that contains the original error code. */ 078 public static final String ATTRIBUTE_ERRORCODE = "org.opencms.util.CmsErrorCode"; 079 080 /** HTTP Accept Header for the cms:device-tag. */ 081 public static final String HEADER_ACCEPT = "Accept"; 082 083 /** HTTP Accept-Charset Header for internal requests used during static export. */ 084 public static final String HEADER_ACCEPT_CHARSET = "Accept-Charset"; 085 086 /** HTTP Accept-Language Header for internal requests used during static export. */ 087 public static final String HEADER_ACCEPT_LANGUAGE = "Accept-Language"; 088 089 /** HTTP Header "Cache-Control". */ 090 public static final String HEADER_CACHE_CONTROL = "Cache-Control"; 091 092 /** HTTP Header "Connection". */ 093 public static final String HEADER_CONNECTION = "Connection"; 094 095 /** The "Content-Disposition" http header. */ 096 public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition"; 097 098 /** The "Content-Type" http header. */ 099 public static final String HEADER_CONTENT_TYPE = "Content-Type"; 100 101 /** HTTP Header "Expires". */ 102 public static final String HEADER_EXPIRES = "Expires"; 103 104 /** HTTP Header "If-Modified-Since". */ 105 public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since"; 106 107 /** The Header that stores the session id (used by OpenCms upload applet). */ 108 public static final String HEADER_JSESSIONID = "JSESSIONID"; 109 110 /** HTTP Header "Last-Modified". */ 111 public static final String HEADER_LAST_MODIFIED = "Last-Modified"; 112 113 /** HTTP Header "Location". */ 114 public static final String HEADER_LOCATION = "Location"; 115 116 /** HTTP Header for internal requests used during static export. */ 117 public static final String HEADER_OPENCMS_EXPORT = "OpenCms-Export"; 118 119 /** HTTP Header "Pragma". */ 120 public static final String HEADER_PRAGMA = "Pragma"; 121 122 /** HTTP Header "Server". */ 123 public static final String HEADER_SERVER = "Server"; 124 125 /** HTTP Header "user-agent". */ 126 public static final String HEADER_USER_AGENT = "user-agent"; 127 128 /** HTTP Header value "max-age=" (for "Cache-Control"). */ 129 public static final String HEADER_VALUE_MAX_AGE = "max-age="; 130 131 /** HTTP Header value "must-revalidate" (for "Cache-Control"). */ 132 public static final String HEADER_VALUE_MUST_REVALIDATE = "must-revalidate"; 133 134 /** HTTP Header value "no-cache" (for "Cache-Control"). */ 135 public static final String HEADER_VALUE_NO_CACHE = "no-cache"; 136 137 /** HTTP Header value "no-store" (for "Cache-Control"). */ 138 public static final String HEADER_VALUE_NO_STORE = "no-store"; 139 140 /** HTTP Header "WWW-Authenticate". */ 141 public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; 142 143 /** Identifier for x-forwarded-for (i.e. proxied) request headers. */ 144 public static final String HEADER_X_FORWARDED_FOR = "x-forwarded-for"; 145 146 /** Assignment char between parameter name and values. */ 147 public static final String PARAMETER_ASSIGNMENT = "="; 148 149 /** Delimiter char between parameters. */ 150 public static final String PARAMETER_DELIMITER = "&"; 151 152 /** Delimiter char between url and query. */ 153 public static final String URL_DELIMITER = "?"; 154 155 /** The prefix for &. */ 156 private static final String AMP = "amp;"; 157 158 /** The log object for this class. */ 159 private static final Log LOG = CmsLog.getLog(CmsRequestUtil.class); 160 161 /** Flag to enable / disable backlink checks. */ 162 public static boolean backlinkCheckEnabled = true; 163 164 /** 165 * Default constructor (empty), private because this class has only 166 * static methods.<p> 167 */ 168 private CmsRequestUtil() { 169 170 // empty 171 } 172 173 /** 174 * Appends a request parameter to the given URL.<p> 175 * 176 * This method takes care about the adding the parameter as an additional 177 * parameter (appending <code>¶m=value</code>) or as the first parameter 178 * (appending <code>?param=value</code>).<p> 179 * 180 * @param url the URL where to append the parameter to 181 * @param paramName the paramter name to append 182 * @param paramValue the parameter value to append 183 * 184 * @return the URL with the given parameter appended 185 */ 186 public static String appendParameter(String url, String paramName, String paramValue) { 187 188 if (CmsStringUtil.isEmpty(url)) { 189 return null; 190 } 191 int pos = url.indexOf(URL_DELIMITER); 192 StringBuffer result = new StringBuffer(256); 193 result.append(url); 194 if (pos >= 0) { 195 // url already has parameters 196 result.append(PARAMETER_DELIMITER); 197 } else { 198 // url does not have parameters 199 result.append(URL_DELIMITER); 200 } 201 result.append(paramName); 202 result.append(PARAMETER_ASSIGNMENT); 203 result.append(paramValue); 204 return result.toString(); 205 } 206 207 /** 208 * Appends a map of request parameters to the given URL.<p> 209 * 210 * The map can contains values of <code>String[]</code> or 211 * simple <code>String</code> values.<p> 212 * 213 * This method takes care about the adding the parameter as an additional 214 * parameter (appending <code>¶m=value</code>) or as the first parameter 215 * (appending <code>?param=value</code>).<p> 216 * 217 * @param url the URL where to append the parameter to 218 * @param params the parameters to append 219 * @param encode if <code>true</code>, the parameter values are encoded before they are appended 220 * 221 * @return the URL with the given parameter appended 222 */ 223 public static String appendParameters(String url, Map<String, String[]> params, boolean encode) { 224 225 if (CmsStringUtil.isEmpty(url)) { 226 return null; 227 } 228 if ((params == null) || params.isEmpty()) { 229 return url; 230 } 231 int pos = url.indexOf(URL_DELIMITER); 232 StringBuffer result = new StringBuffer(256); 233 result.append(url); 234 if (pos >= 0) { 235 // url already has parameters 236 result.append(PARAMETER_DELIMITER); 237 } else { 238 // url does not have parameters 239 result.append(URL_DELIMITER); 240 } 241 // ensure all values are of type String[] 242 Iterator<Map.Entry<String, String[]>> i = params.entrySet().iterator(); 243 while (i.hasNext()) { 244 Map.Entry<String, String[]> entry = i.next(); 245 String key = entry.getKey(); 246 Object value = entry.getValue(); 247 // generics where added later, so make sure that the value really is a String[] 248 String[] values = value instanceof String[] ? (String[])value : new String[] {value.toString()}; 249 for (int j = 0; j < values.length; j++) { 250 String strValue = values[j]; 251 if (encode) { 252 strValue = CmsEncoder.encode(strValue); 253 } 254 result.append(key); 255 result.append(PARAMETER_ASSIGNMENT); 256 result.append(strValue); 257 if ((j + 1) < values.length) { 258 result.append(PARAMETER_DELIMITER); 259 } 260 } 261 if (i.hasNext()) { 262 result.append(PARAMETER_DELIMITER); 263 } 264 } 265 return result.toString(); 266 } 267 268 /** 269 * Checks that the given link is a valid backlink for editors. 270 * 271 * <p>This means that the link is either just a path, or starts with a scheme/domain/port that is either registered in the 272 * site configuration, or is the same as that of the current request. 273 * 274 * @param backlink the link to check 275 * @param optionalRequest the current request - may be null if no check against the current request is desired 276 * @return true if the link is a valid backlink 277 */ 278 public static boolean checkBacklink(String backlink, HttpServletRequest optionalRequest) { 279 280 if (!backlinkCheckEnabled) { 281 return true; 282 } 283 284 if (CmsStringUtil.isEmptyOrWhitespaceOnly(backlink)) { 285 return true; 286 } 287 288 if (backlink.startsWith("javascript")) { 289 return false; 290 } 291 292 if (backlink.startsWith("/")) { 293 return true; 294 } 295 CmsSiteMatcher matcher = new CmsSiteMatcher(backlink, 0); 296 if (OpenCms.getSiteManager().isMatching(matcher)) { 297 return true; 298 } 299 300 if (optionalRequest != null) { 301 if (optionalRequest.getServerName().equals(matcher.getServerName())) { 302 return true; 303 } 304 } 305 return false; 306 307 } 308 309 /** 310 * Creates a valid request parameter map from the given map, 311 * most notably changing the values form <code>String</code> 312 * to <code>String[]</code> if required.<p> 313 * 314 * If the given parameter map is <code>null</code>, then <code>null</code> is returned.<p> 315 * 316 * @param params the map of parameters to create a parameter map from 317 * @return the created parameter map, all values will be instances of <code>String[]</code> 318 */ 319 public static Map<String, String[]> createParameterMap(Map<String, ?> params) { 320 321 if (params == null) { 322 return null; 323 } 324 Map<String, String[]> result = new HashMap<String, String[]>(); 325 Iterator<?> i = params.entrySet().iterator(); 326 while (i.hasNext()) { 327 @SuppressWarnings("unchecked") 328 Map.Entry<String, ?> entry = (Entry<String, ?>)i.next(); 329 String key = entry.getKey(); 330 Object values = entry.getValue(); 331 if (values instanceof String[]) { 332 result.put(key, (String[])values); 333 } else { 334 if (values != null) { 335 result.put(key, new String[] {values.toString()}); 336 } 337 } 338 } 339 return result; 340 } 341 342 /** 343 * Parses the parameters of the given request query part and creates a parameter map out of them.<p> 344 * 345 * Please note: This does not parse a full request URI/URL, only the query part that 346 * starts after the "?". For example, in the URI <code>/system/index.html?a=b&c=d</code>, 347 * the query part is <code>a=b&c=d</code>.<p> 348 * 349 * If the given String is empty, an empty map is returned.<p> 350 * 351 * @param query the query to parse 352 * @return the parameter map created from the query 353 */ 354 public static Map<String, String[]> createParameterMap(String query) { 355 356 return createParameterMap(query, false, null); 357 } 358 359 /** 360 * Parses the parameters of the given request query part, optionally decodes them, and creates a parameter map out of them.<p> 361 * 362 * Please note: This does not parse a full request URI/URL, only the query part that 363 * starts after the "?". For example, in the URI <code>/system/index.html?a=b&c=d</code>, 364 * the query part is <code>a=b&c=d</code>.<p> 365 * 366 * If the given String is empty, an empty map is returned.<p> 367 * 368 * @param query the query to parse 369 * @param decodeParameters a flag, indicating if the parameters should be decoded. 370 * @param encoding the character encoding used while decoding. If <code>null</code>, the default character encoding is used. 371 * @return the parameter map created from the query 372 */ 373 public static Map<String, String[]> createParameterMap(String query, boolean decodeParameters, String encoding) { 374 375 if (CmsStringUtil.isEmpty(query)) { 376 // empty query 377 return new HashMap<String, String[]>(); 378 } 379 if (query.charAt(0) == URL_DELIMITER.charAt(0)) { 380 // remove leading '?' if required 381 query = query.substring(1); 382 } 383 // cut along the different parameters 384 String[] params = CmsStringUtil.splitAsArray(query, PARAMETER_DELIMITER); 385 Map<String, String[]> parameters = new HashMap<String, String[]>(params.length); 386 for (int i = 0; i < params.length; i++) { 387 String key = null; 388 String value = null; 389 // get key and value, separated by a '=' 390 int pos = params[i].indexOf(PARAMETER_ASSIGNMENT); 391 if (pos > 0) { 392 key = params[i].substring(0, pos); 393 value = params[i].substring(pos + 1); 394 } else if (pos < 0) { 395 key = params[i]; 396 value = ""; 397 } 398 // adjust the key if it starts with "amp;" 399 // this happens when "&" is used instead of a simple "&" 400 if ((key != null) && (key.startsWith(AMP))) { 401 key = key.substring(AMP.length()); 402 } 403 // now make sure the values are of type String[] 404 if (key != null) { 405 if (decodeParameters) { 406 key = CmsEncoder.decode(key, encoding); 407 value = CmsEncoder.decode(value, encoding); 408 } 409 String[] values = parameters.get(key); 410 if (values == null) { 411 // this is the first value, create new array 412 values = new String[] {value}; 413 } else { 414 // append to the existing value array 415 String[] copy = new String[values.length + 1]; 416 System.arraycopy(values, 0, copy, 0, values.length); 417 copy[copy.length - 1] = value; 418 values = copy; 419 } 420 parameters.put(key, values); 421 } 422 } 423 return parameters; 424 } 425 426 /** 427 * Sets HTTP response headers to disable embedding in iframes on different domains. 428 * 429 * @param response the response on which to set the HTTP headers 430 */ 431 public static void disableCrossSiteFrameEmbedding(HttpServletResponse response) { 432 433 response.addHeader("Content-Security-Policy", "frame-ancestors 'self';"); 434 response.addHeader("X-Frame-Options", "SAMEORIGIN"); 435 } 436 437 /** 438 * Returns all parameters of the given request 439 * as a request parameter URL String, that is in the form <code>key1=value1&key2=value2</code> etc. 440 * 441 * The result will be encoded using the <code>{@link CmsEncoder#encode(String)}</code> function.<p> 442 * 443 * @param req the request to read the parameters from 444 * 445 * @return all initialized parameters of the given request as request parameter URL String 446 */ 447 public static String encodeParams(HttpServletRequest req) { 448 449 StringBuffer result = new StringBuffer(512); 450 Map<String, String[]> params = CmsCollectionsGenericWrapper.map(req.getParameterMap()); 451 Iterator<Map.Entry<String, String[]>> i = params.entrySet().iterator(); 452 while (i.hasNext()) { 453 Map.Entry<String, String[]> entry = i.next(); 454 String param = entry.getKey(); 455 String[] values = entry.getValue(); 456 for (int j = 0; j < values.length; j++) { 457 result.append(param); 458 result.append("="); 459 result.append(CmsEncoder.encode(values[j])); 460 if ((j + 1) < values.length) { 461 result.append("&"); 462 } 463 } 464 if (i.hasNext()) { 465 result.append("&"); 466 } 467 } 468 return CmsEncoder.encode(result.toString()); 469 } 470 471 /** 472 * Encodes the given URI, with all parameters from the given request appended.<p> 473 * 474 * The result will be encoded using the <code>{@link CmsEncoder#encode(String)}</code> function.<p> 475 * 476 * @param req the request where to read the parameters from 477 * @param uri the URI to encode 478 * @return the encoded URI, with all parameters from the given request appended 479 */ 480 public static String encodeParamsWithUri(String uri, HttpServletRequest req) { 481 482 String result; 483 String params = encodeParams(req); 484 if (CmsStringUtil.isNotEmpty(params)) { 485 result = CmsEncoder.encode(uri + "?") + params; 486 } else { 487 result = CmsEncoder.encode(uri); 488 } 489 return result; 490 } 491 492 /** 493 * Forwards the response to the given target, which may contain parameters appended like for example <code>?a=b&c=d</code>.<p> 494 * 495 * Please note: If possible, use <code>{@link #forwardRequest(String, Map, HttpServletRequest, HttpServletResponse)}</code> 496 * where the parameters are passed as a map, since the parsing of the parameters may introduce issues with encoding 497 * and is in general much less effective.<p> 498 * 499 * The parsing of parameters will likely fail for "large values" (e.g. full blown web forms with <textarea> 500 * elements etc. Use this method only if you know that the target will just contain up to 3 parameters which 501 * are relatively short and have no encoding or line break issues.<p> 502 * 503 * @param target the target to forward to (may contain parameters like <code>?a=b&c=d</code>) 504 * @param req the request to forward 505 * @param res the response to forward 506 * 507 * @throws IOException in case the forwarding fails 508 * @throws ServletException in case the forwarding fails 509 */ 510 public static void forwardRequest(String target, HttpServletRequest req, HttpServletResponse res) 511 throws IOException, ServletException { 512 513 // clear the current parameters 514 CmsUriSplitter uri = new CmsUriSplitter(target); 515 Map<String, String[]> params = createParameterMap(uri.getQuery()); 516 forwardRequest(uri.getPrefix(), params, req, res); 517 } 518 519 /** 520 * Forwards the response to the given target, with the provided parameter map.<p> 521 * 522 * The target URI must NOT have parameters appended like for example <code>?a=b&c=d</code>. 523 * The values in the provided map must be of type <code>String[]</code>. If required, use 524 * <code>{@link #createParameterMap(Map)}</code> before calling this method to make sure 525 * all values are actually of the required array type.<p> 526 * 527 * @param target the target to forward to (may NOT contain parameters like <code>?a=b&c=d</code>) 528 * @param params the parameter map (the values must be of type <code>String[]</code> 529 * @param req the request to forward 530 * @param res the response to forward 531 * 532 * @throws IOException in case the forwarding fails 533 * @throws ServletException in case the forwarding fails 534 */ 535 public static void forwardRequest( 536 String target, 537 Map<String, String[]> params, 538 HttpServletRequest req, 539 HttpServletResponse res) 540 throws IOException, ServletException { 541 542 // cast the request back to a flex request so the parameter map can be accessed 543 CmsFlexRequest f_req = (CmsFlexRequest)req; 544 // set the parameters 545 f_req.setParameterMap(params); 546 // check for links "into" OpenCms, these may need the webapp name to be removed 547 String vfsPrefix = OpenCms.getStaticExportManager().getVfsPrefix(); 548 if (target.startsWith(vfsPrefix)) { 549 // remove VFS prefix (will also work for empty vfs prefix in ROOT webapp case with proxy rules) 550 target = target.substring(vfsPrefix.length()); 551 // append the servlet name 552 target = OpenCms.getSystemInfo().getServletPath() + target; 553 } 554 // forward the request 555 f_req.getRequestDispatcher(target).forward(f_req, res); 556 } 557 558 /** 559 * Exactly like getAttributeMap, but incorrectly spelled.<p> 560 * 561 * Kept for backward compatibility. 562 * 563 * @param req the request 564 * 565 * @return the attribute map 566 */ 567 public static Map<String, Object> getAtrributeMap(ServletRequest req) { 568 569 return getAttributeMap(req); 570 } 571 572 /** 573 * Returns a map with all request attributes.<p> 574 * 575 * @param req the request 576 * 577 * @return the attribute map 578 */ 579 public static Map<String, Object> getAttributeMap(ServletRequest req) { 580 581 if (req instanceof CmsFlexRequest) { 582 return ((CmsFlexRequest)req).getAttributeMap(); 583 } 584 Map<String, Object> attrs = new HashMap<String, Object>(); 585 Enumeration<String> atrrEnum = CmsCollectionsGenericWrapper.enumeration(req.getAttributeNames()); 586 while (atrrEnum.hasMoreElements()) { 587 String key = atrrEnum.nextElement(); 588 Object value = req.getAttribute(key); 589 attrs.put(key, value); 590 } 591 return attrs; 592 } 593 594 /** 595 * Returns the value of the cookie with the given name.<p/> 596 * 597 * @param jsp the CmsJspActionElement to use 598 * @param name the name of the cookie 599 * 600 * @return the value of the cookie with the given name or null, if no cookie exists with the name 601 */ 602 public static String getCookieValue(CmsJspActionElement jsp, String name) { 603 604 Cookie[] cookies = jsp.getRequest().getCookies(); 605 return getCookieValue(cookies, name); 606 } 607 608 /** 609 * Gets the value of a specific cookie from an array of cookies.<p> 610 * 611 * @param cookies the cookie array 612 * @param name the name of the cookie we want 613 * 614 * @return the cookie value, or null if cookie with the given name wasn't found 615 */ 616 public static String getCookieValue(Cookie[] cookies, String name) { 617 618 for (int i = 0; (cookies != null) && (i < cookies.length); i++) { 619 if (name.equalsIgnoreCase(cookies[i].getName())) { 620 return cookies[i].getValue(); 621 } 622 } 623 return null; 624 } 625 626 /** 627 * Converts the given parameter map into an JSON object.<p> 628 * 629 * @param params the parameters map to convert 630 * 631 * @return the JSON representation of the given parameter map 632 */ 633 public static JSONObject getJsonParameterMap(Map<String, String[]> params) { 634 635 JSONObject result = new JSONObject(); 636 for (Map.Entry<String, String[]> entry : params.entrySet()) { 637 String paramKey = entry.getKey(); 638 JSONArray paramValue = new JSONArray(); 639 for (int i = 0, l = entry.getValue().length; i < l; i++) { 640 paramValue.put(entry.getValue()[i]); 641 } 642 try { 643 result.putOpt(paramKey, paramValue); 644 } catch (JSONException e) { 645 // should never happen 646 LOG.warn(e.getLocalizedMessage(), e); 647 } 648 } 649 return result; 650 } 651 652 /** 653 * Reads value from the request parameters, 654 * will return <code>null</code> if the value is not available or only white space.<p> 655 * 656 * The value of the request will also be decoded using <code>{@link CmsEncoder#decode(String)}</code> 657 * and also trimmed using <code>{@link String#trim()}</code>.<p> 658 * 659 * @param request the request to read the parameter from 660 * @param paramName the parameter name to read 661 * 662 * @return the request parameter value for the given parameter 663 */ 664 public static String getNotEmptyDecodedParameter(HttpServletRequest request, String paramName) { 665 666 String result = getNotEmptyParameter(request, paramName); 667 if (result != null) { 668 result = CmsEncoder.decode(result.trim()); 669 } 670 return result; 671 } 672 673 /** 674 * Reads value from the request parameters, 675 * will return <code>null</code> if the value is not available or only white space.<p> 676 * 677 * @param request the request to read the parameter from 678 * @param paramName the parameter name to read 679 * 680 * @return the request parameter value for the given parameter 681 */ 682 public static String getNotEmptyParameter(HttpServletRequest request, String paramName) { 683 684 String result = request.getParameter(paramName); 685 if (CmsStringUtil.isEmptyOrWhitespaceOnly(result)) { 686 result = null; 687 } 688 return result; 689 } 690 691 /** 692 * Converts the given JSON object into a valid parameter map.<p> 693 * 694 * @param params the JSON object to convert 695 * 696 * @return the parameter map from the given JSON object 697 */ 698 public static Map<String, String[]> getParameterMapFromJSON(JSONObject params) { 699 700 Map<String, String[]> result = new HashMap<String, String[]>(); 701 Iterator<String> itKeys = params.keys(); 702 while (itKeys.hasNext()) { 703 String key = itKeys.next(); 704 JSONArray paramValue = params.optJSONArray(key); 705 result.put(key, new String[paramValue.length()]); 706 for (int i = 0, l = paramValue.length(); i < l; i++) { 707 result.get(key)[i] = paramValue.optString(i); 708 } 709 } 710 return result; 711 } 712 713 /** 714 * Parses parameter map from the given URI.<p> 715 * 716 * @param uri the URI 717 * @return the parameter map 718 */ 719 public static Multimap<String, String> getParameters(URI uri) { 720 721 return getParametersFromRawQuery(uri.getRawQuery()); 722 } 723 724 /** 725 * Parses the parameter map from a raw query string.<p> 726 * 727 * @param rawQuery the raw query string 728 * 729 * @return the parameter map 730 */ 731 public static Multimap<String, String> getParametersFromRawQuery(String rawQuery) { 732 733 Multimap<String, String> result = ArrayListMultimap.create(); 734 if (rawQuery != null) { 735 for (String keyValuePair : CmsStringUtil.splitAsList(rawQuery, "&")) { 736 try { 737 String decodedKeyValue = URLDecoder.decode(keyValuePair, "UTF-8"); 738 int eqPos = decodedKeyValue.indexOf("="); 739 if (eqPos < 0) { 740 decodedKeyValue = decodedKeyValue + "="; 741 eqPos = decodedKeyValue.indexOf("="); 742 } 743 String key = decodedKeyValue.substring(0, eqPos); 744 String value = decodedKeyValue.substring(eqPos + 1); 745 result.put(key, value); 746 } catch (UnsupportedEncodingException e) { 747 // UTF8 should be present 748 } 749 } 750 } 751 return result; 752 } 753 754 /** 755 * Returns the link without parameters from a String that is formatted for a GET request.<p> 756 * 757 * @param url the URL to remove the parameters from 758 * @return the URL without any parameters 759 */ 760 public static String getRequestLink(String url) { 761 762 if (CmsStringUtil.isEmpty(url)) { 763 return null; 764 } 765 int pos = url.indexOf(URL_DELIMITER); 766 if (pos >= 0) { 767 return url.substring(0, pos); 768 } 769 return url; 770 771 } 772 773 /** 774 * Reads an object from the session of the given HTTP request.<p> 775 * 776 * A session will be initialized if the request does not currently have a session. 777 * As a result, the request will always have a session after this method has been called.<p> 778 * 779 * Will return <code>null</code> if no corresponding object is found in the session.<p> 780 * 781 * @param request the request to get the session from 782 * @param key the key of the object to read from the session 783 * @return the object received form the session, or <code>null</code> 784 */ 785 public static Object getSessionValue(HttpServletRequest request, String key) { 786 787 HttpSession session = request.getSession(true); 788 return session.getAttribute(key); 789 } 790 791 /** 792 * Parses a request of the form <code>multipart/form-data</code>. 793 * 794 * The result list will contain items of type <code>{@link FileItem}</code>. 795 * If the request is not of type <code>multipart/form-data</code>, then <code>null</code> is returned.<p> 796 * 797 * @param request the HTTP servlet request to parse 798 * 799 * @return the list of <code>{@link FileItem}</code> extracted from the multipart request, 800 * or <code>null</code> if the request was not of type <code>multipart/form-data</code> 801 */ 802 public static List<FileItem> readMultipartFileItems(HttpServletRequest request) { 803 804 return readMultipartFileItems(request, OpenCms.getSystemInfo().getPackagesRfsPath()); 805 } 806 807 /** 808 * Parses a request of the form <code>multipart/form-data</code>. 809 * 810 * The result list will contain items of type <code>{@link FileItem}</code>. 811 * If the request is not of type <code>multipart/form-data</code>, then <code>null</code> is returned.<p> 812 * 813 * @param request the HTTP servlet request to parse 814 * @param tempFolderPath the real file system path to the temp file folder 815 * 816 * @return the list of <code>{@link FileItem}</code> extracted from the multipart request, 817 * or <code>null</code> if the request was not of type <code>multipart/form-data</code> 818 */ 819 public static List<FileItem> readMultipartFileItems(HttpServletRequest request, String tempFolderPath) { 820 821 if (!ServletFileUpload.isMultipartContent(request)) { 822 return null; 823 } 824 DiskFileItemFactory factory = new DiskFileItemFactory(); 825 // maximum size that will be stored in memory 826 factory.setSizeThreshold(4096); 827 // the location for saving data that is larger than getSizeThreshold() 828 factory.setRepository(new File(tempFolderPath)); 829 ServletFileUpload fu = new ServletFileUpload(factory); 830 // set encoding to correctly handle special chars (e.g. in filenames) 831 fu.setHeaderEncoding(request.getCharacterEncoding()); 832 List<FileItem> result = new ArrayList<FileItem>(); 833 try { 834 List<FileItem> items = CmsCollectionsGenericWrapper.list(fu.parseRequest(request)); 835 if (items != null) { 836 result = items; 837 } 838 } catch (FileUploadException e) { 839 LOG.error(Messages.get().getBundle().key(Messages.LOG_PARSE_MULIPART_REQ_FAILED_0), e); 840 } 841 return result; 842 } 843 844 /** 845 * Creates a "standard" request parameter map from the values of a 846 * <code>multipart/form-data</code> request.<p> 847 * 848 * @param encoding the encoding to use when creating the values 849 * @param multiPartFileItems the list of parsed multi part file items 850 * 851 * @return a map containing all non-file request parameters 852 * 853 * @see #readMultipartFileItems(HttpServletRequest) 854 */ 855 public static Map<String, String[]> readParameterMapFromMultiPart( 856 String encoding, 857 List<FileItem> multiPartFileItems) { 858 859 Map<String, String[]> parameterMap = new HashMap<String, String[]>(); 860 Iterator<FileItem> i = multiPartFileItems.iterator(); 861 while (i.hasNext()) { 862 FileItem item = i.next(); 863 String name = item.getFieldName(); 864 String value = null; 865 if ((name != null) && (item.getName() == null)) { 866 // only put to map if current item is no file and not null 867 try { 868 value = item.getString(encoding); 869 } catch (UnsupportedEncodingException e) { 870 LOG.error(Messages.get().getBundle().key(Messages.LOG_ENC_MULTIPART_REQ_ERROR_0), e); 871 value = item.getString(); 872 } 873 if (parameterMap.containsKey(name)) { 874 875 // append value to parameter values array 876 String[] oldValues = parameterMap.get(name); 877 String[] newValues = new String[oldValues.length + 1]; 878 System.arraycopy(oldValues, 0, newValues, 0, oldValues.length); 879 newValues[oldValues.length] = value; 880 parameterMap.put(name, newValues); 881 882 } else { 883 parameterMap.put(name, new String[] {value}); 884 } 885 } 886 } 887 return parameterMap; 888 } 889 890 /** 891 * Redirects the response to the target link using a "301 - Moved Permanently" header.<p> 892 * 893 * This implementation will work only on JSP pages in OpenCms that use the default JSP loader implementation.<p> 894 * 895 * @param jsp the OpenCms JSP context 896 * @param target the target link 897 */ 898 public static void redirectPermanently(CmsJspActionElement jsp, String target) { 899 900 target = OpenCms.getLinkManager().substituteLink(jsp.getCmsObject(), target); 901 jsp.getResponse().setHeader(HEADER_CONNECTION, "close"); 902 try { 903 HttpServletResponse response = jsp.getResponse(); 904 if (response instanceof CmsFlexResponse) { 905 ((CmsFlexResponse)jsp.getResponse()).sendRedirect(target, true); 906 } else { 907 response.setHeader("Location", target); 908 response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); 909 } 910 } catch (IOException e) { 911 LOG.error(Messages.get().getBundle().key(Messages.ERR_IOERROR_0), e); 912 } 913 } 914 915 /** 916 * Redirects the response to the target link.<p> 917 * 918 * Use this method instead of {@link javax.servlet.http.HttpServletResponse#sendRedirect(java.lang.String)} 919 * to avoid relative links with secure sites (and issues with apache).<p> 920 * 921 * @param jsp the OpenCms JSP context 922 * @param target the target link 923 * 924 * @throws IOException if something goes wrong during redirection 925 */ 926 public static void redirectRequestSecure(CmsJspActionElement jsp, String target) throws IOException { 927 928 jsp.getResponse().sendRedirect(OpenCms.getLinkManager().substituteLink(jsp.getCmsObject(), target, null, true)); 929 } 930 931 /** 932 * Removes an object from the session of the given http request.<p> 933 * 934 * A session will be initialized if the request does not currently have a session. 935 * As a result, the request will always have a session after this method has been called.<p> 936 * 937 * @param request the request to get the session from 938 * @param key the key of the object to be removed from the session 939 */ 940 public static void removeSessionValue(HttpServletRequest request, String key) { 941 942 HttpSession session = request.getSession(true); 943 session.removeAttribute(key); 944 } 945 946 /** 947 * Sets the value of a specific cookie.<p> 948 * If no cookie exists with the value, a new cookie will be created. 949 * 950 * @param jsp the CmsJspActionElement to use 951 * @param name the name of the cookie 952 * @param value the value of the cookie 953 */ 954 public static void setCookieValue(CmsJspActionElement jsp, String name, String value) { 955 956 Cookie[] cookies = jsp.getRequest().getCookies(); 957 for (int i = 0; (cookies != null) && (i < cookies.length); i++) { 958 if (name.equalsIgnoreCase(cookies[i].getName())) { 959 cookies[i].setValue(value); 960 return; 961 } 962 } 963 Cookie cookie = new Cookie(name, value); 964 jsp.getResponse().addCookie(cookie); 965 } 966 967 /** 968 * Sets headers to the given response to prevent client side caching.<p> 969 * 970 * The following headers are set:<p> 971 * <code> 972 * Cache-Control: max-age=0<br> 973 * Cache-Control: must-revalidate<br> 974 * Pragma: no-cache 975 * </code> 976 * 977 * @param res the request where to set the no-cache headers 978 */ 979 public static void setNoCacheHeaders(HttpServletResponse res) { 980 981 res.setHeader(CmsRequestUtil.HEADER_CACHE_CONTROL, CmsRequestUtil.HEADER_VALUE_MAX_AGE + "0"); 982 res.addHeader(CmsRequestUtil.HEADER_CACHE_CONTROL, CmsRequestUtil.HEADER_VALUE_MUST_REVALIDATE); 983 res.addHeader(CmsRequestUtil.HEADER_CACHE_CONTROL, CmsRequestUtil.HEADER_VALUE_NO_CACHE); 984 res.addHeader(CmsRequestUtil.HEADER_CACHE_CONTROL, CmsRequestUtil.HEADER_VALUE_NO_STORE); 985 res.setHeader(CmsRequestUtil.HEADER_PRAGMA, CmsRequestUtil.HEADER_VALUE_NO_CACHE); 986 } 987 988 /** 989 * Adds an object to the session of the given HTTP request.<p> 990 * 991 * A session will be initialized if the request does not currently have a session. 992 * As a result, the request will always have a session after this method has been called.<p> 993 * 994 * @param request the request to get the session from 995 * @param key the key of the object to be stored in the session 996 * @param value the object to be stored in the session 997 */ 998 public static void setSessionValue(HttpServletRequest request, String key, Object value) { 999 1000 HttpSession session = request.getSession(true); 1001 session.setAttribute(key, value); 1002 } 1003}