Home | History | Annotate | Download | only in imap
      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