View Javadoc

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