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