/* * 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.harmony.jndi.internal.parser; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.naming.ldap.Rdn; import org.apache.harmony.jndi.internal.nls.Messages; import org.apache.harmony.security.x509.DNParser; /** * A relative distinguished name parser that does less checking than DNParser */ public class RelaxedDnParser extends DNParser { private static final char[] specialChars = new char[] { '!', '$', '%', '&', '\'', '(', ')', '*', '/', ':', '<', '=', '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '\"', '#', '+', ',', '\\', ';' }; /** * Constructor * * @param dn * the String to parse * @throws IOException */ public RelaxedDnParser(String dn) throws IOException { super(dn); } @Override protected String nextAT() throws IOException { int equalIndex = pos; try { while (chars[equalIndex] != '=') { equalIndex++; // points to the equals on the current attribute // type pair } } catch (ArrayIndexOutOfBoundsException e) { throw new IOException(Messages.getString("ldap.1C")); //$NON-NLS-1$ } // Check for quotations and special chars on the type for (int i = pos; i < equalIndex; i++) { if (chars[i] == '\\') { throw new IOException(Messages.getString("ldap.1D")); //$NON-NLS-1$ } for (int j = 0; j < specialChars.length; j++) { if (chars[i] == specialChars[j]) { throw new IOException(Messages.getString("ldap.1E")); //$NON-NLS-1$ } } } return super.nextAT(); } protected String hexAV() throws IOException { beg = pos; // store '#' position pos++; while (true) { // check for end of attribute value // looks for space and component separators if (pos == length || chars[pos] == '+' || chars[pos] == ',' || chars[pos] == ';') { end = pos; break; } if (chars[pos] >= 'A' && chars[pos] <= 'F') { chars[pos] += 32; // to low case } pos++; } while (end > beg + 1 && chars[end - 1] == ' ') { end--; } // verify length of hex string // encoded byte array must be even number int hexLen = end - beg; // skip first '#' char // get byte encoding from string representation encoded = new byte[hexLen / 2]; for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) { encoded[i] = (byte) getByte(p); } return new String(chars, beg, hexLen); } /** * Begin to parse the string given in the constructor */ @Override public List parse() throws IOException { List list = new ArrayList(); String attValue; String attType = nextAT(); List atav = new ArrayList(); String charCopy = new String(chars); while (true) { if (pos == length) { // empty Attribute Value atav.add(new AttributeTypeAndValuePair(attType, "")); list.add(0, atav); return list; } switch (chars[pos]) { case '"': attValue = quotedAV(); if (attValue.length() > 0 && attValue.charAt(attValue.length() - 1) == ' ') { int p = pos - 1; while (p >= 0 && charCopy.charAt(p) == ' ') { p--; } if (p >= 3 && charCopy.substring(p - 3, p + 1).equals( "\\20\"")) { attValue = attValue.substring(0, attValue.length() - 1); } } atav.add(new AttributeTypeAndValuePair(attType, attValue)); break; case '#': attValue = hexAV(); atav.add(new AttributeTypeAndValuePair(attType, Rdn .unescapeValue(attValue))); break; case '+': // empty attribute value atav.add(new AttributeTypeAndValuePair(attType, "")); break; default: attValue = escapedAV(); if (attValue.length() > 0 && attValue.charAt(attValue.length() - 1) == ' ') { int p = pos - 1; while (p >= 0 && charCopy.charAt(p) == ' ') { p--; } if (p >= 2 && charCopy.substring(p - 2, p + 1).equals( "\\20")) { attValue = attValue.substring(0, attValue.length() - 1); } } atav.add(new AttributeTypeAndValuePair(attType, attValue)); } if (pos >= length) { list.add(0, atav); return list; } if (chars[pos] == ',' || chars[pos] == ';') { throw new IOException(Messages.getString("ldap.1F")); } pos++; attType = nextAT(); } } protected char getEscaped() throws IOException { pos++; if (pos == length) { throw new IOException(Messages.getString("ldap.1F")); } switch (chars[pos]) { case '"': return chars[pos]; case '\\': hasQE = true; return hexToUTF8(new String(chars, pos++, 2)); case ',': case '=': case '+': case '<': case '>': case '#': case ';': case ' ': // FIXME: escaping is allowed only for leading or trailing space // char return chars[pos]; default: /* * RFC doesn't explicitly say that escaped hex pair is * interpreted as UTF-8 char. It only contains an example of * such DN. */ return super.getUTF8(); } } /** * Converts an String in Hexadecimal to a character * * @param hex * the Hexadecimal string * @throws IOException * when an invalid Digit is passed or when the size is different * than 2 * @return the corresponding character */ public static Character hexToUTF8(String hex) throws IOException { try { return new Character((char) Integer.parseInt(hex, 16)); } catch (NumberFormatException e) { throw new IOException(Messages.getString("ldap.20")); //$NON-NLS-1$ } } }