blob: 80cf50e341ceb495ff63f78b17015d67d72581af [file] [log] [blame]
/**
* 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.geronimo.javamail.store.imap.connection;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.mail.MessagingException;
import javax.mail.internet.ContentDisposition;
import javax.mail.internet.ContentType;
import org.apache.geronimo.javamail.util.ResponseFormatException;
public class IMAPBodyStructure extends IMAPFetchDataItem {
// the MIME type information
public ContentType mimeType = new ContentType();
// the content disposition info
public ContentDisposition disposition = null;
// the message ID
public String contentID;
public String contentDescription;
public String transferEncoding;
// size of the message
public int bodySize;
// number of lines, which only applies to text types.
public int lines = -1;
// "parts is parts". If this is a multipart message, we have a body structure item for each subpart.
public IMAPBodyStructure[] parts;
// optional dispostiion parameters
public Map dispositionParameters;
// language parameters
public List languages;
// the MD5 hash
public String md5Hash;
// references to nested message information.
public IMAPEnvelope nestedEnvelope;
public IMAPBodyStructure nestedBody;
public IMAPBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
super(BODYSTRUCTURE);
parseBodyStructure(source);
}
protected void parseBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
// the body structure needs to start with a left paren
source.checkLeftParen();
// if we start with a parentized item, we have a multipart content type. We need to
// recurse on each of those as appropriate
if (source.peek().getType() == '(') {
parseMultipartBodyStructure(source);
}
else {
parseSinglepartBodyStructure(source);
}
}
protected void parseMultipartBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
mimeType.setPrimaryType("multipart");
ArrayList partList = new ArrayList();
do {
// parse the subpiece (which might also be a multipart).
IMAPBodyStructure part = new IMAPBodyStructure(source);
partList.add(part);
// we keep doing this as long as we seen parenthized items.
} while (source.peek().getType() == '(');
parts = (IMAPBodyStructure[])partList.toArray(new IMAPBodyStructure[partList.size()]);
// get the subtype (required)
mimeType.setSubType(source.readString());
// if the next token is the list terminator, we're done. Otherwise, we need to read extension
// data.
if (source.checkListEnd()) {
return;
}
// read the content parameter information and copy into the ContentType.
mimeType.setParameterList(source.readParameterList());
// more optional stuff
if (source.checkListEnd()) {
return;
}
// go parse the extensions that are common to both single- and multi-part messages.
parseMessageExtensions(source);
}
protected void parseSinglepartBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
// get the primary and secondary types.
mimeType.setPrimaryType(source.readString());
mimeType.setSubType(source.readString());
// read the parameters associated with the content type.
mimeType.setParameterList(source.readParameterList());
// now a bunch of string value parameters
contentID = source.readStringOrNil();
contentDescription = source.readStringOrNil();
transferEncoding = source.readStringOrNil();
bodySize = source.readInteger();
// is this an embedded message type? Embedded messages include envelope and body structure
// information for the embedded message next.
if (mimeType.match("message/rfc822")) {
// parse the nested information
nestedEnvelope = new IMAPEnvelope(source);
nestedBody = new IMAPBodyStructure(source);
lines = source.readInteger();
}
// text types include a line count
else if (mimeType.match("text/*")) {
lines = source.readInteger();
}
// now the optional extension data. All of these are optional, but must be in the specified order.
if (source.checkListEnd()) {
return;
}
md5Hash = source.readString();
// go parse the extensions that are common to both single- and multi-part messages.
parseMessageExtensions(source);
}
/**
* Parse common message extension information shared between
* single part and multi part messages.
*
* @param source The source tokenizer..
*/
protected void parseMessageExtensions(IMAPResponseTokenizer source) throws MessagingException {
// now the optional extension data. All of these are optional, but must be in the specified order.
if (source.checkListEnd()) {
return;
}
disposition = new ContentDisposition();
// now the dispostion. This is a string, followed by a parameter list.
if (source.peek(true).getType() == '(') {
source.checkLeftParen();
disposition.setDisposition(source.readString());
disposition.setParameterList(source.readParameterList());
source.checkRightParen();
} else if (source.peek(true) == IMAPResponseTokenizer.NIL) {
source.next();
} else {
throw new ResponseFormatException("Expecting NIL or '(' in response");
}
// once more
if (source.checkListEnd()) {
return;
}
// read the language info.
languages = source.readStringList();
// next is the body location information. The Javamail APIs don't really expose that, so
// we'll just skip over that.
// once more
if (source.checkListEnd()) {
return;
}
// read the location info.
source.readStringList();
// we don't recognize any other forms of extension, so just skip over these.
while (source.notListEnd()) {
source.skipExtensionItem();
}
// step over the closing paren
source.next();
}
/**
* Tests if a body structure is for a multipart body.
*
* @return true if this is a multipart body part, false for a single part.
*/
public boolean isMultipart() {
return parts != null;
}
/**
* Test if this body structure represents an attached message. If it's a
* message, this will be a single part of MIME type message/rfc822.
*
* @return True if this is a nested message type, false for either a multipart or
* a single part of another type.
*/
public boolean isAttachedMessage() {
return !isMultipart() && mimeType.match("message/rfc822");
}
}