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.jakarta;
18  
19  import java.io.ByteArrayOutputStream;
20  import java.io.File;
21  import java.io.InputStream;
22  import java.net.URL;
23  import java.nio.charset.StandardCharsets;
24  import java.nio.file.Paths;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import org.apache.commons.io.FileUtils;
29  import org.apache.commons.io.IOUtils;
30  import org.apache.commons.mail2.core.EmailException;
31  import org.apache.commons.mail2.jakarta.resolver.DataSourceUrlResolver;
32  import org.apache.commons.mail2.jakarta.settings.EmailConfiguration;
33  import org.apache.commons.mail2.jakarta.util.MimeMessageUtils;
34  import org.junit.jupiter.api.BeforeEach;
35  import org.junit.jupiter.api.Test;
36  
37  import jakarta.activation.DataSource;
38  import jakarta.activation.URLDataSource;
39  import jakarta.mail.Session;
40  import jakarta.mail.Transport;
41  import jakarta.mail.internet.MimeMessage;
42  
43  /**
44   * These are regression test sending REAL email to REAL mail servers using REAL recipients.
45   *
46   * The intention is to field-test certain aspects of email using a variety of mail clients since I'm not a mockist (see
47   * https://martinfowler.com/articles/mocksArentStubs.html#ClassicalAndMockistTesting).
48   */
49  public class EmailLiveTest extends AbstractEmailTest {
50      /**
51       * Factory method to create a pre-configured email instance.
52       *
53       * @param clazz the requested implementation class
54       * @return the new instance
55       * @throws Exception creating the Email instance failed
56       */
57      private Email create(final Class<? extends Email> clazz) throws Exception {
58  
59          final Email email = clazz.getConstructor().newInstance();
60  
61          email.setStartTLSEnabled(EmailConfiguration.MAIL_USE_STARTTLS);
62          email.setStartTLSRequired(EmailConfiguration.MAIL_STARTTLS_REQUIRED);
63          email.setSSLOnConnect(EmailConfiguration.MAIL_USE_SSL);
64          email.setSSLCheckServerIdentity(EmailConfiguration.MAIL_SSL_CHECKSERVERIDENTITY);
65          email.setHostName(EmailConfiguration.MAIL_SERVER);
66          email.setSmtpPort(EmailConfiguration.MAIL_SERVER_PORT);
67          email.setBounceAddress(EmailConfiguration.TEST_FROM);
68          email.setDebug(EmailConfiguration.MAIL_DEBUG);
69          email.setCharset(EmailConfiguration.MAIL_CHARSET);
70          email.setFrom(EmailConfiguration.TEST_FROM);
71          email.addTo(EmailConfiguration.TEST_TO);
72          email.setAuthenticator(new DefaultAuthenticator(EmailConfiguration.TEST_USER, EmailConfiguration.TEST_PASSWD));
73          return email;
74      }
75  
76      protected String getFromUrl(final URL url) throws Exception {
77          final URLDataSource dataSource = new URLDataSource(url);
78          final ByteArrayOutputStream baos = new ByteArrayOutputStream();
79          try (InputStream inputStream = dataSource.getInputStream()) {
80              IOUtils.copy(inputStream, baos);
81          }
82          return new String(baos.toByteArray(), StandardCharsets.UTF_8);
83      }
84  
85      protected Email send(final Email email) throws EmailException {
86          if (EmailConfiguration.MAIL_FORCE_SEND) {
87              email.send();
88          } else {
89              email.buildMimeMessage();
90          }
91          return email;
92      }
93  
94      @BeforeEach
95      public void setUpLiveTest() {
96          // enforce a default charset UTF-8 otherwise non-ASCII attachment names will not work
97          System.setProperty("mail.mime.charset", StandardCharsets.UTF_8.name());
98  
99          // enforce encoding of non-ASCII characters (violating the MIME specification - see
100         // https://java.sun.com/products/javamail/javadocs/javax/mail/internet/package-summary.html
101         System.setProperty("mail.mime.encodefilename", "true");
102     }
103 
104     /**
105      * This test checks the correct character encoding when sending non-ASCII content using SimpleEmail.
106      *
107      * https://issues.apache.org/jira/browse/EMAIL-79
108      *
109      * @throws Exception the test failed
110      */
111     @Test
112     public void testCorrectCharacterEncoding() throws Exception {
113         // U+03B1 : GREEK SMALL LETTER ALPHA
114         // U+03B2 : GREEK SMALL LETTER BETA
115         // U+03B3 : GREEK SMALL LETTER GAMMA
116 
117         final String subject = "[email] 5.Test: Subject with three greek UTF-8 characters : \u03B1\u03B2\u03B3";
118         final String textMsg = "My test body with three greek UTF-8 characters : \u03B1\u03B2\u03B3\n";
119         final String attachmentName = "\u03B1\u03B2\u03B3.txt";
120 
121         // make sure to set the charset before adding the message content
122         final MultiPartEmail email = (MultiPartEmail) create(MultiPartEmail.class);
123         email.setSubject(subject);
124         email.setMsg(textMsg);
125 
126         // create a proper UTF-8 sequence for the text attachment (matching our default charset)
127         final DataSource attachment = new jakarta.mail.util.ByteArrayDataSource(textMsg.getBytes(StandardCharsets.UTF_8), "text/plain");
128         email.attach(attachment, attachmentName, "Attachment in Greek");
129 
130         MimeMessageUtils.writeMimeMessage(send(email).getMimeMessage(), new File("./target/test-emails/correct-encoding.eml"));
131     }
132 
133     /**
134      * A sanity check that a header folding works correctly.
135      *
136      * @throws Exception the test failed
137      */
138     @Test
139     public void testFoldedHeaderValue() throws Exception {
140         final SimpleEmail email = (SimpleEmail) create(SimpleEmail.class);
141         email.setSubject("TestFoldedHeaderMail");
142         email.setMsg("This is a test mail with a folded header value... :-)");
143         email.addHeader("X-TestHeader", "This is a very long header value which should be folded into two lines, hopefully");
144 
145         MimeMessageUtils.writeMimeMessage(send(email).getMimeMessage(), new File("./target/test-emails/foldedheader.eml"));
146     }
147 
148     /**
149      * This test checks the various options of building a HTML email.
150      *
151      * https://issues.apache.org/jira/browse/EMAIL-65
152      *
153      * @throws Exception the test failed
154      */
155     @Test
156     public void testHtmlMailMimeLayout() throws Exception {
157         String textMsg;
158         String htmlMsg;
159 
160         // prepare attachments
161 
162         String cid;
163 
164         final URL url = new URL(EmailConfiguration.TEST_URL);
165         final File imageFile = new File("./src/test/resources/images/asf_logo_wide.gif");
166 
167         final EmailAttachment attachment = new EmailAttachment();
168         final File attachmentFile = new File("./src/test/resources/attachments/logo.pdf");
169         attachment.setName("logo.pdf");
170         attachment.setDescription("The official Apache logo");
171         attachment.setPath(attachmentFile.getAbsolutePath());
172 
173         // 1) text + html content
174 
175         final HtmlEmail htmlEmail1 = (HtmlEmail) create(HtmlEmail.class);
176         textMsg = "Your email client does not support HTML messages";
177         htmlMsg = "<html><b>This is a HTML message without any image</b><html>";
178 
179         htmlEmail1.setSubject("[email] 1.Test: text + html content");
180         htmlEmail1.setTextMsg(textMsg);
181         htmlEmail1.setHtmlMsg(htmlMsg);
182 
183         MimeMessageUtils.writeMimeMessage(send(htmlEmail1).getMimeMessage(), new File("./target/test-emails/htmlemail1.eml"));
184 
185         // 2) text + html content + image as attachment
186 
187         final HtmlEmail htmlEmail2 = (HtmlEmail) create(HtmlEmail.class);
188         textMsg = "Your email client does not support HTML messages";
189         htmlMsg = "<html><b>This is a HTML message with an image attachment</b><html>";
190 
191         htmlEmail2.setSubject("[email] 2.Test: text + html content + image as attachment");
192         htmlEmail2.setTextMsg(textMsg);
193         htmlEmail2.setHtmlMsg(htmlMsg);
194         htmlEmail2.attach(url, "Apache Logo", "The official Apache logo");
195 
196         MimeMessageUtils.writeMimeMessage(send(htmlEmail2).getMimeMessage(), new File("./target/test-emails/htmlemail2.eml"));
197 
198         // 3) text + html content + inline image
199 
200         final HtmlEmail htmlEmail3 = (HtmlEmail) create(HtmlEmail.class);
201         textMsg = "Your email client does not support HTML messages";
202         cid = htmlEmail3.embed(imageFile, "Apache Logo");
203 
204         htmlMsg = "<html><b>This is a HTML message with an inline image - <img src=\"cid:" + cid + "\"> and NO attachment</b><html>";
205 
206         htmlEmail3.setSubject("[email] 3.Test: text + html content + inline image");
207         htmlEmail3.setTextMsg(textMsg);
208         htmlEmail3.setHtmlMsg(htmlMsg);
209 
210         MimeMessageUtils.writeMimeMessage(send(htmlEmail3).getMimeMessage(), new File("./target/test-emails/htmlemail3.eml"));
211 
212         // 4) text + html content + inline image + attachment
213 
214         final HtmlEmail htmlEmail4 = (HtmlEmail) create(HtmlEmail.class);
215         textMsg = "Your email client does not support HTML messages";
216         cid = htmlEmail4.embed(imageFile, "Apache Logo");
217         htmlMsg = "<html><b>This is a HTML message with an inline image - <img src=\"cid:" + cid + "\"> and attachment</b><html>";
218 
219         htmlEmail4.setSubject("[email] 4.Test: text + html content + inline image + attachment");
220         htmlEmail4.setTextMsg(textMsg);
221         htmlEmail4.setHtmlMsg(htmlMsg);
222         htmlEmail4.attach(attachment);
223 
224         MimeMessageUtils.writeMimeMessage(send(htmlEmail4).getMimeMessage(), new File("./target/test-emails/htmlemail4.eml"));
225     }
226 
227     /**
228      * Test sending a image HTML mail bases on a local HTML page and local image.
229      *
230      * @throws Exception the test failed
231      */
232     @Test
233     public void testImageHtmlEmailLocal() throws Exception {
234         // use a simple HTML page with one image
235 
236         final File htmlFile = new File("./src/test/resources/html/www.apache.org.html");
237         final String htmlMsg1 = FileUtils.readFileToString(htmlFile, StandardCharsets.ISO_8859_1.name());
238 
239         final ImageHtmlEmail email = (ImageHtmlEmail) create(ImageHtmlEmail.class);
240         email.setDataSourceResolver(new DataSourceUrlResolver(htmlFile.getParentFile().toURI().toURL(), false));
241         email.setSubject("[testImageHtmlEmail] 1.Test: simple html content");
242         email.setHtmlMsg(htmlMsg1);
243 
244         MimeMessageUtils.writeMimeMessage(send(email).getMimeMessage(), new File("./target/test-emails/testImageHtmlEmailLocal.eml"));
245     }
246 
247     /**
248      * Test sending a image HTML mail based on a real world website. We would expect to see the ApacheCon logo at the bottom of the email. Please note that not
249      * all major email clients can display the email properly.
250      *
251      * @throws Exception the test failed
252      */
253     @Test
254     public void testImageHtmlEmailRemote() throws Exception {
255         if (EmailConfiguration.MAIL_FORCE_SEND) {
256             final URL url = new URL("https://commons.apache.org/email/");
257             // URL url = new URL("http://www.dzone.com/links/index.html");
258             final String htmlMsg = getFromUrl(url);
259 
260             final ImageHtmlEmail email = (ImageHtmlEmail) create(ImageHtmlEmail.class);
261             email.setDataSourceResolver(new DataSourceUrlResolver(url, true));
262             email.setSubject("[testImageHtmlEmail] 2.Test: complex html content");
263             email.setHtmlMsg(htmlMsg);
264 
265             MimeMessageUtils.writeMimeMessage(send(email).getMimeMessage(), new File("./target/test-emails/testImageHtmlEmailRemote.eml"));
266         }
267     }
268 
269     /**
270      * A sanity check that a simple email also works in reality.
271      *
272      * @throws Exception the test failed
273      */
274     @Test
275     public void testMultiPartEmailFile() throws Exception {
276         final MultiPartEmail email = (MultiPartEmail) create(MultiPartEmail.class);
277         email.setSubject("TestMultiPartMail");
278         email.setMsg("This is a test mail ... :-)");
279         email.attach(new File("./src/test/resources/attachments/logo.pdf"));
280 
281         MimeMessageUtils.writeMimeMessage(send(email).getMimeMessage(), new File("./target/test-emails/multipart.eml"));
282     }
283 
284     /**
285      * A sanity check that a simple email also works in reality.
286      *
287      * @throws Exception the test failed
288      */
289     @Test
290     public void testMultiPartEmailPath() throws Exception {
291         final MultiPartEmail email = (MultiPartEmail) create(MultiPartEmail.class);
292         email.setSubject("TestMultiPartMail");
293         email.setMsg("This is a test mail ... :-)");
294         email.attach(Paths.get("./src/test/resources/attachments/logo.pdf"));
295 
296         MimeMessageUtils.writeMimeMessage(send(email).getMimeMessage(), new File("./target/test-emails/multipart.eml"));
297     }
298 
299     /**
300      * Testing if we are able to send a partial email with an invalid address.
301      *
302      * https://issues.apache.org/jira/browse/EMAIL-132
303      *
304      * @throws Exception the test failed.
305      */
306     @Test
307     public void testPartialSend() throws Exception {
308         final SimpleEmail email = (SimpleEmail) create(SimpleEmail.class);
309         email.addTo(EmailConfiguration.TEST_TO);
310         email.addTo("nobody@is.invalid");
311         email.setSubject("TestPartialMail");
312         email.setMsg("This is a test mail ... :-)");
313 
314         email.setSendPartial(true);
315 
316         MimeMessageUtils.writeMimeMessage(send(email).getMimeMessage(), new File("./target/test-emails/partialmail.eml"));
317     }
318 
319     /**
320      * Testing if we are able to send a few emails in a batch, i.e. using a single authenticated {@code Transport} instance. Use a single instance speeds up
321      * processing since the authorization is only done once.
322      *
323      * https://issues.apache.org/jira/browse/EMAIL-72
324      *
325      * @throws Exception the test failed.
326      */
327     @Test
328     public void testSendingEmailsInBatch() throws Exception {
329         final List<SimpleEmail> emails = new ArrayList<>();
330 
331         // we need to instantiate an email to provide the mail session - a bit ugly
332         final Session session = create(SimpleEmail.class).getMailSession();
333         try (Transport transport = session.getTransport()) {
334 
335             // simulate creating a bunch of emails using an existing mail session
336             for (int i = 0; i < 3; i++) {
337                 final SimpleEmail personalizedEmail = (SimpleEmail) create(SimpleEmail.class);
338                 personalizedEmail.setMailSession(session);
339                 personalizedEmail.setSubject("Personalized Test Mail Nr. " + i);
340                 personalizedEmail.setMsg("This is a personalized test mail ... :-)");
341                 personalizedEmail.buildMimeMessage();
342                 emails.add(personalizedEmail);
343             }
344 
345             // send the list of emails using a single 'Transport' instance.
346             if (EmailConfiguration.MAIL_FORCE_SEND) {
347                 transport.connect();
348 
349                 for (final SimpleEmail personalizedEmail : emails) {
350                     final MimeMessage mimeMessage = personalizedEmail.getMimeMessage();
351                     Transport.send(mimeMessage);
352                     System.out.println("Successfully sent the following email : " + mimeMessage.getMessageID());
353                 }
354             }
355         }
356     }
357 
358     /**
359      * A sanity check that a simple email also works in reality.
360      *
361      * @throws Exception the test failed
362      */
363     @Test
364     public void testSimpleEmail() throws Exception {
365         final SimpleEmail email = (SimpleEmail) create(SimpleEmail.class);
366         email.setSubject("TestSimpleMail");
367         email.setMsg("This is a test mail ... :-)");
368 
369         MimeMessageUtils.writeMimeMessage(send(email).getMimeMessage(), new File("./target/test-emails/simplemail.eml"));
370     }
371 
372 }