001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.jndi.internal.parser;
019:
020: import java.io.IOException;
021: import java.util.ArrayList;
022: import java.util.List;
023:
024: import javax.naming.ldap.Rdn;
025:
026: import org.apache.harmony.jndi.internal.nls.Messages;
027: import org.apache.harmony.security.x509.DNParser;
028:
029: /**
030: * A relative distinguished name parser that does less checking than DNParser
031: */
032: public class RelaxedDnParser extends DNParser {
033:
034: private static final char[] specialChars = new char[] { '!', '$',
035: '%', '&', '\'', '(', ')', '*', '/', ':', '<', '=', '>',
036: '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~',
037: '\"', '#', '+', ',', '\\', ';' };
038:
039: /**
040: * Constructor
041: *
042: * @param dn
043: * the String to parse
044: * @throws IOException
045: */
046: public RelaxedDnParser(String dn) throws IOException {
047: super (dn);
048: }
049:
050: @Override
051: protected String nextAT() throws IOException {
052: int equalIndex = pos;
053: try {
054: while (chars[equalIndex] != '=') {
055: equalIndex++; // points to the equals on the current attribute
056: // type pair
057: }
058: } catch (ArrayIndexOutOfBoundsException e) {
059: throw new IOException(Messages.getString("ldap.1C")); //$NON-NLS-1$
060: }
061:
062: // Check for quotations and special chars on the type
063: for (int i = pos; i < equalIndex; i++) {
064: if (chars[i] == '\\') {
065: throw new IOException(Messages.getString("ldap.1D")); //$NON-NLS-1$
066: }
067: for (int j = 0; j < specialChars.length; j++) {
068: if (chars[i] == specialChars[j]) {
069: throw new IOException(Messages.getString("ldap.1E")); //$NON-NLS-1$
070: }
071: }
072: }
073: return super .nextAT();
074: }
075:
076: protected String hexAV() throws IOException {
077: beg = pos; // store '#' position
078: pos++;
079:
080: while (true) {
081: // check for end of attribute value
082: // looks for space and component separators
083: if (pos == length || chars[pos] == '+' || chars[pos] == ','
084: || chars[pos] == ';') {
085: end = pos;
086: break;
087: }
088:
089: if (chars[pos] >= 'A' && chars[pos] <= 'F') {
090: chars[pos] += 32; // to low case
091: }
092: pos++;
093: }
094:
095: while (end > beg + 1 && chars[end - 1] == ' ') {
096: end--;
097: }
098:
099: // verify length of hex string
100: // encoded byte array must be even number
101: int hexLen = end - beg; // skip first '#' char
102: // get byte encoding from string representation
103: encoded = new byte[hexLen / 2];
104: for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) {
105: encoded[i] = (byte) getByte(p);
106: }
107: return new String(chars, beg, hexLen);
108:
109: }
110:
111: /**
112: * Begin to parse the string given in the constructor
113: */
114: @Override
115: public List parse() throws IOException {
116: List list = new ArrayList();
117:
118: String attValue;
119: String attType = nextAT();
120: List atav = new ArrayList();
121: String charCopy = new String(chars);
122: while (true) {
123: if (pos == length) {
124: // empty Attribute Value
125: atav.add(new AttributeTypeAndValuePair(attType, ""));
126: list.add(0, atav);
127: return list;
128: }
129:
130: switch (chars[pos]) {
131: case '"':
132: attValue = quotedAV();
133: if (attValue.length() > 0
134: && attValue.charAt(attValue.length() - 1) == ' ') {
135: int p = pos - 1;
136: while (p >= 0 && charCopy.charAt(p) == ' ') {
137: p--;
138: }
139: if (p >= 3
140: && charCopy.substring(p - 3, p + 1).equals(
141: "\\20\"")) {
142: attValue = attValue.substring(0, attValue
143: .length() - 1);
144: }
145: }
146: atav.add(new AttributeTypeAndValuePair(attType,
147: attValue));
148: break;
149: case '#':
150: attValue = hexAV();
151: atav.add(new AttributeTypeAndValuePair(attType, Rdn
152: .unescapeValue(attValue)));
153: break;
154: case '+':
155: // empty attribute value
156: atav.add(new AttributeTypeAndValuePair(attType, ""));
157: break;
158: default:
159: attValue = escapedAV();
160: if (attValue.length() > 0
161: && attValue.charAt(attValue.length() - 1) == ' ') {
162: int p = pos - 1;
163: while (p >= 0 && charCopy.charAt(p) == ' ') {
164: p--;
165: }
166: if (p >= 2
167: && charCopy.substring(p - 2, p + 1).equals(
168: "\\20")) {
169: attValue = attValue.substring(0, attValue
170: .length() - 1);
171: }
172: }
173: atav.add(new AttributeTypeAndValuePair(attType,
174: attValue));
175: }
176:
177: if (pos >= length) {
178: list.add(0, atav);
179: return list;
180: }
181:
182: if (chars[pos] == ',' || chars[pos] == ';') {
183: throw new IOException(Messages.getString("ldap.1F"));
184: }
185:
186: pos++;
187: attType = nextAT();
188: }
189: }
190:
191: protected char getEscaped() throws IOException {
192: pos++;
193: if (pos == length) {
194: throw new IOException(Messages.getString("ldap.1F"));
195: }
196:
197: switch (chars[pos]) {
198: case '"':
199: return chars[pos];
200: case '\\':
201: hasQE = true;
202: return hexToUTF8(new String(chars, pos++, 2));
203: case ',':
204: case '=':
205: case '+':
206: case '<':
207: case '>':
208: case '#':
209: case ';':
210: case ' ':
211: // FIXME: escaping is allowed only for leading or trailing space
212: // char
213: return chars[pos];
214: default:
215: /*
216: * RFC doesn't explicitly say that escaped hex pair is
217: * interpreted as UTF-8 char. It only contains an example of
218: * such DN.
219: */
220: return super .getUTF8();
221: }
222: }
223:
224: /**
225: * Converts an String in Hexadecimal to a character
226: *
227: * @param hex
228: * the Hexadecimal string
229: * @throws IOException
230: * when an invalid Digit is passed or when the size is different
231: * than 2
232: * @return the corresponding character
233: */
234: public static Character hexToUTF8(String hex) throws IOException {
235: try {
236: return new Character((char) Integer.parseInt(hex, 16));
237: } catch (NumberFormatException e) {
238: throw new IOException(Messages.getString("ldap.20")); //$NON-NLS-1$
239: }
240: }
241:
242: }
|