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 */
017 package org.apache.commons.mail;
018
019 import java.io.UnsupportedEncodingException;
020 import java.nio.charset.Charset;
021 import java.util.ArrayList;
022 import java.util.Collection;
023 import java.util.Date;
024 import java.util.HashMap;
025 import java.util.Iterator;
026 import java.util.List;
027 import java.util.Map;
028 import java.util.Properties;
029
030 import javax.mail.Authenticator;
031 import javax.mail.Message;
032 import javax.mail.MessagingException;
033 import javax.mail.Session;
034 import javax.mail.Store;
035 import javax.mail.Transport;
036 import javax.mail.internet.AddressException;
037 import javax.mail.internet.InternetAddress;
038 import javax.mail.internet.MimeMessage;
039 import javax.mail.internet.MimeMultipart;
040 import javax.naming.Context;
041 import javax.naming.InitialContext;
042 import javax.naming.NamingException;
043
044 /**
045 * The base class for all email messages. This class sets the
046 * sender's email & name, receiver's email & name, subject, and the
047 * sent date. Subclasses are responsible for setting the message
048 * body.
049 *
050 * @since 1.0
051 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
052 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
053 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
054 * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
055 * @author <a href="mailto:greg@shwoop.com">Greg Ritter</a>
056 * @author <a href="mailto:unknown">Regis Koenig</a>
057 * @author <a href="mailto:colin.chalmers@maxware.nl">Colin Chalmers</a>
058 * @author <a href="mailto:matthias@wessendorf.net">Matthias Wessendorf</a>
059 * @author <a href="mailto:corey.scott@gmail.com">Corey Scott</a>
060 * @version $Revision: 783910 $ $Date: 2009-06-11 23:13:54 +0200 (Thu, 11 Jun 2009) $
061 * @version $Id: Email.java 783910 2009-06-11 21:13:54Z sgoeschl $
062 */
063 public abstract class Email
064 {
065 /** Constants used by Email classes. */
066
067 /** */
068 public static final String SENDER_EMAIL = "sender.email";
069 /** */
070 public static final String SENDER_NAME = "sender.name";
071 /** */
072 public static final String RECEIVER_EMAIL = "receiver.email";
073 /** */
074 public static final String RECEIVER_NAME = "receiver.name";
075 /** */
076 public static final String EMAIL_SUBJECT = "email.subject";
077 /** */
078 public static final String EMAIL_BODY = "email.body";
079 /** */
080 public static final String CONTENT_TYPE = "content.type";
081
082 /** */
083 public static final String MAIL_HOST = "mail.smtp.host";
084 /** */
085 public static final String MAIL_PORT = "mail.smtp.port";
086 /** */
087 public static final String MAIL_SMTP_FROM = "mail.smtp.from";
088 /** */
089 public static final String MAIL_SMTP_AUTH = "mail.smtp.auth";
090 /** */
091 public static final String MAIL_SMTP_USER = "mail.smtp.user";
092 /** */
093 public static final String MAIL_SMTP_PASSWORD = "mail.smtp.password";
094 /** */
095 public static final String MAIL_TRANSPORT_PROTOCOL =
096 "mail.transport.protocol";
097 /**
098 * @since 1.1
099 */
100 public static final String MAIL_TRANSPORT_TLS = "mail.smtp.starttls.enable";
101 /** */
102 public static final String MAIL_SMTP_SOCKET_FACTORY_FALLBACK = "mail.smtp.socketFactory.fallback";
103 /** */
104 public static final String MAIL_SMTP_SOCKET_FACTORY_CLASS = "mail.smtp.socketFactory.class";
105 /** */
106 public static final String MAIL_SMTP_SOCKET_FACTORY_PORT = "mail.smtp.socketFactory.port";
107
108
109 /**
110 * Socket connection timeout value in milliseconds. Default is infinite timeout.
111 * @since 1.2
112 */
113 public static final String MAIL_SMTP_CONNECTIONTIMEOUT = "mail.smtp.connectiontimeout";
114
115 /**
116 * Socket I/O timeout value in milliseconds. Default is infinite timeout.
117 * @since 1.2
118 */
119 public static final String MAIL_SMTP_TIMEOUT = "mail.smtp.timeout";
120
121
122 /** */
123 public static final String SMTP = "smtp";
124 /** */
125 public static final String TEXT_HTML = "text/html";
126 /** */
127 public static final String TEXT_PLAIN = "text/plain";
128 /** */
129 public static final String ATTACHMENTS = "attachments";
130 /** */
131 public static final String FILE_SERVER = "file.server";
132 /** */
133 public static final String MAIL_DEBUG = "mail.debug";
134
135 /** */
136 public static final String KOI8_R = "koi8-r";
137 /** */
138 public static final String ISO_8859_1 = "iso-8859-1";
139 /** */
140 public static final String US_ASCII = "us-ascii";
141
142 /** The email message to send. */
143 protected MimeMessage message;
144
145 /** The charset to use for this message */
146 protected String charset;
147
148 /** The Address of the sending party, mandatory */
149 protected InternetAddress fromAddress;
150
151 /** The Subject */
152 protected String subject;
153
154 /** An attachment */
155 protected MimeMultipart emailBody;
156
157 /** The content */
158 protected Object content;
159
160 /** The content type */
161 protected String contentType;
162
163 /** Set session debugging on or off */
164 protected boolean debug;
165
166 /** Sent date */
167 protected Date sentDate;
168
169 /**
170 * Instance of an <code>Authenticator</code> object that will be used
171 * when authentication is requested from the mail server.
172 */
173 protected Authenticator authenticator;
174
175 /**
176 * The hostname of the mail server with which to connect. If null will try
177 * to get property from system.properties. If still null, quit
178 */
179 protected String hostName;
180
181 /**
182 * The port number of the mail server to connect to.
183 * Defaults to the standard port ( 25 ).
184 */
185 protected String smtpPort = "25";
186
187 /**
188 * The port number of the SSL enabled SMTP server;
189 * defaults to the standard port, 465.
190 */
191 protected String sslSmtpPort = "465";
192
193 /** List of "to" email adresses */
194 protected List toList = new ArrayList();
195
196 /** List of "cc" email adresses */
197 protected List ccList = new ArrayList();
198
199 /** List of "bcc" email adresses */
200 protected List bccList = new ArrayList();
201
202 /** List of "replyTo" email adresses */
203 protected List replyList = new ArrayList();
204
205 /**
206 * Address to which undeliverable mail should be sent.
207 * Because this is handled by JavaMail as a String property
208 * in the mail session, this property is of type <code>String</code>
209 * rather than <code>InternetAddress</code>.
210 */
211 protected String bounceAddress;
212
213 /**
214 * Used to specify the mail headers. Example:
215 *
216 * X-Mailer: Sendmail, X-Priority: 1( highest )
217 * or 2( high ) 3( normal ) 4( low ) and 5( lowest )
218 * Disposition-Notification-To: user@domain.net
219 */
220 protected Map headers = new HashMap();
221
222 /**
223 * Used to determine whether to use pop3 before smtp, and if so the settings.
224 */
225 protected boolean popBeforeSmtp;
226 /** the host name of the pop3 server */
227 protected String popHost;
228 /** the user name to log into the pop3 server */
229 protected String popUsername;
230 /** the password to log into the pop3 server */
231 protected String popPassword;
232
233 /** does server require TLS encryption for authentication */
234 protected boolean tls;
235 /** does the current transport use SSL encryption? */
236 protected boolean ssl;
237
238 /** socket I/O timeout value in milliseconds */
239 protected int socketTimeout;
240 /** socket connection timeout value in milliseconds */
241 protected int socketConnectionTimeout;
242
243 /** The Session to mail with */
244 private Session session;
245
246 /**
247 * Setting to true will enable the display of debug information.
248 *
249 * @param d A boolean.
250 * @since 1.0
251 */
252 public void setDebug(boolean d)
253 {
254 this.debug = d;
255 }
256
257 /**
258 * Sets the userName and password if authentication is needed. If this
259 * method is not used, no authentication will be performed.
260 * <p>
261 * This method will create a new instance of
262 * <code>DefaultAuthenticator</code> using the supplied parameters.
263 *
264 * @param userName User name for the SMTP server
265 * @param password password for the SMTP server
266 * @see DefaultAuthenticator
267 * @see #setAuthenticator
268 * @since 1.0
269 */
270 public void setAuthentication(String userName, String password)
271 {
272 this.authenticator = new DefaultAuthenticator(userName, password);
273 this.setAuthenticator(this.authenticator);
274 }
275
276 /**
277 * Sets the <code>Authenticator</code> to be used when authentication
278 * is requested from the mail server.
279 * <p>
280 * This method should be used when your outgoing mail server requires
281 * authentication. Your mail server must also support RFC2554.
282 *
283 * @param newAuthenticator the <code>Authenticator</code> object.
284 * @see Authenticator
285 * @since 1.0
286 */
287 public void setAuthenticator(Authenticator newAuthenticator)
288 {
289 this.authenticator = newAuthenticator;
290 }
291
292 /**
293 * Set the charset of the message.
294 *
295 * @param newCharset A String.
296 * @throws java.nio.charset.IllegalCharsetNameException if the charset name is invalid
297 * @throws java.nio.charset.UnsupportedCharsetException if no support for the named charset
298 * exists in the current JVM
299 * @since 1.0
300 */
301 public void setCharset(String newCharset)
302 {
303 Charset set = Charset.forName(newCharset);
304 this.charset = set.name();
305 }
306
307 /**
308 * Set the emailBody to a MimeMultiPart
309 *
310 * @param aMimeMultipart aMimeMultipart
311 * @since 1.0
312 */
313 public void setContent(MimeMultipart aMimeMultipart)
314 {
315 this.emailBody = aMimeMultipart;
316 }
317
318 /**
319 * Set the content & contentType
320 *
321 * @param aObject aObject
322 * @param aContentType aContentType
323 * @since 1.0
324 */
325 public void setContent(Object aObject, String aContentType)
326 {
327 this.content = aObject;
328 this.updateContentType(aContentType);
329 }
330
331
332 /**
333 * Update the contentType.
334 *
335 * @param aContentType aContentType
336 * @since 1.2
337 */
338 public void updateContentType(final String aContentType)
339 {
340 if (EmailUtils.isEmpty(aContentType))
341 {
342 this.contentType = null;
343 }
344 else
345 {
346 // set the content type
347 this.contentType = aContentType;
348
349 // set the charset if the input was properly formed
350 String strMarker = "; charset=";
351 int charsetPos = aContentType.toLowerCase().indexOf(strMarker);
352
353 if (charsetPos != -1)
354 {
355 // find the next space (after the marker)
356 charsetPos += strMarker.length();
357 int intCharsetEnd =
358 aContentType.toLowerCase().indexOf(" ", charsetPos);
359
360 if (intCharsetEnd != -1)
361 {
362 this.charset =
363 aContentType.substring(charsetPos, intCharsetEnd);
364 }
365 else
366 {
367 this.charset = aContentType.substring(charsetPos);
368 }
369 }
370 else
371 {
372 // use the default charset, if one exists, for messages
373 // whose content-type is some form of text.
374 if (this.contentType.startsWith("text/") && EmailUtils.isNotEmpty(this.charset))
375 {
376 StringBuffer contentTypeBuf = new StringBuffer(this.contentType);
377 contentTypeBuf.append(strMarker);
378 contentTypeBuf.append(this.charset);
379 this.contentType = contentTypeBuf.toString();
380 }
381 }
382 }
383 }
384
385 /**
386 * Set the hostname of the outgoing mail server
387 *
388 * @param aHostName aHostName
389 * @since 1.0
390 */
391 public void setHostName(String aHostName)
392 {
393 this.hostName = aHostName;
394 }
395
396 /**
397 * Set or disable the TLS encryption
398 *
399 * @param withTLS true if TLS needed, false otherwise
400 * @since 1.1
401 */
402 public void setTLS(boolean withTLS)
403 {
404 this.tls = withTLS;
405 }
406
407 /**
408 * Set the port number of the outgoing mail server.
409 * @param aPortNumber aPortNumber
410 * @since 1.0
411 */
412 public void setSmtpPort(int aPortNumber)
413 {
414 if (aPortNumber < 1)
415 {
416 throw new IllegalArgumentException(
417 "Cannot connect to a port number that is less than 1 ( "
418 + aPortNumber
419 + " )");
420 }
421
422 this.smtpPort = Integer.toString(aPortNumber);
423 }
424
425 /**
426 * Supply a mail Session object to use. Please note that passing
427 * a username and password (in the case of mail authentication) will
428 * create a new mail session with a DefaultAuthenticator. This is a
429 * convience but might come unexpected.
430 *
431 * If mail authentication is used but NO username and password
432 * is supplied the implementation assumes that you have set a
433 * authenticator and will use the existing mail session (as expected).
434 *
435 * @param aSession mail session to be used
436 * @since 1.0
437 */
438 public void setMailSession(Session aSession)
439 {
440 EmailUtils.notNull(aSession, "no mail session supplied");
441
442 Properties sessionProperties = aSession.getProperties();
443 String auth = sessionProperties.getProperty(MAIL_SMTP_AUTH);
444
445 if ("true".equalsIgnoreCase(auth))
446 {
447 String userName = sessionProperties.getProperty(MAIL_SMTP_USER);
448 String password = sessionProperties.getProperty(MAIL_SMTP_PASSWORD);
449
450 if (EmailUtils.isNotEmpty(userName) && EmailUtils.isNotEmpty(password))
451 {
452 // only create a new mail session with an authenticator if
453 // authentication is required and no user name is given
454 this.authenticator = new DefaultAuthenticator(userName, password);
455 this.session = Session.getInstance(sessionProperties, this.authenticator);
456 }
457 else
458 {
459 // assume that the given mail session contains a working authenticator
460 this.session = aSession;
461 }
462 }
463 else
464 {
465 this.session = aSession;
466 }
467 }
468
469 /**
470 * Supply a mail Session object from a JNDI directory
471 * @param jndiName name of JNDI ressource (javax.mail.Session type), ressource
472 * if searched in java:comp/env if name dont start with "java:"
473 * @throws IllegalArgumentException JNDI name null or empty
474 * @throws NamingException ressource can be retrieved from JNDI directory
475 * @since 1.1
476 */
477 public void setMailSessionFromJNDI(String jndiName) throws NamingException
478 {
479 if (EmailUtils.isEmpty(jndiName))
480 {
481 throw new IllegalArgumentException("JNDI name missing");
482 }
483 Context ctx = null;
484 if (jndiName.startsWith("java:"))
485 {
486 ctx = new InitialContext();
487 }
488 else
489 {
490 ctx = (Context) new InitialContext().lookup("java:comp/env");
491
492 }
493 this.setMailSession((Session) ctx.lookup(jndiName));
494 }
495
496 /**
497 * Initialise a mailsession object
498 *
499 * @return A Session.
500 * @throws EmailException thrown when host name was not set.
501 * @since 1.0
502 */
503 public Session getMailSession() throws EmailException
504 {
505 if (this.session == null)
506 {
507 Properties properties = new Properties(System.getProperties());
508 properties.setProperty(MAIL_TRANSPORT_PROTOCOL, SMTP);
509
510 if (EmailUtils.isEmpty(this.hostName))
511 {
512 this.hostName = properties.getProperty(MAIL_HOST);
513 }
514
515 if (EmailUtils.isEmpty(this.hostName))
516 {
517 throw new EmailException(
518 "Cannot find valid hostname for mail session");
519 }
520
521 properties.setProperty(MAIL_PORT, smtpPort);
522 properties.setProperty(MAIL_HOST, hostName);
523 properties.setProperty(MAIL_DEBUG, String.valueOf(this.debug));
524
525 if (this.authenticator != null)
526 {
527 properties.setProperty(MAIL_TRANSPORT_TLS, tls ? "true" : "false");
528 properties.setProperty(MAIL_SMTP_AUTH, "true");
529 }
530
531 if (this.ssl)
532 {
533 properties.setProperty(MAIL_PORT, sslSmtpPort);
534 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_PORT, sslSmtpPort);
535 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
536 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_FALLBACK, "false");
537 }
538
539 if (this.bounceAddress != null)
540 {
541 properties.setProperty(MAIL_SMTP_FROM, this.bounceAddress);
542 }
543
544 if (this.socketTimeout > 0)
545 {
546 properties.setProperty(MAIL_SMTP_TIMEOUT, Integer.toString(this.socketTimeout));
547 }
548
549 if (this.socketConnectionTimeout > 0)
550 {
551 properties.setProperty(MAIL_SMTP_CONNECTIONTIMEOUT, Integer.toString(this.socketConnectionTimeout));
552 }
553
554 // changed this (back) to getInstance due to security exceptions
555 // caused when testing using maven
556 this.session =
557 Session.getInstance(properties, this.authenticator);
558 }
559 return this.session;
560 }
561
562 /**
563 * Creates a InternetAddress.
564 *
565 * @param email An email address.
566 * @param name A name.
567 * @param charsetName The name of the charset to encode the name with.
568 * @return An internet address.
569 * @throws EmailException Thrown when the supplied address, name or charset were invalid.
570 */
571 private InternetAddress createInternetAddress(String email, String name, String charsetName)
572 throws EmailException
573 {
574 InternetAddress address = null;
575
576 try
577 {
578 address = new InternetAddress(email);
579
580 // check name input
581 if (EmailUtils.isEmpty(name))
582 {
583 name = email;
584 }
585
586 // check charset input.
587 if (EmailUtils.isEmpty(charsetName))
588 {
589 address.setPersonal(name);
590 }
591 else
592 {
593 // canonicalize the charset name and make sure
594 // the current platform supports it.
595 Charset set = Charset.forName(charsetName);
596 address.setPersonal(name, set.name());
597 }
598
599 // run sanity check on new InternetAddress object; if this fails
600 // it will throw AddressException.
601 address.validate();
602 }
603 catch (AddressException e)
604 {
605 throw new EmailException(e);
606 }
607 catch (UnsupportedEncodingException e)
608 {
609 throw new EmailException(e);
610 }
611 return address;
612 }
613
614
615 /**
616 * Set the FROM field of the email to use the specified address. The email
617 * address will also be used as the personal name.
618 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
619 * If it is not set, it will be encoded using
620 * the Java platform's default charset (UTF-16) if it contains
621 * non-ASCII characters; otherwise, it is used as is.
622 *
623 * @param email A String.
624 * @return An Email.
625 * @throws EmailException Indicates an invalid email address.
626 * @since 1.0
627 */
628 public Email setFrom(String email)
629 throws EmailException
630 {
631 return setFrom(email, null);
632 }
633
634 /**
635 * Set the FROM field of the email to use the specified address and the
636 * specified personal name.
637 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
638 * If it is not set, it will be encoded using
639 * the Java platform's default charset (UTF-16) if it contains
640 * non-ASCII characters; otherwise, it is used as is.
641 *
642 * @param email A String.
643 * @param name A String.
644 * @throws EmailException Indicates an invalid email address.
645 * @return An Email.
646 * @since 1.0
647 */
648 public Email setFrom(String email, String name)
649 throws EmailException
650 {
651 return setFrom(email, name, this.charset);
652 }
653
654 /**
655 * Set the FROM field of the email to use the specified address, personal
656 * name, and charset encoding for the name.
657 *
658 * @param email A String.
659 * @param name A String.
660 * @param charset The charset to encode the name with.
661 * @throws EmailException Indicates an invalid email address or charset.
662 * @return An Email.
663 * @since 1.1
664 */
665 public Email setFrom(String email, String name, String charset)
666 throws EmailException
667 {
668 this.fromAddress = createInternetAddress(email, name, charset);
669 return this;
670 }
671
672 /**
673 * Add a recipient TO to the email. The email
674 * address will also be used as the personal name.
675 * The name will be encoded by the charset of
676 * {@link #setCharset(java.lang.String) setCharset()}.
677 * If it is not set, it will be encoded using
678 * the Java platform's default charset (UTF-16) if it contains
679 * non-ASCII characters; otherwise, it is used as is.
680 *
681 * @param email A String.
682 * @throws EmailException Indicates an invalid email address.
683 * @return An Email.
684 * @since 1.0
685 */
686 public Email addTo(String email)
687 throws EmailException
688 {
689 return addTo(email, null);
690 }
691
692 /**
693 * Add a recipient TO to the email using the specified address and the
694 * specified personal name.
695 * The name will be encoded by the charset of
696 * {@link #setCharset(java.lang.String) setCharset()}.
697 * If it is not set, it will be encoded using
698 * the Java platform's default charset (UTF-16) if it contains
699 * non-ASCII characters; otherwise, it is used as is.
700 *
701 * @param email A String.
702 * @param name A String.
703 * @throws EmailException Indicates an invalid email address.
704 * @return An Email.
705 * @since 1.0
706 */
707 public Email addTo(String email, String name)
708 throws EmailException
709 {
710 return addTo(email, name, this.charset);
711 }
712
713 /**
714 * Add a recipient TO to the email using the specified address, personal
715 * name, and charset encoding for the name.
716 *
717 * @param email A String.
718 * @param name A String.
719 * @param charset The charset to encode the name with.
720 * @throws EmailException Indicates an invalid email address or charset.
721 * @return An Email.
722 * @since 1.1
723 */
724 public Email addTo(String email, String name, String charset)
725 throws EmailException
726 {
727 this.toList.add(createInternetAddress(email, name, charset));
728 return this;
729 }
730
731 /**
732 * Set a list of "TO" addresses. All elements in the specified
733 * <code>Collection</code> are expected to be of type
734 * <code>java.mail.internet.InternetAddress</code>.
735 *
736 * @param aCollection collection of <code>InternetAddress</code> objects.
737 * @throws EmailException Indicates an invalid email address.
738 * @return An Email.
739 * @see javax.mail.internet.InternetAddress
740 * @since 1.0
741 */
742 public Email setTo(Collection aCollection) throws EmailException
743 {
744 if (aCollection == null || aCollection.isEmpty())
745 {
746 throw new EmailException("Address List provided was invalid");
747 }
748
749 this.toList = new ArrayList(aCollection);
750 return this;
751 }
752
753 /**
754 * Add a recipient CC to the email. The email
755 * address will also be used as the personal name.
756 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
757 * If it is not set, it will be encoded using
758 * the Java platform's default charset (UTF-16) if it contains
759 * non-ASCII characters; otherwise, it is used as is.
760 *
761 * @param email A String.
762 * @return An Email.
763 * @throws EmailException Indicates an invalid email address.
764 * @since 1.0
765 */
766 public Email addCc(String email)
767 throws EmailException
768 {
769 return this.addCc(email, null);
770 }
771
772 /**
773 * Add a recipient CC to the email using the specified address and the
774 * specified personal name.
775 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
776 * If it is not set, it will be encoded using
777 * the Java platform's default charset (UTF-16) if it contains
778 * non-ASCII characters; otherwise, it is used as is.
779 *
780 * @param email A String.
781 * @param name A String.
782 * @throws EmailException Indicates an invalid email address.
783 * @return An Email.
784 * @since 1.0
785 */
786 public Email addCc(String email, String name)
787 throws EmailException
788 {
789 return addCc(email, name, this.charset);
790 }
791
792 /**
793 * Add a recipient CC to the email using the specified address, personal
794 * name, and charset encoding for the name.
795 *
796 * @param email A String.
797 * @param name A String.
798 * @param charset The charset to encode the name with.
799 * @throws EmailException Indicates an invalid email address or charset.
800 * @return An Email.
801 * @since 1.1
802 */
803 public Email addCc(String email, String name, String charset)
804 throws EmailException
805 {
806 this.ccList.add(createInternetAddress(email, name, charset));
807 return this;
808 }
809
810 /**
811 * Set a list of "CC" addresses. All elements in the specified
812 * <code>Collection</code> are expected to be of type
813 * <code>java.mail.internet.InternetAddress</code>.
814 *
815 * @param aCollection collection of <code>InternetAddress</code> objects.
816 * @return An Email.
817 * @throws EmailException Indicates an invalid email address.
818 * @see javax.mail.internet.InternetAddress
819 * @since 1.0
820 */
821 public Email setCc(Collection aCollection) throws EmailException
822 {
823 if (aCollection == null || aCollection.isEmpty())
824 {
825 throw new EmailException("Address List provided was invalid");
826 }
827
828 this.ccList = new ArrayList(aCollection);
829 return this;
830 }
831
832 /**
833 * Add a blind BCC recipient to the email. The email
834 * address will also be used as the personal name.
835 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
836 * If it is not set, it will be encoded using
837 * the Java platform's default charset (UTF-16) if it contains
838 * non-ASCII characters; otherwise, it is used as is.
839 *
840 * @param email A String.
841 * @return An Email.
842 * @throws EmailException Indicates an invalid email address
843 * @since 1.0
844 */
845 public Email addBcc(String email)
846 throws EmailException
847 {
848 return this.addBcc(email, null);
849 }
850
851 /**
852 * Add a blind BCC recipient to the email using the specified address and
853 * the specified personal name.
854 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
855 * If it is not set, it will be encoded using
856 * the Java platform's default charset (UTF-16) if it contains
857 * non-ASCII characters; otherwise, it is used as is.
858 *
859 * @param email A String.
860 * @param name A String.
861 * @return An Email.
862 * @throws EmailException Indicates an invalid email address
863 * @since 1.0
864 */
865 public Email addBcc(String email, String name)
866 throws EmailException
867 {
868 return addBcc(email, name, this.charset);
869 }
870
871 /**
872 * Add a blind BCC recipient to the email using the specified address,
873 * personal name, and charset encoding for the name.
874 *
875 * @param email A String.
876 * @param name A String.
877 * @param charset The charset to encode the name with.
878 * @return An Email.
879 * @throws EmailException Indicates an invalid email address
880 * @since 1.1
881 */
882 public Email addBcc(String email, String name, String charset)
883 throws EmailException
884 {
885 this.bccList.add(createInternetAddress(email, name, charset));
886 return this;
887 }
888
889 /**
890 * Set a list of "BCC" addresses. All elements in the specified
891 * <code>Collection</code> are expected to be of type
892 * <code>java.mail.internet.InternetAddress</code>.
893 *
894 * @param aCollection collection of <code>InternetAddress</code> objects
895 * @return An Email.
896 * @throws EmailException Indicates an invalid email address
897 * @see javax.mail.internet.InternetAddress
898 * @since 1.0
899 */
900 public Email setBcc(Collection aCollection) throws EmailException
901 {
902 if (aCollection == null || aCollection.isEmpty())
903 {
904 throw new EmailException("Address List provided was invalid");
905 }
906
907 this.bccList = new ArrayList(aCollection);
908 return this;
909 }
910
911 /**
912 * Add a reply to address to the email. The email
913 * address will also be used as the personal name.
914 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
915 * If it is not set, it will be encoded using
916 * the Java platform's default charset (UTF-16) if it contains
917 * non-ASCII characters; otherwise, it is used as is.
918 *
919 * @param email A String.
920 * @return An Email.
921 * @throws EmailException Indicates an invalid email address
922 * @since 1.0
923 */
924 public Email addReplyTo(String email)
925 throws EmailException
926 {
927 return this.addReplyTo(email, null);
928 }
929
930 /**
931 * Add a reply to address to the email using the specified address and
932 * the specified personal name.
933 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
934 * If it is not set, it will be encoded using
935 * the Java platform's default charset (UTF-16) if it contains
936 * non-ASCII characters; otherwise, it is used as is.
937 *
938 * @param email A String.
939 * @param name A String.
940 * @return An Email.
941 * @throws EmailException Indicates an invalid email address
942 * @since 1.0
943 */
944 public Email addReplyTo(String email, String name)
945 throws EmailException
946 {
947 return addReplyTo(email, name, this.charset);
948 }
949
950 /**
951 * Add a reply to address to the email using the specified address,
952 * personal name, and charset encoding for the name.
953 *
954 * @param email A String.
955 * @param name A String.
956 * @param charset The charset to encode the name with.
957 * @return An Email.
958 * @throws EmailException Indicates an invalid email address or charset.
959 * @since 1.1
960 */
961 public Email addReplyTo(String email, String name, String charset)
962 throws EmailException
963 {
964 this.replyList.add(createInternetAddress(email, name, charset));
965 return this;
966 }
967
968 /**
969 * Set a list of reply to addresses. All elements in the specified
970 * <code>Collection</code> are expected to be of type
971 * <code>java.mail.internet.InternetAddress</code>.
972 *
973 * @param aCollection collection of <code>InternetAddress</code> objects
974 * @return An Email.
975 * @throws EmailException Indicates an invalid email address
976 * @see javax.mail.internet.InternetAddress
977 * @since 1.1
978 */
979 public Email setReplyTo(Collection aCollection) throws EmailException
980 {
981 if (aCollection == null || aCollection.isEmpty())
982 {
983 throw new EmailException("Address List provided was invalid");
984 }
985
986 this.replyList = new ArrayList(aCollection);
987 return this;
988 }
989
990 /**
991 * Used to specify the mail headers. Example:
992 *
993 * X-Mailer: Sendmail, X-Priority: 1( highest )
994 * or 2( high ) 3( normal ) 4( low ) and 5( lowest )
995 * Disposition-Notification-To: user@domain.net
996 *
997 * @param map A Map.
998 * @since 1.0
999 */
1000 public void setHeaders(Map map)
1001 {
1002 Iterator iterKeyBad = map.entrySet().iterator();
1003
1004 while (iterKeyBad.hasNext())
1005 {
1006 Map.Entry entry = (Map.Entry) iterKeyBad.next();
1007 String strName = (String) entry.getKey();
1008 String strValue = (String) entry.getValue();
1009
1010 if (EmailUtils.isEmpty(strName))
1011 {
1012 throw new IllegalArgumentException("name can not be null");
1013 }
1014 if (EmailUtils.isEmpty(strValue))
1015 {
1016 throw new IllegalArgumentException("value can not be null");
1017 }
1018 }
1019
1020 // all is ok, update headers
1021 this.headers = map;
1022 }
1023
1024 /**
1025 * Adds a header ( name, value ) to the headers Map.
1026 *
1027 * @param name A String with the name.
1028 * @param value A String with the value.
1029 * @since 1.0
1030 */
1031 public void addHeader(String name, String value)
1032 {
1033 if (EmailUtils.isEmpty(name))
1034 {
1035 throw new IllegalArgumentException("name can not be null");
1036 }
1037 if (EmailUtils.isEmpty(value))
1038 {
1039 throw new IllegalArgumentException("value can not be null");
1040 }
1041
1042 this.headers.put(name, value);
1043 }
1044
1045 /**
1046 * Set the email subject.
1047 *
1048 * @param aSubject A String.
1049 * @return An Email.
1050 * @since 1.0
1051 */
1052 public Email setSubject(String aSubject)
1053 {
1054 this.subject = aSubject;
1055 return this;
1056 }
1057
1058 /**
1059 * Set the "bounce address" - the address to which undeliverable messages
1060 * will be returned. If this value is never set, then the message will be
1061 * sent to the address specified with the System property "mail.smtp.from",
1062 * or if that value is not set, then to the "from" address.
1063 *
1064 * @param email A String.
1065 * @return An Email.
1066 * @since 1.0
1067 */
1068 public Email setBounceAddress(String email)
1069 {
1070 this.bounceAddress = email;
1071 return this;
1072 }
1073
1074
1075 /**
1076 * Define the content of the mail. It should be overidden by the
1077 * subclasses.
1078 *
1079 * @param msg A String.
1080 * @return An Email.
1081 * @throws EmailException generic exception.
1082 * @since 1.0
1083 */
1084 public abstract Email setMsg(String msg) throws EmailException;
1085
1086 /**
1087 * Build the internal MimeMessage to be sent.
1088 *
1089 * @throws EmailException if there was an error.
1090 * @since 1.0
1091 */
1092 public void buildMimeMessage() throws EmailException
1093 {
1094 try
1095 {
1096 this.getMailSession();
1097 this.message = this.createMimeMessage(this.session);
1098
1099 if (EmailUtils.isNotEmpty(this.subject))
1100 {
1101 if (EmailUtils.isNotEmpty(this.charset))
1102 {
1103 this.message.setSubject(this.subject, this.charset);
1104 }
1105 else
1106 {
1107 this.message.setSubject(this.subject);
1108 }
1109 }
1110
1111 // update content type (and encoding)
1112 this.updateContentType(this.contentType);
1113
1114 if (this.content != null)
1115 {
1116 this.message.setContent(this.content, this.contentType);
1117 }
1118 else if (this.emailBody != null)
1119 {
1120 if (this.contentType == null)
1121 {
1122 this.message.setContent(this.emailBody);
1123 }
1124 else
1125 {
1126 this.message.setContent(this.emailBody, this.contentType);
1127 }
1128 }
1129 else
1130 {
1131 this.message.setContent("", Email.TEXT_PLAIN);
1132 }
1133
1134 if (this.fromAddress != null)
1135 {
1136 this.message.setFrom(this.fromAddress);
1137 }
1138 else
1139 {
1140 if (session.getProperty(MAIL_SMTP_FROM) == null)
1141 {
1142 throw new EmailException("From address required");
1143 }
1144 }
1145
1146 if (this.toList.size() + this.ccList.size() + this.bccList.size() == 0)
1147 {
1148 throw new EmailException(
1149 "At least one receiver address required");
1150 }
1151
1152 if (this.toList.size() > 0)
1153 {
1154 this.message.setRecipients(
1155 Message.RecipientType.TO,
1156 this.toInternetAddressArray(this.toList));
1157 }
1158
1159 if (this.ccList.size() > 0)
1160 {
1161 this.message.setRecipients(
1162 Message.RecipientType.CC,
1163 this.toInternetAddressArray(this.ccList));
1164 }
1165
1166 if (this.bccList.size() > 0)
1167 {
1168 this.message.setRecipients(
1169 Message.RecipientType.BCC,
1170 this.toInternetAddressArray(this.bccList));
1171 }
1172
1173 if (this.replyList.size() > 0)
1174 {
1175 this.message.setReplyTo(
1176 this.toInternetAddressArray(this.replyList));
1177 }
1178
1179 if (this.headers.size() > 0)
1180 {
1181 Iterator iterHeaderKeys = this.headers.keySet().iterator();
1182 while (iterHeaderKeys.hasNext())
1183 {
1184 String name = (String) iterHeaderKeys.next();
1185 String value = (String) headers.get(name);
1186 this.message.addHeader(name, value);
1187 }
1188 }
1189
1190 if (this.message.getSentDate() == null)
1191 {
1192 this.message.setSentDate(getSentDate());
1193 }
1194
1195 if (this.popBeforeSmtp)
1196 {
1197 Store store = session.getStore("pop3");
1198 store.connect(this.popHost, this.popUsername, this.popPassword);
1199 }
1200 }
1201 catch (MessagingException me)
1202 {
1203 throw new EmailException(me);
1204 }
1205 }
1206
1207 /**
1208 * Factory method to create a customized MimeMessage which can be
1209 * implemented by a derived class, e.g. to set the message id.
1210 *
1211 * @param aSession mail session to be used
1212 * @return the newly created message
1213 */
1214 protected MimeMessage createMimeMessage(Session aSession)
1215 {
1216 return new MimeMessage(aSession);
1217 }
1218
1219 /**
1220 * Sends the previously created MimeMessage to the SMTP server.
1221 *
1222 * @return the message id of the underlying MimeMessage
1223 * @throws EmailException the sending failed
1224 */
1225 public String sendMimeMessage()
1226 throws EmailException
1227 {
1228 EmailUtils.notNull(this.message, "message");
1229
1230 try
1231 {
1232 Transport.send(this.message);
1233 return this.message.getMessageID();
1234 }
1235 catch (Throwable t)
1236 {
1237 String msg = "Sending the email to the following server failed : "
1238 + this.getHostName()
1239 + ":"
1240 + this.getSmtpPort();
1241
1242 throw new EmailException(msg, t);
1243 }
1244 }
1245
1246 /**
1247 * Returns the internal MimeMessage. Please not that the
1248 * MimeMessage is build by the buildMimeMessage() method.
1249 *
1250 * @return the MimeMessage
1251 */
1252 public MimeMessage getMimeMessage()
1253 {
1254 return this.message;
1255 }
1256
1257 /**
1258 * Sends the email. Internally we build a MimeMessage
1259 * which is afterwards sent to the SMTP server.
1260 *
1261 * @return the message id of the underlying MimeMessage
1262 * @throws EmailException the sending failed
1263 */
1264 public String send() throws EmailException
1265 {
1266 this.buildMimeMessage();
1267 return this.sendMimeMessage();
1268 }
1269
1270 /**
1271 * Sets the sent date for the email. The sent date will default to the
1272 * current date if not explictly set.
1273 *
1274 * @param date Date to use as the sent date on the email
1275 * @since 1.0
1276 */
1277 public void setSentDate(Date date)
1278 {
1279 if (date != null)
1280 {
1281 // create a seperate instance to keep findbugs happy
1282 this.sentDate = new Date(date.getTime());
1283 }
1284 }
1285
1286 /**
1287 * Gets the sent date for the email.
1288 *
1289 * @return date to be used as the sent date for the email
1290 * @since 1.0
1291 */
1292 public Date getSentDate()
1293 {
1294 if (this.sentDate == null)
1295 {
1296 return new Date();
1297 }
1298 return new Date(this.sentDate.getTime());
1299 }
1300
1301 /**
1302 * Gets the subject of the email.
1303 *
1304 * @return email subject
1305 */
1306 public String getSubject()
1307 {
1308 return this.subject;
1309 }
1310
1311 /**
1312 * Gets the sender of the email.
1313 *
1314 * @return from address
1315 */
1316 public InternetAddress getFromAddress()
1317 {
1318 return this.fromAddress;
1319 }
1320
1321 /**
1322 * Gets the host name of the SMTP server,
1323 *
1324 * @return host name
1325 */
1326 public String getHostName()
1327 {
1328 if (EmailUtils.isNotEmpty(this.hostName))
1329 {
1330 return this.hostName;
1331 }
1332 else if (this.session != null)
1333 {
1334 return this.session.getProperty(MAIL_HOST);
1335 }
1336 return null;
1337 }
1338
1339 /**
1340 * Gets the listening port of the SMTP server.
1341 *
1342 * @return smtp port
1343 */
1344 public String getSmtpPort()
1345 {
1346 if (EmailUtils.isNotEmpty(this.smtpPort))
1347 {
1348 return this.smtpPort;
1349 }
1350 else if (this.session != null)
1351 {
1352 return this.session.getProperty(MAIL_PORT);
1353 }
1354 return null;
1355 }
1356
1357 /**
1358 * Gets encryption mode for authentication
1359 *
1360 * @return true if using TLS for authentication, false otherwise
1361 * @since 1.1
1362 */
1363 public boolean isTLS()
1364 {
1365 return this.tls;
1366 }
1367
1368 /**
1369 * Utility to copy List of known InternetAddress objects into an
1370 * array.
1371 *
1372 * @param list A List.
1373 * @return An InternetAddress[].
1374 * @since 1.0
1375 */
1376 protected InternetAddress[] toInternetAddressArray(List list)
1377 {
1378 InternetAddress[] ia =
1379 (InternetAddress[]) list.toArray(new InternetAddress[list.size()]);
1380
1381 return ia;
1382 }
1383
1384 /**
1385 * Set details regarding "pop3 before smtp" authentication.
1386 *
1387 * @param newPopBeforeSmtp Wether or not to log into pop3
1388 * server before sending mail.
1389 * @param newPopHost The pop3 host to use.
1390 * @param newPopUsername The pop3 username.
1391 * @param newPopPassword The pop3 password.
1392 * @since 1.0
1393 */
1394 public void setPopBeforeSmtp(
1395 boolean newPopBeforeSmtp,
1396 String newPopHost,
1397 String newPopUsername,
1398 String newPopPassword)
1399 {
1400 this.popBeforeSmtp = newPopBeforeSmtp;
1401 this.popHost = newPopHost;
1402 this.popUsername = newPopUsername;
1403 this.popPassword = newPopPassword;
1404 }
1405
1406 /**
1407 * Returns whether SSL encryption for the transport is currently enabled.
1408 * @return true if SSL enabled for the transport
1409 */
1410 public boolean isSSL()
1411 {
1412 return ssl;
1413 }
1414
1415 /**
1416 * Sets whether SSL encryption should be enabled for the SMTP transport.
1417 * @param ssl whether to enable the SSL transport
1418 */
1419 public void setSSL(boolean ssl)
1420 {
1421 this.ssl = ssl;
1422 }
1423
1424 /**
1425 * Returns the current SSL port used by the SMTP transport.
1426 * @return the current SSL port used by the SMTP transport
1427 */
1428 public String getSslSmtpPort()
1429 {
1430 if (EmailUtils.isNotEmpty(this.sslSmtpPort))
1431 {
1432 return this.sslSmtpPort;
1433 }
1434 else if (this.session != null)
1435 {
1436 return this.session.getProperty(MAIL_SMTP_SOCKET_FACTORY_PORT);
1437 }
1438 return null;
1439 }
1440
1441 /**
1442 * Sets the SSL port to use for the SMTP transport. Defaults to the standard
1443 * port, 465.
1444 * @param sslSmtpPort the SSL port to use for the SMTP transport
1445 */
1446 public void setSslSmtpPort(String sslSmtpPort)
1447 {
1448 this.sslSmtpPort = sslSmtpPort;
1449 }
1450
1451 /**
1452 * Get the list of "To" addresses.
1453 *
1454 * @return List addresses
1455 */
1456 public List getToAddresses()
1457 {
1458 return this.toList;
1459 }
1460
1461 /**
1462 * Get the list of "CC" addresses.
1463 *
1464 * @return List addresses
1465 */
1466 public List getCcAddresses()
1467 {
1468 return this.ccList;
1469 }
1470
1471 /**
1472 * Get the list of "Bcc" addresses.
1473 *
1474 * @return List addresses
1475 */
1476 public List getBccAddresses()
1477 {
1478 return this.bccList;
1479 }
1480
1481 /**
1482 * Get the list of "Reply-To" addresses.
1483 *
1484 * @return List addresses
1485 */
1486 public List getReplyToAddresses()
1487 {
1488 return this.replyList;
1489 }
1490
1491 /**
1492 * Get the socket connection timeout value in milliseconds.
1493 *
1494 * @return the timeout in milliseconds.
1495 * @since 1.2
1496 */
1497 public int getSocketConnectionTimeout()
1498 {
1499 return this.socketConnectionTimeout;
1500 }
1501
1502 /**
1503 * Set the socket connection timeout value in milliseconds.
1504 * Default is infinite timeout.
1505 *
1506 * @param socketConnectionTimeout the connection timeout
1507 * @since 1.2
1508 */
1509 public void setSocketConnectionTimeout(int socketConnectionTimeout)
1510 {
1511 this.socketConnectionTimeout = socketConnectionTimeout;
1512 }
1513
1514 /**
1515 * Get the socket I/O timeout value in milliseconds.
1516 *
1517 * @return the socket I/O timeout
1518 * @since 1.2
1519 */
1520 public int getSocketTimeout()
1521 {
1522 return this.socketTimeout;
1523 }
1524
1525 /**
1526 * Set the socket I/O timeout value in milliseconds.
1527 * Default is infinite timeout.
1528 *
1529 * @param socketTimeout the socket I/O timeout
1530 * @since 1.2
1531 */
1532 public void setSocketTimeout(int socketTimeout)
1533 {
1534 this.socketTimeout = socketTimeout;
1535 }
1536 }