You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by jl...@apache.org on 2020/10/07 15:58:41 UTC
svn commit: r1882306 [5/17] - in
/geronimo/javamail/trunk/geronimo-javamail_1.6: ./
geronimo-javamail_1.6_mail/ geronimo-javamail_1.6_mail/src/
geronimo-javamail_1.6_mail/src/site/ geronimo-javamail_1.6_provider/
geronimo-javamail_1.6_provider/src/ ger...
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPStore.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPStore.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPStore.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/IMAPStore.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,614 @@
+/**
+ * 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.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.mail.AuthenticationFailedException;
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+import javax.mail.Quota;
+import javax.mail.QuotaAwareStore;
+import javax.mail.Session;
+import javax.mail.Store;
+import javax.mail.URLName;
+import javax.mail.event.StoreEvent;
+
+import org.apache.geronimo.javamail.store.imap.connection.IMAPConnection;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPConnectionPool;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPOkResponse;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPNamespaceResponse;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPNamespace;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPServerStatusResponse;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPUntaggedResponse;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPUntaggedResponseHandler;
+import org.apache.geronimo.javamail.util.ProtocolProperties;
+
+/**
+ * IMAP implementation of javax.mail.Store
+ * POP protocol spec is implemented in
+ * org.apache.geronimo.javamail.store.pop3.IMAPConnection
+ *
+ * @version $Rev$ $Date$
+ */
+
+public class IMAPStore extends Store implements QuotaAwareStore, IMAPUntaggedResponseHandler {
+ // the default connection ports for secure and non-secure variations
+ protected static final int DEFAULT_IMAP_PORT = 143;
+ protected static final int DEFAULT_IMAP_SSL_PORT = 993;
+
+ protected static final String MAIL_STATUS_TIMEOUT = "statuscacheimeout";
+ protected static final int DEFAULT_STATUS_TIMEOUT = 1000;
+
+ // our accessor for protocol properties and the holder of
+ // protocol-specific information
+ protected ProtocolProperties props;
+
+ // the connection pool we use for access
+ protected IMAPConnectionPool connectionPool;
+
+ // the root folder
+ protected IMAPRootFolder root;
+
+ // the list of open folders (which also represents an open connection).
+ protected List openFolders = new LinkedList();
+
+ // our session provided debug output stream.
+ protected PrintStream debugStream;
+ // the debug flag
+ protected boolean debug;
+ // until we're connected, we're closed
+ boolean closedForBusiness = true;
+ // The timeout value for our status cache
+ long statusCacheTimeout = 0;
+
+ /**
+ * Construct an IMAPStore item.
+ *
+ * @param session The owning javamail Session.
+ * @param urlName The Store urlName, which can contain server target information.
+ */
+ public IMAPStore(Session session, URLName urlName) {
+ // we're the imap protocol, our default connection port is 119, and don't use
+ // an SSL connection for the initial hookup
+ this(session, urlName, "imap", false, DEFAULT_IMAP_PORT);
+ }
+
+ /**
+ * Protected common constructor used by both the IMAPStore and the IMAPSSLStore
+ * to initialize the Store instance.
+ *
+ * @param session The Session we're attached to.
+ * @param urlName The urlName.
+ * @param protocol The protocol name.
+ * @param sslConnection
+ * The sslConnection flag.
+ * @param defaultPort
+ * The default connection port.
+ */
+ protected IMAPStore(Session session, URLName urlName, String protocol, boolean sslConnection, int defaultPort) {
+ super(session, urlName);
+ // create the protocol property holder. This gives an abstraction over the different
+ // flavors of the protocol.
+ props = new ProtocolProperties(session, protocol, sslConnection, defaultPort);
+
+ // get the status timeout value for the folders.
+ statusCacheTimeout = props.getIntProperty(MAIL_STATUS_TIMEOUT, DEFAULT_STATUS_TIMEOUT);
+
+ // get our debug settings
+ debugStream = session.getDebugOut();
+ debug = session.getDebug();
+
+ // create a connection pool we can retrieve connections from
+ connectionPool = new IMAPConnectionPool(this, props);
+ }
+
+
+ /**
+ * Attempt the protocol-specific connection; subclasses should override this to establish
+ * a connection in the appropriate manner.
+ *
+ * This method should return true if the connection was established.
+ * It may return false to cause the {@link #connect(String, int, String, String)} method to
+ * reattempt the connection after trying to obtain user and password information from the user.
+ * Alternatively it may throw a AuthenticatedFailedException to abandon the conection attempt.
+ *
+ * @param host The target host name of the service.
+ * @param port The connection port for the service.
+ * @param user The user name used for the connection.
+ * @param password The password used for the connection.
+ *
+ * @return true if a connection was established, false if there was authentication
+ * error with the connection.
+ * @throws AuthenticationFailedException
+ * if authentication fails
+ * @throws MessagingException
+ * for other failures
+ */
+ protected synchronized boolean protocolConnect(String host, int port, String username, String password) throws MessagingException {
+ if (debug) {
+ debugOut("Connecting to server " + host + ":" + port + " for user " + username);
+ }
+
+ // the connection pool handles all of the details here.
+ if (connectionPool.protocolConnect(host, port, username, password))
+ {
+ // the store is now open
+ closedForBusiness = false;
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Close this service and terminate its physical connection.
+ * The default implementation simply calls setConnected(false) and then
+ * sends a CLOSED event to all registered ConnectionListeners.
+ * Subclasses overriding this method should still ensure it is closed; they should
+ * also ensure that it is called if the connection is closed automatically, for
+ * for example in a finalizer.
+ *
+ *@throws MessagingException if there were errors closing; the connection is still closed
+ */
+ public synchronized void close() throws MessagingException{
+ // if already closed, nothing to do.
+ if (closedForBusiness) {
+ return;
+ }
+
+ // close the folders first, then shut down the Store.
+ closeOpenFolders();
+
+ connectionPool.close();
+ connectionPool = null;
+
+ // make sure we do the superclass close operation first so
+ // notification events get broadcast properly.
+ super.close();
+ }
+
+
+ /**
+ * Return a Folder object that represents the root of the namespace for the current user.
+ *
+ * Note that in some store configurations (such as IMAP4) the root folder might
+ * not be the INBOX folder.
+ *
+ * @return the root Folder
+ * @throws MessagingException if there was a problem accessing the store
+ */
+ public Folder getDefaultFolder() throws MessagingException {
+ checkConnectionStatus();
+ // if no root yet, create a root folder instance.
+ if (root == null) {
+ return new IMAPRootFolder(this);
+ }
+ return root;
+ }
+
+ /**
+ * Return the Folder corresponding to the given name.
+ * The folder might not physically exist; the {@link Folder#exists()} method can be used
+ * to determine if it is real.
+ *
+ * @param name the name of the Folder to return
+ *
+ * @return the corresponding folder
+ * @throws MessagingException
+ * if there was a problem accessing the store
+ */
+ public Folder getFolder(String name) throws MessagingException {
+ return getDefaultFolder().getFolder(name);
+ }
+
+
+ /**
+ * Return the folder identified by the URLName; the URLName must refer to this Store.
+ * Implementations may use the {@link URLName#getFile()} method to determined the folder name.
+ *
+ * @param url
+ *
+ * @return the corresponding folder
+ * @throws MessagingException
+ * if there was a problem accessing the store
+ */
+ public Folder getFolder(URLName url) throws MessagingException {
+ return getDefaultFolder().getFolder(url.getFile());
+ }
+
+
+ /**
+ * Return the root folders of the personal namespace belonging to the current user.
+ *
+ * The default implementation simply returns an array containing the folder returned by {@link #getDefaultFolder()}.
+ * @return the root folders of the user's peronal namespaces
+ * @throws MessagingException if there was a problem accessing the store
+ */
+ public Folder[] getPersonalNamespaces() throws MessagingException {
+ IMAPNamespaceResponse namespaces = getNamespaces();
+
+ // if nothing is returned, then use the API-defined default for this
+ if (namespaces.personalNamespaces.size() == 0) {
+ return super.getPersonalNamespaces();
+ }
+
+ // convert the list into an array of Folders.
+ return getNamespaceFolders(namespaces.personalNamespaces);
+ }
+
+
+ /**
+ * Return the root folders of the personal namespaces belonging to the supplied user.
+ *
+ * The default implementation simply returns an empty array.
+ *
+ * @param user the user whose namespaces should be returned
+ * @return the root folders of the given user's peronal namespaces
+ * @throws MessagingException if there was a problem accessing the store
+ */
+ public Folder[] getUserNamespaces(String user) throws MessagingException {
+ IMAPNamespaceResponse namespaces = getNamespaces();
+
+ // if nothing is returned, then use the API-defined default for this
+ if (namespaces.otherUserNamespaces == null || namespaces.otherUserNamespaces.isEmpty()) {
+ return super.getUserNamespaces(user);
+ }
+
+ // convert the list into an array of Folders.
+ return getNamespaceFolders(namespaces.otherUserNamespaces);
+ }
+
+
+ /**
+ * Return the root folders of namespaces that are intended to be shared between users.
+ *
+ * The default implementation simply returns an empty array.
+ * @return the root folders of all shared namespaces
+ * @throws MessagingException if there was a problem accessing the store
+ */
+ public Folder[] getSharedNamespaces() throws MessagingException {
+ IMAPNamespaceResponse namespaces = getNamespaces();
+
+ // if nothing is returned, then use the API-defined default for this
+ if (namespaces.sharedNamespaces == null || namespaces.sharedNamespaces.isEmpty()) {
+ return super.getSharedNamespaces();
+ }
+
+ // convert the list into an array of Folders.
+ return getNamespaceFolders(namespaces.sharedNamespaces);
+ }
+
+
+ /**
+ * Get the quotas for the specified root element.
+ *
+ * @param root The root name for the quota information.
+ *
+ * @return An array of Quota objects defined for the root.
+ * @throws MessagingException if the quotas cannot be retrieved
+ */
+ public Quota[] getQuota(String root) throws MessagingException {
+ // get our private connection for access
+ IMAPConnection connection = getStoreConnection();
+ try {
+ // request the namespace information from the server
+ return connection.fetchQuota(root);
+ } finally {
+ releaseStoreConnection(connection);
+ }
+ }
+
+ /**
+ * Set a quota item. The root contained in the Quota item identifies
+ * the quota target.
+ *
+ * @param quota The source quota item.
+ * @throws MessagingException if the quota cannot be set
+ */
+ public void setQuota(Quota quota) throws MessagingException {
+ // get our private connection for access
+ IMAPConnection connection = getStoreConnection();
+ try {
+ // request the namespace information from the server
+ connection.setQuota(quota);
+ } finally {
+ releaseStoreConnection(connection);
+ }
+ }
+
+ /**
+ * Verify that the server is in a connected state before
+ * performing operations that required that status.
+ *
+ * @exception MessagingException
+ */
+ private void checkConnectionStatus() throws MessagingException {
+ // we just check the connection status with the superclass. This
+ // tells us we've gotten a connection. We don't want to do the
+ // complete connection checks that require pinging the server.
+ if (!super.isConnected()){
+ throw new MessagingException("Not connected ");
+ }
+ }
+
+
+ /**
+ * Test to see if we're still connected. This will ping the server
+ * to see if we're still alive.
+ *
+ * @return true if we have a live, active culture, false otherwise.
+ */
+ public synchronized boolean isConnected() {
+ // check if we're in a presumed connected state. If not, we don't really have a connection
+ // to check on.
+ if (!super.isConnected()) {
+ return false;
+ }
+
+ try {
+ IMAPConnection connection = getStoreConnection();
+ try {
+ // check with the connecition to see if it's still alive.
+ // we use a zero timeout value to force it to check.
+ return connection.isAlive(0);
+ } finally {
+ releaseStoreConnection(connection);
+ }
+ } catch (MessagingException e) {
+ return false;
+ }
+
+ }
+
+ /**
+ * Internal debug output routine.
+ *
+ * @param value The string value to output.
+ */
+ void debugOut(String message) {
+ debugStream.println("IMAPStore DEBUG: " + message);
+ }
+
+ /**
+ * Internal debugging routine for reporting exceptions.
+ *
+ * @param message A message associated with the exception context.
+ * @param e The received exception.
+ */
+ void debugOut(String message, Throwable e) {
+ debugOut("Received exception -> " + message);
+ debugOut("Exception message -> " + e.getMessage());
+ e.printStackTrace(debugStream);
+ }
+
+
+ /**
+ * Retrieve the server connection created by this store.
+ *
+ * @return The active connection object.
+ */
+ protected IMAPConnection getStoreConnection() throws MessagingException {
+ return connectionPool.getStoreConnection();
+ }
+
+ protected void releaseStoreConnection(IMAPConnection connection) throws MessagingException {
+ // This is a bit of a pain. We need to delay processing of the
+ // unsolicited responses until after each user of the connection has
+ // finished processing the expected responses. We need to do this because
+ // the unsolicited responses may include EXPUNGED messages. The EXPUNGED
+ // messages will alter the message sequence numbers for the messages in the
+ // cache. Processing the EXPUNGED messages too early will result in
+ // updates getting applied to the wrong message instances. So, as a result,
+ // we delay that stage of the processing until all expected responses have
+ // been handled.
+
+ // process any pending messages before returning.
+ connection.processPendingResponses();
+ // return this to the connectin pool
+ connectionPool.releaseStoreConnection(connection);
+ }
+
+ synchronized IMAPConnection getFolderConnection(IMAPFolder folder) throws MessagingException {
+ IMAPConnection connection = connectionPool.getFolderConnection();
+ openFolders.add(folder);
+ return connection;
+ }
+
+
+ synchronized void releaseFolderConnection(IMAPFolder folder, IMAPConnection connection) throws MessagingException {
+ openFolders.remove(folder);
+ // return this to the connectin pool
+ // NB: It is assumed that the Folder has already triggered handling of
+ // unsolicited responses on this connection before returning it.
+ connectionPool.releaseFolderConnection(connection);
+ }
+
+
+ /**
+ * Retrieve the Session object this Store is operating under.
+ *
+ * @return The attached Session instance.
+ */
+ Session getSession() {
+ return session;
+ }
+
+ /**
+ * Close all open folders. We have a small problem here with a race condition. There's no safe, single
+ * synchronization point for us to block creation of new folders while we're closing. So we make a copy of
+ * the folders list, close all of those folders, and keep repeating until we're done.
+ */
+ protected void closeOpenFolders() {
+ // we're no longer accepting additional opens. Any folders that open after this point will get an
+ // exception trying to get a connection.
+ closedForBusiness = true;
+
+ while (true) {
+ List folders = null;
+
+ // grab our lock, copy the open folders reference, and null this out. Once we see a null
+ // open folders ref, we're done closing.
+ synchronized(connectionPool) {
+ folders = openFolders;
+ openFolders = new LinkedList();
+ }
+
+ // null folder, we're done
+ if (folders.isEmpty()) {
+ return;
+ }
+ // now close each of the open folders.
+ for (int i = 0; i < folders.size(); i++) {
+ IMAPFolder folder = (IMAPFolder)folders.get(i);
+ try {
+ folder.close(false);
+ } catch (MessagingException e) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the namespace information from the IMAP server.
+ *
+ * @return An IMAPNamespaceResponse with the namespace information.
+ * @exception MessagingException
+ */
+ protected IMAPNamespaceResponse getNamespaces() throws MessagingException {
+ // get our private connection for access
+ IMAPConnection connection = getStoreConnection();
+ try {
+ // request the namespace information from the server
+ return connection.getNamespaces();
+ } finally {
+ releaseStoreConnection(connection);
+ }
+ }
+
+
+ /**
+ * Convert a List of IMAPNamespace definitions into an array of Folder
+ * instances.
+ *
+ * @param namespaces The namespace List
+ *
+ * @return An array of the same size as the namespace list containing a Folder
+ * instance for each defined namespace.
+ * @exception MessagingException
+ */
+ protected Folder[] getNamespaceFolders(List namespaces) throws MessagingException {
+ Folder[] folders = new Folder[namespaces.size()];
+
+ // convert each of these to a Folder instance.
+ for (int i = 0; i < namespaces.size(); i++) {
+ IMAPNamespace namespace = (IMAPNamespace)namespaces.get(i);
+ folders[i] = new IMAPNamespaceFolder(this, namespace);
+ }
+ return folders;
+ }
+
+
+ /**
+ * Test if this connection has a given capability.
+ *
+ * @param capability The capability name.
+ *
+ * @return true if this capability is in the list, false for a mismatch.
+ */
+ public boolean hasCapability(String capability) {
+ return connectionPool.hasCapability(capability);
+ }
+
+
+ /**
+ * Handle an unsolicited response from the server. Most unsolicited responses
+ * are replies to specific commands sent to the server. The remainder must
+ * be handled by the Store or the Folder using the connection. These are
+ * critical to handle, as events such as expunged messages will alter the
+ * sequence numbers of the live messages. We need to keep things in sync.
+ *
+ * @param response The UntaggedResponse to process.
+ *
+ * @return true if we handled this response and no further handling is required. false
+ * means this one wasn't one of ours.
+ */
+ public boolean handleResponse(IMAPUntaggedResponse response) {
+ // Some sort of ALERT response from the server?
+ // we need to broadcast this to any of the listeners
+ if (response.isKeyword("ALERT")) {
+ notifyStoreListeners(StoreEvent.ALERT, ((IMAPOkResponse)response).getMessage());
+ return true;
+ }
+ // potentially some sort of unsolicited OK notice. This is also an event.
+ else if (response.isKeyword("OK")) {
+ String message = ((IMAPOkResponse)response).getMessage();
+ if (message.length() > 0) {
+ notifyStoreListeners(StoreEvent.NOTICE, message);
+ }
+ return true;
+ }
+ // potentially some sort of unsolicited notice. This is also an event.
+ else if (response.isKeyword("BAD") || response.isKeyword("NO")) {
+ String message = ((IMAPServerStatusResponse)response).getMessage();
+ if (message.length() > 0) {
+ notifyStoreListeners(StoreEvent.NOTICE, message);
+ }
+ return true;
+ }
+ // this is a BYE response on our connection. Folders should be handling the
+ // BYE events on their connections, so we should only be seeing this if
+ // it's on the store connection.
+ else if (response.isKeyword("BYE")) {
+ // this is essentially a close event. We need to clean everything up
+ try {
+ close();
+ } catch (MessagingException e) {
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Finalizer to perform IMAPStore() cleanup when
+ * no longer in use.
+ *
+ * @exception Throwable
+ */
+ protected void finalize() throws Throwable {
+ super.finalize();
+ close();
+ }
+
+ /**
+ * Retrieve the protocol properties for the Store.
+ *
+ * @return The protocol properties bundle.
+ */
+ ProtocolProperties getProperties() {
+ return props;
+ }
+}
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/Rights.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/Rights.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/Rights.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/Rights.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,303 @@
+/**
+ * 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.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Represents a set of rights associated with a user to manipulate the
+ * IMAP Store.
+ */
+public class Rights implements Cloneable {
+
+ /**
+ * An individual right for IMAP Store manipulation.
+ */
+ public static final class Right {
+ // The set of created stores. The getInstance() method ensures
+ // that each right is a singleton object.
+ static private Map rights = new HashMap();
+
+ /**
+ * lookup (mailbox is visible to LIST/LSUB commands)
+ */
+ public static final Right LOOKUP = getInstance('l');
+ /**
+ * read (SELECT the mailbox, perform CHECK, FETCH, PARTIAL,
+ * SEARCH, COPY from mailbox)
+ */
+ public static final Right READ = getInstance('r');
+ /**
+ * keep seen/unseen information across sessions (STORE SEEN flag)
+ */
+ public static final Right KEEP_SEEN = getInstance('s');
+ /**
+ * write (STORE flags other than SEEN and DELETED)
+ */
+ public static final Right WRITE = getInstance('w');
+ /**
+ * insert (perform APPEND, COPY into mailbox)
+ */
+ public static final Right INSERT = getInstance('i');
+ /**
+ * post (send mail to submission address for mailbox,
+ * not enforced by IMAP4 itself)
+ */
+ public static final Right POST = getInstance('p');
+ /**
+ * create (CREATE new sub-mailboxes in any implementation-defined
+ * hierarchy)
+ */
+ public static final Right CREATE = getInstance('c');
+ /**
+ * delete (STORE DELETED flag, perform EXPUNGE)
+ */
+ public static final Right DELETE = getInstance('d');
+ /**
+ * administer (perform SETACL)
+ */
+ public static final Right ADMINISTER = getInstance('a');
+
+ // the actual right definition
+ String right;
+
+ /**
+ * Private constructor for an individual Right. Used by getInstance().
+ *
+ * @param right The String name of the right (a single character).
+ */
+ private Right(String right) {
+ this.right = right;
+ }
+
+ /**
+ * Get an instance for a right from the single character right value. The
+ * returned instance will be a singleton for that character value.
+ *
+ * @param right The right character value.
+ *
+ * @return A Right instance that's the mapping for the character value.
+ */
+ public static synchronized Right getInstance(char right) {
+ String name = String.valueOf(right);
+ Right instance = (Right)rights.get(name);
+ if (instance == null) {
+ instance = new Right(name);
+ rights.put(name, instance);
+ }
+ return instance;
+ }
+
+ /**
+ * Return the string value of the Right. The string value is the character
+ * used to create the Right with newInstance().
+ *
+ * @return The string representation of the Right.
+ */
+ public String toString() {
+ return right;
+ }
+ }
+
+ /**
+ * The set of Rights contained in this instance. This is a TreeSet so that
+ * we can create the string value more consistently.
+ */
+ private SortedSet rights = new TreeSet(new RightComparator());
+
+ /**
+ * Construct an empty set of Rights.
+ */
+ public Rights() {
+ }
+
+ /**
+ * Construct a Rights set from a single Right instance.
+ *
+ * @param right The source Right.
+ */
+ public Rights(Right right) {
+ rights.add(right);
+ }
+
+ /**
+ * Construct a set of rights from an existing Rights set. This will copy
+ * the rights values.
+ *
+ * @param list The source Rights instance.
+ */
+ public Rights(Rights list) {
+ add(list);
+ Rights[] otherRights = list.getRights();
+ for (int i = 0; i < otherRights.length; i++) {
+ rights.add(otherRights[i]);
+ }
+ }
+
+ /**
+ * Construct a Rights et from a character string. Each character in the
+ * string represents an individual Right.
+ *
+ * @param list The source set of rights.
+ */
+ public Rights(String list) {
+ for (int i = 0; i < list.length(); i++) {
+ rights.add(Right.getInstance(list.charAt(i)));
+ }
+ }
+
+ /**
+ * Add a single Right to the set.
+ *
+ * @param right The new Right. If the Rigtht is already part of the Set, this is a nop.
+ */
+ public void add(Right right) {
+ rights.add(right);
+ }
+
+ /**
+ * Merge a Rights set with this set. Duplicates are eliminated.
+ *
+ * @param list The source for the added Rights.
+ */
+ public void add(Rights list) {
+ Rights[] otherRights = list.getRights();
+ for (int i = 0; i < otherRights.length; i++) {
+ rights.add(otherRights[i]);
+ }
+ }
+
+ /**
+ * Clone a set of Rights.
+ */
+ public Object clone() {
+ return new Rights(this);
+ }
+
+ /**
+ * Test if a Rights set contains a given Right.
+ *
+ * @param right The Right instance to test.
+ *
+ * @return true if the Right exists in the Set, false otherwise.
+ */
+ public boolean contains(Right right) {
+ return rights.contains(right);
+ }
+
+ /**
+ * Test if this Rights set contains all of the Rights contained in another
+ * set.
+ *
+ * @param list The source Rights set for the test.
+ *
+ * @return true if all of the Rights in the source set exist in the target set.
+ */
+ public boolean contains(Rights list) {
+ return rights.containsAll(list.rights);
+ }
+
+ /**
+ * Test if two Rights sets are equivalent.
+ *
+ * @param list The source rights set.
+ *
+ * @return true if both Rigths sets contain the same Rights values.
+ */
+ public boolean equals(Rights list) {
+ return rights.equals(list.rights);
+ }
+
+ /**
+ * Get an array of Rights contained in the set.
+ *
+ * @return An array of Rights[] values.
+ */
+ public Rights[] getRights() {
+ Rights[] list = new Rights[rights.size()];
+ return (Rights[])rights.toArray(list);
+ }
+
+ /**
+ * Compute a hashCode for the Rights set.
+ *
+ * @return The computed hashCode.
+ */
+ public int hashCode() {
+ return rights.hashCode();
+ }
+
+ /**
+ * Remove a Right from the set.
+ *
+ * @param right The single Right to remove.
+ */
+ public void remove(Right right) {
+ rights.remove(right);
+ }
+
+ /**
+ * Remove a set of rights from the set.
+ *
+ * @param list The list of rights to be removed.
+ */
+ public void remove(Rights list) {
+ rights.removeAll(list.rights);
+ }
+
+ /**
+ * Return a string value for the Rights set. The string value is the
+ * concatenation of the single-character Rights names.
+ *
+ * @return The string representation of this Rights set.
+ */
+ public String toString() {
+ StringBuffer buff = new StringBuffer();
+ Iterator i = rights.iterator();
+ while (i.hasNext()) {
+ buff.append(i.next().toString());
+ }
+ return buff.toString();
+ }
+
+ class RightComparator implements Comparator {
+ /**
+ * Perform a sort comparison to order two Right objects.
+ * The sort is performed using the string value.
+ *
+ * @param o1 The left comparator
+ * @param o2 The right comparator.
+ *
+ * @return 0 if the two items have equal ordering, -1 if the
+ * left item is lower, 1 if the left item is greater.
+ */
+ public int compare(Object o1, Object o2) {
+ // compare on the string value
+ String left = o1.toString();
+ return left.compareTo(o2.toString());
+ }
+ }
+
+}
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPACLResponse.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPACLResponse.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPACLResponse.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPACLResponse.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,52 @@
+/**
+ * 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.connection;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.javamail.store.imap.ACL;
+import org.apache.geronimo.javamail.store.imap.Rights;
+
+import org.apache.geronimo.javamail.store.imap.connection.IMAPResponseTokenizer.Token;
+
+/**
+ * Utility class to aggregate status responses for a mailbox.
+ */
+public class IMAPACLResponse extends IMAPUntaggedResponse {
+ public String mailbox;
+ public ACL[] acls;
+
+ public IMAPACLResponse(byte[] data, IMAPResponseTokenizer source) throws MessagingException {
+ super("ACL", data);
+
+ mailbox = source.readEncodedString();
+ List temp = new ArrayList();
+
+ while (source.hasMore()) {
+ String name = source.readString();
+ String rights = source.readString();
+ temp.add(new ACL(name, new Rights(rights)));
+ }
+
+ acls = new ACL[temp.size()];
+ acls = (ACL[])temp.toArray(acls);
+ }
+}
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBody.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBody.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBody.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBody.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,78 @@
+/**
+ * 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.connection;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import javax.mail.MessagingException;
+
+
+/**
+ * The full body content of a message.
+ */
+public class IMAPBody extends IMAPFetchBodyPart {
+ // the body content data
+ byte[] content = null;
+
+ /**
+ * Construct a top-level MessageText data item.
+ *
+ * @param data The data for the Message Text
+ *
+ * @exception MessagingException
+ */
+ public IMAPBody(byte[] data) throws MessagingException {
+ this(new IMAPBodySection(IMAPBodySection.BODY), data);
+ }
+
+ /**
+ * Create a Message Text instance.
+ *
+ * @param section The section information. This may include substring information if this
+ * was just a partical fetch.
+ * @param data The message content data.
+ *
+ * @exception MessagingException
+ */
+ public IMAPBody(IMAPBodySection section, byte[] data) throws MessagingException {
+ super(BODY, section);
+ // save the content
+ content = data;
+ }
+
+
+ /**
+ * Get the part content as a byte array.
+ *
+ * @return The part content as a byte array.
+ */
+ public byte[] getContent() {
+ return content;
+ }
+
+ /**
+ * Get an input stream for reading the part content.
+ *
+ * @return An ByteArrayInputStream sourced to the part content.
+ */
+ public InputStream getInputStream() {
+ return new ByteArrayInputStream(content);
+ }
+}
+
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBodySection.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBodySection.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBodySection.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBodySection.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,273 @@
+/**
+ * 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.connection;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.javamail.util.ResponseFormatException;
+import org.apache.geronimo.javamail.store.imap.connection.IMAPResponseTokenizer.Token;
+
+/**
+ * Class to represent a FETCH response BODY segment qualifier. The qualifier is
+ * of the form "BODY[<section>]<<partial>>". The optional section qualifier is
+ * a "." separated part specifiers. A part specifier is either a number, or
+ * one of the tokens HEADER, HEADER.FIELD, HEADER.FIELD.NOT, MIME, and TEXT.
+ * The partial specification is in the form "<start.length>".
+ *
+ * @version $Rev$ $Date$
+ */
+public class IMAPBodySection {
+ // the section type qualifiers
+ static public final int BODY = 0;
+ static public final int HEADERS = 1;
+ static public final int HEADERSUBSET = 2;
+ static public final int MIME = 3;
+ static public final int TEXT = 4;
+
+ // the optional part number
+ public String partNumber = "1";
+ // the string name of the section
+ public String sectionName = "";
+ // the section qualifier
+ public int section;
+ // the starting substring position
+ public int start = -1;
+ // the substring length (requested)
+ public int length = -1;
+ // the list of any explicit header names
+ public List headers = null;
+
+ /**
+ * Construct a simple-toplevel BodySection tag.
+ *
+ * @param section The section identifier.
+ */
+ public IMAPBodySection(int section) {
+ this.section = section;
+ partNumber = "1";
+ start = -1;
+ length = -1;
+ }
+
+ /**
+ * construct a BodySegment descriptor from the FETCH returned name.
+ *
+ * @param name The name code, which may be encoded with a section identifier and
+ * substring qualifiers.
+ *
+ * @exception MessagingException
+ */
+ public IMAPBodySection(IMAPResponseTokenizer source) throws MessagingException {
+
+ // this could be just "BODY" alone.
+ if (!source.peek(false, true).isType('[')) {
+ // complete body, all other fields take default
+ section = BODY;
+ return;
+ }
+
+ // now we need to scan along this, building up the pieces as we go.
+ // NOTE: The section identifiers use "[", "]", "." as delimiters, which
+ // are normally acceptable in ATOM names. We need to use the expanded
+ // delimiter set to parse these tokens off.
+ Token token = source.next(false, true);
+ // the first token was the "[", now step to the next token in line.
+ token = source.next(false, true);
+
+ if (token.isType(Token.NUMERIC)) {
+ token = parsePartNumber(token, source);
+ }
+
+ // have a potential name here?
+ if (token.isType(Token.ATOM)) {
+ token = parseSectionName(token, source);
+ }
+
+ // the HEADER.FIELD and HEADER.FIELD.NOT section types
+ // are followed by a list of header names.
+ if (token.isType('(')) {
+ token = parseHeaderList(source);
+ }
+
+ // ok, in theory, our current token should be a ']'
+ if (!token.isType(']')) {
+ throw new ResponseFormatException("Invalid section identifier on FETCH response");
+ }
+
+ // do we have a substring qualifier?
+ // that needs to be stripped off too
+ parseSubstringValues(source);
+
+ // now fill in the type information
+ if (sectionName.equals("")) {
+ section = BODY;
+ }
+ else if (sectionName.equals("HEADER")) {
+ section = HEADERS;
+ }
+ else if (sectionName.equals("HEADER.FIELDS")) {
+ section = HEADERSUBSET;
+ }
+ else if (sectionName.equals("HEADER.FIELDS.NOT")) {
+ section = HEADERSUBSET;
+ }
+ else if (sectionName.equals("TEXT")) {
+ section = TEXT;
+ }
+ else if (sectionName.equals("MIME")) {
+ section = MIME;
+ }
+ }
+
+
+ /**
+ * Strip the part number off of a BODY section identifier. The part number
+ * is a series of "." separated tokens. So "BODY[3.2.1]" would be the BODY for
+ * section 3.2.1 of a multipart message. The section may also have a qualifier
+ * name on the end. "BODY[3.2.1.HEADER}" would be the HEADERS for that
+ * body section. The return value is the name of the section, which can
+ * be a "" or the the section qualifier (e.g., "HEADER").
+ *
+ * @param name The section name.
+ *
+ * @return The remainder of the section name after the numeric part number has
+ * been removed.
+ */
+ private Token parsePartNumber(Token token, IMAPResponseTokenizer source) throws MessagingException {
+ StringBuffer part = new StringBuffer(token.getValue());
+ // NB: We're still parsing with the expanded delimiter set
+ token = source.next(false, true);
+
+ while (true) {
+ // Not a period? We've reached the end of the section number,
+ // finalize the part number and let the caller figure out what
+ // to do from here.
+ if (!token.isType('.')) {
+ partNumber = part.toString();
+ return token;
+ }
+ // might have another number section
+ else {
+ // step to the next token
+ token = source.next(false, true);
+ // another section number piece?
+ if (token.isType(Token.NUMERIC)) {
+ // add this to the collection, and continue
+ part.append('.');
+ part.append(token.getValue());
+ token = source.next(false, true);
+ }
+ else {
+ partNumber = part.toString();
+ // this is likely the start of the section name
+ return token;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Parse the section name, if any, in a BODY section qualifier. The
+ * section name may stand alone within the body section (e.g.,
+ * "BODY[HEADERS]" or follow the section number (e.g.,
+ * "BODY[1.2.3.HEADERS.FIELDS.NOT]".
+ *
+ * @param token The first token of the name sequence.
+ * @param source The source tokenizer.
+ *
+ * @return The first non-name token in the response.
+ */
+ private Token parseSectionName(Token token, IMAPResponseTokenizer source) throws MessagingException {
+ StringBuffer part = new StringBuffer(token.getValue());
+ // NB: We're still parsing with the expanded delimiter set
+ token = source.next(false, true);
+
+ while (true) {
+ // Not a period? We've reached the end of the section number,
+ // finalize the part number and let the caller figure out what
+ // to do from here.
+ if (!token.isType('.')) {
+ sectionName = part.toString();
+ return token;
+ }
+ // might have another number section
+ else {
+ // add this to the collection, and continue
+ part.append('.');
+ part.append(source.readString());
+ token = source.next(false, true);
+ }
+ }
+ }
+
+
+ /**
+ * Parse a header list that may follow the HEADER.FIELD or HEADER.FIELD.NOT
+ * name qualifier. This is a list of string values enclosed in parens.
+ *
+ * @param source The source tokenizer.
+ *
+ * @return The next token in the response (which should be the section terminator, ']')
+ * @exception MessagingException
+ */
+ private Token parseHeaderList(IMAPResponseTokenizer source) throws MessagingException {
+ headers = new ArrayList();
+
+ // normal parsing rules going on here
+ while (source.notListEnd()) {
+ String value = source.readString();
+ headers.add(value);
+ }
+ // step over the closing paren
+ source.next();
+ // NB, back to the expanded token rules again
+ return source.next(false, true);
+ }
+
+
+ /**
+ * Parse off the substring values following the section identifier, if
+ * any. If present, they will be in the format "<start.len>".
+ *
+ * @param source The source tokenizer.
+ *
+ * @exception MessagingException
+ */
+ private void parseSubstringValues(IMAPResponseTokenizer source) throws MessagingException {
+ // We rarely have one of these, so it's a quick out
+ if (!source.peek(false, true).isType('<')) {
+ return;
+ }
+ // step over the angle bracket.
+ source.next(false, true);
+ // pull out the start information
+ start = source.next(false, true).getInteger();
+ // step over the period
+ source.next(false, true);
+ // now the length bit
+ length = source.next(false, true).getInteger();
+ // and consume the closing angle bracket
+ source.next(false, true);
+ }
+}
+
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBodyStructure.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBodyStructure.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBodyStructure.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPBodyStructure.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,228 @@
+/**
+ * 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.connection;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.ContentDisposition;
+import javax.mail.internet.ContentType;
+
+import org.apache.geronimo.javamail.util.ResponseFormatException;
+
+
+public class IMAPBodyStructure extends IMAPFetchDataItem {
+
+ // the MIME type information
+ public ContentType mimeType = new ContentType();
+ // the content disposition info
+ public ContentDisposition disposition = null;
+ // the message ID
+ public String contentID;
+ public String contentDescription;
+ public String transferEncoding;
+ // size of the message
+ public int bodySize;
+ // number of lines, which only applies to text types.
+ public int lines = -1;
+
+ // "parts is parts". If this is a multipart message, we have a body structure item for each subpart.
+ public IMAPBodyStructure[] parts;
+ // optional dispostiion parameters
+ public Map dispositionParameters;
+ // language parameters
+ public List languages;
+ // the MD5 hash
+ public String md5Hash;
+
+ // references to nested message information.
+ public IMAPEnvelope nestedEnvelope;
+ public IMAPBodyStructure nestedBody;
+
+
+ public IMAPBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
+ super(BODYSTRUCTURE);
+ parseBodyStructure(source);
+ }
+
+
+ protected void parseBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
+ // the body structure needs to start with a left paren
+ source.checkLeftParen();
+
+ // if we start with a parentized item, we have a multipart content type. We need to
+ // recurse on each of those as appropriate
+ if (source.peek().getType() == '(') {
+ parseMultipartBodyStructure(source);
+ }
+ else {
+ parseSinglepartBodyStructure(source);
+ }
+ }
+
+
+ protected void parseMultipartBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
+ mimeType.setPrimaryType("multipart");
+ ArrayList partList = new ArrayList();
+
+ do {
+ // parse the subpiece (which might also be a multipart).
+ IMAPBodyStructure part = new IMAPBodyStructure(source);
+ partList.add(part);
+ // we keep doing this as long as we seen parenthized items.
+ } while (source.peek().getType() == '(');
+
+ parts = (IMAPBodyStructure[])partList.toArray(new IMAPBodyStructure[partList.size()]);
+
+ // get the subtype (required)
+ mimeType.setSubType(source.readString());
+
+ // if the next token is the list terminator, we're done. Otherwise, we need to read extension
+ // data.
+ if (source.checkListEnd()) {
+ return;
+ }
+ // read the content parameter information and copy into the ContentType.
+ mimeType.setParameterList(source.readParameterList());
+
+ // more optional stuff
+ if (source.checkListEnd()) {
+ return;
+ }
+
+ // go parse the extensions that are common to both single- and multi-part messages.
+ parseMessageExtensions(source);
+ }
+
+
+ protected void parseSinglepartBodyStructure(IMAPResponseTokenizer source) throws MessagingException {
+ // get the primary and secondary types.
+ mimeType.setPrimaryType(source.readString());
+ mimeType.setSubType(source.readString());
+
+ // read the parameters associated with the content type.
+ mimeType.setParameterList(source.readParameterList());
+
+ // now a bunch of string value parameters
+ contentID = source.readStringOrNil();
+ contentDescription = source.readStringOrNil();
+ transferEncoding = source.readStringOrNil();
+ bodySize = source.readInteger();
+
+ // is this an embedded message type? Embedded messages include envelope and body structure
+ // information for the embedded message next.
+ if (mimeType.match("message/rfc822")) {
+ // parse the nested information
+ nestedEnvelope = new IMAPEnvelope(source);
+ nestedBody = new IMAPBodyStructure(source);
+ lines = source.readInteger();
+ }
+ // text types include a line count
+ else if (mimeType.match("text/*")) {
+ lines = source.readInteger();
+ }
+
+ // now the optional extension data. All of these are optional, but must be in the specified order.
+ if (source.checkListEnd()) {
+ return;
+ }
+
+ md5Hash = source.readString();
+
+ // go parse the extensions that are common to both single- and multi-part messages.
+ parseMessageExtensions(source);
+ }
+
+ /**
+ * Parse common message extension information shared between
+ * single part and multi part messages.
+ *
+ * @param source The source tokenizer..
+ */
+ protected void parseMessageExtensions(IMAPResponseTokenizer source) throws MessagingException {
+
+ // now the optional extension data. All of these are optional, but must be in the specified order.
+ if (source.checkListEnd()) {
+ return;
+ }
+
+ disposition = new ContentDisposition();
+ // now the dispostion. This is a string, followed by a parameter list.
+ if (source.peek(true).getType() == '(') {
+ source.checkLeftParen();
+ disposition.setDisposition(source.readString());
+ disposition.setParameterList(source.readParameterList());
+ source.checkRightParen();
+ } else if (source.peek(true) == IMAPResponseTokenizer.NIL) {
+ source.next();
+ } else {
+ throw new ResponseFormatException("Expecting NIL or '(' in response");
+ }
+
+ // once more
+ if (source.checkListEnd()) {
+ return;
+ }
+ // read the language info.
+ languages = source.readStringList();
+ // next is the body location information. The Javamail APIs don't really expose that, so
+ // we'll just skip over that.
+
+ // once more
+ if (source.checkListEnd()) {
+ return;
+ }
+ // read the location info.
+ source.readStringList();
+
+ // we don't recognize any other forms of extension, so just skip over these.
+ while (source.notListEnd()) {
+ source.skipExtensionItem();
+ }
+
+ // step over the closing paren
+ source.next();
+ }
+
+
+ /**
+ * Tests if a body structure is for a multipart body.
+ *
+ * @return true if this is a multipart body part, false for a single part.
+ */
+ public boolean isMultipart() {
+ return parts != null;
+ }
+
+
+ /**
+ * Test if this body structure represents an attached message. If it's a
+ * message, this will be a single part of MIME type message/rfc822.
+ *
+ * @return True if this is a nested message type, false for either a multipart or
+ * a single part of another type.
+ */
+ public boolean isAttachedMessage() {
+ return !isMultipart() && mimeType.match("message/rfc822");
+ }
+}
+
Added: geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPCapabilityResponse.java
URL: http://svn.apache.org/viewvc/geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPCapabilityResponse.java?rev=1882306&view=auto
==============================================================================
--- geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPCapabilityResponse.java (added)
+++ geronimo/javamail/trunk/geronimo-javamail_1.6/geronimo-javamail_1.6_provider/src/main/java/org/apache/geronimo/javamail/store/imap/connection/IMAPCapabilityResponse.java Wed Oct 7 15:58:39 2020
@@ -0,0 +1,90 @@
+/**
+ * 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.connection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.mail.MessagingException;
+
+import org.apache.geronimo.javamail.store.imap.connection.IMAPResponseTokenizer.Token;
+
+/**
+ * Util class to represent a CAPABILITY response from a IMAP server
+ *
+ * @version $Rev$ $Date$
+ */
+public class IMAPCapabilityResponse extends IMAPUntaggedResponse {
+ // the advertised capabilities
+ protected Map capabilities = new HashMap();
+ // the authentication mechanisms. The order is important with
+ // the authentications, as we a) want to process these in the
+ // order presented, and b) need to convert them into String arrays
+ // for Sasl API calls.
+ protected List authentications = new ArrayList();
+
+ /**
+ * Create a reply object from a server response line (normally, untagged). This includes
+ * doing the parsing of the response line.
+ *
+ * @param response The response line used to create the reply object.
+ */
+ public IMAPCapabilityResponse(IMAPResponseTokenizer source, byte [] response) throws MessagingException {
+ super("CAPABILITY", response);
+
+ // parse each of the capability tokens. We're using the default RFC822 parsing rules,
+ // which does not consider "=" to be a delimiter token, so all "AUTH=" capabilities will
+ // come through as a single token.
+ while (source.hasMore()) {
+ // the capabilities are always ATOMs.
+ String value = source.readAtom().toUpperCase();
+ // is this an authentication option?
+ if (value.startsWith("AUTH=")) {
+ // parse off the mechanism that fillows the "=", and add this to the supported list.
+ String mechanism = value.substring(5);
+ authentications.add(mechanism);
+ }
+ else {
+ // just add this to the capabilities map.
+ capabilities.put(value, value);
+ }
+ }
+ }
+
+
+ /**
+ * Return the capability map for the server.
+ *
+ * @return A map of the capability items.
+ */
+ public Map getCapabilities() {
+ return capabilities;
+ }
+
+ /**
+ * Retrieve the map of the server-supported authentication
+ * mechanisms.
+ *
+ * @return
+ */
+ public List getAuthentications() {
+ return authentications;
+ }
+}
+