1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.geronimo.javamail.store.imap.connection;
19
20 import java.util.ArrayList;
21 import java.util.Date;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25
26 import javax.mail.MessagingException;
27 import javax.mail.internet.ContentDisposition;
28 import javax.mail.internet.ContentType;
29
30
31 public class IMAPBodyStructure extends IMAPFetchDataItem {
32
33 // the MIME type information
34 public ContentType mimeType = new ContentType();
35 // the content disposition info
36 public ContentDisposition disposition = null;
37 // the message ID
38 public String contentID;
39 public String contentDescription;
40 public String transferEncoding;
41 // size of the message
42 public int bodySize;
43 // number of lines, which only applies to text types.
44 public int lines = -1;
45
46 // "parts is parts". If this is a multipart message, we have a body structure item for each subpart.
47 public IMAPBodyStructure[] parts;
48 // optional dispostiion parameters
49 public Map dispositionParameters;
50 // language parameters
51 public List languages;
52 // the MD5 hash
53 public String md5Hash;
54
55 // references to nested message information.
56 public IMAPEnvelope nestedEnvelope;
57 public IMAPBodyStructure nestedBody;
58
59
60 public IMAPBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
61 super(BODYSTRUCTURE);
62 parseBodyStructure(source);
63 }
64
65
66 protected void parseBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
67 // the body structure needs to start with a left paren
68 source.checkLeftParen();
69
70 // if we start with a parentized item, we have a multipart content type. We need to
71 // recurse on each of those as appropriate
72 if (source.peek().getType() == '(') {
73 parseMultipartBodyStructure(source);
74 }
75 else {
76 parseSinglepartBodyStructure(source);
77 }
78 }
79
80
81 protected void parseMultipartBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
82 mimeType.setPrimaryType("multipart");
83 ArrayList partList = new ArrayList();
84
85 do {
86 // parse the subpiece (which might also be a multipart).
87 IMAPBodyStructure part = new IMAPBodyStructure(source);
88 partList.add(part);
89 // we keep doing this as long as we seen parenthized items.
90 } while (source.peek().getType() == '(');
91
92 parts = (IMAPBodyStructure[])partList.toArray(new IMAPBodyStructure[partList.size()]);
93
94 // get the subtype (required)
95 mimeType.setSubType(source.readString());
96
97 if (source.checkListEnd()) {
98 return;
99 }
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