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.UnsupportedEncodingException;
20 import java.nio.charset.Charset;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Date;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Properties;
29
30 import javax.mail.Authenticator;
31 import javax.mail.Message;
32 import javax.mail.MessagingException;
33 import javax.mail.Session;
34 import javax.mail.Store;
35 import javax.mail.Transport;
36 import javax.mail.internet.AddressException;
37 import javax.mail.internet.InternetAddress;
38 import javax.mail.internet.MimeMessage;
39 import javax.mail.internet.MimeMultipart;
40 import javax.mail.internet.MimeUtility;
41 import javax.naming.Context;
42 import javax.naming.InitialContext;
43 import javax.naming.NamingException;
44
45 /**
46 * The base class for all email messages. This class sets the
47 * sender's email & name, receiver's email & name, subject, and the
48 * sent date. Subclasses are responsible for setting the message
49 * body.
50 *
51 * @since 1.0
52 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
53 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
54 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
55 * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
56 * @author <a href="mailto:greg@shwoop.com">Greg Ritter</a>
57 * @author <a href="mailto:unknown">Regis Koenig</a>
58 * @author <a href="mailto:colin.chalmers@maxware.nl">Colin Chalmers</a>
59 * @author <a href="mailto:matthias@wessendorf.net">Matthias Wessendorf</a>
60 * @author <a href="mailto:corey.scott@gmail.com">Corey Scott</a>
61 * @version $Id: Email.java 1448626 2013-02-21 12:57:29Z tn $
62 */
63 public abstract class Email
64 {
65 /** @deprecated since 1.3, use {@link EmailConstants#SENDER_EMAIL} instead */
66 @Deprecated
67 public static final String SENDER_EMAIL = EmailConstants.SENDER_EMAIL;
68
69 /** @deprecated since 1.3, use {@link EmailConstants#SENDER_NAME} instead */
70 @Deprecated
71 public static final String SENDER_NAME = EmailConstants.SENDER_NAME;
72
73 /** @deprecated since 1.3, use {@link EmailConstants#RECEIVER_EMAIL} instead */
74 @Deprecated
75 public static final String RECEIVER_EMAIL = EmailConstants.RECEIVER_EMAIL;
76
77 /** @deprecated since 1.3, use {@link EmailConstants#RECEIVER_NAME} instead */
78 @Deprecated
79 public static final String RECEIVER_NAME = EmailConstants.RECEIVER_NAME;
80
81 /** @deprecated since 1.3, use {@link EmailConstants#EMAIL_SUBJECT} instead */
82 @Deprecated
83 public static final String EMAIL_SUBJECT = EmailConstants.EMAIL_SUBJECT;
84
85 /** @deprecated since 1.3, use {@link EmailConstants#EMAIL_BODY} instead */
86 @Deprecated
87 public static final String EMAIL_BODY = EmailConstants.EMAIL_BODY;
88
89 /** @deprecated since 1.3, use {@link EmailConstants#CONTENT_TYPE} instead */
90 @Deprecated
91 public static final String CONTENT_TYPE = EmailConstants.CONTENT_TYPE;
92
93 /** @deprecated since 1.3, use {@link EmailConstants#ATTACHMENTS} instead */
94 @Deprecated
95 public static final String ATTACHMENTS = EmailConstants.ATTACHMENTS;
96
97 /** @deprecated since 1.3, use {@link EmailConstants#FILE_SERVER} instead */
98 @Deprecated
99 public static final String FILE_SERVER = EmailConstants.FILE_SERVER;
100
101 /** @deprecated since 1.3, use {@link EmailConstants#KOI8_R} instead */
102 @Deprecated
103 public static final String KOI8_R = EmailConstants.KOI8_R;
104
105 /** @deprecated since 1.3, use {@link EmailConstants#ISO_8859_1} instead */
106 @Deprecated
107 public static final String ISO_8859_1 = EmailConstants.ISO_8859_1;
108
109 /** @deprecated since 1.3, use {@link EmailConstants#US_ASCII} instead */
110 @Deprecated
111 public static final String US_ASCII = EmailConstants.US_ASCII;
112
113 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_DEBUG} instead */
114 @Deprecated
115 public static final String MAIL_DEBUG = EmailConstants.MAIL_DEBUG;
116
117 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_HOST} instead */
118 @Deprecated
119 public static final String MAIL_HOST = EmailConstants.MAIL_HOST;
120
121 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_PORT} instead */
122 @Deprecated
123 public static final String MAIL_PORT = EmailConstants.MAIL_PORT;
124
125 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_FROM} instead */
126 @Deprecated
127 public static final String MAIL_SMTP_FROM = EmailConstants.MAIL_SMTP_FROM;
128
129 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_AUTH} instead */
130 @Deprecated
131 public static final String MAIL_SMTP_AUTH = EmailConstants.MAIL_SMTP_AUTH;
132
133 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_USER} instead */
134 @Deprecated
135 public static final String MAIL_SMTP_USER = EmailConstants.MAIL_SMTP_USER;
136
137 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_PASSWORD} instead */
138 @Deprecated
139 public static final String MAIL_SMTP_PASSWORD = EmailConstants.MAIL_SMTP_PASSWORD;
140
141 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_TRANSPORT_PROTOCOL} instead */
142 @Deprecated
143 public static final String MAIL_TRANSPORT_PROTOCOL = EmailConstants.MAIL_TRANSPORT_PROTOCOL;
144
145 /** @deprecated since 1.3, use {@link EmailConstants#SMTP} instead */
146 @Deprecated
147 public static final String SMTP = EmailConstants.SMTP;
148
149 /** @deprecated since 1.3, use {@link EmailConstants#TEXT_HTML} instead */
150 @Deprecated
151 public static final String TEXT_HTML = EmailConstants.TEXT_HTML;
152
153 /** @deprecated since 1.3, use {@link EmailConstants#TEXT_PLAIN} instead */
154 @Deprecated
155 public static final String TEXT_PLAIN = EmailConstants.TEXT_PLAIN;
156
157 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_TRANSPORT_TLS} instead */
158 @Deprecated
159 public static final String MAIL_TRANSPORT_TLS = EmailConstants.MAIL_TRANSPORT_TLS;
160
161 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_SOCKET_FACTORY_FALLBACK} instead */
162 @Deprecated
163 public static final String MAIL_SMTP_SOCKET_FACTORY_FALLBACK = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_FALLBACK;
164
165 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_SOCKET_FACTORY_CLASS} instead */
166 @Deprecated
167 public static final String MAIL_SMTP_SOCKET_FACTORY_CLASS = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_CLASS;
168
169 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_SOCKET_FACTORY_PORT} instead */
170 @Deprecated
171 public static final String MAIL_SMTP_SOCKET_FACTORY_PORT = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_PORT;
172
173 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_CONNECTIONTIMEOUT} instead */
174 @Deprecated
175 public static final String MAIL_SMTP_CONNECTIONTIMEOUT = EmailConstants.MAIL_SMTP_CONNECTIONTIMEOUT;
176
177 /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_TIMEOUT} instead */
178 @Deprecated
179 public static final String MAIL_SMTP_TIMEOUT = EmailConstants.MAIL_SMTP_TIMEOUT;
180
181 /** The email message to send. */
182 protected MimeMessage message;
183
184 /** The charset to use for this message. */
185 protected String charset;
186
187 /** The Address of the sending party, mandatory. */
188 protected InternetAddress fromAddress;
189
190 /** The Subject. */
191 protected String subject;
192
193 /** An attachment. */
194 protected MimeMultipart emailBody;
195
196 /** The content. */
197 protected Object content;
198
199 /** The content type. */
200 protected String contentType;
201
202 /** Set session debugging on or off. */
203 protected boolean debug;
204
205 /** Sent date. */
206 protected Date sentDate;
207
208 /**
209 * Instance of an <code>Authenticator</code> object that will be used
210 * when authentication is requested from the mail server.
211 */
212 protected Authenticator authenticator;
213
214 /**
215 * The hostname of the mail server with which to connect. If null will try
216 * to get property from system.properties. If still null, quit.
217 */
218 protected String hostName;
219
220 /**
221 * The port number of the mail server to connect to.
222 * Defaults to the standard port ( 25 ).
223 */
224 protected String smtpPort = "25";
225
226 /**
227 * The port number of the SSL enabled SMTP server;
228 * defaults to the standard port, 465.
229 */
230 protected String sslSmtpPort = "465";
231
232 /** List of "to" email addresses. */
233 protected List<InternetAddress> toList = new ArrayList<InternetAddress>();
234
235 /** List of "cc" email addresses. */
236 protected List<InternetAddress> ccList = new ArrayList<InternetAddress>();
237
238 /** List of "bcc" email addresses. */
239 protected List<InternetAddress> bccList = new ArrayList<InternetAddress>();
240
241 /** List of "replyTo" email addresses. */
242 protected List<InternetAddress> replyList = new ArrayList<InternetAddress>();
243
244 /**
245 * Address to which undeliverable mail should be sent.
246 * Because this is handled by JavaMail as a String property
247 * in the mail session, this property is of type <code>String</code>
248 * rather than <code>InternetAddress</code>.
249 */
250 protected String bounceAddress;
251
252 /**
253 * Used to specify the mail headers. Example:
254 *
255 * X-Mailer: Sendmail, X-Priority: 1( highest )
256 * or 2( high ) 3( normal ) 4( low ) and 5( lowest )
257 * Disposition-Notification-To: user@domain.net
258 */
259 protected Map<String, String> headers = new HashMap<String, String>();
260
261 /**
262 * Used to determine whether to use pop3 before smtp, and if so the settings.
263 */
264 protected boolean popBeforeSmtp;
265
266 /** the host name of the pop3 server. */
267 protected String popHost;
268
269 /** the user name to log into the pop3 server. */
270 protected String popUsername;
271
272 /** the password to log into the pop3 server. */
273 protected String popPassword;
274
275 /**
276 * Does server require TLS encryption for authentication?
277 * @deprecated since 1.3, use setStartTLSEnabled() instead
278 */
279 @Deprecated
280 protected boolean tls;
281
282 /**
283 * Does the current transport use SSL/TLS encryption upon connection?
284 * @deprecated since 1.3, use setSSLOnConnect() instead
285 */
286 @Deprecated
287 protected boolean ssl;
288
289 /** socket I/O timeout value in milliseconds. */
290 protected int socketTimeout = EmailConstants.SOCKET_TIMEOUT_MS;
291
292 /** socket connection timeout value in milliseconds. */
293 protected int socketConnectionTimeout = EmailConstants.SOCKET_TIMEOUT_MS;
294
295 /**
296 * If true, enables the use of the STARTTLS command (if supported by
297 * the server) to switch the connection to a TLS-protected connection
298 * before issuing any login commands. Note that an appropriate trust
299 * store must configured so that the client will trust the server's
300 * certificate.
301 * Defaults to false.
302 */
303 private boolean startTlsEnabled;
304
305 /**
306 * If true, requires the use of the STARTTLS command. If the server doesn't
307 * support the STARTTLS command, or the command fails, the connect method
308 * will fail.
309 * Defaults to false.
310 */
311 private boolean startTlsRequired;
312
313 /** does the current transport use SSL/TLS encryption upon connection? */
314 private boolean sslOnConnect;
315
316 /**
317 * If set to true, check the server identity as specified by RFC 2595. These
318 * additional checks based on the content of the server's certificate are
319 * intended to prevent man-in-the-middle attacks.
320 * Defaults to false.
321 */
322 private boolean sslCheckServerIdentity;
323
324 /** The Session to mail with. */
325 private Session session;
326
327 /**
328 * Setting to true will enable the display of debug information.
329 *
330 * @param d A boolean.
331 * @since 1.0
332 */
333 public void setDebug(boolean d)
334 {
335 this.debug = d;
336 }
337
338 /**
339 * Sets the userName and password if authentication is needed. If this
340 * method is not used, no authentication will be performed.
341 * <p>
342 * This method will create a new instance of
343 * <code>DefaultAuthenticator</code> using the supplied parameters.
344 *
345 * @param userName User name for the SMTP server
346 * @param password password for the SMTP server
347 * @see DefaultAuthenticator
348 * @see #setAuthenticator
349 * @since 1.0
350 */
351 public void setAuthentication(String userName, String password)
352 {
353 this.setAuthenticator(new DefaultAuthenticator(userName, password));
354 }
355
356 /**
357 * Sets the <code>Authenticator</code> to be used when authentication
358 * is requested from the mail server.
359 * <p>
360 * This method should be used when your outgoing mail server requires
361 * authentication. Your mail server must also support RFC2554.
362 *
363 * @param newAuthenticator the <code>Authenticator</code> object.
364 * @see Authenticator
365 * @since 1.0
366 */
367 public void setAuthenticator(Authenticator newAuthenticator)
368 {
369 this.authenticator = newAuthenticator;
370 }
371
372 /**
373 * Set the charset of the message. Please note that you should set the charset before
374 * adding the message content.
375 *
376 * @param newCharset A String.
377 * @throws java.nio.charset.IllegalCharsetNameException if the charset name is invalid
378 * @throws java.nio.charset.UnsupportedCharsetException if no support for the named charset
379 * exists in the current JVM
380 * @since 1.0
381 */
382 public void setCharset(String newCharset)
383 {
384 Charset set = Charset.forName(newCharset);
385 this.charset = set.name();
386 }
387
388 /**
389 * Set the emailBody to a MimeMultiPart
390 *
391 * @param aMimeMultipart aMimeMultipart
392 * @since 1.0
393 */
394 public void setContent(MimeMultipart aMimeMultipart)
395 {
396 this.emailBody = aMimeMultipart;
397 }
398
399 /**
400 * Set the content & contentType
401 *
402 * @param aObject aObject
403 * @param aContentType aContentType
404 * @since 1.0
405 */
406 public void setContent(Object aObject, String aContentType)
407 {
408 this.content = aObject;
409 this.updateContentType(aContentType);
410 }
411
412 /**
413 * Update the contentType.
414 *
415 * @param aContentType aContentType
416 * @since 1.2
417 */
418 public void updateContentType(final String aContentType)
419 {
420 if (EmailUtils.isEmpty(aContentType))
421 {
422 this.contentType = null;
423 }
424 else
425 {
426 // set the content type
427 this.contentType = aContentType;
428
429 // set the charset if the input was properly formed
430 String strMarker = "; charset=";
431 int charsetPos = aContentType.toLowerCase().indexOf(strMarker);
432
433 if (charsetPos != -1)
434 {
435 // find the next space (after the marker)
436 charsetPos += strMarker.length();
437 int intCharsetEnd =
438 aContentType.toLowerCase().indexOf(" ", charsetPos);
439
440 if (intCharsetEnd != -1)
441 {
442 this.charset =
443 aContentType.substring(charsetPos, intCharsetEnd);
444 }
445 else
446 {
447 this.charset = aContentType.substring(charsetPos);
448 }
449 }
450 else
451 {
452 // use the default charset, if one exists, for messages
453 // whose content-type is some form of text.
454 if (this.contentType.startsWith("text/") && EmailUtils.isNotEmpty(this.charset))
455 {
456 StringBuffer contentTypeBuf = new StringBuffer(this.contentType);
457 contentTypeBuf.append(strMarker);
458 contentTypeBuf.append(this.charset);
459 this.contentType = contentTypeBuf.toString();
460 }
461 }
462 }
463 }
464
465 /**
466 * Set the hostname of the outgoing mail server.
467 *
468 * @param aHostName aHostName
469 * @since 1.0
470 */
471 public void setHostName(String aHostName)
472 {
473 checkSessionAlreadyInitialized();
474 this.hostName = aHostName;
475 }
476
477 /**
478 * Set or disable the STARTTLS encryption. Please see EMAIL-105
479 * for the reasons of deprecation.
480 *
481 * @deprecated since 1.3, use setStartTLSEnabled() instead
482 * @param withTLS true if STARTTLS requested, false otherwise
483 * @since 1.1
484 */
485 @Deprecated
486 public void setTLS(boolean withTLS)
487 {
488 setStartTLSEnabled(withTLS);
489 }
490
491 /**
492 * Set or disable the STARTTLS encryption.
493 *
494 * @param startTlsEnabled true if STARTTLS requested, false otherwise
495 * @return An Email.
496 * @since 1.3
497 */
498 public Email setStartTLSEnabled(boolean startTlsEnabled)
499 {
500 checkSessionAlreadyInitialized();
501 this.startTlsEnabled = startTlsEnabled;
502 this.tls = startTlsEnabled;
503 return this;
504 }
505
506 /**
507 * Set or disable the required STARTTLS encryption.
508 *
509 * @param startTlsRequired true if STARTTLS requested, false otherwise
510 * @return An Email.
511 * @since 1.3
512 */
513 public Email setStartTLSRequired(boolean startTlsRequired)
514 {
515 checkSessionAlreadyInitialized();
516 this.startTlsRequired = startTlsRequired;
517 return this;
518 }
519
520 /**
521 * Set the port number of the outgoing mail server.
522 *
523 * @param aPortNumber aPortNumber
524 * @throws IllegalArgumentException if the port number is < 1
525 * @since 1.0
526 */
527 public void setSmtpPort(int aPortNumber)
528 {
529 checkSessionAlreadyInitialized();
530
531 if (aPortNumber < 1)
532 {
533 throw new IllegalArgumentException(
534 "Cannot connect to a port number that is less than 1 ( "
535 + aPortNumber
536 + " )");
537 }
538
539 this.smtpPort = Integer.toString(aPortNumber);
540 }
541
542 /**
543 * Supply a mail Session object to use. Please note that passing
544 * a user name and password (in the case of mail authentication) will
545 * create a new mail session with a DefaultAuthenticator. This is a
546 * convenience but might come unexpected.
547 *
548 * If mail authentication is used but NO username and password
549 * is supplied the implementation assumes that you have set a
550 * authenticator and will use the existing mail session (as expected).
551 *
552 * @param aSession mail session to be used
553 * @throws IllegalArgumentException if the session is {@code null}
554 * @since 1.0
555 */
556 public void setMailSession(Session aSession)
557 {
558 EmailUtils.notNull(aSession, "no mail session supplied");
559
560 Properties sessionProperties = aSession.getProperties();
561 String auth = sessionProperties.getProperty(MAIL_SMTP_AUTH);
562
563 if ("true".equalsIgnoreCase(auth))
564 {
565 String userName = sessionProperties.getProperty(MAIL_SMTP_USER);
566 String password = sessionProperties.getProperty(MAIL_SMTP_PASSWORD);
567
568 if (EmailUtils.isNotEmpty(userName) && EmailUtils.isNotEmpty(password))
569 {
570 // only create a new mail session with an authenticator if
571 // authentication is required and no user name is given
572 this.authenticator = new DefaultAuthenticator(userName, password);
573 this.session = Session.getInstance(sessionProperties, this.authenticator);
574 }
575 else
576 {
577 // assume that the given mail session contains a working authenticator
578 this.session = aSession;
579 }
580 }
581 else
582 {
583 this.session = aSession;
584 }
585 }
586
587 /**
588 * Supply a mail Session object from a JNDI directory.
589 *
590 * @param jndiName name of JNDI resource (javax.mail.Session type), resource
591 * if searched in java:comp/env if name does not start with "java:"
592 * @throws IllegalArgumentException if the JNDI name is null or empty
593 * @throws NamingException if the resource cannot be retrieved from JNDI directory
594 * @since 1.1
595 */
596 public void setMailSessionFromJNDI(String jndiName) throws NamingException
597 {
598 if (EmailUtils.isEmpty(jndiName))
599 {
600 throw new IllegalArgumentException("JNDI name missing");
601 }
602 Context ctx = null;
603 if (jndiName.startsWith("java:"))
604 {
605 ctx = new InitialContext();
606 }
607 else
608 {
609 ctx = (Context) new InitialContext().lookup("java:comp/env");
610
611 }
612 this.setMailSession((Session) ctx.lookup(jndiName));
613 }
614
615 /**
616 * Determines the mail session used when sending this Email, creating
617 * the Session if necessary. When a mail session is already
618 * initialized setting the session related properties will cause
619 * an IllegalStateException.
620 *
621 * @return A Session.
622 * @throws EmailException if the host name was not set
623 * @since 1.0
624 */
625 public Session getMailSession() throws EmailException
626 {
627 if (this.session == null)
628 {
629 Properties properties = new Properties(System.getProperties());
630 properties.setProperty(MAIL_TRANSPORT_PROTOCOL, SMTP);
631
632 if (EmailUtils.isEmpty(this.hostName))
633 {
634 this.hostName = properties.getProperty(MAIL_HOST);
635 }
636
637 if (EmailUtils.isEmpty(this.hostName))
638 {
639 throw new EmailException("Cannot find valid hostname for mail session");
640 }
641
642 properties.setProperty(MAIL_PORT, this.smtpPort);
643 properties.setProperty(MAIL_HOST, this.hostName);
644 properties.setProperty(MAIL_DEBUG, String.valueOf(this.debug));
645
646 properties.setProperty(EmailConstants.MAIL_TRANSPORT_STARTTLS_ENABLE,
647 isStartTLSEnabled() ? "true" : "false");
648 properties.setProperty(EmailConstants.MAIL_TRANSPORT_STARTTLS_REQUIRED,
649 isStartTLSRequired() ? "true" : "false");
650
651 if (this.authenticator != null)
652 {
653 properties.setProperty(MAIL_SMTP_AUTH, "true");
654 }
655
656 if (isSSLOnConnect())
657 {
658 properties.setProperty(MAIL_PORT, this.sslSmtpPort);
659 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_PORT, this.sslSmtpPort);
660 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
661 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_FALLBACK, "false");
662 }
663
664 if ((isSSLOnConnect() || isStartTLSEnabled()) && isSSLCheckServerIdentity())
665 {
666 properties.setProperty(EmailConstants.MAIL_SMTP_SSL_CHECKSERVERIDENTITY, "true");
667 }
668
669 if (this.bounceAddress != null)
670 {
671 properties.setProperty(MAIL_SMTP_FROM, this.bounceAddress);
672 }
673
674 if (this.socketTimeout > 0)
675 {
676 properties.setProperty(MAIL_SMTP_TIMEOUT, Integer.toString(this.socketTimeout));
677 }
678
679 if (this.socketConnectionTimeout > 0)
680 {
681 properties.setProperty(MAIL_SMTP_CONNECTIONTIMEOUT, Integer.toString(this.socketConnectionTimeout));
682 }
683
684 // changed this (back) to getInstance due to security exceptions
685 // caused when testing using maven
686 this.session = Session.getInstance(properties, this.authenticator);
687 }
688 return this.session;
689 }
690
691 /**
692 * Set the FROM field of the email to use the specified address. The email
693 * address will also be used as the personal name.
694 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
695 * If it is not set, it will be encoded using
696 * the Java platform's default charset (UTF-16) if it contains
697 * non-ASCII characters; otherwise, it is used as is.
698 *
699 * @param email A String.
700 * @return An Email.
701 * @throws EmailException Indicates an invalid email address.
702 * @since 1.0
703 */
704 public Email setFrom(String email)
705 throws EmailException
706 {
707 return setFrom(email, null);
708 }
709
710 /**
711 * Set the FROM field of the email to use the specified address and the
712 * specified personal name.
713 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
714 * If it is not set, it will be encoded using
715 * the Java platform's default charset (UTF-16) if it contains
716 * non-ASCII characters; otherwise, it is used as is.
717 *
718 * @param email A String.
719 * @param name A String.
720 * @return An Email.
721 * @throws EmailException Indicates an invalid email address.
722 * @since 1.0
723 */
724 public Email setFrom(String email, String name)
725 throws EmailException
726 {
727 return setFrom(email, name, this.charset);
728 }
729
730 /**
731 * Set the FROM field of the email to use the specified address, personal
732 * name, and charset encoding for the name.
733 *
734 * @param email A String.
735 * @param name A String.
736 * @param charset The charset to encode the name with.
737 * @return An Email.
738 * @throws EmailException Indicates an invalid email address or charset.
739 * @since 1.1
740 */
741 public Email setFrom(String email, String name, String charset)
742 throws EmailException
743 {
744 this.fromAddress = createInternetAddress(email, name, charset);
745 return this;
746 }
747
748 /**
749 * Add a recipient TO to the email. The email
750 * address will also be used as the personal name.
751 * The name will be encoded by the charset of
752 * {@link #setCharset(java.lang.String) setCharset()}.
753 * If it is not set, it will be encoded using
754 * the Java platform's default charset (UTF-16) if it contains
755 * non-ASCII characters; otherwise, it is used as is.
756 *
757 * @param email A String.
758 * @return An Email.
759 * @throws EmailException Indicates an invalid email address.
760 * @since 1.0
761 */
762 public Email addTo(String email)
763 throws EmailException
764 {
765 return addTo(email, null);
766 }
767
768 /**
769 * Add a list of TO recipients to the email. The email
770 * addresses will also be used as the personal names.
771 * The names will be encoded by the charset of
772 * {@link #setCharset(java.lang.String) setCharset()}.
773 * If it is not set, it will be encoded using
774 * the Java platform's default charset (UTF-16) if it contains
775 * non-ASCII characters; otherwise, it is used as is.
776 *
777 * @param emails A String array.
778 * @return An Email.
779 * @throws EmailException Indicates an invalid email address.
780 * @since 1.3
781 */
782 public Email addTo(String... emails)
783 throws EmailException
784 {
785 if (emails == null || emails.length == 0)
786 {
787 throw new EmailException("Address List provided was invalid");
788 }
789
790 for (String email : emails)
791 {
792 addTo(email, null);
793 }
794
795 return this;
796 }
797
798 /**
799 * Add a recipient TO to the email using the specified address and the
800 * specified personal name.
801 * The name will be encoded by the charset of
802 * {@link #setCharset(java.lang.String) setCharset()}.
803 * If it is not set, it will be encoded using
804 * the Java platform's default charset (UTF-16) if it contains
805 * non-ASCII characters; otherwise, it is used as is.
806 *
807 * @param email A String.
808 * @param name A String.
809 * @return An Email.
810 * @throws EmailException Indicates an invalid email address.
811 * @since 1.0
812 */
813 public Email addTo(String email, String name)
814 throws EmailException
815 {
816 return addTo(email, name, this.charset);
817 }
818
819 /**
820 * Add a recipient TO to the email using the specified address, personal
821 * name, and charset encoding for the name.
822 *
823 * @param email A String.
824 * @param name A String.
825 * @param charset The charset to encode the name with.
826 * @return An Email.
827 * @throws EmailException Indicates an invalid email address or charset.
828 * @since 1.1
829 */
830 public Email addTo(String email, String name, String charset)
831 throws EmailException
832 {
833 this.toList.add(createInternetAddress(email, name, charset));
834 return this;
835 }
836
837 /**
838 * Set a list of "TO" addresses. All elements in the specified
839 * <code>Collection</code> are expected to be of type
840 * <code>java.mail.internet.InternetAddress</code>.
841 *
842 * @param aCollection collection of <code>InternetAddress</code> objects.
843 * @return An Email.
844 * @throws EmailException Indicates an invalid email address.
845 * @see javax.mail.internet.InternetAddress
846 * @since 1.0
847 */
848 public Email setTo(Collection<InternetAddress> aCollection) throws EmailException
849 {
850 if (aCollection == null || aCollection.isEmpty())
851 {
852 throw new EmailException("Address List provided was invalid");
853 }
854
855 this.toList = new ArrayList<InternetAddress>(aCollection);
856 return this;
857 }
858
859 /**
860 * Add a recipient CC to the email. The email
861 * address will also be used as the personal name.
862 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
863 * If it is not set, it will be encoded using
864 * the Java platform's default charset (UTF-16) if it contains
865 * non-ASCII characters; otherwise, it is used as is.
866 *
867 * @param email A String.
868 * @return An Email.
869 * @throws EmailException Indicates an invalid email address.
870 * @since 1.0
871 */
872 public Email addCc(String email)
873 throws EmailException
874 {
875 return this.addCc(email, null);
876 }
877
878 /**
879 * Add an array of CC recipients to the email. The email
880 * addresses will also be used as the personal name.
881 * The names will be encoded by the charset of
882 * {@link #setCharset(java.lang.String) setCharset()}.
883 * If it is not set, it will be encoded using
884 * the Java platform's default charset (UTF-16) if it contains
885 * non-ASCII characters; otherwise, it is used as is.
886 *
887 * @param emails A String array.
888 * @return An Email.
889 * @throws EmailException Indicates an invalid email address.
890 * @since 1.3
891 */
892 public Email addCc(String... emails)
893 throws EmailException
894 {
895 if (emails == null || emails.length == 0)
896 {
897 throw new EmailException("Address List provided was invalid");
898 }
899
900 for (String email : emails)
901 {
902 addCc(email, null);
903 }
904
905 return this;
906 }
907
908 /**
909 * Add a recipient CC to the email using the specified address and the
910 * specified personal name.
911 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
912 * If it is not set, it will be encoded using
913 * the Java platform's default charset (UTF-16) if it contains
914 * non-ASCII characters; otherwise, it is used as is.
915 *
916 * @param email A String.
917 * @param name A String.
918 * @return An Email.
919 * @throws EmailException Indicates an invalid email address.
920 * @since 1.0
921 */
922 public Email addCc(String email, String name)
923 throws EmailException
924 {
925 return addCc(email, name, this.charset);
926 }
927
928 /**
929 * Add a recipient CC to the email using the specified address, personal
930 * name, and charset encoding for the name.
931 *
932 * @param email A String.
933 * @param name A String.
934 * @param charset The charset to encode the name with.
935 * @return An Email.
936 * @throws EmailException Indicates an invalid email address or charset.
937 * @since 1.1
938 */
939 public Email addCc(String email, String name, String charset)
940 throws EmailException
941 {
942 this.ccList.add(createInternetAddress(email, name, charset));
943 return this;
944 }
945
946 /**
947 * Set a list of "CC" addresses. All elements in the specified
948 * <code>Collection</code> are expected to be of type
949 * <code>java.mail.internet.InternetAddress</code>.
950 *
951 * @param aCollection collection of <code>InternetAddress</code> objects.
952 * @return An Email.
953 * @throws EmailException Indicates an invalid email address.
954 * @see javax.mail.internet.InternetAddress
955 * @since 1.0
956 */
957 public Email setCc(Collection<InternetAddress> aCollection) throws EmailException
958 {
959 if (aCollection == null || aCollection.isEmpty())
960 {
961 throw new EmailException("Address List provided was invalid");
962 }
963
964 this.ccList = new ArrayList<InternetAddress>(aCollection);
965 return this;
966 }
967
968 /**
969 * Add a blind BCC recipient to the email. The email
970 * address will also be used as the personal name.
971 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
972 * If it is not set, it will be encoded using
973 * the Java platform's default charset (UTF-16) if it contains
974 * non-ASCII characters; otherwise, it is used as is.
975 *
976 * @param email A String.
977 * @return An Email.
978 * @throws EmailException Indicates an invalid email address
979 * @since 1.0
980 */
981 public Email addBcc(String email)
982 throws EmailException
983 {
984 return this.addBcc(email, null);
985 }
986
987 /**
988 * Add an array of blind BCC recipients to the email. The email
989 * addresses will also be used as the personal name.
990 * The names will be encoded by the charset of
991 * {@link #setCharset(java.lang.String) setCharset()}.
992 * If it is not set, it will be encoded using
993 * the Java platform's default charset (UTF-16) if it contains
994 * non-ASCII characters; otherwise, it is used as is.
995 *
996 * @param emails A String array.
997 * @return An Email.
998 * @throws EmailException Indicates an invalid email address
999 * @since 1.3
1000 */
1001 public Email addBcc(String... emails)
1002 throws EmailException
1003 {
1004 if (emails == null || emails.length == 0)
1005 {
1006 throw new EmailException("Address List provided was invalid");
1007 }
1008
1009 for (String email : emails)
1010 {
1011 addBcc(email, null);
1012 }
1013
1014 return this;
1015 }
1016
1017 /**
1018 * Add a blind BCC recipient to the email using the specified address and
1019 * the specified personal name.
1020 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
1021 * If it is not set, it will be encoded using
1022 * the Java platform's default charset (UTF-16) if it contains
1023 * non-ASCII characters; otherwise, it is used as is.
1024 *
1025 * @param email A String.
1026 * @param name A String.
1027 * @return An Email.
1028 * @throws EmailException Indicates an invalid email address
1029 * @since 1.0
1030 */
1031 public Email addBcc(String email, String name)
1032 throws EmailException
1033 {
1034 return addBcc(email, name, this.charset);
1035 }
1036
1037 /**
1038 * Add a blind BCC recipient to the email using the specified address,
1039 * personal name, and charset encoding for the name.
1040 *
1041 * @param email A String.
1042 * @param name A String.
1043 * @param charset The charset to encode the name with.
1044 * @return An Email.
1045 * @throws EmailException Indicates an invalid email address
1046 * @since 1.1
1047 */
1048 public Email addBcc(String email, String name, String charset)
1049 throws EmailException
1050 {
1051 this.bccList.add(createInternetAddress(email, name, charset));
1052 return this;
1053 }
1054
1055 /**
1056 * Set a list of "BCC" addresses. All elements in the specified
1057 * <code>Collection</code> are expected to be of type
1058 * <code>java.mail.internet.InternetAddress</code>.
1059 *
1060 * @param aCollection collection of <code>InternetAddress</code> objects
1061 * @return An Email.
1062 * @throws EmailException Indicates an invalid email address
1063 * @see javax.mail.internet.InternetAddress
1064 * @since 1.0
1065 */
1066 public Email setBcc(Collection<InternetAddress> aCollection) throws EmailException
1067 {
1068 if (aCollection == null || aCollection.isEmpty())
1069 {
1070 throw new EmailException("Address List provided was invalid");
1071 }
1072
1073 this.bccList = new ArrayList<InternetAddress>(aCollection);
1074 return this;
1075 }
1076
1077 /**
1078 * Add a reply to address to the email. The email
1079 * address will also be used as the personal name.
1080 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
1081 * If it is not set, it will be encoded using
1082 * the Java platform's default charset (UTF-16) if it contains
1083 * non-ASCII characters; otherwise, it is used as is.
1084 *
1085 * @param email A String.
1086 * @return An Email.
1087 * @throws EmailException Indicates an invalid email address
1088 * @since 1.0
1089 */
1090 public Email addReplyTo(String email)
1091 throws EmailException
1092 {
1093 return this.addReplyTo(email, null);
1094 }
1095
1096 /**
1097 * Add a reply to address to the email using the specified address and
1098 * the specified personal name.
1099 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
1100 * If it is not set, it will be encoded using
1101 * the Java platform's default charset (UTF-16) if it contains
1102 * non-ASCII characters; otherwise, it is used as is.
1103 *
1104 * @param email A String.
1105 * @param name A String.
1106 * @return An Email.
1107 * @throws EmailException Indicates an invalid email address
1108 * @since 1.0
1109 */
1110 public Email addReplyTo(String email, String name)
1111 throws EmailException
1112 {
1113 return addReplyTo(email, name, this.charset);
1114 }
1115
1116 /**
1117 * Add a reply to address to the email using the specified address,
1118 * personal name, and charset encoding for the name.
1119 *
1120 * @param email A String.
1121 * @param name A String.
1122 * @param charset The charset to encode the name with.
1123 * @return An Email.
1124 * @throws EmailException Indicates an invalid email address or charset.
1125 * @since 1.1
1126 */
1127 public Email addReplyTo(String email, String name, String charset)
1128 throws EmailException
1129 {
1130 this.replyList.add(createInternetAddress(email, name, charset));
1131 return this;
1132 }
1133
1134 /**
1135 * Set a list of reply to addresses. All elements in the specified
1136 * <code>Collection</code> are expected to be of type
1137 * <code>java.mail.internet.InternetAddress</code>.
1138 *
1139 * @param aCollection collection of <code>InternetAddress</code> objects
1140 * @return An Email.
1141 * @throws EmailException Indicates an invalid email address
1142 * @see javax.mail.internet.InternetAddress
1143 * @since 1.1
1144 */
1145 public Email setReplyTo(Collection<InternetAddress> aCollection) throws EmailException
1146 {
1147 if (aCollection == null || aCollection.isEmpty())
1148 {
1149 throw new EmailException("Address List provided was invalid");
1150 }
1151
1152 this.replyList = new ArrayList<InternetAddress>(aCollection);
1153 return this;
1154 }
1155
1156 /**
1157 * Used to specify the mail headers. Example:
1158 *
1159 * X-Mailer: Sendmail, X-Priority: 1( highest )
1160 * or 2( high ) 3( normal ) 4( low ) and 5( lowest )
1161 * Disposition-Notification-To: user@domain.net
1162 *
1163 * @param map A Map.
1164 * @throws IllegalArgumentException if either of the provided header / value is null or empty
1165 * @since 1.0
1166 */
1167 public void setHeaders(Map<String, String> map)
1168 {
1169 this.headers.clear();
1170
1171 Iterator<Map.Entry<String, String>> iterKeyBad = map.entrySet().iterator();
1172
1173 while (iterKeyBad.hasNext())
1174 {
1175 Map.Entry<String, String> entry = iterKeyBad.next();
1176 addHeader(entry.getKey(), entry.getValue());
1177 }
1178 }
1179
1180 /**
1181 * Adds a header ( name, value ) to the headers Map.
1182 *
1183 * @param name A String with the name.
1184 * @param value A String with the value.
1185 * @since 1.0
1186 * @throws IllegalArgumentException if either {@code name} or {@code value} is null or empty
1187 */
1188 public void addHeader(String name, String value)
1189 {
1190 if (EmailUtils.isEmpty(name))
1191 {
1192 throw new IllegalArgumentException("name can not be null or empty");
1193 }
1194 if (EmailUtils.isEmpty(value))
1195 {
1196 throw new IllegalArgumentException("value can not be null or empty");
1197 }
1198
1199 this.headers.put(name, value);
1200 }
1201
1202 /**
1203 * Set the email subject.
1204 *
1205 * @param aSubject A String.
1206 * @return An Email.
1207 * @since 1.0
1208 */
1209 public Email setSubject(String aSubject)
1210 {
1211 this.subject = aSubject;
1212 return this;
1213 }
1214
1215 /**
1216 * Set the "bounce address" - the address to which undeliverable messages
1217 * will be returned. If this value is never set, then the message will be
1218 * sent to the address specified with the System property "mail.smtp.from",
1219 * or if that value is not set, then to the "from" address.
1220 *
1221 * @param email A String.
1222 * @return An Email.
1223 * @throws IllegalStateException when the mail session is already initialized
1224 * @since 1.0
1225 */
1226 public Email setBounceAddress(String email)
1227 {
1228 checkSessionAlreadyInitialized();
1229 this.bounceAddress = email;
1230 return this;
1231 }
1232
1233
1234 /**
1235 * Define the content of the mail. It should be overridden by the
1236 * subclasses.
1237 *
1238 * @param msg A String.
1239 * @return An Email.
1240 * @throws EmailException generic exception.
1241 * @since 1.0
1242 */
1243 public abstract Email setMsg(String msg) throws EmailException;
1244
1245 /**
1246 * Does the work of actually building the MimeMessage. Please note that
1247 * a user rarely calls this method directly and only if he/she is
1248 * interested in the sending the underlying MimeMessage without
1249 * commons-email.
1250 *
1251 * @throws IllegalStateException if the MimeMessage was already built
1252 * @throws EmailException if there was an error.
1253 * @since 1.0
1254 */
1255 public void buildMimeMessage() throws EmailException
1256 {
1257 if (this.message != null)
1258 {
1259 // [EMAIL-95] we assume that an email is not reused therefore invoking
1260 // buildMimeMessage() more than once is illegal.
1261 throw new IllegalStateException("The MimeMessage is already built.");
1262 }
1263
1264 try
1265 {
1266 this.message = this.createMimeMessage(this.getMailSession());
1267
1268 if (EmailUtils.isNotEmpty(this.subject))
1269 {
1270 if (EmailUtils.isNotEmpty(this.charset))
1271 {
1272 this.message.setSubject(this.subject, this.charset);
1273 }
1274 else
1275 {
1276 this.message.setSubject(this.subject);
1277 }
1278 }
1279
1280 // update content type (and encoding)
1281 this.updateContentType(this.contentType);
1282
1283 if (this.content != null)
1284 {
1285 this.message.setContent(this.content, this.contentType);
1286 }
1287 else if (this.emailBody != null)
1288 {
1289 if (this.contentType == null)
1290 {
1291 this.message.setContent(this.emailBody);
1292 }
1293 else
1294 {
1295 this.message.setContent(this.emailBody, this.contentType);
1296 }
1297 }
1298 else
1299 {
1300 this.message.setContent("", Email.TEXT_PLAIN);
1301 }
1302
1303 if (this.fromAddress != null)
1304 {
1305 this.message.setFrom(this.fromAddress);
1306 }
1307 else
1308 {
1309 if (session.getProperty(MAIL_SMTP_FROM) == null)
1310 {
1311 throw new EmailException("From address required");
1312 }
1313 }
1314
1315 if (this.toList.size() + this.ccList.size() + this.bccList.size() == 0)
1316 {
1317 throw new EmailException("At least one receiver address required");
1318 }
1319
1320 if (this.toList.size() > 0)
1321 {
1322 this.message.setRecipients(
1323 Message.RecipientType.TO,
1324 this.toInternetAddressArray(this.toList));
1325 }
1326
1327 if (this.ccList.size() > 0)
1328 {
1329 this.message.setRecipients(
1330 Message.RecipientType.CC,
1331 this.toInternetAddressArray(this.ccList));
1332 }
1333
1334 if (this.bccList.size() > 0)
1335 {
1336 this.message.setRecipients(
1337 Message.RecipientType.BCC,
1338 this.toInternetAddressArray(this.bccList));
1339 }
1340
1341 if (this.replyList.size() > 0)
1342 {
1343 this.message.setReplyTo(
1344 this.toInternetAddressArray(this.replyList));
1345 }
1346
1347
1348 if (this.headers.size() > 0)
1349 {
1350 Iterator<String> iterHeaderKeys = this.headers.keySet().iterator();
1351 while (iterHeaderKeys.hasNext())
1352 {
1353 String name = iterHeaderKeys.next();
1354 String value = headers.get(name);
1355 String foldedValue = createFoldedHeaderValue(name, value);
1356 this.message.addHeader(name, foldedValue);
1357 }
1358 }
1359
1360 if (this.message.getSentDate() == null)
1361 {
1362 this.message.setSentDate(getSentDate());
1363 }
1364
1365 if (this.popBeforeSmtp)
1366 {
1367 Store store = session.getStore("pop3");
1368 store.connect(this.popHost, this.popUsername, this.popPassword);
1369 }
1370 }
1371 catch (MessagingException me)
1372 {
1373 throw new EmailException(me);
1374 }
1375 }
1376
1377 /**
1378 * Sends the previously created MimeMessage to the SMTP server.
1379 *
1380 * @return the message id of the underlying MimeMessage
1381 * @throws IllegalArgumentException if the MimeMessage has not been created
1382 * @throws EmailException the sending failed
1383 */
1384 public String sendMimeMessage()
1385 throws EmailException
1386 {
1387 EmailUtils.notNull(this.message, "MimeMessage has not been created yet");
1388
1389 try
1390 {
1391 Transport.send(this.message);
1392 return this.message.getMessageID();
1393 }
1394 catch (Throwable t)
1395 {
1396 String msg = "Sending the email to the following server failed : "
1397 + this.getHostName()
1398 + ":"
1399 + this.getSmtpPort();
1400
1401 throw new EmailException(msg, t);
1402 }
1403 }
1404
1405 /**
1406 * Returns the internal MimeMessage. Please not that the
1407 * MimeMessage is build by the buildMimeMessage() method.
1408 *
1409 * @return the MimeMessage
1410 */
1411 public MimeMessage getMimeMessage()
1412 {
1413 return this.message;
1414 }
1415
1416 /**
1417 * Sends the email. Internally we build a MimeMessage
1418 * which is afterwards sent to the SMTP server.
1419 *
1420 * @return the message id of the underlying MimeMessage
1421 * @throws IllegalStateException if the MimeMessage was already built, ie {@link #buildMimeMessage()}
1422 * was already called
1423 * @throws EmailException the sending failed
1424 */
1425 public String send() throws EmailException
1426 {
1427 this.buildMimeMessage();
1428 return this.sendMimeMessage();
1429 }
1430
1431 /**
1432 * Sets the sent date for the email. The sent date will default to the
1433 * current date if not explicitly set.
1434 *
1435 * @param date Date to use as the sent date on the email
1436 * @since 1.0
1437 */
1438 public void setSentDate(Date date)
1439 {
1440 if (date != null)
1441 {
1442 // create a separate instance to keep findbugs happy
1443 this.sentDate = new Date(date.getTime());
1444 }
1445 }
1446
1447 /**
1448 * Gets the sent date for the email.
1449 *
1450 * @return date to be used as the sent date for the email
1451 * @since 1.0
1452 */
1453 public Date getSentDate()
1454 {
1455 if (this.sentDate == null)
1456 {
1457 return new Date();
1458 }
1459 return new Date(this.sentDate.getTime());
1460 }
1461
1462 /**
1463 * Gets the subject of the email.
1464 *
1465 * @return email subject
1466 */
1467 public String getSubject()
1468 {
1469 return this.subject;
1470 }
1471
1472 /**
1473 * Gets the sender of the email.
1474 *
1475 * @return from address
1476 */
1477 public InternetAddress getFromAddress()
1478 {
1479 return this.fromAddress;
1480 }
1481
1482 /**
1483 * Gets the host name of the SMTP server,
1484 *
1485 * @return host name
1486 */
1487 public String getHostName()
1488 {
1489 if (this.session != null)
1490 {
1491 return this.session.getProperty(MAIL_HOST);
1492 }
1493 else if (EmailUtils.isNotEmpty(this.hostName))
1494 {
1495 return this.hostName;
1496 }
1497 return null;
1498 }
1499
1500 /**
1501 * Gets the listening port of the SMTP server.
1502 *
1503 * @return smtp port
1504 */
1505 public String getSmtpPort()
1506 {
1507 if (this.session != null)
1508 {
1509 return this.session.getProperty(MAIL_PORT);
1510 }
1511 else if (EmailUtils.isNotEmpty(this.smtpPort))
1512 {
1513 return this.smtpPort;
1514 }
1515 return null;
1516 }
1517
1518 /**
1519 * Gets whether the client is configured to require STARTTLS.
1520 *
1521 * @return true if using STARTTLS for authentication, false otherwise
1522 * @since 1.3
1523 */
1524 public boolean isStartTLSRequired()
1525 {
1526 return this.startTlsRequired;
1527 }
1528
1529 /**
1530 * Gets whether the client is configured to try to enable STARTTLS.
1531 *
1532 * @return true if using STARTTLS for authentication, false otherwise
1533 * @since 1.3
1534 */
1535 public boolean isStartTLSEnabled()
1536 {
1537 return this.startTlsEnabled || tls;
1538 }
1539
1540 /**
1541 * Gets whether the client is configured to try to enable STARTTLS.
1542 * See EMAIL-105 for reason of deprecation.
1543 *
1544 * @deprecated since 1.3, use isStartTLSEnabled() instead
1545 * @return true if using STARTTLS for authentication, false otherwise
1546 * @since 1.1
1547 */
1548 @Deprecated
1549 public boolean isTLS()
1550 {
1551 return isStartTLSEnabled();
1552 }
1553
1554 /**
1555 * Utility to copy List of known InternetAddress objects into an
1556 * array.
1557 *
1558 * @param list A List.
1559 * @return An InternetAddress[].
1560 * @since 1.0
1561 */
1562 protected InternetAddress[] toInternetAddressArray(List<InternetAddress> list)
1563 {
1564 InternetAddress[] ia = list.toArray(new InternetAddress[list.size()]);
1565
1566 return ia;
1567 }
1568
1569 /**
1570 * Set details regarding "pop3 before smtp" authentication.
1571 *
1572 * @param newPopBeforeSmtp Whether or not to log into pop3 server before sending mail.
1573 * @param newPopHost The pop3 host to use.
1574 * @param newPopUsername The pop3 username.
1575 * @param newPopPassword The pop3 password.
1576 * @since 1.0
1577 */
1578 public void setPopBeforeSmtp(
1579 boolean newPopBeforeSmtp,
1580 String newPopHost,
1581 String newPopUsername,
1582 String newPopPassword)
1583 {
1584 this.popBeforeSmtp = newPopBeforeSmtp;
1585 this.popHost = newPopHost;
1586 this.popUsername = newPopUsername;
1587 this.popPassword = newPopPassword;
1588 }
1589
1590 /**
1591 * Returns whether SSL/TLS encryption for the transport is currently enabled (SMTPS/POPS).
1592 * See EMAIL-105 for reason of deprecation.
1593 *
1594 * @deprecated since 1.3, use isSSLOnConnect() instead
1595 * @return true if SSL enabled for the transport
1596 */
1597 @Deprecated
1598 public boolean isSSL()
1599 {
1600 return isSSLOnConnect();
1601 }
1602
1603 /**
1604 * Returns whether SSL/TLS encryption for the transport is currently enabled (SMTPS/POPS).
1605 *
1606 * @return true if SSL enabled for the transport
1607 * @since 1.3
1608 */
1609 public boolean isSSLOnConnect()
1610 {
1611 return sslOnConnect || ssl;
1612 }
1613
1614 /**
1615 * Sets whether SSL/TLS encryption should be enabled for the SMTP transport upon connection (SMTPS/POPS).
1616 * See EMAIL-105 for reason of deprecation.
1617 *
1618 * @deprecated since 1.3, use setSSLOnConnect() instead
1619 * @param ssl whether to enable the SSL transport
1620 */
1621 @Deprecated
1622 public void setSSL(boolean ssl)
1623 {
1624 setSSLOnConnect(ssl);
1625 }
1626
1627 /**
1628 * Sets whether SSL/TLS encryption should be enabled for the SMTP transport upon connection (SMTPS/POPS).
1629 *
1630 * @param ssl whether to enable the SSL transport
1631 * @return An Email.
1632 * @since 1.3
1633 */
1634 public Email setSSLOnConnect(boolean ssl)
1635 {
1636 checkSessionAlreadyInitialized();
1637 this.sslOnConnect = ssl;
1638 this.ssl = ssl;
1639 return this;
1640 }
1641
1642 /**
1643 * Is the server identity checked as specified by RFC 2595
1644 *
1645 * @return true if the server identity is checked
1646 * @since 1.3
1647 */
1648 public boolean isSSLCheckServerIdentity()
1649 {
1650 return sslCheckServerIdentity;
1651 }
1652
1653 /**
1654 * Sets whether the server identity is checked as specified by RFC 2595
1655 *
1656 * @param sslCheckServerIdentity whether to enable server identity check
1657 * @return An Email.
1658 * @since 1.3
1659 */
1660 public Email setSSLCheckServerIdentity(boolean sslCheckServerIdentity)
1661 {
1662 this.sslCheckServerIdentity = sslCheckServerIdentity;
1663 return this;
1664 }
1665
1666 /**
1667 * Returns the current SSL port used by the SMTP transport.
1668 *
1669 * @return the current SSL port used by the SMTP transport
1670 */
1671 public String getSslSmtpPort()
1672 {
1673 if (this.session != null)
1674 {
1675 return this.session.getProperty(MAIL_SMTP_SOCKET_FACTORY_PORT);
1676 }
1677 else if (EmailUtils.isNotEmpty(this.sslSmtpPort))
1678 {
1679 return this.sslSmtpPort;
1680 }
1681 return null;
1682 }
1683
1684 /**
1685 * Sets the SSL port to use for the SMTP transport. Defaults to the standard
1686 * port, 465.
1687 *
1688 * @param sslSmtpPort the SSL port to use for the SMTP transport
1689 */
1690 public void setSslSmtpPort(String sslSmtpPort)
1691 {
1692 checkSessionAlreadyInitialized();
1693 this.sslSmtpPort = sslSmtpPort;
1694 }
1695
1696 /**
1697 * Get the list of "To" addresses.
1698 *
1699 * @return List addresses
1700 */
1701 public List<InternetAddress> getToAddresses()
1702 {
1703 return this.toList;
1704 }
1705
1706 /**
1707 * Get the list of "CC" addresses.
1708 *
1709 * @return List addresses
1710 */
1711 public List<InternetAddress> getCcAddresses()
1712 {
1713 return this.ccList;
1714 }
1715
1716 /**
1717 * Get the list of "Bcc" addresses.
1718 *
1719 * @return List addresses
1720 */
1721 public List<InternetAddress> getBccAddresses()
1722 {
1723 return this.bccList;
1724 }
1725
1726 /**
1727 * Get the list of "Reply-To" addresses.
1728 *
1729 * @return List addresses
1730 */
1731 public List<InternetAddress> getReplyToAddresses()
1732 {
1733 return this.replyList;
1734 }
1735
1736 /**
1737 * Get the socket connection timeout value in milliseconds.
1738 *
1739 * @return the timeout in milliseconds.
1740 * @since 1.2
1741 */
1742 public int getSocketConnectionTimeout()
1743 {
1744 return this.socketConnectionTimeout;
1745 }
1746
1747 /**
1748 * Set the socket connection timeout value in milliseconds.
1749 * Default is a 60 second timeout.
1750 *
1751 * @param socketConnectionTimeout the connection timeout
1752 * @since 1.2
1753 */
1754 public void setSocketConnectionTimeout(int socketConnectionTimeout)
1755 {
1756 checkSessionAlreadyInitialized();
1757 this.socketConnectionTimeout = socketConnectionTimeout;
1758 }
1759
1760 /**
1761 * Get the socket I/O timeout value in milliseconds.
1762 *
1763 * @return the socket I/O timeout
1764 * @since 1.2
1765 */
1766 public int getSocketTimeout()
1767 {
1768 return this.socketTimeout;
1769 }
1770
1771 /**
1772 * Set the socket I/O timeout value in milliseconds.
1773 * Default is 60 second timeout.
1774 *
1775 * @param socketTimeout the socket I/O timeout
1776 * @since 1.2
1777 */
1778 public void setSocketTimeout(int socketTimeout)
1779 {
1780 checkSessionAlreadyInitialized();
1781 this.socketTimeout = socketTimeout;
1782 }
1783
1784 /**
1785 * Factory method to create a customized MimeMessage which can be
1786 * implemented by a derived class, e.g. to set the message id.
1787 *
1788 * @param aSession mail session to be used
1789 * @return the newly created message
1790 */
1791 protected MimeMessage createMimeMessage(Session aSession)
1792 {
1793 return new MimeMessage(aSession);
1794 }
1795
1796 /**
1797 * Create a folded header value containing 76 character chunks.
1798 *
1799 * @param name the name of the header
1800 * @param value the value of the header
1801 * @return the folded header value
1802 * @throws IllegalArgumentException if either the name or value is null or empty
1803 */
1804 private String createFoldedHeaderValue(String name, Object value)
1805 {
1806 String result;
1807
1808 if (EmailUtils.isEmpty(name))
1809 {
1810 throw new IllegalArgumentException("name can not be null or empty");
1811 }
1812 if (value == null || EmailUtils.isEmpty(value.toString()))
1813 {
1814 throw new IllegalArgumentException("value can not be null or empty");
1815 }
1816
1817 try
1818 {
1819 result = MimeUtility.fold(name.length() + 2, MimeUtility.encodeText(value.toString(), this.charset, null));
1820 }
1821 catch (UnsupportedEncodingException e)
1822 {
1823 result = value.toString();
1824 }
1825
1826 return result;
1827 }
1828
1829 /**
1830 * Creates a InternetAddress.
1831 *
1832 * @param email An email address.
1833 * @param name A name.
1834 * @param charsetName The name of the charset to encode the name with.
1835 * @return An internet address.
1836 * @throws EmailException Thrown when the supplied address, name or charset were invalid.
1837 */
1838 private InternetAddress createInternetAddress(String email, String name, String charsetName)
1839 throws EmailException
1840 {
1841 InternetAddress address = null;
1842
1843 try
1844 {
1845 address = new InternetAddress(email);
1846
1847 // check name input
1848 if (EmailUtils.isNotEmpty(name))
1849 {
1850 // check charset input.
1851 if (EmailUtils.isEmpty(charsetName))
1852 {
1853 address.setPersonal(name);
1854 }
1855 else
1856 {
1857 // canonicalize the charset name and make sure
1858 // the current platform supports it.
1859 Charset set = Charset.forName(charsetName);
1860 address.setPersonal(name, set.name());
1861 }
1862 }
1863
1864 // run sanity check on new InternetAddress object; if this fails
1865 // it will throw AddressException.
1866 address.validate();
1867 }
1868 catch (AddressException e)
1869 {
1870 throw new EmailException(e);
1871 }
1872 catch (UnsupportedEncodingException e)
1873 {
1874 throw new EmailException(e);
1875 }
1876 return address;
1877 }
1878
1879 /**
1880 * When a mail session is already initialized setting the
1881 * session properties has no effect. In order to flag the
1882 * problem throw an IllegalStateException.
1883 *
1884 * @throws IllegalStateException when the mail session is already initialized
1885 */
1886 private void checkSessionAlreadyInitialized()
1887 {
1888 if (this.session != null)
1889 {
1890 throw new IllegalStateException("The mail session is already initialized");
1891 }
1892 }
1893 }