1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.mailet;
22
23 import java.util.Locale;
24 import javax.mail.internet.AddressException;
25 import javax.mail.internet.InternetAddress;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class MailAddress implements java.io.Serializable {
64
65
66
67
68
69
70 public static final long serialVersionUID = 2779163542539434916L;
71
72 private final static char[] SPECIAL =
73 {'<', '>', '(', ')', '[', ']', '\\', '.', ',', ';', ':', '@', '\"'};
74
75 private String localPart = null;
76 private String domain = null;
77
78 private int pos = 0;
79
80
81
82
83
84
85
86 private void stripSourceRoute(String address) {
87 if (pos < address.length()) {
88 if (address.charAt(pos)=='@') {
89 int i = address.indexOf(':');
90 if (i != -1) {
91 pos = i+1;
92 }
93 }
94 }
95 }
96
97
98
99
100
101
102
103 public MailAddress(String address) throws AddressException {
104 address = address.trim();
105
106
107
108 stripSourceRoute(address);
109
110 StringBuffer localPartSB = new StringBuffer();
111 StringBuffer domainSB = new StringBuffer();
112
113
114
115 try {
116
117
118 if (address.charAt(pos) == '\"') {
119 localPartSB.append(parseQuotedLocalPart(address));
120 if (localPartSB.toString().length() == 2) {
121 throw new AddressException("No quoted local-part (user account) found at position " + (pos + 2) + " in '" + address + "'",address,pos+2);
122 }
123 } else {
124 localPartSB.append(parseUnquotedLocalPart(address));
125 if (localPartSB.toString().length() == 0) {
126 throw new AddressException("No local-part (user account) found at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
127 }
128 }
129
130
131 if (pos >= address.length() || address.charAt(pos) != '@') {
132 throw new AddressException("Did not find @ between local-part and domain at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
133 }
134 pos++;
135
136
137
138
139 while (true) {
140 if (address.charAt(pos) == '#') {
141 domainSB.append(parseNumber(address));
142 } else if (address.charAt(pos) == '[') {
143 domainSB.append(parseDomainLiteral(address));
144 } else {
145 domainSB.append(parseDomain(address));
146 }
147 if (pos >= address.length()) {
148 break;
149 }
150 if (address.charAt(pos) == '.') {
151 domainSB.append('.');
152 pos++;
153 continue;
154 }
155 break;
156 }
157
158 if (domainSB.toString().length() == 0) {
159 throw new AddressException("No domain found at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
160 }
161 } catch (IndexOutOfBoundsException ioobe) {
162 throw new AddressException("Out of data at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
163 }
164
165 localPart = localPartSB.toString();
166 domain = domainSB.toString();
167 }
168
169
170
171
172
173
174
175
176
177
178
179 public MailAddress(String localPart, String domain) throws AddressException {
180 this(new InternetAddress(localPart+"@"+domain));
181 }
182
183
184
185
186
187
188
189
190
191 public MailAddress(InternetAddress address) throws AddressException {
192 this(address.getAddress());
193 }
194
195
196
197
198
199
200
201
202
203 public String getHost() {
204 return getDomain();
205 }
206
207
208
209
210
211
212
213
214 public String getDomain() {
215 if (!(domain.startsWith("[") && domain.endsWith("]"))) {
216 return domain;
217 }
218 return domain.substring(1, domain.length() -1);
219 }
220
221
222
223
224
225
226
227
228 public String getUser() {
229 return getLocalPart();
230 }
231
232
233
234
235
236
237
238
239
240
241
242 public String getLocalPart() {
243 return localPart;
244 }
245
246 public String toString() {
247 StringBuffer addressBuffer =
248 new StringBuffer(128)
249 .append(localPart)
250 .append("@")
251 .append(domain);
252 return addressBuffer.toString();
253 }
254
255
256
257
258
259
260
261 public InternetAddress toInternetAddress() {
262 try {
263 return new InternetAddress(toString());
264 } catch (javax.mail.internet.AddressException ae) {
265
266 return null;
267 }
268 }
269
270
271
272
273
274
275
276
277
278
279
280 public boolean equals(Object obj) {
281 if (obj == null) {
282 return false;
283 } else if (obj instanceof String) {
284 String theString = (String)obj;
285 return toString().equalsIgnoreCase(theString);
286 } else if (obj instanceof MailAddress) {
287 MailAddress addr = (MailAddress)obj;
288 return getLocalPart().equalsIgnoreCase(addr.getLocalPart()) && getDomain().equalsIgnoreCase(addr.getDomain());
289 }
290 return false;
291 }
292
293
294
295
296
297
298
299
300
301
302 public int hashCode() {
303 return toString().toLowerCase(Locale.US).hashCode();
304 }
305
306 private String parseQuotedLocalPart(String address) throws AddressException {
307 StringBuffer resultSB = new StringBuffer();
308 resultSB.append('\"');
309 pos++;
310
311
312 while (true) {
313 if (address.charAt(pos) == '\"') {
314 resultSB.append('\"');
315
316 pos++;
317 break;
318 }
319 if (address.charAt(pos) == '\\') {
320 resultSB.append('\\');
321 pos++;
322
323 char x = address.charAt(pos);
324 if (x < 0 || x > 127) {
325 throw new AddressException("Invalid \\ syntaxed character at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
326 }
327 resultSB.append(x);
328 pos++;
329 } else {
330
331
332 char q = address.charAt(pos);
333 if (q <= 0 || q == '\n' || q == '\r' || q == '\"' || q == '\\') {
334 throw new AddressException("Unquoted local-part (user account) must be one of the 128 ASCI characters exception <CR>, <LF>, quote (\"), or backslash (\\) at position " + (pos + 1) + " in '" + address + "'");
335 }
336 resultSB.append(q);
337 pos++;
338 }
339 }
340 return resultSB.toString();
341 }
342
343 private String parseUnquotedLocalPart(String address) throws AddressException {
344 StringBuffer resultSB = new StringBuffer();
345
346 boolean lastCharDot = false;
347 while (true) {
348
349
350 if (address.charAt(pos) == '\\') {
351 resultSB.append('\\');
352 pos++;
353
354 char x = address.charAt(pos);
355 if (x < 0 || x > 127) {
356 throw new AddressException("Invalid \\ syntaxed character at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
357 }
358 resultSB.append(x);
359 pos++;
360 lastCharDot = false;
361 } else if (address.charAt(pos) == '.') {
362 resultSB.append('.');
363 pos++;
364 lastCharDot = true;
365 } else if (address.charAt(pos) == '@') {
366
367 break;
368 } else {
369
370
371
372
373
374
375
376 char c = address.charAt(pos);
377 if (c <= 31 || c >= 127 || c == ' ') {
378 throw new AddressException("Invalid character in local-part (user account) at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
379 }
380 for (int i = 0; i < SPECIAL.length; i++) {
381 if (c == SPECIAL[i]) {
382 throw new AddressException("Invalid character in local-part (user account) at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
383 }
384 }
385 resultSB.append(c);
386 pos++;
387 lastCharDot = false;
388 }
389 }
390 if (lastCharDot) {
391 throw new AddressException("local-part (user account) ended with a \".\", which is invalid in address '" + address + "'",address,pos);
392 }
393 return resultSB.toString();
394 }
395
396 private String parseNumber(String address) throws AddressException {
397
398
399 StringBuffer resultSB = new StringBuffer();
400
401 while (true) {
402 if (pos >= address.length()) {
403 break;
404 }
405
406 char d = address.charAt(pos);
407 if (d == '.') {
408 break;
409 }
410 if (d < '0' || d > '9') {
411 throw new AddressException("In domain, did not find a number in # address at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
412 }
413 resultSB.append(d);
414 pos++;
415 }
416 return resultSB.toString();
417 }
418
419 private String parseDomainLiteral(String address) throws AddressException {
420
421 while(address.indexOf("\\")>-1){
422 address= address.substring(0,address.indexOf("\\")) + address.substring(address.indexOf("\\")+1);
423 }
424 StringBuffer resultSB = new StringBuffer();
425
426
427 resultSB.append(address.charAt(pos));
428 pos++;
429
430
431 for (int octet = 0; octet < 4; octet++) {
432
433
434
435 StringBuffer snumSB = new StringBuffer();
436 for (int digits = 0; digits < 3; digits++) {
437 char d = address.charAt(pos);
438 if (d == '.') {
439 break;
440 }
441 if (d == ']') {
442 break;
443 }
444 if (d < '0' || d > '9') {
445 throw new AddressException("Invalid number at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
446 }
447 snumSB.append(d);
448 pos++;
449 }
450 if (snumSB.toString().length() == 0) {
451 throw new AddressException("Number not found at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
452 }
453 try {
454 int snum = Integer.parseInt(snumSB.toString());
455 if (snum > 255) {
456 throw new AddressException("Invalid number at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
457 }
458 } catch (NumberFormatException nfe) {
459 throw new AddressException("Invalid number at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
460 }
461 resultSB.append(snumSB.toString());
462 if (address.charAt(pos) == ']') {
463 if (octet < 3) {
464 throw new AddressException("End of number reached too quickly at " + (pos + 1) + " in '" + address + "'",address,pos+1);
465 }
466 break;
467 }
468 if (address.charAt(pos) == '.') {
469 resultSB.append('.');
470 pos++;
471 }
472 }
473 if (address.charAt(pos) != ']') {
474 throw new AddressException("Did not find closing bracket \"]\" in domain at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
475 }
476 resultSB.append(']');
477 pos++;
478 return resultSB.toString();
479 }
480
481 private String parseDomain(String address) throws AddressException {
482 StringBuffer resultSB = new StringBuffer();
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498 while (true) {
499 if (pos >= address.length()) {
500 break;
501 }
502 char ch = address.charAt(pos);
503 if ((ch >= '0' && ch <= '9') ||
504 (ch >= 'a' && ch <= 'z') ||
505 (ch >= 'A' && ch <= 'Z') ||
506 (ch == '-')) {
507 resultSB.append(ch);
508 pos++;
509 continue;
510 }
511 if (ch == '.') {
512 break;
513 }
514 throw new AddressException("Invalid character at " + pos + " in '" + address + "'",address,pos);
515 }
516 String result = resultSB.toString();
517 if (result.startsWith("-") || result.endsWith("-")) {
518 throw new AddressException("Domain name cannot begin or end with a hyphen \"-\" at position " + (pos + 1) + " in '" + address + "'",address,pos+1);
519 }
520 return result;
521 }
522 }