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.io.UnsupportedEncodingException; 023import java.net.URL; 024 025import javax.activation.DataHandler; 026import javax.activation.DataSource; 027import javax.activation.FileDataSource; 028import javax.activation.URLDataSource; 029import javax.mail.BodyPart; 030import javax.mail.MessagingException; 031import javax.mail.internet.MimeBodyPart; 032import javax.mail.internet.MimeMultipart; 033import javax.mail.internet.MimePart; 034import javax.mail.internet.MimeUtility; 035 036/** 037 * A multipart email. 038 * 039 * <p>This class is used to send multi-part internet email like 040 * messages with attachments. 041 * 042 * <p>To create a multi-part email, call the default constructor and 043 * then you can call setMsg() to set the message and call the 044 * different attach() methods. 045 * 046 * @since 1.0 047 * @version $Id: MultiPartEmail.java 1606710 2014-06-30 12:29:13Z ggregory $ 048 */ 049public class MultiPartEmail extends Email 050{ 051 /** Body portion of the email. */ 052 private MimeMultipart container; 053 054 /** The message container. */ 055 private BodyPart primaryBodyPart; 056 057 /** The MIME subtype. */ 058 private String subType; 059 060 /** Indicates if the message has been initialized. */ 061 private boolean initialized; 062 063 /** Indicates if attachments have been added to the message. */ 064 private boolean boolHasAttachments; 065 066 /** 067 * Set the MIME subtype of the email. 068 * 069 * @param aSubType MIME subtype of the email 070 * @since 1.0 071 */ 072 public void setSubType(final String aSubType) 073 { 074 this.subType = aSubType; 075 } 076 077 /** 078 * Get the MIME subtype of the email. 079 * 080 * @return MIME subtype of the email 081 * @since 1.0 082 */ 083 public String getSubType() 084 { 085 return subType; 086 } 087 088 /** 089 * Add a new part to the email. 090 * 091 * @param partContent The content. 092 * @param partContentType The content type. 093 * @return An Email. 094 * @throws EmailException see javax.mail.internet.MimeBodyPart 095 * for definitions 096 * @since 1.0 097 */ 098 public Email addPart(final String partContent, final String partContentType) 099 throws EmailException 100 { 101 final BodyPart bodyPart = createBodyPart(); 102 try 103 { 104 bodyPart.setContent(partContent, partContentType); 105 getContainer().addBodyPart(bodyPart); 106 } 107 catch (final MessagingException me) 108 { 109 throw new EmailException(me); 110 } 111 112 return this; 113 } 114 115 /** 116 * Add a new part to the email. 117 * 118 * @param multipart The MimeMultipart. 119 * @return An Email. 120 * @throws EmailException see javax.mail.internet.MimeBodyPart 121 * for definitions 122 * @since 1.0 123 */ 124 public Email addPart(final MimeMultipart multipart) throws EmailException 125 { 126 try 127 { 128 return addPart(multipart, getContainer().getCount()); 129 } 130 catch (final MessagingException me) 131 { 132 throw new EmailException(me); 133 } 134 } 135 136 /** 137 * Add a new part to the email. 138 * 139 * @param multipart The part to add. 140 * @param index The index to add at. 141 * @return The email. 142 * @throws EmailException An error occurred while adding the part. 143 * @since 1.0 144 */ 145 public Email addPart(final MimeMultipart multipart, final int index) throws EmailException 146 { 147 final BodyPart bodyPart = createBodyPart(); 148 try 149 { 150 bodyPart.setContent(multipart); 151 getContainer().addBodyPart(bodyPart, index); 152 } 153 catch (final MessagingException me) 154 { 155 throw new EmailException(me); 156 } 157 158 return this; 159 } 160 161 /** 162 * Initialize the multipart email. 163 * @since 1.0 164 */ 165 protected void init() 166 { 167 if (initialized) 168 { 169 throw new IllegalStateException("Already initialized"); 170 } 171 172 container = createMimeMultipart(); 173 super.setContent(container); 174 175 initialized = true; 176 } 177 178 /** 179 * Set the message of the email. 180 * 181 * @param msg A String. 182 * @return An Email. 183 * @throws EmailException see javax.mail.internet.MimeBodyPart 184 * for definitions 185 * @since 1.0 186 */ 187 @Override 188 public Email setMsg(final String msg) throws EmailException 189 { 190 // throw exception on null message 191 if (EmailUtils.isEmpty(msg)) 192 { 193 throw new EmailException("Invalid message supplied"); 194 } 195 try 196 { 197 final BodyPart primary = getPrimaryBodyPart(); 198 199 if (primary instanceof MimePart && EmailUtils.isNotEmpty(charset)) 200 { 201 ((MimePart) primary).setText(msg, charset); 202 } 203 else 204 { 205 primary.setText(msg); 206 } 207 } 208 catch (final MessagingException me) 209 { 210 throw new EmailException(me); 211 } 212 return this; 213 } 214 215 /** 216 * Does the work of actually building the MimeMessage. Please note that 217 * a user rarely calls this method directly and only if he/she is 218 * interested in the sending the underlying MimeMessage without 219 * commons-email. 220 * 221 * @exception EmailException if there was an error. 222 * @since 1.0 223 */ 224 @Override 225 public void buildMimeMessage() throws EmailException 226 { 227 try 228 { 229 if (primaryBodyPart != null) 230 { 231 // before a multipart message can be sent, we must make sure that 232 // the content for the main body part was actually set. If not, 233 // an IOException will be thrown during super.send(). 234 235 final BodyPart body = this.getPrimaryBodyPart(); 236 try 237 { 238 body.getContent(); 239 } 240 catch (final IOException e) // NOPMD 241 { 242 // do nothing here. 243 // content will be set to an empty string as a result. 244 // (Should this really be rethrown as an email exception?) 245 // throw new EmailException(e); 246 } 247 } 248 249 if (subType != null) 250 { 251 getContainer().setSubType(subType); 252 } 253 254 super.buildMimeMessage(); 255 } 256 catch (final MessagingException me) 257 { 258 throw new EmailException(me); 259 } 260 } 261 262 /** 263 * Attach a file. 264 * 265 * @param file A file attachment 266 * @return A MultiPartEmail. 267 * @throws EmailException see javax.mail.internet.MimeBodyPart 268 * for definitions 269 * @since 1.3 270 */ 271 public MultiPartEmail attach(final File file) 272 throws EmailException 273 { 274 final String fileName = file.getAbsolutePath(); 275 276 try 277 { 278 if (!file.exists()) 279 { 280 throw new IOException("\"" + fileName + "\" does not exist"); 281 } 282 283 final FileDataSource fds = new FileDataSource(file); 284 285 return attach(fds, file.getName(), null, EmailAttachment.ATTACHMENT); 286 } 287 catch (final IOException e) 288 { 289 throw new EmailException("Cannot attach file \"" + fileName + "\"", e); 290 } 291 } 292 293 /** 294 * Attach an EmailAttachment. 295 * 296 * @param attachment An EmailAttachment. 297 * @return A MultiPartEmail. 298 * @throws EmailException see javax.mail.internet.MimeBodyPart 299 * for definitions 300 * @since 1.0 301 */ 302 public MultiPartEmail attach(final EmailAttachment attachment) 303 throws EmailException 304 { 305 MultiPartEmail result = null; 306 307 if (attachment == null) 308 { 309 throw new EmailException("Invalid attachment supplied"); 310 } 311 312 final URL url = attachment.getURL(); 313 314 if (url == null) 315 { 316 String fileName = null; 317 try 318 { 319 fileName = attachment.getPath(); 320 final File file = new File(fileName); 321 if (!file.exists()) 322 { 323 throw new IOException("\"" + fileName + "\" does not exist"); 324 } 325 result = 326 attach( 327 new FileDataSource(file), 328 attachment.getName(), 329 attachment.getDescription(), 330 attachment.getDisposition()); 331 } 332 catch (final IOException e) 333 { 334 throw new EmailException("Cannot attach file \"" + fileName + "\"", e); 335 } 336 } 337 else 338 { 339 result = 340 attach( 341 url, 342 attachment.getName(), 343 attachment.getDescription(), 344 attachment.getDisposition()); 345 } 346 347 return result; 348 } 349 350 /** 351 * Attach a file located by its URL. The disposition of the file 352 * is set to mixed. 353 * 354 * @param url The URL of the file (may be any valid URL). 355 * @param name The name field for the attachment. 356 * @param description A description for the attachment. 357 * @return A MultiPartEmail. 358 * @throws EmailException see javax.mail.internet.MimeBodyPart 359 * for definitions 360 * @since 1.0 361 */ 362 public MultiPartEmail attach(final URL url, final String name, final String description) 363 throws EmailException 364 { 365 return attach(url, name, description, EmailAttachment.ATTACHMENT); 366 } 367 368 /** 369 * Attach a file located by its URL. 370 * 371 * @param url The URL of the file (may be any valid URL). 372 * @param name The name field for the attachment. 373 * @param description A description for the attachment. 374 * @param disposition Either mixed or inline. 375 * @return A MultiPartEmail. 376 * @throws EmailException see javax.mail.internet.MimeBodyPart 377 * for definitions 378 * @since 1.0 379 */ 380 public MultiPartEmail attach( 381 final URL url, 382 final String name, 383 final String description, 384 final String disposition) 385 throws EmailException 386 { 387 // verify that the URL is valid 388 try 389 { 390 final InputStream is = url.openStream(); 391 is.close(); 392 } 393 catch (final IOException e) 394 { 395 throw new EmailException("Invalid URL set:" + url, e); 396 } 397 398 return attach(new URLDataSource(url), name, description, disposition); 399 } 400 401 /** 402 * Attach a file specified as a DataSource interface. 403 * 404 * @param ds A DataSource interface for the file. 405 * @param name The name field for the attachment. 406 * @param description A description for the attachment. 407 * @return A MultiPartEmail. 408 * @throws EmailException see javax.mail.internet.MimeBodyPart 409 * for definitions 410 * @since 1.0 411 */ 412 public MultiPartEmail attach( 413 final DataSource ds, 414 final String name, 415 final String description) 416 throws EmailException 417 { 418 // verify that the DataSource is valid 419 try 420 { 421 final InputStream is = ds != null ? ds.getInputStream() : null; 422 if (is != null) 423 { 424 // close the input stream to prevent file locking on windows 425 is.close(); 426 } 427 428 if (is == null) 429 { 430 throw new EmailException("Invalid Datasource"); 431 } 432 } 433 catch (final IOException e) 434 { 435 throw new EmailException("Invalid Datasource", e); 436 } 437 438 return attach(ds, name, description, EmailAttachment.ATTACHMENT); 439 } 440 441 /** 442 * Attach a file specified as a DataSource interface. 443 * 444 * @param ds A DataSource interface for the file. 445 * @param name The name field for the attachment. 446 * @param description A description for the attachment. 447 * @param disposition Either mixed or inline. 448 * @return A MultiPartEmail. 449 * @throws EmailException see javax.mail.internet.MimeBodyPart 450 * for definitions 451 * @since 1.0 452 */ 453 public MultiPartEmail attach( 454 final DataSource ds, 455 String name, 456 final String description, 457 final String disposition) 458 throws EmailException 459 { 460 if (EmailUtils.isEmpty(name)) 461 { 462 name = ds.getName(); 463 } 464 final BodyPart bodyPart = createBodyPart(); 465 try 466 { 467 bodyPart.setDisposition(disposition); 468 bodyPart.setFileName(MimeUtility.encodeText(name)); 469 bodyPart.setDescription(description); 470 bodyPart.setDataHandler(new DataHandler(ds)); 471 472 getContainer().addBodyPart(bodyPart); 473 } 474 catch (final UnsupportedEncodingException uee) 475 { 476 // in case the filename could not be encoded 477 throw new EmailException(uee); 478 } 479 catch (final MessagingException me) 480 { 481 throw new EmailException(me); 482 } 483 setBoolHasAttachments(true); 484 485 return this; 486 } 487 488 /** 489 * Gets first body part of the message. 490 * 491 * @return The primary body part. 492 * @throws MessagingException An error occurred while getting the primary body part. 493 * @since 1.0 494 */ 495 protected BodyPart getPrimaryBodyPart() throws MessagingException 496 { 497 if (!initialized) 498 { 499 init(); 500 } 501 502 // Add the first body part to the message. The fist body part must be 503 if (this.primaryBodyPart == null) 504 { 505 primaryBodyPart = createBodyPart(); 506 getContainer().addBodyPart(primaryBodyPart, 0); 507 } 508 509 return primaryBodyPart; 510 } 511 512 /** 513 * Gets the message container. 514 * 515 * @return The message container. 516 * @since 1.0 517 */ 518 protected MimeMultipart getContainer() 519 { 520 if (!initialized) 521 { 522 init(); 523 } 524 return container; 525 } 526 527 /** 528 * Creates a body part object. 529 * Can be overridden if you don't want to create a BodyPart. 530 * 531 * @return the created body part 532 */ 533 protected BodyPart createBodyPart() 534 { 535 return new MimeBodyPart(); 536 } 537 538 /** 539 * Creates a mime multipart object. 540 * 541 * @return the created mime part 542 */ 543 protected MimeMultipart createMimeMultipart() 544 { 545 return new MimeMultipart(); 546 } 547 548 /** 549 * Checks whether there are attachments. 550 * 551 * @return true if there are attachments 552 * @since 1.0 553 */ 554 public boolean isBoolHasAttachments() 555 { 556 return boolHasAttachments; 557 } 558 559 /** 560 * Sets whether there are attachments. 561 * 562 * @param b the attachments flag 563 * @since 1.0 564 */ 565 public void setBoolHasAttachments(final boolean b) 566 { 567 boolHasAttachments = b; 568 } 569 570 /** 571 * Checks if this object is initialized. 572 * 573 * @return true if initialized 574 */ 575 protected boolean isInitialized() 576 { 577 return initialized; 578 } 579 580 /** 581 * Sets the initialized status of this object. 582 * 583 * @param b the initialized status flag 584 */ 585 protected void setInitialized(final boolean b) 586 { 587 initialized = b; 588 } 589 590}