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.util;
18  
19  import javax.activation.DataHandler;
20  import javax.activation.DataSource;
21  import javax.mail.Message;
22  import javax.mail.MessagingException;
23  import javax.mail.Multipart;
24  import javax.mail.Part;
25  import javax.mail.internet.InternetAddress;
26  import javax.mail.internet.MimeBodyPart;
27  import javax.mail.internet.MimeMessage;
28  import javax.mail.internet.MimePart;
29  import javax.mail.internet.MimeUtility;
30  import javax.mail.util.ByteArrayDataSource;
31  import java.io.BufferedInputStream;
32  import java.io.BufferedOutputStream;
33  import java.io.ByteArrayOutputStream;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.io.UnsupportedEncodingException;
37  import java.util.ArrayList;
38  import java.util.Arrays;
39  import java.util.List;
40  
41  /**
42   * Parses a MimeMessage and stores the individual parts such a plain text,
43   * HTML text and attachments.
44   *
45   * @since 1.3
46   * @version $Id: MimeMessageParser.java 1420381 2012-12-11 20:18:05Z tn $
47   */
48  public class MimeMessageParser
49  {
50      /** The MimeMessage to convert */
51      private final MimeMessage mimeMessage;
52  
53      /** Plain mail content from MimeMessage */
54      private String plainContent;
55  
56      /** Html mail content from MimeMessage */
57      private String htmlContent;
58  
59      /** List of attachments of MimeMessage */
60      private final List<DataSource> attachmentList;
61  
62      /** Is this a Multipart email */
63      private boolean isMultiPart;
64  
65      /**
66       * Constructs an instance with the MimeMessage to be extracted.
67       *
68       * @param message the message to parse
69       */
70      public MimeMessageParser(final MimeMessage message)
71      {
72          attachmentList = new ArrayList<DataSource>();
73          this.mimeMessage = message;
74          this.isMultiPart = false;
75      }
76  
77      /**
78       * Does the actual extraction.
79       *
80       * @return this instance
81       * @throws Exception parsing the mime message failed
82       */
83      public MimeMessageParser parse() throws Exception
84      {
85          this.parse(null, mimeMessage);
86          return this;
87      }
88  
89      /**
90       * @return the 'to' recipients of the message
91       * @throws Exception determining the recipients failed
92       */
93      public List<javax.mail.Address> getTo() throws Exception
94      {
95          javax.mail.Address[] recipients = this.mimeMessage.getRecipients(Message.RecipientType.TO);
96          return recipients != null ? Arrays.asList(recipients) : new ArrayList<javax.mail.Address>();
97      }
98  
99      /**
100      * @return the 'cc' recipients of the message
101      * @throws Exception determining the recipients failed
102      */
103     public List<javax.mail.Address> getCc() throws Exception
104     {
105         javax.mail.Address[] recipients = this.mimeMessage.getRecipients(Message.RecipientType.CC);
106         return recipients != null ? Arrays.asList(recipients) : new ArrayList<javax.mail.Address>();
107     }
108 
109     /**
110      * @return the 'bcc' recipients of the message
111      * @throws Exception determining the recipients failed
112      */
113     public List<javax.mail.Address> getBcc() throws Exception
114     {
115         javax.mail.Address[] recipients = this.mimeMessage.getRecipients(Message.RecipientType.BCC);
116         return recipients != null ? Arrays.asList(recipients) : new ArrayList<javax.mail.Address>();
117     }
118 
119     /**
120      * @return the 'from' field of the message
121      * @throws Exception parsing the mime message failed
122      */
123     public String getFrom() throws Exception
124     {
125         javax.mail.Address[] addresses = this.mimeMessage.getFrom();
126         if ((addresses == null) || (addresses.length == 0))
127         {
128             return null;
129         }
130         else
131         {
132             return ((InternetAddress) addresses[0]).getAddress();
133         }
134     }
135 
136     /**
137      * @return the 'replyTo' address of the email
138      * @throws Exception parsing the mime message failed
139      */
140     public String getReplyTo() throws Exception
141     {
142         javax.mail.Address[] addresses = this.mimeMessage.getReplyTo();
143         if ((addresses == null) || (addresses.length == 0))
144         {
145             return null;
146         }
147         else
148         {
149             return ((InternetAddress) addresses[0]).getAddress();
150         }
151     }
152 
153     /**
154      * @return the mail subject
155      * @throws Exception parsing the mime message failed
156      */
157     public String getSubject() throws Exception
158     {
159         return this.mimeMessage.getSubject();
160     }
161 
162     /**
163      * Extracts the content of a MimeMessage recursively.
164      *
165      * @param parent the parent multi-part
166      * @param part   the current MimePart
167      * @throws MessagingException parsing the MimeMessage failed
168      * @throws IOException        parsing the MimeMessage failed
169      */
170     protected void parse(Multipart parent, MimePart part)
171         throws MessagingException, IOException
172     {
173         if (part.isMimeType("text/plain") && (plainContent == null))
174         {
175             plainContent = (String) part.getContent();
176         }
177         else
178         {
179             if (part.isMimeType("text/html") && (htmlContent == null))
180             {
181                 htmlContent = (String) part.getContent();
182             }
183             else
184             {
185                 if (part.isMimeType("multipart/*"))
186                 {
187                     this.isMultiPart = true;
188                     Multipart mp = (Multipart) part.getContent();
189                     int count = mp.getCount();
190 
191                     // iterate over all MimeBodyPart
192 
193                     for (int i = 0; i < count; i++)
194                     {
195                         parse(mp, (MimeBodyPart) mp.getBodyPart(i));
196                     }
197                 }
198                 else
199                 {
200                     this.attachmentList.add(createDataSource(parent, part));
201                 }
202             }
203         }
204     }
205 
206     /**
207      * Parses the MimePart to create a DataSource.
208      *
209      * @param parent the parent multi-part
210      * @param part   the current part to be processed
211      * @return the DataSource
212      * @throws MessagingException creating the DataSource failed
213      * @throws IOException        creating the DataSource failed
214      */
215     protected DataSource createDataSource(Multipart parent, MimePart part)
216         throws MessagingException, IOException
217     {
218         DataHandler dataHandler = part.getDataHandler();
219         DataSource dataSource = dataHandler.getDataSource();
220         String contentType = getBaseMimeType(dataSource.getContentType());
221         byte[] content = this.getContent(dataSource.getInputStream());
222         ByteArrayDataSource result = new ByteArrayDataSource(content, contentType);
223         String dataSourceName = getDataSourceName(part, dataSource);
224 
225         result.setName(dataSourceName);
226         return result;
227     }
228 
229     /** @return Returns the mimeMessage. */
230     public MimeMessage getMimeMessage()
231     {
232         return mimeMessage;
233     }
234 
235     /** @return Returns the isMultiPart. */
236     public boolean isMultipart()
237     {
238         return isMultiPart;
239     }
240 
241     /** @return Returns the plainContent if any */
242     public String getPlainContent()
243     {
244         return plainContent;
245     }
246 
247     /** @return Returns the attachmentList. */
248     public List<DataSource> getAttachmentList()
249     {
250         return attachmentList;
251     }
252 
253     /** @return Returns the htmlContent if any */
254     public String getHtmlContent()
255     {
256         return htmlContent;
257     }
258 
259     /** @return true if a plain content is available */
260     public boolean hasPlainContent()
261     {
262         return this.plainContent != null;
263     }
264 
265     /** @return true if HTML content is available */
266     public boolean hasHtmlContent()
267     {
268         return this.htmlContent != null;
269     }
270 
271     /** @return true if attachments are available */
272     public boolean hasAttachments()
273     {
274         return this.attachmentList.size() > 0;
275     }
276 
277     /**
278      * Find an attachment using its name.
279      *
280      * @param name the name of the attachment
281      * @return the corresponding datasource or null if nothing was found
282      */
283     public DataSource findAttachmentByName(String name)
284     {
285         DataSource dataSource;
286 
287         for (int i = 0; i < getAttachmentList().size(); i++)
288         {
289             dataSource = getAttachmentList().get(i);
290             if (name.equalsIgnoreCase(dataSource.getName()))
291             {
292                 return dataSource;
293             }
294         }
295 
296         return null;
297     }
298 
299     /**
300      * Determines the name of the data source if it is not already set.
301      *
302      * @param part the mail part
303      * @param dataSource the data source
304      * @return the name of the data source or {@code null} if no name can be determined
305      * @throws MessagingException accessing the part failed
306      * @throws UnsupportedEncodingException decoding the text failed
307      */
308     protected String getDataSourceName(Part part, DataSource dataSource)
309         throws MessagingException, UnsupportedEncodingException
310     {
311         String result = dataSource.getName();
312 
313         if (result == null || result.length() == 0)
314         {
315             result = part.getFileName();
316         }
317 
318         if (result != null && result.length() > 0)
319         {
320             result = MimeUtility.decodeText(result);
321         }
322         else
323         {
324             result = null;
325         }
326 
327         return result;
328     }
329 
330     /**
331      * Read the content of the input stream.
332      *
333      * @param is the input stream to process
334      * @return the content of the input stream
335      * @throws IOException reading the input stream failed
336      */
337     private byte[] getContent(InputStream is)
338         throws IOException
339     {
340         int ch;
341         byte[] result;
342 
343         ByteArrayOutputStream os = new ByteArrayOutputStream();
344         BufferedInputStream isReader = new BufferedInputStream(is);
345         BufferedOutputStream osWriter = new BufferedOutputStream(os);
346 
347         while ((ch = isReader.read()) != -1)
348         {
349             osWriter.write(ch);
350         }
351 
352         osWriter.flush();
353         result = os.toByteArray();
354         osWriter.close();
355 
356         return result;
357     }
358 
359     /**
360      * Parses the mimeType.
361      *
362      * @param fullMimeType the mime type from the mail api
363      * @return the real mime type
364      */
365     private String getBaseMimeType(String fullMimeType)
366     {
367         int pos = fullMimeType.indexOf(';');
368         if (pos >= 0)
369         {
370             return fullMimeType.substring(0, pos);
371         }
372         else
373         {
374             return fullMimeType;
375         }
376     }
377 }