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; 019 020 import java.io.InputStream; 021 import java.io.UnsupportedEncodingException; 022 import java.util.Enumeration; 023 024 import javax.activation.DataHandler; 025 026 import javax.mail.IllegalWriteException; 027 import javax.mail.MessagingException; 028 import javax.mail.Multipart; 029 030 import javax.mail.internet.MimeBodyPart; 031 import javax.mail.internet.MimeUtility; 032 033 import org.apache.geronimo.javamail.store.imap.connection.IMAPBodyStructure; 034 import org.apache.geronimo.javamail.store.imap.connection.IMAPConnection; 035 036 037 public class IMAPMimeBodyPart extends MimeBodyPart { 038 // the message we're part of 039 protected IMAPMessage message; 040 // the retrieved BODYSTRUCTURE information for this part. 041 protected IMAPBodyStructure bodyStructure; 042 // the section identifier. This will be in a format such as 1.2.3, which 043 // would refer to the "third part contained in the second part of the first part"... 044 // got all that? There will be a quiz at the end of class :-) 045 protected String section; 046 // flag to indicate whether the body part headers have been loaded. 047 boolean headersLoaded = false; 048 049 /** 050 * Create an instance of a MimeBodyPart within an 051 * IMAP message. 052 * 053 * @param message The parent Message instance containing this part. 054 * @param bodyStructure 055 * The IMAPBodyStructure information describing the part. 056 * @param section The numeric section identifier string for this part. 057 * This is a hierarchical set of numbers describing 058 * how to navigate to the message part on the IMAP 059 * server. For example, "2.1.3" would be the third 060 * subpart of the first subpart of the second main 061 * message part. 062 */ 063 public IMAPMimeBodyPart(IMAPMessage message, IMAPBodyStructure bodyStructure, String section) { 064 super(); 065 this.message = message; 066 this.bodyStructure = bodyStructure; 067 this.section = section; 068 } 069 070 071 /** 072 * Get the size of the message part. 073 * 074 * @return The size information returned in the IMAP body structure. 075 * @exception MessagingException 076 */ 077 public int getSize() throws MessagingException { 078 return bodyStructure.bodySize; 079 } 080 081 /** 082 * Get the estimated line count for the body part. 083 * 084 * @return The line count information returned by the IMAP 085 * server. 086 * @exception MessagingException 087 */ 088 public int getLineCount() throws MessagingException { 089 return bodyStructure.lines; 090 } 091 092 /** 093 * Get the content type for the body part. 094 * 095 * @return The mimetype for the body part, in string format. 096 * @exception MessagingException 097 */ 098 public String getContentType() throws MessagingException { 099 return bodyStructure.mimeType.toString(); 100 } 101 102 /** 103 * Test if the body part is of a particular MIME type. 104 * 105 * @param type The string MIME-type name. A wild card * can be 106 * specified for the subpart type. 107 * 108 * @return true if the body part matches the give MIME-type. 109 * @exception MessagingException 110 */ 111 public boolean isMimeType(String type) throws MessagingException { 112 return bodyStructure.mimeType.match(type); 113 } 114 115 /** 116 * Retrieve the disposition information about this 117 * body part. 118 * 119 * @return The disposition information, as a string value. 120 * @exception MessagingException 121 */ 122 public String getDisposition() throws MessagingException { 123 return bodyStructure.disposition.getDisposition(); 124 } 125 126 /** 127 * Set the disposition information. The IMAP message 128 * is read-only, so this is an error. 129 * 130 * @param disposition 131 * The disposition string. 132 * 133 * @exception MessagingException 134 */ 135 public void setDisposition(String disposition) throws MessagingException { 136 throw new IllegalWriteException("IMAP message parts are read-only"); 137 } 138 139 public String getEncoding() throws MessagingException { 140 return bodyStructure.transferEncoding; 141 } 142 143 public String getContentID() throws MessagingException { 144 return bodyStructure.contentID; 145 } 146 147 public void setContentID(String id) throws MessagingException { 148 throw new IllegalWriteException("IMAP message parts are read-only"); 149 } 150 151 public String getContentMD5() throws MessagingException { 152 return bodyStructure.md5Hash; 153 } 154 155 public void setContentMD5(String id) throws MessagingException { 156 throw new IllegalWriteException("IMAP message parts are read-only"); 157 } 158 159 public String getDescription() throws MessagingException { 160 String description = bodyStructure.contentDescription; 161 if (description != null) { 162 try { 163 // this could be both folded and encoded. Return this to usable form. 164 return MimeUtility.decodeText(MimeUtility.unfold(description)); 165 } catch (UnsupportedEncodingException e) { 166 // ignore 167 } 168 } 169 // return the raw version for any errors (this might be null also) 170 return description; 171 } 172 173 public void setDescription(String d, String charset) throws MessagingException { 174 throw new IllegalWriteException("IMAP message parts are read-only"); 175 } 176 177 public String getFileName() throws MessagingException { 178 String filename = bodyStructure.disposition.getParameter("filename"); 179 if (filename == null) { 180 filename = bodyStructure.mimeType.getParameter("name"); 181 } 182 return filename; 183 } 184 185 public void setFileName(String name) throws MessagingException { 186 throw new IllegalWriteException("IMAP message parts are read-only"); 187 } 188 189 protected InputStream getContentStream() throws MessagingException { 190 191 // no content loaded yet? 192 if (content == null) { 193 // make sure we're still valid 194 message.checkValidity(); 195 // make sure the content is fully loaded 196 loadContent(); 197 } 198 199 // allow the super class to handle creating it from the loaded content. 200 return super.getContentStream(); 201 } 202 203 204 /** 205 * Create the DataHandler object for this message. 206 * 207 * @return The DataHandler object that processes the content set for this 208 * message. 209 * @exception MessagingException 210 */ 211 public synchronized DataHandler getDataHandler() throws MessagingException { 212 if (dh == null) { 213 // are we working with a multipart message here? 214 if (bodyStructure.isMultipart()) { 215 dh = new DataHandler(new IMAPMultipartDataSource(message, this, section, bodyStructure)); 216 return dh; 217 } 218 else if (bodyStructure.isAttachedMessage()) { 219 dh = new DataHandler(new IMAPAttachedMessage(message, section, bodyStructure.nestedEnvelope, 220 bodyStructure.nestedBody), bodyStructure.mimeType.toString()); 221 return dh; 222 } 223 } 224 225 // single part messages get handled the normal way. 226 return super.getDataHandler(); 227 } 228 229 public void setDataHandler(DataHandler content) throws MessagingException { 230 throw new IllegalWriteException("IMAP body parts are read-only"); 231 } 232 233 public void setContent(Object o, String type) throws MessagingException { 234 throw new IllegalWriteException("IMAP body parts are read-only"); 235 } 236 237 public void setContent(Multipart mp) throws MessagingException { 238 throw new IllegalWriteException("IMAP body parts are read-only"); 239 } 240 241 242 /****************************************************************** 243 * Following is a set of methods that deal with headers 244 * These methods are just overrides on the superclass methods to 245 * allow lazy loading of the header information. 246 ********************************************************************/ 247 248 public String[] getHeader(String name) throws MessagingException { 249 loadHeaders(); 250 return headers.getHeader(name); 251 } 252 253 public String getHeader(String name, String delimiter) throws MessagingException { 254 loadHeaders(); 255 return headers.getHeader(name, delimiter); 256 } 257 258 public Enumeration getAllHeaders() throws MessagingException { 259 loadHeaders(); 260 return headers.getAllHeaders(); 261 } 262 263 public Enumeration getMatchingHeaders(String[] names) throws MessagingException { 264 loadHeaders(); 265 return headers.getMatchingHeaders(names); 266 } 267 268 public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException { 269 loadHeaders(); 270 return headers.getNonMatchingHeaders(names); 271 } 272 273 public Enumeration getAllHeaderLines() throws MessagingException { 274 loadHeaders(); 275 return headers.getAllHeaderLines(); 276 } 277 278 public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException { 279 loadHeaders(); 280 return headers.getMatchingHeaderLines(names); 281 } 282 283 public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException { 284 loadHeaders(); 285 return headers.getNonMatchingHeaderLines(names); 286 } 287 288 // the following are overrides for header modification methods. These messages are read only, 289 // so the headers cannot be modified. 290 public void addHeader(String name, String value) throws MessagingException { 291 throw new IllegalWriteException("IMAP messages are read-only"); 292 } 293 294 public void setHeader(String name, String value) throws MessagingException { 295 throw new IllegalWriteException("IMAP messages are read-only"); 296 } 297 298 299 public void removeHeader(String name) throws MessagingException { 300 throw new IllegalWriteException("IMAP messages are read-only"); 301 } 302 303 public void addHeaderLine(String line) throws MessagingException { 304 throw new IllegalWriteException("IMAP messages are read-only"); 305 } 306 307 308 /** 309 * Load the mime part headers into this body part. 310 * 311 * @exception MessagingException 312 */ 313 protected synchronized void loadHeaders() throws MessagingException { 314 // have them already? Super.. 315 if (headers != null) { 316 return; 317 } 318 319 IMAPConnection connection = message.getConnection(); 320 try { 321 // this asks for the MIME subsection of the given section piece. 322 headers = connection.fetchHeaders(message.getSequenceNumber(), section); 323 } finally { 324 message.releaseConnection(connection); 325 } 326 327 } 328 329 330 /** 331 * Load the message content into the BodyPart object. 332 * 333 * @exception MessagingException 334 */ 335 protected void loadContent() throws MessagingException { 336 // if we've loaded this already, just return 337 if (content != null) { 338 return; 339 } 340 341 IMAPConnection connection = message.getConnection(); 342 try { 343 // load the content from the server. 344 content = connection.fetchContent(message.getSequenceNumber(), section); 345 } finally { 346 message.releaseConnection(connection); 347 } 348 } 349 } 350