| 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 |
|
} |