001 package railo.runtime.net.http; 002 003 import java.io.UnsupportedEncodingException; 004 import java.lang.reflect.Method; 005 import java.net.InetAddress; 006 import java.net.URL; 007 import java.util.ArrayList; 008 import java.util.Enumeration; 009 import java.util.LinkedList; 010 import java.util.List; 011 012 import javax.servlet.ServletContext; 013 import javax.servlet.ServletInputStream; 014 import javax.servlet.http.Cookie; 015 import javax.servlet.http.HttpServletRequest; 016 import javax.servlet.http.HttpServletResponse; 017 018 import org.xml.sax.InputSource; 019 020 import railo.commons.io.IOUtil; 021 import railo.commons.lang.Pair; 022 import railo.commons.lang.StringUtil; 023 import railo.commons.lang.mimetype.MimeType; 024 import railo.commons.net.HTTPUtil; 025 import railo.commons.net.URLDecoder; 026 import railo.commons.net.URLEncoder; 027 import railo.runtime.PageContext; 028 import railo.runtime.config.Config; 029 import railo.runtime.converter.WDDXConverter; 030 import railo.runtime.exp.PageException; 031 import railo.runtime.functions.decision.IsLocalHost; 032 import railo.runtime.interpreter.CFMLExpressionInterpreter; 033 import railo.runtime.interpreter.JSONExpressionInterpreter; 034 import railo.runtime.op.Caster; 035 import railo.runtime.text.xml.XMLCaster; 036 import railo.runtime.text.xml.XMLUtil; 037 import railo.runtime.type.UDF; 038 039 public final class ReqRspUtil { 040 041 042 043 public static String get(Pair<String,Object>[] items, String name) { 044 for(int i=0;i<items.length;i++) { 045 if(items[i].getName().equalsIgnoreCase(name)) 046 return Caster.toString(items[i].getValue(),null); 047 } 048 return null; 049 } 050 051 public static Pair<String,Object>[] add(Pair<String,Object>[] items, String name, Object value) { 052 Pair<String,Object>[] tmp = new Pair[items.length+1]; 053 for(int i=0;i<items.length;i++) { 054 tmp[i]=items[i]; 055 } 056 tmp[items.length]=new Pair<String,Object>(name,value); 057 return tmp; 058 } 059 060 public static Pair<String,Object>[] set(Pair<String,Object>[] items, String name, Object value) { 061 for(int i=0;i<items.length;i++) { 062 if(items[i].getName().equalsIgnoreCase(name)) { 063 items[i]=new Pair<String,Object>(name,value); 064 return items; 065 } 066 } 067 return add(items, name, value); 068 } 069 070 /** 071 * return path to itself 072 * @param req 073 */ 074 public static String self(HttpServletRequest req) { 075 StringBuffer sb=new StringBuffer(req.getServletPath()); 076 String qs=req.getQueryString(); 077 if(!StringUtil.isEmpty(qs))sb.append('?').append(qs); 078 return sb.toString(); 079 } 080 081 public static void setContentLength(HttpServletResponse rsp, int length) { 082 rsp.setContentLength(length); 083 } 084 public static void setContentLength(HttpServletResponse rsp, long length) { 085 if(length <= Integer.MAX_VALUE){ 086 setContentLength(rsp,(int)length); 087 } 088 else{ 089 rsp.addHeader("Content-Length", Caster.toString(length)); 090 } 091 } 092 093 public static Cookie[] getCookies(Config config,HttpServletRequest req) { 094 Cookie[] cookies = req.getCookies(); 095 String charset = config.getWebCharset(); 096 097 if(cookies!=null) { 098 Cookie cookie; 099 String tmp; 100 for(int i=0;i<cookies.length;i++){ 101 cookie=cookies[i]; 102 // value (is decoded by the servlet engine with iso-8859-1) 103 if(!StringUtil.isAscii(cookie.getValue())) { 104 tmp=encode(cookie.getValue(), "iso-8859-1"); 105 cookie.setValue(decode(tmp, charset,false)); 106 } 107 108 } 109 } 110 else { 111 String str = req.getHeader("Cookie"); 112 if(str!=null) { 113 try{ 114 String[] arr = railo.runtime.type.util.ListUtil.listToStringArray(str, ';'),tmp; 115 java.util.List<Cookie> list=new ArrayList<Cookie>(); 116 for(int i=0;i<arr.length;i++){ 117 tmp=railo.runtime.type.util.ListUtil.listToStringArray(arr[i], '='); 118 if(tmp.length>0) { 119 list.add(new Cookie(dec(tmp[0],charset,false), tmp.length>1?dec(tmp[1],charset,false):"")); 120 } 121 } 122 cookies=list.toArray(new Cookie[list.size()]); 123 124 } 125 catch(Throwable t){} 126 } 127 } 128 return cookies; 129 } 130 131 132 public static void setCharacterEncoding(HttpServletResponse rsp,String charset) { 133 try { 134 Method setCharacterEncoding = rsp.getClass().getMethod("setCharacterEncoding", new Class[0]); 135 setCharacterEncoding.invoke(rsp, new Object[0]); 136 } 137 catch (Throwable t) {} 138 } 139 140 public static String getQueryString(HttpServletRequest req) { 141 //String qs = req.getAttribute("javax.servlet.include.query_string"); 142 return req.getQueryString(); 143 } 144 145 public static String getHeader(HttpServletRequest request, String name,String defaultValue) { 146 try { 147 return request.getHeader(name); 148 } 149 catch(Throwable t){ 150 return defaultValue; 151 } 152 } 153 154 public static String getHeaderIgnoreCase(PageContext pc, String name,String defaultValue) { 155 String charset = pc.getConfig().getWebCharset(); 156 HttpServletRequest req = pc.getHttpServletRequest(); 157 Enumeration e = req.getHeaderNames(); 158 String keyDecoded,key; 159 while(e.hasMoreElements()) { 160 key=e.nextElement().toString(); 161 keyDecoded=ReqRspUtil.decode(key, charset,false); 162 if(name.equalsIgnoreCase(key) || name.equalsIgnoreCase(keyDecoded)) 163 return ReqRspUtil.decode(req.getHeader(key),charset,false); 164 } 165 return defaultValue; 166 } 167 168 public static List<String> getHeadersIgnoreCase(PageContext pc, String name) { 169 String charset = pc.getConfig().getWebCharset(); 170 HttpServletRequest req = pc.getHttpServletRequest(); 171 Enumeration e = req.getHeaderNames(); 172 List<String> rtn=new ArrayList<String>(); 173 String keyDecoded,key; 174 while(e.hasMoreElements()) { 175 key=e.nextElement().toString(); 176 keyDecoded=ReqRspUtil.decode(key, charset,false); 177 if(name.equalsIgnoreCase(key) || name.equalsIgnoreCase(keyDecoded)) 178 rtn.add(ReqRspUtil.decode(req.getHeader(key),charset,false)); 179 } 180 return rtn; 181 } 182 183 public static String getScriptName(HttpServletRequest req) { 184 return StringUtil.emptyIfNull(req.getContextPath())+StringUtil.emptyIfNull(req.getServletPath()); 185 } 186 187 private static boolean isHex(char c) { 188 return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'); 189 } 190 191 192 private static String dec(String str, String charset, boolean force) { 193 str=str.trim(); 194 if(StringUtil.startsWith(str, '"') && StringUtil.endsWith(str, '"')) 195 str=str.substring(1,str.length()-1); 196 197 return decode(str,charset,force);//java.net.URLDecoder.decode(str.trim(), charset); 198 } 199 200 201 public static String decode(String str,String charset, boolean force) { 202 try { 203 return URLDecoder.decode(str, charset,force); 204 } 205 catch (UnsupportedEncodingException e) { 206 return str; 207 } 208 } 209 public static String encode(String str,String charset) { 210 try { 211 return URLEncoder.encode(str, charset); 212 } 213 catch (UnsupportedEncodingException e) { 214 return str; 215 } 216 } 217 218 219 220 public static boolean needEncoding(String str, boolean allowPlus){ 221 if(StringUtil.isEmpty(str,false)) return false; 222 223 int len=str.length(); 224 char c; 225 for(int i=0;i<len;i++){ 226 c=str.charAt(i); 227 if(c >='0' && c <= '9') continue; 228 if(c >='a' && c <= 'z') continue; 229 if(c >='A' && c <= 'Z') continue; 230 231 // _-.* 232 if(c =='-') continue; 233 if(c =='_') continue; 234 if(c =='.') continue; 235 if(c =='*') continue; 236 if(c =='/') continue; 237 if(allowPlus && c =='+') continue; 238 239 if(c =='%') { 240 if(i+2>=len) return true; 241 try{ 242 Integer.parseInt(str.substring(i+1,i+3),16); 243 } 244 catch(NumberFormatException nfe){ 245 return true; 246 } 247 i+=3; 248 continue; 249 } 250 return true; 251 } 252 return false; 253 } 254 255 public static boolean needDecoding(String str){ 256 if(StringUtil.isEmpty(str,false)) return false; 257 258 boolean need=false; 259 int len=str.length(); 260 char c; 261 for(int i=0;i<len;i++){ 262 c=str.charAt(i); 263 if(c >='0' && c <= '9') continue; 264 if(c >='a' && c <= 'z') continue; 265 if(c >='A' && c <= 'Z') continue; 266 267 // _-.* 268 if(c =='-') continue; 269 if(c =='_') continue; 270 if(c =='.') continue; 271 if(c =='*') continue; 272 if(c =='+') { 273 need=true; 274 continue; 275 } 276 277 if(c =='%') { 278 if(i+2>=len) return false; 279 try{ 280 Integer.parseInt(str.substring(i+1,i+3),16); 281 } 282 catch(NumberFormatException nfe){ 283 return false; 284 } 285 i+=3; 286 need=true; 287 continue; 288 } 289 return false; 290 } 291 return need; 292 } 293 294 public static boolean isThis(HttpServletRequest req, String url) { 295 try { 296 return isThis(req, HTTPUtil.toURL(url)); 297 } 298 catch (Throwable t) { 299 return false; 300 } 301 } 302 303 public static boolean isThis(HttpServletRequest req, URL url) { 304 try { 305 // Port 306 int reqPort=req.getServerPort(); 307 int urlPort=url.getPort(); 308 if(urlPort<=0) urlPort=HTTPUtil.isSecure(url)?443:80; 309 if(reqPort<=0) reqPort=req.isSecure()?443:80; 310 if(reqPort!=urlPort) return false; 311 312 // host 313 String reqHost = req.getServerName(); 314 String urlHost = url.getHost(); 315 if(reqHost.equalsIgnoreCase(urlHost)) return true; 316 if(IsLocalHost.invoke(reqHost) && IsLocalHost.invoke(reqHost)) return true; 317 318 InetAddress urlAddr = InetAddress.getByName(urlHost); 319 320 InetAddress reqAddr = InetAddress.getByName(reqHost); 321 if(reqAddr.getHostName().equalsIgnoreCase(urlAddr.getHostName())) return true; 322 if(reqAddr.getHostAddress().equalsIgnoreCase(urlAddr.getHostAddress())) return true; 323 324 reqAddr = InetAddress.getByName(req.getRemoteAddr()); 325 if(reqAddr.getHostName().equalsIgnoreCase(urlAddr.getHostName())) return true; 326 if(reqAddr.getHostAddress().equalsIgnoreCase(urlAddr.getHostAddress())) return true; 327 } 328 catch(Throwable t){} 329 return false; 330 } 331 332 333 public static LinkedList<MimeType> getAccept(PageContext pc) { 334 LinkedList<MimeType> accept=new LinkedList<MimeType>(); 335 java.util.Iterator<String> it = ReqRspUtil.getHeadersIgnoreCase(pc, "accept").iterator(); 336 String value; 337 while(it.hasNext()){ 338 value=it.next(); 339 MimeType[] mtes = MimeType.getInstances(value, ','); 340 if(mtes!=null)for(int i=0;i<mtes.length;i++){ 341 accept.add(mtes[i]); 342 } 343 } 344 return accept; 345 } 346 347 public static MimeType getContentType(PageContext pc) { 348 java.util.Iterator<String> it = ReqRspUtil.getHeadersIgnoreCase(pc, "content-type").iterator(); 349 String value; 350 MimeType rtn=null; 351 while(it.hasNext()){ 352 value=it.next(); 353 MimeType[] mtes = MimeType.getInstances(value, ','); 354 if(mtes!=null)for(int i=0;i<mtes.length;i++){ 355 rtn= mtes[i]; 356 } 357 } 358 if(rtn==null) return MimeType.ALL; 359 return rtn; 360 } 361 362 public static String getContentTypeAsString(PageContext pc,String defaultValue) { 363 MimeType mt = getContentType(pc); 364 if(mt==MimeType.ALL) return defaultValue; 365 return mt.toString(); 366 } 367 368 /** 369 * returns the body of the request 370 * @param pc 371 * @param deserialized if true railo tries to deserialize the body based on the content-type, for example when the content type is "application/json" 372 * @param defaultValue value returned if there is no body 373 * @return 374 */ 375 public static Object getRequestBody(PageContext pc,boolean deserialized, Object defaultValue) { 376 HttpServletRequest req = pc.getHttpServletRequest(); 377 378 MimeType contentType = getContentType(pc); 379 String strContentType=contentType==MimeType.ALL?null:contentType.toString(); 380 String charEncoding = req.getCharacterEncoding(); 381 Object obj = ""; 382 383 boolean isBinary =!( 384 strContentType == null || 385 HTTPUtil.isTextMimeType(contentType) || 386 strContentType.toLowerCase().startsWith("application/x-www-form-urlencoded")); 387 388 if(req.getContentLength() > -1) { 389 ServletInputStream is=null; 390 try { 391 byte[] data = IOUtil.toBytes(is=req.getInputStream());//new byte[req.getContentLength()]; 392 393 if(isBinary) return data; 394 395 String str; 396 if(charEncoding != null && charEncoding.length() > 0) 397 obj = str = new String(data, charEncoding); 398 else 399 obj = str = new String(data); 400 401 if(deserialized){ 402 int format = MimeType.toFormat(contentType, -1); 403 switch(format) { 404 case UDF.RETURN_FORMAT_JSON: 405 try{ 406 obj=new JSONExpressionInterpreter().interpret(pc, str); 407 } 408 catch(PageException pe){} 409 break; 410 case UDF.RETURN_FORMAT_SERIALIZE: 411 try{ 412 obj=new CFMLExpressionInterpreter().interpret(pc, str); 413 } 414 catch(PageException pe){} 415 break; 416 case UDF.RETURN_FORMAT_WDDX: 417 try{ 418 WDDXConverter converter =new WDDXConverter(pc.getTimeZone(),false,true); 419 converter.setTimeZone(pc.getTimeZone()); 420 obj = converter.deserialize(str,false); 421 } 422 catch(Exception pe){} 423 break; 424 case UDF.RETURN_FORMAT_XML: 425 try{ 426 InputSource xml = XMLUtil.toInputSource(pc,str.trim()); 427 InputSource validator =null; 428 obj = XMLCaster.toXMLStruct(XMLUtil.parse(xml,validator,false),true); 429 } 430 catch(Exception pe){} 431 break; 432 } 433 } 434 435 436 437 } 438 catch(Exception e) { 439 return defaultValue; 440 } 441 finally { 442 IOUtil.closeEL(is); 443 } 444 } 445 else { 446 return defaultValue; 447 } 448 return obj; 449 } 450 451 452 /** 453 * returns the full request URL 454 * 455 * @param req - the HttpServletRequest 456 * @param includeQueryString - if true, the QueryString will be appended if one exists 457 * */ 458 public static String getRequestURL( HttpServletRequest req, boolean includeQueryString ) { 459 460 StringBuffer sb = req.getRequestURL(); 461 int maxpos = sb.indexOf( "/", 8 ); 462 463 if ( maxpos > -1 ) { 464 465 if ( req.isSecure() ) { 466 if ( sb.substring( maxpos - 4, maxpos ).equals( ":443" ) ) 467 sb.delete( maxpos - 4, maxpos ); 468 } 469 else { 470 if ( sb.substring( maxpos - 3, maxpos ).equals( ":80" ) ) 471 sb.delete( maxpos - 3, maxpos ); 472 } 473 474 if ( includeQueryString && !StringUtil.isEmpty( req.getQueryString() ) ) 475 sb.append( '?' ).append( req.getQueryString() ); 476 } 477 478 return sb.toString(); 479 } 480 481 482 public static String getRootPath(ServletContext sc) { 483 if(sc==null) throw new RuntimeException("cannot determine webcontext root, because the ServletContext is null"); 484 String root = sc.getRealPath("/"); 485 if(root==null) throw new RuntimeException("cannot determinae webcontext root, the ServletContext from class ["+sc.getClass().getName()+"] is returning null for the method call sc.getRealPath(\"/\"), possibly due to configuration problem."); 486 return root; 487 } 488 }