/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2008 Sun Microsystems, Inc.
* Portions Copyright 2012-2015 ForgeRock AS.
*/
/**
* This class defines a data structure that represents the components
* of an LDAP URL, including the scheme, host, port, base DN,
* attributes, scope, filter, and extensions. It has the ability to
* create an LDAP URL based on all of these individual components, as
* well as parsing them from their string representations.
*/
mayInstantiate=true,
mayExtend=false,
mayInvoke=true)
public final class LDAPURL
{
/** The default scheme that will be used if none is provided. */
/** The default port value that will be used if none is provided. */
/** The default base DN that will be used if none is provided. */
/** The default search scope that will be used if none is provided. */
/** The default search filter that will be used if none is provided. */
/** The host for this LDAP URL. */
/** The port number for this LDAP URL. */
private int port;
/** The base DN for this LDAP URL. */
/** The raw base DN for this LDAP URL. */
/** The search scope for this LDAP URL. */
/** The search filter for this LDAP URL. */
/** The raw filter for this LDAP URL. */
/** The set of attributes for this LDAP URL. */
/** The set of extensions for this LDAP URL. */
/** The scheme (i.e., protocol) for this LDAP URL. */
/**
* Creates a new LDAP URL with the provided information.
*
* @param scheme The scheme (i.e., protocol) for this LDAP
* URL.
* @param host The address for this LDAP URL.
* @param port The port number for this LDAP URL.
* @param rawBaseDN The raw base DN for this LDAP URL.
* @param attributes The set of requested attributes for this LDAP
* URL.
* @param scope The search scope for this LDAP URL.
* @param rawFilter The string representation of the search
* filter for this LDAP URL.
* @param extensions The set of extensions for this LDAP URL.
*/
{
{
this.scheme = "ldap";
}
else
{
}
{
this.rawBaseDN = "";
}
else
{
}
if (attributes == null)
{
this.attributes = new LinkedHashSet<>();
}
else
{
this.attributes = attributes;
}
{
this.scope = DEFAULT_SEARCH_SCOPE;
}
else
{
}
{
}
else
{
}
if (extensions == null)
{
this.extensions = new LinkedList<>();
}
else
{
this.extensions = extensions;
}
}
/**
* Creates a new LDAP URL with the provided information.
*
* @param scheme The scheme (i.e., protocol) for this LDAP
* URL.
* @param host The address for this LDAP URL.
* @param port The port number for this LDAP URL.
* @param baseDN The base DN for this LDAP URL.
* @param attributes The set of requested attributes for this LDAP
* URL.
* @param scope The search scope for this LDAP URL.
* @param filter The search filter for this LDAP URL.
* @param extensions The set of extensions for this LDAP URL.
*/
{
{
this.scheme = "ldap";
}
else
{
}
{
this.baseDN = DEFAULT_BASE_DN;
}
else
{
}
if (attributes == null)
{
this.attributes = new LinkedHashSet<>();
}
else
{
this.attributes = attributes;
}
{
this.scope = DEFAULT_SEARCH_SCOPE;
}
else
{
}
{
this.filter = DEFAULT_SEARCH_FILTER;
}
else
{
}
if (extensions == null)
{
this.extensions = new LinkedList<>();
}
else
{
this.extensions = extensions;
}
}
/**
* Decodes the provided string as an LDAP URL.
*
* @param url The URL string to be decoded.
* @param fullyDecode Indicates whether the URL should be fully
* decoded (e.g., parsing the base DN and
* search filter) or just leaving them in their
* string representations. The latter may be
* required for client-side use.
*
* @return The LDAP URL decoded from the provided string.
*
* @throws DirectoryException If a problem occurs while attempting
* to decode the provided string as an
* LDAP URL.
*/
throws DirectoryException
{
// Find the "://" component, which will separate the scheme from
// the host.
if (schemeEndPos < 0)
{
}
else if (schemeEndPos == 0)
{
}
else
{
}
// If the "://" was the end of the URL, then we're done.
{
}
// Look at the next character. If it's anything but a slash, then
// it should be part of the host and optional port.
int port = DEFAULT_PORT;
{
if (c == '/')
{
break;
}
pos++;
}
{
if (colonPos < 0)
{
}
else if (colonPos == 0)
{
}
{
}
else
{
try
{
}
catch (NumberFormatException e)
{
}
catch (IllegalArgumentException e)
{
}
}
}
// Move past the slash. If we're at or past the end of the
// string, then we're done.
pos++;
{
null);
}
else
{
}
// The next delimiter should be a question mark. If there isn't
// one, then the rest of the value must be the base DN.
if (pos < 0)
{
}
else
{
}
if (fullyDecode)
{
}
else
{
}
{
if (fullyDecode)
{
}
else
{
}
}
// Find the next question mark (or the end of the string if there
// aren't any more) and get the attribute list from it.
if (pos < 0)
{
}
else
{
}
while (tokenizer.hasMoreTokens())
{
}
{
if (fullyDecode)
{
}
else
{
null);
}
}
// Find the next question mark (or the end of the string if there
// aren't any more) and get the scope from it.
if (pos < 0)
{
}
else
{
}
{
}
{
}
{
}
{
}
{
}
else
{
throw new DirectoryException(
}
{
if (fullyDecode)
{
}
else
{
}
}
// Find the next question mark (or the end of the string if there
// aren't any more) and get the filter from it.
if (pos < 0)
{
}
else
{
}
if (fullyDecode)
{
{
}
else
{
}
{
if (fullyDecode)
{
}
else
{
}
}
}
else
{
}
// The rest of the string must be the set of extensions.
while (tokenizer.hasMoreTokens())
{
}
if (fullyDecode)
{
}
else
{
}
}
/**
* Converts the provided string to a form that has decoded "special"
* characters that have been encoded for use in an LDAP URL.
*
* @param s The string to be decoded.
*
* @return The decoded string.
*
* @throws DirectoryException If a problem occurs while attempting
* to decode the contents of the
* provided string.
*/
{
if (s == null)
{
return "";
}
byte[] stringBytes = getBytes(s);
byte[] decodedBytes = new byte[length];
int pos = 0;
for (int i=0; i < length; i++)
{
if (stringBytes[i] == '%')
{
// There must be at least two bytes left. If not, then that's
// a problem.
if (i+2 > length)
{
throw new DirectoryException(
}
byte b;
switch (stringBytes[++i])
{
case '0':
b = (byte) 0x00;
break;
case '1':
b = (byte) 0x10;
break;
case '2':
b = (byte) 0x20;
break;
case '3':
b = (byte) 0x30;
break;
case '4':
b = (byte) 0x40;
break;
case '5':
b = (byte) 0x50;
break;
case '6':
b = (byte) 0x60;
break;
case '7':
b = (byte) 0x70;
break;
case '8':
b = (byte) 0x80;
break;
case '9':
b = (byte) 0x90;
break;
case 'a':
case 'A':
b = (byte) 0xA0;
break;
case 'b':
case 'B':
b = (byte) 0xB0;
break;
case 'c':
case 'C':
b = (byte) 0xC0;
break;
case 'd':
case 'D':
b = (byte) 0xD0;
break;
case 'e':
case 'E':
b = (byte) 0xE0;
break;
case 'f':
case 'F':
b = (byte) 0xF0;
break;
default:
}
switch (stringBytes[++i])
{
case '0':
break;
case '1':
b |= 0x01;
break;
case '2':
b |= 0x02;
break;
case '3':
b |= 0x03;
break;
case '4':
b |= 0x04;
break;
case '5':
b |= 0x05;
break;
case '6':
b |= 0x06;
break;
case '7':
b |= 0x07;
break;
case '8':
b |= 0x08;
break;
case '9':
b |= 0x09;
break;
case 'a':
case 'A':
b |= 0x0A;
break;
case 'b':
case 'B':
b |= 0x0B;
break;
case 'c':
case 'C':
b |= 0x0C;
break;
case 'd':
case 'D':
b |= 0x0D;
break;
case 'e':
case 'E':
b |= 0x0E;
break;
case 'f':
case 'F':
b |= 0x0F;
break;
default:
}
decodedBytes[pos++] = b;
}
else
{
}
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
// This should never happen.
getExceptionMessage(e));
throw new DirectoryException(
}
}
/**
* Encodes the provided string portion for inclusion in an LDAP URL
* and appends it to the provided buffer.
*
* @param s The string portion to be encoded.
* @param isExtension Indicates whether the provided component is
* an extension and therefore needs to have
* commas encoded.
* @param buffer The buffer to which the information should
* be appended.
*/
{
if (s == null)
{
return;
}
for (int i=0; i < length; i++)
{
char c = s.charAt(i);
{
continue;
}
if (c == ',')
{
if (isExtension)
{
}
else
{
}
continue;
}
switch (c)
{
case '-':
case '.':
case '_':
case '~':
case ':':
case '/':
case '#':
case '[':
case ']':
case '@':
case '!':
case '$':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ';':
case '=':
break;
default:
break;
}
}
}
/**
* Appends a percent-encoded representation of the provided
* character to the given buffer.
*
* @param c The character to add to the buffer.
* @param buffer The buffer to which the percent-encoded
* representation should be written.
*/
{
if ((c & (byte) 0xFF) == c)
{
// It's a single byte.
}
else
{
// It requires two bytes, and each should be prefixed by a
// percent sign.
byte b2 = (byte) (c & 0xFF);
}
}
/**
* Retrieves the scheme for this LDAP URL.
*
* @return The scheme for this LDAP URL.
*/
{
return scheme;
}
/**
* Specifies the scheme for this LDAP URL.
*
* @param scheme The scheme for this LDAP URL.
*/
{
{
this.scheme = DEFAULT_SCHEME;
}
else
{
}
}
/**
* Retrieves the host for this LDAP URL.
*
* @return The host for this LDAP URL, or <CODE>null</CODE> if none
* was provided.
*/
{
return host;
}
/**
* Specifies the host for this LDAP URL.
*
* @param host The host for this LDAP URL.
*/
{
}
/**
* Retrieves the port for this LDAP URL.
*
* @return The port for this LDAP URL.
*/
public int getPort()
{
return port;
}
/**
* Specifies the port for this LDAP URL.
*
* @param port The port for this LDAP URL.
*/
{
}
{
{
return port;
}
return DEFAULT_PORT;
}
/**
* Retrieve the raw, unprocessed base DN for this LDAP URL.
*
* @return The raw, unprocessed base DN for this LDAP URL, or
* <CODE>null</CODE> if none was given (in which case a
* default of the null DN "" should be assumed).
*/
{
return rawBaseDN;
}
/**
* Specifies the raw, unprocessed base DN for this LDAP URL.
*
* @param rawBaseDN The raw, unprocessed base DN for this LDAP
* URL.
*/
{
}
/**
* Retrieves the processed DN for this LDAP URL.
*
* @return The processed DN for this LDAP URL.
*
* @throws DirectoryException If the raw base DN cannot be decoded
* as a valid DN.
*/
throws DirectoryException
{
{
{
return DEFAULT_BASE_DN;
}
}
return baseDN;
}
/**
* Specifies the base DN for this LDAP URL.
*
* @param baseDN The base DN for this LDAP URL.
*/
{
{
}
else
{
}
}
/**
* Retrieves the set of attributes for this LDAP URL. The contents
* of the returned set may be altered by the caller.
*
* @return The set of attributes for this LDAP URL.
*/
{
return attributes;
}
/**
* Retrieves the search scope for this LDAP URL.
*
* @return The search scope for this LDAP URL, or <CODE>null</CODE>
* if none was given (in which case the base-level scope
* should be assumed).
*/
{
return scope;
}
/**
* Specifies the search scope for this LDAP URL.
*
* @param scope The search scope for this LDAP URL.
*/
{
{
this.scope = DEFAULT_SEARCH_SCOPE;
}
else
{
}
}
/**
* Retrieves the raw, unprocessed search filter for this LDAP URL.
*
* @return The raw, unprocessed search filter for this LDAP URL, or
* <CODE>null</CODE> if none was given (in which case a
* default filter of "(objectClass=*)" should be assumed).
*/
{
return rawFilter;
}
/**
* Specifies the raw, unprocessed search filter for this LDAP URL.
*
* @param rawFilter The raw, unprocessed search filter for this
* LDAP URL.
*/
{
}
/**
* Retrieves the processed search filter for this LDAP URL.
*
* @return The processed search filter for this LDAP URL.
*
* @throws DirectoryException If a problem occurs while attempting
* to decode the raw filter.
*/
throws DirectoryException
{
{
{
}
else
{
}
}
return filter;
}
/**
* Specifies the search filter for this LDAP URL.
*
* @param filter The search filter for this LDAP URL.
*/
{
{
}
else
{
}
}
/**
* Retrieves the set of extensions for this LDAP URL. The contents
* of the returned list may be altered by the caller.
*
* @return The set of extensions for this LDAP URL.
*/
{
return extensions;
}
/**
* Indicates whether the provided entry matches the criteria defined
* in this LDAP URL.
*
* @param entry The entry for which to make the determination.
*
* @return {@code true} if the provided entry does match the
* criteria specified in this LDAP URL, or {@code false} if
* it does not.
*
* @throws DirectoryException If a problem occurs while attempting
* to make the determination.
*/
throws DirectoryException
{
{
}
}
/**
* Indicates whether the provided object is equal to this LDAP URL.
*
* @param o The object for which to make the determination.
*
* @return <CODE>true</CODE> if the object is equal to this LDAP
* URL, or <CODE>false</CODE> if not.
*/
{
if (o == this)
{
return true;
}
if (!(o instanceof LDAPURL))
{
return false;
}
&& hostEquals(url)
&& baseDnsEqual(url)
&& filtersEqual(url)
}
{
{
}
}
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
}
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
}
{
{
return false;
}
{
{
return false;
}
}
return true;
}
{
{
{
return true;
}
}
return false;
}
{
{
return false;
}
{
{
return false;
}
}
return true;
}
/**
* Retrieves the hash code for this LDAP URL.
*
* @return The hash code for this LDAP URL.
*/
public int hashCode()
{
int hashCode = 0;
{
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
{
}
}
{
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
{
}
}
{
}
return hashCode;
}
/**
* Retrieves a string representation of this LDAP URL.
*
* @return A string representation of this LDAP URL.
*/
{
}
/**
* Appends a string representation of this LDAP URL to the provided
* buffer.
*
* @param buffer The buffer to which the information is to be
* appended.
* @param baseOnly Indicates whether the resulting URL string
* should only include the portion up to the base
* DN, omitting the attributes, scope, filter, and
* extensions.
*/
{
{
}
if (baseOnly)
{
// If there are extensions, then we need to include them.
// Technically, we only have to include critical extensions, but
// we'll use all of them.
if (! extensions.isEmpty())
{
{
}
}
return;
}
if (! attributes.isEmpty())
{
{
}
}
{
case BASE_OBJECT:
break;
case SINGLE_LEVEL:
break;
case WHOLE_SUBTREE:
break;
case SUBORDINATES:
break;
}
if (! extensions.isEmpty())
{
{
}
}
}
}