View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.net;
15  
16  import static org.junit.Assert.assertEquals;
17  import static org.junit.Assert.assertNotNull;
18  import static org.junit.Assert.assertTrue;
19  
20  import java.io.ByteArrayOutputStream;
21  import java.util.List;
22  import java.util.Random;
23  import java.util.concurrent.ThreadPoolExecutor;
24  import java.util.concurrent.TimeUnit;
25  
26  import javax.mail.Part;
27  import javax.mail.internet.MimeMessage;
28  import javax.mail.internet.MimeMultipart;
29  
30  import org.dom4j.io.SAXReader;
31  import org.junit.After;
32  import org.junit.Before;
33  import org.junit.Ignore;
34  import org.junit.Test;
35  import org.subethamail.smtp.auth.EasyAuthenticationHandlerFactory;
36  import org.subethamail.smtp.auth.LoginFailedException;
37  import org.subethamail.smtp.auth.UsernamePasswordValidator;
38  import org.subethamail.wiser.Wiser;
39  import org.subethamail.wiser.WiserMessage;
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.html.XHTMLEntityResolver;
46  import ch.qos.logback.classic.spi.ILoggingEvent;
47  import ch.qos.logback.core.CoreConstants;
48  import ch.qos.logback.core.Layout;
49  import ch.qos.logback.core.util.StatusPrinter;
50  
51  public class SMTPAppender_SubethaSMTPTest {
52      static final String TEST_SUBJECT = "test subject";
53      static final String HEADER = "HEADER\n";
54      static final String FOOTER = "FOOTER\n";
55  
56      static int DIFF = 1024 + new Random().nextInt(30000);
57      static Wiser WISER;
58  
59      SMTPAppender smtpAppender;
60      LoggerContext loggerContext = new LoggerContext();
61  
62      int numberOfOldMessages;
63  
64  //    @BeforeClass
65  //    static public void beforeClass() {
66  //        WISER = new Wiser();
67  //        WISER.setPort(DIFF);
68  //        WISER.start();
69  //    }
70  
71  //    @AfterClass
72  //    static public void afterClass() throws Exception {
73  //        WISER.stop();
74  //    }
75  
76      @Before
77      public void setUp() throws Exception {
78          WISER = new Wiser();
79          WISER.setPort(DIFF);
80          WISER.start();
81          numberOfOldMessages = WISER.getMessages().size();
82          buildSMTPAppender();
83      }
84  
85      @After
86      public void tearDown() {
87          // clear any authentication handler factory
88          //WISER.getServer().setAuthenticationHandlerFactory(null);
89          WISER.stop();
90      }
91  
92      void buildSMTPAppender() throws Exception {
93          smtpAppender = new SMTPAppender();
94          smtpAppender.setContext(loggerContext);
95          smtpAppender.setName("smtp");
96          smtpAppender.setFrom("user@host.dom");
97          smtpAppender.setSMTPHost("localhost");
98          smtpAppender.setSMTPPort(DIFF);
99          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 }