//---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995
//
//  File:      usrutils.cxx
//
//  Contents:  NetWare compatible UserCollection Enumeration Code
//
//  History:   22-Mar-96    t-ptam (PatrickT) migrated from KrishnaG for NetWare
//----------------------------------------------------------------------------
#include "nwcompat.hxx"
#pragma hdrstop

USER_GROUP_ENTRY UserGroupEntry;

//
// This assumes that addr is an LPBYTE type.
//

#define WORD_ALIGN_DOWN(addr) \
        addr = ((LPBYTE)((DWORD)addr & ~1))

DWORD UserGrpEntryStrings[]=
                             {
                             FIELD_OFFSET(USER_GROUP_ENTRY, Parent),
                             FIELD_OFFSET(USER_GROUP_ENTRY, Computer),
                             FIELD_OFFSET(USER_GROUP_ENTRY, Name),
                             0xFFFFFFFF
                             };

DECLARE_INFOLEVEL(UsrUt)
DECLARE_DEBUG(UsrUt)
#define UsrUtDebugOut(x) UsrUtInlineDebugOut x

//----------------------------------------------------------------------------
//
//  Function: NWCOMPATComputerUserOpen
//
//  Synopsis: This function opens a handle to a INI_COMP_USER structure.
//
//----------------------------------------------------------------------------
BOOL
NWCOMPATComputerUserOpen(
    LPWSTR szComputerName,
    LPWSTR szGroupName,
    CCredentials &Credentials,
    PHANDLE phUser
    )
{
    WCHAR szTempBuffer[MAX_PATH];
    PINI_COMP_USER pIniCompUsr;
    HRESULT hr = S_OK;

    if (!phUser) {
        return(FALSE);
    }

    pIniCompUsr = (PINI_COMP_USER)AllocADsMem(
                                       sizeof(INI_COMP_USER)
                                       );
    if (!pIniCompUsr) {
        return(FALSE);
    }

    //
    // Fill structure's fields.
    //

    if (!(pIniCompUsr->szComputerName =  AllocADsStr(szComputerName))){
        goto error;
    }

    if (!(pIniCompUsr->szGroupName = AllocADsStr(szGroupName))){
        goto error;
    }

    hr = NWApiGetBinderyHandle(
             &pIniCompUsr->_hConn,
             szComputerName,
             Credentials
             );
    BAIL_ON_FAILURE(hr);

    //
    // Return
    //

    *phUser =  (HANDLE)pIniCompUsr;

    return(TRUE);


error:
    if (pIniCompUsr) {
        FreeIniCompUser(pIniCompUsr);
    }

    *phUser = NULL;

    return(FALSE);
}

//----------------------------------------------------------------------------
//
//  Function: NWCOMPATComputerUserEnum
//
//  Synopsis: This function returns a buffer which contains all the binding
//            informations for the requested number of objects without any
//            references.
//            It returns TRUE iff dwReturned = dwRequested.
//
//----------------------------------------------------------------------------
BOOL
NWCOMPATComputerUserEnum(
    HANDLE hUser,
    DWORD  dwRequested,
    LPBYTE * ppBuffer,
    PDWORD pdwReturned
    )
{
    LPUSER_GROUP_ENTRY * ppUserMembers = NULL;
    DWORD i = 0;
    BOOL dwRet = FALSE;
    DWORD dwReturned = 0;
    DWORD dwSize = 0;
    LPUSER_GROUP_ENTRY pBuffer = NULL;
    LPBYTE pEnd = NULL;

    //
    // Allocate buffer for the number of requested members.
    //

    ppUserMembers = (LPUSER_GROUP_ENTRY *)AllocADsMem(
                                                    sizeof(LPUSER_GROUP_ENTRY)* dwRequested
                                                    );
    if (!ppUserMembers) {
        return(FALSE);
    }

    //
    // Fill in ppUserMembers one by one.
    //

    for (i = 0; i < dwRequested; i++) {

        dwRet = NWCOMPATComputerUserGetObject(
                    hUser,
                    &ppUserMembers[i]
                    );
        if (!dwRet) {
            break;
        }
    }

    dwReturned = i;

    if (!dwRet) {
        goto error;
    }
    else {

        //
        // Determine actual size of ppUserMembers[], ie. since each string in
        // USER_GROUP_ENTRY have a different length, a buffer that is going
        // to contain all the data without any references is unknown.
        //

        dwRet = ComputeComputerUserDataSize(
                    ppUserMembers,
                    dwReturned,
                    &dwSize
                    );

        pBuffer = (LPUSER_GROUP_ENTRY)AllocADsMem(
                                               dwSize
                                               );
        if (!pBuffer) {
            goto error;
        }

        pEnd = (LPBYTE)((LPBYTE)(pBuffer) + dwSize);

        //
        // Put data into pBuffer, starting from the end.
        //

        for (i = 0; i < dwReturned; i++) {

            pEnd = CopyIniCompUserToCompUser(
                       ppUserMembers[i],
                       (LPBYTE)(pBuffer + i),
                       pEnd
                       );
        }

        //
        // Clean up.
        //

        for (i = 0; i < dwReturned; i++ ) {
            FreeIntCompUser(*(ppUserMembers + i));
        }

        //
        // Return values.
        //

        *ppBuffer = (LPBYTE)pBuffer;
        *pdwReturned  = dwReturned;
    }

    FreeADsMem(ppUserMembers);

    if (dwReturned == dwRequested){
        return(TRUE);
    }else {
        return(FALSE);
    }
error:

    for (i = 0; i < dwReturned; i++ ) {
        FreeIntCompUser(*(ppUserMembers + i));
    }
    
    FreeADsMem(ppUserMembers);
    
    return(FALSE);
}

//----------------------------------------------------------------------------
//
//  Function: NWCOMPATComputerUserGetObject
//
//  Synopsis: This function returns binding information of a user (group member)
//            object one by one.  In its first call, it builds a buffer that
//            contains all the UserID of the group members.  Then, and in
//            subsequent calls, this UserID is translated into a user name.
//
//----------------------------------------------------------------------------
BOOL
NWCOMPATComputerUserGetObject(
    HANDLE hUser,
    LPUSER_GROUP_ENTRY * ppUserMember
    )
{
    BOOL            dwRet = FALSE;
    DWORD           dwGroupId = 0;
    HRESULT         hr = S_OK;
    PINI_COMP_USER pIniCompUsr = (PINI_COMP_USER)hUser;

    //
    // Fill buffer with User ID.  NetWare returns all UserID in one call.
    //

    if (!pIniCompUsr->_pBuffer) {

        pIniCompUsr->_dwCurrentObject = 0;

        hr = NWApiUserGetGroups(
                 pIniCompUsr->_hConn,
                 pIniCompUsr->szGroupName,
                 &(pIniCompUsr->_pBuffer)
                 );
        BAIL_ON_FAILURE(hr);
    }

    //
    // Build one group member.
    //

    dwGroupId = *((LPDWORD)pIniCompUsr->_pBuffer + pIniCompUsr->_dwCurrentObject);

    if (dwGroupId != 0x0000) {

        dwRet = BuildComputerUserMember(
                    hUser,
                    dwGroupId,
                    ppUserMember
                    );
        if (!dwRet) {
            goto error;
        }

        pIniCompUsr->_dwCurrentObject++;

        return(TRUE);
    }

error:

    return(FALSE);
}

//----------------------------------------------------------------------------
//
//  Function: NWCOMPATComputerUserClose
//
//  Synopsis: Wrapper of FreeIniCompUser.
//
//----------------------------------------------------------------------------
BOOL
NWCOMPATComputerUserClose(
    HANDLE hUser
    )
{
    PINI_COMP_USER pIniCompUsr = (PINI_COMP_USER)hUser;

    if (pIniCompUsr) {
        FreeIniCompUser(pIniCompUsr);
    }
    return(TRUE);
}

//----------------------------------------------------------------------------
//
//  Function: FreeIniCompUser
//
//  Synopsis: Free an INI_COMP_USER structure.
//
//----------------------------------------------------------------------------
void
FreeIniCompUser(
    PINI_COMP_USER pIniCompUsr
    )
{
    HRESULT hr = S_OK;

    if (pIniCompUsr) {

        if (pIniCompUsr->szComputerName) {
            FreeADsStr(pIniCompUsr->szComputerName);
        }

        if (pIniCompUsr->szGroupName) {
            FreeADsStr(pIniCompUsr->szGroupName);
        }

        if (pIniCompUsr->_pBuffer) {
            FreeADsMem(pIniCompUsr->_pBuffer);
        }

        if (pIniCompUsr->_hConn) {
            hr = NWApiReleaseBinderyHandle(pIniCompUsr->_hConn);
        }

        FreeADsMem(pIniCompUsr);
    }

    return;
}

//----------------------------------------------------------------------------
//
//  Function: FreeIntCompUser
//
//  Synopsis: Free a USER_GROUP_ENTRY structure.
//
//----------------------------------------------------------------------------
void
FreeIntCompUser(
    LPUSER_GROUP_ENTRY pCompUserMember
    )
{
    if (pCompUserMember) {

        if (pCompUserMember->Parent) {
            FreeADsStr(pCompUserMember->Parent);
        }

        if (pCompUserMember->Computer) {
            FreeADsStr(pCompUserMember->Computer);
        }

        if (pCompUserMember->Name) {
            FreeADsStr(pCompUserMember->Name);
        }

        FreeADsMem(pCompUserMember);
    }

    return;
}

//----------------------------------------------------------------------------
//
//  Function: ComputeComputerUserDataSize
//
//  Synopsis: Calculate the size of a buffer that is going to store the data in
//            ppUserMembers without any references.
//
//----------------------------------------------------------------------------
BOOL
ComputeComputerUserDataSize(
        LPUSER_GROUP_ENTRY * ppUserMembers,
        DWORD  dwReturned,
        PDWORD pdwSize
        )
{

    DWORD i = 0;
    DWORD cb = 0;
    LPUSER_GROUP_ENTRY pMember = NULL;

    for (i = 0; i < dwReturned; i++) {

        pMember = *(ppUserMembers + i);

        cb += sizeof(USER_GROUP_ENTRY);

        if (pMember->Parent) {
            cb += wcslen(pMember->Parent)*sizeof(WCHAR) + sizeof(WCHAR);
        }

        if (pMember->Computer) {
            cb += wcslen(pMember->Computer)*sizeof(WCHAR) + sizeof(WCHAR);
        }

        if (pMember->Name) {
            cb += wcslen(pMember->Name)*sizeof(WCHAR) + sizeof(WCHAR);
        }
    }

    *pdwSize = cb;
    return(TRUE);
}

//------------------------------------------------------------------------------
//
//  Function: CopyIniCompUserToCompUser
//
//  Synopsis: Pack referenced data (string) into a buffer without any reference.
//
//------------------------------------------------------------------------------
LPBYTE
CopyIniCompUserToCompUser(
    LPUSER_GROUP_ENTRY  pIntCompGrp,
    LPBYTE  pExtCompGrp,
    LPBYTE  pEnd
    )
{
    LPWSTR   SourceStrings[sizeof(USER_GROUP_ENTRY)/sizeof(LPWSTR)];
    LPWSTR   *pSourceStrings =  SourceStrings;
    LPUSER_GROUP_ENTRY pCompGrpMember = (LPUSER_GROUP_ENTRY)pExtCompGrp;

    memset(SourceStrings, 0, sizeof(USER_GROUP_ENTRY));
    *pSourceStrings++ = pIntCompGrp->Parent;
    *pSourceStrings++ = pIntCompGrp->Computer;
    *pSourceStrings++ = pIntCompGrp->Name;

    pEnd = PackStrings(
                SourceStrings,
                pExtCompGrp,
                UserGrpEntryStrings,
                pEnd
                );

    pCompGrpMember->Type = pIntCompGrp->Type;

    return pEnd;
}

//----------------------------------------------------------------------------
//
//  Function: BuildComputerUserMember
//
//  Synopsis: Put binding information of a group member into ppUserMember.
//
//----------------------------------------------------------------------------
BOOL
BuildComputerUserMember(
    HANDLE hUser,
    DWORD  dwGroupId,
    LPUSER_GROUP_ENTRY * ppUserMember
    )
{
    DWORD                   dwTempUserId = dwGroupId;
    HRESULT                 hr = S_OK;
    LPUSER_GROUP_ENTRY pUserMember = NULL;
    LPINI_COMP_USER        pUser = (LPINI_COMP_USER)hUser;
    WCHAR                   szADsParent[MAX_PATH];

    //
    // Allocate one USER_GROUP_ENTRY.
    //

    pUserMember = (LPUSER_GROUP_ENTRY)AllocADsMem(
                                                sizeof(USER_GROUP_ENTRY)
                                                );
    if (!pUserMember) {
        return(FALSE);
    }

    //
    // Fill structure's fields.
    //

    pUserMember->Parent = NULL;
    pUserMember->Computer = NULL;

    pUserMember->Type = NWCOMPAT_USER_ID;

    wsprintf(
        szADsParent,
        L"%s://%s",
        szProviderName,
        pUser->szComputerName
        );
    pUserMember->Parent = AllocADsStr(szADsParent);
    if (!pUserMember->Parent) {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    pUserMember->Computer = AllocADsStr(pUser->szComputerName);
    if (!pUserMember->Computer) {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }

    hr = NWApiGetObjectName(
             pUser->_hConn,
             dwTempUserId,
             &pUserMember->Name
             );
    BAIL_ON_FAILURE(hr);

    //
    // Return.
    //

    *ppUserMember = pUserMember;
    return(TRUE);

error:
    if (pUserMember) {

        if (pUserMember->Parent)
            FreeADsStr(pUserMember->Parent);

        if (pUserMember->Computer)
            FreeADsStr(pUserMember->Computer);

        FreeADsMem(pUserMember);
    }

    return(FALSE);
}

