View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.mail;
18  
19  import static org.junit.Assert.*;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.net.URL;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.activation.DataSource;
29  import javax.activation.FileDataSource;
30  import javax.mail.internet.MimeMessage;
31  
32  import org.apache.commons.mail.mocks.MockHtmlEmailConcrete;
33  import org.apache.commons.mail.settings.EmailConfiguration;
34  import org.apache.commons.mail.util.MimeMessageParser;
35  import org.junit.Before;
36  import org.junit.Test;
37  import org.junit.runner.RunWith;
38  import org.powermock.core.classloader.annotations.PrepareForTest;
39  import org.powermock.modules.junit4.PowerMockRunner;
40  
41  /**
42   * JUnit test case for HtmlEmail Class.
43   *
44   * @since 1.0
45   * @version $Id: HtmlEmailTest.java 1532531 2013-10-15 21:13:11Z tn $
46   */
47  @RunWith(PowerMockRunner.class)
48  @PrepareForTest( { MockHtmlEmailConcrete.class })
49  public class HtmlEmailTest extends AbstractEmailTest
50  {
51      private MockHtmlEmailConcrete email;
52  
53      @Before
54      public void setUpHtmlEmailTest()
55      {
56          // reusable objects to be used across multiple tests
57          this.email = new MockHtmlEmailConcrete();
58      }
59  
60      @Test
61      public void testGetSetTextMsg() throws EmailException
62      {
63          // ====================================================================
64          // Test Success
65          // ====================================================================
66          for (String validChar : testCharsValid)
67          {
68              this.email.setTextMsg(validChar);
69              assertEquals(validChar, this.email.getTextMsg());
70          }
71  
72          // ====================================================================
73          // Test Exception
74          // ====================================================================
75          for (String invalidChar : this.testCharsNotValid)
76          {
77              try
78              {
79                  this.email.setTextMsg(invalidChar);
80                  fail("Should have thrown an exception");
81              }
82              catch (EmailException e)
83              {
84                  assertTrue(true);
85              }
86          }
87  
88      }
89  
90      @Test
91      public void testGetSetHtmlMsg() throws EmailException
92      {
93          // ====================================================================
94          // Test Success
95          // ====================================================================
96          for (String validChar : testCharsValid)
97          {
98              this.email.setHtmlMsg(validChar);
99              assertEquals(validChar, this.email.getHtmlMsg());
100         }
101 
102         // ====================================================================
103         // Test Exception
104         // ====================================================================
105         for (String invalidChar : this.testCharsNotValid)
106         {
107             try
108             {
109                 this.email.setHtmlMsg(invalidChar);
110                 fail("Should have thrown an exception");
111             }
112             catch (EmailException e)
113             {
114                 assertTrue(true);
115             }
116         }
117 
118     }
119 
120     @Test
121     public void testGetSetMsg() throws EmailException
122     {
123         // ====================================================================
124         // Test Success
125         // ====================================================================
126         for (String validChar : testCharsValid)
127         {
128             this.email.setMsg(validChar);
129             assertEquals(validChar, this.email.getTextMsg());
130 
131             assertTrue(
132                     this.email.getHtmlMsg().contains(validChar));
133         }
134 
135         // ====================================================================
136         // Test Exception
137         // ====================================================================
138         for (String invalidChar : this.testCharsNotValid)
139         {
140             try
141             {
142                 this.email.setMsg(invalidChar);
143                 fail("Should have thrown an exception");
144             }
145             catch (EmailException e)
146             {
147                 assertTrue(true);
148             }
149         }
150 
151     }
152 
153     @Test
154     public void testEmbedUrl() throws Exception
155     {
156         // ====================================================================
157         // Test Success
158         // ====================================================================
159 
160         String strEmbed =
161             this.email.embed(new URL(this.strTestURL), "Test name");
162         assertNotNull(strEmbed);
163         assertEquals(HtmlEmail.CID_LENGTH, strEmbed.length());
164 
165         // if we embed the same name again, do we get the same content ID
166         // back?
167         String testCid =
168             this.email.embed(new URL(this.strTestURL), "Test name");
169         assertEquals(strEmbed, testCid);
170 
171         // if we embed the same URL under a different name, is the content ID
172         // unique?
173         String newCid =
174             this.email.embed(new URL(this.strTestURL), "Test name 2");
175         assertFalse(strEmbed.equals(newCid));
176 
177         // ====================================================================
178         // Test Exceptions
179         // ====================================================================
180 
181         // Does an invalid URL throw an exception?
182         try
183         {
184             this.email.embed(createInvalidURL(), "Bad URL");
185             fail("Should have thrown an exception");
186         }
187         catch (EmailException e)
188         {
189             // expected
190         }
191 
192         // if we try to embed a different URL under a previously used name,
193         // does it complain?
194         try
195         {
196             this.email.embed(new URL("http://www.google.com"), "Test name");
197             fail("shouldn't be able to use an existing name with a different URL!");
198         }
199         catch (EmailException e)
200         {
201             // expected
202         }
203     }
204 
205     @Test
206     public void testEmbedFile() throws Exception
207     {
208         // ====================================================================
209         // Test Success
210         // ====================================================================
211 
212         File file = File.createTempFile("testEmbedFile", "txt");
213         file.deleteOnExit();
214         String strEmbed = this.email.embed(file);
215         assertNotNull(strEmbed);
216         assertEquals("generated CID has wrong length",
217                 HtmlEmail.CID_LENGTH, strEmbed.length());
218 
219         // if we embed the same file again, do we get the same content ID
220         // back?
221         String testCid =
222             this.email.embed(file);
223         assertEquals("didn't get same CID after embedding same file twice",
224                 strEmbed, testCid);
225 
226         // if we embed a new file, is the content ID unique?
227         File otherFile = File.createTempFile("testEmbedFile2", "txt");
228         otherFile.deleteOnExit();
229         String newCid = this.email.embed(otherFile);
230         assertFalse("didn't get unique CID from embedding new file",
231                 strEmbed.equals(newCid));
232     }
233 
234     @Test
235     public void testEmbedUrlAndFile() throws Exception
236     {
237         File tmpFile = File.createTempFile("testfile", "txt");
238         tmpFile.deleteOnExit();
239         String fileCid = this.email.embed(tmpFile);
240 
241         URL fileUrl = tmpFile.toURI().toURL();
242         String urlCid = this.email.embed(fileUrl, "urlName");
243 
244         assertFalse("file and URL cids should be different even for same resource",
245                 fileCid.equals(urlCid));
246     }
247 
248     @Test
249     public void testEmbedDataSource() throws Exception
250     {
251         File tmpFile = File.createTempFile("testEmbedDataSource", "txt");
252         tmpFile.deleteOnExit();
253         FileDataSource dataSource = new FileDataSource(tmpFile);
254 
255         // does embedding a datasource without a name fail?
256         try
257         {
258             this.email.embed(dataSource, "");
259             fail("embedding with an empty string for a name should fail");
260         }
261         catch (EmailException e)
262         {
263             // expected
264         }
265 
266         // properly embed the datasource
267         String cid = this.email.embed(dataSource, "testname");
268 
269         // does embedding the same datasource under the same name return
270         // the original cid?
271         String sameCid = this.email.embed(dataSource, "testname");
272         assertEquals("didn't get same CID for embedding same datasource twice",
273                 cid, sameCid);
274 
275         // does embedding another datasource under the same name fail?
276         File anotherFile = File.createTempFile("testEmbedDataSource2", "txt");
277         anotherFile.deleteOnExit();
278         FileDataSource anotherDS = new FileDataSource(anotherFile);
279         try
280         {
281             this.email.embed(anotherDS, "testname");
282         }
283         catch (EmailException e)
284         {
285             // expected
286         }
287     }
288 
289     /**
290      * @throws EmailException when bad addresses and attachments are used
291      * @throws IOException if creating a temp file, URL or sending fails
292      */
293     @Test
294     public void testSend() throws EmailException, IOException
295     {
296         EmailAttachment attachment = new EmailAttachment();
297         File testFile = null;
298 
299         /** File to used to test file attachments (Must be valid) */
300         testFile = File.createTempFile("commons-email-testfile", ".txt");
301         testFile.deleteOnExit();
302 
303         // ====================================================================
304         // Test Success
305         // ====================================================================
306         this.getMailServer();
307 
308         String strSubject = "Test HTML Send #1 Subject (w charset)";
309 
310         this.email = new MockHtmlEmailConcrete();
311         this.email.setHostName(this.strTestMailServer);
312         this.email.setSmtpPort(this.getMailServerPort());
313         this.email.setFrom(this.strTestMailFrom);
314         this.email.addTo(this.strTestMailTo);
315 
316         /** File to used to test file attachmetns (Must be valid) */
317         attachment.setName("Test Attachment");
318         attachment.setDescription("Test Attachment Desc");
319         attachment.setPath(testFile.getAbsolutePath());
320         this.email.attach(attachment);
321 
322         this.email.setAuthentication(this.strTestUser, this.strTestPasswd);
323 
324         this.email.setCharset(EmailConstants.ISO_8859_1);
325         this.email.setSubject(strSubject);
326 
327         URL url = new URL(EmailConfiguration.TEST_URL);
328         String cid = this.email.embed(url, "Apache Logo");
329 
330         String strHtmlMsg =
331             "<html>The Apache logo - <img src=\"cid:" + cid + "\"><html>";
332 
333         this.email.setHtmlMsg(strHtmlMsg);
334         this.email.setTextMsg(
335             "Your email client does not support HTML emails");
336 
337         this.email.send();
338         this.fakeMailServer.stop();
339         // validate txt message
340         validateSend(
341             this.fakeMailServer,
342             strSubject,
343             this.email.getTextMsg(),
344             this.email.getFromAddress(),
345             this.email.getToAddresses(),
346             this.email.getCcAddresses(),
347             this.email.getBccAddresses(),
348             true);
349 
350         // validate html message
351         validateSend(
352             this.fakeMailServer,
353             strSubject,
354             this.email.getHtmlMsg(),
355             this.email.getFromAddress(),
356             this.email.getToAddresses(),
357             this.email.getCcAddresses(),
358             this.email.getBccAddresses(),
359             false);
360 
361         // validate attachment
362         validateSend(
363             this.fakeMailServer,
364             strSubject,
365             attachment.getName(),
366             this.email.getFromAddress(),
367             this.email.getToAddresses(),
368             this.email.getCcAddresses(),
369             this.email.getBccAddresses(),
370             false);
371 
372         this.getMailServer();
373 
374         this.email = new MockHtmlEmailConcrete();
375         this.email.setHostName(this.strTestMailServer);
376         this.email.setSmtpPort(this.getMailServerPort());
377         this.email.setFrom(this.strTestMailFrom);
378         this.email.addTo(this.strTestMailTo);
379 
380         if (this.strTestUser != null && this.strTestPasswd != null)
381         {
382             this.email.setAuthentication(
383                 this.strTestUser,
384                 this.strTestPasswd);
385         }
386 
387         strSubject = "Test HTML Send #1 Subject (wo charset)";
388         this.email.setSubject(strSubject);
389         this.email.setTextMsg("Test message");
390 
391         this.email.send();
392         this.fakeMailServer.stop();
393         // validate txt message
394         validateSend(
395             this.fakeMailServer,
396             strSubject,
397             this.email.getTextMsg(),
398             this.email.getFromAddress(),
399             this.email.getToAddresses(),
400             this.email.getCcAddresses(),
401             this.email.getBccAddresses(),
402             true);
403     }
404 
405     @Test
406     public void testSend2() throws Exception
407     {
408         // ====================================================================
409         // Test Success
410         // ====================================================================
411 
412         this.getMailServer();
413 
414         this.email = new MockHtmlEmailConcrete();
415         this.email.setHostName(this.strTestMailServer);
416         this.email.setSmtpPort(this.getMailServerPort());
417         this.email.setFrom(this.strTestMailFrom);
418         this.email.addTo(this.strTestMailTo);
419 
420         if (this.strTestUser != null && this.strTestPasswd != null)
421         {
422             this.email.setAuthentication(
423                 this.strTestUser,
424                 this.strTestPasswd);
425         }
426 
427         String strSubject = "Test HTML Send #2 Subject (wo charset)";
428         this.email.setSubject(strSubject);
429         this.email.setMsg("Test txt msg");
430 
431         this.email.send();
432         this.fakeMailServer.stop();
433         // validate txt message
434         validateSend(
435             this.fakeMailServer,
436             strSubject,
437             this.email.getTextMsg(),
438             this.email.getFromAddress(),
439             this.email.getToAddresses(),
440             this.email.getCcAddresses(),
441             this.email.getBccAddresses(),
442             true);
443 
444         // validate html message
445         validateSend(
446             this.fakeMailServer,
447             strSubject,
448             this.email.getHtmlMsg(),
449             this.email.getFromAddress(),
450             this.email.getToAddresses(),
451             this.email.getCcAddresses(),
452             this.email.getBccAddresses(),
453             false);
454 
455         this.getMailServer();
456 
457         this.email = new MockHtmlEmailConcrete();
458         this.email.setHostName(this.strTestMailServer);
459         this.email.setFrom(this.strTestMailFrom);
460         this.email.setSmtpPort(this.getMailServerPort());
461         this.email.addTo(this.strTestMailTo);
462 
463         if (this.strTestUser != null && this.strTestPasswd != null)
464         {
465             this.email.setAuthentication(
466                 this.strTestUser,
467                 this.strTestPasswd);
468         }
469 
470         strSubject = "Test HTML Send #2 Subject (w charset)";
471         this.email.setCharset(EmailConstants.ISO_8859_1);
472         this.email.setSubject(strSubject);
473         this.email.setMsg("Test txt msg");
474 
475         this.email.send();
476         this.fakeMailServer.stop();
477         // validate txt message
478         validateSend(
479             this.fakeMailServer,
480             strSubject,
481             this.email.getTextMsg(),
482             this.email.getFromAddress(),
483             this.email.getToAddresses(),
484             this.email.getCcAddresses(),
485             this.email.getBccAddresses(),
486             true);
487 
488         // validate html message
489         validateSend(
490             this.fakeMailServer,
491             strSubject,
492             this.email.getHtmlMsg(),
493             this.email.getFromAddress(),
494             this.email.getToAddresses(),
495             this.email.getCcAddresses(),
496             this.email.getBccAddresses(),
497             false);
498 
499     }
500 
501     @Test
502     public void testSendWithDefaultCharset() throws Exception
503     {
504         // ====================================================================
505         // Test Success
506         // ====================================================================
507 
508         System.setProperty(EmailConstants.MAIL_MIME_CHARSET, "iso-8859-15");
509 
510         this.getMailServer();
511 
512         this.email = new MockHtmlEmailConcrete();
513         this.email.setHostName(this.strTestMailServer);
514         this.email.setSmtpPort(this.getMailServerPort());
515         this.email.setFrom(this.strTestMailFrom);
516         this.email.addTo(this.strTestMailTo);
517 
518         if (this.strTestUser != null && this.strTestPasswd != null)
519         {
520             this.email.setAuthentication(
521                 this.strTestUser,
522                 this.strTestPasswd);
523         }
524 
525         String strSubject = "Test HTML Send Subject (w default charset)";
526         this.email.setSubject(strSubject);
527         this.email.setMsg("Test txt msg รค"); // add non-ascii character, otherwise us-ascii will be used
528 
529         this.email.send();
530         this.fakeMailServer.stop();
531         // validate charset
532         validateSend(
533             this.fakeMailServer,
534             strSubject,
535             "charset=iso-8859-15",
536             this.email.getFromAddress(),
537             this.email.getToAddresses(),
538             this.email.getCcAddresses(),
539             this.email.getBccAddresses(),
540             true);
541 
542         System.clearProperty(EmailConstants.MAIL_MIME_CHARSET);
543 
544     }
545 
546     /**
547      * Create a HTML email containing an URL pointing to a ZIP file
548      * to be downloaded. According to EMAIL-93 the resulting URL
549      * "http://paradisedelivery.homeip.net/delivery/?file=TZC268X93337..zip"
550      * contains TWO dots instead of one dot which breaks the link.
551      */
552     @Test
553     public void testAddZipUrl() throws Exception
554     {
555         String htmlMsg =
556                 "Please click on the following link: <br><br>" +
557                 "<a href=\"http://paradisedelivery.homeip.net/delivery/?file=3DTZC268X93337.zip\">" +
558                 "http://paradisedelivery.homeip.net/delivery/?file=3DTZC268X93337.zip" +
559                 "</a><br><br>Customer satisfaction is very important for us.";
560 
561         this.getMailServer();
562 
563         this.email = new MockHtmlEmailConcrete();
564         this.email.setHostName(this.strTestMailServer);
565         this.email.setSmtpPort(this.getMailServerPort());
566         this.email.setFrom(this.strTestMailFrom);
567         this.email.addTo(this.strTestMailTo);
568         this.email.setCharset(EmailConstants.ISO_8859_1);
569 
570         if (this.strTestUser != null && this.strTestPasswd != null)
571         {
572             this.email.setAuthentication(
573                 this.strTestUser,
574                 this.strTestPasswd);
575         }
576 
577         String strSubject = "A dot (\".\") is appended to some ULRs of a HTML mail.";
578         this.email.setSubject(strSubject);
579         this.email.setHtmlMsg(htmlMsg);
580 
581         this.email.send();
582         this.fakeMailServer.stop();
583 
584         // validate html message
585         validateSend(
586             this.fakeMailServer,
587             strSubject,
588             this.email.getHtmlMsg(),
589             this.email.getFromAddress(),
590             this.email.getToAddresses(),
591             this.email.getCcAddresses(),
592             this.email.getBccAddresses(),
593             false);
594         
595         // make sure that no double dots show up
596         assertTrue(this.email.getHtmlMsg().contains("3DTZC268X93337.zip"));
597         assertFalse(this.email.getHtmlMsg().contains("3DTZC268X93337..zip"));
598     }
599 
600     /**
601      * According to EMAIL-95 calling buildMimeMessage() before calling send()
602      * causes duplicate mime parts - now we throw an exception to catch the
603      * problem
604      */
605     @Test
606     public void testCallingBuildMimeMessageBeforeSent() throws Exception {
607 
608         String htmlMsg = "<b>Hello World</b>";
609 
610         this.email = new MockHtmlEmailConcrete();
611         this.email.setHostName(this.strTestMailServer);
612         this.email.setSmtpPort(this.getMailServerPort());
613         this.email.setFrom(this.strTestMailFrom);
614         this.email.addTo(this.strTestMailTo);
615         this.email.setCharset(EmailConstants.ISO_8859_1);
616 
617         if (this.strTestUser != null && this.strTestPasswd != null)
618         {
619             this.email.setAuthentication(
620                 this.strTestUser,
621                 this.strTestPasswd);
622         }
623 
624         String strSubject = "testCallingBuildMimeMessageBeforeSent";
625         this.email.setSubject(strSubject);
626         this.email.setHtmlMsg(htmlMsg);
627 
628         // this should NOT be called when sending a message
629         this.email.buildMimeMessage();
630 
631         try
632         {
633             this.email.send();
634         }
635         catch(IllegalStateException e)
636         {
637             return;
638         }
639 
640         fail("Expecting an exception when calling buildMimeMessage() before send() ...");
641     }
642 
643     /**
644      * EMAIL-73 - check that providing a plain text content using setMsg()
645      * creates a plain content and HTML content using <pre> tags.
646      */
647     @Test
648     public void testSendWithPlainTextButNoHtmlContent() throws EmailException, IOException
649     {
650         this.getMailServer();
651 
652         String strSubject = "testSendWithPlainTextButNoHtmlContent";
653 
654         this.email = new MockHtmlEmailConcrete();
655         this.email.setHostName(this.strTestMailServer);
656         this.email.setSmtpPort(this.getMailServerPort());
657         this.email.setFrom(this.strTestMailFrom);
658         this.email.addTo(this.strTestMailTo);
659         this.email.setAuthentication(this.strTestUser, this.strTestPasswd);
660         this.email.setCharset(EmailConstants.ISO_8859_1);
661         this.email.setSubject(strSubject);
662         this.email.setMsg("This is a plain text content : <b><&npsb;></html></b>");
663 
664         this.email.send();
665 
666         this.fakeMailServer.stop();
667 
668         // validate text message
669         validateSend(
670             this.fakeMailServer,
671             strSubject,
672             this.email.getTextMsg(),
673             this.email.getFromAddress(),
674             this.email.getToAddresses(),
675             this.email.getCcAddresses(),
676             this.email.getBccAddresses(),
677             true);
678     }
679 
680     /**
681      * Test that the specified Content-ID is used when embedding a File
682      * object in an HtmlEmail.
683      *
684      * Rolled back the changes since they broke real emails therefore
685      * the test is currently disabled.
686      *
687      * see https://issues.apache.org/jira/browse/EMAIL-101
688      */
689     @Test
690     public void testEmbedFileWithCID() throws Exception
691     {
692          // ====================================================================
693          // Test Success
694          // ====================================================================
695 
696          File file = File.createTempFile("testEmbedFile", "txt");
697          file.deleteOnExit();
698 
699          String testCid = "Test CID";
700          String encodedCid = EmailUtils.encodeUrl(testCid);
701 
702          // if we embed a new file, do we get the content ID we specified back?
703          String strEmbed = this.email.embed(file, testCid);
704          assertNotNull(strEmbed);
705          assertEquals("didn't get same CID when embedding with a specified CID", encodedCid, strEmbed);
706 
707          // if we embed the same file again, do we get the same content ID
708          // back?
709          String returnedCid = this.email.embed(file);
710          assertEquals("didn't get same CID after embedding same file twice", encodedCid, returnedCid);
711     }
712 
713     @Test
714     public void testHtmlMailMimeLayout() throws Exception
715     {
716         assertCorrectContentType("contentTypeTest.gif", "image/gif");
717         assertCorrectContentType("contentTypeTest.jpg", "image/jpeg");
718         assertCorrectContentType("contentTypeTest.png", "image/png");
719     }
720 
721     private void assertCorrectContentType(String picture, String contentType) throws Exception {
722         HtmlEmail htmlEmail = createDefaultHtmlEmail();
723         String cid = htmlEmail.embed(new File("./src/test/resources/images/" + picture), "Apache Logo");
724         String htmlMsg = "<html><img src=\"cid:" + cid + "\"><html>";
725         htmlEmail.setHtmlMsg(htmlMsg);
726         htmlEmail.buildMimeMessage();
727 
728         MimeMessage mm = htmlEmail.getMimeMessage();
729         mm.saveChanges();
730         MimeMessageParser mmp = new MimeMessageParser(mm);
731         mmp.parse();
732 
733         List<?> attachments = mmp.getAttachmentList();
734         assertEquals("Attachment size", 1, attachments.size());
735 
736         DataSource ds = (DataSource) attachments.get(0);
737         assertEquals("Content type", contentType, ds.getContentType());
738     }
739 
740     private HtmlEmail createDefaultHtmlEmail() throws EmailException {
741         HtmlEmail htmlEmail = new HtmlEmail();
742         htmlEmail.setHostName(this.strTestMailServer);
743         htmlEmail.setSmtpPort(this.getMailServerPort());
744         htmlEmail.setFrom("a@b.com");
745         htmlEmail.addTo("c@d.com");
746         return htmlEmail;
747     }
748 }