001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.geronimo.javamail.store.imap.connection; 019 020 import java.util.ArrayList; 021 import java.util.Date; 022 import java.util.Iterator; 023 import java.util.List; 024 import java.util.Map; 025 026 import javax.mail.MessagingException; 027 import javax.mail.internet.ContentDisposition; 028 import javax.mail.internet.ContentType; 029 030 031 public class IMAPBodyStructure extends IMAPFetchDataItem { 032 033 // the MIME type information 034 public ContentType mimeType = new ContentType(); 035 // the content disposition info 036 public ContentDisposition disposition = null; 037 // the message ID 038 public String contentID; 039 public String contentDescription; 040 public String transferEncoding; 041 // size of the message 042 public int bodySize; 043 // number of lines, which only applies to text types. 044 public int lines = -1; 045 046 // "parts is parts". If this is a multipart message, we have a body structure item for each subpart. 047 public IMAPBodyStructure[] parts; 048 // optional dispostiion parameters 049 public Map dispositionParameters; 050 // language parameters 051 public List languages; 052 // the MD5 hash 053 public String md5Hash; 054 055 // references to nested message information. 056 public IMAPEnvelope nestedEnvelope; 057 public IMAPBodyStructure nestedBody; 058 059 060 public IMAPBodyStructure(IMAPResponseTokenizer source) throws MessagingException { 061 super(BODYSTRUCTURE); 062 parseBodyStructure(source); 063 } 064 065 066 protected void parseBodyStructure(IMAPResponseTokenizer source) throws MessagingException { 067 // the body structure needs to start with a left paren 068 source.checkLeftParen(); 069 070 // if we start with a parentized item, we have a multipart content type. We need to 071 // recurse on each of those as appropriate 072 if (source.peek().getType() == '(') { 073 parseMultipartBodyStructure(source); 074 } 075 else { 076 parseSinglepartBodyStructure(source); 077 } 078 } 079 080 081 protected void parseMultipartBodyStructure(IMAPResponseTokenizer source) throws MessagingException { 082 mimeType.setPrimaryType("multipart"); 083 ArrayList partList = new ArrayList(); 084 085 do { 086 // parse the subpiece (which might also be a multipart). 087 IMAPBodyStructure part = new IMAPBodyStructure(source); 088 partList.add(part); 089 // we keep doing this as long as we seen parenthized items. 090 } while (source.peek().getType() == '('); 091 092 parts = (IMAPBodyStructure[])partList.toArray(new IMAPBodyStructure[partList.size()]); 093 094 // get the subtype (required) 095 mimeType.setSubType(source.readString()); 096 097 if (source.checkListEnd()) { 098 return; 099 } 100 // if the next token is the list terminator, we're done. Otherwise, we need to read extension 101 // data. 102 if (source.checkListEnd()) { 103 return; 104 } 105 // read the content parameter information and copy into the ContentType. 106 mimeType.setParameterList(source.readParameterList()); 107 108 // more optional stuff 109 if (source.checkListEnd()) { 110 return; 111 } 112 113 // go parse the extensions that are common to both single- and multi-part messages. 114 parseMessageExtensions(source); 115 } 116 117 118 protected void parseSinglepartBodyStructure(IMAPResponseTokenizer source) throws MessagingException { 119 // get the primary and secondary types. 120 mimeType.setPrimaryType(source.readString()); 121 mimeType.setSubType(source.readString()); 122 123 // read the parameters associated with the content type. 124 mimeType.setParameterList(source.readParameterList()); 125 126 // now a bunch of string value parameters 127 contentID = source.readStringOrNil(); 128 contentDescription = source.readStringOrNil(); 129 transferEncoding = source.readStringOrNil(); 130 bodySize = source.readInteger(); 131 132 // is this an embedded message type? Embedded messages include envelope and body structure 133 // information for the embedded message next. 134 if (mimeType.match("message/rfc822")) { 135 // parse the nested information 136 nestedEnvelope = new IMAPEnvelope(source); 137 nestedBody = new IMAPBodyStructure(source); 138 lines = source.readInteger(); 139 } 140 // text types include a line count 141 else if (mimeType.match("text/*")) { 142 lines = source.readInteger(); 143 } 144 145 // now the optional extension data. All of these are optional, but must be in the specified order. 146 if (source.checkListEnd()) { 147 return; 148 } 149 150 md5Hash = source.readString(); 151 152 // go parse the extensions that are common to both single- and multi-part messages. 153 parseMessageExtensions(source); 154 } 155 156 /** 157 * Parse common message extension information shared between 158 * single part and multi part messages. 159 * 160 * @param source The source tokenizer.. 161 */ 162 protected void parseMessageExtensions(IMAPResponseTokenizer source) throws MessagingException { 163 164 // now the optional extension data. All of these are optional, but must be in the specified order. 165 if (source.checkListEnd()) { 166 return; 167 } 168 169 disposition = new ContentDisposition(); 170 // now the dispostion. This is a string, followed by a parameter list. 171 disposition.setDisposition(source.readString()); 172 disposition.setParameterList(source.readParameterList()); 173 174 // once more 175 if (source.checkListEnd()) { 176 return; 177 } 178 // read the language info. 179 languages = source.readStringList(); 180 // next is the body location information. The Javamail APIs don't really expose that, so 181 // we'll just skip over that. 182 183 // once more 184 if (source.checkListEnd()) { 185 return; 186 } 187 // read the location info. 188 source.readStringList(); 189 190 191 // we don't recognize any other forms of extension, so just skip over these. 192 while (source.notListEnd()) { 193 source.skipExtensionItem(); 194 } 195 } 196 197 198 /** 199 * Tests if a body structure is for a multipart body. 200 * 201 * @return true if this is a multipart body part, false for a single part. 202 */ 203 public boolean isMultipart() { 204 return parts != null; 205 } 206 207 208 /** 209 * Test if this body structure represents an attached message. If it's a 210 * message, this will be a single part of MIME type message/rfc822. 211 * 212 * @return True if this is a nested message type, false for either a multipart or 213 * a single part of another type. 214 */ 215 public boolean isAttachedMessage() { 216 return !isMultipart() && mimeType.match("message/rfc822"); 217 } 218 } 219