commits@javamail.java.net

[javamail~mercurial:301] Handle space in user.name in InternetAddress.getLocalAddress.

From: <shannon_at_kenai.com>
Date: Tue, 12 Oct 2010 03:08:07 +0000

Project: javamail
Repository: mercurial
Revision: 301
Author: shannon
Date: 2010-10-05 20:49:06 UTC
Link:

Log Message:
------------
Fix javadoc error.
More updates from Jason:

MailHandler allow content type override for message body.
MailHandler map getEncoding to charset.
MailHandler perform verify outside of lock.
MailHandler make verify determine cause of InternetAddress.getLocalAddress failure.
MailHandler make local verify perform a lookup of localhost.
MailHandler make verify call InternetAddress.validate
MailHandlerTest add test for encoding.
MailHandlerTest add test for properties verify.
MailHandlerTest add test for body content type override.
Fix test failure.
MailHandlerTest find unused port on localhost.

(from Jason)
Handle space in user.name in InternetAddress.getLocalAddress.
Handle IP address literals in InternetAddress.getLocalAddress.
Chain exceptions in InternetAddress.getLocalAddress to MimeMessage.setFrom().


Revisions:
----------
297
298
299
300
301


Modified Paths:
---------------
mail/src/main/java/javax/mail/internet/MimeMessage.java
mail/src/main/java/com/sun/mail/util/logging/MailHandler.java
mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java
mail/src/main/java/javax/mail/internet/InternetAddress.java


Added Paths:
------------
mail/src/test/java/javax/mail/internet/GetLocalAddressTest.java


Diffs:
------
diff -r cf5fbe756fee -r 3d712ebdf910 mail/src/main/java/javax/mail/internet/MimeMessage.java
--- a/mail/src/main/java/javax/mail/internet/MimeMessage.java Thu Sep 23 14:35:00 2010 -0700
+++ b/mail/src/main/java/javax/mail/internet/MimeMessage.java Thu Sep 30 10:41:06 2010 -0700
@@ -2053,7 +2053,7 @@
      *
      * This method sets the <code>modified</code> flag to true, the
      * <code>save</code> flag to true, and then calls the
- * <code>updateHeaders<code> method.
+ * <code>updateHeaders</code> method.
      *
      * @exception IllegalWriteException if the underlying
      * implementation does not support modification


diff -r 3d712ebdf910 -r d84d23c86449 mail/src/main/java/com/sun/mail/util/logging/MailHandler.java
--- a/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java Thu Sep 30 10:41:06 2010 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java Fri Oct 01 16:49:24 2010 -0700
@@ -43,6 +43,8 @@
 
 import java.io.*;
 import java.lang.reflect.*;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.net.URLConnection;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -67,6 +69,7 @@
  * Properties props = new Properties();
  * props.put("mail.smtp.host", "my-mail-server");
  * props.put("mail.to", "me_at_example.com");
+ * props.put("verify", "local");
  * MailHandler h = new MailHandler(props);
  * h.setLevel(Level.WARNING);
  * </pre></tt>
@@ -190,13 +193,15 @@
  * (defaults to empty string)
  *
  * <li>com.sun.mail.util.logging.MailHandler.verify <a name="verify">used</a> to
- * verify all of the <tt>Handler</tt> properties prior to a push. If set to a value of
- * <tt>local</tt> the <tt>Handler</tt> will only verify settings of the local
- * machine. If set to a value of <tt>remote</tt>, the <tt>Handler</tt> will
- * verify all local settings and try to establish a connection with the email
- * server. If the value is not set, equal to an empty string, or equal to the
- * literal <tt>null</tt> then minimal or no settings are verified prior to a
- * push. (defaults to <tt>null</tt>, no verify).
+ * verify all of the <tt>Handler</tt> properties prior to a push. If set to a
+ * value of <tt>local</tt> the <tt>Handler</tt> will only verify settings of the
+ * local machine. If set to a value of <tt>remote</tt>, the <tt>Handler</tt>
+ * will verify all local settings and try to establish a connection with the
+ * email server. If the value is not set, equal to an empty string, or equal to
+ * the literal <tt>null</tt> then minimal or no settings are verified prior to a
+ * push. If this <tt>Handler</tt> is only implicitly closed by the
+ * <tt>LogManager</tt>, then verification should be turned on.
+ * (defaults to <tt>null</tt>, no verify).
  *
  * <p>
  * <b>Sorting:</b>
@@ -214,7 +219,12 @@
  * <tt>getFormatter()</tt>. Only records that pass the filter returned by
  * <tt>getFilter()</tt> will be included in the message body. The subject
  * <tt>Formatter</tt> will see all <tt>LogRecord</tt> objects that were
- * published regardless of the current <tt>Filter</tt>.
+ * published regardless of the current <tt>Filter</tt>. The MIME type of the
+ * message body can be {_at_linkplain FileTypeMap#setDefaultFileTypeMap overridden}
+ * by adding a MIME {_at_linkplain MimetypesFileTypeMap entry} using the simple
+ * class name of the body formatter as the file extension. The MIME type of the
+ * attachments can be overridden by changing the attachment file name extension
+ * or by editing the default MIME entry for a specific file name extension.
  *
  * <p>
  * <b>Attachments:</b>
@@ -364,6 +374,11 @@
      * all of the format calls, plus one getTail call.
      */
     private Formatter[] attachmentNames;
+ /**
+ * Used to override the content type for the body and set the content type
+ * for each attachment.
+ */
+ private FileTypeMap contentTypes;
 
     /**
      * Creates a <tt>MailHandler</tt> that is configured by the
@@ -671,14 +686,18 @@
      * caller does not have <tt>LoggingPermission("control")</tt>.
      * @throws IllegalStateException if called from inside a push.
      */
- public final synchronized void setAuthenticator(final Authenticator auth) {
+ public final void setAuthenticator(final Authenticator auth) {
         checkAccess();
 
- if (isWriting) {
- throw new IllegalStateException();
+ Session settings;
+ synchronized(this) {
+ if (isWriting) {
+ throw new IllegalStateException();
+ }
+ this.auth = auth;
+ settings = this.fixUpSession();
         }
- this.auth = auth;
- this.fixUpSession();
+ verifySettings(settings);
     }
 
     /**
@@ -699,13 +718,15 @@
     private void setMailProperties0(Properties props) {
         checkAccess();
         props = (Properties) props.clone();
+ Session settings;
         synchronized (this) {
             if (isWriting) {
                 throw new IllegalStateException();
             }
             this.mailProps = props;
- this.fixUpSession();
+ settings = this.fixUpSession();
         }
+ verifySettings(settings);
     }
 
     /**
@@ -1003,11 +1024,20 @@
      * @param head any head string.
      * @return return the mime type or null for text/plain.
      */
- final String contentTypeOf(final String head) {
+ final String contentTypeOf(String head) {
         if (head != null && head.length() > 0) {
+ final int MAX_CHARS = 15;
+ if (head.length() > MAX_CHARS) {
+ head = head.substring(0, MAX_CHARS);
+ }
             try {
- final ByteArrayInputStream in =
- new ByteArrayInputStream(head.getBytes("US-ASCII"));
+ final ByteArrayInputStream in;
+ final String encoding = getEncoding();
+ if (encoding == null) {
+ in = new ByteArrayInputStream(head.getBytes());
+ } else {
+ in = new ByteArrayInputStream(head.getBytes(encoding));
+ }
                 assert in.markSupported() : in.getClass().getName();
                 return URLConnection.guessContentTypeFromStream(in);
             } catch (final IOException IOE) {
@@ -1018,27 +1048,93 @@
     }
 
     /**
+ * Determines the mimeType from the given file name.
+ * Used to override the body content type and used for all attachments.
+ * @param name the file name or class name.
+ * @return the mime type or null for text/plain.
+ */
+ private String getContentType(final String name) {
+ assert Thread.holdsLock(this);
+ final String type = contentTypes.getContentType(name);
+ if ("application/octet-stream".equalsIgnoreCase(type)) {
+ return null; //formatters return strings, default to text/plain.
+ }
+ return type;
+ }
+
+ /**
      * Set the content for a part.
      * @param part the part to assign.
      * @param buf the formatted data.
      * @param type the mime type.
      * @throws MessagingException if there is a problem.
      */
- private void setContent(Part part, StringBuffer buf, String type) throws MessagingException {
+ private void setContent(MimeBodyPart part, StringBuffer buf, String type) throws MessagingException {
+ final String encoding = getEncoding();
         if (type != null && !"text/plain".equals(type)) {
+ if (encoding == null) {
+ type = contentWithDefault(type);
+ } else {
+ type = contentWithEncoding(type, encoding);
+ }
+
             try {
                 DataSource source = new ByteArrayDataSource(buf.toString(), type);
                 part.setDataHandler(new DataHandler(source));
             } catch (final IOException IOE) {
                 reportError(IOE.getMessage(), IOE, ErrorManager.FORMAT_FAILURE);
- part.setText(buf.toString());
+ part.setText(buf.toString(), encoding);
             }
         } else {
- part.setText(buf.toString());
+ part.setText(buf.toString(), encoding);
         }
     }
 
     /**
+ * Only call if encoding is not null.
+ * Replaces the charset parameter with the current encoding.
+ * @param type the content type.
+ * @param encoding the encoding.
+ * @return the type with a specified encoding.
+ */
+ private String contentWithEncoding(String type, String encoding) {
+ try {
+ final ContentType ct = new ContentType(type);
+ ct.setParameter("charset", encoding);
+ encoding = ct.toString();
+ if (encoding != null) {
+ type = encoding;
+ }
+ } catch (final MessagingException ME) {
+ reportError(type, ME, ErrorManager.FORMAT_FAILURE);
+ }
+ return type;
+ }
+
+ /**
+ * Removes the encoding parameter from the content type.
+ * @param type the content type.
+ * @return the content type without encoding.
+ */
+ private String contentWithDefault(String type) {
+ try {
+ final ContentType ct = new ContentType(type);
+ if (ct.getParameter("charset") != null) {
+ final ParameterList list = ct.getParameterList();
+ list.remove("charset");
+ ct.setParameterList(list);
+ final String newType = ct.toString();
+ if (newType != null) {
+ type = newType;
+ }
+ }
+ } catch (final MessagingException ME) {
+ reportError(type, ME, ErrorManager.FORMAT_FAILURE);
+ }
+ return type;
+ }
+
+ /**
      * Sets the capacity for this handler. This method is kept private
      * because we would have to define a public policy for when the size is
      * greater than the capacity.
@@ -1177,6 +1273,7 @@
         final LogManager manager = LogManagerProperties.manager;
         final String p = getClass().getName();
         this.mailProps = new Properties();
+ this.contentTypes = FileTypeMap.getDefaultFileTypeMap();
 
         //Assign any custom error manager first so it can detect all failures.
         ErrorManager em = (ErrorManager) initObject(p.concat(".errorManager"), ErrorManager.class);
@@ -1189,7 +1286,7 @@
         initCapacity(manager, p);
 
         this.auth = (Authenticator) initObject(p.concat(".authenticator"), Authenticator.class);
- this.fixUpSession();
+ final Session settings = this.fixUpSession();
 
         initEncoding(manager, p);
         initFormatter(p);
@@ -1215,6 +1312,7 @@
             reportError("attachment names.",
                     attachmentMismatch("length mismatch"), ErrorManager.OPEN_FAILURE);
         }
+ verifySettings(settings);
     }
 
     private /*<T> T*/ Object objectFromNew(final String name,
@@ -1549,7 +1647,7 @@
              * call. Therefore, a null part at an index means that the head is
              * required.
              */
- BodyPart[] parts = new BodyPart[attachmentFormatters.length];
+ MimeBodyPart[] parts = new MimeBodyPart[attachmentFormatters.length];
 
             /**
              * The buffers are lazily created when the part requires a getHead.
@@ -1597,18 +1695,18 @@
             }
             this.size = 0;
 
- for (int i = parts.length - 1; i >= 0; i--) {
+ for (int i = parts.length - 1; i >= 0; --i) {
                 if (parts[i] != null) {
                     appendFileName(parts[i], tail(attachmentNames[i], "err"));
                     buffers[i].append(tail(attachmentFormatters[i], ""));
 
- final String content = buffers[i].toString();
- if (content.length() > 0) {
+ if (buffers[i].length() > 0) {
                         String name = parts[i].getFileName();
                         if (name == null || name.length() == 0) {
- parts[i].setFileName(toString(attachmentFormatters[i]));
+ name = toString(attachmentFormatters[i]);
+ parts[i].setFileName(name);
                         }
- parts[i].setText(content);
+ setContent(parts[i], buffers[i], getContentType(name));
                     } else {
                         parts[i] = null;
                     }
@@ -1625,8 +1723,9 @@
             appendSubject(msg, tail(subjectFormatter, ""));
 
             MimeMultipart multipart = new MimeMultipart();
- BodyPart body = createBodyPart();
- setContent(body, buf, contentType);
+ MimeBodyPart body = createBodyPart();
+ String altType = getContentType(bodyFormat.getClass().getName());
+ setContent(body, buf, altType == null ? contentType : altType);
             buf = null;
             multipart.addBodyPart(body);
 
@@ -1700,6 +1799,7 @@
             return;
         }
 
+ //Perform all of the copy actions first.
         final MimeMessage abort = new MimeMessage(session);
         envelopeFor(new MessageContext(abort), true);
 
@@ -1711,26 +1811,6 @@
         }
 
         try {
- Address[] from = abort.getFrom();
- Address sender = abort.getSender();
-
- if (from == null || from.length == 0) {
- reportError(msg,
- new MessagingException("No from address."),
- ErrorManager.OPEN_FAILURE);
- } else {
- if (abort.getHeader("From", ",") != null) {
- for (int i = 0; i < from.length; ++i) {
- if (from[i].equals(sender)) {
- reportError(msg, new MessagingException(
- "Sender address equals from address."),
- ErrorManager.OPEN_FAILURE);
- break;
- }
- }
- }
- }
-
             Address[] all = abort.getAllRecipients();
             Transport t;
             try {
@@ -1742,16 +1822,16 @@
                     reportError(msg, me, ErrorManager.OPEN_FAILURE);
                     throw me;
                 }
- } catch (MessagingException smtp) {
+ } catch (final MessagingException smtp) {
                 try {
                     t = session.getTransport("smtp");
- } catch (MessagingException tryDefault) {
+ } catch (final MessagingException tryDefault) {
                     try {
                         t = session.getTransport();
- } catch (MessagingException fail) {
+ } catch (final MessagingException fail) {
                         smtp.setNextException(tryDefault);
                         tryDefault.setNextException(fail);
- throw fail;
+ throw smtp;
                     }
                 }
             }
@@ -1771,16 +1851,70 @@
                 } finally {
                     t.close();
                 }
- } else {
+ } else { //force a property copy.
                 final String protocol = t.getURLName().getProtocol();
                 session.getProperty("mail.host");
                 session.getProperty("mail.user");
                 session.getProperty("mail." + protocol + ".host");
+ session.getProperty("mail." + protocol + ".port");
                 session.getProperty("mail." + protocol + ".user");
             }
- } catch (MessagingException ME) {
+
+ Address[] from = abort.getFrom();
+ Address sender = abort.getSender();
+
+ if (from == null || from.length == 0) {
+ String noFromAddress = "No from address.";
+ MessagingException me;
+ try { //no from address, check security policy and host name.
+ System.getProperty("user.name");
+ if (InetAddress.getLocalHost().getHostName().length() == 0) {
+ me = new MessagingException(noFromAddress,
+ new UnknownHostException());
+ } else {
+ me = new MessagingException(noFromAddress);
+ }
+ } catch (final SecurityException SE) {
+ me = new MessagingException(noFromAddress, SE);
+ } catch (final IOException IOE) {
+ me = new MessagingException(noFromAddress, IOE);
+ }
+ reportError(msg, me, ErrorManager.OPEN_FAILURE);
+ } else {
+ if (abort.getHeader("From", ",") != null) {
+ for (int i = 0; i < from.length; ++i) {
+ if (from[i].equals(sender)) {
+ reportError(msg, new MessagingException(
+ "Sender address equals from address."),
+ ErrorManager.OPEN_FAILURE);
+ break;
+ }
+ }
+ }
+ }
+
+ if (all != null) {
+ for (int i = 0; i < all.length; ++i) {
+ Address a = all[i];
+ if (a instanceof InternetAddress) {
+ ((InternetAddress) a).validate();
+ }
+ }
+ }
+
+ //Check the host name after the address checks.
+ if ("local".equals(verify)) {
+ try {
+ if (InetAddress.getLocalHost().getHostName().length() == 0) {
+ throw new UnknownHostException();
+ }
+ } catch (final IOException IOE) {
+ reportError(msg, IOE, ErrorManager.OPEN_FAILURE);
+ }
+ }
+ } catch (final MessagingException ME) {
             reportError(msg, ME, ErrorManager.OPEN_FAILURE);
- } catch (RuntimeException RE) {
+ } catch (final RuntimeException RE) {
             reportError(msg, RE, ErrorManager.OPEN_FAILURE);
         }
     }
@@ -1788,13 +1922,14 @@
     /**
      * Used to update the cached session object based on changes in
      * mail properties or authenticator.
+ * @return the current session.
      */
- private void fixUpSession() {
+ private Session fixUpSession() {
         assert Thread.holdsLock(this);
         String p = getClass().getName();
         LogManagerProperties proxy = new LogManagerProperties(mailProps, p);
         session = Session.getInstance(proxy, auth);
- verifySettings(session);
+ return session;
     }
 
     /**
@@ -1823,14 +1958,14 @@
         }
     }
 
- private BodyPart createBodyPart() throws MessagingException {
+ private MimeBodyPart createBodyPart() throws MessagingException {
         final MimeBodyPart part = new MimeBodyPart();
         part.setDisposition(Part.INLINE);
         part.setDescription(descriptionFrom(getFormatter(), getFilter()));
         return part;
     }
 
- private BodyPart createBodyPart(final int index) throws MessagingException {
+ private MimeBodyPart createBodyPart(final int index) throws MessagingException {
         assert Thread.holdsLock(this);
         final MimeBodyPart part = new MimeBodyPart();
         part.setDisposition(Part.ATTACHMENT);
@@ -1914,7 +2049,9 @@
     private void appendSubject0(final Message msg, final String chunk) {
         try {
             final String old = msg.getSubject();
- msg.setSubject(old != null ? old.concat(chunk) : chunk);
+ assert msg instanceof MimeMessage;
+ ((MimeMessage) msg).setSubject(old != null ?
+ old.concat(chunk) : chunk, getEncoding());
         } catch (final MessagingException ME) {
             reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE);
         }
@@ -2080,7 +2217,7 @@
             final int nbytes = Math.max(msg.getSize() + 1024, 1024);
             final ByteArrayOutputStream out = new ByteArrayOutputStream(nbytes);
             msg.writeTo(out);
- return out.toString("US-ASCII");
+ return out.toString("US-ASCII"); //raw message is always ASCII.
         } else { //Must match this.reportError behavior, see push method.
             return null; //null is the safe choice.
         }

diff -r 3d712ebdf910 -r d84d23c86449 mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java Thu Sep 30 10:41:06 2010 -0700
+++ b/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java Fri Oct 01 16:49:24 2010 -0700
@@ -49,6 +49,7 @@
 import java.util.Properties;
 import java.util.Random;
 import java.util.logging.*;
+import javax.activation.*;
 import javax.mail.*;
 import javax.mail.internet.*;
 import org.junit.AfterClass;
@@ -63,7 +64,6 @@
 public class MailHandlerTest {
 
     private static final String LOG_CFG_KEY = "java.util.logging.config.file";
-
     /**
      * Used to prevent G.C. of loggers.
      */
@@ -580,6 +580,57 @@
     }
 
     @Test
+ public void testEncoding() throws Exception {
+ final String enc = "iso-8859-1";
+ LogManager manager = LogManager.getLogManager();
+ final MailHandler instance = new MailHandler();
+ MessageErrorManager em = new MessageErrorManager(instance) {
+
+ protected void error(MimeMessage msg, Throwable t, int code) {
+ try {
+ MimeMultipart multi = (MimeMultipart) msg.getContent();
+ BodyPart body = multi.getBodyPart(0);
+ assertEquals(Part.INLINE, body.getDisposition());
+ ContentType ct = new ContentType(body.getContentType());
+ assertEquals(enc, ct.getParameter("charset"));
+
+ BodyPart attach = multi.getBodyPart(1);
+ ct = new ContentType(attach.getContentType());
+ assertEquals(enc, ct.getParameter("charset"));
+ } catch (Throwable E) {
+ E.printStackTrace();
+ fail(E.toString());
+ }
+ }
+ };
+
+ instance.setErrorManager(em);
+ Properties props = new Properties();
+ props.put("mail.smtp.host", "bad-host-name");
+ props.put("mail.host", "bad-host-name");
+ props.put("mail.to", "localhost_at_localdomain");
+ instance.setMailProperties(props);
+ instance.setAttachmentFormatters(new Formatter[]{new XMLFormatter()});
+ instance.setAttachmentNames(new String[]{"all.xml"});
+ String p = instance.getClass().getName();
+
+ assertEquals(manager.getProperty(p.concat(".encoding")), instance.getEncoding());
+ try {
+ instance.setEncoding("unsupported encoding exception");
+ fail("Missing encoding check.");
+ } catch (UnsupportedEncodingException expect) {
+ }
+
+ assertTrue(em.exceptions.isEmpty());
+
+ instance.setEncoding(enc);
+ instance.setSubject("ORA-17043=Ung\u00FCltige maximale Stream-Gr\u00F6\u00DFe");
+ LogRecord record = new LogRecord(Level.SEVERE, "Zeit\u00FCberschreitung bei Anweisung");
+ instance.publish(record);
+ instance.close();
+ }
+
+ @Test
     public void testPushInsidePush() {
         Level[] lvls = getAllLevels();
 
@@ -1163,7 +1214,7 @@
 
         MailHandler instance = new MailHandler(10);
         instance.setLevel(Level.ALL);
-
+
         assertEquals(InternalErrorManager.class, instance.getErrorManager().getClass());
 
         final String CLASS_NAME = MailHandlerTest.class.getName();
@@ -1256,7 +1307,6 @@
         head = instance.contentTypeOf(new XMLFormatter().getHead(instance));
         assertEquals("application/xml", head);
 
-
         instance.setEncoding(null);
         head = instance.contentTypeOf(new SimpleFormatter().getHead(instance));
         assertNull(head);
@@ -1277,6 +1327,11 @@
         head = instance.contentTypeOf(new HeadFormatter("<HTML><BODY>").getHead(instance));
         assertEquals("text/html", head);
 
+ instance.setEncoding("US-ASCII");
+ head = instance.contentTypeOf(new HeadFormatter("<HTML><HEAD></HEAD>"
+ + "<BODY></BODY></HTML>").getHead(instance));
+ assertEquals("text/html", head);
+
         instance.setEncoding(null);
         head = instance.contentTypeOf(new HeadFormatter("Head").getHead(instance));
         if (head != null) {//null is assumed to be plain text.
@@ -1288,6 +1343,12 @@
         if (head != null) { //null is assumed to be plain text.
             assertEquals("text/plain", head);
         }
+
+ instance.setEncoding("US-ASCII");
+ head = instance.contentTypeOf(new HeadFormatter("Head.......Neck.......Body").getHead(instance));
+ if (head != null) { //null is assumed to be plain text.
+ assertEquals("text/plain", head);
+ }
         instance.close();
 
         for (int i = 0; i < em.exceptions.size(); i++) {
@@ -1296,6 +1357,58 @@
     }
 
     @Test
+ public void testContentTypeOverride() throws Exception {
+ String expected = "application/xml; charset=us-ascii";
+ String type = getInlineContentType();
+ assertEquals(expected, type);
+
+ MimetypesFileTypeMap m = new MimetypesFileTypeMap();
+ m.addMimeTypes("text/plain txt TXT XMLFormatter");
+ final FileTypeMap old = FileTypeMap.getDefaultFileTypeMap();
+ FileTypeMap.setDefaultFileTypeMap(m);
+ try {
+ type = getInlineContentType();
+ assertEquals("text/plain; charset=us-ascii", type);
+ } finally {
+ FileTypeMap.setDefaultFileTypeMap(old);
+ }
+
+ type = getInlineContentType();
+ assertEquals(expected, type);
+ }
+
+ private String getInlineContentType() throws Exception {
+ final String[] value = new String[1];
+ MailHandler instance = new MailHandler();
+ instance.setEncoding("us-ascii");
+ MessageErrorManager em = new MessageErrorManager(instance) {
+
+ protected void error(MimeMessage msg, Throwable t, int code) {
+ try {
+ MimeMultipart multi = (MimeMultipart) msg.getContent();
+ BodyPart body = multi.getBodyPart(0);
+ assertEquals(Part.INLINE, body.getDisposition());
+ value[0] = body.getContentType();
+ } catch (Throwable E) {
+ E.printStackTrace();
+ fail(E.toString());
+ }
+ }
+ };
+ instance.setErrorManager(em);
+ Properties props = new Properties();
+ props.put("mail.smtp.host", "bad-host-name");
+ props.put("mail.host", "bad-host-name");
+ props.put("mail.to", "localhost_at_localdomain");
+ instance.setMailProperties(props);
+ instance.setFormatter(new XMLFormatter());
+ instance.publish(new LogRecord(Level.SEVERE, "test"));
+ instance.close();
+
+ return value[0];
+ }
+
+ @Test
     public void testComparator() {
         MailHandler instance = new MailHandler();
         InternalErrorManager em = new InternalErrorManager();
@@ -1324,7 +1437,8 @@
     @Test
     public void testCapacity() {
         try {
- new MailHandler(-1);
+ MailHandler h = new MailHandler(-1);
+ h.getCapacity();
             fail("Negative capacity was allowed.");
         } catch (IllegalArgumentException pass) {
         } catch (RuntimeException RE) {
@@ -1332,7 +1446,8 @@
         }
 
         try {
- new MailHandler(0);
+ MailHandler h = new MailHandler(0);
+ h.getCapacity();
             fail("Zero capacity was allowed.");
         } catch (IllegalArgumentException pass) {
         } catch (RuntimeException RE) {
@@ -1340,12 +1455,12 @@
         }
 
         try {
- new MailHandler(1);
+ MailHandler h = new MailHandler(1);
+ h.getCapacity();
         } catch (RuntimeException RE) {
             fail(RE.toString());
         }
 
-
         final int expResult = 20;
         MailHandler instance = new MailHandler(20);
         InternalErrorManager em = new InternalErrorManager();
@@ -2414,6 +2529,131 @@
         }
     }
 
+ @Test
+ public void testVerifyLogManager() throws Exception {
+ LogManager manager = LogManager.getLogManager();
+ try {
+ manager.reset();
+
+ final String p = MailHandler.class.getName();
+ Properties props = new Properties();
+ props.put(p.concat(".mail.host"), "bad-host-name");
+ props.put(p.concat(".mail.smtp.host"), "bad-host-name");
+ props.put(p.concat(".mail.smtp.port"), "80"); //bad port.
+ props.put(p.concat(".mail.to"), "badAddress");
+ props.put(p.concat(".mail.cc"), "badAddress");
+ props.put(p.concat(".mail.subject"), p.concat(" test"));
+ props.put(p.concat(".mail.from"), "badAddress");
+ props.put(p.concat(".errorManager"), InternalErrorManager.class.getName());
+ props.put(p.concat(".mail.smtp.connectiontimeout"), "1");
+ props.put(p.concat(".mail.smtp.timeout"), "1");
+ props.put(p.concat(".verify"), "local");
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ props.store(out, "No comment");
+
+ manager.readConfiguration(new ByteArrayInputStream(out.toByteArray()));
+ out = null;
+
+ MailHandler instance = new MailHandler();
+ InternalErrorManager em =
+ (InternalErrorManager) instance.getErrorManager();
+
+ assertFalse(em.exceptions.isEmpty());
+
+ for (int i = 0; i < em.exceptions.size(); i++) {
+ final Throwable t = em.exceptions.get(i);
+ if (t instanceof AddressException == false) {
+ t.printStackTrace();
+ fail(t.toString());
+ }
+ }
+
+ instance.close();
+
+ props.put(p.concat(".verify"), "remote");
+ out = new ByteArrayOutputStream();
+ props.store(out, "No comment");
+ manager.readConfiguration(new ByteArrayInputStream(out.toByteArray()));
+ out = null;
+
+
+ instance = new MailHandler();
+ em = (InternalErrorManager) instance.getErrorManager();
+
+ assertFalse(em.exceptions.isEmpty());
+
+ for (int i = 0; i < em.exceptions.size(); i++) {
+ final Throwable t = em.exceptions.get(i);
+ if (t instanceof AddressException) {
+ continue;
+ } else if (t.getMessage().indexOf("bad-host-name") > -1) {
+ continue;
+ } else {
+ t.printStackTrace();
+ fail(t.toString());
+ }
+ }
+ } finally {
+ manager.reset();
+ }
+ }
+
+ @Test
+ public void testVerifyProperties() throws Exception {
+ Properties props = new Properties();
+ props.put("mail.host", "bad-host-name");
+ props.put("mail.smtp.host", "bad-host-name");
+ props.put("mail.smtp.port", "80"); //bad port.
+ props.put("mail.to", "badAddress");
+ props.put("mail.cc", "badAddress");
+ props.put("mail.subject", "test");
+ props.put("mail.from", "badAddress");
+ props.put("mail.smtp.connectiontimeout", "1");
+ props.put("mail.smtp.timeout", "1");
+ props.put("verify", "local");
+
+ InternalErrorManager em = new InternalErrorManager();
+ MailHandler instance = new MailHandler();
+ try {
+ instance.setErrorManager(em);
+ instance.setMailProperties(props);
+
+ for (int i = 0; i < em.exceptions.size(); i++) {
+ final Throwable t = em.exceptions.get(i);
+ if (t instanceof AddressException == false) {
+ t.printStackTrace();
+ fail(t.toString());
+ }
+ }
+ } finally {
+ instance.close();
+ }
+
+ props.put("verify", "remote");
+ instance = new MailHandler();
+ try {
+ em = new InternalErrorManager();
+ instance.setErrorManager(em);
+ instance.setMailProperties(props);
+
+ assertFalse(em.exceptions.isEmpty());
+
+ for (int i = 0; i < em.exceptions.size(); i++) {
+ final Throwable t = em.exceptions.get(i);
+ if (t instanceof AddressException) {
+ continue;
+ } else if (t.getMessage().indexOf("bad-host-name") > -1) {
+ continue;
+ } else {
+ t.printStackTrace();
+ fail(t.toString());
+ }
+ }
+ } finally {
+ instance.close();
+ }
+ }
+
     /**
      * Test must run last.
      */
@@ -2469,7 +2709,7 @@
             } finally {
                 boolean v;
                 v = cfg.delete();
- assertTrue(v);
+ assertTrue(v || !cfg.exists());
 
                 System.clearProperty(LOG_CFG_KEY);
                 LogManager.getLogManager().readConfiguration();


diff -r d84d23c86449 -r 383e37e639ce mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java Fri Oct 01 16:49:24 2010 -0700
+++ b/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java Mon Oct 04 10:27:33 2010 -0700
@@ -38,7 +38,6 @@
  * only if the new code is made subject to such option by the copyright
  * holder.
  */
-
 package com.sun.mail.util.logging;
 
 import java.lang.reflect.*;
@@ -1230,7 +1229,14 @@
 
     private static boolean isConnectOrTimeout(Throwable t) {
         if (t instanceof MessagingException) {
- return isConnectOrTimeout(((MessagingException) t).getCause());
+ Throwable cause = ((MessagingException) t).getCause();
+ if (cause != null) {
+ return isConnectOrTimeout(cause);
+ } else {
+ String msg = t.getMessage();
+ return msg != null && (msg.indexOf("connect") > -1 ||
+ msg.indexOf("80") > -1);
+ }
         } else {
             return t instanceof java.net.ConnectException
                     || t instanceof java.net.SocketTimeoutException;


diff -r 383e37e639ce -r 403bd9cb4b56 mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java Mon Oct 04 10:27:33 2010 -0700
+++ b/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java Tue Oct 05 13:45:39 2010 -0700
@@ -41,6 +41,10 @@
 package com.sun.mail.util.logging;
 
 import java.lang.reflect.*;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.net.UnknownHostException;
 import java.io.*;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -51,7 +55,9 @@
 import javax.activation.*;
 import javax.mail.*;
 import javax.mail.internet.*;
+import org.junit.After;
 import org.junit.AfterClass;
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import static org.junit.Assert.*;
@@ -62,20 +68,40 @@
  */
 public class MailHandlerTest {
 
+ /**
+ * See LogManager.
+ */
     private static final String LOG_CFG_KEY = "java.util.logging.config.file";
     /**
+ * Stores the value of a port that is not used on the local machine.
+ */
+ private static volatile int OPEN_PORT = Integer.MIN_VALUE;
+ /**
      * Used to prevent G.C. of loggers.
      */
- private Object hardRef;
+ private volatile Object hardRef;
 
     @BeforeClass
     public static void setUpClass() throws Exception {
         checkJVMOptions();
+ OPEN_PORT = findOpenPort();
     }
 
     @AfterClass
     public static void tearDownClass() throws Exception {
         checkJVMOptions();
+ assertTrue(checkUnusedPort(OPEN_PORT));
+ OPEN_PORT = Integer.MIN_VALUE;
+ }
+
+ @Before
+ public void setUp() {
+ assertNull(hardRef);
+ }
+
+ @After
+ public void tearDown() {
+ hardRef = null;
     }
 
     private static void checkJVMOptions() {
@@ -87,6 +113,13 @@
         assertTrue(LOW_CAPACITY < NUM_RUNS);
         //Try to hold MAX_CAPACITY array with log records.
         assertTrue((60L * 1024L * 1024L) <= Runtime.getRuntime().maxMemory());
+ try {
+ if (InetAddress.getLocalHost().getHostName().length() == 0) {
+ throw new UnknownHostException();
+ }
+ } catch (UnknownHostException UHE) {
+ throw new AssertionError(UHE);
+ }
     }
 
     @Test
@@ -1184,7 +1217,7 @@
         Properties props = new Properties();
         props.put(p.concat(".mail.host"), "localhost");
         props.put(p.concat(".mail.smtp.host"), "localhost");
- props.put(p.concat(".mail.smtp.port"), "80"); //bad port.
+ props.put(p.concat(".mail.smtp.port"), Integer.toString(OPEN_PORT));
         props.put(p.concat(".mail.to"), "localhost_at_localdomain");
         props.put(p.concat(".mail.cc"), "localhost_at_localdomain");
         props.put(p.concat(".mail.subject"), p.concat(" test"));
@@ -1227,22 +1260,6 @@
         return instance;
     }
 
- private static boolean isConnectOrTimeout(Throwable t) {
- if (t instanceof MessagingException) {
- Throwable cause = ((MessagingException) t).getCause();
- if (cause != null) {
- return isConnectOrTimeout(cause);
- } else {
- String msg = t.getMessage();
- return msg != null && (msg.indexOf("connect") > -1 ||
- msg.indexOf("80") > -1);
- }
- } else {
- return t instanceof java.net.ConnectException
- || t instanceof java.net.SocketTimeoutException;
- }
- }
-
     @Test
     public void testPushLevel() {
         MailHandler instance = new MailHandler();
@@ -2475,11 +2492,15 @@
                 fail(fail.toString());
             }
 
- //check for internal security exceptions
+
+ //check for internal exceptions caused by security manager.
+ next:
             for (Exception e : em.exceptions) {
                 for (Throwable t = e; t != null; t = t.getCause()) {
                     if (t instanceof SecurityException) {
- throw (SecurityException) t; //fail
+ continue next; //expected
+ } else if (t instanceof RuntimeException) {
+ throw (RuntimeException) t; //fail
                     }
                 }
             }
@@ -2545,7 +2566,7 @@
             Properties props = new Properties();
             props.put(p.concat(".mail.host"), "bad-host-name");
             props.put(p.concat(".mail.smtp.host"), "bad-host-name");
- props.put(p.concat(".mail.smtp.port"), "80"); //bad port.
+ props.put(p.concat(".mail.smtp.port"), Integer.toString(OPEN_PORT)); //bad port.
             props.put(p.concat(".mail.to"), "badAddress");
             props.put(p.concat(".mail.cc"), "badAddress");
             props.put(p.concat(".mail.subject"), p.concat(" test"));
@@ -2609,7 +2630,7 @@
         Properties props = new Properties();
         props.put("mail.host", "bad-host-name");
         props.put("mail.smtp.host", "bad-host-name");
- props.put("mail.smtp.port", "80"); //bad port.
+ props.put("mail.smtp.port", Integer.toString(OPEN_PORT));
         props.put("mail.to", "badAddress");
         props.put("mail.cc", "badAddress");
         props.put("mail.subject", "test");
@@ -2617,7 +2638,7 @@
         props.put("mail.smtp.connectiontimeout", "1");
         props.put("mail.smtp.timeout", "1");
         props.put("verify", "local");
-
+
         InternalErrorManager em = new InternalErrorManager();
         MailHandler instance = new MailHandler();
         try {
@@ -2977,6 +2998,65 @@
         return (Level[]) a.toArray(new Level[a.size()]);
     }
 
+ private static boolean isConnectOrTimeout(Throwable t) {
+ if (t instanceof MessagingException) {
+ return isConnectOrTimeout(t.getCause());
+ } else {
+ return t instanceof java.net.ConnectException
+ || t instanceof java.net.SocketTimeoutException;
+ }
+ }
+
+ /**
+ * http://www.iana.org/assignments/port-numbers
+ * @return a open dynamic port.
+ */
+ private static int findOpenPort() {
+ final int MAX_PORT = 65535;
+ for (int i = 49152; i <= MAX_PORT; ++i) {
+ if (checkUnusedPort(i)) {
+ return i;
+ }
+ }
+
+ try {
+ close(new ServerSocket(MAX_PORT));
+ close(new Socket("localhost", MAX_PORT));
+ return MAX_PORT;
+ } catch (Throwable t) { //Config error or fix isConnectOrTimeout method.
+ throw new Error("Can't find open port.", t);
+ }
+ }
+
+ private static boolean checkUnusedPort(int port) {
+ try {
+ close(new ServerSocket(port));
+ try {
+ close(new Socket("localhost", port));
+ } catch (UnknownHostException UHE) {
+ throw new AssertionError(UHE);
+ } catch (IOException IOE) {
+ return isConnectOrTimeout(IOE);
+ }
+ } catch (IOException inUse) {
+ }
+ return false; //listening or bound.
+ }
+
+ private static void close(Socket s) {
+ try {
+ s.close();
+ } catch (IOException ignore) {
+ }
+ }
+
+ private static void close(ServerSocket s) {
+ try {
+ s.close();
+ } catch (IOException ignore) {
+ }
+ }
+
     private static abstract class MessageErrorManager extends InternalErrorManager {
 
         private final MailHandler h;


diff -r 403bd9cb4b56 -r ff83aab363d9 mail/src/main/java/javax/mail/internet/InternetAddress.java
--- a/mail/src/main/java/javax/mail/internet/InternetAddress.java Tue Oct 05 13:45:39 2010 -0700
+++ b/mail/src/main/java/javax/mail/internet/InternetAddress.java Tue Oct 05 13:49:06 2010 -0700
@@ -509,34 +509,8 @@
      * @return current user's email address
      */
     public static InternetAddress getLocalAddress(Session session) {
- String user=null, host=null, address=null;
         try {
- if (session == null) {
- user = System.getProperty("user.name");
- host = InetAddress.getLocalHost().getHostName();
- } else {
- address = session.getProperty("mail.from");
- if (address == null) {
- user = session.getProperty("mail.user");
- if (user == null || user.length() == 0)
- user = session.getProperty("user.name");
- if (user == null || user.length() == 0)
- user = System.getProperty("user.name");
- host = session.getProperty("mail.host");
- if (host == null || host.length() == 0) {
- InetAddress me = InetAddress.getLocalHost();
- if (me != null)
- host = me.getHostName();
- }
- }
- }
-
- if (address == null && user != null && user.length() != 0 &&
- host != null && host.length() != 0)
- address = user + "@" + host;
-
- if (address != null)
- return new InternetAddress(address);
+ return _getLocalAddress(session);
         } catch (SecurityException sex) { // ignore it
         } catch (AddressException ex) { // ignore it
         } catch (UnknownHostException ex) { } // ignore it
@@ -544,6 +518,86 @@
     }
 
     /**
+ * A package-private version of getLocalAddress that doesn't swallow
+ * the exception. Used by MimeMessage.setFrom() to report the reason
+ * for the failure.
+ */
+ // package-private
+ static InternetAddress _getLocalAddress(Session session)
+ throws SecurityException, AddressException, UnknownHostException {
+ String user = null, host = null, address = null;
+ if (session == null) {
+ user = System.getProperty("user.name");
+ host = getLocalHostName();
+ } else {
+ address = session.getProperty("mail.from");
+ if (address == null) {
+ user = session.getProperty("mail.user");
+ if (user == null || user.length() == 0)
+ user = session.getProperty("user.name");
+ if (user == null || user.length() == 0)
+ user = System.getProperty("user.name");
+ host = session.getProperty("mail.host");
+ if (host == null || host.length() == 0)
+ host = getLocalHostName();
+ }
+ }
+
+ if (address == null && user != null && user.length() != 0 &&
+ host != null && host.length() != 0)
+ address = MimeUtility.quote(user, HeaderTokenizer.RFC822) +
+ "@" + host;
+
+ if (address == null)
+ return null;
+
+ return new InternetAddress(address);
+ }
+
+ /**
+ * Get the local host name from InetAddress and return it in a form
+ * suitable for use in an email address.
+ */
+ private static String getLocalHostName() throws UnknownHostException {
+ String host = null;
+ InetAddress me = InetAddress.getLocalHost();
+ if (me != null) {
+ host = me.getHostName();
+ if (host != null && host.length() > 0 && isInetAddressLiteral(host))
+ host = '[' + host + ']';
+ }
+ return host;
+ }
+
+ /**
+ * Is the address an IPv4 or IPv6 address literal, which needs to
+ * be enclosed in "[]" in an email address? IPv4 literals contain
+ * decimal digits and dots, IPv6 literals contain hex digits, dots,
+ * and colons. We're lazy and don't check the exact syntax, just
+ * the allowed characters; strings that have only the allowed
+ * characters in a literal but don't meet the syntax requirements
+ * for a literal definitely can't be a host name and thus will fail
+ * later when used as an address literal.
+ */
+ private static boolean isInetAddressLiteral(String addr) {
+ boolean sawHex = false, sawColon = false;
+ for (int i = 0; i < addr.length(); i++) {
+ char c = addr.charAt(i);
+ if (c >= '0' && c <= '9')
+ ; // digits always ok
+ else if (c == '.')
+ ; // dot always ok
+ else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+ sawHex = true; // need to see a colon too
+ else if (c == ':')
+ sawColon = true;
+ else
+ return false; // anything else, definitely not a literal
+ }
+ return !sawHex || sawColon;
+ }
+
+ /**
      * Parse the given comma separated sequence of addresses into
      * InternetAddress objects. Addresses must follow RFC822 syntax.
      *

diff -r 403bd9cb4b56 -r ff83aab363d9 mail/src/main/java/javax/mail/internet/MimeMessage.java
--- a/mail/src/main/java/javax/mail/internet/MimeMessage.java Tue Oct 05 13:45:39 2010 -0700
+++ b/mail/src/main/java/javax/mail/internet/MimeMessage.java Tue Oct 05 13:49:06 2010 -0700
@@ -398,7 +398,14 @@
      * @exception MessagingException
      */
     public void setFrom() throws MessagingException {
- InternetAddress me = InternetAddress.getLocalAddress(session);
+ InternetAddress me = null;
+ try {
+ me = InternetAddress._getLocalAddress(session);
+ } catch (Exception ex) {
+ // if anything goes wrong (SecurityException, UnknownHostException),
+ // chain the exception
+ throw new MessagingException("No From address", ex);
+ }
         if (me != null)
             setFrom(me);
         else

diff -r 403bd9cb4b56 -r ff83aab363d9 mail/src/test/java/javax/mail/internet/GetLocalAddressTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mail/src/test/java/javax/mail/internet/GetLocalAddressTest.java Tue Oct 05 13:49:06 2010 -0700
@@ -0,0 +1,133 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package javax.mail.internet;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Properties;
+
+import javax.mail.Session;
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetAddress;
+
+import org.junit.*;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test the InternetAddress.getLocalAddress() method.
+ */
+public class GetLocalAddressTest {
+
+ private static String localhost;
+ static {
+ try {
+ localhost = InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException ex) {
+ localhost = "localhost";
+ }
+ }
+
+ @Test
+ public void testUserName() throws Exception {
+ System.setProperty("user.name", "Joe");
+ InternetAddress ia = InternetAddress.
[truncated due to length]