/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. 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.html
* or glassfish/bootstrap/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 glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [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.
*/
/*
* @(#)Session.java 1.76 07/05/04
*/
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 javax.activation.*;
import com.sun.mail.util.LineInputStream;
/**
* 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>java.home/lib/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>
* The first method allows the user to include their own version of the
* resource file by placing it in the <code>lib</code> 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. <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. JDK 1.1 has a limitation that
* the number of files of each name that will be found in the CLASSPATH is
* limited to one. However, this only affects method two, above; method
* one is loaded from a specific location (if allowed by the
* SecurityManager) and method three uses a different name to ensure that
* the default resource file is always loaded successfully. J2SE 1.2 and
* later are not limited to one file of a given name. <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. <p>
*
* <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=Sun Microsystems, Inc.;
* protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Sun Microsystems, Inc.;
* </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>
*
* @version 1.76, 07/05/04
* @author John Mani
* @author Bill Shannon
* @author Max Spivak
*/
public final class Session {
private final Properties props;
private final Authenticator authenticator;
private final Hashtable authTable = new Hashtable();
private boolean debug = false;
private PrintStream out; // debug output stream
private final Vector providers = new Vector();
private final Hashtable providersByProtocol = new Hashtable();
private final Hashtable providersByClassName = new Hashtable();
private final Properties addressMap = new Properties();
// maps type to protocol
// The default session.
private static Session defaultSession = null;
// 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;
if (debug)
pr("DEBUG: JavaMail version " + Version.version);
// get the Class associated with the Authenticator
Class cl;
if (authenticator != null)
cl = authenticator.getClass();
else
cl = this.getClass();
// load the resources
loadProviders(cl);
loadAddressMap(cl);
}
/**
* 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>
*
* In JDK 1.2, additional security Permission objects may be used to
* control access to the default session.
*
* @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)
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;
if (debug)
pr("DEBUG: setDebug: JavaMail version " + 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;
}
/**
* 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() {
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.<protocol>.class property exists
String _className = props.getProperty("mail."+protocol+".class");
if (_className != null) {
if (debug) {
pr("DEBUG: mail."+protocol+
".class property exists and points to " +
_className);
}
_provider = (Provider)providersByClassName.get(_className);
}
if (_provider != null) {
return _provider;
} else {
// returning currently default protocol in providersByProtocol
_provider = (Provider)providersByProtocol.get(protocol);
}
if (_provider == null) {
throw new NoSuchProviderException("No provider for " + protocol);
} else {
if (debug) {
pr("DEBUG: 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);
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
* @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");
}
try {
return (Store) getService(provider, url);
} catch (ClassCastException cce) {
throw new NoSuchProviderException("incorrect 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 {
return getTransport(getProperty("mail.transport.protocol"));
}
/**
* Get a Transport object that implements the specified protocol.
* If an appropriate Transport object cannot be obtained, null is
* returned.
*
* @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 to the
* specified address type.
*
* @param address
* @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 = (String)addressMap.get(address.getType());
if (transportProtocol == null) {
throw new NoSuchProviderException("No provider for Address type: "+
address.getType());
} else {
return getTransport(transportProtocol);
}
}
/**
* 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");
}
try {
return (Transport) getService(provider, url);
} catch (ClassCastException cce) {
throw new NoSuchProviderException("incorrect 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)
* @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 Object getService(Provider provider, URLName url)
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 = ccl.loadClass(provider.getClassName());
} catch (ClassNotFoundException ex) {
// ignore it
}
if (serviceClass == null)
serviceClass = cl.loadClass(provider.getClassName());
} 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());
} catch (Exception ex) {
// Nothing worked, give up.
if (debug) ex.printStackTrace(getDebugOut());
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) {
if (debug) ex.printStackTrace(getDebugOut());
throw new NoSuchProviderException(provider.getProtocol());
}
return 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.
*/
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.
*
* @return the PasswordAuthentication corresponding to the URLName
*/
public PasswordAuthentication getPasswordAuthentication(URLName url) {
return (PasswordAuthentication)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:
* <p> <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 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.
*
* @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 <java.home>/lib dir
try {
String res = System.getProperty("java.home") +
File.separator + "lib" +
File.separator + "javamail.providers";
loadFile(res, loader);
} catch (SecurityException sex) {
if (debug)
pr("DEBUG: can't get java.home: " + sex);
}
// 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);
if (providers.size() == 0) {
if (debug)
pr("DEBUG: 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",
"Sun Microsystems, Inc.", Version.version));
addProvider(new Provider(Provider.Type.STORE,
"imaps", "com.sun.mail.imap.IMAPSSLStore",
"Sun Microsystems, Inc.", Version.version));
addProvider(new Provider(Provider.Type.STORE,
"pop3", "com.sun.mail.pop3.POP3Store",
"Sun Microsystems, Inc.", Version.version));
addProvider(new Provider(Provider.Type.STORE,
"pop3s", "com.sun.mail.pop3.POP3SSLStore",
"Sun Microsystems, Inc.", Version.version));
addProvider(new Provider(Provider.Type.TRANSPORT,
"smtp", "com.sun.mail.smtp.SMTPTransport",
"Sun Microsystems, Inc.", Version.version));
addProvider(new Provider(Provider.Type.TRANSPORT,
"smtps", "com.sun.mail.smtp.SMTPSSLTransport",
"Sun Microsystems, Inc.", Version.version));
}
if (debug) {
// dump the output of the tables for debugging
pr("DEBUG: Tables of loaded providers");
pr("DEBUG: Providers Listed By Class Name: " +
providersByClassName.toString());
pr("DEBUG: Providers Listed By Protocol: " +
providersByProtocol.toString());
}
}
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;
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) {
if (debug)
pr("DEBUG: Bad provider entry: " + 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) {
providers.addElement(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() {
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);
// 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>/lib dir
try {
String res = System.getProperty("java.home") +
File.separator + "lib" +
File.separator + "javamail.address.map";
loadFile(res, loader);
} catch (SecurityException sex) {
if (debug)
pr("DEBUG: can't get java.home: " + sex);
}
if (addressMap.isEmpty()) {
if (debug)
pr("DEBUG: 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);
if (debug)
pr("DEBUG: successfully loaded file: " + name);
} catch (IOException e) {
if (debug) {
pr("DEBUG: not loading file: " + name);
pr("DEBUG: " + e);
}
} catch (SecurityException sex) {
if (debug) {
pr("DEBUG: not loading file: " + name);
pr("DEBUG: " + 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);
if (debug)
pr("DEBUG: successfully loaded resource: " + name);
} else {
if (debug)
pr("DEBUG: not loading resource: " + name);
}
} catch (IOException e) {
if (debug)
pr("DEBUG: " + e);
} catch (SecurityException sex) {
if (debug)
pr("DEBUG: " + 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;
if (debug)
pr("DEBUG: URL " + url);
try {
clis = openStream(url);
if (clis != null) {
loader.load(clis);
anyLoaded = true;
if (debug)
pr("DEBUG: successfully loaded resource: " +
url);
} else {
if (debug)
pr("DEBUG: not loading resource: " + url);
}
} catch (IOException ioex) {
if (debug)
pr("DEBUG: " + ioex);
} catch (SecurityException sex) {
if (debug)
pr("DEBUG: " + sex);
} finally {
try {
if (clis != null)
clis.close();
} catch (IOException cex) { }
}
}
}
} catch (Exception ex) {
if (debug)
pr("DEBUG: " + ex);
}
// if failed to load anything, fall back to old technique, just in case
if (!anyLoaded) {
if (debug)
pr("DEBUG: !anyLoaded");
loadResource("/" + name, cl, loader);
}
}
private void pr(String str) {
getDebugOut().println(str);
}
/*
* Following are security related methods that work on JDK 1.2 or newer.
*/
private static ClassLoader getContextClassLoader() {
return (ClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object 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 (InputStream)
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws IOException {
return c.getResourceAsStream(name);
}
});
} catch (PrivilegedActionException e) {
throw (IOException)e.getException();
}
}
private static URL[] getResources(final ClassLoader cl, final String name) {
return (URL[])
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
URL[] ret = null;
try {
Vector v = new Vector();
Enumeration e = cl.getResources(name);
while (e != null && e.hasMoreElements()) {
URL url = (URL)e.nextElement();
if (url != null)
v.addElement(url);
}
if (v.size() > 0) {
ret = new URL[v.size()];
v.copyInto(ret);
}
} catch (IOException ioex) {
} catch (SecurityException ex) { }
return ret;
}
});
}
private static URL[] getSystemResources(final String name) {
return (URL[])
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
URL[] ret = null;
try {
Vector v = new Vector();
Enumeration e = ClassLoader.getSystemResources(name);
while (e != null && e.hasMoreElements()) {
URL url = (URL)e.nextElement();
if (url != null)
v.addElement(url);
}
if (v.size() > 0) {
ret = new URL[v.size()];
v.copyInto(ret);
}
} catch (IOException ioex) {
} catch (SecurityException ex) { }
return ret;
}
});
}
private static InputStream openStream(final URL url) throws IOException {
try {
return (InputStream)
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws IOException {
return url.openStream();
}
});
} catch (PrivilegedActionException e) {
throw (IOException)e.getException();
}
}
}
/**
* Support interface to generalize
* code that loads resources from stream.
*/
interface StreamLoader {
public void load(InputStream is) throws IOException;
}