git clone of logicmail with some fixes/features added
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Added handling for line-breaks in IMAP quotable sections (refs #349)

git-svn-id: https://logicmail.svn.sourceforge.net/svnroot/logicmail/trunk@923 5c734088-3d25-0410-9155-b3c3832efda5

octorian ea3cbed4 32d5226a

+116 -9
+2
LogicMail/src/org/logicprobe/LogicMail/mail/imap/ImapParser.java
··· 625 625 } 626 626 627 627 i += 2; 628 + } else if (ch == (byte)'\r' || ch == (byte)'\n') { 629 + i++; 628 630 } else if (ch == '"') { 629 631 result.addElement(buf.toArray()); 630 632 i++;
+50 -9
LogicMail/src/org/logicprobe/LogicMail/mail/imap/ImapResponseLineTester.java
··· 35 35 36 36 /** 37 37 * Tests received data from a Connection for complete lines, making special 38 - * exception to ignore line breaks that are part of the IMAP "literal" format. 38 + * exception to ignore line breaks that are part of the IMAP "literal" format, 39 + * or that erroneously appear in the middle of the IMAP "quoted" format. 39 40 */ 40 41 public class ImapResponseLineTester extends ConnectionResponseTester { 41 - private static final byte CR = (byte) 0x0D; 42 - private static final byte LF = (byte) 0x0A; 42 + private static final byte CR = (byte)0x0D; 43 + private static final byte LF = (byte)0x0A; 44 + private static final byte LCBRACKET = (byte)'{'; 45 + private static final byte RCBRACKET = (byte)'}'; 46 + private static final byte DQUOTE = (byte)'\"'; 47 + private static final byte BACKSLASH = (byte)'\\'; 48 + private static final byte DIGIT_ZERO = (byte)'0'; 49 + private static final byte DIGIT_NINE = (byte)'9'; 50 + 43 51 private int trimCount; 44 52 private int lastLength = 0; 45 53 private int literalLength = -1; 54 + private boolean inQuoted; 55 + private boolean inQuotedEscape; 46 56 47 57 public int checkForCompleteResponse(byte[] buf, int len) { 48 58 trimCount = 0; ··· 58 68 return -1; 59 69 } 60 70 } 61 - 62 - int p = StringArrays.indexOf(buf, LF, lastLength); 71 + 72 + int p = indexOfLinefeedIgnoringQuoted(buf, lastLength); 63 73 64 74 while ((p != -1) && (p < len)) { 65 75 if ((p > 0) && (buf[p - 1] == CR)) { 66 - if ((p > 3) && (buf[p - 2] == '}')) { 76 + if ((p > 3) && (buf[p - 2] == RCBRACKET)) { 67 77 int i = p - 3; 68 78 69 79 while (i >= 0) { 70 - if ((buf[i] >= '0') && (buf[i] <= '9')) { 80 + if ((buf[i] >= DIGIT_ZERO) && (buf[i] <= DIGIT_NINE)) { 71 81 i--; 72 82 } 73 - else if (buf[i] == '{' && (p - i - 3) > 0) { 83 + else if (buf[i] == LCBRACKET && (p - i - 3) > 0) { 74 84 try { 75 85 literalLength = StringArrays.parseInt(buf, i + 1, p - i - 3); 76 86 break; ··· 84 94 } 85 95 86 96 trimCount = 2; 87 - } else { 97 + } 98 + else { 88 99 trimCount = 1; 89 100 } 90 101 ··· 117 128 return -1; 118 129 } 119 130 131 + private int indexOfLinefeedIgnoringQuoted(byte[] array, int fromIndex) { 132 + if(fromIndex >= array.length) { 133 + return -1; 134 + } 135 + 136 + for(int i = fromIndex; i<array.length; i++) { 137 + if(inQuoted) { 138 + if(array[i] == BACKSLASH && !inQuotedEscape) { 139 + inQuotedEscape = true; 140 + } 141 + else if(inQuotedEscape) { 142 + inQuotedEscape = false; 143 + } 144 + else if(array[i] == DQUOTE) { 145 + inQuoted = false; 146 + } 147 + } 148 + else { 149 + if(array[i] == DQUOTE) { 150 + inQuoted = true; 151 + } 152 + else if(array[i] == LF) { 153 + return i; 154 + } 155 + } 156 + } 157 + return -1; 158 + } 159 + 160 + 120 161 public int trimCount() { 121 162 return trimCount; 122 163 }
+30
LogicMailTests/src/org/logicprobe/LogicMail/mail/imap/ImapParserTest.java
··· 95 95 assertEquals("Empty body literal", expected, result); 96 96 } 97 97 98 + public void testParenStringLexerQuotedWithLineBreak() { 99 + // This test covers the case where a line-break appears in the middle 100 + // of a quoted section. Since this is technically invalid according 101 + // to the IMAP grammar, this test verifies that the line break 102 + // characters are removed from the result. 103 + 104 + String rawText = "(TOKEN \"Hello\n World\")"; 105 + Object[] expected = new Object[] { 106 + LPAREN, "TOKEN", "Hello World".getBytes(), RPAREN 107 + }; 108 + 109 + byte[] data = rawText.getBytes(); 110 + Vector result = ImapParser.parenListLexer(data, 0, data.length); 111 + 112 + assertEquals("Quoted with LF", expected, result); 113 + 114 + rawText = "(TOKEN \"Hello\r\n World\")"; 115 + expected = new Object[] { 116 + LPAREN, "TOKEN", "Hello World".getBytes(), RPAREN 117 + }; 118 + 119 + data = rawText.getBytes(); 120 + result = ImapParser.parenListLexer(data, 0, data.length); 121 + 122 + assertEquals("Quoted with CRLF", expected, result); 123 + } 124 + 98 125 public void testParenStringLexerEnvelope1() { 99 126 String rawText = "(FLAGS (\\Answered \\Seen) " + 100 127 "ENVELOPE (\"Mon, 12 Mar 2007 19:38:31 -0700\" \"Re: Calm down! :-)\" " + ··· 422 449 423 450 suite.addTest(new ImapParserTest("parenStringLexerSimple", new TestMethod() 424 451 { public void run(TestCase tc) { ((ImapParserTest) tc).testParenStringLexerSimple(); }})); 452 + 453 + suite.addTest(new ImapParserTest("parenStringLexerQuotedWithLineBreak", new TestMethod() 454 + { public void run(TestCase tc) { ((ImapParserTest) tc).testParenStringLexerQuotedWithLineBreak(); }})); 425 455 426 456 suite.addTest(new ImapParserTest("parenStringLexerEnvelope1", new TestMethod() 427 457 { public void run(TestCase tc) { ((ImapParserTest) tc).testParenStringLexerEnvelope1(); }}));
+34
LogicMailTests/src/org/logicprobe/LogicMail/mail/imap/ImapResponseLineTesterTest.java
··· 225 225 assertEquals("Complete", 2, trimCount); 226 226 } 227 227 228 + public void testLinesWithQuoted() { 229 + byte[] input = "\"Hello World\"\r\nFoo\r\n".getBytes(); 230 + int responseLength = instance.checkForCompleteResponse(input, input.length); 231 + int trimCount = instance.trimCount(); 232 + assertEquals("Case 1", 15, responseLength); // Length of '"Hello World"\r\n' 233 + assertEquals("Case 1", 2, trimCount); 234 + 235 + input = "\"Hello \\\"Foo\\\\Bar\\\" World\"\r\nFoo\r\n".getBytes(); 236 + responseLength = instance.checkForCompleteResponse(input, input.length); 237 + trimCount = instance.trimCount(); 238 + assertEquals("Case 2", 28, responseLength); // Length of '"Hello \"Foo\\Bar\" World"\r\n' 239 + assertEquals("Case 2", 2, trimCount); 240 + } 241 + 242 + public void testLinesWithMalformedQuoted() { 243 + // This tests for a case that is technically invalid according to the 244 + // IMAP grammar, but an actual server has been observed to produce it. 245 + byte[] input = "\"Hello\n World\"\r\nFoo\r\n".getBytes(); 246 + int responseLength = instance.checkForCompleteResponse(input, input.length); 247 + int trimCount = instance.trimCount(); 248 + assertEquals("Case 1", 16, responseLength); // Length of '"Hello\n World"\r\n' 249 + assertEquals("Case 1", 2, trimCount); 250 + 251 + input = "\"Hello\r\n World\"\r\nFoo\r\n".getBytes(); 252 + responseLength = instance.checkForCompleteResponse(input, input.length); 253 + trimCount = instance.trimCount(); 254 + assertEquals("Case 2", 17, responseLength); // Length of '"Hello\r\n World"\r\n' 255 + assertEquals("Case 2", 2, trimCount); 256 + } 257 + 228 258 public Test suite() { 229 259 TestSuite suite = new TestSuite("ImapResponseLineTester"); 230 260 ··· 240 270 { public void run(TestCase tc) { ((ImapResponseLineTesterTest) tc).testMultipleChecks(); }})); 241 271 suite.addTest(new ImapResponseLineTesterTest("multipleChecksWithLiteral", new TestMethod() 242 272 { public void run(TestCase tc) { ((ImapResponseLineTesterTest) tc).testMultipleChecksWithLiteral(); }})); 273 + suite.addTest(new ImapResponseLineTesterTest("linesWithQuoted", new TestMethod() 274 + { public void run(TestCase tc) { ((ImapResponseLineTesterTest) tc).testLinesWithQuoted(); }})); 275 + suite.addTest(new ImapResponseLineTesterTest("linesWithMalformedQuoted", new TestMethod() 276 + { public void run(TestCase tc) { ((ImapResponseLineTesterTest) tc).testLinesWithMalformedQuoted(); }})); 243 277 244 278 return suite; 245 279 }