You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rd...@apache.org on 2008/03/26 22:34:58 UTC

svn commit: r641587 - in /james/server/trunk: imap-api/src/main/java/org/apache/james/api/imap/message/ imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/base/ imap-codec-library/src/main/java/org/apache/james/imapserver/codec/d...

Author: rdonkin
Date: Wed Mar 26 14:34:56 2008
New Revision: 641587

URL: http://svn.apache.org/viewvc?rev=641587&view=rev
Log:
Parse partial FETCH

Added:
    james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/FetchCommandParserPartialFetchTest.java
Modified:
    james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/BodyFetchElement.java
    james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/FetchData.java
    james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/base/AbstractImapCommandParser.java
    james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/FetchCommandParser.java

Modified: james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/BodyFetchElement.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/BodyFetchElement.java?rev=641587&r1=641586&r2=641587&view=diff
==============================================================================
--- james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/BodyFetchElement.java (original)
+++ james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/BodyFetchElement.java Wed Mar 26 14:34:56 2008
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.james.api.imap.message;
 
+import java.util.Arrays;
 import java.util.Collection;
 
 import org.apache.james.api.imap.ImapConstants;
@@ -32,9 +33,9 @@
     public static final int HEADER_NOT_FIELDS = 4;
     public static final int CONTENT = 5;
     
-    private static final BodyFetchElement rfc822 = new BodyFetchElement(ImapConstants.FETCH_RFC822, CONTENT, null, null);
-    private static final BodyFetchElement rfc822Header = new BodyFetchElement(ImapConstants.FETCH_RFC822_HEADER, HEADER, null, null);
-    private static final BodyFetchElement rfc822Text = new BodyFetchElement(ImapConstants.FETCH_RFC822_TEXT, TEXT, null, null);
+    private static final BodyFetchElement rfc822 = new BodyFetchElement(ImapConstants.FETCH_RFC822, CONTENT, null, null, null, null);
+    private static final BodyFetchElement rfc822Header = new BodyFetchElement(ImapConstants.FETCH_RFC822_HEADER, HEADER, null, null, null, null);
+    private static final BodyFetchElement rfc822Text = new BodyFetchElement(ImapConstants.FETCH_RFC822_TEXT, TEXT, null, null, null, null);
 
     public static final BodyFetchElement createRFC822() {
         return rfc822;
@@ -48,19 +49,22 @@
         return rfc822Text;
     }
     
-    
+    private final Long firstOctet;
+    private final Long numberOfOctets;
     private final String name;
     private final int sectionType;
     private final int[] path; 
     private final Collection fieldNames;
 
     public BodyFetchElement( final String name, final int sectionType, 
-            final int[] path, final Collection fieldNames)
+            final int[] path, final Collection fieldNames, Long firstOctet, Long numberOfOctets)
     {
         this.name = name;
         this.sectionType = sectionType;
         this.fieldNames = fieldNames;
         this.path = path;
+        this.firstOctet = firstOctet;
+        this.numberOfOctets = numberOfOctets;
     }
     
     public String getResponseName() {
@@ -94,4 +98,71 @@
     public final int getSectionType() {
         return sectionType;
     }
+
+    /**
+     * Gets the first octet for a partial fetch.
+     * @return the firstOctet when this is a partial fetch
+     * or null 
+     */
+    public final Long getFirstOctet() {
+        return firstOctet;
+    }
+
+    /**
+     * For a partial fetch, gets the number of octets to be returned.
+     * @return the lastOctet,
+     * or null
+     */
+    public final Long getNumberOfOctets() {
+        return numberOfOctets;
+    }
+
+    public int hashCode() {
+        final int PRIME = 31;
+        int result = 1;
+        result = PRIME * result + ((fieldNames == null) ? 0 : fieldNames.hashCode());
+        result = PRIME * result + ((firstOctet == null) ? 0 : firstOctet.hashCode());
+        result = PRIME * result + ((name == null) ? 0 : name.hashCode());
+        result = PRIME * result + ((numberOfOctets == null) ? 0 : numberOfOctets.hashCode());
+        result = PRIME * result + ((path == null) ? 0 : path.length);
+        result = PRIME * result + sectionType;
+        return result;
+    }
+
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        final BodyFetchElement other = (BodyFetchElement) obj;
+        if (fieldNames == null) {
+            if (other.fieldNames != null)
+                return false;
+        } else if (!fieldNames.equals(other.fieldNames))
+            return false;
+        if (firstOctet == null) {
+            if (other.firstOctet != null)
+                return false;
+        } else if (!firstOctet.equals(other.firstOctet))
+            return false;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        if (numberOfOctets == null) {
+            if (other.numberOfOctets != null)
+                return false;
+        } else if (!numberOfOctets.equals(other.numberOfOctets))
+            return false;
+        if (!Arrays.equals(path, other.path))
+            return false;
+        if (sectionType != other.sectionType)
+            return false;
+        return true;
+    }
+    
+    
 }

Modified: james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/FetchData.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/FetchData.java?rev=641587&r1=641586&r2=641587&view=diff
==============================================================================
--- james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/FetchData.java (original)
+++ james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/FetchData.java Wed Mar 26 14:34:56 2008
@@ -108,4 +108,51 @@
         }
         bodyElements.add(element);
     }
+
+    public int hashCode() {
+        final int PRIME = 31;
+        int result = 1;
+        result = PRIME * result + (body ? 1231 : 1237);
+        result = PRIME * result + ((bodyElements == null) ? 0 : bodyElements.hashCode());
+        result = PRIME * result + (bodyStructure ? 1231 : 1237);
+        result = PRIME * result + (envelope ? 1231 : 1237);
+        result = PRIME * result + (flags ? 1231 : 1237);
+        result = PRIME * result + (internalDate ? 1231 : 1237);
+        result = PRIME * result + (setSeen ? 1231 : 1237);
+        result = PRIME * result + (size ? 1231 : 1237);
+        result = PRIME * result + (uid ? 1231 : 1237);
+        return result;
+    }
+
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        final FetchData other = (FetchData) obj;
+        if (body != other.body)
+            return false;
+        if (bodyElements == null) {
+            if (other.bodyElements != null)
+                return false;
+        } else if (!bodyElements.equals(other.bodyElements))
+            return false;
+        if (bodyStructure != other.bodyStructure)
+            return false;
+        if (envelope != other.envelope)
+            return false;
+        if (flags != other.flags)
+            return false;
+        if (internalDate != other.internalDate)
+            return false;
+        if (setSeen != other.setSeen)
+            return false;
+        if (size != other.size)
+            return false;
+        if (uid != other.uid)
+            return false;
+        return true;
+    }
 }

Modified: james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/base/AbstractImapCommandParser.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/base/AbstractImapCommandParser.java?rev=641587&r1=641586&r2=641587&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/base/AbstractImapCommandParser.java (original)
+++ james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/base/AbstractImapCommandParser.java Wed Mar 26 14:34:56 2008
@@ -472,10 +472,42 @@
      */
     public long number( ImapRequestLineReader request ) throws ProtocolException
     {
-        String digits = consumeWord( request, new DigitCharValidator() );
-        return Long.parseLong( digits );
+        return readDigits(request, 0, 0, true);
     }
-
+    
+    private long readDigits( final ImapRequestLineReader request, int add, final long total, final boolean first ) throws ProtocolException
+    {
+        final char next;
+        if (first) {
+            next = request.nextWordChar();
+        } else {
+            request.consume();
+            next = request.nextChar();
+        }
+        final long currentTotal = (10 * total) + add;
+        switch (next) {
+            case '0': return readDigits(request, 0, currentTotal, false);
+            case '1': return readDigits(request, 1, currentTotal, false);
+            case '2': return readDigits(request, 2, currentTotal, false);
+            case '3': return readDigits(request, 3, currentTotal, false);
+            case '4': return readDigits(request, 4, currentTotal, false);
+            case '5': return readDigits(request, 5, currentTotal, false);
+            case '6': return readDigits(request, 6, currentTotal, false);
+            case '7': return readDigits(request, 7, currentTotal, false);
+            case '8': return readDigits(request, 8, currentTotal, false);
+            case '9': return readDigits(request, 9, currentTotal, false);
+            case '.':
+            case ' ':
+            case '>':
+            case '\r':
+            case '\n':
+            case '\t':
+                return currentTotal;
+            default:
+                throw new ProtocolException("Expected a digit but was " + next);
+        }
+    }
+    
     /**
      * Reads an argument of type "nznumber" (a non-zero number)
      * (NOTE this isn't strictly as per the spec, since the spec disallows
@@ -612,15 +644,6 @@
                     chr == '{' ||
                     chr == ' ' ||
                     chr == Character.CONTROL );
-        }
-    }
-
-    public static class DigitCharValidator implements CharacterValidator
-    {
-        public boolean isValid( char chr )
-        {
-            return ( ( chr >= '0' && chr <= '9' ) ||
-                     chr == '*' );
         }
     }
 

Modified: james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/FetchCommandParser.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/FetchCommandParser.java?rev=641587&r1=641586&r2=641587&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/FetchCommandParser.java (original)
+++ james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/FetchCommandParser.java Wed Mar 26 14:34:56 2008
@@ -45,9 +45,9 @@
         final ImapCommand command = factory.getFetch();
         setCommand(command);
     }
-    
+
     public FetchData fetchRequest( ImapRequestLineReader request )
-            throws ProtocolException
+    throws ProtocolException
     {
         FetchData fetch = new FetchData();
 
@@ -63,81 +63,90 @@
             consumeChar(request, ')');
         } else {
             addNextElement( request, fetch );
-            
+
         }
 
         return fetch;
     }
 
-    private void addNextElement( ImapRequestLineReader command, FetchData fetch)
-            throws ProtocolException
-    {
-        /*char next = nextCharInLine( command );
-            StringBuffer element = new StringBuffer();
-            while ( next != ' ' && next != '[' && next != ')' && next!='\n' && next!='\r' ) {
-                element.append(next);
-                command.consume();
-                next = nextCharInLine( command );
-            }*/
-         
-        
-            //String name = element.toString();
-            String name = readWord(command, " [)\r\n");
-            char next = command.nextChar();
-            // Simple elements with no '[]' parameters.
-            //if ( next == ' ' || next == ')'  || next == '\n' || next == '\r') {
-            if (next != '[') {
-                if ( "FAST".equalsIgnoreCase( name ) ) {
-                    fetch.setFlags(true);
-                    fetch.setInternalDate(true);
-                    fetch.setSize(true);
-                } else if ("FULL".equalsIgnoreCase(name)) {
-                    fetch.setFlags(true);
-                    fetch.setInternalDate(true);
-                    fetch.setSize(true);
-                    fetch.setEnvelope(true);
-                    fetch.setBody(true);
-                } else if ("ALL".equalsIgnoreCase(name)) {
-                    fetch.setFlags(true);
-                    fetch.setInternalDate(true);
-                    fetch.setSize(true);
-                    fetch.setEnvelope(true);
-                } else if ("FLAGS".equalsIgnoreCase(name)) {
-                    fetch.setFlags(true);
-                } else if ("RFC822.SIZE".equalsIgnoreCase(name)) {
-                    fetch.setSize(true);
-                } else if ("ENVELOPE".equalsIgnoreCase(name)) {
-                    fetch.setEnvelope(true);
-                } else if ("INTERNALDATE".equalsIgnoreCase(name)) {
-                    fetch.setInternalDate(true);
-                } else if ("BODY".equalsIgnoreCase(name)) {
-                    fetch.setBody(true);
-                } else if ("BODYSTRUCTURE".equalsIgnoreCase(name)) {
-                    fetch.setBodyStructure(true);
-                } else if ("UID".equalsIgnoreCase(name)) {
-                    fetch.setUid(true);
-                } else if ("RFC822".equalsIgnoreCase(name)) {
-                    fetch.add(BodyFetchElement.createRFC822(), false);
-                } else if ("RFC822.HEADER".equalsIgnoreCase(name)) {
-                    fetch.add(BodyFetchElement.createRFC822Header(), true);
-                } else if ("RFC822.TEXT".equalsIgnoreCase(name)) {
-                    fetch.add(BodyFetchElement.createRFC822Text(), false);
-                } else {
-                    throw new ProtocolException( "Invalid fetch attribute: " + name );
-                }
+    private void addNextElement( ImapRequestLineReader reader, FetchData fetch)
+    throws ProtocolException
+    {   
+        //String name = element.toString();
+        String name = readWord(reader, " [)\r\n");
+        char next = reader.nextChar();
+        // Simple elements with no '[]' parameters.
+        if (next != '[') {
+            if ( "FAST".equalsIgnoreCase( name ) ) {
+                fetch.setFlags(true);
+                fetch.setInternalDate(true);
+                fetch.setSize(true);
+            } else if ("FULL".equalsIgnoreCase(name)) {
+                fetch.setFlags(true);
+                fetch.setInternalDate(true);
+                fetch.setSize(true);
+                fetch.setEnvelope(true);
+                fetch.setBody(true);
+            } else if ("ALL".equalsIgnoreCase(name)) {
+                fetch.setFlags(true);
+                fetch.setInternalDate(true);
+                fetch.setSize(true);
+                fetch.setEnvelope(true);
+            } else if ("FLAGS".equalsIgnoreCase(name)) {
+                fetch.setFlags(true);
+            } else if ("RFC822.SIZE".equalsIgnoreCase(name)) {
+                fetch.setSize(true);
+            } else if ("ENVELOPE".equalsIgnoreCase(name)) {
+                fetch.setEnvelope(true);
+            } else if ("INTERNALDATE".equalsIgnoreCase(name)) {
+                fetch.setInternalDate(true);
+            } else if ("BODY".equalsIgnoreCase(name)) {
+                fetch.setBody(true);
+            } else if ("BODYSTRUCTURE".equalsIgnoreCase(name)) {
+                fetch.setBodyStructure(true);
+            } else if ("UID".equalsIgnoreCase(name)) {
+                fetch.setUid(true);
+            } else if ("RFC822".equalsIgnoreCase(name)) {
+                fetch.add(BodyFetchElement.createRFC822(), false);
+            } else if ("RFC822.HEADER".equalsIgnoreCase(name)) {
+                fetch.add(BodyFetchElement.createRFC822Header(), true);
+            } else if ("RFC822.TEXT".equalsIgnoreCase(name)) {
+                fetch.add(BodyFetchElement.createRFC822Text(), false);
+            } else {
+                throw new ProtocolException( "Invalid fetch attribute: " + name );
             }
-            else {
-                consumeChar( command, '[' );
+        }
+        else {
+            consumeChar( reader, '[' );
+
+            String parameter = readWord(reader, "]");
 
-                String parameter = readWord(command, "]");
+            consumeChar( reader, ']');
 
-                consumeChar( command, ']');
-                
-                final BodyFetchElement bodyFetchElement = createBodyElement(parameter);
-                final boolean isPeek = isPeek(name);
-                fetch.add(bodyFetchElement, isPeek);
+            final Long firstOctet;
+            final Long numberOfOctets;
+            if(reader.nextChar() == '<') {
+                consumeChar(reader, '<');
+                firstOctet = new Long(number(reader));
+                if (reader.nextChar() == '.') {
+                    consumeChar(reader, '.');
+                    numberOfOctets = new Long(nzNumber(reader));
+                } else {
+                    numberOfOctets = null;
+                }
+                consumeChar(reader, '>');
+            } else {
+                firstOctet = null;
+                numberOfOctets = null;
             }
+            
+            
+            final BodyFetchElement bodyFetchElement 
+                = createBodyElement(parameter, firstOctet, numberOfOctets);
+            final boolean isPeek = isPeek(name);
+            fetch.add(bodyFetchElement, isPeek);
         }
+    }
 
     private boolean isPeek(String name) throws ProtocolException {
         final boolean isPeek;
@@ -151,16 +160,15 @@
         return isPeek;
     }
 
-    private BodyFetchElement createBodyElement(String parameter) throws ProtocolException {
+    private BodyFetchElement createBodyElement(String parameter, Long firstOctet, Long numberOfOctets) throws ProtocolException {
         final String responseName = "BODY[" + parameter + "]";
         FetchPartPathDecoder decoder = new FetchPartPathDecoder();
         decoder.decode(parameter);
         final int sectionType = getSectionType(decoder);
-        
+
         final List names = decoder.getNames();
         final int[] path = decoder.getPath();
-        final BodyFetchElement bodyFetchElement 
-            = new BodyFetchElement(responseName, sectionType, path, names);
+        final BodyFetchElement bodyFetchElement = new BodyFetchElement(responseName, sectionType, path, names, firstOctet, numberOfOctets);
         return bodyFetchElement;
     }
 
@@ -202,19 +210,9 @@
         }
         return buf.toString();
     }
-    
-    private char nextCharInLine( ImapRequestLineReader request )
-            throws ProtocolException
-    {
-        char next = request.nextChar();
-        if ( next == '\r' || next == '\n' ) {
-            throw new ProtocolException( "Unexpected end of line." );
-        }
-        return next;
-    }
 
     private char nextNonSpaceChar( ImapRequestLineReader request )
-            throws ProtocolException
+    throws ProtocolException
     {
         char next = request.nextChar();
         while ( next == ' ' ) {
@@ -224,11 +222,12 @@
         return next;
     }
 
-    protected ImapMessage decode(ImapCommand command, ImapRequestLineReader request, String tag, boolean useUids) throws ProtocolException {
+    protected ImapMessage decode(ImapCommand command, ImapRequestLineReader request, 
+            String tag, boolean useUids) throws ProtocolException {
         IdRange[] idSet = parseIdRange( request );
         FetchData fetch = fetchRequest( request );
         endLine( request );
-        
+
         final Imap4Rev1MessageFactory factory = getMessageFactory();
         final ImapMessage result  = factory.createFetchMessage(command, useUids, idSet, fetch, tag);
         return result;

Added: james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/FetchCommandParserPartialFetchTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/FetchCommandParserPartialFetchTest.java?rev=641587&view=auto
==============================================================================
--- james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/FetchCommandParserPartialFetchTest.java (added)
+++ james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/FetchCommandParserPartialFetchTest.java Wed Mar 26 14:34:56 2008
@@ -0,0 +1,101 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.imapserver.codec.decode.imap4rev1;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import org.apache.james.api.imap.ImapCommand;
+import org.apache.james.api.imap.ImapMessage;
+import org.apache.james.api.imap.ProtocolException;
+import org.apache.james.api.imap.imap4rev1.Imap4Rev1CommandFactory;
+import org.apache.james.api.imap.imap4rev1.Imap4Rev1MessageFactory;
+import org.apache.james.api.imap.message.BodyFetchElement;
+import org.apache.james.api.imap.message.FetchData;
+import org.apache.james.api.imap.message.IdRange;
+import org.apache.james.imapserver.codec.decode.ImapRequestLineReader;
+import org.jmock.Mock;
+import org.jmock.MockObjectTestCase;
+import org.jmock.core.Constraint;
+
+public class FetchCommandParserPartialFetchTest extends MockObjectTestCase {
+
+    FetchCommandParser parser;
+    Mock mockCommandFactory;
+    Mock mockMessageFactory;
+    Mock mockCommand;
+    Mock mockMessage;
+    ImapCommand command;
+    ImapMessage message;
+    
+    protected void setUp() throws Exception {
+        super.setUp();
+        parser = new FetchCommandParser();
+        mockCommandFactory = mock(Imap4Rev1CommandFactory.class);
+        mockCommandFactory.expects(once()).method("getFetch");
+        mockMessageFactory = mock(Imap4Rev1MessageFactory.class);
+        mockCommand = mock(ImapCommand.class);
+        command = (ImapCommand) mockCommand.proxy();
+        mockMessage = mock(ImapMessage.class);
+        message = (ImapMessage) mockMessage.proxy();
+        parser.init((Imap4Rev1CommandFactory) mockCommandFactory.proxy());
+        parser.setMessageFactory((Imap4Rev1MessageFactory) mockMessageFactory.proxy());
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testShouldParseZeroAndLength() throws Exception {
+        IdRange[] ranges = {new IdRange(1)};
+        FetchData data = new FetchData();
+        data.add(new BodyFetchElement("BODY[]", BodyFetchElement.CONTENT, null, null, 
+                new Long(0), new Long(100)), false);
+        check("1 (BODY[]<0.100>)\r\n", ranges, false, data, "A01");
+    }
+    
+    public void testShouldParseNonZeroAndLength() throws Exception {
+        IdRange[] ranges = {new IdRange(1)};
+        FetchData data = new FetchData();
+        data.add(new BodyFetchElement("BODY[]", BodyFetchElement.CONTENT, null, null, 
+                new Long(20), new Long(12342348)), false);
+        check("1 (BODY[]<20.12342348>)\r\n", ranges, false, data, "A01");
+    }
+    
+    public void testShouldNotParseZeroLength() throws Exception {
+        try {
+            ImapRequestLineReader reader = new ImapRequestLineReader(new ByteArrayInputStream("1 (BODY[]<20.0>)\r\n".getBytes("US-ASCII")), 
+                    new ByteArrayOutputStream());        
+            parser.decode(command, reader, "A01", false);
+            fail("Number of octets must be non-zero");
+        } catch (ProtocolException e) {
+            // expected
+        }
+    }
+    
+    
+    private void check(String input, final IdRange[] idSet, final boolean useUids, FetchData data, String tag) throws Exception {
+        ImapRequestLineReader reader = new ImapRequestLineReader(new ByteArrayInputStream(input.getBytes("US-ASCII")), 
+                    new ByteArrayOutputStream());        
+        Constraint[] constraints = {eq(command), eq(useUids), eq(idSet), eq(data), same(tag)};
+        mockMessageFactory.expects(once()).method("createFetchMessage").with(constraints).will(returnValue(message));
+        parser.decode(command, reader, tag, useUids);
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org