/*
* 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;
}