FileDocCategorySizeDatePackage
IMAPStore.javaAPI DocGlassfish v2 API55911Mon May 14 15:28:44 BST 2007com.sun.mail.imap

IMAPStore

public class IMAPStore extends Store implements QuotaAwareStore, ResponseHandler
This class provides access to an IMAP message store.

Applications that need to make use of IMAP-specific features may cast a Store object to an IMAPStore object and use the methods on this class. The {@link #getQuota getQuota} and {@link #setQuota setQuota} methods support the IMAP QUOTA extension. Refer to RFC 2087 for more information.

See the com.sun.mail.imap package documentation for further information on the IMAP protocol provider.

WARNING: The APIs unique to this class should be considered EXPERIMENTAL. They may be changed in the future in ways that are incompatible with applications using the current APIs.

version
1.72, 07/05/04
author
John Mani
author
Bill Shannon
author
Jim Glennon

Fields Summary
public static final int
RESPONSE
A special event type for a StoreEvent to indicate an IMAP response, if the mail.imap.enableimapevents property is set.
private String
name
private int
defaultPort
private boolean
isSSL
private int
port
private int
blksize
private int
statusCacheTimeout
private int
appendBufferSize
private int
minIdleTime
private String
host
private String
user
private String
password
private String
proxyAuthUser
private String
authorizationID
private String
saslRealm
private Namespaces
namespaces
private boolean
disableAuthLogin
private boolean
disableAuthPlain
private boolean
enableStartTLS
private boolean
enableSASL
private String[]
saslMechanisms
private boolean
forcePasswordRefresh
private boolean
enableImapEvents
private volatile boolean
connected
private PrintStream
out
private ConnectionPool
pool
Constructors Summary
public IMAPStore(Session session, URLName url)
Constructor that takes a Session object and a URLName that represents a specific IMAP server.


 
                        
         
	this(session, url, "imap", 143, false);
    
protected IMAPStore(Session session, URLName url, String name, int defaultPort, boolean isSSL)
Constructor used by this class and by IMAPSSLStore subclass.

	super(session, url); // call super constructor
	if (url != null)
	    name = url.getProtocol();
	this.name = name;
	this.defaultPort = defaultPort;
	this.isSSL = isSSL;

        pool.lastTimePruned = System.currentTimeMillis();

        debug = session.getDebug();
	out = session.getDebugOut();
	if (out == null)	// should never happen
	    out = System.out;

        String s = session.getProperty(
	    "mail." + name + ".connectionpool.debug");        

        if (s != null && s.equalsIgnoreCase("true"))
            pool.debug = true;

	s = session.getProperty("mail." + name + ".partialfetch");

	if (s != null && s.equalsIgnoreCase("false")) {
	    // property exits and is set to false
	    blksize = -1; // turn off partial-fetch
	    if (debug)
		out.println("DEBUG: mail.imap.partialfetch: false");
	} else { // either property doesn't exist, or its set to true
	    if ((s = session.getProperty("mail." + name +".fetchsize"))
		     != null)
		// Set the block size to be used in FETCH requests
		blksize = Integer.parseInt(s);
                if (debug)
                    out.println("DEBUG: mail.imap.fetchsize: " + blksize);
	}

	s = session.getProperty("mail." + name + ".statuscachetimeout");
	if (s != null) {
	    statusCacheTimeout = Integer.parseInt(s);
	    if (debug)
		out.println("DEBUG: mail.imap.statuscachetimeout: " +
						statusCacheTimeout);
	}
	s = session.getProperty("mail." + name + ".appendbuffersize");
	if (s != null) {
	    appendBufferSize = Integer.parseInt(s);
	    if (debug)
		out.println("DEBUG: mail.imap.appendbuffersize: " +
						appendBufferSize);
	}
	s = session.getProperty("mail." + name + ".minidletime");
	if (s != null) {
	    minIdleTime = Integer.parseInt(s);
	    if (debug)
		out.println("DEBUG: mail.imap.minidletime: " + minIdleTime);
	}

        // check if the default connection pool size is overridden
        s = session.getProperty("mail." + name + ".connectionpoolsize");
        if (s != null) {
            try {
                int size = Integer.parseInt(s);
                if (size > 0)
                    pool.poolSize = size;
            } catch (NumberFormatException nfe) {
            }
            if (pool.debug)
                out.println("DEBUG: mail.imap.connectionpoolsize: " +
                    pool.poolSize);
        }


        // check if the default client-side timeout value is overridden
        s = session.getProperty("mail." + name + ".connectionpooltimeout");
        if (s != null) {
            try {
                int connectionPoolTimeout = Integer.parseInt(s);
                if (connectionPoolTimeout > 0)
                    pool.clientTimeoutInterval = connectionPoolTimeout;
            } catch (NumberFormatException nfe) {
            }
            if (pool.debug)
                out.println("DEBUG: mail.imap.connectionpooltimeout: " +
                    pool.clientTimeoutInterval);
        } 

        // check if the default server-side timeout value is overridden
        s = session.getProperty("mail." + name + ".servertimeout");
        if (s != null) {
            try {
                int serverTimeout = Integer.parseInt(s);
                if (serverTimeout > 0)
                    pool.serverTimeoutInterval = serverTimeout;
            } catch (NumberFormatException nfe) {
            }
            if (pool.debug)
                out.println("DEBUG: mail.imap.servertimeout: " +
                    pool.serverTimeoutInterval);
        } 
 
        // check to see if we should use a separate (i.e. dedicated)
        // store connection
        s = session.getProperty("mail." + name + ".separatestoreconnection");
        if (s != null && s.equalsIgnoreCase("true")) {
            if (pool.debug)
                out.println("DEBUG: dedicate a store connection");
            pool.separateStoreConnection = true;
        }

	// check if we should do a PROXYAUTH login
	s = session.getProperty("mail." + name + ".proxyauth.user");
	if (s != null) {
	    proxyAuthUser = s;
	    if (debug)
		out.println("DEBUG: mail.imap.proxyauth.user: " +
						proxyAuthUser);
	}

	// check if AUTH=LOGIN is disabled
	s = session.getProperty("mail." + name + ".auth.login.disable");
	if (s != null && s.equalsIgnoreCase("true")) {
	    if (debug)
		out.println("DEBUG: disable AUTH=LOGIN");
	    disableAuthLogin = true;
	}

	// check if AUTH=PLAIN is disabled
	s = session.getProperty("mail." + name + ".auth.plain.disable");
	if (s != null && s.equalsIgnoreCase("true")) {
	    if (debug)
		out.println("DEBUG: disable AUTH=PLAIN");
	    disableAuthPlain = true;
	}

	// check if STARTTLS is enabled
	s = session.getProperty("mail." + name + ".starttls.enable");
	if (s != null && s.equalsIgnoreCase("true")) {
	    if (debug)
		out.println("DEBUG: enable STARTTLS");
	    enableStartTLS = true;
	}

	// check if SASL is enabled
	s = session.getProperty("mail." + name + ".sasl.enable");
	if (s != null && s.equalsIgnoreCase("true")) {
	    if (debug)
		out.println("DEBUG: enable SASL");
	    enableSASL = true;
	}

	// check if SASL mechanisms are specified
	if (enableSASL) {
	    s = session.getProperty("mail." + name + ".sasl.mechanisms");
	    if (s != null && s.length() > 0) {
		if (debug)
		    out.println("DEBUG: SASL mechanisms allowed: " + s);
		Vector v = new Vector(5);
		StringTokenizer st = new StringTokenizer(s, " ,");
		while (st.hasMoreTokens()) {
		    String m = st.nextToken();
		    if (m.length() > 0)
			v.addElement(m);
		}
		saslMechanisms = new String[v.size()];
		v.copyInto(saslMechanisms);
	    }
	}

	// check if an authorization ID has been specified
	s = session.getProperty("mail." + name + ".sasl.authorizationid");
	if (s != null) {
	    authorizationID = s;
	    if (debug)
		out.println("DEBUG: mail.imap.sasl.authorizationid: " +
						authorizationID);
	}

	// check if a SASL realm has been specified
	s = session.getProperty("mail." + name + ".sasl.realm");
	if (s != null) {
	    saslRealm = s;
	    if (debug)
		out.println("DEBUG: mail.imap.sasl.realm: " + saslRealm);
	}

	// check if forcePasswordRefresh is enabled
	s = session.getProperty("mail." + name + ".forcepasswordrefresh");
	if (s != null && s.equalsIgnoreCase("true")) {
	    if (debug)
		out.println("DEBUG: enable forcePasswordRefresh");
	    forcePasswordRefresh = true;
	}

	// check if enableimapevents is enabled
	s = session.getProperty("mail." + name + ".enableimapevents");
	if (s != null && s.equalsIgnoreCase("true")) {
	    if (debug)
		out.println("DEBUG: enable IMAP events");
	    enableImapEvents = true;
	}
    
Methods Summary
booleanallowReadOnlySelect()
If a SELECT succeeds, but indicates that the folder is READ-ONLY, and the user asked to open the folder READ_WRITE, do we allow the open to succeed?

	String s = session.getProperty("mail." + name + 
	    ".allowreadonlyselect");        
	return s != null && s.equalsIgnoreCase("true");
    
private voidcheckConnected()

	assert Thread.holdsLock(this);
	if (!connected) {
	    super.setConnected(false);	// just in case
	    throw new IllegalStateException("Not connected");
	}
    
private voidcleanup()

	cleanup(false);
    
private voidcleanup(boolean force)
Cleanup before dying. If force is true, we force the folders to close abruptly without waiting for the server. Used when the store connection times out. Not synchronized so that it can be safely called from handleResponse.

	if (debug)
	    out.println("DEBUG: IMAPStore cleanup, force " + force);
        
        Vector foldersCopy = null;
        boolean done = true;

	// To avoid violating the locking hierarchy, there's no lock we
	// can hold that prevents another thread from trying to open a
	// folder at the same time we're trying to close all the folders.
	// Thus, there's an inherent race condition here.  We close all
	// the folders we know about and then check whether any new folders
	// have been opened in the mean time.  We keep trying until we're
	// successful in closing all the folders.
	for (;;) {
	    // Make a copy of the folders list so we do not violate the
	    // folder-connection pool locking hierarchy.
	    synchronized (pool) {
		if (pool.folders != null) {
		    done = false;
		    foldersCopy = pool.folders;
		    pool.folders = null;
		} else {
                    done = true;
                }
	    }
	    if (done)
		break;

	    // Close and remove any open folders under this Store.
	    for (int i = 0, fsize = foldersCopy.size(); i < fsize; i++) {
		IMAPFolder f = (IMAPFolder)foldersCopy.elementAt(i);

		try {
		    if (force) {
			if (debug)
			    out.println("DEBUG: force folder to close");
			// Don't want to wait for folder connection to timeout
			// (if, for example, the server is down) so we close
			// folders abruptly.
			f.forceClose();
		    } else {
			if (debug)
			    out.println("DEBUG: close folder");
			f.close(false);
		    }
		} catch (MessagingException mex) {
		    // Who cares ?! Ignore 'em.
		} catch (IllegalStateException ex) {
		    // Ditto
		}
	    }

	}

        synchronized (pool) {
	    emptyConnectionPool(force);
	}

	connected = false;
	notifyConnectionListeners(ConnectionEvent.CLOSED);
	if (debug)
	    out.println("DEBUG: IMAPStore cleanup done");
    
public synchronized voidclose()
Close this Store.

	if (!super.isConnected()) // Already closed.
	    return;

        IMAPProtocol protocol = null;
	try {
	    boolean isEmpty;
	    synchronized (pool) {
		// If there's no authenticated connections available
		// don't create a new one
		isEmpty = pool.authenticatedConnections.isEmpty();
	    }
	    /*
	     * Have to drop the lock before calling cleanup.
	     * Yes, there's a potential race here.  The pool could
	     * become empty after we check, in which case we'll just
	     * waste time getting a new connection and closing it.
	     * Or, the pool could be empty now and not empty by the
	     * time we get into cleanup, but that's ok because cleanup
	     * will just close the connection.
	     */
	    if (isEmpty) {
		if (pool.debug)
		    out.println("DEBUG: close() - no connections ");
		cleanup();
		return;
	    }

            protocol = getStoreProtocol();
	    /*
	     * We have to remove the protocol from the pool so that,
	     * when our response handler processes the BYE response
	     * and calls cleanup, which calls emptyConnection, that
	     * we don't try to log out this connection twice.
	     */
	    synchronized (pool) {
                pool.authenticatedConnections.removeElement(protocol);
	    }

	    /*
	     * LOGOUT. 
	     *
	     * Note that protocol.logout() closes the server socket
	     * connection, regardless of what happens ..
	     *
	     * Also note that protocol.logout() results in a BYE
	     * response (As per rfc 2060, BYE is a *required* response
	     * to LOGOUT). In fact, even if protocol.logout() fails
	     * with an IOException (if the server connection is dead),
	     * iap.Protocol.command() converts that exception into a 
	     * BYE response. So, I depend on my BYE handler to do the
	     * Store cleanup.
	     */
	    protocol.logout();
	} catch (ProtocolException pex) { 
	    // Hmm .. will this ever happen ?
	    cleanup();
	    throw new MessagingException(pex.getMessage(), pex);
        } finally {
            releaseStoreProtocol(protocol);
        }
    
private voidemptyConnectionPool(boolean force)
Empty the connection pool.


        synchronized (pool) {
            for (int index = pool.authenticatedConnections.size() - 1;
		    index >= 0; --index) {
                try {
		    IMAPProtocol p = (IMAPProtocol)
			pool.authenticatedConnections.elementAt(index);
		    p.removeResponseHandler(this);
		    if (force)
			p.disconnect();
		    else
			p.logout();
                } catch (ProtocolException pex) {};
            }

            pool.authenticatedConnections.removeAllElements();
        }
        
        if (pool.debug)
            out.println("DEBUG: removed all authenticated connections");
    
protected voidfinalize()

	super.finalize();
	close();
    
intgetAppendBufferSize()
Get the maximum size of a message to buffer for append.

	return appendBufferSize;
    
booleangetConnectionPoolDebug()
Report whether connection pool debugging is enabled.

        return pool.debug; 
    
public synchronized javax.mail.FoldergetDefaultFolder()
Get the default folder, representing the root of this user's namespace. Returns a closed DefaultFolder object.

	checkConnected();
	return new DefaultFolder(this);
    
intgetFetchBlockSize()
Get the block size to use for fetch requests on this Store.

	return blksize;
    
public synchronized javax.mail.FoldergetFolder(java.lang.String name)
Get named folder. Returns a new, closed IMAPFolder.

	checkConnected();
	return new IMAPFolder(name, IMAPFolder.UNKNOWN_SEPARATOR, this);
    
public synchronized javax.mail.FoldergetFolder(javax.mail.URLName url)
Get named folder. Returns a new, closed IMAPFolder.

	checkConnected();
	return new IMAPFolder(url.getFile(), 
			      IMAPFolder.UNKNOWN_SEPARATOR, 
			      this);
    
intgetMinIdleTime()
Get the minimum amount of time to delay when returning from idle.

	return minIdleTime;
    
private synchronized com.sun.mail.imap.protocol.NamespacesgetNamespaces()

	checkConnected();

        IMAPProtocol p = null;

	if (namespaces == null) {
	    try {
                p = getStoreProtocol();
		namespaces = p.namespace();
	    } catch (BadCommandException bex) { 
		// NAMESPACE not supported, ignore it
	    } catch (ConnectionException cex) {
		throw new StoreClosedException(this, cex.getMessage());
	    } catch (ProtocolException pex) { 
		throw new MessagingException(pex.getMessage(), pex);
	    } finally {
		releaseStoreProtocol(p);
		if (p == null) {	// failed to get a Store connection
		    // have to force Store to be closed
		    cleanup();
		}
	    }
	}
	return namespaces;
    
public javax.mail.Folder[]getPersonalNamespaces()
Using the IMAP NAMESPACE command (RFC 2342), return a set of folders representing the Personal namespaces.

	Namespaces ns = getNamespaces();
	if (ns == null || ns.personal == null)
	    return super.getPersonalNamespaces();
	return namespaceToFolders(ns.personal, null);
    
com.sun.mail.imap.protocol.IMAPProtocolgetProtocol(com.sun.mail.imap.IMAPFolder folder)

	IMAPProtocol p = null;

	// keep looking for a connection until we get a good one
	while (p == null) {
 
        // New authenticated protocol objects are either acquired
        // from the connection pool, or created when the pool is
        // empty or no connections are available. None are available
        // if the current pool size is one and the separate store
        // property is set or the connection is in use.

        synchronized (pool) {

            // If there's none available in the pool,
            // create a new one.
            if (pool.authenticatedConnections.isEmpty() ||
                (pool.authenticatedConnections.size() == 1 &&
                (pool.separateStoreConnection || pool.storeConnectionInUse))) {

                if (debug)
                    out.println("DEBUG: no connections in the pool, " +
                                       "creating a new one");
                try {
		    /*
		     * Some authentication systems use one time passwords
		     * or tokens, so each authentication request requires
		     * a new password.  This "kludge" allows a callback
		     * to application code to get a new password.
		     *
		     * XXX - remove this when SASL support is added
		     */
		    if (forcePasswordRefresh) {
			InetAddress addr;
			try {
			    addr = InetAddress.getByName(host);
			} catch (UnknownHostException e) {
			    addr = null;
			}
			PasswordAuthentication pa =
			    session.requestPasswordAuthentication(addr, port,
							name, null, user);
			if (pa != null) {
			    user = pa.getUserName();
			    password = pa.getPassword();
			}
		    }
                    // Use cached host, port and timeout values.
                    p = new IMAPProtocol(name, host, port,
                                         session.getDebug(),
                                         session.getDebugOut(),
                                         session.getProperties(),
					 isSSL
                                        );
                    // Use cached auth info
                    login(p, user, password);
                } catch(Exception ex1) {
                    if (p != null)
                        try {
                            p.disconnect();
                        } catch (Exception ex2) { }
                    p = null;
                }
                 
                if (p == null)
                    throw new MessagingException("connection failure");
            } else {
                if (debug)
                    out.println("DEBUG: connection available -- size: " +
                        pool.authenticatedConnections.size());

                // remove the available connection from the Authenticated queue
                p = (IMAPProtocol)pool.authenticatedConnections.lastElement();
                pool.authenticatedConnections.removeElement(p);

		// check if the connection is still live
		long lastUsed = System.currentTimeMillis() - p.getTimestamp();
		if (lastUsed > pool.serverTimeoutInterval) {
		    try {
			// note that store is still the response handler,
			// in case we get any alerts
			p.noop();
		    } catch (ProtocolException pex) {
			try {
			    p.removeResponseHandler(this);
			    p.disconnect();
			} finally {
			    // don't let any exception stop us
			    p = null;
			    continue;	// try again, from the top
			}
		    }
		}

                // remove the store as a response handler.
                p.removeResponseHandler(this);
	    }

            // check if we need to look for client-side timeouts
            timeoutConnections();

	    // Add folder to folder-list
	    if (folder != null) {
                if (pool.folders == null)
                    pool.folders = new Vector();
		pool.folders.addElement(folder);
	    }
        }

	}
	
	return p;
    
public synchronized javax.mail.Quota[]getQuota(java.lang.String root)
Get the quotas for the named quota root. Quotas are controlled on the basis of a quota root, not (necessarily) a folder. The relationship between folders and quota roots depends on the IMAP server. Some servers might implement a single quota root for all folders owned by a user. Other servers might implement a separate quota root for each folder. A single folder can even have multiple quota roots, perhaps controlling quotas for different resources.

param
root the name of the quota root
return
array of Quota objects
exception
MessagingException if the server doesn't support the QUOTA extension

	checkConnected();
	Quota[] qa = null;

        IMAPProtocol p = null;
	try {
	    p = getStoreProtocol();
	    qa = p.getQuotaRoot(root);
	} catch (BadCommandException bex) {
	    throw new MessagingException("QUOTA not supported", bex);
	} catch (ConnectionException cex) {
	    throw new StoreClosedException(this, cex.getMessage());
	} catch (ProtocolException pex) {
	    throw new MessagingException(pex.getMessage(), pex);
	} finally {
	    releaseStoreProtocol(p);
	    if (p == null) {	// failed to get a Store connection
		// have to force Store to be closed
		cleanup();
	    }
	}
	return qa;
    
javax.mail.SessiongetSession()
Get a reference to the session.

        return session;
    
public javax.mail.Folder[]getSharedNamespaces()
Using the IMAP NAMESPACE command (RFC 2342), return a set of folders representing the Shared namespaces.

	Namespaces ns = getNamespaces();
	if (ns == null || ns.shared == null)
	    return super.getSharedNamespaces();
	return namespaceToFolders(ns.shared, null);
    
intgetStatusCacheTimeout()
Get the number of milliseconds to cache STATUS response.

	return statusCacheTimeout;
    
com.sun.mail.imap.protocol.IMAPProtocolgetStoreProtocol()
Get this Store's protocol connection. When acquiring a store protocol object, it is important to use the following steps: IMAPProtocol p = null; try { p = getStoreProtocol(); // perform the command } catch (ConnectionException cex) { throw new StoreClosedException(this, cex.getMessage()); } catch (WhateverException ex) { // handle it } finally { releaseStoreProtocol(p); if (p == null) { // failed to get a Store connection // have to force Store to be closed cleanup(); } }

        IMAPProtocol p = null;

	while (p == null) {
        synchronized (pool) {
	    waitIfIdle();

            // If there's no authenticated connections available create a 
            // new one and place it in the authenticated queue.
            if (pool.authenticatedConnections.isEmpty()) {
                if (pool.debug)
                    out.println("DEBUG: getStoreProtocol() - no connections " +
                        "in the pool, creating a new one");
                try {
                    // Use cached host, port and timeout values.
                    p = new IMAPProtocol(name, host, port,
                                         session.getDebug(),
                                         session.getDebugOut(),
                                         session.getProperties(),
					 isSSL
                                        );
                    // Use cached auth info
                    login(p, user, password);
                } catch(Exception ex1) {
                    if (p != null)
                        try {
                            p.logout();
                        } catch (Exception ex2) { }
                    p = null;
                }
 
                if (p == null)
                    throw new ConnectionException(
				"failed to create new store connection");
             
	        p.addResponseHandler(this);
                pool.authenticatedConnections.addElement(p);
 
            } else {
                // Always use the first element in the Authenticated queue.
                if (pool.debug)
                    out.println("DEBUG: getStoreProtocol() - " +
                        "connection available -- size: " +
                        pool.authenticatedConnections.size());
                p = (IMAPProtocol)pool.authenticatedConnections.firstElement();
            }
 
	    if (pool.storeConnectionInUse) {
		try {
		    // someone else is using the connection, give up
		    // and wait until they're done
		    p = null;
		    pool.wait();
		} catch (InterruptedException ex) { }
	    } else {
		pool.storeConnectionInUse = true;

		if (pool.debug) 
		    out.println("DEBUG: getStoreProtocol() -- " +
			"storeConnectionInUse");
	    }
 
            timeoutConnections();
        }
	}
	return p;
    
public javax.mail.Folder[]getUserNamespaces(java.lang.String user)
Using the IMAP NAMESPACE command (RFC 2342), return a set of folders representing the User's namespaces.

	Namespaces ns = getNamespaces();
	if (ns == null || ns.otherUsers == null)
	    return super.getUserNamespaces(user);
	return namespaceToFolders(ns.otherUsers, user);
    
public voidhandleResponse(com.sun.mail.iap.Response r)
Response handler method.

	// Any of these responses may have a response code.
	if (r.isOK() || r.isNO() || r.isBAD() || r.isBYE())
	    handleResponseCode(r);
	if (r.isBYE()) {
	    if (debug)
		out.println("DEBUG: IMAPStore connection dead");
	    // Store's IMAP connection is dead, cleanup.
	    if (connected) // Check if its already closed
		cleanup(r.isSynthetic());
	    return;
	}
    
voidhandleResponseCode(com.sun.mail.iap.Response r)
Handle notifications and alerts. Response must be an OK, NO, BAD, or BYE response.

	String s = r.getRest();	// get the text after the response
	boolean isAlert = false;
	if (s.startsWith("[")) {	// a response code
	    int i = s.indexOf(']");
	    // remember if it's an alert
	    if (i > 0 && s.substring(0, i + 1).equalsIgnoreCase("[ALERT]"))
		isAlert = true;
	    // strip off the response code in any event
	    s = s.substring(i + 1).trim();
	}
	if (isAlert)
	    notifyStoreListeners(StoreEvent.ALERT, s);
	else if (r.isUnTagged() && s.length() > 0)
	    // Only send notifications that come with untagged
	    // responses, and only if there is actually some
	    // text there.
	    notifyStoreListeners(StoreEvent.NOTICE, s);
    
public synchronized booleanhasCapability(java.lang.String capability)
Return true if the specified capability string is in the list of capabilities the server announced.

since
JavaMail 1.3.3

        IMAPProtocol p = null;
	try {
	    p = getStoreProtocol();
            return p.hasCapability(capability);
	} catch (ProtocolException pex) {
	    if (p == null) {	// failed to get a Store connection
		// have to force Store to be closed
		cleanup();
	    }
	    throw new MessagingException(pex.getMessage(), pex);
        } finally {
            releaseStoreProtocol(p);
        }
    
booleanhasSeparateStoreConnection()
Report whether the separateStoreConnection is set.

        return pool.separateStoreConnection;
    
public voididle()
Use the IMAP IDLE command (see RFC 2177), if supported by the server, to enter idle mode so that the server can send unsolicited notifications without the need for the client to constantly poll the server. Use a ConnectionListener to be notified of events. When another thread (e.g., the listener thread) needs to issue an IMAP comand for this Store, the idle mode will be terminated and this method will return. Typically the caller will invoke this method in a loop.

If the mail.imap.enableimapevents property is set, notifications received while the IDLE command is active will be delivered to ConnectionListeners as events with a type of IMAPStore.RESPONSE. The event's message will be the raw IMAP response string. Note that most IMAP servers will not deliver any events when using the IDLE command on a connection with no mailbox selected (i.e., this method). In most cases you'll want to use the idle method on IMAPFolder.

NOTE: This capability is highly experimental and likely will change in future releases.

The mail.imap.minidletime property enforces a minimum delay before returning from this method, to ensure that other threads have a chance to issue commands before the caller invokes this method again. The default delay is 10 milliseconds.

exception
MessagingException if the server doesn't support the IDLE extension
exception
IllegalStateException if the store isn't connected
since
JavaMail 1.4.1

	IMAPProtocol p = null;
	// ASSERT: Must NOT be called with the connection pool
	// synchronization lock held.
	assert !Thread.holdsLock(pool);
	synchronized (this) {
	    checkConnected();
	}
	try {
	    synchronized (pool) {
		p = getStoreProtocol();
		if (pool.idleState == ConnectionPool.RUNNING) {
		    p.idleStart();
		    pool.idleState = ConnectionPool.IDLE;
		} else {
		    // some other thread must be running the IDLE
		    // command, we'll just wait for it to finish
		    // without aborting it ourselves
		    try {
			// give up lock and wait to be not idle
			pool.wait();
		    } catch (InterruptedException ex) { }
		    return;
		}
		pool.idleProtocol = p;
	    }

	    /*
	     * We gave up the pool lock so that other threads
	     * can get into the pool far enough to see that we're
	     * in IDLE and abort the IDLE.
	     *
	     * Now we read responses from the IDLE command, especially
	     * including unsolicited notifications from the server.
	     * We don't hold the pool lock while reading because
	     * it protects the idleState and other threads need to be
	     * able to examine the state.
	     *
	     * We hold the pool lock while processing the responses.
	     */
	    for (;;) {
		Response r = p.readIdleResponse();
		synchronized (pool) {
		    if (r == null || !p.processIdleResponse(r)) {
			pool.idleState = ConnectionPool.RUNNING;
			pool.notifyAll();
			break;
		    }
		}
		if (enableImapEvents && r.isUnTagged()) {
		    notifyStoreListeners(IMAPStore.RESPONSE, r.toString());
		}
	    }

	    /*
	     * Enforce a minimum delay to give time to threads
	     * processing the responses that came in while we
	     * were idle.
	     */
	    int minidle = getMinIdleTime();
	    if (minidle > 0) {
		try {
		    Thread.sleep(minidle);
		} catch (InterruptedException ex) { }
	    }

	} catch (BadCommandException bex) {
	    throw new MessagingException("IDLE not supported", bex);
	} catch (ConnectionException cex) {
	    throw new StoreClosedException(this, cex.getMessage());
	} catch (ProtocolException pex) {
	    throw new MessagingException(pex.getMessage(), pex);
	} finally {
	    synchronized (pool) {
		pool.idleProtocol = null;
	    }
	    releaseStoreProtocol(p);
	    if (p == null) {	// failed to get a Store connection
		// have to force Store to be closed
		cleanup();
	    }
	}
    
public synchronized booleanisConnected()
Check whether this store is connected. Override superclass method, to actually ping our server connection.

	if (!connected) {
	    // if we haven't been connected at all, don't bother with
	    // the NOOP.
	    super.setConnected(false);	// just in case
	    return false;
	}

	/*
	 * The below noop() request can:
	 * (1) succeed - in which case all is fine.
	 *
	 * (2) fail because the server returns NO or BAD, in which
	 * 	case we ignore it since we can't really do anything.
	 * (2) fail because a BYE response is obtained from the 
	 *	server
	 * (3) fail because the socket.write() to the server fails,
	 *	in which case the iap.protocol() code converts the
	 *	IOException into a BYE response.
	 *
	 * Thus, our BYE handler will take care of closing the Store
	 * in case our connection is really gone.
	 */
   
        IMAPProtocol p = null;
	try {
	    p = getStoreProtocol();
            p.noop();
	} catch (ProtocolException pex) {
	    if (p == null) {	// failed to get a Store connection
		// have to force Store to be closed
		cleanup();
	    }
	    // will return false below
        } finally {
            releaseStoreProtocol(p);
        }


	return super.isConnected();
    
booleanisConnectionPoolFull()
Report whether the connection pool is full.


        synchronized (pool) {
            if (pool.debug)
                out.println("DEBUG: current size: " +
                    pool.authenticatedConnections.size() + 
                    "   pool size: " + pool.poolSize);

            return (pool.authenticatedConnections.size() >= pool.poolSize);

        }
    
private voidlogin(com.sun.mail.imap.protocol.IMAPProtocol p, java.lang.String u, java.lang.String pw)

	// turn on TLS if it's been enabled and is supported
	if (enableStartTLS && p.hasCapability("STARTTLS")) {
	    p.startTLS();
	    // if startTLS succeeds, refresh capabilities
	    p.capability();
	}
	if (p.isAuthenticated())
	    return;		// no need to login

	/*
	 * Put a special "marker" in the capabilities list so we can
	 * detect if the server refreshed the capabilities in the OK
	 * response.
	 */
	p.getCapabilities().put("__PRELOGIN__", "");
	String authzid;
	if (authorizationID != null)
	    authzid = authorizationID;
	else if (proxyAuthUser != null)
	    authzid = proxyAuthUser;
	else
	    authzid = u;

	if (enableSASL)
	    p.sasllogin(saslMechanisms, saslRealm, authzid, u, pw);

	if (p.isAuthenticated())
	    ;	// SASL login succeeded, go to bottom
	else if (p.hasCapability("AUTH=PLAIN") && !disableAuthPlain)
	    p.authplain(authzid, u, pw);
	else if ((p.hasCapability("AUTH-LOGIN") ||
		p.hasCapability("AUTH=LOGIN")) && !disableAuthLogin)
	    p.authlogin(u, pw);
	else if (!p.hasCapability("LOGINDISABLED"))
	    p.login(u, pw);
	else
	    throw new ProtocolException("No login methods supported!");

	if (proxyAuthUser != null)
	    p.proxyauth(proxyAuthUser);

	/*
	 * If marker is still there, capabilities haven't been refreshed,
	 * refresh them now.
	 */
	if (p.hasCapability("__PRELOGIN__")) {
	    try {
		p.capability();
	    } catch (ConnectionException cex) {
		throw cex;	// rethrow connection failures
		// XXX - assume connection has been closed
	    } catch (ProtocolException pex) {
		// ignore other exceptions that "should never happen"
	    }
	}
    
private javax.mail.Folder[]namespaceToFolders(com.sun.mail.imap.protocol.Namespaces$Namespace[] ns, java.lang.String user)

	Folder[] fa = new Folder[ns.length];
	for (int i = 0; i < fa.length; i++) {
	    String name = ns[i].prefix;
	    if (user == null) {
		// strip trailing delimiter
		int len = name.length();
		if ( len > 0 && name.charAt(len - 1) == ns[i].delimiter)
		    name = name.substring(0, len - 1);
	    } else {
		// add user
		name += user;
	    }
	    fa[i] = new IMAPFolder(name, ns[i].delimiter, this, user == null);
	}
	return fa;
    
protected synchronized booleanprotocolConnect(java.lang.String host, int pport, java.lang.String user, java.lang.String password)
Implementation of protocolConnect(). Will create a connection to the server and authenticate the user using the mechanisms specified by various properties.

The host, user, and password parameters must all be non-null. If the authentication mechanism being used does not require a password, an empty string or other suitable dummy password should be used.

        
        IMAPProtocol protocol = null;

	// check for non-null values of host, password, user
	if (host == null || password == null || user == null) {
	    if (debug)
		out.println("DEBUG: protocolConnect returning false" +
				", host=" + host +
				", user=" + user +
				", password=" + (password != null ?
					"<non-null>" : "<null>"));
	    return false;
	}

	// set the port correctly
	if (pport != -1) {
	    port = pport;
	} else {
	    String portstring = session.getProperty("mail."+name+".port");
	    if (portstring != null) {
		port = Integer.parseInt(portstring);
	    }
	} 
	
	// use the default if needed
	if (port == -1) {
	    port = defaultPort;
	}
	
	try {
            boolean poolEmpty;
            synchronized (pool) {
                poolEmpty = pool.authenticatedConnections.isEmpty();
            }

            if (poolEmpty) {
                protocol = new IMAPProtocol(name, host, port, 
		    			    session.getDebug(),
		    			    session.getDebugOut(),
					    session.getProperties(),
					    isSSL
					   );
		if (debug)
		    out.println("DEBUG: protocolConnect login" +
				", host=" + host +
				", user=" + user +
				", password=<non-null>");
	        login(protocol, user, password);

	        protocol.addResponseHandler(this);

	        this.host = host;
	        this.user = user;
	        this.password = password;

                synchronized (pool) {
                    pool.authenticatedConnections.addElement(protocol);
                }
            }
	} catch (CommandFailedException cex) {
	    // login failure, close connection to server
	    if (protocol != null)
		protocol.disconnect();
	    protocol = null;
	    throw new AuthenticationFailedException(
					cex.getResponse().getRest());
	} catch (ProtocolException pex) { // any other exception
	    throw new MessagingException(pex.getMessage(), pex);
	} catch (IOException ioex) {
	    throw new MessagingException(ioex.getMessage(), ioex);
	} 

	connected = true;
        return true;

    
voidreleaseProtocol(com.sun.mail.imap.IMAPFolder folder, com.sun.mail.imap.protocol.IMAPProtocol protocol)
Release the protocol object back to the connection pool.


        synchronized (pool) {
            if (protocol != null) {
                // If the pool is not full, add the store as a response handler
                // and return the protocol object to the connection pool.
                if (!isConnectionPoolFull()) {
                    protocol.addResponseHandler(this);
                    pool.authenticatedConnections.addElement(protocol);

                    if (debug)
                        out.println("DEBUG: added an " +
                            "Authenticated connection -- size: " +
                            pool.authenticatedConnections.size());
                } else {
                    if (debug)
                        out.println("DEBUG: pool is full, not adding " +
                            "an Authenticated connection");
                    try {
                        protocol.logout();
                    } catch (ProtocolException pex) {};
                }
            }

            if (pool.folders != null)
                pool.folders.removeElement(folder);

            timeoutConnections();
        }
    
voidreleaseStoreProtocol(com.sun.mail.imap.protocol.IMAPProtocol protocol)
Release the store connection.


	if (protocol == null)
	    return;		// nothing to release
        synchronized (pool) {
	    pool.storeConnectionInUse = false;
	    pool.notifyAll();	// in case anyone waiting

	    if (pool.debug)
		out.println("DEBUG: releaseStoreProtocol()");

            timeoutConnections();
        }
    
public synchronized voidsetPassword(java.lang.String password)
Set the password that will be used for subsequent connections after this Store is first connected (for example, when creating a connection to open a Folder). This value is overridden by any call to the Store's connect method.

Most applications will never need to use this method.

since
JavaMail 1.3.3

	this.password = password;
    
public synchronized voidsetQuota(javax.mail.Quota quota)
Set the quotas for the quota root specified in the quota argument. Typically this will be one of the quota roots obtained from the getQuota method, but it need not be.

param
quota the quota to set
exception
MessagingException if the server doesn't support the QUOTA extension

	checkConnected();
        IMAPProtocol p = null;
	try {
	    p = getStoreProtocol();
	    p.setQuota(quota);
	} catch (BadCommandException bex) {
	    throw new MessagingException("QUOTA not supported", bex);
	} catch (ConnectionException cex) {
	    throw new StoreClosedException(this, cex.getMessage());
	} catch (ProtocolException pex) {
	    throw new MessagingException(pex.getMessage(), pex);
	} finally {
	    releaseStoreProtocol(p);
	    if (p == null) {	// failed to get a Store connection
		// have to force Store to be closed
		cleanup();
	    }
	}
    
public synchronized voidsetUsername(java.lang.String user)
Set the user name that will be used for subsequent connections after this Store is first connected (for example, when creating a connection to open a Folder). This value is overridden by any call to the Store's connect method.

Some IMAP servers may provide an authentication ID that can be used for more efficient authentication for future connections. This authentication ID is provided in a server-specific manner not described here.

Most applications will never need to use this method.

since
JavaMail 1.3.3

	this.user = user;
    
private voidtimeoutConnections()
Check to see if it's time to shrink the connection pool.


        synchronized (pool) {

            // If we've exceeded the pruning interval, look for stale
            // connections to logout.
            if (System.currentTimeMillis() - pool.lastTimePruned > 
                pool.pruningInterval && 
                pool.authenticatedConnections.size() > 1) {

                if (pool.debug) {
                    out.println("DEBUG: checking for connections " +
                        "to prune: " +
                        (System.currentTimeMillis() - pool.lastTimePruned));
                    out.println("DEBUG: clientTimeoutInterval: " +
                        pool.clientTimeoutInterval);
                }   
 
                IMAPProtocol p;
 
                // Check the timestamp of the protocol objects in the pool and
                // logout if the interval exceeds the client timeout value
                // (leave the first connection).
                for (int index = pool.authenticatedConnections.size() - 1; 
                     index > 0; index--) {
                    p = (IMAPProtocol)pool.authenticatedConnections.
                        elementAt(index);
                    if (pool.debug) {
                        out.println("DEBUG: protocol last used: " +
                            (System.currentTimeMillis() - p.getTimestamp()));
                    }
                    if (System.currentTimeMillis() - p.getTimestamp() >
                        pool.clientTimeoutInterval) {
 
                        if (pool.debug) {
                            out.println("DEBUG: authenticated " +
                                "connection timed out");
                            out.println("DEBUG: logging out " + 
                                "the connection");
                        }
 
                        p.removeResponseHandler(this);
                        pool.authenticatedConnections.removeElementAt(index);

                        try {
                            p.logout();
                        } catch (ProtocolException pex) {}
                    }
                }
                pool.lastTimePruned = System.currentTimeMillis();
            }
        }
    
private voidwaitIfIdle()

	assert Thread.holdsLock(pool);
	while (pool.idleState != ConnectionPool.RUNNING) {
	    if (pool.idleState == ConnectionPool.IDLE) {
		pool.idleProtocol.idleAbort();
		pool.idleState = ConnectionPool.ABORTING;
	    }
	    try {
		// give up lock and wait to be not idle
		pool.wait();
	    } catch (InterruptedException ex) { }
	}