diff --git a/src/java/davmail/AbstractConnection.java b/src/java/davmail/AbstractConnection.java index 5441f8e8..4419840b 100644 --- a/src/java/davmail/AbstractConnection.java +++ b/src/java/davmail/AbstractConnection.java @@ -18,6 +18,7 @@ */ package davmail; +import davmail.exception.DavMailException; import davmail.exchange.ExchangeSession; import davmail.ui.tray.DavGatewayTray; import org.apache.commons.codec.binary.Base64; @@ -35,9 +36,74 @@ public class AbstractConnection extends Thread { INITIAL, LOGIN, USER, PASSWORD, AUTHENTICATED, STARTMAIL, RECIPIENT, MAILDATA } + protected static class LineReaderInputStream extends PushbackInputStream { + final String encoding; + + /** + * @inheritDoc + */ + protected LineReaderInputStream(InputStream in, String encoding) { + super(in); + if (encoding == null) { + this.encoding = "ASCII"; + } else { + this.encoding = encoding; + } + } + + public String readLine() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int b; + while ((b = read()) > -1) { + if (b == '\r') { + int next = read(); + if (next != '\n') { + unread(next); + } + break; + } else if (b == '\n') { + break; + } + baos.write(b); + } + return new String(baos.toByteArray(), encoding); + } + + /** + * Read byteSize bytes from inputStream, return content as String. + * @param byteSize content size + * @return content + * @throws IOException on error + */ + public String readContentAsString(int byteSize) throws IOException { + return new String(readContent(byteSize), encoding); + } + + /** + * Read byteSize bytes from inputStream, return content as byte array. + * @param byteSize content size + * @return content + * @throws IOException on error + */ + public byte[] readContent(int byteSize) throws IOException { + byte[] buffer = new byte[byteSize]; + int startIndex = 0; + int count = 0; + while (count >= 0 && startIndex < byteSize) { + count = in.read(buffer, startIndex, byteSize - startIndex); + startIndex += count; + } + if (startIndex < byteSize) { + throw new DavMailException("EXCEPTION_END_OF_STREAM"); + } + + return buffer; + } + } + protected final Socket client; - protected BufferedReader in; + protected LineReaderInputStream in; protected OutputStream os; // user name and password initialized through connection protected String userName; @@ -70,12 +136,7 @@ public class AbstractConnection extends Thread { super(name + '-' + clientSocket.getPort()); this.client = clientSocket; try { - if (encoding == null) { - //noinspection IOResourceOpenedButNotSafelyClosed - in = new BufferedReader(new InputStreamReader(client.getInputStream())); - } else { - in = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); - } + in = new LineReaderInputStream(client.getInputStream(), encoding); os = new BufferedOutputStream(client.getOutputStream()); } catch (IOException e) { close(); diff --git a/src/java/davmail/caldav/CaldavConnection.java b/src/java/davmail/caldav/CaldavConnection.java index 4251a800..b54b2528 100644 --- a/src/java/davmail/caldav/CaldavConnection.java +++ b/src/java/davmail/caldav/CaldavConnection.java @@ -91,21 +91,7 @@ public class CaldavConnection extends AbstractConnection { } catch (NumberFormatException e) { throw new DavMailException("EXCEPTION_INVALID_CONTENT_LENGTH", contentLength); } - char[] buffer = new char[size]; - StringBuilder builder = new StringBuilder(); - int actualSize = in.read(buffer); - builder.append(buffer, 0, actualSize); - if (actualSize < 0) { - throw new DavMailException("EXCEPTION_END_OF_STREAM"); - } - // dirty hack to ensure full content read - // TODO : replace with a dedicated reader - while (builder.toString().getBytes("UTF-8").length < size) { - actualSize = in.read(buffer); - builder.append(buffer, 0, actualSize); - } - - String content = builder.toString(); + String content = in.readContentAsString(size); if (wireLogger.isDebugEnabled()) { wireLogger.debug("< " + content); } diff --git a/src/java/davmail/exchange/DoubleDotInputStream.java b/src/java/davmail/exchange/DoubleDotInputStream.java new file mode 100644 index 00000000..9c6287f8 --- /dev/null +++ b/src/java/davmail/exchange/DoubleDotInputStream.java @@ -0,0 +1,89 @@ +/* + * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway + * Copyright (C) 2010 Mickael Guessant + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package davmail.exchange; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; + +/** + * Replace double dot lines with single dot in input stream. + * A line with a single dot means end of stream + */ +public class DoubleDotInputStream extends PushbackInputStream { + int[] buffer = new int[4]; + int index = -1; + + /** + * @inheritDoc + */ + public DoubleDotInputStream(InputStream in) { + super(in, 3); + } + + /** + * Push current byte to buffer and read next byte. + * + * @param currentByte current byte + * @return next byte + * @throws IOException on error + */ + protected int readNextByte() throws IOException { + int b = super.read(); + buffer[++index] = b; + return b; + } + + @Override + public int read() throws IOException { + int b = super.read(); + if (b == '\r') { + // \r\n + if (readNextByte() == '\n') { + // \r\n. + if (readNextByte() == '.') { + // \r\n.\r + if (readNextByte() == '\r') { + // \r\n.\r\n + if (readNextByte() == '\n') { + // end of stream + index = -1; + b = -1; + } + // \r\n.. + } else if (buffer[index] == '.') { + // \r\n..\r + if ((readNextByte()) == '\r') { + // replace double dot + buffer[--index] = '\r'; + } + } + } + } + // push back characters + if (index >= 0) { + while(index >= 0) { + unread(buffer[index--]); + } + } + } + return b; + } + +} diff --git a/src/java/davmail/exchange/DoubleDotOutputStream.java b/src/java/davmail/exchange/DoubleDotOutputStream.java new file mode 100644 index 00000000..17b6ea49 --- /dev/null +++ b/src/java/davmail/exchange/DoubleDotOutputStream.java @@ -0,0 +1,59 @@ +/* + * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway + * Copyright (C) 2010 Mickael Guessant + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package davmail.exchange; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Replace single dot lines with double dot. + */ +public class DoubleDotOutputStream extends FilterOutputStream { + enum State { + CR, CRLF, CRLFDOT + } + + State currentState; + + /** + * @inheritDoc + */ + public DoubleDotOutputStream(OutputStream out) { + super(out); + } + + @Override + public void write(int b) throws IOException { + if (currentState == null && b == '\r') { + currentState = State.CR; + } else if (currentState == State.CR && b == '\n') { + currentState = State.CRLF; + } else if (currentState == State.CRLF && b == '.') { + currentState = State.CRLFDOT; + } else if (currentState == State.CRLFDOT && b == '\r') { + out.write('.'); + currentState = null; + } else { + currentState = null; + } + out.write(b); + } + +} diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index cb699309..ff85f26f 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -35,7 +35,9 @@ import org.htmlcleaner.CommentToken; import org.htmlcleaner.HtmlCleaner; import org.htmlcleaner.TagNode; +import javax.mail.Address; import javax.mail.MessagingException; +import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimePart; @@ -473,7 +475,7 @@ public abstract class ExchangeSession { * @param messageBody mail body * @throws IOException when unable to create message */ - public abstract void createMessage(String folderPath, String messageName, HashMap properties, String messageBody) throws IOException; + public abstract void createMessage(String folderPath, String messageName, HashMap properties, byte[] messageBody) throws IOException; /** * Update given properties on message. @@ -496,11 +498,10 @@ public abstract class ExchangeSession { /** * Send message to recipients, properties contains bcc recipients and other non MIME flags. * - * @param properties additional message properties * @param messageBody MIME message body * @throws IOException on error */ - public abstract void sendMessage(HashMap properties, String messageBody) throws IOException; + public abstract void sendMessage(byte[] messageBody) throws IOException; /** * Create message MIME body reader. @@ -906,67 +907,25 @@ public abstract class ExchangeSession { * @param reader message stream * @throws IOException on error */ - public void sendMessage(List recipients, BufferedReader reader) throws IOException { - String line = reader.readLine(); - StringBuilder mailBuffer = new StringBuilder(); - StringBuilder recipientBuffer = new StringBuilder(); - boolean inHeader = true; - boolean inRecipientHeader = false; - while (!".".equals(line)) { - // Exchange 2007 : skip From: header - if ((inHeader && line.length() >= 5)) { - String prefix = line.substring(0, 5).toLowerCase(); - if ("from:".equals(prefix)) { - line = reader.readLine(); - } - } + public void sendMessage(List rcptToRecipients, MimeMessage mimeMessage) throws IOException, MessagingException { + // Exchange 2007 : skip From: header + mimeMessage.removeHeader("from"); - if (inHeader && line.length() == 0) { - inHeader = false; - } - - inRecipientHeader = inRecipientHeader && line.startsWith(" "); - - if ((inHeader && line.length() >= 3) || inRecipientHeader) { - String prefix = line.substring(0, 3).toLowerCase(); - if ("to:".equalsIgnoreCase(prefix) || "cc:".equalsIgnoreCase(prefix) || inRecipientHeader) { - inRecipientHeader = true; - recipientBuffer.append(line); - } - } - String nextLine = reader.readLine(); - mailBuffer.append(line); - if (!".".equals(nextLine)) { - mailBuffer.append((char) 13).append((char) 10); - } - line = nextLine; - } // remove visible recipients from list - List visibleRecipients = new ArrayList(); - for (String recipient : recipients) { - if (recipientBuffer.indexOf(recipient) >= 0) { - visibleRecipients.add(recipient); + Set visibleRecipients = new HashSet(); + Address[] recipients = mimeMessage.getAllRecipients(); + for (Address address:recipients) { + visibleRecipients.add(address.toString()); + } + for (String recipient : rcptToRecipients) { + if (!visibleRecipients.contains(recipient)) { + mimeMessage.addRecipient(javax.mail.Message.RecipientType.BCC, new InternetAddress(recipient)); } } - recipients.removeAll(visibleRecipients); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + mimeMessage.writeTo(baos); - StringBuilder bccBuffer = new StringBuilder(); - for (String recipient : recipients) { - if (bccBuffer.length() > 0) { - bccBuffer.append(','); - } - bccBuffer.append('<'); - bccBuffer.append(recipient); - bccBuffer.append('>'); - } - - String bcc = bccBuffer.toString(); - HashMap properties = new HashMap(); - if (bcc.length() > 0) { - properties.put("bcc", bcc); - } - - sendMessage(properties, mailBuffer.toString()); + sendMessage(baos.toByteArray()); } /** diff --git a/src/java/davmail/exchange/dav/DavExchangeSession.java b/src/java/davmail/exchange/dav/DavExchangeSession.java index c6c73f18..be15fd19 100644 --- a/src/java/davmail/exchange/dav/DavExchangeSession.java +++ b/src/java/davmail/exchange/dav/DavExchangeSession.java @@ -1407,7 +1407,7 @@ public class DavExchangeSession extends ExchangeSession { * @throws IOException when unable to create message */ @Override - public void createMessage(String folderPath, String messageName, HashMap properties, String messageBody) throws IOException { + public void createMessage(String folderPath, String messageName, HashMap properties, byte[] messageBody) throws IOException { String messageUrl = URIUtil.encodePathQuery(getFolderPath(folderPath) + '/' + messageName + ".EML"); PropPatchMethod patchMethod; List davProperties = buildProperties(properties); @@ -1434,7 +1434,7 @@ public class DavExchangeSession extends ExchangeSession { putmethod.setRequestHeader("Translate", "f"); try { // use same encoding as client socket reader - putmethod.setRequestEntity(new ByteArrayRequestEntity(messageBody.getBytes())); + putmethod.setRequestEntity(new ByteArrayRequestEntity(messageBody)); int code = httpClient.executeMethod(putmethod); if (code != HttpStatus.SC_OK && code != HttpStatus.SC_CREATED) { @@ -1495,10 +1495,10 @@ public class DavExchangeSession extends ExchangeSession { * @inheritDoc */ @Override - public void sendMessage(HashMap properties, String messageBody) throws IOException { + public void sendMessage(byte[] messageBody) throws IOException { String messageName = UUID.randomUUID().toString(); - createMessage("Drafts", messageName, properties, messageBody); + createMessage("Drafts", messageName, null, messageBody); String tempUrl = draftsUrl + '/' + messageName + ".EML"; MoveMethod method = new MoveMethod(URIUtil.encodePath(tempUrl), URIUtil.encodePath(sendmsgUrl), true); diff --git a/src/java/davmail/exchange/ews/EwsExchangeSession.java b/src/java/davmail/exchange/ews/EwsExchangeSession.java index 283179ab..f080a4a3 100644 --- a/src/java/davmail/exchange/ews/EwsExchangeSession.java +++ b/src/java/davmail/exchange/ews/EwsExchangeSession.java @@ -162,17 +162,10 @@ public class EwsExchangeSession extends ExchangeSession { } @Override - public void createMessage(String folderPath, String messageName, HashMap properties, String messageBody) throws IOException { + public void createMessage(String folderPath, String messageName, HashMap properties, byte[] messageBody) throws IOException { EWSMethod.Item item = new EWSMethod.Item(); item.type = "Message"; - String bcc = properties.get("bcc"); - if (bcc != null) { - properties.remove("bcc"); - // put bcc header back into mime body, Exchange will handle it on send - item.mimeContent = Base64.encodeBase64(("bcc: "+bcc+ "\r\n" +messageBody).getBytes()); - } else { - item.mimeContent = Base64.encodeBase64(messageBody.getBytes()); - } + item.mimeContent = Base64.encodeBase64(messageBody); Set fieldUpdates = buildProperties(properties); if (!properties.containsKey("draft")) { @@ -204,23 +197,13 @@ public class EwsExchangeSession extends ExchangeSession { } @Override - public void sendMessage(HashMap properties, String messageBody) throws IOException { + public void sendMessage(byte[] messageBody) throws IOException { EWSMethod.Item item = new EWSMethod.Item(); item.type = "Message"; - String bcc = properties.get("bcc"); - if (bcc != null) { - properties.remove("bcc"); - // put bcc header back into mime body, Exchange will handle it on send - item.mimeContent = Base64.encodeBase64(("bcc: "+bcc+ "\r\n" +messageBody).getBytes()); - } else { - item.mimeContent = Base64.encodeBase64(messageBody.getBytes()); - } + item.mimeContent = Base64.encodeBase64(messageBody); - Set fieldUpdates = buildProperties(properties); - item.setFieldUpdates(fieldUpdates); CreateItemMethod createItemMethod = new CreateItemMethod(MessageDisposition.SendAndSaveCopy, getFolderId(SENT), item); executeMethod(createItemMethod); - } /** diff --git a/src/java/davmail/imap/ImapConnection.java b/src/java/davmail/imap/ImapConnection.java index f7f0ba0c..4ea54271 100644 --- a/src/java/davmail/imap/ImapConnection.java +++ b/src/java/davmail/imap/ImapConnection.java @@ -419,20 +419,12 @@ public class ImapConnection extends AbstractConnection { } int size = Integer.parseInt(nextToken); sendClient("+ send literal data"); - char[] buffer = new char[size]; - int index = 0; - int count = 0; - while (count >= 0 && index < size) { - count = in.read(buffer, index, size - index); - if (count >= 0) { - index += count; - } - } + byte[] buffer = in.readContent(size); // empty line readClient(); String messageName = UUID.randomUUID().toString(); - session.createMessage(folderName, messageName, properties, new String(buffer)); + session.createMessage(folderName, messageName, properties, buffer); sendClient(commandId + " OK APPEND completed"); } else if ("idle".equalsIgnoreCase(command) && imapIdleDelay > 0) { sendClient("+ idling "); @@ -441,7 +433,7 @@ public class ImapConnection extends AbstractConnection { DavGatewayTray.resetIcon(); try { int count = 0; - while (!in.ready()) { + while (in.available() == 0) { if (++count >= imapIdleDelay) { count = 0; List previousImapUidList = currentFolder.getImapUidList(); diff --git a/src/java/davmail/smtp/SmtpConnection.java b/src/java/davmail/smtp/SmtpConnection.java index 44172d2a..606f1674 100644 --- a/src/java/davmail/smtp/SmtpConnection.java +++ b/src/java/davmail/smtp/SmtpConnection.java @@ -21,18 +21,22 @@ package davmail.smtp; import davmail.AbstractConnection; import davmail.BundleMessage; import davmail.exception.DavMailException; +import davmail.exchange.DoubleDotInputStream; import davmail.exchange.ExchangeSessionFactory; import davmail.ui.tray.DavGatewayTray; -import javax.mail.internet.InternetAddress; import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import javax.mail.util.SharedByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.Socket; import java.net.SocketException; -import java.util.Date; -import java.util.StringTokenizer; -import java.util.List; import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.StringTokenizer; /** * Dav Gateway smtp connection implementation @@ -48,6 +52,7 @@ public class SmtpConnection extends AbstractConnection { super(SmtpConnection.class.getSimpleName(), clientSocket, null); } + @Override public void run() { String line; @@ -146,7 +151,15 @@ public class SmtpConnection extends AbstractConnection { sendClient("354 Start mail input; end with ."); try { - session.sendMessage(recipients, in); + // read message in buffer + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DoubleDotInputStream doubleDotInputStream = new DoubleDotInputStream(in); + int b; + while ((b = doubleDotInputStream.read()) >= 0) { + baos.write(b); + } + MimeMessage mimeMessage = new MimeMessage(null, new SharedByteArrayInputStream(baos.toByteArray())); + session.sendMessage(recipients, mimeMessage); state = State.AUTHENTICATED; sendClient("250 Queued mail for delivery"); } catch (Exception e) { diff --git a/src/test/davmail/exchange/AbstractExchangeSessionTestCase.java b/src/test/davmail/exchange/AbstractExchangeSessionTestCase.java index cc3a76f3..b8a982ac 100644 --- a/src/test/davmail/exchange/AbstractExchangeSessionTestCase.java +++ b/src/test/davmail/exchange/AbstractExchangeSessionTestCase.java @@ -59,11 +59,10 @@ public class AbstractExchangeSessionTestCase extends AbstractDavMailTestCase { return mimeMessage; } - protected String getMimeBody(MimeMessage mimeMessage) throws IOException, MessagingException { + protected byte[] getMimeBody(MimeMessage mimeMessage) throws IOException, MessagingException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); mimeMessage.writeTo(baos); - byte[] content = baos.toByteArray(); - return new String(content); + return baos.toByteArray(); } } diff --git a/src/test/davmail/exchange/TestDoubleDotInputStream.java b/src/test/davmail/exchange/TestDoubleDotInputStream.java new file mode 100644 index 00000000..2bcc8ceb --- /dev/null +++ b/src/test/davmail/exchange/TestDoubleDotInputStream.java @@ -0,0 +1,67 @@ +/* + * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway + * Copyright (C) 2010 Mickael Guessant + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package davmail.exchange; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Test double dot input stream. + */ +public class TestDoubleDotInputStream extends TestCase { + static final String END_OF_STREAM = "\r\n.\r\n"; + + protected String doubleDotRead(String value) throws IOException { + DoubleDotInputStream doubleDotInputStream = new DoubleDotInputStream(new ByteArrayInputStream(value.getBytes())); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int b; + while ((b = doubleDotInputStream.read()) != -1) { + baos.write(b); + } + return new String(baos.toByteArray()); + } + + public void testSimple() throws IOException { + String value = "simple test"; + assertEquals(value, doubleDotRead(value + END_OF_STREAM)); + } + + public void testNoEof() throws IOException { + String value = "simple test"; + assertEquals(value, doubleDotRead(value)); + } + + public void testMultiLine() throws IOException { + String value = "simple test\r\nsecond line"; + assertEquals(value, doubleDotRead(value+END_OF_STREAM)); + } + + public void testDoubleDot() throws IOException { + String value = "simple test\r\n..\r\nsecond line"; + assertEquals(value.replaceAll("\\.\\.", "."), doubleDotRead(value+END_OF_STREAM)); + } + public void testDoubleDotEnd() throws IOException { + String value = "simple test\r\n.."; + assertEquals(value.replaceAll("\\.\\.", "."), doubleDotRead(value+END_OF_STREAM)); + assertEquals("..", doubleDotRead(".."+END_OF_STREAM)); + } +} diff --git a/src/test/davmail/smtp/TestSmtp.java b/src/test/davmail/smtp/TestSmtp.java index 6da164f8..7dc99838 100644 --- a/src/test/davmail/smtp/TestSmtp.java +++ b/src/test/davmail/smtp/TestSmtp.java @@ -21,6 +21,7 @@ package davmail.smtp; import davmail.AbstractDavMailTestCase; import davmail.DavGateway; import davmail.Settings; +import davmail.exchange.DoubleDotOutputStream; import davmail.exchange.ExchangeSession; import davmail.exchange.ExchangeSessionFactory; import org.apache.commons.codec.binary.Base64; @@ -78,14 +79,6 @@ public class TestSmtp extends AbstractDavMailTestCase { return new String(baos.toByteArray(), "ASCII"); } - protected String readFullAnswer(String prefix) throws IOException { - String line = readLine(); - while (!line.startsWith(prefix)) { - line = readLine(); - } - return line; - } - @Override public void setUp() throws IOException { super.setUp(); @@ -95,43 +88,26 @@ public class TestSmtp extends AbstractDavMailTestCase { clientSocket = new Socket("localhost", Settings.getIntProperty("davmail.smtpPort")); socketOutputStream = new BufferedOutputStream(clientSocket.getOutputStream()); socketInputStream = new BufferedInputStream(clientSocket.getInputStream()); + + String banner = readLine(); + assertNotNull(banner); + String credentials = (char) 0+Settings.getProperty("davmail.username")+ (char) 0 +Settings.getProperty("davmail.password"); + writeLine("AUTH PLAIN " + new String(new Base64().encode(credentials.getBytes()))); + assertEquals("235 OK Authenticated", readLine()); } if (session == null) { session = ExchangeSessionFactory.getInstance(Settings.getProperty("davmail.username"), Settings.getProperty("davmail.password")); } } - public void testBanner() throws IOException { - String banner = readLine(); - assertNotNull(banner); - } - - public void testLogin() throws IOException { - String credentials = (char) 0+Settings.getProperty("davmail.username")+ (char) 0 +Settings.getProperty("davmail.password"); - writeLine("AUTH PLAIN " + new String(new Base64().encode(credentials.getBytes()))); - assertEquals("235 OK Authenticated", readLine()); - } - - - public void testSendMessage() throws IOException, MessagingException, InterruptedException { - String body = "Test message\r\n" + - "Special characters: éèçà\r\n" + - "Chinese: "+((char)0x604F)+((char)0x7D59); - MimeMessage mimeMessage = new MimeMessage((Session) null); - mimeMessage.addHeader("To", Settings.getProperty("davmail.to")); - mimeMessage.setText(body, "UTF-8"); - //mimeMessage.setHeader("Content-Transfer-Encoding", "8bit"); - mimeMessage.setSubject("Test subject"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - mimeMessage.writeTo(baos); - byte[] content = baos.toByteArray(); + public void sendAndCheckMessage(MimeMessage mimeMessage) throws IOException, MessagingException, InterruptedException { writeLine("MAIL FROM:"+session.getEmail()); readLine(); writeLine("RCPT TO:"+Settings.getProperty("davmail.to")); readLine(); writeLine("DATA"); assertEquals("354 Start mail input; end with .", readLine()); - mimeMessage.writeTo(socketOutputStream); + mimeMessage.writeTo(new DoubleDotOutputStream(socketOutputStream)); writeLine(""); writeLine("."); assertEquals("250 Queued mail for delivery", readLine()); @@ -141,29 +117,35 @@ public class TestSmtp extends AbstractDavMailTestCase { assertEquals(1, messages.size()); ExchangeSession.Message message = messages.get(0); message.getMimeMessage().writeTo(System.out); - assertEquals(body, (String) message.getMimeMessage().getDataHandler().getContent()); - + assertEquals(mimeMessage.getDataHandler().getContent(), (String) message.getMimeMessage().getDataHandler().getContent()); } - public void testBccMessage() throws IOException, MessagingException { + public void testSendMessage() throws IOException, MessagingException, InterruptedException { + String body = "Test message\r\n" + + "Special characters: éèçà\r\n" + + "Chinese: "+((char)0x604F)+((char)0x7D59); + MimeMessage mimeMessage = new MimeMessage((Session) null); + mimeMessage.addHeader("To", Settings.getProperty("davmail.to")); + mimeMessage.setSubject("Test subject"); + mimeMessage.setText(body, "UTF-8"); + sendAndCheckMessage(mimeMessage); + } + + public void testBccMessage() throws IOException, MessagingException, InterruptedException { MimeMessage mimeMessage = new MimeMessage((Session) null); mimeMessage.addHeader("to", Settings.getProperty("davmail.to")); - mimeMessage.setText("Test message"); mimeMessage.setSubject("Test subject"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - mimeMessage.writeTo(baos); - byte[] content = baos.toByteArray(); - writeLine("MAIL FROM:"+session.getEmail()); - readLine(); - writeLine("RCPT TO:"+Settings.getProperty("davmail.to")); - readLine(); - writeLine("RCPT TO:"+Settings.getProperty("davmail.bcc")); - readLine(); - writeLine("DATA"); - assertEquals("354 Start mail input; end with .", readLine()); - writeLine(new String(content)); - writeLine("."); - assertEquals("250 Queued mail for delivery", readLine()); + mimeMessage.setText("Test message"); + sendAndCheckMessage(mimeMessage); + } + + public void testDotMessage() throws IOException, MessagingException, InterruptedException { + String body = "First line\r\n.\r\nSecond line"; + MimeMessage mimeMessage = new MimeMessage((Session) null); + mimeMessage.addHeader("to", Settings.getProperty("davmail.to")); + mimeMessage.setSubject("Test subject"); + mimeMessage.setText(body); + sendAndCheckMessage(mimeMessage); } public void testQuit() throws IOException {