commits@javamail.java.net

[javamail~mercurial:423] Close the SMTP connection if there's an I/O error.

From: <shannon_at_kenai.com>
Date: Mon, 6 Feb 2012 22:31:06 +0000

Project: javamail
Repository: mercurial
Revision: 423
Author: shannon
Date: 2012-02-04 08:18:45 UTC
Link:

Log Message:
------------
Have to skip whitespace before looking for envelope of nested message.
Close the SMTP connection if there's an I/O error.


Revisions:
----------
422
423


Modified Paths:
---------------
mail/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java
doc/release/CHANGES.txt
mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java


Added Paths:
------------
mail/src/test/java/com/sun/mail/smtp/SMTPIOExceptionTest.java


Diffs:
------
diff -r c9dc98c736d7 -r 5a8b94fefd5b mail/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java Fri Feb 03 16:36:14 2012 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java Fri Feb 03 20:07:02 2012 -0800
@@ -262,6 +262,7 @@
                 // messages just like simple body parts and fails to return
                 // the envelope and body structure of the message (sort of
                 // like IMAP4 before rev1).
+ r.skipSpaces();
                 if (r.peekByte() == '(') { // the envelope follows
                     envelope = new ENVELOPE(r);
                     if (parseDebug)


diff -r 5a8b94fefd5b -r b8387d747e69 doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Feb 03 20:07:02 2012 -0800
+++ b/doc/release/CHANGES.txt Sat Feb 04 00:18:45 2012 -0800
@@ -32,6 +32,7 @@
 <no id> better handle timeouts from POP3 server, throwing FolderClosedException
 <no id> add com.sun.mail.util.ReadableMime to allow reading raw MIME data
 <no id> work around Gmail IMAP bug with nested messages
+<no id> close the SMTP connection if there's an I/O error
 
 
                   CHANGES IN THE 1.4.4 RELEASE

diff -r 5a8b94fefd5b -r b8387d747e69 mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java
--- a/mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java Fri Feb 03 20:07:02 2012 -0800
+++ b/mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java Sat Feb 04 00:18:45 2012 -0800
@@ -1139,8 +1139,20 @@
                                      validSentAddr, validUnsentAddr,
                                      invalidAddr, this.message);
         } catch (MessagingException mex) {
- if (debug)
+ if (debug) {
+ out.println("DEBUG SMTP: MessagingException while sending");
                 mex.printStackTrace(out);
+ }
+ // the MessagingException might be wrapping an IOException
+ if (mex.getNextException() instanceof IOException) {
+ // if we catch an IOException, it means that we want
+ // to drop the connection so that the message isn't sent
+ if (debug)
+ out.println("DEBUG SMTP: nested IOException, closing");
+ try {
+ closeConnection();
+ } catch (MessagingException cex) { /* ignore it */ }
+ }
             addressesFailed();
             notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED,
                                      validSentAddr, validUnsentAddr,
@@ -1148,8 +1160,10 @@
 
             throw mex;
         } catch (IOException ex) {
- if (debug)
+ if (debug) {
+ out.println("DEBUG SMTP: IOException while sending, closing");
                 ex.printStackTrace(out);
+ }
             // if we catch an IOException, it means that we want
             // to drop the connection so that the message isn't sent
             try {

diff -r 5a8b94fefd5b -r b8387d747e69 mail/src/test/java/com/sun/mail/smtp/SMTPIOExceptionTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mail/src/test/java/com/sun/mail/smtp/SMTPIOExceptionTest.java Sat Feb 04 00:18:45 2012 -0800
@@ -0,0 +1,141 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2009-2012 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 com.sun.mail.smtp;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import javax.mail.event.ConnectionAdapter;
+import javax.mail.event.ConnectionEvent;
+
+import org.junit.Test;
+import org.junit.Rule;
+import org.junit.rules.Timeout;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Test that the connection is closed when an IOException is detected.
+ */
+public final class SMTPIOExceptionTest {
+
+ // timeout the test in case of failure
+ @Rule
+ public Timeout deadlockTimeout = new Timeout(5000);
+
+ private boolean closed = false;
+
+ @Test
+ public void test() throws Exception {
+ SMTPServer server = null;
+ try {
+ SMTPHandler handler = new SMTPHandler() {
+ public void rcpt() throws IOException {
+ try {
+ // delay long enough to cause timeout
+ Thread.sleep(400);
+ } catch (Exception ex) { }
+ super.rcpt();
+ }
+ };
+ server = new SMTPServer(handler, 26423);
+ server.start();
+ Thread.sleep(1000);
+
+ final Properties properties = new Properties();
+ properties.setProperty("mail.smtp.host", "localhost");
+ properties.setProperty("mail.smtp.port", "26423");
+ properties.setProperty("mail.smtp.timeout", "200");
+ final Session session = Session.getInstance(properties);
+ //session.setDebug(true);
+
+ final Transport t = session.getTransport("smtp");
+ /*
+ * Use a listener to detect the connection being closed
+ * because if we called isConnected() and the connection
+ * wasn't already closed, it will issue a command that
+ * might detect that the connection was closed, even
+ * though it wasn't closed already.
+ */
+ t.addConnectionListener(new ConnectionAdapter() {
+ @Override
+ public void closed(ConnectionEvent e) {
+ setClosed(true);
+ }
+ });
+ try {
+ MimeMessage msg = new MimeMessage(session);
+ msg.setRecipients(Message.RecipientType.TO, "joe_at_example.com");
+ msg.setSubject("test");
+ msg.setText("test");
+ t.connect();
+ t.sendMessage(msg, msg.getAllRecipients());
+ } catch (MessagingException ex) {
+ // expect an exception from sendMessage
+ Thread.sleep(100); // give event thread time to run
+ assertTrue(getClosed());
+ } finally {
+ t.close();
+ }
+ } catch (final Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ } finally {
+ if (server != null) {
+ server.quit();
+ server.interrupt();
+ }
+ }
+ }
+
+ private synchronized void setClosed(boolean v) {
+ closed = v;
+ }
+
+ private synchronized boolean getClosed() {
+ return closed;
+ }
+}