1. /*
  2. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  3. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  4. */
  5. package javax.mail;
  6. import java.lang.*;
  7. import java.lang.reflect.*;
  8. import java.io.*;
  9. import java.net.*;
  10. import java.util.Enumeration;
  11. import java.util.Hashtable;
  12. import java.util.Properties;
  13. import java.util.StringTokenizer;
  14. import java.util.Vector;
  15. import javax.activation.*;
  16. import com.sun.mail.util.LineInputStream;
  17. /**
  18. * The Session class represents a mail session and is not subclassed.
  19. * It collects together properties and defaults used by the mail API's.
  20. * A single default session can be shared by multiple applications on the
  21. * desktop. Unshared sessions can also be created.
  22. *
  23. * @version 1.47, 00/10/23
  24. * @author John Mani
  25. * @author Bill Shannon
  26. * @author Max Spivak
  27. */
  28. public final class Session {
  29. private Properties props;
  30. private Authenticator authenticator;
  31. private Hashtable authTable = new Hashtable();
  32. private boolean debug = false;
  33. private Vector providers = new Vector();
  34. private Hashtable providersByProtocol = new Hashtable();
  35. private Hashtable providersByClassName = new Hashtable();
  36. private Properties addressMap = new Properties(); // maps type to protocol
  37. private static Method getResources = null;
  38. private static Method getSystemResources = null;
  39. static {
  40. try {
  41. Class c = java.lang.ClassLoader.class;
  42. // assume both succeed or both fail
  43. getResources = c.getMethod("getResources",
  44. new Class[] { String.class });
  45. getSystemResources = c.getMethod("getSystemResources",
  46. new Class[] { String.class });
  47. } catch (Throwable t) { } // ignore any errors
  48. }
  49. // The default session.
  50. private static Session defaultSession = null;
  51. // Constructor is not public
  52. private Session(Properties props, Authenticator authenticator) {
  53. this.props = props;
  54. this.authenticator = authenticator;
  55. if (Boolean.valueOf(props.getProperty("mail.debug")).booleanValue())
  56. debug = true;
  57. // get the Class associated with the Authenticator
  58. Class cl;
  59. if (authenticator != null)
  60. cl = authenticator.getClass();
  61. else
  62. cl = this.getClass();
  63. // load the resources
  64. loadProviders(cl);
  65. loadAddressMap(cl);
  66. }
  67. /**
  68. * Get a new Session object.
  69. *
  70. * @param props Properties object that hold relevant properties.<br>
  71. * It is expected that the client supplies values
  72. * for the properties listed in Appendix A of the
  73. * JavaMail spec (particularly mail.store.protocol,
  74. * mail.transport.protocol, mail.host, mail.user,
  75. * and mail.from) as the defaults are unlikely to
  76. * work in all cases.
  77. * @param authenticator Authenticator object used to call back to
  78. * the application when a user name and password is
  79. * needed.
  80. * @return a new Session object
  81. * @see javax.mail.Authenticator
  82. */
  83. public static Session getInstance(Properties props,
  84. Authenticator authenticator) {
  85. return new Session(props, authenticator);
  86. }
  87. /**
  88. * Get a new Session object.
  89. *
  90. * @param props Properties object that hold relevant properties.<br>
  91. * It is expected that the client supplies values
  92. * for the properties listed in Appendix A of the
  93. * JavaMail spec (particularly mail.store.protocol,
  94. * mail.transport.protocol, mail.host, mail.user,
  95. * and mail.from) as the defaults are unlikely to
  96. * work in all cases.
  97. * @return a new Session object
  98. * @since JavaMail 1.2
  99. */
  100. public static Session getInstance(Properties props) {
  101. return new Session(props, null);
  102. }
  103. /**
  104. * Get the default Session object. If a default has not yet been
  105. * setup, a new Session object is created and installed as the
  106. * default. <p>
  107. *
  108. * Since the default session is potentially available to all
  109. * code executing in the same Java virtual machine, and the session
  110. * can contain security sensitive information such as user names
  111. * and passwords, access to the default session is restricted.
  112. * The Authenticator object, which must be created by the caller,
  113. * is used indirectly to check access permission. The Authenticator
  114. * object passed in when the session is created is compared with
  115. * the Authenticator object passed in to subsequent requests to
  116. * get the default session. If both objects are the same, or are
  117. * from the same ClassLoader, the request is allowed. Otherwise,
  118. * it is denied. <p>
  119. *
  120. * Note that if the Authenticator object used to create the session
  121. * is null, anyone can get the default session by passing in null. <p>
  122. *
  123. * In JDK 1.2, additional security Permission objects may be used to
  124. * control access to the default session.
  125. *
  126. * @param props Properties object. Used only if a new Session
  127. * object is created.<br>
  128. * It is expected that the client supplies values
  129. * for the properties listed in Appendix A of the
  130. * JavaMail spec (particularly mail.store.protocol,
  131. * mail.transport.protocol, mail.host, mail.user,
  132. * and mail.from) as the defaults are unlikely to
  133. * work in all cases.
  134. * @param authenticator Authenticator object. Used only if a
  135. * new Session object is created. Otherwise,
  136. * it must match the Authenticator used to create
  137. * the Session.
  138. * @return the default Session object
  139. */
  140. public static Session getDefaultInstance(Properties props,
  141. Authenticator authenticator) {
  142. if (defaultSession == null)
  143. defaultSession = new Session(props, authenticator);
  144. else {
  145. // have to check whether caller is allowed to see default session
  146. if (defaultSession.authenticator == authenticator)
  147. ; // either same object or both null, either way OK
  148. else if (defaultSession.authenticator != null &&
  149. authenticator != null &&
  150. defaultSession.authenticator.getClass().getClassLoader() ==
  151. authenticator.getClass().getClassLoader())
  152. ; // both objects came from the same class loader, OK
  153. else
  154. // anything else is not allowed
  155. throw new SecurityException("Access to default session denied");
  156. }
  157. return defaultSession;
  158. }
  159. /**
  160. * Get the default Session object. If a default has not yet been
  161. * setup, a new Session object is created and installed as the
  162. * default. <p>
  163. *
  164. * Note that a default session created with no Authenticator is
  165. * available to all code executing in the same Java virtual
  166. * machine, and the session can contain security sensitive
  167. * information such as user names and passwords.
  168. *
  169. * @param props Properties object. Used only if a new Session
  170. * object is created.<br>
  171. * It is expected that the client supplies values
  172. * for the properties listed in Appendix A of the
  173. * JavaMail spec (particularly mail.store.protocol,
  174. * mail.transport.protocol, mail.host, mail.user,
  175. * and mail.from) as the defaults are unlikely to
  176. * work in all cases.
  177. * @return the default Session object
  178. * @since JavaMail 1.2
  179. */
  180. public static Session getDefaultInstance(Properties props) {
  181. return getDefaultInstance(props, null);
  182. }
  183. /**
  184. * Set the debug setting for this Session.
  185. * <p>
  186. * Since the debug setting can be turned on only after the Session
  187. * has been created, to turn on debugging in the Session
  188. * constructor, set the property <code>mail.debug</code> in the
  189. * Properties object passed in to the constructor to true. The
  190. * value of the <code>mail.debug</code> property is used to
  191. * initialize the per-Session debugging flag. Subsequent calls to
  192. * the <code>setDebug</code> method manipulate the per-Session
  193. * debugging flag and have no affect on the <code>mail.debug</code>
  194. * property.
  195. *
  196. * @param debug Debug setting
  197. */
  198. public void setDebug(boolean debug) {
  199. this.debug = debug;
  200. }
  201. /**
  202. * Get the debug setting for this Session.
  203. *
  204. * @return current debug setting
  205. */
  206. public boolean getDebug() {
  207. return debug;
  208. }
  209. /**
  210. * This method returns an array of all the implementations installed
  211. * via the javamail.[default.]providers files that can
  212. * be loaded using the ClassLoader available to this application.
  213. *
  214. * @return Array of configured providers
  215. */
  216. public Provider[] getProviders() {
  217. Provider[] _providers = new Provider[providers.size()];
  218. providers.copyInto(_providers);
  219. return _providers;
  220. }
  221. /**
  222. * Returns the default Provider for the protocol
  223. * specified. Checks mail.<protocol>.class property
  224. * first and if it exists, returns the Provider
  225. * associated with this implementation. If it doesn't exist,
  226. * returns the Provider that appeared first in the
  227. * configuration files. If an implementation for the protocol
  228. * isn't found, throws NoSuchProviderException
  229. *
  230. * @param protocol Configured protocol (i.e. smtp, imap, etc)
  231. * @return Currently configured Provider for the specified protocol
  232. * @exception NoSuchProviderException If a provider for the given
  233. * protocol is not found.
  234. */
  235. public Provider getProvider(String protocol)
  236. throws NoSuchProviderException {
  237. if (protocol == null || protocol.length() <= 0) {
  238. throw new NoSuchProviderException("Invalid protocol: null");
  239. }
  240. Provider _provider = null;
  241. // check if the mail.<protocol>.class property exists
  242. String _className = props.getProperty("mail."+protocol+".class");
  243. if (_className != null) {
  244. if (debug) {
  245. System.out.println("DEBUG: mail."+protocol+
  246. ".class property exists and points to " +
  247. _className);
  248. }
  249. _provider = (Provider)providersByClassName.get(_className);
  250. }
  251. if (_provider != null) {
  252. return _provider;
  253. } else {
  254. // returning currently default protocol in providersByProtocol
  255. _provider = (Provider)providersByProtocol.get(protocol);
  256. }
  257. if (_provider == null) {
  258. throw new NoSuchProviderException("No provider for " + protocol);
  259. } else {
  260. if (debug) {
  261. System.out.println("\nDEBUG: getProvider() returning " +
  262. _provider.toString());
  263. }
  264. return _provider;
  265. }
  266. }
  267. /**
  268. * Set the passed Provider to be the default implementation
  269. * for the protocol in Provider.protocol overriding any previous values.
  270. *
  271. * @param provider Currently configured Provider which will be
  272. * set as the default for the protocol
  273. * @exception NoSuchProviderException If the provider passed in
  274. * is invalid.
  275. */
  276. public void setProvider(Provider provider) throws NoSuchProviderException {
  277. if (provider == null) {
  278. throw new NoSuchProviderException("Can't set null provider");
  279. }
  280. providersByProtocol.put(provider.getProtocol(), provider);
  281. props.put("mail." + provider.getProtocol() + ".class",
  282. provider.getClassName());
  283. }
  284. /**
  285. * Get a Store object that implements this user's desired Store
  286. * protocol. The <code>mail.store.protocol</code> property specifies the
  287. * desired protocol. If an appropriate Store object is not obtained,
  288. * NoSuchProviderException is thrown
  289. *
  290. * @return a Store object
  291. * @exception NoSuchProviderException If a provider for the given
  292. * protocol is not found.
  293. */
  294. public Store getStore() throws NoSuchProviderException {
  295. return getStore(getProperty("mail.store.protocol"));
  296. }
  297. /**
  298. * Get a Store object that implements the specified protocol. If an
  299. * appropriate Store object cannot be obtained,
  300. * NoSuchProviderException is thrown.
  301. *
  302. * @param protocol
  303. * @return a Store object
  304. * @exception NoSuchProviderException If a provider for the given
  305. * protocol is not found.
  306. */
  307. public Store getStore(String protocol) throws NoSuchProviderException {
  308. return getStore(new URLName(protocol, null, -1, null, null, null));
  309. }
  310. /**
  311. * Get a Store object for the given URLName. If the requested Store
  312. * object cannot be obtained, NoSuchProviderException is thrown.
  313. *
  314. * The "scheme" part of the URL string (Refer RFC 1738) is used
  315. * to locate the Store protocol. <p>
  316. *
  317. * @param url URLName that represents the desired Store
  318. * @return a closed Store object
  319. * @see #getFolder(URLName)
  320. * @see javax.mail.URLName
  321. * @exception NoSuchProviderException If a provider for the given
  322. * URLName is not found.
  323. */
  324. public Store getStore(URLName url) throws NoSuchProviderException {
  325. String protocol = url.getProtocol();
  326. Provider p = getProvider(protocol);
  327. return getStore(p, url);
  328. }
  329. /**
  330. * Get an instance of the store specified by Provider. Instantiates
  331. * the store and returns it.
  332. *
  333. * @param provider Store Provider that will be instantiated
  334. * @return Instantiated Store
  335. * @exception NoSuchProviderException If a provider for the given
  336. * Provider is not found.
  337. */
  338. public Store getStore(Provider provider) throws NoSuchProviderException {
  339. return getStore(provider, null);
  340. }
  341. /**
  342. * Get an instance of the store specified by Provider. If the URLName
  343. * is not null, uses it, otherwise creates a new one. Instantiates
  344. * the store and returns it. This is a private method used by
  345. * getStore(Provider) and getStore(URLName)
  346. *
  347. * @param provider Store Provider that will be instantiated
  348. * @param url URLName used to instantiate the Store
  349. * @return Instantiated Store
  350. * @exception NoSuchProviderException If a provider for the given
  351. * Provider/URLName is not found.
  352. */
  353. private Store getStore(Provider provider, URLName url)
  354. throws NoSuchProviderException {
  355. // make sure we have the correct type of provider
  356. if (provider == null || provider.getType() != Provider.Type.STORE ) {
  357. throw new NoSuchProviderException("invalid provider");
  358. }
  359. try {
  360. return (Store) getService(provider, url);
  361. } catch (ClassCastException cce) {
  362. throw new NoSuchProviderException("incorrect class");
  363. }
  364. }
  365. /**
  366. * Get a closed Folder object for the given URLName. If the requested
  367. * Folder object cannot be obtained, null is returned. <p>
  368. *
  369. * The "scheme" part of the URL string (Refer RFC 1738) is used
  370. * to locate the Store protocol. The rest of the URL string (that is,
  371. * the "schemepart", as per RFC 1738) is used by that Store
  372. * in a protocol dependent manner to locate and instantiate the
  373. * appropriate Folder object. <p>
  374. *
  375. * Note that RFC 1738 also specifies the syntax for the
  376. * "schemepart" for IP-based protocols (IMAP4, POP3, etc.).
  377. * Providers of IP-based mail Stores should implement that
  378. * syntax for referring to Folders. <p>
  379. *
  380. * @param url URLName that represents the desired folder
  381. * @return Folder
  382. * @see #getStore(URLName)
  383. * @see javax.mail.URLName
  384. * @exception NoSuchProviderException If a provider for the given
  385. * URLName is not found.
  386. * @exception MessagingException if the Folder could not be
  387. * located or created.
  388. */
  389. public Folder getFolder(URLName url)
  390. throws MessagingException {
  391. // First get the Store
  392. Store store = getStore(url);
  393. store.connect();
  394. return store.getFolder(url);
  395. }
  396. /**
  397. * Get a Transport object that implements this user's desired
  398. * Transport protcol. The <code>mail.transport.protocol</code> property
  399. * specifies the desired protocol. If an appropriate Transport
  400. * object cannot be obtained, MessagingException is thrown.
  401. *
  402. * @return a Transport object
  403. * @exception NoSuchProviderException If the provider is not found.
  404. */
  405. public Transport getTransport() throws NoSuchProviderException {
  406. return getTransport(getProperty("mail.transport.protocol"));
  407. }
  408. /**
  409. * Get a Transport object that implements the specified protocol.
  410. * If an appropriate Transport object cannot be obtained, null is
  411. * returned.
  412. *
  413. * @return a Transport object
  414. * @exception NoSuchProviderException If provider for the given
  415. * protocol is not found.
  416. */
  417. public Transport getTransport(String protocol)
  418. throws NoSuchProviderException {
  419. return getTransport(new URLName(protocol, null, -1, null, null, null));
  420. }
  421. /**
  422. * Get a Transport object for the given URLName. If the requested
  423. * Transport object cannot be obtained, NoSuchProviderException is thrown.
  424. *
  425. * The "scheme" part of the URL string (Refer RFC 1738) is used
  426. * to locate the Transport protocol. <p>
  427. *
  428. * @param url URLName that represents the desired Transport
  429. * @return a closed Transport object
  430. * @see javax.mail.URLName
  431. * @exception NoSuchProviderException If a provider for the given
  432. * URLName is not found.
  433. */
  434. public Transport getTransport(URLName url) throws NoSuchProviderException {
  435. String protocol = url.getProtocol();
  436. Provider p = getProvider(protocol);
  437. return getTransport(p, url);
  438. }
  439. /**
  440. * Get an instance of the transport specified in the Provider. Instantiates
  441. * the transport and returns it.
  442. *
  443. * @param provider Transport Provider that will be instantiated
  444. * @return Instantiated Transport
  445. * @exception NoSuchProviderException If provider for the given
  446. * provider is not found.
  447. */
  448. public Transport getTransport(Provider provider)
  449. throws NoSuchProviderException {
  450. return getTransport(provider, null);
  451. }
  452. /**
  453. * Get a Transport object that can transport a Message to the
  454. * specified address type.
  455. *
  456. * @param address
  457. * @return A Transport object
  458. * @see javax.mail.Address
  459. * @exception NoSuchProviderException If provider for the
  460. * Address type is not found
  461. */
  462. public Transport getTransport(Address address)
  463. throws NoSuchProviderException {
  464. String transportProtocol = (String)addressMap.get(address.getType());
  465. if (transportProtocol == null) {
  466. throw new NoSuchProviderException("No provider for Address type: "+
  467. address.getType());
  468. } else {
  469. return getTransport(transportProtocol);
  470. }
  471. }
  472. /**
  473. * Get a Transport object using the given provider and urlname.
  474. *
  475. * @param provider the provider to use
  476. * @param url urlname to use (can be null)
  477. * @return A Transport object
  478. * @exception NoSuchProviderException If no provider or the provider
  479. * was the wrong class.
  480. */
  481. private Transport getTransport(Provider provider, URLName url)
  482. throws NoSuchProviderException {
  483. // make sure we have the correct type of provider
  484. if (provider == null || provider.getType() != Provider.Type.TRANSPORT) {
  485. throw new NoSuchProviderException("invalid provider");
  486. }
  487. try {
  488. return (Transport) getService(provider, url);
  489. } catch (ClassCastException cce) {
  490. throw new NoSuchProviderException("incorrect class");
  491. }
  492. }
  493. /**
  494. * Get a Service object. Needs a provider object, but will
  495. * create a URLName if needed. It attempts to instantiate
  496. * the correct class.
  497. *
  498. * @param provider which provider to use
  499. * @param url which URLName to use (can be null)
  500. * @exception NoSuchProviderException thrown when the class cannot be
  501. * found or when it does not have the correct constructor
  502. * (Session, URLName), or if it is not derived from
  503. * Service.
  504. */
  505. private Object getService(Provider provider, URLName url)
  506. throws NoSuchProviderException {
  507. // need a provider and url
  508. if (provider == null) {
  509. throw new NoSuchProviderException("null");
  510. }
  511. // create a url if needed
  512. if (url == null) {
  513. url = new URLName(provider.getProtocol(), null, -1,
  514. null, null, null);
  515. }
  516. Object service = null;
  517. // get the ClassLoader associated with the Authenticator
  518. ClassLoader cl;
  519. if (authenticator != null)
  520. cl = authenticator.getClass().getClassLoader();
  521. else
  522. cl = this.getClass().getClassLoader();
  523. // now load the class
  524. Class serviceClass = null;
  525. try {
  526. // First try the "application's" class loader.
  527. // This should eventually be replaced by
  528. // Thread.currentThread().getContextClassLoader().
  529. serviceClass = cl.loadClass(provider.getClassName());
  530. } catch (Exception ex1) {
  531. // That didn't work, now try the "system" class loader.
  532. // (Need both of these because JDK 1.1 class loaders
  533. // may not delegate to their parent class loader.)
  534. try {
  535. serviceClass = Class.forName(provider.getClassName());
  536. } catch (Exception ex) {
  537. // Nothing worked, give up.
  538. if (debug) ex.printStackTrace();
  539. throw new NoSuchProviderException(provider.getProtocol());
  540. }
  541. }
  542. // construct an instance of the class
  543. try {
  544. Class[] c = {javax.mail.Session.class, javax.mail.URLName.class};
  545. Constructor cons = serviceClass.getConstructor(c);
  546. Object[] o = {this, url};
  547. service = cons.newInstance(o);
  548. } catch (Exception ex) {
  549. if (debug) ex.printStackTrace();
  550. throw new NoSuchProviderException(provider.getProtocol());
  551. }
  552. return service;
  553. }
  554. /**
  555. * Save a PasswordAuthentication for this (store or transport) URLName.
  556. * If pw is null the entry corresponding to the URLName is removed.
  557. * <p>
  558. * This is normally used only by the store or transport implementations
  559. * to allow authentication information to be shared among multiple
  560. * uses of a session.
  561. */
  562. public void setPasswordAuthentication(URLName url,
  563. PasswordAuthentication pw) {
  564. if (pw == null)
  565. authTable.remove(url);
  566. else
  567. authTable.put(url, pw);
  568. }
  569. /**
  570. * Return any saved PasswordAuthentication for this (store or transport)
  571. * URLName. Normally used only by store or transport implementations.
  572. *
  573. * @return the PasswordAuthentication corresponding to the URLName
  574. */
  575. public PasswordAuthentication getPasswordAuthentication(URLName url) {
  576. return (PasswordAuthentication)authTable.get(url);
  577. }
  578. /**
  579. * Call back to the application to get the needed user name and password.
  580. * The application should put up a dialog something like:
  581. * <p> <pre>
  582. * Connecting to <protocol> mail service on host <addr>, port <port>.
  583. * <prompt>
  584. *
  585. * User Name: <defaultUserName>
  586. * Password:
  587. * </pre>
  588. *
  589. * @param addr InetAddress of the host. may be null.
  590. * @param protocol protocol scheme (e.g. imap, pop3, etc.)
  591. * @param prompt any additional String to show as part of
  592. * the prompt; may be null.
  593. * @param defaultUserName the default username. may be null.
  594. * @return the authentication which was collected by the authenticator;
  595. * may be null.
  596. */
  597. public PasswordAuthentication requestPasswordAuthentication(
  598. InetAddress addr, int port,
  599. String protocol, String prompt, String defaultUserName) {
  600. if (authenticator != null) {
  601. return authenticator.requestPasswordAuthentication(
  602. addr, port, protocol, prompt, defaultUserName);
  603. } else {
  604. return null;
  605. }
  606. }
  607. /**
  608. * Returns the Properties object associated with this Session
  609. *
  610. * @return Properties object
  611. */
  612. public Properties getProperties() {
  613. return props;
  614. }
  615. /**
  616. * Returns the value of the specified property. Returns null
  617. * if this property does not exist.
  618. *
  619. * @return String that is the property value
  620. */
  621. public String getProperty(String name) {
  622. return props.getProperty(name);
  623. }
  624. private void loadProviders(Class cl) {
  625. // load system-wide javamail.providers from the <java.home>/lib dir
  626. // since we have an absolute path to the file, use FileInputStream
  627. InputStream javahomeProviderStream = null;
  628. try {
  629. String res = System.getProperty("java.home") +
  630. File.separator + "lib" +
  631. File.separator + "javamail.providers";
  632. javahomeProviderStream =
  633. new BufferedInputStream(new FileInputStream(res));
  634. if (javahomeProviderStream != null) {
  635. loadProvidersFromStream(javahomeProviderStream);
  636. javahomeProviderStream.close();
  637. if (debug)
  638. pr("DEBUG: loaded providers in <java.home>/lib");
  639. } else {
  640. if (debug)
  641. pr("DEBUG: not loading system providers in <java.home>/lib");
  642. }
  643. } catch (FileNotFoundException fex) { /*ignore: don't load resource*/
  644. if (debug)
  645. pr("DEBUG: not loading system providers in <java.home>/lib");
  646. } catch (IOException ioex) { /*ignore: don't load resource*/
  647. if (debug)
  648. pr("DEBUG: " + ioex.getMessage());
  649. } catch (SecurityException sex) { /*ignore: don't load resource*/
  650. if (debug)
  651. pr("DEBUG: not loading system providers in <java.home>/lib");
  652. }
  653. // load the META-INF/javamail.providers file supplied by an application
  654. // first try loading the resource file using the app classloader
  655. // if it fails, try loading it as system res.
  656. InputStream appProviderStream = null;
  657. final String clappRes = "META-INF/javamail.providers";
  658. final String appRes = "/" + clappRes;
  659. boolean anyLoaded = false;
  660. if (getResources != null) {
  661. try {
  662. Enumeration e;
  663. ClassLoader cld = cl.getClassLoader();
  664. if (cld != null)
  665. e = (Enumeration)getResources.invoke(
  666. cld, new String[] { clappRes });
  667. else
  668. e = (Enumeration)getSystemResources.invoke(
  669. cld, new String[] { clappRes });
  670. while (e.hasMoreElements()) {
  671. URL url = (URL)e.nextElement();
  672. appProviderStream = url.openStream();
  673. if (appProviderStream != null) {
  674. try {
  675. loadProvidersFromStream(appProviderStream);
  676. anyLoaded = true;
  677. appProviderStream.close();
  678. if (debug)
  679. pr("DEBUG: successfully loaded " +
  680. "optional custom providers from URL: " +
  681. url);
  682. } catch (IOException ioex) {
  683. if (debug)
  684. pr("DEBUG: " + ioex.getMessage());
  685. }
  686. } else {
  687. if (debug)
  688. pr("DEBUG: not loading optional custom providers " +
  689. "from URL: " + url);
  690. }
  691. }
  692. } catch (Exception ex) {
  693. if (debug)
  694. pr("DEBUG: " + ex);
  695. }
  696. }
  697. // if failed to load anything, fall back to old technique, just in case
  698. if (!anyLoaded) {
  699. appProviderStream = cl.getResourceAsStream(appRes);
  700. if (appProviderStream != null) {
  701. try {
  702. loadProvidersFromStream(appProviderStream);
  703. appProviderStream.close();
  704. if (debug)
  705. pr("DEBUG: successfully loaded " +
  706. "optional custom providers: " + appRes);
  707. } catch (IOException ioex) {
  708. if (debug)
  709. pr("DEBUG: " + ioex);
  710. }
  711. } else {
  712. if (debug)
  713. pr("DEBUG: not loading optional custom providers file: " +
  714. appRes);
  715. }
  716. }
  717. // load default META-INF/javamail.default.providers from mail.jar file
  718. InputStream defProviderStream = null;
  719. String defRes = "/META-INF/javamail.default.providers";
  720. defProviderStream = cl.getResourceAsStream(defRes);
  721. if (defProviderStream != null) {
  722. try {
  723. loadProvidersFromStream(defProviderStream);
  724. defProviderStream.close();
  725. if (debug)
  726. pr("DEBUG: successfully loaded default providers");
  727. } catch (IOException ioex) {
  728. if (debug)
  729. pr("DEBUG: " + ioex.getMessage());
  730. }
  731. } else {
  732. if (debug)
  733. pr("DEBUG: can't load default providers file" + defRes);
  734. }
  735. if (debug) {
  736. System.out.println("\nDEBUG: Tables of loaded providers");
  737. // dump the output of the tables for debugging
  738. //pr("DEBUG: --- Providers Listed By Class Name --------");
  739. //System.out.println("size " + providersByClassName.size());
  740. pr("DEBUG: Providers Listed By Class Name: " +
  741. providersByClassName.toString());
  742. //pr("\nDEBUG: --- Providers Listed By Protocol ---------");
  743. //System.out.println("size " + providersByProtocol.size());
  744. pr("DEBUG: Providers Listed By Protocol: " +
  745. providersByProtocol.toString());
  746. }
  747. }
  748. private void loadProvidersFromStream(InputStream is)
  749. throws IOException {
  750. if (is != null) {
  751. LineInputStream lis = new LineInputStream(is);
  752. String currLine;
  753. // load and process one line at a time using LineInputStream
  754. while ((currLine = lis.readLine()) != null) {
  755. if (currLine.startsWith("#"))
  756. continue;
  757. Provider.Type type = null;
  758. String protocol = null, className = null;
  759. String vendor = null, version = null;
  760. // separate line into key-value tuples
  761. StringTokenizer tuples = new StringTokenizer(currLine,";");
  762. while (tuples.hasMoreTokens()) {
  763. String currTuple = tuples.nextToken().trim();
  764. // set the value of each attribute based on its key
  765. int sep = currTuple.indexOf("=");
  766. if (currTuple.startsWith("protocol=")) {
  767. protocol = currTuple.substring(sep+1);
  768. } else if (currTuple.startsWith("type=")) {
  769. String strType = currTuple.substring(sep+1);
  770. if (strType.equalsIgnoreCase("store")) {
  771. type = Provider.Type.STORE;
  772. } else if (strType.equalsIgnoreCase("transport")) {
  773. type = Provider.Type.TRANSPORT;
  774. }
  775. } else if (currTuple.startsWith("class=")) {
  776. className = currTuple.substring(sep+1);
  777. } else if (currTuple.startsWith("vendor=")) {
  778. vendor = currTuple.substring(sep+1);
  779. } else if (currTuple.startsWith("version=")) {
  780. version = currTuple.substring(sep+1);
  781. }
  782. }
  783. // check if a valid Provider; else, continue
  784. if (type == null || protocol == null || className == null
  785. || protocol.length() <= 0 || className.length() <= 0) {
  786. if (debug)
  787. System.out.println("DEBUG: Bad provider entry: " +
  788. currLine);
  789. continue;
  790. }
  791. Provider provider = new Provider(type, protocol, className,
  792. vendor, version);
  793. // add the newly-created Provider to the lookup tables
  794. providers.addElement(provider);
  795. providersByClassName.put(className, provider);
  796. if (!providersByProtocol.containsKey(protocol)) {
  797. providersByProtocol.put(protocol, provider);
  798. }
  799. }
  800. }
  801. }
  802. // load maps in reverse order of preference so that the preferred
  803. // map is loaded last since its entries will override the previous ones
  804. private void loadAddressMap(Class cl) {
  805. // load default META-INF/javamail.default.address.map from mail.jar
  806. // first try loading the resource file using the app classloader
  807. // if it fails, try loading it as system res.
  808. InputStream defAddressStream = null;
  809. String defRes = "/META-INF/javamail.default.address.map";
  810. defAddressStream = cl.getResourceAsStream(defRes);
  811. if (defAddressStream != null) {
  812. try {
  813. addressMap.load(defAddressStream);
  814. defAddressStream.close();
  815. } catch (IOException ioex) { /* ignore it */
  816. } catch (SecurityException sex) { /* ignore it */ }
  817. }
  818. // load the META-INF/javamail.address.map file supplied by an app
  819. // first try loading the resource file using the app classloader
  820. // if it fails, try loading it as system res.
  821. InputStream appAddressStream = null;
  822. final String clappRes = "META-INF/javamail.address.map";
  823. final String appRes = "/" + clappRes;
  824. boolean anyLoaded = false;
  825. if (getResources != null) {
  826. try {
  827. Enumeration e;
  828. ClassLoader cld = cl.getClassLoader();
  829. if (cld != null)
  830. e = (Enumeration)getResources.invoke(
  831. cld, new String[] { clappRes });
  832. else
  833. e = (Enumeration)getSystemResources.invoke(
  834. cld, new String[] { clappRes });
  835. while (e.hasMoreElements()) {
  836. URL url = (URL)e.nextElement();
  837. appAddressStream = url.openStream();
  838. if (appAddressStream != null) {
  839. try {
  840. addressMap.load(appAddressStream);
  841. anyLoaded = true;
  842. appAddressStream.close();
  843. if (debug)
  844. pr("DEBUG: successfully loaded " +
  845. "optional address map from URL: " + url);
  846. } catch (IOException ioex) {
  847. if (debug)
  848. pr("DEBUG: " + ioex.getMessage());
  849. }
  850. } else {
  851. if (debug)
  852. pr("DEBUG: not loading optional address map " +
  853. "from URL: " + url);
  854. }
  855. }
  856. } catch (Exception ex) {
  857. if (debug)
  858. pr("DEBUG: " + ex);
  859. }
  860. }
  861. // if failed to load anything, fall back to old technique, just in case
  862. if (!anyLoaded) {
  863. appAddressStream = cl.getResourceAsStream(appRes);
  864. if (appAddressStream != null) {
  865. try {
  866. addressMap.load(appAddressStream);
  867. appAddressStream.close();
  868. if (debug)
  869. pr("DEBUG: successfully loaded " +
  870. "optional address map: " + appRes);
  871. } catch (IOException ioex) {
  872. if (debug)
  873. pr("DEBUG: " + ioex);
  874. }
  875. } else {
  876. if (debug)
  877. pr("DEBUG: not loading optional address map file: " +
  878. appRes);
  879. }
  880. }
  881. // load system-wide javamail.address.map from the <java.home>/lib dir
  882. // since we have an absolute path to the file, use FileInputStream
  883. InputStream javahomeAddressStream = null;
  884. try {
  885. String res = System.getProperty("java.home") +
  886. File.separator + "lib" +
  887. File.separator + "javamail.address.map";
  888. javahomeAddressStream =
  889. new BufferedInputStream(new FileInputStream(res));
  890. } catch (FileNotFoundException fex) {
  891. /*ignore: don't load resource*/
  892. } catch (SecurityException sex) {
  893. /*ignore: don't load resource*/
  894. }
  895. if (javahomeAddressStream != null) {
  896. try {
  897. addressMap.load(javahomeAddressStream);
  898. javahomeAddressStream.close();
  899. } catch (IOException ioex) { /* ignore it */ }
  900. }
  901. }
  902. private static void pr(String str) {
  903. System.out.println(str);
  904. }
  905. }