commits@javamail.java.net

[javamail~mercurial:883] Be much more tolerant of buggy IMAP servers that put spaces where

From: <shannon_at_java.net>
Date: Mon, 21 Nov 2016 22:28:43 +0000

Project: javamail
Repository: mercurial
Revision: 883
Author: shannon
Date: 2016-11-19 00:41:17 UTC
Link:

Log Message:
------------
Add compatibility note about YoungerTerm/OlderTerm change - bug 8550
The UIDFolder interface should have a MAXUID constant - bug 8568
Forgot to update CHANGES.txt for bug 8568
Add some more SMTP AUTH tests.
Be much more tolerant of buggy IMAP servers that put spaces where
they don't belong - bug 7182


Revisions:
----------
879
880
881
882
883


Modified Paths:
---------------
doc/release/COMPAT.txt
doc/spec/JavaMail-1.6-changes.txt
mail/src/main/java/javax/mail/UIDFolder.java
doc/release/CHANGES.txt
mail/src/test/java/com/sun/mail/smtp/SMTPSaslLoginTest.java
mail/src/main/java/com/sun/mail/iap/Response.java
mail/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java
mail/src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java
mail/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java
mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java
mail/src/main/java/com/sun/mail/imap/protocol/MODSEQ.java
mail/src/main/java/com/sun/mail/imap/protocol/Namespaces.java
mail/src/main/java/com/sun/mail/imap/protocol/Status.java
mail/src/test/java/com/sun/mail/iap/ResponseTest.java
mail/src/test/java/com/sun/mail/imap/protocol/StatusTest.java


Added Paths:
------------
mail/src/test/java/com/sun/mail/imap/protocol/MODSEQTest.java
mail/src/test/java/com/sun/mail/imap/protocol/NamespacesTest.java


Diffs:
------
diff -r f627c0ed80b9 -r 73cc72a5566f doc/release/COMPAT.txt
--- a/doc/release/COMPAT.txt Tue Nov 08 15:27:35 2016 -0800
+++ b/doc/release/COMPAT.txt Wed Nov 16 15:55:56 2016 -0800
@@ -59,6 +59,18 @@
         this method. The IMAPFolder class implements the UIDFolder
         interface and has provided this method for some time.
 
+- YoungerTerm, OlderTerm, and ModifiedSinceTerm fall back to local searching
+
+ In general, SearchTerms that are not understood by the IMAP
+ provider or not supported by the IMAP server fall back to doing
+ local searching. The IMAP-specific YoungerTerm, OlderTerm, and
+ ModifiedSinceTerm were instead throwing an exception if the server
+ didn't support the extension that the search term depended on.
+ This has been fixed to fall back to doing the search locally.
+ This behavior can be changed by setting the property
+ "mail.imap.throwsearchexception" to "true" to cause an exception
+ to be thrown if the server can't perform the search.
+
 
 
 -- JavaMail 1.5.6 --


diff -r 73cc72a5566f -r 6ec6c452e080 doc/spec/JavaMail-1.6-changes.txt
--- a/doc/spec/JavaMail-1.6-changes.txt Wed Nov 16 15:55:56 2016 -0800
+++ b/doc/spec/JavaMail-1.6-changes.txt Wed Nov 16 16:54:15 2016 -0800
@@ -403,3 +403,29 @@
 likely that the only class implementing this interface is the IMAPFolder
 class in the JavaMail reference implementation, thus this incompatibility
 is extremely unlikely to cause a problem in practice.
+
+
+===================================================================
+
+7. The UIDFolder interface should have a MAXUID constant (8568)
+---------------------------------------------------------------
+
+An IMAP UID is a 32-bit unsigned integer and is represented as a Java long.
+A new constant indicates the maximum value of a UID:
+
+ /**
+ * The largest value possible for a UID, a 32-bit unsigned integer.
+ * This can be used to fetch all new messages by keeping track of the
+ * last UID that was seen and using:
+ * <blockquote><pre>
+ *
+ * Folder f = store.getFolder("whatever");
+ * UIDFolder uf = (UIDFolder)f;
+ * Message[] newMsgs =
+ * uf.getMessagesByUID(lastSeenUID + 1, UIDFolder.MAXUID);
+ *
+ * </pre></blockquote><p>
+ *
+ * @since JavaMail 1.6
+ */
+ public static final long MAXUID = 0xffffffffL;

diff -r 73cc72a5566f -r 6ec6c452e080 mail/src/main/java/javax/mail/UIDFolder.java
--- a/mail/src/main/java/javax/mail/UIDFolder.java Wed Nov 16 15:55:56 2016 -0800
+++ b/mail/src/main/java/javax/mail/UIDFolder.java Wed Nov 16 16:54:15 2016 -0800
@@ -75,6 +75,7 @@
  *
  * </pre></blockquote><p>
  *
+ * @author Bill Shannon
  * @author John Mani
  */
 
@@ -119,7 +120,24 @@
      *
      * @see #getMessagesByUID
      */
- public final static long LASTUID = -1;
+ public static final long LASTUID = -1;
+
+ /**
+ * The largest value possible for a UID, a 32-bit unsigned integer.
+ * This can be used to fetch all new messages by keeping track of the
+ * last UID that was seen and using:
+ * <blockquote><pre>
+ *
+ * Folder f = store.getFolder("whatever");
+ * UIDFolder uf = (UIDFolder)f;
+ * Message[] newMsgs =
+ * uf.getMessagesByUID(lastSeenUID + 1, UIDFolder.MAXUID);
+ *
+ * </pre></blockquote><p>
+ *
+ * @since JavaMail 1.6
+ */
+ public static final long MAXUID = 0xffffffffL; // max 32-bit unsigned int
 
     /**
      * Returns the UIDValidity value associated with this folder. <p>


diff -r 6ec6c452e080 -r 4ef087aaf0b4 doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Wed Nov 16 16:54:15 2016 -0800
+++ b/doc/release/CHANGES.txt Fri Nov 18 13:16:54 2016 -0800
@@ -40,6 +40,7 @@
 K 8492 MailHandler should support 'login' verify type.
 K 8540 MailHandler support for non-multipart messages
 K 8550 use of YoungerTerm/OlderTerm on server without WITHIN support fails
+K 8568 The UIDFolder interface should have a MAXUID constant
 
 
                   CHANGES IN THE 1.5.6 RELEASE


diff -r 4ef087aaf0b4 -r a07a4ef4f652 mail/src/test/java/com/sun/mail/smtp/SMTPSaslLoginTest.java
--- a/mail/src/test/java/com/sun/mail/smtp/SMTPSaslLoginTest.java Fri Nov 18 13:16:54 2016 -0800
+++ b/mail/src/test/java/com/sun/mail/smtp/SMTPSaslLoginTest.java Fri Nov 18 16:40:04 2016 -0800
@@ -54,7 +54,8 @@
 import static org.junit.Assert.fail;
 
 /**
- * Test login using a SASL mechanism.
+ * Test login using a SASL mechanism on the server
+ * with SASL and non-SASL on the client.
  */
 public class SMTPSaslLoginTest {
 
@@ -217,4 +218,96 @@
             }
         }
     }
+
+ /**
+ * Test that AUTH with no mechanisms fails.
+ */
+ @Test
+ public void testAuthNoParam() {
+ TestServer server = null;
+ try {
+ server = new TestServer(new SMTPSaslHandler() {
+ @Override
+ public void ehlo() throws IOException {
+ println("250-hello");
+ println("250 AUTH");
+ }
+ });
+ server.start();
+
+ Properties properties = new Properties();
+ properties.setProperty("mail.smtp.host", "localhost");
+ properties.setProperty("mail.smtp.port", "" + server.getPort());
+ //properties.setProperty("mail.debug.auth", "true");
+ Session session = Session.getInstance(properties);
+ //session.setDebug(true);
+
+ Transport t = session.getTransport("smtp");
+ try {
+ t.connect("test", "test");
+ fail("Connect didn't fail");
+ } catch (AuthenticationFailedException ex) {
+ // success
+ } catch (Exception ex) {
+ fail(ex.toString());
+ } finally {
+ t.close();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ } finally {
+ if (server != null) {
+ server.quit();
+ server.interrupt();
+ }
+ }
+ }
+
+ /**
+ * Test that no AUTH succeeds by skipping authentication entirely.
+ */
+ @Test
+ public void testNoAuth() {
+ TestServer server = null;
+ try {
+ server = new TestServer(new SMTPSaslHandler() {
+ @Override
+ public void ehlo() throws IOException {
+ println("250-hello");
+ println("250 XXX");
+ }
+ @Override
+ public void auth(String line) throws IOException {
+ println("501 Authentication failed");
+ }
+ });
+ server.start();
+
+ Properties properties = new Properties();
+ properties.setProperty("mail.smtp.host", "localhost");
+ properties.setProperty("mail.smtp.port", "" + server.getPort());
+ //properties.setProperty("mail.debug.auth", "true");
+ Session session = Session.getInstance(properties);
+ //session.setDebug(true);
+
+ Transport t = session.getTransport("smtp");
+ try {
+ t.connect("test", "test");
+ // success
+ } catch (Exception ex) {
+ fail(ex.toString());
+ } finally {
+ t.close();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ } finally {
+ if (server != null) {
+ server.quit();
+ server.interrupt();
+ }
+ }
+ }
 }


diff -r a07a4ef4f652 -r fa2068436d55 doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Nov 18 16:40:04 2016 -0800
+++ b/doc/release/CHANGES.txt Fri Nov 18 16:41:17 2016 -0800
@@ -25,6 +25,7 @@
 K 6823 Store, Transport, and Folder should implement AutoCloseable
 K 6949 MailDateFormat changes for version 1.6
 K 7010 Fix javac warnings
+K 7182 fails to parse some fetch response that has space before final ')'
 K 7371 IMAP doesn't handle illegal CAPABILITY response after LOGIN/AUTHENTICATE
 K 8398 MailSessionDefinition should use the Repeatable annotation for Java EE 8
 K 8399 IdleManager fails on Android

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/main/java/com/sun/mail/iap/Response.java
--- a/mail/src/main/java/com/sun/mail/iap/Response.java Fri Nov 18 16:40:04 2016 -0800
+++ b/mail/src/main/java/com/sun/mail/iap/Response.java Fri Nov 18 16:41:17 2016 -0800
@@ -191,6 +191,20 @@
     }
 
     /**
+ * Skip past any spaces. If the next non-space character is c,
+ * consume it and return true. Otherwise stop at that point
+ * and return false.
+ */
+ public boolean isNextNonSpace(char c) {
+ skipSpaces();
+ if (index < size && buffer[index] == (byte)c) {
+ index++;
+ return true;
+ }
+ return false;
+ }
+
+ /**
      * Skip to the next space, for use in error recovery while parsing.
      */
     public void skipToken() {
@@ -287,14 +301,11 @@
         }
         index++; // skip '('
 
+ // to handle buggy IMAP servers, we tolerate multiple spaces as
+ // well as spaces after the left paren or before the right paren
         List<String> result = new ArrayList<>();
- skipSpaces();
- if (peekByte() != ')') {
- do {
- result.add(atom ? readAtomString() : readString());
- } while (index < size && buffer[index++] != ')');
- } else
- index++; // skip ')'
+ while (!isNextNonSpace(')'))
+ result.add(atom ? readAtomString() : readString());
 
         return result.toArray(new String[result.size()]);
     }

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java Fri Nov 18 16:40:04 2016 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/BODYSTRUCTURE.java Fri Nov 18 16:41:17 2016 -0800
@@ -124,7 +124,7 @@
             if (parseDebug)
                 System.out.println("DEBUG IMAP: subtype " + subtype);
 
- if (r.readByte() == ')') { // done
+ if (r.isNextNonSpace(')')) { // done
                 if (parseDebug)
                     System.out.println("DEBUG IMAP: parse DONE");
                 return;
@@ -136,7 +136,7 @@
                 System.out.println("DEBUG IMAP: parsing extension data");
             // Body parameters
             cParams = parseParameters(r);
- if (r.readByte() == ')') { // done
+ if (r.isNextNonSpace(')')) { // done
                 if (parseDebug)
                     System.out.println("DEBUG IMAP: body parameters DONE");
                 return;
@@ -152,7 +152,7 @@
                     System.out.println("DEBUG IMAP: disposition " +
                                                         disposition);
                 dParams = parseParameters(r);
- if (r.readByte() != ')') // eat the end ')'
+ if (!r.isNextNonSpace(')')) // eat the end ')'
                     throw new ParsingException(
                         "BODYSTRUCTURE parse error: " +
                         "missing ``)'' at end of disposition in multipart");
@@ -171,17 +171,12 @@
 
             // RFC3501 allows no body-fld-lang after body-fld-disp,
             // even though RFC2060 required it
- if ((b = r.readByte()) == ')') {
+ if (r.isNextNonSpace(')')) {
                 if (parseDebug)
                     System.out.println("DEBUG IMAP: no body-fld-lang");
                 return; // done
             }
 
- if (b != ' ')
- throw new ParsingException(
- "BODYSTRUCTURE parse error: " +
- "missing space after disposition");
-
             // Language
             if (r.peekByte() == '(') { // a list follows
                 language = r.readStringList();
@@ -312,8 +307,7 @@
                                 type + "/" + subtype);
             }
 
- if (r.peekByte() == ')') {
- r.readByte();
+ if (r.isNextNonSpace(')')) {
                 if (parseDebug)
                     System.out.println("DEBUG IMAP: parse DONE");
                 return; // done
@@ -323,7 +317,7 @@
 
             // MD5
             md5 = r.readString();
- if (r.readByte() == ')') {
+ if (r.isNextNonSpace(')')) {
                 if (parseDebug)
                     System.out.println("DEBUG IMAP: no MD5 DONE");
                 return; // done
@@ -339,7 +333,7 @@
                 dParams = parseParameters(r);
                 if (parseDebug)
                     System.out.println("DEBUG IMAP: dParams " + dParams);
- if (r.readByte() != ')') // eat the end ')'
+ if (!r.isNextNonSpace(')')) // eat the end ')'
                     throw new ParsingException(
                         "BODYSTRUCTURE parse error: " +
                         "missing ``)'' at end of disposition");
@@ -354,7 +348,7 @@
                     "bad single part disposition, b " + b);
             }
 
- if (r.readByte() == ')') {
+ if (r.isNextNonSpace(')')) {
                 if (parseDebug)
                     System.out.println("DEBUG IMAP: disposition DONE");
                 return; // done
@@ -422,7 +416,7 @@
                 if (value == null) // work around buggy servers
                     value = "";
                 list.set(name, value);
- } while (r.readByte() != ')');
+ } while (!r.isNextNonSpace(')'));
             list.combineSegments();
         } else if (b == 'N' || b == 'n') {
             if (parseDebug)
@@ -442,7 +436,7 @@
             r.skip(1); // skip '('
             do {
                 parseBodyExtension(r);
- } while (r.readByte() != ')');
+ } while (!r.isNextNonSpace(')'));
         } else if (Character.isDigit((char)b)) // number
             r.readNumber();
         else // nstring

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java Fri Nov 18 16:40:04 2016 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/ENVELOPE.java Fri Nov 18 16:41:17 2016 -0800
@@ -133,7 +133,7 @@
         if (parseDebug)
             System.out.println(" Message-ID: " + messageId);
 
- if (r.readByte() != ')')
+ if (!r.isNextNonSpace(')'))
             throw new ParsingException("ENVELOPE parse error");
     }
 
@@ -148,10 +148,8 @@
              * list instead of NIL. Handle that here even though it
              * doesn't conform to the IMAP spec.
              */
- if (r.peekByte() == ')') {
- r.skip(1);
+ if (r.isNextNonSpace(')'))
                 return null;
- }
 
             List<InternetAddress> v = new ArrayList<>();
 
@@ -162,10 +160,7 @@
                 // if we see an end-of-group address at the top, ignore it
                 if (!a.isEndOfGroup())
                     v.add(a);
- } while (r.peekByte() != ')');
-
- // skip the terminating ')' at the end of the addresslist
- r.skip(1);
+ } while (!r.isNextNonSpace(')'));
 
             return v.toArray(new InternetAddress[v.size()]);
         } else if (b == 'N' || b == 'n') { // NIL
@@ -197,7 +192,7 @@
         // skip bogus spaces inserted by Yahoo IMAP server if
         // "undisclosed-recipients" is a recipient
         r.skipSpaces();
- if (r.readByte() != ')') // skip past terminating ')'
+ if (!r.isNextNonSpace(')')) // skip past terminating ')'
             throw new ParsingException("ADDRESS parse error");
 
         if (host == null) {

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java Fri Nov 18 16:40:04 2016 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java Fri Nov 18 16:41:17 2016 -0800
@@ -196,15 +196,14 @@
     private final static char[] TEXT = {'.','T','E','X','T'};
 
     private void parse() throws ParsingException {
- skipSpaces();
- if (buffer[index] != '(')
+ if (!isNextNonSpace('('))
             throw new ParsingException(
                 "error in FETCH parsing, missing '(' at index " + index);
 
         List<Item> v = new ArrayList<>();
         Item i = null;
+ skipSpaces();
         do {
- index++; // skip '(', or SPACE
 
             if (index >= size)
                 throw new ParsingException(
@@ -217,9 +216,8 @@
                 throw new ParsingException(
                     "error in FETCH parsing, unrecognized item at index " +
                     index + ", starts with \"" + next20() + "\"");
- } while (buffer[index] != ')');
+ } while (!isNextNonSpace(')'));
 
- index++; // skip ')'
         items = v.toArray(new Item[v.size()]);
     }
 

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java Fri Nov 18 16:40:04 2016 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java Fri Nov 18 16:41:17 2016 -0800
@@ -2911,7 +2911,7 @@
             throw new ParsingException("parse error in QUOTA");
 
         List<Quota.Resource> v = new ArrayList<>();
- while (r.peekByte() != ')') {
+ while (!r.isNextNonSpace(')')) {
             // quota_resource ::= atom SP number SP number
             String name = r.readAtom();
             if (name != null) {
@@ -2921,7 +2921,6 @@
                 v.add(res);
             }
         }
- r.readByte();
         q.resources = v.toArray(new Quota.Resource[v.size()]);
         return q;
     }

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/main/java/com/sun/mail/imap/protocol/MODSEQ.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/MODSEQ.java Fri Nov 18 16:40:04 2016 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/MODSEQ.java Fri Nov 18 16:41:17 2016 -0800
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997-2016 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
@@ -71,7 +71,7 @@
 
         modseq = r.readLong();
 
- if (r.readByte() != ')')
+ if (!r.isNextNonSpace(')'))
             throw new ParsingException("MODSEQ parse error");
     }
 }

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/main/java/com/sun/mail/imap/protocol/Namespaces.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/Namespaces.java Fri Nov 18 16:40:04 2016 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/Namespaces.java Fri Nov 18 16:41:17 2016 -0800
@@ -77,7 +77,7 @@
         public Namespace(Response r) throws ProtocolException {
             // Namespace_Element = "(" string SP (<"> QUOTED_CHAR <"> / nil)
             // *(Namespace_Response_Extension) ")"
- if (r.readByte() != '(')
+ if (!r.isNextNonSpace('('))
                 throw new ProtocolException(
                                         "Missing '(' at start of Namespace");
             // first, the prefix
@@ -101,16 +101,16 @@
                 delimiter = 0;
             }
             // at end of Namespace data?
- if (r.peekByte() != ')') {
- // otherwise, must be a Namespace_Response_Extension
- // Namespace_Response_Extension = SP string SP
- // "(" string *(SP string) ")"
- r.skipSpaces();
- r.readString();
- r.skipSpaces();
- r.readStringList();
- }
- if (r.readByte() != ')')
+ if (r.isNextNonSpace(')'))
+ return;
+
+ // otherwise, must be a Namespace_Response_Extension
+ // Namespace_Response_Extension = SP string SP
+ // "(" string *(SP string) ")"
+ r.readString();
+ r.skipSpaces();
+ r.readStringList();
+ if (!r.isNextNonSpace(')'))
                 throw new ProtocolException("Missing ')' at end of Namespace");
         }
     };
@@ -149,16 +149,13 @@
      * Parse out one of the three sets of namespaces.
      */
     private Namespace[] getNamespaces(Response r) throws ProtocolException {
- r.skipSpaces();
         // Namespace = nil / "(" 1*( Namespace_Element) ")"
- if (r.peekByte() == '(') {
+ if (r.isNextNonSpace('(')) {
             List<Namespace> v = new ArrayList<>();
- r.readByte();
             do {
                 Namespace ns = new Namespace(r);
                 v.add(ns);
- } while (r.peekByte() != ')');
- r.readByte();
+ } while (!r.isNextNonSpace(')'));
             return v.toArray(new Namespace[v.size()]);
         } else {
             String s = r.readAtom();

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/main/java/com/sun/mail/imap/protocol/Status.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/Status.java Fri Nov 18 16:40:04 2016 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/Status.java Fri Nov 18 16:41:17 2016 -0800
@@ -114,7 +114,7 @@
                 items.put(attr.toUpperCase(Locale.ENGLISH),
                             Long.valueOf(r.readLong()));
             }
- } while (r.readByte() != ')');
+ } while (!r.isNextNonSpace(')'));
     }
 
     /**

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/test/java/com/sun/mail/iap/ResponseTest.java
--- a/mail/src/test/java/com/sun/mail/iap/ResponseTest.java Fri Nov 18 16:40:04 2016 -0800
+++ b/mail/src/test/java/com/sun/mail/iap/ResponseTest.java Fri Nov 18 16:41:17 2016 -0800
@@ -103,6 +103,41 @@
      */
     @Test
     public void testAStringList() throws Exception {
+ Response r = new Response("* " + "(A B C)");
+ assertArrayEquals(new String[] { "A", "B", "C" },
+ r.readAtomStringList());
+ }
+
+ @Test
+ public void testAStringListInitialSpace() throws Exception {
+ Response r = new Response("* " + "( A B C)");
+ assertArrayEquals(new String[] { "A", "B", "C" },
+ r.readAtomStringList());
+ }
+
+ @Test
+ public void testAStringListTrailingSpace() throws Exception {
+ Response r = new Response("* " + "(A B C )");
+ assertArrayEquals(new String[] { "A", "B", "C" },
+ r.readAtomStringList());
+ }
+
+ @Test
+ public void testAStringListInitialAndTrailingSpace() throws Exception {
+ Response r = new Response("* " + "( A B C )");
+ assertArrayEquals(new String[] { "A", "B", "C" },
+ r.readAtomStringList());
+ }
+
+ @Test
+ public void testAStringListMultipleSpaces() throws Exception {
+ Response r = new Response("* " + "(A B C)");
+ assertArrayEquals(new String[] { "A", "B", "C" },
+ r.readAtomStringList());
+ }
+
+ @Test
+ public void testAStringListQuoted() throws Exception {
         Response r = new Response("* " + "(A B \"C\")");
         assertArrayEquals(new String[] { "A", "B", "C" },
                             r.readAtomStringList());

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/test/java/com/sun/mail/imap/protocol/MODSEQTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mail/src/test/java/com/sun/mail/imap/protocol/MODSEQTest.java Fri Nov 18 16:41:17 2016 -0800
@@ -0,0 +1,77 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2015-2016 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.imap.protocol;
+
+import com.sun.mail.iap.ParsingException;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test the MODSEQ class.
+ */
+public class MODSEQTest {
+ /**
+ * Test an example MODSEQ response.
+ */
+ @Test
+ public void testAll() throws Exception {
+ IMAPResponse response = new IMAPResponse(
+ "* 1 FETCH (MODSEQ (624140003))");
+ FetchResponse fr = new FetchResponse(response);
+ MODSEQ m = fr.getItem(MODSEQ.class);
+ assertEquals(1, m.seqnum);
+ assertEquals(624140003, m.modseq);
+ }
+
+ /**
+ * Test an example MODSEQ response with unnecessary spaces.
+ */
+ @Test
+ public void testSpaces() throws Exception {
+ IMAPResponse response = new IMAPResponse(
+ "* 1 FETCH ( MODSEQ ( 624140003 ) )");
+ FetchResponse fr = new FetchResponse(response);
+ MODSEQ m = fr.getItem(MODSEQ.class);
+ assertEquals(1, m.seqnum);
+ assertEquals(624140003, m.modseq);
+ }
+}

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/test/java/com/sun/mail/imap/protocol/NamespacesTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mail/src/test/java/com/sun/mail/imap/protocol/NamespacesTest.java Fri Nov 18 16:41:17 2016 -0800
@@ -0,0 +1,111 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2015-2016 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.imap.protocol;
+
+import com.sun.mail.iap.ParsingException;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test the Namespaces class.
+ */
+public class NamespacesTest {
+ /**
+ * Test an example NAMESPACE response.
+ */
+ @Test
+ public void testAll() throws Exception {
+ IMAPResponse response = new IMAPResponse(
+ "* NAMESPACE ((\"\" \"/\")) " + // personal
+ "((\"~\" \"/\")) " + // other users
+ "((\"#shared/\" \"/\")" + // shared
+ "(\"#public/\" \"/\")" +
+ "(\"#ftp/\" \"/\")" +
+ "(\"#news.\" \".\"))");
+ Namespaces ns = new Namespaces(response);
+ assertEquals(1, ns.personal.length);
+ assertEquals("", ns.personal[0].prefix);
+ assertEquals('/', ns.personal[0].delimiter);
+ assertEquals(1, ns.otherUsers.length);
+ assertEquals("~", ns.otherUsers[0].prefix);
+ assertEquals('/', ns.otherUsers[0].delimiter);
+ assertEquals(4, ns.shared.length);
+ assertEquals("#shared/", ns.shared[0].prefix);
+ assertEquals('/', ns.shared[0].delimiter);
+ assertEquals("#public/", ns.shared[1].prefix);
+ assertEquals('/', ns.shared[1].delimiter);
+ assertEquals("#ftp/", ns.shared[2].prefix);
+ assertEquals('/', ns.shared[2].delimiter);
+ assertEquals("#news.", ns.shared[3].prefix);
+ assertEquals('.', ns.shared[3].delimiter);
+ }
+
+ /**
+ * Test an example NAMESPACE response with unnecessary spaces.
+ */
+ @Test
+ public void testSpaces() throws Exception {
+ IMAPResponse response = new IMAPResponse(
+ "* NAMESPACE ((\"\" \"/\")) " + // personal
+ "( ( \"~\" \"/\" ) ) " + // other users
+ "(( \"#shared/\" \"/\" )" + // shared
+ "( \"#public/\" \"/\" )" +
+ "( \"#ftp/\" \"/\" )" +
+ " (\"#news.\" \".\" ))");
+ Namespaces ns = new Namespaces(response);
+ assertEquals(1, ns.personal.length);
+ assertEquals("", ns.personal[0].prefix);
+ assertEquals('/', ns.personal[0].delimiter);
+ assertEquals(1, ns.otherUsers.length);
+ assertEquals("~", ns.otherUsers[0].prefix);
+ assertEquals('/', ns.otherUsers[0].delimiter);
+ assertEquals(4, ns.shared.length);
+ assertEquals("#shared/", ns.shared[0].prefix);
+ assertEquals('/', ns.shared[0].delimiter);
+ assertEquals("#public/", ns.shared[1].prefix);
+ assertEquals('/', ns.shared[1].delimiter);
+ assertEquals("#ftp/", ns.shared[2].prefix);
+ assertEquals('/', ns.shared[2].delimiter);
+ assertEquals("#news.", ns.shared[3].prefix);
+ assertEquals('.', ns.shared[3].delimiter);
+ }
+}

diff -r a07a4ef4f652 -r fa2068436d55 mail/src/test/java/com/sun/mail/imap/protocol/StatusTest.java
--- a/mail/src/test/java/com/sun/mail/imap/protocol/StatusTest.java Fri Nov 18 16:40:04 2016 -0800
+++ b/mail/src/test/java/com/sun/mail/imap/protocol/StatusTest.java Fri Nov 18 16:41:17 2016 -0800
@@ -66,6 +66,19 @@
     }
 
     /**
+ * Test that spaces in the response don't confuse it.
+ */
+ @Test
+ public void testSpaces() throws Exception {
+ IMAPResponse response = new IMAPResponse(
+ "* STATUS test ( MESSAGES 231 UIDNEXT 44292 )");
+ Status s = new Status(response);
+ assertEquals("test", s.mbox);
+ assertEquals(231, s.total);
+ assertEquals(44292, s.uidnext);
+ }
+
+ /**
      * Test that a bad response throws a ParsingException
      */
     @Test(expected = ParsingException.class)