/* 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 .
 *
 * 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 Group Information
 *
 * Authors: Krishna Ganugapati (krishnag@likewisesoftware.com)
 *          Sriram Nambakam (snambakam@likewisesoftware.com)
 *
 */
#include "lsanss.h"
#include "externs.h"
#include 
static const DWORD MAX_NUM_GROUPS = 500;
static LSA_ENUMGROUPS_STATE gEnumGroupsState = {0};
NSS_STATUS
_nss_lsass_setgrent(
    void
    )
{
    return LsaNssCommonGroupSetgrent(&hLsaConnection,
                                     &gEnumGroupsState);
}
NSS_STATUS
_nss_lsass_getgrent_r(
    struct group*  pResultGroup,
    char *         pszBuf,
    size_t         bufLen,
    int*           pErrorNumber
    )
{
    return LsaNssCommonGroupGetgrent(&hLsaConnection,
                                     &gEnumGroupsState,
                                     pResultGroup,
                                     pszBuf,
                                     bufLen,
                                     pErrorNumber);
}
NSS_STATUS
_nss_lsass_endgrent(
    void
    )
{
    return LsaNssCommonGroupEndgrent(&hLsaConnection, &gEnumGroupsState);
}
NSS_STATUS
_nss_lsass_getgrgid_r(
    gid_t          gid,
    struct group*  pResultGroup,
    char*          pszBuf,
    size_t         bufLen,
    int*           pErrorNumber
    )
{
    return LsaNssCommonGroupGetgrgid(&hLsaConnection,
                                     gid,
                                     pResultGroup,
                                     pszBuf,
                                     bufLen,
                                     pErrorNumber);
}
NSS_STATUS
_nss_lsass_getgrnam_r(
    const char *   pszGroupName,
    struct group * pResultGroup,
    char *         pszBuf,
    size_t         bufLen,
    int*           pErrorNumber
    )
{
    return LsaNssCommonGroupGetgrnam(&hLsaConnection,
                                     pszGroupName,
                                     pResultGroup,
                                     pszBuf,
                                     bufLen,
                                     pErrorNumber);
}
NSS_STATUS
_nss_lsass_initgroups_dyn(
    PCSTR     pszUserName,
    gid_t     groupGid,
    long int* pResultsSize,
    long int* pResultsCapacity,
    gid_t**   ppGidResults,
    long int  maxGroups,
    int*      pErrorNumber
    )
{
    int   ret = NSS_STATUS_SUCCESS;
    DWORD dwNumGroupsFound = 0;
    gid_t* pGidTotalResult = NULL;
    gid_t* pGidNewResult = NULL;
    gid_t* pExistingResult = NULL;
    DWORD dwNumTotalGroup = 0;
    DWORD iGroup = 0, iExistingGroup = 0, iNewGroup = 0;
    if ((*pResultsCapacity > maxGroups && maxGroups != -1) ||
        *pResultsSize > *pResultsCapacity)
    {
        ret = NSS_STATUS_UNAVAIL;
        *pErrorNumber = EINVAL;
        BAIL_ON_NSS_ERROR(ret);
    }
    if (hLsaConnection == (HANDLE)NULL)
    {
        ret = MAP_LSA_ERROR(pErrorNumber,
                            LsaOpenServer(&hLsaConnection));
        BAIL_ON_NSS_ERROR(ret);
    }
    ret = MAP_LSA_ERROR(pErrorNumber,
                        LsaGetGidsForUserByName(
                           hLsaConnection,
                           pszUserName,
                           &dwNumGroupsFound,
                           &pGidNewResult));
    BAIL_ON_NSS_ERROR(ret);
    dwNumTotalGroup += dwNumGroupsFound;
    dwNumTotalGroup += 1; //count in the group that is passed in
    //count in the groups that are already in ppGidResults
    dwNumTotalGroup += *pResultsSize;
    if (dwNumTotalGroup > *pResultsCapacity)
    {
        if (dwNumTotalGroup > maxGroups && maxGroups != -1)
            dwNumTotalGroup = maxGroups;
        ret = MAP_LSA_ERROR(pErrorNumber,
                            LwAllocateMemory(
                                sizeof(gid_t) * dwNumTotalGroup * 2,
                                (PVOID*)&pGidTotalResult));
        BAIL_ON_NSS_ERROR(ret);
        pExistingResult = *ppGidResults;
        for (iExistingGroup = 0, iGroup = 0;
             iExistingGroup < *pResultsSize;
             iExistingGroup++, iGroup++)
        {
            pGidTotalResult[iGroup] = pExistingResult[iExistingGroup];
        }
        for (iNewGroup = 0;
             iNewGroup < dwNumGroupsFound && iGroup < dwNumTotalGroup;
             iNewGroup++, iGroup++)
        {
            pGidTotalResult[iGroup] = pGidNewResult[iNewGroup];
        }
        *pResultsCapacity = dwNumTotalGroup * 2;
        *pResultsSize = dwNumTotalGroup;
        *ppGidResults = pGidTotalResult;
        pGidTotalResult = NULL;
        LW_SAFE_FREE_MEMORY(pExistingResult);
    }
    else
    {
        pExistingResult = *ppGidResults;
        iGroup = *pResultsSize;
        pExistingResult[iGroup++] = groupGid;
        for (iNewGroup = 0;
             iNewGroup < dwNumGroupsFound && iGroup < dwNumTotalGroup;
             iNewGroup++, iGroup++)
        {
            pExistingResult[iGroup] = pGidNewResult[iNewGroup];
        }
    }
    *pResultsSize = iGroup;
cleanup:
    LW_SAFE_FREE_MEMORY(pGidNewResult);
    return ret;
error:
    if (ret != NSS_STATUS_TRYAGAIN && hLsaConnection != (HANDLE)NULL)
    {
       LsaCloseServer(hLsaConnection);
       hLsaConnection = (HANDLE)NULL;
    }
    LW_SAFE_FREE_MEMORY(pGidTotalResult);
    LW_SAFE_FREE_MEMORY(pGidNewResult);
    goto cleanup;
}