001 package railo.runtime.type.scope; 002 003 import java.lang.reflect.Method; 004 import java.util.Date; 005 import java.util.HashMap; 006 import java.util.Iterator; 007 import java.util.Map; 008 009 import javax.servlet.http.HttpServletRequest; 010 import javax.servlet.http.HttpServletResponse; 011 012 import railo.commons.lang.StringUtil; 013 import railo.runtime.PageContext; 014 import railo.runtime.config.Config; 015 import railo.runtime.engine.ThreadLocalPageContext; 016 import railo.runtime.exp.ExpressionException; 017 import railo.runtime.exp.PageException; 018 import railo.runtime.listener.ApplicationContext; 019 import railo.runtime.net.http.ReqRspUtil; 020 import railo.runtime.op.Caster; 021 import railo.runtime.op.Decision; 022 import railo.runtime.op.date.DateCaster; 023 import railo.runtime.security.ScriptProtect; 024 import railo.runtime.type.Collection; 025 import railo.runtime.type.KeyImpl; 026 import railo.runtime.type.Struct; 027 import railo.runtime.type.dt.DateTime; 028 import railo.runtime.type.dt.TimeSpan; 029 import railo.runtime.type.util.KeyConstants; 030 031 /** 032 * Implementation of the Cookie scope 033 */ 034 public final class CookieImpl extends ScopeSupport implements Cookie,ScriptProtected { 035 036 private static final long serialVersionUID = -2341079090783313736L; 037 038 public static final int NEVER = 946626690; 039 040 private HttpServletResponse rsp; 041 private int scriptProtected=ScriptProtected.UNDEFINED; 042 private Map<String,String> raw=new HashMap<String,String>(); 043 private String charset; 044 045 046 private static final Class[] IS_HTTP_ONLY_ARGS_CLASSES = new Class[]{}; 047 private static final Object[] IS_HTTP_ONLY_ARGS = new Object[]{}; 048 049 private static final Class[] SET_HTTP_ONLY_ARGS_CLASSES = new Class[]{boolean.class}; 050 private static final Object[] SET_HTTP_ONLY_ARGS = new Object[]{Boolean.TRUE}; 051 private static Method isHttpOnly; 052 private static Method setHttpOnly; 053 054 /** 055 * constructor for the Cookie Scope 056 */ 057 public CookieImpl() { 058 super(false,"cookie",SCOPE_COOKIE); 059 } 060 061 062 @Override 063 public Object setEL(Collection.Key key, Object value) { 064 try { 065 return set(key,value); 066 } catch (PageException e) { 067 return null; 068 } 069 } 070 071 public Object set(Collection.Key key, Object value) throws PageException { 072 raw.remove(key.getLowerString()); 073 074 if(Decision.isStruct(value)) { 075 Struct sct = Caster.toStruct(value); 076 int expires=Caster.toIntValue(sct.get(KeyConstants._expires,null),-1); 077 Object val=sct.get(KeyConstants._value,null); 078 boolean secure=Caster.toBooleanValue(sct.get(KeyConstants._secure,null),false); 079 boolean httpOnly=Caster.toBooleanValue(sct.get(KeyConstants._httponly,null),false); 080 String domain=Caster.toString(sct.get(KeyConstants._domain,null),null); 081 String path=Caster.toString(sct.get(KeyConstants._path,null),null); 082 boolean preserveCase=Caster.toBooleanValue(sct.get(KeyConstants._preservecase,null),false); 083 Boolean encode=Caster.toBoolean(sct.get(KeyConstants._encode,null),null); 084 if(encode==null)encode=Caster.toBoolean(sct.get(KeyConstants._encodevalue,Boolean.TRUE),Boolean.TRUE); 085 086 setCookie(key, val, expires, secure, path, domain,httpOnly,preserveCase,encode.booleanValue()); 087 } 088 else setCookie(key,value,-1,false,"/",null,false,false,false); 089 return value; 090 } 091 092 private void set(Config config,javax.servlet.http.Cookie cookie) throws PageException { 093 094 String name=StringUtil.toLowerCase(ReqRspUtil.decode(cookie.getName(),charset,false)); 095 raw.put(name,cookie.getValue()); 096 if(isScriptProtected()) super.set (KeyImpl.init(name),ScriptProtect.translate(dec(cookie.getValue()))); 097 else super.set (KeyImpl.init(name),dec(cookie.getValue())); 098 } 099 100 @Override 101 public void clear() { 102 raw.clear(); 103 Collection.Key[] keys = keys(); 104 for(int i=0;i<keys.length;i++) { 105 removeEL(keys[i],false); 106 } 107 } 108 109 @Override 110 111 public Object remove(Collection.Key key) throws PageException { 112 raw.remove(key.getLowerString()); 113 return remove(key, true); 114 } 115 116 public Object remove(Collection.Key key, boolean alsoInResponse) throws PageException { 117 raw.remove(key.getLowerString()); 118 Object obj=super.remove (key); 119 if(alsoInResponse)removeCookie(key); 120 return obj; 121 } 122 123 public Object removeEL(Collection.Key key) { 124 return removeEL(key, true); 125 } 126 127 private Object removeEL(Collection.Key key, boolean alsoInResponse) { 128 raw.remove(key.getLowerString()); 129 Object obj=super.removeEL (key); 130 if(obj!=null && alsoInResponse) removeCookie(key); 131 return obj; 132 } 133 134 private void removeCookie(Collection.Key key) { 135 javax.servlet.http.Cookie cookie=new javax.servlet.http.Cookie(key.getUpperString(),""); 136 cookie.setMaxAge(0); 137 cookie.setSecure(false); 138 cookie.setPath("/"); 139 rsp.addCookie(cookie); 140 } 141 142 @Override 143 public void setCookie(Collection.Key key, Object value, Object expires, boolean secure, String path, String domain) throws PageException { 144 setCookie(key, value, expires, secure, path, domain, false, false, true); 145 } 146 147 @Override 148 public void setCookie(Collection.Key key, Object value, int expires, boolean secure, String path, String domain) throws PageException { 149 setCookie(key, value, expires, secure, path, domain, false, false, true); 150 } 151 152 @Override 153 public void setCookieEL(Collection.Key key, Object value, int expires, boolean secure, String path, String domain) { 154 setCookieEL(key, value, expires, secure, path, domain, false, false, true); 155 } 156 157 @Override 158 public void setCookie(Collection.Key key, Object value, Object expires, boolean secure, String path, String domain, 159 boolean httpOnly, boolean preserveCase, boolean encode) throws PageException { 160 int exp=-1; 161 162 // expires 163 if(expires instanceof Date) { 164 exp=toExpires((Date)expires); 165 } 166 else if(expires instanceof TimeSpan) { 167 exp=toExpires((TimeSpan)expires); 168 } 169 else if(expires instanceof Number) { 170 exp=toExpires((Number)expires); 171 } 172 else if(expires instanceof String) { 173 exp=toExpires((String)expires); 174 175 } 176 else { 177 throw new ExpressionException("invalid type ["+Caster.toClassName(expires)+"] for expires"); 178 } 179 180 setCookie(key, value, exp, secure, path, domain,httpOnly,preserveCase,encode); 181 } 182 183 @Override 184 public void setCookie(Collection.Key key, Object value, int expires, boolean secure, String path, String domain, 185 boolean httpOnly, boolean preserveCase, boolean encode) throws PageException { 186 187 _addCookie(key,Caster.toString(value),expires,secure,path,domain,httpOnly,preserveCase,encode); 188 super.set (key, value); 189 } 190 191 192 @Override 193 public void setCookieEL(Collection.Key key, Object value, int expires, boolean secure, String path, String domain, 194 boolean httpOnly, boolean preserveCase, boolean encode) { 195 196 _addCookie(key,Caster.toString(value,""),expires,secure,path,domain,httpOnly,preserveCase,encode); 197 super.setEL (key, value); 198 } 199 200 private void _addCookie(Key key, String value, int expires, boolean secure, String path, String domain, 201 boolean httpOnly, boolean preserveCase, boolean encode) { 202 String name=preserveCase?key.getString():key.getUpperString(); 203 if(encode) { 204 name=enc(name); 205 value=enc(value); 206 } 207 208 javax.servlet.http.Cookie cookie = new javax.servlet.http.Cookie(name,value); 209 cookie.setMaxAge(expires); 210 cookie.setSecure(secure); 211 cookie.setPath(path); 212 if(!StringUtil.isEmpty(domain,true))cookie.setDomain(domain); 213 if(httpOnly) setHTTPOnly(cookie); 214 rsp.addCookie(cookie); 215 216 } 217 218 219 private int toExpires(String expires) throws ExpressionException { 220 String str=StringUtil.toLowerCase(expires.toString()); 221 if(str.equals("now"))return 0; 222 else if(str.equals("never"))return NEVER; 223 else { 224 DateTime dt = DateCaster.toDateAdvanced(expires,false,null,null); 225 if(dt!=null) { 226 return toExpires(dt); 227 } 228 return toExpires(Caster.toIntValue(expires)); 229 } 230 } 231 232 233 private int toExpires(Number expires) { 234 return toExpires(expires.intValue()); 235 } 236 private int toExpires(int expires) { 237 return expires*24*60*60; 238 } 239 240 241 private int toExpires(Date expires) { 242 double diff = expires.getTime()-System.currentTimeMillis(); 243 return (int)Math.round(diff/1000D); 244 } 245 private int toExpires(TimeSpan span) { 246 return (int)span.getSeconds(); 247 } 248 249 250 251 252 253 254 @Override 255 public void initialize(PageContext pc) { 256 Config config = ThreadLocalPageContext.getConfig(pc); 257 charset = pc.getConfig().getWebCharset(); 258 if(scriptProtected==ScriptProtected.UNDEFINED) { 259 scriptProtected=((pc.getApplicationContext().getScriptProtect()&ApplicationContext.SCRIPT_PROTECT_COOKIE)>0)? 260 ScriptProtected.YES:ScriptProtected.NO; 261 } 262 super.initialize(pc); 263 264 HttpServletRequest req = pc. getHttpServletRequest(); 265 this.rsp=pc. getHttpServletResponse(); 266 javax.servlet.http.Cookie[] cookies=ReqRspUtil.getCookies(config,req); 267 try { 268 for(int i=0;i<cookies.length;i++) { 269 set(config,cookies[i]); 270 } 271 } 272 catch (Exception e) {} 273 } 274 275 @Override 276 public void release() { 277 raw.clear(); 278 scriptProtected=ScriptProtected.UNDEFINED; 279 super.release(); 280 } 281 282 @Override 283 public void release(PageContext pc) { 284 raw.clear(); 285 scriptProtected=ScriptProtected.UNDEFINED; 286 super.release(pc); 287 } 288 289 290 291 @Override 292 public boolean isScriptProtected() { 293 return scriptProtected==ScriptProtected.YES; 294 } 295 296 @Override 297 public void setScriptProtecting(ApplicationContext ac,boolean scriptProtected) { 298 int _scriptProtected = scriptProtected?ScriptProtected.YES:ScriptProtected.NO; 299 if(isInitalized() && _scriptProtected!=this.scriptProtected) { 300 Iterator<Entry<String, String>> it = raw.entrySet().iterator(); 301 Entry<String, String> entry; 302 String key,value; 303 304 while(it.hasNext()){ 305 entry = it.next(); 306 key=entry.getKey().toString(); 307 value=dec(entry.getValue().toString()); 308 super.setEL(KeyImpl.init(key), scriptProtected?ScriptProtect.translate(value):value); 309 } 310 } 311 this.scriptProtected=_scriptProtected; 312 } 313 314 315 public String dec(String str) { 316 return ReqRspUtil.decode(str,charset,false); 317 } 318 public String enc(String str) { 319 return ReqRspUtil.encode(str,charset); 320 } 321 322 323 @Override 324 public void resetEnv(PageContext pc) { 325 } 326 327 328 @Override 329 public void touchBeforeRequest(PageContext pc) { 330 } 331 332 333 @Override 334 public void touchAfterRequest(PageContext pc) { 335 } 336 337 public static void setHTTPOnly(javax.servlet.http.Cookie cookie) { 338 try { 339 if(setHttpOnly==null) { 340 setHttpOnly=cookie.getClass().getMethod("setHttpOnly", SET_HTTP_ONLY_ARGS_CLASSES); 341 } 342 setHttpOnly.invoke(cookie, SET_HTTP_ONLY_ARGS); 343 } 344 catch (Throwable t) { 345 // HTTPOnly is not supported in this enviroment 346 } 347 } 348 349 public static boolean isHTTPOnly(javax.servlet.http.Cookie cookie) { 350 try { 351 if(isHttpOnly==null) { 352 isHttpOnly=cookie.getClass().getMethod("isHttpOnly", IS_HTTP_ONLY_ARGS_CLASSES); 353 } 354 return Caster.toBooleanValue(isHttpOnly.invoke(cookie, IS_HTTP_ONLY_ARGS)); 355 } 356 catch (Throwable t) { 357 return false; 358 } 359 } 360 }