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.url;
21
22
23 import java.io.ByteArrayOutputStream;
24 import java.io.UnsupportedEncodingException;
25 import java.text.ParseException;
26 import java.util.ArrayList;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32
33 import org.apache.directory.api.i18n.I18n;
34 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
35 import org.apache.directory.api.ldap.model.exception.LdapURLEncodingException;
36 import org.apache.directory.api.ldap.model.exception.LdapUriException;
37 import org.apache.directory.api.ldap.model.exception.UrlDecoderException;
38 import org.apache.directory.api.ldap.model.filter.FilterParser;
39 import org.apache.directory.api.ldap.model.message.SearchScope;
40 import org.apache.directory.api.ldap.model.name.Dn;
41 import org.apache.directory.api.util.Chars;
42 import org.apache.directory.api.util.StringConstants;
43 import org.apache.directory.api.util.Strings;
44 import org.apache.directory.api.util.Unicode;
45
46 import sun.net.util.IPAddressUtil;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 public class LdapUrl
101 {
102
103 public static final String LDAPS_SCHEME = "ldaps://";
104
105
106 public static final String LDAP_SCHEME = "ldap://";
107
108
109 public static final LdapUrl EMPTY_URL = new LdapUrl();
110
111
112 private String scheme;
113
114
115 private String host;
116
117
118 private int port;
119
120
121 private Dn dn;
122
123
124 private List<String> attributes;
125
126
127 private SearchScope scope;
128
129
130 private String filter;
131
132
133 private List<Extension> extensionList;
134
135
136 private String string;
137
138
139 private byte[] bytes;
140
141
142 private boolean forceScopeRendering;
143
144
145 private HostTypeEnum hostType = HostTypeEnum.REGULAR_NAME;
146
147
148 private static final Pattern ATTRIBUTE = Pattern
149 .compile( "(?:(?:\\d|[1-9]\\d*)(?:\\.(?:\\d|[1-9]\\d*))+)|(?:[a-zA-Z][a-zA-Z0-9-]*)" );
150
151
152
153
154
155 public LdapUrl()
156 {
157 scheme = LDAP_SCHEME;
158 host = null;
159 port = -1;
160 dn = null;
161 attributes = new ArrayList<String>();
162 scope = SearchScope.OBJECT;
163 filter = null;
164 extensionList = new ArrayList<Extension>( 2 );
165 }
166
167
168
169
170
171
172
173
174 private void parse( char[] chars ) throws LdapURLEncodingException
175 {
176 scheme = LDAP_SCHEME;
177 host = null;
178 port = -1;
179 dn = null;
180 attributes = new ArrayList<String>();
181 scope = SearchScope.OBJECT;
182 filter = null;
183 extensionList = new ArrayList<Extension>( 2 );
184
185 if ( ( chars == null ) || ( chars.length == 0 ) )
186 {
187 host = "";
188 return;
189 }
190
191
192
193
194
195 int pos = 0;
196
197
198 if ( ( ( pos = Strings.areEquals( chars, 0, LDAP_SCHEME ) ) == StringConstants.NOT_EQUAL )
199 && ( ( pos = Strings.areEquals( chars, 0, LDAPS_SCHEME ) ) == StringConstants.NOT_EQUAL ) )
200 {
201 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04398 ) );
202 }
203 else
204 {
205 scheme = new String( chars, 0, pos );
206 }
207
208
209 if ( ( pos = parseHostPort( chars, pos ) ) == -1 )
210 {
211 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04399 ) );
212 }
213
214 if ( pos == chars.length )
215 {
216 return;
217 }
218
219
220 if ( !Chars.isCharASCII( chars, pos, '/' ) )
221 {
222 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04400, pos, chars[pos] ) );
223 }
224
225 pos++;
226
227 if ( pos == chars.length )
228 {
229 return;
230 }
231
232
233 if ( ( pos = parseDN( chars, pos ) ) == -1 )
234 {
235 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04401 ) );
236 }
237
238 if ( pos == chars.length )
239 {
240 return;
241 }
242
243
244 if ( !Chars.isCharASCII( chars, pos, '?' ) )
245 {
246 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04402, pos, chars[pos] ) );
247 }
248
249 pos++;
250
251 if ( ( pos = parseAttributes( chars, pos ) ) == -1 )
252 {
253 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04403 ) );
254 }
255
256 if ( pos == chars.length )
257 {
258 return;
259 }
260
261
262 if ( !Chars.isCharASCII( chars, pos, '?' ) )
263 {
264 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04402, pos, chars[pos] ) );
265 }
266
267 pos++;
268
269 if ( ( pos = parseScope( chars, pos ) ) == -1 )
270 {
271 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04404 ) );
272 }
273
274 if ( pos == chars.length )
275 {
276 return;
277 }
278
279
280 if ( !Chars.isCharASCII( chars, pos, '?' ) )
281 {
282 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04402, pos, chars[pos] ) );
283 }
284
285 pos++;
286
287 if ( pos == chars.length )
288 {
289 return;
290 }
291
292 if ( ( pos = parseFilter( chars, pos ) ) == -1 )
293 {
294 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04405 ) );
295 }
296
297 if ( pos == chars.length )
298 {
299 return;
300 }
301
302
303 if ( !Chars.isCharASCII( chars, pos, '?' ) )
304 {
305 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04402, pos, chars[pos] ) );
306 }
307
308 pos++;
309
310 if ( ( pos = parseExtensions( chars, pos ) ) == -1 )
311 {
312 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04406 ) );
313 }
314
315 if ( pos == chars.length )
316 {
317 return;
318 }
319 else
320 {
321 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04407 ) );
322 }
323 }
324
325
326
327
328
329
330
331
332 public LdapUrl( String string ) throws LdapURLEncodingException
333 {
334 if ( string == null )
335 {
336 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04408 ) );
337 }
338
339 try
340 {
341 bytes = string.getBytes( "UTF-8" );
342 this.string = string;
343 parse( string.toCharArray() );
344 }
345 catch ( UnsupportedEncodingException uee )
346 {
347 throw new LdapURLEncodingException( I18n.err( I18n.ERR_04409, string ) );
348 }
349 }
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373 private int parseHost( char[] chars, int pos )
374 {
375 int start = pos;
376
377
378
379
380
381 switch ( chars[pos] )
382 {
383 case '[' :
384
385 return parseIpLiteral( chars, pos+1 );
386
387 case '0' :
388 case '1' :
389 case '2' :
390 case '3' :
391 case '4' :
392 case '5' :
393 case '6' :
394 case '7' :
395 case '8' :
396 case '9' :
397
398
399 int currentPos = parseIPV4( chars, pos );
400
401 if ( currentPos != -1 )
402 {
403 host = new String( chars, start, currentPos - start );
404
405 return currentPos;
406 }
407 else
408 {
409
410 }
411
412 case 'a' : case 'b' : case 'c' : case 'd' : case 'e' :
413 case 'A' : case 'B' : case 'C' : case 'D' : case 'E' :
414 case 'f' : case 'g' : case 'h' : case 'i' : case 'j' :
415 case 'F' : case 'G' : case 'H' : case 'I' : case 'J' :
416 case 'k' : case 'l' : case 'm' : case 'n' : case 'o' :
417 case 'K' : case 'L' : case 'M' : case 'N' : case 'O' :
418 case 'p' : case 'q' : case 'r' : case 's' : case 't' :
419 case 'P' : case 'Q' : case 'R' : case 'S' : case 'T' :
420 case 'u' : case 'v' : case 'w' : case 'x' : case 'y' :
421 case 'U' : case 'V' : case 'W' : case 'X' : case 'Y' :
422 case 'z' : case 'Z' : case '-' : case '.' : case '_' :
423 case '~' : case '%' : case '!' : case '$' : case '&' :
424 case '\'' : case '(' : case ')' : case '*' : case '+' :
425 case ',' : case ';' : case '=' :
426
427 return parseRegName( chars, pos );
428 }
429
430 host = new String( chars, start, pos - start );
431
432 return pos;
433 }
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453 private int parseIpLiteral( char[] chars, int pos )
454 {
455 int start = pos;
456
457 if ( Chars.isCharASCII( chars, pos, 'v' ) )
458 {
459
460 pos++;
461 hostType = HostTypeEnum.IPV_FUTURE;
462
463 pos = parseIPvFuture( chars, pos );
464
465 if ( pos != -1 )
466 {
467
468 host = new String( chars, start, pos - start - 1 );
469 }
470
471 return pos;
472 }
473 else
474 {
475
476 hostType = HostTypeEnum.IPV6;
477
478 return parseIPV6( chars, pos );
479 }
480 }
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499 private int parseIPV6( char[] chars, int pos )
500 {
501
502 int start = pos;
503
504 while ( !Chars.isCharASCII( chars, pos, ']' ) )
505 {
506 pos++;
507 }
508
509 if ( Chars.isCharASCII( chars, pos, ']' ) )
510 {
511 String hostString = new String( chars, start, pos - start );
512
513 if ( sun.net.util.IPAddressUtil.isIPv6LiteralAddress( hostString ) )
514 {
515 host = hostString;
516
517 return pos + 1;
518 }
519 else
520 {
521 return -1;
522 }
523 }
524
525 return -1;
526 }
527
528
529
530
531
532
533
534
535
536 private int parseIPvFuture( char[] chars, int pos )
537 {
538
539 boolean hexFound = false;
540
541 while ( Chars.isHex( chars, pos ) )
542 {
543 hexFound = true;
544 pos++;
545 }
546
547 if ( ! hexFound )
548 {
549 return -1;
550 }
551
552
553 if ( !Chars.isCharASCII( chars, pos, '.' ) )
554 {
555 return -1;
556 }
557
558
559 boolean valueFound = false;
560
561 while ( !Chars.isCharASCII( chars, pos, ']' ) )
562 {
563 switch ( chars[pos] )
564 {
565
566
567 case 'a' : case 'b' : case 'c' : case 'd' : case 'e' :
568 case 'A' : case 'B' : case 'C' : case 'D' : case 'E' :
569 case 'f' : case 'g' : case 'h' : case 'i' : case 'j' :
570 case 'F' : case 'G' : case 'H' : case 'I' : case 'J' :
571 case 'k' : case 'l' : case 'm' : case 'n' : case 'o' :
572 case 'K' : case 'L' : case 'M' : case 'N' : case 'O' :
573 case 'p' : case 'q' : case 'r' : case 's' : case 't' :
574 case 'P' : case 'Q' : case 'R' : case 'S' : case 'T' :
575 case 'u' : case 'v' : case 'w' : case 'x' : case 'y' :
576 case 'U' : case 'V' : case 'W' : case 'X' : case 'Y' :
577 case 'z' : case 'Z' :
578
579
580 case '0' : case '1' : case '2' : case '3' : case '4' :
581 case '5' : case '6' : case '7' : case '8' : case '9' :
582
583
584 case '-' : case '.' : case '_' : case '~' :
585
586
587 case '!' : case '$' : case '&' : case '\'' :
588 case '(' : case ')' : case '*' : case '+' : case ',' :
589 case ';' : case '=' :
590
591
592 case ':' :
593 pos++;
594 valueFound = true;
595 break;
596
597 default :
598
599 return -1;
600 }
601 }
602
603 if ( !valueFound )
604 {
605 return -1;
606 }
607
608 return pos;
609 }
610
611
612
613
614
615
616
617
618
619
620
621
622 private int parseRegName( char[] chars, int pos )
623 {
624 int start = pos;
625
626 while ( !Chars.isCharASCII( chars, pos, ':' ) && !Chars.isCharASCII( chars, pos, '/' ) && ( pos < chars.length ) )
627 {
628 switch ( chars[pos] )
629 {
630
631
632 case 'a' : case 'b' : case 'c' : case 'd' : case 'e' :
633 case 'A' : case 'B' : case 'C' : case 'D' : case 'E' :
634 case 'f' : case 'g' : case 'h' : case 'i' : case 'j' :
635 case 'F' : case 'G' : case 'H' : case 'I' : case 'J' :
636 case 'k' : case 'l' : case 'm' : case 'n' : case 'o' :
637 case 'K' : case 'L' : case 'M' : case 'N' : case 'O' :
638 case 'p' : case 'q' : case 'r' : case 's' : case 't' :
639 case 'P' : case 'Q' : case 'R' : case 'S' : case 'T' :
640 case 'u' : case 'v' : case 'w' : case 'x' : case 'y' :
641 case 'U' : case 'V' : case 'W' : case 'X' : case 'Y' :
642 case 'z' : case 'Z' :
643
644
645 case '0' : case '1' : case '2' : case '3' : case '4' :
646 case '5' : case '6' : case '7' : case '8' : case '9' :
647
648
649 case '-' : case '.' : case '_' : case '~' :
650
651
652 case '!' : case '$' : case '&' : case '\'' :
653 case '(' : case ')' : case '*' : case '+' : case ',' :
654 case ';' : case '=' :
655 pos++;
656 break;
657
658
659 case '%' :
660 if ( Chars.isHex( chars, pos + 1 ) && Chars.isHex( chars, pos + 2 ) )
661 {
662 pos+=3;
663 }
664 else
665 {
666 return -1;
667 }
668
669 default :
670
671 return -1;
672 }
673 }
674
675 host = new String( chars, start, pos - start );
676 hostType = HostTypeEnum.REGULAR_NAME;
677
678 return pos;
679 }
680
681
682
683
684
685
686
687
688
689
690
691
692
693 private int parseIPV4( char[] chars, int pos )
694 {
695 int[] ipElem = new int[4];
696 int ipPos = pos;
697 int start = pos;
698
699 for ( int i = 0; i < 3; i++ )
700 {
701 ipPos = parseDecOctet( chars, ipPos, ipElem, i );
702
703 if ( ipPos == -1 )
704 {
705
706 return -1;
707 }
708
709 if ( chars[ipPos] != '.' )
710 {
711
712 return -1;
713 }
714 else
715 {
716 ipPos++;
717 }
718 }
719
720 ipPos = parseDecOctet( chars, ipPos, ipElem, 3 );
721
722 if ( ipPos == -1 )
723 {
724
725 return -1;
726 }
727 else
728 {
729 pos = ipPos;
730 host = new String( chars, start, pos - start );
731 hostType = HostTypeEnum.IPV4;
732
733 return pos;
734 }
735 }
736
737
738
739
740
741
742
743
744 private int parseDecOctet( char[] chars, int pos, int[] ipElem, int octetNb )
745 {
746 int ipElemValue = 0;
747 boolean ipElemSeen = false;
748 boolean hasTailingZeroes = false;
749
750 while ( Chars.isDigit( chars, pos ) )
751 {
752 ipElemSeen = true;
753 ipElemValue = ( ipElemValue * 10 ) + ( chars[pos] - '0' );
754
755 if ( ( chars[pos] == '0' ) && hasTailingZeroes && ( ipElemValue > 0 ) )
756 {
757
758 return -1;
759 }
760
761 if ( ipElemValue > 255 )
762 {
763
764 return -1;
765 }
766
767 pos++;
768 }
769
770 if ( ipElemSeen )
771 {
772 ipElem[octetNb] = ipElemValue;
773
774 return pos;
775 }
776 else
777 {
778 return -1;
779 }
780 }
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796 private int parsePort( char[] chars, int pos )
797 {
798
799 if ( !Chars.isDigit( chars, pos ) )
800 {
801 return -1;
802 }
803
804 port = chars[pos] - '0';
805
806 pos++;
807
808 while ( Chars.isDigit( chars, pos ) )
809 {
810 port = ( port * 10 ) + ( chars[pos] - '0' );
811
812 if ( port > 65535 )
813 {
814 return -1;
815 }
816
817 pos++;
818 }
819
820 return pos;
821 }
822
823
824
825
826
827
828
829
830
831
832
833
834
835 private int parseHostPort( char[] chars, int pos )
836 {
837 int hostPos = pos;
838
839 if ( ( pos = parseHost( chars, pos ) ) == -1 )
840 {
841 return -1;
842 }
843
844
845 if ( Chars.isCharASCII( chars, pos, ':' ) )
846 {
847 if ( pos == hostPos )
848 {
849
850 return -1;
851 }
852
853 pos++;
854 }
855 else
856 {
857 return pos;
858 }
859
860
861 if ( ( pos = parsePort( chars, pos ) ) == -1 )
862 {
863 return -1;
864 }
865
866 return pos;
867 }
868
869
870
871
872
873
874
875
876
877 private static byte[] getAsciiBytes( final String data ) throws UrlDecoderException
878 {
879
880 if ( data == null )
881 {
882 throw new IllegalArgumentException( I18n.err( I18n.ERR_04411 ) );
883 }
884
885 try
886 {
887 return data.getBytes( "US-ASCII" );
888 }
889 catch ( UnsupportedEncodingException e )
890 {
891 throw new UrlDecoderException( I18n.err( I18n.ERR_04413 ) );
892 }
893 }
894
895
896
897
898
899
900
901
902
903
904
905 private static byte[] decodeUrl( byte[] bytes ) throws UrlDecoderException
906 {
907 if ( bytes == null )
908 {
909 return StringConstants.EMPTY_BYTES;
910 }
911
912 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
913
914 for ( int i = 0; i < bytes.length; i++ )
915 {
916 int b = bytes[i];
917
918 if ( b == '%' )
919 {
920 try
921 {
922 int u = Character.digit( ( char ) bytes[++i], 16 );
923 int l = Character.digit( ( char ) bytes[++i], 16 );
924
925 if ( ( u == -1 ) || ( l == -1 ) )
926 {
927 throw new UrlDecoderException( I18n.err( I18n.ERR_04414 ) );
928 }
929
930 buffer.write( ( char ) ( ( u << 4 ) + l ) );
931 }
932 catch ( ArrayIndexOutOfBoundsException e )
933 {
934 throw new UrlDecoderException( I18n.err( I18n.ERR_04414 ) );
935 }
936 }
937 else
938 {
939 buffer.write( b );
940 }
941 }
942
943 return buffer.toByteArray();
944 }
945
946
947
948
949
950
951
952
953
954
955 private static String decode( String escaped ) throws LdapUriException
956 {
957 try
958 {
959 byte[] rawdata = decodeUrl( getAsciiBytes( escaped ) );
960 return Strings.getString( rawdata, "UTF-8" );
961 }
962 catch ( UrlDecoderException e )
963 {
964 throw new LdapUriException( e.getMessage(), e );
965 }
966 }
967
968
969
970
971
972
973
974
975
976
977 private int parseDN( char[] chars, int pos )
978 {
979
980 int end = pos;
981
982 for ( int i = pos; ( i < chars.length ) && ( chars[i] != '?' ); i++ )
983 {
984 end++;
985 }
986
987 try
988 {
989 String dnStr = new String( chars, pos, end - pos );
990 dn = new Dn( decode( dnStr ) );
991 }
992 catch ( LdapUriException ue )
993 {
994 return -1;
995 }
996 catch ( LdapInvalidDnException de )
997 {
998 return -1;
999 }
1000
1001 return end;
1002 }
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019 private void validateAttribute( String attribute ) throws LdapURLEncodingException
1020 {
1021 Matcher matcher = ATTRIBUTE.matcher( attribute );
1022
1023 if ( !matcher.matches() )
1024 {
1025 throw new LdapURLEncodingException( "Attribute " + attribute + " is invalid" );
1026 }
1027 }
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037 private int parseAttributes( char[] chars, int pos )
1038 {
1039 int start = pos;
1040 int end = pos;
1041 Set<String> hAttributes = new HashSet<String>();
1042 boolean hadComma = false;
1043
1044 try
1045 {
1046
1047 for ( int i = pos; ( i < chars.length ) && ( chars[i] != '?' ); i++ )
1048 {
1049
1050 if ( Chars.isCharASCII( chars, i, ',' ) )
1051 {
1052 hadComma = true;
1053
1054 if ( ( end - start ) == 0 )
1055 {
1056
1057
1058 return -1;
1059 }
1060 else
1061 {
1062 String attribute = null;
1063
1064
1065 attribute = new String( chars, start, end - start ).trim();
1066
1067 if ( attribute.length() == 0 )
1068 {
1069 return -1;
1070 }
1071
1072
1073 try
1074 {
1075 validateAttribute( attribute );
1076 }
1077 catch ( LdapURLEncodingException luee )
1078 {
1079 return -1;
1080 }
1081
1082 String decodedAttr = decode( attribute );
1083
1084 if ( !hAttributes.contains( decodedAttr ) )
1085 {
1086 attributes.add( decodedAttr );
1087 hAttributes.add( decodedAttr );
1088 }
1089 }
1090
1091 start = i + 1;
1092 }
1093 else
1094 {
1095 hadComma = false;
1096 }
1097
1098 end++;
1099 }
1100
1101 if ( hadComma )
1102 {
1103
1104
1105
1106 return -1;
1107 }
1108 else
1109 {
1110
1111 if ( end == start )
1112 {
1113
1114
1115 return end;
1116 }
1117
1118
1119
1120 String attribute = null;
1121
1122 attribute = new String( chars, start, end - start ).trim();
1123
1124 if ( attribute.length() == 0 )
1125 {
1126 return -1;
1127 }
1128
1129 String decodedAttr = decode( attribute );
1130
1131 if ( !hAttributes.contains( decodedAttr ) )
1132 {
1133 attributes.add( decodedAttr );
1134 hAttributes.add( decodedAttr );
1135 }
1136 }
1137
1138 return end;
1139 }
1140 catch ( LdapUriException ue )
1141 {
1142 return -1;
1143 }
1144 }
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154 private int parseFilter( char[] chars, int pos )
1155 {
1156
1157 int end = pos;
1158
1159 for ( int i = pos; ( i < chars.length ) && ( chars[i] != '?' ); i++ )
1160 {
1161 end++;
1162 }
1163
1164 if ( end == pos )
1165 {
1166
1167 return end;
1168 }
1169
1170 try
1171 {
1172 filter = decode( new String( chars, pos, end - pos ) );
1173 FilterParser.parse( null, filter );
1174 }
1175 catch ( LdapUriException ue )
1176 {
1177 return -1;
1178 }
1179 catch ( ParseException pe )
1180 {
1181 return -1;
1182 }
1183
1184 return end;
1185 }
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195 private int parseScope( char[] chars, int pos )
1196 {
1197
1198 if ( Chars.isCharASCII( chars, pos, 'b' ) || Chars.isCharASCII( chars, pos, 'B' ) )
1199 {
1200 pos++;
1201
1202 if ( Chars.isCharASCII( chars, pos, 'a' ) || Chars.isCharASCII( chars, pos, 'A' ) )
1203 {
1204 pos++;
1205
1206 if ( Chars.isCharASCII( chars, pos, 's' ) || Chars.isCharASCII( chars, pos, 'S' ) )
1207 {
1208 pos++;
1209
1210 if ( Chars.isCharASCII( chars, pos, 'e' ) || Chars.isCharASCII( chars, pos, 'E' ) )
1211 {
1212 pos++;
1213 scope = SearchScope.OBJECT;
1214 return pos;
1215 }
1216 }
1217 }
1218 }
1219 else if ( Chars.isCharASCII( chars, pos, 'o' ) || Chars.isCharASCII( chars, pos, 'O' ) )
1220 {
1221 pos++;
1222
1223 if ( Chars.isCharASCII( chars, pos, 'n' ) || Chars.isCharASCII( chars, pos, 'N' ) )
1224 {
1225 pos++;
1226
1227 if ( Chars.isCharASCII( chars, pos, 'e' ) || Chars.isCharASCII( chars, pos, 'E' ) )
1228 {
1229 pos++;
1230
1231 scope = SearchScope.ONELEVEL;
1232 return pos;
1233 }
1234 }
1235 }
1236 else if ( Chars.isCharASCII( chars, pos, 's' ) || Chars.isCharASCII( chars, pos, 'S' ) )
1237 {
1238 pos++;
1239
1240 if ( Chars.isCharASCII( chars, pos, 'u' ) || Chars.isCharASCII( chars, pos, 'U' ) )
1241 {
1242 pos++;
1243
1244 if ( Chars.isCharASCII( chars, pos, 'b' ) || Chars.isCharASCII( chars, pos, 'B' ) )
1245 {
1246 pos++;
1247
1248 scope = SearchScope.SUBTREE;
1249 return pos;
1250 }
1251 }
1252 }
1253 else if ( Chars.isCharASCII( chars, pos, '?' ) )
1254 {
1255
1256 return pos;
1257 }
1258 else if ( pos == chars.length )
1259 {
1260
1261 return pos;
1262 }
1263
1264
1265 return -1;
1266 }
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281 private int parseExtensions( char[] chars, int pos )
1282 {
1283 int start = pos;
1284 boolean isCritical = false;
1285 boolean isNewExtension = true;
1286 boolean hasValue = false;
1287 String extension = null;
1288 String value = null;
1289
1290 if ( pos == chars.length )
1291 {
1292 return pos;
1293 }
1294
1295 try
1296 {
1297 for ( int i = pos; ( i < chars.length ); i++ )
1298 {
1299 if ( Chars.isCharASCII( chars, i, ',' ) )
1300 {
1301 if ( isNewExtension )
1302 {
1303
1304
1305 return -1;
1306 }
1307 else
1308 {
1309 if ( extension == null )
1310 {
1311 extension = decode( new String( chars, start, i - start ) ).trim();
1312 }
1313 else
1314 {
1315 value = decode( new String( chars, start, i - start ) ).trim();
1316 }
1317
1318 Extension ext = new Extension( isCritical, extension, value );
1319 extensionList.add( ext );
1320
1321 isNewExtension = true;
1322 hasValue = false;
1323 isCritical = false;
1324 start = i + 1;
1325 extension = null;
1326 value = null;
1327 }
1328 }
1329 else if ( Chars.isCharASCII( chars, i, '=' ) )
1330 {
1331 if ( hasValue )
1332 {
1333
1334 continue;
1335 }
1336
1337
1338 extension = decode( new String( chars, start, i - start ) ).trim();
1339
1340 if ( extension.length() == 0 )
1341 {
1342
1343 return -1;
1344 }
1345
1346 hasValue = true;
1347 start = i + 1;
1348 }
1349 else if ( Chars.isCharASCII( chars, i, '!' ) )
1350 {
1351 if ( hasValue )
1352 {
1353
1354 continue;
1355 }
1356
1357 if ( !isNewExtension )
1358 {
1359
1360 return -1;
1361 }
1362
1363 isCritical = true;
1364 start++;
1365 }
1366 else
1367 {
1368 isNewExtension = false;
1369 }
1370 }
1371
1372 if ( extension == null )
1373 {
1374 extension = decode( new String( chars, start, chars.length - start ) ).trim();
1375 }
1376 else
1377 {
1378 value = decode( new String( chars, start, chars.length - start ) ).trim();
1379 }
1380
1381 Extension ext = new Extension( isCritical, extension, value );
1382 extensionList.add( ext );
1383
1384 return chars.length;
1385 }
1386 catch ( LdapUriException ue )
1387 {
1388 return -1;
1389 }
1390 }
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438 public static String urlEncode( String url, boolean doubleEncode )
1439 {
1440 StringBuffer sb = new StringBuffer();
1441
1442 for ( int i = 0; i < url.length(); i++ )
1443 {
1444 char c = url.charAt( i );
1445
1446 switch ( c )
1447
1448 {
1449
1450
1451
1452
1453
1454 case ':':
1455 case '/':
1456 case '#':
1457 case '[':
1458 case ']':
1459 case '@':
1460
1461
1462
1463
1464 case '!':
1465 case '$':
1466 case '&':
1467 case '\'':
1468 case '(':
1469 case ')':
1470 case '*':
1471 case '+':
1472 case ';':
1473 case '=':
1474
1475
1476
1477 case 'a':
1478 case 'b':
1479 case 'c':
1480 case 'd':
1481 case 'e':
1482 case 'f':
1483 case 'g':
1484 case 'h':
1485 case 'i':
1486 case 'j':
1487 case 'k':
1488 case 'l':
1489 case 'm':
1490 case 'n':
1491 case 'o':
1492 case 'p':
1493 case 'q':
1494 case 'r':
1495 case 's':
1496 case 't':
1497 case 'u':
1498 case 'v':
1499 case 'w':
1500 case 'x':
1501 case 'y':
1502 case 'z':
1503
1504 case 'A':
1505 case 'B':
1506 case 'C':
1507 case 'D':
1508 case 'E':
1509 case 'F':
1510 case 'G':
1511 case 'H':
1512 case 'I':
1513 case 'J':
1514 case 'K':
1515 case 'L':
1516 case 'M':
1517 case 'N':
1518 case 'O':
1519 case 'P':
1520 case 'Q':
1521 case 'R':
1522 case 'S':
1523 case 'T':
1524 case 'U':
1525 case 'V':
1526 case 'W':
1527 case 'X':
1528 case 'Y':
1529 case 'Z':
1530
1531 case '0':
1532 case '1':
1533 case '2':
1534 case '3':
1535 case '4':
1536 case '5':
1537 case '6':
1538 case '7':
1539 case '8':
1540 case '9':
1541
1542 case '-':
1543 case '.':
1544 case '_':
1545 case '~':
1546
1547 sb.append( c );
1548 break;
1549
1550 case ',':
1551
1552
1553 if ( doubleEncode )
1554 {
1555 sb.append( "%2c" );
1556 }
1557 else
1558 {
1559 sb.append( c );
1560 }
1561 break;
1562
1563 default:
1564
1565
1566 byte[] bytes = Unicode.charToBytes( c );
1567 char[] hex = Strings.toHexString( bytes ).toCharArray();
1568 for ( int j = 0; j < hex.length; j++ )
1569 {
1570 if ( j % 2 == 0 )
1571 {
1572 sb.append( '%' );
1573 }
1574 sb.append( hex[j] );
1575
1576 }
1577
1578 break;
1579 }
1580 }
1581
1582 return sb.toString();
1583 }
1584
1585
1586
1587
1588
1589
1590
1591
1592 @Override
1593 public String toString()
1594 {
1595 StringBuffer sb = new StringBuffer();
1596
1597 sb.append( scheme );
1598
1599 if ( host != null )
1600 {
1601 switch ( hostType )
1602 {
1603 case IPV4 :
1604 case REGULAR_NAME :
1605 sb.append( host );
1606 break;
1607
1608 case IPV6 :
1609 case IPV_FUTURE :
1610 sb.append( '[' ).append( host ).append( ']' );
1611 }
1612 }
1613
1614 if ( port != -1 )
1615 {
1616 sb.append( ':' ).append( port );
1617 }
1618
1619 if ( dn != null )
1620 {
1621 sb.append( '/' ).append( urlEncode( dn.getName(), false ) );
1622
1623 if ( ( attributes.size() != 0 ) || forceScopeRendering
1624 || ( ( scope != SearchScope.OBJECT ) || ( filter != null ) || ( extensionList.size() != 0 ) ) )
1625 {
1626 sb.append( '?' );
1627
1628 boolean isFirst = true;
1629
1630 for ( String attribute : attributes )
1631 {
1632 if ( isFirst )
1633 {
1634 isFirst = false;
1635 }
1636 else
1637 {
1638 sb.append( ',' );
1639 }
1640
1641 sb.append( urlEncode( attribute, false ) );
1642 }
1643 }
1644
1645 if ( forceScopeRendering )
1646 {
1647 sb.append( '?' );
1648
1649 sb.append( scope.getLdapUrlValue() );
1650 }
1651 else
1652 {
1653 if ( ( scope != SearchScope.OBJECT ) || ( filter != null ) || ( extensionList.size() != 0 ) )
1654 {
1655 sb.append( '?' );
1656
1657 switch ( scope )
1658 {
1659 case ONELEVEL:
1660 case SUBTREE:
1661 sb.append( scope.getLdapUrlValue() );
1662 break;
1663
1664 default:
1665 break;
1666 }
1667
1668 if ( ( filter != null ) || ( ( extensionList.size() != 0 ) ) )
1669 {
1670 sb.append( "?" );
1671
1672 if ( filter != null )
1673 {
1674 sb.append( urlEncode( filter, false ) );
1675 }
1676
1677 if ( ( extensionList.size() != 0 ) )
1678 {
1679 sb.append( '?' );
1680
1681 boolean isFirst = true;
1682
1683 if ( extensionList.size() != 0 )
1684 {
1685 for ( Extension extension : extensionList )
1686 {
1687 if ( !isFirst )
1688 {
1689 sb.append( ',' );
1690 }
1691 else
1692 {
1693 isFirst = false;
1694 }
1695
1696 if ( extension.isCritical )
1697 {
1698 sb.append( '!' );
1699 }
1700 sb.append( urlEncode( extension.type, false ) );
1701
1702 if ( extension.value != null )
1703 {
1704 sb.append( '=' );
1705 sb.append( urlEncode( extension.value, true ) );
1706 }
1707 }
1708 }
1709 }
1710 }
1711 }
1712 }
1713 }
1714 else
1715 {
1716 sb.append( '/' );
1717 }
1718
1719 return sb.toString();
1720 }
1721
1722
1723
1724
1725
1726 public List<String> getAttributes()
1727 {
1728 return attributes;
1729 }
1730
1731
1732
1733
1734
1735 public Dn getDn()
1736 {
1737 return dn;
1738 }
1739
1740
1741
1742
1743
1744 public List<Extension> getExtensions()
1745 {
1746 return extensionList;
1747 }
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758 public Extension getExtension( String type )
1759 {
1760 for ( Extension extension : getExtensions() )
1761 {
1762 if ( extension.getType().equalsIgnoreCase( type ) )
1763 {
1764 return extension;
1765 }
1766 }
1767 return null;
1768 }
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779 public String getExtensionValue( String type )
1780 {
1781 for ( Extension extension : getExtensions() )
1782 {
1783 if ( extension.getType().equalsIgnoreCase( type ) )
1784 {
1785 return extension.getValue();
1786 }
1787 }
1788 return null;
1789 }
1790
1791
1792
1793
1794
1795 public String getFilter()
1796 {
1797 return filter;
1798 }
1799
1800
1801
1802
1803
1804 public String getHost()
1805 {
1806 return host;
1807 }
1808
1809
1810
1811
1812
1813 public int getPort()
1814 {
1815 return port;
1816 }
1817
1818
1819
1820
1821
1822
1823
1824
1825 public SearchScope getScope()
1826 {
1827 return scope;
1828 }
1829
1830
1831
1832
1833
1834 public String getScheme()
1835 {
1836 return scheme;
1837 }
1838
1839
1840
1841
1842
1843 public int getNbBytes()
1844 {
1845 return ( bytes != null ? bytes.length : 0 );
1846 }
1847
1848
1849
1850
1851
1852 public byte[] getBytesReference()
1853 {
1854 return bytes;
1855 }
1856
1857
1858
1859
1860
1861 public byte[] getBytesCopy()
1862 {
1863 if ( bytes != null )
1864 {
1865 byte[] copy = new byte[bytes.length];
1866 System.arraycopy( bytes, 0, copy, 0, bytes.length );
1867 return copy;
1868 }
1869 else
1870 {
1871 return null;
1872 }
1873 }
1874
1875
1876
1877
1878
1879 public String getString()
1880 {
1881 return string;
1882 }
1883
1884
1885
1886
1887
1888 @Override
1889 public int hashCode()
1890 {
1891 return this.toString().hashCode();
1892 }
1893
1894
1895
1896
1897
1898 @Override
1899 public boolean equals( Object obj )
1900 {
1901 if ( this == obj )
1902 {
1903 return true;
1904 }
1905 if ( obj == null )
1906 {
1907 return false;
1908 }
1909 if ( getClass() != obj.getClass() )
1910 {
1911 return false;
1912 }
1913
1914 final LdapUrl other = ( LdapUrl ) obj;
1915 return this.toString().equals( other.toString() );
1916 }
1917
1918
1919
1920
1921
1922
1923
1924 public void setScheme( String scheme )
1925 {
1926 if ( ( ( scheme != null ) && LDAP_SCHEME.equals( scheme ) ) || LDAPS_SCHEME.equals( scheme ) )
1927 {
1928 this.scheme = scheme;
1929 }
1930 else
1931 {
1932 this.scheme = LDAP_SCHEME;
1933 }
1934
1935 }
1936
1937
1938
1939
1940
1941
1942
1943 public void setHost( String host )
1944 {
1945 this.host = host;
1946 }
1947
1948
1949
1950
1951
1952
1953
1954 public void setPort( int port )
1955 {
1956 if ( ( port < 1 ) || ( port > 65535 ) )
1957 {
1958 this.port = -1;
1959 }
1960 else
1961 {
1962 this.port = port;
1963 }
1964 }
1965
1966
1967
1968
1969
1970
1971
1972 public void setDn( Dn dn )
1973 {
1974 this.dn = dn;
1975 }
1976
1977
1978
1979
1980
1981
1982
1983 public void setAttributes( List<String> attributes )
1984 {
1985 if ( attributes == null )
1986 {
1987 this.attributes.clear();
1988 }
1989 else
1990 {
1991 this.attributes = attributes;
1992 }
1993 }
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003 public void setScope( int scope )
2004 {
2005 try
2006 {
2007 this.scope = SearchScope.getSearchScope( scope );
2008 }
2009 catch ( IllegalArgumentException iae )
2010 {
2011 this.scope = SearchScope.OBJECT;
2012 }
2013 }
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023 public void setScope( SearchScope scope )
2024 {
2025 if ( scope == null )
2026 {
2027 this.scope = SearchScope.OBJECT;
2028 }
2029 else
2030 {
2031 this.scope = scope;
2032 }
2033 }
2034
2035
2036
2037
2038
2039
2040
2041 public void setFilter( String filter )
2042 {
2043 this.filter = filter;
2044 }
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054 public void setForceScopeRendering( boolean forceScopeRendering )
2055 {
2056 this.forceScopeRendering = forceScopeRendering;
2057 }
2058
2059
2060
2061
2062
2063
2064 public static class Extension
2065 {
2066 private boolean isCritical;
2067 private String type;
2068 private String value;
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078 public Extension( boolean isCritical, String type, String value )
2079 {
2080 super();
2081 this.isCritical = isCritical;
2082 this.type = type;
2083 this.value = value;
2084 }
2085
2086
2087
2088
2089
2090
2091
2092 public boolean isCritical()
2093 {
2094 return isCritical;
2095 }
2096
2097
2098
2099
2100
2101
2102
2103 public void setCritical( boolean critical )
2104 {
2105 this.isCritical = critical;
2106 }
2107
2108
2109
2110
2111
2112
2113
2114 public String getType()
2115 {
2116 return type;
2117 }
2118
2119
2120
2121
2122
2123
2124
2125 public void setType( String type )
2126 {
2127 this.type = type;
2128 }
2129
2130
2131
2132
2133
2134
2135
2136 public String getValue()
2137 {
2138 return value;
2139 }
2140
2141
2142
2143
2144
2145
2146
2147 public void setValue( String value )
2148 {
2149 this.value = value;
2150 }
2151 }
2152 }