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.commons.net;
020
021import java.io.UnsupportedEncodingException;
022
023import lucee.commons.io.SystemUtil;
024import lucee.runtime.net.http.ReqRspUtil;
025
026public class URLDecoder {
027
028        private URLDecoder(){}
029
030        /**
031         * @param string
032         * @return
033         */
034        public static String decode(String str, boolean force) {
035                try {
036                        return decode(str,SystemUtil.getCharset().name(), force);
037                } 
038                catch (UnsupportedEncodingException e) {
039                        return str;
040                }
041        }
042
043    public static String decode(String s, String enc, boolean force) throws UnsupportedEncodingException {
044        if(!force && !ReqRspUtil.needDecoding(s)) return s;
045        //if(true) return java.net.URLDecoder.decode(s, enc);
046        
047        boolean needToChange = false;
048        StringBuilder sb = new StringBuilder();
049        int numChars = s.length();
050        int i = 0;
051
052        
053
054        while (i < numChars) {
055            char c = s.charAt(i);
056            switch (c) {
057            case '+':
058                sb.append(' ');
059                i++;
060                needToChange = true;
061                break;
062            case '%':
063                
064                try {
065                    byte[] bytes = new byte[(numChars-i)/3];
066                    int pos = 0;
067                    
068                    while ( ((i+2) < numChars) && 
069                            (c=='%')) {
070                        bytes[pos++] = (byte)Integer.parseInt(s.substring(i+1,i+3),16);
071                        i+= 3;
072                        if (i < numChars)
073                            c = s.charAt(i);
074                    }
075
076                    if ((i < numChars) && (c=='%')){
077                        needToChange = true;
078                                sb.append(c); 
079                                i++;
080                                continue;
081                        //throw new IOException("Incomplete trailing escape (%) pattern");
082                    }
083                    sb.append(new String(bytes, 0, pos, enc));
084                } catch (NumberFormatException e) {
085                        needToChange = true;
086                        sb.append(c); 
087                        i++;
088                    //throw new IOException("Illegal hex characters in escape (%) pattern - " + e.getMessage());
089                }
090                needToChange = true;
091                break;
092            default: 
093                sb.append(c); 
094                i++;
095                break; 
096            }
097        }
098
099        return (needToChange? sb.toString() : s);
100    }
101}
102
103