/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2016 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://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/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 packager/legal/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.Enumeration; import java.util.Hashtable; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; import java.util.logging.Level; import java.util.concurrent.Executor; import com.sun.mail.util.LineInputStream; import com.sun.mail.util.MailLogger; import java.util.Collections; import java.util.List; /** * 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.
*
* The Session class provides access to the protocol providers that
* implement the Store
, Transport
, and related
* classes. The protocol providers are configured using the following files:
*
javamail.providers
and
* javamail.default.providers
javamail.address.map
and
* javamail.default.address.map
* Each javamail.
X resource file is searched for using
* three methods in the following order:
*
java.home/lib/javamail.
X META-INF/javamail.
X META-INF/javamail.default.
X
* The first method allows the user to include their own version of the
* resource file by placing it in the lib
directory where the
* java.home
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 META-INF
* directory. The javamail.default.
X default files
* are part of the JavaMail mail.jar
file.
*
* File location depends upon how the ClassLoader
method
* getResource
is implemented. Usually, the
* getResource
method searches through CLASSPATH until it
* finds the requested file and then stops.
* * 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.
*
* javamail.providers
and
* javamail.default.providers
* * 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. * *
Name | Description | *
---|---|
protocol | *Name assigned to protocol.
* For example, smtp for Transport. |
*
type | *Valid entries are store and transport . |
*
class | *Class name that implements this protocol. | *
vendor | *Optional string identifying the vendor. | *
version | *Optional string identifying the version. | *
*
* Here's an example of META-INF/javamail.default.providers
* file contents:
*
* protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Oracle; * protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Oracle; *
*
* javamail.address.map
and
* javamail.default.address.map
*
* These resource files map transport address types to the transport
* protocol. The getType
method of
* javax.mail.Address
returns the address type. The
* javamail.address.map
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
* javax.mail.Address
implementation that is present if it is
* to be used. For example, the
* javax.mail.internet.InternetAddress
method
* getType
returns "rfc822". Each referenced protocol should
* be installed on the system. For the case of news
, below,
* the client should install a Transport provider supporting the nntp
* protocol.
*
* Here are the typical contents of a javamail.address.map
file:
*
* rfc822=smtp * news=nntp ** * @author John Mani * @author Bill Shannon * @author Max Spivak */ public final class Session { private final Properties props; private final Authenticator authenticator; private final Hashtable
* * 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.
* * Note that if the Authenticator object used to create the session * is null, anyone can get the default session by passing in null.
*
* 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
* getInstance
method to get a new Session object every
* time the method is called.
* * Additional security Permission objects may be used to * control access to the default session.
*
* In the current implementation, if a SecurityManager is set, the
* caller must have the RuntimePermission("setFactory")
* permission.
*
* @param props Properties object. Used only if a new Session
* object is created.
* 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.
*
* 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.
* 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.
*
* 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
*
* @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.
*
* 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.
*
* 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.
*
* @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
*
* @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
* 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:
* mail.debug
in the
* Properties object passed in to the constructor to true. The
* value of the mail.debug
property is used to
* initialize the per-Session debugging flag. Subsequent calls to
* the setDebug
method manipulate the per-Session
* debugging flag and have no affect on the mail.debug
* 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 out
is null, System.out
will be used.
* Note that debugging output that occurs before any session is created,
* as a result of setting the mail.debug
system property,
* will always be sent to System.out
.
*
* @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, System.out
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() {
Provider[] _providers = new Provider[providers.size()];
providers.copyInto(_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.mail.store.protocol
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. mail.transport.protocol
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.
* Connecting to <protocol> mail service on host <addr>, port <port>.
* <prompt>
*
* User Name: <defaultUserName>
* Password:
*
*
* @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);
}
/**
* Load the protocol providers config files.
*/
private void loadProviders(Class> cl) {
StreamLoader loader = new StreamLoader() {
public void load(InputStream is) throws IOException {
loadProvidersFromStream(is);
}
};
// load system-wide javamail.providers from the javamail.default.address.map
or
* javamail.address.map
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) {
InputStream clis = null;
try {
clis = getResourceAsStream(cl, name);
if (clis != null) {
loader.load(clis);
logger.log(Level.CONFIG, "successfully loaded resource: {0}",
name);
} else {
/*
logger.log(Level.CONFIG, "not loading resource: {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);
}
}
/*
* Following are security related methods that work on JDK 1.2 or newer.
*/
static ClassLoader getContextClassLoader() {
return AccessController.doPrivileged(
new PrivilegedAction