1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 1997-2007 Sun Microsystems, Inc. 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 obtain
10 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
11 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
12 * language governing permissions and limitations under the License.
13 *
14 * When distributing the software, include this License Header Notice in each
15 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
16 * Sun designates this particular file as subject to the "Classpath" exception
17 * as provided by Sun in the GPL Version 2 section of the License file that
18 * accompanied this code. If applicable, add the following below the License
19 * Header, with the fields enclosed by brackets [] replaced by your own
20 * identifying information: "Portions Copyrighted [year]
21 * [name of copyright owner]"
22 *
23 * Contributor(s):
24 *
25 * If you wish your version of this file to be governed by only the CDDL or
26 * only the GPL Version 2, indicate your decision by adding "[Contributor]
27 * elects to include this software in this distribution under the [CDDL or GPL
28 * Version 2] license." If you don't indicate a single choice of license, a
29 * recipient has the option to distribute your version of this file under
30 * either the CDDL, the GPL Version 2 or to extend the choice of license to
31 * its licensees as provided above. However, if you add GPL Version 2 code
32 * and therefore, elected the GPL Version 2 license, then the option applies
33 * only if the new code is made subject to such option by the copyright
34 * holder.
35 */
36
37 /*
38 * @(#)Session.java 1.76 07/05/04
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
53 import javax.activation;
54
55 import com.sun.mail.util.LineInputStream;
56
57 /**
58 * The Session class represents a mail session and is not subclassed.
59 * It collects together properties and defaults used by the mail API's.
60 * A single default session can be shared by multiple applications on the
61 * desktop. Unshared sessions can also be created. <p>
62 *
63 * The Session class provides access to the protocol providers that
64 * implement the <code>Store</code>, <code>Transport</code>, and related
65 * classes. The protocol providers are configured using the following files:
66 * <ul>
67 * <li> <code>javamail.providers</code> and
68 * <code>javamail.default.providers</code> </li>
69 * <li> <code>javamail.address.map</code> and
70 * <code>javamail.default.address.map</code> </li>
71 * </ul>
72 * <p>
73 * Each <code>javamail.</code><i>X</i> resource file is searched for using
74 * three methods in the following order:
75 * <ol>
76 * <li> <code>java.home/lib/javamail.</code><i>X</i> </li>
77 * <li> <code>META-INF/javamail.</code><i>X</i> </li>
78 * <li> <code>META-INF/javamail.default.</code><i>X</i> </li>
79 * </ol>
80 * <p>
81 * The first method allows the user to include their own version of the
82 * resource file by placing it in the <code>lib</code> directory where the
83 * <code>java.home</code> property points. The second method allows an
84 * application that uses the JavaMail APIs to include their own resource
85 * files in their application's or jar file's <code>META-INF</code>
86 * directory. The <code>javamail.default.</code><i>X</i> default files
87 * are part of the JavaMail <code>mail.jar</code> file. <p>
88 *
89 * File location depends upon how the <code>ClassLoader</code> method
90 * <code>getResource</code> is implemented. Usually, the
91 * <code>getResource</code> method searches through CLASSPATH until it
92 * finds the requested file and then stops. JDK 1.1 has a limitation that
93 * the number of files of each name that will be found in the CLASSPATH is
94 * limited to one. However, this only affects method two, above; method
95 * one is loaded from a specific location (if allowed by the
96 * SecurityManager) and method three uses a different name to ensure that
97 * the default resource file is always loaded successfully. J2SE 1.2 and
98 * later are not limited to one file of a given name. <p>
99 *
100 * The ordering of entries in the resource files matters. If multiple
101 * entries exist, the first entries take precedence over the later
102 * entries. For example, the first IMAP provider found will be set as the
103 * default IMAP implementation until explicitly changed by the
104 * application. The user- or system-supplied resource files augment, they
105 * do not override, the default files included with the JavaMail APIs.
106 * This means that all entries in all files loaded will be available. <p>
107 *
108 * <b><code>javamail.providers</code></b> and
109 * <b><code>javamail.default.providers</code></b><p>
110 *
111 * These resource files specify the stores and transports that are
112 * available on the system, allowing an application to "discover" what
113 * store and transport implementations are available. The protocol
114 * implementations are listed one per line. The file format defines four
115 * attributes that describe a protocol implementation. Each attribute is
116 * an "="-separated name-value pair with the name in lowercase. Each
117 * name-value pair is semi-colon (";") separated. The following names
118 * are defined. <p>
119 *
120 * <table border=1>
121 * <caption>
122 * Attribute Names in Providers Files
123 * </caption>
124 * <tr>
125 * <th>Name</th><th>Description</th>
126 * </tr>
127 * <tr>
128 * <td>protocol</td>
129 * <td>Name assigned to protocol.
130 * For example, <code>smtp</code> for Transport.</td>
131 * </tr>
132 * <tr>
133 * <td>type</td>
134 * <td>Valid entries are <code>store</code> and <code>transport</code>.</td>
135 * </tr>
136 * <tr>
137 * <td>class</td>
138 * <td>Class name that implements this protocol.</td>
139 * </tr>
140 * <tr>
141 * <td>vendor</td>
142 * <td>Optional string identifying the vendor.</td>
143 * </tr>
144 * <tr>
145 * <td>version</td>
146 * <td>Optional string identifying the version.</td>
147 * </tr>
148 * </table><p>
149 *
150 * Here's an example of <code>META-INF/javamail.default.providers</code>
151 * file contents:
152 * <pre>
153 * protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Sun Microsystems, Inc.;
154 * protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Sun Microsystems, Inc.;
155 * </pre><p>
156 *
157 * <b><code>javamail.address.map</code></b> and
158 * <b><code>javamail.default.address.map</code></b><p>
159 *
160 * These resource files map transport address types to the transport
161 * protocol. The <code>getType</code> method of
162 * </code>javax.mail.Address</code> returns the address type. The
163 * <code>javamail.address.map</code> file maps the transport type to the
164 * protocol. The file format is a series of name-value pairs. Each key
165 * name should correspond to an address type that is currently installed
166 * on the system; there should also be an entry for each
167 * <code>javax.mail.Address</code> implementation that is present if it is
168 * to be used. For example, the
169 * <code>javax.mail.internet.InternetAddress</code> method
170 * <code>getType</code> returns "rfc822". Each referenced protocol should
171 * be installed on the system. For the case of <code>news</code>, below,
172 * the client should install a Transport provider supporting the nntp
173 * protocol. <p>
174 *
175 * Here are the typical contents of a <code>javamail.address.map</code> file:
176 * <pre>
177 * rfc822=smtp
178 * news=nntp
179 * </pre>
180 *
181 * @version 1.76, 07/05/04
182 * @author John Mani
183 * @author Bill Shannon
184 * @author Max Spivak
185 */
186
187 public final class Session {
188
189 private final Properties props;
190 private final Authenticator authenticator;
191 private final Hashtable authTable = new Hashtable();
192 private boolean debug = false;
193 private PrintStream out; // debug output stream
194 private final Vector providers = new Vector();
195 private final Hashtable providersByProtocol = new Hashtable();
196 private final Hashtable providersByClassName = new Hashtable();
197 private final Properties addressMap = new Properties();
198 // maps type to protocol
199 // The default session.
200 private static Session defaultSession = null;
201
202 // Constructor is not public
203 private Session(Properties props, Authenticator authenticator) {
204 this.props = props;
205 this.authenticator = authenticator;
206
207 if (Boolean.valueOf(props.getProperty("mail.debug")).booleanValue())
208 debug = true;
209
210 if (debug)
211 pr("DEBUG: JavaMail version " + Version.version);
212
213 // get the Class associated with the Authenticator
214 Class cl;
215 if (authenticator != null)
216 cl = authenticator.getClass();
217 else
218 cl = this.getClass();
219 // load the resources
220 loadProviders(cl);
221 loadAddressMap(cl);
222 }
223
224 /**
225 * Get a new Session object.
226 *
227 * @param props Properties object that hold relevant properties.<br>
228 * It is expected that the client supplies values
229 * for the properties listed in Appendix A of the
230 * JavaMail spec (particularly mail.store.protocol,
231 * mail.transport.protocol, mail.host, mail.user,
232 * and mail.from) as the defaults are unlikely to
233 * work in all cases.
234 * @param authenticator Authenticator object used to call back to
235 * the application when a user name and password is
236 * needed.
237 * @return a new Session object
238 * @see javax.mail.Authenticator
239 */
240 public static Session getInstance(Properties props,
241 Authenticator authenticator) {
242 return new Session(props, authenticator);
243 }
244
245 /**
246 * Get a new Session object.
247 *
248 * @param props Properties object that hold relevant properties.<br>
249 * It is expected that the client supplies values
250 * for the properties listed in Appendix A of the
251 * JavaMail spec (particularly mail.store.protocol,
252 * mail.transport.protocol, mail.host, mail.user,
253 * and mail.from) as the defaults are unlikely to
254 * work in all cases.
255 * @return a new Session object
256 * @since JavaMail 1.2
257 */
258 public static Session getInstance(Properties props) {
259 return new Session(props, null);
260 }
261
262 /**
263 * Get the default Session object. If a default has not yet been
264 * setup, a new Session object is created and installed as the
265 * default. <p>
266 *
267 * Since the default session is potentially available to all
268 * code executing in the same Java virtual machine, and the session
269 * can contain security sensitive information such as user names
270 * and passwords, access to the default session is restricted.
271 * The Authenticator object, which must be created by the caller,
272 * is used indirectly to check access permission. The Authenticator
273 * object passed in when the session is created is compared with
274 * the Authenticator object passed in to subsequent requests to
275 * get the default session. If both objects are the same, or are
276 * from the same ClassLoader, the request is allowed. Otherwise,
277 * it is denied. <p>
278 *
279 * Note that if the Authenticator object used to create the session
280 * is null, anyone can get the default session by passing in null. <p>
281 *
282 * Note also that the Properties object is used only the first time
283 * this method is called, when a new Session object is created.
284 * Subsequent calls return the Session object that was created by the
285 * first call, and ignore the passed Properties object. Use the
286 * <code>getInstance</code> method to get a new Session object every
287 * time the method is called. <p>
288 *
289 * In JDK 1.2, additional security Permission objects may be used to
290 * control access to the default session.
291 *
292 * @param props Properties object. Used only if a new Session
293 * object is created.<br>
294 * It is expected that the client supplies values
295 * for the properties listed in Appendix A of the
296 * JavaMail spec (particularly mail.store.protocol,
297 * mail.transport.protocol, mail.host, mail.user,
298 * and mail.from) as the defaults are unlikely to
299 * work in all cases.
300 * @param authenticator Authenticator object. Used only if a
301 * new Session object is created. Otherwise,
302 * it must match the Authenticator used to create
303 * the Session.
304 * @return the default Session object
305 */
306 public static synchronized Session getDefaultInstance(Properties props,
307 Authenticator authenticator) {
308 if (defaultSession == null)
309 defaultSession = new Session(props, authenticator);
310 else {
311 // have to check whether caller is allowed to see default session
312 if (defaultSession.authenticator == authenticator)
313 ; // either same object or both null, either way OK
314 else if (defaultSession.authenticator != null &&
315 authenticator != null &&
316 defaultSession.authenticator.getClass().getClassLoader() ==
317 authenticator.getClass().getClassLoader())
318 ; // both objects came from the same class loader, OK
319 else
320 // anything else is not allowed
321 throw new SecurityException("Access to default session denied");
322 }
323
324 return defaultSession;
325 }
326
327 /**
328 * Get the default Session object. If a default has not yet been
329 * setup, a new Session object is created and installed as the
330 * default. <p>
331 *
332 * Note that a default session created with no Authenticator is
333 * available to all code executing in the same Java virtual
334 * machine, and the session can contain security sensitive
335 * information such as user names and passwords.
336 *
337 * @param props Properties object. Used only if a new Session
338 * object is created.<br>
339 * It is expected that the client supplies values
340 * for the properties listed in Appendix A of the
341 * JavaMail spec (particularly mail.store.protocol,
342 * mail.transport.protocol, mail.host, mail.user,
343 * and mail.from) as the defaults are unlikely to
344 * work in all cases.
345 * @return the default Session object
346 * @since JavaMail 1.2
347 */
348 public static Session getDefaultInstance(Properties props) {
349 return getDefaultInstance(props, null);
350 }
351
352 /**
353 * Set the debug setting for this Session.
354 * <p>
355 * Since the debug setting can be turned on only after the Session
356 * has been created, to turn on debugging in the Session
357 * constructor, set the property <code>mail.debug</code> in the
358 * Properties object passed in to the constructor to true. The
359 * value of the <code>mail.debug</code> property is used to
360 * initialize the per-Session debugging flag. Subsequent calls to
361 * the <code>setDebug</code> method manipulate the per-Session
362 * debugging flag and have no affect on the <code>mail.debug</code>
363 * property.
364 *
365 * @param debug Debug setting
366 */
367 public synchronized void setDebug(boolean debug) {
368 this.debug = debug;
369 if (debug)
370 pr("DEBUG: setDebug: JavaMail version " + Version.version);
371 }
372
373 /**
374 * Get the debug setting for this Session.
375 *
376 * @return current debug setting
377 */
378 public synchronized boolean getDebug() {
379 return debug;
380 }
381
382 /**
383 * Set the stream to be used for debugging output for this session.
384 * If <code>out</code> is null, <code>System.out</code> will be used.
385 * Note that debugging output that occurs before any session is created,
386 * as a result of setting the <code>mail.debug</code> system property,
387 * will always be sent to <code>System.out</code>.
388 *
389 * @param out the PrintStream to use for debugging output
390 * @since JavaMail 1.3
391 */
392 public synchronized void setDebugOut(PrintStream out) {
393 this.out = out;
394 }
395
396 /**
397 * Returns the stream to be used for debugging output. If no stream
398 * has been set, <code>System.out</code> is returned.
399 *
400 * @return the PrintStream to use for debugging output
401 * @since JavaMail 1.3
402 */
403 public synchronized PrintStream getDebugOut() {
404 if (out == null)
405 return System.out;
406 else
407 return out;
408 }
409
410 /**
411 * This method returns an array of all the implementations installed
412 * via the javamail.[default.]providers files that can
413 * be loaded using the ClassLoader available to this application.
414 *
415 * @return Array of configured providers
416 */
417 public synchronized Provider[] getProviders() {
418 Provider[] _providers = new Provider[providers.size()];
419 providers.copyInto(_providers);
420 return _providers;
421 }
422
423 /**
424 * Returns the default Provider for the protocol
425 * specified. Checks mail.<protocol>.class property
426 * first and if it exists, returns the Provider
427 * associated with this implementation. If it doesn't exist,
428 * returns the Provider that appeared first in the
429 * configuration files. If an implementation for the protocol
430 * isn't found, throws NoSuchProviderException
431 *
432 * @param protocol Configured protocol (i.e. smtp, imap, etc)
433 * @return Currently configured Provider for the specified protocol
434 * @exception NoSuchProviderException If a provider for the given
435 * protocol is not found.
436 */
437 public synchronized Provider getProvider(String protocol)
438 throws NoSuchProviderException {
439
440 if (protocol == null || protocol.length() <= 0) {
441 throw new NoSuchProviderException("Invalid protocol: null");
442 }
443
444 Provider _provider = null;
445
446 // check if the mail.<protocol>.class property exists
447 String _className = props.getProperty("mail."+protocol+".class");
448 if (_className != null) {
449 if (debug) {
450 pr("DEBUG: mail."+protocol+
451 ".class property exists and points to " +
452 _className);
453 }
454 _provider = (Provider)providersByClassName.get(_className);
455 }
456
457 if (_provider != null) {
458 return _provider;
459 } else {
460 // returning currently default protocol in providersByProtocol
461 _provider = (Provider)providersByProtocol.get(protocol);
462 }
463
464 if (_provider == null) {
465 throw new NoSuchProviderException("No provider for " + protocol);
466 } else {
467 if (debug) {
468 pr("DEBUG: getProvider() returning " +
469 _provider.toString());
470 }
471 return _provider;
472 }
473 }
474
475 /**
476 * Set the passed Provider to be the default implementation
477 * for the protocol in Provider.protocol overriding any previous values.
478 *
479 * @param provider Currently configured Provider which will be
480 * set as the default for the protocol
481 * @exception NoSuchProviderException If the provider passed in
482 * is invalid.
483 */
484 public synchronized void setProvider(Provider provider)
485 throws NoSuchProviderException {
486 if (provider == null) {
487 throw new NoSuchProviderException("Can't set null provider");
488 }
489 providersByProtocol.put(provider.getProtocol(), provider);
490 props.put("mail." + provider.getProtocol() + ".class",
491 provider.getClassName());
492 }
493
494
495 /**
496 * Get a Store object that implements this user's desired Store
497 * protocol. The <code>mail.store.protocol</code> property specifies the
498 * desired protocol. If an appropriate Store object is not obtained,
499 * NoSuchProviderException is thrown
500 *
501 * @return a Store object
502 * @exception NoSuchProviderException If a provider for the given
503 * protocol is not found.
504 */
505 public Store getStore() throws NoSuchProviderException {
506 return getStore(getProperty("mail.store.protocol"));
507 }
508
509 /**
510 * Get a Store object that implements the specified protocol. If an
511 * appropriate Store object cannot be obtained,
512 * NoSuchProviderException is thrown.
513 *
514 * @param protocol
515 * @return a Store object
516 * @exception NoSuchProviderException If a provider for the given
517 * protocol is not found.
518 */
519 public Store getStore(String protocol) throws NoSuchProviderException {
520 return getStore(new URLName(protocol, null, -1, null, null, null));
521 }
522
523
524 /**
525 * Get a Store object for the given URLName. If the requested Store
526 * object cannot be obtained, NoSuchProviderException is thrown.
527 *
528 * The "scheme" part of the URL string (Refer RFC 1738) is used
529 * to locate the Store protocol. <p>
530 *
531 * @param url URLName that represents the desired Store
532 * @return a closed Store object
533 * @see #getFolder(URLName)
534 * @see javax.mail.URLName
535 * @exception NoSuchProviderException If a provider for the given
536 * URLName is not found.
537 */
538 public Store getStore(URLName url) throws NoSuchProviderException {
539 String protocol = url.getProtocol();
540 Provider p = getProvider(protocol);
541 return getStore(p, url);
542 }
543
544 /**
545 * Get an instance of the store specified by Provider. Instantiates
546 * the store and returns it.
547 *
548 * @param provider Store Provider that will be instantiated
549 * @return Instantiated Store
550 * @exception NoSuchProviderException If a provider for the given
551 * Provider is not found.
552 */
553 public Store getStore(Provider provider) throws NoSuchProviderException {
554 return getStore(provider, null);
555 }
556
557
558 /**
559 * Get an instance of the store specified by Provider. If the URLName
560 * is not null, uses it, otherwise creates a new one. Instantiates
561 * the store and returns it. This is a private method used by
562 * getStore(Provider) and getStore(URLName)
563 *
564 * @param provider Store Provider that will be instantiated
565 * @param url URLName used to instantiate the Store
566 * @return Instantiated Store
567 * @exception NoSuchProviderException If a provider for the given
568 * Provider/URLName is not found.
569 */
570 private Store getStore(Provider provider, URLName url)
571 throws NoSuchProviderException {
572
573 // make sure we have the correct type of provider
574 if (provider == null || provider.getType() != Provider.Type.STORE ) {
575 throw new NoSuchProviderException("invalid provider");
576 }
577
578 try {
579 return (Store) getService(provider, url);
580 } catch (ClassCastException cce) {
581 throw new NoSuchProviderException("incorrect class");
582 }
583 }
584
585 /**
586 * Get a closed Folder object for the given URLName. If the requested
587 * Folder object cannot be obtained, null is returned. <p>
588 *
589 * The "scheme" part of the URL string (Refer RFC 1738) is used
590 * to locate the Store protocol. The rest of the URL string (that is,
591 * the "schemepart", as per RFC 1738) is used by that Store
592 * in a protocol dependent manner to locate and instantiate the
593 * appropriate Folder object. <p>
594 *
595 * Note that RFC 1738 also specifies the syntax for the
596 * "schemepart" for IP-based protocols (IMAP4, POP3, etc.).
597 * Providers of IP-based mail Stores should implement that
598 * syntax for referring to Folders. <p>
599 *
600 * @param url URLName that represents the desired folder
601 * @return Folder
602 * @see #getStore(URLName)
603 * @see javax.mail.URLName
604 * @exception NoSuchProviderException If a provider for the given
605 * URLName is not found.
606 * @exception MessagingException if the Folder could not be
607 * located or created.
608 */
609 public Folder getFolder(URLName url)
610 throws MessagingException {
611 // First get the Store
612 Store store = getStore(url);
613 store.connect();
614 return store.getFolder(url);
615 }
616
617 /**
618 * Get a Transport object that implements this user's desired
619 * Transport protcol. The <code>mail.transport.protocol</code> property
620 * specifies the desired protocol. If an appropriate Transport
621 * object cannot be obtained, MessagingException is thrown.
622 *
623 * @return a Transport object
624 * @exception NoSuchProviderException If the provider is not found.
625 */
626 public Transport getTransport() throws NoSuchProviderException {
627 return getTransport(getProperty("mail.transport.protocol"));
628 }
629
630 /**
631 * Get a Transport object that implements the specified protocol.
632 * If an appropriate Transport object cannot be obtained, null is
633 * returned.
634 *
635 * @return a Transport object
636 * @exception NoSuchProviderException If provider for the given
637 * protocol is not found.
638 */
639 public Transport getTransport(String protocol)
640 throws NoSuchProviderException {
641 return getTransport(new URLName(protocol, null, -1, null, null, null));
642 }
643
644
645 /**
646 * Get a Transport object for the given URLName. If the requested
647 * Transport object cannot be obtained, NoSuchProviderException is thrown.
648 *
649 * The "scheme" part of the URL string (Refer RFC 1738) is used
650 * to locate the Transport protocol. <p>
651 *
652 * @param url URLName that represents the desired Transport
653 * @return a closed Transport object
654 * @see javax.mail.URLName
655 * @exception NoSuchProviderException If a provider for the given
656 * URLName is not found.
657 */
658 public Transport getTransport(URLName url) throws NoSuchProviderException {
659 String protocol = url.getProtocol();
660 Provider p = getProvider(protocol);
661 return getTransport(p, url);
662 }
663
664 /**
665 * Get an instance of the transport specified in the Provider. Instantiates
666 * the transport and returns it.
667 *
668 * @param provider Transport Provider that will be instantiated
669 * @return Instantiated Transport
670 * @exception NoSuchProviderException If provider for the given
671 * provider is not found.
672 */
673 public Transport getTransport(Provider provider)
674 throws NoSuchProviderException {
675 return getTransport(provider, null);
676 }
677
678 /**
679 * Get a Transport object that can transport a Message to the
680 * specified address type.
681 *
682 * @param address
683 * @return A Transport object
684 * @see javax.mail.Address
685 * @exception NoSuchProviderException If provider for the
686 * Address type is not found
687 */
688 public Transport getTransport(Address address)
689 throws NoSuchProviderException {
690
691 String transportProtocol = (String)addressMap.get(address.getType());
692 if (transportProtocol == null) {
693 throw new NoSuchProviderException("No provider for Address type: "+
694 address.getType());
695 } else {
696 return getTransport(transportProtocol);
697 }
698 }
699
700 /**
701 * Get a Transport object using the given provider and urlname.
702 *
703 * @param provider the provider to use
704 * @param url urlname to use (can be null)
705 * @return A Transport object
706 * @exception NoSuchProviderException If no provider or the provider
707 * was the wrong class.
708 */
709
710 private Transport getTransport(Provider provider, URLName url)
711 throws NoSuchProviderException {
712 // make sure we have the correct type of provider
713 if (provider == null || provider.getType() != Provider.Type.TRANSPORT) {
714 throw new NoSuchProviderException("invalid provider");
715 }
716
717 try {
718 return (Transport) getService(provider, url);
719 } catch (ClassCastException cce) {
720 throw new NoSuchProviderException("incorrect class");
721 }
722 }
723
724 /**
725 * Get a Service object. Needs a provider object, but will
726 * create a URLName if needed. It attempts to instantiate
727 * the correct class.
728 *
729 * @param provider which provider to use
730 * @param url which URLName to use (can be null)
731 * @exception NoSuchProviderException thrown when the class cannot be
732 * found or when it does not have the correct constructor
733 * (Session, URLName), or if it is not derived from
734 * Service.
735 */
736 private Object getService(Provider provider, URLName url)
737 throws NoSuchProviderException {
738 // need a provider and url
739 if (provider == null) {
740 throw new NoSuchProviderException("null");
741 }
742
743 // create a url if needed
744 if (url == null) {
745 url = new URLName(provider.getProtocol(), null, -1,
746 null, null, null);
747 }
748
749 Object service = null;
750
751 // get the ClassLoader associated with the Authenticator
752 ClassLoader cl;
753 if (authenticator != null)
754 cl = authenticator.getClass().getClassLoader();
755 else
756 cl = this.getClass().getClassLoader();
757
758 // now load the class
759 Class serviceClass = null;
760 try {
761 // First try the "application's" class loader.
762 ClassLoader ccl = getContextClassLoader();
763 if (ccl != null)
764 try {
765 serviceClass = ccl.loadClass(provider.getClassName());
766 } catch (ClassNotFoundException ex) {
767 // ignore it
768 }
769 if (serviceClass == null)
770 serviceClass = cl.loadClass(provider.getClassName());
771 } catch (Exception ex1) {
772 // That didn't work, now try the "system" class loader.
773 // (Need both of these because JDK 1.1 class loaders
774 // may not delegate to their parent class loader.)
775 try {
776 serviceClass = Class.forName(provider.getClassName());
777 } catch (Exception ex) {
778 // Nothing worked, give up.
779 if (debug) ex.printStackTrace(getDebugOut());
780 throw new NoSuchProviderException(provider.getProtocol());
781 }
782 }
783
784 // construct an instance of the class
785 try {
786 Class[] c = {javax.mail.Session.class, javax.mail.URLName.class};
787 Constructor cons = serviceClass.getConstructor(c);
788
789 Object[] o = {this, url};
790 service = cons.newInstance(o);
791
792 } catch (Exception ex) {
793 if (debug) ex.printStackTrace(getDebugOut());
794 throw new NoSuchProviderException(provider.getProtocol());
795 }
796
797 return service;
798 }
799
800 /**
801 * Save a PasswordAuthentication for this (store or transport) URLName.
802 * If pw is null the entry corresponding to the URLName is removed.
803 * <p>
804 * This is normally used only by the store or transport implementations
805 * to allow authentication information to be shared among multiple
806 * uses of a session.
807 */
808 public void setPasswordAuthentication(URLName url,
809 PasswordAuthentication pw) {
810 if (pw == null)
811 authTable.remove(url);
812 else
813 authTable.put(url, pw);
814 }
815
816 /**
817 * Return any saved PasswordAuthentication for this (store or transport)
818 * URLName. Normally used only by store or transport implementations.
819 *
820 * @return the PasswordAuthentication corresponding to the URLName
821 */
822 public PasswordAuthentication getPasswordAuthentication(URLName url) {
823 return (PasswordAuthentication)authTable.get(url);
824 }
825
826 /**
827 * Call back to the application to get the needed user name and password.
828 * The application should put up a dialog something like:
829 * <p> <pre>
830 * Connecting to <protocol> mail service on host <addr>, port <port>.
831 * <prompt>
832 *
833 * User Name: <defaultUserName>
834 * Password:
835 * </pre>
836 *
837 * @param addr InetAddress of the host. may be null.
838 * @param protocol protocol scheme (e.g. imap, pop3, etc.)
839 * @param prompt any additional String to show as part of
840 * the prompt; may be null.
841 * @param defaultUserName the default username. may be null.
842 * @return the authentication which was collected by the authenticator;
843 * may be null.
844 */
845 public PasswordAuthentication requestPasswordAuthentication(
846 InetAddress addr, int port,
847 String protocol, String prompt, String defaultUserName) {
848
849 if (authenticator != null) {
850 return authenticator.requestPasswordAuthentication(
851 addr, port, protocol, prompt, defaultUserName);
852 } else {
853 return null;
854 }
855 }
856
857 /**
858 * Returns the Properties object associated with this Session
859 *
860 * @return Properties object
861 */
862 public Properties getProperties() {
863 return props;
864 }
865
866 /**
867 * Returns the value of the specified property. Returns null
868 * if this property does not exist.
869 *
870 * @return String that is the property value
871 */
872 public String getProperty(String name) {
873 return props.getProperty(name);
874 }
875
876 /**
877 * Load the protocol providers config files.
878 */
879 private void loadProviders(Class cl) {
880 StreamLoader loader = new StreamLoader() {
881 public void load(InputStream is) throws IOException {
882 loadProvidersFromStream(is);
883 }
884 };
885
886 // load system-wide javamail.providers from the <java.home>/lib dir
887 try {
888 String res = System.getProperty("java.home") +
889 File.separator + "lib" +
890 File.separator + "javamail.providers";
891 loadFile(res, loader);
892 } catch (SecurityException sex) {
893 if (debug)
894 pr("DEBUG: can't get java.home: " + sex);
895 }
896
897 // load the META-INF/javamail.providers file supplied by an application
898 loadAllResources("META-INF/javamail.providers", cl, loader);
899
900 // load default META-INF/javamail.default.providers from mail.jar file
901 loadResource("/META-INF/javamail.default.providers", cl, loader);
902
903 if (providers.size() == 0) {
904 if (debug)
905 pr("DEBUG: failed to load any providers, using defaults");
906 // failed to load any providers, initialize with our defaults
907 addProvider(new Provider(Provider.Type.STORE,
908 "imap", "com.sun.mail.imap.IMAPStore",
909 "Sun Microsystems, Inc.", Version.version));
910 addProvider(new Provider(Provider.Type.STORE,
911 "imaps", "com.sun.mail.imap.IMAPSSLStore",
912 "Sun Microsystems, Inc.", Version.version));
913 addProvider(new Provider(Provider.Type.STORE,
914 "pop3", "com.sun.mail.pop3.POP3Store",
915 "Sun Microsystems, Inc.", Version.version));
916 addProvider(new Provider(Provider.Type.STORE,
917 "pop3s", "com.sun.mail.pop3.POP3SSLStore",
918 "Sun Microsystems, Inc.", Version.version));
919 addProvider(new Provider(Provider.Type.TRANSPORT,
920 "smtp", "com.sun.mail.smtp.SMTPTransport",
921 "Sun Microsystems, Inc.", Version.version));
922 addProvider(new Provider(Provider.Type.TRANSPORT,
923 "smtps", "com.sun.mail.smtp.SMTPSSLTransport",
924 "Sun Microsystems, Inc.", Version.version));
925 }
926
927 if (debug) {
928 // dump the output of the tables for debugging
929 pr("DEBUG: Tables of loaded providers");
930 pr("DEBUG: Providers Listed By Class Name: " +
931 providersByClassName.toString());
932 pr("DEBUG: Providers Listed By Protocol: " +
933 providersByProtocol.toString());
934 }
935 }
936
937 private void loadProvidersFromStream(InputStream is)
938 throws IOException {
939 if (is != null) {
940 LineInputStream lis = new LineInputStream(is);
941 String currLine;
942
943 // load and process one line at a time using LineInputStream
944 while ((currLine = lis.readLine()) != null) {
945
946 if (currLine.startsWith("#"))
947 continue;
948 Provider.Type type = null;
949 String protocol = null, className = null;
950 String vendor = null, version = null;
951
952 // separate line into key-value tuples
953 StringTokenizer tuples = new StringTokenizer(currLine,";");
954 while (tuples.hasMoreTokens()) {
955 String currTuple = tuples.nextToken().trim();
956
957 // set the value of each attribute based on its key
958 int sep = currTuple.indexOf("=");
959 if (currTuple.startsWith("protocol=")) {
960 protocol = currTuple.substring(sep+1);
961 } else if (currTuple.startsWith("type=")) {
962 String strType = currTuple.substring(sep+1);
963 if (strType.equalsIgnoreCase("store")) {
964 type = Provider.Type.STORE;
965 } else if (strType.equalsIgnoreCase("transport")) {
966 type = Provider.Type.TRANSPORT;
967 }
968 } else if (currTuple.startsWith("class=")) {
969 className = currTuple.substring(sep+1);
970 } else if (currTuple.startsWith("vendor=")) {
971 vendor = currTuple.substring(sep+1);
972 } else if (currTuple.startsWith("version=")) {
973 version = currTuple.substring(sep+1);
974 }
975 }
976
977 // check if a valid Provider; else, continue
978 if (type == null || protocol == null || className == null
979 || protocol.length() <= 0 || className.length() <= 0) {
980
981 if (debug)
982 pr("DEBUG: Bad provider entry: " + currLine);
983 continue;
984 }
985 Provider provider = new Provider(type, protocol, className,
986 vendor, version);
987
988 // add the newly-created Provider to the lookup tables
989 addProvider(provider);
990 }
991 }
992 }
993
994 /**
995 * Add a provider to the session.
996 *
997 * @param provider the provider to add
998 * @since JavaMail 1.4
999 */
1000 public synchronized void addProvider(Provider provider) {
1001 providers.addElement(provider);
1002 providersByClassName.put(provider.getClassName(), provider);
1003 if (!providersByProtocol.containsKey(provider.getProtocol()))
1004 providersByProtocol.put(provider.getProtocol(), provider);
1005 }
1006
1007 // load maps in reverse order of preference so that the preferred
1008 // map is loaded last since its entries will override the previous ones
1009 private void loadAddressMap(Class cl) {
1010 StreamLoader loader = new StreamLoader() {
1011 public void load(InputStream is) throws IOException {
1012 addressMap.load(is);
1013 }
1014 };
1015
1016 // load default META-INF/javamail.default.address.map from mail.jar
1017 loadResource("/META-INF/javamail.default.address.map", cl, loader);
1018
1019 // load the META-INF/javamail.address.map file supplied by an app
1020 loadAllResources("META-INF/javamail.address.map", cl, loader);
1021
1022 // load system-wide javamail.address.map from the <java.home>/lib dir
1023 try {
1024 String res = System.getProperty("java.home") +
1025 File.separator + "lib" +
1026 File.separator + "javamail.address.map";
1027 loadFile(res, loader);
1028 } catch (SecurityException sex) {
1029 if (debug)
1030 pr("DEBUG: can't get java.home: " + sex);
1031 }
1032
1033 if (addressMap.isEmpty()) {
1034 if (debug)
1035 pr("DEBUG: failed to load address map, using defaults");
1036 addressMap.put("rfc822", "smtp");
1037 }
1038 }
1039
1040 /**
1041 * Set the default transport protocol to use for addresses of
1042 * the specified type. Normally the default is set by the
1043 * <code>javamail.default.address.map</code> or
1044 * <code>javamail.address.map</code> files or resources.
1045 *
1046 * @param addresstype type of address
1047 * @param protocol name of protocol
1048 * @see #getTransport(Address)
1049 * @since JavaMail 1.4
1050 */
1051 public synchronized void setProtocolForAddress(String addresstype,
1052 String protocol) {
1053 if (protocol == null)
1054 addressMap.remove(addresstype);
1055 else
1056 addressMap.put(addresstype, protocol);
1057 }
1058
1059 /**
1060 * Load from the named file.
1061 */
1062 private void loadFile(String name, StreamLoader loader) {
1063 InputStream clis = null;
1064 try {
1065 clis = new BufferedInputStream(new FileInputStream(name));
1066 loader.load(clis);
1067 if (debug)
1068 pr("DEBUG: successfully loaded file: " + name);
1069 } catch (IOException e) {
1070 if (debug) {
1071 pr("DEBUG: not loading file: " + name);
1072 pr("DEBUG: " + e);
1073 }
1074 } catch (SecurityException sex) {
1075 if (debug) {
1076 pr("DEBUG: not loading file: " + name);
1077 pr("DEBUG: " + sex);
1078 }
1079 } finally {
1080 try {
1081 if (clis != null)
1082 clis.close();
1083 } catch (IOException ex) { } // ignore it
1084 }
1085 }
1086
1087 /**
1088 * Load from the named resource.
1089 */
1090 private void loadResource(String name, Class cl, StreamLoader loader) {
1091 InputStream clis = null;
1092 try {
1093 clis = getResourceAsStream(cl, name);
1094 if (clis != null) {
1095 loader.load(clis);
1096 if (debug)
1097 pr("DEBUG: successfully loaded resource: " + name);
1098 } else {
1099 if (debug)
1100 pr("DEBUG: not loading resource: " + name);
1101 }
1102 } catch (IOException e) {
1103 if (debug)
1104 pr("DEBUG: " + e);
1105 } catch (SecurityException sex) {
1106 if (debug)
1107 pr("DEBUG: " + sex);
1108 } finally {
1109 try {
1110 if (clis != null)
1111 clis.close();
1112 } catch (IOException ex) { } // ignore it
1113 }
1114 }
1115
1116 /**
1117 * Load all of the named resource.
1118 */
1119 private void loadAllResources(String name, Class cl, StreamLoader loader) {
1120 boolean anyLoaded = false;
1121 try {
1122 URL[] urls;
1123 ClassLoader cld = null;
1124 // First try the "application's" class loader.
1125 cld = getContextClassLoader();
1126 if (cld == null)
1127 cld = cl.getClassLoader();
1128 if (cld != null)
1129 urls = getResources(cld, name);
1130 else
1131 urls = getSystemResources(name);
1132 if (urls != null) {
1133 for (int i = 0; i < urls.length; i++) {
1134 URL url = urls[i];
1135 InputStream clis = null;
1136 if (debug)
1137 pr("DEBUG: URL " + url);
1138 try {
1139 clis = openStream(url);
1140 if (clis != null) {
1141 loader.load(clis);
1142 anyLoaded = true;
1143 if (debug)
1144 pr("DEBUG: successfully loaded resource: " +
1145 url);
1146 } else {
1147 if (debug)
1148 pr("DEBUG: not loading resource: " + url);
1149 }
1150 } catch (IOException ioex) {
1151 if (debug)
1152 pr("DEBUG: " + ioex);
1153 } catch (SecurityException sex) {
1154 if (debug)
1155 pr("DEBUG: " + sex);
1156 } finally {
1157 try {
1158 if (clis != null)
1159 clis.close();
1160 } catch (IOException cex) { }
1161 }
1162 }
1163 }
1164 } catch (Exception ex) {
1165 if (debug)
1166 pr("DEBUG: " + ex);
1167 }
1168
1169 // if failed to load anything, fall back to old technique, just in case
1170 if (!anyLoaded) {
1171 if (debug)
1172 pr("DEBUG: !anyLoaded");
1173 loadResource("/" + name, cl, loader);
1174 }
1175 }
1176
1177 private void pr(String str) {
1178 getDebugOut().println(str);
1179 }
1180
1181 /*
1182 * Following are security related methods that work on JDK 1.2 or newer.
1183 */
1184
1185 private static ClassLoader getContextClassLoader() {
1186 return (ClassLoader)
1187 AccessController.doPrivileged(new PrivilegedAction() {
1188 public Object run() {
1189 ClassLoader cl = null;
1190 try {
1191 cl = Thread.currentThread().getContextClassLoader();
1192 } catch (SecurityException ex) { }
1193 return cl;
1194 }
1195 });
1196 }
1197
1198 private static InputStream getResourceAsStream(final Class c,
1199 final String name) throws IOException {
1200 try {
1201 return (InputStream)
1202 AccessController.doPrivileged(new PrivilegedExceptionAction() {
1203 public Object run() throws IOException {
1204 return c.getResourceAsStream(name);
1205 }
1206 });
1207 } catch (PrivilegedActionException e) {
1208 throw (IOException)e.getException();
1209 }
1210 }
1211
1212 private static URL[] getResources(final ClassLoader cl, final String name) {
1213 return (URL[])
1214 AccessController.doPrivileged(new PrivilegedAction() {
1215 public Object run() {
1216 URL[] ret = null;
1217 try {
1218 Vector v = new Vector();
1219 Enumeration e = cl.getResources(name);
1220 while (e != null && e.hasMoreElements()) {
1221 URL url = (URL)e.nextElement();
1222 if (url != null)
1223 v.addElement(url);
1224 }
1225 if (v.size() > 0) {
1226 ret = new URL[v.size()];
1227 v.copyInto(ret);
1228 }
1229 } catch (IOException ioex) {
1230 } catch (SecurityException ex) { }
1231 return ret;
1232 }
1233 });
1234 }
1235
1236 private static URL[] getSystemResources(final String name) {
1237 return (URL[])
1238 AccessController.doPrivileged(new PrivilegedAction() {
1239 public Object run() {
1240 URL[] ret = null;
1241 try {
1242 Vector v = new Vector();
1243 Enumeration e = ClassLoader.getSystemResources(name);
1244 while (e != null && e.hasMoreElements()) {
1245 URL url = (URL)e.nextElement();
1246 if (url != null)
1247 v.addElement(url);
1248 }
1249 if (v.size() > 0) {
1250 ret = new URL[v.size()];
1251 v.copyInto(ret);
1252 }
1253 } catch (IOException ioex) {
1254 } catch (SecurityException ex) { }
1255 return ret;
1256 }
1257 });
1258 }
1259
1260 private static InputStream openStream(final URL url) throws IOException {
1261 try {
1262 return (InputStream)
1263 AccessController.doPrivileged(new PrivilegedExceptionAction() {
1264 public Object run() throws IOException {
1265 return url.openStream();
1266 }
1267 });
1268 } catch (PrivilegedActionException e) {
1269 throw (IOException)e.getException();
1270 }
1271 }
1272 }
1273
1274 /**
1275 * Support interface to generalize
1276 * code that loads resources from stream.
1277 */
1278 interface StreamLoader {
1279 public void load(InputStream is) throws IOException;
1280 }