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