1 17 18 package org.apache.james.imapserver.commands; 19 20 import org.apache.excalibur.util.StringUtil; 21 import org.apache.james.core.MimeMessageWrapper; 22 import org.apache.james.imapserver.ImapRequestLineReader; 23 import org.apache.james.imapserver.ImapResponse; 24 import org.apache.james.imapserver.ImapSession; 25 import org.apache.james.imapserver.ProtocolException; 26 import org.apache.james.imapserver.store.MailboxException; 27 import org.apache.james.imapserver.store.MessageFlags; 28 import org.apache.james.imapserver.store.SimpleImapMessage; 29 import org.apache.james.imapserver.store.ImapMailbox; 30 31 import javax.mail.MessagingException; 32 import javax.mail.internet.MimeMessage; 33 import java.io.ByteArrayOutputStream; 34 import java.io.IOException; 35 import java.util.ArrayList; 36 import java.util.Collections; 37 import java.util.Enumeration; 38 import java.util.Iterator; 39 import java.util.List; 40 import java.util.Collection; 41 42 48 class FetchCommand extends SelectedStateCommand implements UidEnabledCommand 49 { 50 public static final String NAME = "FETCH"; 51 public static final String ARGS = "<message-set> <fetch-profile>"; 52 53 private FetchCommandParser parser = new FetchCommandParser(); 54 55 56 protected void doProcess( ImapRequestLineReader request, 57 ImapResponse response, 58 ImapSession session ) 59 throws ProtocolException, MailboxException 60 { 61 doProcess( request, response, session, false ); 62 } 63 64 public void doProcess( ImapRequestLineReader request, 65 ImapResponse response, 66 ImapSession session, 67 boolean useUids ) 68 throws ProtocolException, MailboxException 69 { 70 IdSet idSet = parser.set( request ); 71 FetchRequest fetch = parser.fetchRequest( request ); 72 parser.endLine( request ); 73 74 ImapMailbox mailbox = session.getSelected(); 75 long[] uids = mailbox.getMessageUids(); 76 for ( int i = 0; i < uids.length; i++ ) { 77 long uid = uids[i]; 78 int msn = mailbox.getMsn( uid ); 79 80 if ( ( useUids && idSet.includes( uid ) ) || 81 ( !useUids && idSet.includes( msn ) ) ) 82 { 83 SimpleImapMessage imapMessage = mailbox.getMessage( uid ); 84 String msgData = outputMessage( fetch, imapMessage ); 85 response.fetchResponse( msn, msgData ); 86 } 87 } 88 89 session.unsolicitedResponses( response ); 90 response.commandComplete( this ); 91 } 92 93 private String outputMessage( FetchRequest fetch, SimpleImapMessage message ) 94 throws MailboxException, ProtocolException 95 { 96 97 StringBuffer response = new StringBuffer(); 98 99 List elements = fetch.getElements(); 100 for ( int i = 0; i < elements.size(); i++ ) { 101 FetchElement fetchElement = ( FetchElement ) elements.get( i ); 102 if ( i > 0 ) { 103 response.append( SP ); 104 } 105 response.append( fetchElement.getResponseName() ); 106 response.append( SP ); 107 108 if ( fetchElement == FetchElement.FLAGS ) { 109 MessageFlags flags = message.getFlags(); 110 response.append( flags.format() ); 111 } 112 else if ( fetchElement == FetchElement.INTERNALDATE ) { 113 String internalDate = message.getAttributes().getInternalDateAsString(); 115 response.append( "\"" ); 116 response.append( internalDate ); 117 response.append ( "\"" ); 118 } 119 else if ( fetchElement == FetchElement.RFC822_SIZE ) { 120 response.append( message.getAttributes().getSize() ); 121 } 122 else if ( fetchElement == FetchElement.ENVELOPE ) { 123 response.append( message.getAttributes().getEnvelope() ); 124 } 125 else if ( fetchElement == FetchElement.BODY ) { 126 response.append( message.getAttributes().getBodyStructure( false ) ); 127 } 128 else if ( fetchElement == FetchElement.BODYSTRUCTURE ) { 129 response.append( message.getAttributes().getBodyStructure( true ) ); 130 } 131 else if ( fetchElement == FetchElement.UID ) { 132 response.append( message.getUid() ); 133 } 134 else if ( fetchElement instanceof BodyFetchElement ) { 136 BodyFetchElement bodyFetchElement = (BodyFetchElement)fetchElement; 137 String sectionSpecifier = bodyFetchElement.getParameters(); 138 boolean peek = bodyFetchElement.isPeek(); 139 140 MimeMessage mimeMessage = message.getMimeMessage(); 141 try { 142 handleBodyFetch( mimeMessage, sectionSpecifier, response ); 143 } 144 catch ( MessagingException e ) { 145 throw new MailboxException( e.getMessage() ); 147 } 148 149 if ( ! peek ) { 150 message.getFlags().setSeen( true ); 151 } 153 } 154 else { 155 throw new ProtocolException( "Invalid fetch attribute" ); 156 } 157 } 158 159 return response.toString(); 160 } 161 162 163 private void handleBodyFetch( MimeMessage mimeMessage, 164 String sectionSpecifier, 165 StringBuffer response ) 166 throws ProtocolException, MessagingException 167 { 168 if ( sectionSpecifier.length() == 0 ) { 169 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 171 try { 172 mimeMessage.writeTo(bout); 173 } 174 catch ( IOException e ) { 175 throw new ProtocolException( "Error reading message source" ); 176 } 177 byte[] bytes = bout.toByteArray(); 178 addLiteral( bytes, response ); 179 } 180 else if ( sectionSpecifier.equalsIgnoreCase( "HEADER" ) ) { 181 Enumeration enum = mimeMessage.getAllHeaderLines(); 182 addHeaders( enum, response ); 183 } 184 else if ( sectionSpecifier.startsWith( "HEADER.FIELDS.NOT " ) ) { 185 String[] excludeNames = extractHeaderList( sectionSpecifier, "HEADER.FIELDS.NOT ".length() ); 186 Enumeration enum = mimeMessage.getNonMatchingHeaderLines( excludeNames ); 187 addHeaders( enum, response ); 188 } 189 else if ( sectionSpecifier.startsWith( "HEADER.FIELDS " ) ) { 190 String[] includeNames = extractHeaderList( sectionSpecifier, "HEADER.FIELDS ".length() ); 191 Enumeration enum = mimeMessage.getMatchingHeaderLines( includeNames ); 192 addHeaders( enum, response ); 193 } 194 else if ( sectionSpecifier.equalsIgnoreCase( "MIME" ) ) { 195 throw new ProtocolException( "MIME not yet implemented." ); 197 } 198 else if ( sectionSpecifier.equalsIgnoreCase( "TEXT" ) ) { 199 ByteArrayOutputStream headerOut = new ByteArrayOutputStream(); 203 ByteArrayOutputStream bodyOut = new ByteArrayOutputStream(); 204 try { 205 MimeMessageWrapper.writeTo( mimeMessage, headerOut, bodyOut ); 206 byte[] bytes = bodyOut.toByteArray(); 207 208 addLiteral( bytes, response ); 209 210 } 211 catch ( IOException e ) { 212 throw new ProtocolException( "Error reading message source" ); 213 } 214 } 215 else { 216 int dotPos = sectionSpecifier.indexOf( '.' ); 220 if ( dotPos == -1 ) { 221 throw new ProtocolException( "Malformed fetch attribute: " + sectionSpecifier ); 222 } 223 int partNumber = Integer.parseInt( sectionSpecifier.substring( 0, dotPos ) ); 224 String partSectionSpecifier = sectionSpecifier.substring( dotPos + 1 ); 225 226 throw new ProtocolException( "Mime parts not yet implemented for fetch." ); 231 } 232 233 } 234 235 private void addLiteral( byte[] bytes, StringBuffer response ) 236 { 237 response.append('{' ); 238 response.append( bytes.length + 1 ); 239 response.append( '}' ); 240 response.append( "\r\n" ); 241 242 for ( int i = 0; i < bytes.length; i++ ) { 243 byte b = bytes[i]; 244 response.append((char)b); 245 } 246 } 247 248 private String[] extractHeaderList( String headerList, int prefixLen ) 250 { 251 String tmp = headerList.substring( prefixLen + 1, headerList.length() - 1 ); 253 String[] headerNames = StringUtil.split( tmp, " " ); 254 return headerNames; 255 } 256 257 private void addHeaders( Enumeration enum, StringBuffer response ) 258 { 259 List lines = new ArrayList(); 260 int count = 0; 261 while (enum.hasMoreElements()) { 262 String line = (String)enum.nextElement(); 263 count += line.length() + 2; 264 lines.add(line); 265 } 266 response.append( '{' ); 267 response.append( count + 2 ); 268 response.append( '}' ); 269 response.append("\r\n"); 270 271 Iterator lit = lines.iterator(); 272 while (lit.hasNext()) { 273 String line = (String)lit.next(); 274 response.append( line ); 275 response.append( "\r\n" ); 276 } 277 response.append("\r\n"); 278 } 279 280 281 public String getName() 282 { 283 return NAME; 284 } 285 286 287 public String getArgSyntax() 288 { 289 return ARGS; 290 } 291 292 private class FetchCommandParser extends CommandParser 293 { 294 295 296 public FetchRequest fetchRequest( ImapRequestLineReader request ) 297 throws ProtocolException 298 { 299 FetchRequest fetch = new FetchRequest(); 300 CharacterValidator validator = new NoopCharValidator(); 301 302 char next = nextNonSpaceChar( request ); 303 consumeChar( request, '(' ); 304 305 next = nextNonSpaceChar( request ); 306 while ( next != ')' ) { 307 fetch.addElement( getNextElement( request ) ); 308 next = nextNonSpaceChar( request ); 309 } 310 311 consumeChar(request, ')'); 312 313 return fetch; 314 } 315 316 private FetchElement getNextElement( ImapRequestLineReader request) 317 throws ProtocolException 318 { 319 char next = nextCharInLine( request ); 320 StringBuffer element = new StringBuffer(); 321 while ( next != ' ' && next != '[' && next != ')' ) { 322 element.append(next); 323 request.consume(); 324 next = nextCharInLine( request ); 325 } 326 String name = element.toString(); 327 if ( next == ' ' || next == ')' ) { 329 if ( "FAST".equalsIgnoreCase( name ) ) { 330 return FetchElement.FAST; 331 } 332 if ( "FULL".equalsIgnoreCase( name ) ) { 333 return FetchElement.FULL; 334 } 335 if ( "ALL".equalsIgnoreCase( name ) ) { 336 return FetchElement.ALL; 337 } 338 if ( "FLAGS".equalsIgnoreCase( name ) ) { 339 return FetchElement.FLAGS; 340 } 341 if ( "RFC822.SIZE".equalsIgnoreCase( name ) ) { 342 return FetchElement.RFC822_SIZE; 343 } 344 if ( "ENVELOPE".equalsIgnoreCase( name ) ) { 345 return FetchElement.ENVELOPE; 346 } 347 if ( "INTERNALDATE".equalsIgnoreCase( name ) ) { 348 return FetchElement.INTERNALDATE; 349 } 350 if ( "BODY".equalsIgnoreCase( name ) ) { 351 return FetchElement.BODY; 352 } 353 if ( "BODYSTRUCTURE".equalsIgnoreCase( name ) ) { 354 return FetchElement.BODYSTRUCTURE; 355 } 356 if ( "UID".equalsIgnoreCase( name ) ) { 357 return FetchElement.UID; 358 } 359 if ( "RFC822".equalsIgnoreCase( name ) ) { 360 return new BodyFetchElement( "RFC822", "", false ); 361 } 362 if ( "RFC822.HEADER".equalsIgnoreCase( name ) ) { 363 return new BodyFetchElement( "RFC822.HEADER", "HEADER", true ); 364 } 365 if ( "RFC822.TEXT".equalsIgnoreCase( name ) ) { 366 return new BodyFetchElement( "RFC822.TEXT", "TEXT", false ); 367 } 368 else { 369 throw new ProtocolException( "Invalid fetch attribute: " + name ); 370 } 371 } 372 else { 373 consumeChar( request, '[' ); 374 375 StringBuffer sectionIdentifier = new StringBuffer(); 376 next = nextCharInLine( request ); 377 while ( next != ']' ) { 378 sectionIdentifier.append( next ); 379 request.consume(); 380 next = nextCharInLine(request); 381 } 382 consumeChar( request, ']' ); 383 384 String parameter = sectionIdentifier.toString(); 385 386 if ( "BODY".equalsIgnoreCase( name ) ) { 387 return new BodyFetchElement( "BODY[" + parameter + "]", parameter, false ); 388 } 389 if ( "BODY.PEEK".equalsIgnoreCase( name ) ) { 390 return new BodyFetchElement( "BODY[" + parameter + "]", parameter, true ); 391 } 392 else { 393 throw new ProtocolException( "Invalid fetch attibute: " + name + "[]" ); 394 } 395 } 396 } 397 398 private char nextCharInLine( ImapRequestLineReader request ) 399 throws ProtocolException 400 { 401 char next = request.nextChar(); 402 if ( next == '\r' || next == '\n' ) { 403 throw new ProtocolException( "Unexpected end of line." ); 404 } 405 return next; 406 } 407 408 private char nextNonSpaceChar( ImapRequestLineReader request ) 409 throws ProtocolException 410 { 411 char next = request.nextChar(); 412 while ( next == ' ' ) { 413 request.consume(); 414 next = request.nextChar(); 415 } 416 return next; 417 } 418 419 } 420 421 422 private static class FetchRequest 423 { 424 private ArrayList elements = new ArrayList(); 425 List getElements() { 426 return Collections.unmodifiableList( elements ); 427 } 428 void addElement( FetchElement element ) 429 { 430 if ( element == FetchElement.ALL ) { 431 elements.add( FetchElement.FLAGS ); 432 elements.add( FetchElement.INTERNALDATE ); 433 elements.add( FetchElement.RFC822_SIZE ); 434 elements.add( FetchElement.ENVELOPE ); 435 } 436 else if ( element == FetchElement.FAST ) { 437 elements.add( FetchElement.FLAGS ); 438 elements.add( FetchElement.INTERNALDATE ); 439 elements.add( FetchElement.RFC822_SIZE ); 440 } 441 else if ( element == FetchElement.FULL ) { 442 elements.add( FetchElement.FLAGS ); 443 elements.add( FetchElement.INTERNALDATE ); 444 elements.add( FetchElement.RFC822_SIZE ); 445 elements.add( FetchElement.ENVELOPE ); 446 elements.add( FetchElement.BODY ); 447 } 448 else { 449 elements.add( element ); 450 } 451 } 452 } 453 454 private static class FetchElement 455 { 456 public static final FetchElement FLAGS = new FetchElement( "FLAGS" ); 457 public static final FetchElement INTERNALDATE = new FetchElement( "INTERNALDATE" ); 458 public static final FetchElement RFC822_SIZE= new FetchElement( "RFC822.SIZE" ); 459 public static final FetchElement ENVELOPE = new FetchElement( "ENVELOPE" ); 460 public static final FetchElement BODY = new FetchElement( "BODY" ); 461 public static final FetchElement BODYSTRUCTURE = new FetchElement( "BODYSTRUCTURE" ); 462 public static final FetchElement UID = new FetchElement( "UID" ); 463 464 public static final FetchElement FAST = new FetchElement( "FAST" ); 465 public static final FetchElement FULL = new FetchElement( "FULL" ); 466 public static final FetchElement ALL = new FetchElement( "ALL" ); 467 468 String name; 469 470 protected FetchElement( String name ) 471 { 472 this.name = name; 473 } 474 475 public String getResponseName() { 476 return this.name; 477 } 478 } 479 480 private class BodyFetchElement extends FetchElement 481 { 482 private boolean peek; 483 private String sectionIdentifier; 484 485 public BodyFetchElement( String name, String sectionIdentifier, boolean peek ) 486 { 487 super( name ); 488 this.sectionIdentifier = sectionIdentifier; 489 this.peek = peek; 490 } 491 492 public String getParameters() 493 { 494 return this.sectionIdentifier; 495 } 496 497 public boolean isPeek() 498 { 499 return this.peek; 500 } 501 } 502 503 } 504 911 | Popular Tags |