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    }