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.<protocol>.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, null, null, null));
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, null, null, null));
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 null, null, null);
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 <protocol> mail service on host <addr>, port <port>.
897 * <prompt>
898 *
899 * User Name: <defaultUserName>
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; else, continue
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