/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package io.milton.ldap; import io.milton.http.exceptions.BadRequestException; import io.milton.http.exceptions.NotAuthorizedException; import io.milton.resource.LdapContact; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * * @author brad */ public class CompoundLdapFilter implements LdapFilter { private final Conditions conditions; private final Set<LdapFilter> criteria = new HashSet<LdapFilter>(); private final int type; CompoundLdapFilter(Conditions conditions, int filterType) { this.conditions = conditions; type = filterType; } @Override public String toString() { StringBuilder buffer = new StringBuilder(); if (type == Ldap.LDAP_FILTER_OR) { buffer.append("(|"); } else if (type == Ldap.LDAP_FILTER_AND) { buffer.append("(&"); } else { buffer.append("(!"); } for (LdapFilter child : criteria) { buffer.append(child.toString()); } buffer.append(')'); return buffer.toString(); } /** * Add child filter * * @param filter inner filter */ @Override public void add(LdapFilter filter) { criteria.add(filter); } /** * This is only a full search if every child * is also a full search * * @return true if full search filter */ @Override public boolean isFullSearch() { for (LdapFilter child : criteria) { if (!child.isFullSearch()) { return false; } } return true; } /** * Build search filter for Contacts folder search. * Use Exchange SEARCH syntax * * @return contact search filter */ @Override public Condition getContactSearchFilter() { MultiCondition condition; if (type == Ldap.LDAP_FILTER_OR) { condition = conditions.or(); } else { condition = conditions.and(); } for (LdapFilter child : criteria) { condition.add(child.getContactSearchFilter()); } return condition; } /** * Test if person matches the current filter. * * @param person person attributes map * @return true if filter match */ @Override public boolean isMatch(LdapContact person) throws NotAuthorizedException, BadRequestException { if (type == Ldap.LDAP_FILTER_OR) { for (LdapFilter child : criteria) { if (!child.isFullSearch()) { if (child.isMatch(person)) { // We've found a match return true; } } } // No subconditions are met return false; } else if (type == Ldap.LDAP_FILTER_AND) { for (LdapFilter child : criteria) { if (!child.isFullSearch()) { if (!child.isMatch(person)) { // We've found a miss return false; } } } // All subconditions are met return true; } return false; } /** * Find persons in Exchange GAL matching filter. * Iterate over child filters to build results. * * @param user Exchange session * @return persons map * @throws IOException on error */ @Override public List<LdapContact> findInGAL(LdapPrincipal user, Set<String> returningAttributes, int sizeLimit) throws IOException, NotAuthorizedException, BadRequestException { List<LdapContact> persons = null; for (LdapFilter child : criteria) { int currentSizeLimit = sizeLimit; if (persons != null) { currentSizeLimit -= persons.size(); } List<LdapContact> childFind = child.findInGAL(user, returningAttributes, currentSizeLimit); if (childFind != null) { if (persons == null) { persons = childFind; } else if (type == Ldap.LDAP_FILTER_OR) { // Create the union of the existing results and the child found results persons.addAll(childFind); } else if (type == Ldap.LDAP_FILTER_AND) { // Append current child filter results that match all child filters to persons. // The hard part is that, due to the 100-item-returned galFind limit // we may catch new items that match all child filters in each child search. // Thus, instead of building the intersection, we check each result against // all filters. for (LdapContact result : childFind) { if (isMatch(result)) { // This item from the child result set matches all sub-criteria, add it persons.add(result); } } } } } if ((persons == null) && !isFullSearch()) { // return an empty map (indicating no results were found) return new ArrayList<LdapContact>(); } return persons; } }