001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.mail;
018    
019    import java.io.BufferedOutputStream;
020    import java.io.ByteArrayOutputStream;
021    import java.io.File;
022    import java.io.FileWriter;
023    import java.io.IOException;
024    import java.util.Date;
025    import java.util.Enumeration;
026    import java.util.List;
027    
028    import javax.activation.DataHandler;
029    import javax.mail.Header;
030    import javax.mail.MessagingException;
031    import javax.mail.Multipart;
032    import javax.mail.internet.InternetAddress;
033    import javax.mail.internet.MimeMessage;
034    
035    import junit.framework.TestCase;
036    
037    import org.apache.commons.mail.settings.EmailConfiguration;
038    import org.subethamail.wiser.Wiser;
039    import org.subethamail.wiser.WiserMessage;
040    
041    
042    
043    /**
044     * Base test case for Email test classes
045     *
046     * @since 1.0
047     * @author <a href="mailto:corey.scott@gmail.com">Corey Scott</a>
048     * @author <a href="mailto:epugh@opensourceconnections.com">Eric Pugh</a>
049     * @version $Id: BaseEmailTestCase.java 544629 2007-06-05 20:55:13Z bspeakmon $
050     */
051    
052    public class BaseEmailTestCase extends TestCase
053    {
054        /** Padding at end of body added by wiser/send */
055        public static final int BODY_END_PAD = 3;
056        /** Padding at start of body added by wiser/send */
057        public static final int BODY_START_PAD = 2;
058    
059        /** Line separator in email messages */
060        private static final String LINE_SEPARATOR = "\r\n";
061    
062        /** default port */
063        private static int mailServerPort = EmailConfiguration.MAIL_SERVER_PORT;
064    
065        /** The fake Wiser email server */
066        protected Wiser fakeMailServer;
067    
068        /** Mail server used for testing */
069        protected String strTestMailServer = EmailConfiguration.MAIL_SERVER;
070        /** From address for the test email */
071        protected String strTestMailFrom = EmailConfiguration.TEST_FROM;
072        /** Destination address for the test email */
073        protected String strTestMailTo = EmailConfiguration.TEST_TO;
074        /** Mailserver username (set if needed) */
075        protected String strTestUser = EmailConfiguration.TEST_USER;
076        /** Mailserver strTestPasswd (set if needed) */
077        protected String strTestPasswd = EmailConfiguration.TEST_PASSWD;
078        /** URL to used to test URL attachmetns (Must be valid) */
079        protected String strTestURL = EmailConfiguration.TEST_URL;
080    
081        /** Test characters acceptable to email */
082        protected String[] testCharsValid =
083        {
084                " ",
085                "a",
086                "A",
087                "\uc5ec",
088                "0123456789",
089                "012345678901234567890",
090                "\n"
091        };
092    
093        /** Array of test strings */
094        protected String[] testCharsNotValid = {"", null};
095    
096        /** Where to save email output **/
097        private File emailOutputDir;
098    
099        /**
100         * @param name name
101         */
102        public BaseEmailTestCase(String name)
103        {
104            super(name);
105            emailOutputDir = new File("target/test-emails");
106            if (!emailOutputDir.exists())
107            {
108                emailOutputDir.mkdirs();
109            }
110        }
111    
112        /** */
113        protected void tearDown()
114        {
115            //stop the fake email server (if started)
116            if (this.fakeMailServer != null && !isMailServerStopped(fakeMailServer))
117            {
118                this.fakeMailServer.stop();
119                assertTrue("Mail server didn't stop", isMailServerStopped(fakeMailServer));
120            }
121    
122            this.fakeMailServer = null;
123        }
124    
125        /**
126         * Gets the mail server port.
127         * @return the port the server is running on.
128         */
129        protected int getMailServerPort()
130        {
131            return mailServerPort;
132        }
133    
134        /**
135         *
136         * @param email email
137         * @throws IOException Exception
138         */
139        protected void saveEmailToFile(WiserMessage email) throws IOException
140        {
141            File emailFile =
142                new File(emailOutputDir, "email" + new Date().getTime() + ".txt");
143            FileWriter fw = new FileWriter(emailFile);
144            fw.write(email.toString());
145            fw.close();
146        }
147    
148        /**
149         * @param intMsgNo the message to retrieve
150         * @return message as string
151         */
152        public String getMessageAsString(int intMsgNo)
153        {
154            List receivedMessages = fakeMailServer.getMessages();
155            assertTrue("mail server didn't get enough messages", receivedMessages.size() >= intMsgNo);
156    
157            WiserMessage emailMessage = (WiserMessage) receivedMessages.get(intMsgNo);
158    
159            if (emailMessage != null)
160            {
161                try
162                {
163                    return serializeEmailMessage(emailMessage);
164                }
165                catch (Exception e)
166                {
167                    // ignore, since the test will fail on an empty string return
168                }
169            }
170            fail("Message not found");
171            return "";
172        }
173    
174        /**
175         * Initializes the stub mail server. Fails if the server cannot be proven
176         * to have started. If the server is already started, this method returns
177         * without changing the state of the server.
178         */
179        public void getMailServer()
180        {
181            if (this.fakeMailServer == null || isMailServerStopped(fakeMailServer))
182            {
183                mailServerPort++;
184    
185                this.fakeMailServer = new Wiser();
186                this.fakeMailServer.setPort(getMailServerPort());
187                this.fakeMailServer.start();
188    
189                assertFalse("fake mail server didn't start", isMailServerStopped(fakeMailServer));
190    
191                Date dtStartWait = new Date();
192                while (isMailServerStopped(fakeMailServer))
193                {
194                    // test for connected
195                    if (this.fakeMailServer != null
196                        && !isMailServerStopped(fakeMailServer))
197                    {
198                        break;
199                    }
200    
201                    // test for timeout
202                    if ((dtStartWait.getTime() + EmailConfiguration.TIME_OUT)
203                        <= new Date().getTime())
204                    {
205                        fail("Mail server failed to start");
206                    }
207                }
208            }
209        }
210    
211        /**
212         * Validate the message was sent properly
213         * @param mailServer reference to the fake mail server
214         * @param strSubject expected subject
215         * @param fromAdd expected from address
216         * @param toAdd list of expected to addresses
217         * @param ccAdd list of expected cc addresses
218         * @param bccAdd list of expected bcc addresses
219         * @param boolSaveToFile true will output to file, false doesnt
220         * @return WiserMessage email to check
221         * @throws IOException Exception
222         */
223        protected WiserMessage validateSend(
224            Wiser mailServer,
225            String strSubject,
226            InternetAddress fromAdd,
227            List toAdd,
228            List ccAdd,
229            List bccAdd,
230            boolean boolSaveToFile)
231            throws IOException
232        {
233            assertTrue("mail server doesn't contain expected message",
234                    mailServer.getMessages().size() == 1);
235            WiserMessage emailMessage = (WiserMessage) mailServer.getMessages().get(0);
236    
237            if (boolSaveToFile)
238            {
239                this.saveEmailToFile(emailMessage);
240            }
241    
242            try
243            {
244                // get the MimeMessage
245                MimeMessage mimeMessage = emailMessage.getMimeMessage();
246    
247                // test subject
248                assertEquals("got wrong subject from mail",
249                        strSubject, mimeMessage.getHeader("Subject", null));
250    
251                //test from address
252                assertEquals("got wrong From: address from mail",
253                        fromAdd.toString(), mimeMessage.getHeader("From", null));
254    
255                //test to address
256                assertTrue("got wrong To: address from mail",
257                    toAdd.toString().indexOf(mimeMessage.getHeader("To", null)) != -1);
258    
259                //test cc address
260                if (ccAdd.size() > 0)
261                {
262                    assertTrue("got wrong Cc: address from mail",
263                        ccAdd.toString().indexOf(mimeMessage.getHeader("Cc", null))
264                            != -1);
265                }
266    
267                //test bcc address
268                if (bccAdd.size() > 0)
269                {
270                    assertTrue("got wrong Bcc: address from mail",
271                        bccAdd.toString().indexOf(mimeMessage.getHeader("Bcc", null))
272                            != -1);
273                }
274            }
275            catch (MessagingException me)
276            {
277                IllegalStateException ise =
278                    new IllegalStateException("caught MessagingException in validateSend()");
279                ise.initCause(me);
280                throw ise;
281            }
282    
283            return emailMessage;
284        }
285    
286        /**
287         * Validate the message was sent properly
288         * @param mailServer reference to the fake mail server
289         * @param strSubject expected subject
290         * @param content the expected message content
291         * @param fromAdd expected from address
292         * @param toAdd list of expected to addresses
293         * @param ccAdd list of expected cc addresses
294         * @param bccAdd list of expected bcc addresses
295         * @param boolSaveToFile true will output to file, false doesnt
296         * @throws IOException Exception
297         */
298        protected void validateSend(
299            Wiser mailServer,
300            String strSubject,
301            Multipart content,
302            InternetAddress fromAdd,
303            List toAdd,
304            List ccAdd,
305            List bccAdd,
306            boolean boolSaveToFile)
307            throws IOException
308        {
309            // test other properties
310            WiserMessage emailMessage = this.validateSend(
311                mailServer,
312                strSubject,
313                fromAdd,
314                toAdd,
315                ccAdd,
316                bccAdd,
317                boolSaveToFile);
318    
319            // test message content
320    
321            // get sent email content
322            String strSentContent =
323                content.getContentType();
324            // get received email content (chop off the auto-added \n
325            // and -- (front and end)
326            String emailMessageBody = getMessageBody(emailMessage);
327            String strMessageBody =
328                emailMessageBody.substring(BaseEmailTestCase.BODY_START_PAD,
329                                           emailMessageBody.length()
330                                           - BaseEmailTestCase.BODY_END_PAD);
331            assertTrue("didn't find expected content type in message body",
332                    strMessageBody.indexOf(strSentContent) != -1);
333        }
334    
335        /**
336         * Validate the message was sent properly
337         * @param mailServer reference to the fake mail server
338         * @param strSubject expected subject
339         * @param strMessage the expected message as a string
340         * @param fromAdd expected from address
341         * @param toAdd list of expected to addresses
342         * @param ccAdd list of expected cc addresses
343         * @param bccAdd list of expected bcc addresses
344         * @param boolSaveToFile true will output to file, false doesnt
345         * @throws IOException Exception
346         */
347        protected void validateSend(
348            Wiser mailServer,
349            String strSubject,
350            String strMessage,
351            InternetAddress fromAdd,
352            List toAdd,
353            List ccAdd,
354            List bccAdd,
355            boolean boolSaveToFile)
356            throws IOException
357        {
358            // test other properties
359            WiserMessage emailMessage = this.validateSend(
360                mailServer,
361                strSubject,
362                fromAdd,
363                toAdd,
364                ccAdd,
365                bccAdd,
366                true);
367    
368            // test message content
369            assertTrue("didn't find expected message content in message body",
370                    getMessageBody(emailMessage).indexOf(strMessage) != -1);
371        }
372    
373        /**
374         * Serializes the {@link MimeMessage} from the <code>WiserMessage</code>
375         * passed in. The headers are serialized first followed by the message
376         * body.
377         *
378         * @param email The <code>WiserMessage</code> to serialize.
379         * @return The string format of the message.
380         * @throws MessagingException
381         * @throws IOException
382         *             Thrown while serializing the body from
383         *             {@link DataHandler#writeTo(java.io.OutputStream)}.
384         * @throws MessagingException
385         *             Thrown while getting the body content from
386         *             {@link MimeMessage#getDataHandler()}
387         * @since 1.1
388         */
389        private String serializeEmailMessage(WiserMessage wiserMessage)
390                throws MessagingException, IOException
391        {
392            if (wiserMessage == null)
393            {
394                return "";
395            }
396    
397            StringBuffer serializedEmail = new StringBuffer();
398            MimeMessage message = wiserMessage.getMimeMessage();
399    
400            // Serialize the headers
401            for (Enumeration headers = message.getAllHeaders(); headers
402                    .hasMoreElements();)
403            {
404                Header header = (Header) headers.nextElement();
405                serializedEmail.append(header.getName());
406                serializedEmail.append(": ");
407                serializedEmail.append(header.getValue());
408                serializedEmail.append(LINE_SEPARATOR);
409            }
410    
411            // Serialize the body
412            byte[] messageBody = getMessageBodyBytes(message);
413    
414            serializedEmail.append(LINE_SEPARATOR);
415            serializedEmail.append(messageBody);
416            serializedEmail.append(LINE_SEPARATOR);
417    
418            return serializedEmail.toString();
419        }
420    
421        /**
422         * Returns a string representation of the message body. If the message body
423         * cannot be read, an empty string is returned.
424         *
425         * @param wiserMessage The wiser message from which to extract the message body
426         * @return The string representation of the message body
427         * @throws IOException
428         *             Thrown while serializing the body from
429         *             {@link DataHandler#writeTo(java.io.OutputStream)}.
430         * @since 1.1
431         */
432        private String getMessageBody(WiserMessage wiserMessage)
433                throws IOException
434        {
435            if (wiserMessage == null)
436            {
437                return "";
438            }
439    
440            byte[] messageBody = null;
441    
442            try
443            {
444                MimeMessage message = wiserMessage.getMimeMessage();
445                messageBody = getMessageBodyBytes(message);
446            }
447            catch (MessagingException me)
448            {
449                // Thrown while getting the body content from
450                // {@link MimeMessage#getDataHandler()}
451                IllegalStateException ise =
452                    new IllegalStateException("couldn't process MimeMessage from WiserMessage in getMessageBody()");
453                ise.initCause(me);
454                throw ise;
455            }
456    
457            return (messageBody != null) ? (new String(messageBody).intern()) : "";
458        }
459    
460        /**
461         * Gets the byte making up the body of the message.
462         *
463         * @param mimeMessage
464         *            The mime message from which to extract the body.
465         * @return A byte array representing the message body
466         * @throws IOException
467         *             Thrown while serializing the body from
468         *             {@link DataHandler#writeTo(java.io.OutputStream)}.
469         * @throws MessagingException
470         *             Thrown while getting the body content from
471         *             {@link MimeMessage#getDataHandler()}
472         * @since 1.1
473         */
474        private byte[] getMessageBodyBytes(MimeMessage mimeMessage)
475                throws IOException, MessagingException
476        {
477            DataHandler dataHandler = mimeMessage.getDataHandler();
478            ByteArrayOutputStream byteArrayOutStream = new ByteArrayOutputStream();
479            BufferedOutputStream buffOs = new BufferedOutputStream(
480                    byteArrayOutStream);
481            dataHandler.writeTo(buffOs);
482            buffOs.flush();
483    
484            return byteArrayOutStream.toByteArray();
485        }
486    
487        /**
488         * Checks if an email server is running at the address stored in the
489         * <code>fakeMailServer</code>.
490         *
491         * @param fakeMailServer
492         *            The server from which the address is picked up.
493         * @return <code>true</code> if the server claims to be running
494         * @since 1.1
495         */
496        protected boolean isMailServerStopped(Wiser fakeMailServer) {
497            return !fakeMailServer.getServer().isRunning();
498        }
499    }