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