/* Editor Settings: expandtabs and use 4 spaces for indentation
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 * -*- mode: c, c-basic-offset: 4 -*- */

/*
 * Copyright Likewise Software    2004-2008
 * 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
 */

/*
 * Copyright (C) Likewise Software. All rights reserved.
 *
 * Module Name:
 *
 *        nss-group.c
 *
 * Abstract:
 *
 *        Name Server Switch (Likewise LSASS)
 *
 *        Handle NSS Foo Information (Common)
 *
 * Authors: Krishna Ganugapati (krishnag@likewisesoftware.com)
 *          Sriram Nambakam (snambakam@likewisesoftware.com)
 *          Brian Koropoff (bkoropoff@likewisesoftware.com)
 *
 */

#include "lsanss.h"

static const DWORD MAX_NUM_GROUPS = 500;

VOID
LsaNssClearEnumFoosState(
    PLSA_ENUMGROUPS_STATE pState
    )
{
    if (pState->hLsaConnection != (HANDLE)NULL) {

        if (pState->ppFooInfoList) {
            LsaFreeNSSArtefactInfoList(
                pState->dwFooInfoLevel,
                pState->ppFooInfoList,
                pState->dwNumFoos
                );
            pState->ppFooInfoList = (HANDLE)NULL;
        }

        if (pState->hResume != (HANDLE)NULL) {
            LsaEndEnumNSSArtefacts(pState->hLsaConnection, pState->hResume);
            pState->hResume = (HANDLE)NULL;
        }

        LsaCloseServer(pState->hLsaConnection);

        pState->hLsaConnection = (HANDLE)NULL;
    }

    memset(pState, 0, sizeof(LSA_ENUMGROUPS_STATE));

    pState->dwFooInfoLevel = 1;
}

DWORD
LsaNssGetNumberFooMembers(
    PSTR* ppszMembers
    )
{
    DWORD dwNumMembers = 0;

    if (ppszMembers){
       for (; ppszMembers && !LW_IS_NULL_OR_EMPTY_STR(*ppszMembers); ppszMembers++)
       {
           dwNumMembers++;
       }
    }

    return dwNumMembers;
}

DWORD
LsaNssComputeFooStringLength(
    DWORD dwAlignBytes,
    PLSA_NSS_ARTEFACTINFO_1 pNSSArtefactInfo
    )
{
    DWORD dwLength = 0;
    PSTR* ppszMember = NULL;
    DWORD dwNumMembers = 0;

    if (!LW_IS_NULL_OR_EMPTY_STR(pNSSArtefactInfo->pszName)) {
       dwLength += strlen(pNSSArtefactInfo->pszName) + 1;
    }

    if (!LW_IS_NULL_OR_EMPTY_STR(pNSSArtefactInfo->pszPasswd)) {
       dwLength += strlen(pNSSArtefactInfo->pszPasswd) + 1;
    }

    /* Adding space for group members */
    dwLength += dwAlignBytes;

    for(ppszMember = pNSSArtefactInfo->ppszMembers;
        ppszMember && !LW_IS_NULL_OR_EMPTY_STR(*ppszMember);
        ppszMember++)
    {
        dwLength += sizeof(ULONG);
        dwLength += strlen(*ppszMember) + 1;
        dwNumMembers++;
    }
    // Account for terminating NULL always
    dwLength += sizeof(ULONG);
    dwLength += 1;

    return dwLength;
}

DWORD
LsaNssWriteFooInfo(
    DWORD       dwNSSArtefactInfoLevel,
    PVOID       pNSSArtefactInfo,
    group_ptr_t pResultFoo,
    char**      ppszBuf,
    int         bufLen)
{
    DWORD dwError = 0;
    PLSA_NSS_ARTEFACTINFO_1 pNSSArtefactInfo_1 = NULL;
    PSTR  pszMarker = *ppszBuf;
    DWORD dwLen = 0;
    DWORD dwAlignBytes = 0;
    DWORD dwNumMembers = 0;

    memset(pResultFoo, 0, sizeof(struct group));

    if ((dwNSSArtefactInfoLevel != 0) && (dwNSSArtefactInfoLevel != 1)) {
        dwError = LW_ERROR_UNSUPPORTED_NSS_ARTEFACT_LEVEL;
        BAIL_ON_LSA_ERROR(dwError);
    }

    pNSSArtefactInfo_1 = (PLSA_NSS_ARTEFACT_INFO_1)pNSSArtefactInfo;

    dwNumMembers = LsaNssGetNumberFooMembers(pNSSArtefactInfo_1->ppszMembers);

    dwAlignBytes = (dwNumMembers ? ((((HANDLE)pszMarker) % sizeof(ULONG)) * sizeof(ULONG)) : 0);

    if (LsaNssComputeFooStringLength(dwAlignBytes, pNSSArtefactInfo_1) > bufLen) {
       dwError = LW_ERROR_INSUFFICIENT_BUFFER;
       BAIL_ON_LSA_ERROR(dwError);
    }

    pResultFoo->gr_gid = pGroupInfo_1->gid;

    memset(pszMarker, 0, bufLen);

    pszMarker += dwAlignBytes;
    pResultFoo->gr_mem = (PSTR*)pszMarker;

    //
    // Handle Group Members first, because we computed the
    // alignment adjustment based on the first pointer position
    //
    if (!dwNumMembers) {
       *(pResultFoo->gr_mem) = NULL;
       pszMarker += sizeof(ULONG) + 1;

    } else {
        PSTR pszMemberMarker = NULL;
        DWORD iMember = 0;

        // This is where we start writing the members
        pszMemberMarker = pszMarker + (sizeof(PSTR) * (dwNumMembers + 1));

        for (iMember = 0; iMember < dwNumMembers; iMember++)
        {
            *(pResultFoo->gr_mem+iMember) = pszMemberMarker;
            pszMarker += sizeof(PSTR);

            dwLen = strlen(*(pNSSArtefactInfo_1->ppszMembers + iMember));
            memcpy(pszMemberMarker, *(pGroupInfo_1->ppszMembers + iMember), dwLen);
            pszMemberMarker += dwLen + 1;
        }
        // Handle the terminating NULL
        *(pResultFoo->gr_mem+iMember) = NULL;
        pszMarker = ++pszMemberMarker; // skip NULL
    }

    if (!LW_IS_NULL_OR_EMPTY_STR(pGroupInfo_1->pszName)) {
       dwLen = strlen(pGroupInfo_1->pszName);
       memcpy(pszMarker, pGroupInfo_1->pszName, dwLen);
       pResultFoo->gr_name = pszMarker;
       pszMarker += dwLen + 1;
    }

    if (!LW_IS_NULL_OR_EMPTY_STR(pGroupInfo_1->pszPasswd)) {
       dwLen = strlen(pGroupInfo_1->pszPasswd);
       memcpy(pszMarker, pGroupInfo_1->pszPasswd, dwLen);
       pResultFoo->gr_passwd = pszMarker;
       pszMarker += dwLen + 1;
    }
    else{
        dwLen = sizeof("x") - 1;
        *pszMarker = 'x';
        pResultFoo->gr_passwd = pszMarker;
        pszMarker += dwLen + 1;
    }

cleanup:

    return dwError;

error:

    goto cleanup;
}

NSS_STATUS
LsaNssCommonFooSetgrent(
    PLSA_ENUMGROUPS_STATE     pEnumFoosState
    )
{
    int                       ret = NSS_STATUS_SUCCESS;

    LsaNssClearEnumFoosState(pEnumFoosState);

    ret = MAP_LSA_ERROR(NULL,
                        LsaOpenServer(&pEnumFoosState->hLsaConnection));
    BAIL_ON_NSS_ERROR(ret);

    ret = MAP_LSA_ERROR(NULL,
                        LsaBeginEnumNSSArtefacts(
                            pEnumFoosState->hLsaConnection,
                            pEnumFoosState->dwNSSArtefactInfoLevel,
                            MAX_NUM_GROUPS,
                            &pEnumFoosState->hResume));
    BAIL_ON_NSS_ERROR(ret);

cleanup:

    return ret;

error:

    LsaNssClearEnumFoosState(pEnumFoosState);

    goto cleanup;
}

NSS_STATUS
LsaNssCommonFooGetgrent(
    PLSA_ENUMGROUPS_STATE     pEnumFoosState,
    struct group*             pResultFoo,
    char *                    pszBuf,
    size_t                    bufLen,
    int*                      pErrorNumber
    )
{
    int                       ret = NSS_STATUS_NOTFOUND;

    if (pEnumFoosState->hLsaConnection == (HANDLE)NULL)
    {
        ret = MAP_LSA_ERROR(pErrorNumber, LW_ERROR_INVALID_LSA_CONNECTION);
        BAIL_ON_NSS_ERROR(ret);
    }

    if (!pEnumFoosState->bTryAgain)
    {
        if (!pEnumFoosState->idxGroup ||
            (pEnumFoosState->idxGroup >= pEnumGroupsState->dwNumGroups))
        {
            if (pEnumFoosState->ppNSSArtefactInfoList) {
                LsaFreeNSSArtefactInfoList(
                   pEnumFoosState->dwNSSArtefactInfoLevel,
                   pEnumFoosState->ppNSSArtefactInfoList,
                   pEnumFoosState->dwNumNSSArtefacts);
                pEnumFoosState->ppNSSArtefactInfoList = NULL;
                pEnumFoosState->dwNumNSSArtefacts = 0;
                pEnumFoosState->idxGroup = 0;
            }

            ret = MAP_LSA_ERROR(pErrorNumber,
                           LsaEnumNSSArtefacts(
                               pEnumFoosState->hLsaConnection,
                               pEnumFoosState->hResume,
                               &pEnumFoosState->dwNumNSSArtefacts,
                               &pEnumFoosState->ppNSSArtefactInfoList));
            BAIL_ON_NSS_ERROR(ret);
        }

    }

    if (pEnumFoosState->dwNumNSSArtefacts) {
        PLSA_NSS_ARTEFACT_INFO_1 pNSSArtefactInfo =
            (PLSA_NSS_ARTEFACT_INFO_1)*(pEnumFoosState->ppNSSArtefactInfoList+pEnumFoosState->idxGroup);
        ret = MAP_LSA_ERROR(pErrorNumber,
                            LsaNssWriteFooInfo(
                                pEnumFoosState->dwNSSArtefactInfoLevel,
                                pFooInfo,
                                pResultFoo,
                                &pszBuf,
                                bufLen));
        BAIL_ON_NSS_ERROR(ret);
        pEnumFoosState->idxGroup++;

        ret = NSS_STATUS_SUCCESS;
    } else {
        ret = NSS_STATUS_UNAVAIL;

        if (pErrorNumber) {
            *pErrorNumber = ENOENT;
        }
    }

    pEnumFoosState->bTryAgain = FALSE;

cleanup:

    return ret;

error:

    if ((ret == NSS_STATUS_TRYAGAIN) && pErrorNumber && (*pErrorNumber == ERANGE))
    {
        pEnumFoosState->bTryAgain = TRUE;
    }
    else
    {
       LsaNssClearEnumFoosState(pEnumFoosState);
    }

    if (bufLen && pszBuf) {
        memset(pszBuf, 0, bufLen);
    }

    goto cleanup;
}

NSS_STATUS
LsaNssCommonFooEndgrent(
    PLSA_ENUMGROUPS_STATE     pEnumFoosState
    )
{
    LsaNssClearEnumFoosState(pEnumFoosState);

    return NSS_STATUS_SUCCESS;
}

NSS_STATUS
LsaNssCommonFooGetgrgid(
    gid_t                     gid,
    struct group*             pResultFoo,
    char*                     pszBuf,
    size_t                    bufLen,
    int*                      pErrorNumber
    )
{
    int                       ret = NSS_STATUS_SUCCESS;
    HANDLE                    hLsaConnection = (HANDLE)NULL;
    PVOID                     pNSSArtefactInfo = NULL;
    DWORD                     dwNSSArtefactInfoLevel = 1;

    ret = MAP_LSA_ERROR(pErrorNumber,
                        LsaOpenServer(&hLsaConnection));
    BAIL_ON_NSS_ERROR(ret);

    ret = MAP_LSA_ERROR(pErrorNumber,
                        LsaFindNSSArtefactById(
                            hLsaConnection,
                            gid,
                            dwNSSArtefactInfoLevel,
                            &pNSSArtefactInfo));
    BAIL_ON_NSS_ERROR(ret);

    ret = MAP_LSA_ERROR(pErrorNumber,
                        LsaNssWriteFooInfo(
                            dwNSSArtefactInfoLevel,
                            pNSSArtefactInfo,
                            pResultFoo,
                            &pszBuf,
                            bufLen
                            ));
    BAIL_ON_NSS_ERROR(ret);

cleanup:

    if (pNSSArtefactInfo) {
        LsaFreeNSSArtefactInfo(dwNSSArtefactInfoLevel, pNSSArtefactInfo);
    }

    if (hLsaConnection != (HANDLE)NULL) {
       LsaCloseServer(hLsaConnection);
    }

    return ret;

error:

    goto cleanup;
}

NSS_STATUS
LsaNssCommonFooGetgrnam(
    const char *              pszGroupName,
    struct group *            pResultFoo,
    char *                    pszBuf,
    size_t                    bufLen,
    int*                      pErrorNumber
    )
{
    int                       ret = NSS_STATUS_SUCCESS;
    HANDLE                    hLsaConnection = (HANDLE)NULL;
    PVOID                     pNSSArtefactInfo = NULL;
    DWORD                     dwNSSArtefactInfoLevel = 1;

    ret = MAP_LSA_ERROR(pErrorNumber,
                        LsaOpenServer(&hLsaConnection));
    BAIL_ON_NSS_ERROR(ret);

    ret = MAP_LSA_ERROR(pErrorNumber,
                        LsaFindNSSArtefactByName(
                            hLsaConnection,
                            pszGroupName,
                            dwNSSArtefactInfoLevel,
                            &pNSSArtefactInfo));
    BAIL_ON_NSS_ERROR(ret);

    ret = MAP_LSA_ERROR(pErrorNumber,
                        LsaNssWriteFooInfo(
                            dwNSSArtefactInfoLevel,
                            pNSSArtefactInfo,
                            pResultFoo,
                            &pszBuf,
                            bufLen));
    BAIL_ON_NSS_ERROR(ret);

cleanup:

    if (pNSSArtefactInfo) {
        LsaFreeNSSArtefactInfo(dwNSSArtefactInfoLevel, pNSSArtefactInfo);
    }

    if (hLsaConnection != (HANDLE)NULL) {
       LsaCloseServer(hLsaConnection);
    }

    return ret;

error:

    goto cleanup;
}
