001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2009 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2015 ForgeRock AS.
016 */
017package org.opends.server.types;
018
019import org.forgerock.opendj.ldap.ByteString;
020import org.forgerock.i18n.LocalizableMessage;
021
022
023
024import java.util.ArrayList;
025import java.io.IOException;
026
027import org.forgerock.i18n.slf4j.LocalizedLogger;
028import org.forgerock.opendj.io.*;
029import org.opends.server.protocols.ldap.LDAPFilter;
030
031import static org.opends.messages.ProtocolMessages.*;
032import static org.opends.server.protocols.ldap.LDAPConstants.*;
033import static org.opends.server.protocols.ldap.LDAPResultCode.*;
034import static org.opends.server.util.StaticUtils.*;
035
036
037
038/**
039 * This class defines the data structures and methods to use when
040 * interacting with a raw search filter, which defines a set of
041 * criteria for locating entries in a search request.
042 */
043@org.opends.server.types.PublicAPI(
044     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
045     mayInstantiate=true,
046     mayExtend=false,
047     mayInvoke=true)
048public abstract class RawFilter
049{
050  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
051
052  /**
053   * Creates a new LDAP filter from the provided filter string.
054   *
055   * @param  filterString  The filter string to use to create this raw
056   *                       filter.
057   *
058   * @return  The raw filter decoded from the provided filter string.
059   *
060   * @throws  LDAPException  If the provied filter string could not be
061   *                         decoded as a raw filter.
062   */
063  public static RawFilter create(String filterString)
064         throws LDAPException
065  {
066    return LDAPFilter.decode(filterString);
067  }
068
069
070
071  /**
072   * Creates a new LDAP filter from the provided search filter.
073   *
074   * @param  filter  The search filter to use to create this raw
075   *                 filter.
076   *
077   * @return  The constructed raw filter.
078   */
079  public static RawFilter create(SearchFilter filter)
080  {
081    return new LDAPFilter(filter);
082  }
083
084
085
086  /**
087   * Creates a new AND search filter with the provided filter
088   * components.
089   *
090   * @param  filterComponents  The filter components for this AND
091   *                           filter.
092   *
093   * @return  The AND search filter with the provided filter
094   *          components.
095   */
096  public static LDAPFilter createANDFilter(ArrayList<RawFilter>
097                                                filterComponents)
098  {
099    return new LDAPFilter(FilterType.AND, filterComponents, null,
100                          null, null, null, null, null, null, false);
101  }
102
103
104
105  /**
106   * Creates a new OR search filter with the provided filter
107   * components.
108   *
109   * @param  filterComponents  The filter components for this OR
110   *                           filter.
111   *
112   * @return  The OR search filter with the provided filter
113   *          components.
114   */
115  public static LDAPFilter createORFilter(ArrayList<RawFilter>
116                                               filterComponents)
117  {
118    return new LDAPFilter(FilterType.OR, filterComponents, null, null,
119                          null, null, null, null, null, false);
120  }
121
122
123
124  /**
125   * Creates a new NOT search filter with the provided filter
126   * component.
127   *
128   * @param  notComponent  The filter component for this NOT filter.
129   *
130   * @return  The NOT search filter with the provided filter
131   *          component.
132   */
133  public static LDAPFilter createNOTFilter(RawFilter notComponent)
134  {
135    return new LDAPFilter(FilterType.NOT, null, notComponent, null,
136                          null, null, null, null, null, false);
137  }
138
139
140
141  /**
142   * Creates a new equality search filter with the provided
143   * information.
144   *
145   * @param  attributeType   The attribute type for this equality
146   *                         filter.
147   * @param  assertionValue  The assertion value for this equality
148   *                         filter.
149   *
150   * @return  The constructed equality search filter.
151   */
152  public static LDAPFilter createEqualityFilter(String attributeType,
153                                ByteString assertionValue)
154  {
155    return new LDAPFilter(FilterType.EQUALITY, null, null,
156                          attributeType, assertionValue, null, null,
157                          null, null, false);
158  }
159
160
161
162  /**
163   * Creates a new substring search filter with the provided
164   * information.
165   *
166   * @param  attributeType      The attribute type for this substring
167   *                            filter.
168   * @param  subInitialElement  The subInitial element for this
169   *                            substring filter.
170   * @param  subAnyElements     The subAny elements for this substring
171   *                            filter.
172   * @param  subFinalElement    The subFinal element for this
173   *                            substring filter.
174   *
175   * @return  The constructed substring search filter.
176   */
177  public static LDAPFilter createSubstringFilter(String attributeType,
178                                ByteString subInitialElement,
179                                ArrayList<ByteString> subAnyElements,
180                                ByteString subFinalElement)
181  {
182    return new LDAPFilter(FilterType.SUBSTRING, null, null,
183                          attributeType, null, subInitialElement,
184                          subAnyElements, subFinalElement, null,
185                          false);
186  }
187
188
189
190  /**
191   * Creates a new greater or equal search filter with the provided
192   * information.
193   *
194   * @param  attributeType   The attribute type for this greater or
195   *                         equal filter.
196   * @param  assertionValue  The assertion value for this greater or
197   *                         equal filter.
198   *
199   * @return  The constructed greater or equal search filter.
200   */
201  public static LDAPFilter createGreaterOrEqualFilter(
202                                String attributeType,
203                                ByteString assertionValue)
204  {
205    return new LDAPFilter(FilterType.GREATER_OR_EQUAL, null, null,
206                          attributeType, assertionValue, null, null,
207                          null, null, false);
208  }
209
210
211
212  /**
213   * Creates a new less or equal search filter with the provided
214   * information.
215   *
216   * @param  attributeType   The attribute type for this less or equal
217   *                         filter.
218   * @param  assertionValue  The assertion value for this less or
219   *                         equal filter.
220   *
221   * @return  The constructed less or equal search filter.
222   */
223  public static LDAPFilter createLessOrEqualFilter(
224                                String attributeType,
225                                ByteString assertionValue)
226  {
227    return new LDAPFilter(FilterType.LESS_OR_EQUAL, null, null,
228                          attributeType, assertionValue, null, null,
229                          null, null, false);
230  }
231
232
233
234  /**
235   * Creates a new presence search filter with the provided attribute
236   * type.
237   *
238   * @param  attributeType  The attribute type for this presence
239   *                        filter.
240   *
241   * @return  The constructed presence search filter.
242   */
243  public static LDAPFilter createPresenceFilter(String attributeType)
244  {
245    return new LDAPFilter(FilterType.PRESENT, null, null,
246                          attributeType, null, null, null, null, null,
247                          false);
248  }
249
250
251
252  /**
253   * Creates a new approximate search filter with the provided
254   * information.
255   *
256   * @param  attributeType   The attribute type for this approximate
257   *                         filter.
258   * @param  assertionValue  The assertion value for this approximate
259   *                         filter.
260   *
261   * @return  The constructed approximate search filter.
262   */
263  public static LDAPFilter createApproximateFilter(
264                                String attributeType,
265                                ByteString assertionValue)
266  {
267    return new LDAPFilter(FilterType.APPROXIMATE_MATCH, null, null,
268                          attributeType, assertionValue, null, null,
269                          null, null, false);
270  }
271
272
273
274  /**
275   * Creates a new extensible matching search filter with the provided
276   * information.
277   *
278   * @param  matchingRuleID  The matching rule ID for this extensible
279   *                         filter.
280   * @param  attributeType   The attribute type for this extensible
281   *                         filter.
282   * @param  assertionValue  The assertion value for this extensible
283   *                         filter.
284   * @param  dnAttributes    The dnAttributes flag for this extensible
285   *                         filter.
286   *
287   * @return  The constructed extensible matching search filter.
288   */
289  public static LDAPFilter createExtensibleFilter(
290                                String matchingRuleID,
291                                String attributeType,
292                                ByteString assertionValue,
293                                boolean dnAttributes)
294  {
295    return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null,
296                          attributeType, assertionValue, null, null,
297                          null, matchingRuleID, dnAttributes);
298  }
299
300
301
302  /**
303   * Retrieves the filter type for this search filter.
304   *
305   * @return  The filter type for this search filter.
306   */
307  public abstract FilterType getFilterType();
308
309
310
311  /**
312   * Retrieves the set of subordinate filter components for AND or OR
313   * searches.  The contents of the returned list may be altered by
314   * the caller.
315   *
316   * @return  The set of subordinate filter components for AND and OR
317   *          searches, or {@code null} if this is not an AND or OR
318   *          search.
319   */
320  public abstract ArrayList<RawFilter> getFilterComponents();
321
322
323
324  /**
325   * Retrieves the subordinate filter component for NOT searches.
326   *
327   * @return  The subordinate filter component for NOT searches, or
328   *          {@code null} if this is not a NOT search.
329   */
330  public abstract RawFilter getNOTComponent();
331
332
333
334  /**
335   * Retrieves the attribute type for this search filter.  This will
336   * not be applicable for AND, OR, or NOT filters.
337   *
338   * @return  The attribute type for this search filter, or
339   *          {@code null} if there is none.
340   */
341  public abstract String getAttributeType();
342
343
344
345  /**
346   * Retrieves the assertion value for this search filter.  This will
347   * only be applicable for equality, greater or equal, less or equal,
348   * approximate, or extensible matching filters.
349   *
350   * @return  The assertion value for this search filter, or
351   *          {@code null} if there is none.
352   */
353  public abstract ByteString getAssertionValue();
354
355
356
357  /**
358   * Retrieves the subInitial component for this substring filter.
359   * This is only applicable for substring search filters, but even
360   * substring filters might not have a value for this component.
361   *
362   * @return  The subInitial component for this substring filter, or
363   *          {@code null} if there is none.
364   */
365  public abstract ByteString getSubInitialElement();
366
367
368
369  /**
370   * Retrieves the set of subAny elements for this substring filter.
371   * This is only applicable for substring search filters, and even
372   * then may be {@code null} or empty for some substring filters.
373   *
374   * @return  The set of subAny elements for this substring filter, or
375   *          {@code null} if there are none.
376   */
377  public abstract ArrayList<ByteString> getSubAnyElements();
378
379
380
381  /**
382   * Retrieves the subFinal element for this substring filter.  This
383   * is not applicable for any other filter type, and may not be
384   * provided even for some substring filters.
385   *
386   * @return  The subFinal element for this substring filter, or
387   *          {@code null} if there is none.
388   */
389  public abstract ByteString getSubFinalElement();
390
391
392
393  /**
394   * Retrieves the matching rule ID for this extensible match filter.
395   * This is not applicable for any other type of filter and may not
396   * be included in some extensible matching filters.
397   *
398   * @return  The matching rule ID for this extensible match filter,
399   *          or {@code null} if there is none.
400   */
401  public abstract String getMatchingRuleID();
402
403
404
405  /**
406   * Retrieves the value of the DN attributes flag for this extensible
407   * match filter, which indicates whether to perform matching on the
408   * components of the DN.  This does not apply for any other type of
409   * filter.
410   *
411   * @return  The value of the DN attributes flag for this
412   *          extensibleMatch filter.
413   */
414  public abstract boolean getDNAttributes();
415
416  /**
417   * Writes this protocol op to an ASN.1 output stream.
418   *
419   * @param stream The ASN.1 output stream to write to.
420   * @throws IOException If a problem occurs while writing to the
421   *                     stream.
422   */
423  public void write(ASN1Writer stream) throws IOException
424  {
425      FilterType filterType = getFilterType();
426    switch (filterType)
427    {
428      case AND:
429      case OR:
430        stream.writeStartSequence(filterType.getBERType());
431        for(RawFilter f : getFilterComponents())
432        {
433          f.write(stream);
434        }
435        stream.writeEndSequence();
436        return;
437      case NOT:
438        stream.writeStartSequence(filterType.getBERType());
439        getNOTComponent().write(stream);
440        stream.writeEndSequence();
441        return;
442      case EQUALITY:
443      case GREATER_OR_EQUAL:
444      case LESS_OR_EQUAL:
445      case APPROXIMATE_MATCH:
446        stream.writeStartSequence(filterType.getBERType());
447        stream.writeOctetString(getAttributeType());
448        stream.writeOctetString(getAssertionValue());
449        stream.writeEndSequence();
450        return;
451      case SUBSTRING:
452        stream.writeStartSequence(filterType.getBERType());
453        stream.writeOctetString(getAttributeType());
454
455        stream.writeStartSequence();
456        ByteString subInitialElement = getSubInitialElement();
457        if (subInitialElement != null)
458        {
459          stream.writeOctetString(TYPE_SUBINITIAL, subInitialElement);
460        }
461
462        ArrayList<ByteString> subAnyElements = getSubAnyElements();
463        if (subAnyElements != null && !subAnyElements.isEmpty())
464        {
465          for (ByteString s : subAnyElements)
466          {
467            stream.writeOctetString(TYPE_SUBANY, s);
468          }
469        }
470
471        ByteString subFinalElement = getSubFinalElement();
472        if (subFinalElement != null)
473        {
474          stream.writeOctetString(TYPE_SUBFINAL, subFinalElement);
475        }
476        stream.writeEndSequence();
477
478        stream.writeEndSequence();
479        return;
480      case PRESENT:
481        stream.writeOctetString(filterType.getBERType(),
482            getAttributeType());
483        return;
484      case EXTENSIBLE_MATCH:
485        stream.writeStartSequence(filterType.getBERType());
486
487        String matchingRuleID = getMatchingRuleID();
488        if (matchingRuleID != null)
489        {
490          stream.writeOctetString(TYPE_MATCHING_RULE_ID,
491              matchingRuleID);
492        }
493
494        String attributeType = getAttributeType();
495        if (attributeType != null)
496        {
497          stream.writeOctetString(TYPE_MATCHING_RULE_TYPE,
498              attributeType);
499        }
500
501        stream.writeOctetString(TYPE_MATCHING_RULE_VALUE,
502            getAssertionValue());
503
504        if (getDNAttributes())
505        {
506          stream.writeBoolean(TYPE_MATCHING_RULE_DN_ATTRIBUTES, true);
507        }
508
509        stream.writeEndSequence();
510        return;
511      default:
512        if (logger.isTraceEnabled())
513        {
514          logger.trace("Invalid search filter type: %s",
515                            filterType);
516        }
517    }
518  }
519
520  /**
521   * Decodes the elements from the provided ASN.1 reader as a
522   * raw search filter.
523   *
524   * @param  reader The ASN.1 reader.
525   *
526   * @return  The decoded search filter.
527   *
528   * @throws  LDAPException  If the provided ASN.1 element cannot be
529   *                         decoded as a raw search filter.
530   */
531  public static LDAPFilter decode(ASN1Reader reader)
532         throws LDAPException
533  {
534    byte type;
535    try
536    {
537      type = reader.peekType();
538    }
539    catch(Exception e)
540    {
541      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_NULL.get();
542      throw new LDAPException(PROTOCOL_ERROR, message);
543    }
544
545    switch (type)
546    {
547      case TYPE_FILTER_AND:
548      case TYPE_FILTER_OR:
549        return decodeCompoundFilter(reader);
550
551      case TYPE_FILTER_NOT:
552        return decodeNotFilter(reader);
553
554      case TYPE_FILTER_EQUALITY:
555      case TYPE_FILTER_GREATER_OR_EQUAL:
556      case TYPE_FILTER_LESS_OR_EQUAL:
557      case TYPE_FILTER_APPROXIMATE:
558        return decodeAVAFilter(reader);
559
560      case TYPE_FILTER_SUBSTRING:
561        return decodeSubstringFilter(reader);
562
563      case TYPE_FILTER_PRESENCE:
564        return decodePresenceFilter(reader);
565
566      case TYPE_FILTER_EXTENSIBLE_MATCH:
567        return decodeExtensibleMatchFilter(reader);
568
569      default:
570        LocalizableMessage message =
571            ERR_LDAP_FILTER_DECODE_INVALID_TYPE.get(type);
572        throw new LDAPException(PROTOCOL_ERROR, message);
573    }
574  }
575
576  /**
577   * Decodes the elements from the provided ASN.1 reader as a compound
578   * filter (i.e. one that holds a set of subordinate filter
579   * components, like AND  or OR filters).
580   *
581   * @param  reader The ASN.1 reader.
582   *
583   * @return  The decoded LDAP search filter.
584   *
585   * @throws  LDAPException  If a problem occurs while trying to
586   *                         decode the provided ASN.1 element as a
587   *                         raw search filter.
588   */
589  private static LDAPFilter decodeCompoundFilter(ASN1Reader reader)
590      throws LDAPException
591  {
592    byte type;
593    try
594    {
595      type = reader.peekType();
596    }
597    catch(Exception e)
598    {
599      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_NULL.get();
600      throw new LDAPException(PROTOCOL_ERROR, message);
601    }
602
603    FilterType filterType;
604    switch (type)
605    {
606      case TYPE_FILTER_AND:
607        filterType = FilterType.AND;
608        break;
609      case TYPE_FILTER_OR:
610        filterType = FilterType.OR;
611        break;
612      default:
613        // This should never happen.
614        if (logger.isTraceEnabled())
615        {
616          logger.trace("Invalid filter type %x for a " +
617              "compound filter", type);
618        }
619        filterType = null;
620    }
621
622    ArrayList<RawFilter> filterComponents = new ArrayList<>();
623    try
624    {
625      reader.readStartSequence();
626      // Should have at least 1 filter
627      // could also be an absolute true/false filter
628      while (reader.hasNextElement())
629      {
630        filterComponents.add(LDAPFilter.decode(reader));
631      }
632      reader.readEndSequence();
633    }
634    catch (LDAPException le)
635    {
636      throw le;
637    }
638    catch (Exception e)
639    {
640      logger.traceException(e);
641
642      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_COMPOUND_COMPONENTS.get(e);
643      throw new LDAPException(PROTOCOL_ERROR, message, e);
644    }
645
646    return new LDAPFilter(filterType, filterComponents, null, null,
647        null, null, null, null, null, false);
648  }
649
650  /**
651   * Decodes the elements from the provided ASN.1 reader as a NOT
652   * filter.
653   *
654   * @param  reader The ASN.1 reader.
655   *
656   * @return  The decoded LDAP search filter.
657   *
658   * @throws  LDAPException  If a problem occurs while trying to
659   *                         decode the provided ASN.1 element as a
660   *                         raw search filter.
661   */
662  private static LDAPFilter decodeNotFilter(ASN1Reader reader)
663          throws LDAPException
664  {
665    RawFilter notComponent;
666    try
667    {
668      reader.readStartSequence();
669      notComponent = decode(reader);
670      reader.readEndSequence();
671    }
672    catch (LDAPException le)
673    {
674      throw le;
675    }
676    catch (Exception e)
677    {
678      logger.traceException(e);
679
680      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_NOT_COMPONENT.get(e);
681      throw new LDAPException(PROTOCOL_ERROR, message, e);
682    }
683
684
685    return new LDAPFilter(FilterType.NOT, null, notComponent, null,
686                          null, null, null, null, null, false);
687  }
688
689  /**
690   * Decodes the elements from the provided ASN.1 read as as a filter
691   * containing an attribute type and an assertion value.  This
692   * includes equality, greater or equal, less or equal, and
693   * approximate search filters.
694   *
695   * @param  reader The ASN.1 reader.
696   *
697   * @return  The decoded LDAP search filter.
698   *
699   * @throws  LDAPException  If a problem occurs while trying to
700   *                         decode the provided ASN.1 element as a
701   *                         raw search filter.
702   */
703  private static LDAPFilter decodeAVAFilter(ASN1Reader reader)
704          throws LDAPException
705  {
706    byte type;
707    try
708    {
709      type = reader.peekType();
710    }
711    catch(Exception e)
712    {
713      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_NULL.get();
714      throw new LDAPException(PROTOCOL_ERROR, message);
715    }
716
717    FilterType filterType;
718    switch (type)
719    {
720      case TYPE_FILTER_EQUALITY:
721        filterType = FilterType.EQUALITY;
722        break;
723      case TYPE_FILTER_GREATER_OR_EQUAL:
724        filterType = FilterType.GREATER_OR_EQUAL;
725        break;
726      case TYPE_FILTER_LESS_OR_EQUAL:
727        filterType = FilterType.LESS_OR_EQUAL;
728        break;
729      case TYPE_FILTER_APPROXIMATE:
730        filterType = FilterType.APPROXIMATE_MATCH;
731        break;
732      default:
733        // This should never happen.
734        if (logger.isTraceEnabled())
735        {
736          logger.trace("Invalid filter type %x for a " +
737              "type-and-value filter", type);
738        }
739        filterType = null;
740    }
741
742    try
743    {
744      reader.readStartSequence();
745    }
746    catch (Exception e)
747    {
748      logger.traceException(e);
749
750      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_TV_SEQUENCE.get(e);
751      throw new LDAPException(PROTOCOL_ERROR, message, e);
752    }
753
754    String attributeType;
755    try
756    {
757      attributeType = reader.readOctetStringAsString();
758    }
759    catch (Exception e)
760    {
761      logger.traceException(e);
762
763      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_TV_TYPE.get(e);
764      throw new LDAPException(PROTOCOL_ERROR, message, e);
765    }
766
767
768    ByteString assertionValue;
769    try
770    {
771      assertionValue = reader.readOctetString();
772    }
773    catch (Exception e)
774    {
775      logger.traceException(e);
776
777      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_TV_VALUE.get(e);
778      throw new LDAPException(PROTOCOL_ERROR, message, e);
779    }
780
781    try
782    {
783      reader.readEndSequence();
784    }
785    catch (Exception e)
786    {
787      logger.traceException(e);
788
789      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_TV_SEQUENCE.get(e);
790      throw new LDAPException(PROTOCOL_ERROR, message, e);
791    }
792
793
794    return new LDAPFilter(filterType, null, null, attributeType,
795                          assertionValue, null, null, null, null,
796                          false);
797  }
798
799  /**
800   * Decodes the elements from the provided ASN.1 reader as a
801   * substring filter.
802   *
803   * @param  reader The ASN.1 reader.
804   *
805   * @return  The decoded LDAP search filter.
806   *
807   * @throws  LDAPException  If a problem occurs while trying to
808   *                         decode the provided ASN.1 element as a
809   *                         raw search filter.
810   */
811  private static LDAPFilter decodeSubstringFilter(ASN1Reader reader)
812          throws LDAPException
813  {
814    try
815    {
816      reader.readStartSequence();
817    }
818    catch (Exception e)
819    {
820      logger.traceException(e);
821
822      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_SUBSTRING_SEQUENCE.get(e);
823      throw new LDAPException(PROTOCOL_ERROR, message, e);
824    }
825
826
827    String attributeType;
828    try
829    {
830      attributeType = reader.readOctetStringAsString();
831    }
832    catch (Exception e)
833    {
834      logger.traceException(e);
835
836      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_SUBSTRING_TYPE.get(e);
837      throw new LDAPException(PROTOCOL_ERROR, message, e);
838    }
839
840    try
841    {
842      reader.readStartSequence();
843    }
844    catch (Exception e)
845    {
846      logger.traceException(e);
847
848      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_SUBSTRING_ELEMENTS.get(e);
849      throw new LDAPException(PROTOCOL_ERROR, message, e);
850    }
851
852
853    try
854    {
855      // Make sure we have at least 1 substring
856      reader.peekType();
857    }
858    catch (Exception e)
859    {
860      LocalizableMessage message =
861          ERR_LDAP_FILTER_DECODE_SUBSTRING_NO_SUBELEMENTS.get();
862      throw new LDAPException(PROTOCOL_ERROR, message);
863    }
864
865
866    ByteString subInitialElement = null;
867    ByteString subFinalElement   = null;
868    ArrayList<ByteString> subAnyElements = null;
869    try
870    {
871      if(reader.hasNextElement() &&
872          reader.peekType() == TYPE_SUBINITIAL)
873      {
874        subInitialElement = reader.readOctetString();
875      }
876      while(reader.hasNextElement() &&
877          reader.peekType() == TYPE_SUBANY)
878      {
879        if(subAnyElements == null)
880        {
881          subAnyElements = new ArrayList<>();
882        }
883        subAnyElements.add(reader.readOctetString());
884      }
885      if(reader.hasNextElement() &&
886          reader.peekType() == TYPE_SUBFINAL)
887      {
888        subFinalElement = reader.readOctetString();
889      }
890    }
891    catch (Exception e)
892    {
893      logger.traceException(e);
894
895      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_SUBSTRING_VALUES.get(e);
896      throw new LDAPException(PROTOCOL_ERROR, message, e);
897    }
898
899    try
900    {
901      reader.readEndSequence();
902    }
903    catch (Exception e)
904    {
905      logger.traceException(e);
906
907      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_SUBSTRING_ELEMENTS.get(e);
908      throw new LDAPException(PROTOCOL_ERROR, message, e);
909    }
910
911    try
912    {
913      reader.readEndSequence();
914    }
915    catch (Exception e)
916    {
917      logger.traceException(e);
918
919      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_SUBSTRING_SEQUENCE.get(e);
920      throw new LDAPException(PROTOCOL_ERROR, message, e);
921    }
922
923    return new LDAPFilter(FilterType.SUBSTRING, null, null,
924                          attributeType, null, subInitialElement,
925                          subAnyElements, subFinalElement, null,
926                          false);
927  }
928
929  /**
930   * Decodes the elements from the provided ASN.1 reader as a
931   * presence filter.
932   *
933   * @param  reader The ASN.1 reader.
934   *
935   * @return  The decoded LDAP search filter.
936   *
937   * @throws  LDAPException  If a problem occurs while trying to
938   *                         decode the provided ASN.1 element as a
939   *                         raw search filter.
940   */
941  private static LDAPFilter decodePresenceFilter(ASN1Reader reader)
942          throws LDAPException
943  {
944    String attributeType;
945    try
946    {
947      attributeType = reader.readOctetStringAsString();
948    }
949    catch (Exception e)
950    {
951      logger.traceException(e);
952
953      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_PRESENCE_TYPE.get(e);
954      throw new LDAPException(PROTOCOL_ERROR, message, e);
955    }
956
957
958    return new LDAPFilter(FilterType.PRESENT, null, null,
959                          attributeType, null, null, null, null, null,
960                          false);
961  }
962
963  /**
964   * Decodes the elements from the provided ASN.1 reader as an
965   * extensible match filter.
966   *
967   * @param  reader The ASN.1 reader.
968   *
969   * @return  The decoded LDAP search filter.
970   *
971   * @throws  LDAPException  If a problem occurs while trying to
972   *                         decode the provided ASN.1 element as a
973   *                         raw search filter.
974   */
975  private static LDAPFilter decodeExtensibleMatchFilter(
976      ASN1Reader reader) throws LDAPException
977  {
978    try
979    {
980      reader.readStartSequence();
981    }
982    catch (Exception e)
983    {
984      logger.traceException(e);
985
986      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_EXTENSIBLE_SEQUENCE.get(e);
987      throw new LDAPException(PROTOCOL_ERROR, message, e);
988    }
989
990
991    ByteString assertionValue;
992    boolean    dnAttributes   = false;
993    String     attributeType  = null;
994    String     matchingRuleID = null;
995    try
996    {
997      if(reader.peekType() == TYPE_MATCHING_RULE_ID)
998      {
999        matchingRuleID = reader.readOctetStringAsString();
1000      }
1001      if(matchingRuleID == null ||
1002          reader.peekType() == TYPE_MATCHING_RULE_TYPE)
1003      {
1004        attributeType = reader.readOctetStringAsString();
1005      }
1006      assertionValue = reader.readOctetString();
1007      if(reader.hasNextElement() &&
1008          reader.peekType() == TYPE_MATCHING_RULE_DN_ATTRIBUTES)
1009      {
1010        dnAttributes = reader.readBoolean();
1011      }
1012    }
1013    catch (Exception e)
1014    {
1015      logger.traceException(e);
1016
1017      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_EXTENSIBLE_ELEMENTS.get(e);
1018      throw new LDAPException(PROTOCOL_ERROR, message, e);
1019    }
1020
1021    try
1022    {
1023      reader.readEndSequence();
1024    }
1025    catch (Exception e)
1026    {
1027      logger.traceException(e);
1028
1029      LocalizableMessage message = ERR_LDAP_FILTER_DECODE_EXTENSIBLE_SEQUENCE.get(e);
1030      throw new LDAPException(PROTOCOL_ERROR, message, e);
1031    }
1032
1033
1034    return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null,
1035                          attributeType, assertionValue, null, null,
1036                          null, matchingRuleID, dnAttributes);
1037  }
1038
1039
1040
1041  /**
1042   * Converts this raw filter to a search filter that may be used by
1043   * the Directory Server's core processing.
1044   *
1045   * @return  The generated search filter.
1046   *
1047   * @throws  DirectoryException  If a problem occurs while attempting
1048   *                              to construct the search filter.
1049   */
1050  public abstract SearchFilter toSearchFilter()
1051         throws DirectoryException;
1052
1053
1054
1055  /**
1056   * Retrieves a string representation of this search filter.
1057   *
1058   * @return  A string representation of this search filter.
1059   */
1060  @Override
1061  public String toString()
1062  {
1063    StringBuilder buffer = new StringBuilder();
1064    toString(buffer);
1065    return buffer.toString();
1066  }
1067
1068
1069
1070  /**
1071   * Appends a string representation of this search filter to the
1072   * provided buffer.
1073   *
1074   * @param  buffer  The buffer to which the information should be
1075   *                 appended.
1076   */
1077  public abstract void toString(StringBuilder buffer);
1078
1079
1080
1081  /**
1082   * Appends a properly-cleaned version of the provided value to the
1083   * given buffer so that it can be safely used in string
1084   * representations of this search filter.  The formatting changes
1085   * that may be performed will be in compliance with the
1086   * specification in RFC 2254.
1087   *
1088   * @param  buffer  The buffer to which the "safe" version of the
1089   *                 value will be appended.
1090   * @param  value   The value to be appended to the buffer.
1091   */
1092  public static void valueToFilterString(StringBuilder buffer,
1093                                         ByteString value)
1094  {
1095    if (value == null)
1096    {
1097      return;
1098    }
1099
1100
1101    // Get the binary representation of the value and iterate through
1102    // it to see if there are any unsafe characters.  If there are,
1103    // then escape them and replace them with a two-digit hex
1104    // equivalent.
1105    buffer.ensureCapacity(buffer.length() + value.length());
1106    for (int i = 0; i < value.length(); i++)
1107    {
1108      byte b = value.byteAt(i);
1109      if (((b & 0x7F) != b) ||  // Not 7-bit clean
1110          (b <= 0x1F) ||        // Below the printable character range
1111          (b == 0x28) ||        // Open parenthesis
1112          (b == 0x29) ||        // Close parenthesis
1113          (b == 0x2A) ||        // Asterisk
1114          (b == 0x5C) ||        // Backslash
1115          (b == 0x7F))          // Delete character
1116      {
1117        buffer.append("\\");
1118        buffer.append(byteToHex(b));
1119      }
1120      else
1121      {
1122        buffer.append((char) b);
1123      }
1124    }
1125  }
1126}
1127