001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.type.scope;
020
021import java.net.InetAddress;
022import java.net.UnknownHostException;
023import java.util.Enumeration;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.Iterator;
027import java.util.Map;
028import java.util.Set;
029
030import javax.servlet.http.HttpServletRequest;
031
032import lucee.commons.lang.ExceptionUtil;
033import lucee.commons.lang.StringUtil;
034import lucee.runtime.PageContext;
035import lucee.runtime.config.NullSupportHelper;
036import lucee.runtime.dump.DumpData;
037import lucee.runtime.dump.DumpProperties;
038import lucee.runtime.engine.ThreadLocalPageContext;
039import lucee.runtime.exp.PageException;
040import lucee.runtime.listener.ApplicationContext;
041import lucee.runtime.net.http.ReqRspUtil;
042import lucee.runtime.op.Caster;
043import lucee.runtime.security.ScriptProtect;
044import lucee.runtime.type.Collection;
045import lucee.runtime.type.KeyImpl;
046import lucee.runtime.type.Struct;
047import lucee.runtime.type.StructImpl;
048import lucee.runtime.type.it.EntryIterator;
049import lucee.runtime.type.it.KeyIterator;
050import lucee.runtime.type.util.KeyConstants;
051import lucee.runtime.type.util.StructSupport;
052import lucee.runtime.type.util.StructUtil;
053
054/**
055 *
056 *
057 * To change the template for this generated type comment go to
058 * Window - Preferences - Java - Code Generation - Code and Comments
059 */
060public final class CGIImpl extends StructSupport implements CGI,ScriptProtected {
061        
062        private static final long serialVersionUID = 5219795840777155232L;
063
064        private static final Collection.Key[] STATIC_KEYS={
065                KeyConstants._auth_password, KeyConstants._auth_type, KeyConstants._auth_user, KeyConstants._cert_cookie, KeyConstants._cert_flags, 
066                KeyConstants._cert_issuer, KeyConstants._cert_keysize, KeyConstants._cert_secretkeysize, KeyConstants._cert_serialnumber, 
067                KeyConstants._cert_server_issuer, KeyConstants._cert_server_subject, KeyConstants._cert_subject,KeyConstants._cf_template_path, 
068                KeyConstants._content_length, KeyConstants._content_type, KeyConstants._gateway_interface, KeyConstants._http_accept, 
069                KeyConstants._http_accept_encoding, KeyConstants._http_accept_language, KeyConstants._http_connection, KeyConstants._http_cookie, 
070                KeyConstants._http_host, KeyConstants._http_user_agent, KeyConstants._http_referer, KeyConstants._https, KeyConstants._https_keysize, 
071                KeyConstants._https_secretkeysize, KeyConstants._https_server_issuer, KeyConstants._https_server_subject, KeyConstants._path_info,
072                KeyConstants._path_translated, KeyConstants._query_string, KeyConstants._remote_addr, KeyConstants._remote_host, KeyConstants._remote_user, 
073                KeyConstants._request_method, KeyConstants._request_url, KeyConstants._script_name, KeyConstants._server_name, KeyConstants._server_port, KeyConstants._server_port_secure,
074                KeyConstants._server_protocol, KeyConstants._server_software, KeyConstants._web_server_api, KeyConstants._context_path, KeyConstants._local_addr, 
075                KeyConstants._local_host
076        };
077        private static Struct staticKeys=new StructImpl();
078        static{
079                for(int i=0;i<STATIC_KEYS.length;i++){
080                        staticKeys.setEL(STATIC_KEYS[i],"");
081                }
082        }
083        
084        private static String localAddress="";
085        private static String localHost="";
086        
087        static {
088                try {
089                        InetAddress addr = InetAddress.getLocalHost();
090                        localAddress=addr.getHostAddress();
091                        localHost=addr.getHostName();
092                }
093                catch(UnknownHostException uhe) {}
094        }
095        
096        private HttpServletRequest req;
097        private boolean isInit;
098        private Struct internal;
099        private Map<Collection.Key,Collection.Key> aliases;
100        private int scriptProtected;
101        
102        
103        public CGIImpl(){
104                //this.setReadOnly(true);
105        }
106
107        @Override
108        public boolean isInitalized() {
109                return isInit;
110        }
111        
112        @Override
113        public void initialize(PageContext pc) {
114                isInit=true;
115                req=pc.getHttpServletRequest();
116                
117                
118        if(scriptProtected==ScriptProtected.UNDEFINED) {
119                        scriptProtected=((pc.getApplicationContext().getScriptProtect()&ApplicationContext.SCRIPT_PROTECT_CGI)>0)?
120                                        ScriptProtected.YES:ScriptProtected.NO;
121                }
122        
123        //if(internal==null) {
124                internal=new StructImpl();
125                aliases=new HashMap<Collection.Key,Collection.Key>();
126                String k,v;
127                Collection.Key key,alias,httpKey;
128                try {
129                        Enumeration e = req.getHeaderNames();
130                        while(e.hasMoreElements()) {
131                                
132                                // keys
133                                k = (String)e.nextElement();
134                        key=KeyImpl.init(k);
135                        if(k.contains("-")) alias=KeyImpl.init(k.replace('-','_'));
136                        else alias=null;
137                        httpKey=KeyImpl.init("http_"+(alias==null?key:alias).getString());
138                        
139                        // value
140                        v = doScriptProtect(req.getHeader(k));
141                        
142                        // set value
143                        internal.setEL(httpKey,v);
144                        
145                        // set alias keys
146                        aliases.put(key, httpKey);
147                        if(alias!=null)aliases.put(alias, httpKey);
148                }
149                }
150                catch(Throwable t){
151                        ExceptionUtil.rethrowIfNecessary(t);
152                        t.printStackTrace();
153                }
154                //}
155        }
156        
157        @Override
158        public void release() {
159                isInit=false;
160                scriptProtected=ScriptProtected.UNDEFINED; 
161                req=null;
162                internal=null;
163                aliases=null;
164        }
165        
166        @Override
167        public void release(PageContext pc) {
168                release();
169        }
170
171        @Override
172        public boolean containsKey(Key key) {
173                return internal.containsKey(key) || staticKeys.containsKey(key) || aliases.containsKey(key);
174        }
175        
176        @Override
177        public boolean containsValue(Object value) {
178                Iterator<Object> it = internal.valueIterator();
179                while(it.hasNext()){
180                        if(it.next().equals(value)) return true;
181                }
182                return false;
183        }
184        
185        @Override
186        public Collection duplicate(boolean deepCopy) {
187                Struct sct=new StructImpl();
188                StructImpl.copy(this,sct,deepCopy);
189                return sct;
190        }
191        
192        
193        @Override
194        public int size() {
195                return keys().length;
196        }
197
198        @Override
199        public Collection.Key[] keys() {
200                Set<Collection.Key> set=new HashSet<Collection.Key>();
201                Iterator<Key> it = internal.keyIterator();
202                while(it.hasNext())set.add(it.next());
203                it = staticKeys.keyIterator();
204                while(it.hasNext())set.add(it.next());
205                return set.toArray(new Collection.Key[set.size()]);
206        }
207        
208        @Override
209        public Object get(Collection.Key key, Object defaultValue) {
210                
211                // do we have internal?
212                Object res = internal.get(key, NullSupportHelper.NULL());
213                if(res!=NullSupportHelper.NULL()) return res;
214                
215                // do we have an alias
216                {
217                        Key k = aliases.get(key);
218                        if(k!=null) {
219                                res = internal.get(k, NullSupportHelper.NULL());
220                                if(res!=NullSupportHelper.NULL()) return res;
221                        }
222                }
223                
224                
225                if(key.length()>7) {
226                        char first=key.lowerCharAt(0);
227                        try{
228            if(first=='a') {
229                if(key.equals(KeyConstants._auth_type)) 
230                        return store(key,toString(req.getAuthType()));
231            }
232            else if(first=='c') {
233                if(key.equals(KeyConstants._context_path))
234                        return store(key,toString(req.getContextPath()));
235                if(key.equals(KeyConstants._cf_template_path)) 
236                        return store(key,getPathTranslated());
237            }
238            else if(first=='h') {
239                if(StringUtil.startsWithIgnoreCase(key.getString(), "http_")) {
240                        // _http_if_modified_since
241                        if(key.equals(KeyConstants._http_if_modified_since)) {
242                        Object o = internal.get(KeyConstants._last_modified,NullSupportHelper.NULL());
243                        if(o!=NullSupportHelper.NULL()) return store(key,(String)o);
244                        }
245                }
246            }
247            else if(first=='r') {
248                if(key.equals(KeyConstants._remote_user))               
249                        return store(key,toString(req.getRemoteUser()));
250                if(key.equals(KeyConstants._remote_addr))               
251                        return store(key,toString(req.getRemoteAddr()));
252                if(key.equals(KeyConstants._remote_host))               
253                        return store(key,toString(req.getRemoteHost()));
254                if(key.equals(KeyConstants._request_method))            
255                        return store(key,req.getMethod());
256                if(key.equals(KeyConstants._request_url))
257                        return store(key,ReqRspUtil.getRequestURL( req, true ));
258                if(key.equals(KeyConstants._request_uri))               
259                        return store(key,toString(req.getAttribute("javax.servlet.include.request_uri")));
260                // we do not store this, to be as backward compatible as possible.
261                if(key.getUpperString().startsWith("REDIRECT_")){
262                        // from attributes (key sensitive)
263                        Object value = req.getAttribute(key.getString());
264                        if(!StringUtil.isEmpty(value)) return toString(value);
265                        
266                        // from attributes (key insensitive)
267                        Enumeration<String> names = req.getAttributeNames();
268                        String k;
269                        while(names.hasMoreElements()){
270                                k=names.nextElement();
271                                if(k.equalsIgnoreCase(key.getString())) {
272                                        return toString(req.getAttribute(k));
273                                }
274                        }
275                }
276            }
277            else if(first=='l') {
278                if(key.equals(KeyConstants._local_addr))
279                        return store(key,toString(localAddress));
280                if(key.equals(KeyConstants._local_host))                
281                        return store(key,toString(localHost));
282            }
283            else if(first=='s') {
284                if(key.equals(KeyConstants._script_name))               
285                        return store(key,ReqRspUtil.getScriptName(null,req));
286                        if(key.equals(KeyConstants._server_name))               
287                                return store(key,toString(req.getServerName()));
288                if(key.equals(KeyConstants._server_protocol))   
289                        return store(key,toString(req.getProtocol()));
290                if(key.equals(KeyConstants._server_port))               
291                        return store(key,Caster.toString(req.getServerPort()));
292                if(key.equals(KeyConstants._server_port_secure))
293                        return store(key,req.isSecure()?"1":"0");
294            }
295            else if(first=='p') {
296                if(key.equals(KeyConstants._path_info)) {
297                        String pathInfo = Caster.toString(req.getAttribute("javax.servlet.include.path_info"),null);
298                        if(StringUtil.isEmpty(pathInfo)) pathInfo = Caster.toString(req.getHeader("xajp-path-info"),null);
299                        if(StringUtil.isEmpty(pathInfo)) pathInfo = req.getPathInfo();
300                        if(StringUtil.isEmpty(pathInfo)) {
301                                pathInfo = Caster.toString(req.getAttribute("requestedPath"),null);
302                                if(!StringUtil.isEmpty(pathInfo,true)) {
303                                        String scriptName = ReqRspUtil.getScriptName(null,req);
304                                        if ( pathInfo.startsWith(scriptName) )
305                                                pathInfo = pathInfo.substring(scriptName.length());
306                                }
307                        }
308                    
309                        if(!StringUtil.isEmpty(pathInfo,true)) 
310                                return store(key,pathInfo);
311                    return "";
312                }
313                if(key.equals(KeyConstants._path_translated)) 
314                        return store(key,getPathTranslated());
315            }
316            else if(first=='q') {
317                if(key.equals(KeyConstants._query_string))
318                        return store(key,doScriptProtect(toString(ReqRspUtil.getQueryString(req))));
319            }
320                        }
321                        catch(Throwable t){
322                                ExceptionUtil.rethrowIfNecessary(t);
323                        }
324        }
325        return other(key,defaultValue);
326        }
327        private Object store(Key key, String value) {
328                internal.setEL(key, value);
329                return value;
330        }
331        
332        private Object other(Collection.Key key, Object defaultValue) {
333                if(staticKeys.containsKey(key)) return "";
334                return defaultValue;
335        }
336
337        private String getPathTranslated() {
338                try{
339                        PageContext pc = ThreadLocalPageContext.get();
340                        return pc.getBasePageSource().getResourceTranslated(pc).toString();
341                }
342                catch(Throwable t){
343                        ExceptionUtil.rethrowIfNecessary(t);
344                }
345                return "";
346        }
347
348        private String doScriptProtect(String value) {
349                if(isScriptProtected()) return ScriptProtect.translate(value);
350                return value;
351        }
352        
353        private String toString(Object str) {
354                return StringUtil.toStringEmptyIfNull(str);
355        }
356        
357        @Override
358        public Object get(Collection.Key key) {
359                Object value=get(key,"");
360                if(value==null)value= "";
361                return value;
362        }
363        
364        @Override
365        public Iterator<Collection.Key> keyIterator() {
366                return new KeyIterator(keys());
367        }
368        
369        @Override
370        public Iterator<Entry<Key, Object>> entryIterator() {
371                return new EntryIterator(this, keys());
372        }
373        
374        
375        @Override
376        public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
377                return StructUtil.toDumpTable(this, "CGI Scope (writable)", pageContext, maxlevel, dp);
378        }
379    
380    @Override
381    public int getType() {
382        return SCOPE_CGI;
383    }
384    
385    @Override
386    public String getTypeAsString() {
387        return "cgi";
388    }
389    
390        @Override
391        public boolean isScriptProtected() {
392                return scriptProtected==ScriptProtected.YES;
393        }
394        
395        @Override
396        public void setScriptProtecting(ApplicationContext ac,boolean scriptProtecting) {
397                scriptProtected=scriptProtecting?ScriptProtected.YES:ScriptProtected.NO;
398        }
399
400        @Override
401        public Object remove(Key key) throws PageException {
402                Key k = aliases.remove(key);
403                if(k!=null) key=k;
404                
405                Object rtn=internal.remove(key);
406                if(staticKeys.containsKey(key))internal.set(key, ""); // we do this to avoid to this get reinit again
407                return rtn;
408        }
409
410
411        @Override
412        public Object removeEL(Key key) {
413                Key k = aliases.remove(key);
414                if(k!=null) key=k;
415                
416                Object rtn=internal.removeEL(key);
417                if(staticKeys.containsKey(key))internal.setEL(key, ""); // we do this to avoid to this get reinit again
418                return rtn;
419        }
420
421
422        @Override
423        public void clear() {
424                Key[] keys = keys();
425                for(int i=0;i<keys.length;i++){
426                        removeEL(keys[i]);
427                }
428        }
429
430
431        @Override
432        public Object set(Key key, Object value) throws PageException {
433                Key k = aliases.get(key);
434                if(k!=null) key=k;
435                return internal.set(key, value);
436        }
437
438
439        @Override
440        public Object setEL(Key key, Object value) {
441                Key k = aliases.get(key);
442                if(k!=null) key=k;
443                return internal.setEL(key, value);
444        }
445
446
447        @Override
448        public Iterator<Object> valueIterator() {
449                return internal.valueIterator();
450        }
451
452}