001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.mail;
018    
019    import java.io.File;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.net.URL;
023    
024    import javax.activation.DataHandler;
025    import javax.activation.DataSource;
026    import javax.activation.FileDataSource;
027    import javax.activation.URLDataSource;
028    import javax.mail.BodyPart;
029    import javax.mail.MessagingException;
030    import javax.mail.internet.MimeBodyPart;
031    import javax.mail.internet.MimeMultipart;
032    import javax.mail.internet.MimePart;
033    
034    /**
035     * A multipart email.
036     *
037     * <p>This class is used to send multi-part internet email like
038     * messages with attachments.
039     *
040     * <p>To create a multi-part email, call the default constructor and
041     * then you can call setMsg() to set the message and call the
042     * different attach() methods.
043     *
044     * @since 1.0
045     * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
046     * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
047     * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
048     * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
049     * @author <a href="mailto:unknown">Regis Koenig</a>
050     * @author <a href="mailto:corey.scott@gmail.com">Corey Scott</a>
051     * @version $Id: MultiPartEmail.java 741633 2009-02-06 17:02:44Z sgoeschl $
052     */
053    public class MultiPartEmail extends Email
054    {
055        /** Body portion of the email. */
056        private MimeMultipart container;
057    
058        /** The message container. */
059        private BodyPart primaryBodyPart;
060    
061        /** The MIME subtype. */
062        private String subType;
063    
064        /** Indicates if the message has been initialized */
065        private boolean initialized;
066    
067        /** Indicates if attachments have been added to the message */
068        private boolean boolHasAttachments;
069    
070        /**
071         * Set the MIME subtype of the email.
072         *
073         * @param aSubType MIME subtype of the email
074         * @since 1.0
075         */
076        public void setSubType(String aSubType)
077        {
078            this.subType = aSubType;
079        }
080    
081        /**
082         * Get the MIME subtype of the email.
083         *
084         * @return MIME subtype of the email
085         * @since 1.0
086         */
087        public String getSubType()
088        {
089            return subType;
090        }
091    
092        /**
093         * Add a new part to the email.
094         *
095         * @param partContent The content.
096         * @param partContentType The content type.
097         * @return An Email.
098         * @throws EmailException see javax.mail.internet.MimeBodyPart
099         *  for definitions
100         * @since 1.0
101         */
102        public Email addPart(String partContent, String partContentType)
103            throws EmailException
104        {
105                BodyPart bodyPart = createBodyPart();
106            try
107            {
108                bodyPart.setContent(partContent, partContentType);
109                getContainer().addBodyPart(bodyPart);
110            }
111            catch (MessagingException me)
112            {
113                throw new EmailException(me);
114            }
115    
116            return this;
117        }
118    
119        /**
120         * Add a new part to the email.
121         *
122         * @param multipart The MimeMultipart.
123         * @return An Email.
124         * @throws EmailException see javax.mail.internet.MimeBodyPart
125         *  for definitions
126         *  @since 1.0
127         */
128        public Email addPart(MimeMultipart multipart) throws EmailException
129        {
130            try
131            {
132                return addPart(multipart, getContainer().getCount());
133            }
134            catch (MessagingException me)
135            {
136                throw new EmailException(me);
137            }
138        }
139    
140        /**
141         * Add a new part to the email.
142         *
143         * @param multipart The part to add.
144         * @param index The index to add at.
145         * @return The email.
146         * @throws EmailException An error occured while adding the part.
147         * @since 1.0
148         */
149        public Email addPart(MimeMultipart multipart, int index) throws EmailException
150        {
151                BodyPart bodyPart = createBodyPart();
152            try
153            {
154                bodyPart.setContent(multipart);
155                getContainer().addBodyPart(bodyPart, index);
156            }
157            catch (MessagingException me)
158            {
159                throw new EmailException(me);
160            }
161    
162            return this;
163        }
164    
165        /**
166         * Initialize the multipart email.
167         * @since 1.0
168         */
169        protected void init()
170        {
171            if (initialized)
172            {
173                throw new IllegalStateException("Already initialized");
174            }
175    
176            container = createMimeMultipart();
177            super.setContent(container);
178    
179            initialized = true;
180        }
181    
182        /**
183         * Set the message of the email.
184         *
185         * @param msg A String.
186         * @return An Email.
187         * @throws EmailException see javax.mail.internet.MimeBodyPart
188         *  for definitions
189         * @since 1.0
190         */
191        public Email setMsg(String msg) throws EmailException
192        {
193            // throw exception on null message
194            if (EmailUtils.isEmpty(msg))
195            {
196                throw new EmailException("Invalid message supplied");
197            }
198            try
199            {
200                BodyPart primary = getPrimaryBodyPart();
201    
202                if ((primary instanceof MimePart) && EmailUtils.isNotEmpty(charset))
203                {
204                    ((MimePart) primary).setText(msg, charset);
205                }
206                else
207                {
208                    primary.setText(msg);
209                }
210            }
211            catch (MessagingException me)
212            {
213                throw new EmailException(me);
214            }
215            return this;
216        }
217    
218        /**
219         * Builds the actual MimeMessage
220         *
221         * @throws EmailException see javax.mail.internet.MimeBodyPart
222         *  for definitions
223         * @since 1.0
224         */
225        public void buildMimeMessage() throws EmailException
226        {
227            try
228            {
229                if (primaryBodyPart != null)
230                {
231                    // before a multipart message can be sent, we must make sure that
232                    // the content for the main body part was actually set.  If not,
233                    // an IOException will be thrown during super.send().
234    
235                    BodyPart body = this.getPrimaryBodyPart();
236                    try
237                    {
238                        body.getContent();
239                    }
240                    catch (IOException e)
241                    {
242                        // do nothing here.  content will be set to an empty string
243                        // as a result.
244                        // (Should this really be rethrown as an email exception?)
245                        // throw new EmailException(e);
246                    }
247                }
248    
249                if (subType != null)
250                {
251                    getContainer().setSubType(subType);
252                }
253    
254                super.buildMimeMessage();
255            }
256            catch (MessagingException me)
257            {
258                throw new EmailException(me);
259            }
260        }
261    
262        /**
263         * Attach an EmailAttachment.
264         *
265         * @param attachment An EmailAttachment.
266         * @return A MultiPartEmail.
267         * @throws EmailException see javax.mail.internet.MimeBodyPart
268         *  for definitions
269         * @since 1.0
270         */
271        public MultiPartEmail attach(EmailAttachment attachment)
272            throws EmailException
273        {
274            MultiPartEmail result = null;
275    
276            if (attachment == null)
277            {
278                throw new EmailException("Invalid attachment supplied");
279            }
280    
281            URL url = attachment.getURL();
282    
283            if (url == null)
284            {
285                String fileName = null;
286                try
287                {
288                    fileName = attachment.getPath();
289                    File file = new File(fileName);
290                    if (!file.exists())
291                    {
292                        throw new IOException(
293                            "\"" + fileName + "\" does not exist");
294                    }
295                    result =
296                        attach(
297                            new FileDataSource(file),
298                            attachment.getName(),
299                            attachment.getDescription(),
300                            attachment.getDisposition());
301                }
302                catch (IOException e)
303                {
304                    throw new EmailException(
305                        "Cannot attach file \"" + fileName + "\"",
306                        e);
307                }
308            }
309            else
310            {
311                result =
312                    attach(
313                        url,
314                        attachment.getName(),
315                        attachment.getDescription(),
316                        attachment.getDisposition());
317            }
318    
319            return result;
320        }
321    
322        /**
323         * Attach a file located by its URL.  The disposition of the file
324         * is set to mixed.
325         *
326         * @param url The URL of the file (may be any valid URL).
327         * @param name The name field for the attachment.
328         * @param description A description for the attachment.
329         * @return A MultiPartEmail.
330         * @throws EmailException see javax.mail.internet.MimeBodyPart
331         *  for definitions
332         * @since 1.0
333         */
334        public MultiPartEmail attach(URL url, String name, String description)
335            throws EmailException
336        {
337            return attach(url, name, description, EmailAttachment.ATTACHMENT);
338        }
339    
340        /**
341         * Attach a file located by its URL.
342         *
343         * @param url The URL of the file (may be any valid URL).
344         * @param name The name field for the attachment.
345         * @param description A description for the attachment.
346         * @param disposition Either mixed or inline.
347         * @return A MultiPartEmail.
348         * @throws EmailException see javax.mail.internet.MimeBodyPart
349         *  for definitions
350         * @since 1.0
351         */
352        public MultiPartEmail attach(
353            URL url,
354            String name,
355            String description,
356            String disposition)
357            throws EmailException
358        {
359            // verify that the URL is valid
360           try
361           {
362               InputStream is = url.openStream();
363               is.close();
364           }
365           catch (IOException e)
366           {
367               throw new EmailException("Invalid URL set:" + url, e);
368           }
369    
370           return attach(new URLDataSource(url), name, description, disposition);
371        }
372    
373        /**
374         * Attach a file specified as a DataSource interface.
375         *
376         * @param ds A DataSource interface for the file.
377         * @param name The name field for the attachment.
378         * @param description A description for the attachment.
379         * @return A MultiPartEmail.
380         * @throws EmailException see javax.mail.internet.MimeBodyPart
381         *  for definitions
382         * @since 1.0
383         */
384        public MultiPartEmail attach(
385            DataSource ds,
386            String name,
387            String description)
388            throws EmailException
389        {
390            // verify that the DataSource is valid
391            try
392            {
393                if (ds == null || ds.getInputStream() == null)
394                {
395                    throw new EmailException("Invalid Datasource");
396                }
397            }
398            catch (IOException e)
399            {
400                throw new EmailException("Invalid Datasource", e);
401            }
402    
403            return attach(ds, name, description, EmailAttachment.ATTACHMENT);
404        }
405    
406        /**
407         * Attach a file specified as a DataSource interface.
408         *
409         * @param ds A DataSource interface for the file.
410         * @param name The name field for the attachment.
411         * @param description A description for the attachment.
412         * @param disposition Either mixed or inline.
413         * @return A MultiPartEmail.
414         * @throws EmailException see javax.mail.internet.MimeBodyPart
415         *  for definitions
416         * @since 1.0
417         */
418        public MultiPartEmail attach(
419            DataSource ds,
420            String name,
421            String description,
422            String disposition)
423            throws EmailException
424        {
425            if (EmailUtils.isEmpty(name))
426            {
427                name = ds.getName();
428            }
429            BodyPart bodyPart = createBodyPart();
430            try
431            {
432                getContainer().addBodyPart(bodyPart);
433    
434                bodyPart.setDisposition(disposition);
435                bodyPart.setFileName(name);
436                bodyPart.setDescription(description);
437                bodyPart.setDataHandler(new DataHandler(ds));
438            }
439            catch (MessagingException me)
440            {
441                throw new EmailException(me);
442            }
443            setBoolHasAttachments(true);
444    
445            return this;
446        }
447    
448        /**
449         * Gets first body part of the message.
450         *
451         * @return The primary body part.
452         * @throws MessagingException An error occured while getting the primary body part.
453         * @since 1.0
454         */
455        protected BodyPart getPrimaryBodyPart() throws MessagingException
456        {
457            if (!initialized)
458            {
459                init();
460            }
461    
462            // Add the first body part to the message.  The fist body part must be
463            if (this.primaryBodyPart == null)
464            {
465                primaryBodyPart = createBodyPart();
466                getContainer().addBodyPart(primaryBodyPart, 0);
467            }
468    
469            return primaryBodyPart;
470        }
471    
472        /**
473         * Gets the message container.
474         *
475         * @return The message container.
476         * @since 1.0
477         */
478        protected MimeMultipart getContainer()
479        {
480            if (!initialized)
481            {
482                init();
483            }
484            return container;
485        }
486    
487        /**
488         * Creates a body part object.
489         * Can be overridden if you don't want to create a BodyPart.
490         *
491         * @return the created body part
492         */
493        protected BodyPart createBodyPart()
494        {
495            BodyPart bodyPart = new MimeBodyPart();
496            return bodyPart;
497        }
498        /**
499         * Creates a mime multipart object.
500         *
501         * @return the created mime part
502         */
503        protected MimeMultipart createMimeMultipart()
504        {
505            MimeMultipart mmp = new MimeMultipart();
506            return mmp;
507        }
508    
509        /**
510         * Checks whether there are attachments.
511         *
512         * @return true if there are attachments
513         * @since 1.0
514         */
515        public boolean isBoolHasAttachments()
516        {
517            return boolHasAttachments;
518        }
519    
520        /**
521         * Sets whether there are attachments.
522         *
523         * @param b  the attachments flag
524         * @since 1.0
525         */
526        public void setBoolHasAttachments(boolean b)
527        {
528            boolHasAttachments = b;
529        }
530    
531        /**
532         * Checks if this object is initialized.
533         *
534         * @return true if initialized
535         */
536        protected boolean isInitialized()
537        {
538            return initialized;
539        }
540    
541        /**
542         * Sets the initialized status of this object.
543         *
544         * @param b  the initialized status flag
545         */
546        protected void setInitialized(boolean b)
547        {
548            initialized = b;
549        }
550    
551    }