5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * The contents of this file are subject to the terms
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * of the Common Development and Distribution License
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * (the License). You may not use this file except in
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * compliance with the License.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * You can obtain a copy of the License at
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * https://opensso.dev.java.net/public/CDDLv1.0.html or
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * See the License for the specific language governing
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * permission and limitations under the License.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * When distributing Covered Code, include this CDDL
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Header Notice in each file and include the License file
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * If applicable, add the following below the CDDL Header,
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * with the fields enclosed by brackets [] replaced by
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * your own identifying information:
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * "Portions Copyrighted [year] [name of copyright owner]"
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * $Id: URLEncDec.java,v 1.5 2009/08/11 13:18:15 si224302 Exp $
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * The list of characters that are not encoded has been determined as
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * RFC 2396 states: ----- Data characters that are allowed in a URI but
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * do not have a reserved purpose are called unreserved. These include
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * upper and lower case letters, decimal digits, and a limited set of
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * punctuation marks and symbols.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * unreserved = alphanum | mark
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Unreserved characters can be escaped without changing the semantics
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * of the URI, but this should not be done unless the URI is being used
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * in a context that does not allow the unescaped character to appear.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * It appears that both Netscape and Internet Explorer escape all
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * special characters from this list with the exception of "-", "_",
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * ".", "*". While it is not clear why they are escaping the other
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * characters, perhaps it is safest to assume that there might be
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * contexts in which the others are unsafe if not escaped. Therefore, we
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * will use the same list. It is also noteworthy that this is consistent
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * with O'Reilly's "HTML: The Definitive Guide" (page 164).
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * As a last note, Intenet Explorer does not encode the "@" character
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * which is clearly not unreserved according to the RFC. We are being
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * consistent with the RFC in this matter, as is Netscape.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * encoding a space to a + is done in the
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * encode() method
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Translates a string into <code>application/x-www-form-urlencoded</code>
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * format using the UTF-8 encoding scheme. The <a
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * href="http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars">
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * World Wide Web Consortium Recommendation</a> states that UTF-8 should be
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * used to ensure compatibilities.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * <code>String</code> to be translated.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @return the translated <code>String</code>.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Decodes a <code>application/x-www-form-urlencoded</code> string using
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * the UTF-8 encoding scheme. The <a
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * href="http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars">
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * World Wide Web Consortium Recommendation</a> states that UTF-8 should be
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * used to ensure compatibilities.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * the <code>String</code> to decode
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @return the newly decoded <code>String</code>
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Translates a string into <code>application/x-www-form-urlencoded</code>
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * format using a specific encoding scheme. This method uses the supplied
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * encoding scheme to obtain the bytes for unsafe characters.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * <em><strong>Note:</strong> The <a href=
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * "http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars">
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * World Wide Web Consortium Recommendation</a> states that
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * UTF-8 should be used. Not doing so may introduce
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * incompatibilites.</em>
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * <code>String</code> to be translated.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @param enc
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * The name of a supported <a
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * href="../lang/package-summary.html#charenc">
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * character encoding</a>.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @return the translated <code>String</code>.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @exception UnsupportedEncodingException
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * If the named encoding is not supported
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @see URLDecoder#decode(java.lang.String, java.lang.String)
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @since 1.4
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster private static String encode(String s, String enc)
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster boolean needToChange = false;
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster boolean wroteUnencodedChar = false;
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster int maxBytesPerChar = 10; // rather arbitrary limit, but safe for now
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster StringBuffer out = new StringBuffer(s.length());
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster OutputStreamWriter writer = new OutputStreamWriter(buf, enc);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster int c = s.charAt(i);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // System.out.println("Examining character: " + c);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster if (c == ' ') {
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // System.out.println("Storing: " + c);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // convert to external encoding before hex conversion
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * If this character represents the start of a Unicode
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * surrogate pair, then pass in two characters. It's not
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * clear what should be done if a bytes reserved in the
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * surrogate pairs range occurs outside of a legal surrogate
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * pair. For now, just treat it as if it were any other
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * character.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * System.out.println(Integer.toHexString(c) + " is high
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * surrogate");
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * System.out.println("\tExamining " +
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Integer.toHexString(d));
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * System.out.println("\t" +
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Integer.toHexString(d) + " is low
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * surrogate");
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // converting to use uppercase letter as part of
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // the hex value if ch is a letter.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Decodes a <code>application/x-www-form-urlencoded</code> string using a
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * specific encoding scheme. The supplied encoding is used to determine what
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * characters are represented by any consecutive sequences of the form
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * "<code>%<i>xy</i></code>".
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * <em><strong>Note:</strong> The <a href=
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * "http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars">
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * World Wide Web Consortium Recommendation</a> states that
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * UTF-8 should be used. Not doing so may introduce
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * incompatibilites.</em>
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * the <code>String</code> to decode
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @param enc
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * The name of a supported <a
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * href="../lang/package-summary.html#charenc">
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * character encoding</a>.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @return the newly decoded <code>String</code>
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @exception UnsupportedEncodingException
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * If the named encoding is not supported
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @see URLEncoder#encode(java.lang.String, java.lang.String)
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * @since 1.4
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster private static String decode(String s, String enc)
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster boolean needToChange = false;
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster "URLDecoder: empty string enc parameter");
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster while (i < numChars) {
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster char c = s.charAt(i);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster switch (c) {
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Starting with this instance of %, process all consecutive
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * substrings of the form %xy. Each substring %xy will yield a
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * byte. Convert all consecutive bytes obtained this way to
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * whatever character(s) they represent in the provided
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // (numChars-i)/3 is an upper bound for the number
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // of remaining bytes
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster bytes[pos++] = (byte) Integer.parseInt(s.substring(
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // A trailing, incomplete byte encoding such as
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // "%x" will cause an exception to be thrown
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster "URLDecoder: Incomplete trailing " +
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster "escape (%) pattern");
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster "URLDecoder: Illegal hex characters " +
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster "in escape (%) pattern - "
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * Construct LDAP url string by converting non-ascii characters to
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * application/x-www-form-urlencoded format using UTF8. See RFC 2255 and RFC
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * 1738 for valid LDAP url format.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster boolean needToChange = false;
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster boolean wroteUnencodedChar = false;
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster int maxBytesPerChar = 10; // rather arbitrary limit, but safe for now
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster StringBuffer out = new StringBuffer(s.length());
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster ByteArrayOutputStream buf = new ByteArrayOutputStream(
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster OutputStreamWriter writer = new OutputStreamWriter(buf, UTF_8);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster int c = s.charAt(i);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster if (c <= 0x80) {
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster if (c == ' ') {
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // convert to external encoding before hex conversion
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * If this character represents the start of a Unicode
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * surrogate pair, then pass in two characters. It's not
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * clear what should be done if a bytes reserved in the
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * surrogate pairs range occurs outside of a legal
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * surrogate pair. For now, just treat it as if it were
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster * any other character.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // converting to use uppercase letter as part of
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // the hex value if ch is a letter.
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster public static String encodeUrlPath(String u) throws MalformedURLException {
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster StringBuffer sb = new StringBuffer(u.length() + 20);
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster // add '/' back if original path has it at the end
5c099afa7c9361afc2f4477fec0e3018588d7840Allan Foster if (path.lastIndexOf('/') == (path.length() - 1)) {