/*++

Copyright (c) 1991-92  Microsoft Corporation

Module Name:

    WksUser.c

Abstract:

    This file contains the RpcXlate code to handle the NetWkstaUserEnum API.

Author:

    John Rogers (JohnRo) 19-Nov-1991

Environment:

    Portable to any flat, 32-bit environment.  (Uses Win32 typedefs.)
    Requires ANSI C extensions: slash-slash comments, long external names.

Revision History:

    19-Nov-1991 JohnRo
        Implement remote NetWkstaUserEnum().
    21-Nov-1991 JohnRo
        Removed NT dependencies to reduce recompiles.
    07-Feb-1992 JohnRo
        Use NetApiBufferAllocate() instead of private version.
    14-Oct-1992 JohnRo
        RAID 9732: NetWkstaUserEnum to downlevel: wrong EntriesRead, Total?
        Set wkui1_oth_domains field.
        Use PREFIX_ equates.
    03-Nov-1992 JohnRo
        RAID 10418: Fixed overactive assert when Status != NO_ERROR.
        Fixed memory leak if we couldn't allocate new buffer (old one got lost).
        Fixed memory leak if nobody is logged-on to target server.

--*/

// These must be included first:

#include <windef.h>             // IN, DWORD, etc.
#include <lmcons.h>             // LM20_ equates, NET_API_STATUS, etc.

// These may be included in any order:

#include <dlwksta.h>            // WKSTA_INFO_0, MAX_WKSTA_ equates, etc.
#include <lmapibuf.h>           // NetApiBufferAllocate().
#include <lmerr.h>              // ERROR_ and NERR_ equates.
#include <netdebug.h>           // DBGSTATIC, NetpKdPrint(()), FORMAT_ equates.
#include <netlib.h>             // NetpCopyStringToBuffer().
#include <prefix.h>     // PREFIX_ equates.
#include <rxpdebug.h>           // IF_DEBUG().
#include <rxwksta.h>            // My prototypes, RxpGetWkstaInfoLevelEquivalent
#include <tstring.h>            // STRLEN().



NET_API_STATUS
RxNetWkstaUserEnum (
    IN LPTSTR UncServerName,
    IN DWORD Level,
    OUT LPBYTE *BufPtr,
    IN DWORD PrefMaxSize,
    OUT LPDWORD EntriesRead,
    OUT LPDWORD TotalEntries,
    IN OUT LPDWORD ResumeHandle OPTIONAL
    )

/*++

Routine Description:

    RxNetWkstaUserEnum performs the same function as NetWkstaUserEnum, except
    that the server name is known to refer to a downlevel server.

Arguments:

    (Same as NetWkstaUserEnum, except UncServerName must not be null, and
    must not refer to the local computer.)

Return Value:

    (Same as NetWkstaUserEnum.)

--*/

{

    LPBYTE NewInfo = NULL;              // Buffer to be returned to caller.
    DWORD NewFixedSize;
    DWORD NewStringSize;

    LPWKSTA_INFO_1 OldInfo = NULL;
    const DWORD OldLevel = 1;

    NET_API_STATUS Status;

    UNREFERENCED_PARAMETER(PrefMaxSize);
    UNREFERENCED_PARAMETER(ResumeHandle);

    IF_DEBUG(WKSTA) {
        NetpKdPrint(( PREFIX_NETAPI
                "RxNetWkstaUserEnum: starting, server=" FORMAT_LPTSTR
                ", lvl=" FORMAT_DWORD ".\n", UncServerName, Level));
    }

    //
    // Error check DLL stub and the app.
    //
    NetpAssert(UncServerName != NULL);
    if (BufPtr == NULL) {
        Status = ERROR_INVALID_PARAMETER;
        goto Cleanup;
    }
    *BufPtr = NULL;  // assume error; it makes error handlers easy to code.
    // This also forces possible GP fault before we allocate memory.

    //
    // Compute size of wksta user structure (including strings)
    //
    switch (Level) {
    case 0 :
        NewFixedSize = sizeof(WKSTA_USER_INFO_0);
        NewStringSize = (LM20_UNLEN+1) * sizeof(TCHAR);
        break;
    case 1 :
        NewFixedSize = sizeof(WKSTA_USER_INFO_1);
        NewStringSize =
                (LM20_UNLEN+1 + LM20_DNLEN+1 + MAX_PATH+1) * sizeof(TCHAR);
        break;
    default:
        Status = ERROR_INVALID_LEVEL;
        goto Cleanup;
    }

    //
    // Actually remote the API, which will get back the (old) info level
    // data in native format.
    //
    Status = RxpWkstaGetOldInfo(
            UncServerName,              // Required, with \\name.
            OldLevel,
            (LPBYTE *) & OldInfo);      // buffer (alloc and set this ptr)

    NetpAssert( Status != ERROR_MORE_DATA );
    NetpAssert( Status != NERR_BufTooSmall );

    if (Status == NERR_Success) {

        NetpAssert( OldInfo != NULL );

        if ( (OldInfo->wki1_username == NULL)
                || ( (*(OldInfo->wki1_username)) == (TCHAR) '\0')) {

            //
            // Nobody logged on.
            //
            *BufPtr = NULL;
            *EntriesRead = 0;
            *TotalEntries = 0;

        } else {

            // These variables are used by the COPY_STRING macro.
            LPBYTE NewFixedEnd;
            LPTSTR NewStringTop;
            LPWKSTA_INFO_1 src = (LPVOID) OldInfo;
            LPWKSTA_USER_INFO_1 dest;  // superset info level

            //
            // Allocate memory for native version of new info, which we'll
            // return to caller.  (Caller must free it with NetApiBufferFree.)
            //

            Status = NetApiBufferAllocate(
                    NewFixedSize + NewStringSize,
                    (LPVOID *) & NewInfo);
            if (Status != NERR_Success) {
                goto Cleanup;
            }
            NetpAssert( NewInfo != NULL );
            IF_DEBUG(WKSTA) {
                NetpKdPrint(( PREFIX_NETAPI
                        "RxNetWkstaUserEnum: allocated new buffer at "
                        FORMAT_LPVOID "\n", (LPVOID) NewInfo ));
            }

            // Set up pointers for use by NetpCopyStringsToBuffer.
            dest = (LPVOID) NewInfo;
            NewStringTop = (LPTSTR) NetpPointerPlusSomeBytes(
                    dest,
                    NewFixedSize+NewStringSize);

            NewFixedEnd = NetpPointerPlusSomeBytes(NewInfo, NewFixedSize);

#define COPY_STRING( InField, OutField ) \
    { \
        BOOL CopyOK; \
        NetpAssert( dest != NULL); \
        NetpAssert( src != NULL); \
        NetpAssert( (src -> InField) != NULL); \
        CopyOK = NetpCopyStringToBuffer ( \
            src->InField, \
            STRLEN(src->InField), \
            NewFixedEnd, \
            & NewStringTop, \
            & dest->OutField); \
        NetpAssert(CopyOK); \
    }
            //
            // Downlevel server, so one user is logged on.
            //
            *EntriesRead = 1;
            *TotalEntries = 1;

            //
            // Copy/convert data from OldInfo to NewInfo.
            //

            // User name is only field in level 0.
            COPY_STRING( wki1_username, wkui1_username );

            if (Level == 1) {

                // Do fields unique to level 1.
                COPY_STRING( wki1_logon_domain, wkui1_logon_domain );
                COPY_STRING( wki1_oth_domains,  wkui1_oth_domains );
                COPY_STRING( wki1_logon_server, wkui1_logon_server );

            }

            NetpAssert( Level < 2 );  // Add code here someday?

            *BufPtr = NewInfo;
        }
    } else {
        // An error from RxpWkstaGetOldInfo()...
        NetpAssert( OldInfo == NULL );
    }

Cleanup:

    if (OldInfo != NULL) {
        (void) NetApiBufferFree( OldInfo );
    }

    return (Status);

} // RxNetWkstaUserEnum