Return-Path: Delivered-To: apmail-geronimo-scm-archive@www.apache.org Received: (qmail 33918 invoked from network); 13 Nov 2007 12:59:35 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 13 Nov 2007 12:59:35 -0000 Received: (qmail 21018 invoked by uid 500); 13 Nov 2007 12:59:22 -0000 Delivered-To: apmail-geronimo-scm-archive@geronimo.apache.org Received: (qmail 20982 invoked by uid 500); 13 Nov 2007 12:59:22 -0000 Mailing-List: contact scm-help@geronimo.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: dev@geronimo.apache.org List-Id: Delivered-To: mailing list scm@geronimo.apache.org Received: (qmail 20971 invoked by uid 99); 13 Nov 2007 12:59:22 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 13 Nov 2007 04:59:22 -0800 X-ASF-Spam-Status: No, hits=-100.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 13 Nov 2007 12:59:22 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 9801A1A9842; Tue, 13 Nov 2007 04:59:02 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit 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 -0000 To: scm@geronimo.apache.org From: rickmcguire@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20071113125902.9801A1A9842@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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