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

/* -------------------------------- */
static int really_ro()
{
    int dir;
    char *ndir = "read only dir";

    if ((dir = FPCreateDir(Conn, VolID, DIRDID_ROOT, ndir))) {
        test_nottested();
        FAIL(FPDelete(Conn, VolID,  dir, ""))
        return 0;
    }

    return 1;
}

/* -------------------------------- */
static void check_test(unsigned int err)
{
    if (err != ntohl(AFPERR_VLOCK) && err != ntohl(AFPERR_ACCESS)) {
        test_failed();
    }
}

/* ----------------- */
STATIC void test510()
{
    char *ndir = "read only dir";
    char *nfile = "read only file";
    int  ofs =  4 * sizeof(uint16_t);
    struct afp_filedir_parms filedir;
    char *file = NULL;
    char *file1 = NULL;
    char *dir = NULL;
    int  fid;
    int  did;
    uint16_t bitmap;
    uint16_t fork, fork1;
    struct afp_volume_parms parms;
    DSI *dsi = &Conn->dsi;
    uint32_t flen;
    unsigned int ret;
    ENTER_TEST

    if (!Test) {
        if (!Quiet) {
            fprintf(stdout,
                    "Has to be tested with a read only volume, two files and one dir.\n");
        }

        test_skipped(T_SINGLE);
        goto test_exit;
    }

    VolID = FPOpenVol(Conn, Vol);

    if (VolID == 0xffff) {
        test_nottested();
        goto test_exit;
    }

    if (!really_ro()) {
        goto test_exit;
    }

    /* get a directory */
    /* FIXME: FPEnumerate* uses my_dsi_data_receive. See afphelper.c:delete_directory_tree() */
    bitmap = (1 << DIRPBIT_LNAME);
    ret = FPEnumerateFull(Conn, VolID, 1, 1, 800,  DIRDID_ROOT, "", 0, bitmap);

    if (ret) {
        test_nottested();
        goto fin;
    }

    filedir.isdir = 1;
    afp_filedir_unpack(&filedir, dsi->data + ofs, 0, bitmap);
    dir = strdup(filedir.lname);

    if (!dir) {
        test_nottested();
        goto fin;
    }

    if (!Quiet) {
        fprintf(stdout, "Directory %s\n", dir);
    }

    /* get a file */
    bitmap = (1 << FILPBIT_LNAME);
    ret = FPEnumerateFull(Conn, VolID, 1, 1, 800,  DIRDID_ROOT, "", bitmap, 0);

    if (ret) {
        test_nottested();
        goto fin;
    }

    filedir.isdir = 0;
    afp_filedir_unpack(&filedir, dsi->data + ofs, bitmap, 0);
    file = strdup(filedir.lname);

    if (!file) {
        test_nottested();
        goto fin;
    }

    if (!Quiet) {
        fprintf(stdout, "File %s\n", file);
    }

    /* get a second file */
    bitmap = (1 << FILPBIT_LNAME);
    ret = FPEnumerateFull(Conn, VolID, 2, 1, 800,  DIRDID_ROOT, "", bitmap, 0);

    if (ret) {
        test_nottested();
        goto fin;
    }

    filedir.isdir = 0;
    afp_filedir_unpack(&filedir, dsi->data + ofs, bitmap, 0);
    file1 = strdup(filedir.lname);

    if (!file1) {
        test_nottested();
        goto fin;
    }

    if (!Quiet) {
        fprintf(stdout, "Second file %s\n", file1);
    }

    /* TESTS */
    /* -- file.c -- */
    ret = FPCreateFile(Conn, VolID,  0, DIRDID_ROOT, nfile);
    FAIL(ntohl(AFPERR_VLOCK) != ret)

    if (!ret) {
        FAIL(FPDelete(Conn, VolID, DIRDID_ROOT, nfile))
    }

    bitmap = (1 << FILPBIT_FINFO) | (1 << FILPBIT_CDATE) | (1 << FILPBIT_BDATE) |
             (1 << FILPBIT_MDATE);

    if (FPGetFileDirParams(Conn, VolID,  DIRDID_ROOT, file, bitmap, 0)) {
        test_failed();
    } else {
        filedir.isdir = 0;
        afp_filedir_unpack(&filedir, dsi->data + 3 * sizeof(uint16_t), bitmap, 0);
        ret  = FPSetFileParams(Conn, VolID,  DIRDID_ROOT, file, bitmap, &filedir);
        check_test(ret);
    }

    FAIL(ntohl(AFPERR_VLOCK) != FPCopyFile(Conn, VolID, DIRDID_ROOT, VolID,
                                           DIRDID_ROOT, file, "", nfile))

    if (!(get_vol_attrib(VolID) & VOLPBIT_ATTR_FILEID) && !Quiet) {
        fprintf(stdout, "FileID calls Not supported\n");
    } else {
        /* with cnid db, if the volume isn't defined as read only in afp.conf
         * CreateID, DeleteID don't return VLOCK
        */
        ret = FPCreateID(Conn, VolID, DIRDID_ROOT, file);

        if (not_valid(ret, /* MAC */AFPERR_VLOCK, AFPERR_EXISTID)) {
            test_failed();
        }

        fid = get_fid(Conn, VolID, DIRDID_ROOT, file);

        if (fid) {
            ret = FPDeleteID(Conn, VolID, filedir.did);

            if (not_valid(ret, /* MAC */AFPERR_VLOCK, AFPERR_NOID)) {
                test_failed();
            }
        }
    }

    ret = FPExchangeFile(Conn, VolID, DIRDID_ROOT, DIRDID_ROOT, file, file1);

    if (not_valid(ret, /* MAC */AFPERR_VLOCK, AFPERR_ACCESS)) {
        test_failed();
    }

    /* -- volume.c -- */
    bitmap = (1 << VOLPBIT_MDATE);
    FAIL(FPGetVolParam(Conn, VolID, bitmap))
    afp_volume_unpack(&parms, dsi->commands + sizeof(uint16_t), bitmap);
    bitmap = (1 << VOLPBIT_BDATE);
    parms.bdate = parms.mdate;
    ret = FPSetVolParam(Conn, VolID, bitmap, &parms);
    check_test(ret);
    /* -- filedir.c -- */
    bitmap = (1 << FILPBIT_FINFO) | (1 << FILPBIT_CDATE) | (1 << FILPBIT_BDATE) |
             (1 << FILPBIT_MDATE);

    if (FPGetFileDirParams(Conn, VolID,  DIRDID_ROOT, file, bitmap, 0)) {
        test_failed();
    } else {
        filedir.isdir = 0;
        afp_filedir_unpack(&filedir, dsi->data + 3 * sizeof(uint16_t), bitmap, 0);
        ret = FPSetFilDirParam(Conn, VolID,  DIRDID_ROOT, file, bitmap, &filedir);
        check_test(ret);
    }

    bitmap = (1 << DIRPBIT_FINFO) | (1 << DIRPBIT_CDATE) | (1 << DIRPBIT_BDATE) |
             (1 << DIRPBIT_MDATE);

    if (FPGetFileDirParams(Conn, VolID,  DIRDID_ROOT, dir, 0, bitmap)) {
        test_failed();
    } else {
        filedir.isdir = 1;
        afp_filedir_unpack(&filedir, dsi->data + 3 * sizeof(uint16_t), 0, bitmap);
        ret = FPSetFilDirParam(Conn, VolID,  DIRDID_ROOT, dir, bitmap, &filedir);
        check_test(ret);
    }

    FAIL(htonl(AFPERR_VLOCK) != FPRename(Conn, VolID, DIRDID_ROOT, file, nfile))
    FAIL(htonl(AFPERR_VLOCK) != FPRename(Conn, VolID, DIRDID_ROOT, dir, ndir))
    bitmap = (1 << DIRPBIT_OFFCNT);

    if (FPGetFileDirParams(Conn, VolID,  DIRDID_ROOT, dir, 0, bitmap)) {
        test_failed();
    } else {
        filedir.isdir = 1;
        afp_filedir_unpack(&filedir, dsi->data + 3 * sizeof(uint16_t), 0, bitmap);
        ret = FPDelete(Conn, VolID, DIRDID_ROOT, dir);

        if (ret != htonl(AFPERR_VLOCK)) {
            if (!filedir.offcnt) {
                test_failed();
            } else if (!Quiet) {
                fprintf(stdout, "\tWARNING \"%s\", not empty FPDelete skipped\n", dir);
            }
        }
    }

    FAIL(htonl(AFPERR_VLOCK) != FPDelete(Conn, VolID, DIRDID_ROOT, file))
    FAIL(htonl(AFPERR_VLOCK) != FPMoveAndRename(Conn, VolID, DIRDID_ROOT,
            DIRDID_ROOT, file, nfile))
    FAIL(htonl(AFPERR_VLOCK) != FPMoveAndRename(Conn, VolID, DIRDID_ROOT,
            DIRDID_ROOT, dir, ndir))
    /* -- directory.c -- */
    bitmap = (1 << DIRPBIT_FINFO) | (1 << DIRPBIT_CDATE) | (1 << DIRPBIT_BDATE) |
             (1 << DIRPBIT_MDATE);

    if (FPGetFileDirParams(Conn, VolID,  DIRDID_ROOT, dir, 0, bitmap)) {
        test_failed();
    } else {
        filedir.isdir = 1;
        afp_filedir_unpack(&filedir, dsi->data + 3 * sizeof(uint16_t), 0, bitmap);
        ret = FPSetDirParms(Conn, VolID,  DIRDID_ROOT, dir, bitmap, &filedir);
        check_test(ret);
    }

    if ((did = FPCreateDir(Conn, VolID, DIRDID_ROOT, ndir))) {
        test_nottested();
        FAIL(FPDelete(Conn, VolID,  did, ""))
        goto test_exit;
    }

    /* -- fork.c -- */
    bitmap = 0;
    fork = FPOpenFork(Conn, VolID, OPENFORK_DATA, bitmap, DIRDID_ROOT, file,
                      OPENACC_RD);

    if (!fork) {
        test_failed();
    }

    fork1 = FPOpenFork(Conn, VolID, OPENFORK_DATA, bitmap, DIRDID_ROOT, file,
                       OPENACC_WR);

    if (fork1) {
        test_failed();
        FAIL(FPCloseFork(Conn, fork1))
    }

    FAIL(FPCloseFork(Conn, fork))
    fork1 = FPOpenFork(Conn, VolID, OPENFORK_DATA, bitmap, DIRDID_ROOT, file,
                       OPENACC_WR);

    if (fork1) {
        test_failed();
        FAIL(FPCloseFork(Conn, fork1))
    }

    /* --------- */
    fork = FPOpenFork(Conn, VolID, OPENFORK_DATA, bitmap, DIRDID_ROOT, file,
                      OPENACC_RD);

    if (!fork) {
        test_failed();
    }

    bitmap = (1 << FILPBIT_DFLEN);
    ret = FPSetForkParam(Conn, fork, bitmap, 0);
    check_test(ret);

    if (FPGetForkParam(Conn, fork, bitmap)) {
        test_failed();
    } else if ((flen = get_forklen(dsi, OPENFORK_DATA))) {
        FAIL(FPRead(Conn, fork, 0, min(100, flen), Data))
    }

    FAIL(FPCloseFork(Conn, fork))
    /* ------------- */
    bitmap = 0;
    fork = FPOpenFork(Conn, VolID, OPENFORK_RSCS, bitmap, DIRDID_ROOT, file,
                      OPENACC_RD);

    if (!fork) {
        test_failed();
    }

    fork1 = FPOpenFork(Conn, VolID, OPENFORK_RSCS, bitmap, DIRDID_ROOT, file,
                       OPENACC_WR);

    if (fork1) {
        test_failed();
        FAIL(FPCloseFork(Conn, fork1))
    }

    FAIL(FPCloseFork(Conn, fork))
    fork1 = FPOpenFork(Conn, VolID, OPENFORK_RSCS, bitmap, DIRDID_ROOT, file,
                       OPENACC_WR);

    if (fork1) {
        test_failed();
        FAIL(FPCloseFork(Conn, fork1))
    }

    /* --------- */
    fork = FPOpenFork(Conn, VolID, OPENFORK_RSCS, bitmap, DIRDID_ROOT, file,
                      OPENACC_RD);

    if (!fork) {
        test_failed();
    }

    ret = FPSetForkParam(Conn, fork, (1 << FILPBIT_RFLEN), 100);
    check_test(ret);
fin:
    free(dir);
    free(file);
    free(file1);
    FAIL(FPCloseVol(Conn, VolID))
test_exit:
    exit_test("Readonly:test510: Access files and directories on a read only volume");
}

void Readonly_test()
{
    ENTER_TESTSET
    test510();
}
