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.net.http;
020
021import java.io.BufferedReader;
022import java.io.IOException;
023import java.io.Serializable;
024import java.io.UnsupportedEncodingException;
025import java.net.InetAddress;
026import java.net.UnknownHostException;
027import java.security.Principal;
028import java.util.ArrayList;
029import java.util.Date;
030import java.util.Enumeration;
031import java.util.HashSet;
032import java.util.Locale;
033import java.util.Map;
034
035import javax.servlet.RequestDispatcher;
036import javax.servlet.ServletInputStream;
037import javax.servlet.http.Cookie;
038import javax.servlet.http.HttpServletRequest;
039import javax.servlet.http.HttpSession;
040
041import lucee.commons.collection.MapFactory;
042import lucee.commons.io.IOUtil;
043import lucee.commons.io.res.Resource;
044import lucee.commons.lang.ExceptionUtil;
045import lucee.commons.lang.Pair;
046import lucee.runtime.config.Config;
047import lucee.runtime.exp.PageException;
048import lucee.runtime.op.Caster;
049import lucee.runtime.op.date.DateCaster;
050import lucee.runtime.type.Array;
051import lucee.runtime.type.KeyImpl;
052import lucee.runtime.type.Struct;
053import lucee.runtime.type.StructImpl;
054import lucee.runtime.type.dt.DateTimeImpl;
055import lucee.runtime.type.it.ItAsEnum;
056import lucee.runtime.util.EnumerationWrapper;
057
058public final class HttpServletRequestDummy implements HttpServletRequest,Serializable {
059        
060
061        private Cookie[] cookies;
062        
063        private String authType;
064        private Pair<String,Object>[] headers=new Pair[0];
065        private Pair<String,Object>[] parameters=new Pair[0];
066        private Struct attributes=new StructImpl();
067        private String method="GET";
068        private String pathInfo;
069        private String pathTranslated;
070        private String contextPath="";
071        private String queryString;
072        private String remoteUser;
073        private String requestedSessionId;
074        private String requestURI;
075
076        private String protocol="HTTP/1.1";
077        private String serverName="localhost";
078        private int port=80;
079
080        private String characterEncoding="ISO-8859-1";
081
082        private String contentType;
083        private byte[] inputData=new byte[0];
084
085
086        private static InetAddress DEFAULT_REMOTE;
087        private static String DEFAULT_REMOTE_ADDR;
088        private static String DEFAULT_REMOTE_HOST;
089        static {
090                try {
091                        DEFAULT_REMOTE=InetAddress.getLocalHost();
092                        DEFAULT_REMOTE_ADDR=DEFAULT_REMOTE.getHostAddress();
093                        DEFAULT_REMOTE_HOST=DEFAULT_REMOTE.getHostName();
094                } 
095                catch (UnknownHostException e) {}
096        }
097        //private InetAddress remoteq=DEFAULT_REMOTE;
098        private String remoteAddr=DEFAULT_REMOTE_ADDR;
099        private String remoteHost=DEFAULT_REMOTE_HOST;
100
101        private Locale locale=Locale.getDefault();
102
103        private boolean secure;
104
105        private Resource contextRoot;
106
107        private String scheme="http";
108
109        private HttpSession session;
110
111
112
113        /**
114         * constructor of the class
115         * @param headers 
116         * @param parameters 
117         * @param httpSession 
118         * @param pairs 
119         * @param cookiess 
120         */
121        public HttpServletRequestDummy(Resource contextRoot,String serverName, String scriptName,String queryString, 
122                        Cookie[] cookies, Pair[] headers, Pair[] parameters, Struct attributes, HttpSession session) {
123                this.serverName=serverName;
124                requestURI=scriptName;
125                this.queryString=queryString;
126                this.parameters=translateQS(queryString);
127                this.contextRoot=contextRoot;
128                if(cookies!=null)setCookies(cookies);
129                if(headers!=null)this.headers=headers;
130                if(parameters!=null)this.parameters=parameters;
131                if(attributes!=null)this.attributes=attributes;
132                this.session=session;
133        }
134        /**
135         * constructor of the class
136         * @throws PageException
137         * /
138        public HttpServletRequestDummy(String serverName, String scriptName,Struct queryString) throws PageException {
139                this.serverName=serverName;
140                requestURI=scriptName;
141                
142                StringBuffer qs=new StringBuffer();
143                String[] keys=queryString.keys();
144                parameters=new Item[keys.length];
145                String key;
146                Object value;
147                for(int i=0;i<keys.length;i++) {
148                        if(i>0) qs.append('&');
149                        key=keys[i];
150                        value=queryString.get(key);
151                        parameters[i]=new Item(key,value);
152                        
153                        qs.append(key);
154                        qs.append('=');
155                        qs.append(Caster.toString(value));
156                }
157                
158                this.queryString=qs.toString();
159        }*/
160        
161        private Pair[] translateQS(String qs) {
162        if(qs==null) return new Pair[0];
163        Array arr=lucee.runtime.type.util.ListUtil.listToArrayRemoveEmpty(qs,"&");
164        Pair[] parameters=new Pair[arr.size()];
165        //Array item;
166        int index;
167        String name;
168        
169        for(int i=1;i<=parameters.length;i++) {
170            name=Caster.toString(arr.get(i,""),"");
171            index=name.indexOf('=');
172            if(index!=-1) parameters[i-1]=new Pair(name.substring(0,index),name.substring(index+1));
173            else parameters[i-1]=new Pair(name,"");
174              
175        }
176        return parameters;
177    }
178        
179        
180
181        @Override
182        public String getAuthType() {
183                return authType;
184        }
185        
186        /**
187         * sets the name of the authentication scheme used to protect the servlet. 
188         * All servlet containers support basic, 
189         * form and client certificate authentication, 
190         * and may additionally support digest authentication. 
191         * @param authType authentication type
192         */
193        public void setAuthType(String authType) {
194                this.authType=authType;
195        }
196        
197        @Override
198        public Cookie[] getCookies() {
199                return cookies;
200        }
201        
202        /**
203         * sets an array containing all of the Cookie objects 
204         * the client sent with this request. 
205         * This method returns null if no cookies were sent.
206         * @param cookies
207         */
208        public void setCookies(Cookie[] cookies) {
209                this.cookies=cookies;
210        }
211        
212        @Override
213        public long getDateHeader(String name) {
214                Object value=getHeader(name);
215                if(value!=null) {
216                        Date date=DateCaster.toDateAdvanced(value,null,null);
217                        if(date!=null)return date.getTime();
218                        throw new IllegalArgumentException("can't convert value "+value+" to a Date");
219                }
220                return -1;
221        }
222        
223        public void setDateHeader(String name, long value) {
224                // TODO wrong format
225                setHeader(name,new DateTimeImpl(value,false).castToString());
226        }
227        
228        @Override
229        public String getHeader(String name) {
230                return ReqRspUtil.get(headers,name);
231        }
232        
233        /**
234         * sets a new header value
235         * @param name name of the new value
236         * @param value header value
237         */ 
238        public void setHeader(String name, String value) {
239                headers=ReqRspUtil.set(headers,name,value);
240        }
241        
242        /**
243         * add a new header value
244         * @param name name of the new value
245         * @param value header value
246         */ 
247        public void addHeader(String name, String value) {
248                headers=ReqRspUtil.add(headers,name,value);
249        }
250        
251        
252        @Override
253        public Enumeration getHeaders(String name) {
254                HashSet set=new HashSet();
255                for(int i=0;i<headers.length;i++) {
256                        if(headers[i].getName().equalsIgnoreCase(name))
257                                set.add(Caster.toString(headers[i].getValue(),null));
258                }
259                return new EnumerationWrapper(set);
260        }
261        
262        @Override
263        public Enumeration getHeaderNames() {
264                HashSet set=new HashSet();
265                for(int i=0;i<headers.length;i++) {
266                        set.add(headers[i].getName());
267                }
268                return new EnumerationWrapper(set);
269        }
270        
271        @Override
272        public int getIntHeader(String name) {
273                Object value=getHeader(name);
274                if(value!=null) {
275                        try {
276                                return Caster.toIntValue(value);
277                        } catch (PageException e) {
278                                throw new NumberFormatException(e.getMessage());
279                        }
280                }
281                return -1;
282        }
283        
284        @Override
285        public String getMethod() {
286                return method;
287        }
288        
289        /**
290         * sets the request method
291         * @param method
292         */
293        public void setMethod(String method) {
294                this.method = method;
295        }
296        
297        @Override
298        public String getPathInfo() {
299                return pathInfo;
300        }
301        
302        
303        /**
304         * Sets any extra path information associated with the URL the client sent 
305         * when it made this request. 
306         * The extra path information follows the servlet path but precedes 
307         * the query string. 
308         * @param pathInfo
309         */
310        public void setPathInfo(String pathInfo) {
311                this.pathInfo = pathInfo;
312        }
313        
314        
315        @Override
316        public String getPathTranslated() {
317                return pathTranslated;
318        }
319
320        /**
321         * sets any extra path information after the servlet name 
322         * but before the query string, translates to a real path. 
323         * Same as the value of the CGI variable PATH_TRANSLATED. 
324         * @param pathTranslated
325         */
326        public void setPathTranslated(String pathTranslated) {
327                // TODO muss auf pathinfo basieren
328                this.pathTranslated = pathTranslated;
329        }
330        
331        @Override
332        public String getContextPath() {
333                return contextPath;
334        }
335        
336        /**
337         * sets the portion of the request URI that indicates the context of the request. 
338         * The context path always comes first in a request URI. 
339         * The path starts with a "/" character but does not end with a "/" character. 
340         * @param contextPath
341         */
342        public void setContextPath(String contextPath) {
343                this.contextPath = contextPath;
344        }
345        
346        @Override
347        public String getQueryString() {
348                return queryString;
349        }
350        
351        /**
352         * sets the query string that is contained in the request URL after the path. 
353         * Same as the value of the CGI variable QUERY_STRING.
354
355         * @param queryString
356         */
357        public void setQueryString(String queryString) {
358                this.queryString = queryString;
359                parameters=translateQS(queryString);
360        }
361        
362        @Override
363        public String getRemoteUser() {
364                return remoteUser;
365        }
366        
367        /**
368         * sets the login of the user making this request, 
369         * if the user has been authenticated, 
370         * or null if the user has not been authenticated. 
371         * Whether the user name is sent with each subsequent request depends 
372         * on the browser and type of authentication. 
373         * Same as the value of the CGI variable REMOTE_USER.
374         * @param remoteUser
375         */
376        public void setRemoteUser(String remoteUser) {
377                this.remoteUser = remoteUser;
378        }
379        
380        @Override
381        public boolean isUserInRole(String role) {
382                // TODO impl
383                return false;
384        }
385        
386        @Override
387        public Principal getUserPrincipal() {
388                 //TODO impl
389                return null;
390        }
391        
392        @Override
393        public String getRequestedSessionId() {
394                return requestedSessionId;
395        }
396        
397        /**
398         * sets the session ID specified by the client. 
399         * This may not be the same as the ID of the actual session in use. 
400         * For example, if the request specified an old (expired) session ID 
401         * and the server has started a new session, 
402         * this method gets a new session with a new ID. 
403         * @param requestedSessionId
404         */
405        public void setRequestedSessionId(String requestedSessionId) {
406                this.requestedSessionId = requestedSessionId;
407        }
408        
409        @Override
410        public String getRequestURI() {
411                return requestURI;
412        }
413
414        /**
415         * sets the part of this request's URL from the protocol name 
416         * up to the query string in the first line of the HTTP request. 
417         * The web container does not decode this String.
418         * @param requestURI
419         */
420        public void setRequestURI(String requestURI) {
421                this.requestURI = requestURI;
422        }
423        
424        @Override
425        public StringBuffer getRequestURL() {
426                return new StringBuffer(isSecure()?"https":"http").
427                        append("://").
428                        append(serverName).
429                        append(':').
430                        append(port).
431                        append('/').
432                        append(requestURI);
433        }
434        
435        @Override
436        public String getServletPath() {
437                // TODO when different ?
438                return requestURI;
439        }
440        @Override
441        public HttpSession getSession(boolean arg0) {
442                return session;
443        }
444        @Override
445        public HttpSession getSession() {
446                return getSession(true);
447        }
448        @Override
449        public boolean isRequestedSessionIdValid() {
450//               not supported
451                return false;
452        }
453        @Override
454        public boolean isRequestedSessionIdFromCookie() {
455//               not supported
456                return false;
457        }
458        @Override
459        public boolean isRequestedSessionIdFromURL() {
460//               not supported
461                return false;
462        }
463        @Override
464        public boolean isRequestedSessionIdFromUrl() {
465                return isRequestedSessionIdFromURL();
466        }
467        @Override
468        public Object getAttribute(String key) {
469                return attributes.get(key,null);
470        }
471        
472        @Override
473        public void setAttribute(String key, Object value) {
474                attributes.setEL(key,value);
475        }
476        
477        @Override
478        public void removeAttribute(String key) {
479                attributes.removeEL(KeyImpl.init(key));
480        }
481        
482        @Override
483        public Enumeration getAttributeNames() {
484                return ItAsEnum.toStringEnumeration(attributes.keyIterator());
485        }
486        @Override
487        public String getCharacterEncoding() {
488                return characterEncoding;
489        }
490        @Override
491        public void setCharacterEncoding(String characterEncoding)
492                        throws UnsupportedEncodingException {
493                this.characterEncoding=characterEncoding;
494        }
495        @Override
496        public int getContentLength() {
497                return -1;
498        }
499        @Override
500        public String getContentType() {
501                return contentType;
502        }
503        
504        /**
505         * sets the content Type of the Request
506         * @param contentType
507         */
508        public void setContentType(String contentType) {
509                this.contentType=contentType;
510        }
511        
512        @Override
513        public ServletInputStream getInputStream() throws IOException {
514                return new ServletInputStreamDummy(inputData);
515        }
516
517        public void setParameter(String key,String value) {
518                parameters=ReqRspUtil.set(parameters,key,value);
519                rewriteQS();
520        }
521
522        public void addParameter(String key,String value) {
523                parameters=ReqRspUtil.add(parameters,key,value);
524                rewriteQS();
525        }
526        
527        @Override
528        public String getParameter(String key) {
529                return ReqRspUtil.get(parameters,key);
530        }
531        
532        @Override
533        public String[] getParameterValues(String key) {
534                ArrayList list=new ArrayList();
535                for(int i=0;i<parameters.length;i++) {
536                        if(parameters[i].getName().equalsIgnoreCase(key))
537                                list.add(Caster.toString(parameters[i].getValue(),null));
538                }
539                return (String[]) list.toArray(new String[list.size()]);
540        }
541        
542        @Override
543        public Enumeration getParameterNames() {
544                HashSet set=new HashSet();
545                for(int i=0;i<parameters.length;i++) {
546                        set.add(parameters[i].getName());
547                }
548                return new EnumerationWrapper(set);
549        }
550        
551        @Override
552        public Map getParameterMap() {
553                Map<String,Object> p=MapFactory.<String,Object>getConcurrentMap(); 
554                for(int i=0;i<parameters.length;i++) {
555                        p.put(parameters[i].getName(), parameters[i].getValue());
556                }
557                return p;
558        }
559
560        /**
561         * set the Protocol (Default "http")
562         * @param protocol
563         */
564        public void setProtocol(String protocol) {
565                this.protocol=protocol;
566        }
567        
568        @Override
569        public String getProtocol() {
570                return protocol;
571        }
572        
573        @Override
574        public String getScheme() {
575                return scheme;
576        }
577        
578        public void setScheme(String scheme) {
579                this.scheme=scheme;
580        }
581        
582        @Override
583        public String getServerName() {
584                return serverName;
585        }
586        
587        @Override
588        public int getServerPort() {
589                return port;
590        }       
591        
592        /**
593         * @param port The port to set.
594         */
595        public void setServerPort(int port) {
596                this.port = port;
597        }
598        
599    @Override
600    public BufferedReader getReader() throws IOException {
601        return IOUtil.toBufferedReader(IOUtil.getReader(getInputStream(),"ISO-8859-1"));
602    }
603
604        @Override
605        public String getRemoteAddr() {
606                return remoteAddr;
607        }
608
609        public void setRemoteAddr(String remoteAddr) {
610                this.remoteAddr=remoteAddr;
611        }
612        public void setRemoteHost(String remoteHost) {
613                this.remoteHost=remoteHost;
614        }
615        
616        @Override
617        public String getRemoteHost() {
618                return remoteHost;
619        }
620        
621        public void setRemoteInetAddress(InetAddress ia) {
622                setRemoteAddr(ia.getHostAddress());
623                setRemoteHost(ia.getHostName());
624        }
625        
626        @Override
627        public Locale getLocale() {
628                return locale;
629        }
630        
631        public void setLocale(Locale locale) {
632                this.locale=locale;
633        }
634        
635        @Override
636        public Enumeration getLocales() {
637                return new EnumerationWrapper(Locale.getAvailableLocales());
638        }
639        
640        @Override
641        public boolean isSecure() {
642                return secure;
643        }
644        
645        public void setSecure(boolean secure) {
646                this.secure=secure;
647        }
648        
649        @Override
650        public RequestDispatcher getRequestDispatcher(String arg0) {
651                return new RequestDispatcherDummy(this);
652        }
653        
654        @Override
655        public String getRealPath(String path) {
656                return contextRoot.getReal(path);
657        }
658
659        /**
660         * @return the inputData
661         */
662        public byte[] getInputData() {
663                return inputData;
664        }
665        /**
666         * @param inputData the inputData to set
667         */
668        public void setInputData(byte[] inputData) {
669                this.inputData = inputData;
670        }
671
672        private void rewriteQS() {
673                StringBuffer qs=new StringBuffer();
674                Pair p;
675                for(int i=0;i<parameters.length;i++) {
676                        if(i>0) qs.append('&');
677                        p=parameters[i];
678                        qs.append(p.getName());
679                        qs.append('=');
680                        qs.append(Caster.toString(p.getValue(),""));
681                }
682                queryString=qs.toString();
683        }
684        public void setSession(HttpSession session) {
685                this.session=session;
686        }
687        public static HttpServletRequestDummy clone(Config config,Resource rootDirectory,HttpServletRequest req) {
688
689                HttpServletRequestDummy dest = new HttpServletRequestDummy(
690                                rootDirectory,
691                                req.getServerName(),
692                                req.getRequestURI(),
693                                req.getQueryString(),
694                                HttpUtil.cloneCookies(config,req),
695                                HttpUtil.cloneHeaders(req),
696                                HttpUtil.cloneParameters(req),
697                                HttpUtil.getAttributesAsStruct(req),
698                                getSessionEL(req)
699                        );
700                
701
702                try {
703                        dest.setCharacterEncoding(req.getCharacterEncoding());
704                } catch (Exception e) {
705                        
706                }
707                
708                dest.setRemoteAddr(req.getRemoteAddr());
709                dest.setRemoteHost(req.getRemoteHost());
710                dest.setAuthType(req.getAuthType());
711                dest.setContentType(req.getContentType());
712                dest.setContextPath(req.getContextPath());
713                dest.setLocale(req.getLocale());
714                dest.setMethod(req.getMethod());
715                dest.setPathInfo(req.getPathInfo());
716                dest.setProtocol(req.getProtocol());
717                dest.setRequestedSessionId(req.getRequestedSessionId());
718                dest.setScheme(req.getScheme());
719                dest.setServerPort(req.getServerPort());
720                dest.setSession(getSessionEL(req));
721                return dest;
722        }
723        private static HttpSession getSessionEL(HttpServletRequest req) {
724                try{
725                        return req.getSession();
726                }
727                catch(Throwable t){
728                        ExceptionUtil.rethrowIfNecessary(t);
729                }
730                return null;
731        }
732        public void setAttributes(Struct attributes) {
733                this.attributes=attributes;
734        }
735        
736}