| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.tomcat.lite.util; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStreamWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.util.BitSet; |
| |
| /** |
| * |
| * This class is very similar to the java.net.URLEncoder class. |
| * |
| * Unfortunately, with java.net.URLEncoder there is no way to specify to the |
| * java.net.URLEncoder which characters should NOT be encoded. |
| * |
| * This code was moved from DefaultServlet.java |
| * |
| * @author Craig R. McClanahan |
| * @author Remy Maucherat |
| */ |
| public class URLEncoder { |
| protected static final char[] hexadecimal = |
| {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
| 'A', 'B', 'C', 'D', 'E', 'F'}; |
| |
| //Array containing the safe characters set. |
| protected BitSet safeChars = new BitSet(128); |
| |
| public URLEncoder() { |
| for (char i = 'a'; i <= 'z'; i++) { |
| addSafeCharacter(i); |
| } |
| for (char i = 'A'; i <= 'Z'; i++) { |
| addSafeCharacter(i); |
| } |
| for (char i = '0'; i <= '9'; i++) { |
| addSafeCharacter(i); |
| } |
| //safe |
| safeChars.set('$'); |
| safeChars.set('-'); |
| safeChars.set('_'); |
| safeChars.set('.'); |
| |
| // Dangerous: someone may treat this as " " |
| // RFC1738 does allow it, it's not reserved |
| // safeChars.set('+'); |
| //extra |
| safeChars.set('!'); |
| safeChars.set('*'); |
| safeChars.set('\''); |
| safeChars.set('('); |
| safeChars.set(')'); |
| safeChars.set(','); |
| } |
| |
| public void addSafeCharacter( char c ) { |
| safeChars.set( c ); |
| } |
| |
| public String encodeURL(String path) { |
| return encodeURL(path, "UTF-8", true); |
| } |
| |
| public String encodeURL(String path, String enc, boolean allowSlash) { |
| int maxBytesPerChar = 10; |
| |
| StringBuffer rewrittenPath = new StringBuffer(path.length()); |
| ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar); |
| OutputStreamWriter writer = null; |
| try { |
| writer = new OutputStreamWriter(buf, enc); |
| } catch (UnsupportedEncodingException e1) { |
| // shouldn't happen. |
| } |
| |
| for (int i = 0; i < path.length(); i++) { |
| int c = (int) path.charAt(i); |
| if (c < 128 && safeChars.get(c) || allowSlash && c == '/') { |
| rewrittenPath.append((char)c); |
| } else { |
| // convert to external encoding before hex conversion |
| try { |
| writer.write((char)c); |
| if (c >= 0xD800 && c <= 0xDBFF) { |
| if ( (i+1) < path.length()) { |
| int d = path.charAt(i+1); |
| if (d >= 0xDC00 && d <= 0xDFFF) { |
| writer.write((char) d); |
| i++; |
| } |
| } |
| } |
| writer.flush(); |
| } catch(IOException e) { |
| buf.reset(); |
| continue; |
| } |
| byte[] ba = buf.toByteArray(); |
| for (int j = 0; j < ba.length; j++) { |
| // Converting each byte in the buffer |
| byte toEncode = ba[j]; |
| rewrittenPath.append('%'); |
| int low = (int) (toEncode & 0x0f); |
| int high = (int) ((toEncode & 0xf0) >> 4); |
| rewrittenPath.append(hexadecimal[high]); |
| rewrittenPath.append(hexadecimal[low]); |
| } |
| buf.reset(); |
| } |
| } |
| return rewrittenPath.toString(); |
| } |
| |
| /** |
| * Decode and return the specified URL-encoded String. |
| * |
| * @param str The url-encoded string |
| * @param enc The encoding to use; if null, the default encoding is used |
| * @exception IllegalArgumentException if a '%' character is not followed |
| * by a valid 2-digit hexadecimal number |
| */ |
| public static String URLDecode(String str, String enc) { |
| |
| if (str == null) |
| return (null); |
| |
| // use the specified encoding to extract bytes out of the |
| // given string so that the encoding is not lost. If an |
| // encoding is not specified, let it use platform default |
| byte[] bytes = null; |
| try { |
| if (enc == null) { |
| bytes = str.getBytes(); |
| } else { |
| bytes = str.getBytes(enc); |
| } |
| } catch (UnsupportedEncodingException uee) {} |
| |
| return URLDecode(bytes, enc); |
| |
| } |
| |
| |
| /** |
| * Decode and return the specified URL-encoded String. |
| * When the byte array is converted to a string, the system default |
| * character encoding is used... This may be different than some other |
| * servers. |
| * |
| * @param str The url-encoded string |
| * |
| * @exception IllegalArgumentException if a '%' character is not followed |
| * by a valid 2-digit hexadecimal number |
| */ |
| public static String URLDecode(String str) { |
| |
| return URLDecode(str, null); |
| |
| } |
| |
| /** |
| * Decode and return the specified URL-encoded byte array. |
| * |
| * @param bytes The url-encoded byte array |
| * @param enc The encoding to use; if null, the default encoding is used |
| * @exception IllegalArgumentException if a '%' character is not followed |
| * by a valid 2-digit hexadecimal number |
| */ |
| private static String URLDecode(byte[] bytes, String enc) { |
| |
| if (bytes == null) |
| return (null); |
| |
| int len = bytes.length; |
| int ix = 0; |
| int ox = 0; |
| while (ix < len) { |
| byte b = bytes[ix++]; // Get byte to test |
| if (b == '+') { |
| b = (byte)' '; |
| } else if (b == '%') { |
| b = (byte) ((convertHexDigit(bytes[ix++]) << 4) |
| + convertHexDigit(bytes[ix++])); |
| } |
| bytes[ox++] = b; |
| } |
| if (enc != null) { |
| try { |
| return new String(bytes, 0, ox, enc); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| return new String(bytes, 0, ox); |
| |
| } |
| |
| /** |
| * Convert a byte character value to hexidecimal digit value. |
| * |
| * @param b the character value byte |
| */ |
| private static byte convertHexDigit( byte b ) { |
| if ((b >= '0') && (b <= '9')) return (byte)(b - '0'); |
| if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10); |
| if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10); |
| return 0; |
| } |
| |
| } |