Skip to content

Package: InternetAddress

InternetAddress

nameinstructionbranchcomplexitylinemethod
InternetAddress()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
InternetAddress(String)
M: 35 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
InternetAddress(String, String)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
InternetAddress(String, String, String)
M: 10 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
InternetAddress(String, boolean)
M: 19 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
_getLocalAddress(Session)
M: 87 C: 0
0%
M: 28 C: 0
0%
M: 15 C: 0
0%
M: 20 C: 0
0%
M: 1 C: 0
0%
checkAddress(String, boolean, boolean)
M: 386 C: 0
0%
M: 106 C: 0
0%
M: 54 C: 0
0%
M: 87 C: 0
0%
M: 1 C: 0
0%
clone()
M: 10 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
equals(Object)
M: 27 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
getAddress()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getGroup(boolean)
M: 35 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
getLocalAddress(Session)
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
getLocalHostName()
M: 42 C: 0
0%
M: 14 C: 0
0%
M: 8 C: 0
0%
M: 12 C: 0
0%
M: 1 C: 0
0%
getPersonal()
M: 23 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
getType()
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
hashCode()
M: 11 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
indexOfAny(String, String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
indexOfAny(String, String, int)
M: 23 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
isGroup()
M: 17 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
isInetAddressLiteral(String)
M: 58 C: 0
0%
M: 22 C: 0
0%
M: 12 C: 0
0%
M: 13 C: 0
0%
M: 1 C: 0
0%
isSimple()
M: 12 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
lengthOfFirstSegment(String)
M: 12 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
lengthOfLastSegment(String, int)
M: 19 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
parse(String)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
parse(String, boolean)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
parse(String, boolean, boolean)
M: 839 C: 0
0%
M: 233 C: 0
0%
M: 125 C: 0
0%
M: 265 C: 0
0%
M: 1 C: 0
0%
parseHeader(String, boolean)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
quotePhrase(String)
M: 110 C: 0
0%
M: 28 C: 0
0%
M: 15 C: 0
0%
M: 22 C: 0
0%
M: 1 C: 0
0%
setAddress(String)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setPersonal(String)
M: 14 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
setPersonal(String, String)
M: 16 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 21 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
toString()
M: 58 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
toString(Address[])
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
toString(Address[], int)
M: 75 C: 0
0%
M: 14 C: 0
0%
M: 8 C: 0
0%
M: 18 C: 0
0%
M: 1 C: 0
0%
toUnicodeString()
M: 41 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
toUnicodeString(Address[])
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
toUnicodeString(Address[], int)
M: 106 C: 0
0%
M: 18 C: 0
0%
M: 10 C: 0
0%
M: 26 C: 0
0%
M: 1 C: 0
0%
unquote(String)
M: 65 C: 0
0%
M: 14 C: 0
0%
M: 8 C: 0
0%
M: 11 C: 0
0%
M: 1 C: 0
0%
validate()
M: 14 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package jakarta.mail.internet;
18:
19: import jakarta.mail.Address;
20: import jakarta.mail.Session;
21:
22: import java.io.UnsupportedEncodingException;
23: import java.net.InetAddress;
24: import java.net.UnknownHostException;
25: import java.nio.charset.StandardCharsets;
26: import java.util.ArrayList;
27: import java.util.List;
28: import java.util.Locale;
29: import java.util.StringTokenizer;
30:
31: /**
32: * This class represents an Internet email address using the syntax
33: * of <a href="http://www.ietf.org/rfc/rfc822.txt" target="_top">RFC822</a>.
34: * Typical address syntax is of the form "user@host.domain" or
35: * "Personal Name <user@host.domain>".
36: *
37: * @author Bill Shannon
38: * @author John Mani
39: */
40:
41: public class InternetAddress extends Address implements Cloneable {
42:
43: /**
44: * The email address.
45: */
46: protected String address;
47:
48: /**
49: * The personal name.
50: */
51: protected String personal;
52:
53: /**
54: * The RFC 2047 encoded version of the personal name. <p>
55: *
56: * This field and the <code>personal</code> field track each
57: * other, so if a subclass sets one of these fields directly, it
58: * should set the other to <code>null</code>, so that it is
59: * suitably recomputed.
60: */
61: protected String encodedPersonal;
62:
63: private static final long serialVersionUID = -7507595530758302903L;
64:
65: private static final boolean ignoreBogusGroupName =
66: MimeUtility.getBooleanSystemProperty(
67: "mail.mime.address.ignorebogusgroupname", true);
68:
69: private static final boolean useCanonicalHostName =
70: MimeUtility.getBooleanSystemProperty(
71: "mail.mime.address.usecanonicalhostname", true);
72:
73: private static final boolean allowUtf8 =
74: MimeUtility.getBooleanSystemProperty("mail.mime.allowutf8", false);
75:
76: /**
77: * Default constructor.
78: */
79: public InternetAddress() {
80: }
81:
82: /**
83: * Constructor. <p>
84: *
85: * Parse the given string and create an InternetAddress.
86: * See the <code>parse</code> method for details of the parsing.
87: * The address is parsed using "strict" parsing.
88: * This constructor does <b>not</b> perform the additional
89: * syntax checks that the
90: * <code>InternetAddress(String address, boolean strict)</code>
91: * constructor does when <code>strict</code> is <code>true</code>.
92: * This constructor is equivalent to
93: * <code>InternetAddress(address, false)</code>.
94: *
95: * @param address the address in RFC822 format
96: * @throws AddressException if the parse failed
97: */
98: public InternetAddress(String address) throws AddressException {
99: // use our address parsing utility routine to parse the string
100: InternetAddress[] a = parse(address, true);
101: // if we got back anything other than a single address, it's an error
102:• if (a.length != 1)
103: throw new AddressException("Illegal address", address);
104:
105: /*
106: * Now copy the contents of the single address we parsed
107: * into the current object, which will be returned from the
108: * constructor.
109: * XXX - this sure is a round-about way of getting this done.
110: */
111: this.address = a[0].address;
112: this.personal = a[0].personal;
113: this.encodedPersonal = a[0].encodedPersonal;
114: }
115:
116: /**
117: * Parse the given string and create an InternetAddress.
118: * If <code>strict</code> is false, the detailed syntax of the
119: * address isn't checked.
120: *
121: * @param address the address in RFC822 format
122: * @param strict enforce RFC822 syntax
123: * @throws AddressException if the parse failed
124: * @since JavaMail 1.3
125: */
126: public InternetAddress(String address, boolean strict)
127: throws AddressException {
128: this(address);
129:• if (strict) {
130:• if (isGroup())
131: getGroup(true); // throw away the result
132: else
133: checkAddress(this.address, true, true);
134: }
135: }
136:
137: /**
138: * Construct an InternetAddress given the address and personal name.
139: * The address is assumed to be a syntactically valid RFC822 address.
140: *
141: * @param address the address in RFC822 format
142: * @param personal the personal name
143: * @throws UnsupportedEncodingException if the personal name
144: * can't be encoded in the given charset
145: */
146: public InternetAddress(String address, String personal)
147: throws UnsupportedEncodingException {
148: this(address, personal, null);
149: }
150:
151: /**
152: * Construct an InternetAddress given the address and personal name.
153: * The address is assumed to be a syntactically valid RFC822 address.
154: *
155: * @param address the address in RFC822 format
156: * @param personal the personal name
157: * @param charset the MIME charset for the name
158: * @throws UnsupportedEncodingException if the personal name
159: * can't be encoded in the given charset
160: */
161: public InternetAddress(String address, String personal, String charset)
162: throws UnsupportedEncodingException {
163: this.address = address;
164: setPersonal(personal, charset);
165: }
166:
167: /**
168: * Return a copy of this InternetAddress object.
169: *
170: * @since JavaMail 1.2
171: */
172: @Override
173: public Object clone() {
174: InternetAddress a = null;
175: try {
176: a = (InternetAddress) super.clone();
177: } catch (CloneNotSupportedException e) {
178: } // Won't happen
179: return a;
180: }
181:
182: /**
183: * Return the type of this address. The type of an InternetAddress
184: * is "rfc822".
185: */
186: @Override
187: public String getType() {
188: return "rfc822";
189: }
190:
191: /**
192: * Set the email address.
193: *
194: * @param address email address
195: */
196: public void setAddress(String address) {
197: this.address = address;
198: }
199:
200: /**
201: * Set the personal name. If the name contains non US-ASCII
202: * characters, then the name will be encoded using the specified
203: * charset as per RFC 2047. If the name contains only US-ASCII
204: * characters, no encoding is done and the name is used as is.
205: *
206: * @param name personal name
207: * @param charset MIME charset to be used to encode the name as
208: * per RFC 2047
209: * @throws UnsupportedEncodingException if the charset encoding
210: * fails.
211: * @see #setPersonal(String)
212: */
213: public void setPersonal(String name, String charset)
214: throws UnsupportedEncodingException {
215: personal = name;
216:• if (name != null)
217: encodedPersonal = MimeUtility.encodeWord(name, charset, null);
218: else
219: encodedPersonal = null;
220: }
221:
222: /**
223: * Set the personal name. If the name contains non US-ASCII
224: * characters, then the name will be encoded using the platform's
225: * default charset. If the name contains only US-ASCII characters,
226: * no encoding is done and the name is used as is.
227: *
228: * @param name personal name
229: * @throws UnsupportedEncodingException if the charset encoding
230: * fails.
231: * @see #setPersonal(String name, String charset)
232: */
233: public void setPersonal(String name)
234: throws UnsupportedEncodingException {
235: personal = name;
236:• if (name != null)
237: encodedPersonal = MimeUtility.encodeWord(name);
238: else
239: encodedPersonal = null;
240: }
241:
242: /**
243: * Get the email address.
244: *
245: * @return email address
246: */
247: public String getAddress() {
248: return address;
249: }
250:
251: /**
252: * Get the personal name. If the name is encoded as per RFC 2047,
253: * it is decoded and converted into Unicode. If the decoding or
254: * conversion fails, the raw data is returned as is.
255: *
256: * @return personal name
257: */
258: public String getPersonal() {
259:• if (personal != null)
260: return personal;
261:
262:• if (encodedPersonal != null) {
263: try {
264: personal = MimeUtility.decodeText(encodedPersonal);
265: return personal;
266: } catch (Exception ex) {
267: // 1. ParseException: either its an unencoded string or
268: //        it can't be parsed
269: // 2. UnsupportedEncodingException: can't decode it.
270: return encodedPersonal;
271: }
272: }
273: // No personal or encodedPersonal, return null
274: return null;
275: }
276:
277: /**
278: * Convert this address into a RFC 822 / RFC 2047 encoded address.
279: * The resulting string contains only US-ASCII characters, and
280: * hence is mail-safe.
281: *
282: * @return possibly encoded address string
283: */
284: @Override
285: public String toString() {
286:• String a = address == null ? "" : address;
287:• if (encodedPersonal == null && personal != null)
288: try {
289: encodedPersonal = MimeUtility.encodeWord(personal);
290: } catch (UnsupportedEncodingException ex) {
291: }
292:
293:• if (encodedPersonal != null)
294: return quotePhrase(encodedPersonal) + " <" + a + ">";
295:• else if (isGroup() || isSimple())
296: return a;
297: else
298: return "<" + a + ">";
299: }
300:
301: /**
302: * Returns a properly formatted address (RFC 822 syntax) of
303: * Unicode characters.
304: *
305: * @return Unicode address string
306: * @since JavaMail 1.2
307: */
308: public String toUnicodeString() {
309: String p = getPersonal();
310:• if (p != null)
311: return quotePhrase(p) + " <" + address + ">";
312:• else if (isGroup() || isSimple())
313: return address;
314: else
315: return "<" + address + ">";
316: }
317:
318: /*
319: * quotePhrase() quotes the words within a RFC822 phrase.
320: *
321: * This is tricky, since a phrase is defined as 1 or more
322: * RFC822 words, separated by LWSP. Now, a word that contains
323: * LWSP is supposed to be quoted, and this is exactly what the
324: * MimeUtility.quote() method does. However, when dealing with
325: * a phrase, any LWSP encountered can be construed to be the
326: * separator between words, and not part of the words themselves.
327: * To deal with this funkiness, we have the below variant of
328: * MimeUtility.quote(), which essentially ignores LWSP when
329: * deciding whether to quote a word.
330: *
331: * It aint pretty, but it gets the job done :)
332: */
333:
334: private static final String rfc822phrase =
335: HeaderTokenizer.RFC822.replace(' ', '\0').replace('\t', '\0');
336:
337: private static String quotePhrase(String phrase) {
338: int len = phrase.length();
339: boolean needQuoting = false;
340:
341:• for (int i = 0; i < len; i++) {
342: char c = phrase.charAt(i);
343:• if (c == '"' || c == '\\') {
344: // need to escape them and then quote the whole string
345: StringBuilder sb = new StringBuilder(len + 3);
346: sb.append('"');
347:• for (int j = 0; j < len; j++) {
348: char cc = phrase.charAt(j);
349:• if (cc == '"' || cc == '\\')
350: // Escape the character
351: sb.append('\\');
352: sb.append(cc);
353: }
354: sb.append('"');
355: return sb.toString();
356:• } else if ((c < 040 && c != '\r' && c != '\n' && c != '\t') ||
357:• (c >= 0177 && !allowUtf8) || rfc822phrase.indexOf(c) >= 0)
358: // These characters cause the string to be quoted
359: needQuoting = true;
360: }
361:
362:• if (needQuoting) {
363: StringBuilder sb = new StringBuilder(len + 2);
364: sb.append('"').append(phrase).append('"');
365: return sb.toString();
366: } else
367: return phrase;
368: }
369:
370: private static String unquote(String s) {
371:• if (s.startsWith("\"") && s.endsWith("\"") && s.length() > 1) {
372: s = s.substring(1, s.length() - 1);
373: // check for any escaped characters
374:• if (s.indexOf('\\') >= 0) {
375: StringBuilder sb = new StringBuilder(s.length()); // approx
376:• for (int i = 0; i < s.length(); i++) {
377: char c = s.charAt(i);
378:• if (c == '\\' && i < s.length() - 1)
379: c = s.charAt(++i);
380: sb.append(c);
381: }
382: s = sb.toString();
383: }
384: }
385: return s;
386: }
387:
388: /**
389: * The equality operator.
390: */
391: @Override
392: public boolean equals(Object a) {
393:• if (!(a instanceof InternetAddress))
394: return false;
395:
396: String s = ((InternetAddress) a).getAddress();
397:• if (s == address)
398: return true;
399:• if (address != null && address.equalsIgnoreCase(s))
400: return true;
401:
402: return false;
403: }
404:
405: /**
406: * Compute a hash code for the address.
407: */
408: @Override
409: public int hashCode() {
410:• if (address == null)
411: return 0;
412: else
413: return address.toLowerCase(Locale.ENGLISH).hashCode();
414: }
415:
416: /**
417: * Convert the given array of InternetAddress objects into
418: * a comma separated sequence of address strings. The
419: * resulting string contains only US-ASCII characters, and
420: * hence is mail-safe.
421: *
422: * @param addresses array of InternetAddress objects
423: * @return comma separated string of addresses
424: * @throws ClassCastException if any address object in the
425: * given array is not an InternetAddress object. Note
426: * that this is a RuntimeException.
427: */
428: public static String toString(Address[] addresses) {
429: return toString(addresses, 0);
430: }
431:
432: /**
433: * Convert the given array of InternetAddress objects into
434: * a comma separated sequence of address strings. The
435: * resulting string contains Unicode characters.
436: *
437: * @param addresses array of InternetAddress objects
438: * @return comma separated string of addresses
439: * @throws ClassCastException if any address object in the
440: * given array is not an InternetAddress object. Note
441: * that this is a RuntimeException.
442: * @since JavaMail 1.6
443: */
444: public static String toUnicodeString(Address[] addresses) {
445: return toUnicodeString(addresses, 0);
446: }
447:
448: /**
449: * Convert the given array of InternetAddress objects into
450: * a comma separated sequence of address strings. The
451: * resulting string contains only US-ASCII characters, and
452: * hence is mail-safe. <p>
453: *
454: * The 'used' parameter specifies the number of character positions
455: * already taken up in the field into which the resulting address
456: * sequence string is to be inserted. It is used to determine the
457: * line-break positions in the resulting address sequence string.
458: *
459: * @param addresses array of InternetAddress objects
460: * @param used number of character positions already used, in
461: * the field into which the address string is to
462: * be inserted.
463: * @return comma separated string of addresses
464: * @throws ClassCastException if any address object in the
465: * given array is not an InternetAddress object. Note
466: * that this is a RuntimeException.
467: */
468: public static String toString(Address[] addresses, int used) {
469:• if (addresses == null || addresses.length == 0)
470: return null;
471:
472: StringBuilder sb = new StringBuilder();
473:
474:• for (int i = 0; i < addresses.length; i++) {
475:• if (i != 0) { // need to append comma
476: sb.append(", ");
477: used += 2;
478: }
479:
480: // prefer not to split a single address across lines so used=0 below
481: String s = MimeUtility.fold(0, addresses[i].toString());
482: int len = lengthOfFirstSegment(s); // length till CRLF
483:• if (used + len > 76) { // overflows ...
484: // smash trailing space from ", " above
485: int curlen = sb.length();
486:• if (curlen > 0 && sb.charAt(curlen - 1) == ' ')
487: sb.setLength(curlen - 1);
488: sb.append("\r\n\t"); // .. start new continuation line
489: used = 8; // account for the starting <tab> char
490: }
491: sb.append(s);
492: used = lengthOfLastSegment(s, used);
493: }
494:
495: return sb.toString();
496: }
497:
498: /**
499: * Convert the given array of InternetAddress objects into
500: * a comma separated sequence of address strings. The
501: * resulting string contains Unicode characters. <p>
502: *
503: * The 'used' parameter specifies the number of character positions
504: * already taken up in the field into which the resulting address
505: * sequence string is to be inserted. It is used to determine the
506: * line-break positions in the resulting address sequence string.
507: *
508: * @param addresses array of InternetAddress objects
509: * @param used number of character positions already used, in
510: * the field into which the address string is to
511: * be inserted.
512: * @return comma separated string of addresses
513: * @throws ClassCastException if any address object in the
514: * given array is not an InternetAddress object. Note
515: * that this is a RuntimeException.
516: * @since JavaMail 1.6
517: */
518: /*
519: * XXX - This is exactly the same as the above, except it uses
520: *         toUnicodeString instead of toString.
521: * XXX - Since the line length restrictions are in bytes, not characters,
522: *         we convert all non-ASCII addresses to UTF-8 byte strings,
523: *         which we then convert to ISO-8859-1 Strings where every
524: *         character respresents one UTF-8 byte. At the end we reverse
525: *         the conversion to get back to a correct Unicode string.
526: *         This is a hack to allow all the other character-based methods
527: *         to work properly with UTF-8 bytes.
528: */
529: public static String toUnicodeString(Address[] addresses, int used) {
530:• if (addresses == null || addresses.length == 0)
531: return null;
532:
533: StringBuilder sb = new StringBuilder();
534:
535: boolean sawNonAscii = false;
536:• for (int i = 0; i < addresses.length; i++) {
537:• if (i != 0) { // need to append comma
538: sb.append(", ");
539: used += 2;
540: }
541:
542: // prefer not to split a single address across lines so used=0 below
543: String as = ((InternetAddress) addresses[i]).toUnicodeString();
544:• if (MimeUtility.checkAscii(as) != MimeUtility.ALL_ASCII) {
545: sawNonAscii = true;
546: as = new String(as.getBytes(StandardCharsets.UTF_8),
547: StandardCharsets.ISO_8859_1);
548: }
549: String s = MimeUtility.fold(0, as);
550: int len = lengthOfFirstSegment(s); // length till CRLF
551:• if (used + len > 76) { // overflows ...
552: // smash trailing space from ", " above
553: int curlen = sb.length();
554:• if (curlen > 0 && sb.charAt(curlen - 1) == ' ')
555: sb.setLength(curlen - 1);
556: sb.append("\r\n\t"); // .. start new continuation line
557: used = 8; // account for the starting <tab> char
558: }
559: sb.append(s);
560: used = lengthOfLastSegment(s, used);
561: }
562:
563: String ret = sb.toString();
564:• if (sawNonAscii)
565: ret = new String(ret.getBytes(StandardCharsets.ISO_8859_1),
566: StandardCharsets.UTF_8);
567: return ret;
568: }
569:
570: /*
571: * Return the length of the first segment within this string.
572: * If no segments exist, the length of the whole line is returned.
573: */
574: private static int lengthOfFirstSegment(String s) {
575: int pos;
576:• if ((pos = s.indexOf("\r\n")) != -1)
577: return pos;
578: else
579: return s.length();
580: }
581:
582: /*
583: * Return the length of the last segment within this string.
584: * If no segments exist, the length of the whole line plus
585: * <code>used</code> is returned.
586: */
587: private static int lengthOfLastSegment(String s, int used) {
588: int pos;
589:• if ((pos = s.lastIndexOf("\r\n")) != -1)
590: return s.length() - pos - 2;
591: else
592: return s.length() + used;
593: }
594:
595: /**
596: * Return an InternetAddress object representing the current user.
597: * The entire email address may be specified in the "mail.from"
598: * property. If not set, the "mail.user" and "mail.host" properties
599: * are tried. If those are not set, the "user.name" property and
600: * <code>InetAddress.getLocalHost</code> method are tried.
601: * Security exceptions that may occur while accessing this information
602: * are ignored. If it is not possible to determine an email address,
603: * null is returned.
604: *
605: * @param session Session object used for property lookup
606: * @return current user's email address
607: */
608: public static InternetAddress getLocalAddress(Session session) {
609: try {
610: return _getLocalAddress(session);
611: } catch (SecurityException | AddressException sex) { // ignore it
612: } catch (UnknownHostException ex) {
613: } // ignore it
614: return null;
615: }
616:
617: /**
618: * A package-private version of getLocalAddress that doesn't swallow
619: * the exception. Used by MimeMessage.setFrom() to report the reason
620: * for the failure.
621: */
622: // package-private
623: static InternetAddress _getLocalAddress(Session session)
624: throws SecurityException, AddressException, UnknownHostException {
625: String user = null, host = null, address = null;
626:• if (session == null) {
627: user = System.getProperty("user.name");
628: host = getLocalHostName();
629: } else {
630: address = session.getProperty("mail.from");
631:• if (address == null) {
632: user = session.getProperty("mail.user");
633:• if (user == null || user.length() == 0)
634: user = session.getProperty("user.name");
635:• if (user == null || user.length() == 0)
636: user = System.getProperty("user.name");
637: host = session.getProperty("mail.host");
638:• if (host == null || host.length() == 0)
639: host = getLocalHostName();
640: }
641: }
642:
643:• if (address == null && user != null && user.length() != 0 &&
644:• host != null && host.length() != 0)
645: address = MimeUtility.quote(user.trim(), specialsNoDot + "\t ") +
646: "@" + host;
647:
648:• if (address == null)
649: return null;
650:
651: return new InternetAddress(address);
652: }
653:
654: /**
655: * Get the local host name from InetAddress and return it in a form
656: * suitable for use in an email address.
657: */
658: private static String getLocalHostName() throws UnknownHostException {
659: String host = null;
660: InetAddress me = InetAddress.getLocalHost();
661:• if (me != null) {
662: // try canonical host name first
663:• if (useCanonicalHostName)
664: host = me.getCanonicalHostName();
665:• if (host == null)
666: host = me.getHostName();
667: // if we can't get our name, use local address literal
668:• if (host == null)
669: host = me.getHostAddress();
670:• if (host != null && host.length() > 0 && isInetAddressLiteral(host))
671: host = '[' + host + ']';
672: }
673: return host;
674: }
675:
676: /**
677: * Is the address an IPv4 or IPv6 address literal, which needs to
678: * be enclosed in "[]" in an email address? IPv4 literals contain
679: * decimal digits and dots, IPv6 literals contain hex digits, dots,
680: * and colons. We're lazy and don't check the exact syntax, just
681: * the allowed characters; strings that have only the allowed
682: * characters in a literal but don't meet the syntax requirements
683: * for a literal definitely can't be a host name and thus will fail
684: * later when used as an address literal.
685: */
686: private static boolean isInetAddressLiteral(String addr) {
687: boolean sawHex = false, sawColon = false;
688:• for (int i = 0; i < addr.length(); i++) {
689: char c = addr.charAt(i);
690:• if (c >= '0' && c <= '9')
691: ; // digits always ok
692:• else if (c == '.')
693: ; // dot always ok
694:• else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
695: sawHex = true; // need to see a colon too
696:• else if (c == ':')
697: sawColon = true;
698: else
699: return false; // anything else, definitely not a literal
700: }
701:• return !sawHex || sawColon;
702: }
703:
704: /**
705: * Parse the given comma separated sequence of addresses into
706: * InternetAddress objects. Addresses must follow RFC822 syntax.
707: *
708: * @param addresslist comma separated address strings
709: * @return array of InternetAddress objects
710: * @throws AddressException if the parse failed
711: */
712: public static InternetAddress[] parse(String addresslist)
713: throws AddressException {
714: return parse(addresslist, true);
715: }
716:
717: /**
718: * Parse the given sequence of addresses into InternetAddress
719: * objects. If <code>strict</code> is false, simple email addresses
720: * separated by spaces are also allowed. If <code>strict</code> is
721: * true, many (but not all) of the RFC822 syntax rules are enforced.
722: * In particular, even if <code>strict</code> is true, addresses
723: * composed of simple names (with no "@domain" part) are allowed.
724: * Such "illegal" addresses are not uncommon in real messages. <p>
725: *
726: * Non-strict parsing is typically used when parsing a list of
727: * mail addresses entered by a human. Strict parsing is typically
728: * used when parsing address headers in mail messages.
729: *
730: * @param addresslist comma separated address strings
731: * @param strict enforce RFC822 syntax
732: * @return array of InternetAddress objects
733: * @throws AddressException if the parse failed
734: */
735: public static InternetAddress[] parse(String addresslist, boolean strict)
736: throws AddressException {
737: return parse(addresslist, strict, false);
738: }
739:
740: /**
741: * Parse the given sequence of addresses into InternetAddress
742: * objects. If <code>strict</code> is false, the full syntax rules for
743: * individual addresses are not enforced. If <code>strict</code> is
744: * true, many (but not all) of the RFC822 syntax rules are enforced. <p>
745: *
746: * To better support the range of "invalid" addresses seen in real
747: * messages, this method enforces fewer syntax rules than the
748: * <code>parse</code> method when the strict flag is false
749: * and enforces more rules when the strict flag is true. If the
750: * strict flag is false and the parse is successful in separating out an
751: * email address or addresses, the syntax of the addresses themselves
752: * is not checked.
753: *
754: * @param addresslist comma separated address strings
755: * @param strict enforce RFC822 syntax
756: * @return array of InternetAddress objects
757: * @throws AddressException if the parse failed
758: * @since JavaMail 1.3
759: */
760: public static InternetAddress[] parseHeader(String addresslist,
761: boolean strict) throws AddressException {
762: return parse(MimeUtility.unfold(addresslist), strict, true);
763: }
764:
765: /*
766: * RFC822 Address parser.
767: *
768: * XXX - This is complex enough that it ought to be a real parser,
769: * not this ad-hoc mess, and because of that, this is not perfect.
770: *
771: * XXX - Deal with encoded Headers too.
772: */
773: @SuppressWarnings("fallthrough")
774: private static InternetAddress[] parse(String s, boolean strict,
775: boolean parseHdr) throws AddressException {
776: int start, end, index, nesting;
777: int start_personal = -1, end_personal = -1;
778: int length = s.length();
779:• boolean ignoreErrors = parseHdr && !strict;
780: boolean in_group = false; // we're processing a group term
781: boolean route_addr = false; // address came from route-addr term
782: boolean rfc822 = false; // looks like an RFC822 address
783: char c;
784: List<InternetAddress> v = new ArrayList<>();
785: InternetAddress ma;
786:
787:• for (start = end = -1, index = 0; index < length; index++) {
788: c = s.charAt(index);
789:
790:• switch (c) {
791: case '(': // We are parsing a Comment. Ignore everything inside.
792: // XXX - comment fields should be parsed as whitespace,
793: //         more than one allowed per address
794: rfc822 = true;
795:• if (start >= 0 && end == -1)
796: end = index;
797: int pindex = index;
798:• for (index++, nesting = 1; index < length && nesting > 0;
799: index++) {
800: c = s.charAt(index);
801:• switch (c) {
802: case '\\':
803: index++; // skip both '\' and the escaped char
804: break;
805: case '(':
806: nesting++;
807: break;
808: case ')':
809: nesting--;
810: break;
811: default:
812: break;
813: }
814: }
815:• if (nesting > 0) {
816:• if (!ignoreErrors)
817: throw new AddressException("Missing ')'", s, index);
818: // pretend the first paren was a regular character and
819: // continue parsing after it
820: index = pindex + 1;
821: break;
822: }
823: index--; // point to closing paren
824:• if (start_personal == -1)
825: start_personal = pindex + 1;
826:• if (end_personal == -1)
827: end_personal = index;
828: break;
829:
830: case ')':
831:• if (!ignoreErrors)
832: throw new AddressException("Missing '('", s, index);
833: // pretend the left paren was a regular character and
834: // continue parsing
835:• if (start == -1)
836: start = index;
837: break;
838:
839: case '<':
840: rfc822 = true;
841:• if (route_addr) {
842:• if (!ignoreErrors)
843: throw new AddressException(
844: "Extra route-addr", s, index);
845:
846: // assume missing comma between addresses
847:• if (start == -1) {
848: route_addr = false;
849: rfc822 = false;
850: start = end = -1;
851: break; // nope, nothing there
852: }
853:• if (!in_group) {
854: // got a token, add this to our InternetAddress list
855:• if (end == -1) // should never happen
856: end = index;
857: String addr = s.substring(start, end).trim();
858:
859: ma = new InternetAddress();
860: ma.setAddress(addr);
861:• if (start_personal >= 0) {
862: ma.encodedPersonal = unquote(
863: s.substring(start_personal, end_personal).
864: trim());
865: }
866: v.add(ma);
867:
868: route_addr = false;
869: rfc822 = false;
870: start = end = -1;
871: start_personal = end_personal = -1;
872: // continue processing this new address...
873: }
874: }
875:
876: int rindex = index;
877: boolean inquote = false;
878: outf:
879:• for (index++; index < length; index++) {
880: c = s.charAt(index);
881:• switch (c) {
882: case '\\': // XXX - is this needed?
883: index++; // skip both '\' and the escaped char
884: break;
885: case '"':
886:• inquote = !inquote;
887: break;
888: case '>':
889:• if (inquote)
890: continue;
891: break outf; // out of for loop
892: default:
893: break;
894: }
895: }
896:
897: // did we find a matching quote?
898:• if (inquote) {
899:• if (!ignoreErrors)
900: throw new AddressException("Missing '\"'", s, index);
901: // didn't find matching quote, try again ignoring quotes
902: // (e.g., ``<"@foo.com>'')
903: outq:
904:• for (index = rindex + 1; index < length; index++) {
905: c = s.charAt(index);
906:• if (c == '\\') // XXX - is this needed?
907: index++; // skip both '\' and the escaped char
908:• else if (c == '>')
909: break;
910: }
911: }
912:
913: // did we find a terminating '>'?
914:• if (index >= length) {
915:• if (!ignoreErrors)
916: throw new AddressException("Missing '>'", s, index);
917: // pretend the "<" was a regular character and
918: // continue parsing after it (e.g., ``<@foo.com'')
919: index = rindex + 1;
920:• if (start == -1)
921: start = rindex; // back up to include "<"
922: break;
923: }
924:
925:• if (!in_group) {
926:• if (start >= 0) {
927: // seen some characters? use them as the personal name
928: start_personal = start;
929: end_personal = rindex;
930: }
931: start = rindex + 1;
932: }
933: route_addr = true;
934: end = index;
935: break;
936:
937: case '>':
938:• if (!ignoreErrors)
939: throw new AddressException("Missing '<'", s, index);
940: // pretend the ">" was a regular character and
941: // continue parsing (e.g., ``>@foo.com'')
942:• if (start == -1)
943: start = index;
944: break;
945:
946: case '"': // parse quoted string
947: int qindex = index;
948: rfc822 = true;
949:• if (start == -1)
950: start = index;
951: outq:
952:• for (index++; index < length; index++) {
953: c = s.charAt(index);
954:• switch (c) {
955: case '\\':
956: index++; // skip both '\' and the escaped char
957: break;
958: case '"':
959: break outq; // out of for loop
960: default:
961: break;
962: }
963: }
964:• if (index >= length) {
965:• if (!ignoreErrors)
966: throw new AddressException("Missing '\"'", s, index);
967: // pretend the quote was a regular character and
968: // continue parsing after it (e.g., ``"@foo.com'')
969: index = qindex + 1;
970: }
971: break;
972:
973: case '[': // a domain-literal, probably
974: int lindex = index;
975: rfc822 = true;
976:• if (start == -1)
977: start = index;
978: outb:
979:• for (index++; index < length; index++) {
980: c = s.charAt(index);
981:• switch (c) {
982: case '\\':
983: index++; // skip both '\' and the escaped char
984: break;
985: case ']':
986: break outb; // out of for loop
987: default:
988: break;
989: }
990: }
991:• if (index >= length) {
992:• if (!ignoreErrors)
993: throw new AddressException("Missing ']'", s, index);
994: // pretend the "[" was a regular character and
995: // continue parsing after it (e.g., ``[@foo.com'')
996: index = lindex + 1;
997: }
998: break;
999:
1000: case ';':
1001:• if (start == -1) {
1002: route_addr = false;
1003: rfc822 = false;
1004: start = end = -1;
1005: break; // nope, nothing there
1006: }
1007:• if (in_group) {
1008: in_group = false;
1009: /*
1010: * If parsing headers, but not strictly, peek ahead.
1011: * If next char is "@", treat the group name
1012: * like the local part of the address, e.g.,
1013: * "Undisclosed-Recipient:;@java.sun.com".
1014: */
1015:• if (parseHdr && !strict &&
1016:• index + 1 < length && s.charAt(index + 1) == '@')
1017: break;
1018: ma = new InternetAddress();
1019: end = index + 1;
1020: ma.setAddress(s.substring(start, end).trim());
1021: v.add(ma);
1022:
1023: route_addr = false;
1024: rfc822 = false;
1025: start = end = -1;
1026: start_personal = end_personal = -1;
1027: break;
1028: }
1029:• if (!ignoreErrors)
1030: throw new AddressException(
1031: "Illegal semicolon, not in group", s, index);
1032:
1033: // otherwise, parsing a header; treat semicolon like comma
1034: // fall through to comma case...
1035:
1036: case ',': // end of an address, probably
1037:• if (start == -1) {
1038: route_addr = false;
1039: rfc822 = false;
1040: start = end = -1;
1041: break; // nope, nothing there
1042: }
1043:• if (in_group) {
1044: route_addr = false;
1045: break;
1046: }
1047: // got a token, add this to our InternetAddress list
1048:• if (end == -1)
1049: end = index;
1050:
1051: String addr = s.substring(start, end).trim();
1052: String pers = null;
1053:• if (rfc822 && start_personal >= 0) {
1054: pers = unquote(
1055: s.substring(start_personal, end_personal).trim());
1056:• if (pers.trim().length() == 0)
1057: pers = null;
1058: }
1059:
1060: /*
1061: * If the personal name field has an "@" and the address
1062: * field does not, assume they were reversed, e.g.,
1063: * ``"joe doe" (john.doe@example.com)''.
1064: */
1065:• if (parseHdr && !strict && pers != null &&
1066:• pers.indexOf('@') >= 0 &&
1067:• addr.indexOf('@') < 0 && addr.indexOf('!') < 0) {
1068: String tmp = addr;
1069: addr = pers;
1070: pers = tmp;
1071: }
1072:• if (rfc822 || strict || parseHdr) {
1073:• if (!ignoreErrors)
1074: checkAddress(addr, route_addr, false);
1075: ma = new InternetAddress();
1076: ma.setAddress(addr);
1077:• if (pers != null)
1078: ma.encodedPersonal = pers;
1079: v.add(ma);
1080: } else {
1081: // maybe we passed over more than one space-separated addr
1082: StringTokenizer st = new StringTokenizer(addr);
1083:• while (st.hasMoreTokens()) {
1084: String a = st.nextToken();
1085: checkAddress(a, false, false);
1086: ma = new InternetAddress();
1087: ma.setAddress(a);
1088: v.add(ma);
1089: }
1090: }
1091:
1092: route_addr = false;
1093: rfc822 = false;
1094: start = end = -1;
1095: start_personal = end_personal = -1;
1096: break;
1097:
1098: case ':':
1099: rfc822 = true;
1100:• if (in_group)
1101:• if (!ignoreErrors)
1102: throw new AddressException("Nested group", s, index);
1103:• if (start == -1)
1104: start = index;
1105:• if (parseHdr && !strict) {
1106: /*
1107: * If next char is a special character that can't occur at
1108: * the start of a valid address, treat the group name
1109: * as the entire address, e.g., "Date:, Tue", "Re:@foo".
1110: */
1111:• if (index + 1 < length) {
1112: String addressSpecials = ")>[]:@\\,.";
1113: char nc = s.charAt(index + 1);
1114:• if (addressSpecials.indexOf(nc) >= 0) {
1115:• if (nc != '@')
1116: break; // don't change in_group
1117: /*
1118: * Handle a common error:
1119: * ``Undisclosed-Recipient:@example.com;''
1120: *
1121: * Scan ahead. If we find a semicolon before
1122: * one of these other special characters,
1123: * consider it to be a group after all.
1124: */
1125:• for (int i = index + 2; i < length; i++) {
1126: nc = s.charAt(i);
1127:• if (nc == ';')
1128: break;
1129:• if (addressSpecials.indexOf(nc) >= 0)
1130: break;
1131: }
1132:• if (nc == ';')
1133: break; // don't change in_group
1134: }
1135: }
1136:
1137: // ignore bogus "mailto:" prefix in front of an address,
1138: // or bogus mail header name included in the address field
1139: String gname = s.substring(start, index);
1140:• if (ignoreBogusGroupName &&
1141:• (gname.equalsIgnoreCase("mailto") ||
1142:• gname.equalsIgnoreCase("From") ||
1143:• gname.equalsIgnoreCase("To") ||
1144:• gname.equalsIgnoreCase("Cc") ||
1145:• gname.equalsIgnoreCase("Subject") ||
1146:• gname.equalsIgnoreCase("Re")))
1147: start = -1; // we're not really in a group
1148: else
1149: in_group = true;
1150: } else
1151: in_group = true;
1152: break;
1153:
1154: // Ignore whitespace
1155: case ' ':
1156: case '\t':
1157: case '\r':
1158: case '\n':
1159: break;
1160:
1161: default:
1162:• if (start == -1)
1163: start = index;
1164: break;
1165: }
1166: }
1167:
1168:• if (start >= 0) {
1169: /*
1170: * The last token, add this to our InternetAddress list.
1171: * Note that this block of code should be identical to the
1172: * block above for "case ','".
1173: */
1174:• if (end == -1)
1175: end = length;
1176:
1177: String addr = s.substring(start, end).trim();
1178: String pers = null;
1179:• if (rfc822 && start_personal >= 0) {
1180: pers = unquote(
1181: s.substring(start_personal, end_personal).trim());
1182:• if (pers.trim().length() == 0)
1183: pers = null;
1184: }
1185:
1186: /*
1187: * If the personal name field has an "@" and the address
1188: * field does not, assume they were reversed, e.g.,
1189: * ``"joe doe" (john.doe@example.com)''.
1190: */
1191:• if (parseHdr && !strict &&
1192:• pers != null && pers.indexOf('@') >= 0 &&
1193:• addr.indexOf('@') < 0 && addr.indexOf('!') < 0) {
1194: String tmp = addr;
1195: addr = pers;
1196: pers = tmp;
1197: }
1198:• if (rfc822 || strict || parseHdr) {
1199:• if (!ignoreErrors)
1200: checkAddress(addr, route_addr, false);
1201: ma = new InternetAddress();
1202: ma.setAddress(addr);
1203:• if (pers != null)
1204: ma.encodedPersonal = pers;
1205: v.add(ma);
1206: } else {
1207: // maybe we passed over more than one space-separated addr
1208: StringTokenizer st = new StringTokenizer(addr);
1209:• while (st.hasMoreTokens()) {
1210: String a = st.nextToken();
1211: checkAddress(a, false, false);
1212: ma = new InternetAddress();
1213: ma.setAddress(a);
1214: v.add(ma);
1215: }
1216: }
1217: }
1218:
1219: InternetAddress[] a = new InternetAddress[v.size()];
1220: v.toArray(a);
1221: return a;
1222: }
1223:
1224: /**
1225: * Validate that this address conforms to the syntax rules of
1226: * RFC 822. The current implementation checks many, but not
1227: * all, syntax rules. Note that even though the syntax of
1228: * the address may be correct, there's no guarantee that a
1229: * mailbox of that name exists.
1230: *
1231: * @throws AddressException if the address isn't valid.
1232: * @since JavaMail 1.3
1233: */
1234: public void validate() throws AddressException {
1235:• if (isGroup())
1236: getGroup(true); // throw away the result
1237: else
1238: checkAddress(getAddress(), true, true);
1239: }
1240:
1241: private static final String specialsNoDotNoAt = "()<>,;:\\\"[]";
1242: private static final String specialsNoDot = specialsNoDotNoAt + "@";
1243:
1244: /**
1245: * Check that the address is a valid "mailbox" per RFC822.
1246: * (We also allow simple names.)
1247: *
1248: * XXX - much more to check
1249: * XXX - doesn't handle domain-literals properly (but no one uses them)
1250: */
1251: private static void checkAddress(String addr,
1252: boolean routeAddr, boolean validate)
1253: throws AddressException {
1254: int i, start = 0;
1255:
1256:• if (addr == null)
1257: throw new AddressException("Address is null");
1258: int len = addr.length();
1259:• if (len == 0)
1260: throw new AddressException("Empty address", addr);
1261:
1262: /*
1263: * routeAddr indicates that the address is allowed
1264: * to have an RFC 822 "route".
1265: */
1266:• if (routeAddr && addr.charAt(0) == '@') {
1267: /*
1268: * Check for a legal "route-addr":
1269: *                [@domain[,@domain ...]:]local@domain
1270: */
1271:• for (start = 0; (i = indexOfAny(addr, ",:", start)) >= 0;
1272: start = i + 1) {
1273:• if (addr.charAt(start) != '@')
1274: throw new AddressException("Illegal route-addr", addr);
1275:• if (addr.charAt(i) == ':') {
1276: // end of route-addr
1277: start = i + 1;
1278: break;
1279: }
1280: }
1281: }
1282:
1283: /*
1284: * The rest should be "local@domain", but we allow simply "local"
1285: * unless called from validate.
1286: *
1287: * local-part must follow RFC 822 - no specials except '.'
1288: * unless quoted.
1289: */
1290:
1291: char c = (char) -1;
1292: char lastc = (char) -1;
1293: boolean inquote = false;
1294:• for (i = start; i < len; i++) {
1295: lastc = c;
1296: c = addr.charAt(i);
1297: // a quoted-pair is only supposed to occur inside a quoted string,
1298: // but some people use it outside so we're more lenient
1299:• if (c == '\\' || lastc == '\\')
1300: continue;
1301:• if (c == '"') {
1302:• if (inquote) {
1303: // peek ahead, next char must be "@"
1304:• if (validate && i + 1 < len && addr.charAt(i + 1) != '@')
1305: throw new AddressException(
1306: "Quote not at end of local address", addr);
1307: inquote = false;
1308: } else {
1309:• if (validate && i != 0)
1310: throw new AddressException(
1311: "Quote not at start of local address", addr);
1312: inquote = true;
1313: }
1314: continue;
1315:• } else if (c == '\r') {
1316: // peek ahead, next char must be LF
1317:• if (i + 1 < len && addr.charAt(i + 1) != '\n')
1318: throw new AddressException(
1319: "Quoted local address contains CR without LF", addr);
1320:• } else if (c == '\n') {
1321: /*
1322: * CRLF followed by whitespace is allowed in a quoted string.
1323: * We allowed naked LF, but ensure LF is always followed by
1324: * whitespace to prevent spoofing the end of the header.
1325: */
1326:• if (i + 1 < len && addr.charAt(i + 1) != ' ' &&
1327:• addr.charAt(i + 1) != '\t')
1328: throw new AddressException(
1329: "Quoted local address contains newline without whitespace",
1330: addr);
1331: }
1332:• if (inquote)
1333: continue;
1334: // dot rules should not be applied to quoted-string
1335:• if (c == '.') {
1336:• if (i == start)
1337: throw new AddressException(
1338: "Local address starts with dot", addr);
1339:• if (lastc == '.')
1340: throw new AddressException(
1341: "Local address contains dot-dot", addr);
1342: }
1343:• if (c == '@') {
1344:• if (i == 0)
1345: throw new AddressException("Missing local name", addr);
1346:• if (lastc == '.')
1347: throw new AddressException(
1348: "Local address ends with dot", addr);
1349: break; // done with local part
1350: }
1351:• if (c <= 040 || c == 0177)
1352: throw new AddressException(
1353: "Local address contains control or whitespace", addr);
1354:• if (specialsNoDot.indexOf(c) >= 0)
1355: throw new AddressException(
1356: "Local address contains illegal character", addr);
1357: }
1358:• if (inquote)
1359: throw new AddressException("Unterminated quote", addr);
1360:
1361: /*
1362: * Done with local part, now check domain.
1363: *
1364: * Note that the MimeMessage class doesn't remember addresses
1365: * as separate objects; it writes them out as headers and then
1366: * parses the headers when the addresses are requested.
1367: * In order to support the case where a "simple" address is used,
1368: * but the address also has a personal name and thus looks like
1369: * it should be a valid RFC822 address when parsed, we only check
1370: * this if we're explicitly called from the validate method.
1371: */
1372:
1373:• if (c != '@') {
1374:• if (validate)
1375: throw new AddressException("Missing final '@domain'", addr);
1376: return;
1377: }
1378:
1379: // check for illegal chars in the domain, but ignore domain literals
1380:
1381: start = i + 1;
1382:• if (start >= len)
1383: throw new AddressException("Missing domain", addr);
1384:
1385:• if (addr.charAt(start) == '.')
1386: throw new AddressException("Domain starts with dot", addr);
1387: boolean inliteral = false;
1388:• for (i = start; i < len; i++) {
1389: c = addr.charAt(i);
1390:• if (c == '[') {
1391:• if (i != start)
1392: throw new AddressException(
1393: "Domain literal not at start of domain", addr);
1394: inliteral = true; // domain literal, don't validate
1395:• } else if (c == ']') {
1396:• if (i != len - 1)
1397: throw new AddressException(
1398: "Domain literal end not at end of domain", addr);
1399: inliteral = false;
1400:• } else if (c <= 040 || c == 0177) {
1401: throw new AddressException(
1402: "Domain contains control or whitespace", addr);
1403: } else {
1404: // RFC 2822 rule
1405: //if (specialsNoDot.indexOf(c) >= 0)
1406: /*
1407: * RFC 1034 rule is more strict
1408: * the full rule is:
1409: *
1410: * <domain> ::= <subdomain> | " "
1411: * <subdomain> ::= <label> | <subdomain> "." <label>
1412: * <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
1413: * <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
1414: * <let-dig-hyp> ::= <let-dig> | "-"
1415: * <let-dig> ::= <letter> | <digit>
1416: */
1417:• if (!inliteral) {
1418:• if (!(Character.isLetterOrDigit(c) || c == '-' || c == '.'))
1419: throw new AddressException(
1420: "Domain contains illegal character", addr);
1421:• if (c == '.' && lastc == '.')
1422: throw new AddressException(
1423: "Domain contains dot-dot", addr);
1424: }
1425: }
1426: lastc = c;
1427: }
1428:• if (lastc == '.')
1429: throw new AddressException("Domain ends with dot", addr);
1430: }
1431:
1432: /**
1433: * Is this a "simple" address? Simple addresses don't contain quotes
1434: * or any RFC822 special characters other than '@' and '.'.
1435: */
1436: private boolean isSimple() {
1437:• return address == null || indexOfAny(address, specialsNoDotNoAt) < 0;
1438: }
1439:
1440: /**
1441: * Indicates whether this address is an RFC 822 group address.
1442: * Note that a group address is different than the mailing
1443: * list addresses supported by most mail servers. Group addresses
1444: * are rarely used; see RFC 822 for details.
1445: *
1446: * @return true if this address represents a group
1447: * @since JavaMail 1.3
1448: */
1449: public boolean isGroup() {
1450: // quick and dirty check
1451:• return address != null &&
1452:• address.endsWith(";") && address.indexOf(':') > 0;
1453: }
1454:
1455: /**
1456: * Return the members of a group address. A group may have zero,
1457: * one, or more members. If this address is not a group, null
1458: * is returned. The <code>strict</code> parameter controls whether
1459: * the group list is parsed using strict RFC 822 rules or not.
1460: * The parsing is done using the <code>parseHeader</code> method.
1461: *
1462: * @param strict use strict RFC 822 rules?
1463: * @return array of InternetAddress objects, or null
1464: * @throws AddressException if the group list can't be parsed
1465: * @since JavaMail 1.3
1466: */
1467: public InternetAddress[] getGroup(boolean strict) throws AddressException {
1468: String addr = getAddress();
1469:• if (addr == null)
1470: return null;
1471: // groups are of the form "name:addr,addr,...;"
1472:• if (!addr.endsWith(";"))
1473: return null;
1474: int ix = addr.indexOf(':');
1475:• if (ix < 0)
1476: return null;
1477: // extract the list
1478: String list = addr.substring(ix + 1, addr.length() - 1);
1479: // parse it and return the individual addresses
1480: return InternetAddress.parseHeader(list, strict);
1481: }
1482:
1483: /**
1484: * Return the first index of any of the characters in "any" in "s",
1485: * or -1 if none are found.
1486: *
1487: * This should be a method on String.
1488: */
1489: private static int indexOfAny(String s, String any) {
1490: return indexOfAny(s, any, 0);
1491: }
1492:
1493: private static int indexOfAny(String s, String any, int start) {
1494: try {
1495: int len = s.length();
1496:• for (int i = start; i < len; i++) {
1497:• if (any.indexOf(s.charAt(i)) >= 0)
1498: return i;
1499: }
1500: return -1;
1501: } catch (StringIndexOutOfBoundsException e) {
1502: return -1;
1503: }
1504: }
1505:
1506: /*
1507: public static void main(String argv[]) throws Exception {
1508:         for (int i = 0; i < argv.length; i++) {
1509:          InternetAddress[] a = InternetAddress.parse(argv[i]);
1510:          for (int j = 0; j < a.length; j++) {
1511:                 System.out.println("arg " + i + " address " + j + ": " + a[j]);
1512:                 System.out.println("\tAddress: " + a[j].getAddress() +
1513:                                  "\tPersonal: " + a[j].getPersonal());
1514:          }
1515:          if (a.length > 1) {
1516:                 System.out.println("address 0 hash code: " + a[0].hashCode());
1517:                 System.out.println("address 1 hash code: " + a[1].hashCode());
1518:                 if (a[0].hashCode() == a[1].hashCode())
1519:                  System.out.println("success, hashcodes equal");
1520:                 else
1521:                  System.out.println("fail, hashcodes not equal");
1522:                 if (a[0].equals(a[1]))
1523:                  System.out.println("success, addresses equal");
1524:                 else
1525:                  System.out.println("fail, addresses not equal");
1526:                 if (a[1].equals(a[0]))
1527:                  System.out.println("success, addresses equal");
1528:                 else
1529:                  System.out.println("fail, addresses not equal");
1530:          }
1531:         }
1532: }
1533: */
1534: }