View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.api.ldap.model.filter;
21  
22  
23  import java.text.Format;
24  import java.text.MessageFormat;
25  
26  
27  /**
28   * An encoder for LDAP filters.
29   * 
30   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
31   */
32  public class FilterEncoder
33  {
34      private static final String[] EMPTY = new String[0];
35  
36  
37      /**
38       * Formats a filter and handles encoding of special characters in the value arguments using the
39       * &lt;valueencoding&gt; rule as described in <a href="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</a>.
40       * <p>
41       * Example of filter template format: <code>(&(cn={0})(uid={1}))</code>
42       * 
43       * @param filterTemplate the filter with placeholders
44       * @param values the values to encode and substitute
45       * @return the formatted filter with escaped values
46       * @throws IllegalArgumentException if the number of values does not match the number of placeholders in the template
47       */
48      public static String format( String filterTemplate, String... values ) throws IllegalArgumentException
49      {
50          if ( values == null )
51          {
52              values = EMPTY;
53          }
54  
55          MessageFormat mf = new MessageFormat( filterTemplate );
56  
57          // check element count and argument count
58          Format[] formats = mf.getFormatsByArgumentIndex();
59          if ( formats.length != values.length )
60          {
61              // TODO: I18n
62              String msg = "Filter template {0} has {1} placeholders but {2} arguments provided.";
63              throw new IllegalArgumentException( MessageFormat.format( msg, filterTemplate, formats.length,
64                  values.length ) );
65          }
66  
67          // encode arguments
68          for ( int i = 0; i < values.length; i++ )
69          {
70              values[i] = encodeFilterValue( values[i] );
71          }
72  
73          // format the filter
74          String format = mf.format( values );
75          return format;
76      }
77  
78  
79      /**
80       * Handles encoding of special characters in LDAP search filter assertion values using the
81       * &lt;valueencoding&gt; rule as described in <a href="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</a>.
82       *
83       * @param value Right hand side of "attrId=value" assertion occurring in an LDAP search filter.
84       * @return Escaped version of <code>value</code>
85       */
86      public static String encodeFilterValue( String value )
87      {
88          StringBuilder sb = null;
89  
90          for ( int i = 0; i < value.length(); i++ )
91          {
92              char ch = value.charAt( i );
93              String replace = null;
94  
95              switch ( ch )
96              {
97                  case '*':
98                      replace = "\\2A";
99                      break;
100 
101                 case '(':
102                     replace = "\\28";
103                     break;
104 
105                 case ')':
106                     replace = "\\29";
107                     break;
108 
109                 case '\\':
110                     replace = "\\5C";
111                     break;
112 
113                 case '\0':
114                     replace = "\\00";
115                     break;
116             }
117 
118             if ( replace != null )
119             {
120                 if ( sb == null )
121                 {
122                     sb = new StringBuilder( value.length() * 2 );
123                     sb.append( value.substring( 0, i ) );
124                 }
125                 sb.append( replace );
126             }
127             else if ( sb != null )
128             {
129                 sb.append( ch );
130             }
131         }
132 
133         return ( sb == null ? value : sb.toString() );
134     }
135 }