/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 * Editor Settings: expandtabs and use 4 spaces for indentation */

/*
 * Copyright Likewise Software
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.  You should have received a copy of the GNU General
 * Public License along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

/*
 * Copyright (C) Likewise Software. All rights reserved.
 *
 * Module Name:
 *
 *        sharedb.c
 *
 * Abstract:
 *
 *        Likewise IO (LWIO) - SRV
 *
 *        Share API
 *
 *        Mapping functions
 *
 * Authors: Sriram Nambakam (snambakam@likewise.com)
 *
 */

#include "includes.h"

static
NTSTATUS
SrvShareMapSpecificToWindowsPath(
    IN  PWSTR  pwszFSPrefix,
    IN  PWSTR  pwszRootPath,
    IN  PWSTR  pwszInputPath,
    OUT PWSTR* ppwszPath
    );

NTSTATUS
SrvShareMapIdToServiceStringW(
    IN SHARE_STYPE Service,
    OUT PWSTR* ppwszService
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PWSTR pwszShareType = NULL;
    PSTR  pszShareType = NULL;

    ntStatus = SrvShareMapIdToServiceStringA(Service, &pszShareType);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SrvMbsToWc16s(pszShareType, &pwszShareType);
    BAIL_ON_NT_STATUS(ntStatus);

    *ppwszService = pwszShareType;

cleanup:

    SRV_SAFE_FREE_MEMORY(pszShareType);

    return ntStatus;

error:

    *ppwszService = NULL;

    goto cleanup;
}

NTSTATUS
SrvShareMapIdToServiceStringA(
    IN SHARE_STYPE Service,
    OUT PSTR* ppszService
    )
{
    NTSTATUS ntStatus = 0;
    PCSTR    pszId = NULL;
    PSTR     pszService = NULL;

    switch (Service)
    {
        case SHARE_STYPE_DISKTREE:

            pszId = LWIO_SRV_SHARE_STRING_ID_DISK_A;

            break;

        case SHARE_STYPE_PRINTQ:

            pszId = LWIO_SRV_SHARE_STRING_ID_PRINTER_A;

            break;

        case SHARE_STYPE_DEVICE:

            pszId = LWIO_SRV_SHARE_STRING_ID_COMM_A;

            break;

        case SHARE_STYPE_IPC:

            pszId = LWIO_SRV_SHARE_STRING_ID_IPC_A;

            break;

        default:

            ntStatus = STATUS_NOT_FOUND;

            break;
    }
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SMBAllocateString(
                    pszId,
                    &pszService);
    BAIL_ON_NT_STATUS(ntStatus);

    *ppszService = pszService;

cleanup:

    return ntStatus;

error:

    *ppszService = NULL;

    goto cleanup;
}

NTSTATUS
SrvShareMapServiceStringToIdA(
    IN PCSTR pszService,
    IN OUT SHARE_STYPE* pService
    )
{
    NTSTATUS ntStatus = 0;
    SHARE_STYPE service = SHARE_STYPE_UNKNOWN;

    if (IsNullOrEmptyString(pszService))
    {
        ntStatus = STATUS_NOT_FOUND;
    }
    else if (!strcmp(pszService, LWIO_SRV_SHARE_STRING_ID_IPC_A))
    {
        service = SHARE_STYPE_IPC;
    }
    else if (!strcmp(pszService, LWIO_SRV_SHARE_STRING_ID_DISK_A))
    {
        service = SHARE_STYPE_DISKTREE;
    }
    else if (!strcmp(pszService, LWIO_SRV_SHARE_STRING_ID_COMM_A))
    {
        service = SHARE_STYPE_DEVICE;
    }
    else if (!strcmp(pszService, LWIO_SRV_SHARE_STRING_ID_PRINTER_A))
    {
        service = SHARE_STYPE_PRINTQ;
    }
    else
    {
        ntStatus = STATUS_NOT_FOUND;
    }
    BAIL_ON_NT_STATUS(ntStatus);

    *pService = service;

cleanup:

    return ntStatus;

error:

    *pService = SHARE_STYPE_UNKNOWN;

    goto cleanup;
}

NTSTATUS
SrvShareMapServiceStringToIdW(
    IN PWSTR pwszService,
    OUT SHARE_STYPE* pService
    )
{
    NTSTATUS      ntStatus  = STATUS_SUCCESS;
    wchar16_t     wszIpc[]  = LWIO_SRV_SHARE_STRING_ID_IPC_W;
    wchar16_t     wszDisk[] = LWIO_SRV_SHARE_STRING_ID_DISK_W;
    wchar16_t     wszComm[] = LWIO_SRV_SHARE_STRING_ID_COMM_W;
    wchar16_t     wszPtr[]  = LWIO_SRV_SHARE_STRING_ID_PRINTER_W;
    SHARE_STYPE service   = SHARE_STYPE_UNKNOWN;

    if (IsNullOrEmptyString(pwszService))
    {
        ntStatus = STATUS_NOT_FOUND;
    }
    else if (!wc16scmp(pwszService, &wszIpc[0]))
    {
        service = SHARE_STYPE_IPC;
    }
    else if (!wc16scmp(pwszService, &wszDisk[0]))
    {
        service = SHARE_STYPE_DISKTREE;
    }
    else if (!wc16scmp(pwszService, &wszComm[0]))
    {
        service = SHARE_STYPE_DEVICE;
    }
    else if (!wc16scmp(pwszService, &wszPtr[0]))
    {
        service = SHARE_STYPE_PRINTQ;
    }
    else
    {
        ntStatus = STATUS_NOT_FOUND;
    }
    BAIL_ON_NT_STATUS(ntStatus);

    *pService = service;

cleanup:

    return ntStatus;

error:

    *pService = SHARE_STYPE_UNKNOWN;

    goto cleanup;
}

NTSTATUS
SrvShareMapFromWindowsPath(
    IN PWSTR pwszClientWin32Path,
    OUT PWSTR* ppwszIoMgrPath
    )
{
    NTSTATUS  ntStatus = 0;
    wchar16_t wszBackslash[2] = {'\\', 0};
    wchar16_t wszFwdslash[2] = {'/', 0};
    wchar16_t wszColon[2] = {':', 0};
    PWSTR pwszLocalWin32Path = NULL;
    PWSTR pwszPathReadCursor = NULL;
    PWSTR     pwszPathWriteCursor = NULL;
    PWSTR     pwszIoMgrPath = NULL;
    size_t    sLocalWin32PathLen = 0;
    size_t    sFSPrefixLen = 3;
    size_t    sFSRootLen = 0;
    size_t    sRequiredLen = 0;
    PWSTR pwszFileSystemRoot = gSharesApiGlobals.pwszFileSystemRoot;

    if (!pwszClientWin32Path || !*pwszClientWin32Path)
    {
        ntStatus = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = SrvOEMSharePathClientWin32ToLocalWin32(pwszClientWin32Path,
                                                      &pwszLocalWin32Path);
    BAIL_ON_NT_STATUS(ntStatus);

    sLocalWin32PathLen = wc16slen(pwszLocalWin32Path);
    pwszPathReadCursor = pwszLocalWin32Path;

    sFSRootLen = wc16slen(pwszFileSystemRoot);

    if ((sLocalWin32PathLen < sFSPrefixLen) ||
        ((pwszLocalWin32Path[1] != wszColon[0]) &&
         !(pwszLocalWin32Path[2] == wszBackslash[0] ||
           pwszLocalWin32Path[2] == wszFwdslash[0])))
    {
        ntStatus = STATUS_OBJECT_PATH_SYNTAX_BAD;
        BAIL_ON_NT_STATUS(ntStatus);
    }
    else
    {
        sRequiredLen += sFSRootLen * sizeof(wchar16_t);
        pwszPathReadCursor += sFSPrefixLen;
    }

    if (!pwszPathReadCursor || !*pwszPathReadCursor)
    {
        ntStatus = STATUS_OBJECT_PATH_SYNTAX_BAD;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    sRequiredLen += sizeof(wchar16_t); // path delimiter

    while (pwszPathReadCursor &&
           *pwszPathReadCursor &&
           ((*pwszPathReadCursor == wszBackslash[0]) ||
            (*pwszPathReadCursor == wszFwdslash[0])))
    {
        pwszPathReadCursor++;
    }

    // The rest of the path
    sRequiredLen += wc16slen(pwszPathReadCursor) * sizeof(wchar16_t);
    sRequiredLen += sizeof(wchar16_t);

    ntStatus = SrvAllocateMemory(sRequiredLen, (PVOID*)&pwszIoMgrPath);
    BAIL_ON_NT_STATUS(ntStatus);

    pwszPathReadCursor = pwszLocalWin32Path;
    pwszPathWriteCursor = pwszIoMgrPath;

    pwszPathReadCursor += sFSPrefixLen;
    memcpy((PBYTE)pwszPathWriteCursor, (PBYTE)pwszFileSystemRoot,
            sFSRootLen * sizeof(wchar16_t));
    pwszPathWriteCursor += sFSRootLen;
    *pwszPathWriteCursor++ = wszBackslash[0];

    while (pwszPathReadCursor &&
           *pwszPathReadCursor &&
           ((*pwszPathReadCursor == wszBackslash[0]) ||
            (*pwszPathReadCursor == wszFwdslash[0])))
    {
        pwszPathReadCursor++;
    }

    while (pwszPathReadCursor && *pwszPathReadCursor)
    {
        if (*pwszPathReadCursor == wszFwdslash[0])
        {
            *pwszPathWriteCursor++ = wszBackslash[0];
        }
        else
        {
            *pwszPathWriteCursor++ = *pwszPathReadCursor;
        }
        pwszPathReadCursor++;
    }

    *ppwszIoMgrPath = pwszIoMgrPath;

cleanup:

    LwRtlWC16StringFree(&pwszLocalWin32Path);

    return ntStatus;

error:

    *ppwszIoMgrPath = NULL;

    SRV_SAFE_FREE_MEMORY(pwszIoMgrPath);

    goto cleanup;
}

NTSTATUS
SrvShareMapToWindowsPath(
    IN PWSTR pwszIoMgrPath,
    OUT PWSTR* ppwszClientWin32Path
    )
{
    NTSTATUS ntStatus = 0;
    PWSTR pwszClientWin32Path = NULL;
    wchar16_t wszFileSystemPrefix[] = LWIO_SRV_FILE_SYSTEM_PREFIX_W;
    PWSTR pwszLocalWin32Path = NULL;
    PWSTR pwszFileSystemRoot = gSharesApiGlobals.pwszFileSystemRoot;

    ntStatus = SrvShareMapSpecificToWindowsPath(
                    &wszFileSystemPrefix[0],
                    pwszFileSystemRoot,
                    pwszIoMgrPath,
                    &pwszLocalWin32Path);
    if (ntStatus == STATUS_OBJECT_PATH_SYNTAX_BAD)
    {
        wchar16_t wszPipeSystemRoot[] = LWIO_SRV_PIPE_SYSTEM_ROOT_W;

        ntStatus = SrvShareMapSpecificToWindowsPath(
                            &wszFileSystemPrefix[0],
                            &wszPipeSystemRoot[0],
                            pwszIoMgrPath,
                            &pwszClientWin32Path);
    }
    else if (ntStatus == STATUS_SUCCESS)
    {
        ntStatus = SrvOEMSharePathLocalWin32ToClientWin32(pwszLocalWin32Path,
                                                          &pwszClientWin32Path);
        BAIL_ON_NT_STATUS(ntStatus);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    *ppwszClientWin32Path = pwszClientWin32Path;

cleanup:

    LwRtlWC16StringFree(&pwszLocalWin32Path);

    return ntStatus;

error:

    *ppwszClientWin32Path = NULL;

    if (pwszClientWin32Path)
    {
        SrvFreeMemory(pwszClientWin32Path);
    }

    goto cleanup;
}

static
NTSTATUS
SrvShareMapSpecificToWindowsPath(
    IN  PWSTR  pwszFSPrefix,
    IN  PWSTR  pwszRootPath,
    IN  PWSTR  pwszInputPath,
    OUT PWSTR* ppwszPath
    )
{
    NTSTATUS  ntStatus = 0;
    wchar16_t wszBackslash[2] = {'\\', 0};
    wchar16_t wszFwdslash[2] = {'/', 0};
    PWSTR     pwszPathReadCursor = pwszInputPath;
    PWSTR     pwszPathWriteCursor = NULL;
    PWSTR     pwszPath = NULL;
    size_t    sInputPathLen = 0;
    size_t    sFSPrefixLen = 0;
    size_t    sFSRootLen = 0;
    size_t    sRequiredLen = 0;

    if (!pwszInputPath || !*pwszInputPath)
    {
        ntStatus = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(ntStatus);
    }
    sInputPathLen = wc16slen(pwszInputPath);

    sFSPrefixLen = wc16slen(pwszFSPrefix);
    sFSRootLen = wc16slen(pwszRootPath);

    if ((sInputPathLen < sFSRootLen) ||
        memcmp((PBYTE)pwszPathReadCursor,
               (PBYTE)pwszRootPath,
               sFSRootLen * sizeof(wchar16_t)))
    {
        ntStatus = STATUS_OBJECT_PATH_SYNTAX_BAD;
        BAIL_ON_NT_STATUS(ntStatus);
    }
    else
    {
        sRequiredLen += sFSPrefixLen * sizeof(wchar16_t);
        pwszPathReadCursor += sFSRootLen;
    }

    while (pwszPathReadCursor &&
           *pwszPathReadCursor &&
           ((*pwszPathReadCursor == wszBackslash[0]) ||
            (*pwszPathReadCursor == wszFwdslash[0])))
    {
        pwszPathReadCursor++;
    }

    // The rest of the path
    sRequiredLen += wc16slen(pwszPathReadCursor) * sizeof(wchar16_t);
    sRequiredLen += sizeof(wchar16_t);

    ntStatus = SrvAllocateMemory(sRequiredLen, (PVOID*)&pwszPath);
    BAIL_ON_NT_STATUS(ntStatus);

    pwszPathReadCursor = pwszInputPath;
    pwszPathWriteCursor = pwszPath;

    pwszPathReadCursor += sFSRootLen;
    memcpy((PBYTE)pwszPathWriteCursor, (PBYTE)pwszFSPrefix,
            sFSPrefixLen * sizeof(wchar16_t));
    pwszPathWriteCursor += sFSPrefixLen;

    while (pwszPathReadCursor &&
           *pwszPathReadCursor &&
           ((*pwszPathReadCursor == wszBackslash[0]) ||
            (*pwszPathReadCursor == wszFwdslash[0])))
    {
        pwszPathReadCursor++;
    }

    while (pwszPathReadCursor && *pwszPathReadCursor)
    {
        if (*pwszPathReadCursor == wszFwdslash[0])
        {
            *pwszPathWriteCursor++ = wszBackslash[0];
        }
        else
        {
            *pwszPathWriteCursor++ = *pwszPathReadCursor;
        }
        pwszPathReadCursor++;
    }

    *ppwszPath = pwszPath;

cleanup:

    return ntStatus;

error:

    *ppwszPath = NULL;

    SRV_SAFE_FREE_MEMORY(pwszPath);

    goto cleanup;
}
