/* Editor Settings: expandtabs and use 4 spaces for indentation
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 */

/*
 * Copyright Likewise Software
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the license, or (at
 * your option) any later version.
 *
 * This library 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 Lesser
 * General Public License for more details.  You should have received a copy
 * of the GNU Lesser 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
 * LESSER 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
 */

#include "includes.h"


#if !defined(HAVE_STRNDUP)
static
char* strndup(const char *s, size_t maxlen)
{
    char *ret;
    size_t len, size;

    len = strlen(s);
    len = (len > maxlen) ? maxlen : len;
    size = (len + 1) * sizeof(char);
    ret = (char*) malloc(size);
    strncpy(ret, s, len);

    /* ensure null termination */
    ret[len] = '\0';

    return ret;
}
#endif


/*
 * Handles "escaped" separator character. It allows separator character
 * to be treated as part of value string.
 */
static
PSTR
RpctCleanupSep(
    PSTR s,
    CHAR sep
    )
{
    char *seppos;
    char sepstr[3] = {0};

    if (s == NULL) return s;

    sepstr[0] = '\\';
    sepstr[1] = sep;

    seppos = strstr(s, sepstr);
    while (seppos) {
        while (*seppos) seppos[0] = (seppos)[1];
        seppos++;

        seppos = strstr(s, sepstr);
    }

    return s;
}


/*
 * Splits a string into array of strings given separator character
 */
PSTR*
RpctGetStringList(
    PSTR       list,
    const CHAR sep
    )
{
    char **ret = NULL;
    char *start = NULL;
    char *end = NULL;
    int i = 0;
    int count = 1;

    /* count the elements */
    start = list;
    end = strchr(list, sep);
    while (end++) {
        end = strchr(end, sep);

        /* skip any "escaped" separator */
        if (end > start && *(end-1) == '\\') continue;
        count++;
    }

    ret = (char**) malloc(sizeof(char*) * (count + 1));
    if (!ret) return NULL;

    memset((void*)ret, 0, sizeof(char*) * (count + 1));

    /* copy elements to the array */
    start = list;
    for (i = 0; i < count; i++) {
        end = strchr(start, sep);

        /* skip any "escaped" separator */
        while (start && end > start && *(end-1) == '\\') {
            char *pos = (char*)(end+1);
            end = strchr(pos,sep);
        }

        if (end) {
            ret[i] = strndup(start, (size_t)(end - start));
            start = &end[1];

        } else if (strlen(start)) {
            ret[i] = strdup(start);
        }

        ret[i] = RpctCleanupSep(ret[i], sep);
    }

    return ret;
}


VOID
RpctFreeStringList(
    PSTR *ppArray
    )
{
    PSTR pStr = NULL;
    DWORD dwCount = 0;

    pStr = ppArray[dwCount];
    while (pStr)
    {
        free(pStr);
        pStr = ppArray[++dwCount];
    }

    free(ppArray);
}


/*
 * Returns array of strings for multi-value parameters
 */
static
PSTR*
RpctGetValueList(
    PCSTR List
    )
{
    const char start_list = '[';
    const char end_list = ']';
    const char element_sep = ':';

    size_t input_str_len = 0;
    char *str_list = NULL;
    const char *start = NULL;
    char **ret = NULL;

    /* List has to start with '[' char ... */
    if (List[0] != start_list) return NULL;

    /* ... and has to end with ']' char */
    input_str_len = strlen(List);
    if (List[input_str_len - 1] != end_list) return NULL;

    start = &(List[1]);
    str_list = strndup(start, strlen(start) - 1);
    if (str_list == NULL) return NULL;

    /* Split the list to array of values */
    ret = RpctGetStringList(str_list, element_sep);

    free(str_list);
    return ret;
}


/*
 * Parses string of optional parameters (key=value pairs) into array
 * of structures.
 */
PPARAMETER
RpctGetOptionalParams(
    PSTR    pszOpt,
    PDWORD  pdwCount
    )
{
    const char separator = ',';
    const char equal = '=';
    PPARAMETER pParams = NULL;
    PSTR *ppszOpts = NULL;
    int i;

    if (!pszOpt) {
        if (pdwCount) *pdwCount = 0;
        return NULL;
    }

    if (!pdwCount) return NULL;

    *pdwCount = 0;
    i = 0;

    ppszOpts = RpctGetStringList(pszOpt, separator);
    if (ppszOpts == NULL) return NULL;

    while (ppszOpts[(*pdwCount)]) (*pdwCount)++;

    pParams = (PPARAMETER) malloc(sizeof(*pParams) * (*pdwCount));
    if (pParams == NULL) return NULL;

    while (ppszOpts[i])
    {
        size_t key_size, val_size;
        char *param = ppszOpts[i];
        char *value = strchr(param, equal);

        if (param)
        {
            key_size = (size_t)(value - param);
            pParams[i].key = strndup(param, key_size);

            if (value) {
                val_size = strlen(param) - key_size - 1; /* equal char doesn't count */
                pParams[i].val = strndup((char*)(value + sizeof(char)), val_size);

            } else {
                /* if param is specified but does not equal to anything we can
                   assume it's a boolean flag set */
                pParams[i].val = strdup("1");
            }

            i++;
        }
    }

    for (i = 0; i < (*pdwCount); i++)
    {
        free(ppszOpts[i]);
    }
    free(ppszOpts);

    return pParams;
}


PCSTR
RpctFindValue(
    PPARAMETER pParams,
    DWORD      dwCount,
    PCSTR      pszKey
    )
{
    int i = 0;
    size_t sParamLen = 0;
    size_t sKeyLen = strlen(pszKey);

    for (i = 0; i < dwCount; i++)
    {
        sParamLen = strlen(pParams[i].key);

        if (sParamLen != sKeyLen)
        {
            continue;
        }

        if (memcmp(pParams[i].key, pszKey, sKeyLen) == 0)
        {
            return pParams[i].val;
        }
    }

    return NULL;
}


/*
 * Converts array of strings to array of 2-byte unicode strings
 */
static
PWSTR *
RpctCreateWC16StringList(
    PSTR* pStringList
    )
{
    int list_len = 0;
    wchar16_t **wc16str_list = NULL;
    int i = 0;

    if (pStringList == NULL) return NULL;

    /* count the elements (including terminating zero) */
    while (pStringList[list_len++]);

    /* allocate the wchar16_t strings array */
    wc16str_list = (wchar16_t**) malloc(sizeof(wchar16_t*) * list_len);
    if (wc16str_list == NULL) return NULL;

    memset((void*)wc16str_list, 0, sizeof(wchar16_t*) * list_len);

    /* copy mbs strings to wchar16_t strings */
    for (i = 0; pStringList[i] && i < list_len; i++) {
        wc16str_list[i] = ambstowc16s(pStringList[i]);
        if (wc16str_list[i] == NULL) {
            i--;
            while (i >= 0) {
                free(wc16str_list[i--]);
            }
            free(wc16str_list);

            return NULL;
        }

    }

    return wc16str_list;
}


/*
 * Converts array of sid strings to array of sids
 */
static
PSID*
RpctCreateSidList(
    PSTR* pStringList
    )
{
    int list_len = 0;
    PSID* sid_list = NULL;
    int i = 0;

    if (pStringList == NULL) return NULL;

    /* count the elements (including terminating zero) */
    while (pStringList[list_len++]);

    /* allocate the wchar16_t strings array */
    sid_list = (PSID*) malloc(sizeof(PSID) * list_len);
    if (sid_list == NULL) return NULL;

    memset((void*)sid_list, 0, sizeof(PSID) * list_len);

    /* copy mbs strings to wchar16_t strings */
    for (i = 0; pStringList[i] && i < list_len; i++) {
        RtlAllocateSidFromCString(&sid_list[i], pStringList[i]);
        if (sid_list[i] == NULL) {
            i--;
            while (i >= 0) {
                RTL_FREE(&sid_list[i--]);
            }
            free(sid_list);

            return NULL;
        }
    }

    return sid_list;
}


DWORD
RpctFetchParameterValue(
    PPARAMETER pParams,
    DWORD Count,
    PCSTR Key,
    PARAM_TYPE Type,
    PVOID pValue,
    PCVOID pDefaultValue
    )
{
    DWORD error = ERROR_SUCCESS;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PCSTR value = NULL;
    PSTR *pValueCString = NULL;
    PSTR *pDefaultCString = NULL;
    PCHAR pValueChar = NULL;
    PCHAR pDefaultChar = NULL;
    PWSTR *pValueWC16String = NULL;
    PWSTR **ppValueWC16StringList = NULL;
    PINT pValueInt = NULL;
    PINT pDefaultInt = NULL;
    PUINT pValueUInt = NULL;
    PUINT pDefaultUInt = NULL;
    PSID* pValueSid = NULL;
    PSID** ppValueSidList = NULL;
    PSTR *stringList = NULL;
    DWORD i = 0;
    PSTR *endPointer = NULL;
    PSTR valueString = NULL;
    PBOOLEAN pValueBoolean = NULL;
    PBOOLEAN pDefaultBoolean = NULL;
    DWORD paramKeyValueTabsCount = 4;

    if (pParams && !Key)
    {
        error = ERROR_INVALID_PARAMETER;
        BAIL_ON_WIN_ERROR(error);
    }

    if (!pValue)
    {
        error = ERROR_INVALID_PARAMETER;
        BAIL_ON_WIN_ERROR(error);
    }

    value = RpctFindValue(pParams, Count, Key);
    if (value == NULL)
    {
        //
        // Try to find the value in environment variables list
        //
        value = getenv(Key);
    }

    if (verboseMode || helpMode)
    {
        size_t keyLength = strlen(Key);
        DWORD tabsCount = paramKeyValueTabsCount;

        printf("%s", Key);

        tabsCount -= (keyLength / 8) + 1;
        for (i = 0; i < tabsCount; i++)
        {
            printf("\t");
        }
    }

    switch (Type) {
    case PT_CSTRING:
        pValueCString = (PSTR*)pValue;
        pDefaultCString = (PSTR*)pDefaultValue;
        *pValueCString = NULL;
        if (value)
        {
            error = LwAllocateString(value, pValueCString);
            BAIL_ON_WIN_ERROR(error);
        }
        else if (pDefaultCString && *pDefaultCString)
        {
            error = LwAllocateString(*pDefaultCString, pValueCString);
            BAIL_ON_WIN_ERROR(error);
        }
        else
        {
            pValueCString = NULL;
        }

        if (verboseMode || helpMode)
        {
            printf("= (PSTR)\"%s\"\n",
                   (pValueCString) ? *pValueCString : "<NULL>");
        }
        break;

    case PT_WC16STRING:
        pValueWC16String = (PWSTR*)pValue;
        pDefaultCString = (PSTR*)pDefaultValue;
        *pValueWC16String = NULL;
        if (value)
        {
            error = LwMbsToWc16s(value, pValueWC16String);
            BAIL_ON_WIN_ERROR(error);
        }
        else if (pDefaultCString && *pDefaultCString)
        {
            error = LwMbsToWc16s(*pDefaultCString, pValueWC16String);
            BAIL_ON_WIN_ERROR(error);
        }
        else
        {
            pValueWC16String = NULL;
        }

        if (verboseMode || helpMode)
        {
            if (pValueWC16String && *pValueWC16String)
            {
                error = LwWc16sToMbs(
                              *pValueWC16String,
                              &valueString);
                BAIL_ON_WIN_ERROR(error);
            }

            printf("= (PWSTR)\"%s\"\n",
                   (valueString) ? valueString : "<NULL>");

            LW_SAFE_FREE_MEMORY(valueString);
        }
        break;

    case PT_WC16STRING_LIST:
        ppValueWC16StringList = (PWSTR**)pValue;
        pDefaultCString = (PSTR*)pDefaultValue;
        stringList = NULL;
        if (value)
        {
            stringList = RpctGetValueList(value);
        }
        else if (pDefaultCString && *pDefaultCString)
        {
            stringList = RpctGetValueList(*pDefaultCString);
        }
        else
        {
            stringList = NULL;
        }

        *ppValueWC16StringList = RpctCreateWC16StringList(stringList);

        if (verboseMode || helpMode)
        {
            printf("= (PWSTR*) ");

            if (*ppValueWC16StringList == NULL)
            {
                printf("<NULL>\n");
            }
            else
            {
                DWORD j = 0;

                printf("\n");
                while ((*ppValueWC16StringList)[j])
                {
                    error = LwWc16sToMbs(
                                  *pValueWC16String,
                                  &valueString);
                    BAIL_ON_WIN_ERROR(error);

                    for (i = 0; i < paramKeyValueTabsCount; i++)
                    {
                        printf("\t");
                    }
                    printf("[%u] \"%s\"\n", j, valueString);

                    LW_SAFE_FREE_MEMORY(valueString);
                }
            }
        }

        break;

    case PT_CHAR:
        pValueChar = (char*)pValue;
        pDefaultChar = (char*)pDefaultValue;
        if (value)
        {
            *pValueChar = value[0];
        }
        else if (pDefaultChar)
        {
            *pValueChar = *pDefaultChar;
        }
        else
        {
            *pValueChar = 0;
        }

        if (verboseMode || helpMode)
        {
            printf("= (CHAR) '%c'\n", *pValueChar);
        }

        break;

    case PT_INT32:
        pValueInt = (PINT)pValue;
        pDefaultInt = (PINT)pDefaultValue;
        if (value)
        {
            *pValueInt = (INT)strtol(value, endPointer, 10);
        }
        else if (pDefaultInt)
        {
            *pValueInt = *pDefaultInt;
        }
        else
        {
            *pValueInt = 0;
        }

        if (verboseMode || helpMode)
        {
            printf("= (INT32) %d\n", *pValueInt);
        }

        break;

    case PT_UINT32:
        pValueUInt = (PUINT)pValue;
        pDefaultUInt = (PUINT)pDefaultValue;
        if (value)
        {
            *pValueUInt = (UINT)strtoul(value, endPointer, 10);
        }
        else if (pDefaultUInt)
        {
            *pValueUInt = *pDefaultUInt;
        }
        else
        {
            *pValueUInt = 0;
        }

        if (verboseMode || helpMode)
        {
            printf("= (UINT32) %u\n", *pValueUInt);
        }

        break;

    case PT_BOOLEAN:
        pValueBoolean = (PBOOLEAN)pValue;
        pDefaultBoolean = (PBOOLEAN)pDefaultValue;
        if (value)
        {
            if (strtol(value, endPointer, 10) == 0)
            {
                *pValueBoolean = FALSE;
            }
            else
            {
                *pValueBoolean = TRUE;
            }
        }
        else if (pDefaultBoolean)
        {
            *pValueBoolean = *pDefaultBoolean;
        }
        else
        {
            *pValueBoolean = FALSE;
        }

        if (verboseMode || helpMode)
        {
            printf("= (BOOLEAN) %s\n", (*pValueBoolean) ? "TRUE" : "FALSE");
        }

        break;

    case PT_SID:
        pValueSid = (PSID*)pValue;
        pDefaultCString = (PSTR*)pDefaultValue;
        if (value)
        {
            ntStatus = RtlAllocateSidFromCString(
                                 pValueSid,
                                 value);
            BAIL_ON_NT_STATUS(ntStatus);

            error = LwAllocateString(
                                 value,
                                 &valueString);
            BAIL_ON_WIN_ERROR(error);
        }
        else if (pDefaultCString && *pDefaultCString)
        {
            ntStatus = RtlAllocateSidFromCString(
                                 pValueSid,
                                 *pDefaultCString);
            BAIL_ON_NT_STATUS(ntStatus);

            error = LwAllocateString(
                                 *pDefaultCString,
                                 &valueString);
            BAIL_ON_WIN_ERROR(error);
        }
        else
        {
            pValueSid = NULL;
            valueString = NULL;
        }

        if (verboseMode || helpMode)
        {
            printf("= (PSID) %s\n",
                   (valueString) ? valueString : "<NULL>");
        }

        LW_SAFE_FREE_MEMORY(valueString);

        break;

    case PT_SID_LIST:
        ppValueSidList = (PSID**)pValue;
        pDefaultCString = (PSTR*)pDefaultValue;
        stringList = NULL;
        if (value)
        {
            stringList = RpctGetValueList(value);
        }
        else if (*pDefaultCString)
        {
            stringList = RpctGetValueList(*pDefaultCString);
        }
        else
        {
            stringList = NULL;
        }

        *ppValueSidList = RpctCreateSidList(stringList);

        if (verboseMode || helpMode)
        {
            printf("= (PSID*) ");

            if (*ppValueSidList == NULL)
            {
                printf("<NULL>\n");
            }
            else
            {
                DWORD j = 0;

                printf("\n");
                while ((*ppValueSidList)[j])
                {
                    ntStatus = RtlAllocateCStringFromSid(
                                     &valueString,
                                     (*ppValueSidList)[j]);
                    BAIL_ON_NT_STATUS(ntStatus);

                    for (i = 0; i < paramKeyValueTabsCount; i++)
                    {
                        printf("\t");
                    }
                    printf("[%u] %s\n", j, valueString);

                    RTL_FREE(&valueString);
                }
            }
        }

        break;

    default:
        error = ERROR_INVALID_PARAMETER;
        BAIL_ON_WIN_ERROR(error);
        break;
    }

error:
    if (stringList)
    {
        i = 0;
        while (stringList[i])
        {
            free(stringList[i++]);
        }

        free(stringList);
    }

    LW_SAFE_FREE_MEMORY(valueString);

    if (error || ntStatus)
    {
        error = LwNtStatusToWin32Error(ntStatus);
    }

    return error;
}


#define CHECK_PARAM_INFO_PTR(v)                             \
    if ((v) == NULL) {                                      \
        printf("warning: Parameter value ptr is NULL!\n");  \
        return;                                             \
    }


static void ParamInfoStr(void *value)
{
    char *v = NULL;

    CHECK_PARAM_INFO_PTR(value);

    v = strdup((char*)value);
    if (!v) goto done;

    printf("(char*)\"%s\"\n", v);

done:
    free(v);
}


static void ParamInfoWc16Str(void *value)
{
    char *v = NULL;

    CHECK_PARAM_INFO_PTR(value);

    v = awc16stombs((wchar16_t*)value);
    if (!v) goto done;

    printf("(wchar16_t*)\"%s\"\n", v);

done:
    free(v);
}


static void ParamInfoChar(void *value)
{
    char *v = NULL;

    CHECK_PARAM_INFO_PTR(value);

    v = (char*)value;
    printf("(char)\'%c\'\n", (*v));
}


static void ParamInfoInt32(void *value)
{
    INT32 *v = NULL;

    CHECK_PARAM_INFO_PTR(value);

    v = (INT32*)value;
    printf("(int32) %d (0x%08x)\n", (*v), (unsigned int)(*v));
}


static void ParamInfoUInt32(void *value)
{
    UINT32 *v = NULL;

    CHECK_PARAM_INFO_PTR(value);

    v = (UINT32*)value;
    printf("(UINT32) %u (0x%08x)\n", (*v), (*v));
}


VOID
ParamInfo(
    PCSTR Name,
    PARAM_TYPE Type,
    PVOID pValue
    )
{
    printf("# %s = ", Name);

    switch (Type) {
    case PT_CSTRING:
        ParamInfoStr(pValue);
        break;

    case PT_WC16STRING:
        ParamInfoWc16Str(pValue);
        break;

    case PT_CHAR:
        ParamInfoChar(pValue);
        break;

    case PT_INT32:
        ParamInfoInt32(pValue);
        break;

    case PT_UINT32:
        ParamInfoUInt32(pValue);
        break;

    default:
        printf("(unknown type)\n");
    }
}


/*
local variables:
mode: c
c-basic-offset: 4
indent-tabs-mode: nil
tab-width: 4
end:
*/
