001    package railo.runtime.net.mail;
002    
003    import java.io.IOException;
004    import java.io.InputStream;
005    import java.net.URL;
006    import java.util.ArrayList;
007    import java.util.Iterator;
008    import java.util.List;
009    
010    import javax.activation.DataHandler;
011    import javax.activation.URLDataSource;
012    import javax.mail.BodyPart;
013    import javax.mail.MessagingException;
014    import javax.mail.internet.MimeBodyPart;
015    import javax.mail.internet.MimeMultipart;
016    
017    import org.apache.commons.mail.Email;
018    import org.apache.commons.mail.EmailException;
019    import org.apache.commons.mail.MultiPartEmail;
020    
021    import railo.commons.lang.StringUtil;
022    
023    /**
024     * An HTML multipart email.
025     *
026     * <p>This class is used to send HTML formatted email.  A text message
027     * can also be set for HTML unaware email clients, such as text-based
028     * email clients.
029     *
030     * <p>This class also inherits from MultiPartEmail, so it is easy to
031     * add attachments to the email.
032     *
033     * <p>To send an email in HTML, one should create a HtmlEmail, then
034     * use the setFrom, addTo, etc. methods.  The HTML content can be set
035     * with the setHtmlMsg method.  The alternative text content can be set
036     * with setTextMsg.
037     *
038     * <p>Either the text or HTML can be omitted, in which case the "main"
039     * part of the multipart becomes whichever is supplied rather than a
040     * multipart/alternative.
041     *
042     *
043     */
044    public final class HtmlEmailImpl extends MultiPartEmail
045    {
046        /** Definition of the length of generated CID's */
047        public static final int CID_LENGTH = 10;
048    
049        /**
050         * Text part of the message.  This will be used as alternative text if
051         * the email client does not support HTML messages.
052         */
053        protected String text;
054    
055        /** Html part of the message */
056        protected String html;
057    
058        /** Embedded images */
059        protected List inlineImages = new ArrayList();
060    
061        /**
062         * Set the text content.
063         *
064         * @param aText A String.
065         * @return An HtmlEmail.
066         * @throws EmailException see javax.mail.internet.MimeBodyPart
067         *  for definitions
068         *
069         */
070        public HtmlEmailImpl setTextMsg(String aText) throws EmailException {
071            if (StringUtil.isEmpty(aText)) {
072                throw new EmailException("Invalid message supplied");
073            }
074            this.text = aText;
075            return this;
076        }
077    
078        /**
079         * Set the HTML content.
080         *
081         * @param aHtml A String.
082         * @return An HtmlEmail.
083         * @throws EmailException see javax.mail.internet.MimeBodyPart
084         *  for definitions
085         *
086         */
087        public HtmlEmailImpl setHtmlMsg(String aHtml) throws EmailException {
088            if (StringUtil.isEmpty(aHtml)) {
089                throw new EmailException("Invalid message supplied");
090            }
091            this.html = aHtml;
092            return this;
093        }
094    
095        /**
096         * Set the message.
097         *
098         * <p>This method overrides the MultiPartEmail setMsg() method in
099         * order to send an HTML message instead of a full text message in
100         * the mail body. The message is formatted in HTML for the HTML
101         * part of the message, it is let as is in the alternate text
102         * part.
103         *
104         * @param msg A String.
105         * @return An Email.
106         * @throws EmailException see javax.mail.internet.MimeBodyPart
107         *  for definitions
108         *
109         */
110        public Email setMsg(String msg) throws EmailException {
111            if (StringUtil.isEmpty(msg)) {
112                throw new EmailException("Invalid message supplied");
113            }
114    
115            setTextMsg(msg);
116    
117            setHtmlMsg(
118                new StringBuffer()
119                    .append("<html><body><pre>")
120                    .append(msg)
121                    .append("</pre></body></html>")
122                    .toString());
123    
124            return this;
125        }
126    
127        /**
128         * Embeds an URL in the HTML.
129         *
130         * <p>This method allows to embed a file located by an URL into
131         * the mail body.  It allows, for instance, to add inline images
132         * to the email.  Inline files may be referenced with a
133         * <code>cid:xxxxxx</code> URL, where xxxxxx is the Content-ID
134         * returned by the embed function.
135         *
136         * <p>Example of use:<br><code><pre>
137         * HtmlEmail he = new HtmlEmail();
138         * he.setHtmlMsg("&lt;html&gt;&lt;img src=cid:" +
139         *  embed("file:/my/image.gif","image.gif") +
140         *  "&gt;&lt;/html&gt;");
141         * // code to set the others email fields (not shown)
142         * </pre></code>
143         *
144         * @param url The URL of the file.
145         * @param cid A String with the Content-ID of the file.
146         * @param name The name that will be set in the filename header
147         * field.
148         * @throws EmailException when URL supplied is invalid
149         *  also see javax.mail.internet.MimeBodyPart for definitions
150         *
151         */
152        public void embed(URL url, String cid, String name) throws EmailException {
153            // verify that the URL is valid
154            try {
155                InputStream is = url.openStream();
156                is.close();
157            }
158            catch (IOException e) {
159                throw new EmailException("Invalid URL");
160            }
161    
162            MimeBodyPart mbp = new MimeBodyPart();
163    
164            try {
165                mbp.setDataHandler(new DataHandler(new URLDataSource(url)));
166                mbp.setFileName(name);
167                mbp.setDisposition("inline");
168                mbp.addHeader("Content-ID", "<" + cid + ">");
169                this.inlineImages.add(mbp);
170            }
171            catch (MessagingException me) {
172                throw new EmailException(me);
173            }
174        }
175    
176        /**
177         * Does the work of actually building the email.
178         *
179         * @exception EmailException if there was an error.
180         *
181         */
182        public void buildMimeMessage() throws EmailException {
183            try {
184                // if the email has attachments then the base type is mixed,
185                // otherwise it should be related
186                if (this.isBoolHasAttachments()) {
187                    this.buildAttachments();
188                }
189                else {
190                    this.buildNoAttachments();
191                }
192    
193            }
194            catch (MessagingException me)
195            {
196                throw new EmailException(me);
197            }
198            super.buildMimeMessage();
199        }
200    
201        /**
202         * @throws EmailException EmailException
203         * @throws MessagingException MessagingException
204         */
205        private void buildAttachments() throws MessagingException, EmailException
206        {
207            MimeMultipart container = this.getContainer();
208            MimeMultipart subContainer = null;
209            MimeMultipart subContainerHTML = new MimeMultipart("related");
210            BodyPart msgHtml = null;
211            BodyPart msgText = null;
212    
213            container.setSubType("mixed");
214            subContainer = new MimeMultipart("alternative");
215    
216            if (!StringUtil.isEmpty(this.text)) {
217                msgText = new MimeBodyPart();
218                subContainer.addBodyPart(msgText);
219    
220                if (!StringUtil.isEmpty(this.charset)) {
221                    msgText.setContent(
222                        this.text,
223                        Email.TEXT_PLAIN + "; charset=" + this.charset);
224                }
225                else {
226                    msgText.setContent(this.text, Email.TEXT_PLAIN);
227                }
228            }
229    
230            if (!StringUtil.isEmpty(this.html))
231            {
232                if (this.inlineImages.size() > 0)
233                {
234                    msgHtml = new MimeBodyPart();
235                    subContainerHTML.addBodyPart(msgHtml);
236                }
237                else
238                {
239                    msgHtml = new MimeBodyPart();
240                    subContainer.addBodyPart(msgHtml);
241                }
242    
243                if (!StringUtil.isEmpty(this.charset))
244                {
245                    msgHtml.setContent(
246                        this.html,
247                        Email.TEXT_HTML + "; charset=" + this.charset);
248                }
249                else
250                {
251                    msgHtml.setContent(this.html, Email.TEXT_HTML);
252                }
253    
254                Iterator iter = this.inlineImages.iterator();
255                while (iter.hasNext())
256                {
257                    subContainerHTML.addBodyPart((BodyPart) iter.next());
258                }
259            }
260    
261            // add sub containers to message
262            this.addPart(subContainer, 0);
263    
264            if (this.inlineImages.size() > 0)
265            {
266                // add sub container to message
267                this.addPart(subContainerHTML, 1);
268            }
269        }
270    
271        /**
272         * @throws EmailException EmailException
273         * @throws MessagingException MessagingException
274         */
275        private void buildNoAttachments() throws MessagingException, EmailException
276        {
277            MimeMultipart container = this.getContainer();
278            MimeMultipart subContainerHTML = new MimeMultipart("related");
279    
280            container.setSubType("alternative");
281    
282            BodyPart msgText = null;
283            BodyPart msgHtml = null;
284    
285            if(!StringUtil.isEmpty(this.text)) {
286                msgText = this.getPrimaryBodyPart();
287                if (!StringUtil.isEmpty(this.charset)) {
288                    msgText.setContent(
289                        this.text,
290                        Email.TEXT_PLAIN + "; charset=" + this.charset);
291                }
292                else {
293                    msgText.setContent(this.text, Email.TEXT_PLAIN);
294                }
295            }
296    
297            if(!StringUtil.isEmpty(this.html)) {
298                // if the txt part of the message was null, then the html part
299                // will become the primary body part
300                if (msgText == null) {
301                    msgHtml = getPrimaryBodyPart();
302                }
303                else {
304                    if (this.inlineImages.size() > 0) {
305                        msgHtml = new MimeBodyPart();
306                        subContainerHTML.addBodyPart(msgHtml);
307                    }
308                    else {
309                        msgHtml = new MimeBodyPart();
310                        container.addBodyPart(msgHtml, 1);
311                    }
312                }
313    
314                if(!StringUtil.isEmpty(this.charset)) {
315                    msgHtml.setContent(
316                        this.html,
317                        Email.TEXT_HTML + "; charset=" + this.charset);
318                }
319                else {
320                    msgHtml.setContent(this.html, Email.TEXT_HTML);
321                }
322    
323                Iterator iter = this.inlineImages.iterator();
324                while (iter.hasNext())
325                {
326                    subContainerHTML.addBodyPart((BodyPart) iter.next());
327                }
328    
329                if (this.inlineImages.size() > 0)
330                {
331                    // add sub container to message
332                    this.addPart(subContainerHTML);
333                }
334            }
335        }
336    }