1 |
|
|
2 |
|
|
3 |
|
|
4 |
|
|
5 |
|
|
6 |
|
|
7 |
|
|
8 |
|
|
9 |
|
|
10 |
|
|
11 |
|
|
12 |
|
|
13 |
|
|
14 |
|
|
15 |
|
|
16 |
|
|
17 |
|
|
18 |
|
|
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 |
|
@link |
47 |
|
|
48 |
|
|
49 |
|
|
50 |
|
@version |
51 |
|
@since |
52 |
|
|
53 |
|
@Component |
54 |
|
@Named("text/html") |
55 |
|
@Singleton |
56 |
|
@SuppressWarnings("unchecked") |
|
|
| 100% |
Uncovered Elements: 0 (74) |
Complexity: 13 |
Complexity Density: 0.24 |
|
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 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (28) |
Complexity: 4 |
Complexity Density: 0.18 |
|
71 |
13 |
@Override... |
72 |
|
public MimeBodyPart create(String content, Map<String, Object> parameters) throws MessagingException |
73 |
|
{ |
74 |
13 |
MimeBodyPart resultBodyPart; |
75 |
|
|
76 |
|
|
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 |
|
|
83 |
13 |
MimeBodyPart htmlBodyPart; |
84 |
13 |
if (!embeddedImageAttachments.isEmpty()) { |
85 |
4 |
htmlBodyPart = new MimeBodyPart(); |
86 |
4 |
htmlBodyPart.setContent(createHTMLMultipart(content, embeddedImageAttachments)); |
87 |
|
} else { |
88 |
|
|
89 |
9 |
htmlBodyPart = createHTMLBodyPart(content, false); |
90 |
|
} |
91 |
|
|
92 |
|
|
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 |
|
|
99 |
5 |
resultBodyPart = htmlBodyPart; |
100 |
|
} |
101 |
|
|
102 |
|
|
103 |
|
|
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 |
|
|
113 |
13 |
addHeaders(resultBodyPart, parameters); |
114 |
|
|
115 |
13 |
return resultBodyPart; |
116 |
|
} |
117 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (2) |
Complexity: 1 |
Complexity Density: 0.5 |
|
118 |
8 |
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 |
128 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (6) |
Complexity: 1 |
Complexity Density: 0.17 |
|
129 |
8 |
private MimeBodyPart createAlternativePart(MimeBodyPart htmlBodyPart, MimeBodyPart textBodyPart)... |
130 |
|
throws MessagingException |
131 |
|
{ |
132 |
8 |
MimeMultipart alternativeMultiPart = new MimeMultipart("alternative"); |
133 |
|
|
134 |
|
|
135 |
|
|
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 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (4) |
Complexity: 1 |
Complexity Density: 0.25 |
|
144 |
4 |
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 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (9) |
Complexity: 2 |
Complexity Density: 0.29 |
|
153 |
13 |
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 |
|
|
167 |
|
|
168 |
|
@return |
169 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (19) |
Complexity: 4 |
Complexity Density: 0.31 |
|
170 |
13 |
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 |
|
|
177 |
|
|
178 |
6 |
List<Attachment> normalAttachments = new ArrayList<>(attachments); |
179 |
|
|
180 |
|
|
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 |
|
|
188 |
|
|
189 |
|
|
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 |
|
} |