Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (101)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (309)
Collections:
Other Resources:
JavaMail 1.6.2 Source Code Files
JavaMail Source Code Files are provided in the source package file, httpcomponents-client-5.2-src.zip.
You can browse JavaMail Source Code files below:
✍: FYIcenter.com
⏎ com/sun/mail/imap/IMAPStore.java
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://oss.oracle.com/licenses/CDDL+GPL-1.1 * or LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.mail.imap; import java.lang.reflect.*; import java.util.Vector; import java.util.StringTokenizer; import java.util.Locale; import java.util.Properties; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Map; import java.util.HashMap; import java.util.logging.Level; import javax.mail.*; import javax.mail.event.*; import com.sun.mail.iap.*; import com.sun.mail.imap.protocol.*; import com.sun.mail.util.PropUtil; import com.sun.mail.util.MailLogger; import com.sun.mail.util.SocketConnectException; import com.sun.mail.util.MailConnectException; import java.util.ArrayList; import java.util.List; /** * This class provides access to an IMAP message store. <p> * * Applications that need to make use of IMAP-specific features may cast * a <code>Store</code> object to an <code>IMAPStore</code> object and * use the methods on this class. The {@link #getQuota getQuota} and * {@link #setQuota setQuota} methods support the IMAP QUOTA extension. * Refer to <A HREF="http://www.ietf.org/rfc/rfc2087.txt">RFC 2087</A> * for more information. <p> * * The {@link #id id} method supports the IMAP ID extension; * see <A HREF="http://www.ietf.org/rfc/rfc2971.txt">RFC 2971</A>. * The fields ID_NAME, ID_VERSION, etc. represent the suggested field names * in RFC 2971 section 3.3 and may be used as keys in the Map containing * client values or server values. <p> * * See the <a href="package-summary.html">com.sun.mail.imap</a> package * documentation for further information on the IMAP protocol provider. <p> * * <strong>WARNING:</strong> The APIs unique to this class should be * considered <strong>EXPERIMENTAL</strong>. They may be changed in the * future in ways that are incompatible with applications using the * current APIs. * * @author John Mani * @author Bill Shannon * @author Jim Glennon */ /* * This package is implemented over the "imap.protocol" package, which * implements the protocol-level commands. <p> * * A connected IMAPStore maintains a pool of IMAP protocol objects for * use in communicating with the IMAP server. The IMAPStore will create * the initial AUTHENTICATED connection and seed the pool with this * connection. As folders are opened and new IMAP protocol objects are * needed, the IMAPStore will provide them from the connection pool, * or create them if none are available. When a folder is closed, * its IMAP protocol object is returned to the connection pool if the * pool is not over capacity. The pool size can be configured by setting * the mail.imap.connectionpoolsize property. <p> * * Note that all connections in the connection pool have their response * handler set to be the Store. When the connection is removed from the * pool for use by a folder, the response handler is removed and then set * to either the Folder or to the special nonStoreResponseHandler, depending * on how the connection is being used. This is probably excessive. * Better would be for the Protocol object to support only a single * response handler, which would be set before the connection is used * and cleared when the connection is in the pool and can't be used. <p> * * A mechanism is provided for timing out idle connection pool IMAP * protocol objects. Timed out connections are closed and removed (pruned) * from the connection pool. The time out interval can be configured via * the mail.imap.connectionpooltimeout property. <p> * * The connected IMAPStore object may or may not maintain a separate IMAP * protocol object that provides the store a dedicated connection to the * IMAP server. This is provided mainly for compatibility with previous * implementations of JavaMail and is determined by the value of the * mail.imap.separatestoreconnection property. <p> * * An IMAPStore object provides closed IMAPFolder objects thru its list() * and listSubscribed() methods. A closed IMAPFolder object acquires an * IMAP protocol object from the store to communicate with the server. When * the folder is opened, it gets its own protocol object and thus its own, * separate connection to the server. The store maintains references to * all 'open' folders. When a folder is/gets closed, the store removes * it from its list. When the store is/gets closed, it closes all open * folders in its list, thus cleaning up all open connections to the * server. <p> * * A mutex is used to control access to the connection pool resources. * Any time any of these resources need to be accessed, the following * convention should be followed: * * synchronized (pool) { // ACQUIRE LOCK * // access connection pool resources * } // RELEASE LOCK <p> * * The locking relationship between the store and folders is that the * store lock must be acquired before a folder lock. This is currently only * applicable in the store's cleanup method. It's important that the * connection pool lock is not held when calling into folder objects. * The locking hierarchy is that a folder lock must be acquired before * any connection pool operations are performed. You never need to hold * all three locks, but if you hold more than one this is the order you * have to acquire them in. <p> * * That is: Store > Folder, Folder > pool, Store > pool <p> * * The IMAPStore implements the ResponseHandler interface and listens to * BYE or untagged OK-notification events from the server as a result of * Store operations. IMAPFolder forwards notifications that result from * Folder operations using the store connection; the IMAPStore ResponseHandler * is not used directly in this case. <p> */ public class IMAPStore extends Store implements QuotaAwareStore, ResponseHandler { /** * A special event type for a StoreEvent to indicate an IMAP * response, if the mail.imap.enableimapevents property is set. */ public static final int RESPONSE = 1000; public static final String ID_NAME = "name"; public static final String ID_VERSION = "version"; public static final String ID_OS = "os"; public static final String ID_OS_VERSION = "os-version"; public static final String ID_VENDOR = "vendor"; public static final String ID_SUPPORT_URL = "support-url"; public static final String ID_ADDRESS = "address"; public static final String ID_DATE = "date"; public static final String ID_COMMAND = "command"; public static final String ID_ARGUMENTS = "arguments"; public static final String ID_ENVIRONMENT = "environment"; protected final String name; // name of this protocol protected final int defaultPort; // default IMAP port protected final boolean isSSL; // use SSL? private final int blksize; // Block size for data requested // in FETCH requests. Defaults to // 16K private boolean ignoreSize; // ignore the size in BODYSTRUCTURE? private final int statusCacheTimeout; // cache Status for 1 second private final int appendBufferSize; // max size of msg buffered for append private final int minIdleTime; // minimum idle time private volatile int port = -1; // port to use // Auth info protected String host; protected String user; protected String password; protected String proxyAuthUser; protected String authorizationID; protected String saslRealm; private Namespaces namespaces; private boolean enableStartTLS = false; // enable STARTTLS private boolean requireStartTLS = false; // require STARTTLS private boolean usingSSL = false; // using SSL? private boolean enableSASL = false; // enable SASL authentication private String[] saslMechanisms; private boolean forcePasswordRefresh = false; // enable notification of IMAP responses private boolean enableResponseEvents = false; // enable notification of IMAP responses during IDLE private boolean enableImapEvents = false; private String guid; // for Yahoo! Mail IMAP private boolean throwSearchException = false; private boolean peek = false; private boolean closeFoldersOnStoreFailure = true; private boolean enableCompress = false; // enable COMPRESS=DEFLATE private boolean finalizeCleanClose = false; /* * This field is set in the Store's response handler if we see * a BYE response. The releaseStore method checks this field * and if set it cleans up the Store. Field is volatile because * there's no lock we consistently hold while manipulating it. * * Because volatile doesn't really work before JDK 1.5, * use a lock to protect these two fields. */ private volatile boolean connectionFailed = false; private volatile boolean forceClose = false; private final Object connectionFailedLock = new Object(); private boolean debugusername; // include username in debug output? private boolean debugpassword; // include password in debug output? protected MailLogger logger; // for debug output private boolean messageCacheDebug; // constructors for IMAPFolder class provided by user private volatile Constructor<?> folderConstructor = null; private volatile Constructor<?> folderConstructorLI = null; // Connection pool info static class ConnectionPool { // container for the pool's IMAP protocol objects private Vector<IMAPProtocol> authenticatedConnections = new Vector<>(); // vectore of open folders private Vector<IMAPFolder> folders; // is the store connection being used? private boolean storeConnectionInUse = false; // the last time (in millis) the pool was checked for timed out // connections private long lastTimePruned; // flag to indicate whether there is a dedicated connection for // store commands private final boolean separateStoreConnection; // client timeout interval private final long clientTimeoutInterval; // server timeout interval private final long serverTimeoutInterval; // size of the connection pool private final int poolSize; // interval for checking for timed out connections private final long pruningInterval; // connection pool logger private final MailLogger logger; /* * The idleState field supports the IDLE command. * Normally when executing an IMAP command we hold the * store's lock. * While executing the IDLE command we can't hold the * lock or it would prevent other threads from * entering Store methods even far enough to check whether * an IDLE command is in progress. We need to check before * issuing another command so that we can abort the IDLE * command. * * The idleState field is protected by the store's lock. * The RUNNING state is the normal state and means no IDLE * command is in progress. The IDLE state means we've issued * an IDLE command and are reading responses. The ABORTING * state means we've sent the DONE continuation command and * are waiting for the thread running the IDLE command to * break out of its read loop. * * When an IDLE command is in progress, the thread calling * the idle method will be reading from the IMAP connection * while not holding the store's lock. * It's obviously critical that no other thread try to send a * command or read from the connection while in this state. * However, other threads can send the DONE continuation * command that will cause the server to break out of the IDLE * loop and send the ending tag response to the IDLE command. * The thread in the idle method that's reading the responses * from the IDLE command will see this ending response and * complete the idle method, setting the idleState field back * to RUNNING, and notifying any threads waiting to use the * connection. * * All uses of the IMAP connection (IMAPProtocol object) must * be preceeded by a check to make sure an IDLE command is not * running, and abort the IDLE command if necessary. This check * is made while holding the connection pool lock. While * waiting for the IDLE command to complete, these other threads * will give up the connection pool lock. This check is done by * the getStoreProtocol() method. */ private static final int RUNNING = 0; // not doing IDLE command private static final int IDLE = 1; // IDLE command in effect private static final int ABORTING = 2; // IDLE command aborting private int idleState = RUNNING; private IMAPProtocol idleProtocol; // protocol object when IDLE ConnectionPool(String name, MailLogger plogger, Session session) { lastTimePruned = System.currentTimeMillis(); Properties props = session.getProperties(); boolean debug = PropUtil.getBooleanProperty(props, "mail." + name + ".connectionpool.debug", false); logger = plogger.getSubLogger("connectionpool", "DEBUG IMAP CP", debug); // check if the default connection pool size is overridden int size = PropUtil.getIntProperty(props, "mail." + name + ".connectionpoolsize", -1); if (size > 0) { poolSize = size; if (logger.isLoggable(Level.CONFIG)) logger.config("mail.imap.connectionpoolsize: " + poolSize); } else poolSize = 1; // check if the default client-side timeout value is overridden int connectionPoolTimeout = PropUtil.getIntProperty(props, "mail." + name + ".connectionpooltimeout", -1); if (connectionPoolTimeout > 0) { clientTimeoutInterval = connectionPoolTimeout; if (logger.isLoggable(Level.CONFIG)) logger.config("mail.imap.connectionpooltimeout: " + clientTimeoutInterval); } else clientTimeoutInterval = 45 * 1000; // 45 seconds // check if the default server-side timeout value is overridden int serverTimeout = PropUtil.getIntProperty(props, "mail." + name + ".servertimeout", -1); if (serverTimeout > 0) { serverTimeoutInterval = serverTimeout; if (logger.isLoggable(Level.CONFIG)) logger.config("mail.imap.servertimeout: " + serverTimeoutInterval); } else serverTimeoutInterval = 30 * 60 * 1000; // 30 minutes // check if the default server-side timeout value is overridden int pruning = PropUtil.getIntProperty(props, "mail." + name + ".pruninginterval", -1); if (pruning > 0) { pruningInterval = pruning; if (logger.isLoggable(Level.CONFIG)) logger.config("mail.imap.pruninginterval: " + pruningInterval); } else pruningInterval = 60 * 1000; // 1 minute // check to see if we should use a separate (i.e. dedicated) // store connection separateStoreConnection = PropUtil.getBooleanProperty(props, "mail." + name + ".separatestoreconnection", false); if (separateStoreConnection) logger.config("dedicate a store connection"); } } private final ConnectionPool pool; /** * A special response handler for connections that are being used * to perform operations on behalf of an object other than the Store. * It DOESN'T cause the Store to be cleaned up if a BYE is seen. * The BYE may be real or synthetic and in either case just indicates * that the connection is dead. */ private ResponseHandler nonStoreResponseHandler = new ResponseHandler() { @Override public void handleResponse(Response r) { // Any of these responses may have a response code. if (r.isOK() || r.isNO() || r.isBAD() || r.isBYE()) handleResponseCode(r); if (r.isBYE()) logger.fine("IMAPStore non-store connection dead"); } }; /** * Constructor that takes a Session object and a URLName that * represents a specific IMAP server. * * @param session the Session * @param url the URLName of this store */ public IMAPStore(Session session, URLName url) { this(session, url, "imap", false); } /** * Constructor used by this class and by IMAPSSLStore subclass. * * @param session the Session * @param url the URLName of this store * @param name the protocol name for this store * @param isSSL use SSL? */ protected IMAPStore(Session session, URLName url, String name, boolean isSSL) { super(session, url); // call super constructor Properties props = session.getProperties(); if (url != null) name = url.getProtocol(); this.name = name; if (!isSSL) isSSL = PropUtil.getBooleanProperty(props, "mail." + name + ".ssl.enable", false); if (isSSL) this.defaultPort = 993; else this.defaultPort = 143; this.isSSL = isSSL; debug = session.getDebug(); debugusername = PropUtil.getBooleanProperty(props, "mail.debug.auth.username", true); debugpassword = PropUtil.getBooleanProperty(props, "mail.debug.auth.password", false); logger = new MailLogger(this.getClass(), "DEBUG " + name.toUpperCase(Locale.ENGLISH), session.getDebug(), session.getDebugOut()); boolean partialFetch = PropUtil.getBooleanProperty(props, "mail." + name + ".partialfetch", true); if (!partialFetch) { blksize = -1; logger.config("mail.imap.partialfetch: false"); } else { blksize = PropUtil.getIntProperty(props, "mail." + name +".fetchsize", 1024 * 16); if (logger.isLoggable(Level.CONFIG)) logger.config("mail.imap.fetchsize: " + blksize); } ignoreSize = PropUtil.getBooleanProperty(props, "mail." + name +".ignorebodystructuresize", false); if (logger.isLoggable(Level.CONFIG)) logger.config("mail.imap.ignorebodystructuresize: " + ignoreSize); statusCacheTimeout = PropUtil.getIntProperty(props, "mail." + name + ".statuscachetimeout", 1000); if (logger.isLoggable(Level.CONFIG)) logger.config("mail.imap.statuscachetimeout: " + statusCacheTimeout); appendBufferSize = PropUtil.getIntProperty(props, "mail." + name + ".appendbuffersize", -1); if (logger.isLoggable(Level.CONFIG)) logger.config("mail.imap.appendbuffersize: " + appendBufferSize); minIdleTime = PropUtil.getIntProperty(props, "mail." + name + ".minidletime", 10); if (logger.isLoggable(Level.CONFIG)) logger.config("mail.imap.minidletime: " + minIdleTime); // check if we should do a PROXYAUTH login String s = session.getProperty("mail." + name + ".proxyauth.user"); if (s != null) { proxyAuthUser = s; if (logger.isLoggable(Level.CONFIG)) logger.config("mail.imap.proxyauth.user: " + proxyAuthUser); } // check if STARTTLS is enabled enableStartTLS = PropUtil.getBooleanProperty(props, "mail." + name + ".starttls.enable", false); if (enableStartTLS) logger.config("enable STARTTLS"); // check if STARTTLS is required requireStartTLS = PropUtil.getBooleanProperty(props, "mail." + name + ".starttls.required", false); if (requireStartTLS) logger.config("require STARTTLS"); // check if SASL is enabled enableSASL = PropUtil.getBooleanProperty(props, "mail." + name + ".sasl.enable", false); if (enableSASL) logger.config("enable SASL"); // check if SASL mechanisms are specified if (enableSASL) { s = session.getProperty("mail." + name + ".sasl.mechanisms"); if (s != null && s.length() > 0) { if (logger.isLoggable(Level.CONFIG)) logger.config("SASL mechanisms allowed: " + s); List<String> v = new ArrayList<>(5); StringTokenizer st = new StringTokenizer(s, " ,"); while (st.hasMoreTokens()) { String m = st.nextToken(); if (m.length() > 0) v.add(m); } saslMechanisms = new String[v.size()]; v.toArray(saslMechanisms); } } // check if an authorization ID has been specified s = session.getProperty("mail." + name + ".sasl.authorizationid"); if (s != null) { authorizationID = s; logger.log(Level.CONFIG, "mail.imap.sasl.authorizationid: {0}", authorizationID); } // check if a SASL realm has been specified s = session.getProperty("mail." + name + ".sasl.realm"); if (s != null) { saslRealm = s; logger.log(Level.CONFIG, "mail.imap.sasl.realm: {0}", saslRealm); } // check if forcePasswordRefresh is enabled forcePasswordRefresh = PropUtil.getBooleanProperty(props, "mail." + name + ".forcepasswordrefresh", false); if (forcePasswordRefresh) logger.config("enable forcePasswordRefresh"); // check if enableimapevents is enabled enableResponseEvents = PropUtil.getBooleanProperty(props, "mail." + name + ".enableresponseevents", false); if (enableResponseEvents) logger.config("enable IMAP response events"); // check if enableresponseevents is enabled enableImapEvents = PropUtil.getBooleanProperty(props, "mail." + name + ".enableimapevents", false); if (enableImapEvents) logger.config("enable IMAP IDLE events"); // check if message cache debugging set messageCacheDebug = PropUtil.getBooleanProperty(props, "mail." + name + ".messagecache.debug", false); guid = session.getProperty("mail." + name + ".yahoo.guid"); if (guid != null) logger.log(Level.CONFIG, "mail.imap.yahoo.guid: {0}", guid); // check if throwsearchexception is enabled throwSearchException = PropUtil.getBooleanProperty(props, "mail." + name + ".throwsearchexception", false); if (throwSearchException) logger.config("throw SearchException"); // check if peek is set peek = PropUtil.getBooleanProperty(props, "mail." + name + ".peek", false); if (peek) logger.config("peek"); // check if closeFoldersOnStoreFailure is set closeFoldersOnStoreFailure = PropUtil.getBooleanProperty(props, "mail." + name + ".closefoldersonstorefailure", true); if (closeFoldersOnStoreFailure) logger.config("closeFoldersOnStoreFailure"); // check if COMPRESS is enabled enableCompress = PropUtil.getBooleanProperty(props, "mail." + name + ".compress.enable", false); if (enableCompress) logger.config("enable COMPRESS"); // check if finalizeCleanClose is enabled finalizeCleanClose = PropUtil.getBooleanProperty(props, "mail." + name + ".finalizecleanclose", false); if (finalizeCleanClose) logger.config("close connection cleanly in finalize"); s = session.getProperty("mail." + name + ".folder.class"); if (s != null) { logger.log(Level.CONFIG, "IMAP: folder class: {0}", s); try { ClassLoader cl = this.getClass().getClassLoader(); // now load the class Class<?> folderClass = null; try { // First try the "application's" class loader. // This should eventually be replaced by // Thread.currentThread().getContextClassLoader(). folderClass = Class.forName(s, false, cl); } catch (ClassNotFoundException ex1) { // That didn't work, now try the "system" class loader. // (Need both of these because JDK 1.1 class loaders // may not delegate to their parent class loader.) folderClass = Class.forName(s); } Class<?>[] c = { String.class, char.class, IMAPStore.class, Boolean.class }; folderConstructor = folderClass.getConstructor(c); Class<?>[] c2 = { ListInfo.class, IMAPStore.class }; folderConstructorLI = folderClass.getConstructor(c2); } catch (Exception ex) { logger.log(Level.CONFIG, "IMAP: failed to load folder class", ex); } } pool = new ConnectionPool(name, logger, session); } /** * Implementation of protocolConnect(). Will create a connection * to the server and authenticate the user using the mechanisms * specified by various properties. <p> * * The <code>host</code>, <code>user</code>, and <code>password</code> * 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. */ @Override protected synchronized boolean protocolConnect(String host, int pport, String user, String password) throws MessagingException { IMAPProtocol protocol = null; // check for non-null values of host, password, user if (host == null || password == null || user == null) { if (logger.isLoggable(Level.FINE)) logger.fine("protocolConnect returning false" + ", host=" + host + ", user=" + traceUser(user) + ", password=" + tracePassword(password)); return false; } // set the port correctly if (pport != -1) { port = pport; } else { port = PropUtil.getIntProperty(session.getProperties(), "mail." + name + ".port", port); } // use the default if needed if (port == -1) { port = defaultPort; } try { boolean poolEmpty; synchronized (pool) { poolEmpty = pool.authenticatedConnections.isEmpty(); } if (poolEmpty) { if (logger.isLoggable(Level.FINE)) logger.fine("trying to connect to host \"" + host + "\", port " + port + ", isSSL " + isSSL); protocol = newIMAPProtocol(host, port); if (logger.isLoggable(Level.FINE)) logger.fine("protocolConnect login" + ", host=" + host + ", user=" + traceUser(user) + ", password=" + tracePassword(password)); protocol.addResponseHandler(nonStoreResponseHandler); login(protocol, user, password); protocol.removeResponseHandler(nonStoreResponseHandler); protocol.addResponseHandler(this); usingSSL = protocol.isSSL(); // in case anyone asks this.host = host; this.user = user; this.password = password; synchronized (pool) { pool.authenticatedConnections.addElement(protocol); } } } catch (IMAPReferralException ex) { // login failure due to IMAP REFERRAL, close connection to server if (protocol != null) protocol.disconnect(); protocol = null; throw new ReferralException(ex.getUrl(), ex.getMessage()); } catch (CommandFailedException cex) { // login failure, close connection to server if (protocol != null) protocol.disconnect(); protocol = null; Response r = cex.getResponse(); throw new AuthenticationFailedException( r != null ? r.getRest() : cex.getMessage()); } catch (ProtocolException pex) { // any other exception // failure in login command, close connection to server if (protocol != null) protocol.disconnect(); protocol = null; throw new MessagingException(pex.getMessage(), pex); } catch (SocketConnectException scex) { throw new MailConnectException(scex); } catch (IOException ioex) { throw new MessagingException(ioex.getMessage(), ioex); } return true; } /** * Create an IMAPProtocol object connected to the host and port. * Subclasses of IMAPStore may override this method to return a * subclass of IMAPProtocol that supports product-specific extensions. * * @param host the host name * @param port the port number * @return the new IMAPProtocol object * @exception IOException for I/O errors * @exception ProtocolException for protocol errors * @since JavaMail 1.4.6 */ protected IMAPProtocol newIMAPProtocol(String host, int port) throws IOException, ProtocolException { return new IMAPProtocol(name, host, port, session.getProperties(), isSSL, logger ); } private void login(IMAPProtocol p, String u, String pw) throws ProtocolException { // turn on TLS if it's been enabled or required and is supported // and we're not already using SSL if ((enableStartTLS || requireStartTLS) && !p.isSSL()) { if (p.hasCapability("STARTTLS")) { p.startTLS(); // if startTLS succeeds, refresh capabilities p.capability(); } else if (requireStartTLS) { logger.fine("STARTTLS required but not supported by server"); throw new ProtocolException( "STARTTLS required but not supported by server"); } } if (p.isAuthenticated()) return; // no need to login // allow subclasses to issue commands before login preLogin(p); // issue special ID command to Yahoo! Mail IMAP server // http://en.wikipedia.org/wiki/Yahoo%21_Mail#Free_IMAP_and_SMTPs_access if (guid != null) { Map<String,String> gmap = new HashMap<>(); gmap.put("GUID", guid); p.id(gmap); } /* * 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 = null; if (enableSASL) { try { p.sasllogin(saslMechanisms, saslRealm, authzid, u, pw); if (!p.isAuthenticated()) throw new CommandFailedException( "SASL authentication failed"); } catch (UnsupportedOperationException ex) { // continue to try other authentication methods below } } if (!p.isAuthenticated()) authenticate(p, authzid, u, pw); 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" } } if (enableCompress) { if (p.hasCapability("COMPRESS=DEFLATE")) { p.compress(); } } // if server supports UTF-8, enable it for client use // note that this is safe to enable even if mail.mime.allowutf8=false if (p.hasCapability("UTF8=ACCEPT") || p.hasCapability("UTF8=ONLY")) p.enable("UTF8=ACCEPT"); } /** * Authenticate using one of the non-SASL mechanisms. * * @param p the IMAPProtocol object * @param authzid the authorization ID * @param user the user name * @param password the password * @exception ProtocolException on failures */ private void authenticate(IMAPProtocol p, String authzid, String user, String password) throws ProtocolException { // this list must match the "if" statements below String defaultAuthenticationMechanisms = "PLAIN LOGIN NTLM XOAUTH2"; // setting mail.imap.auth.mechanisms controls which mechanisms will // be used, and in what order they'll be considered. only the first // match is used. String mechs = session.getProperty("mail." + name + ".auth.mechanisms"); if (mechs == null) mechs = defaultAuthenticationMechanisms; /* * Loop through the list of mechanisms supplied by the user * (or defaulted) and try each in turn. If the server supports * the mechanism and we have an authenticator for the mechanism, * and it hasn't been disabled, use it. */ StringTokenizer st = new StringTokenizer(mechs); while (st.hasMoreTokens()) { String m = st.nextToken(); m = m.toUpperCase(Locale.ENGLISH); /* * If using the default mechanisms, check if this one is disabled. */ if (mechs == defaultAuthenticationMechanisms) { String dprop = "mail." + name + ".auth." + m.toLowerCase(Locale.ENGLISH) + ".disable"; boolean disabled = PropUtil.getBooleanProperty( session.getProperties(), dprop, m.equals("XOAUTH2")); if (disabled) { if (logger.isLoggable(Level.FINE)) logger.fine("mechanism " + m + " disabled by property: " + dprop); continue; } } if (!(p.hasCapability("AUTH=" + m) || (m.equals("LOGIN") && p.hasCapability("AUTH-LOGIN")))) { logger.log(Level.FINE, "mechanism {0} not supported by server", m); continue; } if (m.equals("PLAIN")) p.authplain(authzid, user, password); else if (m.equals("LOGIN")) p.authlogin(user, password); else if (m.equals("NTLM")) p.authntlm(authzid, user, password); else if (m.equals("XOAUTH2")) p.authoauth2(user, password); else { logger.log(Level.FINE, "no authenticator for mechanism {0}", m); continue; } return; } if (!p.hasCapability("LOGINDISABLED")) { p.login(user, password); return; } throw new ProtocolException("No login methods supported!"); } /** * This method is called after the connection is made and * TLS is started (if needed), but before any authentication * is attempted. Subclasses can override this method to * issue commands that are needed in the "not authenticated" * state. Note that if the connection is pre-authenticated, * this method won't be called. <p> * * The implementation of this method in this class does nothing. * * @param p the IMAPProtocol connection * @exception ProtocolException for protocol errors * @since JavaMail 1.4.4 */ protected void preLogin(IMAPProtocol p) throws ProtocolException { } /** * Does this IMAPStore use SSL when connecting to the server? * * @return true if using SSL * @since JavaMail 1.4.6 */ public synchronized boolean isSSL() { return usingSSL; } /** * 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. <p> * * 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. <p> * * Most applications will never need to use this method. * * @param user the user name for the store * @since JavaMail 1.3.3 */ public synchronized void setUsername(String user) { this.user = user; } /** * 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. <p> * * Most applications will never need to use this method. * * @param password the password for the store * @since JavaMail 1.3.3 */ public synchronized void setPassword(String password) { this.password = password; } /* * Get a new authenticated protocol object for this Folder. * Also store a reference to this folder in our list of * open folders. */ IMAPProtocol getProtocol(IMAPFolder folder) throws MessagingException { 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))) { logger.fine("no connections in the pool, creating a new one"); try { if (forcePasswordRefresh) refreshPassword(); // Use cached host, port and timeout values. p = newIMAPProtocol(host, port); p.addResponseHandler(nonStoreResponseHandler); // Use cached auth info login(p, user, password); p.removeResponseHandler(nonStoreResponseHandler); } catch(Exception ex1) { if (p != null) try { p.disconnect(); } catch (Exception ex2) { } p = null; } if (p == null) throw new MessagingException("connection failure"); } else { if (logger.isLoggable(Level.FINE)) logger.fine("connection available -- size: " + pool.authenticatedConnections.size()); // remove the available connection from the Authenticated queue p = 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 { /* * Swap in a special response handler that will handle * alerts, but won't cause the store to be closed and * cleaned up if the connection is dead. */ p.removeResponseHandler(this); p.addResponseHandler(nonStoreResponseHandler); p.noop(); p.removeResponseHandler(nonStoreResponseHandler); p.addResponseHandler(this); } catch (ProtocolException pex) { try { p.removeResponseHandler(nonStoreResponseHandler); p.disconnect(); } catch (RuntimeException ignored) { // don't let any exception stop us } p = null; continue; // try again, from the top } } // if proxyAuthUser has changed, switch to new user if (proxyAuthUser != null && !proxyAuthUser.equals(p.getProxyAuthUser()) && p.hasCapability("X-UNAUTHENTICATE")) { try { /* * Swap in a special response handler that will handle * alerts, but won't cause the store to be closed and * cleaned up if the connection is dead. */ p.removeResponseHandler(this); p.addResponseHandler(nonStoreResponseHandler); p.unauthenticate(); login(p, user, password); p.removeResponseHandler(nonStoreResponseHandler); p.addResponseHandler(this); } catch (ProtocolException pex) { try { p.removeResponseHandler(nonStoreResponseHandler); p.disconnect(); } catch (RuntimeException ignored) { // 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; } /** * 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); * } */ private IMAPProtocol getStoreProtocol() throws ProtocolException { 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()) { pool.logger.fine("getStoreProtocol() - no connections " + "in the pool, creating a new one"); try { if (forcePasswordRefresh) refreshPassword(); // Use cached host, port and timeout values. p = newIMAPProtocol(host, port); // 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.logger.isLoggable(Level.FINE)) pool.logger.fine("getStoreProtocol() - " + "connection available -- size: " + pool.authenticatedConnections.size()); p = pool.authenticatedConnections.firstElement(); // if proxyAuthUser has changed, switch to new user if (proxyAuthUser != null && !proxyAuthUser.equals(p.getProxyAuthUser()) && p.hasCapability("X-UNAUTHENTICATE")) { p.unauthenticate(); login(p, user, password); } } 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) { // restore the interrupted state, which callers might // depend on Thread.currentThread().interrupt(); // don't keep looking for a connection if we've been // interrupted throw new ProtocolException( "Interrupted getStoreProtocol", ex); } } else { pool.storeConnectionInUse = true; pool.logger.fine("getStoreProtocol() -- storeConnectionInUse"); } timeoutConnections(); } } return p; } /** * Get a store protocol object for use by a folder. */ IMAPProtocol getFolderStoreProtocol() throws ProtocolException { IMAPProtocol p = getStoreProtocol(); p.removeResponseHandler(this); p.addResponseHandler(nonStoreResponseHandler); return p; } /* * 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 */ private void refreshPassword() { if (logger.isLoggable(Level.FINE)) logger.fine("refresh password, user: " + traceUser(user)); 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(); } } /** * 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? */ boolean allowReadOnlySelect() { return PropUtil.getBooleanProperty(session.getProperties(), "mail." + name + ".allowreadonlyselect", false); } /** * Report whether the separateStoreConnection is set. */ boolean hasSeparateStoreConnection() { return pool.separateStoreConnection; } /** * Return the connection pool logger. */ MailLogger getConnectionPoolLogger() { return pool.logger; } /** * Report whether message cache debugging is enabled. */ boolean getMessageCacheDebug() { return messageCacheDebug; } /** * Report whether the connection pool is full. */ boolean isConnectionPoolFull() { synchronized (pool) { if (pool.logger.isLoggable(Level.FINE)) pool.logger.fine("connection pool current size: " + pool.authenticatedConnections.size() + " pool size: " + pool.poolSize); return (pool.authenticatedConnections.size() >= pool.poolSize); } } /** * Release the protocol object back to the connection pool. */ void releaseProtocol(IMAPFolder folder, IMAPProtocol protocol) { 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 (logger.isLoggable(Level.FINE)) logger.fine( "added an Authenticated connection -- size: " + pool.authenticatedConnections.size()); } else { logger.fine( "pool is full, not adding an Authenticated connection"); try { protocol.logout(); } catch (ProtocolException pex) {}; } } if (pool.folders != null) pool.folders.removeElement(folder); timeoutConnections(); } } /** * Release the store connection. */ private void releaseStoreProtocol(IMAPProtocol protocol) { // will be called from idle() without the Store lock held, // but cleanup is synchronized and will acquire the Store lock if (protocol == null) { cleanup(); // failed to ever get the connection return; // nothing to release } /* * Read out the flag that says whether this connection failed * before releasing the protocol object for others to use. */ boolean failed; synchronized (connectionFailedLock) { failed = connectionFailed; connectionFailed = false; // reset for next use } // now free the store connection synchronized (pool) { pool.storeConnectionInUse = false; pool.notifyAll(); // in case anyone waiting pool.logger.fine("releaseStoreProtocol()"); timeoutConnections(); } /* * If the connection died while we were using it, clean up. * It's critical that the store connection be freed and the * connection pool not be locked while we do this. */ assert !Thread.holdsLock(pool); if (failed) cleanup(); } /** * Release a store protocol object that was being used by a folder. */ void releaseFolderStoreProtocol(IMAPProtocol protocol) { if (protocol == null) return; // should never happen protocol.removeResponseHandler(nonStoreResponseHandler); protocol.addResponseHandler(this); synchronized (pool) { pool.storeConnectionInUse = false; pool.notifyAll(); // in case anyone waiting pool.logger.fine("releaseFolderStoreProtocol()"); timeoutConnections(); } } /** * Empty the connection pool. */ private void emptyConnectionPool(boolean force) { synchronized (pool) { for (int index = pool.authenticatedConnections.size() - 1; index >= 0; --index) { try { IMAPProtocol p = pool.authenticatedConnections.elementAt(index); p.removeResponseHandler(this); if (force) p.disconnect(); else p.logout(); } catch (ProtocolException pex) {}; } pool.authenticatedConnections.removeAllElements(); } pool.logger.fine("removed all authenticated connections from pool"); } /** * Check to see if it's time to shrink the connection pool. */ private void timeoutConnections() { 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.logger.isLoggable(Level.FINE)) { pool.logger.fine("checking for connections to prune: " + (System.currentTimeMillis() - pool.lastTimePruned)); pool.logger.fine("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 = pool.authenticatedConnections. elementAt(index); if (pool.logger.isLoggable(Level.FINE)) pool.logger.fine("protocol last used: " + (System.currentTimeMillis() - p.getTimestamp())); if (System.currentTimeMillis() - p.getTimestamp() > pool.clientTimeoutInterval) { pool.logger.fine( "authenticated connection timed out, " + "logging out the connection"); p.removeResponseHandler(this); pool.authenticatedConnections.removeElementAt(index); try { p.logout(); } catch (ProtocolException pex) {} } } pool.lastTimePruned = System.currentTimeMillis(); } } } /** * Get the block size to use for fetch requests on this Store. */ int getFetchBlockSize() { return blksize; } /** * Ignore the size reported in the BODYSTRUCTURE when fetching data? */ boolean ignoreBodyStructureSize() { return ignoreSize; } /** * Get a reference to the session. */ Session getSession() { return session; } /** * Get the number of milliseconds to cache STATUS response. */ int getStatusCacheTimeout() { return statusCacheTimeout; } /** * Get the maximum size of a message to buffer for append. */ int getAppendBufferSize() { return appendBufferSize; } /** * Get the minimum amount of time to delay when returning from idle. */ int getMinIdleTime() { return minIdleTime; } /** * Throw a SearchException if the search expression is too complex? */ boolean throwSearchException() { return throwSearchException; } /** * Get the default "peek" value. */ boolean getPeek() { return peek; } /** * Return true if the specified capability string is in the list * of capabilities the server announced. * * @param capability the capability string * @return true if the server supports this capability * @exception MessagingException for failures * @since JavaMail 1.3.3 */ public synchronized boolean hasCapability(String capability) throws MessagingException { IMAPProtocol p = null; try { p = getStoreProtocol(); return p.hasCapability(capability); } catch (ProtocolException pex) { throw new MessagingException(pex.getMessage(), pex); } finally { releaseStoreProtocol(p); } } /** * Set the user name to be used with the PROXYAUTH command. * The PROXYAUTH user name can also be set using the * <code>mail.imap.proxyauth.user</code> property when this * Store is created. * * @param user the user name to set * @since JavaMail 1.5.1 */ public void setProxyAuthUser(String user) { proxyAuthUser = user; } /** * Get the user name to be used with the PROXYAUTH command. * * @return the user name * @since JavaMail 1.5.1 */ public String getProxyAuthUser() { return proxyAuthUser; } /** * Check whether this store is connected. Override superclass * method, to actually ping our server connection. */ @Override public synchronized boolean isConnected() { if (!super.isConnected()) { // if we haven't been connected at all, don't bother with // the NOOP. 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) { // will return false below } finally { releaseStoreProtocol(p); } return super.isConnected(); } /** * Close this Store. */ @Override public synchronized void close() throws MessagingException { cleanup(); // do these again in case cleanup returned early // because we were already closed due to a failure closeAllFolders(false); emptyConnectionPool(false); } @Override protected void finalize() throws Throwable { if (!finalizeCleanClose) { // when finalizing, close connections abruptly synchronized (connectionFailedLock) { connectionFailed = true; forceClose = true; } closeFoldersOnStoreFailure = true; // make sure folders get closed } try { close(); } finally { super.finalize(); } } /** * Cleanup before dying. */ private synchronized void cleanup() { // if we're not connected, someone beat us to it if (!super.isConnected()) { logger.fine("IMAPStore cleanup, not connected"); return; } /* * If forceClose is true, some thread ran into an error that suggests * the server might be dead, so we force the folders to close * abruptly without waiting for the server. Used when * the store connection times out, for example. */ boolean force; synchronized (connectionFailedLock) { force = forceClose; forceClose = false; connectionFailed = false; } if (logger.isLoggable(Level.FINE)) logger.fine("IMAPStore cleanup, force " + force); if (!force || closeFoldersOnStoreFailure) { closeAllFolders(force); } emptyConnectionPool(force); // to set the state and send the closed connection event try { super.close(); } catch (MessagingException mex) { // ignore it } logger.fine("IMAPStore cleanup done"); } /** * Close all open Folders. If force is true, close them forcibly. */ private void closeAllFolders(boolean force) { List<IMAPFolder> 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 = foldersCopy.get(i); try { if (force) { logger.fine("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 { logger.fine("close folder"); f.close(false); } } catch (MessagingException mex) { // Who cares ?! Ignore 'em. } catch (IllegalStateException ex) { // Ditto } } } } /** * Get the default folder, representing the root of this user's * namespace. Returns a closed DefaultFolder object. */ @Override public synchronized Folder getDefaultFolder() throws MessagingException { checkConnected(); return new DefaultFolder(this); } /** * Get named folder. Returns a new, closed IMAPFolder. */ @Override public synchronized Folder getFolder(String name) throws MessagingException { checkConnected(); return newIMAPFolder(name, IMAPFolder.UNKNOWN_SEPARATOR); } /** * Get named folder. Returns a new, closed IMAPFolder. */ @Override public synchronized Folder getFolder(URLName url) throws MessagingException { checkConnected(); return newIMAPFolder(url.getFile(), IMAPFolder.UNKNOWN_SEPARATOR); } /** * Create an IMAPFolder object. If user supplied their own class, * use it. Otherwise, call the constructor. * * @param fullName the full name of the folder * @param separator the separator character for the folder hierarchy * @param isNamespace does this name represent a namespace? * @return the new IMAPFolder object */ protected IMAPFolder newIMAPFolder(String fullName, char separator, Boolean isNamespace) { IMAPFolder f = null; if (folderConstructor != null) { try { Object[] o = { fullName, Character.valueOf(separator), this, isNamespace }; f = (IMAPFolder)folderConstructor.newInstance(o); } catch (Exception ex) { logger.log(Level.FINE, "exception creating IMAPFolder class", ex); } } if (f == null) f = new IMAPFolder(fullName, separator, this, isNamespace); return f; } /** * Create an IMAPFolder object. Call the newIMAPFolder method * above with a null isNamespace. * * @param fullName the full name of the folder * @param separator the separator character for the folder hierarchy * @return the new IMAPFolder object */ protected IMAPFolder newIMAPFolder(String fullName, char separator) { return newIMAPFolder(fullName, separator, null); } /** * Create an IMAPFolder object. If user supplied their own class, * use it. Otherwise, call the constructor. * * @param li the ListInfo for the folder * @return the new IMAPFolder object */ protected IMAPFolder newIMAPFolder(ListInfo li) { IMAPFolder f = null; if (folderConstructorLI != null) { try { Object[] o = { li, this }; f = (IMAPFolder)folderConstructorLI.newInstance(o); } catch (Exception ex) { logger.log(Level.FINE, "exception creating IMAPFolder class LI", ex); } } if (f == null) f = new IMAPFolder(li, this); return f; } /** * Using the IMAP NAMESPACE command (RFC 2342), return a set * of folders representing the Personal namespaces. */ @Override public Folder[] getPersonalNamespaces() throws MessagingException { Namespaces ns = getNamespaces(); if (ns == null || ns.personal == null) return super.getPersonalNamespaces(); return namespaceToFolders(ns.personal, null); } /** * Using the IMAP NAMESPACE command (RFC 2342), return a set * of folders representing the User's namespaces. */ @Override public Folder[] getUserNamespaces(String user) throws MessagingException { Namespaces ns = getNamespaces(); if (ns == null || ns.otherUsers == null) return super.getUserNamespaces(user); return namespaceToFolders(ns.otherUsers, user); } /** * Using the IMAP NAMESPACE command (RFC 2342), return a set * of folders representing the Shared namespaces. */ @Override public Folder[] getSharedNamespaces() throws MessagingException { Namespaces ns = getNamespaces(); if (ns == null || ns.shared == null) return super.getSharedNamespaces(); return namespaceToFolders(ns.shared, null); } private synchronized Namespaces getNamespaces() throws MessagingException { 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); } } return namespaces; } private Folder[] namespaceToFolders(Namespaces.Namespace[] ns, 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] = newIMAPFolder(name, ns[i].delimiter, Boolean.valueOf(user == null)); } return fa; } /** * 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 */ @Override public synchronized Quota[] getQuota(String root) throws MessagingException { 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); } return qa; } /** * Set the quotas for the quota root specified in the quota argument. * Typically this will be one of the quota roots obtained from the * <code>getQuota</code> method, but it need not be. * * @param quota the quota to set * @exception MessagingException if the server doesn't support the * QUOTA extension */ @Override public synchronized void setQuota(Quota quota) throws MessagingException { 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); } } private void checkConnected() { assert Thread.holdsLock(this); if (!super.isConnected()) throw new IllegalStateException("Not connected"); } /** * Response handler method. */ @Override public void handleResponse(Response r) { // Any of these responses may have a response code. if (r.isOK() || r.isNO() || r.isBAD() || r.isBYE()) handleResponseCode(r); if (r.isBYE()) { logger.fine("IMAPStore connection dead"); // Store's IMAP connection is dead, save the response so that // releaseStoreProtocol will cleanup later. synchronized (connectionFailedLock) { connectionFailed = true; if (r.isSynthetic()) forceClose = true; } return; } } /** * Use the IMAP IDLE command (see * <A HREF="http://www.ietf.org/rfc/rfc2177.txt">RFC 2177</A>), * 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 <code>ConnectionListener</code> 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. <p> * * If the mail.imap.enableimapevents property is set, notifications * received while the IDLE command is active will be delivered to * <code>ConnectionListener</code>s as events with a type of * <code>IMAPStore.RESPONSE</code>. 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 * <code>idle</code> method on <code>IMAPFolder</code>. <p> * * NOTE: This capability is highly experimental and likely will change * in future releases. <p> * * 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 */ public void idle() throws MessagingException { IMAPProtocol p = null; // ASSERT: Must NOT be called with the connection pool // synchronization lock held. assert !Thread.holdsLock(pool); synchronized (this) { checkConnected(); } boolean needNotification = false; try { synchronized (pool) { p = getStoreProtocol(); if (pool.idleState != ConnectionPool.RUNNING) { // 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) { // restore the interrupted state, which callers might // depend on Thread.currentThread().interrupt(); // stop waiting and return to caller throw new MessagingException("idle interrupted", ex); } return; } p.idleStart(); needNotification = true; pool.idleState = ConnectionPool.IDLE; 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.idleProtocol = null; pool.notifyAll(); needNotification = false; 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) { // restore the interrupted state, which callers might // depend on Thread.currentThread().interrupt(); } } } 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 { if (needNotification) { synchronized (pool) { pool.idleState = ConnectionPool.RUNNING; pool.idleProtocol = null; pool.notifyAll(); } } releaseStoreProtocol(p); } } /* * If an IDLE command is in progress, abort it if necessary, * and wait until it completes. * ASSERT: Must be called with the pool's lock held. */ private void waitIfIdle() throws ProtocolException { 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) { // If someone is trying to interrupt us we can't keep going // around the loop waiting for IDLE to complete, but we can't // just return because callers expect the idleState to be // RUNNING when we return. Throwing this exception seems // like the best choice. throw new ProtocolException("Interrupted waitIfIdle", ex); } } } /** * Send the IMAP ID command (if supported by the server) and return * the result from the server. The ID command identfies the client * to the server and returns information about the server to the client. * See <A HREF="http://www.ietf.org/rfc/rfc2971.txt">RFC 2971</A>. * The returned Map is unmodifiable. * * @param clientParams a Map of keys and values identifying the client * @return a Map of keys and values identifying the server * @exception MessagingException if the server doesn't support the * ID extension * @since JavaMail 1.5.1 */ public synchronized Map<String, String> id(Map<String, String> clientParams) throws MessagingException { checkConnected(); Map<String, String> serverParams = null; IMAPProtocol p = null; try { p = getStoreProtocol(); serverParams = p.id(clientParams); } catch (BadCommandException bex) { throw new MessagingException("ID not supported", bex); } catch (ConnectionException cex) { throw new StoreClosedException(this, cex.getMessage()); } catch (ProtocolException pex) { throw new MessagingException(pex.getMessage(), pex); } finally { releaseStoreProtocol(p); } return serverParams; } /** * Handle notifications and alerts. * Response must be an OK, NO, BAD, or BYE response. */ void handleResponseCode(Response r) { if (enableResponseEvents) notifyStoreListeners(IMAPStore.RESPONSE, r.toString()); 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); } private String traceUser(String user) { return debugusername ? user : "<user name suppressed>"; } private String tracePassword(String password) { return debugpassword ? password : (password == null ? "<null>" : "<non-null>"); } }
⏎ com/sun/mail/imap/IMAPStore.java
Or download all of them as a single archive file:
File name: javax.mail-1.6.2-sources.jar File size: 851487 bytes Release date: 2018-08-29 Download
⇒ Download and Install javax.mail-1.5.4.jar
⇐ Download and Install javax.mail-1.6.2.jar
2016-01-07, 9768👍, 0💬
Popular Posts:
Commons Pool provides an Object-pooling API, with three major aspects: 1. A generic object pool inte...
commons-lang-2.6.jar is the JAR file for Apache Commons Lang 2.6, which provides a host of helper ut...
The Apache FontBox library is an open source Java tool to obtain low level information from font fil...
JDK 17 java.base.jmod is the JMOD file for JDK 17 Base module. JDK 17 Base module compiled class fil...
JDK 11 java.security.jgss.jmod is the JMOD file for JDK 11 Security JGSS (Java Generic Security Serv...