/* * 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.Iterator; import java.util.List; import javax.naming.InvalidNameException; import org.apache.harmony.jndi.internal.nls.Messages; /** * Class used to parse RDN components of a Distinguished name */ public class LdapRdnParser implements LdapParser { private String name = null; LdapTypeAndValueList list = new LdapTypeAndValueList(); private List<AttributeTypeAndValuePair> attrList; private List<AttributeTypeAndValuePair> listAll; private RelaxedDnParser parser = null; /** * Constructor */ public LdapRdnParser(String name) { if (name.endsWith("+") && name.length() > 1 && name.charAt(name.length()-2) != '\\') { this.name = name.substring(0, name.lastIndexOf('+')); } else { this.name = name; } } /** * Used to get the list of Type/Values */ public List getList() throws InvalidNameException { try { checkTypeRestrictions(name); parser = new RelaxedDnParser(name); listAll = parser.parse(); } catch (IOException e) { throw (InvalidNameException) (new InvalidNameException(Messages .getString("ldap.17") + name).initCause(e)); } attrList = (List) listAll.get(0); for (Iterator<AttributeTypeAndValuePair> iter = attrList.iterator(); iter .hasNext();) { AttributeTypeAndValuePair element = iter.next(); list.put(element.getType(), element.getValue()); } return list.toAttributeList(); } private void checkTypeRestrictions(String rdn) throws InvalidNameException { int in = rdn.indexOf("="); try { if (rdn.substring(0, in).length() == 0) { throw new InvalidNameException(Messages.getString("ldap.18") + rdn); } } catch (StringIndexOutOfBoundsException e) { throw new InvalidNameException(Messages.getString("ldap.17") + rdn); } } /** * returns a string escaped according to the rules specified in RFC 2253. * * @param obj * the String or byteArray to escape * @return The escaped value * @throws ClassCastException * when obj is not an instance of String or ByteArray */ public static String escapeValue(Object obj) { if (obj instanceof String) { String val = String.valueOf(obj); return getEscaped(val.toCharArray()); } else if (obj instanceof byte[]) { return getHexValues((byte[]) obj); } else { throw new ClassCastException(Messages.getString("ldap.19")); } } private static String getEscaped(char[] chars) { StringBuilder sb = new StringBuilder(); int leftSpaceCnt = 0, rightSpaceCnt = 0; int pos = chars.length - 1; while (pos >= 0 && chars[pos] == ' ') { rightSpaceCnt++; pos--; } for (int i = 0; i <= pos && chars[i] == ' '; i++) { leftSpaceCnt++; sb.append("\\ "); } for (int i = leftSpaceCnt; i < chars.length - rightSpaceCnt; i++) { if (isSpecialChar(chars, i)) { sb.append('\\'); } sb.append(new Character(chars[i])); } for (int i = 0; i < rightSpaceCnt; i++) { sb.append("\\ "); } return sb.toString(); } private static String getHexValues(byte[] byteArray) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < byteArray.length; i++) { sb.append(Integer.toHexString(byteArray[i] >> 4 & 0x0F)); sb.append(Integer.toHexString(byteArray[i] & 0x0F)); } return '#' + sb.toString(); } /** * Given an attribute value string formated according to the rules specified * in RFC 2253, returns the unescaped value. * * @param val * String to unescape * @return the unescaped value (String or ByteArray) */ public static Object unescapeValue(String val) { if (val.startsWith("#") && numeralCounter(val)) { return getByteFromHexString(val); } else if (val.startsWith("#")) { return getByteFromHexString(val); } else { String tmpVal = val.trim(); if (tmpVal.length() > 0 && tmpVal.charAt(0) == '\"' && tmpVal.charAt(tmpVal.length() - 1) == '\"') { if (tmpVal.length() == 1) { val = ""; } else { val = tmpVal.substring(1, tmpVal.length() - 1); } } char[] chars; int pos = val.length() - 1; boolean trailingSpace = false; while (pos >= 0 && val.charAt(pos) == ' ') { trailingSpace = true; pos--; } if (pos >= 0 && val.charAt(pos) == '\\' && trailingSpace) { chars = (new String(val.trim()) + " ").toCharArray(); } else { chars = new String(val.trim()).toCharArray(); } return getUnEscapedValues(chars); } } private static boolean numeralCounter(String val) { int counter = 0; for (int i = 0; i < val.length(); i++) { if (val.charAt(i) == '#') { counter++; } } return counter == 1; } private static String getUnEscapedValues(char[] chars) { StringBuilder sb = new StringBuilder(); boolean trailing20h = false; for (int i = 0; i < chars.length; i++) { trailing20h = false; if (chars[i] != '\\') { sb.append(chars[i]); } else { try { if (chars[i + 1] == ' ') { continue; } if (chars[i + 1] == '\\') { sb.append('\\'); i = i + 1; continue; } if (!isSpecialChar(chars, i + 1) && !isSpecialChar(chars, i + 2)) { try { sb.append(RelaxedDnParser.hexToUTF8(new String( chars, i + 1, 2))); if (sb.charAt(sb.length() - 1) == ' ') { trailing20h = true; } i = i + 2; } catch (IOException e) { throw new IllegalArgumentException(Messages .getString("ldap.1A")); } } } catch (ArrayIndexOutOfBoundsException e) { sb.append(chars[i]); } } } if (trailing20h && sb.length() > 0 && sb.charAt(sb.length() - 1) == ' ') { sb = sb.deleteCharAt(sb.length() - 1); } return sb.toString(); } private static byte[] getByteFromHexString(String val) { String str = val.substring(val.indexOf("#") + 1); if (str.length() % 2 != 0) { throw new IllegalArgumentException(Messages.getString("ldap.1A")); } byte[] ret = null; try { ret = new byte[str.length() / 2]; for (int i = 0; i < ret.length; i++) { ret[i] = (byte) Integer.parseInt(str .substring(2 * i, 2 * i + 2), 16); } } catch (NumberFormatException e) { throw new IllegalArgumentException(Messages.getString("ldap.17") + val); } return ret; } private static boolean isSpecialChar(char[] chars, int index) { switch (chars[index]) { case '"': case '\\': case ',': case '=': case '+': case '<': case '>': case '#': case ';': return true; default: return false; } } }