001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.mail; 018 019import java.io.File; 020import java.io.IOException; 021import java.io.InputStream; 022import java.net.URL; 023 024import javax.activation.DataHandler; 025import javax.activation.DataSource; 026import javax.activation.FileDataSource; 027import javax.activation.URLDataSource; 028import javax.mail.BodyPart; 029import javax.mail.MessagingException; 030import javax.mail.internet.MimeBodyPart; 031import javax.mail.internet.MimeMultipart; 032import javax.mail.internet.MimePart; 033 034/** 035 * A multipart email. 036 * 037 * <p>This class is used to send multi-part internet email like 038 * messages with attachments. 039 * 040 * <p>To create a multi-part email, call the default constructor and 041 * then you can call setMsg() to set the message and call the 042 * different attach() methods. 043 * 044 * @since 1.0 045 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a> 046 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a> 047 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a> 048 * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a> 049 * @author <a href="mailto:unknown">Regis Koenig</a> 050 * @author <a href="mailto:corey.scott@gmail.com">Corey Scott</a> 051 * @version $Id: MultiPartEmail.java 1420402 2012-12-11 20:59:15Z tn $ 052 */ 053public class MultiPartEmail extends Email 054{ 055 /** Body portion of the email. */ 056 private MimeMultipart container; 057 058 /** The message container. */ 059 private BodyPart primaryBodyPart; 060 061 /** The MIME subtype. */ 062 private String subType; 063 064 /** Indicates if the message has been initialized. */ 065 private boolean initialized; 066 067 /** Indicates if attachments have been added to the message. */ 068 private boolean boolHasAttachments; 069 070 /** 071 * Set the MIME subtype of the email. 072 * 073 * @param aSubType MIME subtype of the email 074 * @since 1.0 075 */ 076 public void setSubType(String aSubType) 077 { 078 this.subType = aSubType; 079 } 080 081 /** 082 * Get the MIME subtype of the email. 083 * 084 * @return MIME subtype of the email 085 * @since 1.0 086 */ 087 public String getSubType() 088 { 089 return subType; 090 } 091 092 /** 093 * Add a new part to the email. 094 * 095 * @param partContent The content. 096 * @param partContentType The content type. 097 * @return An Email. 098 * @throws EmailException see javax.mail.internet.MimeBodyPart 099 * 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}