0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common Development
0008: * and Distribution License("CDDL") (collectively, the "License"). You
0009: * may not use this file except in compliance with the License. You can obtain
0010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
0011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
0012: * language governing permissions and limitations under the License.
0013: *
0014: * When distributing the software, include this License Header Notice in each
0015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
0016: * Sun designates this particular file as subject to the "Classpath" exception
0017: * as provided by Sun in the GPL Version 2 section of the License file that
0018: * accompanied this code. If applicable, add the following below the License
0019: * Header, with the fields enclosed by brackets [] replaced by your own
0020: * identifying information: "Portions Copyrighted [year]
0021: * [name of copyright owner]"
0022: *
0023: * Contributor(s):
0024: *
0025: * If you wish your version of this file to be governed by only the CDDL or
0026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
0027: * elects to include this software in this distribution under the [CDDL or GPL
0028: * Version 2] license." If you don't indicate a single choice of license, a
0029: * recipient has the option to distribute your version of this file under
0030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
0031: * its licensees as provided above. However, if you add GPL Version 2 code
0032: * and therefore, elected the GPL Version 2 license, then the option applies
0033: * only if the new code is made subject to such option by the copyright
0034: * holder.
0035: */
0036:
0037: /*
0038: * @(#)Session.java 1.76 07/05/04
0039: */
0040:
0041: package javax.mail;
0042:
0043: import java.lang.reflect.*;
0044: import java.io.*;
0045: import java.net.*;
0046: import java.security.*;
0047: import java.util.Enumeration;
0048: import java.util.Hashtable;
0049: import java.util.Properties;
0050: import java.util.StringTokenizer;
0051: import java.util.Vector;
0052:
0053: import javax.activation.*;
0054:
0055: import com.sun.mail.util.LineInputStream;
0056:
0057: /**
0058: * The Session class represents a mail session and is not subclassed.
0059: * It collects together properties and defaults used by the mail API's.
0060: * A single default session can be shared by multiple applications on the
0061: * desktop. Unshared sessions can also be created. <p>
0062: *
0063: * The Session class provides access to the protocol providers that
0064: * implement the <code>Store</code>, <code>Transport</code>, and related
0065: * classes. The protocol providers are configured using the following files:
0066: * <ul>
0067: * <li> <code>javamail.providers</code> and
0068: * <code>javamail.default.providers</code> </li>
0069: * <li> <code>javamail.address.map</code> and
0070: * <code>javamail.default.address.map</code> </li>
0071: * </ul>
0072: * <p>
0073: * Each <code>javamail.</code><i>X</i> resource file is searched for using
0074: * three methods in the following order:
0075: * <ol>
0076: * <li> <code>java.home/lib/javamail.</code><i>X</i> </li>
0077: * <li> <code>META-INF/javamail.</code><i>X</i> </li>
0078: * <li> <code>META-INF/javamail.default.</code><i>X</i> </li>
0079: * </ol>
0080: * <p>
0081: * The first method allows the user to include their own version of the
0082: * resource file by placing it in the <code>lib</code> directory where the
0083: * <code>java.home</code> property points. The second method allows an
0084: * application that uses the JavaMail APIs to include their own resource
0085: * files in their application's or jar file's <code>META-INF</code>
0086: * directory. The <code>javamail.default.</code><i>X</i> default files
0087: * are part of the JavaMail <code>mail.jar</code> file. <p>
0088: *
0089: * File location depends upon how the <code>ClassLoader</code> method
0090: * <code>getResource</code> is implemented. Usually, the
0091: * <code>getResource</code> method searches through CLASSPATH until it
0092: * finds the requested file and then stops. JDK 1.1 has a limitation that
0093: * the number of files of each name that will be found in the CLASSPATH is
0094: * limited to one. However, this only affects method two, above; method
0095: * one is loaded from a specific location (if allowed by the
0096: * SecurityManager) and method three uses a different name to ensure that
0097: * the default resource file is always loaded successfully. J2SE 1.2 and
0098: * later are not limited to one file of a given name. <p>
0099: *
0100: * The ordering of entries in the resource files matters. If multiple
0101: * entries exist, the first entries take precedence over the later
0102: * entries. For example, the first IMAP provider found will be set as the
0103: * default IMAP implementation until explicitly changed by the
0104: * application. The user- or system-supplied resource files augment, they
0105: * do not override, the default files included with the JavaMail APIs.
0106: * This means that all entries in all files loaded will be available. <p>
0107: *
0108: * <b><code>javamail.providers</code></b> and
0109: * <b><code>javamail.default.providers</code></b><p>
0110: *
0111: * These resource files specify the stores and transports that are
0112: * available on the system, allowing an application to "discover" what
0113: * store and transport implementations are available. The protocol
0114: * implementations are listed one per line. The file format defines four
0115: * attributes that describe a protocol implementation. Each attribute is
0116: * an "="-separated name-value pair with the name in lowercase. Each
0117: * name-value pair is semi-colon (";") separated. The following names
0118: * are defined. <p>
0119: *
0120: * <table border=1>
0121: * <caption>
0122: * Attribute Names in Providers Files
0123: * </caption>
0124: * <tr>
0125: * <th>Name</th><th>Description</th>
0126: * </tr>
0127: * <tr>
0128: * <td>protocol</td>
0129: * <td>Name assigned to protocol.
0130: * For example, <code>smtp</code> for Transport.</td>
0131: * </tr>
0132: * <tr>
0133: * <td>type</td>
0134: * <td>Valid entries are <code>store</code> and <code>transport</code>.</td>
0135: * </tr>
0136: * <tr>
0137: * <td>class</td>
0138: * <td>Class name that implements this protocol.</td>
0139: * </tr>
0140: * <tr>
0141: * <td>vendor</td>
0142: * <td>Optional string identifying the vendor.</td>
0143: * </tr>
0144: * <tr>
0145: * <td>version</td>
0146: * <td>Optional string identifying the version.</td>
0147: * </tr>
0148: * </table><p>
0149: *
0150: * Here's an example of <code>META-INF/javamail.default.providers</code>
0151: * file contents:
0152: * <pre>
0153: * protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Sun Microsystems, Inc.;
0154: * protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Sun Microsystems, Inc.;
0155: * </pre><p>
0156: *
0157: * <b><code>javamail.address.map</code></b> and
0158: * <b><code>javamail.default.address.map</code></b><p>
0159: *
0160: * These resource files map transport address types to the transport
0161: * protocol. The <code>getType</code> method of
0162: * </code>javax.mail.Address</code> returns the address type. The
0163: * <code>javamail.address.map</code> file maps the transport type to the
0164: * protocol. The file format is a series of name-value pairs. Each key
0165: * name should correspond to an address type that is currently installed
0166: * on the system; there should also be an entry for each
0167: * <code>javax.mail.Address</code> implementation that is present if it is
0168: * to be used. For example, the
0169: * <code>javax.mail.internet.InternetAddress</code> method
0170: * <code>getType</code> returns "rfc822". Each referenced protocol should
0171: * be installed on the system. For the case of <code>news</code>, below,
0172: * the client should install a Transport provider supporting the nntp
0173: * protocol. <p>
0174: *
0175: * Here are the typical contents of a <code>javamail.address.map</code> file:
0176: * <pre>
0177: * rfc822=smtp
0178: * news=nntp
0179: * </pre>
0180: *
0181: * @version 1.76, 07/05/04
0182: * @author John Mani
0183: * @author Bill Shannon
0184: * @author Max Spivak
0185: */
0186:
0187: public final class Session {
0188:
0189: private final Properties props;
0190: private final Authenticator authenticator;
0191: private final Hashtable authTable = new Hashtable();
0192: private boolean debug = false;
0193: private PrintStream out; // debug output stream
0194: private final Vector providers = new Vector();
0195: private final Hashtable providersByProtocol = new Hashtable();
0196: private final Hashtable providersByClassName = new Hashtable();
0197: private final Properties addressMap = new Properties();
0198: // maps type to protocol
0199: // The default session.
0200: private static Session defaultSession = null;
0201:
0202: // Constructor is not public
0203: private Session(Properties props, Authenticator authenticator) {
0204: this .props = props;
0205: this .authenticator = authenticator;
0206:
0207: if (Boolean.valueOf(props.getProperty("mail.debug"))
0208: .booleanValue())
0209: debug = true;
0210:
0211: if (debug)
0212: pr("DEBUG: JavaMail version " + Version.version);
0213:
0214: // get the Class associated with the Authenticator
0215: Class cl;
0216: if (authenticator != null)
0217: cl = authenticator.getClass();
0218: else
0219: cl = this .getClass();
0220: // load the resources
0221: loadProviders(cl);
0222: loadAddressMap(cl);
0223: }
0224:
0225: /**
0226: * Get a new Session object.
0227: *
0228: * @param props Properties object that hold relevant properties.<br>
0229: * It is expected that the client supplies values
0230: * for the properties listed in Appendix A of the
0231: * JavaMail spec (particularly mail.store.protocol,
0232: * mail.transport.protocol, mail.host, mail.user,
0233: * and mail.from) as the defaults are unlikely to
0234: * work in all cases.
0235: * @param authenticator Authenticator object used to call back to
0236: * the application when a user name and password is
0237: * needed.
0238: * @return a new Session object
0239: * @see javax.mail.Authenticator
0240: */
0241: public static Session getInstance(Properties props,
0242: Authenticator authenticator) {
0243: return new Session(props, authenticator);
0244: }
0245:
0246: /**
0247: * Get a new Session object.
0248: *
0249: * @param props Properties object that hold relevant properties.<br>
0250: * It is expected that the client supplies values
0251: * for the properties listed in Appendix A of the
0252: * JavaMail spec (particularly mail.store.protocol,
0253: * mail.transport.protocol, mail.host, mail.user,
0254: * and mail.from) as the defaults are unlikely to
0255: * work in all cases.
0256: * @return a new Session object
0257: * @since JavaMail 1.2
0258: */
0259: public static Session getInstance(Properties props) {
0260: return new Session(props, null);
0261: }
0262:
0263: /**
0264: * Get the default Session object. If a default has not yet been
0265: * setup, a new Session object is created and installed as the
0266: * default. <p>
0267: *
0268: * Since the default session is potentially available to all
0269: * code executing in the same Java virtual machine, and the session
0270: * can contain security sensitive information such as user names
0271: * and passwords, access to the default session is restricted.
0272: * The Authenticator object, which must be created by the caller,
0273: * is used indirectly to check access permission. The Authenticator
0274: * object passed in when the session is created is compared with
0275: * the Authenticator object passed in to subsequent requests to
0276: * get the default session. If both objects are the same, or are
0277: * from the same ClassLoader, the request is allowed. Otherwise,
0278: * it is denied. <p>
0279: *
0280: * Note that if the Authenticator object used to create the session
0281: * is null, anyone can get the default session by passing in null. <p>
0282: *
0283: * Note also that the Properties object is used only the first time
0284: * this method is called, when a new Session object is created.
0285: * Subsequent calls return the Session object that was created by the
0286: * first call, and ignore the passed Properties object. Use the
0287: * <code>getInstance</code> method to get a new Session object every
0288: * time the method is called. <p>
0289: *
0290: * In JDK 1.2, additional security Permission objects may be used to
0291: * control access to the default session.
0292: *
0293: * @param props Properties object. Used only if a new Session
0294: * object is created.<br>
0295: * It is expected that the client supplies values
0296: * for the properties listed in Appendix A of the
0297: * JavaMail spec (particularly mail.store.protocol,
0298: * mail.transport.protocol, mail.host, mail.user,
0299: * and mail.from) as the defaults are unlikely to
0300: * work in all cases.
0301: * @param authenticator Authenticator object. Used only if a
0302: * new Session object is created. Otherwise,
0303: * it must match the Authenticator used to create
0304: * the Session.
0305: * @return the default Session object
0306: */
0307: public static synchronized Session getDefaultInstance(
0308: Properties props, Authenticator authenticator) {
0309: if (defaultSession == null)
0310: defaultSession = new Session(props, authenticator);
0311: else {
0312: // have to check whether caller is allowed to see default session
0313: if (defaultSession.authenticator == authenticator)
0314: ; // either same object or both null, either way OK
0315: else if (defaultSession.authenticator != null
0316: && authenticator != null
0317: && defaultSession.authenticator.getClass()
0318: .getClassLoader() == authenticator
0319: .getClass().getClassLoader())
0320: ; // both objects came from the same class loader, OK
0321: else
0322: // anything else is not allowed
0323: throw new SecurityException(
0324: "Access to default session denied");
0325: }
0326:
0327: return defaultSession;
0328: }
0329:
0330: /**
0331: * Get the default Session object. If a default has not yet been
0332: * setup, a new Session object is created and installed as the
0333: * default. <p>
0334: *
0335: * Note that a default session created with no Authenticator is
0336: * available to all code executing in the same Java virtual
0337: * machine, and the session can contain security sensitive
0338: * information such as user names and passwords.
0339: *
0340: * @param props Properties object. Used only if a new Session
0341: * object is created.<br>
0342: * It is expected that the client supplies values
0343: * for the properties listed in Appendix A of the
0344: * JavaMail spec (particularly mail.store.protocol,
0345: * mail.transport.protocol, mail.host, mail.user,
0346: * and mail.from) as the defaults are unlikely to
0347: * work in all cases.
0348: * @return the default Session object
0349: * @since JavaMail 1.2
0350: */
0351: public static Session getDefaultInstance(Properties props) {
0352: return getDefaultInstance(props, null);
0353: }
0354:
0355: /**
0356: * Set the debug setting for this Session.
0357: * <p>
0358: * Since the debug setting can be turned on only after the Session
0359: * has been created, to turn on debugging in the Session
0360: * constructor, set the property <code>mail.debug</code> in the
0361: * Properties object passed in to the constructor to true. The
0362: * value of the <code>mail.debug</code> property is used to
0363: * initialize the per-Session debugging flag. Subsequent calls to
0364: * the <code>setDebug</code> method manipulate the per-Session
0365: * debugging flag and have no affect on the <code>mail.debug</code>
0366: * property.
0367: *
0368: * @param debug Debug setting
0369: */
0370: public synchronized void setDebug(boolean debug) {
0371: this .debug = debug;
0372: if (debug)
0373: pr("DEBUG: setDebug: JavaMail version " + Version.version);
0374: }
0375:
0376: /**
0377: * Get the debug setting for this Session.
0378: *
0379: * @return current debug setting
0380: */
0381: public synchronized boolean getDebug() {
0382: return debug;
0383: }
0384:
0385: /**
0386: * Set the stream to be used for debugging output for this session.
0387: * If <code>out</code> is null, <code>System.out</code> will be used.
0388: * Note that debugging output that occurs before any session is created,
0389: * as a result of setting the <code>mail.debug</code> system property,
0390: * will always be sent to <code>System.out</code>.
0391: *
0392: * @param out the PrintStream to use for debugging output
0393: * @since JavaMail 1.3
0394: */
0395: public synchronized void setDebugOut(PrintStream out) {
0396: this .out = out;
0397: }
0398:
0399: /**
0400: * Returns the stream to be used for debugging output. If no stream
0401: * has been set, <code>System.out</code> is returned.
0402: *
0403: * @return the PrintStream to use for debugging output
0404: * @since JavaMail 1.3
0405: */
0406: public synchronized PrintStream getDebugOut() {
0407: if (out == null)
0408: return System.out;
0409: else
0410: return out;
0411: }
0412:
0413: /**
0414: * This method returns an array of all the implementations installed
0415: * via the javamail.[default.]providers files that can
0416: * be loaded using the ClassLoader available to this application.
0417: *
0418: * @return Array of configured providers
0419: */
0420: public synchronized Provider[] getProviders() {
0421: Provider[] _providers = new Provider[providers.size()];
0422: providers.copyInto(_providers);
0423: return _providers;
0424: }
0425:
0426: /**
0427: * Returns the default Provider for the protocol
0428: * specified. Checks mail.<protocol>.class property
0429: * first and if it exists, returns the Provider
0430: * associated with this implementation. If it doesn't exist,
0431: * returns the Provider that appeared first in the
0432: * configuration files. If an implementation for the protocol
0433: * isn't found, throws NoSuchProviderException
0434: *
0435: * @param protocol Configured protocol (i.e. smtp, imap, etc)
0436: * @return Currently configured Provider for the specified protocol
0437: * @exception NoSuchProviderException If a provider for the given
0438: * protocol is not found.
0439: */
0440: public synchronized Provider getProvider(String protocol)
0441: throws NoSuchProviderException {
0442:
0443: if (protocol == null || protocol.length() <= 0) {
0444: throw new NoSuchProviderException("Invalid protocol: null");
0445: }
0446:
0447: Provider _provider = null;
0448:
0449: // check if the mail.<protocol>.class property exists
0450: String _className = props.getProperty("mail." + protocol
0451: + ".class");
0452: if (_className != null) {
0453: if (debug) {
0454: pr("DEBUG: mail." + protocol
0455: + ".class property exists and points to "
0456: + _className);
0457: }
0458: _provider = (Provider) providersByClassName.get(_className);
0459: }
0460:
0461: if (_provider != null) {
0462: return _provider;
0463: } else {
0464: // returning currently default protocol in providersByProtocol
0465: _provider = (Provider) providersByProtocol.get(protocol);
0466: }
0467:
0468: if (_provider == null) {
0469: throw new NoSuchProviderException("No provider for "
0470: + protocol);
0471: } else {
0472: if (debug) {
0473: pr("DEBUG: getProvider() returning "
0474: + _provider.toString());
0475: }
0476: return _provider;
0477: }
0478: }
0479:
0480: /**
0481: * Set the passed Provider to be the default implementation
0482: * for the protocol in Provider.protocol overriding any previous values.
0483: *
0484: * @param provider Currently configured Provider which will be
0485: * set as the default for the protocol
0486: * @exception NoSuchProviderException If the provider passed in
0487: * is invalid.
0488: */
0489: public synchronized void setProvider(Provider provider)
0490: throws NoSuchProviderException {
0491: if (provider == null) {
0492: throw new NoSuchProviderException("Can't set null provider");
0493: }
0494: providersByProtocol.put(provider.getProtocol(), provider);
0495: props.put("mail." + provider.getProtocol() + ".class", provider
0496: .getClassName());
0497: }
0498:
0499: /**
0500: * Get a Store object that implements this user's desired Store
0501: * protocol. The <code>mail.store.protocol</code> property specifies the
0502: * desired protocol. If an appropriate Store object is not obtained,
0503: * NoSuchProviderException is thrown
0504: *
0505: * @return a Store object
0506: * @exception NoSuchProviderException If a provider for the given
0507: * protocol is not found.
0508: */
0509: public Store getStore() throws NoSuchProviderException {
0510: return getStore(getProperty("mail.store.protocol"));
0511: }
0512:
0513: /**
0514: * Get a Store object that implements the specified protocol. If an
0515: * appropriate Store object cannot be obtained,
0516: * NoSuchProviderException is thrown.
0517: *
0518: * @param protocol
0519: * @return a Store object
0520: * @exception NoSuchProviderException If a provider for the given
0521: * protocol is not found.
0522: */
0523: public Store getStore(String protocol)
0524: throws NoSuchProviderException {
0525: return getStore(new URLName(protocol, null, -1, null, null,
0526: null));
0527: }
0528:
0529: /**
0530: * Get a Store object for the given URLName. If the requested Store
0531: * object cannot be obtained, NoSuchProviderException is thrown.
0532: *
0533: * The "scheme" part of the URL string (Refer RFC 1738) is used
0534: * to locate the Store protocol. <p>
0535: *
0536: * @param url URLName that represents the desired Store
0537: * @return a closed Store object
0538: * @see #getFolder(URLName)
0539: * @see javax.mail.URLName
0540: * @exception NoSuchProviderException If a provider for the given
0541: * URLName is not found.
0542: */
0543: public Store getStore(URLName url) throws NoSuchProviderException {
0544: String protocol = url.getProtocol();
0545: Provider p = getProvider(protocol);
0546: return getStore(p, url);
0547: }
0548:
0549: /**
0550: * Get an instance of the store specified by Provider. Instantiates
0551: * the store and returns it.
0552: *
0553: * @param provider Store Provider that will be instantiated
0554: * @return Instantiated Store
0555: * @exception NoSuchProviderException If a provider for the given
0556: * Provider is not found.
0557: */
0558: public Store getStore(Provider provider)
0559: throws NoSuchProviderException {
0560: return getStore(provider, null);
0561: }
0562:
0563: /**
0564: * Get an instance of the store specified by Provider. If the URLName
0565: * is not null, uses it, otherwise creates a new one. Instantiates
0566: * the store and returns it. This is a private method used by
0567: * getStore(Provider) and getStore(URLName)
0568: *
0569: * @param provider Store Provider that will be instantiated
0570: * @param url URLName used to instantiate the Store
0571: * @return Instantiated Store
0572: * @exception NoSuchProviderException If a provider for the given
0573: * Provider/URLName is not found.
0574: */
0575: private Store getStore(Provider provider, URLName url)
0576: throws NoSuchProviderException {
0577:
0578: // make sure we have the correct type of provider
0579: if (provider == null
0580: || provider.getType() != Provider.Type.STORE) {
0581: throw new NoSuchProviderException("invalid provider");
0582: }
0583:
0584: try {
0585: return (Store) getService(provider, url);
0586: } catch (ClassCastException cce) {
0587: throw new NoSuchProviderException("incorrect class");
0588: }
0589: }
0590:
0591: /**
0592: * Get a closed Folder object for the given URLName. If the requested
0593: * Folder object cannot be obtained, null is returned. <p>
0594: *
0595: * The "scheme" part of the URL string (Refer RFC 1738) is used
0596: * to locate the Store protocol. The rest of the URL string (that is,
0597: * the "schemepart", as per RFC 1738) is used by that Store
0598: * in a protocol dependent manner to locate and instantiate the
0599: * appropriate Folder object. <p>
0600: *
0601: * Note that RFC 1738 also specifies the syntax for the
0602: * "schemepart" for IP-based protocols (IMAP4, POP3, etc.).
0603: * Providers of IP-based mail Stores should implement that
0604: * syntax for referring to Folders. <p>
0605: *
0606: * @param url URLName that represents the desired folder
0607: * @return Folder
0608: * @see #getStore(URLName)
0609: * @see javax.mail.URLName
0610: * @exception NoSuchProviderException If a provider for the given
0611: * URLName is not found.
0612: * @exception MessagingException if the Folder could not be
0613: * located or created.
0614: */
0615: public Folder getFolder(URLName url) throws MessagingException {
0616: // First get the Store
0617: Store store = getStore(url);
0618: store.connect();
0619: return store.getFolder(url);
0620: }
0621:
0622: /**
0623: * Get a Transport object that implements this user's desired
0624: * Transport protcol. The <code>mail.transport.protocol</code> property
0625: * specifies the desired protocol. If an appropriate Transport
0626: * object cannot be obtained, MessagingException is thrown.
0627: *
0628: * @return a Transport object
0629: * @exception NoSuchProviderException If the provider is not found.
0630: */
0631: public Transport getTransport() throws NoSuchProviderException {
0632: return getTransport(getProperty("mail.transport.protocol"));
0633: }
0634:
0635: /**
0636: * Get a Transport object that implements the specified protocol.
0637: * If an appropriate Transport object cannot be obtained, null is
0638: * returned.
0639: *
0640: * @return a Transport object
0641: * @exception NoSuchProviderException If provider for the given
0642: * protocol is not found.
0643: */
0644: public Transport getTransport(String protocol)
0645: throws NoSuchProviderException {
0646: return getTransport(new URLName(protocol, null, -1, null, null,
0647: null));
0648: }
0649:
0650: /**
0651: * Get a Transport object for the given URLName. If the requested
0652: * Transport object cannot be obtained, NoSuchProviderException is thrown.
0653: *
0654: * The "scheme" part of the URL string (Refer RFC 1738) is used
0655: * to locate the Transport protocol. <p>
0656: *
0657: * @param url URLName that represents the desired Transport
0658: * @return a closed Transport object
0659: * @see javax.mail.URLName
0660: * @exception NoSuchProviderException If a provider for the given
0661: * URLName is not found.
0662: */
0663: public Transport getTransport(URLName url)
0664: throws NoSuchProviderException {
0665: String protocol = url.getProtocol();
0666: Provider p = getProvider(protocol);
0667: return getTransport(p, url);
0668: }
0669:
0670: /**
0671: * Get an instance of the transport specified in the Provider. Instantiates
0672: * the transport and returns it.
0673: *
0674: * @param provider Transport Provider that will be instantiated
0675: * @return Instantiated Transport
0676: * @exception NoSuchProviderException If provider for the given
0677: * provider is not found.
0678: */
0679: public Transport getTransport(Provider provider)
0680: throws NoSuchProviderException {
0681: return getTransport(provider, null);
0682: }
0683:
0684: /**
0685: * Get a Transport object that can transport a Message to the
0686: * specified address type.
0687: *
0688: * @param address
0689: * @return A Transport object
0690: * @see javax.mail.Address
0691: * @exception NoSuchProviderException If provider for the
0692: * Address type is not found
0693: */
0694: public Transport getTransport(Address address)
0695: throws NoSuchProviderException {
0696:
0697: String transportProtocol = (String) addressMap.get(address
0698: .getType());
0699: if (transportProtocol == null) {
0700: throw new NoSuchProviderException(
0701: "No provider for Address type: "
0702: + address.getType());
0703: } else {
0704: return getTransport(transportProtocol);
0705: }
0706: }
0707:
0708: /**
0709: * Get a Transport object using the given provider and urlname.
0710: *
0711: * @param provider the provider to use
0712: * @param url urlname to use (can be null)
0713: * @return A Transport object
0714: * @exception NoSuchProviderException If no provider or the provider
0715: * was the wrong class.
0716: */
0717:
0718: private Transport getTransport(Provider provider, URLName url)
0719: throws NoSuchProviderException {
0720: // make sure we have the correct type of provider
0721: if (provider == null
0722: || provider.getType() != Provider.Type.TRANSPORT) {
0723: throw new NoSuchProviderException("invalid provider");
0724: }
0725:
0726: try {
0727: return (Transport) getService(provider, url);
0728: } catch (ClassCastException cce) {
0729: throw new NoSuchProviderException("incorrect class");
0730: }
0731: }
0732:
0733: /**
0734: * Get a Service object. Needs a provider object, but will
0735: * create a URLName if needed. It attempts to instantiate
0736: * the correct class.
0737: *
0738: * @param provider which provider to use
0739: * @param url which URLName to use (can be null)
0740: * @exception NoSuchProviderException thrown when the class cannot be
0741: * found or when it does not have the correct constructor
0742: * (Session, URLName), or if it is not derived from
0743: * Service.
0744: */
0745: private Object getService(Provider provider, URLName url)
0746: throws NoSuchProviderException {
0747: // need a provider and url
0748: if (provider == null) {
0749: throw new NoSuchProviderException("null");
0750: }
0751:
0752: // create a url if needed
0753: if (url == null) {
0754: url = new URLName(provider.getProtocol(), null, -1, null,
0755: null, null);
0756: }
0757:
0758: Object service = null;
0759:
0760: // get the ClassLoader associated with the Authenticator
0761: ClassLoader cl;
0762: if (authenticator != null)
0763: cl = authenticator.getClass().getClassLoader();
0764: else
0765: cl = this .getClass().getClassLoader();
0766:
0767: // now load the class
0768: Class serviceClass = null;
0769: try {
0770: // First try the "application's" class loader.
0771: ClassLoader ccl = getContextClassLoader();
0772: if (ccl != null)
0773: try {
0774: serviceClass = ccl.loadClass(provider
0775: .getClassName());
0776: } catch (ClassNotFoundException ex) {
0777: // ignore it
0778: }
0779: if (serviceClass == null)
0780: serviceClass = cl.loadClass(provider.getClassName());
0781: } catch (Exception ex1) {
0782: // That didn't work, now try the "system" class loader.
0783: // (Need both of these because JDK 1.1 class loaders
0784: // may not delegate to their parent class loader.)
0785: try {
0786: serviceClass = Class.forName(provider.getClassName());
0787: } catch (Exception ex) {
0788: // Nothing worked, give up.
0789: if (debug)
0790: ex.printStackTrace(getDebugOut());
0791: throw new NoSuchProviderException(provider
0792: .getProtocol());
0793: }
0794: }
0795:
0796: // construct an instance of the class
0797: try {
0798: Class[] c = { javax.mail.Session.class,
0799: javax.mail.URLName.class };
0800: Constructor cons = serviceClass.getConstructor(c);
0801:
0802: Object[] o = { this , url };
0803: service = cons.newInstance(o);
0804:
0805: } catch (Exception ex) {
0806: if (debug)
0807: ex.printStackTrace(getDebugOut());
0808: throw new NoSuchProviderException(provider.getProtocol());
0809: }
0810:
0811: return service;
0812: }
0813:
0814: /**
0815: * Save a PasswordAuthentication for this (store or transport) URLName.
0816: * If pw is null the entry corresponding to the URLName is removed.
0817: * <p>
0818: * This is normally used only by the store or transport implementations
0819: * to allow authentication information to be shared among multiple
0820: * uses of a session.
0821: */
0822: public void setPasswordAuthentication(URLName url,
0823: PasswordAuthentication pw) {
0824: if (pw == null)
0825: authTable.remove(url);
0826: else
0827: authTable.put(url, pw);
0828: }
0829:
0830: /**
0831: * Return any saved PasswordAuthentication for this (store or transport)
0832: * URLName. Normally used only by store or transport implementations.
0833: *
0834: * @return the PasswordAuthentication corresponding to the URLName
0835: */
0836: public PasswordAuthentication getPasswordAuthentication(URLName url) {
0837: return (PasswordAuthentication) authTable.get(url);
0838: }
0839:
0840: /**
0841: * Call back to the application to get the needed user name and password.
0842: * The application should put up a dialog something like:
0843: * <p> <pre>
0844: * Connecting to <protocol> mail service on host <addr>, port <port>.
0845: * <prompt>
0846: *
0847: * User Name: <defaultUserName>
0848: * Password:
0849: * </pre>
0850: *
0851: * @param addr InetAddress of the host. may be null.
0852: * @param protocol protocol scheme (e.g. imap, pop3, etc.)
0853: * @param prompt any additional String to show as part of
0854: * the prompt; may be null.
0855: * @param defaultUserName the default username. may be null.
0856: * @return the authentication which was collected by the authenticator;
0857: * may be null.
0858: */
0859: public PasswordAuthentication requestPasswordAuthentication(
0860: InetAddress addr, int port, String protocol, String prompt,
0861: String defaultUserName) {
0862:
0863: if (authenticator != null) {
0864: return authenticator.requestPasswordAuthentication(addr,
0865: port, protocol, prompt, defaultUserName);
0866: } else {
0867: return null;
0868: }
0869: }
0870:
0871: /**
0872: * Returns the Properties object associated with this Session
0873: *
0874: * @return Properties object
0875: */
0876: public Properties getProperties() {
0877: return props;
0878: }
0879:
0880: /**
0881: * Returns the value of the specified property. Returns null
0882: * if this property does not exist.
0883: *
0884: * @return String that is the property value
0885: */
0886: public String getProperty(String name) {
0887: return props.getProperty(name);
0888: }
0889:
0890: /**
0891: * Load the protocol providers config files.
0892: */
0893: private void loadProviders(Class cl) {
0894: StreamLoader loader = new StreamLoader() {
0895: public void load(InputStream is) throws IOException {
0896: loadProvidersFromStream(is);
0897: }
0898: };
0899:
0900: // load system-wide javamail.providers from the <java.home>/lib dir
0901: try {
0902: String res = System.getProperty("java.home")
0903: + File.separator + "lib" + File.separator
0904: + "javamail.providers";
0905: loadFile(res, loader);
0906: } catch (SecurityException sex) {
0907: if (debug)
0908: pr("DEBUG: can't get java.home: " + sex);
0909: }
0910:
0911: // load the META-INF/javamail.providers file supplied by an application
0912: loadAllResources("META-INF/javamail.providers", cl, loader);
0913:
0914: // load default META-INF/javamail.default.providers from mail.jar file
0915: loadResource("/META-INF/javamail.default.providers", cl, loader);
0916:
0917: if (providers.size() == 0) {
0918: if (debug)
0919: pr("DEBUG: failed to load any providers, using defaults");
0920: // failed to load any providers, initialize with our defaults
0921: addProvider(new Provider(Provider.Type.STORE, "imap",
0922: "com.sun.mail.imap.IMAPStore",
0923: "Sun Microsystems, Inc.", Version.version));
0924: addProvider(new Provider(Provider.Type.STORE, "imaps",
0925: "com.sun.mail.imap.IMAPSSLStore",
0926: "Sun Microsystems, Inc.", Version.version));
0927: addProvider(new Provider(Provider.Type.STORE, "pop3",
0928: "com.sun.mail.pop3.POP3Store",
0929: "Sun Microsystems, Inc.", Version.version));
0930: addProvider(new Provider(Provider.Type.STORE, "pop3s",
0931: "com.sun.mail.pop3.POP3SSLStore",
0932: "Sun Microsystems, Inc.", Version.version));
0933: addProvider(new Provider(Provider.Type.TRANSPORT, "smtp",
0934: "com.sun.mail.smtp.SMTPTransport",
0935: "Sun Microsystems, Inc.", Version.version));
0936: addProvider(new Provider(Provider.Type.TRANSPORT, "smtps",
0937: "com.sun.mail.smtp.SMTPSSLTransport",
0938: "Sun Microsystems, Inc.", Version.version));
0939: }
0940:
0941: if (debug) {
0942: // dump the output of the tables for debugging
0943: pr("DEBUG: Tables of loaded providers");
0944: pr("DEBUG: Providers Listed By Class Name: "
0945: + providersByClassName.toString());
0946: pr("DEBUG: Providers Listed By Protocol: "
0947: + providersByProtocol.toString());
0948: }
0949: }
0950:
0951: private void loadProvidersFromStream(InputStream is)
0952: throws IOException {
0953: if (is != null) {
0954: LineInputStream lis = new LineInputStream(is);
0955: String currLine;
0956:
0957: // load and process one line at a time using LineInputStream
0958: while ((currLine = lis.readLine()) != null) {
0959:
0960: if (currLine.startsWith("#"))
0961: continue;
0962: Provider.Type type = null;
0963: String protocol = null, className = null;
0964: String vendor = null, version = null;
0965:
0966: // separate line into key-value tuples
0967: StringTokenizer tuples = new StringTokenizer(currLine,
0968: ";");
0969: while (tuples.hasMoreTokens()) {
0970: String currTuple = tuples.nextToken().trim();
0971:
0972: // set the value of each attribute based on its key
0973: int sep = currTuple.indexOf("=");
0974: if (currTuple.startsWith("protocol=")) {
0975: protocol = currTuple.substring(sep + 1);
0976: } else if (currTuple.startsWith("type=")) {
0977: String strType = currTuple.substring(sep + 1);
0978: if (strType.equalsIgnoreCase("store")) {
0979: type = Provider.Type.STORE;
0980: } else if (strType
0981: .equalsIgnoreCase("transport")) {
0982: type = Provider.Type.TRANSPORT;
0983: }
0984: } else if (currTuple.startsWith("class=")) {
0985: className = currTuple.substring(sep + 1);
0986: } else if (currTuple.startsWith("vendor=")) {
0987: vendor = currTuple.substring(sep + 1);
0988: } else if (currTuple.startsWith("version=")) {
0989: version = currTuple.substring(sep + 1);
0990: }
0991: }
0992:
0993: // check if a valid Provider; else, continue
0994: if (type == null || protocol == null
0995: || className == null || protocol.length() <= 0
0996: || className.length() <= 0) {
0997:
0998: if (debug)
0999: pr("DEBUG: Bad provider entry: " + currLine);
1000: continue;
1001: }
1002: Provider provider = new Provider(type, protocol,
1003: className, vendor, version);
1004:
1005: // add the newly-created Provider to the lookup tables
1006: addProvider(provider);
1007: }
1008: }
1009: }
1010:
1011: /**
1012: * Add a provider to the session.
1013: *
1014: * @param provider the provider to add
1015: * @since JavaMail 1.4
1016: */
1017: public synchronized void addProvider(Provider provider) {
1018: providers.addElement(provider);
1019: providersByClassName.put(provider.getClassName(), provider);
1020: if (!providersByProtocol.containsKey(provider.getProtocol()))
1021: providersByProtocol.put(provider.getProtocol(), provider);
1022: }
1023:
1024: // load maps in reverse order of preference so that the preferred
1025: // map is loaded last since its entries will override the previous ones
1026: private void loadAddressMap(Class cl) {
1027: StreamLoader loader = new StreamLoader() {
1028: public void load(InputStream is) throws IOException {
1029: addressMap.load(is);
1030: }
1031: };
1032:
1033: // load default META-INF/javamail.default.address.map from mail.jar
1034: loadResource("/META-INF/javamail.default.address.map", cl,
1035: loader);
1036:
1037: // load the META-INF/javamail.address.map file supplied by an app
1038: loadAllResources("META-INF/javamail.address.map", cl, loader);
1039:
1040: // load system-wide javamail.address.map from the <java.home>/lib dir
1041: try {
1042: String res = System.getProperty("java.home")
1043: + File.separator + "lib" + File.separator
1044: + "javamail.address.map";
1045: loadFile(res, loader);
1046: } catch (SecurityException sex) {
1047: if (debug)
1048: pr("DEBUG: can't get java.home: " + sex);
1049: }
1050:
1051: if (addressMap.isEmpty()) {
1052: if (debug)
1053: pr("DEBUG: failed to load address map, using defaults");
1054: addressMap.put("rfc822", "smtp");
1055: }
1056: }
1057:
1058: /**
1059: * Set the default transport protocol to use for addresses of
1060: * the specified type. Normally the default is set by the
1061: * <code>javamail.default.address.map</code> or
1062: * <code>javamail.address.map</code> files or resources.
1063: *
1064: * @param addresstype type of address
1065: * @param protocol name of protocol
1066: * @see #getTransport(Address)
1067: * @since JavaMail 1.4
1068: */
1069: public synchronized void setProtocolForAddress(String addresstype,
1070: String protocol) {
1071: if (protocol == null)
1072: addressMap.remove(addresstype);
1073: else
1074: addressMap.put(addresstype, protocol);
1075: }
1076:
1077: /**
1078: * Load from the named file.
1079: */
1080: private void loadFile(String name, StreamLoader loader) {
1081: InputStream clis = null;
1082: try {
1083: clis = new BufferedInputStream(new FileInputStream(name));
1084: loader.load(clis);
1085: if (debug)
1086: pr("DEBUG: successfully loaded file: " + name);
1087: } catch (IOException e) {
1088: if (debug) {
1089: pr("DEBUG: not loading file: " + name);
1090: pr("DEBUG: " + e);
1091: }
1092: } catch (SecurityException sex) {
1093: if (debug) {
1094: pr("DEBUG: not loading file: " + name);
1095: pr("DEBUG: " + sex);
1096: }
1097: } finally {
1098: try {
1099: if (clis != null)
1100: clis.close();
1101: } catch (IOException ex) {
1102: } // ignore it
1103: }
1104: }
1105:
1106: /**
1107: * Load from the named resource.
1108: */
1109: private void loadResource(String name, Class cl, StreamLoader loader) {
1110: InputStream clis = null;
1111: try {
1112: clis = getResourceAsStream(cl, name);
1113: if (clis != null) {
1114: loader.load(clis);
1115: if (debug)
1116: pr("DEBUG: successfully loaded resource: " + name);
1117: } else {
1118: if (debug)
1119: pr("DEBUG: not loading resource: " + name);
1120: }
1121: } catch (IOException e) {
1122: if (debug)
1123: pr("DEBUG: " + e);
1124: } catch (SecurityException sex) {
1125: if (debug)
1126: pr("DEBUG: " + sex);
1127: } finally {
1128: try {
1129: if (clis != null)
1130: clis.close();
1131: } catch (IOException ex) {
1132: } // ignore it
1133: }
1134: }
1135:
1136: /**
1137: * Load all of the named resource.
1138: */
1139: private void loadAllResources(String name, Class cl,
1140: StreamLoader loader) {
1141: boolean anyLoaded = false;
1142: try {
1143: URL[] urls;
1144: ClassLoader cld = null;
1145: // First try the "application's" class loader.
1146: cld = getContextClassLoader();
1147: if (cld == null)
1148: cld = cl.getClassLoader();
1149: if (cld != null)
1150: urls = getResources(cld, name);
1151: else
1152: urls = getSystemResources(name);
1153: if (urls != null) {
1154: for (int i = 0; i < urls.length; i++) {
1155: URL url = urls[i];
1156: InputStream clis = null;
1157: if (debug)
1158: pr("DEBUG: URL " + url);
1159: try {
1160: clis = openStream(url);
1161: if (clis != null) {
1162: loader.load(clis);
1163: anyLoaded = true;
1164: if (debug)
1165: pr("DEBUG: successfully loaded resource: "
1166: + url);
1167: } else {
1168: if (debug)
1169: pr("DEBUG: not loading resource: "
1170: + url);
1171: }
1172: } catch (IOException ioex) {
1173: if (debug)
1174: pr("DEBUG: " + ioex);
1175: } catch (SecurityException sex) {
1176: if (debug)
1177: pr("DEBUG: " + sex);
1178: } finally {
1179: try {
1180: if (clis != null)
1181: clis.close();
1182: } catch (IOException cex) {
1183: }
1184: }
1185: }
1186: }
1187: } catch (Exception ex) {
1188: if (debug)
1189: pr("DEBUG: " + ex);
1190: }
1191:
1192: // if failed to load anything, fall back to old technique, just in case
1193: if (!anyLoaded) {
1194: if (debug)
1195: pr("DEBUG: !anyLoaded");
1196: loadResource("/" + name, cl, loader);
1197: }
1198: }
1199:
1200: private void pr(String str) {
1201: getDebugOut().println(str);
1202: }
1203:
1204: /*
1205: * Following are security related methods that work on JDK 1.2 or newer.
1206: */
1207:
1208: private static ClassLoader getContextClassLoader() {
1209: return (ClassLoader) AccessController
1210: .doPrivileged(new PrivilegedAction() {
1211: public Object run() {
1212: ClassLoader cl = null;
1213: try {
1214: cl = Thread.currentThread()
1215: .getContextClassLoader();
1216: } catch (SecurityException ex) {
1217: }
1218: return cl;
1219: }
1220: });
1221: }
1222:
1223: private static InputStream getResourceAsStream(final Class c,
1224: final String name) throws IOException {
1225: try {
1226: return (InputStream) AccessController
1227: .doPrivileged(new PrivilegedExceptionAction() {
1228: public Object run() throws IOException {
1229: return c.getResourceAsStream(name);
1230: }
1231: });
1232: } catch (PrivilegedActionException e) {
1233: throw (IOException) e.getException();
1234: }
1235: }
1236:
1237: private static URL[] getResources(final ClassLoader cl,
1238: final String name) {
1239: return (URL[]) AccessController
1240: .doPrivileged(new PrivilegedAction() {
1241: public Object run() {
1242: URL[] ret = null;
1243: try {
1244: Vector v = new Vector();
1245: Enumeration e = cl.getResources(name);
1246: while (e != null && e.hasMoreElements()) {
1247: URL url = (URL) e.nextElement();
1248: if (url != null)
1249: v.addElement(url);
1250: }
1251: if (v.size() > 0) {
1252: ret = new URL[v.size()];
1253: v.copyInto(ret);
1254: }
1255: } catch (IOException ioex) {
1256: } catch (SecurityException ex) {
1257: }
1258: return ret;
1259: }
1260: });
1261: }
1262:
1263: private static URL[] getSystemResources(final String name) {
1264: return (URL[]) AccessController
1265: .doPrivileged(new PrivilegedAction() {
1266: public Object run() {
1267: URL[] ret = null;
1268: try {
1269: Vector v = new Vector();
1270: Enumeration e = ClassLoader
1271: .getSystemResources(name);
1272: while (e != null && e.hasMoreElements()) {
1273: URL url = (URL) e.nextElement();
1274: if (url != null)
1275: v.addElement(url);
1276: }
1277: if (v.size() > 0) {
1278: ret = new URL[v.size()];
1279: v.copyInto(ret);
1280: }
1281: } catch (IOException ioex) {
1282: } catch (SecurityException ex) {
1283: }
1284: return ret;
1285: }
1286: });
1287: }
1288:
1289: private static InputStream openStream(final URL url)
1290: throws IOException {
1291: try {
1292: return (InputStream) AccessController
1293: .doPrivileged(new PrivilegedExceptionAction() {
1294: public Object run() throws IOException {
1295: return url.openStream();
1296: }
1297: });
1298: } catch (PrivilegedActionException e) {
1299: throw (IOException) e.getException();
1300: }
1301: }
1302: }
1303:
1304: /**
1305: * Support interface to generalize
1306: * code that loads resources from stream.
1307: */
1308: interface StreamLoader {
1309: public void load(InputStream is) throws IOException;
1310: }
|