/* * 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 * or http://forgerock.org/license/CDDLv1.0.html. * 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 2009 Sun Microsystems, Inc. */ package com.forgerock.opendj.ldap; import static com.forgerock.opendj.ldap.LDAPConstants.*; import java.io.IOException; import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.forgerock.opendj.asn1.ASN1Reader; import org.forgerock.opendj.asn1.ASN1Writer; import org.forgerock.opendj.ldap.ByteSequence; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.DecodeOptions; import org.forgerock.opendj.ldap.Filter; import org.forgerock.opendj.ldap.FilterVisitor; import org.forgerock.opendj.ldap.responses.SearchResultEntry; /** * Common LDAP utility methods which may be used when implementing new controls * and extension. */ public final class LDAPUtils { private static final FilterVisitor ASN1_ENCODER = new FilterVisitor() { public IOException visitAndFilter(final ASN1Writer writer, final List subFilters) { try { writer.writeStartSequence(TYPE_FILTER_AND); for (final Filter subFilter : subFilters) { final IOException e = subFilter.accept(this, writer); if (e != null) { return e; } } writer.writeEndSequence(); return null; } catch (final IOException e) { return e; } } public IOException visitApproxMatchFilter(final ASN1Writer writer, final String attributeDescription, final ByteString assertionValue) { try { writer.writeStartSequence(TYPE_FILTER_APPROXIMATE); writer.writeOctetString(attributeDescription); writer.writeOctetString(assertionValue); writer.writeEndSequence(); return null; } catch (final IOException e) { return e; } } public IOException visitEqualityMatchFilter(final ASN1Writer writer, final String attributeDescription, final ByteString assertionValue) { try { writer.writeStartSequence(TYPE_FILTER_EQUALITY); writer.writeOctetString(attributeDescription); writer.writeOctetString(assertionValue); writer.writeEndSequence(); return null; } catch (final IOException e) { return e; } } public IOException visitExtensibleMatchFilter(final ASN1Writer writer, final String matchingRule, final String attributeDescription, final ByteString assertionValue, final boolean dnAttributes) { try { writer.writeStartSequence(TYPE_FILTER_EXTENSIBLE_MATCH); if (matchingRule != null) { writer.writeOctetString(TYPE_MATCHING_RULE_ID, matchingRule); } if (attributeDescription != null) { writer.writeOctetString(TYPE_MATCHING_RULE_TYPE, attributeDescription); } writer.writeOctetString(TYPE_MATCHING_RULE_VALUE, assertionValue); if (dnAttributes) { writer.writeBoolean(TYPE_MATCHING_RULE_DN_ATTRIBUTES, true); } writer.writeEndSequence(); return null; } catch (final IOException e) { return e; } } public IOException visitGreaterOrEqualFilter(final ASN1Writer writer, final String attributeDescription, final ByteString assertionValue) { try { writer.writeStartSequence(TYPE_FILTER_GREATER_OR_EQUAL); writer.writeOctetString(attributeDescription); writer.writeOctetString(assertionValue); writer.writeEndSequence(); return null; } catch (final IOException e) { return e; } } public IOException visitLessOrEqualFilter(final ASN1Writer writer, final String attributeDescription, final ByteString assertionValue) { try { writer.writeStartSequence(TYPE_FILTER_LESS_OR_EQUAL); writer.writeOctetString(attributeDescription); writer.writeOctetString(assertionValue); writer.writeEndSequence(); return null; } catch (final IOException e) { return e; } } public IOException visitNotFilter(final ASN1Writer writer, final Filter subFilter) { try { writer.writeStartSequence(TYPE_FILTER_NOT); final IOException e = subFilter.accept(this, writer); if (e != null) { return e; } writer.writeEndSequence(); return null; } catch (final IOException e) { return e; } } public IOException visitOrFilter(final ASN1Writer writer, final List subFilters) { try { writer.writeStartSequence(TYPE_FILTER_OR); for (final Filter subFilter : subFilters) { final IOException e = subFilter.accept(this, writer); if (e != null) { return e; } } writer.writeEndSequence(); return null; } catch (final IOException e) { return e; } } public IOException visitPresentFilter(final ASN1Writer writer, final String attributeDescription) { try { writer.writeOctetString(TYPE_FILTER_PRESENCE, attributeDescription); return null; } catch (final IOException e) { return e; } } public IOException visitSubstringsFilter(final ASN1Writer writer, final String attributeDescription, final ByteString initialSubstring, final List anySubstrings, final ByteString finalSubstring) { try { writer.writeStartSequence(TYPE_FILTER_SUBSTRING); writer.writeOctetString(attributeDescription); writer.writeStartSequence(); if (initialSubstring != null) { writer.writeOctetString(TYPE_SUBINITIAL, initialSubstring); } for (final ByteSequence anySubstring : anySubstrings) { writer.writeOctetString(TYPE_SUBANY, anySubstring); } if (finalSubstring != null) { writer.writeOctetString(TYPE_SUBFINAL, finalSubstring); } writer.writeEndSequence(); writer.writeEndSequence(); return null; } catch (final IOException e) { return e; } } public IOException visitUnrecognizedFilter(final ASN1Writer writer, final byte filterTag, final ByteString filterBytes) { try { writer.writeOctetString(filterTag, filterBytes); return null; } catch (final IOException e) { return e; } } }; /** * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a * {@code Filter}. * * @param reader * The {@code ASN1Reader} from which the ASN.1 encoded * {@code Filter} should be read. * @return The decoded {@code Filter}. * @throws IOException * If an error occurs while reading from {@code reader}. */ public static Filter decodeFilter(final ASN1Reader reader) throws IOException { final byte type = reader.peekType(); switch (type) { case TYPE_FILTER_AND: return decodeAndFilter(reader); case TYPE_FILTER_OR: return decodeOrFilter(reader); case TYPE_FILTER_NOT: return decodeNotFilter(reader); case TYPE_FILTER_EQUALITY: return decodeEqualityMatchFilter(reader); case TYPE_FILTER_GREATER_OR_EQUAL: return decodeGreaterOrEqualMatchFilter(reader); case TYPE_FILTER_LESS_OR_EQUAL: return decodeLessOrEqualMatchFilter(reader); case TYPE_FILTER_APPROXIMATE: return decodeApproxMatchFilter(reader); case TYPE_FILTER_SUBSTRING: return decodeSubstringsFilter(reader); case TYPE_FILTER_PRESENCE: return Filter.present(reader.readOctetStringAsString(type)); case TYPE_FILTER_EXTENSIBLE_MATCH: return decodeExtensibleMatchFilter(reader); default: return Filter.unrecognized(type, reader.readOctetString(type)); } } /** * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a * {@code SearchResultEntry}. * * @param reader * The {@code ASN1Reader} from which the ASN.1 encoded * {@code SearchResultEntry} should be read. * @param options * The decode options to use when decoding the entry. * @return The decoded {@code SearchResultEntry}. * @throws IOException * If an error occurs while reading from {@code reader}. */ public static SearchResultEntry decodeSearchResultEntry(final ASN1Reader reader, final DecodeOptions options) throws IOException { return LDAPReader.decodeEntry(reader, options); } /** * Writes the ASN.1 encoding of the provided {@code Filter} to the provided * {@code ASN1Writer}. * * @param writer * The {@code ASN1Writer} to which the ASN.1 encoding of the * provided {@code Filter} should be written. * @param filter * The filter to be encoded. * @return The updated {@code ASN1Writer}. * @throws IOException * If an error occurs while writing to {@code writer}. */ public static ASN1Writer encodeFilter(final ASN1Writer writer, final Filter filter) throws IOException { final IOException e = filter.accept(ASN1_ENCODER, writer); if (e != null) { throw e; } else { return writer; } } /** * Writes the ASN.1 encoding of the provided {@code SearchResultEntry} to * the provided {@code ASN1Writer}. * * @param writer * The {@code ASN1Writer} to which the ASN.1 encoding of the * provided {@code SearchResultEntry} should be written. * @param entry * The Search Result Entry to be encoded. * @return The updated {@code ASN1Writer}. * @throws IOException * If an error occurs while writing to {@code writer}. */ public static ASN1Writer encodeSearchResultEntry(final ASN1Writer writer, final SearchResultEntry entry) throws IOException { // FIXME: this should include Controls. LDAPWriter.encodeEntry(writer, entry); return writer; } // Decodes an and filter. private static Filter decodeAndFilter(final ASN1Reader reader) throws IOException { Filter filter; reader.readStartSequence(TYPE_FILTER_AND); try { if (reader.hasNextElement()) { final List subFilters = new LinkedList(); do { subFilters.add(decodeFilter(reader)); } while (reader.hasNextElement()); filter = Filter.and(subFilters); } else { // No sub-filters - this is an RFC 4526 absolute true filter. filter = Filter.alwaysTrue(); } } finally { reader.readEndSequence(); } return filter; } // Decodes an approximate match filter. private static Filter decodeApproxMatchFilter(final ASN1Reader reader) throws IOException { String attributeDescription; ByteString assertionValue; reader.readStartSequence(TYPE_FILTER_APPROXIMATE); try { attributeDescription = reader.readOctetStringAsString(); assertionValue = reader.readOctetString(); } finally { reader.readEndSequence(); } return Filter.approx(attributeDescription, assertionValue); } // Decodes an equality match filter. private static Filter decodeEqualityMatchFilter(final ASN1Reader reader) throws IOException { String attributeDescription; ByteString assertionValue; reader.readStartSequence(TYPE_FILTER_EQUALITY); try { attributeDescription = reader.readOctetStringAsString(); assertionValue = reader.readOctetString(); } finally { reader.readEndSequence(); } return Filter.equality(attributeDescription, assertionValue); } // Decodes an extensible match filter. private static Filter decodeExtensibleMatchFilter(final ASN1Reader reader) throws IOException { String matchingRule; String attributeDescription; boolean dnAttributes; ByteString assertionValue; reader.readStartSequence(TYPE_FILTER_EXTENSIBLE_MATCH); try { matchingRule = null; if (reader.peekType() == TYPE_MATCHING_RULE_ID) { matchingRule = reader.readOctetStringAsString(TYPE_MATCHING_RULE_ID); } attributeDescription = null; if (reader.peekType() == TYPE_MATCHING_RULE_TYPE) { attributeDescription = reader.readOctetStringAsString(TYPE_MATCHING_RULE_TYPE); } dnAttributes = false; if (reader.hasNextElement() && (reader.peekType() == TYPE_MATCHING_RULE_DN_ATTRIBUTES)) { dnAttributes = reader.readBoolean(); } assertionValue = reader.readOctetString(TYPE_MATCHING_RULE_VALUE); } finally { reader.readEndSequence(); } return Filter.extensible(matchingRule, attributeDescription, assertionValue, dnAttributes); } // Decodes a greater than or equal filter. private static Filter decodeGreaterOrEqualMatchFilter(final ASN1Reader reader) throws IOException { String attributeDescription; ByteString assertionValue; reader.readStartSequence(TYPE_FILTER_GREATER_OR_EQUAL); try { attributeDescription = reader.readOctetStringAsString(); assertionValue = reader.readOctetString(); } finally { reader.readEndSequence(); } return Filter.greaterOrEqual(attributeDescription, assertionValue); } // Decodes a less than or equal filter. private static Filter decodeLessOrEqualMatchFilter(final ASN1Reader reader) throws IOException { String attributeDescription; ByteString assertionValue; reader.readStartSequence(TYPE_FILTER_LESS_OR_EQUAL); try { attributeDescription = reader.readOctetStringAsString(); assertionValue = reader.readOctetString(); } finally { reader.readEndSequence(); } return Filter.lessOrEqual(attributeDescription, assertionValue); } // Decodes a not filter. private static Filter decodeNotFilter(final ASN1Reader reader) throws IOException { Filter subFilter; reader.readStartSequence(TYPE_FILTER_NOT); try { subFilter = decodeFilter(reader); } finally { reader.readEndSequence(); } return Filter.not(subFilter); } // Decodes an or filter. private static Filter decodeOrFilter(final ASN1Reader reader) throws IOException { Filter filter; reader.readStartSequence(TYPE_FILTER_OR); try { if (reader.hasNextElement()) { final List subFilters = new LinkedList(); do { subFilters.add(decodeFilter(reader)); } while (reader.hasNextElement()); filter = Filter.or(subFilters); } else { // No sub-filters - this is an RFC 4526 absolute false filter. filter = Filter.alwaysFalse(); } } finally { reader.readEndSequence(); } return filter; } // Decodes a sub-strings filter. private static Filter decodeSubstringsFilter(final ASN1Reader reader) throws IOException { ByteString initialSubstring = null; List anySubstrings = null; ByteString finalSubstring = null; String attributeDescription; reader.readStartSequence(TYPE_FILTER_SUBSTRING); try { attributeDescription = reader.readOctetStringAsString(); reader.readStartSequence(); try { // FIXME: There should be at least one element in this substring // filter sequence. if (reader.peekType() == TYPE_SUBINITIAL) { initialSubstring = reader.readOctetString(TYPE_SUBINITIAL); } if (reader.hasNextElement() && (reader.peekType() == TYPE_SUBANY)) { anySubstrings = new LinkedList(); do { anySubstrings.add(reader.readOctetString(TYPE_SUBANY)); } while (reader.hasNextElement() && (reader.peekType() == TYPE_SUBANY)); } if (reader.hasNextElement() && (reader.peekType() == TYPE_SUBFINAL)) { finalSubstring = reader.readOctetString(TYPE_SUBFINAL); } } finally { reader.readEndSequence(); } } finally { reader.readEndSequence(); } if (anySubstrings == null) { anySubstrings = Collections.emptyList(); } return Filter.substrings(attributeDescription, initialSubstring, anySubstrings, finalSubstring); } /** * Prevent instantiation. */ private LDAPUtils() { // Nothing to do. } }