You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
7.5 KiB
254 lines
7.5 KiB
/*
|
|
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package javax.naming.ldap;
|
|
|
|
import java.util.List;
|
|
import java.util.ArrayList;
|
|
|
|
import javax.naming.InvalidNameException;
|
|
|
|
/*
|
|
* RFC2253Parser implements a recursive descent parser for a single DN.
|
|
*/
|
|
final class Rfc2253Parser {
|
|
|
|
private final String name; // DN being parsed
|
|
private final char[] chars; // characters in LDAP name being parsed
|
|
private final int len; // length of "chars"
|
|
private int cur = 0; // index of first unconsumed char in "chars"
|
|
|
|
/*
|
|
* Given an LDAP DN in string form, returns a parser for it.
|
|
*/
|
|
Rfc2253Parser(String name) {
|
|
this.name = name;
|
|
len = name.length();
|
|
chars = name.toCharArray();
|
|
}
|
|
|
|
/*
|
|
* Parses the DN, returning a List of its RDNs.
|
|
*/
|
|
// public List<Rdn> getDN() throws InvalidNameException {
|
|
|
|
List<Rdn> parseDn() throws InvalidNameException {
|
|
cur = 0;
|
|
|
|
// ArrayList<Rdn> rdns =
|
|
// new ArrayList<Rdn>(len / 3 + 10); // leave room for growth
|
|
|
|
ArrayList<Rdn> rdns =
|
|
new ArrayList<>(len / 3 + 10); // leave room for growth
|
|
|
|
if (len == 0) {
|
|
return rdns;
|
|
}
|
|
|
|
rdns.add(doParse(new Rdn()));
|
|
while (cur < len) {
|
|
if (chars[cur] == ',' || chars[cur] == ';') {
|
|
++cur;
|
|
rdns.add(0, doParse(new Rdn()));
|
|
} else {
|
|
throw new InvalidNameException("Invalid name: " + name);
|
|
}
|
|
}
|
|
return rdns;
|
|
}
|
|
|
|
/*
|
|
* Parses the DN, if it is known to contain a single RDN.
|
|
*/
|
|
Rdn parseRdn() throws InvalidNameException {
|
|
return parseRdn(new Rdn());
|
|
}
|
|
|
|
/*
|
|
* Parses the DN, if it is known to contain a single RDN.
|
|
*/
|
|
Rdn parseRdn(Rdn rdn) throws InvalidNameException {
|
|
rdn = doParse(rdn);
|
|
if (cur < len) {
|
|
throw new InvalidNameException("Invalid RDN: " + name);
|
|
}
|
|
return rdn;
|
|
}
|
|
|
|
/*
|
|
* Parses the next RDN and returns it. Throws an exception if
|
|
* none is found. Leading and trailing whitespace is consumed.
|
|
*/
|
|
private Rdn doParse(Rdn rdn) throws InvalidNameException {
|
|
|
|
while (cur < len) {
|
|
consumeWhitespace();
|
|
String attrType = parseAttrType();
|
|
consumeWhitespace();
|
|
if (cur >= len || chars[cur] != '=') {
|
|
throw new InvalidNameException("Invalid name: " + name);
|
|
}
|
|
++cur; // consume '='
|
|
consumeWhitespace();
|
|
String value = parseAttrValue();
|
|
consumeWhitespace();
|
|
|
|
rdn.put(attrType, Rdn.unescapeValue(value));
|
|
if (cur >= len || chars[cur] != '+') {
|
|
break;
|
|
}
|
|
++cur; // consume '+'
|
|
}
|
|
rdn.sort();
|
|
return rdn;
|
|
}
|
|
|
|
/*
|
|
* Returns the attribute type that begins at the next unconsumed
|
|
* char. No leading whitespace is expected.
|
|
* This routine is more generous than RFC 2253. It accepts
|
|
* attribute types composed of any nonempty combination of Unicode
|
|
* letters, Unicode digits, '.', '-', and internal space characters.
|
|
*/
|
|
private String parseAttrType() throws InvalidNameException {
|
|
|
|
final int beg = cur;
|
|
while (cur < len) {
|
|
char c = chars[cur];
|
|
if (Character.isLetterOrDigit(c) ||
|
|
c == '.' ||
|
|
c == '-' ||
|
|
c == ' ') {
|
|
++cur;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
// Back out any trailing spaces.
|
|
while ((cur > beg) && (chars[cur - 1] == ' ')) {
|
|
--cur;
|
|
}
|
|
|
|
if (beg == cur) {
|
|
throw new InvalidNameException("Invalid name: " + name);
|
|
}
|
|
return new String(chars, beg, cur - beg);
|
|
}
|
|
|
|
/*
|
|
* Returns the attribute value that begins at the next unconsumed
|
|
* char. No leading whitespace is expected.
|
|
*/
|
|
private String parseAttrValue() throws InvalidNameException {
|
|
|
|
if (cur < len && chars[cur] == '#') {
|
|
return parseBinaryAttrValue();
|
|
} else if (cur < len && chars[cur] == '"') {
|
|
return parseQuotedAttrValue();
|
|
} else {
|
|
return parseStringAttrValue();
|
|
}
|
|
}
|
|
|
|
private String parseBinaryAttrValue() throws InvalidNameException {
|
|
final int beg = cur;
|
|
++cur; // consume '#'
|
|
while ((cur < len) &&
|
|
Character.isLetterOrDigit(chars[cur])) {
|
|
++cur;
|
|
}
|
|
return new String(chars, beg, cur - beg);
|
|
}
|
|
|
|
private String parseQuotedAttrValue() throws InvalidNameException {
|
|
|
|
final int beg = cur;
|
|
++cur; // consume '"'
|
|
|
|
while ((cur < len) && chars[cur] != '"') {
|
|
if (chars[cur] == '\\') {
|
|
++cur; // consume backslash, then what follows
|
|
}
|
|
++cur;
|
|
}
|
|
if (cur >= len) { // no closing quote
|
|
throw new InvalidNameException("Invalid name: " + name);
|
|
}
|
|
++cur; // consume closing quote
|
|
|
|
return new String(chars, beg, cur - beg);
|
|
}
|
|
|
|
private String parseStringAttrValue() throws InvalidNameException {
|
|
|
|
final int beg = cur;
|
|
int esc = -1; // index of the most recently escaped character
|
|
|
|
while ((cur < len) && !atTerminator()) {
|
|
if (chars[cur] == '\\') {
|
|
++cur; // consume backslash, then what follows
|
|
esc = cur;
|
|
}
|
|
++cur;
|
|
}
|
|
if (cur > len) { // 'twas backslash followed by nothing
|
|
throw new InvalidNameException("Invalid name: " + name);
|
|
}
|
|
|
|
// Trim off (unescaped) trailing whitespace.
|
|
int end;
|
|
for (end = cur; end > beg; end--) {
|
|
if (!isWhitespace(chars[end - 1]) || (esc == end - 1)) {
|
|
break;
|
|
}
|
|
}
|
|
return new String(chars, beg, end - beg);
|
|
}
|
|
|
|
private void consumeWhitespace() {
|
|
while ((cur < len) && isWhitespace(chars[cur])) {
|
|
++cur;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns true if next unconsumed character is one that terminates
|
|
* a string attribute value.
|
|
*/
|
|
private boolean atTerminator() {
|
|
return (cur < len &&
|
|
(chars[cur] == ',' ||
|
|
chars[cur] == ';' ||
|
|
chars[cur] == '+'));
|
|
}
|
|
|
|
/*
|
|
* Best guess as to what RFC 2253 means by "whitespace".
|
|
*/
|
|
private static boolean isWhitespace(char c) {
|
|
return (c == ' ' || c == '\r');
|
|
}
|
|
}
|