darrell 2003/11/30 02:48:47 Modified: proposals/imap2 build-test.xml proposals/imap2/java/org/apache/james/imapserver ImapSessionImpl.java proposals/imap2/java/org/apache/james/imapserver/commands FetchCommand.java proposals/imap2/test/org/apache/james/imapserver FetchMultipleMessages.test FetchSingleMessage.test proposals/imap2/test/org/apache/james/imapserver/concurrent Concurrent.todo.txt FetchResponse.test Log: - Make sure FLAGS response is added to FETCH when SEEN flag is implicitly set. - Order of elements in FETCH response is now fixed. Revision Changes Path 1.9 +3 -1 james-server/proposals/imap2/build-test.xml Index: build-test.xml =================================================================== RCS file: /home/cvs/james-server/proposals/imap2/build-test.xml,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- build-test.xml 30 Nov 2003 07:03:47 -0000 1.8 +++ build-test.xml 30 Nov 2003 10:48:47 -0000 1.9 @@ -162,7 +162,7 @@ <!-- Tests IMAP commands against a running instance of James --> <target name="testimap-commands" depends="compile"> - <junit fork="yes"> + <junit fork="yes" failureproperty="protocol-test.failed" > <classpath> <pathelement location="${build.test.classes}"/> <path refid="test.class.path"/> @@ -176,6 +176,8 @@ <test name="org.apache.james.imapserver.TestCompound"/> <test name="org.apache.james.imapserver.TestConcurrentSessions"/> </junit> + + <fail message="One or more protocol tests failed" if="protocol-test.failed"/> </target> <target name="build-and-test"> 1.10 +1 -2 james-server/proposals/imap2/java/org/apache/james/imapserver/ImapSessionImpl.java Index: ImapSessionImpl.java =================================================================== RCS file: /home/cvs/james-server/proposals/imap2/java/org/apache/james/imapserver/ImapSessionImpl.java,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- ImapSessionImpl.java 30 Nov 2003 07:08:10 -0000 1.9 +++ ImapSessionImpl.java 30 Nov 2003 10:48:47 -0000 1.10 @@ -65,7 +65,6 @@ import org.apache.mailet.UsersRepository; import javax.mail.Flags; -import java.util.Map; import java.util.Iterator; import java.util.List; 1.8 +164 -182 james-server/proposals/imap2/java/org/apache/james/imapserver/commands/FetchCommand.java Index: FetchCommand.java =================================================================== RCS file: /home/cvs/james-server/proposals/imap2/java/org/apache/james/imapserver/commands/FetchCommand.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- FetchCommand.java 30 Nov 2003 07:03:48 -0000 1.7 +++ FetchCommand.java 30 Nov 2003 10:48:47 -0000 1.8 @@ -62,22 +62,24 @@ import org.apache.james.imapserver.ImapRequestLineReader; import org.apache.james.imapserver.ImapResponse; import org.apache.james.imapserver.ImapSession; +import org.apache.james.imapserver.ImapSessionMailbox; import org.apache.james.imapserver.ProtocolException; -import org.apache.james.imapserver.store.ImapMailbox; import org.apache.james.imapserver.store.MailboxException; import org.apache.james.imapserver.store.MessageFlags; import org.apache.james.imapserver.store.SimpleImapMessage; -import javax.mail.MessagingException; import javax.mail.Flags; +import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; +import java.util.Collection; import java.util.Enumeration; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; /** * Handles processeing for the FETCH imap command. @@ -113,10 +115,10 @@ parser.endLine( request ); if (useUids) { - fetch.addElement(FetchElement.UID); + fetch.uid = true; } - ImapMailbox mailbox = session.getSelected(); + ImapSessionMailbox mailbox = session.getSelected(); long[] uids = mailbox.getMessageUids(); for ( int i = 0; i < uids.length; i++ ) { long uid = uids[i]; @@ -126,11 +128,8 @@ ( !useUids && includes( idSet, msn ) ) ) { SimpleImapMessage imapMessage = mailbox.getMessage( uid ); - String msgData = outputMessage( fetch, imapMessage ); + String msgData = outputMessage( fetch, imapMessage, mailbox, useUids ); response.fetchResponse( msn, msgData ); - if (imapMessage.getFlags().contains(Flags.Flag.RECENT)) { - mailbox.setFlags(new Flags(Flags.Flag.RECENT), false, uid, null, false); - } } } @@ -139,73 +138,93 @@ response.commandComplete( this ); } - private String outputMessage( FetchRequest fetch, SimpleImapMessage message ) + private String outputMessage( FetchRequest fetch, SimpleImapMessage message, + ImapSessionMailbox mailbox, boolean useUids ) throws MailboxException, ProtocolException { - + // Check if this fetch will cause the "SEEN" flag to be set on this message + // If so, update the flags, and ensure that a flags response is included in the response. + boolean ensureFlagsResponse = false; + if (fetch.isSetSeen() && !message.getFlags().contains(Flags.Flag.SEEN)) { + mailbox.setFlags(new Flags(Flags.Flag.SEEN), true, message.getUid(), + mailbox, useUids); + message.getFlags().add(Flags.Flag.SEEN); + ensureFlagsResponse = true; + } + StringBuffer response = new StringBuffer(); - List elements = fetch.getElements(); - for ( int i = 0; i < elements.size(); i++ ) { - FetchElement fetchElement = ( FetchElement ) elements.get( i ); - if ( i > 0 ) { - response.append( SP ); - } - response.append( fetchElement.getResponseName() ); - response.append( SP ); - - if ( fetchElement == FetchElement.FLAGS ) { - Flags flags = message.getFlags(); - response.append( MessageFlags.format(flags) ); - } - else if ( fetchElement == FetchElement.INTERNALDATE ) { - // TODO format properly - String internalDate = message.getAttributes().getInternalDateAsString(); - response.append( "\"" ); - response.append( internalDate ); - response.append ( "\"" ); - } - else if ( fetchElement == FetchElement.RFC822_SIZE ) { - response.append( message.getAttributes().getSize() ); - } - else if ( fetchElement == FetchElement.ENVELOPE ) { - response.append( message.getAttributes().getEnvelope() ); - } - else if ( fetchElement == FetchElement.BODY ) { - response.append( message.getAttributes().getBodyStructure( false ) ); - } - else if ( fetchElement == FetchElement.BODYSTRUCTURE ) { - response.append( message.getAttributes().getBodyStructure( true ) ); - } - else if ( fetchElement == FetchElement.UID ) { - response.append( message.getUid() ); - } + // FLAGS response + if (fetch.flags || ensureFlagsResponse) { + response.append(" FLAGS "); + response.append( MessageFlags.format(message.getFlags()) ); + } + + // INTERNALDATE response + if (fetch.internalDate) { + response.append(" INTERNALDATE \""); + // TODO format properly + response.append( message.getAttributes().getInternalDateAsString() ); + response.append ( "\"" ); + + } + + // RFC822.SIZE response + if (fetch.size) { + response.append(" RFC822.SIZE "); + response.append(message.getAttributes().getSize()); + } + + // ENVELOPE response + if (fetch.envelope) { + response.append(" ENVELOPE "); + response.append(message.getAttributes().getEnvelope()); + } + + // BODY response + if (fetch.body) { + response.append(" BODY "); + response.append(message.getAttributes().getBodyStructure(false)); + } + + // BODYSTRUCTURE response + if (fetch.bodyStructure) { + response.append(" BODYSTRUCTURE "); + response.append(message.getAttributes().getBodyStructure(true)); + } + + // UID response + if (fetch.uid) { + response.append(" UID "); + response.append(message.getUid()); + } + + // BODY part responses. + Collection elements = fetch.getBodyElements(); + for (Iterator iterator = elements.iterator(); iterator.hasNext();) { + BodyFetchElement fetchElement = (BodyFetchElement) iterator.next(); + response.append(SP); + response.append(fetchElement.getResponseName()); + response.append(SP); + // Various mechanisms for returning message body. - else if ( fetchElement instanceof BodyFetchElement ) { - BodyFetchElement bodyFetchElement = (BodyFetchElement)fetchElement; - String sectionSpecifier = bodyFetchElement.getParameters(); - boolean peek = bodyFetchElement.isPeek(); - - MimeMessage mimeMessage = message.getMimeMessage(); - try { - handleBodyFetch( mimeMessage, sectionSpecifier, response ); - } - catch ( MessagingException e ) { - // TODO chain exceptions - throw new MailboxException( e.getMessage() ); - } + String sectionSpecifier = fetchElement.getParameters(); - if ( ! peek ) { - message.getFlags().add(Flags.Flag.SEEN); - // TODO need to store this change. - } - } - else { - throw new ProtocolException( "Invalid fetch attribute" ); + MimeMessage mimeMessage = message.getMimeMessage(); + try { + handleBodyFetch(mimeMessage, sectionSpecifier, response); + } catch (MessagingException e) { + // TODO chain exceptions + throw new MailboxException(e.getMessage()); } } - return response.toString(); + if (response.length() > 0) { + // Remove the leading " ". + return response.substring(1); + } else { + return ""; + } } @@ -361,14 +380,13 @@ throws ProtocolException { FetchRequest fetch = new FetchRequest(); - CharacterValidator validator = new NoopCharValidator(); char next = nextNonSpaceChar( request ); consumeChar( request, '(' ); next = nextNonSpaceChar( request ); while ( next != ')' ) { - fetch.addElement( getNextElement( request ) ); + addNextElement( request, fetch ); next = nextNonSpaceChar( request ); } @@ -377,83 +395,77 @@ return fetch; } - private FetchElement getNextElement( ImapRequestLineReader request) + private void addNextElement( ImapRequestLineReader command, FetchRequest fetch) throws ProtocolException { - char next = nextCharInLine( request ); + char next = nextCharInLine( command ); StringBuffer element = new StringBuffer(); while ( next != ' ' && next != '[' && next != ')' ) { element.append(next); - request.consume(); - next = nextCharInLine( request ); + command.consume(); + next = nextCharInLine( command ); } String name = element.toString(); // Simple elements with no '[]' parameters. if ( next == ' ' || next == ')' ) { if ( "FAST".equalsIgnoreCase( name ) ) { - return FetchElement.FAST; - } - if ( "FULL".equalsIgnoreCase( name ) ) { - return FetchElement.FULL; - } - if ( "ALL".equalsIgnoreCase( name ) ) { - return FetchElement.ALL; - } - if ( "FLAGS".equalsIgnoreCase( name ) ) { - return FetchElement.FLAGS; - } - if ( "RFC822.SIZE".equalsIgnoreCase( name ) ) { - return FetchElement.RFC822_SIZE; - } - if ( "ENVELOPE".equalsIgnoreCase( name ) ) { - return FetchElement.ENVELOPE; - } - if ( "INTERNALDATE".equalsIgnoreCase( name ) ) { - return FetchElement.INTERNALDATE; - } - if ( "BODY".equalsIgnoreCase( name ) ) { - return FetchElement.BODY; - } - if ( "BODYSTRUCTURE".equalsIgnoreCase( name ) ) { - return FetchElement.BODYSTRUCTURE; - } - if ( "UID".equalsIgnoreCase( name ) ) { - return FetchElement.UID; - } - if ( "RFC822".equalsIgnoreCase( name ) ) { - return new BodyFetchElement( "RFC822", "", false ); - } - if ( "RFC822.HEADER".equalsIgnoreCase( name ) ) { - return new BodyFetchElement( "RFC822.HEADER", "HEADER", true ); - } - if ( "RFC822.TEXT".equalsIgnoreCase( name ) ) { - return new BodyFetchElement( "RFC822.TEXT", "TEXT", false ); - } - else { + fetch.flags = true; + fetch.internalDate = true; + fetch.size = true; + } else if ("FULL".equalsIgnoreCase(name)) { + fetch.flags = true; + fetch.internalDate = true; + fetch.size = true; + fetch.envelope = true; + fetch.body = true; + } else if ("ALL".equalsIgnoreCase(name)) { + fetch.flags = true; + fetch.internalDate = true; + fetch.size = true; + fetch.envelope = true; + } else if ("FLAGS".equalsIgnoreCase(name)) { + fetch.flags = true; + } else if ("RFC822.SIZE".equalsIgnoreCase(name)) { + fetch.size = true; + } else if ("ENVELOPE".equalsIgnoreCase(name)) { + fetch.envelope = true; + } else if ("INTERNALDATE".equalsIgnoreCase(name)) { + fetch.internalDate = true; + } else if ("BODY".equalsIgnoreCase(name)) { + fetch.body = true; + } else if ("BODYSTRUCTURE".equalsIgnoreCase(name)) { + fetch.bodyStructure = true; + } else if ("UID".equalsIgnoreCase(name)) { + fetch.uid = true; + } else if ("RFC822".equalsIgnoreCase(name)) { + fetch.add(new BodyFetchElement("RFC822", ""), false); + } else if ("RFC822.HEADER".equalsIgnoreCase(name)) { + fetch.add(new BodyFetchElement("RFC822.HEADER", "HEADER"), true); + } else if ("RFC822.TEXT".equalsIgnoreCase(name)) { + fetch.add(new BodyFetchElement("RFC822.TEXT", "TEXT"), false); + } else { throw new ProtocolException( "Invalid fetch attribute: " + name ); } } else { - consumeChar( request, '[' ); + consumeChar( command, '[' ); StringBuffer sectionIdentifier = new StringBuffer(); - next = nextCharInLine( request ); + next = nextCharInLine( command ); while ( next != ']' ) { sectionIdentifier.append( next ); - request.consume(); - next = nextCharInLine(request); + command.consume(); + next = nextCharInLine(command); } - consumeChar( request, ']' ); + consumeChar( command, ']' ); String parameter = sectionIdentifier.toString(); if ( "BODY".equalsIgnoreCase( name ) ) { - return new BodyFetchElement( "BODY[" + parameter + "]", parameter, false ); - } - if ( "BODY.PEEK".equalsIgnoreCase( name ) ) { - return new BodyFetchElement( "BODY[" + parameter + "]", parameter, true ); - } - else { + fetch.add(new BodyFetchElement("BODY[" + parameter + "]", parameter), false); + } else if ( "BODY.PEEK".equalsIgnoreCase( name ) ) { + fetch.add(new BodyFetchElement("BODY[" + parameter + "]", parameter), true); + } else { throw new ProtocolException( "Invalid fetch attibute: " + name + "[]" ); } } @@ -482,75 +494,46 @@ } - private static class FetchRequest { - private ArrayList elements = new ArrayList(); - List getElements() { - return Collections.unmodifiableList( elements ); - } - void addElement( FetchElement element ) - { - if ( element == FetchElement.ALL ) { - elements.add( FetchElement.FLAGS ); - elements.add( FetchElement.INTERNALDATE ); - elements.add( FetchElement.RFC822_SIZE ); - elements.add( FetchElement.ENVELOPE ); - } - else if ( element == FetchElement.FAST ) { - elements.add( FetchElement.FLAGS ); - elements.add( FetchElement.INTERNALDATE ); - elements.add( FetchElement.RFC822_SIZE ); - } - else if ( element == FetchElement.FULL ) { - elements.add( FetchElement.FLAGS ); - elements.add( FetchElement.INTERNALDATE ); - elements.add( FetchElement.RFC822_SIZE ); - elements.add( FetchElement.ENVELOPE ); - elements.add( FetchElement.BODY ); - } - else { - elements.add( element ); - } + boolean flags; + boolean uid; + boolean internalDate; + boolean size; + boolean envelope; + boolean body; + boolean bodyStructure; + + private boolean setSeen = false; + + private Set bodyElements = new HashSet(); + + public Collection getBodyElements() { + return bodyElements; } - } - private static class FetchElement - { - public static final FetchElement FLAGS = new FetchElement( "FLAGS" ); - public static final FetchElement INTERNALDATE = new FetchElement( "INTERNALDATE" ); - public static final FetchElement RFC822_SIZE= new FetchElement( "RFC822.SIZE" ); - public static final FetchElement ENVELOPE = new FetchElement( "ENVELOPE" ); - public static final FetchElement BODY = new FetchElement( "BODY" ); - public static final FetchElement BODYSTRUCTURE = new FetchElement( "BODYSTRUCTURE" ); - public static final FetchElement UID = new FetchElement( "UID" ); - - public static final FetchElement FAST = new FetchElement( "FAST" ); - public static final FetchElement FULL = new FetchElement( "FULL" ); - public static final FetchElement ALL = new FetchElement( "ALL" ); - - String name; - - protected FetchElement( String name ) - { - this.name = name; + public boolean isSetSeen() { + return setSeen; } - public String getResponseName() { - return this.name; + public void add( BodyFetchElement element, boolean peek ) + { + if (!peek) { + setSeen = true; + } + bodyElements.add(element); } } - private class BodyFetchElement extends FetchElement + private class BodyFetchElement { - private boolean peek; + private String name; private String sectionIdentifier; - public BodyFetchElement( String name, String sectionIdentifier, boolean peek ) + public BodyFetchElement( String name, String sectionIdentifier) { - super( name ); + this.name = name; this.sectionIdentifier = sectionIdentifier; - this.peek = peek; } public String getParameters() @@ -558,9 +541,8 @@ return this.sectionIdentifier; } - public boolean isPeek() - { - return this.peek; + public String getResponseName() { + return this.name; } } 1.3 +1 -1 james-server/proposals/imap2/test/org/apache/james/imapserver/FetchMultipleMessages.test Index: FetchMultipleMessages.test =================================================================== RCS file: /home/cvs/james-server/proposals/imap2/test/org/apache/james/imapserver/FetchMultipleMessages.test,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- FetchMultipleMessages.test 13 Jul 2003 06:04:57 -0000 1.2 +++ FetchMultipleMessages.test 30 Nov 2003 10:48:47 -0000 1.3 @@ -6,7 +6,7 @@ S: f1 OK FETCH completed # Fetch 3 messages with BODY -C: f1 FETCH 1:3 (BODY[HEADER.FIELDS (Subject)]) +C: f1 FETCH 1:3 (BODY.PEEK[HEADER.FIELDS (Subject)]) S: \* 1 FETCH \(BODY\[HEADER\.FIELDS \(Subject\)\] \{20\} S: Subject\: Test 01 S: 1.3 +2 -1 james-server/proposals/imap2/test/org/apache/james/imapserver/FetchSingleMessage.test Index: FetchSingleMessage.test =================================================================== RCS file: /home/cvs/james-server/proposals/imap2/test/org/apache/james/imapserver/FetchSingleMessage.test,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- FetchSingleMessage.test 3 Dec 2002 14:00:13 -0000 1.2 +++ FetchSingleMessage.test 30 Nov 2003 10:48:47 -0000 1.3 @@ -5,8 +5,9 @@ S: f1 OK FETCH completed # BODY[] +# Not PEEK, so the Seen flag is implicitly set. C: f1 FETCH 1 (BODY[]) -S: \* 1 FETCH \(BODY\[\] \{255\} +S: \* 1 FETCH \(FLAGS \(\\Seen\) BODY\[\] \{255\} S: Date: Mon, 7 Feb 1994 21:52:25 -0800 \(PST\) S: From: Fred Foobar <[EMAIL PROTECTED]> S: Subject: Test 01 1.2 +1 -0 james-server/proposals/imap2/test/org/apache/james/imapserver/concurrent/Concurrent.todo.txt Index: Concurrent.todo.txt =================================================================== RCS file: /home/cvs/james-server/proposals/imap2/test/org/apache/james/imapserver/concurrent/Concurrent.todo.txt,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- Concurrent.todo.txt 26 Nov 2003 14:18:38 -0000 1.1 +++ Concurrent.todo.txt 30 Nov 2003 10:48:47 -0000 1.2 @@ -5,6 +5,7 @@ (concurrent/ExistsResponse.test) - still need RECENT 3 Expunge response when another session performs a EXPUNGE against the mailbox +FETCH should set the /SEEN flag, and this should be propogated to other sessions. From RFC2180 1.3 +0 -4 james-server/proposals/imap2/test/org/apache/james/imapserver/concurrent/FetchResponse.test Index: FetchResponse.test =================================================================== RCS file: /home/cvs/james-server/proposals/imap2/test/org/apache/james/imapserver/concurrent/FetchResponse.test,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- FetchResponse.test 30 Nov 2003 07:03:49 -0000 1.2 +++ FetchResponse.test 30 Nov 2003 10:48:47 -0000 1.3 @@ -1,9 +1,5 @@ # Tests that updates made by one session trigger a fetch response # in a concurrent session on the same mailbox - -# TODO: Check sending of fetch responses when using other selected state commands. -# eg FETCH, STORE, COPY... -# TODO: make sure SILENT is only effective on calling mailbox (not other listeners) SESSION: 1 C: 1a CREATE multibox S: 1a OK CREATE completed.
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]