diff --git a/src/com/fsck/k9/mail/filter/EOLConvertingOutputStream.java b/src/com/fsck/k9/mail/filter/EOLConvertingOutputStream.java index ea89c3083..c19780a36 100644 --- a/src/com/fsck/k9/mail/filter/EOLConvertingOutputStream.java +++ b/src/com/fsck/k9/mail/filter/EOLConvertingOutputStream.java @@ -5,8 +5,11 @@ import java.io.IOException; import java.io.OutputStream; public class EOLConvertingOutputStream extends FilterOutputStream { + private static final int CR = '\r'; + private static final int LF = '\n'; private int lastChar; - private boolean ignoreNextIfLF = false; + private static final int IGNORE_LF = Integer.MIN_VALUE; + public EOLConvertingOutputStream(OutputStream out) { super(out); @@ -14,26 +17,27 @@ public class EOLConvertingOutputStream extends FilterOutputStream { @Override public void write(int oneByte) throws IOException { - if (!ignoreNextIfLF) { - if ((oneByte == '\n') && (lastChar != '\r')) { - super.write('\r'); - } - super.write(oneByte); - lastChar = oneByte; + if (oneByte == LF && lastChar == IGNORE_LF) { + lastChar = LF; + return; } - ignoreNextIfLF = false; + if (oneByte == LF && lastChar != CR) { + super.write(CR); + } else if (oneByte != LF && lastChar == CR) { + super.write(LF); + } + super.write(oneByte); + lastChar = oneByte; } @Override public void flush() throws IOException { - if (lastChar == '\r') { - super.write('\n'); - lastChar = '\n'; - + if (lastChar == CR) { + super.write(LF); // We have to ignore the next character if it is . Otherwise it // will be expanded to an additional sequence although it // belongs to the one just completed. - ignoreNextIfLF = true; + lastChar = IGNORE_LF; } super.flush(); } diff --git a/tests-on-jvm/src/com/fsck/k9/mail/filter/EOLConvertingOutputStreamTest.java b/tests-on-jvm/src/com/fsck/k9/mail/filter/EOLConvertingOutputStreamTest.java new file mode 100644 index 000000000..ebb9dac64 --- /dev/null +++ b/tests-on-jvm/src/com/fsck/k9/mail/filter/EOLConvertingOutputStreamTest.java @@ -0,0 +1,69 @@ +package com.fsck.k9.mail.filter; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; + +public class EOLConvertingOutputStreamTest extends TestCase { + private EOLConvertingOutputStream subject; + private ByteArrayOutputStream out; + + @Override + public void setUp() throws Exception { + super.setUp(); + out = new ByteArrayOutputStream(); + subject = new EOLConvertingOutputStream(out); + } + + public void testFilterWithoutCRorLF() throws Exception { + subject.write("Unchanged".getBytes()); + subject.flush(); + assertEquals("Unchanged", out.toString()); + } + + public void testFilterWithCRLF() throws Exception { + subject.write("Filter\r\nNext Line".getBytes()); + subject.flush(); + assertEquals("Filter\r\nNext Line", out.toString()); + } + + public void testFilterWithJustCR() throws Exception { + subject.write("\n\n\n".getBytes()); + subject.flush(); + assertEquals("\r\n\r\n\r\n", out.toString()); + } + + public void testFilterWithCR() throws Exception { + subject.write("Filter\rNext Line".getBytes()); + subject.flush(); + assertEquals("Filter\r\nNext Line", out.toString()); + } + + public void testFilterWithLF() throws Exception { + subject.write("Filter\nNext Line".getBytes()); + subject.flush(); + assertEquals("Filter\r\nNext Line", out.toString()); + } + + public void testFlushWithCR() throws Exception { + subject.write("Flush\r".getBytes()); + subject.flush(); + assertEquals("Flush\r\n", out.toString()); + subject.write("\n\n\n".getBytes()); + assertEquals("Flush\r\n\r\n\r\n", out.toString()); + } + + public void testFlushWithCRNotFollowedByLF() throws Exception { + subject.write("Flush\r".getBytes()); + subject.flush(); + subject.write("Next line".getBytes()); + assertEquals("Flush\r\nNext line", out.toString()); + } + + public void testFlushWithLF() throws Exception { + subject.write("Flush\n".getBytes()); + subject.flush(); + subject.write("\n".getBytes()); + assertEquals("Flush\r\n\r\n", out.toString()); + } +}