#include "afpcmd.h"
#include "afphelper.h"
#include "testhelper.h"

/*!
  @file
  @brief Test the following:
  @code
   test500()
   =========

   client 1:
      mkdir dir1
      mkdir dir2

   client 2:
      mv dir1 dir2/renamed

   client 1:
      ls dir2
      stat dir2/renamed

   Check: CNID must not change
   Targets: enumerate()

   test501()
   =========

   client 1:
      mkdir dir1
      mkdir dir2

   client 2:
      mv dir1 dir2/renamed

   client 1:
      stat renamed

   Check: CNID must not change
   Targets: getfildirparms()


   test502()
   =========

   client 1:
      mkdir dir1
      mkdir dir2

   client 2:
      mv dir1 dir2/renamed

   client 1:
      ls renamed

   Check: CNID must not change
   Targets: enumerate()


   test503()
   =========

   client 1:
      mkdir dir1
      mkdir dir2

   client 2:
      mv dir1 dir2/renamed

   client 1:
      stat renamed

   Check: CNID must not change
   Targets: getfildirparms()


   test504()
   =========

   client 1:
      mkdir -p dir1/dir2
      touch dir1/dir2/file

   client 2:
      mv dir1 renamed1

   client 1:
      stat file

   Check: CNID must not change
   Targets: getfildirparms()


   test505()
   =========

   client 1:
      mkdir -p dir1/dir2

   client 2:
      mv dir1 renamed1

   client 1:
      stat dir2

   Check: CNID must not change
   Targets: getfildirparms()


   test506()
   =========

   client 1:
      mkdir -p dir1/dir2

   client 2:
      mv dir1 renamed1
      mkdir dir1

   client 1:
      stat dir2
  @endcode
 */

/*! move and rename dir, enumerate new parent, stat renamed dir */
STATIC void test500()
{
    const DSI *dsi = &Conn->dsi;
    uint16_t vol1 = VolID;
    uint16_t vol2;
    char *dir = "t500 dir";
    char *subdir1 = "t500 subdir1";
    char *subdir2 = "t500 subdir2";
    char *renamedsubdir1 = "t500 renamedsubdir1";
    uint32_t dir_id, subdir1_id, subdir2_id;
    int ofs = 3 * sizeof(uint16_t);
    struct afp_filedir_parms filedir;
    uint16_t bitmap = (1 << DIRPBIT_DID) | (1 << DIRPBIT_LNAME);
    ENTER_TEST

    if (!Conn2) {
        test_skipped(T_CONN2);
        goto test_exit;
    }

    if ((vol2 = FPOpenVol(Conn2, Vol)) == 0xffff) {
        test_failed();
        goto test_exit;
    }

    /* Create directories with first connection */
    if ((dir_id = FPCreateDir(Conn, vol1, DIRDID_ROOT, dir)) == 0) {
        test_failed();
    }

    if ((subdir1_id = FPCreateDir(Conn, vol1, dir_id, subdir1)) == 0) {
        test_failed();
    }

    if ((subdir2_id = FPCreateDir(Conn, vol1, dir_id, subdir2)) == 0) {
        test_failed();
    }

    /* Move and rename dir with second connection */
    FAIL(FPMoveAndRename(Conn2, vol2, dir_id, subdir2_id, subdir1, renamedsubdir1));
    /* Enumerate with first connection, does it crash or similar ? */
    FAIL(FPEnumerate(Conn, vol1, subdir2_id, "", (1 << FILPBIT_FNUM),
                     (1 << DIRPBIT_PDID)));
    /* Manually check name and CNID */
    FAIL(FPGetFileDirParams(Conn, vol1, subdir2_id, renamedsubdir1, 0, bitmap));
    filedir.isdir = 1;
    afp_filedir_unpack(&filedir, dsi->data + ofs, 0, bitmap);

    if (filedir.did != subdir1_id) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %x should be %x\n", filedir.did, subdir1_id);
        }

        test_failed();
    }

    if (strcmp(filedir.lname, renamedsubdir1)) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %s should be %s\n", filedir.lname, renamedsubdir1);
        }

        test_failed();
    }

fin:
    FAIL(FPCloseVol(Conn2, vol2));
    FAIL(FPDelete(Conn, vol1, subdir1_id, ""));
    FAIL(FPDelete(Conn, vol1, subdir2_id, ""));
    FAIL(FPDelete(Conn, vol1, dir_id, ""));
test_exit:
    exit_test("Dircache:test500: move and rename dir, enumerate new parent, stat renamed dir");
}

/*! move and rename dir, then stat it */
STATIC void test501()
{
    const DSI *dsi = &Conn->dsi;
    uint16_t vol1 = VolID;
    uint16_t vol2;
    char *dir = "t501 dir";
    char *subdir1 = "t501 subdir1";
    char *subdir2 = "t501 subdir2";
    char *renamedsubdir1 = "t501 renamedsubdir1";
    uint32_t dir_id, subdir1_id, subdir2_id;
    int ofs = 3 * sizeof(uint16_t);
    struct afp_filedir_parms filedir;
    uint16_t bitmap = (1 << DIRPBIT_DID) | (1 << DIRPBIT_LNAME);
    ENTER_TEST

    if (!Conn2) {
        test_skipped(T_CONN2);
        goto test_exit;
    }

    if ((vol2 = FPOpenVol(Conn2, Vol)) == 0xffff) {
        test_failed();
        goto test_exit;
    }

    /* Create directories with first connection */
    if ((dir_id = FPCreateDir(Conn, vol1, DIRDID_ROOT, dir)) == 0) {
        test_failed();
    }

    if ((subdir1_id = FPCreateDir(Conn, vol1, dir_id, subdir1)) == 0) {
        test_failed();
    }

    if ((subdir2_id = FPCreateDir(Conn, vol1, dir_id, subdir2)) == 0) {
        test_failed();
    }

    /* Move and rename dir with second connection */
    FAIL(FPMoveAndRename(Conn2, vol2, dir_id, subdir2_id, subdir1, renamedsubdir1));
    /* Manually check name and CNID */
    FAIL(FPGetFileDirParams(Conn, vol1, subdir2_id, renamedsubdir1, 0, bitmap));
    filedir.isdir = 1;
    afp_filedir_unpack(&filedir, dsi->data + ofs, 0, bitmap);

    if (filedir.did != subdir1_id) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %x should be %x\n", filedir.did, subdir1_id);
        }

        test_failed();
    }

    if (strcmp(filedir.lname, renamedsubdir1)) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %s should be %s\n", filedir.lname, renamedsubdir1);
        }

        test_failed();
    }

fin:
    FAIL(FPCloseVol(Conn2, vol2));
    FAIL(FPDelete(Conn, vol1, subdir1_id, ""));
    FAIL(FPDelete(Conn, vol1, subdir2_id, ""));
    FAIL(FPDelete(Conn, vol1, dir_id, ""));
test_exit:
    exit_test("Dircache:test501: move and rename dir, then stat it");
}

/*! move and rename dir, enumerate renamed dir */
STATIC void test502()
{
    const DSI *dsi = &Conn->dsi;
    uint16_t vol1 = VolID;
    uint16_t vol2;
    char *dir = "t502 dir";
    char *subdir1 = "t502 subdir1";
    char *subdir2 = "t502 subdir2";
    char *renamedsubdir1 = "t502 renamedsubdir1";
    uint32_t dir_id, subdir1_id, subdir2_id;
    int ofs = 3 * sizeof(uint16_t);
    struct afp_filedir_parms filedir;
    uint16_t bitmap = (1 << DIRPBIT_DID) | (1 << DIRPBIT_LNAME);
    ENTER_TEST

    if (!Conn2) {
        test_skipped(T_CONN2);
        goto test_exit;
    }

    if ((vol2 = FPOpenVol(Conn2, Vol)) == 0xffff) {
        test_failed();
        goto test_exit;
    }

    /* Create directories with first connection */
    if ((dir_id = FPCreateDir(Conn, vol1, DIRDID_ROOT, dir)) == 0) {
        test_failed();
    }

    if ((subdir1_id = FPCreateDir(Conn, vol1, dir_id, subdir1)) == 0) {
        test_failed();
    }

    FAIL(FPCreateFile(Conn, vol1,  0, subdir1_id, "file1"))

    if ((subdir2_id = FPCreateDir(Conn, vol1, dir_id, subdir2)) == 0) {
        test_failed();
    }

    /* Move and rename dir with second connection */
    FAIL(FPMoveAndRename(Conn2, vol2, dir_id, subdir2_id, subdir1, renamedsubdir1));
    /* Enumerate with first connection, does it crash or similar ? */
    FAIL(FPEnumerate(Conn, vol1, subdir1_id, "", (1 << FILPBIT_FNUM),
                     (1 << DIRPBIT_PDID)));
    /* Manually check name and CNID */
    FAIL(FPGetFileDirParams(Conn, vol1, subdir1_id, "", 0, bitmap));
    filedir.isdir = 1;
    afp_filedir_unpack(&filedir, dsi->data + ofs, 0, bitmap);

    if (filedir.did != subdir1_id) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %x should be %x\n", filedir.did, subdir1_id);
        }

        test_failed();
    }

    if (strcmp(filedir.lname, renamedsubdir1)) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %s should be %s\n", filedir.lname, renamedsubdir1);
        }

        test_failed();
    }

fin:
    FAIL(FPCloseVol(Conn2, vol2));
    FAIL(FPDelete(Conn, vol1, subdir1_id, "file1"))
    FAIL(FPDelete(Conn, vol1, subdir1_id, ""));
    FAIL(FPDelete(Conn, vol1, subdir2_id, ""));
    FAIL(FPDelete(Conn, vol1, dir_id, ""));
test_exit:
    exit_test("Dircache:test502: move and rename dir, enumerate renamed dir");
}

/*! move and rename dir, stat renamed dir */
STATIC void test503()
{
    const DSI *dsi = &Conn->dsi;
    uint16_t vol1 = VolID;
    uint16_t vol2;
    char *dir = "t503 dir";
    char *subdir1 = "t503 subdir1";
    char *subdir2 = "t503 subdir2";
    char *renamedsubdir1 = "t503 renamedsubdir1";
    uint32_t dir_id, subdir1_id, subdir2_id;
    int ofs = 3 * sizeof(uint16_t);
    struct afp_filedir_parms filedir;
    uint16_t bitmap = (1 << DIRPBIT_DID) | (1 << DIRPBIT_LNAME);
    ENTER_TEST

    if (!Conn2) {
        test_skipped(T_CONN2);
        goto test_exit;
    }

    if ((vol2 = FPOpenVol(Conn2, Vol)) == 0xffff) {
        test_failed();
        goto test_exit;
    }

    /* Create directories with first connection */
    if ((dir_id = FPCreateDir(Conn, vol1, DIRDID_ROOT, dir)) == 0) {
        test_failed();
    }

    if ((subdir1_id = FPCreateDir(Conn, vol1, dir_id, subdir1)) == 0) {
        test_failed();
    }

    if ((subdir2_id = FPCreateDir(Conn, vol1, dir_id, subdir2)) == 0) {
        test_failed();
    }

    /* Move and rename dir with second connection */
    FAIL(FPMoveAndRename(Conn2, vol2, dir_id, subdir2_id, subdir1, renamedsubdir1));
    /* Manually check name and CNID */
    FAIL(FPGetFileDirParams(Conn, vol1, subdir1_id, "", 0, bitmap));
    filedir.isdir = 1;
    afp_filedir_unpack(&filedir, dsi->data + ofs, 0, bitmap);

    if (filedir.did != subdir1_id) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %x should be %x\n", filedir.did, subdir1_id);
        }

        test_failed();
    }

    if (strcmp(filedir.lname, renamedsubdir1)) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %s should be %s\n", filedir.lname, renamedsubdir1);
        }

        test_failed();
    }

fin:
    FAIL(FPCloseVol(Conn2, vol2));
    FAIL(FPDelete(Conn, vol1, subdir1_id, ""));
    FAIL(FPDelete(Conn, vol1, subdir2_id, ""));
    FAIL(FPDelete(Conn, vol1, dir_id, ""));
test_exit:
    exit_test("Dircache:test503: move and rename dir, enumerate renamed dir");
}

/*! rename topdir, stat file in subdir of renamed topdir */
STATIC void test504()
{
    const DSI *dsi = &Conn->dsi;
    uint16_t vol1 = VolID;
    uint16_t vol2;
    char *dir = "t504 dir";
    char *subdir1 = "t504 subdir1";
    char *subdir2 = "t504 subdir2";
    char *renamedsubdir1 = "t504 renamedsubdir1";
    uint32_t dir_id, subdir1_id, subdir2_id, file_id;
    int ofs = 3 * sizeof(uint16_t);
    struct afp_filedir_parms filedir;
    uint16_t bitmap = (1 << DIRPBIT_DID) | (1 << DIRPBIT_LNAME);
    ENTER_TEST

    if (!Conn2) {
        test_skipped(T_CONN2);
        goto test_exit;
    }

    if ((vol2 = FPOpenVol(Conn2, Vol)) == 0xffff) {
        test_failed();
        goto test_exit;
    }

    /* Create directories with first connection */
    if ((dir_id = FPCreateDir(Conn, vol1, DIRDID_ROOT, dir)) == 0) {
        test_failed();
    }

    if ((subdir1_id = FPCreateDir(Conn, vol1, dir_id, subdir1)) == 0) {
        test_failed();
    }

    if ((subdir2_id = FPCreateDir(Conn, vol1, subdir1_id, subdir2)) == 0) {
        test_failed();
    }

    /* Create file and get CNID */
    FAIL(FPCreateFile(Conn, vol1,  0, subdir2_id, "file1"));
    FAIL(FPGetFileDirParams(Conn, vol1, subdir2_id, "file1", 0, bitmap));
    filedir.isdir = 0;
    afp_filedir_unpack(&filedir, dsi->data + ofs, 0, bitmap);
    file_id = filedir.did;
    /* Move and rename dir with second connection */
    FAIL(FPMoveAndRename(Conn2, vol2, dir_id, dir_id, subdir1, renamedsubdir1));
    /* check CNID */
    FAIL(FPGetFileDirParams(Conn, vol1, subdir2_id, "file1", 0, bitmap));
    filedir.isdir = 0;
    afp_filedir_unpack(&filedir, dsi->data + ofs, 0, bitmap);

    if (filedir.did != file_id) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %x should be %x\n", filedir.did, subdir1_id);
        }

        test_failed();
    }

fin:
    FAIL(FPCloseVol(Conn2, vol2));
    FAIL(FPDelete(Conn, vol1, subdir2_id, "file1"))
    FAIL(FPDelete(Conn, vol1, subdir2_id, ""));
    FAIL(FPDelete(Conn, vol1, subdir1_id, ""));
    FAIL(FPDelete(Conn, vol1, dir_id, ""));
test_exit:
    exit_test("Dircache:test504: rename topdir, stat file in subdir of renamed topdir");
}

/*! rename dir, stat subdir in renamed dir */
STATIC void test505()
{
    const DSI *dsi = &Conn->dsi;
    uint16_t vol1 = VolID;
    uint16_t vol2;
    char *dir = "t505 dir";
    char *subdir1 = "t505 subdir1";
    char *subdir2 = "t505 subdir2";
    char *renamedsubdir1 = "t505 renamedsubdir1";
    uint32_t dir_id, subdir1_id, subdir2_id;
    int ofs = 3 * sizeof(uint16_t);
    struct afp_filedir_parms filedir;
    uint16_t bitmap = (1 << DIRPBIT_DID) | (1 << DIRPBIT_LNAME);
    ENTER_TEST

    if (!Conn2) {
        test_skipped(T_CONN2);
        goto test_exit;
    }

    if ((vol2 = FPOpenVol(Conn2, Vol)) == 0xffff) {
        test_failed();
        goto test_exit;
    }

    /* Create directories with first connection */
    if ((dir_id = FPCreateDir(Conn, vol1, DIRDID_ROOT, dir)) == 0) {
        test_failed();
    }

    if ((subdir1_id = FPCreateDir(Conn, vol1, dir_id, subdir1)) == 0) {
        test_failed();
    }

    if ((subdir2_id = FPCreateDir(Conn, vol1, subdir1_id, subdir2)) == 0) {
        test_failed();
    }

    /* Move and rename dir with second connection */
    FAIL(FPMoveAndRename(Conn2, vol2, dir_id, dir_id, subdir1, renamedsubdir1));
    /* Manually check name and CNID */
    FAIL(FPGetFileDirParams(Conn, vol1, subdir2_id, "", 0, bitmap));
    filedir.isdir = 1;
    afp_filedir_unpack(&filedir, dsi->data + ofs, 0, bitmap);

    if (filedir.did != subdir2_id) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %x should be %x\n", filedir.did, subdir2_id);
        }

        test_failed();
    }

    if (strcmp(filedir.lname, subdir2
              )) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %s should be %s\n", filedir.lname, subdir2);
        }

        test_failed();
    }

fin:
    FAIL(FPCloseVol(Conn2, vol2));
    FAIL(FPDelete(Conn, vol1, subdir2_id, ""));
    FAIL(FPDelete(Conn, vol1, subdir1_id, ""));
    FAIL(FPDelete(Conn, vol1, dir_id, ""));
test_exit:
    exit_test("Dircache:test505: rename dir, stat subdir in renamed dir");
}

/*! stat subdir in poisened path */
STATIC void test506()
{
    const DSI *dsi = &Conn->dsi;
    uint16_t vol1 = VolID;
    uint16_t vol2;
    char *dir = "t506 dir";
    char *subdir1 = "t506 subdir1";
    char *subdir2 = "t506 subdir2";
    char *renamedsubdir1 = "t506 renamedsubdir1";
    uint32_t dir_id, subdir1_id, subdir2_id, poisondir_id;
    int ofs = 3 * sizeof(uint16_t);
    struct afp_filedir_parms filedir;
    uint16_t bitmap = (1 << DIRPBIT_DID) | (1 << DIRPBIT_LNAME);
    ENTER_TEST

    if (!Conn2) {
        test_skipped(T_CONN2);
        goto test_exit;
    }

    if ((vol2 = FPOpenVol(Conn2, Vol)) == 0xffff) {
        test_failed();
        goto test_exit;
    }

    /* Create directories with first connection */
    if ((dir_id = FPCreateDir(Conn, vol1, DIRDID_ROOT, dir)) == 0) {
        test_failed();
    }

    if ((subdir1_id = FPCreateDir(Conn, vol1, dir_id, subdir1)) == 0) {
        test_failed();
    }

    if ((subdir2_id = FPCreateDir(Conn, vol1, subdir1_id, subdir2)) == 0) {
        test_failed();
    }

    /* Move and rename dir with second connection */
    FAIL(FPMoveAndRename(Conn2, vol2, dir_id, dir_id, subdir1, renamedsubdir1));

    /* Re-create renamed directory */
    if ((poisondir_id = FPCreateDir(Conn2, vol2, dir_id, subdir1)) == 0) {
        test_failed();
    }

    /* Manually check name and CNID */
    FAIL(FPGetFileDirParams(Conn, vol1, subdir2_id, "", 0, bitmap));
    filedir.isdir = 1;
    afp_filedir_unpack(&filedir, dsi->data + ofs, 0, bitmap);

    if (filedir.did != subdir2_id) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %x should be %x\n", filedir.did, subdir2_id);
        }

        test_failed();
    }

    if (strcmp(filedir.lname, subdir2
              )) {
        if (!Quiet) {
            fprintf(stdout, "\tFAILED %s should be %s\n", filedir.lname, subdir2);
        }

        test_failed();
    }

fin:
    FAIL(FPCloseVol(Conn2, vol2));
    FAIL(FPDelete(Conn, vol1, subdir2_id, ""));
    FAIL(FPDelete(Conn, vol1, subdir1_id, ""));
    FAIL(FPDelete(Conn, vol1, poisondir_id, ""));
    FAIL(FPDelete(Conn, vol1, dir_id, ""));
test_exit:
    exit_test("Dircache:test506: stat subdir in poisoned path");
}

void Dircache_attack_test()
{
    ENTER_TESTSET
    test500();
    test501();
    test502();
    test503();
    test504();
    test505();
    test506();
}
