1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.api.ldap.model.filter;
21
22
23 import java.text.ParseException;
24
25 import org.apache.directory.api.i18n.I18n;
26 import org.apache.directory.api.ldap.model.entry.AttributeUtils;
27 import org.apache.directory.api.ldap.model.entry.BinaryValue;
28 import org.apache.directory.api.ldap.model.entry.StringValue;
29 import org.apache.directory.api.ldap.model.entry.Value;
30 import org.apache.directory.api.ldap.model.exception.LdapException;
31 import org.apache.directory.api.ldap.model.schema.AttributeType;
32 import org.apache.directory.api.ldap.model.schema.SchemaManager;
33 import org.apache.directory.api.util.Chars;
34 import org.apache.directory.api.util.Hex;
35 import org.apache.directory.api.util.Position;
36 import org.apache.directory.api.util.Strings;
37 import org.apache.directory.api.util.Unicode;
38
39
40
41
42
43
44
45 public final class FilterParser
46 {
47 private FilterParser()
48 {
49 }
50
51
52
53
54
55
56
57
58
59 private static ExprNode parseExtensible( SchemaManager schemaManager, String attribute, byte[] filter,
60 Position pos, boolean relaxed ) throws LdapException, ParseException
61 {
62 ExtensibleNode node;
63
64 if ( schemaManager != null )
65 {
66 AttributeType attributeType = schemaManager.getAttributeType( attribute );
67
68 if ( attributeType != null )
69 {
70 node = new ExtensibleNode( attributeType );
71 }
72 else
73 {
74 return UndefinedNode.UNDEFINED_NODE;
75 }
76 }
77 else
78 {
79 node = new ExtensibleNode( attribute );
80 }
81
82 if ( attribute != null )
83 {
84
85 if ( Strings.areEquals( filter, pos.start, "dn" ) >= 0 )
86 {
87
88 node.setDnAttributes( true );
89 pos.start += 2;
90 }
91 else
92 {
93
94 pos.start--;
95 }
96
97
98 if ( Strings.byteAt( filter, pos.start ) == ':' )
99 {
100 pos.start++;
101
102 if ( Strings.byteAt( filter, pos.start ) == '=' )
103 {
104 pos.start++;
105
106
107 node.setValue( parseAssertionValue( schemaManager, filter, pos ) );
108
109 return node;
110 }
111 else
112 {
113 String matchingRuleId = AttributeUtils.parseAttribute( filter, pos, false, relaxed );
114
115 node.setMatchingRuleId( matchingRuleId );
116
117 if ( Strings.areEquals( filter, pos.start, ":=" ) >= 0 )
118 {
119 pos.start += 2;
120
121
122 node.setValue( parseAssertionValue( schemaManager, filter, pos ) );
123
124 return node;
125 }
126 else
127 {
128 throw new ParseException( I18n.err( I18n.ERR_04146 ), pos.start );
129 }
130 }
131 }
132 else
133 {
134 throw new ParseException( I18n.err( I18n.ERR_04147 ), pos.start );
135 }
136 }
137 else
138 {
139
140 boolean oidRequested = false;
141
142
143 if ( Strings.areEquals( filter, pos.start, ":dn" ) >= 0 )
144 {
145
146 node.setDnAttributes( true );
147 pos.start += 3;
148 }
149 else
150 {
151 oidRequested = true;
152 }
153
154
155 if ( Strings.byteAt( filter, pos.start ) == ':' )
156 {
157 pos.start++;
158
159 if ( Strings.byteAt( filter, pos.start ) == '=' )
160 {
161 if ( oidRequested )
162 {
163 throw new ParseException( I18n.err( I18n.ERR_04148 ), pos.start );
164 }
165
166 pos.start++;
167
168
169 node.setValue( parseAssertionValue( schemaManager, null, filter, pos ) );
170
171 return node;
172 }
173 else
174 {
175 String matchingRuleId = AttributeUtils.parseAttribute( filter, pos, false, relaxed );
176
177 node.setMatchingRuleId( matchingRuleId );
178
179 if ( Strings.areEquals( filter, pos.start, ":=" ) >= 0 )
180 {
181 pos.start += 2;
182
183
184 node.setValue( parseAssertionValue( schemaManager, null, filter, pos ) );
185
186 return node;
187 }
188 else
189 {
190 throw new ParseException( I18n.err( I18n.ERR_04146 ), pos.start );
191 }
192 }
193 }
194 else
195 {
196 throw new ParseException( I18n.err( I18n.ERR_04147 ), pos.start );
197 }
198 }
199 }
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235 private static Value<?> parseAssertionValue( SchemaManager schemaManager, String attribute, byte[] filter,
236 Position pos ) throws ParseException
237 {
238 byte b = Strings.byteAt( filter, pos.start );
239
240
241 byte[] value = new byte[filter.length - pos.start];
242 int current = 0;
243
244 do
245 {
246 if ( Unicode.isUnicodeSubset( b ) )
247 {
248 value[current++] = b;
249 pos.start++;
250 }
251 else if ( Strings.isCharASCII( filter, pos.start, '\\' ) )
252 {
253
254 pos.start++;
255
256
257 if ( Chars.isHex( filter, pos.start ) )
258 {
259 pos.start++;
260 }
261 else
262 {
263 throw new ParseException( I18n.err( I18n.ERR_04149 ), pos.start );
264 }
265
266
267 if ( Chars.isHex( filter, pos.start ) )
268 {
269 value[current++] = Hex.getHexValue( filter[pos.start - 1], filter[pos.start] );
270 pos.start++;
271 }
272 else
273 {
274 throw new ParseException( I18n.err( I18n.ERR_04149 ), pos.start );
275 }
276 }
277 else
278 {
279
280 break;
281 }
282
283 b = Strings.byteAt( filter, pos.start );
284 }
285 while ( b != '\0' );
286
287 if ( current != 0 )
288 {
289 byte[] result = new byte[current];
290 System.arraycopy( value, 0, result, 0, current );
291
292 if ( schemaManager != null )
293 {
294 AttributeType attributeType = schemaManager.getAttributeType( attribute );
295
296 if ( attributeType == null )
297 {
298 return new BinaryValue( result );
299 }
300
301 if ( attributeType.getSyntax().isHumanReadable() )
302 {
303 return new StringValue( Strings.utf8ToString( result ) );
304 }
305 else
306 {
307 return new BinaryValue( result );
308 }
309 }
310 else
311 {
312 return new BinaryValue( result );
313 }
314 }
315 else
316 {
317 if ( schemaManager != null )
318 {
319 AttributeType attributeType = schemaManager.getAttributeType( attribute );
320
321 if ( attributeType.getEquality().getSyntax().isHumanReadable() )
322 {
323 return new StringValue( ( String ) null );
324 }
325 else
326 {
327 return new BinaryValue( null );
328 }
329 }
330 else
331 {
332 return new BinaryValue( ( byte[] ) null );
333 }
334 }
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370 private static Value<?> parseAssertionValue( SchemaManager schemaManager, byte[] filter, Position pos )
371 throws ParseException
372 {
373 byte b = Strings.byteAt( filter, pos.start );
374
375
376 byte[] value = new byte[filter.length - pos.start];
377 int current = 0;
378
379 do
380 {
381 if ( Unicode.isUnicodeSubset( b ) )
382 {
383 value[current++] = b;
384 pos.start++;
385 }
386 else if ( Strings.isCharASCII( filter, pos.start, '\\' ) )
387 {
388
389 pos.start++;
390
391
392 if ( Chars.isHex( filter, pos.start ) )
393 {
394 pos.start++;
395 }
396 else
397 {
398 throw new ParseException( I18n.err( I18n.ERR_04149 ), pos.start );
399 }
400
401
402 if ( Chars.isHex( filter, pos.start ) )
403 {
404 value[current++] = Hex.getHexValue( filter[pos.start - 1], filter[pos.start] );
405 pos.start++;
406 }
407 else
408 {
409 throw new ParseException( I18n.err( I18n.ERR_04149 ), pos.start );
410 }
411 }
412 else
413 {
414
415 break;
416 }
417
418 b = Strings.byteAt( filter, pos.start );
419 }
420 while ( b != '\0' );
421
422 if ( current != 0 )
423 {
424 byte[] result = new byte[current];
425 System.arraycopy( value, 0, result, 0, current );
426
427 return new BinaryValue( result );
428 }
429 else
430 {
431 return new BinaryValue( null );
432 }
433 }
434
435
436
437
438
439 private static ExprNode parseSubstring( SchemaManager schemaManager, String attribute, Value<?> initial,
440 byte[] filter, Position pos )
441 throws ParseException, LdapException
442 {
443 if ( Strings.isCharASCII( filter, pos.start, '*' ) )
444 {
445
446 SubstringNode node;
447
448 if ( schemaManager != null )
449 {
450 AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( attribute );
451
452 if ( attributeType != null )
453 {
454 node = new SubstringNode( schemaManager.lookupAttributeTypeRegistry( attribute ) );
455 }
456 else
457 {
458 return null;
459 }
460 }
461 else
462 {
463 node = new SubstringNode( attribute );
464 }
465
466 if ( ( initial != null ) && !initial.isNull() )
467 {
468
469
470 String initialStr = initial.getString();
471 node.setInitial( initialStr );
472 }
473
474 pos.start++;
475
476
477 while ( true )
478 {
479 Value<?> assertionValue = parseAssertionValue( schemaManager, attribute, filter, pos );
480
481
482 if ( Strings.isCharASCII( filter, pos.start, ')' ) )
483 {
484
485
486 if ( !assertionValue.isNull() )
487 {
488 String finalStr = assertionValue.getString();
489 node.setFinal( finalStr );
490 }
491
492 return node;
493 }
494 else if ( Strings.isCharASCII( filter, pos.start, '*' ) )
495 {
496
497
498
499 if ( !assertionValue.isNull() )
500 {
501 String anyStr = assertionValue.getString();
502 node.addAny( anyStr );
503 }
504
505 pos.start++;
506 }
507 else
508 {
509
510 throw new ParseException( I18n.err( I18n.ERR_04150 ), pos.start );
511 }
512 }
513 }
514 else
515 {
516
517 throw new ParseException( I18n.err( I18n.ERR_04150 ), pos.start );
518 }
519 }
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546 @SuppressWarnings(
547 { "rawtypes", "unchecked" })
548 private static ExprNode parsePresenceEqOrSubstring( SchemaManager schemaManager, String attribute, byte[] filter,
549 Position pos )
550 throws ParseException, LdapException
551 {
552 if ( Strings.isCharASCII( filter, pos.start, '*' ) )
553 {
554
555 pos.start++;
556
557 if ( Strings.isCharASCII( filter, pos.start, ')' ) )
558 {
559
560 if ( schemaManager != null )
561 {
562 AttributeType attributeType = schemaManager.getAttributeType( attribute );
563
564 if ( attributeType != null )
565 {
566 return new PresenceNode( attributeType );
567 }
568 else
569 {
570 return null;
571 }
572 }
573 else
574 {
575 return new PresenceNode( attribute );
576 }
577 }
578 else
579 {
580
581
582 pos.start--;
583
584 return parseSubstring( schemaManager, attribute, null, filter, pos );
585 }
586 }
587 else if ( Strings.isCharASCII( filter, pos.start, ')' ) )
588 {
589
590 if ( schemaManager != null )
591 {
592 AttributeType attributeType = schemaManager.getAttributeType( attribute );
593
594 if ( attributeType != null )
595 {
596 return new EqualityNode( attributeType, new BinaryValue( ( byte[] ) null ) );
597 }
598
599 else
600 {
601 return null;
602 }
603 }
604 else
605 {
606 return new EqualityNode( attribute, new BinaryValue( ( byte[] ) null ) );
607 }
608 }
609 else
610 {
611
612 Value<?> value = parseAssertionValue( schemaManager, attribute, filter, pos );
613
614
615 if ( Strings.isCharASCII( filter, pos.start, ')' ) )
616 {
617
618 if ( schemaManager != null )
619 {
620 AttributeType attributeType = schemaManager.getAttributeType( attribute );
621
622 if ( attributeType != null )
623 {
624 return new EqualityNode( attributeType, value );
625 }
626 else
627 {
628 return null;
629 }
630 }
631 else
632 {
633 return new EqualityNode( attribute, value );
634 }
635 }
636
637 return parseSubstring( schemaManager, attribute, value, filter, pos );
638 }
639 }
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655 @SuppressWarnings(
656 { "rawtypes", "unchecked" })
657 private static ExprNode parseItem( SchemaManager schemaManager, byte[] filter, Position pos, byte b,
658 boolean relaxed ) throws ParseException, LdapException
659 {
660 String attribute;
661
662 if ( b == '\0' )
663 {
664 throw new ParseException( I18n.err( I18n.ERR_04151 ), pos.start );
665 }
666
667 if ( b == ':' )
668 {
669
670 return parseExtensible( schemaManager, null, filter, pos, relaxed );
671 }
672 else
673 {
674
675 attribute = AttributeUtils.parseAttribute( filter, pos, true, relaxed );
676
677
678 skipWhiteSpaces( filter, pos );
679
680
681 byte currentByte = Strings.byteAt( filter, pos.start );
682
683 switch ( currentByte )
684 {
685 case '=':
686
687 pos.start++;
688
689 return parsePresenceEqOrSubstring( schemaManager, attribute, filter, pos );
690
691 case '~':
692
693 pos.start++;
694
695
696 if ( !Strings.isCharASCII( filter, pos.start, '=' ) )
697 {
698 throw new ParseException( I18n.err( I18n.ERR_04152 ), pos.start );
699 }
700
701 pos.start++;
702
703
704 if ( schemaManager == null )
705 {
706 return new ApproximateNode( attribute, parseAssertionValue( schemaManager, attribute, filter,
707 pos ) );
708 }
709 else
710 {
711 AttributeType attributeType = schemaManager.getAttributeType( attribute );
712
713 if ( attributeType != null )
714 {
715 return new ApproximateNode( attributeType, parseAssertionValue( schemaManager, attribute,
716 filter, pos ) );
717 }
718 else
719 {
720 return UndefinedNode.UNDEFINED_NODE;
721 }
722 }
723
724 case '>':
725
726 pos.start++;
727
728
729 if ( !Strings.isCharASCII( filter, pos.start, '=' ) )
730 {
731 throw new ParseException( I18n.err( I18n.ERR_04152 ), pos.start );
732 }
733
734 pos.start++;
735
736
737 if ( schemaManager == null )
738 {
739 return new GreaterEqNode( attribute,
740 parseAssertionValue( schemaManager, attribute, filter, pos ) );
741 }
742 else
743 {
744 AttributeType attributeType = schemaManager.getAttributeType( attribute );
745
746 if ( attributeType != null )
747 {
748 return new GreaterEqNode( attributeType, parseAssertionValue( schemaManager, attribute,
749 filter, pos ) );
750 }
751 else
752 {
753 return UndefinedNode.UNDEFINED_NODE;
754 }
755 }
756
757 case '<':
758
759 pos.start++;
760
761
762 if ( !Strings.isCharASCII( filter, pos.start, '=' ) )
763 {
764 throw new ParseException( I18n.err( I18n.ERR_04152 ), pos.start );
765 }
766
767 pos.start++;
768
769
770 if ( schemaManager == null )
771 {
772 return new LessEqNode( attribute, parseAssertionValue( schemaManager, attribute, filter, pos ) );
773 }
774 else
775 {
776 AttributeType attributeType = schemaManager.getAttributeType( attribute );
777
778 if ( attributeType != null )
779 {
780 return new LessEqNode( attributeType, parseAssertionValue( schemaManager, attribute,
781 filter, pos ) );
782 }
783 else
784 {
785 return UndefinedNode.UNDEFINED_NODE;
786 }
787 }
788
789 case ':':
790
791 pos.start++;
792 return parseExtensible( schemaManager, attribute, filter, pos, relaxed );
793
794 default:
795
796 throw new ParseException( I18n.err( I18n.ERR_04153 ), pos.start );
797 }
798 }
799 }
800
801
802
803
804
805
806
807
808
809
810
811
812 private static ExprNode parseBranchNode( SchemaManager schemaManager, ExprNode node, byte[] filter, Position pos,
813 boolean relaxed ) throws ParseException, LdapException
814 {
815 BranchNode branchNode = ( BranchNode ) node;
816 int nbChildren = 0;
817
818
819 ExprNode child = parseFilterInternal( schemaManager, filter, pos, relaxed );
820
821 if ( child != UndefinedNode.UNDEFINED_NODE )
822 {
823
824 branchNode.addNode( child );
825
826 if ( branchNode instanceof NotNode )
827 {
828 return node;
829 }
830
831 nbChildren++;
832 }
833 else if ( node instanceof AndNode )
834 {
835 return UndefinedNode.UNDEFINED_NODE;
836 }
837
838
839 while ( ( child = parseFilterInternal( schemaManager, filter, pos, relaxed ) ) != UndefinedNode.UNDEFINED_NODE )
840 {
841
842 if ( child != null )
843 {
844 branchNode.addNode( child );
845 nbChildren++;
846 }
847 else if ( node instanceof AndNode )
848 {
849 return UndefinedNode.UNDEFINED_NODE;
850 }
851 }
852
853 if ( nbChildren > 0 )
854 {
855 return node;
856 }
857 else
858 {
859 return UndefinedNode.UNDEFINED_NODE;
860 }
861 }
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878 private static ExprNode parseFilterComp( SchemaManager schemaManager, byte[] filter, Position pos,
879 boolean relaxed ) throws ParseException, LdapException
880 {
881 ExprNode node;
882
883 if ( pos.start == pos.length )
884 {
885 throw new ParseException( I18n.err( I18n.ERR_04154 ), pos.start );
886 }
887
888 byte c = Strings.byteAt( filter, pos.start );
889
890 switch ( c )
891 {
892 case '&':
893
894 pos.start++;
895
896
897 skipWhiteSpaces( filter, pos );
898
899 node = new AndNode();
900 node = parseBranchNode( schemaManager, node, filter, pos, relaxed );
901 break;
902
903 case '|':
904
905 pos.start++;
906
907
908 skipWhiteSpaces( filter, pos );
909
910 node = new OrNode();
911 node = parseBranchNode( schemaManager, node, filter, pos, relaxed );
912 break;
913
914 case '!':
915
916 pos.start++;
917
918
919 skipWhiteSpaces( filter, pos );
920
921 node = new NotNode();
922 node = parseBranchNode( schemaManager, node, filter, pos, relaxed );
923 break;
924
925 default:
926
927 node = parseItem( schemaManager, filter, pos, c, relaxed );
928 break;
929
930 }
931
932 return node;
933 }
934
935
936
937
938
939
940
941 private static void skipWhiteSpaces( byte[] filter, Position pos )
942 {
943 while ( Strings.isCharASCII( filter, pos.start, ' ' )
944 || Strings.isCharASCII( filter, pos.start, '\t' )
945 || Strings.isCharASCII( filter, pos.start, '\n' ) )
946 {
947 pos.start++;
948 }
949 }
950
951
952
953
954
955
956 private static ExprNode parseFilterInternal( SchemaManager schemaManager, byte[] filter, Position pos,
957 boolean relaxed ) throws ParseException, LdapException
958 {
959
960 skipWhiteSpaces( filter, pos );
961
962
963 if ( !Strings.isCharASCII( filter, pos.start, '(' ) )
964 {
965
966 if ( ( pos.start == 0 ) && ( pos.length != 0 ) )
967 {
968 throw new ParseException( I18n.err( I18n.ERR_04155 ), 0 );
969 }
970 else
971 {
972 return UndefinedNode.UNDEFINED_NODE;
973 }
974 }
975
976 pos.start++;
977
978
979 skipWhiteSpaces( filter, pos );
980
981
982 ExprNode node = parseFilterComp( schemaManager, filter, pos, relaxed );
983
984 if ( node == UndefinedNode.UNDEFINED_NODE )
985 {
986 return UndefinedNode.UNDEFINED_NODE;
987 }
988
989
990 skipWhiteSpaces( filter, pos );
991
992
993 if ( !Strings.isCharASCII( filter, pos.start, ')' ) )
994 {
995 throw new ParseException( I18n.err( I18n.ERR_04157 ), pos.start );
996 }
997
998 pos.start++;
999
1000
1001 skipWhiteSpaces( filter, pos );
1002
1003 return node;
1004 }
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014 public static ExprNode parse( String filter ) throws ParseException
1015 {
1016 return parse( null, Strings.getBytesUtf8( filter ), false );
1017 }
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027 public static ExprNode parse( byte[] filter ) throws ParseException
1028 {
1029 return parse( null, filter, false );
1030 }
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041 public static ExprNode parse( SchemaManager schemaManager, String filter ) throws ParseException
1042 {
1043 return parse( schemaManager, Strings.getBytesUtf8( filter ), false );
1044 }
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055 public static ExprNode parse( SchemaManager schemaManager, byte[] filter ) throws ParseException
1056 {
1057 return parse( schemaManager, filter, false );
1058 }
1059
1060
1061 private static ExprNode parse( SchemaManager schemaManager, byte[] filter, boolean relaxed )
1062 throws ParseException
1063 {
1064
1065 if ( Strings.isEmpty( filter ) )
1066 {
1067 throw new ParseException( I18n.err( I18n.ERR_04158 ), 0 );
1068 }
1069
1070 Position pos = new Position();
1071 pos.start = 0;
1072 pos.end = 0;
1073 pos.length = filter.length;
1074
1075 try
1076 {
1077 ExprNode node = parseFilterInternal( schemaManager, filter, pos, relaxed );
1078
1079 if ( node == UndefinedNode.UNDEFINED_NODE )
1080 {
1081 return null;
1082 }
1083 else
1084 {
1085 return node;
1086 }
1087 }
1088 catch ( LdapException le )
1089 {
1090 throw new ParseException( le.getMessage(), pos.start );
1091 }
1092 }
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104 public static ExprNode parse( SchemaManager schemaManager, String filter, Position pos ) throws ParseException
1105 {
1106
1107 if ( Strings.isEmpty( filter ) )
1108 {
1109 throw new ParseException( I18n.err( I18n.ERR_04158 ), 0 );
1110 }
1111
1112 pos.start = 0;
1113 pos.end = 0;
1114 pos.length = filter.length();
1115
1116 try
1117 {
1118 return parseFilterInternal( schemaManager, Strings.getBytesUtf8( filter ), pos, false );
1119 }
1120 catch ( LdapException le )
1121 {
1122 throw new ParseException( le.getMessage(), pos.start );
1123 }
1124 }
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137 public static ExprNode parse( String filter, boolean relaxed ) throws ParseException
1138 {
1139 return parse( null, Strings.getBytesUtf8( filter ), relaxed );
1140 }
1141 }