1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
5  *
6  * The contents of this file are subject to the terms of either the GNU
7  * General Public License Version 2 only ("GPL") or the Common Development
8  * and Distribution License("CDDL") (collectively, the "License").  You
9  * may not use this file except in compliance with the License.  You can
10  * obtain a copy of the License at
11  * https://oss.oracle.com/licenses/CDDL+GPL-1.1
12  * or LICENSE.txt.  See the License for the specific
13  * language governing permissions and limitations under the License.
14  *
15  * When distributing the software, include this License Header Notice in each
16  * file and include the License file at LICENSE.txt.
17  *
18  * GPL Classpath Exception:
19  * Oracle designates this particular file as subject to the "Classpath"
20  * exception as provided by Oracle in the GPL Version 2 section of the License
21  * file that accompanied this code.
22  *
23  * Modifications:
24  * If applicable, add the following below the License Header, with the fields
25  * enclosed by brackets [] replaced by your own identifying information:
26  * "Portions Copyright [year] [name of copyright owner]"
27  *
28  * Contributor(s):
29  * If you wish your version of this file to be governed by only the CDDL or
30  * only the GPL Version 2, indicate your decision by adding "[Contributor]
31  * elects to include this software in this distribution under the [CDDL or GPL
32  * Version 2] license."  If you don't indicate a single choice of license, a
33  * recipient has the option to distribute your version of this file under
34  * either the CDDL, the GPL Version 2 or to extend the choice of license to
35  * its licensees as provided above.  However, if you add GPL Version 2 code
36  * and therefore, elected the GPL Version 2 license, then the option applies
37  * only if the new code is made subject to such option by the copyright
38  * holder.
39  */

40
41 package javax.mail;
42
43 import java.lang.reflect.*;
44 import java.io.*;
45 import java.net.*;
46 import java.security.*;
47 import java.util.Enumeration;
48 import java.util.Hashtable;
49 import java.util.Properties;
50 import java.util.StringTokenizer;
51 import java.util.Vector;
52 import java.util.logging.Level;
53 import java.util.concurrent.Executor;
54
55
56 import com.sun.mail.util.LineInputStream;
57 import com.sun.mail.util.MailLogger;
58 import java.util.Collections;
59 import java.util.List;
60
61 /**
62  * The Session class represents a mail session and is not subclassed.
63  * It collects together properties and defaults used by the mail API's.
64  * A single default session can be shared by multiple applications on the
65  * desktop.  Unshared sessions can also be created. <p>
66  *
67  * The Session class provides access to the protocol providers that
68  * implement the <code>Store</code>, <code>Transport</code>, and related
69  * classes.  The protocol providers are configured using the following files:
70  * <ul>
71  *  <li> <code>javamail.providers</code> and
72  *     <code>javamail.default.providers</code> </li>
73  *  <li> <code>javamail.address.map</code> and
74  *     <code>javamail.default.address.map</code> </li>
75  * </ul>
76  * <p>
77  * Each <code>javamail.</code><i>X</i> resource file is searched for using
78  * three methods in the following order:
79  * <ol>
80  *  <li> <code><i>java.home</i>/<i>conf</i>/javamail.</code><i>X</i> </li>
81  *  <li> <code>META-INF/javamail.</code><i>X</i> </li>
82  *  <li> <code>META-INF/javamail.default.</code><i>X</i> </li>
83  * </ol>
84  * <p>
85  * (Where <i>java.home</i> is the value of the "java.home" System property
86  * and <i>conf</i> is the directory named "conf" if it exists,
87  * otherwise the directory named "lib"; the "conf" directory was
88  * introduced in JDK 1.9.)
89  * <p>
90  * The first method allows the user to include their own version of the
91  * resource file by placing it in the <i>conf</i> directory where the
92  * <code>java.home</code> property points.  The second method allows an
93  * application that uses the JavaMail APIs to include their own resource
94  * files in their application's or jar file's <code>META-INF</code>
95  * directory.  The <code>javamail.default.</code><i>X</i> default files
96  * are part of the JavaMail <code>mail.jar</code> file and should not be
97  * supplied by users. <p>
98  *
99  * File location depends upon how the <code>ClassLoader</code> method
100  * <code>getResource</code> is implemented.  Usually, the
101  * <code>getResource</code> method searches through CLASSPATH until it
102  * finds the requested file and then stops. <p>
103  *
104  * The ordering of entries in the resource files matters.  If multiple
105  * entries exist, the first entries take precedence over the later
106  * entries.  For example, the first IMAP provider found will be set as the
107  * default IMAP implementation until explicitly changed by the
108  * application.  The user- or system-supplied resource files augment, they
109  * do not override, the default files included with the JavaMail APIs.
110  * This means that all entries in all files loaded will be available. <p>
111  *
112  * <b><code>javamail.providers</code></b> and
113  * <b><code>javamail.default.providers</code></b><p>
114  *
115  * These resource files specify the stores and transports that are
116  * available on the system, allowing an application to "discover" what
117  * store and transport implementations are available.  The protocol
118  * implementations are listed one per line.  The file format defines four
119  * attributes that describe a protocol implementation.  Each attribute is
120  * an "="-separated name-value pair with the name in lowercase. Each
121  * name-value pair is semi-colon (";") separated.  The following names
122  * are defined.
123  *
124  * <table border=1>
125  * <caption>
126  * Attribute Names in Providers Files
127  * </caption>
128  * <tr>
129  * <th>Name</th><th>Description</th>
130  * </tr>
131  * <tr>
132  * <td>protocol</td>
133  * <td>Name assigned to protocol.
134  * For example, <code>smtp</code> for Transport.</td>
135  * </tr>
136  * <tr>
137  * <td>type</td>
138  * <td>Valid entries are <code>store</code> and <code>transport</code>.</td>
139  * </tr>
140  * <tr>
141  * <td>class</td>
142  * <td>Class name that implements this protocol.</td>
143  * </tr>
144  * <tr>
145  * <td>vendor</td>
146  * <td>Optional string identifying the vendor.</td>
147  * </tr>
148  * <tr>
149  * <td>version</td>
150  * <td>Optional string identifying the version.</td>
151  * </tr>
152  * </table><p>
153  *
154  * Here's an example of <code>META-INF/javamail.default.providers</code>
155  * file contents:
156  * <pre>
157  * protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Oracle;
158  * protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Oracle;
159  * </pre><p>
160  *
161  * <b><code>javamail.address.map</code></b> and
162  * <b><code>javamail.default.address.map</code></b><p>
163  *
164  * These resource files map transport address types to the transport
165  * protocol.  The <code>getType</code> method of
166  * <code>javax.mail.Address</code> returns the address type.  The
167  * <code>javamail.address.map</code> file maps the transport type to the
168  * protocol.  The file format is a series of name-value pairs.  Each key
169  * name should correspond to an address type that is currently installed
170  * on the system; there should also be an entry for each
171  * <code>javax.mail.Address</code> implementation that is present if it is
172  * to be used.  For example, the
173  * <code>javax.mail.internet.InternetAddress</code> method
174  * <code>getType</code> returns "rfc822". Each referenced protocol should
175  * be installed on the system.  For the case of <code>news</code>, below,
176  * the client should install a Transport provider supporting the nntp
177  * protocol. <p>
178  *
179  * Here are the typical contents of a <code>javamail.address.map</code> file:
180  * <pre>
181  * rfc822=smtp
182  * news=nntp
183  * </pre>
184  *
185  * @author John Mani
186  * @author Bill Shannon
187  * @author Max Spivak
188  */

189
190 public final class Session {
191
192     private final Properties props;
193     private final Authenticator authenticator;
194     private final Hashtable<URLName, PasswordAuthentication> authTable
195         = new Hashtable<>();
196     private boolean debug = false;
197     private PrintStream out;            // debug output stream
198     private MailLogger logger;
199     private final Vector<Provider> providers = new Vector<>();
200     private final Hashtable<String, Provider> providersByProtocol
201         = new Hashtable<>();
202     private final Hashtable<String, Provider> providersByClassName
203         = new Hashtable<>();
204     private final Properties addressMap = new Properties();
205                         // maps type to protocol
206     // the queue of events to be delivered, if mail.event.scope===session
207     private final EventQueue q;
208
209     // The default session.
210     private static Session defaultSession = null;
211
212     private static final String confDir;
213
214     static {
215     String dir = null;
216     try {
217         dir = AccessController.doPrivileged(
218         new PrivilegedAction<String>() {
219             @Override
220             public String run() {
221             String home = System.getProperty("java.home");
222             String newdir = home + File.separator + "conf";
223             File conf = new File(newdir);
224             if (conf.exists())
225                 return newdir + File.separator;
226             else
227                 return home + File.separator +
228                     "lib" + File.separator;
229             }
230         });
231     } catch (Exception ex) {
232         // ignore any exceptions
233     }
234     confDir = dir;
235     }
236
237     // Constructor is not public
238     private Session(Properties props, Authenticator authenticator) {
239     this.props = props;
240     this.authenticator = authenticator;
241
242     if (Boolean.valueOf(props.getProperty("mail.debug")).booleanValue())
243         debug = true;
244
245     initLogger();
246     logger.log(Level.CONFIG, "JavaMail version {0}", Version.version);
247
248     // get the Class associated with the Authenticator
249     Class<?> cl;
250     if (authenticator != null)
251         cl = authenticator.getClass();
252     else
253         cl = this.getClass();
254     // load the resources
255     loadProviders(cl);
256     loadAddressMap(cl);
257     q = new EventQueue((Executor)props.get("mail.event.executor"));
258     }
259
260     private final synchronized void initLogger() {
261     logger = new MailLogger(this.getClass(), "DEBUG", debug, getDebugOut());
262     }
263
264     /**
265      * Get a new Session object.
266      *
267      * @param    props    Properties object that hold relevant properties.<br>
268      *                  It is expected that the client supplies values
269      *                  for the properties listed in Appendix A of the
270      *                  JavaMail spec (particularly  mail.store.protocol, 
271      *                  mail.transport.protocol, mail.host, mail.user, 
272      *                  and mail.from) as the defaults are unlikely to 
273      *                  work in all cases.
274      * @param    authenticator Authenticator object used to call back to
275      *            the application when a user name and password is
276      *            needed.
277      * @return        a new Session object
278      * @see    javax.mail.Authenticator
279      */

280     public static Session getInstance(Properties props,
281                     Authenticator authenticator) {
282     return new Session(props, authenticator);
283     }
284
285     /**
286      * Get a new Session object.
287      *
288      * @param    props    Properties object that hold relevant properties.<br>
289      *                  It is expected that the client supplies values
290      *                  for the properties listed in Appendix A of the
291      *                  JavaMail spec (particularly  mail.store.protocol, 
292      *                  mail.transport.protocol, mail.host, mail.user, 
293      *                  and mail.from) as the defaults are unlikely to 
294      *                  work in all cases.
295      * @return        a new Session object
296      * @since        JavaMail 1.2
297      */

298     public static Session getInstance(Properties props) {
299     return new Session(props, null);
300     }
301
302     /**
303      * Get the default Session object. If a default has not yet been
304      * setup, a new Session object is created and installed as the 
305      * default. <p>
306      *
307      * Since the default session is potentially available to all
308      * code executing in the same Java virtual machine, and the session
309      * can contain security sensitive information such as user names
310      * and passwords, access to the default session is restricted.
311      * The Authenticator object, which must be created by the caller,
312      * is used indirectly to check access permission.  The Authenticator
313      * object passed in when the session is created is compared with
314      * the Authenticator object passed in to subsequent requests to
315      * get the default session.  If both objects are the same, or are
316      * from the same ClassLoader, the request is allowed.  Otherwise,
317      * it is denied.  <p>
318      *
319      * Note that if the Authenticator object used to create the session
320      * is null, anyone can get the default session by passing in null.  <p>
321      *
322      * Note also that the Properties object is used only the first time
323      * this method is called, when a new Session object is created.
324      * Subsequent calls return the Session object that was created by the
325      * first call, and ignore the passed Properties object.  Use the
326      * <code>getInstance</code> method to get a new Session object every
327      * time the method is called. <p>
328      *
329      * Additional security Permission objects may be used to
330      * control access to the default session. <p>
331      *
332      * In the current implementation, if a SecurityManager is set, the
333      * caller must have the <code>RuntimePermission("setFactory")</code>
334      * permission.
335      *
336      * @param    props    Properties object. Used only if a new Session
337      *            object is created.<br>
338      *                  It is expected that the client supplies values
339      *                  for the properties listed in Appendix A of the
340      *                  JavaMail spec (particularly  mail.store.protocol, 
341      *                  mail.transport.protocol, mail.host, mail.user, 
342      *                  and mail.from) as the defaults are unlikely to 
343      *                  work in all cases.
344      * @param    authenticator Authenticator object.  Used only if a
345      *            new Session object is created.  Otherwise, 
346      *            it must match the Authenticator used to create
347      *            the Session.
348      * @return        the default Session object
349      */

350     public static synchronized Session getDefaultInstance(Properties props,
351                     Authenticator authenticator) {
352     if (defaultSession == null) {
353         SecurityManager security = System.getSecurityManager();
354         if (security != null)
355         security.checkSetFactory();
356         defaultSession = new Session(props, authenticator);
357     } else {
358         // have to check whether caller is allowed to see default session
359         if (defaultSession.authenticator == authenticator)
360         ;    // either same object or both null, either way OK
361         else if (defaultSession.authenticator != null &&
362             authenticator != null &&
363             defaultSession.authenticator.getClass().getClassLoader() ==
364             authenticator.getClass().getClassLoader())
365         ;    // both objects came from the same class loader, OK
366         else
367         // anything else is not allowed
368         throw new SecurityException("Access to default session denied");
369     }
370
371     return defaultSession;
372     }
373
374     /**
375      * Get the default Session object. If a default has not yet been
376      * setup, a new Session object is created and installed as the 
377      * default. <p>
378      *
379      * Note that a default session created with no Authenticator is
380      * available to all code executing in the same Java virtual
381      * machine, and the session can contain security sensitive
382      * information such as user names and passwords.
383      *
384      * @param    props    Properties object. Used only if a new Session
385      *            object is created.<br>
386      *                  It is expected that the client supplies values
387      *                  for the properties listed in Appendix A of the
388      *                  JavaMail spec (particularly  mail.store.protocol, 
389      *                  mail.transport.protocol, mail.host, mail.user, 
390      *                  and mail.from) as the defaults are unlikely to 
391      *                  work in all cases.
392      * @return        the default Session object
393      * @since        JavaMail 1.2
394      */

395     public static Session getDefaultInstance(Properties props) {
396         return getDefaultInstance(props, null);
397     }
398
399     /**
400      * Set the debug setting for this Session.
401      * <p>
402      * Since the debug setting can be turned on only after the Session
403      * has been created, to turn on debugging in the Session
404      * constructor, set the property <code>mail.debug</code> in the
405      * Properties object passed in to the constructor to true.  The
406      * value of the <code>mail.debug</code> property is used to
407      * initialize the per-Session debugging flag.  Subsequent calls to
408      * the <code>setDebug</code> method manipulate the per-Session
409      * debugging flag and have no affect on the <code>mail.debug</code>
410      * property.
411      *
412      * @param debug    Debug setting
413      */

414     public synchronized void setDebug(boolean debug) {
415     this.debug = debug;
416     initLogger();
417     logger.log(Level.CONFIG, "setDebug: JavaMail version {0}",
418                     Version.version);
419     }
420
421     /**
422      * Get the debug setting for this Session.
423      *
424      * @return current debug setting
425      */

426     public synchronized boolean getDebug() {
427     return debug;
428     }
429
430     /**
431      * Set the stream to be used for debugging output for this session.
432      * If <code>out</code> is null, <code>System.out</code> will be used.
433      * Note that debugging output that occurs before any session is created,
434      * as a result of setting the <code>mail.debug</code> system property,
435      * will always be sent to <code>System.out</code>.
436      *
437      * @param    out    the PrintStream to use for debugging output
438      * @since        JavaMail 1.3
439      */

440     public synchronized void setDebugOut(PrintStream out) {
441     this.out = out;
442     initLogger();
443     }
444
445     /**
446      * Returns the stream to be used for debugging output.  If no stream
447      * has been set, <code>System.out</code> is returned.
448      *
449      * @return        the PrintStream to use for debugging output
450      * @since        JavaMail 1.3
451      */

452     public synchronized PrintStream getDebugOut() {
453     if (out == null)
454         return System.out;
455     else
456         return out;
457     }
458
459     /**
460      * This method returns an array of all the implementations installed 
461      * via the javamail.[default.]providers files that can
462      * be loaded using the ClassLoader available to this application.
463      *
464      * @return Array of configured providers
465      */

466     public synchronized Provider[] getProviders() {
467     Provider[] _providers = new Provider[providers.size()];
468     providers.copyInto(_providers);
469     return _providers;
470     }
471
472     /**
473      * Returns the default Provider for the protocol
474      * specified. Checks mail.&lt;protocol&gt;.class property
475      * first and if it exists, returns the Provider
476      * associated with this implementation. If it doesn't exist, 
477      * returns the Provider that appeared first in the 
478      * configuration files. If an implementation for the protocol 
479      * isn't found, throws NoSuchProviderException
480      *
481      * @param  protocol  Configured protocol (i.e. smtp, imap, etc)
482      * @return Currently configured Provider for the specified protocol
483      * @exception    NoSuchProviderException If a provider for the given
484      *            protocol is not found.
485      */

486     public synchronized Provider getProvider(String protocol)
487                                     throws NoSuchProviderException {
488
489     if (protocol == null || protocol.length() <= 0) {
490         throw new NoSuchProviderException("Invalid protocol: null");
491     }
492
493     Provider _provider = null;
494
495     // check if the mail.<protocol>.class property exists
496     String _className = props.getProperty("mail."+protocol+".class");
497     if (_className != null) {
498         if (logger.isLoggable(Level.FINE)) {
499         logger.fine("mail."+protocol+
500                    ".class property exists and points to " + 
501                    _className);
502         }
503         _provider = providersByClassName.get(_className);
504     } 
505
506     if (_provider != null) {
507         return _provider;
508     } else {
509         // returning currently default protocol in providersByProtocol
510         _provider = providersByProtocol.get(protocol);
511     }
512
513     if (_provider == null) {
514         throw new NoSuchProviderException("No provider for " + protocol);
515     } else {
516         if (logger.isLoggable(Level.FINE)) {
517         logger.fine("getProvider() returning " + _provider.toString());
518         }
519         return _provider;
520     }
521     }
522
523     /**
524      * Set the passed Provider to be the default implementation
525      * for the protocol in Provider.protocol overriding any previous values.
526      *
527      * @param provider Currently configured Provider which will be 
528      * set as the default for the protocol
529      * @exception    NoSuchProviderException If the provider passed in
530      *            is invalid.
531      */

532     public synchronized void setProvider(Provider provider)
533                 throws NoSuchProviderException {
534     if (provider == null) {
535         throw new NoSuchProviderException("Can't set null provider");
536     }
537     providersByProtocol.put(provider.getProtocol(), provider);
538     props.put("mail." + provider.getProtocol() + ".class"
539           provider.getClassName());
540     }
541
542
543     /**
544      * Get a Store object that implements this user's desired Store
545      * protocol. The <code>mail.store.protocol</code> property specifies the
546      * desired protocol. If an appropriate Store object is not obtained, 
547      * NoSuchProviderException is thrown
548      *
549      * @return         a Store object 
550      * @exception    NoSuchProviderException If a provider for the given
551      *            protocol is not found.
552      */

553     public Store getStore() throws NoSuchProviderException {
554     return getStore(getProperty("mail.store.protocol"));
555     }
556
557     /**
558      * Get a Store object that implements the specified protocol. If an
559      * appropriate Store object cannot be obtained, 
560      * NoSuchProviderException is thrown.
561      *
562      * @param            protocol    the Store protocol
563      * @return        a Store object 
564      * @exception    NoSuchProviderException If a provider for the given
565      *            protocol is not found.
566      */

567     public Store getStore(String protocol) throws NoSuchProviderException {
568     return getStore(new URLName(protocol, null, -1, nullnullnull));
569     }
570
571
572     /**
573      * Get a Store object for the given URLName. If the requested Store
574      * object cannot be obtained, NoSuchProviderException is thrown.
575      *
576      * The "scheme" part of the URL string (Refer RFC 1738) is used 
577      * to locate the Store protocol. <p>
578      *
579      * @param    url    URLName that represents the desired Store
580      * @return        a closed Store object
581      * @see        #getFolder(URLName)
582      * @see        javax.mail.URLName
583      * @exception    NoSuchProviderException If a provider for the given
584      *            URLName is not found.
585      */

586     public Store getStore(URLName url) throws NoSuchProviderException {
587     String protocol = url.getProtocol();
588     Provider p = getProvider(protocol);
589     return getStore(p, url);
590     }
591
592     /**
593      * Get an instance of the store specified by Provider. Instantiates
594      * the store and returns it.
595      * 
596      * @param provider Store Provider that will be instantiated
597      * @return Instantiated Store
598      * @exception    NoSuchProviderException If a provider for the given
599      *            Provider is not found.
600      */

601     public Store getStore(Provider provider) throws NoSuchProviderException {
602     return getStore(provider, null);
603     }
604
605
606     /**
607      * Get an instance of the store specified by Provider. If the URLName
608      * is not null, uses it, otherwise creates a new one. Instantiates
609      * the store and returns it. This is a private method used by
610      * getStore(Provider) and getStore(URLName)
611      * 
612      * @param provider Store Provider that will be instantiated
613      * @param url      URLName used to instantiate the Store
614      * @return Instantiated Store
615      * @exception    NoSuchProviderException If a provider for the given
616      *            Provider/URLName is not found.
617      */

618     private Store getStore(Provider provider, URLName url) 
619     throws NoSuchProviderException {
620
621     // make sure we have the correct type of provider
622     if (provider == null || provider.getType() != Provider.Type.STORE ) {
623         throw new NoSuchProviderException("invalid provider");
624     }
625
626     return getService(provider, url, Store.class);
627     }
628
629     /**
630      * Get a closed Folder object for the given URLName. If the requested
631      * Folder object cannot be obtained, null is returned. <p>
632      *
633      * The "scheme" part of the URL string (Refer RFC 1738) is used
634      * to locate the Store protocol. The rest of the URL string (that is,
635      * the "schemepart", as per RFC 1738) is used by that Store
636      * in a protocol dependent manner to locate and instantiate the
637      * appropriate Folder object. <p>
638      *
639      * Note that RFC 1738 also specifies the syntax for the 
640      * "schemepart" for IP-based protocols (IMAP4, POP3, etc.).
641      * Providers of IP-based mail Stores should implement that
642      * syntax for referring to Folders. <p>
643      *
644      * @param    url    URLName that represents the desired folder
645      * @return        Folder
646      * @see        #getStore(URLName)
647      * @see        javax.mail.URLName
648      * @exception    NoSuchProviderException If a provider for the given
649      *            URLName is not found.
650      * @exception    MessagingException if the Folder could not be 
651      *            located or created.
652      */

653     public Folder getFolder(URLName url)
654         throws MessagingException {
655     // First get the Store
656     Store store = getStore(url);
657     store.connect();
658     return store.getFolder(url);
659     }
660
661     /**
662      * Get a Transport object that implements this user's desired 
663      * Transport protcol. The <code>mail.transport.protocol</code> property 
664      * specifies the desired protocol. If an appropriate Transport 
665      * object cannot be obtained, MessagingException is thrown.
666      *
667      * @return         a Transport object 
668      * @exception    NoSuchProviderException If the provider is not found.
669      */

670     public Transport getTransport() throws NoSuchProviderException {
671     String prot = getProperty("mail.transport.protocol");
672     if (prot != null)
673         return getTransport(prot);
674     // if the property isn't set, use the protocol for "rfc822"
675     prot = (String)addressMap.get("rfc822");
676     if (prot != null)
677         return getTransport(prot);
678     return getTransport("smtp");    // if all else fails
679     }
680
681     /**
682      * Get a Transport object that implements the specified protocol.
683      * If an appropriate Transport object cannot be obtained, null is
684      * returned.
685      *
686      * @param    protocol the Transport protocol
687      * @return         a Transport object 
688      * @exception    NoSuchProviderException If provider for the given
689      *            protocol is not found.
690      */

691     public Transport getTransport(String protocol)
692                 throws NoSuchProviderException {
693     return getTransport(new URLName(protocol, null, -1, nullnullnull));
694     }
695
696
697     /**
698      * Get a Transport object for the given URLName. If the requested 
699      * Transport object cannot be obtained, NoSuchProviderException is thrown.
700      *
701      * The "scheme" part of the URL string (Refer RFC 1738) is used 
702      * to locate the Transport protocol. <p>
703      *
704      * @param    url    URLName that represents the desired Transport
705      * @return        a closed Transport object
706      * @see        javax.mail.URLName
707      * @exception    NoSuchProviderException If a provider for the given
708      *            URLName is not found.
709      */

710     public Transport getTransport(URLName url) throws NoSuchProviderException {
711     String protocol = url.getProtocol();
712     Provider p = getProvider(protocol);
713     return getTransport(p, url);
714     }
715
716     /**
717      * Get an instance of the transport specified in the Provider. Instantiates
718      * the transport and returns it.
719      * 
720      * @param provider Transport Provider that will be instantiated
721      * @return Instantiated Transport
722      * @exception    NoSuchProviderException If provider for the given
723      *            provider is not found.
724      */

725     public Transport getTransport(Provider provider) 
726                                          throws NoSuchProviderException {
727     return getTransport(provider, null);
728     }
729
730     /**
731      * Get a Transport object that can transport a Message of the
732      * specified address type.
733      *
734      * @param    address    an address for which a Transport is needed
735      * @return    A Transport object
736      * @see javax.mail.Address
737      * @exception    NoSuchProviderException If provider for the 
738      *            Address type is not found
739      */

740     public Transport getTransport(Address address) 
741                                          throws NoSuchProviderException {
742
743     String transportProtocol;
744     transportProtocol =
745         getProperty("mail.transport.protocol." + address.getType());
746     if (transportProtocol != null)
747         return getTransport(transportProtocol);
748     transportProtocol = (String)addressMap.get(address.getType());
749     if (transportProtocol != null)
750         return getTransport(transportProtocol);
751     throw new NoSuchProviderException("No provider for Address type: "+
752                         address.getType());
753     }
754
755     /**
756      * Get a Transport object using the given provider and urlname.
757      *
758      * @param    provider    the provider to use
759      * @param    url        urlname to use (can be null)
760      * @return A Transport object
761      * @exception    NoSuchProviderException    If no provider or the provider
762      *            was the wrong class.    
763      */

764
765     private Transport getTransport(Provider provider, URLName url)
766                     throws NoSuchProviderException {
767     // make sure we have the correct type of provider
768     if (provider == null || provider.getType() != Provider.Type.TRANSPORT) {
769         throw new NoSuchProviderException("invalid provider");
770     }
771
772     return getService(provider, url, Transport.class);
773     }
774
775     /**
776      * Get a Service object.  Needs a provider object, but will
777      * create a URLName if needed.  It attempts to instantiate
778      * the correct class.
779      *
780      * @param provider    which provider to use
781      * @param url    which URLName to use (can be null)
782      * @param type    the service type (class)
783      * @exception    NoSuchProviderException    thrown when the class cannot be
784      *            found or when it does not have the correct constructor
785      *            (Session, URLName), or if it is not derived from
786      *            Service.
787      */

788     private <T extends Service> T getService(Provider provider, URLName url,
789                     Class<T> type)
790                     throws NoSuchProviderException {
791     // need a provider and url
792     if (provider == null) {
793         throw new NoSuchProviderException("null");
794     }
795
796     // create a url if needed
797     if (url == null) {
798         url = new URLName(provider.getProtocol(), null, -1, 
799                   nullnullnull);
800     }
801
802     Object service = null;
803     
804     // get the ClassLoader associated with the Authenticator
805     ClassLoader cl;
806     if (authenticator != null)
807         cl = authenticator.getClass().getClassLoader();
808     else
809         cl = this.getClass().getClassLoader();
810
811     // now load the class
812     Class<?> serviceClass = null;
813     try {
814         // First try the "application's" class loader.
815         ClassLoader ccl = getContextClassLoader();
816         if (ccl != null)
817         try {
818             serviceClass =
819             Class.forName(provider.getClassName(), false, ccl);
820         } catch (ClassNotFoundException ex) {
821             // ignore it
822         }
823         if (serviceClass == null || !type.isAssignableFrom(serviceClass))
824         serviceClass =
825             Class.forName(provider.getClassName(), false, cl);
826             
827         if (!type.isAssignableFrom(serviceClass))
828         throw new ClassCastException(
829                 type.getName() + " " + serviceClass.getName());
830     } catch (Exception ex1) {
831         // That didn't work, now try the "system" class loader.
832         // (Need both of these because JDK 1.1 class loaders
833         // may not delegate to their parent class loader.)
834         try {
835         serviceClass = Class.forName(provider.getClassName());
836         if (!type.isAssignableFrom(serviceClass))
837             throw new ClassCastException(
838                 type.getName() + " " + serviceClass.getName());
839         } catch (Exception ex) {
840         // Nothing worked, give up.
841         logger.log(Level.FINE, "Exception loading provider", ex);
842         throw new NoSuchProviderException(provider.getProtocol());
843         }
844     }
845
846     // construct an instance of the class
847     try {
848         Class<?>[] c = {javax.mail.Session.class, javax.mail.URLName.class};
849         Constructor<?> cons = serviceClass.getConstructor(c);
850
851         Object[] o = {this, url};
852         service = cons.newInstance(o);
853
854     } catch (Exception ex) {
855         logger.log(Level.FINE, "Exception loading provider", ex);
856         throw new NoSuchProviderException(provider.getProtocol());
857     }
858
859     return type.cast(service);
860     }
861
862     /**
863      * Save a PasswordAuthentication for this (store or transport) URLName.
864      * If pw is null the entry corresponding to the URLName is removed.
865      * <p>
866      * This is normally used only by the store or transport implementations
867      * to allow authentication information to be shared among multiple
868      * uses of a session.
869      *
870      * @param    url    the URLName
871      * @param    pw    the PasswordAuthentication to save
872      */

873     public void setPasswordAuthentication(URLName url,
874                       PasswordAuthentication pw) {
875     if (pw == null)
876         authTable.remove(url);
877     else
878         authTable.put(url, pw);
879     }
880
881     /**
882      * Return any saved PasswordAuthentication for this (store or transport)
883      * URLName.  Normally used only by store or transport implementations.
884      *
885      * @param    url    the URLName
886      * @return    the PasswordAuthentication corresponding to the URLName
887      */

888     public PasswordAuthentication getPasswordAuthentication(URLName url) {
889     return authTable.get(url);
890     }
891
892     /**
893      * Call back to the application to get the needed user name and password.
894      * The application should put up a dialog something like:
895      * <pre>
896      * Connecting to &lt;protocol&gt; mail service on host &lt;addr&gt;, port &lt;port&gt;.
897      * &lt;prompt&gt;
898      *
899      * User Name: &lt;defaultUserName&gt;
900      * Password:
901      * </pre>
902      *
903      * @param    addr        InetAddress of the host.  may be null.
904      * @param    port        the port on the host
905      * @param    protocol    protocol scheme (e.g. imap, pop3, etc.)
906      * @param    prompt        any additional String to show as part of
907      *                          the prompt; may be null.
908      * @param    defaultUserName    the default username. may be null.
909      * @return    the authentication which was collected by the authenticator; 
910      *          may be null.
911      */

912     public PasswordAuthentication requestPasswordAuthentication(
913     InetAddress addr, int port,
914     String protocol, String prompt, String defaultUserName) {
915
916     if (authenticator != null) {
917         return authenticator.requestPasswordAuthentication(
918         addr, port, protocol, prompt, defaultUserName);
919     } else {
920         return null;
921     }
922     }
923
924     /**
925      * Returns the Properties object associated with this Session
926      *
927      * @return        Properties object
928      */

929     public Properties getProperties() { 
930        return props; 
931     }
932
933     /**
934      * Returns the value of the specified property. Returns null
935      * if this property does not exist.
936      *
937      * @param    name    the property name
938      * @return        String that is the property value
939      */

940     public String getProperty(String name) { 
941        return props.getProperty(name); 
942     }
943
944     /**
945      * Load the protocol providers config files.
946      */

947     private void loadProviders(Class<?> cl) {
948     StreamLoader loader = new StreamLoader() {
949         @Override
950         public void load(InputStream is) throws IOException {
951         loadProvidersFromStream(is);
952         }
953     };
954
955     // load system-wide javamail.providers from the
956     // <java.home>/{conf,lib} directory
957     try {
958         if (confDir != null)
959         loadFile(confDir + "javamail.providers", loader);
960     } catch (SecurityException ex) {}
961
962     // load the META-INF/javamail.providers file supplied by an application
963     loadAllResources("META-INF/javamail.providers", cl, loader);
964
965     // load default META-INF/javamail.default.providers from mail.jar file
966     loadResource("/META-INF/javamail.default.providers", cl, loader, true);
967
968     if (providers.size() == 0) {
969         logger.config("failed to load any providers, using defaults");
970         // failed to load any providers, initialize with our defaults
971         addProvider(new Provider(Provider.Type.STORE,
972             "imap""com.sun.mail.imap.IMAPStore",
973             "Oracle", Version.version));
974         addProvider(new Provider(Provider.Type.STORE,
975             "imaps""com.sun.mail.imap.IMAPSSLStore",
976             "Oracle", Version.version));
977         addProvider(new Provider(Provider.Type.STORE,
978             "pop3""com.sun.mail.pop3.POP3Store",
979             "Oracle", Version.version));
980         addProvider(new Provider(Provider.Type.STORE,
981             "pop3s""com.sun.mail.pop3.POP3SSLStore",
982             "Oracle", Version.version));
983         addProvider(new Provider(Provider.Type.TRANSPORT,
984             "smtp""com.sun.mail.smtp.SMTPTransport",
985             "Oracle", Version.version));
986         addProvider(new Provider(Provider.Type.TRANSPORT,
987             "smtps""com.sun.mail.smtp.SMTPSSLTransport",
988             "Oracle", Version.version));
989     }
990
991     if (logger.isLoggable(Level.CONFIG)) {
992         // dump the output of the tables for debugging
993         logger.config("Tables of loaded providers");
994         logger.config("Providers Listed By Class Name: " + 
995            providersByClassName.toString());
996         logger.config("Providers Listed By Protocol: " + 
997            providersByProtocol.toString());
998     }
999     }
1000
1001     private void loadProvidersFromStream(InputStream is) 
1002                 throws IOException {
1003     if (is != null) {
1004         LineInputStream lis = new LineInputStream(is);
1005         String currLine;
1006
1007         // load and process one line at a time using LineInputStream
1008         while ((currLine = lis.readLine()) != null) {
1009
1010         if (currLine.startsWith("#"))
1011             continue;
1012         if (currLine.trim().length() == 0)
1013             continue;    // skip blank line
1014         Provider.Type type = null;
1015         String protocol = null, className = null;
1016         String vendor = null, version = null;
1017             
1018         // separate line into key-value tuples
1019         StringTokenizer tuples = new StringTokenizer(currLine,";");
1020         while (tuples.hasMoreTokens()) {
1021             String currTuple = tuples.nextToken().trim();
1022             
1023             // set the value of each attribute based on its key
1024             int sep = currTuple.indexOf("=");
1025             if (currTuple.startsWith("protocol=")) {
1026             protocol = currTuple.substring(sep+1);
1027             } else if (currTuple.startsWith("type=")) {
1028             String strType = currTuple.substring(sep+1);
1029             if (strType.equalsIgnoreCase("store")) {
1030                 type = Provider.Type.STORE;
1031             } else if (strType.equalsIgnoreCase("transport")) {
1032                 type = Provider.Type.TRANSPORT;
1033                 }
1034             } else if (currTuple.startsWith("class=")) {
1035             className = currTuple.substring(sep+1);
1036             } else if (currTuple.startsWith("vendor=")) {
1037             vendor = currTuple.substring(sep+1);
1038             } else if (currTuple.startsWith("version=")) {
1039             version = currTuple.substring(sep+1);
1040             }
1041         }
1042
1043         // check if a valid Provider; elsecontinue
1044         if (type == null || protocol == null || className == null 
1045             || protocol.length() <= 0 || className.length() <= 0) {
1046             
1047             logger.log(Level.CONFIG, "Bad provider entry: {0}",
1048                         currLine);
1049             continue;
1050         }
1051         Provider provider = new Provider(type, protocol, className,
1052                              vendor, version);
1053
1054         // add the newly-created Provider to the lookup tables
1055         addProvider(provider);
1056         }
1057     }
1058     }
1059
1060     /**
1061      * Add a provider to the session.
1062      *
1063      * @param    provider    the provider to add
1064      * @since    JavaMail 1.4
1065      */

1066     public synchronized void addProvider(Provider provider) {
1067     providers.addElement(provider);
1068     providersByClassName.put(provider.getClassName(), provider);
1069     if (!providersByProtocol.containsKey(provider.getProtocol()))
1070         providersByProtocol.put(provider.getProtocol(), provider);
1071     }
1072
1073     // load maps in reverse order of preference so that the preferred
1074     // map is loaded last since its entries will override the previous ones
1075     private void loadAddressMap(Class<?> cl) {
1076     StreamLoader loader = new StreamLoader() {
1077         @Override
1078         public void load(InputStream is) throws IOException {
1079         addressMap.load(is);
1080         }
1081     };
1082
1083     // load default META-INF/javamail.default.address.map from mail.jar
1084     loadResource("/META-INF/javamail.default.address.map", cl, loader, true);
1085
1086     // load the META-INF/javamail.address.map file supplied by an app
1087     loadAllResources("META-INF/javamail.address.map", cl, loader);
1088
1089     // load system-wide javamail.address.map from the
1090     // <java.home>/{conf,lib} directory
1091     try {
1092         if (confDir != null)
1093         loadFile(confDir + "javamail.address.map", loader);
1094     } catch (SecurityException ex) {}
1095
1096     if (addressMap.isEmpty()) {
1097         logger.config("failed to load address map, using defaults");
1098         addressMap.put("rfc822""smtp");
1099     }
1100     }
1101
1102     /**
1103      * Set the default transport protocol to use for addresses of
1104      * the specified type.  Normally the default is set by the
1105      * <code>javamail.default.address.map</code> or
1106      * <code>javamail.address.map</code> files or resources.
1107      *
1108      * @param    addresstype    type of address
1109      * @param    protocol    name of protocol
1110      * @see #getTransport(Address)
1111      * @since    JavaMail 1.4
1112      */

1113     public synchronized void setProtocolForAddress(String addresstype,
1114                 String protocol) {
1115     if (protocol == null)
1116         addressMap.remove(addresstype);
1117     else
1118         addressMap.put(addresstype, protocol);
1119     }
1120
1121     /**
1122      * Load from the named file.
1123      */

1124     private void loadFile(String name, StreamLoader loader) {
1125     InputStream clis = null;
1126     try {
1127         clis = new BufferedInputStream(new FileInputStream(name));
1128         loader.load(clis);
1129         logger.log(Level.CONFIG, "successfully loaded file: {0}", name);
1130     } catch (FileNotFoundException fex) {
1131         // ignore it
1132     } catch (IOException e) {
1133         if (logger.isLoggable(Level.CONFIG))
1134         logger.log(Level.CONFIG, "not loading file: " + name, e);
1135     } catch (SecurityException sex) {
1136         if (logger.isLoggable(Level.CONFIG))
1137         logger.log(Level.CONFIG, "not loading file: " + name, sex);
1138     } finally {
1139         try {
1140         if (clis != null)
1141             clis.close();
1142         } catch (IOException ex) { }    // ignore it
1143     }
1144     }
1145
1146     /**
1147      * Load from the named resource.
1148      */

1149     private void loadResource(String name, Class<?> cl, StreamLoader loader,
1150                 boolean expected) {
1151     InputStream clis = null;
1152     try {
1153         clis = getResourceAsStream(cl, name);
1154         if (clis != null) {
1155         loader.load(clis);
1156         logger.log(Level.CONFIG, "successfully loaded resource: {0}",
1157                         name);
1158         } else {
1159         if (expected)
1160             logger.log(Level.WARNING,
1161                     "expected resource not found: {0}", name);
1162         }
1163     } catch (IOException e) {
1164         logger.log(Level.CONFIG, "Exception loading resource", e);
1165     } catch (SecurityException sex) {
1166         logger.log(Level.CONFIG, "Exception loading resource", sex);
1167     } finally {
1168         try {
1169         if (clis != null)
1170             clis.close();
1171         } catch (IOException ex) { }    // ignore it
1172     }
1173     }
1174
1175     /**
1176      * Load all of the named resource.
1177      */

1178     private void loadAllResources(String name, Class<?> cl,
1179         StreamLoader loader) {
1180     boolean anyLoaded = false;
1181     try {
1182         URL[] urls;
1183         ClassLoader cld = null;
1184         // First try the "application's" class loader.
1185         cld = getContextClassLoader();
1186         if (cld == null)
1187         cld = cl.getClassLoader();
1188         if (cld != null)
1189         urls = getResources(cld, name);
1190         else
1191         urls = getSystemResources(name);
1192         if (urls != null) {
1193         for (int i = 0; i < urls.length; i++) {
1194             URL url = urls[i];
1195             InputStream clis = null;
1196             logger.log(Level.CONFIG, "URL {0}", url);
1197             try {
1198             clis = openStream(url);
1199             if (clis != null) {
1200                 loader.load(clis);
1201                 anyLoaded = true;
1202                 logger.log(Level.CONFIG,
1203                 "successfully loaded resource: {0}", url);
1204             } else {
1205                 logger.log(Level.CONFIG,
1206                 "not loading resource: {0}", url);
1207             }
1208             } catch (FileNotFoundException fex) {
1209             // ignore it
1210             } catch (IOException ioex) {
1211             logger.log(Level.CONFIG, "Exception loading resource",
1212                     ioex);
1213             } catch (SecurityException sex) {
1214             logger.log(Level.CONFIG, "Exception loading resource",
1215                     sex);
1216             } finally {
1217             try {
1218                 if (clis != null)
1219                 clis.close();
1220             } catch (IOException cex) { }
1221             }
1222         }
1223         }
1224     } catch (Exception ex) {
1225         logger.log(Level.CONFIG, "Exception loading resource", ex);
1226     }
1227
1228     // if failed to load anything, fall back to old technique, just in case
1229     if (!anyLoaded) {
1230         /*
1231         logger.config("!anyLoaded");
1232         */

1233         loadResource("/" + name, cl, loader, false);
1234     }
1235     }
1236
1237     /*
1238      * Following are security related methods that work on JDK 1.2 or newer.
1239      */

1240
1241     static ClassLoader getContextClassLoader() {
1242     return AccessController.doPrivileged(
1243         new PrivilegedAction<ClassLoader>() {
1244             @Override
1245             public ClassLoader run() {
1246             ClassLoader cl = null;
1247             try {
1248                 cl = Thread.currentThread().getContextClassLoader();
1249             } catch (SecurityException ex) {
1250             }
1251             return cl;
1252             }
1253         }
1254     );
1255     }
1256
1257     private static InputStream getResourceAsStream(final Class<?> c,
1258                 final String name) throws IOException {
1259     try {
1260         return AccessController.doPrivileged(
1261             new PrivilegedExceptionAction<InputStream>() {
1262             @Override
1263             public InputStream run() throws IOException {
1264                 try {
1265                 return c.getResourceAsStream(name);
1266                 } catch (RuntimeException e) {
1267                 // gracefully handle ClassLoader bugs (Tomcat)
1268                 IOException ioex = new IOException(
1269                     "ClassLoader.getResourceAsStream failed");
1270                 ioex.initCause(e);
1271                 throw ioex;
1272                 }
1273             }
1274             }
1275         );
1276     } catch (PrivilegedActionException e) {
1277         throw (IOException)e.getException();
1278     }
1279     }
1280
1281     private static URL[] getResources(final ClassLoader cl, final String name) {
1282     return AccessController.doPrivileged(new PrivilegedAction<URL[]>() {
1283         @Override
1284         public URL[] run() {
1285         URL[] ret = null;
1286         try {
1287             List<URL> v = Collections.list(cl.getResources(name));
1288             if (!v.isEmpty()) {
1289             ret = new URL[v.size()];
1290             v.toArray(ret);
1291             }
1292         } catch (IOException ioex) {
1293         } catch (SecurityException ex) { }
1294         return ret;
1295         }
1296     });
1297     }
1298
1299     private static URL[] getSystemResources(final String name) {
1300     return AccessController.doPrivileged(new PrivilegedAction<URL[]>() {
1301         @Override
1302         public URL[] run() {
1303         URL[] ret = null;
1304         try {
1305             List<URL> v = Collections.list(
1306                 ClassLoader.getSystemResources(name));
1307             if (!v.isEmpty()) {
1308             ret = new URL[v.size()];
1309             v.toArray(ret);
1310             }
1311         } catch (IOException ioex) {
1312         } catch (SecurityException ex) { }
1313         return ret;
1314         }
1315     });
1316     }
1317
1318     private static InputStream openStream(final URL url) throws IOException {
1319     try {
1320         return AccessController.doPrivileged(
1321             new PrivilegedExceptionAction<InputStream>() {
1322             @Override
1323             public InputStream run() throws IOException {
1324                 return url.openStream();
1325             }
1326             }
1327         );
1328     } catch (PrivilegedActionException e) {
1329         throw (IOException)e.getException();
1330     }
1331     }
1332
1333     EventQueue getEventQueue() {
1334     return q;
1335     }
1336 }
1337
1338 /**
1339  * Support interface to generalize
1340  * code that loads resources from stream.
1341  */

1342 interface StreamLoader {
1343     public void load(InputStream is) throws IOException;
1344 }
1345