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    }