#include <stdlib.h>
#include <time.h>
#include "cunit/unit.h"
#include "imapurl.h"

static void test_fromurl_cclient(void)
{
    static const char URL[] = "{foo}bar";
    struct imapurl iurl;
    int r;

    memset(&iurl, 0x45, sizeof(iurl));
    r = imapurl_fromURL(&iurl, URL);
    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NULL(iurl.user);
    CU_ASSERT_PTR_NULL(iurl.auth);
    CU_ASSERT_STRING_EQUAL(iurl.server, "foo");
    CU_ASSERT_STRING_EQUAL(iurl.mailbox, "bar");
    CU_ASSERT_EQUAL(iurl.uidvalidity, 0);
    CU_ASSERT_EQUAL(iurl.uid, 0);
    CU_ASSERT_PTR_NULL(iurl.section);
    CU_ASSERT_EQUAL(iurl.start_octet, 0);
    CU_ASSERT_EQUAL(iurl.octet_count, 0);
    CU_ASSERT_PTR_NULL(iurl.urlauth.access);
    CU_ASSERT_PTR_NULL(iurl.urlauth.mech);
    CU_ASSERT_PTR_NULL(iurl.urlauth.token);
    CU_ASSERT_EQUAL(iurl.urlauth.expire, 0);
    CU_ASSERT_EQUAL(iurl.urlauth.rump_len, 0);
    free(iurl.freeme);
}

static void test_fromurl(void)
{
    static const char URL[] = "imap://jeeves/deverill";
    struct imapurl iurl;
    int r;

    memset(&iurl, 0x45, sizeof(iurl));
    r = imapurl_fromURL(&iurl, URL);
    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NULL(iurl.user);
    CU_ASSERT_PTR_NULL(iurl.auth);
    CU_ASSERT_STRING_EQUAL(iurl.server, "jeeves");
    CU_ASSERT_STRING_EQUAL(iurl.mailbox, "deverill");
    CU_ASSERT_EQUAL(iurl.uidvalidity, 0);
    CU_ASSERT_EQUAL(iurl.uid, 0);
    CU_ASSERT_PTR_NULL(iurl.section);
    CU_ASSERT_EQUAL(iurl.start_octet, 0);
    CU_ASSERT_EQUAL(iurl.octet_count, 0);
    CU_ASSERT_PTR_NULL(iurl.urlauth.access);
    CU_ASSERT_PTR_NULL(iurl.urlauth.mech);
    CU_ASSERT_PTR_NULL(iurl.urlauth.token);
    CU_ASSERT_EQUAL(iurl.urlauth.expire, 0);
    CU_ASSERT_EQUAL(iurl.urlauth.rump_len, 0);
    free(iurl.freeme);
}

static void test_fromurl_user(void)
{
    static const char URL[] = "imap://wooster@jeeves/deverill";
    struct imapurl iurl;
    int r;

    memset(&iurl, 0x45, sizeof(iurl));
    r = imapurl_fromURL(&iurl, URL);
    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_STRING_EQUAL(iurl.user, "wooster");
    CU_ASSERT_PTR_NULL(iurl.auth);
    CU_ASSERT_STRING_EQUAL(iurl.server, "jeeves");
    CU_ASSERT_STRING_EQUAL(iurl.mailbox, "deverill");
    CU_ASSERT_EQUAL(iurl.uidvalidity, 0);
    CU_ASSERT_EQUAL(iurl.uid, 0);
    CU_ASSERT_PTR_NULL(iurl.section);
    CU_ASSERT_EQUAL(iurl.start_octet, 0);
    CU_ASSERT_EQUAL(iurl.octet_count, 0);
    CU_ASSERT_PTR_NULL(iurl.urlauth.access);
    CU_ASSERT_PTR_NULL(iurl.urlauth.mech);
    CU_ASSERT_PTR_NULL(iurl.urlauth.token);
    CU_ASSERT_EQUAL(iurl.urlauth.expire, 0);
    CU_ASSERT_EQUAL(iurl.urlauth.rump_len, 0);
    free(iurl.freeme);
}

static void test_fromurl_encuser(void)
{
    static const char URL[] = "imap://%53po%64e@jeeves/deverill";
    struct imapurl iurl;
    int r;

    memset(&iurl, 0x45, sizeof(iurl));
    r = imapurl_fromURL(&iurl, URL);
    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_STRING_EQUAL(iurl.user, "Spode");
    CU_ASSERT_PTR_NULL(iurl.auth);
    CU_ASSERT_STRING_EQUAL(iurl.server, "jeeves");
    CU_ASSERT_STRING_EQUAL(iurl.mailbox, "deverill");
    CU_ASSERT_EQUAL(iurl.uidvalidity, 0);
    CU_ASSERT_EQUAL(iurl.uid, 0);
    CU_ASSERT_PTR_NULL(iurl.section);
    CU_ASSERT_EQUAL(iurl.start_octet, 0);
    CU_ASSERT_EQUAL(iurl.octet_count, 0);
    CU_ASSERT_PTR_NULL(iurl.urlauth.access);
    CU_ASSERT_PTR_NULL(iurl.urlauth.mech);
    CU_ASSERT_PTR_NULL(iurl.urlauth.token);
    CU_ASSERT_EQUAL(iurl.urlauth.expire, 0);
    CU_ASSERT_EQUAL(iurl.urlauth.rump_len, 0);
    free(iurl.freeme);
}

static void test_fromurl_options(void)
{
    static const char URL[] = "imap://wooster@jeeves/deverill"
                              ";UIDVALIDITY=7890"
                              "/;UID=123456"
                              "/;SECTION=1.4"
                              "/;PARTIAL=1.1023";
    struct imapurl iurl;
    int r;

    memset(&iurl, 0x45, sizeof(iurl));
    r = imapurl_fromURL(&iurl, URL);
    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_STRING_EQUAL(iurl.user, "wooster");
    CU_ASSERT_PTR_NULL(iurl.auth);
    CU_ASSERT_STRING_EQUAL(iurl.server, "jeeves");
    CU_ASSERT_STRING_EQUAL(iurl.mailbox, "deverill");
    CU_ASSERT_EQUAL(iurl.uidvalidity, 7890);
    CU_ASSERT_EQUAL(iurl.uid, 123456);
    CU_ASSERT_STRING_EQUAL(iurl.section, "1.4");
    CU_ASSERT_EQUAL(iurl.start_octet, 1);
    CU_ASSERT_EQUAL(iurl.octet_count, 1023);
    CU_ASSERT_PTR_NULL(iurl.urlauth.access);
    CU_ASSERT_PTR_NULL(iurl.urlauth.mech);
    CU_ASSERT_PTR_NULL(iurl.urlauth.token);
    CU_ASSERT_EQUAL(iurl.urlauth.expire, 0);
    CU_ASSERT_EQUAL(iurl.urlauth.rump_len, 0);
    free(iurl.freeme);
}

static void test_fromurl_urlauth(void)
{
    static const char URL[] = "imap://wooster@jeeves/deverill"
                              "/;UID=123456"
                              ";EXPIRE=2010-11-24T06:57:26Z"
                              ";URLAUTH=submit+fred:internal:91354a473744909de610943775f92038";
    struct imapurl iurl;
    int r;

    memset(&iurl, 0x45, sizeof(iurl));
    r = imapurl_fromURL(&iurl, URL);
    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_STRING_EQUAL(iurl.user, "wooster");
    CU_ASSERT_PTR_NULL(iurl.auth);
    CU_ASSERT_STRING_EQUAL(iurl.server, "jeeves");
    CU_ASSERT_STRING_EQUAL(iurl.mailbox, "deverill");
    CU_ASSERT_EQUAL(iurl.uidvalidity, 0);
    CU_ASSERT_EQUAL(iurl.uid, 123456);
    CU_ASSERT_PTR_NULL(iurl.section);
    CU_ASSERT_EQUAL(iurl.start_octet, 0);
    CU_ASSERT_EQUAL(iurl.octet_count, 0);
    CU_ASSERT_STRING_EQUAL(iurl.urlauth.access, "submit+fred");
    CU_ASSERT_STRING_EQUAL(iurl.urlauth.mech, "internal");
    CU_ASSERT_STRING_EQUAL(iurl.urlauth.token,
                            "91354a473744909de610943775f92038");
    CU_ASSERT_EQUAL(iurl.urlauth.expire, 1290581846);
    CU_ASSERT_EQUAL(iurl.urlauth.rump_len, 90);
    free(iurl.freeme);
}

// TODO: {foo is invalid
// TODO: imap:// is invalid
// TODO: imap://server/ and imap://server
// TODO: %xx encoding in username
// TODO: %xx encoding in section
// TODO: lowercase option names
// TODO: relative urls
// TODO: it doesn't seem like it parses a host port
//       specially, it will be accidentally appended to .server
// TODO: no / before ;
// TODO: invalidly formatted uidvalidity
// TODO: invalidly formatted uid
// TODO: invalidly formatted section
// TODO: ;AUTH=* at end of user name
// TODO: ;AUTH=<mech> at end of user name
// TODO: ?enc-search at the end

static void test_tourl(void)
{
    static const char URL[] = "imap://jeeves/deverill";
    struct imapurl iurl;
    struct buf buf = BUF_INITIALIZER;

    memset(&iurl, 0, sizeof(iurl));
    iurl.server = "jeeves";
    iurl.mailbox = "deverill";

    imapurl_toURL(&buf, &iurl);
    CU_ASSERT_STRING_EQUAL(buf_cstring(&buf), URL);
    buf_free(&buf);
}

static void test_tourl_server(void)
{
    static const char URL[] = "imap://jeeves";
    struct imapurl iurl;
    struct buf buf = BUF_INITIALIZER;

    memset(&iurl, 0, sizeof(iurl));
    iurl.server = "jeeves";

    imapurl_toURL(&buf, &iurl);
    CU_ASSERT_STRING_EQUAL(buf_cstring(&buf), URL);
    buf_free(&buf);
}

static void test_tourl_user(void)
{
    static const char URL[] = "imap://wooster@jeeves/deverill";
    struct imapurl iurl;
    struct buf buf = BUF_INITIALIZER;

    memset(&iurl, 0, sizeof(iurl));
    iurl.user = "wooster";
    iurl.server = "jeeves";
    iurl.mailbox = "deverill";
    imapurl_toURL(&buf, &iurl);
    CU_ASSERT_STRING_EQUAL(buf_cstring(&buf), URL);
    buf_free(&buf);
}

static void test_tourl_options(void)
{
    static const char URL[] = "imap://jeeves/deverill"
                              ";UIDVALIDITY=7890"
                              "/;UID=123456"
                              "/;SECTION=1.4"
                              "/;PARTIAL=1.1023";
    struct imapurl iurl;
    struct buf buf = BUF_INITIALIZER;

    memset(&iurl, 0, sizeof(iurl));
    iurl.server = "jeeves";
    iurl.mailbox = "deverill";
    iurl.uidvalidity = 7890;
    iurl.uid = 123456;
    iurl.section = "1.4";
    iurl.start_octet = 1;
    iurl.octet_count = 1023;

    imapurl_toURL(&buf, &iurl);
    CU_ASSERT_STRING_EQUAL(buf_cstring(&buf), URL);
    buf_free(&buf);
}

static void test_tourl_urlauth(void)
{
    static const char URL[] = "imap://jeeves/deverill"
                              "/;UID=123456"
                              ";EXPIRE=2010-11-24T06:57:26Z"
                              ";URLAUTH=submit+fred:internal:91354a473744909de610943775f92038";
    struct imapurl iurl;
    struct buf buf = BUF_INITIALIZER;

    memset(&iurl, 0, sizeof(iurl));
    iurl.server = "jeeves";
    iurl.mailbox = "deverill";
    iurl.uid = 123456;
    iurl.urlauth.access = "submit+fred";
    iurl.urlauth.mech = "internal";
    iurl.urlauth.token = "91354a473744909de610943775f92038";
    iurl.urlauth.expire = 1290581846;

    imapurl_toURL(&buf, &iurl);
    CU_ASSERT_STRING_EQUAL(buf_cstring(&buf), URL);
    buf_free(&buf);
}

static void test_tourl_ampersand(void)
{
    /*
     * Test a mailbox name containing an ampersand, which
     * character is significant to both the modified UTF-7
     * encoding of mailbox names and to URL-encoding.
     */
    static const char URL[] = "imap://goons/Goosey%26Bawks";
    struct imapurl iurl;
    struct buf buf = BUF_INITIALIZER;

    memset(&iurl, 0, sizeof(iurl));
    iurl.server = "goons";
    iurl.mailbox = "Goosey&-Bawks";

    imapurl_toURL(&buf, &iurl);
    CU_ASSERT_STRING_EQUAL(buf_cstring(&buf), URL);
    buf_free(&buf);
}

static void test_tourl_urlunsafe(void)
{
    /*
     * Test a mailbox name containing characters which are
     * not special to modified UTF-7 but are unsafe in URLs.
     */
    static const char URL[] = "imap://gibberish/%20%22%23%25%2B%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%60%7B%7C%7D";
    struct imapurl iurl;
    struct buf buf = BUF_INITIALIZER;

    memset(&iurl, 0, sizeof(iurl));
    iurl.server = "gibberish";
    iurl.mailbox = " \"#%+:;<=>?@[\\]^`{|}";

    imapurl_toURL(&buf, &iurl);
    CU_ASSERT_STRING_EQUAL(buf_cstring(&buf), URL);
    buf_free(&buf);
}

static void test_tourl_utf7_high(void)
{
    /*
     * This guy has a mailbox whose name is expressed in
     * Unicode 5.0 Cuneiform.  The name is three glyphs:
     *
     * 12073 CUNEIFORM SIGN DIB
     * 12020 CUNEIFORM SIGN AL
     * 12129 CUNEIFORM SIGN HA
     *
     * Presumably it contains emails discussing some ancestor
     * of Cut Me Own Throat Dibbler.  Anyway, this tests the
     * UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> URL-encoded
     * path when converting a mailbox name.
     */
    static const char URL[] = "imap://uruk/%F0%92%81%B3%F0%92%80%A0%F0%92%84%A9";
    struct imapurl iurl;
    struct buf buf = BUF_INITIALIZER;

    memset(&iurl, 0, sizeof(iurl));
    iurl.server = "uruk";
    iurl.mailbox = "&2Ajcc9gI3CDYCN0p-";

    imapurl_toURL(&buf, &iurl);
    CU_ASSERT_STRING_EQUAL(buf_cstring(&buf), URL);
    buf_free(&buf);
}

static void test_fromurl_utf2_high(void)
{
    /* Same Cuneiform as above, but test the opposite conversion */
    static const char URL[] = "imap://uruk/%F0%92%81%B3%F0%92%80%A0%F0%92%84%A9";
    struct imapurl iurl;
    int r;

    memset(&iurl, 0x45, sizeof(iurl));
    r = imapurl_fromURL(&iurl, URL);
    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NULL(iurl.user);
    CU_ASSERT_PTR_NULL(iurl.auth);
    CU_ASSERT_STRING_EQUAL(iurl.server, "uruk");
    CU_ASSERT_STRING_EQUAL(iurl.mailbox, "&2Ajcc9gI3CDYCN0p-");
    CU_ASSERT_EQUAL(iurl.uidvalidity, 0);
    CU_ASSERT_EQUAL(iurl.uid, 0);
    CU_ASSERT_PTR_NULL(iurl.section);
    CU_ASSERT_EQUAL(iurl.start_octet, 0);
    CU_ASSERT_EQUAL(iurl.octet_count, 0);
    CU_ASSERT_PTR_NULL(iurl.urlauth.access);
    CU_ASSERT_PTR_NULL(iurl.urlauth.mech);
    CU_ASSERT_PTR_NULL(iurl.urlauth.token);
    CU_ASSERT_EQUAL(iurl.urlauth.expire, 0);
    CU_ASSERT_EQUAL(iurl.urlauth.rump_len, 0);
    free(iurl.freeme);
}

static void test_cycle(void)
{
    struct imapurl iurl;
    struct imapurl iurl2;
    static const char URL[] = "imap://;AUTH=*@server/%C3%A4%20%C3%84;UIDVALIDITY=1234567890";
    int r;
    struct buf buf = BUF_INITIALIZER;

    memset(&iurl, 0, sizeof(struct imapurl));
    iurl.server = "server";
    iurl.auth = "*";
    iurl.mailbox = "&AOQ- &AMQ-";  /* "ä Ä" */
    iurl.uidvalidity = 1234567890;

    imapurl_toURL(&buf, &iurl);
    CU_ASSERT_STRING_EQUAL(buf_cstring(&buf), URL);

    r = imapurl_fromURL(&iurl2, buf_cstring(&buf));
    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_STRING_EQUAL(iurl2.mailbox, "&AOQ- &AMQ-");
    CU_ASSERT_EQUAL(iurl2.uidvalidity, 1234567890);
    free(iurl2.freeme);

    buf_free(&buf);
}

/* vim: set ft=c: */
