geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rickmcgu...@apache.org
Subject svn commit: r594520 [3/10] - in /geronimo/javamail/trunk/geronimo-javamail_1.4: ./ geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/authentication/ geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/handl...
Date Tue, 13 Nov 2007 12:57:53 GMT
Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPFolder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPFolder.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPFolder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMessage.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMessage.java?rev=594520&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMessage.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMessage.java Tue Nov 13 04:57:39 2007
@@ -0,0 +1,1298 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.imap;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.activation.DataHandler;
+
+import javax.mail.Address; 
+import javax.mail.FetchProfile;
+import javax.mail.Flags;
+import javax.mail.Folder;
+import javax.mail.Header;
+import javax.mail.IllegalWriteException;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.MessageRemovedException;
+import javax.mail.Session;
+import javax.mail.Store;
+import javax.mail.UIDFolder;
+import javax.mail.event.MessageChangedEvent;
+
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.InternetHeaders;
+import javax.mail.internet.MailDateFormat;     
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeUtility;
+
+import org.apache.geronimo.javamail.store.imap.connection.IMAPBody; 
+import org.apache.geronimo.javamail.store.imap.connection.IMAPBodyStructure; 
+import org.apache.geronimo.javamail.store.imap.connection.IMAPConnection; 
+import org.apache.geronimo.javamail.store.imap.connection.IMAPEnvelope;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPFetchDataItem;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPFetchResponse;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPInternalDate;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPInternetHeader;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPMessageSize;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPUid;
+
+/**
+ * IMAP implementation of javax.mail.internet.MimeMessage
+ *
+ * Only the most basic information is given and
+ * Message objects created here is a light-weight reference to the actual Message
+ * As per the JavaMail spec items from the actual message will get filled up on demand
+ *
+ * If some other items are obtained from the server as a result of one call, then the other
+ * details are also processed and filled in. For ex if RETR is called then header information
+ * will also be processed in addition to the content
+ *
+ * @version $Rev$ $Date$
+ */
+public class IMAPMessage extends MimeMessage {
+    // the Store we're stored in (which manages the connection and other stuff).
+    protected IMAPStore store;
+
+    // the IMAP server sequence number (potentially updated during the life of this message object).
+    protected int sequenceNumber;
+    // the IMAP uid value;
+    protected long uid = -1;
+    // the section identifier.  This is only really used for nested messages.  The toplevel version  
+    // will be null, and each nested message will set the appropriate part identifier 
+    protected String section; 
+    // the loaded message envelope (delayed until needed)
+    protected IMAPEnvelope envelope;
+    // the body structure information (also lazy loaded).
+    protected IMAPBodyStructure bodyStructure;
+    // the IMAP INTERNALDATE value.
+    protected Date receivedDate;
+    // the size item, which is maintained separately from the body structure 
+    // as it can be retrieved without getting the body structure
+    protected int size; 
+    // turned on once we've requested the entire header set. 
+    protected boolean allHeadersRetrieved = false; 
+    // singleton date formatter for this class.
+    static protected MailDateFormat dateFormat = new MailDateFormat();
+
+
+    /**
+     * Contruct an IMAPMessage instance.
+     * 
+     * @param folder   The hosting folder for the message.
+     * @param store    The Store owning the article (and folder).
+     * @param msgnum   The article message number.  This is assigned by the Folder, and is unique
+     *                 for each message in the folder.  The message numbers are only valid
+     *                 as long as the Folder is open.
+     * @param sequenceNumber The IMAP server manages messages by sequence number, which is subject to
+     *                 change whenever messages are expunged.  This is the server retrieval number
+     *                 of the message, which needs to be synchronized with status updates
+     *                 sent from the server.
+     * 
+     * @exception MessagingException
+     */
+	IMAPMessage(IMAPFolder folder, IMAPStore store, int msgnum, int sequenceNumber) {
+		super(folder, msgnum);
+        this.sequenceNumber = sequenceNumber;
+		this.store = store;
+        // The default constructor creates an empty Flags item.  We need to clear this out so we 
+        // know if the flags need to be fetched from the server when requested.  
+        flags = null;
+        // make sure this is a totally fresh set of headers.  We'll fill things in as we retrieve them.
+        headers = new InternetHeaders();
+	}
+
+
+    /**
+     * Override for the Message class setExpunged() method to allow
+     * us to do additional cleanup for expunged messages.
+     *
+     * @param value  The new expunge setting.
+     */
+    public void setExpunged(boolean value) {
+        // super class handles most of the details
+        super.setExpunged(value);
+        // if we're now expunged, this removes us from the server message sequencing scheme, so
+        // we need to invalidate the sequence number.
+        if (isExpunged()) {
+            sequenceNumber = -1;
+        }
+    }
+    
+
+    /**
+     * Return a copy the flags associated with this message.
+     *
+     * @return a copy of the flags for this message
+     * @throws MessagingException if there was a problem accessing the Store
+     */
+    public synchronized Flags getFlags() throws MessagingException {
+        // load the flags, if needed 
+        loadFlags(); 
+        return super.getFlags(); 
+    }
+
+
+    /**
+     * Check whether the supplied flag is set.
+     * The default implementation checks the flags returned by {@link #getFlags()}.
+     *
+     * @param flag the flags to check for
+     * @return true if the flags is set
+     * @throws MessagingException if there was a problem accessing the Store
+     */
+    public synchronized boolean isSet(Flags.Flag flag) throws MessagingException {
+        // load the flags, if needed 
+        loadFlags(); 
+        return super.isSet(flag); 
+    }
+
+    /**
+     * Set or clear a flag value.
+     *
+     * @param flags  The set of flags to effect.
+     * @param set    The value to set the flag to (true or false).
+     *
+     * @exception MessagingException
+     */
+    public synchronized void setFlags(Flags flag, boolean set) throws MessagingException {
+        // make sure this is in a valid state.
+        checkValidity();
+        
+        // we need to ensure that we're the only ones with access to the folder's 
+        // message cache any time we need to talk to the server.  This needs to be 
+        // held until after we release the connection so that any pending EXPUNGE 
+        // untagged responses are processed before the next time the folder connection is 
+        // used. 
+        synchronized (folder) {
+            IMAPConnection connection = getConnection();
+
+            try {
+                // set the flags for this item and update the 
+                // internal state with the new values returned from the 
+                // server. 
+                flags = connection.setFlags(sequenceNumber, flag, set); 
+            } finally {
+                releaseConnection(connection); 
+            }
+        }
+    }
+
+
+    /**
+     * Return an InputStream instance for accessing the 
+     * message content.
+     * 
+     * @return An InputStream instance for accessing the content
+     *         (body) of the message.
+     * @exception MessagingException
+     * @see javax.mail.internet.MimeMessage#getContentStream()
+     */
+	protected InputStream getContentStream() throws MessagingException {
+
+        // no content loaded yet?
+        if (content == null) {
+            // make sure we're still valid
+            checkValidity();
+            // make sure the content is fully loaded
+            loadContent();
+        }
+
+        // allow the super class to handle creating it from the loaded content.
+        return super.getContentStream();
+	}
+
+
+    /**
+     * Write out the byte data to the provided output stream.
+     *
+     * @param out    The target stream.
+     *
+     * @exception IOException
+     * @exception MessagingException
+     */
+    public void writeTo(OutputStream out) throws IOException, MessagingException {
+        // TODO:  The connection is shared on a folder basis, so some of these operations
+        // will need to use a common locking object.
+        // make sure we're still good
+        checkValidity();
+        
+        // we need to ensure that we're the only ones with access to the folder's 
+        // message cache any time we need to talk to the server.  This needs to be 
+        // held until after we release the connection so that any pending EXPUNGE 
+        // untagged responses are processed before the next time the folder connection is 
+        // used. 
+        synchronized (folder) {
+            IMAPConnection connection = getConnection();
+            try {
+                IMAPBody body = connection.fetchBody(sequenceNumber, section);
+                byte[] outData = body.getContent();
+
+                // write out the data.
+                out.write(outData);
+            } finally {
+                releaseConnection(connection); 
+            }
+        }
+    }
+	/******************************************************************
+	 * Following is a set of methods that deal with information in the 
+	 * envelope.  These methods ensure the enveloper is loaded and   
+     * retrieve the information.                         
+	 ********************************************************************/
+     
+
+    /**
+     * Get the message "From" addresses.  This looks first at the
+     * "From" headers, and no "From" header is found, the "Sender"
+     * header is checked.  Returns null if not found.
+     *
+     * @return An array of addresses identifying the message from target.  Returns
+     *         null if this is not resolveable from the headers.
+     * @exception MessagingException
+     */
+    public Address[] getFrom() throws MessagingException {
+        // make sure we've retrieved the envelope information.
+        loadEnvelope();
+        // make sure we return a copy of the array so this can't be changed. 
+        Address[] addresses = envelope.from; 
+        if (addresses == null) {
+            return null;
+        }
+        return (Address[])addresses.clone(); 
+    }
+    
+
+    /**
+     * Return the "Sender" header as an address.
+     *
+     * @return the "Sender" header as an address, or null if not present
+     * @throws MessagingException if there was a problem parsing the header
+     */
+    public Address getSender() throws MessagingException {
+        // make sure we've retrieved the envelope information.
+        loadEnvelope();
+        // make sure we return a copy of the array so this can't be changed. 
+        Address[] addresses = envelope.sender; 
+        if (addresses == null) {
+            return null;
+        }
+        // There's only a single sender, despite IMAP potentially returning a list
+        return addresses[0]; 
+    }
+
+    /**
+     * Gets the recipients by type.  Returns null if there are no
+     * headers of the specified type.  Acceptable RecipientTypes are:
+     *
+     *   javax.mail.Message.RecipientType.TO
+     *   javax.mail.Message.RecipientType.CC
+     *   javax.mail.Message.RecipientType.BCC
+     *   javax.mail.internet.MimeMessage.RecipientType.NEWSGROUPS
+     *
+     * @param type   The message RecipientType identifier.
+     *
+     * @return The array of addresses for the specified recipient types.
+     * @exception MessagingException
+     */
+    public Address[] getRecipients(Message.RecipientType type) throws MessagingException {
+        // make sure we've retrieved the envelope information.
+        loadEnvelope();
+        Address[] addresses = null; 
+        
+        if (type == Message.RecipientType.TO) {
+            addresses = envelope.to; 
+        }
+        else if (type == Message.RecipientType.CC) {
+            addresses = envelope.cc; 
+        }
+        else if (type == Message.RecipientType.BCC) {
+            addresses = envelope.bcc; 
+        }
+        else {
+            // this could be a newsgroup type, which will tickle the message headers. 
+            return super.getRecipients(type);
+        }
+        // make sure we return a copy of the array so this can't be changed. 
+        if (addresses == null) {
+            return null;
+        }
+        return (Address[])addresses.clone(); 
+    }
+
+    /**
+     * Get the ReplyTo address information.  The headers are parsed
+     * using the "mail.mime.address.strict" setting.  If the "Reply-To" header does
+     * not have any addresses, then the value of the "From" field is used.
+     *
+     * @return An array of addresses obtained from parsing the header.
+     * @exception MessagingException
+     */
+    public Address[] getReplyTo() throws MessagingException {
+        // make sure we've retrieved the envelope information.
+        loadEnvelope();
+        // make sure we return a copy of the array so this can't be changed. 
+        Address[] addresses = envelope.replyTo; 
+        if (addresses == null) {
+            return null;
+        }
+        return (Address[])addresses.clone(); 
+    }
+
+    /**
+     * Returns the value of the "Subject" header.  If the subject
+     * is encoded as an RFC 2047 value, the value is decoded before
+     * return.  If decoding fails, the raw string value is
+     * returned.
+     *
+     * @return The String value of the subject field.
+     * @exception MessagingException
+     */
+    public String getSubject() throws MessagingException {
+        // make sure we've retrieved the envelope information.
+        loadEnvelope();
+        
+        if (envelope.subject == null) {
+            return null; 
+        }
+        // the subject could be encoded.  If there is a decoding error, 
+        // return the raw subject string. 
+        try {
+            return MimeUtility.decodeText(envelope.subject); 
+        } catch (UnsupportedEncodingException e) {
+            return envelope.subject; 
+        }
+    }
+
+    /**
+     * Get the value of the "Date" header field.  Returns null if
+     * if the field is absent or the date is not in a parseable format.
+     *
+     * @return A Date object parsed according to RFC 822.
+     * @exception MessagingException
+     */
+    public Date getSentDate() throws MessagingException {
+        // make sure we've retrieved the envelope information.
+        loadEnvelope();
+        // just return that directly 
+        return envelope.date; 
+    }
+
+
+    /**
+     * Get the message received date.
+     *
+     * @return Always returns the formatted INTERNALDATE, if available.
+     * @exception MessagingException
+     */
+    public Date getReceivedDate() throws MessagingException {
+        loadEnvelope();
+        return receivedDate; 
+    }
+
+
+    /**
+     * Retrieve the size of the message content.  The content will
+     * be retrieved from the server, if necessary.
+     *
+     * @return The size of the content.
+     * @exception MessagingException
+     */
+	public int getSize() throws MessagingException {
+        // make sure we've retrieved the envelope information.  We load the 
+        // size when we retrieve that. 
+        loadEnvelope();
+        return size;                          
+	}
+    
+
+    /**
+     * Get a line count for the IMAP message.  This is potentially
+     * stored in the Lines article header.  If not there, we return
+     * a default of -1.
+     *
+     * @return The header line count estimate, or -1 if not retrieveable.
+     * @exception MessagingException
+     */
+    public int getLineCount() throws MessagingException {
+        loadBodyStructure();
+        return bodyStructure.lines; 
+    }
+
+    /**
+     * Return the IMAP in reply to information (retrieved with the
+     * ENVELOPE).
+     *
+     * @return The in reply to String value, if available.
+     * @exception MessagingException
+     */
+    public String getInReplyTo() throws MessagingException {
+        loadEnvelope();
+        return envelope.inReplyTo;
+    }
+
+    /**
+     * Returns the current content type (defined in the "Content-Type"
+     * header.  If not available, "text/plain" is the default.
+     *
+     * @return The String name of the message content type.
+     * @exception MessagingException
+     */
+    public String getContentType() throws MessagingException {
+        loadBodyStructure();
+        return bodyStructure.mimeType.toString(); 
+    }
+
+
+    /**
+     * Tests to see if this message has a mime-type match with the
+     * given type name.
+     *
+     * @param type   The tested type name.
+     *
+     * @return If this is a type match on the primary and secondare portion of the types.
+     * @exception MessagingException
+     */
+    public boolean isMimeType(String type) throws MessagingException {
+        loadBodyStructure();
+        return bodyStructure.mimeType.match(type); 
+    }
+
+    /**
+     * Retrieve the message "Content-Disposition" header field.
+     * This value represents how the part should be represented to
+     * the user.
+     *
+     * @return The string value of the Content-Disposition field.
+     * @exception MessagingException
+     */
+    public String getDisposition() throws MessagingException {
+        loadBodyStructure();
+        if (bodyStructure.disposition != null) {
+            return bodyStructure.disposition.getDisposition(); 
+        }
+        return null; 
+    }
+
+    /**
+     * Decode the Content-Transfer-Encoding header to determine
+     * the transfer encoding type.
+     *
+     * @return The string name of the required encoding.
+     * @exception MessagingException
+     */
+    public String getEncoding() throws MessagingException {
+        loadBodyStructure();
+        return bodyStructure.transferEncoding; 
+    }
+
+    /**
+     * Retrieve the value of the "Content-ID" header.  Returns null
+     * if the header does not exist.
+     *
+     * @return The current header value or null.
+     * @exception MessagingException
+     */
+    public String getContentID() throws MessagingException {
+        loadBodyStructure();
+        return bodyStructure.contentID;         
+    }
+
+    public String getContentMD5() throws MessagingException {
+        loadBodyStructure();
+        return bodyStructure.md5Hash;         
+    }
+    
+    
+    public String getDescription() throws MessagingException {
+        loadBodyStructure();
+        
+        if (bodyStructure.contentDescription == null) {
+            return null; 
+        }
+        // the subject could be encoded.  If there is a decoding error, 
+        // return the raw subject string. 
+        try {
+            return MimeUtility.decodeText(bodyStructure.contentDescription); 
+        } catch (UnsupportedEncodingException e) {
+            return bodyStructure.contentDescription;
+        }
+    }
+
+    /**
+     * Return the content languages associated with this 
+     * message.
+     * 
+     * @return 
+     * @exception MessagingException
+     */
+    public String[] getContentLanguage() throws MessagingException {
+        loadBodyStructure();
+        
+        if (!bodyStructure.languages.isEmpty()) {
+            return (String[])bodyStructure.languages.toArray(new String[bodyStructure.languages.size()]); 
+        }
+        return null; 
+    }
+
+    public String getMessageID() throws MessagingException {
+        loadEnvelope();
+        return envelope.messageID; 
+    }
+    
+    public void setFrom(Address address) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void addFrom(Address[] address) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void setSender(Address address) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void setRecipients(Message.RecipientType type, Address[] addresses) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void setRecipients(Message.RecipientType type, String address) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void addRecipients(Message.RecipientType type, Address[] address) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void setReplyTo(Address[] address) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void setSubject(String subject) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void setSubject(String subject, String charset) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void setSentDate(Date sent) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void setDisposition(String disposition) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void setContentID(String cid) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+    public void setContentMD5(String md5) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+    public void setDescription(String description) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+    public void setDescription(String description, String charset) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+    public void setContentLanguage(String[] languages) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+    
+
+	/******************************************************************
+	 * Following is a set of methods that deal with headers
+	 * These methods are just overrides on the superclass methods to
+     * allow lazy loading of the header information.
+	 ********************************************************************/
+
+	public String[] getHeader(String name) throws MessagingException {
+        loadHeaders();
+		return headers.getHeader(name);
+	}
+
+	public String getHeader(String name, String delimiter) throws MessagingException {
+        loadHeaders();
+		return headers.getHeader(name, delimiter);
+	}
+
+	public Enumeration getAllHeaders() throws MessagingException {
+        loadHeaders();
+		return headers.getAllHeaders();
+	}
+
+	public Enumeration getMatchingHeaders(String[] names)  throws MessagingException {
+        loadHeaders();
+		return headers.getMatchingHeaders(names);
+	}
+
+	public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException {
+        loadHeaders();
+		return headers.getNonMatchingHeaders(names);
+	}
+
+	public Enumeration getAllHeaderLines() throws MessagingException {
+        loadHeaders();
+		return headers.getAllHeaderLines();
+	}
+
+	public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException {
+        loadHeaders();
+		return headers.getMatchingHeaderLines(names);
+	}
+
+	public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException {
+        loadHeaders();
+		return headers.getNonMatchingHeaderLines(names);
+	}
+
+    // the following are overrides for header modification methods.  These messages are read only,
+    // so the headers cannot be modified.
+    public void addHeader(String name, String value) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+    public void setHeader(String name, String value) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+
+    public void removeHeader(String name) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+    public void addHeaderLine(String line) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+	/**
+	 * We cannot modify these messages
+	 */
+	public void saveChanges() throws MessagingException {
+		throw new IllegalWriteException("IMAP messages are read-only");
+	}
+
+
+    /**
+     * Utility method for synchronizing IMAP envelope information and
+     * the message headers.
+     *
+     * @param header    The target header name.
+     * @param addresses The update addresses.
+     */
+    protected void updateHeader(String header, InternetAddress[] addresses) throws MessagingException {
+        if (addresses != null) {
+            headers.addHeader(header, InternetAddress.toString(envelope.from));
+        }
+    }
+
+    /**
+     * Utility method for synchronizing IMAP envelope information and
+     * the message headers.
+     *
+     * @param header    The target header name.
+     * @param address   The update address.
+     */
+    protected void updateHeader(String header, Address address) throws MessagingException {
+        if (address != null) {
+            headers.setHeader(header, address.toString());
+        }
+    }
+
+    /**
+     * Utility method for synchronizing IMAP envelope information and
+     * the message headers.
+     *
+     * @param header    The target header name.
+     * @param value     The update value.
+     */
+    protected void updateHeader(String header, String value) throws MessagingException {
+        if (value != null) {
+            headers.setHeader(header, value);
+        }
+    }
+
+
+    /**
+     * Create the DataHandler object for this message.
+     *
+     * @return The DataHandler object that processes the content set for this
+     *         message.
+     * @exception MessagingException
+     */
+    public synchronized DataHandler getDataHandler() throws MessagingException {
+        // check the validity and make sure we have the body structure information. 
+        checkValidity();
+        loadBodyStructure();
+        if (dh == null) {
+            // are we working with a multipart message here?
+            if (bodyStructure.isMultipart()) {
+                dh = new DataHandler(new IMAPMultipartDataSource(this, this, section, bodyStructure));
+                return dh;
+            }
+            else if (bodyStructure.isAttachedMessage()) {
+                dh = new DataHandler(new IMAPAttachedMessage(this, section, bodyStructure.nestedEnvelope, bodyStructure.nestedBody), 
+                     bodyStructure.mimeType.toString()); 
+                return dh;
+            }
+        }
+
+        // single part messages get handled the normal way.
+        return super.getDataHandler();
+    }
+
+    public void setDataHandler(DataHandler content) throws MessagingException {
+        throw new IllegalWriteException("IMAP body parts are read-only");
+    }
+
+    /**
+     * Update the message headers from an input stream.
+     *
+     * @param in     The InputStream source for the header information.
+     *
+     * @exception MessagingException
+     */
+    public void updateHeaders(InputStream in) throws MessagingException {
+        // wrap a stream around the reply data and read as headers.
+        headers = new InternetHeaders(in);
+        allHeadersRetrieved = true;
+    }
+    
+    /**
+     * Load the flag set for this message from the server. 
+     * 
+     * @exception MessagingeException
+     */
+    public void loadFlags() throws MessagingException {
+        // make sure this is in a valid state.
+        checkValidity();
+        // if the flags are already loaded, nothing to do 
+        if (flags != null) {
+            return; 
+        }
+        // we need to ensure that we're the only ones with access to the folder's 
+        // message cache any time we need to talk to the server.  This needs to be 
+        // held until after we release the connection so that any pending EXPUNGE 
+        // untagged responses are processed before the next time the folder connection is 
+        // used. 
+        synchronized (folder) {
+            IMAPConnection connection = getConnection();
+
+            try {
+                // fetch the flags for this item. 
+                flags = connection.fetchFlags(sequenceNumber); 
+            } finally {
+                releaseConnection(connection); 
+            }
+        }
+    }
+
+
+    /**
+     * Retrieve the message raw message headers from the IMAP server, synchronizing with the existing header set.
+     *
+     * @exception MessagingException
+     */
+    protected synchronized void loadHeaders() throws MessagingException {
+        // don't retrieve if already loaded.
+        if (allHeadersRetrieved) {
+            return;
+        }
+
+        // make sure this is in a valid state.
+        checkValidity();
+        // we need to ensure that we're the only ones with access to the folder's 
+        // message cache any time we need to talk to the server.  This needs to be 
+        // held until after we release the connection so that any pending EXPUNGE 
+        // untagged responses are processed before the next time the folder connection is 
+        // used. 
+        synchronized (folder) {
+            IMAPConnection connection = getConnection();
+
+            try {
+                // get the headers and set 
+                headers = connection.fetchHeaders(sequenceNumber, section);
+                // we have the entire header set, not just a subset. 
+                allHeadersRetrieved = true;                                               
+            } finally {
+                releaseConnection(connection); 
+            }
+        }
+    }
+
+
+    /**
+     * Retrieve the message envelope from the IMAP server, synchronizing the headers with the
+     * information.
+     *
+     * @exception MessagingException
+     */                                
+    protected synchronized void loadEnvelope() throws MessagingException {
+        // don't retrieve if already loaded.
+        if (envelope != null) {
+            return;
+        }
+
+        // make sure this is in a valid state.
+        checkValidity();
+        // we need to ensure that we're the only ones with access to the folder's 
+        // message cache any time we need to talk to the server.  This needs to be 
+        // held until after we release the connection so that any pending EXPUNGE 
+        // untagged responses are processed before the next time the folder connection is 
+        // used. 
+        synchronized (folder) {
+            IMAPConnection connection = getConnection();
+            try {
+                // fetch the envelope information for this
+                List fetches = connection.fetchEnvelope(sequenceNumber);
+                // now process all of the fetch responses before releasing the folder lock.  
+                // it's possible that an unsolicited update on another thread might try to 
+                // make an update, causing a potential deadlock. 
+                for (int i = 0; i < fetches.size(); i++) {
+                    // get the returned data items from each of the fetch responses
+                    // and process. 
+                    IMAPFetchResponse fetch = (IMAPFetchResponse)fetches.get(i);
+                    // update the internal info 
+                    updateMessageInformation(fetch); 
+                }
+            } finally {
+                releaseConnection(connection); 
+            }
+        }
+    }
+
+
+    /**
+     * Retrieve the message envelope from the IMAP server, synchronizing the headers with the
+     * information.
+     *
+     * @exception MessagingException
+     */
+    protected synchronized void updateEnvelope(IMAPEnvelope envelope) throws MessagingException {
+        // set the envelope item 
+        this.envelope = envelope; 
+
+        // copy header type information from the envelope into the headers.
+        updateHeader("From", envelope.from);
+        if (envelope.sender != null) {
+            // we can only have a single sender, even though the envelope theoretically supports more.
+            updateHeader("Sender", envelope.sender[0]);
+        }
+        updateHeader("To", envelope.to);
+        updateHeader("Cc", envelope.cc);
+        updateHeader("Bcc", envelope.bcc);
+        updateHeader("Reply-To", envelope.replyTo);
+        // NB:  This is already in encoded form, if needed.
+        updateHeader("Subject", envelope.subject);
+        updateHeader("Message-ID", envelope.messageID);
+    }
+
+
+    /**
+     * Retrieve the BODYSTRUCTURE information from the IMAP server.
+     *
+     * @exception MessagingException
+     */
+    protected synchronized void loadBodyStructure() throws MessagingException {
+        // don't retrieve if already loaded.
+        if (bodyStructure != null) {
+            return;
+        }
+
+        // make sure this is in a valid state.
+        checkValidity();
+        // we need to ensure that we're the only ones with access to the folder's 
+        // message cache any time we need to talk to the server.  This needs to be 
+        // held until after we release the connection so that any pending EXPUNGE 
+        // untagged responses are processed before the next time the folder connection is 
+        // used. 
+        synchronized (folder) {
+            IMAPConnection connection = getConnection();
+            try {
+                // fetch the envelope information for this
+                bodyStructure = connection.fetchBodyStructure(sequenceNumber);
+                // go update all of the information 
+            } finally {
+                releaseConnection(connection); 
+            }
+            
+            // update this before we release the folder lock so we can avoid 
+            // deadlock. 
+            updateBodyStructure(bodyStructure); 
+        }
+    }
+
+
+    /**
+     * Update the BODYSTRUCTURE information from the IMAP server.
+     *
+     * @exception MessagingException
+     */
+    protected synchronized void updateBodyStructure(IMAPBodyStructure structure) throws MessagingException {
+        // save the reference. 
+        bodyStructure = structure; 
+        // now update various headers with the information from the body structure 
+
+        // now update header information with the body structure data.
+        if (bodyStructure.lines != -1) {
+            updateHeader("Lines", Integer.toString(bodyStructure.lines));
+        }
+
+        // languages are a little more complicated
+        if (bodyStructure.languages != null) {
+            // this is a duplicate of what happens in the super class, but 
+            // the superclass methods call setHeader(), which we override and 
+            // throw an exception for.  We need to set the headers ourselves. 
+            if (bodyStructure.languages.size() == 1) {
+                updateHeader("Content-Language", (String)bodyStructure.languages.get(0));
+            }
+            else {
+                StringBuffer buf = new StringBuffer(bodyStructure.languages.size() * 20);
+                buf.append(bodyStructure.languages.get(0));
+                for (int i = 1; i < bodyStructure.languages.size(); i++) {
+                    buf.append(',').append(bodyStructure.languages.get(i));
+                }
+                updateHeader("Content-Language", buf.toString());
+            }
+        }
+
+        updateHeader("Content-Type", bodyStructure.mimeType.toString());
+        if (bodyStructure.disposition != null) {
+            updateHeader("Content-Disposition", bodyStructure.disposition.toString());
+        }
+
+        updateHeader("Content-Transfer-Encoding", bodyStructure.transferEncoding);
+        updateHeader("Content-ID", bodyStructure.contentID);
+        // NB:  This is already in encoded form, if needed.
+        updateHeader("Content-Description", bodyStructure.contentDescription);
+    }
+
+
+    /**
+     * Load the message content into the Message object.
+     *
+     * @exception MessagingException
+     */
+    protected void loadContent() throws MessagingException {
+        // if we've loaded this already, just return
+        if (content != null) {
+            return;
+        }
+        
+        // we need to ensure that we're the only ones with access to the folder's 
+        // message cache any time we need to talk to the server.  This needs to be 
+        // held until after we release the connection so that any pending EXPUNGE 
+        // untagged responses are processed before the next time the folder connection is 
+        // used. 
+        synchronized (folder) {
+            IMAPConnection connection = getConnection();
+            try {
+                // load the content from the server. 
+                content = connection.fetchContent(getSequenceNumber(), section); 
+            } finally {
+                releaseConnection(connection); 
+            }
+        }
+    }
+
+    
+    /**
+     * Retrieve the sequence number assigned to this message.
+     *
+     * @return The messages assigned sequence number.  This maps back to the server's assigned number for
+     * this message.
+     */
+    int getSequenceNumber() {
+        return sequenceNumber;
+    }
+    
+    /**
+     * Set the sequence number for the message.  This 
+     * is updated whenever messages get expunged from 
+     * the folder. 
+     * 
+     * @param s      The new sequence number.
+     */
+    void setSequenceNumber(int s) {
+        sequenceNumber = s; 
+    }
+
+
+    /**
+     * Retrieve the message UID value.
+     *
+     * @return The assigned UID value, if we have the information.
+     */
+    long getUID() {
+        return uid;
+    }
+
+    /**
+     * Set the message UID value.
+     *
+     * @param uid    The new UID value.
+     */
+    void setUID(long uid) {
+        this.uid = uid;
+    }
+
+    
+    /**
+     * get the current connection pool attached to the folder.  We need
+     * to do this dynamically, to A) ensure we're only accessing an
+     * currently open folder, and B) to make sure we're using the
+     * correct connection attached to the folder.
+     *
+     * @return A connection attached to the hosting folder.
+     */
+    protected IMAPConnection getConnection() throws MessagingException {
+        // the folder owns everything.
+        return ((IMAPFolder)folder).getMessageConnection();
+    }
+    
+    /**
+     * Release the connection back to the Folder after performing an operation 
+     * that requires a connection.
+     * 
+     * @param connection The previously acquired connection.
+     */
+    protected void releaseConnection(IMAPConnection connection) throws MessagingException {
+        // the folder owns everything.
+        ((IMAPFolder)folder).releaseMessageConnection(connection);
+    }
+
+
+    /**
+     * Check the validity of the current message.  This ensures that
+     * A) the folder is currently open, B) that the message has not
+     * been expunged (after getting the latest status from the server).
+     *
+     * @exception MessagingException
+     */
+    protected void checkValidity() throws MessagingException {
+        checkValidity(false); 
+    }
+
+
+    /**
+     * Check the validity of the current message.  This ensures that
+     * A) the folder is currently open, B) that the message has not
+     * been expunged (after getting the latest status from the server).
+     *
+     * @exception MessagingException
+     */
+    protected void checkValidity(boolean update) throws MessagingException {
+        // we need to ensure that we're the only ones with access to the folder's 
+        // message cache any time we need to talk to the server.  This needs to be 
+        // held until after we release the connection so that any pending EXPUNGE 
+        // untagged responses are processed before the next time the folder connection is 
+        // used. 
+        if (update) {
+            synchronized (folder) {
+                // have the connection update the folder status.  This might result in this message
+                // changing its state to expunged.  It might also result in an exception if the
+                // folder has been closed.
+                IMAPConnection connection = getConnection(); 
+
+                try {
+                    connection.updateMailboxStatus();
+                } finally {
+                    // this will force any expunged messages to be processed before we release 
+                    // the lock. 
+                    releaseConnection(connection); 
+                }
+            }
+        }
+
+        // now see if we've been expunged, this is a bad op on the message.
+        if (isExpunged()) {
+            throw new MessageRemovedException("Illegal opertion on a deleted message");
+        }
+    }
+    
+    
+    /**
+     * Evaluate whether this message requires any of the information 
+     * in a FetchProfile to be fetched from the server.  If the messages 
+     * already contains the information in the profile, it returns false.
+     * This allows IMAPFolder to optimize fetch() requests to just the 
+     * messages that are missing any of the requested information.
+     * 
+     * NOTE:  If any of the items in the profile are missing, then this 
+     * message will be updated with ALL of the items.  
+     * 
+     * @param profile The FetchProfile indicating the information that should be prefetched.
+     * 
+     * @return true if any of the profile information requires fetching.  false if this 
+     *         message already contains the given information.
+     */
+    protected boolean evaluateFetch(FetchProfile profile) {
+        // the fetch profile can contain a number of different item types.  Validate
+        // whether we need any of these and return true on the first mismatch. 
+        
+        // the UID is a common fetch request, put it first. 
+        if (profile.contains(UIDFolder.FetchProfileItem.UID) && uid == -1) {
+            return true; 
+        }
+        if (profile.contains(FetchProfile.Item.ENVELOPE) && envelope == null) {
+            return true; 
+        }
+        if (profile.contains(FetchProfile.Item.FLAGS) && flags == null) {
+            return true; 
+        }
+        if (profile.contains(FetchProfile.Item.CONTENT_INFO) && bodyStructure == null) {
+            return true; 
+        }
+        // The following profile items are our implementation of items that the 
+        // Sun IMAPFolder implementation supports.  
+        if (profile.contains(IMAPFolder.FetchProfileItem.HEADERS) && !allHeadersRetrieved) {
+            return true; 
+        }
+        if (profile.contains(IMAPFolder.FetchProfileItem.SIZE) && bodyStructure.bodySize < 0) {
+            return true; 
+        }
+        // last bit after checking each of the information types is to see if 
+        // particular headers have been requested and whether those are on the 
+        // set we do have loaded. 
+        String [] requestedHeaders = profile.getHeaderNames(); 
+         
+        // ok, any missing header in the list is enough to force us to request the 
+        // information. 
+        for (int i = 0; i < requestedHeaders.length; i++) {
+            if (headers.getHeader(requestedHeaders[i]) == null) {
+                return true; 
+            }
+        }
+        // this message, at least, does not need anything fetched. 
+        return false; 
+    }
+    
+    /**
+     * Update a message instance with information retrieved via an IMAP FETCH 
+     * command.  The command response for this message may contain multiple pieces
+     * that we need to process.
+     * 
+     * @param response The response line, which may contain multiple data items.
+     * 
+     * @exception MessagingException
+     */
+    void updateMessageInformation(IMAPFetchResponse response) throws MessagingException {
+        // get the list of data items associated with this response.  We can have 
+        // a large number of items returned in a single update. 
+        List items = response.getDataItems(); 
+        
+        for (int i = 0; i < items.size(); i++) {
+            IMAPFetchDataItem item = (IMAPFetchDataItem)items.get(i); 
+            
+            switch (item.getType()) {
+                // if the envelope has been requested, we'll end up with all of these items. 
+                case IMAPFetchDataItem.ENVELOPE:
+                    // update the envelope and map the envelope items into the headers. 
+                    updateEnvelope((IMAPEnvelope)item);
+                    break;
+                case IMAPFetchDataItem.INTERNALDATE:
+                    receivedDate = ((IMAPInternalDate)item).getDate();;
+                    break;
+                case IMAPFetchDataItem.SIZE:
+                    size = ((IMAPMessageSize)item).size;      
+                    break;
+                case IMAPFetchDataItem.UID:
+                    uid = ((IMAPUid)item).uid;       
+                    // make sure the folder knows about the UID update. 
+                    ((IMAPFolder)folder).addToUidCache(new Long(uid), this); 
+                    break; 
+                case IMAPFetchDataItem.BODYSTRUCTURE: 
+                    updateBodyStructure((IMAPBodyStructure)item); 
+                    break; 
+                    // a partial or full header update 
+                case IMAPFetchDataItem.HEADER:
+                {
+                    // if we've fetched the complete set, then replace what we have 
+                    IMAPInternetHeader h = (IMAPInternetHeader)item; 
+                    if (h.isComplete()) {
+                        // we've got a complete header set now. 
+                        this.headers = h.headers;     
+                        allHeadersRetrieved = true; 
+                    }
+                    else {
+                        // need to merge the requested headers in with 
+                        // our existing set.  We need to be careful, since we 
+                        // don't want to add duplicates. 
+                        mergeHeaders(h.headers); 
+                    }
+                }
+                default:
+            }
+        }
+    }
+    
+    
+    /**
+     * Merge a subset of the requested headers with our existing partial set. 
+     * The new set will contain all headers requested from the server, plus 
+     * any of our existing headers that were not included in the retrieved set.
+     * 
+     * @param newHeaders The retrieved set of headers.
+     */
+    protected synchronized void mergeHeaders(InternetHeaders newHeaders) {
+        // This is sort of tricky to manage.  The input headers object is a fresh set  
+        // retrieved from the server, but it's a subset of the headers.  Our existing set
+        // might not be complete, but it may contain duplicates of information in the 
+        // retrieved set, plus headers that are not in the retrieved set.  To keep from 
+        // adding duplicates, we'll only add headers that are not in the retrieved set to 
+        // that set.   
+        
+        // start by running through the list of headers 
+        Enumeration e = headers.getAllHeaders(); 
+        
+        while (e.hasMoreElements()) {
+            Header header = (Header)e.nextElement(); 
+            // if there are no headers with this name in the new set, then 
+            // we can add this.  Note that to add the header, we need to 
+            // retrieve all instances by this name and add them as a unit.  
+            // When we hit one of the duplicates again with the enumeration, 
+            // we'll skip it then because the merge target will have everything. 
+            if (newHeaders.getHeader(header.getName()) == null) {
+                // get all occurrences of this name and stuff them into the 
+                // new list 
+                String name = header.getName(); 
+                String[] a = headers.getHeader(name); 
+                for (int i = 0; i < a.length; i++) {
+                    newHeaders.addHeader(name, a[i]); 
+                }
+            }
+        }
+        // and replace the current header set
+        headers = newHeaders; 
+    }
+}

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMessage.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMessage.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMessage.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMimeBodyPart.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMimeBodyPart.java?rev=594520&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMimeBodyPart.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMimeBodyPart.java Tue Nov 13 04:57:39 2007
@@ -0,0 +1,350 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.imap;
+
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration; 
+
+import javax.activation.DataHandler;   
+
+import javax.mail.IllegalWriteException; 
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeUtility;
+
+import org.apache.geronimo.javamail.store.imap.connection.IMAPBodyStructure;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPConnection;
+
+
+public class IMAPMimeBodyPart extends MimeBodyPart {
+    // the message we're part of
+    protected IMAPMessage message;
+    // the retrieved BODYSTRUCTURE information for this part.
+    protected IMAPBodyStructure bodyStructure;
+    // the section identifier.  This will be in a format such as 1.2.3, which 
+    // would refer to the "third part contained in the second part of the first part"...
+    // got all that?  There will be a quiz at the end of class :-)
+    protected String section;
+    // flag to indicate whether the body part headers have been loaded.
+    boolean headersLoaded = false;
+
+    /**
+     * Create an instance of a MimeBodyPart within an 
+     * IMAP message.
+     * 
+     * @param message The parent Message instance containing this part.
+     * @param bodyStructure
+     *                The IMAPBodyStructure information describing the part.
+     * @param section The numeric section identifier string for this part.
+     *                This is a hierarchical set of numbers describing
+     *                how to navigate to the message part on the IMAP
+     *                server.  For example, "2.1.3" would be the third
+     *                subpart of the first subpart of the second main
+     *                message part.
+     */
+    public IMAPMimeBodyPart(IMAPMessage message, IMAPBodyStructure bodyStructure, String section) {
+        super();
+        this.message = message;
+        this.bodyStructure = bodyStructure;
+        this.section = section;
+    }
+
+
+    /**
+     * Get the size of the message part.
+     * 
+     * @return The size information returned in the IMAP body structure.
+     * @exception MessagingException
+     */
+    public int getSize() throws MessagingException {
+        return bodyStructure.bodySize;
+    }
+
+    /**
+     * Get the estimated line count for the body part.
+     * 
+     * @return The line count information returned by the IMAP 
+     *         server.
+     * @exception MessagingException
+     */
+    public int getLineCount() throws MessagingException {
+        return bodyStructure.lines;
+    }
+
+    /**
+     * Get the content type for the body part.
+     * 
+     * @return The mimetype for the body part, in string format.
+     * @exception MessagingException
+     */
+    public String getContentType() throws MessagingException {
+        return bodyStructure.mimeType.toString();
+    }
+
+    /**
+     * Test if the body part is of a particular MIME type.
+     * 
+     * @param type   The string MIME-type name.  A wild card * can be
+     *               specified for the subpart type.
+     * 
+     * @return true if the body part matches the give MIME-type.
+     * @exception MessagingException
+     */
+    public boolean isMimeType(String type) throws MessagingException {
+        return bodyStructure.mimeType.match(type);
+    }
+
+    /**
+     * Retrieve the disposition information about this 
+     * body part.
+     * 
+     * @return The disposition information, as a string value.
+     * @exception MessagingException
+     */
+    public String getDisposition() throws MessagingException {
+        return bodyStructure.disposition.getDisposition();
+    }
+
+    /**
+     * Set the disposition information.  The IMAP message 
+     * is read-only, so this is an error.
+     * 
+     * @param disposition
+     *               The disposition string.
+     * 
+     * @exception MessagingException
+     */
+    public void setDisposition(String disposition) throws MessagingException {
+        throw new IllegalWriteException("IMAP message parts are read-only");
+    }
+
+    public String getEncoding() throws MessagingException {
+        return bodyStructure.transferEncoding;
+    }
+
+    public String getContentID() throws MessagingException {
+        return bodyStructure.contentID;
+    }
+
+    public void setContentID(String id) throws MessagingException {
+        throw new IllegalWriteException("IMAP message parts are read-only");
+    }
+
+    public String getContentMD5() throws MessagingException {
+        return bodyStructure.md5Hash;
+    }
+
+    public void setContentMD5(String id) throws MessagingException {
+        throw new IllegalWriteException("IMAP message parts are read-only");
+    }
+
+    public String getDescription() throws MessagingException {
+        String description = bodyStructure.contentDescription;
+        if (description != null) {
+            try {
+                // this could be both folded and encoded.  Return this to usable form.
+                return MimeUtility.decodeText(MimeUtility.unfold(description));
+            } catch (UnsupportedEncodingException e) {
+                // ignore
+            }
+        }
+        // return the raw version for any errors (this might be null also)
+        return description;
+    }
+
+    public void setDescription(String d, String charset) throws MessagingException {
+        throw new IllegalWriteException("IMAP message parts are read-only");
+    }
+
+    public String getFileName() throws MessagingException {
+        String filename = bodyStructure.disposition.getParameter("filename");
+        if (filename == null) {
+            filename = bodyStructure.mimeType.getParameter("name");
+        }
+        return filename;
+    }
+
+    public void setFileName(String name) throws MessagingException {
+        throw new IllegalWriteException("IMAP message parts are read-only");
+    }
+
+    protected InputStream getContentStream() throws MessagingException {
+
+        // no content loaded yet?
+        if (content == null) {
+            // make sure we're still valid
+            message.checkValidity();
+            // make sure the content is fully loaded
+            loadContent();
+        }
+
+        // allow the super class to handle creating it from the loaded content.
+        return super.getContentStream();
+    }
+
+
+    /**
+     * Create the DataHandler object for this message.
+     *
+     * @return The DataHandler object that processes the content set for this
+     *         message.
+     * @exception MessagingException
+     */
+    public synchronized DataHandler getDataHandler() throws MessagingException {
+        if (dh == null) {                                                
+            // are we working with a multipart message here?
+            if (bodyStructure.isMultipart()) {
+                dh = new DataHandler(new IMAPMultipartDataSource(message, this, section, bodyStructure));
+                return dh;
+            }
+            else if (bodyStructure.isAttachedMessage()) {
+                dh = new DataHandler(new IMAPAttachedMessage(message, section, bodyStructure.nestedEnvelope, 
+                    bodyStructure.nestedBody), bodyStructure.mimeType.toString());
+                return dh;
+            }
+        }
+
+        // single part messages get handled the normal way.
+        return super.getDataHandler();
+    }
+
+    public void setDataHandler(DataHandler content) throws MessagingException {
+        throw new IllegalWriteException("IMAP body parts are read-only");
+    }
+
+    public void setContent(Object o, String type) throws MessagingException {
+        throw new IllegalWriteException("IMAP body parts are read-only");
+    }
+
+    public void setContent(Multipart mp) throws MessagingException {
+        throw new IllegalWriteException("IMAP body parts are read-only");
+    }
+
+
+	/******************************************************************
+	 * Following is a set of methods that deal with headers
+	 * These methods are just overrides on the superclass methods to
+     * allow lazy loading of the header information.
+	 ********************************************************************/
+
+	public String[] getHeader(String name) throws MessagingException {
+        loadHeaders();
+		return headers.getHeader(name);
+	}
+
+	public String getHeader(String name, String delimiter) throws MessagingException {
+        loadHeaders();
+		return headers.getHeader(name, delimiter);
+	}
+
+	public Enumeration getAllHeaders() throws MessagingException {
+        loadHeaders();
+		return headers.getAllHeaders();
+	}
+
+	public Enumeration getMatchingHeaders(String[] names)  throws MessagingException {
+        loadHeaders();
+		return headers.getMatchingHeaders(names);
+	}
+
+	public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException {
+        loadHeaders();
+		return headers.getNonMatchingHeaders(names);
+	}
+
+	public Enumeration getAllHeaderLines() throws MessagingException {
+        loadHeaders();
+		return headers.getAllHeaderLines();
+	}
+
+	public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException {
+        loadHeaders();
+		return headers.getMatchingHeaderLines(names);
+	}
+
+	public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException {
+        loadHeaders();
+		return headers.getNonMatchingHeaderLines(names);
+	}
+
+    // the following are overrides for header modification methods.  These messages are read only,
+    // so the headers cannot be modified.
+    public void addHeader(String name, String value) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+    public void setHeader(String name, String value) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+
+    public void removeHeader(String name) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+    public void addHeaderLine(String line) throws MessagingException {
+        throw new IllegalWriteException("IMAP messages are read-only");
+    }
+
+
+    /**
+     * Load the mime part headers into this body part.
+     *
+     * @exception MessagingException
+     */
+    protected synchronized void loadHeaders() throws MessagingException {
+        // have them already?  Super..
+        if (headers != null) {
+            return;
+        }
+                           
+        IMAPConnection connection = message.getConnection();
+        try {
+            // this asks for the MIME subsection of the given section piece.
+            headers = connection.fetchHeaders(message.getSequenceNumber(), section);
+        } finally {    
+            message.releaseConnection(connection);
+        }
+
+    }
+
+
+    /**
+     * Load the message content into the BodyPart object.
+     *
+     * @exception MessagingException
+     */
+    protected void loadContent() throws MessagingException {
+        // if we've loaded this already, just return
+        if (content != null) {
+            return;
+        }
+        
+        IMAPConnection connection = message.getConnection();
+        try {
+            // load the content from the server. 
+            content = connection.fetchContent(message.getSequenceNumber(), section); 
+        } finally {
+            message.releaseConnection(connection); 
+        }
+    }
+}
+

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMimeBodyPart.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMimeBodyPart.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMimeBodyPart.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMultipartDataSource.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMultipartDataSource.java?rev=594520&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMultipartDataSource.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMultipartDataSource.java Tue Nov 13 04:57:39 2007
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.imap;
+
+import javax.mail.BodyPart;
+import javax.mail.MessagingException;
+import javax.mail.MultipartDataSource;
+
+import javax.mail.internet.MimePart;
+import javax.mail.internet.MimePartDataSource;
+
+import org.apache.geronimo.javamail.store.imap.connection.IMAPBodyStructure;
+
+public class IMAPMultipartDataSource extends MimePartDataSource implements MultipartDataSource {
+    // the list of parts
+    protected BodyPart[] parts;
+
+    IMAPMultipartDataSource(IMAPMessage message, MimePart parent, String section, IMAPBodyStructure bodyStructure) {
+        super(parent);
+
+        parts = new BodyPart[bodyStructure.parts.length];
+        
+        // We're either created from the parent message, in which case we're the top level 
+        // of the hierarchy, or we're created from a nested message, so we need to apply the 
+        // parent numbering prefix. 
+        String sectionBase = section == null ? "" : section + "."; 
+
+        for (int i = 0; i < parts.length; i++) {
+            // create a section id.  This is either the count (origin zero) or a subpart of the previous section.
+            parts[i] = new IMAPMimeBodyPart(message, (IMAPBodyStructure)bodyStructure.parts[i], sectionBase + (i + 1));
+        }
+    }
+
+    public int getCount() {
+        return parts.length;
+    }
+
+    public BodyPart getBodyPart(int index) throws MessagingException {
+        return parts[index];
+    }
+}

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMultipartDataSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMultipartDataSource.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPMultipartDataSource.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPNamespaceFolder.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPNamespaceFolder.java?rev=594520&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPNamespaceFolder.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPNamespaceFolder.java Tue Nov 13 04:57:39 2007
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geronimo.javamail.store.imap;
+
+import org.apache.geronimo.javamail.store.imap.connection.IMAPNamespace;
+
+/**
+ * An override of the base IMAPFolder class for folders representing namespace roots. 
+ * @see javax.mail.Folder
+ *
+ * @version $Rev$
+ */
+public class IMAPNamespaceFolder extends IMAPFolder {
+    
+    IMAPNamespaceFolder(IMAPStore store, IMAPNamespace namespace) {
+        // initialize with the namespace information 
+        super(store, namespace.prefix, namespace.separator); 
+    }
+    
+    
+    /**
+     * Override of the default IMAPFolder method to provide the mailbox name 
+     * as the prefix + delimiter. 
+     * 
+     * @return The string name to use as the mailbox name for exists() and issubscribed() 
+     *         calls.
+     */
+    protected String getMailBoxName() {
+        // no delimiter is a possibility, so 
+        // we need to check.  
+        if (separator == '\0') {
+            return fullname;
+        }
+        return fullname + separator; 
+    }
+}

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPNamespaceFolder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPNamespaceFolder.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPNamespaceFolder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPRootFolder.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPRootFolder.java?rev=594520&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPRootFolder.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPRootFolder.java Tue Nov 13 04:57:39 2007
@@ -0,0 +1,131 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.geronimo.javamail.store.imap;
+
+import javax.mail.Folder; 
+import javax.mail.Message; 
+import javax.mail.MessagingException; 
+import javax.mail.MethodNotSupportedException;
+import javax.mail.Store; 
+
+import org.apache.geronimo.javamail.store.imap.connection.IMAPConnection; 
+import org.apache.geronimo.javamail.store.imap.connection.IMAPEnvelope; 
+import org.apache.geronimo.javamail.store.imap.connection.IMAPBodyStructure; 
+
+/**
+ * An IMAP folder instance for the root of IMAP folder tree.  This has 
+ * some of the folder operations disabled. 
+ */
+public class IMAPRootFolder extends IMAPFolder {
+    
+    /**
+     * Create a default IMAPRootFolder attached to a specific Store instance.
+     * 
+     * @param store  The Store instance this is the root for.
+     */
+    public IMAPRootFolder(IMAPStore store) {
+        // create a folder with a null string name and the default separator. 
+        super(store, "", '/'); 
+        // this only holds folders 
+        folderType = HOLDS_FOLDERS; 
+    }
+
+    /**
+     * Get the Folder determined by the supplied name; if the name is relative
+     * then it is interpreted relative to this folder. This does not check that
+     * the named folder actually exists.
+     *
+     * @param name the name of the folder to return
+     * @return the named folder
+     * @throws MessagingException if there was a problem accessing the store
+     */
+    public Folder getFolder(String name) throws MessagingException {
+        // The root folder is a dummy one.  Any getFolder() request starting 
+        // at the root will use the request name for the full name.  The separator 
+        // used in that folder's namespace will be determined when the folder is 
+        // first opened. 
+        return new IMAPFolder((IMAPStore)store, name, UNDETERMINED);
+    }
+    
+    
+    public Folder getParent() {
+        // we never have a parent folder 
+        return null; 
+    }
+    
+    
+    public boolean exists() throws MessagingException {
+        // this always exists 
+        return true; 
+    }
+    
+    public boolean hasNewMessages() {
+        // we don't really exist, so the answer is always false. 
+        return false; 
+    }
+    
+    
+    public int getMessagesCount() {
+        // we don't really exist, so the answer is always 0; 
+        return 0; 
+    }
+    
+    
+    public int getNewMessagesCount() {
+        // we don't really exist, so the answer is always 0; 
+        return 0; 
+    }
+    
+    
+    public int getUnreadMessagesCount() {
+        // we don't really exist, so the answer is always 0; 
+        return 0; 
+    }
+    
+    
+    public int getDeletedMessagesCount() {
+        // we don't really exist, so the answer is always 0; 
+        return 0; 
+    }
+    
+    
+	public boolean create(int newType) throws MessagingException {
+        throw new MethodNotSupportedException("Default IMAP folder cannot be created"); 
+    }
+    
+    public boolean delete(boolean recurse) throws MessagingException {
+        throw new MethodNotSupportedException("Default IMAP folder cannot be deleted"); 
+    }
+    
+    
+    public boolean rename(boolean recurse) throws MessagingException {
+        throw new MethodNotSupportedException("Default IMAP folder cannot be renamed"); 
+    }
+    
+    
+    public void appendMessages(Message[] msgs) throws MessagingException {
+        throw new MethodNotSupportedException("Messages cannot be appended to Default IMAP folder"); 
+    }
+    
+    
+    public Message[] expunge() throws MessagingException {
+        throw new MethodNotSupportedException("Messages cannot be expunged from Default IMAP folder"); 
+    }
+}
+

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPRootFolder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPRootFolder.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPRootFolder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPSSLStore.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPSSLStore.java?rev=594520&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPSSLStore.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPSSLStore.java Tue Nov 13 04:57:39 2007
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.geronimo.javamail.store.imap;
+
+import javax.mail.Session;
+import javax.mail.URLName;
+
+/**
+ * IMAP implementation of javax.mail.Store
+ * POP protocol spec is implemented in
+ * org.apache.geronimo.javamail.store.pop3.IMAPConnection
+ *
+ * @version $Rev$ $Date$
+ */
+public class IMAPSSLStore extends IMAPStore {
+    /**
+     * Construct an IMAPSSLStore item.
+     *
+     * @param session The owning javamail Session.
+     * @param urlName The Store urlName, which can contain server target information.
+     */
+	public IMAPSSLStore(Session session, URLName urlName) {
+        // we're the imaps protocol, our default connection port is 993, and we must use
+        // an SSL connection for the initial hookup 
+		super(session, urlName, "imaps", true, DEFAULT_IMAP_SSL_PORT);
+	}
+}
+

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPSSLStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPSSLStore.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/javamail/trunk/geronimo-javamail_1.4/geronimo-javamail_1.4_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPSSLStore.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message