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 */
017package org.apache.commons.mail;
018
019import java.io.File;
020import java.io.IOException;
021import java.io.InputStream;
022import java.net.URL;
023
024import javax.activation.DataHandler;
025import javax.activation.DataSource;
026import javax.activation.FileDataSource;
027import javax.activation.URLDataSource;
028import javax.mail.BodyPart;
029import javax.mail.MessagingException;
030import javax.mail.internet.MimeBodyPart;
031import javax.mail.internet.MimeMultipart;
032import 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 */
053public 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}