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   *    https://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  import java.util.Locale;
26  
27  import org.apache.directory.api.i18n.I18n;
28  import org.apache.directory.api.util.Strings;
29  
30  
31  /**
32   * An encoder for LDAP filters.
33   * 
34   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
35   */
36  public final class FilterEncoder
37  {
38      private FilterEncoder()
39      {
40      }
41  
42  
43      /**
44       * Formats a filter and handles encoding of special characters in the value arguments using the
45       * &lt;valueencoding&gt; rule as described in <a href="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</a>.
46       * <p>
47       * Example of filter template format: <code>(&amp;(cn={0})(uid={1}))</code>
48       * 
49       * @param filterTemplate the filter with placeholders
50       * @param values the values to encode and substitute
51       * @return the formatted filter with escaped values
52       * @throws IllegalArgumentException if the number of values does not match the number of placeholders in the template
53       */
54      public static String format( String filterTemplate, String... values )
55      {
56          if ( values == null )
57          {
58              values = Strings.EMPTY_STRING_ARRAY;
59          }
60  
61          MessageFormat mf = new MessageFormat( filterTemplate, Locale.ROOT );
62  
63          // check element count and argument count
64          Format[] formats = mf.getFormatsByArgumentIndex();
65          
66          if ( formats.length != values.length )
67          {
68              throw new IllegalArgumentException( I18n.err( I18n.ERR_13300_BAD_PLACE_HOLDERS_NUMBER, filterTemplate, formats.length, values.length ) );
69          }
70  
71          // encode arguments
72          for ( int i = 0; i < values.length; i++ )
73          {
74              values[i] = encodeFilterValue( values[i] );
75          }
76  
77          // format the filter
78          return mf.format( values );
79      }
80  
81  
82      /**
83       * Handles encoding of special characters in LDAP search filter assertion values using the
84       * &lt;valueencoding&gt; rule as described in <a href="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</a>.
85       *
86       * @param value Right hand side of "attrId=value" assertion occurring in an LDAP search filter.
87       * @return Escaped version of <code>value</code>
88       */
89      public static String encodeFilterValue( String value )
90      {
91          StringBuilder sb = new StringBuilder( value.length() );
92          boolean escaped = false;
93          boolean hexPair = false;
94          char hex = '\0';
95  
96          for ( int i = 0; i < value.length(); i++ )
97          {
98              char ch = value.charAt( i );
99  
100             switch ( ch )
101             {
102                 case '*':
103                     if ( escaped )
104                     {
105                         sb.append( "\\5C" );
106 
107                         if ( hexPair )
108                         {
109                             sb.append( hex );
110                             hexPair = false;
111                         }
112 
113                         escaped = false;
114                     }
115 
116                     sb.append( "\\2A" );
117                     break;
118 
119                 case '(':
120                     if ( escaped )
121                     {
122                         sb.append( "\\5C" );
123 
124                         if ( hexPair )
125                         {
126                             sb.append( hex );
127                             hexPair = false;
128                         }
129 
130                         escaped = false;
131                     }
132 
133                     sb.append( "\\28" );
134                     break;
135 
136                 case ')':
137                     if ( escaped )
138                     {
139                         sb.append( "\\5C" );
140 
141                         if ( hexPair )
142                         {
143                             sb.append( hex );
144                             hexPair = false;
145                         }
146 
147                         escaped = false;
148                     }
149 
150                     sb.append( "\\29" );
151                     break;
152 
153                 case '\0':
154                     if ( escaped )
155                     {
156                         sb.append( "\\5C" );
157 
158                         if ( hexPair )
159                         {
160                             sb.append( hex );
161                             hexPair = false;
162                         }
163 
164                         escaped = false;
165                     }
166 
167                     sb.append( "\\00" );
168                     break;
169 
170                 case '\\':
171                     if ( escaped )
172                     {
173                         sb.append( "\\5C" );
174                         escaped = false;
175                     }
176                     else
177                     {
178                         escaped = true;
179                         hexPair = false;
180                     }
181 
182                     break;
183 
184                 case '0':
185                 case '1':
186                 case '2':
187                 case '3':
188                 case '4':
189                 case '5':
190                 case '6':
191                 case '7':
192                 case '8':
193                 case '9':
194                 case 'a':
195                 case 'b':
196                 case 'c':
197                 case 'd':
198                 case 'e':
199                 case 'f':
200                 case 'A':
201                 case 'B':
202                 case 'C':
203                 case 'D':
204                 case 'E':
205                 case 'F':
206                     if ( escaped )
207                     {
208                         if ( hexPair )
209                         {
210                             sb.append( '\\' ).append( hex ).append( ch );
211                             escaped = false;
212                             hexPair = false;
213                         }
214                         else
215                         {
216                             hexPair = true;
217                             hex = ch;
218                         }
219                     }
220                     else
221                     {
222                         sb.append( ch );
223                     }
224 
225                     break;
226 
227                 default:
228                     if ( escaped )
229                     {
230                         sb.append( "\\5C" );
231 
232                         if ( hexPair )
233                         {
234                             sb.append( hex );
235                             hexPair = false;
236                         }
237 
238                         escaped = false;
239                     }
240 
241                     sb.append( ch );
242             }
243         }
244 
245         if ( escaped )
246         {
247             sb.append( "\\5C" );
248         }
249 
250         return sb.toString();
251     }
252 }