001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.protocol.http; 018 019import java.nio.charset.Charset; 020import java.nio.charset.StandardCharsets; 021import java.nio.charset.UnsupportedCharsetException; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.List; 025 026import jakarta.servlet.http.HttpServletRequest; 027 028import org.apache.wicket.Application; 029import org.apache.wicket.request.cycle.RequestCycle; 030import org.apache.wicket.request.mapper.parameter.INamedParameters; 031import org.apache.wicket.request.mapper.parameter.PageParameters; 032import org.apache.wicket.util.encoding.UrlDecoder; 033import org.apache.wicket.util.string.Strings; 034 035/** 036 * Wicket Http specific utilities class. 037 */ 038public final class RequestUtils 039{ 040 /** 041 * Decode the provided queryString as a series of key/ value pairs and set them in the provided 042 * value map. 043 * 044 * @param queryString 045 * string to decode, uses '&' to separate parameters and '=' to separate key from 046 * value 047 * @param params 048 * parameters map to write the found key/ value pairs to 049 */ 050 public static void decodeParameters(String queryString, PageParameters params) 051 { 052 decodeParameters(queryString, params, getCurrentCharset()); 053 } 054 055 /** 056 * Decode the provided queryString as a series of key/ value pairs and set them in the provided 057 * value map. 058 * 059 * @param queryString 060 * string to decode, uses '&' to separate parameters and '=' to separate key from 061 * value 062 * @param params 063 * parameters map to write the found key/ value pairs to 064 * @param currentCharset 065 * charset resolved via current requestCycle 066 */ 067 static void decodeParameters(String queryString, PageParameters params, Charset currentCharset) 068 { 069 070 if (Strings.indexOf(queryString, '?') == 0) 071 { 072 queryString = queryString.substring(1); 073 } 074 075 for (String paramTuple : Strings.split(queryString, '&')) 076 { 077 final String[] bits = Strings.split(paramTuple, '='); 078 079 if (bits.length == 2) 080 { 081 params.add(UrlDecoder.QUERY_INSTANCE.decode(bits[0], currentCharset), 082 UrlDecoder.QUERY_INSTANCE.decode(bits[1], currentCharset), INamedParameters.Type.QUERY_STRING); 083 } 084 else 085 { 086 params.add(UrlDecoder.QUERY_INSTANCE.decode(bits[0], currentCharset), "", INamedParameters.Type.QUERY_STRING); 087 } 088 } 089 } 090 091 /** 092 * Remove occurrences of ".." from the path 093 * 094 * @param path 095 * @return path string with double dots removed 096 */ 097 public static String removeDoubleDots(String path) 098 { 099 String[] segments = Strings.split(path, '/'); 100 List<String> newcomponents = new ArrayList<>(Arrays.asList(segments)); 101 102 for (int i = 0; i < newcomponents.size(); i++) 103 { 104 if (i < newcomponents.size() - 1) 105 { 106 // Verify for a ".." component at next iteration 107 if ((newcomponents.get(i)).length() > 0 && newcomponents.get(i + 1).equals("..")) 108 { 109 newcomponents.remove(i); 110 newcomponents.remove(i); 111 i = i - 2; 112 if (i < -1) 113 { 114 i = -1; 115 } 116 } 117 } 118 } 119 String newpath = Strings.join("/", newcomponents); 120 if (path.endsWith("/")) 121 { 122 return newpath + "/"; 123 } 124 return newpath; 125 } 126 127 /** 128 * Hidden utility class constructor. 129 */ 130 private RequestUtils() 131 { 132 } 133 134 135 /** 136 * Calculates absolute path to url relative to another absolute url. 137 * 138 * @param requestPath 139 * absolute path. 140 * @param relativePagePath 141 * path, relative to requestPath 142 * @return absolute path for given url 143 */ 144 public static String toAbsolutePath(final String requestPath, String relativePagePath) 145 { 146 final StringBuilder result; 147 if (requestPath.endsWith("/")) 148 { 149 result = new StringBuilder(requestPath); 150 } 151 else 152 { 153 // Remove everything after last slash (but not slash itself) 154 result = new StringBuilder(requestPath.substring(0, requestPath.lastIndexOf('/') + 1)); 155 } 156 157 if (relativePagePath.startsWith("./")) 158 { 159 relativePagePath = relativePagePath.substring(2); 160 } 161 162 if (relativePagePath.startsWith("../")) 163 { 164 StringBuilder tempRelative = new StringBuilder(relativePagePath); 165 166 // Go up through hierarchy until we find most common directory for both pathes. 167 while (tempRelative.indexOf("../") == 0) 168 { 169 // Delete ../ from relative path 170 tempRelative.delete(0, 3); 171 172 // Delete last slash from result 173 result.setLength(result.length() - 1); 174 175 // Delete everyting up to last slash 176 result.delete(result.lastIndexOf("/") + 1, result.length()); 177 } 178 result.append(tempRelative); 179 } 180 else 181 { 182 // Pages are in the same directory 183 result.append(relativePagePath); 184 } 185 return result.toString(); 186 } 187 188 private static Charset getDefaultCharset() 189 { 190 String charsetName = null; 191 if (Application.exists()) 192 { 193 charsetName = Application.get().getRequestCycleSettings().getResponseRequestEncoding(); 194 } 195 196 if (Strings.isEmpty(charsetName)) 197 { 198 return StandardCharsets.UTF_8; 199 } 200 201 return Charset.forName(charsetName); 202 } 203 204 private static Charset getCurrentCharset() 205 { 206 return RequestCycle.get().getRequest().getCharset(); 207 } 208 209 /** 210 * @param request 211 * the http servlet request to extract the charset from 212 * @return the request's charset or a default it request is {@code null} or has an unsupported 213 * character encoding 214 * 215 * @see org.apache.wicket.settings.RequestCycleSettings#getResponseRequestEncoding() 216 */ 217 public static Charset getCharset(HttpServletRequest request) 218 { 219 Charset charset = null; 220 if (request != null) 221 { 222 String charsetName = request.getCharacterEncoding(); 223 if (charsetName != null) 224 { 225 try 226 { 227 charset = Charset.forName(charsetName); 228 } 229 catch (UnsupportedCharsetException useDefault) 230 { 231 } 232 } 233 } 234 if (charset == null) 235 { 236 charset = getDefaultCharset(); 237 } 238 return charset; 239 } 240}