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.mail2.javax;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotEquals;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.io.File;
27  import java.io.IOException;
28  import java.net.URL;
29  import java.util.List;
30  
31  import javax.activation.DataSource;
32  import javax.activation.FileDataSource;
33  import javax.mail.internet.MimeMessage;
34  
35  import org.apache.commons.mail2.core.EmailConstants;
36  import org.apache.commons.mail2.core.EmailException;
37  import org.apache.commons.mail2.core.EmailUtils;
38  import org.apache.commons.mail2.javax.mocks.MockHtmlEmailConcrete;
39  import org.apache.commons.mail2.javax.settings.EmailConfiguration;
40  import org.apache.commons.mail2.javax.util.MimeMessageParser;
41  import org.junit.jupiter.api.BeforeEach;
42  import org.junit.jupiter.api.Disabled;
43  import org.junit.jupiter.api.Test;
44  
45  /**
46   * JUnit test case for HtmlEmail Class.
47   */
48  public class HtmlEmailTest extends AbstractEmailTest {
49  
50      private MockHtmlEmailConcrete email;
51  
52      private void assertCorrectContentType(final String picture, final String contentType) throws Exception {
53          final HtmlEmail htmlEmail = createDefaultHtmlEmail();
54          final String cid = htmlEmail.embed(new File("./src/test/resources/images/" + picture), "Apache Logo");
55          final String htmlMsg = "<html><img src=\"cid:" + cid + "\"><html>";
56          htmlEmail.setHtmlMsg(htmlMsg);
57          htmlEmail.buildMimeMessage();
58  
59          final MimeMessage mm = htmlEmail.getMimeMessage();
60          mm.saveChanges();
61          final MimeMessageParser mmp = new MimeMessageParser(mm);
62          mmp.parse();
63  
64          final List<?> attachments = mmp.getAttachmentList();
65          assertEquals(1, attachments.size(), "Attachment size");
66  
67          final DataSource ds = (DataSource) attachments.get(0);
68          assertEquals(contentType, ds.getContentType(), "Content type");
69      }
70  
71      private HtmlEmail createDefaultHtmlEmail() throws EmailException {
72          final HtmlEmail htmlEmail = new HtmlEmail();
73          htmlEmail.setHostName(strTestMailServer);
74          htmlEmail.setSmtpPort(getMailServerPort());
75          htmlEmail.setFrom("a@b.com");
76          htmlEmail.addTo("c@d.com");
77          return htmlEmail;
78      }
79  
80      @BeforeEach
81      public void setUpHtmlEmailTest() {
82          // reusable objects to be used across multiple tests
83          email = new MockHtmlEmailConcrete();
84      }
85  
86      /**
87       * Create a HTML email containing an URL pointing to a ZIP file to be downloaded. According to EMAIL-93 the resulting URL
88       * "https://paradisedelivery.homeip.net/delivery/?file=TZC268X93337..zip" contains TWO dots instead of one dot which breaks the link.
89       */
90      @Test
91      public void testAddZipUrl() throws Exception {
92          final String htmlMsg = "Please click on the following link: <br><br>"
93                  + "<a href=\"http://paradisedelivery.homeip.net/delivery/?file=3DTZC268X93337.zip\">"
94                  + "http://paradisedelivery.homeip.net/delivery/?file=3DTZC268X93337.zip" + "</a><br><br>Customer satisfaction is very important for us.";
95  
96          getMailServer();
97  
98          email = new MockHtmlEmailConcrete();
99          email.setHostName(strTestMailServer);
100         email.setSmtpPort(getMailServerPort());
101         email.setFrom(strTestMailFrom);
102         email.addTo(strTestMailTo);
103         email.setCharset(EmailConstants.ISO_8859_1);
104 
105         if (strTestUser != null && strTestPasswd != null) {
106             email.setAuthentication(strTestUser, strTestPasswd);
107         }
108 
109         final String strSubject = "A dot (\".\") is appended to some ULRs of a HTML mail.";
110         email.setSubject(strSubject);
111         email.setHtmlMsg(htmlMsg);
112 
113         email.send();
114         fakeMailServer.stop();
115 
116         // validate html message
117         validateSend(fakeMailServer, strSubject, email.getHtml(), email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
118                 email.getBccAddresses(), false);
119 
120         // make sure that no double dots show up
121         assertTrue(email.getHtml().contains("3DTZC268X93337.zip"));
122         assertFalse(email.getHtml().contains("3DTZC268X93337..zip"));
123     }
124 
125     /**
126      * According to EMAIL-95 calling buildMimeMessage() before calling send() causes duplicate mime parts - now we throw an exception to catch the problem
127      */
128     @Test
129     public void testCallingBuildMimeMessageBeforeSent() throws Exception {
130 
131         final String htmlMsg = "<b>Hello World</b>";
132 
133         email = new MockHtmlEmailConcrete();
134         email.setHostName(strTestMailServer);
135         email.setSmtpPort(getMailServerPort());
136         email.setFrom(strTestMailFrom);
137         email.addTo(strTestMailTo);
138         email.setCharset(EmailConstants.ISO_8859_1);
139 
140         if (strTestUser != null && strTestPasswd != null) {
141             email.setAuthentication(strTestUser, strTestPasswd);
142         }
143 
144         final String strSubject = "testCallingBuildMimeMessageBeforeSent";
145         email.setSubject(strSubject);
146         email.setHtmlMsg(htmlMsg);
147 
148         // this should NOT be called when sending a message
149         email.buildMimeMessage();
150 
151         assertThrows(IllegalStateException.class, email::send);
152     }
153 
154     @Test
155     public void testEmbedDataSource() throws Exception {
156         final File tmpFile = File.createTempFile("testEmbedDataSource", "txt");
157         tmpFile.deleteOnExit();
158         final FileDataSource dataSource = new FileDataSource(tmpFile);
159 
160         // does embedding a datasource without a name fail?
161         assertThrows(EmailException.class, () -> email.embed(dataSource, ""));
162 
163         // properly embed the datasource
164         final String cid = email.embed(dataSource, "testname");
165 
166         // does embedding the same datasource under the same name return
167         // the original cid?
168         final String sameCid = email.embed(dataSource, "testname");
169 
170         assertEquals(cid, sameCid, "didn't get same CID for embedding same datasource twice");
171 
172         // does embedding another datasource under the same name fail?
173         final File anotherFile = File.createTempFile("testEmbedDataSource2", "txt");
174         anotherFile.deleteOnExit();
175         final FileDataSource anotherDS = new FileDataSource(anotherFile);
176         assertThrows(EmailException.class, () -> email.embed(anotherDS, "testname"));
177     }
178 
179     @Test
180     public void testEmbedFile() throws Exception {
181         // Test Success
182 
183         final File file = File.createTempFile("testEmbedFile", "txt");
184         file.deleteOnExit();
185         final String strEmbed = email.embed(file);
186         assertNotNull(strEmbed);
187 
188         assertEquals(HtmlEmail.CID_LENGTH, strEmbed.length(), "generated CID has wrong length");
189 
190         // if we embed the same file again, do we get the same content ID
191         // back?
192         final String testCid = email.embed(file);
193         assertEquals(strEmbed, testCid, "didn't get same CID after embedding same file twice");
194 
195         // if we embed a new file, is the content ID unique?
196         final File otherFile = File.createTempFile("testEmbedFile2", "txt");
197         otherFile.deleteOnExit();
198         final String newCid = email.embed(otherFile);
199 
200         assertNotEquals(strEmbed, newCid, "didn't get unique CID from embedding new file");
201     }
202 
203     /**
204      * Test that the specified Content-ID is used when embedding a File object in an HtmlEmail.
205      *
206      * Rolled back the changes since they broke real emails therefore the test is currently disabled.
207      *
208      * see https://issues.apache.org/jira/browse/EMAIL-101
209      */
210     @Test
211     public void testEmbedFileWithCID() throws Exception {
212         // Test Success
213 
214         final File file = File.createTempFile("testEmbedFile", "txt");
215         file.deleteOnExit();
216 
217         final String testCid = "Test CID";
218         final String encodedCid = EmailUtils.encodeUrl(testCid);
219 
220         // if we embed a new file, do we get the content ID we specified back?
221         final String strEmbed = email.embed(file, testCid);
222         assertNotNull(strEmbed);
223         assertEquals(encodedCid, strEmbed, "didn't get same CID when embedding with a specified CID");
224 
225         // if we embed the same file again, do we get the same content ID
226         // back?
227         final String returnedCid = email.embed(file);
228         assertEquals(encodedCid, returnedCid, "didn't get same CID after embedding same file twice");
229     }
230 
231     @Test
232     public void testEmbedUrl() throws Exception {
233         // Test Success
234 
235         final String strEmbed = email.embed(new URL(strTestURL), "Test name");
236         assertNotNull(strEmbed);
237         assertEquals(HtmlEmail.CID_LENGTH, strEmbed.length());
238 
239         // if we embed the same name again, do we get the same content ID
240         // back?
241         final String testCid = email.embed(new URL(strTestURL), "Test name");
242         assertEquals(strEmbed, testCid);
243 
244         // if we embed the same URL under a different name, is the content ID
245         // unique?
246         final String newCid = email.embed(new URL(strTestURL), "Test name 2");
247         assertNotEquals(strEmbed, newCid);
248         // Test Exceptions
249 
250         // Does an invalid URL throw an exception?
251         assertThrows(EmailException.class, () -> email.embed(createInvalidURL(), "Bad URL"));
252 
253         // if we try to embed a different URL under a previously used name,
254         // does it complain?
255         assertThrows(EmailException.class, () -> email.embed(new URL("http://www.google.com"), "Test name"));
256     }
257 
258     @Test
259     public void testEmbedUrlAndFile() throws Exception {
260         final File tmpFile = File.createTempFile("testfile", "txt");
261         tmpFile.deleteOnExit();
262         final String fileCid = email.embed(tmpFile);
263 
264         final URL fileUrl = tmpFile.toURI().toURL();
265         final String urlCid = email.embed(fileUrl, "urlName");
266 
267         assertNotEquals(fileCid, urlCid, "file and URL cids should be different even for same resource");
268     }
269 
270     @Test
271     public void testGetSetHtmlMsg() throws EmailException {
272         // Test Success
273         for (final String validChar : testCharsValid) {
274             email.setHtmlMsg(validChar);
275             assertEquals(validChar, email.getHtml());
276         }
277         // Test Exception
278         for (final String invalidChar : testCharsNotValid) {
279             assertThrows(EmailException.class, () -> email.setHtmlMsg(invalidChar));
280         }
281 
282     }
283 
284     @Test
285     public void testGetSetMsg() throws EmailException {
286         // Test Success
287         for (final String validChar : testCharsValid) {
288             email.setMsg(validChar);
289             assertEquals(validChar, email.getText());
290 
291             assertTrue(email.getHtml().contains(validChar));
292         }
293         // Test Exception
294         for (final String invalidChar : testCharsNotValid) {
295             assertThrows(EmailException.class, () -> email.setMsg(invalidChar));
296         }
297 
298     }
299 
300     @Test
301     public void testGetSetTextMsg() throws EmailException {
302         // Test Success
303         for (final String validChar : testCharsValid) {
304             email.setTextMsg(validChar);
305             assertEquals(validChar, email.getText());
306         }
307         // Test Exception
308         for (final String invalidChar : testCharsNotValid) {
309             assertThrows(EmailException.class, () -> email.setTextMsg(invalidChar));
310         }
311 
312     }
313 
314     @Test
315     public void testHtmlMailMimeLayout() throws Exception {
316         assertCorrectContentType("contentTypeTest.gif", "image/gif");
317         assertCorrectContentType("contentTypeTest.jpg", "image/jpeg");
318         assertCorrectContentType("contentTypeTest.png", "image/png");
319     }
320 
321     /**
322      * @throws EmailException when bad addresses and attachments are used
323      * @throws IOException    if creating a temp file, URL or sending fails
324      */
325     @Test
326     public void testSend() throws EmailException, IOException {
327         final EmailAttachment attachment = new EmailAttachment();
328 
329         /* File to used to test file attachments (Must be valid) */
330         final File testFile = File.createTempFile("commons-email-testfile", ".txt");
331         testFile.deleteOnExit();
332         // Test Success
333         getMailServer();
334 
335         String strSubject = "Test HTML Send #1 Subject (w charset)";
336 
337         email = new MockHtmlEmailConcrete();
338         email.setHostName(strTestMailServer);
339         email.setSmtpPort(getMailServerPort());
340         email.setFrom(strTestMailFrom);
341         email.addTo(strTestMailTo);
342 
343         /* File to used to test file attachments (Must be valid) */
344         attachment.setName("Test Attachment");
345         attachment.setDescription("Test Attachment Desc");
346         attachment.setPath(testFile.getAbsolutePath());
347         email.attach(attachment);
348 
349         // email.setAuthentication(strTestUser, strTestPasswd);
350 
351         email.setCharset(EmailConstants.ISO_8859_1);
352         email.setSubject(strSubject);
353 
354         final URL url = new URL(EmailConfiguration.TEST_URL);
355         final String cid = email.embed(url, "Apache Logo");
356 
357         final String strHtmlMsg = "<html>The Apache logo - <img src=\"cid:" + cid + "\"><html>";
358 
359         email.setHtmlMsg(strHtmlMsg);
360         email.setTextMsg("Your email client does not support HTML emails");
361 
362         email.send();
363         fakeMailServer.stop();
364         // validate txt message
365         validateSend(fakeMailServer, strSubject, email.getText(), email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
366                 email.getBccAddresses(), true);
367 
368         // validate html message
369         validateSend(fakeMailServer, strSubject, email.getHtml(), email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
370                 email.getBccAddresses(), false);
371 
372         // validate attachment
373         validateSend(fakeMailServer, strSubject, attachment.getName(), email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
374                 email.getBccAddresses(), false);
375 
376         getMailServer();
377 
378         email = new MockHtmlEmailConcrete();
379         email.setHostName(strTestMailServer);
380         email.setSmtpPort(getMailServerPort());
381         email.setFrom(strTestMailFrom);
382         email.addTo(strTestMailTo);
383 
384         if (strTestUser != null && strTestPasswd != null) {
385             email.setAuthentication(strTestUser, strTestPasswd);
386         }
387 
388         strSubject = "Test HTML Send #1 Subject (wo charset)";
389         email.setSubject(strSubject);
390         email.setTextMsg("Test message");
391 
392         email.send();
393         fakeMailServer.stop();
394         // validate txt message
395         validateSend(fakeMailServer, strSubject, email.getText(), email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
396                 email.getBccAddresses(), true);
397     }
398 
399     @Test
400     public void testSend2() throws Exception {
401         // Test Success
402 
403         getMailServer();
404 
405         email = new MockHtmlEmailConcrete();
406         email.setHostName(strTestMailServer);
407         email.setSmtpPort(getMailServerPort());
408         email.setFrom(strTestMailFrom);
409         email.addTo(strTestMailTo);
410 
411         if (strTestUser != null && strTestPasswd != null) {
412             email.setAuthentication(strTestUser, strTestPasswd);
413         }
414 
415         String strSubject = "Test HTML Send #2 Subject (wo charset)";
416         email.setSubject(strSubject);
417         email.setMsg("Test txt msg");
418 
419         email.send();
420         fakeMailServer.stop();
421         // validate txt message
422         validateSend(fakeMailServer, strSubject, email.getText(), email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
423                 email.getBccAddresses(), true);
424 
425         // validate html message
426         validateSend(fakeMailServer, strSubject, email.getHtml(), email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
427                 email.getBccAddresses(), false);
428 
429         getMailServer();
430 
431         email = new MockHtmlEmailConcrete();
432         email.setHostName(strTestMailServer);
433         email.setFrom(strTestMailFrom);
434         email.setSmtpPort(getMailServerPort());
435         email.addTo(strTestMailTo);
436 
437         if (strTestUser != null && strTestPasswd != null) {
438             email.setAuthentication(strTestUser, strTestPasswd);
439         }
440 
441         strSubject = "Test HTML Send #2 Subject (w charset)";
442         email.setCharset(EmailConstants.ISO_8859_1);
443         email.setSubject(strSubject);
444         email.setMsg("Test txt msg");
445 
446         email.send();
447         fakeMailServer.stop();
448         // validate txt message
449         validateSend(fakeMailServer, strSubject, email.getText(), email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
450                 email.getBccAddresses(), true);
451 
452         // validate html message
453         validateSend(fakeMailServer, strSubject, email.getHtml(), email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
454                 email.getBccAddresses(), false);
455 
456     }
457 
458     @Test
459     @Disabled
460     public void testSendWithDefaultCharset() throws Exception {
461         // Test is disabled as its result is dependent on the execution order:
462         // the mail.mime.charset property is normally cached by the MimeUtility
463         // class, thus setting it to another value while running the tests
464         // might not have the expected result.
465         // Test Success
466 
467         System.setProperty(EmailConstants.MAIL_MIME_CHARSET, "iso-8859-15");
468 
469         getMailServer();
470 
471         email = new MockHtmlEmailConcrete();
472         email.setHostName(strTestMailServer);
473         email.setSmtpPort(getMailServerPort());
474         email.setFrom(strTestMailFrom);
475         email.addTo(strTestMailTo);
476 
477         if (strTestUser != null && strTestPasswd != null) {
478             email.setAuthentication(strTestUser, strTestPasswd);
479         }
480 
481         final String strSubject = "Test HTML Send Subject (w default charset)";
482         email.setSubject(strSubject);
483         email.setMsg("Test txt msg ä"); // add non-ascii character, otherwise us-ascii will be used
484 
485         email.send();
486         fakeMailServer.stop();
487         // validate charset
488         validateSend(fakeMailServer, strSubject, "charset=iso-8859-15", email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
489                 email.getBccAddresses(), true);
490 
491         System.clearProperty(EmailConstants.MAIL_MIME_CHARSET);
492 
493     }
494 
495     /**
496      * EMAIL-73 - check that providing a plain text content using setMsg() creates a plain content and HTML content.
497      */
498     @Test
499     public void testSendWithPlainTextButNoHtmlContent() throws EmailException, IOException {
500         getMailServer();
501 
502         final String strSubject = "testSendWithPlainTextButNoHtmlContent";
503 
504         email = new MockHtmlEmailConcrete();
505         email.setHostName(strTestMailServer);
506         email.setSmtpPort(getMailServerPort());
507         email.setFrom(strTestMailFrom);
508         email.addTo(strTestMailTo);
509         email.setAuthentication(strTestUser, strTestPasswd);
510         email.setCharset(EmailConstants.ISO_8859_1);
511         email.setSubject(strSubject);
512         email.setMsg("This is a plain text content : <b><&npsb;></html></b>");
513 
514         email.send();
515 
516         fakeMailServer.stop();
517 
518         // validate text message
519         validateSend(fakeMailServer, strSubject, email.getText(), email.getFromAddress(), email.getToAddresses(), email.getCcAddresses(),
520                 email.getBccAddresses(), true);
521     }
522 }