001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic.net; 015 016import static org.junit.Assert.assertEquals; 017import static org.junit.Assert.assertNotNull; 018import static org.junit.Assert.assertTrue; 019 020import java.io.ByteArrayOutputStream; 021import java.util.List; 022import java.util.Random; 023import java.util.concurrent.ThreadPoolExecutor; 024import java.util.concurrent.TimeUnit; 025 026import javax.mail.Part; 027import javax.mail.internet.MimeMessage; 028import javax.mail.internet.MimeMultipart; 029 030import org.dom4j.io.SAXReader; 031import org.junit.After; 032import org.junit.Before; 033import org.junit.Ignore; 034import org.junit.Test; 035import org.subethamail.smtp.auth.EasyAuthenticationHandlerFactory; 036import org.subethamail.smtp.auth.LoginFailedException; 037import org.subethamail.smtp.auth.UsernamePasswordValidator; 038import org.subethamail.wiser.Wiser; 039import org.subethamail.wiser.WiserMessage; 040 041import ch.qos.logback.classic.Logger; 042import ch.qos.logback.classic.LoggerContext; 043import ch.qos.logback.classic.PatternLayout; 044import ch.qos.logback.classic.html.HTMLLayout; 045import ch.qos.logback.classic.html.XHTMLEntityResolver; 046import ch.qos.logback.classic.spi.ILoggingEvent; 047import ch.qos.logback.core.CoreConstants; 048import ch.qos.logback.core.Layout; 049import ch.qos.logback.core.util.StatusPrinter; 050 051public class SMTPAppender_SubethaSMTPTest { 052 static final String TEST_SUBJECT = "test subject"; 053 static final String HEADER = "HEADER\n"; 054 static final String FOOTER = "FOOTER\n"; 055 056 static int DIFF = 1024 + new Random().nextInt(30000); 057 static Wiser WISER; 058 059 SMTPAppender smtpAppender; 060 LoggerContext loggerContext = new LoggerContext(); 061 062 int numberOfOldMessages; 063 064// @BeforeClass 065// static public void beforeClass() { 066// WISER = new Wiser(); 067// WISER.setPort(DIFF); 068// WISER.start(); 069// } 070 071// @AfterClass 072// static public void afterClass() throws Exception { 073// WISER.stop(); 074// } 075 076 @Before 077 public void setUp() throws Exception { 078 WISER = new Wiser(); 079 WISER.setPort(DIFF); 080 WISER.start(); 081 numberOfOldMessages = WISER.getMessages().size(); 082 buildSMTPAppender(); 083 } 084 085 @After 086 public void tearDown() { 087 // clear any authentication handler factory 088 //WISER.getServer().setAuthenticationHandlerFactory(null); 089 WISER.stop(); 090 } 091 092 void buildSMTPAppender() throws Exception { 093 smtpAppender = new SMTPAppender(); 094 smtpAppender.setContext(loggerContext); 095 smtpAppender.setName("smtp"); 096 smtpAppender.setFrom("user@host.dom"); 097 smtpAppender.setSMTPHost("localhost"); 098 smtpAppender.setSMTPPort(DIFF); 099 smtpAppender.setSubject(TEST_SUBJECT); 100 smtpAppender.addTo("noreply@qos.ch"); 101 } 102 103 private Layout<ILoggingEvent> buildPatternLayout(LoggerContext lc) { 104 PatternLayout layout = new PatternLayout(); 105 layout.setContext(lc); 106 layout.setFileHeader(HEADER); 107 layout.setPattern("%-4relative [%thread] %-5level %logger %class - %msg%n"); 108 layout.setFileFooter(FOOTER); 109 layout.start(); 110 return layout; 111 } 112 113 private Layout<ILoggingEvent> buildHTMLLayout(LoggerContext lc) { 114 HTMLLayout layout = new HTMLLayout(); 115 layout.setContext(lc); 116 // layout.setFileHeader(HEADER); 117 layout.setPattern("%level%class%msg"); 118 // layout.setFileFooter(FOOTER); 119 layout.start(); 120 return layout; 121 } 122 123 private static String getWholeMessage(Part msg) { 124 try { 125 ByteArrayOutputStream bodyOut = new ByteArrayOutputStream(); 126 msg.writeTo(bodyOut); 127 return bodyOut.toString("US-ASCII").trim(); 128 } catch (Exception e) { 129 throw new RuntimeException(e); 130 } 131 } 132 133 void waitUntilEmailIsSent() throws Exception { 134 System.out.println("About to wait for sending thread to finish"); 135 loggerContext.getExecutorService().shutdown(); 136 loggerContext.getExecutorService().awaitTermination(3000, TimeUnit.MILLISECONDS); 137 } 138 139 private static String getBody(Part msg) { 140 String all = getWholeMessage(msg); 141 int i = all.indexOf("\r\n\r\n"); 142 return all.substring(i + 4, all.length()); 143 } 144 145 @Test 146 public void smoke() throws Exception { 147 smtpAppender.setLayout(buildPatternLayout(loggerContext)); 148 smtpAppender.start(); 149 Logger logger = loggerContext.getLogger("test"); 150 logger.addAppender(smtpAppender); 151 logger.debug("hello"); 152 logger.error("en error", new Exception("an exception")); 153 154 waitUntilEmailIsSent(); 155 System.out.println("*** " + ((ThreadPoolExecutor) loggerContext.getExecutorService()).getCompletedTaskCount()); 156 List<WiserMessage> wiserMsgList = WISER.getMessages(); 157 158 assertNotNull(wiserMsgList); 159 assertEquals(numberOfOldMessages + 1, wiserMsgList.size()); 160 WiserMessage wm = wiserMsgList.get(numberOfOldMessages); 161 // http://jira.qos.ch/browse/LBCLASSIC-67 162 MimeMessage mm = wm.getMimeMessage(); 163 assertEquals(TEST_SUBJECT, mm.getSubject()); 164 165 MimeMultipart mp = (MimeMultipart) mm.getContent(); 166 String body = getBody(mp.getBodyPart(0)); 167 System.out.println("[" + body); 168 assertTrue(body.startsWith(HEADER.trim())); 169 assertTrue(body.endsWith(FOOTER.trim())); 170 } 171 172 @Test 173 public void html() throws Exception { 174 175 smtpAppender.setLayout(buildHTMLLayout(loggerContext)); 176 smtpAppender.start(); 177 Logger logger = loggerContext.getLogger("test"); 178 logger.addAppender(smtpAppender); 179 logger.debug("hello"); 180 logger.error("en error", new Exception("an exception")); 181 waitUntilEmailIsSent(); 182 183 List<WiserMessage> wiserMsgList = WISER.getMessages(); 184 185 assertNotNull(wiserMsgList); 186 assertEquals(numberOfOldMessages + 1, wiserMsgList.size()); 187 WiserMessage wm = wiserMsgList.get(numberOfOldMessages); 188 MimeMessage mm = wm.getMimeMessage(); 189 assertEquals(TEST_SUBJECT, mm.getSubject()); 190 191 MimeMultipart mp = (MimeMultipart) mm.getContent(); 192 193 // verify strict adherence to xhtml1-strict.dtd 194 SAXReader reader = new SAXReader(); 195 reader.setValidation(true); 196 reader.setEntityResolver(new XHTMLEntityResolver()); 197 reader.read(mp.getBodyPart(0).getInputStream()); 198 // System.out.println(GreenMailUtil.getBody(mp.getBodyPart(0))); 199 } 200 201 @Test 202 /** 203 * Checks that even when many events are processed, the output is still 204 * conforms to xhtml-strict.dtd. 205 * 206 * Note that SMTPAppender only keeps only 500 or so (=buffer size) events. So 207 * the generated output will be rather short. 208 */ 209 public void htmlLong() throws Exception { 210 smtpAppender.setLayout(buildHTMLLayout(loggerContext)); 211 smtpAppender.start(); 212 Logger logger = loggerContext.getLogger("test"); 213 logger.addAppender(smtpAppender); 214 for (int i = 0; i < CoreConstants.TABLE_ROW_LIMIT * 3; i++) { 215 logger.debug("hello " + i); 216 } 217 logger.error("en error", new Exception("an exception")); 218 waitUntilEmailIsSent(); 219 List<WiserMessage> wiserMsgList = WISER.getMessages(); 220 221 assertNotNull(wiserMsgList); 222 assertEquals(numberOfOldMessages + 1, wiserMsgList.size()); 223 WiserMessage wm = wiserMsgList.get(numberOfOldMessages); 224 MimeMessage mm = wm.getMimeMessage(); 225 assertEquals(TEST_SUBJECT, mm.getSubject()); 226 227 MimeMultipart mp = (MimeMultipart) mm.getContent(); 228 229 // verify strict adherence to xhtml1-strict.dtd 230 SAXReader reader = new SAXReader(); 231 reader.setValidation(true); 232 reader.setEntityResolver(new XHTMLEntityResolver()); 233 reader.read(mp.getBodyPart(0).getInputStream()); 234 } 235 236 static String REQUIRED_USERNAME = "user"; 237 static String REQUIRED_PASSWORD = "password"; 238 239 class RequiredUsernamePasswordValidator implements UsernamePasswordValidator { 240 public void login(String username, String password) throws LoginFailedException { 241 if (!username.equals(REQUIRED_USERNAME) || !password.equals(REQUIRED_PASSWORD)) { 242 throw new LoginFailedException(); 243 } 244 } 245 } 246 247 248 249 250 @Test 251 public void authenticated() throws Exception { 252 setAuthenticanHandlerFactory(); 253 // MessageListenerAdapter mla = (MessageListenerAdapter) WISER.getServer().getMessageHandlerFactory(); 254 // mla.setAuthenticationHandlerFactory(new TrivialAuthHandlerFactory()); 255 256 smtpAppender.setUsername(REQUIRED_USERNAME); 257 smtpAppender.setPassword(REQUIRED_PASSWORD); 258 259 smtpAppender.setLayout(buildPatternLayout(loggerContext)); 260 smtpAppender.start(); 261 Logger logger = loggerContext.getLogger("test"); 262 logger.addAppender(smtpAppender); 263 logger.debug("hello"); 264 logger.error("en error", new Exception("an exception")); 265 waitUntilEmailIsSent(); 266 List<WiserMessage> wiserMsgList = WISER.getMessages(); 267 268 assertNotNull(wiserMsgList); 269 assertEquals(numberOfOldMessages + 1, wiserMsgList.size()); 270 WiserMessage wm = wiserMsgList.get(numberOfOldMessages); 271 // http://jira.qos.ch/browse/LBCLASSIC-67 272 MimeMessage mm = wm.getMimeMessage(); 273 assertEquals(TEST_SUBJECT, mm.getSubject()); 274 275 MimeMultipart mp = (MimeMultipart) mm.getContent(); 276 String body = getBody(mp.getBodyPart(0)); 277 assertTrue(body.startsWith(HEADER.trim())); 278 assertTrue(body.endsWith(FOOTER.trim())); 279 } 280 281 private void setAuthenticanHandlerFactory() { 282 UsernamePasswordValidator validator = new RequiredUsernamePasswordValidator(); 283 EasyAuthenticationHandlerFactory authenticationHandlerFactory = new EasyAuthenticationHandlerFactory(validator); 284 WISER.getServer().setAuthenticationHandlerFactory(authenticationHandlerFactory); 285 } 286 287 288 // Unfortunately, there seems to be a problem with SubethaSMTP's implementation 289 // of startTLS. The same SMTPAppender code works fine when tested with gmail. 290 @Test 291 public void authenticatedSSL() throws Exception { 292 293 setAuthenticanHandlerFactory(); 294 295 smtpAppender.setSTARTTLS(true); 296 smtpAppender.setUsername(REQUIRED_USERNAME); 297 smtpAppender.setPassword(REQUIRED_PASSWORD); 298 299 smtpAppender.setLayout(buildPatternLayout(loggerContext)); 300 smtpAppender.start(); 301 Logger logger = loggerContext.getLogger("test"); 302 logger.addAppender(smtpAppender); 303 logger.debug("hello"); 304 logger.error("en error", new Exception("an exception")); 305 306 waitUntilEmailIsSent(); 307 List<WiserMessage> wiserMsgList = WISER.getMessages(); 308 309 assertNotNull(wiserMsgList); 310 assertEquals(1, wiserMsgList.size()); 311 } 312 313 314 static String GMAIL_USER_NAME = "xx@gmail.com"; 315 static String GMAIL_PASSWORD = "xxx"; 316 317 @Ignore 318 @Test 319 public void authenticatedGmailStartTLS() throws Exception { 320 smtpAppender.setSMTPHost("smtp.gmail.com"); 321 smtpAppender.setSMTPPort(587); 322 smtpAppender.setAsynchronousSending(false); 323 smtpAppender.addTo(GMAIL_USER_NAME); 324 325 smtpAppender.setSTARTTLS(true); 326 smtpAppender.setUsername(GMAIL_USER_NAME); 327 smtpAppender.setPassword(GMAIL_PASSWORD); 328 329 smtpAppender.setLayout(buildPatternLayout(loggerContext)); 330 smtpAppender.setSubject("authenticatedGmailStartTLS - %level %logger{20} - %m"); 331 smtpAppender.start(); 332 Logger logger = loggerContext.getLogger("authenticatedGmailSTARTTLS"); 333 logger.addAppender(smtpAppender); 334 logger.debug("authenticatedGmailStartTLS =- hello"); 335 logger.error("en error", new Exception("an exception")); 336 337 StatusPrinter.print(loggerContext); 338 } 339 340 @Ignore 341 @Test 342 public void authenticatedGmail_SSL() throws Exception { 343 smtpAppender.setSMTPHost("smtp.gmail.com"); 344 smtpAppender.setSMTPPort(465); 345 smtpAppender.setSubject("authenticatedGmail_SSL - %level %logger{20} - %m"); 346 smtpAppender.addTo(GMAIL_USER_NAME); 347 smtpAppender.setSSL(true); 348 smtpAppender.setUsername(GMAIL_USER_NAME); 349 smtpAppender.setPassword(GMAIL_PASSWORD); 350 smtpAppender.setAsynchronousSending(false); 351 smtpAppender.setLayout(buildPatternLayout(loggerContext)); 352 smtpAppender.start(); 353 Logger logger = loggerContext.getLogger("authenticatedGmail_SSL"); 354 logger.addAppender(smtpAppender); 355 logger.debug("hello"+new java.util.Date()); 356 logger.error("en error", new Exception("an exception")); 357 358 StatusPrinter.print(loggerContext); 359 360 361 } 362 363 @Test 364 public void testMultipleTo() throws Exception { 365 smtpAppender.setLayout(buildPatternLayout(loggerContext)); 366 smtpAppender.addTo("Test <test@example.com>, other-test@example.com"); 367 smtpAppender.start(); 368 Logger logger = loggerContext.getLogger("test"); 369 logger.addAppender(smtpAppender); 370 logger.debug("hello"); 371 logger.error("en error", new Exception("an exception")); 372 waitUntilEmailIsSent(); 373 374 List<WiserMessage> wiserMsgList = WISER.getMessages(); 375 assertNotNull(wiserMsgList); 376 assertEquals(numberOfOldMessages + 3, wiserMsgList.size()); 377 } 378 379// public class TrivialAuthHandlerFactory implements AuthenticationHandlerFactory { 380// public AuthenticationHandler create() { 381// PluginAuthenticationHandler ret = new PluginAuthenticationHandler(); 382// UsernamePasswordValidator validator = new UsernamePasswordValidator() { 383// public void login(String username, String password) throws LoginFailedException { 384// if (!username.equals(password)) { 385// throw new LoginFailedException("username=" + username + ", password=" + password); 386// } 387// } 388// }; 389// ret.addPlugin(new PlainAuthenticationHandler(validator)); 390// ret.addPlugin(new LoginAuthenticationHandler(validator)); 391// return ret; 392// } 393// } 394 395}