Skip to content

Package: InternetAddress

InternetAddress

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