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
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.net.URL;
23
24 import javax.activation.DataHandler;
25 import javax.activation.DataSource;
26 import javax.activation.FileDataSource;
27 import javax.activation.URLDataSource;
28 import javax.mail.BodyPart;
29 import javax.mail.MessagingException;
30 import javax.mail.internet.MimeBodyPart;
31 import javax.mail.internet.MimeMultipart;
32 import javax.mail.internet.MimePart;
33
34 /**
35 * A multipart email.
36 *
37 * <p>This class is used to send multi-part internet email like
38 * messages with attachments.
39 *
40 * <p>To create a multi-part email, call the default constructor and
41 * then you can call setMsg() to set the message and call the
42 * different attach() methods.
43 *
44 * @since 1.0
45 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
46 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
47 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
48 * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
49 * @author <a href="mailto:unknown">Regis Koenig</a>
50 * @author <a href="mailto:corey.scott@gmail.com">Corey Scott</a>
51 * @version $Id: MultiPartEmail.java 1420402 2012-12-11 20:59:15Z tn $
52 */
53 public class MultiPartEmail extends Email
54 {
55 /** Body portion of the email. */
56 private MimeMultipart container;
57
58 /** The message container. */
59 private BodyPart primaryBodyPart;
60
61 /** The MIME subtype. */
62 private String subType;
63
64 /** Indicates if the message has been initialized. */
65 private boolean initialized;
66
67 /** Indicates if attachments have been added to the message. */
68 private boolean boolHasAttachments;
69
70 /**
71 * Set the MIME subtype of the email.
72 *
73 * @param aSubType MIME subtype of the email
74 * @since 1.0
75 */
76 public void setSubType(String aSubType)
77 {
78 this.subType = aSubType;
79 }
80
81 /**
82 * Get the MIME subtype of the email.
83 *
84 * @return MIME subtype of the email
85 * @since 1.0
86 */
87 public String getSubType()
88 {
89 return subType;
90 }
91
92 /**
93 * Add a new part to the email.
94 *
95 * @param partContent The content.
96 * @param partContentType The content type.
97 * @return An Email.
98 * @throws EmailException see javax.mail.internet.MimeBodyPart
99 * for definitions
100 * @since 1.0
101 */
102 public Email addPart(String partContent, String partContentType)
103 throws EmailException
104 {
105 BodyPart bodyPart = createBodyPart();
106 try
107 {
108 bodyPart.setContent(partContent, partContentType);
109 getContainer().addBodyPart(bodyPart);
110 }
111 catch (MessagingException me)
112 {
113 throw new EmailException(me);
114 }
115
116 return this;
117 }
118
119 /**
120 * Add a new part to the email.
121 *
122 * @param multipart The MimeMultipart.
123 * @return An Email.
124 * @throws EmailException see javax.mail.internet.MimeBodyPart
125 * for definitions
126 * @since 1.0
127 */
128 public Email addPart(MimeMultipart multipart) throws EmailException
129 {
130 try
131 {
132 return addPart(multipart, getContainer().getCount());
133 }
134 catch (MessagingException me)
135 {
136 throw new EmailException(me);
137 }
138 }
139
140 /**
141 * Add a new part to the email.
142 *
143 * @param multipart The part to add.
144 * @param index The index to add at.
145 * @return The email.
146 * @throws EmailException An error occured while adding the part.
147 * @since 1.0
148 */
149 public Email addPart(MimeMultipart multipart, int index) throws EmailException
150 {
151 BodyPart bodyPart = createBodyPart();
152 try
153 {
154 bodyPart.setContent(multipart);
155 getContainer().addBodyPart(bodyPart, index);
156 }
157 catch (MessagingException me)
158 {
159 throw new EmailException(me);
160 }
161
162 return this;
163 }
164
165 /**
166 * Initialize the multipart email.
167 * @since 1.0
168 */
169 protected void init()
170 {
171 if (initialized)
172 {
173 throw new IllegalStateException("Already initialized");
174 }
175
176 container = createMimeMultipart();
177 super.setContent(container);
178
179 initialized = true;
180 }
181
182 /**
183 * Set the message of the email.
184 *
185 * @param msg A String.
186 * @return An Email.
187 * @throws EmailException see javax.mail.internet.MimeBodyPart
188 * for definitions
189 * @since 1.0
190 */
191 @Override
192 public Email setMsg(String msg) throws EmailException
193 {
194 // throw exception on null message
195 if (EmailUtils.isEmpty(msg))
196 {
197 throw new EmailException("Invalid message supplied");
198 }
199 try
200 {
201 BodyPart primary = getPrimaryBodyPart();
202
203 if ((primary instanceof MimePart) && EmailUtils.isNotEmpty(charset))
204 {
205 ((MimePart) primary).setText(msg, charset);
206 }
207 else
208 {
209 primary.setText(msg);
210 }
211 }
212 catch (MessagingException me)
213 {
214 throw new EmailException(me);
215 }
216 return this;
217 }
218
219 /**
220 * Does the work of actually building the MimeMessage. Please note that
221 * a user rarely calls this method directly and only if he/she is
222 * interested in the sending the underlying MimeMessage without
223 * commons-email.
224 *
225 * @exception EmailException if there was an error.
226 * @since 1.0
227 */
228 @Override
229 public void buildMimeMessage() throws EmailException
230 {
231 try
232 {
233 if (primaryBodyPart != null)
234 {
235 // before a multipart message can be sent, we must make sure that
236 // the content for the main body part was actually set. If not,
237 // an IOException will be thrown during super.send().
238
239 BodyPart body = this.getPrimaryBodyPart();
240 try
241 {
242 body.getContent();
243 }
244 catch (IOException e) // NOPMD
245 {
246 // do nothing here.
247 // content will be set to an empty string as a result.
248 // (Should this really be rethrown as an email exception?)
249 // throw new EmailException(e);
250 }
251 }
252
253 if (subType != null)
254 {
255 getContainer().setSubType(subType);
256 }
257
258 super.buildMimeMessage();
259 }
260 catch (MessagingException me)
261 {
262 throw new EmailException(me);
263 }
264 }
265
266 /**
267 * Attach a file.
268 *
269 * @param file A file attachment
270 * @return A MultiPartEmail.
271 * @throws EmailException see javax.mail.internet.MimeBodyPart
272 * for definitions
273 * @since 1.3
274 */
275 public MultiPartEmail attach(File file)
276 throws EmailException
277 {
278 String fileName = file.getAbsolutePath();
279
280 try
281 {
282 if (!file.exists())
283 {
284 throw new IOException("\"" + fileName + "\" does not exist");
285 }
286
287 FileDataSource fds = new FileDataSource(file);
288
289 return attach(fds, file.getName(), null, EmailAttachment.ATTACHMENT);
290 }
291 catch (IOException e)
292 {
293 throw new EmailException("Cannot attach file \"" + fileName + "\"", e);
294 }
295 }
296
297 /**
298 * Attach an EmailAttachment.
299 *
300 * @param attachment An EmailAttachment.
301 * @return A MultiPartEmail.
302 * @throws EmailException see javax.mail.internet.MimeBodyPart
303 * for definitions
304 * @since 1.0
305 */
306 public MultiPartEmail attach(EmailAttachment attachment)
307 throws EmailException
308 {
309 MultiPartEmail result = null;
310
311 if (attachment == null)
312 {
313 throw new EmailException("Invalid attachment supplied");
314 }
315
316 URL url = attachment.getURL();
317
318 if (url == null)
319 {
320 String fileName = null;
321 try
322 {
323 fileName = attachment.getPath();
324 File file = new File(fileName);
325 if (!file.exists())
326 {
327 throw new IOException("\"" + fileName + "\" does not exist");
328 }
329 result =
330 attach(
331 new FileDataSource(file),
332 attachment.getName(),
333 attachment.getDescription(),
334 attachment.getDisposition());
335 }
336 catch (IOException e)
337 {
338 throw new EmailException("Cannot attach file \"" + fileName + "\"", e);
339 }
340 }
341 else
342 {
343 result =
344 attach(
345 url,
346 attachment.getName(),
347 attachment.getDescription(),
348 attachment.getDisposition());
349 }
350
351 return result;
352 }
353
354 /**
355 * Attach a file located by its URL. The disposition of the file
356 * is set to mixed.
357 *
358 * @param url The URL of the file (may be any valid URL).
359 * @param name The name field for the attachment.
360 * @param description A description for the attachment.
361 * @return A MultiPartEmail.
362 * @throws EmailException see javax.mail.internet.MimeBodyPart
363 * for definitions
364 * @since 1.0
365 */
366 public MultiPartEmail attach(URL url, String name, String description)
367 throws EmailException
368 {
369 return attach(url, name, description, EmailAttachment.ATTACHMENT);
370 }
371
372 /**
373 * Attach a file located by its URL.
374 *
375 * @param url The URL of the file (may be any valid URL).
376 * @param name The name field for the attachment.
377 * @param description A description for the attachment.
378 * @param disposition Either mixed or inline.
379 * @return A MultiPartEmail.
380 * @throws EmailException see javax.mail.internet.MimeBodyPart
381 * for definitions
382 * @since 1.0
383 */
384 public MultiPartEmail attach(
385 URL url,
386 String name,
387 String description,
388 String disposition)
389 throws EmailException
390 {
391 // verify that the URL is valid
392 try
393 {
394 InputStream is = url.openStream();
395 is.close();
396 }
397 catch (IOException e)
398 {
399 throw new EmailException("Invalid URL set:" + url, e);
400 }
401
402 return attach(new URLDataSource(url), name, description, disposition);
403 }
404
405 /**
406 * Attach a file specified as a DataSource interface.
407 *
408 * @param ds A DataSource interface for the file.
409 * @param name The name field for the attachment.
410 * @param description A description for the attachment.
411 * @return A MultiPartEmail.
412 * @throws EmailException see javax.mail.internet.MimeBodyPart
413 * for definitions
414 * @since 1.0
415 */
416 public MultiPartEmail attach(
417 DataSource ds,
418 String name,
419 String description)
420 throws EmailException
421 {
422 // verify that the DataSource is valid
423 try
424 {
425 final InputStream is = (ds != null) ? ds.getInputStream() : null;
426 if (is != null)
427 {
428 // close the input stream to prevent file locking on windows
429 is.close();
430 }
431
432 if (is == null)
433 {
434 throw new EmailException("Invalid Datasource");
435 }
436 }
437 catch (IOException e)
438 {
439 throw new EmailException("Invalid Datasource", e);
440 }
441
442 return attach(ds, name, description, EmailAttachment.ATTACHMENT);
443 }
444
445 /**
446 * Attach a file specified as a DataSource interface.
447 *
448 * @param ds A DataSource interface for the file.
449 * @param name The name field for the attachment.
450 * @param description A description for the attachment.
451 * @param disposition Either mixed or inline.
452 * @return A MultiPartEmail.
453 * @throws EmailException see javax.mail.internet.MimeBodyPart
454 * for definitions
455 * @since 1.0
456 */
457 public MultiPartEmail attach(
458 DataSource ds,
459 String name,
460 String description,
461 String disposition)
462 throws EmailException
463 {
464 if (EmailUtils.isEmpty(name))
465 {
466 name = ds.getName();
467 }
468 BodyPart bodyPart = createBodyPart();
469 try
470 {
471 getContainer().addBodyPart(bodyPart);
472
473 bodyPart.setDisposition(disposition);
474 bodyPart.setFileName(name);
475 bodyPart.setDescription(description);
476 bodyPart.setDataHandler(new DataHandler(ds));
477 }
478 catch (MessagingException me)
479 {
480 throw new EmailException(me);
481 }
482 setBoolHasAttachments(true);
483
484 return this;
485 }
486
487 /**
488 * Gets first body part of the message.
489 *
490 * @return The primary body part.
491 * @throws MessagingException An error occurred while getting the primary body part.
492 * @since 1.0
493 */
494 protected BodyPart getPrimaryBodyPart() throws MessagingException
495 {
496 if (!initialized)
497 {
498 init();
499 }
500
501 // Add the first body part to the message. The fist body part must be
502 if (this.primaryBodyPart == null)
503 {
504 primaryBodyPart = createBodyPart();
505 getContainer().addBodyPart(primaryBodyPart, 0);
506 }
507
508 return primaryBodyPart;
509 }
510
511 /**
512 * Gets the message container.
513 *
514 * @return The message container.
515 * @since 1.0
516 */
517 protected MimeMultipart getContainer()
518 {
519 if (!initialized)
520 {
521 init();
522 }
523 return container;
524 }
525
526 /**
527 * Creates a body part object.
528 * Can be overridden if you don't want to create a BodyPart.
529 *
530 * @return the created body part
531 */
532 protected BodyPart createBodyPart()
533 {
534 BodyPart bodyPart = new MimeBodyPart();
535 return bodyPart;
536 }
537
538 /**
539 * Creates a mime multipart object.
540 *
541 * @return the created mime part
542 */
543 protected MimeMultipart createMimeMultipart()
544 {
545 MimeMultipart mmp = new MimeMultipart();
546 return mmp;
547 }
548
549 /**
550 * Checks whether there are attachments.
551 *
552 * @return true if there are attachments
553 * @since 1.0
554 */
555 public boolean isBoolHasAttachments()
556 {
557 return boolHasAttachments;
558 }
559
560 /**
561 * Sets whether there are attachments.
562 *
563 * @param b the attachments flag
564 * @since 1.0
565 */
566 public void setBoolHasAttachments(boolean b)
567 {
568 boolHasAttachments = b;
569 }
570
571 /**
572 * Checks if this object is initialized.
573 *
574 * @return true if initialized
575 */
576 protected boolean isInitialized()
577 {
578 return initialized;
579 }
580
581 /**
582 * Sets the initialized status of this object.
583 *
584 * @param b the initialized status flag
585 */
586 protected void setInitialized(boolean b)
587 {
588 initialized = b;
589 }
590
591 }