001    package railo.commons.net;
002    
003    import java.io.UnsupportedEncodingException;
004    
005    import railo.commons.io.SystemUtil;
006    import railo.runtime.net.http.ReqRspUtil;
007    
008    public class URLDecoder {
009    
010            private URLDecoder(){}
011    
012            /**
013             * @param string
014             * @return
015             */
016            public static String decode(String str, boolean force) {
017                    try {
018                            return decode(str,SystemUtil.getCharset(), force);
019                    } 
020                    catch (UnsupportedEncodingException e) {
021                            return str;
022                    }
023            }
024    
025        /**
026         * Decodes a <code>application/x-www-form-urlencoded</code> string using a specific 
027         * encoding scheme.
028         * The supplied encoding is used to determine
029         * what characters are represented by any consecutive sequences of the
030         * form "<code>%<i>xy</i></code>".
031         * <p>
032         * <em><strong>Note:</strong> The <a href=
033         * "http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars">
034         * World Wide Web Consortium Recommendation</a> states that
035         * UTF-8 should be used. Not doing so may introduce
036         * incompatibilites.</em>
037         *
038         * @param s the <code>String</code> to decode
039         * @param enc   The name of a supported 
040         *    <a href="../lang/package-summary.html#charenc">character
041         *    encoding</a>. 
042         * @param force if set to false Railo only encodes when there is at least one  "%<2-digit-hex-value>" in string, means string with only + inside are not encoded
043         * @return the newly decoded <code>String</code>
044         * @throws UnsupportedEncodingException 
045         * @see URLEncoder#encode(java.lang.String, java.lang.String)
046         */
047        public static String decode(String s, String enc, boolean force) throws UnsupportedEncodingException {
048            if(!force && !ReqRspUtil.needDecoding(s)) return s;
049            //if(true) return java.net.URLDecoder.decode(s, enc);
050            
051            boolean needToChange = false;
052            StringBuilder sb = new StringBuilder();
053            int numChars = s.length();
054            int i = 0;
055    
056            
057    
058            while (i < numChars) {
059                char c = s.charAt(i);
060                switch (c) {
061                case '+':
062                    sb.append(' ');
063                    i++;
064                    needToChange = true;
065                    break;
066                case '%':
067                    
068                    try {
069                        byte[] bytes = new byte[(numChars-i)/3];
070                        int pos = 0;
071                        
072                        while ( ((i+2) < numChars) && 
073                                (c=='%')) {
074                            bytes[pos++] = (byte)Integer.parseInt(s.substring(i+1,i+3),16);
075                            i+= 3;
076                            if (i < numChars)
077                                c = s.charAt(i);
078                        }
079    
080                        if ((i < numChars) && (c=='%')){
081                            needToChange = true;
082                                    sb.append(c); 
083                                    i++;
084                                    continue;
085                            //throw new IOException("Incomplete trailing escape (%) pattern");
086                        }
087                        sb.append(new String(bytes, 0, pos, enc));
088                    } catch (NumberFormatException e) {
089                            needToChange = true;
090                            sb.append(c); 
091                            i++;
092                        //throw new IOException("Illegal hex characters in escape (%) pattern - " + e.getMessage());
093                    }
094                    needToChange = true;
095                    break;
096                default: 
097                    sb.append(c); 
098                    i++;
099                    break; 
100                }
101            }
102    
103            return (needToChange? sb.toString() : s);
104        }
105    }
106    
107