Merge pull request #6030 from k9mail/fix_format_flowed
Fix bug in `FlowedMessageUtils.deflow()`
This commit is contained in:
commit
77227b3290
2 changed files with 239 additions and 19 deletions
|
@ -45,39 +45,54 @@ public final class FlowedMessageUtils {
|
|||
String line = i < lines.length ? lines[i] : null;
|
||||
int actualQuoteDepth = 0;
|
||||
|
||||
if (line != null && line.length() > 0) {
|
||||
if (line.equals(RFC2646_SIGNATURE))
|
||||
if (line != null) {
|
||||
if (line.equals(RFC2646_SIGNATURE)) {
|
||||
// signature handling (the previous line is not flowed)
|
||||
resultLineFlowed = false;
|
||||
|
||||
else if (line.charAt(0) == RFC2646_QUOTE) {
|
||||
} else if (line.length() > 0 && line.charAt(0) == RFC2646_QUOTE) {
|
||||
// Quote
|
||||
actualQuoteDepth = 1;
|
||||
while (actualQuoteDepth < line.length() && line.charAt(actualQuoteDepth) == RFC2646_QUOTE) actualQuoteDepth ++;
|
||||
while (actualQuoteDepth < line.length() && line.charAt(actualQuoteDepth) == RFC2646_QUOTE) {
|
||||
actualQuoteDepth++;
|
||||
}
|
||||
// if quote-depth changes wrt the previous line then this is not flowed
|
||||
if (resultLineQuoteDepth != actualQuoteDepth) resultLineFlowed = false;
|
||||
if (resultLineQuoteDepth != actualQuoteDepth) {
|
||||
resultLineFlowed = false;
|
||||
}
|
||||
line = line.substring(actualQuoteDepth);
|
||||
|
||||
} else {
|
||||
// id quote-depth changes wrt the first line then this is not flowed
|
||||
if (resultLineQuoteDepth > 0) resultLineFlowed = false;
|
||||
// if quote-depth changes wrt the first line then this is not flowed
|
||||
if (resultLineQuoteDepth > 0) {
|
||||
resultLineFlowed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (line.length() > 0 && line.charAt(0) == RFC2646_SPACE)
|
||||
if (line.length() > 0 && line.charAt(0) == RFC2646_SPACE) {
|
||||
// Line space-stuffed
|
||||
line = line.substring(1);
|
||||
}
|
||||
|
||||
// if the previous was the last then it was not flowed
|
||||
} else if (line == null) resultLineFlowed = false;
|
||||
} else {
|
||||
resultLineFlowed = false;
|
||||
}
|
||||
|
||||
// Add the PREVIOUS line.
|
||||
// This often will find the flow looking for a space as the last char of the line.
|
||||
// With quote changes or signatures it could be the followinf line to void the flow.
|
||||
// With quote changes or signatures it could be the following line to void the flow.
|
||||
if (!resultLineFlowed && i > 0) {
|
||||
if (resultLineQuoteDepth > 0) resultLine.insert(0, RFC2646_SPACE);
|
||||
for (int j = 0; j < resultLineQuoteDepth; j++) resultLine.insert(0, RFC2646_QUOTE);
|
||||
if (result == null) result = new StringBuffer();
|
||||
else result.append(RFC2646_CRLF);
|
||||
if (resultLineQuoteDepth > 0) {
|
||||
resultLine.insert(0, RFC2646_SPACE);
|
||||
}
|
||||
for (int j = 0; j < resultLineQuoteDepth; j++) {
|
||||
resultLine.insert(0, RFC2646_QUOTE);
|
||||
}
|
||||
if (result == null) {
|
||||
result = new StringBuffer();
|
||||
} else {
|
||||
result.append(RFC2646_CRLF);
|
||||
}
|
||||
result.append(resultLine.toString());
|
||||
resultLine = new StringBuffer();
|
||||
resultLineFlowed = false;
|
||||
|
@ -86,13 +101,16 @@ public final class FlowedMessageUtils {
|
|||
|
||||
if (line != null) {
|
||||
if (!line.equals(RFC2646_SIGNATURE) && line.endsWith("" + RFC2646_SPACE) && i < lines.length - 1) {
|
||||
// Line flowed (NOTE: for the split operation the line having i == lines.length is the last that does not end with RFC2646_CRLF)
|
||||
if (delSp) line = line.substring(0, line.length() - 1);
|
||||
// Line flowed (NOTE: for the split operation the line having i == lines.length is the last that
|
||||
// does not end with RFC2646_CRLF)
|
||||
if (delSp) {
|
||||
line = line.substring(0, line.length() - 1);
|
||||
}
|
||||
resultLineFlowed = true;
|
||||
} else {
|
||||
resultLineFlowed = false;
|
||||
}
|
||||
|
||||
else resultLineFlowed = false;
|
||||
|
||||
resultLine.append(line);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
package com.fsck.k9.mail.internet
|
||||
|
||||
import com.fsck.k9.mail.crlf
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
|
||||
private const val DEL_SP_NO = false
|
||||
private const val DEL_SP_YES = true
|
||||
|
||||
class FlowedMessageUtilsTest {
|
||||
@Test
|
||||
fun `deflow() with simple text`() {
|
||||
val input = "Text that should be \r\n" +
|
||||
"displayed on one line"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo("Text that should be displayed on one line")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with only some lines ending in space`() {
|
||||
val input = "Text that \r\n" +
|
||||
"should be \r\n" +
|
||||
"displayed on \r\n" +
|
||||
"one line.\r\n" +
|
||||
"Text that should retain\r\n" +
|
||||
"its line break."
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
Text that should be displayed on one line.
|
||||
Text that should retain
|
||||
its line break.
|
||||
""".trimIndent().crlf()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with nothing to do`() {
|
||||
val input = "Line one\r\nLine two\r\n"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo(input)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with quoted text`() {
|
||||
val input = "On [date], [user] wrote:\r\n" +
|
||||
"> Text that should be displayed \r\n" +
|
||||
"> on one line\r\n" +
|
||||
"\r\n" +
|
||||
"Some more text that should be \r\n" +
|
||||
"displayed on one line.\r\n"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
|On [date], [user] wrote:
|
||||
|> Text that should be displayed on one line
|
||||
|
|
||||
|Some more text that should be displayed on one line.
|
||||
|
|
||||
""".trimMargin().crlf()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with quoted text ending in space`() {
|
||||
val input = "> Quoted text \r\n" +
|
||||
"Some other text"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo("> Quoted text \r\nSome other text")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with quoted text ending in space before quoted text of different quoting depth`() {
|
||||
val input = ">> Depth 2 \r\n" +
|
||||
"> Depth 1 \r\n" +
|
||||
"> is here\r\n" +
|
||||
"Some other text"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
>> Depth 2${" "}
|
||||
> Depth 1 is here
|
||||
Some other text
|
||||
""".trimIndent().crlf()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with quoted text ending in space followed by empty line`() {
|
||||
val input = "> Quoted \r\n" +
|
||||
"\r\n" +
|
||||
"Text"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo(input)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with delSp=true`() {
|
||||
val input = "Text that is wrapped mid wo \r\nrd"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_YES)
|
||||
|
||||
assertThat(result).isEqualTo("Text that is wrapped mid word")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with quoted text and space-stuffing and delSp=true`() {
|
||||
val input = "> Quoted te \r\n" +
|
||||
"> xt"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_YES)
|
||||
|
||||
assertThat(result).isEqualTo("> Quoted text")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with space-stuffed second line`() {
|
||||
val input = "Text that should be \r\n" +
|
||||
" displayed on one line"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo("Text that should be displayed on one line")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with only space-stuffing`() {
|
||||
val input = "Line 1\r\n" +
|
||||
" Line 2\r\n" +
|
||||
" Line 3\r\n"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo("Line 1\r\nLine 2\r\nLine 3\r\n")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with quoted space-stuffed second line`() {
|
||||
val input = "> Text that should be \r\n" +
|
||||
"> displayed on one line"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo("> Text that should be displayed on one line")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with text containing signature`() {
|
||||
val input = "Text that should be \r\n" +
|
||||
"displayed on one line.\r\n" +
|
||||
"\r\n" +
|
||||
"-- \r\n" +
|
||||
"Signature \r\n" +
|
||||
"text"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
Text that should be displayed on one line.
|
||||
|
||||
--${" "}
|
||||
Signature text
|
||||
""".trimIndent().crlf()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deflow() with quoted text containing signature`() {
|
||||
val input = "> Text that should be \r\n" +
|
||||
"> displayed on one line.\r\n" +
|
||||
"> \r\n" +
|
||||
"> -- \r\n" +
|
||||
"> Signature \r\n" +
|
||||
"> text"
|
||||
|
||||
val result = FlowedMessageUtils.deflow(input, DEL_SP_NO)
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
> Text that should be displayed on one line.
|
||||
>${" "}
|
||||
> --${" "}
|
||||
> Signature text
|
||||
""".trimIndent().crlf()
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue