1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.classic.blackbox.net;
15
16 import java.io.ByteArrayInputStream;
17 import java.io.ByteArrayOutputStream;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.TimeUnit;
22
23 import ch.qos.logback.classic.blackbox.BlackboxClassicTestConstants;
24 import ch.qos.logback.classic.net.SMTPAppender;
25 import ch.qos.logback.classic.util.LogbackMDCAdapter;
26 import ch.qos.logback.core.util.EnvUtil;
27 import org.dom4j.DocumentException;
28 import org.dom4j.io.SAXReader;
29 import org.junit.jupiter.api.AfterEach;
30 import org.junit.jupiter.api.BeforeEach;
31 import org.junit.jupiter.api.Disabled;
32 import org.junit.jupiter.api.Test;
33 import org.slf4j.MDC;
34
35 import com.icegreen.greenmail.util.DummySSLSocketFactory;
36 import com.icegreen.greenmail.util.GreenMail;
37 import com.icegreen.greenmail.util.GreenMailUtil;
38 import com.icegreen.greenmail.util.ServerSetup;
39
40
41 import ch.qos.logback.classic.Logger;
42 import ch.qos.logback.classic.LoggerContext;
43 import ch.qos.logback.classic.PatternLayout;
44 import ch.qos.logback.classic.html.HTMLLayout;
45 import ch.qos.logback.classic.blackbox.html.XHTMLEntityResolver;
46 import ch.qos.logback.classic.joran.JoranConfigurator;
47 import ch.qos.logback.classic.spi.ILoggingEvent;
48 import ch.qos.logback.core.Layout;
49 import ch.qos.logback.core.joran.spi.JoranException;
50 import ch.qos.logback.core.status.OnConsoleStatusListener;
51 import ch.qos.logback.core.testUtil.RandomUtil;
52 import ch.qos.logback.core.util.StatusListenerConfigHelper;
53 import ch.qos.logback.core.util.StatusPrinter;
54 import jakarta.mail.MessagingException;
55 import jakarta.mail.internet.MimeMessage;
56 import jakarta.mail.internet.MimeMultipart;
57
58 import static org.junit.jupiter.api.Assertions.*;
59
60 public class SMTPAppender_GreenTest {
61
62 static boolean NO_SSL = false;
63 static boolean WITH_SSL = true;
64
65 static final String HEADER = "HEADER\n";
66 static final String FOOTER = "FOOTER\n";
67 static final String DEFAULT_PATTERN = "%-4relative %mdc [%thread] %-5level %class - %msg%n";
68
69 static final boolean SYNCHRONOUS = false;
70 static final boolean ASYNCHRONOUS = true;
71 static int TIMEOUT = 3000;
72
73 int port = RandomUtil.getRandomServerPort();
74
75 GreenMail greenMailServer;
76
77 SMTPAppender smtpAppender;
78 LoggerContext loggerContext = new LoggerContext();
79 LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter();
80 Logger logger = loggerContext.getLogger(this.getClass());
81
82 static String REQUIRED_USERNAME = "alice";
83 static String REQUIRED_PASSWORD = "alicepass";
84
85 @BeforeEach
86 public void setUp() throws Exception {
87 loggerContext.setMDCAdapter(logbackMDCAdapter);
88 StatusListenerConfigHelper.addOnConsoleListenerInstance(loggerContext, new OnConsoleStatusListener());
89 }
90
91 void startSMTPServer(boolean withSSL) {
92 ServerSetup serverSetup;
93
94 if (withSSL) {
95 serverSetup = new ServerSetup(port, null, ServerSetup.PROTOCOL_SMTPS);
96 } else {
97 serverSetup = new ServerSetup(port, null, ServerSetup.PROTOCOL_SMTP);
98 }
99 greenMailServer = new GreenMail(serverSetup);
100
101 greenMailServer.setUser(REQUIRED_USERNAME, REQUIRED_PASSWORD);
102 greenMailServer.start();
103
104 try {
105 Thread.sleep(10);
106 } catch (InterruptedException e) {
107 }
108 }
109
110 @AfterEach
111 public void tearDown() throws Exception {
112 greenMailServer.stop();
113 }
114
115 void buildSMTPAppender(String subject, boolean synchronicity) throws Exception {
116 smtpAppender = new SMTPAppender();
117 smtpAppender.setContext(loggerContext);
118 smtpAppender.setName("smtp");
119 smtpAppender.setFrom("user@host.dom");
120 smtpAppender.setSMTPHost("localhost");
121 smtpAppender.setSMTPPort(port);
122 smtpAppender.setSubject(subject);
123 smtpAppender.addTo("nospam@qos.ch");
124 smtpAppender.setAsynchronousSending(synchronicity);
125 }
126
127 private Layout<ILoggingEvent> buildPatternLayout(String pattern) {
128 PatternLayout layout = new PatternLayout();
129 layout.setContext(loggerContext);
130 layout.setFileHeader(HEADER);
131 layout.setOutputPatternAsHeader(false);
132 layout.setPattern(pattern);
133 layout.setFileFooter(FOOTER);
134 layout.start();
135 return layout;
136 }
137
138 private Layout<ILoggingEvent> buildHTMLLayout() {
139 HTMLLayout layout = new HTMLLayout();
140 layout.setContext(loggerContext);
141 layout.setPattern("%level%class%msg");
142 layout.start();
143 return layout;
144 }
145
146 private void waitForServerToReceiveEmails(int emailCount) throws InterruptedException {
147 greenMailServer.waitForIncomingEmail(5000, emailCount);
148 }
149
150 private MimeMultipart verifyAndExtractMimeMultipart(String subject)
151 throws MessagingException, IOException, InterruptedException {
152 int oldCount = 0;
153 int expectedEmailCount = 1;
154
155 waitForServerToReceiveEmails(expectedEmailCount);
156 MimeMessage[] mma = greenMailServer.getReceivedMessages();
157 assertNotNull(mma);
158 assertEquals(expectedEmailCount, mma.length);
159 MimeMessage mm = mma[oldCount];
160
161 assertEquals(subject, mm.getSubject());
162 return (MimeMultipart) mm.getContent();
163 }
164
165
166
167 void waitUntilEmailIsSent() throws InterruptedException {
168 ExecutorService es = loggerContext.getExecutorService();
169 es.shutdown();
170 boolean terminated = es.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS);
171
172
173 if(!terminated && !EnvUtil.isMacOs()) {
174 fail("executor elapsed before accorded delay " + System.getProperty("os.name"));
175 }
176
177 }
178
179 @Test
180 public void synchronousSmoke() throws Exception {
181 startSMTPServer(NO_SSL);
182 String subject = "synchronousSmoke";
183 buildSMTPAppender(subject, SYNCHRONOUS);
184
185 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
186
187 smtpAppender.start();
188
189 logger.addAppender(smtpAppender);
190 logger.debug("hello");
191 logger.error("en error", new Exception("an exception"));
192
193 MimeMultipart mp = verifyAndExtractMimeMultipart(subject);
194 String body = GreenMailUtil.getBody(mp.getBodyPart(0));
195 assertTrue(body.startsWith(HEADER.trim()));
196 assertTrue(body.endsWith(FOOTER.trim()));
197 }
198
199 @Test
200 public void asynchronousSmoke() throws Exception {
201 startSMTPServer(NO_SSL);
202
203 String subject = "asynchronousSmoke";
204 buildSMTPAppender(subject, ASYNCHRONOUS);
205 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
206 smtpAppender.start();
207
208 logger.addAppender(smtpAppender);
209 logger.debug("hello");
210 logger.error("en error", new Exception("an exception"));
211
212 waitUntilEmailIsSent();
213 MimeMultipart mp = verifyAndExtractMimeMultipart(subject);
214 String body = GreenMailUtil.getBody(mp.getBodyPart(0));
215 assertTrue(body.startsWith(HEADER.trim()));
216 assertTrue(body.endsWith(FOOTER.trim()));
217 }
218
219
220 @Test
221 public void callerDataShouldBeCorrectlySetWithAsynchronousSending() throws Exception {
222 startSMTPServer(NO_SSL);
223 String subject = "LOGBACK-734";
224 buildSMTPAppender("LOGBACK-734", ASYNCHRONOUS);
225 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
226 smtpAppender.setIncludeCallerData(true);
227 smtpAppender.start();
228 logger.addAppender(smtpAppender);
229 logger.debug("LOGBACK-734");
230 logger.error("callerData", new Exception("ShouldBeCorrectlySetWithAsynchronousSending"));
231
232 waitUntilEmailIsSent();
233 MimeMultipart mp = verifyAndExtractMimeMultipart(subject);
234 String body = GreenMailUtil.getBody(mp.getBodyPart(0));
235 assertTrue(body.contains("DEBUG " + this.getClass().getName() + " - LOGBACK-734"), "actual [" + body + "]");
236 }
237
238
239 @Test
240 public void LOGBACK_352() throws Exception {
241 startSMTPServer(NO_SSL);
242 String subject = "LOGBACK_352";
243 buildSMTPAppender(subject, SYNCHRONOUS);
244 smtpAppender.setAsynchronousSending(false);
245 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
246 smtpAppender.start();
247 logger.addAppender(smtpAppender);
248 logbackMDCAdapter.put("key", "val");
249 logger.debug("LBCLASSIC_104");
250 logbackMDCAdapter.clear();
251 logger.error("en error", new Exception("test"));
252
253 MimeMultipart mp = verifyAndExtractMimeMultipart(subject);
254 String body = GreenMailUtil.getBody(mp.getBodyPart(0));
255 assertTrue(body.startsWith(HEADER.trim()));
256 System.out.println(body);
257 assertTrue(body.contains("key=val"));
258 assertTrue(body.endsWith(FOOTER.trim()));
259 }
260
261 @Test
262 public void html() throws Exception {
263 startSMTPServer(NO_SSL);
264 String subject = "html";
265 buildSMTPAppender(subject, SYNCHRONOUS);
266 smtpAppender.setAsynchronousSending(false);
267 smtpAppender.setLayout(buildHTMLLayout());
268 smtpAppender.start();
269 logger.addAppender(smtpAppender);
270 logger.debug("html");
271 logger.error("en error", new Exception("an exception"));
272
273 MimeMultipart mp = verifyAndExtractMimeMultipart(subject);
274
275
276 SAXReader reader = new SAXReader();
277 reader.setValidation(true);
278 reader.setEntityResolver(new XHTMLEntityResolver());
279 byte[] messageBytes = getAsByteArray(mp.getBodyPart(0).getInputStream());
280 ByteArrayInputStream bais = new ByteArrayInputStream(messageBytes);
281 try {
282 reader.read(bais);
283 } catch (DocumentException de) {
284 System.out.println("incoming message:");
285 System.out.println(new String(messageBytes));
286 throw de;
287 }
288 System.out.println("incoming message:");
289 System.out.println(new String(messageBytes));
290 }
291
292 private byte[] getAsByteArray(InputStream inputStream) throws IOException {
293 ByteArrayOutputStream baos = new ByteArrayOutputStream();
294
295 byte[] buffer = new byte[1024];
296 int n = -1;
297 while ((n = inputStream.read(buffer)) != -1) {
298 baos.write(buffer, 0, n);
299 }
300 return baos.toByteArray();
301 }
302
303 private void configure(String file) throws JoranException {
304 JoranConfigurator jc = new JoranConfigurator();
305 jc.setContext(loggerContext);
306 loggerContext.putProperty("port", "" + port);
307 jc.doConfigure(file);
308 }
309
310 @Test
311 public void testCustomEvaluator() throws Exception {
312 startSMTPServer(NO_SSL);
313 configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "smtp/customEvaluator.xml");
314
315 logger.debug("test");
316 String msg2 = "CustomEvaluator";
317 logger.debug(msg2);
318 logger.debug("invisible");
319 waitUntilEmailIsSent();
320 MimeMultipart mp = verifyAndExtractMimeMultipart(
321 "testCustomEvaluator " + this.getClass().getName() + " - " + msg2);
322 String body = GreenMailUtil.getBody(mp.getBodyPart(0));
323 assertEquals("testCustomEvaluator", body);
324 }
325
326 @Test
327 public void testCustomBufferSize() throws Exception {
328 startSMTPServer(NO_SSL);
329 configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "smtp/customBufferSize.xml");
330
331 logger.debug("invisible1");
332 logger.debug("invisible2");
333 String msg = "hello";
334 logger.error(msg);
335 waitUntilEmailIsSent();
336 MimeMultipart mp = verifyAndExtractMimeMultipart(
337 "testCustomBufferSize " + this.getClass().getName() + " - " + msg);
338 String body = GreenMailUtil.getBody(mp.getBodyPart(0));
339 assertEquals(msg, body);
340 }
341
342
343 @Test
344 public void testMultipleTo() throws Exception {
345 startSMTPServer(NO_SSL);
346 buildSMTPAppender("testMultipleTo", SYNCHRONOUS);
347 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
348
349 smtpAppender.addTo("Test <test@example.com>, other-test@example.com");
350 smtpAppender.start();
351 logger.addAppender(smtpAppender);
352 logger.debug("testMultipleTo hello");
353 logger.error("testMultipleTo en error", new Exception("an exception"));
354 Thread.yield();
355 int expectedEmailCount = 3;
356 waitForServerToReceiveEmails(expectedEmailCount);
357 MimeMessage[] mma = greenMailServer.getReceivedMessages();
358 assertNotNull(mma);
359 assertEquals(expectedEmailCount, mma.length);
360 }
361
362
363 @Test
364 public void bufferShouldBeResetBetweenMessages() throws Exception {
365 startSMTPServer(NO_SSL);
366 buildSMTPAppender("bufferShouldBeResetBetweenMessages", SYNCHRONOUS);
367 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
368 smtpAppender.start();
369 logger.addAppender(smtpAppender);
370 String msg0 = "hello zero";
371 logger.debug(msg0);
372 logger.error("error zero");
373
374 String msg1 = "hello one";
375 logger.debug(msg1);
376 logger.error("error one");
377
378 Thread.yield();
379 int oldCount = 0;
380 int expectedEmailCount = oldCount + 2;
381 waitForServerToReceiveEmails(expectedEmailCount);
382
383 MimeMessage[] mma = greenMailServer.getReceivedMessages();
384 assertNotNull(mma);
385 assertEquals(expectedEmailCount, mma.length);
386
387 MimeMessage mm0 = mma[oldCount];
388 MimeMultipart content0 = (MimeMultipart) mm0.getContent();
389 @SuppressWarnings("unused")
390 String body0 = GreenMailUtil.getBody(content0.getBodyPart(0));
391
392 MimeMessage mm1 = mma[oldCount + 1];
393 MimeMultipart content1 = (MimeMultipart) mm1.getContent();
394 String body1 = GreenMailUtil.getBody(content1.getBodyPart(0));
395
396 assertFalse(body1.contains(msg0));
397 }
398
399 @Test
400 public void multiLineSubjectTruncatedAtFirstNewLine() throws Exception {
401 startSMTPServer(NO_SSL);
402 String line1 = "line 1 of subject";
403 String subject = line1 + "\nline 2 of subject\n";
404 buildSMTPAppender(subject, ASYNCHRONOUS);
405
406 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
407 smtpAppender.start();
408 logger.addAppender(smtpAppender);
409 logger.debug("hello");
410 logger.error("en error", new Exception("an exception"));
411
412 Thread.yield();
413 waitUntilEmailIsSent();
414 waitForServerToReceiveEmails(1);
415
416 MimeMessage[] mma = greenMailServer.getReceivedMessages();
417 assertEquals(1, mma.length);
418 assertEquals(line1, mma[0].getSubject());
419 }
420
421 @Test
422 public void authenticated() throws Exception {
423 startSMTPServer(NO_SSL);
424 buildSMTPAppender("testMultipleTo", SYNCHRONOUS);
425 smtpAppender.setUsername(REQUIRED_USERNAME);
426 smtpAppender.setPassword(REQUIRED_PASSWORD);
427
428 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
429 smtpAppender.start();
430
431 logger.addAppender(smtpAppender);
432 logger.debug("authenticated");
433 logger.error("authenticated en error", new Exception("an exception"));
434
435 waitUntilEmailIsSent();
436 waitForServerToReceiveEmails(1);
437
438 MimeMessage[] mma = greenMailServer.getReceivedMessages();
439 assertNotNull(mma);
440 assertTrue(mma.length == 1, "body should not be empty");
441 }
442
443 void setSystemPropertiesForStartTLS() {
444 String PREFIX = "mail.smtp.";
445 System.setProperty(PREFIX + "starttls.enable", "true");
446 System.setProperty(PREFIX + "socketFactory.class", DummySSLSocketFactory.class.getName());
447 System.setProperty(PREFIX + "socketFactory.fallback", "false");
448 }
449
450 void unsetSystemPropertiesForStartTLS() {
451 String PREFIX = "mail.smtp.";
452 System.clearProperty(PREFIX + "starttls.enable");
453 System.clearProperty(PREFIX + "socketFactory.class");
454 System.clearProperty(PREFIX + "socketFactory.fallback");
455 }
456
457 @Test
458 public void authenticatedSSL() throws Exception {
459 try {
460 setSystemPropertiesForStartTLS();
461
462 startSMTPServer(WITH_SSL);
463 buildSMTPAppender("testMultipleTo", SYNCHRONOUS);
464 smtpAppender.setUsername(REQUIRED_USERNAME);
465 smtpAppender.setPassword(REQUIRED_PASSWORD);
466 smtpAppender.setSTARTTLS(true);
467 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
468 smtpAppender.start();
469
470 logger.addAppender(smtpAppender);
471 logger.debug("authenticated");
472 logger.error("authenticated en error", new Exception("an exception"));
473
474 waitUntilEmailIsSent();
475 waitForServerToReceiveEmails(1);
476
477 MimeMessage[] mma = greenMailServer.getReceivedMessages();
478 assertNotNull(mma);
479 assertTrue(mma.length == 1, "body should not be empty");
480 } finally {
481 unsetSystemPropertiesForStartTLS();
482 }
483 }
484
485
486
487
488 static String GMAIL_USER_NAME = "xx@gmail.com";
489 static String GMAIL_PASSWORD = "xxx";
490
491 @Disabled
492 @Test
493 public void authenticatedGmailStartTLS() throws Exception {
494 smtpAppender.setSMTPHost("smtp.gmail.com");
495 smtpAppender.setSMTPPort(587);
496 smtpAppender.setAsynchronousSending(false);
497 smtpAppender.addTo(GMAIL_USER_NAME);
498
499 smtpAppender.setSTARTTLS(true);
500 smtpAppender.setUsername(GMAIL_USER_NAME);
501 smtpAppender.setPassword(GMAIL_PASSWORD);
502
503 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
504 smtpAppender.setSubject("authenticatedGmailStartTLS - %level %logger{20} - %m");
505 smtpAppender.start();
506 Logger logger = loggerContext.getLogger("authenticatedGmailSTARTTLS");
507 logger.addAppender(smtpAppender);
508 logger.debug("authenticatedGmailStartTLS =- hello");
509 logger.error("en error", new Exception("an exception"));
510
511 StatusPrinter.print(loggerContext);
512 }
513
514 @Disabled
515 @Test
516 public void authenticatedGmail_SSL() throws Exception {
517 smtpAppender.setSMTPHost("smtp.gmail.com");
518 smtpAppender.setSMTPPort(465);
519 smtpAppender.setSubject("authenticatedGmail_SSL - %level %logger{20} - %m");
520 smtpAppender.addTo(GMAIL_USER_NAME);
521 smtpAppender.setSSL(true);
522 smtpAppender.setUsername(GMAIL_USER_NAME);
523 smtpAppender.setPassword(GMAIL_PASSWORD);
524 smtpAppender.setAsynchronousSending(false);
525 smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
526 smtpAppender.start();
527 Logger logger = loggerContext.getLogger("authenticatedGmail_SSL");
528 logger.addAppender(smtpAppender);
529 logger.debug("hello" + new java.util.Date());
530 logger.error("en error", new Exception("an exception"));
531
532 StatusPrinter.print(loggerContext);
533
534 }
535 }