1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.mail.internal.factory.html

File HTMLMimeBodyPartFactory.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

14
54
6
1
200
123
13
0.24
9
6
2.17

Classes

Class Line # Actions
HTMLMimeBodyPartFactory 57 54 0% 13 0
1.0100%
 

Contributing tests

This file is covered by 7 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.mail.internal.factory.html;
21   
22    import java.nio.charset.StandardCharsets;
23    import java.util.ArrayList;
24    import java.util.Collections;
25    import java.util.List;
26    import java.util.Map;
27    import java.util.regex.Matcher;
28    import java.util.regex.Pattern;
29   
30    import javax.inject.Inject;
31    import javax.inject.Named;
32    import javax.inject.Singleton;
33    import javax.mail.MessagingException;
34    import javax.mail.internet.MimeBodyPart;
35    import javax.mail.internet.MimeMultipart;
36   
37    import org.apache.commons.lang3.tuple.ImmutablePair;
38    import org.apache.commons.lang3.tuple.Pair;
39    import org.xwiki.component.annotation.Component;
40    import org.xwiki.mail.MimeBodyPartFactory;
41    import org.xwiki.mail.internal.factory.AbstractMimeBodyPartFactory;
42   
43    import com.xpn.xwiki.api.Attachment;
44   
45    /**
46    * Creates an HTML {@link javax.mail.BodyPart} object that supports a text alternative and a list of attachments
47    * that will be added to the mail as standard attachments and also as embedded images if they are referenced in the
48    * passed HTML using the format {@code <img src="cid:(attachment name)"/>}.
49    *
50    * @version $Id: 4afbd850cbc55b5359ed17d17926b47c22f1da46 $
51    * @since 6.1M2
52    */
53    @Component
54    @Named("text/html")
55    @Singleton
56    @SuppressWarnings("unchecked")
 
57    public class HTMLMimeBodyPartFactory extends AbstractMimeBodyPartFactory<String>
58    {
59    private static final Pattern CID_PATTERN =
60    Pattern.compile("src=('|\")cid:([^'\"]*)('|\")", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
61   
62    private static final String TEXT_HTML_CONTENT_TYPE = "text/html; charset=" + StandardCharsets.UTF_8.name();
63   
64    @Inject
65    @Named("xwiki/attachment")
66    private MimeBodyPartFactory<Attachment> attachmentPartFactory;
67   
68    @Inject
69    private MimeBodyPartFactory<String> defaultPartFactory;
70   
 
71  13 toggle @Override
72    public MimeBodyPart create(String content, Map<String, Object> parameters) throws MessagingException
73    {
74  13 MimeBodyPart resultBodyPart;
75   
76    // Separate normal attachment from embedded image attachments
77  13 List<Attachment> allAttachments = (List<Attachment>) parameters.get("attachments");
78  13 Pair<List<Attachment>, List<Attachment>> attachmentPairs = separateAttachments(content, allAttachments);
79  13 List<Attachment> embeddedImageAttachments = attachmentPairs.getLeft();
80  13 List<Attachment> normalAttachments = attachmentPairs.getRight();
81   
82    // Step 1: Handle the HTML section of the mail.
83  13 MimeBodyPart htmlBodyPart;
84  13 if (!embeddedImageAttachments.isEmpty()) {
85  4 htmlBodyPart = new MimeBodyPart();
86  4 htmlBodyPart.setContent(createHTMLMultipart(content, embeddedImageAttachments));
87    } else {
88    // Create the HTML body part of the email
89  9 htmlBodyPart = createHTMLBodyPart(content, false);
90    }
91   
92    // Step 2: Handle the optional alternative text
93  13 String alternativeText = (String) parameters.get("alternate");
94  13 if (alternativeText != null) {
95  8 resultBodyPart = createAlternativePart(htmlBodyPart,
96    this.defaultPartFactory.create(alternativeText, Collections.<String, Object>emptyMap()));
97    } else {
98    // No alternative text, just add the HTML body part to the Multipart
99  5 resultBodyPart = htmlBodyPart;
100    }
101   
102    // Step 3 Add the normal attachments (if any). Any embedded images have already been handled in the HTML body
103    // part. Note: If there are attachments we need to wrap our body part inside a "mixed" Multipart.
104  13 if (!normalAttachments.isEmpty()) {
105  4 MimeMultipart multipart = new MimeMultipart("mixed");
106  4 multipart.addBodyPart(resultBodyPart);
107  4 handleAttachments(multipart, normalAttachments);
108  4 resultBodyPart = new MimeBodyPart();
109  4 resultBodyPart.setContent(multipart);
110    }
111   
112    // Handle headers passed as parameter
113  13 addHeaders(resultBodyPart, parameters);
114   
115  13 return resultBodyPart;
116    }
117   
 
118  8 toggle private void handleAttachments(MimeMultipart multipart, List<Attachment> attachments) throws MessagingException
119    {
120  8 for (Attachment attachment : attachments) {
121  27 multipart.addBodyPart(
122    this.attachmentPartFactory.create(attachment, Collections.<String, Object>emptyMap()));
123    }
124    }
125   
126    /**
127    * @return Multipart part of the email, define the HTML as a multipart/alternative
128    */
 
129  8 toggle private MimeBodyPart createAlternativePart(MimeBodyPart htmlBodyPart, MimeBodyPart textBodyPart)
130    throws MessagingException
131    {
132  8 MimeMultipart alternativeMultiPart = new MimeMultipart("alternative");
133   
134    // Note: it's important to have the text before the HTML, from low fidelity to high fidelity according to the
135    // MIME specification. Otherwise some readers will display text even though there's HTML in your mail message.
136  8 alternativeMultiPart.addBodyPart(textBodyPart);
137  8 alternativeMultiPart.addBodyPart(htmlBodyPart);
138   
139  8 MimeBodyPart alternativePartWrapper = new MimeBodyPart();
140  8 alternativePartWrapper.setContent(alternativeMultiPart);
141  8 return alternativePartWrapper;
142    }
143   
 
144  4 toggle private MimeMultipart createHTMLMultipart(String content, List<Attachment> embeddedImages)
145    throws MessagingException
146    {
147  4 MimeMultipart htmlMultipart = new MimeMultipart("related");
148  4 htmlMultipart.addBodyPart(createHTMLBodyPart(content, true));
149  4 handleAttachments(htmlMultipart, embeddedImages);
150  4 return htmlMultipart;
151    }
152   
 
153  13 toggle private MimeBodyPart createHTMLBodyPart(String content, boolean hasAttachments) throws MessagingException
154    {
155  13 MimeBodyPart htmlPart = new MimeBodyPart();
156  13 htmlPart.setContent(content, TEXT_HTML_CONTENT_TYPE);
157  13 htmlPart.setHeader("Content-Type", TEXT_HTML_CONTENT_TYPE);
158  13 if (hasAttachments) {
159  4 htmlPart.setHeader("Content-Disposition", "inline");
160  4 htmlPart.setHeader("Content-Transfer-Encoding", "quoted-printable");
161    }
162  13 return htmlPart;
163    }
164   
165    /**
166    * Separate embedded images from attachments list.
167    *
168    * @return the embedded attachments on the left and the normal attachments on the right of the Pair
169    */
 
170  13 toggle private Pair<List<Attachment>, List<Attachment>> separateAttachments(String content, List<Attachment> attachments)
171    {
172  13 if (attachments == null) {
173  7 return new ImmutablePair<>(Collections.<Attachment>emptyList(), Collections.<Attachment>emptyList());
174    }
175   
176    // Copy all attachments in the list of attachments to add to the email. We'll then remove the attachments
177    // that are embedded from the list below.
178  6 List<Attachment> normalAttachments = new ArrayList<>(attachments);
179   
180    // Find images used with src="cid:" in the email HTML part
181  6 Matcher matcher = CID_PATTERN.matcher(content);
182  6 List<String> embeddedImageNames = new ArrayList<>();
183  40 while (matcher.find()) {
184  34 embeddedImageNames.add(matcher.group(2));
185    }
186   
187    // Loop over the attachments of the email, add images used from the HTML to the list of attachments to be
188    // embedded with the HTML part, add the other attachments to the list of attachments to be attached to the
189    // email.
190  6 List<Attachment> embeddedImageAttachments = new ArrayList<>();
191  6 for (Attachment attachment : attachments) {
192  27 if (embeddedImageNames.contains(attachment.getFilename())) {
193  23 embeddedImageAttachments.add(attachment);
194  23 normalAttachments.remove(attachment);
195    }
196    }
197   
198  6 return new ImmutablePair<>(embeddedImageAttachments, normalAttachments);
199    }
200    }