Java tutorial
/* * 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 javax.mail; import java.lang.reflect.*; import java.io.*; import java.net.*; import java.security.*; import java.util.Collections; import java.util.Hashtable; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Properties; import java.util.StringTokenizer; import java.util.ServiceLoader; import java.util.logging.Level; import java.util.concurrent.Executor; import com.sun.mail.util.LineInputStream; import com.sun.mail.util.MailLogger; /** * The Session class represents a mail session and is not subclassed. * It collects together properties and defaults used by the mail API's. * A single default session can be shared by multiple applications on the * desktop. Unshared sessions can also be created. <p> * * The Session class provides access to the protocol providers that * implement the <code>Store</code>, <code>Transport</code>, and related * classes. The protocol providers are configured using the following files: * <ul> * <li> <code>javamail.providers</code> and * <code>javamail.default.providers</code> </li> * <li> <code>javamail.address.map</code> and * <code>javamail.default.address.map</code> </li> * </ul> * <p> * Each <code>javamail.</code><i>X</i> resource file is searched for using * three methods in the following order: * <ol> * <li> <code><i>java.home</i>/<i>conf</i>/javamail.</code><i>X</i> </li> * <li> <code>META-INF/javamail.</code><i>X</i> </li> * <li> <code>META-INF/javamail.default.</code><i>X</i> </li> * </ol> * <p> * (Where <i>java.home</i> is the value of the "java.home" System property * and <i>conf</i> is the directory named "conf" if it exists, * otherwise the directory named "lib"; the "conf" directory was * introduced in JDK 1.9.) * <p> * The first method allows the user to include their own version of the * resource file by placing it in the <i>conf</i> directory where the * <code>java.home</code> property points. The second method allows an * application that uses the JavaMail APIs to include their own resource * files in their application's or jar file's <code>META-INF</code> * directory. The <code>javamail.default.</code><i>X</i> default files * are part of the JavaMail <code>mail.jar</code> file and should not be * supplied by users. <p> * * File location depends upon how the <code>ClassLoader</code> method * <code>getResource</code> is implemented. Usually, the * <code>getResource</code> method searches through CLASSPATH until it * finds the requested file and then stops. <p> * * The ordering of entries in the resource files matters. If multiple * entries exist, the first entries take precedence over the later * entries. For example, the first IMAP provider found will be set as the * default IMAP implementation until explicitly changed by the * application. The user- or system-supplied resource files augment, they * do not override, the default files included with the JavaMail APIs. * This means that all entries in all files loaded will be available. <p> * * <b><code>javamail.providers</code></b> and * <b><code>javamail.default.providers</code></b><p> * * These resource files specify the stores and transports that are * available on the system, allowing an application to "discover" what * store and transport implementations are available. The protocol * implementations are listed one per line. The file format defines four * attributes that describe a protocol implementation. Each attribute is * an "="-separated name-value pair with the name in lowercase. Each * name-value pair is semi-colon (";") separated. The following names * are defined. * * <table border=1> * <caption> * Attribute Names in Providers Files * </caption> * <tr> * <th>Name</th><th>Description</th> * </tr> * <tr> * <td>protocol</td> * <td>Name assigned to protocol. * For example, <code>smtp</code> for Transport.</td> * </tr> * <tr> * <td>type</td> * <td>Valid entries are <code>store</code> and <code>transport</code>.</td> * </tr> * <tr> * <td>class</td> * <td>Class name that implements this protocol.</td> * </tr> * <tr> * <td>vendor</td> * <td>Optional string identifying the vendor.</td> * </tr> * <tr> * <td>version</td> * <td>Optional string identifying the version.</td> * </tr> * </table><p> * * Here's an example of <code>META-INF/javamail.default.providers</code> * file contents: * <pre> * protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Oracle; * protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Oracle; * </pre><p> * * The current implementation also supports configuring providers using * the Java SE {@link java.util.ServiceLoader ServiceLoader} mechanism. * When creating your own provider, create a {@link Provider} subclass, * for example: * <pre> * package com.example; * * import javax.mail.Provider; * * public class MyProvider extends Provider { * public MyProvider() { * super(Provider.Type.STORE, "myprot", MyStore.class.getName(), * "Example", null); * } * } * </pre> * Then include a file named <code>META-INF/services/javax.mail.Provider</code> * in your jar file that lists the name of your Provider class: * <pre> * com.example.MyProvider * </pre> * <p> * * <b><code>javamail.address.map</code></b> and * <b><code>javamail.default.address.map</code></b><p> * * These resource files map transport address types to the transport * protocol. The <code>getType</code> method of * <code>javax.mail.Address</code> returns the address type. The * <code>javamail.address.map</code> file maps the transport type to the * protocol. The file format is a series of name-value pairs. Each key * name should correspond to an address type that is currently installed * on the system; there should also be an entry for each * <code>javax.mail.Address</code> implementation that is present if it is * to be used. For example, the * <code>javax.mail.internet.InternetAddress</code> method * <code>getType</code> returns "rfc822". Each referenced protocol should * be installed on the system. For the case of <code>news</code>, below, * the client should install a Transport provider supporting the nntp * protocol. <p> * * Here are the typical contents of a <code>javamail.address.map</code> file: * <pre> * rfc822=smtp * news=nntp * </pre> * * @author John Mani * @author Bill Shannon * @author Max Spivak */ public final class Session { private final Properties props; private final Authenticator authenticator; private final Hashtable<URLName, PasswordAuthentication> authTable = new Hashtable<>(); private boolean debug = false; private PrintStream out; // debug output stream private MailLogger logger; private List<Provider> providers; private final Map<String, Provider> providersByProtocol = new HashMap<>(); private final Map<String, Provider> providersByClassName = new HashMap<>(); private final Properties addressMap = new Properties(); // maps type to protocol private boolean loadedProviders; // javamail.[default.]providers loaded? // the queue of events to be delivered, if mail.event.scope===session private final EventQueue q; // The default session. private static Session defaultSession = null; private static final String confDir; static { String dir = null; try { dir = AccessController.doPrivileged(new PrivilegedAction<String>() { @Override public String run() { String home = System.getProperty("java.home"); String newdir = home + File.separator + "conf"; File conf = new File(newdir); if (conf.exists()) return newdir + File.separator; else return home + File.separator + "lib" + File.separator; } }); } catch (Exception ex) { // ignore any exceptions } confDir = dir; } // Constructor is not public private Session(Properties props, Authenticator authenticator) { this.props = props; this.authenticator = authenticator; if (Boolean.valueOf(props.getProperty("mail.debug")).booleanValue()) debug = true; initLogger(); logger.log(Level.CONFIG, "JavaMail version {0}", Version.version); // get the Class associated with the Authenticator Class<?> cl; if (authenticator != null) cl = authenticator.getClass(); else cl = this.getClass(); // load the resources loadAddressMap(cl); q = new EventQueue((Executor) props.get("mail.event.executor")); } private final synchronized void initLogger() { logger = new MailLogger(this.getClass(), "DEBUG", debug, getDebugOut()); } /** * Get a new Session object. * * @param props Properties object that hold relevant properties.<br> * It is expected that the client supplies values * for the properties listed in Appendix A of the * JavaMail spec (particularly mail.store.protocol, * mail.transport.protocol, mail.host, mail.user, * and mail.from) as the defaults are unlikely to * work in all cases. * @param authenticator Authenticator object used to call back to * the application when a user name and password is * needed. * @return a new Session object * @see javax.mail.Authenticator */ public static Session getInstance(Properties props, Authenticator authenticator) { return new Session(props, authenticator); } /** * Get a new Session object. * * @param props Properties object that hold relevant properties.<br> * It is expected that the client supplies values * for the properties listed in Appendix A of the * JavaMail spec (particularly mail.store.protocol, * mail.transport.protocol, mail.host, mail.user, * and mail.from) as the defaults are unlikely to * work in all cases. * @return a new Session object * @since JavaMail 1.2 */ public static Session getInstance(Properties props) { return new Session(props, null); } /** * Get the default Session object. If a default has not yet been * setup, a new Session object is created and installed as the * default. <p> * * Since the default session is potentially available to all * code executing in the same Java virtual machine, and the session * can contain security sensitive information such as user names * and passwords, access to the default session is restricted. * The Authenticator object, which must be created by the caller, * is used indirectly to check access permission. The Authenticator * object passed in when the session is created is compared with * the Authenticator object passed in to subsequent requests to * get the default session. If both objects are the same, or are * from the same ClassLoader, the request is allowed. Otherwise, * it is denied. <p> * * Note that if the Authenticator object used to create the session * is null, anyone can get the default session by passing in null. <p> * * Note also that the Properties object is used only the first time * this method is called, when a new Session object is created. * Subsequent calls return the Session object that was created by the * first call, and ignore the passed Properties object. Use the * <code>getInstance</code> method to get a new Session object every * time the method is called. <p> * * Additional security Permission objects may be used to * control access to the default session. <p> * * In the current implementation, if a SecurityManager is set, the * caller must have the <code>RuntimePermission("setFactory")</code> * permission. * * @param props Properties object. Used only if a new Session * object is created.<br> * It is expected that the client supplies values * for the properties listed in Appendix A of the * JavaMail spec (particularly mail.store.protocol, * mail.transport.protocol, mail.host, mail.user, * and mail.from) as the defaults are unlikely to * work in all cases. * @param authenticator Authenticator object. Used only if a * new Session object is created. Otherwise, * it must match the Authenticator used to create * the Session. * @return the default Session object */ public static synchronized Session getDefaultInstance(Properties props, Authenticator authenticator) { if (defaultSession == null) { SecurityManager security = System.getSecurityManager(); if (security != null) security.checkSetFactory(); defaultSession = new Session(props, authenticator); } else { // have to check whether caller is allowed to see default session if (defaultSession.authenticator == authenticator) ; // either same object or both null, either way OK else if (defaultSession.authenticator != null && authenticator != null && defaultSession.authenticator .getClass().getClassLoader() == authenticator.getClass().getClassLoader()) ; // both objects came from the same class loader, OK else // anything else is not allowed throw new SecurityException("Access to default session denied"); } return defaultSession; } /** * Get the default Session object. If a default has not yet been * setup, a new Session object is created and installed as the * default. <p> * * Note that a default session created with no Authenticator is * available to all code executing in the same Java virtual * machine, and the session can contain security sensitive * information such as user names and passwords. * * @param props Properties object. Used only if a new Session * object is created.<br> * It is expected that the client supplies values * for the properties listed in Appendix A of the * JavaMail spec (particularly mail.store.protocol, * mail.transport.protocol, mail.host, mail.user, * and mail.from) as the defaults are unlikely to * work in all cases. * @return the default Session object * @since JavaMail 1.2 */ public static Session getDefaultInstance(Properties props) { return getDefaultInstance(props, null); } /** * Set the debug setting for this Session. * <p> * Since the debug setting can be turned on only after the Session * has been created, to turn on debugging in the Session * constructor, set the property <code>mail.debug</code> in the * Properties object passed in to the constructor to true. The * value of the <code>mail.debug</code> property is used to * initialize the per-Session debugging flag. Subsequent calls to * the <code>setDebug</code> method manipulate the per-Session * debugging flag and have no affect on the <code>mail.debug</code> * property. * * @param debug Debug setting */ public synchronized void setDebug(boolean debug) { this.debug = debug; initLogger(); logger.log(Level.CONFIG, "setDebug: JavaMail version {0}", Version.version); } /** * Get the debug setting for this Session. * * @return current debug setting */ public synchronized boolean getDebug() { return debug; } /** * Set the stream to be used for debugging output for this session. * If <code>out</code> is null, <code>System.out</code> will be used. * Note that debugging output that occurs before any session is created, * as a result of setting the <code>mail.debug</code> system property, * will always be sent to <code>System.out</code>. * * @param out the PrintStream to use for debugging output * @since JavaMail 1.3 */ public synchronized void setDebugOut(PrintStream out) { this.out = out; initLogger(); } /** * Returns the stream to be used for debugging output. If no stream * has been set, <code>System.out</code> is returned. * * @return the PrintStream to use for debugging output * @since JavaMail 1.3 */ public synchronized PrintStream getDebugOut() { if (out == null) return System.out; else return out; } /** * This method returns an array of all the implementations installed * via the javamail.[default.]providers files that can * be loaded using the ClassLoader available to this application. * * @return Array of configured providers */ public synchronized Provider[] getProviders() { List<Provider> plist = new ArrayList<Provider>(); boolean needFallback = true; // first, add all the services ServiceLoader<Provider> loader = ServiceLoader.load(Provider.class); for (Provider p : loader) { plist.add(p); needFallback = false; } // then, add all the providers from config files if (!loadedProviders) loadProviders(needFallback); if (providers != null) plist.addAll(providers); Provider[] _providers = new Provider[plist.size()]; plist.toArray(_providers); return _providers; } /** * Returns the default Provider for the protocol * specified. Checks mail.<protocol>.class property * first and if it exists, returns the Provider * associated with this implementation. If it doesn't exist, * returns the Provider that appeared first in the * configuration files. If an implementation for the protocol * isn't found, throws NoSuchProviderException * * @param protocol Configured protocol (i.e. smtp, imap, etc) * @return Currently configured Provider for the specified protocol * @exception NoSuchProviderException If a provider for the given * protocol is not found. */ public synchronized Provider getProvider(String protocol) throws NoSuchProviderException { if (protocol == null || protocol.length() <= 0) { throw new NoSuchProviderException("Invalid protocol: null"); } Provider _provider = null; // check if the mail.<protocol>.class property exists String _className = props.getProperty("mail." + protocol + ".class"); if (_className != null) { if (logger.isLoggable(Level.FINE)) { logger.fine("mail." + protocol + ".class property exists and points to " + _className); } _provider = getProviderByClassName(_className); } if (_provider == null) _provider = getProviderByProtocol(protocol); if (_provider == null) { throw new NoSuchProviderException("No provider for " + protocol); } else { if (logger.isLoggable(Level.FINE)) { logger.fine("getProvider() returning " + _provider.toString()); } return _provider; } } /** * Set the passed Provider to be the default implementation * for the protocol in Provider.protocol overriding any previous values. * * @param provider Currently configured Provider which will be * set as the default for the protocol * @exception NoSuchProviderException If the provider passed in * is invalid. */ public synchronized void setProvider(Provider provider) throws NoSuchProviderException { if (provider == null) { throw new NoSuchProviderException("Can't set null provider"); } providersByProtocol.put(provider.getProtocol(), provider); providersByClassName.put(provider.getClassName(), provider); props.put("mail." + provider.getProtocol() + ".class", provider.getClassName()); } /** * Get a Store object that implements this user's desired Store * protocol. The <code>mail.store.protocol</code> property specifies the * desired protocol. If an appropriate Store object is not obtained, * NoSuchProviderException is thrown * * @return a Store object * @exception NoSuchProviderException If a provider for the given * protocol is not found. */ public Store getStore() throws NoSuchProviderException { return getStore(getProperty("mail.store.protocol")); } /** * Get a Store object that implements the specified protocol. If an * appropriate Store object cannot be obtained, * NoSuchProviderException is thrown. * * @param protocol the Store protocol * @return a Store object * @exception NoSuchProviderException If a provider for the given * protocol is not found. */ public Store getStore(String protocol) throws NoSuchProviderException { return getStore(new URLName(protocol, null, -1, null, null, null)); } /** * Get a Store object for the given URLName. If the requested Store * object cannot be obtained, NoSuchProviderException is thrown. * * The "scheme" part of the URL string (Refer RFC 1738) is used * to locate the Store protocol. <p> * * @param url URLName that represents the desired Store * @return a closed Store object * @see #getFolder(URLName) * @see javax.mail.URLName * @exception NoSuchProviderException If a provider for the given * URLName is not found. */ public Store getStore(URLName url) throws NoSuchProviderException { String protocol = url.getProtocol(); Provider p = getProvider(protocol); return getStore(p, url); } /** * Get an instance of the store specified by Provider. Instantiates * the store and returns it. * * @param provider Store Provider that will be instantiated * @return Instantiated Store * @exception NoSuchProviderException If a provider for the given * Provider is not found. */ public Store getStore(Provider provider) throws NoSuchProviderException { return getStore(provider, null); } /** * Get an instance of the store specified by Provider. If the URLName * is not null, uses it, otherwise creates a new one. Instantiates * the store and returns it. This is a private method used by * getStore(Provider) and getStore(URLName) * * @param provider Store Provider that will be instantiated * @param url URLName used to instantiate the Store * @return Instantiated Store * @exception NoSuchProviderException If a provider for the given * Provider/URLName is not found. */ private Store getStore(Provider provider, URLName url) throws NoSuchProviderException { // make sure we have the correct type of provider if (provider == null || provider.getType() != Provider.Type.STORE) { throw new NoSuchProviderException("invalid provider"); } return getService(provider, url, Store.class); } /** * Get a closed Folder object for the given URLName. If the requested * Folder object cannot be obtained, null is returned. <p> * * The "scheme" part of the URL string (Refer RFC 1738) is used * to locate the Store protocol. The rest of the URL string (that is, * the "schemepart", as per RFC 1738) is used by that Store * in a protocol dependent manner to locate and instantiate the * appropriate Folder object. <p> * * Note that RFC 1738 also specifies the syntax for the * "schemepart" for IP-based protocols (IMAP4, POP3, etc.). * Providers of IP-based mail Stores should implement that * syntax for referring to Folders. <p> * * @param url URLName that represents the desired folder * @return Folder * @see #getStore(URLName) * @see javax.mail.URLName * @exception NoSuchProviderException If a provider for the given * URLName is not found. * @exception MessagingException if the Folder could not be * located or created. */ public Folder getFolder(URLName url) throws MessagingException { // First get the Store Store store = getStore(url); store.connect(); return store.getFolder(url); } /** * Get a Transport object that implements this user's desired * Transport protcol. The <code>mail.transport.protocol</code> property * specifies the desired protocol. If an appropriate Transport * object cannot be obtained, MessagingException is thrown. * * @return a Transport object * @exception NoSuchProviderException If the provider is not found. */ public Transport getTransport() throws NoSuchProviderException { String prot = getProperty("mail.transport.protocol"); if (prot != null) return getTransport(prot); // if the property isn't set, use the protocol for "rfc822" prot = (String) addressMap.get("rfc822"); if (prot != null) return getTransport(prot); return getTransport("smtp"); // if all else fails } /** * Get a Transport object that implements the specified protocol. * If an appropriate Transport object cannot be obtained, null is * returned. * * @param protocol the Transport protocol * @return a Transport object * @exception NoSuchProviderException If provider for the given * protocol is not found. */ public Transport getTransport(String protocol) throws NoSuchProviderException { return getTransport(new URLName(protocol, null, -1, null, null, null)); } /** * Get a Transport object for the given URLName. If the requested * Transport object cannot be obtained, NoSuchProviderException is thrown. * * The "scheme" part of the URL string (Refer RFC 1738) is used * to locate the Transport protocol. <p> * * @param url URLName that represents the desired Transport * @return a closed Transport object * @see javax.mail.URLName * @exception NoSuchProviderException If a provider for the given * URLName is not found. */ public Transport getTransport(URLName url) throws NoSuchProviderException { String protocol = url.getProtocol(); Provider p = getProvider(protocol); return getTransport(p, url); } /** * Get an instance of the transport specified in the Provider. Instantiates * the transport and returns it. * * @param provider Transport Provider that will be instantiated * @return Instantiated Transport * @exception NoSuchProviderException If provider for the given * provider is not found. */ public Transport getTransport(Provider provider) throws NoSuchProviderException { return getTransport(provider, null); } /** * Get a Transport object that can transport a Message of the * specified address type. * * @param address an address for which a Transport is needed * @return A Transport object * @see javax.mail.Address * @exception NoSuchProviderException If provider for the * Address type is not found */ public Transport getTransport(Address address) throws NoSuchProviderException { String transportProtocol; transportProtocol = getProperty("mail.transport.protocol." + address.getType()); if (transportProtocol != null) return getTransport(transportProtocol); transportProtocol = (String) addressMap.get(address.getType()); if (transportProtocol != null) return getTransport(transportProtocol); throw new NoSuchProviderException("No provider for Address type: " + address.getType()); } /** * Get a Transport object using the given provider and urlname. * * @param provider the provider to use * @param url urlname to use (can be null) * @return A Transport object * @exception NoSuchProviderException If no provider or the provider * was the wrong class. */ private Transport getTransport(Provider provider, URLName url) throws NoSuchProviderException { // make sure we have the correct type of provider if (provider == null || provider.getType() != Provider.Type.TRANSPORT) { throw new NoSuchProviderException("invalid provider"); } return getService(provider, url, Transport.class); } /** * Get a Service object. Needs a provider object, but will * create a URLName if needed. It attempts to instantiate * the correct class. * * @param provider which provider to use * @param url which URLName to use (can be null) * @param type the service type (class) * @exception NoSuchProviderException thrown when the class cannot be * found or when it does not have the correct constructor * (Session, URLName), or if it is not derived from * Service. */ private <T extends Service> T getService(Provider provider, URLName url, Class<T> type) throws NoSuchProviderException { // need a provider and url if (provider == null) { throw new NoSuchProviderException("null"); } // create a url if needed if (url == null) { url = new URLName(provider.getProtocol(), null, -1, null, null, null); } Object service = null; // get the ClassLoader associated with the Authenticator ClassLoader cl; if (authenticator != null) cl = authenticator.getClass().getClassLoader(); else cl = this.getClass().getClassLoader(); // now load the class Class<?> serviceClass = null; try { // First try the "application's" class loader. ClassLoader ccl = getContextClassLoader(); if (ccl != null) try { serviceClass = Class.forName(provider.getClassName(), false, ccl); } catch (ClassNotFoundException ex) { // ignore it } if (serviceClass == null || !type.isAssignableFrom(serviceClass)) serviceClass = Class.forName(provider.getClassName(), false, cl); if (!type.isAssignableFrom(serviceClass)) throw new ClassCastException(type.getName() + " " + serviceClass.getName()); } catch (Exception 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.) try { serviceClass = Class.forName(provider.getClassName()); if (!type.isAssignableFrom(serviceClass)) throw new ClassCastException(type.getName() + " " + serviceClass.getName()); } catch (Exception ex) { // Nothing worked, give up. logger.log(Level.FINE, "Exception loading provider", ex); throw new NoSuchProviderException(provider.getProtocol()); } } // construct an instance of the class try { Class<?>[] c = { javax.mail.Session.class, javax.mail.URLName.class }; Constructor<?> cons = serviceClass.getConstructor(c); Object[] o = { this, url }; service = cons.newInstance(o); } catch (Exception ex) { logger.log(Level.FINE, "Exception loading provider", ex); throw new NoSuchProviderException(provider.getProtocol()); } return type.cast(service); } /** * Save a PasswordAuthentication for this (store or transport) URLName. * If pw is null the entry corresponding to the URLName is removed. * <p> * This is normally used only by the store or transport implementations * to allow authentication information to be shared among multiple * uses of a session. * * @param url the URLName * @param pw the PasswordAuthentication to save */ public void setPasswordAuthentication(URLName url, PasswordAuthentication pw) { if (pw == null) authTable.remove(url); else authTable.put(url, pw); } /** * Return any saved PasswordAuthentication for this (store or transport) * URLName. Normally used only by store or transport implementations. * * @param url the URLName * @return the PasswordAuthentication corresponding to the URLName */ public PasswordAuthentication getPasswordAuthentication(URLName url) { return authTable.get(url); } /** * Call back to the application to get the needed user name and password. * The application should put up a dialog something like: * <pre> * Connecting to <protocol> mail service on host <addr>, port <port>. * <prompt> * * User Name: <defaultUserName> * Password: * </pre> * * @param addr InetAddress of the host. may be null. * @param port the port on the host * @param protocol protocol scheme (e.g. imap, pop3, etc.) * @param prompt any additional String to show as part of * the prompt; may be null. * @param defaultUserName the default username. may be null. * @return the authentication which was collected by the authenticator; * may be null. */ public PasswordAuthentication requestPasswordAuthentication(InetAddress addr, int port, String protocol, String prompt, String defaultUserName) { if (authenticator != null) { return authenticator.requestPasswordAuthentication(addr, port, protocol, prompt, defaultUserName); } else { return null; } } /** * Returns the Properties object associated with this Session * * @return Properties object */ public Properties getProperties() { return props; } /** * Returns the value of the specified property. Returns null * if this property does not exist. * * @param name the property name * @return String that is the property value */ public String getProperty(String name) { return props.getProperty(name); } /** * Get the Provider that uses the specified class name. * * @param className the class name * @return the Provider */ private Provider getProviderByClassName(String className) { // first, try our local list of providers Provider p = providersByClassName.get(className); if (p != null) return p; // now, try services ServiceLoader<Provider> loader = ServiceLoader.load(Provider.class); for (Provider pp : loader) { if (className.equals(pp.getClassName())) return pp; } // finally, if we haven't loaded our config, load it and try again if (!loadedProviders) { loadProviders(true); p = providersByClassName.get(className); } return p; } /** * Get the Provider for the specified protocol. * * @param protocol the protocol * @return the Provider */ private Provider getProviderByProtocol(String protocol) { // first, try our local list of providers Provider p = providersByProtocol.get(protocol); if (p != null) return p; // now, try services ServiceLoader<Provider> loader = ServiceLoader.load(Provider.class); for (Provider pp : loader) { if (protocol.equals(pp.getProtocol())) return pp; } // finally, if we haven't loaded our config, load it and try again if (!loadedProviders) { loadProviders(true); p = providersByProtocol.get(protocol); } return p; } /** * Load the protocol providers config files. * If fallback is true, provide built in defaults if nothing is loaded. */ private void loadProviders(boolean fallback) { StreamLoader loader = new StreamLoader() { @Override public void load(InputStream is) throws IOException { loadProvidersFromStream(is); } }; // load system-wide javamail.providers from the // <java.home>/{conf,lib} directory try { if (confDir != null) loadFile(confDir + "javamail.providers", loader); } catch (SecurityException ex) { } // get the Class associated with the Authenticator Class<?> cl; if (authenticator != null) cl = authenticator.getClass(); else cl = this.getClass(); // load the META-INF/javamail.providers file supplied by an application loadAllResources("META-INF/javamail.providers", cl, loader); // load default META-INF/javamail.default.providers from mail.jar file loadResource("/META-INF/javamail.default.providers", cl, loader, false); /* * If we haven't loaded any providers and the fallback configuration * is needed, fake it. */ if ((providers == null || providers.size() == 0) && fallback) { logger.config("failed to load any providers, using defaults"); // failed to load any providers, initialize with our defaults addProvider(new Provider(Provider.Type.STORE, "imap", "com.sun.mail.imap.IMAPStore", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.STORE, "imaps", "com.sun.mail.imap.IMAPSSLStore", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.STORE, "pop3", "com.sun.mail.pop3.POP3Store", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.STORE, "pop3s", "com.sun.mail.pop3.POP3SSLStore", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.TRANSPORT, "smtp", "com.sun.mail.smtp.SMTPTransport", "Oracle", Version.version)); addProvider(new Provider(Provider.Type.TRANSPORT, "smtps", "com.sun.mail.smtp.SMTPSSLTransport", "Oracle", Version.version)); } if (logger.isLoggable(Level.CONFIG)) { // dump the output of the tables for debugging logger.config("Tables of loaded providers from javamail.providers"); logger.config("Providers Listed By Class Name: " + providersByClassName.toString()); logger.config("Providers Listed By Protocol: " + providersByProtocol.toString()); } loadedProviders = true; } private void loadProvidersFromStream(InputStream is) throws IOException { if (is != null) { LineInputStream lis = new LineInputStream(is); String currLine; // load and process one line at a time using LineInputStream while ((currLine = lis.readLine()) != null) { if (currLine.startsWith("#")) continue; if (currLine.trim().length() == 0) continue; // skip blank line Provider.Type type = null; String protocol = null, className = null; String vendor = null, version = null; // separate line into key-value tuples StringTokenizer tuples = new StringTokenizer(currLine, ";"); while (tuples.hasMoreTokens()) { String currTuple = tuples.nextToken().trim(); // set the value of each attribute based on its key int sep = currTuple.indexOf("="); if (currTuple.startsWith("protocol=")) { protocol = currTuple.substring(sep + 1); } else if (currTuple.startsWith("type=")) { String strType = currTuple.substring(sep + 1); if (strType.equalsIgnoreCase("store")) { type = Provider.Type.STORE; } else if (strType.equalsIgnoreCase("transport")) { type = Provider.Type.TRANSPORT; } } else if (currTuple.startsWith("class=")) { className = currTuple.substring(sep + 1); } else if (currTuple.startsWith("vendor=")) { vendor = currTuple.substring(sep + 1); } else if (currTuple.startsWith("version=")) { version = currTuple.substring(sep + 1); } } // check if a valid Provider; else, continue if (type == null || protocol == null || className == null || protocol.length() <= 0 || className.length() <= 0) { logger.log(Level.CONFIG, "Bad provider entry: {0}", currLine); continue; } Provider provider = new Provider(type, protocol, className, vendor, version); // add the newly-created Provider to the lookup tables addProvider(provider); } } } /** * Add a provider to the session. * * @param provider the provider to add * @since JavaMail 1.4 */ public synchronized void addProvider(Provider provider) { if (providers == null) providers = new ArrayList<Provider>(); providers.add(provider); providersByClassName.put(provider.getClassName(), provider); if (!providersByProtocol.containsKey(provider.getProtocol())) providersByProtocol.put(provider.getProtocol(), provider); } // load maps in reverse order of preference so that the preferred // map is loaded last since its entries will override the previous ones private void loadAddressMap(Class<?> cl) { StreamLoader loader = new StreamLoader() { @Override public void load(InputStream is) throws IOException { addressMap.load(is); } }; // load default META-INF/javamail.default.address.map from mail.jar loadResource("/META-INF/javamail.default.address.map", cl, loader, true); // load the META-INF/javamail.address.map file supplied by an app loadAllResources("META-INF/javamail.address.map", cl, loader); // load system-wide javamail.address.map from the // <java.home>/{conf,lib} directory try { if (confDir != null) loadFile(confDir + "javamail.address.map", loader); } catch (SecurityException ex) { } if (addressMap.isEmpty()) { logger.config("failed to load address map, using defaults"); addressMap.put("rfc822", "smtp"); } } /** * Set the default transport protocol to use for addresses of * the specified type. Normally the default is set by the * <code>javamail.default.address.map</code> or * <code>javamail.address.map</code> files or resources. * * @param addresstype type of address * @param protocol name of protocol * @see #getTransport(Address) * @since JavaMail 1.4 */ public synchronized void setProtocolForAddress(String addresstype, String protocol) { if (protocol == null) addressMap.remove(addresstype); else addressMap.put(addresstype, protocol); } /** * Load from the named file. */ private void loadFile(String name, StreamLoader loader) { InputStream clis = null; try { clis = new BufferedInputStream(new FileInputStream(name)); loader.load(clis); logger.log(Level.CONFIG, "successfully loaded file: {0}", name); } catch (FileNotFoundException fex) { // ignore it } catch (IOException e) { if (logger.isLoggable(Level.CONFIG)) logger.log(Level.CONFIG, "not loading file: " + name, e); } catch (SecurityException sex) { if (logger.isLoggable(Level.CONFIG)) logger.log(Level.CONFIG, "not loading file: " + name, sex); } finally { try { if (clis != null) clis.close(); } catch (IOException ex) { } // ignore it } } /** * Load from the named resource. */ private void loadResource(String name, Class<?> cl, StreamLoader loader, boolean expected) { InputStream clis = null; try { clis = getResourceAsStream(cl, name); if (clis != null) { loader.load(clis); logger.log(Level.CONFIG, "successfully loaded resource: {0}", name); } else { if (expected) logger.log(Level.WARNING, "expected resource not found: {0}", name); } } catch (IOException e) { logger.log(Level.CONFIG, "Exception loading resource", e); } catch (SecurityException sex) { logger.log(Level.CONFIG, "Exception loading resource", sex); } finally { try { if (clis != null) clis.close(); } catch (IOException ex) { } // ignore it } } /** * Load all of the named resource. */ private void loadAllResources(String name, Class<?> cl, StreamLoader loader) { boolean anyLoaded = false; try { URL[] urls; ClassLoader cld = null; // First try the "application's" class loader. cld = getContextClassLoader(); if (cld == null) cld = cl.getClassLoader(); if (cld != null) urls = getResources(cld, name); else urls = getSystemResources(name); if (urls != null) { for (int i = 0; i < urls.length; i++) { URL url = urls[i]; InputStream clis = null; logger.log(Level.CONFIG, "URL {0}", url); try { clis = openStream(url); if (clis != null) { loader.load(clis); anyLoaded = true; logger.log(Level.CONFIG, "successfully loaded resource: {0}", url); } else { logger.log(Level.CONFIG, "not loading resource: {0}", url); } } catch (FileNotFoundException fex) { // ignore it } catch (IOException ioex) { logger.log(Level.CONFIG, "Exception loading resource", ioex); } catch (SecurityException sex) { logger.log(Level.CONFIG, "Exception loading resource", sex); } finally { try { if (clis != null) clis.close(); } catch (IOException cex) { } } } } } catch (Exception ex) { logger.log(Level.CONFIG, "Exception loading resource", ex); } // if failed to load anything, fall back to old technique, just in case if (!anyLoaded) { /* logger.config("!anyLoaded"); */ loadResource("/" + name, cl, loader, false); } } /* * Following are security related methods that work on JDK 1.2 or newer. */ static ClassLoader getContextClassLoader() { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { ClassLoader cl = null; try { cl = Thread.currentThread().getContextClassLoader(); } catch (SecurityException ex) { } return cl; } }); } private static InputStream getResourceAsStream(final Class<?> c, final String name) throws IOException { try { return AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() { @Override public InputStream run() throws IOException { try { return c.getResourceAsStream(name); } catch (RuntimeException e) { // gracefully handle ClassLoader bugs (Tomcat) IOException ioex = new IOException("ClassLoader.getResourceAsStream failed"); ioex.initCause(e); throw ioex; } } }); } catch (PrivilegedActionException e) { throw (IOException) e.getException(); } } private static URL[] getResources(final ClassLoader cl, final String name) { return AccessController.doPrivileged(new PrivilegedAction<URL[]>() { @Override public URL[] run() { URL[] ret = null; try { List<URL> v = Collections.list(cl.getResources(name)); if (!v.isEmpty()) { ret = new URL[v.size()]; v.toArray(ret); } } catch (IOException ioex) { } catch (SecurityException ex) { } return ret; } }); } private static URL[] getSystemResources(final String name) { return AccessController.doPrivileged(new PrivilegedAction<URL[]>() { @Override public URL[] run() { URL[] ret = null; try { List<URL> v = Collections.list(ClassLoader.getSystemResources(name)); if (!v.isEmpty()) { ret = new URL[v.size()]; v.toArray(ret); } } catch (IOException ioex) { } catch (SecurityException ex) { } return ret; } }); } private static InputStream openStream(final URL url) throws IOException { try { return AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() { @Override public InputStream run() throws IOException { return url.openStream(); } }); } catch (PrivilegedActionException e) { throw (IOException) e.getException(); } } EventQueue getEventQueue() { return q; } } /** * Support interface to generalize * code that loads resources from stream. */ interface StreamLoader { public void load(InputStream is) throws IOException; }