History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/jndi/toolkit/dir/SearchFilter.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/jndi/toolkit/dir/SearchFilter.java+674−0
@@ -0,0 +1,674 @@
1+/*
2+ * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+package com.sun.jndi.toolkit.dir;
26+
27+import javax.naming.*;
28+import javax.naming.directory.*;
29+import java.util.Enumeration;
30+import java.util.StringTokenizer;
31+import java.util.Vector;
32+
33+/**
34+ * A class for parsing LDAP search filters (defined in RFC 1960, 2254)
35+ *
36+ * @author Jon Ruiz
37+ * @author Rosanna Lee
38+ */
39+public class SearchFilter implements AttrFilter {
40+
41+ interface StringFilter extends AttrFilter {
42+ public void parse() throws InvalidSearchFilterException;
43+ }
44+
45+ // %%% "filter" and "pos" are not declared "private" due to bug 4064984.
46+ String filter;
47+ int pos;
48+ private StringFilter rootFilter;
49+
50+ protected static final boolean debug = false;
51+
52+ protected static final char BEGIN_FILTER_TOKEN = '(';
53+ protected static final char END_FILTER_TOKEN = ')';
54+ protected static final char AND_TOKEN = '&';
55+ protected static final char OR_TOKEN = '|';
56+ protected static final char NOT_TOKEN = '!';
57+ protected static final char EQUAL_TOKEN = '=';
58+ protected static final char APPROX_TOKEN = '~';
59+ protected static final char LESS_TOKEN = '<';
60+ protected static final char GREATER_TOKEN = '>';
61+ protected static final char EXTEND_TOKEN = ':';
62+ protected static final char WILDCARD_TOKEN = '*';
63+
64+ public SearchFilter(String filter) throws InvalidSearchFilterException {
65+ this.filter = filter;
66+ pos = 0;
67+ normalizeFilter();
68+ rootFilter = this.createNextFilter();
69+ }
70+
71+ // Returns true if targetAttrs passes the filter
72+ public boolean check(Attributes targetAttrs) throws NamingException {
73+ if (targetAttrs == null)
74+ return false;
75+
76+ return rootFilter.check(targetAttrs);
77+ }
78+
79+ /*
80+ * Utility routines used by member classes
81+ */
82+
83+ // does some pre-processing on the string to make it look exactly lik
84+ // what the parser expects. This only needs to be called once.
85+ protected void normalizeFilter() {
86+ skipWhiteSpace(); // get rid of any leading whitespaces
87+
88+ // Sometimes, search filters don't have "(" and ")" - add them
89+ if(getCurrentChar() != BEGIN_FILTER_TOKEN) {
90+ filter = BEGIN_FILTER_TOKEN + filter + END_FILTER_TOKEN;
91+ }
92+ // this would be a good place to strip whitespace if desired
93+
94+ if(debug) {System.out.println("SearchFilter: normalized filter:" +
95+ filter);}
96+ }
97+
98+ private void skipWhiteSpace() {
99+ while (Character.isWhitespace(getCurrentChar())) {
100+ consumeChar();
101+ }
102+ }
103+
104+ protected StringFilter createNextFilter()
105+ throws InvalidSearchFilterException {
106+ StringFilter filter;
107+
108+ skipWhiteSpace();
109+
110+ try {
111+ // make sure every filter starts with "("
112+ if(getCurrentChar() != BEGIN_FILTER_TOKEN) {
113+ throw new InvalidSearchFilterException("expected \"" +
114+ BEGIN_FILTER_TOKEN +
115+ "\" at position " +
116+ pos);
117+ }
118+
119+ // skip past the "("
120+ this.consumeChar();
121+
122+ skipWhiteSpace();
123+
124+ // use the next character to determine the type of filter
125+ switch(getCurrentChar()) {
126+ case AND_TOKEN:
127+ if (debug) {System.out.println("SearchFilter: creating AND");}
128+ filter = new CompoundFilter(true);
129+ filter.parse();
130+ break;
131+ case OR_TOKEN:
132+ if (debug) {System.out.println("SearchFilter: creating OR");}
133+ filter = new CompoundFilter(false);
134+ filter.parse();
135+ break;
136+ case NOT_TOKEN:
137+ if (debug) {System.out.println("SearchFilter: creating OR");}
138+ filter = new NotFilter();
139+ filter.parse();
140+ break;
141+ default:
142+ if (debug) {System.out.println("SearchFilter: creating SIMPLE");}
143+ filter = new AtomicFilter();
144+ filter.parse();
145+ break;
146+ }
147+
148+ skipWhiteSpace();
149+
150+ // make sure every filter ends with ")"
151+ if(getCurrentChar() != END_FILTER_TOKEN) {
152+ throw new InvalidSearchFilterException("expected \"" +
153+ END_FILTER_TOKEN +
154+ "\" at position " +
155+ pos);
156+ }
157+
158+ // skip past the ")"
159+ this.consumeChar();
160+ } catch (InvalidSearchFilterException e) {
161+ if (debug) {System.out.println("rethrowing e");}
162+ throw e; // just rethrow these
163+
164+ // catch all - any uncaught exception while parsing will end up here
165+ } catch (Exception e) {
166+ if(debug) {System.out.println(e.getMessage());e.printStackTrace();}
167+ throw new InvalidSearchFilterException("Unable to parse " +
168+ "character " + pos + " in \""+
169+ this.filter + "\"");
170+ }
171+
172+ return filter;
173+ }
174+
175+ protected char getCurrentChar() {
176+ return filter.charAt(pos);
177+ }
178+
179+ protected char relCharAt(int i) {
180+ return filter.charAt(pos + i);
181+ }
182+
183+ protected void consumeChar() {
184+ pos++;
185+ }
186+
187+ protected void consumeChars(int i) {
188+ pos += i;
189+ }
190+
191+ protected int relIndexOf(int ch) {
192+ return filter.indexOf(ch, pos) - pos;
193+ }
194+
195+ protected String relSubstring(int beginIndex, int endIndex){
196+ if(debug){System.out.println("relSubString: " + beginIndex +
197+ " " + endIndex);}
198+ return filter.substring(beginIndex+pos, endIndex+pos);
199+ }
200+
201+
202+ /**
203+ * A class for dealing with compound filters ("and" & "or" filters).
204+ */
205+ final class CompoundFilter implements StringFilter {
206+ private Vector subFilters;
207+ private boolean polarity;
208+
209+ CompoundFilter(boolean polarity) {
210+ subFilters = new Vector();
211+ this.polarity = polarity;
212+ }
213+
214+ public void parse() throws InvalidSearchFilterException {
215+ SearchFilter.this.consumeChar(); // consume the "&"
216+ while(SearchFilter.this.getCurrentChar() != END_FILTER_TOKEN) {
217+ if (debug) {System.out.println("CompoundFilter: adding");}
218+ StringFilter filter = SearchFilter.this.createNextFilter();
219+ subFilters.addElement(filter);
220+ skipWhiteSpace();
221+ }
222+ }
223+
224+ public boolean check(Attributes targetAttrs) throws NamingException {
225+ for(int i = 0; i<subFilters.size(); i++) {
226+ StringFilter filter = (StringFilter)subFilters.elementAt(i);
227+ if(filter.check(targetAttrs) != this.polarity) {
228+ return !polarity;
229+ }
230+ }
231+ return polarity;
232+ }
233+ } /* CompoundFilter */
234+
235+ /**
236+ * A class for dealing with NOT filters
237+ */
238+ final class NotFilter implements StringFilter {
239+ private StringFilter filter;
240+
241+ public void parse() throws InvalidSearchFilterException {
242+ SearchFilter.this.consumeChar(); // consume the "!"
243+ filter = SearchFilter.this.createNextFilter();
244+ }
245+
246+ public boolean check(Attributes targetAttrs) throws NamingException {
247+ return !filter.check(targetAttrs);
248+ }
249+ } /* notFilter */
250+
251+ // note: declared here since member classes can't have static variables
252+ static final int EQUAL_MATCH = 1;
253+ static final int APPROX_MATCH = 2;
254+ static final int GREATER_MATCH = 3;
255+ static final int LESS_MATCH = 4;
256+
257+ /**
258+ * A class for dealing wtih atomic filters
259+ */
260+ final class AtomicFilter implements StringFilter {
261+ private String attrID;
262+ private String value;
263+ private int matchType;
264+
265+ public void parse() throws InvalidSearchFilterException {
266+
267+ skipWhiteSpace();
268+
269+ try {
270+ // find the end
271+ int endPos = SearchFilter.this.relIndexOf(END_FILTER_TOKEN);
272+
273+ //determine the match type
274+ int i = SearchFilter.this.relIndexOf(EQUAL_TOKEN);
275+ if(debug) {System.out.println("AtomicFilter: = at " + i);}
276+ int qualifier = SearchFilter.this.relCharAt(i-1);
277+ switch(qualifier) {
278+ case APPROX_TOKEN:
279+ if (debug) {System.out.println("Atomic: APPROX found");}
280+ matchType = APPROX_MATCH;
281+ attrID = SearchFilter.this.relSubstring(0, i-1);
282+ value = SearchFilter.this.relSubstring(i+1, endPos);
283+ break;
284+
285+ case GREATER_TOKEN:
286+ if (debug) {System.out.println("Atomic: GREATER found");}
287+ matchType = GREATER_MATCH;
288+ attrID = SearchFilter.this.relSubstring(0, i-1);
289+ value = SearchFilter.this.relSubstring(i+1, endPos);
290+ break;
291+
292+ case LESS_TOKEN:
293+ if (debug) {System.out.println("Atomic: LESS found");}
294+ matchType = LESS_MATCH;
295+ attrID = SearchFilter.this.relSubstring(0, i-1);
296+ value = SearchFilter.this.relSubstring(i+1, endPos);
297+ break;
298+
299+ case EXTEND_TOKEN:
300+ if(debug) {System.out.println("Atomic: EXTEND found");}
301+ throw new OperationNotSupportedException("Extensible match not supported");
302+
303+ default:
304+ if (debug) {System.out.println("Atomic: EQUAL found");}
305+ matchType = EQUAL_MATCH;
306+ attrID = SearchFilter.this.relSubstring(0,i);
307+ value = SearchFilter.this.relSubstring(i+1, endPos);
308+ break;
309+ }
310+
311+ attrID = attrID.trim();
312+ value = value.trim();
313+
314+ //update our position
315+ SearchFilter.this.consumeChars(endPos);
316+
317+ } catch (Exception e) {
318+ if (debug) {System.out.println(e.getMessage());
319+ e.printStackTrace();}
320+ InvalidSearchFilterException sfe =
321+ new InvalidSearchFilterException("Unable to parse " +
322+ "character " + SearchFilter.this.pos + " in \""+
323+ SearchFilter.this.filter + "\"");
324+ sfe.setRootCause(e);
325+ throw(sfe);
326+ }
327+
328+ if(debug) {System.out.println("AtomicFilter: " + attrID + "=" +
329+ value);}
330+ }
331+
332+ public boolean check(Attributes targetAttrs) {
333+ Enumeration candidates;
334+
335+ try {
336+ Attribute attr = targetAttrs.get(attrID);
337+ if(attr == null) {
338+ return false;
339+ }
340+ candidates = attr.getAll();
341+ } catch (NamingException ne) {
342+ if (debug) {System.out.println("AtomicFilter: should never " +
343+ "here");}
344+ return false;
345+ }
346+
347+ while(candidates.hasMoreElements()) {
348+ String val = candidates.nextElement().toString();
349+ if (debug) {System.out.println("Atomic: comparing: " + val);}
350+ switch(matchType) {
351+ case APPROX_MATCH:
352+ case EQUAL_MATCH:
353+ if(substringMatch(this.value, val)) {
354+ if (debug) {System.out.println("Atomic: EQUAL match");}
355+ return true;
356+ }
357+ break;
358+ case GREATER_MATCH:
359+ if (debug) {System.out.println("Atomic: GREATER match");}
360+ if(val.compareTo(this.value) >= 0) {
361+ return true;
362+ }
363+ break;
364+ case LESS_MATCH:
365+ if (debug) {System.out.println("Atomic: LESS match");}
366+ if(val.compareTo(this.value) <= 0) {
367+ return true;
368+ }
369+ break;
370+ default:
371+ if (debug) {System.out.println("AtomicFilter: unkown " +
372+ "matchType");}
373+ }
374+ }
375+ return false;
376+ }
377+
378+ // used for substring comparisons (where proto has "*" wildcards
379+ private boolean substringMatch(String proto, String value) {
380+ // simple case 1: "*" means attribute presence is being tested
381+ if(proto.equals(new Character(WILDCARD_TOKEN).toString())) {
382+ if(debug) {System.out.println("simple presence assertion");}
383+ return true;
384+ }
385+
386+ // simple case 2: if there are no wildcards, call String.equals()
387+ if(proto.indexOf(WILDCARD_TOKEN) == -1) {
388+ return proto.equalsIgnoreCase(value);
389+ }
390+
391+ if(debug) {System.out.println("doing substring comparison");}
392+ // do the work: make sure all the substrings are present
393+ int currentPos = 0;
394+ StringTokenizer subStrs = new StringTokenizer(proto, "*", false);
395+
396+ // do we need to begin with the first token?
397+ if(proto.charAt(0) != WILDCARD_TOKEN &&
398+ !value.toString().toLowerCase().startsWith(
399+ subStrs.nextToken().toLowerCase())) {
400+ if(debug) {System.out.println("faild initial test");}
401+ return false;
402+ }
403+
404+
405+ while(subStrs.hasMoreTokens()) {
406+ String currentStr = subStrs.nextToken();
407+ if (debug) {System.out.println("looking for \"" +
408+ currentStr +"\"");}
409+ currentPos = value.toLowerCase().indexOf(
410+ currentStr.toLowerCase(), currentPos);
411+ if(currentPos == -1) {
412+ return false;
413+ }
414+ currentPos += currentStr.length();
415+ }
416+
417+ // do we need to end with the last token?
418+ if(proto.charAt(proto.length() - 1) != WILDCARD_TOKEN &&
419+ currentPos != value.length() ) {
420+ if(debug) {System.out.println("faild final test");}
421+ return false;
422+ }
423+
424+ return true;
425+ }
426+
427+ } /* AtomicFilter */
428+
429+ // ----- static methods for producing string filters given attribute set
430+ // ----- or object array
431+
432+
433+ /**
434+ * Creates an LDAP filter as a conjuction of the attributes supplied.
435+ */
436+ public static String format(Attributes attrs) throws NamingException {
437+ if (attrs == null || attrs.size() == 0) {
438+ return "objectClass=*";
439+ }
440+
441+ String answer;
442+ answer = "(& ";
443+ Attribute attr;
444+ for (NamingEnumeration e = attrs.getAll(); e.hasMore(); ) {
445+ attr = (Attribute)e.next();
446+ if (attr.size() == 0 || (attr.size() == 1 && attr.get() == null)) {
447+ // only checking presence of attribute
448+ answer += "(" + attr.getID() + "=" + "*)";
449+ } else {
450+ for (NamingEnumeration ve = attr.getAll();
451+ ve.hasMore();
452+ ) {
453+ String val = getEncodedStringRep(ve.next());
454+ if (val != null) {
455+ answer += "(" + attr.getID() + "=" + val + ")";
456+ }
457+ }
458+ }
459+ }
460+
461+ answer += ")";
462+ //System.out.println("filter: " + answer);
463+ return answer;
464+ }
465+
466+ // Writes the hex representation of a byte to a StringBuffer.
467+ private static void hexDigit(StringBuffer buf, byte x) {
468+ char c;
469+
470+ c = (char) ((x >> 4) & 0xf);
471+ if (c > 9)
472+ c = (char) ((c-10) + 'A');
473+ else
474+ c = (char)(c + '0');
475+
476+ buf.append(c);
477+ c = (char) (x & 0xf);
478+ if (c > 9)
479+ c = (char)((c-10) + 'A');
480+ else
481+ c = (char)(c + '0');
482+ buf.append(c);
483+ }
484+
485+
486+ /**
487+ * Returns the string representation of an object (such as an attr value).
488+ * If obj is a byte array, encode each item as \xx, where xx is hex encoding
489+ * of the byte value.
490+ * Else, if obj is not a String, use its string representation (toString()).
491+ * Special characters in obj (or its string representation) are then
492+ * encoded appropriately according to RFC 2254.
493+ * * \2a
494+ * ( \28
495+ * ) \29
496+ * \ \5c
497+ * NUL \00
498+ */
499+ private static String getEncodedStringRep(Object obj) throws NamingException {
500+ String str;
501+ if (obj == null)
502+ return null;
503+
504+ if (obj instanceof byte[]) {
505+ // binary data must be encoded as \hh where hh is a hex char
506+ byte[] bytes = (byte[])obj;
507+ StringBuffer b1 = new StringBuffer(bytes.length*3);
508+ for (int i = 0; i < bytes.length; i++) {
509+ b1.append('\\');
510+ hexDigit(b1, bytes[i]);
511+ }
512+ return b1.toString();
513+ }
514+ if (!(obj instanceof String)) {
515+ str = obj.toString();
516+ } else {
517+ str = (String)obj;
518+ }
519+ int len = str.length();
520+ StringBuffer buf = new StringBuffer(len);
521+ char ch;
522+ for (int i = 0; i < len; i++) {
523+ switch (ch=str.charAt(i)) {
524+ case '*':
525+ buf.append("\\2a");
526+ break;
527+ case '(':
528+ buf.append("\\28");
529+ break;
530+ case ')':
531+ buf.append("\\29");
532+ break;
533+ case '\\':
534+ buf.append("\\5c");
535+ break;
536+ case 0:
537+ buf.append("\\00");
538+ break;
539+ default:
540+ buf.append(ch);
541+ }
542+ }
543+ return buf.toString();
544+ }
545+
546+
547+ /**
548+ * Finds the first occurrence of <tt>ch</tt> in <tt>val</tt> starting
549+ * from position <tt>start</tt>. It doesn't count if <tt>ch</tt>
550+ * has been escaped by a backslash (\)
551+ */
552+ public static int findUnescaped(char ch, String val, int start) {
553+ int len = val.length();
554+
555+ while (start < len) {
556+ int where = val.indexOf(ch, start);
557+ // if at start of string, or not there at all, or if not escaped
558+ if (where == start || where == -1 || val.charAt(where-1) != '\\')
559+ return where;
560+
561+ // start search after escaped star
562+ start = where + 1;
563+ }
564+ return -1;
565+ }
566+
567+ /**
568+ * Formats the expression <tt>expr</tt> using arguments from the array
569+ * <tt>args</tt>.
570+ *
571+ * <code>{i}</code> specifies the <code>i</code>'th element from
572+ * the array <code>args</code> is to be substituted for the
573+ * string "<code>{i}</code>".
574+ *
575+ * To escape '{' or '}' (or any other character), use '\'.
576+ *
577+ * Uses getEncodedStringRep() to do encoding.
578+ */
579+
580+ public static String format(String expr, Object[] args)
581+ throws NamingException {
582+
583+ int param;
584+ int where = 0, start = 0;
585+ StringBuffer answer = new StringBuffer(expr.length());
586+
587+ while ((where = findUnescaped('{', expr, start)) >= 0) {
588+ int pstart = where + 1; // skip '{'
589+ int pend = expr.indexOf('}', pstart);
590+
591+ if (pend < 0) {
592+ throw new InvalidSearchFilterException("unbalanced {: " + expr);
593+ }
594+
595+ // at this point, pend should be pointing at '}'
596+ try {
597+ param = Integer.parseInt(expr.substring(pstart, pend));
598+ } catch (NumberFormatException e) {
599+ throw new InvalidSearchFilterException(
600+ "integer expected inside {}: " + expr);
601+ }
602+
603+ if (param >= args.length) {
604+ throw new InvalidSearchFilterException(
605+ "number exceeds argument list: " + param);
606+ }
607+
608+ answer.append(expr.substring(start, where)).append(getEncodedStringRep(args[param]));
609+ start = pend + 1; // skip '}'
610+ }
611+
612+ if (start < expr.length())
613+ answer.append(expr.substring(start));
614+
615+ return answer.toString();
616+ }
617+
618+ /*
619+ * returns an Attributes instance containing only attributeIDs given in
620+ * "attributeIDs" whose values come from the given DSContext.
621+ */
622+ public static Attributes selectAttributes(Attributes originals,
623+ String[] attrIDs) throws NamingException {
624+
625+ if (attrIDs == null)
626+ return originals;
627+
628+ Attributes result = new BasicAttributes();
629+
630+ for(int i=0; i<attrIDs.length; i++) {
631+ Attribute attr = originals.get(attrIDs[i]);
632+ if(attr != null) {
633+ result.put(attr);
634+ }
635+ }
636+
637+ return result;
638+ }
639+
640+/* For testing filter
641+ public static void main(String[] args) {
642+
643+ Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
644+ attrs.put("cn", "Rosanna Lee");
645+ attrs.put("sn", "Lee");
646+ attrs.put("fn", "Rosanna");
647+ attrs.put("id", "10414");
648+ attrs.put("machine", "jurassic");
649+
650+
651+ try {
652+ System.out.println(format(attrs));
653+
654+ String expr = "(&(Age = {0})(Account Balance <= {1}))";
655+ Object[] fargs = new Object[2];
656+ // fill in the parameters
657+ fargs[0] = new Integer(65);
658+ fargs[1] = new Float(5000);
659+
660+ System.out.println(format(expr, fargs));
661+
662+
663+ System.out.println(format("bin={0}",
664+ new Object[] {new byte[] {0, 1, 2, 3, 4, 5}}));
665+
666+ System.out.println(format("bin=\\{anything}", null));
667+
668+ } catch (NamingException e) {
669+ e.printStackTrace();
670+ }
671+ }
672+*/
673+
674+}