1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.mail.javamail;
  17. import java.io.ByteArrayInputStream;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.OutputStream;
  22. import java.io.UnsupportedEncodingException;
  23. import javax.activation.DataHandler;
  24. import javax.activation.DataSource;
  25. import javax.activation.FileDataSource;
  26. import javax.activation.FileTypeMap;
  27. import javax.mail.BodyPart;
  28. import javax.mail.Message;
  29. import javax.mail.MessagingException;
  30. import javax.mail.internet.InternetAddress;
  31. import javax.mail.internet.MimeBodyPart;
  32. import javax.mail.internet.MimeMessage;
  33. import javax.mail.internet.MimeMultipart;
  34. import javax.mail.internet.MimePart;
  35. import org.springframework.core.io.InputStreamSource;
  36. /**
  37. * Helper class for easy population of a <code>javax.mail.internet.MimeMessage</code>.
  38. *
  39. * <p>Mirrors the simple setters of SimpleMailMessage, directly applying the values
  40. * to the underlying MimeMessage. Allows to define a character encoding for the
  41. * entire message, automatically applied by all methods of this helper.
  42. *
  43. * <p>Also offers support for typical mail attachments, and for personal names
  44. * that accompany mail addresses. Note that advanced settings can still be applied
  45. * directly to underlying MimeMessage!
  46. *
  47. * <p>Typically used in MimeMessagePreparator implementations or JavaMailSender
  48. * client code: simply instantiating it as a MimeMessage wrapper, invoking
  49. * setters on the wrapper, using the underlying MimeMessage for mail sending.
  50. * Also used internally by JavaMailSenderImpl.
  51. *
  52. * <p>Sample code:
  53. *
  54. * <pre>
  55. * mailSender.send(new MimeMessagePreparator() {
  56. * public void prepare(MimeMessage mimeMessage) throws MessagingException {
  57. * MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true);
  58. * message.setFrom("me@mail.com");
  59. * message.setTo("you@mail.com");
  60. * message.setSubject("my subject");
  61. * message.setText("my text");
  62. * message.addAttachment("logo.gif", new ClassPathResource("images/mylogo.gif"));
  63. * }
  64. * });</pre>
  65. *
  66. * @author Juergen Hoeller
  67. * @since 19.01.2004
  68. * @see javax.mail.internet.MimeMessage
  69. * @see #getMimeMessage
  70. * @see MimeMessagePreparator
  71. * @see JavaMailSender
  72. * @see JavaMailSenderImpl
  73. * @see org.springframework.mail.SimpleMailMessage
  74. */
  75. public class MimeMessageHelper {
  76. private final MimeMessage mimeMessage;
  77. private MimeMultipart mimeMultipart;
  78. private String encoding;
  79. /**
  80. * Create new MimeMessageHelper for the given MimeMessage,
  81. * assuming a simple text message (no multipart content).
  82. * @param mimeMessage MimeMessage to work on
  83. * @see #MimeMessageHelper(javax.mail.internet.MimeMessage, boolean)
  84. */
  85. public MimeMessageHelper(MimeMessage mimeMessage) {
  86. this.mimeMessage = mimeMessage;
  87. }
  88. /**
  89. * Create new MimeMessageHelper for the given MimeMessage,
  90. * assuming a simple text message (no multipart content).
  91. * @param mimeMessage MimeMessage to work on
  92. * @param encoding the character encoding to use for the message
  93. * @see #MimeMessageHelper(javax.mail.internet.MimeMessage, boolean)
  94. */
  95. public MimeMessageHelper(MimeMessage mimeMessage, String encoding) {
  96. this(mimeMessage);
  97. this.encoding = encoding;
  98. }
  99. /**
  100. * Create new MimeMessageHelper for the given MimeMessage,
  101. * in multipart mode (supporting attachments) if requested.
  102. * @param mimeMessage MimeMessage to work on
  103. * @param multipart whether to create a multipart message that
  104. * supports attachments
  105. */
  106. public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart) throws MessagingException {
  107. this.mimeMessage = mimeMessage;
  108. if (multipart) {
  109. this.mimeMultipart = new MimeMultipart();
  110. this.mimeMessage.setContent(this.mimeMultipart);
  111. }
  112. }
  113. /**
  114. * Create new MimeMessageHelper for the given MimeMessage,
  115. * in multipart mode (supporting attachments) if requested.
  116. * @param mimeMessage MimeMessage to work on
  117. * @param multipart whether to create a multipart message that
  118. * supports attachments
  119. * @param encoding the character encoding to use for the message
  120. */
  121. public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart, String encoding)
  122. throws MessagingException {
  123. this(mimeMessage, multipart);
  124. this.encoding = encoding;
  125. }
  126. /**
  127. * Return the underlying MimeMessage.
  128. */
  129. public MimeMessage getMimeMessage() {
  130. return mimeMessage;
  131. }
  132. /**
  133. * Return the character encoding used for this message.
  134. */
  135. public String getEncoding() {
  136. return encoding;
  137. }
  138. public void setFrom(InternetAddress from) throws MessagingException {
  139. this.mimeMessage.setFrom(from);
  140. }
  141. public void setFrom(String from) throws MessagingException {
  142. this.mimeMessage.setFrom(new InternetAddress(from));
  143. }
  144. public void setFrom(String from, String personal) throws MessagingException, UnsupportedEncodingException {
  145. this.mimeMessage.setFrom(this.encoding != null ?
  146. new InternetAddress(from, personal, this.encoding) :
  147. new InternetAddress(from, personal));
  148. }
  149. public void setTo(InternetAddress to) throws MessagingException {
  150. this.mimeMessage.setRecipient(Message.RecipientType.TO, to);
  151. }
  152. public void setTo(InternetAddress[] to) throws MessagingException {
  153. this.mimeMessage.setRecipients(Message.RecipientType.TO, to);
  154. }
  155. public void setTo(String to) throws MessagingException {
  156. this.mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
  157. }
  158. public void setTo(String[] to) throws MessagingException {
  159. InternetAddress[] addresses = new InternetAddress[to.length];
  160. for (int i = 0; i < to.length; i++) {
  161. addresses[i] = new InternetAddress(to[i]);
  162. }
  163. this.mimeMessage.setRecipients(Message.RecipientType.TO, addresses);
  164. }
  165. public void addTo(InternetAddress to) throws MessagingException {
  166. this.mimeMessage.addRecipient(Message.RecipientType.TO, to);
  167. }
  168. public void addTo(String to) throws MessagingException {
  169. this.mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
  170. }
  171. public void addTo(String to, String personal) throws MessagingException, UnsupportedEncodingException {
  172. this.mimeMessage.addRecipient(Message.RecipientType.TO,
  173. this.encoding != null ?
  174. new InternetAddress(to, personal, this.encoding) :
  175. new InternetAddress(to, personal));
  176. }
  177. public void setCc(InternetAddress cc) throws MessagingException {
  178. this.mimeMessage.setRecipient(Message.RecipientType.CC, cc);
  179. }
  180. public void setCc(InternetAddress[] cc) throws MessagingException {
  181. this.mimeMessage.setRecipients(Message.RecipientType.CC, cc);
  182. }
  183. public void setCc(String cc) throws MessagingException {
  184. this.mimeMessage.setRecipient(Message.RecipientType.CC, new InternetAddress(cc));
  185. }
  186. public void setCc(String[] cc) throws MessagingException {
  187. InternetAddress[] addresses = new InternetAddress[cc.length];
  188. for (int i = 0; i < cc.length; i++) {
  189. addresses[i] = new InternetAddress(cc[i]);
  190. }
  191. this.mimeMessage.setRecipients(Message.RecipientType.CC, addresses);
  192. }
  193. public void addCc(InternetAddress cc) throws MessagingException {
  194. this.mimeMessage.addRecipient(Message.RecipientType.CC, cc);
  195. }
  196. public void addCc(String cc) throws MessagingException {
  197. this.mimeMessage.addRecipient(Message.RecipientType.CC, new InternetAddress(cc));
  198. }
  199. public void addCc(String cc, String personal) throws MessagingException, UnsupportedEncodingException {
  200. this.mimeMessage.addRecipient(Message.RecipientType.CC,
  201. this.encoding != null ?
  202. new InternetAddress(cc, personal, this.encoding) :
  203. new InternetAddress(cc, personal));
  204. }
  205. public void setBcc(InternetAddress bcc) throws MessagingException {
  206. this.mimeMessage.setRecipient(Message.RecipientType.BCC, bcc);
  207. }
  208. public void setBcc(InternetAddress[] bcc) throws MessagingException {
  209. this.mimeMessage.setRecipients(Message.RecipientType.BCC, bcc);
  210. }
  211. public void setBcc(String bcc) throws MessagingException {
  212. this.mimeMessage.setRecipient(Message.RecipientType.BCC, new InternetAddress(bcc));
  213. }
  214. public void setBcc(String[] bcc) throws MessagingException {
  215. InternetAddress[] addresses = new InternetAddress[bcc.length];
  216. for (int i = 0; i < bcc.length; i++) {
  217. addresses[i] = new InternetAddress(bcc[i]);
  218. }
  219. this.mimeMessage.setRecipients(Message.RecipientType.BCC, addresses);
  220. }
  221. public void addBcc(InternetAddress bcc) throws MessagingException {
  222. this.mimeMessage.addRecipient(Message.RecipientType.BCC, bcc);
  223. }
  224. public void addBcc(String bcc) throws MessagingException {
  225. this.mimeMessage.addRecipient(Message.RecipientType.BCC, new InternetAddress(bcc));
  226. }
  227. public void addBcc(String bcc, String personal) throws MessagingException, UnsupportedEncodingException {
  228. this.mimeMessage.addRecipient(Message.RecipientType.BCC,
  229. this.encoding != null ?
  230. new InternetAddress(bcc, personal, this.encoding) :
  231. new InternetAddress(bcc, personal));
  232. }
  233. public void setSubject(String subject) throws MessagingException {
  234. if (this.encoding != null) {
  235. this.mimeMessage.setSubject(subject, this.encoding);
  236. }
  237. else {
  238. this.mimeMessage.setSubject(subject);
  239. }
  240. }
  241. public void setText(String text) throws MessagingException {
  242. setText(text, false);
  243. }
  244. /**
  245. * Set the given text directly as content in non-multipart mode
  246. * respectively as default body part in multipart mode.
  247. * @param text text to set
  248. * @param html whether to apply content type "text/html" for an
  249. * HTML mail, using default content type ("text/plain") else
  250. */
  251. public void setText(final String text, boolean html) throws MessagingException {
  252. MimePart partToUse = null;
  253. if (this.mimeMultipart != null) {
  254. MimeBodyPart bodyPart = null;
  255. for (int i = 0; i < this.mimeMultipart.getCount(); i++) {
  256. BodyPart bp = this.mimeMultipart.getBodyPart(i);
  257. if (bp.getFileName() == null) {
  258. bodyPart = (MimeBodyPart) bp;
  259. }
  260. }
  261. if (bodyPart == null) {
  262. MimeBodyPart mimeBodyPart = new MimeBodyPart();
  263. this.mimeMultipart.addBodyPart(mimeBodyPart);
  264. bodyPart = mimeBodyPart;
  265. }
  266. partToUse = bodyPart;
  267. }
  268. else {
  269. partToUse = this.mimeMessage;
  270. }
  271. if (html) {
  272. // need to use a javax.activation.DataSource (!) to set a text
  273. // with content type "text/html"
  274. partToUse.setDataHandler(new DataHandler(
  275. new DataSource() {
  276. public InputStream getInputStream() throws IOException {
  277. return new ByteArrayInputStream(encoding != null ? text.getBytes(encoding) : text.getBytes());
  278. }
  279. public OutputStream getOutputStream() throws IOException {
  280. throw new UnsupportedOperationException("Read-only javax.activation.DataSource");
  281. }
  282. public String getContentType() {
  283. return "text/html";
  284. }
  285. public String getName() {
  286. return "text";
  287. }
  288. }
  289. ));
  290. }
  291. else {
  292. if (this.encoding != null) {
  293. partToUse.setText(text, this.encoding);
  294. }
  295. else {
  296. partToUse.setText(text);
  297. }
  298. }
  299. }
  300. /**
  301. * Add an attachment to the given MimeMessage, taking the content
  302. * from a java.io.File.
  303. * <p>The content type will be determined by the name of the given
  304. * content file. Do not use this for temporary files with arbitrary
  305. * filenames (possibly ending in ".tmp" or the like)!
  306. * @param attachmentFilename the name of the attachment as it will
  307. * appear in the mail
  308. * @param file the File resource to take the content from
  309. * @throws MessagingException
  310. * @see #addAttachment(String, org.springframework.core.io.InputStreamSource)
  311. * @see #addAttachment(String, javax.activation.DataSource)
  312. */
  313. public void addAttachment(String attachmentFilename, File file) throws MessagingException {
  314. addAttachment(attachmentFilename, new FileDataSource(file));
  315. }
  316. /**
  317. * Add an attachment to the given MimeMessage, taking the content
  318. * from an org.springframework.core.InputStreamResource.
  319. * <p>The content type will be determined by the given filename for
  320. * the attachment. Thus, any content source will be fine, including
  321. * temporary files with arbitrary filenames.
  322. * @param attachmentFilename the name of the attachment as it will
  323. * appear in the mail
  324. * @param inputStreamSource the resource to take the content from
  325. * @see #addAttachment(String, File)
  326. * @see #addAttachment(String, javax.activation.DataSource)
  327. */
  328. public void addAttachment(final String attachmentFilename, final InputStreamSource inputStreamSource)
  329. throws MessagingException {
  330. addAttachment(attachmentFilename,
  331. new DataSource() {
  332. public InputStream getInputStream() throws IOException {
  333. return inputStreamSource.getInputStream();
  334. }
  335. public OutputStream getOutputStream() {
  336. throw new UnsupportedOperationException("Read-only javax.activation.DataSource");
  337. }
  338. public String getContentType() {
  339. return FileTypeMap.getDefaultFileTypeMap().getContentType(attachmentFilename);
  340. }
  341. public String getName() {
  342. return attachmentFilename;
  343. }
  344. });
  345. }
  346. /**
  347. * Add an attachment to the given MimeMessage,
  348. * taking the content from a <code>javax.activation.DataSource</code>.
  349. * <p>Note that the InputStream returned by the DataSource implementation
  350. * needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
  351. * getInputStream() multiple times.
  352. * @param attachmentFilename the name of the attachment as it will
  353. * appear in the mail (the content type will be determined by this)
  354. * @param dataSource the <code>javax.activation.DataSource</code> to take
  355. * the content from, determining the InputStream and the content type
  356. * @throws MessagingException in case of errors
  357. * @see #addAttachment(String, File)
  358. * @see #addAttachment(String, org.springframework.core.io.InputStreamSource)
  359. */
  360. public void addAttachment(String attachmentFilename, DataSource dataSource) throws MessagingException {
  361. if (this.mimeMultipart == null) {
  362. throw new IllegalStateException("Cannot add attachment - not in multipart mode");
  363. }
  364. MimeBodyPart bodyPart = new MimeBodyPart();
  365. bodyPart.setFileName(attachmentFilename);
  366. bodyPart.setDataHandler(new DataHandler(dataSource));
  367. this.mimeMultipart.addBodyPart(bodyPart);
  368. }
  369. }