/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* <code>LdapName</code> implements compound names for LDAP v3 as
* specified by RFC 2253.
*<p>
* RFC 2253 has a few ambiguities and outright inconsistencies. These
* are resolved as follows:
* <ul>
* <li> RFC 2253 leaves the term "whitespace" undefined. The
* definition of "optional-space" given in RFC 1779 is used in
* its place: either a space character or a carriage return ("\r").
* <li> Whitespace is allowed on either side of ',', ';', '=', and '+'.
* Such whitespace is accepted but not generated by this code,
* and is ignored when comparing names.
* <li> AttributeValue strings containing '=' or non-leading '#'
* characters (unescaped) are accepted.
* </ul>
*<p>
* String names passed to <code>LdapName</code> or returned by it
* use the full 16-bit Unicode character set. They may also contain
* characters encoded into UTF-8 with each octet represented by a
* three-character substring such as "\\B4".
* They may not, however, contain characters encoded into UTF-8 with
* each octet represented by a single character in the string: the
* meaning would be ambiguous.
*<p>
* <code>LdapName</code> will properly parse all valid names, but
* does not attempt to detect all possible violations when parsing
* invalid names. It's "generous".
*<p>
* When names are tested for equality, attribute types and binary
* values are case-insensitive, and string values are by default
* case-insensitive.
* String values with different but equivalent usage of quoting,
* escaping, or UTF8-hex-encoding are considered equal. The order of
* components in multi-valued RDNs (such as "ou=Sales+cn=Bob") is not
* significant.
*
* @author Scott Seligman
*/
private transient boolean valuesCaseSensitive = false;
/**
* Constructs an LDAP name from the given DN.
*
* @param name An LDAP DN. To JNDI, a compound name.
*
* @throws InvalidNameException if a syntax violation is detected.
*/
parse();
}
/*
* Constructs an LDAP name given its parsed components and, optionally
* (if "name" is not null), the unparsed DN.
*/
}
/*
* Constructs an LDAP name given its parsed components (the elements
* of "rdns" in the range [beg,end)) and, optionally
* (if "name" is not null), the unparsed DN.
*/
}
}
}
return unparsed;
}
}
}
return unparsed;
}
}
if ((obj == this) || // check possible shortcuts
return 0;
}
// Compare RDNs one by one, lexicographically.
for (int i = 0 ; i < minSize; i++) {
// Compare a single pair of RDNs.
if (diff != 0) {
return diff;
}
}
}
public int hashCode() {
// Sum up the hash codes of the components.
int hash = 0;
// For each RDN...
}
return hash;
}
public int size() {
}
public boolean isEmpty() {
}
return new Enumeration () {
public boolean hasMoreElements() {
return enum_.hasMoreElements();
}
public Object nextElement() {
}
};
}
}
}
}
}
}
/**
* Controls whether string-values are treated as case-sensitive
* when the string values within names are compared. The default
* behavior is case-insensitive comparison.
*/
toString();
try {
parse();
} catch (InvalidNameException e) {
// shouldn't happen
}
}
/*
* Helper method for startsWith() and endsWith().
* Returns true if components [beg,end) match the components of "n".
* If "n" is not an LdapName, each of its components is parsed as
* the string form of an RDN.
* The following must hold: end - beg == n.size().
*/
if (n instanceof LdapName) {
} else {
try {
} catch (InvalidNameException e) {
return false;
}
}
return false;
}
}
return true;
}
}
/*
* If "suffix" is not an LdapName, each of its components is parsed as
* the string form of an RDN.
*/
}
} else {
while (comps.hasMoreElements()) {
}
}
return this;
}
}
return this;
}
return comp;
}
}
/*
* Best guess as to what RFC 2253 means by "whitespace".
*/
private static boolean isWhitespace(char c) {
return (c == ' ' || c == '\r');
}
/**
* Given the value of an attribute, returns a string suitable
* for inclusion in a DN. If the value is a string, this is
* accomplished by using backslash (\) to escape the following
* characters:
*<ul>
*<li>leading and trailing whitespace
*<li><pre>, = + < > # ; " \</pre>
*</ul>
* If the value is a byte array, it is converted to hex
* notation (such as "#CEB1DF80").
*/
}
/**
* Given an attribute value formated according to RFC 2253,
* returns the unformated value. Returns a string value as
* a string, and a binary value as a byte array.
*/
}
/**
* Serializes only the unparsed DN, for compactness and to avoid
* any implementation dependency.
*
* @serialdata The DN string and a boolean indicating whether
* the values are case sensitive.
*/
s.writeObject(toString());
}
valuesCaseSensitive = s.readBoolean();
try {
parse();
} catch (InvalidNameException e) {
// shouldn't happen
"Invalid name: " + unparsed);
}
}
/*
* DnParser implements a recursive descent parser for a single DN.
*/
static class DnParser {
private boolean valuesCaseSensitive;
/*
* Given an LDAP DN in string form, returns a parser for it.
*/
throws InvalidNameException {
}
/*
* Parses the DN, returning a Vector of its RDNs.
*/
cur = 0;
if (len == 0) {
return rdns;
}
++cur;
} else {
}
}
return rdns;
}
/*
* Parses the DN, if it is known to contain a single RDN.
*/
}
return rdn;
}
/*
* Parses the next RDN and returns it. Throws an exception if
* none is found. Leading and trailing whitespace is consumed.
*/
}
++cur; // consume '='
break;
}
++cur; // consume '+'
}
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.
*/
if (Character.isLetterOrDigit(c) ||
c == '.' ||
c == '-' ||
c == ' ') {
++cur;
} else {
break;
}
}
// Back out any trailing spaces.
--cur;
}
}
}
/*
* Returns the attribute value that begins at the next unconsumed
* char. No leading whitespace is expected.
*/
return parseBinaryAttrValue();
return parseQuotedAttrValue();
} else {
return parseStringAttrValue();
}
}
++cur; // consume '#'
++cur;
}
}
++cur; // consume '"'
++cur; // consume backslash, then what follows
}
++cur;
}
}
++cur ; // consume closing quote
}
++cur; // consume backslash, then what follows
}
++cur;
}
}
// Trim off (unescaped) trailing whitespace.
int end;
break;
}
}
}
private void consumeWhitespace() {
++cur;
}
}
/*
* Returns true if next unconsumed character is one that terminates
* a string attribute value.
*/
private boolean atTerminator() {
}
}
/*
* Class Rdn represents a set of TypeAndValue.
*/
static class Rdn {
/*
* A vector of the TypeAndValue elements of this Rdn.
* It is sorted to facilitate set operations.
*/
// Set i to index of first element greater than tv, or to
// tvs.size() if there is none.
int i;
if (diff == 0) {
return; // tv is a duplicate: ignore it
} else if (diff < 0) {
break;
}
}
}
if (i > 0) {
}
}
}
}
// Compare TypeAndValue components one by one, lexicographically.
for (int i = 0; i < minSize; i++) {
if (diff != 0) {
return diff;
}
}
}
public int hashCode() {
// Sum up the hash codes of the components.
int hash = 0;
}
return hash;
}
} else {
}
}
return attrs;
}
}
/*
* Class TypeAndValue represents an attribute type and its
* corresponding value.
*/
static class TypeAndValue {
private final boolean binary;
private final boolean valueCaseSensitive;
// If non-null, a canonical represention of the value suitable
// for comparison using String.compareTo().
this.valueCaseSensitive = valueCaseSensitive;
}
}
// NB: Any change here affecting equality must be
// reflected in hashCode().
if (diff != 0) {
return diff;
}
return 0;
}
}
// NB: Any change here must be reflected in hashCode().
if (!(obj instanceof TypeAndValue)) {
return false;
}
}
public int hashCode() {
// If two objects are equal, their hash codes must match.
getValueComparable().hashCode());
}
/*
* Returns the type.
*/
return type;
}
/*
* Returns the unescaped value.
*/
return unescapeValue(value);
}
/*
* Returns a canonical representation of "value" suitable for
* comparison using String.compareTo(). If "value" is a string,
* it is returned with escapes and quotes stripped away, and
* hex-encoded UTF-8 converted to 16-bit Unicode chars.
* If value's case is to be ignored, it is returned in uppercase.
* If "value" is binary, it is returned in uppercase but
* otherwise unmodified.
*/
if (comparable != null) {
return comparable; // return cached result
}
// cache result
if (binary) {
} else {
if (!valueCaseSensitive) {
}
}
return comparable;
}
/*
* Given the value of an attribute, returns a string suitable
* for inclusion in a DN.
*/
return (val instanceof byte[])
? escapeBinaryValue((byte[])val)
}
/*
* Given the value of a string-valued attribute, returns a
* string suitable for inclusion in a DN. This is accomplished by
* using backslash (\) to escape the following characters:
* leading and trailing whitespace
* , = + < > # ; " \
*/
// Find leading and trailing whitespace.
int lead; // index of first char that is not leading whitespace
break;
}
}
int trail; // index of last char that is not trailing whitespace
break;
}
}
char c = chars[i];
}
}
}
/*
* Given the value of a binary attribute, returns a string
* suitable for inclusion in a DN (such as "#CEB1DF80").
*/
byte b = val[i];
}
}
/*
* Given an attribute value formated according to RFC 2253,
* returns the unformated value. Escapes and quotes are
* stripped away, and hex-encoded UTF-8 is converted to 16-bit
* Unicode chars. Returns a string value as a String, and a
* binary value as a byte array.
*/
int beg = 0;
// Trim off leading and trailing whitespace.
++beg;
}
--end;
}
// Add back the trailing whitespace with a preceeding '\'
// (escaped or unescaped) that was taken off in the above
// loop. Whether or not to retain this whitespace is
// decided below.
end++;
}
return "";
}
// Value is binary (eg: "#CEB1DF80").
}
// Trim off quotes.
++beg;
--end;
}
++i; // skip backslash
esc = i;
} else {
// Convert hex-encoded UTF-8 to 16-bit chars.
try {
// shouldn't happen
}
} else {
throw new IllegalArgumentException(
"Not a valid attribute string value:" +
val +", improper usage of backslash");
}
}
} else {
}
}
// Get rid of the unescaped trailing whitespace with the
// preceeding '\' character that was previously added back.
}
}
/*
* Given an array of chars (with starting and ending indexes into it)
* representing bytes encoded as hex-pairs (such as "CEB1DF80"),
* returns a byte array containing the decoded bytes.
*/
break;
}
beg += 2;
}
throw new IllegalArgumentException(
}
return bytes;
}
/*
* Given an array of chars (with starting and ending indexes into it),
* finds the largest prefix consisting of hex-encoded UTF-8 octets,
* and returns a byte array containing the corresponding UTF-8 octets.
*
* Hex-encoded UTF-8 octets look like this:
* \03\B1\DF\80
*/
break;
}
}
return utf8;
} else {
return res;
}
}
}
/*
* For testing.
*/
/*
public static void main(String[] args) {
try {
if (args.length == 1) { // parse and print components
LdapName n = new LdapName(args[0]);
Enumeration rdns = n.rdns.elements();
while (rdns.hasMoreElements()) {
Rdn rdn = (Rdn)rdns.nextElement();
for (int i = 0; i < rdn.tvs.size(); i++) {
System.out.print("[" + rdn.tvs.elementAt(i) + "]");
}
System.out.println();
}
} else { // compare two names
LdapName n1 = new LdapName(args[0]);
LdapName n2 = new LdapName(args[1]);
n1.unparsed = null;
n2.unparsed = null;
boolean eq = n1.equals(n2);
System.out.println("[" + n1 + (eq ? "] == [" : "] != [")
+ n2 + "]");
}
} catch (Exception e) {
e.printStackTrace();
}
}
*/
}