1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.email.mail.store.imap; 18 19 import static com.android.email.mail.store.imap.ImapTestUtils.assertElement; 20 import static com.android.email.mail.store.imap.ImapTestUtils.buildList; 21 import static com.android.email.mail.store.imap.ImapTestUtils.buildResponse; 22 import static com.android.email.mail.store.imap.ImapTestUtils.createFixedLengthInputStream; 23 24 import com.android.email.mail.store.imap.ImapResponseParser.ByeException; 25 import com.android.email.mail.transport.DiscourseLogger; 26 import com.android.emailcommon.TempDirectory; 27 import com.android.emailcommon.mail.MessagingException; 28 import com.android.emailcommon.utility.Utility; 29 30 import android.test.AndroidTestCase; 31 import android.test.suitebuilder.annotation.SmallTest; 32 33 import java.io.ByteArrayInputStream; 34 import java.io.IOException; 35 36 @SmallTest 37 public class ImapResponseParserTest extends AndroidTestCase { 38 private static ImapResponseParser generateParser(int literalKeepInMemoryThreshold, 39 String responses) { 40 return new ImapResponseParser(new ByteArrayInputStream(Utility.toAscii(responses)), 41 new DiscourseLogger(4), literalKeepInMemoryThreshold); 42 } 43 44 @Override 45 protected void setUp() throws Exception { 46 super.setUp(); 47 TempDirectory.setTempDirectory(getContext()); 48 } 49 50 public void testExpect() throws Exception { 51 final ImapResponseParser p = generateParser(100000, "abc"); 52 p.expect('a'); 53 p.expect('b'); 54 try { 55 p.expect('C'); 56 fail(); 57 } catch (IOException e) { 58 // OK 59 } 60 } 61 62 public void testreadUntil() throws Exception { 63 final ImapResponseParser p = generateParser(100000, "!ab!c!!def!"); 64 assertEquals("", p.readUntil('!')); 65 assertEquals("ab", p.readUntil('!')); 66 assertEquals("c", p.readUntil('!')); 67 assertEquals("", p.readUntil('!')); 68 assertEquals("def", p.readUntil('!')); 69 } 70 71 public void testBasic() throws Exception { 72 ImapResponse r; 73 final ImapResponseParser p = generateParser(100000, 74 "* STATUS \"INBOX\" (UNSEEN 2)\r\n" + 75 "100 OK STATUS completed\r\n" + 76 "+ continuation request+(\r\n" + 77 "* STATUS {5}\r\n" + 78 "IN%OX (UNSEEN 10) \"a b c\"\r\n" + 79 "101 OK STATUS completed %!(\r\n" + 80 "102 OK 1\r\n" + 81 "* 1 FETCH\r\n" + 82 "103 OK\r\n" + // shortest OK 83 "* a\r\n" // shortest response 84 ); 85 r = p.readResponse(); 86 assertElement(buildResponse(null, false, 87 new ImapSimpleString("STATUS"), 88 new ImapSimpleString("INBOX"), 89 buildList( 90 new ImapSimpleString("UNSEEN"), 91 new ImapSimpleString("2") 92 ) 93 ), r); 94 95 r = p.readResponse(); 96 assertElement(buildResponse("100", false, 97 new ImapSimpleString("OK"), 98 new ImapSimpleString("STATUS completed") // one string 99 ), r); 100 101 r = p.readResponse(); 102 assertElement(buildResponse(null, true, 103 new ImapSimpleString("continuation request+(") // one string 104 ), r); 105 106 r = p.readResponse(); 107 assertElement(buildResponse(null, false, 108 new ImapSimpleString("STATUS"), 109 new ImapMemoryLiteral(createFixedLengthInputStream("IN%OX")), 110 buildList( 111 new ImapSimpleString("UNSEEN"), 112 new ImapSimpleString("10") 113 ), 114 new ImapSimpleString("a b c") 115 ), r); 116 117 r = p.readResponse(); 118 assertElement(buildResponse("101", false, 119 new ImapSimpleString("OK"), 120 new ImapSimpleString("STATUS completed %!(") // one string 121 ), r); 122 123 r = p.readResponse(); 124 assertElement(buildResponse("102", false, 125 new ImapSimpleString("OK"), 126 new ImapSimpleString("1") 127 ), r); 128 129 r = p.readResponse(); 130 assertElement(buildResponse(null, false, 131 new ImapSimpleString("1"), 132 new ImapSimpleString("FETCH") 133 ), r); 134 135 r = p.readResponse(); 136 assertElement(buildResponse("103", false, 137 new ImapSimpleString("OK") 138 ), r); 139 140 r = p.readResponse(); 141 assertElement(buildResponse(null, false, 142 new ImapSimpleString("a") 143 ), r); 144 } 145 146 public void testNil() throws Exception { 147 ImapResponse r; 148 final ImapResponseParser p = generateParser(100000, 149 "* nil nil NIL \"NIL\" {3}\r\n" + 150 "NIL\r\n" 151 ); 152 153 r = p.readResponse(); 154 assertElement(buildResponse(null, false, 155 ImapString.EMPTY, 156 ImapString.EMPTY, 157 ImapString.EMPTY, 158 new ImapSimpleString("NIL"), 159 new ImapMemoryLiteral(createFixedLengthInputStream("NIL")) 160 ), r); 161 } 162 163 public void testBareLf() throws Exception { 164 ImapResponse r; 165 166 // Threshold = 3 bytes: use in memory literal. 167 ImapResponseParser p = generateParser(3, 168 "* a b\n" + // Bare LF -- should be treated like CRLF 169 "* x y\r\n" 170 ); 171 r = p.readResponse(); 172 assertElement(buildResponse(null, false, 173 new ImapSimpleString("a"), 174 new ImapSimpleString("b") 175 ), r); 176 177 r = p.readResponse(); 178 assertElement(buildResponse(null, false, 179 new ImapSimpleString("x"), 180 new ImapSimpleString("y") 181 ), r); 182 } 183 184 public void testLiteral() throws Exception { 185 ImapResponse r; 186 187 // Threshold = 3 bytes: use in memory literal. 188 ImapResponseParser p = generateParser(3, 189 "* test {3}\r\n" + 190 "ABC\r\n" 191 ); 192 r = p.readResponse(); 193 assertElement(buildResponse(null, false, 194 new ImapSimpleString("test"), 195 new ImapMemoryLiteral(createFixedLengthInputStream("ABC")) 196 ), r); 197 198 // Threshold = 2 bytes: use temp file literal. 199 p = generateParser(2, 200 "* test {3}\r\n" + 201 "ABC\r\n" 202 ); 203 r = p.readResponse(); 204 assertElement(buildResponse(null, false, 205 new ImapSimpleString("test"), 206 new ImapTempFileLiteral(createFixedLengthInputStream("ABC")) 207 ), r); 208 209 // 2 literals in a line 210 p = generateParser(0, 211 "* test {3}\r\n" + 212 "ABC {4}\r\n" + 213 "wxyz\r\n" 214 ); 215 r = p.readResponse(); 216 assertElement(buildResponse(null, false, 217 new ImapSimpleString("test"), 218 new ImapTempFileLiteral(createFixedLengthInputStream("ABC")), 219 new ImapTempFileLiteral(createFixedLengthInputStream("wxyz")) 220 ), r); 221 } 222 223 public void testAlert() throws Exception { 224 ImapResponse r; 225 final ImapResponseParser p = generateParser(100000, 226 "* OK [ALERT]\r\n" + // No message 227 "* OK [ALERT] alert ( message ) %*\r\n" + 228 "* OK [ABC] not alert\r\n" 229 ); 230 r = p.readResponse(); 231 assertTrue(r.isOk()); 232 assertTrue(r.getAlertTextOrEmpty().isEmpty()); 233 234 r = p.readResponse(); 235 assertTrue(r.isOk()); 236 assertEquals("alert ( message ) %*", r.getAlertTextOrEmpty().getString()); 237 238 r = p.readResponse(); 239 assertTrue(r.isOk()); 240 assertTrue(r.getAlertTextOrEmpty().isEmpty()); 241 } 242 243 /** 244 * If a [ appears in the middle of a string, the following string until the next ']' will 245 * be considered a part of the string. 246 */ 247 public void testBracket() throws Exception { 248 ImapResponse r; 249 final ImapResponseParser p = generateParser(100000, 250 "* AAA BODY[HEADER.FIELDS (\"DATE\" \"SUBJECT\")]\r\n" + 251 "* BBB B[a b c]d e f\r\n" 252 ); 253 r = p.readResponse(); 254 assertEquals("BODY[HEADER.FIELDS (\"DATE\" \"SUBJECT\")]", 255 r.getStringOrEmpty(1).getString()); 256 257 r = p.readResponse(); 258 assertEquals("B[a b c]d", r.getStringOrEmpty(1).getString()); 259 } 260 261 public void testNest() throws Exception { 262 ImapResponse r; 263 final ImapResponseParser p = generateParser(100000, 264 "* A (a B () DEF) (a (ab)) ((() ())) ((a) ab) ((x y ZZ) () [] [A B] (A B C))" + 265 " ([abc] a[abc])\r\n" 266 ); 267 r = p.readResponse(); 268 assertElement(buildResponse(null, false, 269 new ImapSimpleString("A"), 270 buildList( 271 new ImapSimpleString("a"), 272 new ImapSimpleString("B"), 273 buildList(), 274 new ImapSimpleString("DEF") 275 ), 276 buildList( 277 new ImapSimpleString("a"), 278 buildList( 279 new ImapSimpleString("ab") 280 ) 281 ), 282 buildList( 283 buildList( 284 buildList(), 285 buildList() 286 ) 287 ), 288 buildList( 289 buildList( 290 new ImapSimpleString("a") 291 ), 292 new ImapSimpleString("ab") 293 ), 294 buildList( 295 buildList( 296 new ImapSimpleString("x"), 297 new ImapSimpleString("y"), 298 new ImapSimpleString("ZZ") 299 ), 300 buildList(), 301 buildList(), 302 buildList( 303 new ImapSimpleString("A"), 304 new ImapSimpleString("B") 305 ), 306 buildList( 307 new ImapSimpleString("A"), 308 new ImapSimpleString("B"), 309 new ImapSimpleString("C") 310 ) 311 ), 312 buildList( 313 buildList( 314 new ImapSimpleString("abc") 315 ), 316 new ImapSimpleString("a[abc]") 317 ) 318 ), r); 319 } 320 321 /** 322 * Parser shouldn't crash for any response. Should just throw IO/MessagingException. 323 */ 324 public void testMalformedResponse() throws Exception { 325 expectMessagingException(""); 326 expectMessagingException("\r"); 327 expectMessagingException("\r\n"); 328 329 expectMessagingException("*\r\n"); 330 expectMessagingException("1\r\n"); 331 332 expectMessagingException("* \r\n"); 333 expectMessagingException("1 \r\n"); 334 335 expectMessagingException("* A (\r\n"); 336 expectMessagingException("* A )\r\n"); 337 expectMessagingException("* A (()\r\n"); 338 expectMessagingException("* A ())\r\n"); 339 expectMessagingException("* A [\r\n"); 340 expectMessagingException("* A ]\r\n"); 341 expectMessagingException("* A [[]\r\n"); 342 expectMessagingException("* A []]\r\n"); 343 344 expectMessagingException("* A ([)]\r\n"); 345 346 expectMessagingException("* A"); 347 expectMessagingException("* {3}"); 348 expectMessagingException("* {3}\r\nab"); 349 } 350 351 private static void expectMessagingException(String response) throws Exception { 352 final ImapResponseParser p = generateParser(100000, response); 353 try { 354 p.readResponse(); 355 fail("Didn't throw Exception: response='" + response + "'"); 356 } catch (MessagingException ok) { 357 return; 358 } catch (IOException ok) { 359 return; 360 } 361 } 362 363 // Compatibility tests... 364 365 /** 366 * OK response with a long message that contains special chars. (including tabs) 367 */ 368 public void testOkWithLongMessage() throws Exception { 369 ImapResponse r; 370 final ImapResponseParser p = generateParser(100000, 371 "* OK [CAPABILITY IMAP4 IMAP4rev1 LITERAL+ ID STARTTLS AUTH=PLAIN AUTH=LOGIN" + 372 "AUTH=CRAM-MD5] server.domain.tld\tCyrus IMAP4 v2.3.8-OS X Server 10.5:" 373 +" \t\t\t9F33 server ready %%\r\n"); 374 assertTrue(p.readResponse().isOk()); 375 } 376 377 /** Make sure literals and strings are interchangeable. */ 378 public void testLiteralStringConversion() throws Exception { 379 ImapResponse r; 380 final ImapResponseParser p = generateParser(100000, 381 "* XXX {5}\r\n" + 382 "a b c\r\n"); 383 assertEquals("a b c", p.readResponse().getStringOrEmpty(1).getString()); 384 } 385 386 public void testByeReceived() throws Exception { 387 final ImapResponseParser p = generateParser(100000, 388 "* BYE Autologout timer; idle for too long\r\n"); 389 try { 390 p.readResponse(); 391 fail("Didn't throw ByeException"); 392 } catch (ByeException ok) { 393 } 394 } 395 } 396