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