PageRenderTime 5169ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/project/src/webapp/projecthl7/web/WEB-INF/classes/org/apache/commons/mail/HtmlEmail.java

https://github.com/milin/Patient-Data-Exchange-Server
Java | 695 lines | 345 code | 57 blank | 293 comment | 31 complexity | ba1dcf7c8df33bdcd56182dc1607f4bd MD5 | raw file
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.commons.mail;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.net.MalformedURLException;
  22. import java.net.URL;
  23. import java.util.HashMap;
  24. import java.util.Iterator;
  25. import java.util.List;
  26. import java.util.Map;
  27. import javax.activation.DataHandler;
  28. import javax.activation.DataSource;
  29. import javax.activation.FileDataSource;
  30. import javax.activation.URLDataSource;
  31. import javax.mail.BodyPart;
  32. import javax.mail.MessagingException;
  33. import javax.mail.internet.MimeBodyPart;
  34. import javax.mail.internet.MimeMultipart;
  35. /**
  36. * An HTML multipart email.
  37. *
  38. * <p>This class is used to send HTML formatted email. A text message
  39. * can also be set for HTML unaware email clients, such as text-based
  40. * email clients.
  41. *
  42. * <p>This class also inherits from {@link MultiPartEmail}, so it is easy to
  43. * add attachments to the email.
  44. *
  45. * <p>To send an email in HTML, one should create a <code>HtmlEmail</code>, then
  46. * use the {@link #setFrom(String)}, {@link #addTo(String)} etc. methods.
  47. * The HTML content can be set with the {@link #setHtmlMsg(String)} method. The
  48. * alternative text content can be set with {@link #setTextMsg(String)}.
  49. *
  50. * <p>Either the text or HTML can be omitted, in which case the "main"
  51. * part of the multipart becomes whichever is supplied rather than a
  52. * <code>multipart/alternative</code>.
  53. *
  54. * <h3>Embedding Images and Media</h3>
  55. *
  56. * <p>It is also possible to embed URLs, files, or arbitrary
  57. * <code>DataSource</code>s directly into the body of the mail:
  58. * <pre><code>
  59. * HtmlEmail he = new HtmlEmail();
  60. * File img = new File("my/image.gif");
  61. * PNGDataSource png = new PNGDataSource(decodedPNGOutputStream); // a custom class
  62. * StringBuffer msg = new StringBuffer();
  63. * msg.append("&lt;html&gt;&lt;body&gt;");
  64. * msg.append("&lt;img src=cid:").append(he.embed(img)).append("&gt;");
  65. * msg.append("&lt;img src=cid:").append(he.embed(png)).append("&gt;");
  66. * msg.append("&lt;/body&gt;&lt;/html&gt;");
  67. * he.setHtmlMsg(msg.toString());
  68. * // code to set the other email fields (not shown)
  69. * </pre></code>
  70. *
  71. * <p>Embedded entities are tracked by their name, which for <code>File</code>s is
  72. * the filename itself and for <code>URL</code>s is the canonical path. It is
  73. * an error to bind the same name to more than one entity, and this class will
  74. * attempt to validate that for <code>File</code>s and <code>URL</code>s. When
  75. * embedding a <code>DataSource</code>, the code uses the <code>equals()</code>
  76. * method defined on the <code>DataSource</code>s to make the determination.
  77. *
  78. * @since 1.0
  79. * @author <a href="mailto:unknown">Regis Koenig</a>
  80. * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
  81. * @version $Id: HtmlEmail.java 785383 2009-06-16 20:36:22Z sgoeschl $
  82. */
  83. public class HtmlEmail extends MultiPartEmail
  84. {
  85. /** Definition of the length of generated CID's */
  86. public static final int CID_LENGTH = 10;
  87. /** prefix for default HTML mail */
  88. private static final String HTML_MESSAGE_START = "<html><body><pre>";
  89. /** suffix for default HTML mail */
  90. private static final String HTML_MESSAGE_END = "</pre></body></html>";
  91. /**
  92. * Text part of the message. This will be used as alternative text if
  93. * the email client does not support HTML messages.
  94. */
  95. protected String text;
  96. /** Html part of the message */
  97. protected String html;
  98. /**
  99. * @deprecated As of commons-email 1.1, no longer used. Inline embedded
  100. * objects are now stored in {@link #inlineEmbeds}.
  101. */
  102. protected List inlineImages;
  103. /**
  104. * Embedded images Map<String, InlineImage> where the key is the
  105. * user-defined image name.
  106. */
  107. protected Map inlineEmbeds = new HashMap();
  108. /**
  109. * Set the text content.
  110. *
  111. * @param aText A String.
  112. * @return An HtmlEmail.
  113. * @throws EmailException see javax.mail.internet.MimeBodyPart
  114. * for definitions
  115. * @since 1.0
  116. */
  117. public HtmlEmail setTextMsg(String aText) throws EmailException
  118. {
  119. if (EmailUtils.isEmpty(aText))
  120. {
  121. throw new EmailException("Invalid message supplied");
  122. }
  123. this.text = aText;
  124. return this;
  125. }
  126. /**
  127. * Set the HTML content.
  128. *
  129. * @param aHtml A String.
  130. * @return An HtmlEmail.
  131. * @throws EmailException see javax.mail.internet.MimeBodyPart
  132. * for definitions
  133. * @since 1.0
  134. */
  135. public HtmlEmail setHtmlMsg(String aHtml) throws EmailException
  136. {
  137. if (EmailUtils.isEmpty(aHtml))
  138. {
  139. throw new EmailException("Invalid message supplied");
  140. }
  141. this.html = aHtml;
  142. return this;
  143. }
  144. /**
  145. * Set the message.
  146. *
  147. * <p>This method overrides {@link MultiPartEmail#setMsg(String)} in
  148. * order to send an HTML message instead of a plain text message in
  149. * the mail body. The message is formatted in HTML for the HTML
  150. * part of the message; it is left as is in the alternate text
  151. * part.
  152. *
  153. * @param msg the message text to use
  154. * @return this <code>HtmlEmail</code>
  155. * @throws EmailException if msg is null or empty;
  156. * see javax.mail.internet.MimeBodyPart for definitions
  157. * @since 1.0
  158. */
  159. public Email setMsg(String msg) throws EmailException
  160. {
  161. if (EmailUtils.isEmpty(msg))
  162. {
  163. throw new EmailException("Invalid message supplied");
  164. }
  165. setTextMsg(msg);
  166. StringBuffer htmlMsgBuf = new StringBuffer(
  167. msg.length()
  168. + HTML_MESSAGE_START.length()
  169. + HTML_MESSAGE_END.length()
  170. );
  171. htmlMsgBuf.append(HTML_MESSAGE_START)
  172. .append(msg)
  173. .append(HTML_MESSAGE_END);
  174. setHtmlMsg(htmlMsgBuf.toString());
  175. return this;
  176. }
  177. /**
  178. * Attempts to parse the specified <code>String</code> as a URL that will
  179. * then be embedded in the message.
  180. *
  181. * @param urlString String representation of the URL.
  182. * @param name The name that will be set in the filename header field.
  183. * @return A String with the Content-ID of the URL.
  184. * @throws EmailException when URL supplied is invalid or if <code> is null
  185. * or empty; also see {@link javax.mail.internet.MimeBodyPart} for definitions
  186. *
  187. * @see #embed(URL, String)
  188. * @since 1.1
  189. */
  190. public String embed(String urlString, String name) throws EmailException
  191. {
  192. try
  193. {
  194. return embed(new URL(urlString), name);
  195. }
  196. catch (MalformedURLException e)
  197. {
  198. throw new EmailException("Invalid URL", e);
  199. }
  200. }
  201. /**
  202. * Embeds an URL in the HTML.
  203. *
  204. * <p>This method embeds a file located by an URL into
  205. * the mail body. It allows, for instance, to add inline images
  206. * to the email. Inline files may be referenced with a
  207. * <code>cid:xxxxxx</code> URL, where xxxxxx is the Content-ID
  208. * returned by the embed function. It is an error to bind the same name
  209. * to more than one URL; if the same URL is embedded multiple times, the
  210. * same Content-ID is guaranteed to be returned.
  211. *
  212. * <p>While functionally the same as passing <code>URLDataSource</code> to
  213. * {@link #embed(DataSource, String, String)}, this method attempts
  214. * to validate the URL before embedding it in the message and will throw
  215. * <code>EmailException</code> if the validation fails. In this case, the
  216. * <code>HtmlEmail</code> object will not be changed.
  217. *
  218. * <p>
  219. * NOTE: Clients should take care to ensure that different URLs are bound to
  220. * different names. This implementation tries to detect this and throw
  221. * <code>EmailException</code>. However, it is not guaranteed to catch
  222. * all cases, especially when the URL refers to a remote HTTP host that
  223. * may be part of a virtual host cluster.
  224. *
  225. * @param url The URL of the file.
  226. * @param name The name that will be set in the filename header
  227. * field.
  228. * @return A String with the Content-ID of the file.
  229. * @throws EmailException when URL supplied is invalid or if <code> is null
  230. * or empty; also see {@link javax.mail.internet.MimeBodyPart} for definitions
  231. * @since 1.0
  232. */
  233. public String embed(URL url, String name) throws EmailException
  234. {
  235. if (EmailUtils.isEmpty(name))
  236. {
  237. throw new EmailException("name cannot be null or empty");
  238. }
  239. // check if a URLDataSource for this name has already been attached;
  240. // if so, return the cached CID value.
  241. if (inlineEmbeds.containsKey(name))
  242. {
  243. InlineImage ii = (InlineImage) inlineEmbeds.get(name);
  244. URLDataSource urlDataSource = (URLDataSource) ii.getDataSource();
  245. // make sure the supplied URL points to the same thing
  246. // as the one already associated with this name.
  247. // NOTE: Comparing URLs with URL.equals() is a blocking operation
  248. // in the case of a network failure therefore we use
  249. // url.toExternalForm().equals() here.
  250. if (url.toExternalForm().equals(urlDataSource.getURL().toExternalForm()))
  251. {
  252. return ii.getCid();
  253. }
  254. else
  255. {
  256. throw new EmailException("embedded name '" + name
  257. + "' is already bound to URL " + urlDataSource.getURL()
  258. + "; existing names cannot be rebound");
  259. }
  260. }
  261. // verify that the URL is valid
  262. InputStream is = null;
  263. try
  264. {
  265. is = url.openStream();
  266. }
  267. catch (IOException e)
  268. {
  269. throw new EmailException("Invalid URL", e);
  270. }
  271. finally
  272. {
  273. try
  274. {
  275. if (is != null)
  276. {
  277. is.close();
  278. }
  279. }
  280. catch (IOException ioe)
  281. { /* sigh */ }
  282. }
  283. return embed(new URLDataSource(url), name);
  284. }
  285. /**
  286. * Embeds a file in the HTML. This implementation delegates to
  287. * {@link #embed(File, String)}.
  288. *
  289. * @param file The <code>File</code> object to embed
  290. * @return A String with the Content-ID of the file.
  291. * @throws EmailException when the supplied <code>File</code> cannot be
  292. * used; also see {@link javax.mail.internet.MimeBodyPart} for definitions
  293. *
  294. * @see #embed(File, String)
  295. * @since 1.1
  296. */
  297. public String embed(File file) throws EmailException
  298. {
  299. String cid = EmailUtils.randomAlphabetic(HtmlEmail.CID_LENGTH).toLowerCase();
  300. return embed(file, cid);
  301. }
  302. /**
  303. * Embeds a file in the HTML.
  304. *
  305. * <p>This method embeds a file located by an URL into
  306. * the mail body. It allows, for instance, to add inline images
  307. * to the email. Inline files may be referenced with a
  308. * <code>cid:xxxxxx</code> URL, where xxxxxx is the Content-ID
  309. * returned by the embed function. Files are bound to their names, which is
  310. * the value returned by {@link java.io.File#getName()}. If the same file
  311. * is embedded multiple times, the same CID is guaranteed to be returned.
  312. *
  313. * <p>While functionally the same as passing <code>FileDataSource</code> to
  314. * {@link #embed(DataSource, String, String)}, this method attempts
  315. * to validate the file before embedding it in the message and will throw
  316. * <code>EmailException</code> if the validation fails. In this case, the
  317. * <code>HtmlEmail</code> object will not be changed.
  318. *
  319. * @param file The <code>File</code> to embed
  320. * @param cid the Content-ID to use for the embedded <code>File</code>
  321. * @return A String with the Content-ID of the file.
  322. * @throws EmailException when the supplied <code>File</code> cannot be used
  323. * or if the file has already been embedded;
  324. * also see {@link javax.mail.internet.MimeBodyPart} for definitions
  325. * @since 1.1
  326. */
  327. public String embed(File file, String cid) throws EmailException
  328. {
  329. if (EmailUtils.isEmpty(file.getName()))
  330. {
  331. throw new EmailException("file name cannot be null or empty");
  332. }
  333. // verify that the File can provide a canonical path
  334. String filePath = null;
  335. try
  336. {
  337. filePath = file.getCanonicalPath();
  338. }
  339. catch (IOException ioe)
  340. {
  341. throw new EmailException("couldn't get canonical path for "
  342. + file.getName(), ioe);
  343. }
  344. // check if a FileDataSource for this name has already been attached;
  345. // if so, return the cached CID value.
  346. if (inlineEmbeds.containsKey(file.getName()))
  347. {
  348. InlineImage ii = (InlineImage) inlineEmbeds.get(file.getName());
  349. FileDataSource fileDataSource = (FileDataSource) ii.getDataSource();
  350. // make sure the supplied file has the same canonical path
  351. // as the one already associated with this name.
  352. String existingFilePath = null;
  353. try
  354. {
  355. existingFilePath = fileDataSource.getFile().getCanonicalPath();
  356. }
  357. catch (IOException ioe)
  358. {
  359. throw new EmailException("couldn't get canonical path for file "
  360. + fileDataSource.getFile().getName()
  361. + "which has already been embedded", ioe);
  362. }
  363. if (filePath.equals(existingFilePath))
  364. {
  365. return ii.getCid();
  366. }
  367. else
  368. {
  369. throw new EmailException("embedded name '" + file.getName()
  370. + "' is already bound to file " + existingFilePath
  371. + "; existing names cannot be rebound");
  372. }
  373. }
  374. // verify that the file is valid
  375. if (!file.exists())
  376. {
  377. throw new EmailException("file " + filePath + " doesn't exist");
  378. }
  379. if (!file.isFile())
  380. {
  381. throw new EmailException("file " + filePath + " isn't a normal file");
  382. }
  383. if (!file.canRead())
  384. {
  385. throw new EmailException("file " + filePath + " isn't readable");
  386. }
  387. return embed(new FileDataSource(file), file.getName());
  388. }
  389. /**
  390. * Embeds the specified <code>DataSource</code> in the HTML using a
  391. * randomly generated Content-ID. Returns the generated Content-ID string.
  392. *
  393. * @param dataSource the <code>DataSource</code> to embed
  394. * @param name the name that will be set in the filename header field
  395. * @return the generated Content-ID for this <code>DataSource</code>
  396. * @throws EmailException if the embedding fails or if <code>name</code> is
  397. * null or empty
  398. * @see #embed(DataSource, String, String)
  399. * @since 1.1
  400. */
  401. public String embed(DataSource dataSource, String name) throws EmailException
  402. {
  403. // check if the DataSource has already been attached;
  404. // if so, return the cached CID value.
  405. if (inlineEmbeds.containsKey(name))
  406. {
  407. InlineImage ii = (InlineImage) inlineEmbeds.get(name);
  408. // make sure the supplied URL points to the same thing
  409. // as the one already associated with this name.
  410. if (dataSource.equals(ii.getDataSource()))
  411. {
  412. return ii.getCid();
  413. }
  414. else
  415. {
  416. throw new EmailException("embedded DataSource '" + name
  417. + "' is already bound to name " + ii.getDataSource().toString()
  418. + "; existing names cannot be rebound");
  419. }
  420. }
  421. String cid = EmailUtils.randomAlphabetic(HtmlEmail.CID_LENGTH).toLowerCase();
  422. return embed(dataSource, name, cid);
  423. }
  424. /**
  425. * Embeds the specified <code>DataSource</code> in the HTML using the
  426. * specified Content-ID. Returns the specified Content-ID string.
  427. *
  428. * @param dataSource the <code>DataSource</code> to embed
  429. * @param name the name that will be set in the filename header field
  430. * @param cid the Content-ID to use for this <code>DataSource</code>
  431. * @return the supplied Content-ID for this <code>DataSource</code>
  432. * @throws EmailException if the embedding fails or if <code>name</code> is
  433. * null or empty
  434. * @since 1.1
  435. */
  436. public String embed(DataSource dataSource, String name, String cid)
  437. throws EmailException
  438. {
  439. if (EmailUtils.isEmpty(name))
  440. {
  441. throw new EmailException("name cannot be null or empty");
  442. }
  443. MimeBodyPart mbp = new MimeBodyPart();
  444. try
  445. {
  446. mbp.setDataHandler(new DataHandler(dataSource));
  447. mbp.setFileName(name);
  448. mbp.setDisposition("inline");
  449. mbp.setContentID("<" + cid + ">");
  450. InlineImage ii = new InlineImage(cid, dataSource, mbp);
  451. this.inlineEmbeds.put(name, ii);
  452. return cid;
  453. }
  454. catch (MessagingException me)
  455. {
  456. throw new EmailException(me);
  457. }
  458. }
  459. /**
  460. * Does the work of actually building the email.
  461. *
  462. * @exception EmailException if there was an error.
  463. * @since 1.0
  464. */
  465. public void buildMimeMessage() throws EmailException
  466. {
  467. try
  468. {
  469. build();
  470. }
  471. catch (MessagingException me)
  472. {
  473. throw new EmailException(me);
  474. }
  475. super.buildMimeMessage();
  476. }
  477. /**
  478. * @throws EmailException EmailException
  479. * @throws MessagingException MessagingException
  480. */
  481. private void build() throws MessagingException, EmailException
  482. {
  483. MimeMultipart rootContainer = this.getContainer();
  484. MimeMultipart bodyEmbedsContainer = rootContainer;
  485. MimeMultipart bodyContainer = rootContainer;
  486. BodyPart msgHtml = null;
  487. BodyPart msgText = null;
  488. rootContainer.setSubType("mixed");
  489. // determine how to form multiparts of email
  490. if (EmailUtils.isNotEmpty(this.html) && this.inlineEmbeds.size() > 0)
  491. {
  492. //If HTML body and embeds are used, create a related container and add it to the root container
  493. bodyEmbedsContainer = new MimeMultipart("related");
  494. bodyContainer = bodyEmbedsContainer;
  495. this.addPart(bodyEmbedsContainer, 0);
  496. //If TEXT body was specified, create a alternative container and add it to the embeds container
  497. if (EmailUtils.isNotEmpty(this.text))
  498. {
  499. bodyContainer = new MimeMultipart("alternative");
  500. BodyPart bodyPart = createBodyPart();
  501. try
  502. {
  503. bodyPart.setContent(bodyContainer);
  504. bodyEmbedsContainer.addBodyPart(bodyPart, 0);
  505. }
  506. catch (MessagingException me)
  507. {
  508. throw new EmailException(me);
  509. }
  510. }
  511. }
  512. else if (EmailUtils.isNotEmpty(this.text) && EmailUtils.isNotEmpty(this.html))
  513. {
  514. //If both HTML and TEXT bodies are provided, create a alternative container and add it to the root container
  515. bodyContainer = new MimeMultipart("alternative");
  516. this.addPart(bodyContainer, 0);
  517. }
  518. if (EmailUtils.isNotEmpty(this.html))
  519. {
  520. msgHtml = new MimeBodyPart();
  521. bodyContainer.addBodyPart(msgHtml, 0);
  522. // apply default charset if one has been set
  523. if (EmailUtils.isNotEmpty(this.charset))
  524. {
  525. msgHtml.setContent(
  526. this.html,
  527. Email.TEXT_HTML + "; charset=" + this.charset);
  528. }
  529. else
  530. {
  531. msgHtml.setContent(this.html, Email.TEXT_HTML);
  532. }
  533. Iterator iter = this.inlineEmbeds.values().iterator();
  534. while (iter.hasNext())
  535. {
  536. InlineImage ii = (InlineImage) iter.next();
  537. bodyEmbedsContainer.addBodyPart(ii.getMbp());
  538. }
  539. }
  540. if (EmailUtils.isNotEmpty(this.text))
  541. {
  542. msgText = new MimeBodyPart();
  543. bodyContainer.addBodyPart(msgText, 0);
  544. // apply default charset if one has been set
  545. if (EmailUtils.isNotEmpty(this.charset))
  546. {
  547. msgText.setContent(
  548. this.text,
  549. Email.TEXT_PLAIN + "; charset=" + this.charset);
  550. }
  551. else
  552. {
  553. msgText.setContent(this.text, Email.TEXT_PLAIN);
  554. }
  555. }
  556. }
  557. /**
  558. * Private bean class that encapsulates data about URL contents
  559. * that are embedded in the final email.
  560. * @since 1.1
  561. */
  562. private static class InlineImage
  563. {
  564. /** content id */
  565. private String cid;
  566. /** <code>DataSource</code> for the content */
  567. private DataSource dataSource;
  568. /** the <code>MimeBodyPart</code> that contains the encoded data */
  569. private MimeBodyPart mbp;
  570. /**
  571. * Creates an InlineImage object to represent the
  572. * specified content ID and <code>MimeBodyPart</code>.
  573. * @param cid the generated content ID
  574. * @param dataSource the <code>DataSource</code> that represents the content
  575. * @param mbp the <code>MimeBodyPart</code> that contains the encoded
  576. * data
  577. */
  578. public InlineImage(String cid, DataSource dataSource, MimeBodyPart mbp)
  579. {
  580. this.cid = cid;
  581. this.dataSource = dataSource;
  582. this.mbp = mbp;
  583. }
  584. /**
  585. * Returns the unique content ID of this InlineImage.
  586. * @return the unique content ID of this InlineImage
  587. */
  588. public String getCid()
  589. {
  590. return cid;
  591. }
  592. /**
  593. * Returns the <code>DataSource</code> that represents the encoded content.
  594. * @return the <code>DataSource</code> representing the encoded content
  595. */
  596. public DataSource getDataSource()
  597. {
  598. return dataSource;
  599. }
  600. /**
  601. * Returns the <code>MimeBodyPart</code> that contains the
  602. * encoded InlineImage data.
  603. * @return the <code>MimeBodyPart</code> containing the encoded
  604. * InlineImage data
  605. */
  606. public MimeBodyPart getMbp()
  607. {
  608. return mbp;
  609. }
  610. // equals()/hashCode() implementations, since this class
  611. // is stored as a entry in a Map.
  612. /**
  613. * {@inheritDoc}
  614. * @return true if the other object is also an InlineImage with the same cid.
  615. */
  616. public boolean equals(Object obj)
  617. {
  618. if (this == obj)
  619. {
  620. return true;
  621. }
  622. if (!(obj instanceof InlineImage))
  623. {
  624. return false;
  625. }
  626. InlineImage that = (InlineImage) obj;
  627. return this.cid.equals(that.cid);
  628. }
  629. /**
  630. * {@inheritDoc}
  631. * @return the cid hashCode.
  632. */
  633. public int hashCode()
  634. {
  635. return cid.hashCode();
  636. }
  637. }
  638. }