|
11 | 11 |
|
12 | 12 | import java.io.InputStream; |
13 | 13 | import java.time.OffsetDateTime; |
| 14 | +import java.util.HashSet; |
14 | 15 | import java.util.List; |
15 | 16 | import java.util.Objects; |
16 | 17 | import java.util.Set; |
| 18 | +import java.util.regex.Pattern; |
17 | 19 |
|
18 | 20 | @Builder |
19 | 21 | @NullMarked |
@@ -48,12 +50,43 @@ public record Email( |
48 | 50 | @Valid |
49 | 51 | Set<Attachment> attachments |
50 | 52 | ) { |
| 53 | + // the "cid:" scheme is case-insensitive, the contentId itself is not |
| 54 | + private static final Pattern CID_REFERENCE_PATTERN = Pattern.compile( |
| 55 | + "cid:([^\\s\"'<>]+)", |
| 56 | + Pattern.CASE_INSENSITIVE |
| 57 | + ); |
| 58 | + |
51 | 59 | @AssertTrue(message = "each inline attachment contentId must be referenced in the htmlBody as cid:contentId") |
52 | 60 | public boolean isInlineAttachmentsValid() { |
| 61 | + var contentIds = inlineContentIds(); |
| 62 | + if (contentIds.isEmpty()) { |
| 63 | + return true; |
| 64 | + } |
| 65 | + |
| 66 | + if (htmlBody == null) { |
| 67 | + return false; |
| 68 | + } |
| 69 | + |
| 70 | + var referencedContentIds = new HashSet<String>(); |
| 71 | + var matcher = CID_REFERENCE_PATTERN.matcher(htmlBody); |
| 72 | + while (matcher.find()) { |
| 73 | + referencedContentIds.add(matcher.group(1)); |
| 74 | + } |
| 75 | + |
| 76 | + return referencedContentIds.containsAll(contentIds); |
| 77 | + } |
| 78 | + |
| 79 | + @AssertTrue(message = "each inline attachment contentId must be unique") |
| 80 | + public boolean isInlineAttachmentContentIdsUnique() { |
| 81 | + var contentIds = inlineContentIds(); |
| 82 | + return contentIds.size() == new HashSet<>(contentIds).size(); |
| 83 | + } |
| 84 | + |
| 85 | + private List<String> inlineContentIds() { |
53 | 86 | return attachments.stream() |
54 | 87 | .map(Attachment::contentId) |
55 | 88 | .filter(Objects::nonNull) |
56 | | - .allMatch(contentId -> htmlBody.contains("cid:" + contentId)); |
| 89 | + .toList(); |
57 | 90 | } |
58 | 91 |
|
59 | 92 | @Builder |
|
0 commit comments