/*
* @(#)Session.java 1.43 99/12/16
*
* Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the proprietary information of Sun Microsystems, Inc.
* Use is subject to license terms.
*
*/
package javax.mail;
import java.lang.*;
import java.lang.reflect.*;
import java.io.*;
import java.net.*;
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.
*
* @version 1.43, 99/12/16
* @author John Mani
* @author Bill Shannon
* @author Max Spivak
*/
public final class Session {
private Properties props;
private Authenticator authenticator;
private Hashtable authTable = new Hashtable();
private boolean debug = false;
private Vector providers = new Vector();
private Hashtable providersByProtocol = new Hashtable();
private Hashtable providersByClassName = new Hashtable();
private Properties addressMap = new Properties(); // maps type to protocol
private static Method getResources = null;
private static Method getSystemResources = null;
static {
try {
Class c = java.lang.ClassLoader.class;
// assume both succeed or both fail
getResources = c.getMethod("getResources",
new Class[] { String.class });
getSystemResources = c.getMethod("getSystemResources",
new Class[] { String.class });
} catch (Throwable t) { } // ignore any errors
}
// 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;
// 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.
* 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 the default Session object. If a default has not yet been
* setup, a new Session object is created and installed as the
* default.
* * 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.
*
* 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.
* 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 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;
}
/**
* Set the debug setting for this Session.
*
* Since debug can be turned on only after the Session
* has been created, to turn on debugging in the Session
* constructor, set the property mail.debug
* to true. Subsequent calls to get/setDebug() have no
* affect on and override the mail.debug
* property.
* @param debug Debug setting
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
/**
* Get the debug setting for this Session.
*
* Since debug 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");
}
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.
*
* The "scheme" part of the URL string (Refer RFC 1738) is used
* to locate the Store protocol. The rest of the URL string (ie
* 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). So
* 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 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 {
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.
*
* @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.
// This should eventually be replaced by
// Thread.currentThread().getContextClassLoader().
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();
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();
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.
*
* 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:
* mail.debug
* to true. Subsequent calls to get/setDebug() have no
* affect on and override the mail.debug
* property.
* @return current debug setting
*/
public boolean getDebug() {
return debug;
}
/**
* 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 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 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.
* 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 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);
}
private void loadProviders(Class cl) {
// load system-wide javamail.providers from the