1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package javax.mail.internet;
21
22 import java.io.UnsupportedEncodingException;
23 import java.lang.reflect.Array;
24 import java.util.ArrayList;
25 import java.util.List;
26
27 class AddressParser {
28
29
30 static public final int NONSTRICT = 0;
31 static public final int PARSE_HEADER = 1;
32 static public final int STRICT = 2;
33
34
35 static protected final int UNKNOWN = 0;
36 static protected final int ROUTE_ADDR = 1;
37 static protected final int GROUP_ADDR = 2;
38 static protected final int SIMPLE_ADDR = 3;
39
40
41 static protected final int END_OF_TOKENS = '\0';
42 static protected final int PERIOD = '.';
43 static protected final int LEFT_ANGLE = '<';
44 static protected final int RIGHT_ANGLE = '>';
45 static protected final int COMMA = ',';
46 static protected final int AT_SIGN = '@';
47 static protected final int SEMICOLON = ';';
48 static protected final int COLON = ':';
49 static protected final int QUOTED_LITERAL = '"';
50 static protected final int DOMAIN_LITERAL = '[';
51 static protected final int COMMENT = '(';
52 static protected final int ATOM = 'A';
53 static protected final int WHITESPACE = ' ';
54
55
56
57 private String addresses;
58
59 private int position;
60
61 private int end;
62
63 private int validationLevel;
64
65 public AddressParser(String addresses, int validation) {
66 this.addresses = addresses;
67 validationLevel = validation;
68 }
69
70
71
72
73
74
75
76
77
78 public InternetAddress[] parseAddressList() throws AddressException
79 {
80
81 TokenStream tokens = tokenizeAddress();
82
83
84 ArrayList addressList = new ArrayList();
85
86
87 while (true) {
88
89
90
91 addressList.addAll(parseSingleAddress(tokens, false));
92
93
94 AddressToken token = tokens.nextToken();
95 if (token.type == END_OF_TOKENS) {
96 break;
97 }
98 }
99
100 return (InternetAddress [])addressList.toArray(new InternetAddress[0]);
101 }
102
103
104
105
106
107
108
109
110 public InternetAddress parseAddress() throws AddressException
111 {
112
113 TokenStream tokens = tokenizeAddress();
114
115
116
117
118 List addressList = parseSingleAddress(tokens, false);
119
120 if (addressList.isEmpty()) {
121 throw new AddressException("Null address", addresses, 0);
122 }
123
124 if (addressList.size() > 1) {
125 throw new AddressException("Illegal Address", addresses, 0);
126 }
127
128
129 AddressToken token = tokens.nextToken();
130 if (token.type != END_OF_TOKENS) {
131 illegalAddress("Illegal Address", token);
132 }
133
134 return (InternetAddress)addressList.get(0);
135 }
136
137
138
139
140
141
142
143
144
145 public void validateAddress() throws AddressException
146 {
147
148 TokenStream tokens = tokenizeAddress();
149
150
151
152
153 List addressList = parseSingleAddress(tokens, false);
154 if (addressList.isEmpty()) {
155 throw new AddressException("Null address", addresses, 0);
156 }
157
158
159 if (addressList.size() > 1) {
160 throw new AddressException("Illegal Address", addresses, 0);
161 }
162
163 InternetAddress address = (InternetAddress)addressList.get(0);
164
165
166
167 if (address.personal != null) {
168 throw new AddressException("Illegal Address", addresses, 0);
169 }
170
171 AddressToken token = tokens.nextToken();
172 if (token.type != END_OF_TOKENS) {
173 illegalAddress("Illegal Address", token);
174 }
175 }
176
177
178
179
180
181
182
183
184 public InternetAddress[] extractGroupList() throws AddressException
185 {
186
187 TokenStream tokens = tokenizeAddress();
188
189
190 ArrayList addresses = new ArrayList();
191
192 AddressToken token = tokens.nextToken();
193
194
195
196 while (token.type != COLON) {
197 if (token.type == END_OF_TOKENS) {
198 illegalAddress("Missing ':'", token);
199 }
200 token = tokens.nextToken();
201 }
202
203
204 while (true) {
205
206
207
208 addresses.addAll(parseSingleAddress(tokens, true));
209
210
211 token = tokens.nextToken();
212 if (token.type == SEMICOLON) {
213 break;
214 }
215 else if (token.type == END_OF_TOKENS) {
216 illegalAddress("Missing ';'", token);
217 }
218 }
219
220 return (InternetAddress [])addresses.toArray(new InternetAddress[0]);
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236 private List parseSingleAddress(TokenStream tokens, boolean inGroup) throws AddressException
237 {
238 List parsedAddresses = new ArrayList();
239
240
241 AddressToken personalStart = null;
242 AddressToken personalEnd = null;
243
244
245 AddressToken addressStart = null;
246 AddressToken addressEnd = null;
247
248
249
250
251 boolean nonStrictRules = true;
252
253
254 int addressType = UNKNOWN;
255
256
257
258
259
260
261
262
263
264 AddressToken first = tokens.nextToken();
265
266 tokens.pushToken(first);
267
268
269 while (addressType == UNKNOWN) {
270
271 AddressToken token = tokens.nextToken();
272 switch (token.type) {
273
274
275
276 case COMMENT:
277
278 nonStrictRules = false;
279 break;
280
281
282
283 case SEMICOLON:
284 if (inGroup) {
285
286 tokens.pushToken(token);
287
288
289 if (addressStart == null) {
290
291 return parsedAddresses;
292 }
293
294 addressEnd = tokens.previousToken(token);
295
296
297 personalStart = null;
298
299 addressType = SIMPLE_ADDR;
300 break;
301 }
302
303
304
305
306
307
308 case DOMAIN_LITERAL:
309 case QUOTED_LITERAL:
310
311 nonStrictRules = false;
312
313 case ATOM:
314 case AT_SIGN:
315 case PERIOD:
316
317
318 if (addressStart == null) {
319 if (personalStart == null) {
320 personalStart = token;
321 }
322
323
324
325
326 addressStart = token;
327 }
328 break;
329
330
331
332 case LEFT_ANGLE:
333
334 nonStrictRules = false;
335
336 addressType = ROUTE_ADDR;
337
338
339 addressStart = tokens.nextRealToken();
340
341 tokens.pushToken(addressStart);
342
343 if (personalStart != null) {
344 personalEnd = tokens.previousToken(token);
345 }
346
347 addressEnd = scanRouteAddress(tokens, false);
348 break;
349
350
351 case COLON:
352
353 nonStrictRules = false;
354
355
356 if (inGroup) {
357 illegalAddress("Nested group element", token);
358 }
359 addressType = GROUP_ADDR;
360
361 personalStart = null;
362
363 addressStart = first;
364 addressEnd = scanGroupAddress(tokens);
365 break;
366
367
368
369
370
371
372 case END_OF_TOKENS:
373
374
375 if (inGroup) {
376 illegalAddress("Missing ';'", token);
377 }
378
379
380
381
382 case COMMA:
383
384 tokens.pushToken(token);
385
386
387 if (addressStart == null) {
388
389 return parsedAddresses;
390 }
391
392 addressEnd = tokens.previousToken(token);
393
394
395 personalStart = null;
396
397 addressType = SIMPLE_ADDR;
398 break;
399
400
401
402 case RIGHT_ANGLE:
403 illegalAddress("Unexpected '>'", token);
404
405 }
406 }
407
408 String personal = null;
409
410
411 if (personalStart != null) {
412 TokenStream personalTokens = tokens.section(personalStart, personalEnd);
413 personal = personalToString(personalTokens);
414 }
415
416
417 else {
418 if (addressType == SIMPLE_ADDR && first.type == COMMENT) {
419 personal = first.value;
420 }
421 }
422
423 TokenStream addressTokens = tokens.section(addressStart, addressEnd);
424
425
426
427
428 if (validationLevel != PARSE_HEADER) {
429 switch (addressType) {
430 case GROUP_ADDR:
431 validateGroup(addressTokens);
432 break;
433
434 case ROUTE_ADDR:
435 validateRouteAddr(addressTokens, false);
436 break;
437
438 case SIMPLE_ADDR:
439
440 validateSimpleAddress(addressTokens);
441 break;
442 }
443 }
444
445
446
447 if (validationLevel != NONSTRICT || addressType != SIMPLE_ADDR || !nonStrictRules) {
448
449
450 addressTokens.reset();
451 String address = addressToString(addressTokens);
452
453
454 InternetAddress result = new InternetAddress();
455 result.setAddress(address);
456 try {
457 result.setPersonal(personal);
458 } catch (UnsupportedEncodingException e) {
459 }
460
461
462 parsedAddresses.add(result);
463 return parsedAddresses;
464 }
465 else {
466 addressTokens.reset();
467
468 TokenStream nextAddress = addressTokens.getBlankDelimitedToken();
469 while (nextAddress != null) {
470 String address = addressToString(nextAddress);
471
472 InternetAddress result = new InternetAddress();
473 result.setAddress(address);
474 parsedAddresses.add(result);
475 nextAddress = addressTokens.getBlankDelimitedToken();
476 }
477 return parsedAddresses;
478 }
479 }
480
481
482
483
484
485
486
487
488
489
490
491
492
493 private AddressToken scanRouteAddress(TokenStream tokens, boolean inGroup) throws AddressException {
494
495 AddressToken token = tokens.nextRealToken();
496
497
498
499 AddressToken previous = null;
500
501
502
503 boolean inRoute = token.type == AT_SIGN;
504
505
506 while (true) {
507 switch (token.type) {
508
509 case ATOM:
510 case QUOTED_LITERAL:
511 case DOMAIN_LITERAL:
512 case PERIOD:
513 case AT_SIGN:
514 break;
515
516 case COLON:
517
518 if (!inRoute) {
519 illegalAddress("Unexpected ':'", token);
520 }
521
522 inRoute = false;
523 break;
524
525 case COMMA:
526
527 if (!inRoute) {
528 illegalAddress("Unexpected ','", token);
529 }
530 break;
531
532 case RIGHT_ANGLE:
533
534 if (previous == null) {
535 illegalAddress("Illegal address", token);
536 }
537
538
539 token = tokens.nextRealToken();
540
541 if (inGroup) {
542 if (token.type != COMMA && token.type != SEMICOLON) {
543 illegalAddress("Illegal address", token);
544 }
545 }
546
547 else {
548 if (token.type != COMMA && token.type != END_OF_TOKENS) {
549 illegalAddress("Illegal address", token);
550 }
551 }
552
553 tokens.pushToken(token);
554
555 return previous;
556
557 case END_OF_TOKENS:
558 illegalAddress("Missing '>'", token);
559
560
561 case SEMICOLON:
562 illegalAddress("Unexpected ';'", token);
563
564 case LEFT_ANGLE:
565 illegalAddress("Unexpected '<'", token);
566 }
567
568 previous = token;
569 token = tokens.nextRealToken();
570 }
571 }
572
573
574
575
576
577
578
579
580
581
582
583
584 private AddressToken scanGroupAddress(TokenStream tokens) throws AddressException {
585
586
587 AddressToken token = tokens.nextRealToken();
588
589
590 while (true) {
591 switch (token.type) {
592
593 case ATOM:
594 case QUOTED_LITERAL:
595 case DOMAIN_LITERAL:
596 case PERIOD:
597 case AT_SIGN:
598 case COMMA:
599 break;
600
601 case COLON:
602 illegalAddress("Nested group", token);
603
604
605
606 case LEFT_ANGLE:
607 scanRouteAddress(tokens, true);
608 break;
609
610
611 case END_OF_TOKENS:
612 illegalAddress("Missing ';'", token);
613
614
615 case SEMICOLON:
616
617 AddressToken next = tokens.nextRealToken();
618 if (next.type != COMMA && next.type != END_OF_TOKENS) {
619 illegalAddress("Illegal address", token);
620 }
621
622 tokens.pushToken(next);
623 return token;
624
625 case RIGHT_ANGLE:
626 illegalAddress("Unexpected '>'", token);
627 }
628 token = tokens.nextRealToken();
629 }
630 }
631
632
633
634
635
636
637
638
639
640 private TokenStream tokenizeAddress() throws AddressException {
641
642
643 TokenStream tokens = new TokenStream();
644
645 end = addresses.length();
646
647
648 while (moreCharacters()) {
649 char ch = currentChar();
650
651 switch (ch) {
652
653 case '(':
654 scanComment(tokens);
655 break;
656
657 case ')':
658 syntaxError("Unexpected ')'", position);
659
660
661
662 case '"':
663 scanQuotedLiteral(tokens);
664 break;
665
666 case '[':
667 scanDomainLiteral(tokens);
668 break;
669
670
671 case ']':
672 syntaxError("Unexpected ']'", position);
673
674
675 case '<':
676 tokens.addToken(new AddressToken(LEFT_ANGLE, position));
677 nextChar();
678 break;
679
680
681
682 case '>':
683 tokens.addToken(new AddressToken(RIGHT_ANGLE, position));
684 nextChar();
685 break;
686 case ':':
687 tokens.addToken(new AddressToken(COLON, position));
688 nextChar();
689 break;
690 case ',':
691 tokens.addToken(new AddressToken(COMMA, position));
692 nextChar();
693 break;
694 case '.':
695 tokens.addToken(new AddressToken(PERIOD, position));
696 nextChar();
697 break;
698 case ';':
699 tokens.addToken(new AddressToken(SEMICOLON, position));
700 nextChar();
701 break;
702 case '@':
703 tokens.addToken(new AddressToken(AT_SIGN, position));
704 nextChar();
705 break;
706
707
708
709
710 case ' ':
711 case '\t':
712 case '\r':
713 case '\n':
714
715 tokens.addToken(new AddressToken(WHITESPACE, position));
716
717 nextChar();
718
719
720 while (moreCharacters()) {
721 char nextChar = currentChar();
722 if (nextChar == ' ' || nextChar == '\t' || nextChar == '\r' || nextChar == '\n') {
723 nextChar();
724 }
725 else {
726 break;
727 }
728 }
729 break;
730
731
732
733 default:
734 if (ch < 040 || ch >= 0177) {
735 syntaxError("Illegal character in address", position);
736 }
737
738 scanAtom(tokens);
739 break;
740 }
741 }
742
743
744 tokens.addToken(new AddressToken(END_OF_TOKENS, addresses.length()));
745 return tokens;
746 }
747
748
749
750
751
752 private void nextChar() {
753 position++;
754 }
755
756
757
758
759
760
761
762 private char currentChar() {
763 return addresses.charAt(position);
764 }
765
766
767
768
769
770
771 private boolean moreCharacters() {
772 return position < end;
773 }
774
775
776
777
778
779
780
781 private void scanQuotedLiteral(TokenStream tokens) throws AddressException {
782 StringBuffer value = new StringBuffer();
783
784
785 int startPosition = position;
786
787 nextChar();
788
789 while (moreCharacters()) {
790 char ch = currentChar();
791
792
793 if (ch == '\\') {
794
795 nextChar();
796 if (!moreCharacters()) {
797 syntaxError("Missing '\"'", position);
798 }
799 value.append(currentChar());
800 }
801
802 else if (ch == '"') {
803
804 tokens.addToken(new AddressToken(value.toString(), QUOTED_LITERAL, position));
805
806 nextChar();
807 return;
808 }
809
810 else if (ch == '\r') {
811 syntaxError("Illegal line end in literal", position);
812 }
813 else
814 {
815 value.append(ch);
816 }
817 nextChar();
818 }
819
820 syntaxError("Missing '\"'", position);
821 }
822
823
824
825
826
827
828
829 private void scanDomainLiteral(TokenStream tokens) throws AddressException {
830 StringBuffer value = new StringBuffer();
831
832 int startPosition = position;
833
834 nextChar();
835
836 while (moreCharacters()) {
837 char ch = currentChar();
838
839
840 if (ch == '\\') {
841
842
843
844 value.append(currentChar());
845
846 nextChar();
847 if (!moreCharacters()) {
848 syntaxError("Missing '\"'", position);
849 }
850 value.append(currentChar());
851 }
852
853 else if (ch == ']') {
854
855 tokens.addToken(new AddressToken(value.toString(), DOMAIN_LITERAL, startPosition));
856
857 nextChar();
858 return;
859 }
860
861 else if (ch == '[') {
862 syntaxError("Unexpected '['", position);
863 }
864
865 else if (ch == '\r') {
866 syntaxError("Illegal line end in domain literal", position);
867 }
868 else
869 {
870 value.append(ch);
871 }
872 nextChar();
873 }
874
875 syntaxError("Missing ']'", position);
876 }
877
878
879
880
881
882
883
884 private void scanAtom(TokenStream tokens) throws AddressException {
885 int start = position;
886 nextChar();
887 while (moreCharacters()) {
888
889 char ch = currentChar();
890 if (isAtom(ch)) {
891 nextChar();
892 }
893 else {
894 break;
895 }
896 }
897
898
899 tokens.addToken(new AddressToken(addresses.substring(start, position), ATOM, start));
900 }
901
902
903
904
905
906
907
908
909 private void scanComment(TokenStream tokens) throws AddressException {
910 StringBuffer value = new StringBuffer();
911
912 int startPosition = position;
913
914 nextChar();
915
916
917 int nest = 1;
918
919
920 while (moreCharacters()) {
921 char ch = currentChar();
922
923 if (ch == '\\') {
924
925
926 nextChar();
927 if (!moreCharacters()) {
928 syntaxError("Missing ')'", position);
929 }
930 value.append(currentChar());
931 }
932
933 else if (ch == '(') {
934
935
936 nest++;
937 value.append(ch);
938 }
939
940 else if (ch == ')') {
941
942
943 nest--;
944 if (nest > 0) {
945 value.append(ch);
946 }
947 else {
948
949
950
951 nextChar();
952 tokens.addToken(new AddressToken(value.toString(), COMMENT, startPosition));
953 return;
954 }
955 }
956 else if (ch == '\r') {
957 syntaxError("Illegal line end in comment", position);
958 }
959 else {
960 value.append(ch);
961 }
962
963 nextChar();
964 }
965
966 syntaxError("Missing ')'", position);
967 }
968
969
970
971
972
973
974
975
976
977 private void validateGroup(TokenStream tokens) throws AddressException {
978
979
980
981 int phraseCount = 0;
982
983 AddressToken token = tokens.nextRealToken();
984
985 while (token.type != COLON) {
986
987 if (token.type != ATOM && token.type != QUOTED_LITERAL) {
988 invalidToken(token);
989 }
990 phraseCount++;
991 token = tokens.nextRealToken();
992 }
993
994
995
996 if (phraseCount == 0) {
997 illegalAddress("Missing group identifier phrase", token);
998 }
999
1000
1001
1002
1003
1004 while (true) {
1005
1006
1007 validateGroupMailbox(tokens);
1008
1009 token = tokens.nextRealToken();
1010
1011
1012 if (token.type == SEMICOLON) {
1013 token = tokens.nextRealToken();
1014 if (token.type != END_OF_TOKENS) {
1015 illegalAddress("Illegal group address", token);
1016 }
1017 return;
1018 }
1019
1020
1021 else if (token.type != COMMA) {
1022 illegalAddress("Illegal group address", token);
1023 }
1024 }
1025 }
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035 private void validateGroupMailbox(TokenStream tokens) throws AddressException {
1036 AddressToken first = tokens.nextRealToken();
1037
1038 if (first.type == COMMA || first.type == SEMICOLON) {
1039 tokens.pushToken(first);
1040 return;
1041 }
1042
1043
1044 AddressToken token = first;
1045
1046
1047
1048 while (first != null) {
1049 switch (token.type) {
1050
1051 case QUOTED_LITERAL:
1052 case ATOM:
1053 break;
1054
1055
1056
1057 case LEFT_ANGLE:
1058 tokens.pushToken(first);
1059 validatePhrase(tokens, false);
1060 validateRouteAddr(tokens, true);
1061 return;
1062
1063
1064
1065 case PERIOD:
1066
1067
1068 case AT_SIGN:
1069 tokens.pushToken(first);
1070 validateAddressSpec(tokens);
1071 return;
1072
1073
1074
1075 case COMMA:
1076
1077 case SEMICOLON:
1078 tokens.pushToken(first);
1079 validateAddressSpec(tokens);
1080 return;
1081
1082 case END_OF_TOKENS:
1083 illegalAddress("Missing ';'", token);
1084
1085 }
1086 token = tokens.nextRealToken();
1087 }
1088 }
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099 private void invalidToken(AddressToken token) throws AddressException {
1100 illegalAddress("Unexpected '" + token.type + "'", token);
1101 }
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112 private void syntaxError(String message, int position) throws AddressException
1113 {
1114 throw new AddressException(message, addresses, position);
1115 }
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125 private void illegalAddress(String message, AddressToken token) throws AddressException {
1126 throw new AddressException(message, addresses, token.position);
1127 }
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138 private void validatePhrase(TokenStream tokens, boolean required) throws AddressException {
1139
1140
1141 AddressToken token = tokens.nextRealToken();
1142 if (token.type != ATOM && token.type != QUOTED_LITERAL) {
1143 if (required) {
1144 illegalAddress("Missing group phrase", token);
1145 }
1146 }
1147
1148
1149 token = tokens.nextRealToken();
1150 while (token.type == ATOM || token.type == QUOTED_LITERAL) {
1151 token = tokens.nextRealToken();
1152 }
1153 }
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167 private void validateRouteAddr(TokenStream tokens, boolean ingroup) throws AddressException {
1168
1169 AddressToken token = tokens.nextRealToken();
1170
1171 if (token.type == AT_SIGN) {
1172
1173 tokens.pushToken(token);
1174 validateRoute(tokens);
1175 }
1176 else {
1177
1178 tokens.pushToken(token);
1179 }
1180
1181
1182 validateAddressSpec(tokens);
1183
1184 token = tokens.nextRealToken();
1185 if (ingroup) {
1186
1187
1188 if (token.type != RIGHT_ANGLE) {
1189 illegalAddress("Missing '>'", token);
1190 }
1191 }
1192 else {
1193
1194
1195 if (token.type != END_OF_TOKENS) {
1196 illegalAddress("Illegal Address", token);
1197 }
1198 }
1199 }
1200
1201
1202
1203
1204
1205
1206
1207
1208 private void validateSimpleAddress(TokenStream tokens) throws AddressException {
1209
1210
1211
1212
1213 validateAddressSpec(tokens);
1214
1215
1216 AddressToken token = tokens.nextRealToken();
1217 if (token.type != END_OF_TOKENS) {
1218 illegalAddress("Illegal Address", token);
1219 }
1220 }
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230 private void validateAddressSpec(TokenStream tokens) throws AddressException {
1231
1232 validateLocalPart(tokens);
1233
1234
1235 AddressToken token = tokens.nextRealToken();
1236 if (token.type == AT_SIGN) {
1237 validateDomain(tokens);
1238 }
1239 else {
1240
1241 tokens.pushToken(token);
1242 }
1243
1244 }
1245
1246
1247
1248
1249
1250
1251
1252
1253 private void validateRoute(TokenStream tokens) throws AddressException {
1254 while (true) {
1255 AddressToken token = tokens.nextRealToken();
1256
1257 if (token.type == AT_SIGN) {
1258 validateDomain(tokens);
1259 }
1260
1261 else if (token.type == COMMA) {
1262 continue;
1263 }
1264
1265 else if (token.type == COLON) {
1266 return;
1267 }
1268
1269 else {
1270 illegalAddress("Missing ':'", token);
1271 }
1272 }
1273 }
1274
1275
1276
1277
1278
1279
1280 private void validateLocalPart(TokenStream tokens) throws AddressException {
1281 while (true) {
1282
1283 AddressToken token = tokens.nextRealToken();
1284
1285
1286 if (token.type != ATOM && token.type != QUOTED_LITERAL) {
1287 illegalAddress("Invalid local part", token);
1288 }
1289
1290
1291 token = tokens.nextRealToken();
1292
1293 if (token.type != PERIOD) {
1294 tokens.pushToken(token);
1295
1296 return;
1297 }
1298 }
1299 }
1300
1301
1302
1303
1304
1305
1306
1307 private void validateDomain(TokenStream tokens) throws AddressException {
1308 while (true) {
1309
1310 AddressToken token = tokens.nextRealToken();
1311
1312
1313 if (token.type != ATOM && token.type != DOMAIN_LITERAL) {
1314 illegalAddress("Invalid domain", token);
1315 }
1316
1317
1318 token = tokens.nextRealToken();
1319
1320 if (token.type != PERIOD) {
1321
1322 tokens.pushToken(token);
1323 return;
1324 }
1325 }
1326 }
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368 private String personalToString(TokenStream tokens) {
1369
1370
1371 AddressToken token = tokens.nextToken();
1372
1373 if (token.type == END_OF_TOKENS) {
1374 return null;
1375 }
1376
1377 AddressToken next = tokens.nextToken();
1378
1379
1380 if (next.type == END_OF_TOKENS) {
1381
1382
1383 return token.value;
1384 }
1385
1386
1387 tokens.pushToken(token);
1388
1389
1390 StringBuffer buffer = new StringBuffer();
1391
1392
1393 token = tokens.nextToken();
1394 addTokenValue(token, buffer);
1395
1396 token = tokens.nextToken();
1397 while (token.type != END_OF_TOKENS) {
1398
1399 buffer.append(' ');
1400
1401 addTokenValue(token, buffer);
1402 token = tokens.nextToken();
1403 }
1404
1405 return buffer.toString();
1406 }
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417 private String addressToString(TokenStream tokens) {
1418 StringBuffer buffer = new StringBuffer();
1419
1420
1421
1422
1423
1424 boolean spaceRequired = false;
1425
1426
1427 AddressToken token = tokens.nextToken();
1428
1429
1430 while (token.type != END_OF_TOKENS) {
1431 switch (token.type) {
1432
1433
1434 case ATOM:
1435 case QUOTED_LITERAL:
1436
1437 if (spaceRequired) {
1438 buffer.append(' ');
1439 }
1440 addTokenValue(token, buffer);
1441
1442 spaceRequired = true;
1443 break;
1444
1445
1446
1447
1448 case LEFT_ANGLE:
1449 case RIGHT_ANGLE:
1450 case COMMA:
1451 case COLON:
1452 case AT_SIGN:
1453 case SEMICOLON:
1454 case PERIOD:
1455 buffer.append((char)token.type);
1456
1457 spaceRequired = false;
1458 break;
1459
1460
1461 case DOMAIN_LITERAL:
1462 addTokenValue(token, buffer);
1463 spaceRequired = false;
1464 break;
1465
1466
1467 case COMMENT:
1468 addTokenValue(token, buffer);
1469 spaceRequired = false;
1470 break;
1471 }
1472 token = tokens.nextToken();
1473 }
1474 return buffer.toString();
1475 }
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485 private void addTokenValue(AddressToken token, StringBuffer buffer) {
1486
1487 if (token.type == ATOM) {
1488 buffer.append(token.value);
1489 }
1490
1491 else if (token.type == QUOTED_LITERAL) {
1492 buffer.append(formatQuotedString(token.value));
1493 }
1494
1495 else if (token.type == DOMAIN_LITERAL) {
1496 buffer.append('[');
1497 buffer.append(token.value);
1498 buffer.append(']');
1499 }
1500
1501 else if (token.type == COMMENT) {
1502 buffer.append('(');
1503 buffer.append(token.value);
1504 buffer.append(')');
1505 }
1506 }
1507
1508
1509
1510 private static final byte[] CHARMAP = {
1511 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x06, 0x02, 0x06, 0x02, 0x02, 0x06, 0x02, 0x02,
1512 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
1513 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
1514 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00,
1515
1516 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1517 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00,
1518 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1519 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
1520 };
1521
1522 private static final byte FLG_SPECIAL = 1;
1523 private static final byte FLG_CONTROL = 2;
1524 private static final byte FLG_SPACE = 4;
1525
1526 private static boolean isSpace(char ch) {
1527 if (ch > '\u007f') {
1528 return false;
1529 } else {
1530 return (CHARMAP[ch] & FLG_SPACE) != 0;
1531 }
1532 }
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543 public static boolean isAtom(char ch) {
1544 if (ch > '\u007f') {
1545 return false;
1546 }
1547 else if (ch == ' ') {
1548 return false;
1549 }
1550 else {
1551 return (CHARMAP[ch] & (FLG_SPECIAL | FLG_CONTROL)) == 0;
1552 }
1553 }
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564 public static boolean containsCharacters(String s, String chars)
1565 {
1566 for (int i = 0; i < s.length(); i++) {
1567 if (chars.indexOf(s.charAt(i)) >= 0) {
1568 return true;
1569 }
1570 }
1571 return false;
1572 }
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585 public static boolean containsSpecials(String s)
1586 {
1587 for (int i = 0; i < s.length(); i++) {
1588 char ch = s.charAt(i);
1589
1590 if (ch == ' ' || isAtom(ch)) {
1591 continue;
1592 }
1593 else {
1594 return true;
1595 }
1596 }
1597 return false;
1598 }
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611 public static boolean isAtom(String s)
1612 {
1613 for (int i = 0; i < s.length(); i++) {
1614 char ch = s.charAt(i);
1615
1616 if (!isAtom(ch)) {
1617 return false;
1618 }
1619 }
1620 return true;
1621 }
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634 public static String quoteString(String s) {
1635
1636
1637
1638 if (s.indexOf('\\') == -1 && s.indexOf('"') == -1) {
1639
1640 if (!containsSpecials(s)) {
1641 return s;
1642 }
1643 StringBuffer buffer = new StringBuffer(s.length() + 2);
1644 buffer.append('"');
1645 buffer.append(s);
1646 buffer.append('"');
1647 return buffer.toString();
1648 }
1649
1650
1651
1652 StringBuffer buffer = new StringBuffer(s.length() + 10);
1653 buffer.append('"');
1654
1655
1656 for (int i = 0; i < s.length(); i++) {
1657 char ch = s.charAt(i);
1658
1659 if (ch == '\\' || ch == '"') {
1660
1661 buffer.append('\\');
1662 }
1663
1664 buffer.append(ch);
1665 }
1666 buffer.append('"');
1667 return buffer.toString();
1668 }
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680 public static String formatQuotedString(String s) {
1681
1682
1683 if (s.indexOf('\\') == -1 && s.indexOf('"') == -1) {
1684 StringBuffer buffer = new StringBuffer(s.length() + 2);
1685 buffer.append('"');
1686 buffer.append(s);
1687 buffer.append('"');
1688 return buffer.toString();
1689 }
1690
1691
1692
1693 StringBuffer buffer = new StringBuffer(s.length() + 10);
1694 buffer.append('"');
1695
1696
1697 for (int i = 0; i < s.length(); i++) {
1698 char ch = s.charAt(i);
1699
1700 if (ch == '\\' || ch == '"') {
1701
1702 buffer.append('\\');
1703 }
1704
1705 buffer.append(ch);
1706 }
1707 buffer.append('"');
1708 return buffer.toString();
1709 }
1710
1711 public class TokenStream {
1712
1713 private List tokens;
1714
1715
1716 int currentToken = 0;
1717
1718
1719
1720
1721
1722
1723
1724
1725 public TokenStream() {
1726 tokens = new ArrayList();
1727 }
1728
1729
1730
1731
1732
1733
1734
1735
1736 public TokenStream(List tokens) {
1737 this.tokens = tokens;
1738 tokens.add(new AddressToken(END_OF_TOKENS, -1));
1739 }
1740
1741
1742
1743
1744
1745
1746 public void addToken(AddressToken token) {
1747 tokens.add(token);
1748 }
1749
1750
1751
1752
1753
1754
1755
1756 public AddressToken nextToken() {
1757 AddressToken token = (AddressToken)tokens.get(currentToken++);
1758
1759
1760 while (token.type == WHITESPACE) {
1761 token = (AddressToken)tokens.get(currentToken++);
1762 }
1763 return token;
1764 }
1765
1766
1767
1768
1769
1770
1771
1772
1773 public AddressToken currentToken() {
1774
1775 return (AddressToken)tokens.get(currentToken);
1776 }
1777
1778
1779
1780
1781
1782
1783
1784
1785 public AddressToken nextRealToken()
1786 {
1787 AddressToken token = nextToken();
1788 if (token.type == COMMENT) {
1789 token = nextToken();
1790 }
1791 return token;
1792 }
1793
1794
1795
1796
1797
1798
1799
1800 public void pushToken(AddressToken token) {
1801
1802 currentToken = tokenIndex(token);
1803 }
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813 public AddressToken nextToken(AddressToken token) {
1814 return (AddressToken)tokens.get(tokenIndex(token) + 1);
1815 }
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825 public AddressToken previousToken(AddressToken token) {
1826 return (AddressToken)tokens.get(tokenIndex(token) - 1);
1827 }
1828
1829
1830
1831
1832
1833
1834
1835 public AddressToken getToken(int index)
1836 {
1837 return (AddressToken)tokens.get(index);
1838 }
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849 public int tokenIndex(AddressToken token) {
1850 return tokens.indexOf(token);
1851 }
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863 public TokenStream section(AddressToken start, AddressToken end) {
1864 int startIndex = tokenIndex(start);
1865 int endIndex = tokenIndex(end);
1866
1867
1868
1869
1870 ArrayList list = new ArrayList(endIndex - startIndex + 2);
1871
1872 for (int i = startIndex; i <= endIndex; i++) {
1873 list.add(tokens.get(i));
1874 }
1875 return new TokenStream(list);
1876 }
1877
1878
1879
1880
1881
1882
1883 public void reset() {
1884 currentToken = 0;
1885 }
1886
1887
1888
1889
1890
1891
1892 public AddressToken getNonBlank()
1893 {
1894 AddressToken token = currentToken();
1895 while (token.type == WHITESPACE) {
1896 currentToken++;
1897 token = currentToken();
1898 }
1899 return token;
1900 }
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910 public TokenStream getBlankDelimitedToken()
1911 {
1912
1913 AddressToken first = getNonBlank();
1914
1915 if (first.type == END_OF_TOKENS) {
1916 return null;
1917 }
1918
1919 AddressToken last = first;
1920
1921
1922
1923 currentToken++;
1924
1925 AddressToken token = currentToken();
1926 while (true) {
1927
1928 if (token.type == END_OF_TOKENS || token.type == WHITESPACE) {
1929 return section(first, last);
1930 }
1931 last = token;
1932 currentToken++;
1933
1934 token = currentToken();
1935 }
1936 }
1937
1938
1939
1940
1941
1942
1943 public int currentIndex() {
1944 return currentToken;
1945 }
1946
1947 public void dumpTokens()
1948 {
1949 System.out.println(">>>>>>>>> Start dumping TokenStream tokens");
1950 for (int i = 0; i < tokens.size(); i++) {
1951 System.out.println("-------- Token: " + tokens.get(i));
1952 }
1953
1954 System.out.println("++++++++ cursor position=" + currentToken);
1955 System.out.println(">>>>>>>>> End dumping TokenStream tokens");
1956 }
1957 }
1958
1959
1960
1961
1962
1963 public class AddressToken {
1964
1965
1966 int type;
1967
1968
1969 String value;
1970
1971
1972 int position;
1973
1974 AddressToken(int type, int position)
1975 {
1976 this.type = type;
1977 this.value = null;
1978 this.position = position;
1979 }
1980
1981 AddressToken(String value, int type, int position)
1982 {
1983 this.type = type;
1984 this.value = value;
1985 this.position = position;
1986 }
1987
1988 public String toString()
1989 {
1990 if (type == END_OF_TOKENS) {
1991 return "AddressToken: type=END_OF_TOKENS";
1992 }
1993 if (value == null) {
1994 return "AddressToken: type=" + (char)type;
1995 }
1996 else {
1997 return "AddressToken: type=" + (char)type + " value=" + value;
1998 }
1999 }
2000 }
2001 }
2002