1
2
3
4
5
6
7
8 package test.atlassian.mail;
9
10 import com.atlassian.core.ofbiz.test.AbstractOFBizTestCase;
11 import com.atlassian.core.ofbiz.test.UtilsForTests;
12 import com.atlassian.core.user.UserUtils;
13 import com.atlassian.mail.MailUtils;
14 import com.opensymphony.user.DuplicateEntityException;
15 import com.opensymphony.user.ImmutableException;
16 import com.opensymphony.user.User;
17 import test.mock.mail.MockMessage;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.List;
25 import java.util.Properties;
26 import javax.mail.Address;
27 import javax.mail.BodyPart;
28 import javax.mail.Message;
29 import javax.mail.MessagingException;
30 import javax.mail.Multipart;
31 import javax.mail.Part;
32 import javax.mail.Session;
33 import javax.mail.internet.AddressException;
34 import javax.mail.internet.InternetAddress;
35 import javax.mail.internet.MimeBodyPart;
36 import javax.mail.internet.MimeMessage;
37 import javax.mail.internet.NewsAddress;
38
39 import org.apache.commons.io.IOUtils;
40
41 public class TestMailUtils extends AbstractOFBizTestCase
42 {
43 private User testuser;
44
45 public TestMailUtils(String s)
46 {
47 super(s);
48 }
49
50 protected void setUp() throws Exception
51 {
52 super.setUp();
53 testuser = UserUtils.createUser("test user", "password", "test@atlassian.com", "test user fullname");
54 }
55
56 protected void tearDown() throws Exception
57 {
58 UtilsForTests.cleanUsers();
59
60 super.tearDown();
61 }
62
63 public void testParseAddresses() throws AddressException
64 {
65 InternetAddress[] addresses = MailUtils.parseAddresses("edwin@atlassian.com, mike@atlassian.com, owen@atlassian.com");
66
67 assertEquals(new InternetAddress("edwin@atlassian.com"), addresses[0]);
68 assertEquals(new InternetAddress("mike@atlassian.com"), addresses[1]);
69 assertEquals(new InternetAddress("owen@atlassian.com"), addresses[2]);
70 }
71
72 public void testGetBody() throws MessagingException, IOException
73 {
74 Message msg = new MimeMessage(null, new ByteArrayInputStream("test".getBytes()));
75 msg.setContent("test message", "");
76 msg.setHeader("Content-Type", "text/plain");
77 assertEquals("test message", MailUtils.getBody(msg));
78
79 msg.setContent(new Integer(1), "text/plain");
80 assertNull(MailUtils.getBody(msg));
81 }
82
83 public void testGetAuthorFromSender() throws MessagingException, ImmutableException, DuplicateEntityException
84 {
85 Message msg = new MimeMessage(null, new ByteArrayInputStream("test".getBytes()));
86 assertNull(MailUtils.getAuthorFromSender(msg));
87
88 msg.setFrom(new InternetAddress("edwin@atlassian.com"));
89 assertNull(MailUtils.getAuthorFromSender(msg));
90
91 msg.setFrom(new InternetAddress("test@atlassian.com"));
92 assertNotNull(MailUtils.getAuthorFromSender(msg));
93 assertEquals(testuser, MailUtils.getAuthorFromSender(msg));
94 }
95
96 public void testGetFirstValidUser() throws ImmutableException, DuplicateEntityException, AddressException
97 {
98 Address[] blankaddresslist = { };
99 assertNull(MailUtils.getFirstValidUser(blankaddresslist));
100
101 Address[] nouseraddresslist = { new InternetAddress("edwin@atlassian.com"), new InternetAddress("owen@atlassian.coM") };
102 assertNull(MailUtils.getFirstValidUser(nouseraddresslist));
103
104 Address[] addresslist = { new InternetAddress("mike@atlassian.com"), new InternetAddress("test@atlassian.com") };
105 assertNotNull(MailUtils.getFirstValidUser(addresslist));
106 assertEquals(testuser, MailUtils.getFirstValidUser(addresslist));
107 }
108
109
110
111
112
113
114
115
116 public void testGetSenders() throws Exception
117 {
118 Address[] blankaddresslist = { };
119
120 Message msg = createMockMessageWithSenders(blankaddresslist);
121 assertEquals(0, MailUtils.getSenders(msg).size());
122
123
124 Address[] allNulls = { null, null };
125 msg = createMockMessageWithSenders(allNulls);
126 assertEquals(0, MailUtils.getSenders(msg).size());
127
128
129 String goodAddress = "good@address.com";
130 Address[] oneGood = { null, new InternetAddress(goodAddress) };
131 msg = createMockMessageWithSenders(oneGood);
132 List senders = MailUtils.getSenders(msg);
133 assertEquals(1, senders.size());
134 assertEquals(goodAddress, senders.get(0));
135
136
137 Address[] mixedBag = {
138 new InternetAddress(goodAddress),
139 new NewsAddress(),
140 null,
141 new InternetAddress(" " + goodAddress) };
142 msg = createMockMessageWithSenders(mixedBag);
143 senders = MailUtils.getSenders(msg);
144 assertEquals(2, senders.size());
145
146 assertEquals(goodAddress, senders.get(0));
147 assertEquals(goodAddress, senders.get(1));
148 }
149
150 private Message createMockMessageWithSenders(final Address[] addresses) throws MessagingException
151 {
152 Message msg = new MockMessage()
153 {
154
155 public Address[] getFrom() throws MessagingException
156 {
157 return addresses;
158 }
159 };
160 return msg;
161 }
162
163
164 public void testHasRecipient() throws MessagingException
165 {
166 Message msg = new MimeMessage(null, new ByteArrayInputStream("test".getBytes()));
167 assertTrue(!MailUtils.hasRecipient("edwin@atlassian.com", msg));
168 msg.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress("edwin@atlassian.com"));
169 assertTrue(MailUtils.hasRecipient("edwin@atlassian.com", msg));
170 }
171
172 public void testCreateAttachmentMimeBodyPartFileNames() throws MessagingException
173 {
174 MimeBodyPart mbp = MailUtils.createAttachmentMimeBodyPart("C:/test/foo/bar/export.zip");
175 assertEquals("export.zip", mbp.getFileName());
176 }
177
178 public void testCreateAttachmentMimeBodyPartFileNames2() throws MessagingException
179 {
180 MimeBodyPart mbp = MailUtils.createAttachmentMimeBodyPart("C:\\test\\foo\\bar\\export.zip");
181 assertEquals("export.zip", mbp.getFileName());
182 }
183
184 public void testGetBodyText() throws MessagingException
185 {
186 assertEquals("This is a simple test mail, which isn't in HTML and has no attachments.",
187 MailUtils.getBody(makeMessageFromResourceFile("/testmails/simplebody.txt")));
188 }
189
190 public void testGetBodyTextIfContentEncodingUnsupported() throws MessagingException
191 {
192 assertEquals("This is a simple test mail, which isn't in HTML and has no attachments.",
193 MailUtils.getBody(makeMessageFromResourceFile("/testmails/simplebodyunsupportedencoding.txt")));
194 }
195
196 public void testGetBodyHtml() throws MessagingException
197 {
198 assertEquals("This is an HTML test mail\nwith a linebreak.",
199 MailUtils.getBody(makeMessageFromResourceFile("/testmails/simplehtml.txt")));
200 }
201
202 public void testGetBodyMultipartAlternativeGetsTextMatch() throws MessagingException
203 {
204 assertEquals("This is the text part of the mail",
205 MailUtils.getBody(makeMessageFromResourceFile("/testmails/multipart.txt")));
206 }
207
208 public void testGetBodyMultipartGetsFirstTextMatch() throws MessagingException
209 {
210 assertEquals("This is the first part",
211 MailUtils.getBody(makeMessageFromResourceFile("/testmails/multipartbothtext.txt")));
212 }
213
214 public void testGetBodyMultipartGetsHtmlIfNoText() throws MessagingException
215 {
216 assertEquals("This is\nthe HTML part of the mail",
217 MailUtils.getBody(makeMessageFromResourceFile("/testmails/multipartnotext.txt")));
218 }
219
220 public void testGetBodyMultipartNoMatch() throws MessagingException
221 {
222 assertEquals("", MailUtils.getBody(makeMessageFromResourceFile("/testmails/multipartnobody.txt")));
223 }
224
225 public void testGetBodyMultipartMixed() throws MessagingException
226 {
227 assertEquals("Text alternative", MailUtils.getBody(makeMessageFromResourceFile("/testmails/multipartmixedotherorder.txt")));
228 }
229
230 public void testGetBodyMultipartMixedWithLotsOfTextAndHtml() throws MessagingException
231 {
232 assertEquals("Text alternative\nThis is the first part after the alternative\nThis is the second part after the alternative",
233 MailUtils.getBody(makeMessageFromResourceFile("/testmails/multipartmixedtextandhtml.txt")));
234 }
235
236 public void testGetAttachmentsNoAttachments() throws MessagingException, IOException
237 {
238 assertEquals("no attachments", 0,
239 MailUtils.getAttachments(makeMessageFromResourceFile("/testmails/multipart.txt")).length);
240 }
241
242 public void testGetAttachmentsIfContentEncodingIsNotSupported() throws MessagingException, IOException
243 {
244 Message message = makeMessageFromResourceFile("/testmails/multipartunsupportedencoding.txt");
245 assertEquals("This is the text part of the mail", MailUtils.getBody(message));
246 assertEquals("no attachments", 0,
247 MailUtils.getAttachments(message).length);
248 }
249
250 public void testGetAttachmentsHasAttachments() throws MessagingException, IOException
251 {
252 MailUtils.Attachment[] attachments = MailUtils.getAttachments(makeMessageFromResourceFile("/testmails/multipartmixed.txt"));
253 assertEquals("has attachments", 2, attachments.length);
254 assertEquals("Attachment 1", new String(attachments[0].getContents()));
255 assertEquals("image/jpeg", attachments[0].getContentType().substring(0, 10));
256 assertEquals("bugger.jpg", attachments[0].getFilename());
257 assertEquals("html<br>attachment", new String(attachments[1].getContents()));
258 assertEquals("text/html", attachments[1].getContentType().substring(0, 9));
259 assertEquals("foo.html", attachments[1].getFilename());
260 }
261
262 private Message makeMessageFromResourceFile(String resourceName) throws MessagingException
263 {
264 return new MimeMessage(null, this.getClass().getResourceAsStream(resourceName));
265 }
266
267
268 public void testGetContentTypeFromHeaderValue()
269 {
270 final String input = "text/plain";
271 final String actual = MailUtils.getContentType(input);
272 final String expected = input;
273
274 assertEquals(expected, actual);
275 }
276
277 public void testGetContentTypeFromHeaderValueWithParameter()
278 {
279 final String input = "text/plain; something=somevalue";
280 final String actual = MailUtils.getContentType(input);
281 final String expected = "text/plain";
282
283 assertEquals(expected, actual);
284 }
285
286
287
288
289
290 final static boolean PLAIN_TEXT_YES = true;
291 final static boolean PLAIN_TEXT_NO = false;
292
293 final static boolean HTML_YES = true;
294 final static boolean HTML_NO = false;
295
296 final static boolean INLINE_YES = true;
297 final static boolean INLINE_NO = false;
298
299 final static boolean ATTACHMENT_YES = true;
300 final static boolean ATTACHMENT_NO = false;
301
302 public void testIsPlainTextPart() throws Exception
303 {
304 this.checkParts("OutlookPlainText.msg", PLAIN_TEXT_YES, HTML_YES, INLINE_NO, ATTACHMENT_NO);
305 }
306
307 public void testIsHtmlPart() throws Exception
308 {
309 this.checkParts("OutlookHtml.msg", PLAIN_TEXT_YES, HTML_YES, INLINE_NO, ATTACHMENT_NO);
310 }
311
312 public void testIsImageAttachedPart() throws Exception
313 {
314 this.checkParts("OutlookHtmlImageAttached.msg", PLAIN_TEXT_YES, HTML_YES, INLINE_NO, ATTACHMENT_YES);
315 }
316
317 public void testIsInlineImagePart() throws Exception
318 {
319 this.checkParts("OutlookHtmlInlineImage.msg", PLAIN_TEXT_YES, HTML_YES, INLINE_YES, ATTACHMENT_NO);
320 }
321
322 public void testIsInlineImagePartWherePartHasDisposition() throws Exception
323 {
324 this.checkParts("ThunderbirdHtmlAndPlainTextInlineImage.msg", PLAIN_TEXT_YES, HTML_YES, INLINE_YES, ATTACHMENT_NO);
325 }
326
327 public void testIsAttachmentPart() throws Exception
328 {
329 this.checkParts("OutlookHtmlBinaryAttachment.msg", PLAIN_TEXT_YES, HTML_YES, INLINE_NO, ATTACHMENT_YES);
330 }
331
332 public void testIsRelatedPart() throws Exception
333 {
334 assertFalse(MailUtils.isPartRelated(makeMessageFromResourceFile("/testmails/multipartmixedtextandhtml.txt")));
335 assertTrue(MailUtils.isPartRelated(makeMessageFromResourceFile("/testmails/multipartrelated.txt")));
336 }
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351 void checkParts(final String filename, final boolean plainTextExpected, final boolean htmlExpected, final boolean inlineExpected, final boolean attachmentExpected)
352 throws Exception
353 {
354 assertNotNull(filename);
355
356 final Part[] parts = this.createPartsFromMessage(filename);
357 assertNotNull("parts", parts);
358 assertTrue("Expected atleast 1 part but got " + parts.length + " part(s)", parts.length > 0);
359
360
361 boolean plainTextFound = false;
362 boolean htmlFound = false;
363 boolean inlineFound = false;
364 boolean attachmentFound = false;
365
366 for (int i = 0; i < parts.length; i++)
367 {
368 final Part part = parts[i];
369
370 if (MailUtils.isPartPlainText(part))
371 {
372 assertTrue("PlainText part found when none was expected", plainTextExpected);
373 plainTextFound = true;
374 continue;
375 }
376
377 if (MailUtils.isPartHtml(part))
378 {
379 assertTrue("Html part found when none was expected", htmlExpected);
380 htmlFound = true;
381 }
382
383 if (MailUtils.isPartInline(part))
384 {
385 assertTrue("Inline part found when none was expected", inlineExpected);
386 inlineFound = true;
387
388 final boolean reportedEmpty = MailUtils.isContentEmpty(part);
389 assertFalse("All inline parts in the prepared msg files are never empty...", reportedEmpty);
390 }
391
392 if (MailUtils.isPartAttachment(part))
393 {
394 assertTrue("Attachment part found when none was expected", attachmentExpected);
395 attachmentFound = true;
396
397 final boolean reportedEmpty = MailUtils.isContentEmpty(part);
398 assertFalse("All attachments parts in the prepared msg files are never empty...", reportedEmpty);
399 }
400 }
401
402 assertEquals("Expected to find a plain text part but one was not found", plainTextExpected, plainTextFound);
403 assertEquals("Expected to find a html part but one was not found", htmlExpected, htmlFound);
404 assertEquals("Expected to find a inline part but one was not found", inlineExpected, inlineFound);
405 assertEquals("Expected to find a attachment part but one was not found", attachmentExpected, attachmentFound);
406 }
407
408
409
410
411
412
413
414
415
416 Part[] createPartsFromMessage(final String filename) throws Exception
417 {
418 assertNotNull(filename);
419
420 FileInputStream fis = null;
421
422 try
423 {
424 fis = new FileInputStream("src/test/resources/testmails/" + filename);
425
426
427 final Message message = new MimeMessage(Session.getDefaultInstance(new Properties()), fis);
428 final Part[] parts = this.getAllParts(message);
429 assertNotNull("parts", parts);
430 assertTrue("Expected atleast 1 part but got " + parts.length + " part(s)", parts.length > 0);
431
432 return parts;
433 }
434 finally
435 {
436 IOUtils.closeQuietly(fis);
437 }
438 }
439
440
441
442
443
444
445
446
447
448 Part[] getAllParts(final Message message) throws Exception
449 {
450 final List partsList = new ArrayList();
451 this.processMessageParts(message, partsList);
452 final Part[] parts = (Part[]) partsList.toArray(new Part[0]);
453 return parts;
454 }
455
456 void processMessageParts(final Message message, final Collection parts) throws Exception
457 {
458 final Object content = message.getContent();
459 this.handlePartOrContent(content, parts);
460 }
461
462 void handlePartOrContent(final Object partOrContent, final Collection parts) throws Exception
463 {
464 if (partOrContent instanceof Multipart)
465 {
466 this.processMultipartParts((Multipart) partOrContent, parts);
467 return;
468 }
469
470 if (partOrContent instanceof BodyPart)
471 {
472 final BodyPart bodyPart = (BodyPart) partOrContent;
473 final Object bodyPartContent = bodyPart.getContent();
474
475 if (bodyPartContent instanceof Multipart)
476 {
477 this.handlePartOrContent(bodyPartContent, parts);
478 }
479 else
480 {
481 parts.add(bodyPart);
482 }
483 return;
484 }
485 parts.add((Part) partOrContent);
486 }
487
488 void processMultipartParts(final Multipart multipart, final Collection parts) throws Exception
489 {
490 final int partsCount = multipart.getCount();
491 for (int i = 0; i < partsCount; i++)
492 {
493 final BodyPart bodyPart = multipart.getBodyPart(i);
494 this.handlePartOrContent(bodyPart, parts);
495 }
496 }
497
498
499 static final String X_PKCS7_SIGNATURE = "application/x-pkcs7-signature";
500
501 public void testPositivePartIsSignature() throws Exception
502 {
503 final Message message = createMessageWithAttachment(X_PKCS7_SIGNATURE, "attachment", "filename", "xxxx");
504 final boolean actual = MailUtils.isPartSignaturePKCS7(message);
505 assertTrue("The message being tested is of \"" + X_PKCS7_SIGNATURE + "\" but MailUtils returned false instead of true", actual);
506 }
507
508 public void testNegativePartIsSignature() throws Exception
509 {
510 final Message message = createMessageWithAttachment("text/plain", "", null, "xxxx");
511 final boolean actual = MailUtils.isPartSignaturePKCS7(message);
512 assertFalse("The message being tested is of \"" + X_PKCS7_SIGNATURE + "\" but MailUtils returned true for a text/plain when it should have returned false.", actual);
513 }
514
515 public void testTextPartContentIsEmpty() throws Exception
516 {
517 final Message message = createMessageWithAttachment("text/plain", "", null, "");
518 final boolean actual = MailUtils.isContentEmpty(message);
519 assertTrue("The plaintext message contains a part that should be empty.", actual);
520 }
521
522
523 public void testTextPartContentIsEmptyOnNullContent() throws Exception
524 {
525 final Message message = createMessageWithAttachment("text/plain", "", null, (String) null);
526 final boolean actual = MailUtils.isContentEmpty(message);
527 assertTrue("The plaintext message contains a part that should be empty.", actual);
528 }
529
530 public void testTextPartContentIsEmptyBecauseContainsOnlyWhitespace() throws Exception
531 {
532 final Message message = createMessageWithAttachment("text/plain", "", null, " ");
533 final boolean actual = MailUtils.isContentEmpty(message);
534 assertTrue("The plaintext message contains a part that should be empty.", actual);
535 }
536
537 public void testBinaryAttachmentPartContentIsEmpty() throws Exception
538 {
539 final Message message = createMessageWithAttachment("binary/octet-stream", "file.bin", Part.ATTACHMENT, new byte[0]);
540 final boolean actual = MailUtils.isContentEmpty(message);
541 assertTrue("The attachment part should be empty.", actual);
542 }
543
544 public void testBinaryAttachmentPartContentIsNotEmpty() throws Exception
545 {
546 final Message message = createMessageWithAttachment("binary/octet-stream", "file.bin", Part.ATTACHMENT, " NOT EMPTY!!! ".getBytes());
547 final boolean actual = MailUtils.isContentEmpty(message);
548 assertFalse("The attachment part should be empty.", actual);
549 }
550
551 public void testInlineAttachment() throws Exception
552 {
553 final Message message = createMessageWithAttachment("image/jpeg", "image.jpeg", Part.INLINE, " NOT EMPTY!!! ".getBytes());
554 final boolean actual = MailUtils.isPartInline(message);
555 assertFalse("The attachment part is an inline part.", actual);
556 }
557
558
559
560
561
562
563
564 public void testInlineAttachmentThatIsntBase64Encoded() throws Exception
565 {
566 final Message message = createMessageWithAttachment("image/jpeg", "image.jpeg", null, " NOT EMPTY!!! ".getBytes());
567 message.setHeader("Content-ID", "1234567890");
568 final boolean actual = MailUtils.isPartInline(message);
569 assertFalse("The attachment part isnt not base64 encoded.", actual);
570 }
571
572
573
574
575
576
577
578
579
580
581
582 Message createMessageWithAttachment(final String mimeType, final String disposition, final String filename, final String content)
583 throws Exception
584 {
585 assertNotNull("mimeType", mimeType);
586 assertTrue("mimeType must not be empty", mimeType.length() > 0);
587
588 final Message message = new MimeMessage(Session.getDefaultInstance(new Properties()));
589 message.setContent(content, mimeType);
590 if (null != filename && filename.length() > 0)
591 {
592 message.setFileName(filename);
593 }
594 message.setHeader("Content-Type", mimeType);
595 message.setDisposition(disposition);
596 return message;
597 }
598
599 Message createMessageWithAttachment(final String mimeType, final String disposition, final String filename, final byte[] content)
600 throws Exception
601 {
602 assertNotNull("mimeType", mimeType);
603 assertTrue("mimeType must not be empty", mimeType.length() > 0);
604
605 final Message message = new MimeMessage(Session.getDefaultInstance(new Properties()));
606 message.setContent(new ByteArrayInputStream(content), mimeType);
607 if (null != filename && filename.length() > 0)
608 {
609 message.setFileName(filename);
610 }
611 message.setHeader("Content-Type", mimeType);
612 message.setDisposition(disposition);
613 return message;
614 }
615 }