View Javadoc

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