| | |
| | | * Multiple-Whole-RDN: A double wildcard may be used to match one or more |
| | | * RDN components: |
| | | * uid=bjensen,**,dc=example,dc=com |
| | | * |
| | | */ |
| | | public class PatternDN |
| | | { |
| | |
| | | private List<PatternRDN[]> subAnyElements; |
| | | private PatternRDN[] subFinal; |
| | | |
| | | |
| | | /** |
| | | * When there is no initial sequence, this is used to distinguish between |
| | | * the case where we have a suffix pattern (zero or more RDN components |
| | |
| | | */ |
| | | private boolean isSuffix; |
| | | |
| | | |
| | | /** |
| | | * Create a DN pattern that does not include any Multiple-Whole-RDN wildcards. |
| | | * @param equality The sequence of RDN patterns making up the DN pattern. |
| | |
| | | this.equality = equality; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a DN pattern that includes Multiple-Whole-RDN wildcards. |
| | | * @param subInitial The sequence of RDN patterns appearing at the |
| | |
| | | this.subFinal = subFinal; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Determine whether a given DN matches this pattern. |
| | | * @param dn The DN to be matched. |
| | |
| | | } |
| | | pos++; |
| | | } |
| | | else |
| | | else if (!isSuffix) |
| | | { |
| | | if (!isSuffix) |
| | | { |
| | | pos++; |
| | | } |
| | | pos++; |
| | | } |
| | | |
| | | |
| | | if (subAnyElements != null && ! subAnyElements.isEmpty()) |
| | | { |
| | | for (PatternRDN[] element : subAnyElements) |
| | |
| | | boolean match = false; |
| | | for (; pos < end; pos++) |
| | | { |
| | | if (element[0].matchesRDN(dn.rdn(pos))) |
| | | if (element[0].matchesRDN(dn.rdn(pos)) |
| | | && subMatch(dn, pos, element, anyLength)) |
| | | { |
| | | if (subMatch(dn, pos, element, anyLength)) |
| | | { |
| | | match = true; |
| | | break; |
| | | } |
| | | match = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | if (subFinal != null) |
| | | { |
| | | int finalLength = subFinal.length; |
| | |
| | | return patternDN; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a new DN pattern matcher from a pattern string. |
| | | * @param dnString The DN pattern string. |
| | |
| | | return new PatternDN(); |
| | | } |
| | | |
| | | |
| | | // Iterate through the DN string. The first thing to do is to get |
| | | // rid of any leading spaces. |
| | | int pos = 0; |
| | |
| | | pos++; |
| | | } |
| | | |
| | | |
| | | // If we are at the end of the DN string, then that must mean |
| | | // that the attribute value was empty. This will probably never |
| | | // happen in a real-world environment, but technically isn't |
| | |
| | | break; |
| | | } |
| | | |
| | | |
| | | // Parse the value for this RDN component. |
| | | List<ByteString> parsedValue = new ArrayList<>(); |
| | | pos = parseValuePattern(dnString, pos, parsedValue); |
| | | |
| | | |
| | | // Create the new RDN with the provided information. |
| | | PatternRDN rdn = new PatternRDN(name, parsedValue, dnString); |
| | | |
| | | |
| | | // Skip over any spaces that might be after the attribute value. |
| | | while (pos < length && ((c = dnString.charAt(pos)) == ' ')) |
| | | { |
| | | pos++; |
| | | } |
| | | |
| | | |
| | | // Most likely, we will be at either the end of the RDN |
| | | // component or the end of the DN. If so, then handle that appropriately. |
| | | if (pos >= length) |
| | |
| | | throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, message); |
| | | } |
| | | |
| | | |
| | | // If we have gotten here, then this must be a multi-valued RDN. |
| | | // In that case, parse the remaining attribute/value pairs and |
| | | // add them to the RDN that we've already created. |
| | |
| | | pos++; |
| | | } |
| | | |
| | | |
| | | // Parse the attribute name from the DN string. |
| | | attributeName = new StringBuilder(); |
| | | pos = parseAttributePattern(dnString, pos, attributeName); |
| | | |
| | | |
| | | // Make sure that we're not at the end of the DN string |
| | | // because that would be invalid. |
| | | if (pos >= length) |
| | |
| | | ERR_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME.get(dnString, attributeName)); |
| | | } |
| | | |
| | | |
| | | name = attributeName.toString(); |
| | | |
| | | // Skip over any spaces between the attribute name and its |
| | |
| | | c = dnString.charAt(pos); |
| | | } |
| | | |
| | | |
| | | // The next character must be an equal sign. If it is not, |
| | | // then that's an error. |
| | | if (c == '=') |
| | |
| | | throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, message); |
| | | } |
| | | |
| | | |
| | | // Skip over any spaces after the equal sign. |
| | | while (pos < length && ((c = dnString.charAt(pos)) == ' ')) |
| | | { |
| | | pos++; |
| | | } |
| | | |
| | | |
| | | // If we are at the end of the DN string, then that must mean |
| | | // that the attribute value was empty. This will probably |
| | | // never happen in a real-world environment, but technically |
| | |
| | | break; |
| | | } |
| | | |
| | | |
| | | // Parse the value for this RDN component. |
| | | parsedValue = new ArrayList<>(); |
| | | pos = parseValuePattern(dnString, pos, parsedValue); |
| | | |
| | | |
| | | // Create the new RDN with the provided information. |
| | | rdn.addValue(name, parsedValue, dnString); |
| | | |
| | | |
| | | // Skip over any spaces that might be after the attribute value. |
| | | while (pos < length && ((c = dnString.charAt(pos)) == ' ')) |
| | | { |
| | | pos++; |
| | | } |
| | | |
| | | |
| | | // Most likely, we will be at either the end of the RDN |
| | | // component or the end of the DN. If so, then handle that appropriately. |
| | | if (pos >= length) |
| | |
| | | return new PatternDN(subInitial, subAnyElements, subFinal); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Parses an attribute name pattern from the provided DN pattern string |
| | | * starting at the specified location. |
| | |
| | | * valid attribute name pattern from the |
| | | * provided DN pattern string. |
| | | */ |
| | | static int parseAttributePattern(String dnString, int pos, |
| | | private static int parseAttributePattern(String dnString, int pos, |
| | | StringBuilder attributeName) |
| | | throws DirectoryException |
| | | { |
| | | int length = dnString.length(); |
| | | |
| | | |
| | | // Skip over any leading spaces. |
| | | if (pos < length) |
| | | { |
| | |
| | | endOfName = true; |
| | | break; |
| | | |
| | | |
| | | case '!': |
| | | case '"': |
| | | case '#': |
| | |
| | | // character immediately following it. |
| | | throw illegalCharacter(dnString, pos, c); |
| | | |
| | | |
| | | case '*': |
| | | // Wildcard character. |
| | | attributeName.append(c); |
| | |
| | | case '+': |
| | | throw illegalCharacter(dnString, pos, c); |
| | | |
| | | |
| | | case ',': |
| | | // This should denote the end of the attribute name. |
| | | endOfName = true; |
| | |
| | | attributeName.append(c); |
| | | break; |
| | | |
| | | |
| | | case '.': |
| | | // The period could be allowed if the attribute name is |
| | | // actually expressed as an OID. We'll accept it for now, |
| | |
| | | checkForOID = true; |
| | | break; |
| | | |
| | | |
| | | case '/': |
| | | throw illegalCharacter(dnString, pos, c); |
| | | |
| | | |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | |
| | | attributeName.append(c); |
| | | break; |
| | | |
| | | |
| | | case ':': |
| | | throw illegalCharacter(dnString, pos, c); |
| | | |
| | | |
| | | case ';': // NOTE: attribute options are not allowed in a DN. |
| | | // This should denote the end of the attribute name. |
| | | endOfName = true; |
| | |
| | | case '<': |
| | | throw illegalCharacter(dnString, pos, c); |
| | | |
| | | |
| | | case '=': |
| | | // This should denote the end of the attribute name. |
| | | endOfName = true; |
| | | break; |
| | | |
| | | |
| | | case '>': |
| | | case '?': |
| | | case '@': |
| | | throw illegalCharacter(dnString, pos, c); |
| | | |
| | | |
| | | case 'A': |
| | | case 'B': |
| | | case 'C': |
| | |
| | | attributeName.append(c); |
| | | break; |
| | | |
| | | |
| | | case '[': |
| | | case '\\': |
| | | case ']': |
| | | case '^': |
| | | throw illegalCharacter(dnString, pos, c); |
| | | |
| | | |
| | | case '_': |
| | | attributeName.append(c); |
| | | break; |
| | | |
| | | |
| | | case '`': |
| | | throw illegalCharacter(dnString, pos, c); |
| | | |
| | | |
| | | case 'a': |
| | | case 'b': |
| | | case 'c': |
| | |
| | | attributeName.append(c); |
| | | break; |
| | | |
| | | |
| | | default: |
| | | // This is not allowed in an attribute name or any character |
| | | // immediately following it. |
| | | throw illegalCharacter(dnString, pos, c); |
| | | } |
| | | |
| | | |
| | | if (endOfName) |
| | | { |
| | | break; |
| | |
| | | pos++; |
| | | } |
| | | |
| | | |
| | | // We should now have the full attribute name. However, we may |
| | | // still need to perform some validation, particularly if the |
| | | // name contains a period or starts with a digit. It must also |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | if (validOID && attributeName.charAt(nameLength-1) == '.') |
| | | { |
| | | validOID = false; |
| | |
| | | ERR_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR.get(dnString, c, pos)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Parses the attribute value pattern from the provided DN pattern |
| | | * string starting at the specified location. The value is split up |
| | |
| | | return pos; |
| | | } |
| | | |
| | | |
| | | // Look at the first character. If it is an octothorpe (#), then |
| | | // that means that the value should be a hex string. |
| | | char c = dnString.charAt(pos++); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | // The rest of the value must be a multiple of two hex |
| | | // characters. The end of the value may be designated by the |
| | | // end of the DN, a comma or semicolon, or a space. |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | // At this point, we should have a valid hex string. Convert it |
| | | // to a byte array and set that as the value of the provided |
| | | // octet string. |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | // If the first character is a quotation mark, then the value |
| | | // should continue until the corresponding closing quotation mark. |
| | | else if (c == '"') |
| | |
| | | return pos; |
| | | } |
| | | |
| | | |
| | | // Otherwise, use general parsing to find the end of the value. |
| | | else |
| | | { |
| | |
| | | valueString.append(c); |
| | | } |
| | | |
| | | |
| | | // Keep reading until we find an unescaped comma or plus sign or the end of the DN. |
| | | while (true) |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | // Strip off any unescaped spaces that may be at the end of the |
| | | // value. |
| | | if (pos > 2 && dnString.charAt(pos-1) == ' ' && |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | attributeValues.add(ByteString.valueOfUtf8(valueString)); |
| | | return pos; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Decodes a hexadecimal string from the provided |
| | | * <CODE>hexChars</CODE> buffer, converts it to a byte array, and |