/*++

Copyright (c) 1991-1992  Microsoft Corporation

Module Name:

    SrvStub.C

Abstract:

    These are the server service API RPC client stubs.

Author:

    Dan Lafferty    (danl)  06-Feb-1991

Environment:

    User Mode - Win32

Revision History:

    06-Feb-1991     Danl
        Created
    07-Jun-1991 JohnRo
        Added downlevel support for NetServer APIs.
        Added NET_API_FUNCTION where necessary.
    15-Jul-1991 RFirth
        Integrated RxNetShare routines into NetShare stubs
    24-Jul-1991 JohnRo
        Implement downlevel NetConnectionEnum.  Try using <netrpc.h> macros.
    25-Jul-1991 JohnRo
        Quiet DLL stub debug output.  Use NetRpc.h macros for NetServer APIs.
    06-Sep-1991 JohnRo
        Downlevel NetFile APIs.
    25-Sep-1991 JohnRo
        Use NetRpc.h macros for all other APIs, to quiet normal debug output.
    07-Oct-1991 JohnRo
        RAID 3210: "NET FILE 0" causes assertion.  (Was bug in NetFileGetInfo
        DLL stub.)
    16-Oct-1991 JohnRo
        Implement remote NetSession APIs.  Changed LPSTR to LPTSTR.
    07-Nov-1991 JohnRo
        RAID 4186: assert in RxNetShareAdd and other DLL stub problems.
    12-Nov-1991 JohnRo
        APIs in this file need SERVICE_SERVER started to run locally.
    04-Dec-1991 JohnRo
        Change RxNetServerSetInfo() to new-style interface.
        Fixed bug in calling RxNetShareSetInfo().
    09-May-1992 rfirth
        Resurrect NetStatisticsGet as NetServerStatisticsGet
    5-Aug-1992 JohnsonA
        Added new share info level 502 to enable passing of security
        descriptors.
    08-Sep-1992 JohnRo
        Fix NET_API_FUNCTION references.
--*/

//
// INCLUDES
//

#include <nt.h>         // DbgPrint prototype

#include <ntrtl.h>      // DbgPrint

#include <rpc.h>        // DataTypes and runtime APIs

#include <srvsvc.h>     // generated by the MIDL complier
#include <rpcutil.h>    // GENERIC_ENUM_STRUCT
#include <lmcons.h>     // NET_API_STATUS
#include <debuglib.h>   // (needed by netrpc.h)
#include <lmsvc.h>      // (needed by netrpc.h)
#include <netdebug.h>   // (needed by netrpc.h)
#include <lmerr.h>      // NetError codes
#include <netlib.h>     // NetpIsServiceStarted().
#include <netlibnt.h>   // NetpNtStatusToApiStatus
#include <netrpc.h>     // NET_REMOTE_ macros.
#include <lmremutl.h>   // SUPPORTS_RPC
#include <lmshare.h>    // Required by rxsess.h.
#include <rap.h>        // Needed by <rxserver.h>.
#include <rxconn.h>     // RxNetConnection routines.
#include <rxfile.h>     // RxNetFile routines.
#include <rxremutl.h>   // RxNetRemoteTOD
#include <rxserver.h>   // RxNetServer routines.
#include <rxsess.h>     // RxNetSession routines.
#include <rxshare.h>    // RxNetShare routines
#include <icanon.h>     // NetpIsRemote
#include <netstats.h>   // NetServerStatisticsGet private prototype
#include <rxstats.h>    // RxNetStatisticsGet (down-level)
#include <netcan.h>     // prototypes for Netps canonicalization functions
#include <rxcanon.h>    // prototypes for down-level canonicalization functions
#include <tstr.h>
#include "cscp.h"

#define SET_ERROR_PARAMETER(a) \
    if ( ARGUMENT_PRESENT( parm_err ) ) { *parm_err = a; }


NET_API_STATUS NET_API_FUNCTION
NetCharDevControl (
    IN  LPCWSTR  servername,
    IN  LPCWSTR  devname,
    IN  DWORD   opcode
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetCharDevControl.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    devname --A pointer to the ASCIIZ string containing the name of
        the device to control

    opcode --Control opcode: currently defined are:
        CHARDEV_CLOSE for the device closed.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrCharDevControl (
                        (LPWSTR)servername,
                        (LPWSTR)devname,
                        opcode);

    NET_REMOTE_RPC_FAILED(
            "NetCharDevControl",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

    apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetCharDevControl


NET_API_STATUS NET_API_FUNCTION
NetCharDevEnum (
    IN  LPCWSTR      servername,
    IN  DWORD       level,
    OUT LPBYTE      *bufptr,
    IN  DWORD       prefmaxlen,
    OUT LPDWORD     entriesread,
    OUT LPDWORD     totalentries,
    IN OUT LPDWORD  resume_handle
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetCharDevEnum.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    level --Level of information required. 0 and 1 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

    prefmaxlen --Prefered maximum length of returned data (in 8-bit
        bytes). 0xffffffff specifies no limit.

    entriesread --On return the actual enumerated element count is
        located in the DWORD pointed to by entriesread.

    totalentries --On return the total entries available to be
        enumerated is located in the DWORD pointed to by
        totalentries.

    resumehandle --On return, a resume handle is stored in the DWORD
        pointed to by resumehandle, and is used to continue an
        existing character device search. The handle should be zero
        on the first call and left unchanged for subsequent calls. If
        resumehandle is NULL, then no resume handle is stored..

Return Value:



--*/

{
    NET_API_STATUS              apiStatus;
    GENERIC_INFO_CONTAINER      genericInfoContainer;
    GENERIC_ENUM_STRUCT         infoStruct;

    genericInfoContainer.Buffer = NULL;
    genericInfoContainer.EntriesRead = 0;

    infoStruct.Container = &genericInfoContainer;
    infoStruct.Level = level;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrCharDevEnum (
                (LPWSTR)servername,
                (LPCHARDEV_ENUM_STRUCT)&infoStruct,
                prefmaxlen,
                totalentries,
                resume_handle);

        if (genericInfoContainer.Buffer != NULL) {
            *bufptr         = (LPBYTE)genericInfoContainer.Buffer;
            *entriesread    = genericInfoContainer.EntriesRead;
        } else {
            *bufptr = NULL;
            *entriesread = 0;
        }

    NET_REMOTE_RPC_FAILED(
            "NetCharDevEnum",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

    apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetCharDevEnum


NET_API_STATUS NET_API_FUNCTION
NetCharDevGetInfo (
    IN  LPCWSTR  servername,
    IN  LPCWSTR  devname,
    IN  DWORD   level,
    OUT LPBYTE  *bufptr
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetCharDevGetInfo.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    devname --A pointer to the ASCIIZ string containing the name of
        the device to return information on.

    level --Level of information required. 0 and 1 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

Return Value:



--*/

{
    NET_API_STATUS              apiStatus;


    *bufptr = NULL;         // Must be NULL so RPC knows to till it in.

    NET_REMOTE_TRY_RPC

        apiStatus = NetrCharDevGetInfo (
            (LPWSTR)servername,
            (LPWSTR)devname,
            level,
            (LPCHARDEV_INFO) bufptr);

    NET_REMOTE_RPC_FAILED(
            "NetCharDevGetInfo",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

    apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetCharDevGetInfo


NET_API_STATUS NET_API_FUNCTION
NetCharDevQEnum (
    IN  LPCWSTR      servername,
    IN  LPCWSTR      username,
    IN  DWORD       level,
    OUT LPBYTE      *bufptr,
    IN  DWORD       prefmaxlen,
    OUT LPDWORD     entriesread,
    OUT LPDWORD     totalentries,
    IN OUT LPDWORD  resume_handle
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetCharDevQEnum.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    username --A pointer to an ASCIIZ string containing an a username
        for the active queues of interest. This parameter is
        optional, if NULL then all device queues  are enumerated.

    level --Level of information required. 0 and 1 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

    prefmaxlen --Prefered maximum length of returned data (in 8-bit
        bytes). 0xffffffff specifies no limit.

    entriesread --On return the actual enumerated element count is
        located in the DWORD pointed to by entriesread.

    totalentries --On return the total entries available to be
        enumerated is located in the DWORD pointed to by
        totalentries.

    resumehandle --On return, a resume handle is stored in the DWORD
        pointed to by resumehandle, and is used to continue an
        existing character device queue search. The handle should be

Return Value:


--*/

{
    NET_API_STATUS              apiStatus;
    GENERIC_INFO_CONTAINER      genericInfoContainer;
    GENERIC_ENUM_STRUCT         infoStruct;


    genericInfoContainer.Buffer = NULL;
    genericInfoContainer.EntriesRead = 0;

    infoStruct.Container = &genericInfoContainer;
    infoStruct.Level = level;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrCharDevQEnum (
                (LPWSTR)servername,
                (LPWSTR)username,
                (LPCHARDEVQ_ENUM_STRUCT) &infoStruct,
                prefmaxlen,
                totalentries,
                resume_handle);

        if (genericInfoContainer.Buffer != NULL) {
            *bufptr         = (LPBYTE)genericInfoContainer.Buffer;
            *entriesread    = genericInfoContainer.EntriesRead;
        } else {
            *bufptr = NULL;
            *entriesread = 0;
        }

    NET_REMOTE_RPC_FAILED(
            "NetCharDevQEnum",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

    apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetCharDevQEnum


NET_API_STATUS NET_API_FUNCTION
NetCharDevQGetInfo (
    IN  LPCWSTR  servername,
    IN  LPCWSTR  queuename,
    IN  LPCWSTR  username,
    IN  DWORD   level,
    OUT LPBYTE  *bufptr
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetCharDevQGetInfo.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    queuename --A pointer to an ASCIIZ string containing the name of
        the queue to return information on.

    username --A pointer to an ASCIIZ string containing the username
        of the a user whose job of of interest for the cq1_numahead
        count.

    level --Level of information required. 0 and 1 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    *bufptr = NULL;         // Must be NULL so RPC knows to till it in.

    NET_REMOTE_TRY_RPC

        apiStatus = NetrCharDevQGetInfo (
            (LPWSTR)servername,
            (LPWSTR)queuename,
            (LPWSTR)username,
            level,
            (LPCHARDEVQ_INFO) bufptr);

    NET_REMOTE_RPC_FAILED(
            "NetCharDevQGetInfo",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

    apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetCharDevQGetInfo


NET_API_STATUS NET_API_FUNCTION
NetCharDevQPurge (
    IN  LPCWSTR  servername,
    IN  LPCWSTR  queuename
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetCharDevQPurge.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    queuename --A pointer to an ASCIIZ string containing the name of
        the queue to be purged.

Return Value:


--*/

{
    NET_API_STATUS              apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrCharDevQPurge (
                        (LPWSTR)servername,
                        (LPWSTR)queuename);

    NET_REMOTE_RPC_FAILED(
            "NetCharDevQPurge",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

    apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetCharDevQPurge


NET_API_STATUS NET_API_FUNCTION
NetCharDevQPurgeSelf (
    IN  LPCWSTR  servername,
    IN  LPCWSTR  queuename,
    IN  LPCWSTR  computername
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetCharDevQPurgeSelf.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    queuename --A pointer to an ASCIIZ string containing the name of
        the queue to be purged of pending entries from the specified
        computer.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrCharDevQPurgeSelf (
                        (LPWSTR)servername,
                        (LPWSTR)queuename,
                        (LPWSTR)computername);

    NET_REMOTE_RPC_FAILED(
            "NetCharDevQPurgeSelf",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

    apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetCharDevQPurgeSelf


NET_API_STATUS NET_API_FUNCTION
NetCharDevQSetInfo (
    IN  LPCWSTR  servername,
    IN  LPCWSTR  queuename,
    IN  DWORD   level,
    IN  LPBYTE  buf,
    OUT LPDWORD parm_err
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetCharDevQSetInfo.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    queuename --A pointer to an ASCIIZ string containing the name of
        the queue to set information on.

    level --Level of information to set.

    buf --A pointer to a buffer containing the chardev information.
        If parmnum is non zero then the buffer contains only the
        appropriate data for the specific element. If parmnum is
        zero, then the buffer contains the whole chardev information
        structure.

    parm_err --Optional pointer to a DWORD to return the index of the
        first parameter in error when ERROR_INVALID_PARAMETER is
        returned. If NULL the parameter is not returned on error.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrCharDevQSetInfo (
            (LPWSTR)servername,
            (LPWSTR)queuename,
            level,
            (LPCHARDEVQ_INFO) &buf,
            parm_err);

    NET_REMOTE_RPC_FAILED(
            "NetCharDevQSetInfo",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

    apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetCharDevQSetInfo


NET_API_STATUS NET_API_FUNCTION
NetConnectionEnum (
    IN  LPTSTR      servername,
    IN  LPTSTR      qualifier,
    IN  DWORD       level,
    OUT LPBYTE      *bufptr,
    IN  DWORD       prefmaxlen,
    OUT LPDWORD     entriesread,
    OUT LPDWORD     totalentries,
    IN OUT LPDWORD  resume_handle
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetConnectionEnum.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    qualifier --A pointer to an ASCIIZ string containing a sharename
        or computername for the connections of interest. If it is a
        sharename, then all the connections made to that sharename
        are listed. If it is a computername (i.e. it starts with two
        backslash characters), then NetConnectionEnum lists all
        connections made from that computer to the server specified.

    level --Level of information required. 0 and 1 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

    prefmaxlen --Prefered maximum length of returned data (in 8-bit
        bytes). 0xffffffff specifies no limit.

    entriesread --On return the actual enumerated element count is
        located in the DWORD pointed to by entriesread.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;
    GENERIC_INFO_CONTAINER      genericInfoContainer;
    GENERIC_ENUM_STRUCT         infoStruct;

    genericInfoContainer.Buffer = NULL;
    genericInfoContainer.EntriesRead = 0;

    infoStruct.Container = &genericInfoContainer;
    infoStruct.Level = level;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrConnectionEnum (
                servername,
                qualifier,
                (LPCONNECT_ENUM_STRUCT)&infoStruct,
                prefmaxlen,
                totalentries,
                resume_handle);

        if (genericInfoContainer.Buffer != NULL) {
            *bufptr         = (LPBYTE)genericInfoContainer.Buffer;
            *entriesread    = genericInfoContainer.EntriesRead;
        } else {
            *bufptr = NULL;
            *entriesread = 0;
        }

    NET_REMOTE_RPC_FAILED(
            "NetConnectionEnum",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // Try call to downlevel.
        //
        apiStatus = RxNetConnectionEnum(
                servername,
                qualifier,
                level,
                bufptr,
                prefmaxlen,
                entriesread,
                totalentries,
                resume_handle
                );

    NET_REMOTE_END

    return(apiStatus);

} // NetConnectionEnum


NET_API_STATUS NET_API_FUNCTION
NetFileClose (
    IN LPTSTR   servername,
    IN DWORD    fileid
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetFileClose.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    fileid --The fileid of the opened resource instance to be closed.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrFileClose (
            servername,
            fileid);

    NET_REMOTE_RPC_FAILED(
            "NetFileClose",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        apiStatus = RxNetFileClose (
            servername,
            fileid);

    NET_REMOTE_END

    return(apiStatus);
}


NET_API_STATUS NET_API_FUNCTION
NetFileEnum (
    IN  LPTSTR         servername,
    IN  LPTSTR         basepath,
    IN  LPTSTR         username,
    IN  DWORD          level,
    OUT LPBYTE         *bufptr,
    IN  DWORD          prefmaxlen,
    OUT LPDWORD        entriesread,
    OUT LPDWORD        totalentries,
    IN OUT PDWORD_PTR  resume_handle
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetFileEnum.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    basepath --A pointer to an ASCIIZ string containing a qualifier
        for the returned information. If NULL then all open resources
        are enumerated, else only resources which have basepath as a
        prefix are enumerated.

    username --A pointer to an ASCIIZ string that specifies the name
        of the user. If not NULL, username serves as a qualifier to
        the ennumeration. The files returned are limited to those
        that have usernames matching the qualifier. If username is
        NULL, no username qualifier is used.

    level --Level of information required. 2 and 3 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

    prefmaxlen --Prefered maximum length of returned data (in 8-bit
        bytes). 0xffffffff specifies no limit.

    entriesread --On return the actual enumerated element count is
        located in the DWORD pointed to by entriesread.

    totalentries --On return the total entries available to be
        enumerated is located in the DWORD pointed to by
        totalentries.

    resumehandle --On return, a resume handle is stored in the DWORD
        pointed to by resumehandle, and is used to continue an
        existing file search. The handle should be zero on the first
        call and left unchanged for subsequent calls. If resumehandle
        is NULL, then no resume handle is stored..

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;
    GENERIC_INFO_CONTAINER      genericInfoContainer;
    GENERIC_ENUM_STRUCT         infoStruct;


    genericInfoContainer.Buffer = NULL;
    genericInfoContainer.EntriesRead = 0;

    infoStruct.Container = &genericInfoContainer;
    infoStruct.Level = level;

    NET_REMOTE_TRY_RPC

        //
        // NetrFileEnum's resume_handle parm is a true index that will remain
        // 32 bits wide for on-the-wire compatibility.  Thus, the cast to
        // (PDWORD) here works.
        //

        apiStatus = NetrFileEnum (
                servername,
                basepath,
                username,
                (LPFILE_ENUM_STRUCT) &infoStruct,
                prefmaxlen,
                totalentries,
                (PDWORD)resume_handle);

        if (genericInfoContainer.Buffer != NULL) {
            *bufptr         = (LPBYTE)genericInfoContainer.Buffer;
            *entriesread    = genericInfoContainer.EntriesRead;
        } else {
            *bufptr = NULL;
            *entriesread = 0;
        }

    NET_REMOTE_RPC_FAILED(
            "NetFileEnum",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        apiStatus = RxNetFileEnum(
                servername,
                basepath,
                username,
                level,
                bufptr,
                prefmaxlen,
                entriesread,
                totalentries,
                resume_handle);

    NET_REMOTE_END

    return(apiStatus);

} // NetFileEnum


NET_API_STATUS NET_API_FUNCTION
NetFileGetInfo (
    IN  LPTSTR  servername,
    IN  DWORD   fileid,
    IN  DWORD   level,
    OUT LPBYTE  *bufptr
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetFileGetInfo.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    fileid --The fileid of the open resource to return information
        on. The fileid value must be that returned in a previous
        enumeration call.

    level --Level of information required. 2 and 3 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    *bufptr = NULL;         // Must be NULL so RPC knows to fill it in.

    NET_REMOTE_TRY_RPC

        apiStatus = NetrFileGetInfo (
                servername,
                fileid,
                level,
                (LPFILE_INFO) bufptr);

    NET_REMOTE_RPC_FAILED(
            "NetFileGetInfo",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        apiStatus = RxNetFileGetInfo (
                servername,
                fileid,
                level,
                bufptr);

    NET_REMOTE_END

    return(apiStatus);

} // NetFileGetInfo


NET_API_STATUS NET_API_FUNCTION
NetSessionDel (
    IN  LPTSTR  servername,
    IN  LPTSTR  clientname,
    IN  LPTSTR  username
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetSessionDel.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    clientname --A pointer to an ASCIIZ string containing the
        computername of the client to disconnect.

    username --A pointer to an ASCIIZ string containing the name of
        the user whose session is to be terminated. A NULL indicates
        that all users' sessions from the computername specified are
        to be terminated.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrSessionDel (
                        servername,
                        clientname,
                        username);

    NET_REMOTE_RPC_FAILED("NetSessionDel", servername, apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER )

        //
        // Call downlevel version of the API.
        //

        apiStatus = RxNetSessionDel (
                servername,
                clientname,
                username);

    NET_REMOTE_END

    return(apiStatus);

} // NetSessionDel


NET_API_STATUS NET_API_FUNCTION
NetSessionEnum (
    IN      LPTSTR      servername,
    IN      LPTSTR      clientname,
    IN      LPTSTR      username,
    IN      DWORD       level,
    OUT     LPBYTE      *bufptr,
    IN      DWORD       prefmaxlen,
    OUT     LPDWORD     entriesread,
    OUT     LPDWORD     totalentries,
    IN OUT  LPDWORD     resume_handle
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetSessionEnum.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    clientname --A pointer to an ASCIIZ string containing the name of
        the computer session for which information is to be returned.
        A NULL pointer or string specifies that all computer sessions
        on the server are to be ennumerated.

    username --A pointer to an ASCIIZ string containing the name of
        the the user for which to ennumerate the sessions. A NULL
        pointer or string specifies that sessions for all users are
        to be ennumerated.

    level --Level of information required. 0, 1, 2 and 10 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

    prefmaxlen --Prefered maximum length of returned data (in 8-bit
        bytes). 0xffffffff specifies no limit.

    entriesread --On return the actual enumerated element count is
        located in the DWORD pointed to by entriesread.

    totalentries --On return the total entries available to be
        enumerated is located in the DWORD pointed to by
        totalentries.

    resumehandle --On return, a resume handle is stored in the DWORD
        pointed to by resumehandle, and is used to continue an
        existing session search. The handle should be zero on the
        first call and left unchanged for subsequent calls. If
        resumehandle is NULL, then no resume handle is stored.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;
    GENERIC_INFO_CONTAINER      genericInfoContainer;
    GENERIC_ENUM_STRUCT         infoStruct;

    genericInfoContainer.Buffer = NULL;
    genericInfoContainer.EntriesRead = 0;

    infoStruct.Container = &genericInfoContainer;
    infoStruct.Level = level;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrSessionEnum (
                servername,
                clientname,
                username,
                (PSESSION_ENUM_STRUCT) &infoStruct,
                prefmaxlen,
                totalentries,
                resume_handle);

        if (genericInfoContainer.Buffer != NULL) {
            *bufptr         = (LPBYTE)genericInfoContainer.Buffer;
            *entriesread    = genericInfoContainer.EntriesRead;
        } else {
            *bufptr = NULL;
            *entriesread = 0;
        }

    NET_REMOTE_RPC_FAILED("NetSessionEnum", servername, apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER )

        //
        // Call downlevel version of the API.
        //

        apiStatus = RxNetSessionEnum (
                servername,
                clientname,
                username,
                level,
                bufptr,
                prefmaxlen,
                entriesread,
                totalentries,
                resume_handle);

    NET_REMOTE_END

    return(apiStatus);

} // NetSessionEnum


NET_API_STATUS NET_API_FUNCTION
NetSessionGetInfo (
    IN      LPTSTR      servername,
    IN      LPTSTR      clientname,
    IN      LPTSTR      username,
    IN      DWORD       level,
    OUT     LPBYTE      *bufptr
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetSessionEnum.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    clientname --A pointer to an ASCIIZ string containing the name of
        the computer session for which information is to be returned.
        This field cannot be NULL.

    username --A pointer to an ASCIIZ string containing the name of
        the the user for which to ennumerate the sessions.  This field
        cannot be NULL.

    level --Level of information required. 0, 1, 2 and 10 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;
    GENERIC_INFO_CONTAINER      genericInfoContainer;
    GENERIC_ENUM_STRUCT         infoStruct;
    DWORD                       totalentries;

    if ( clientname == NULL || username == NULL ) {
        return ERROR_INVALID_PARAMETER;
    }

    genericInfoContainer.Buffer = NULL;
    genericInfoContainer.EntriesRead = 0;

    infoStruct.Container = &genericInfoContainer;
    infoStruct.Level = level;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrSessionEnum (
                servername,
                clientname,
                username,
                (PSESSION_ENUM_STRUCT) &infoStruct,
                (DWORD)-1,
                &totalentries,
                NULL);

        if (genericInfoContainer.Buffer != NULL) {
            *bufptr = (LPBYTE)genericInfoContainer.Buffer;
        } else {
            *bufptr = NULL;
            if ( apiStatus == NO_ERROR ) {
                return NERR_ClientNameNotFound;
            }
        }

    NET_REMOTE_RPC_FAILED("NetSessionGetInfo", servername, apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER )

        //
        // Call downlevel version of the API.
        //

        apiStatus = RxNetSessionGetInfo (
                servername,
                clientname,
                username,
                level,
                bufptr);

    NET_REMOTE_END

    return(apiStatus);

} // NetSessionGetInfo


NET_API_STATUS NET_API_FUNCTION
NetShareAdd (
    IN  LPTSTR  servername,
    IN  DWORD   level,
    IN  LPBYTE  buf,
    OUT LPDWORD parm_err
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetShareAdd.  Only levels 2 and 502
    are allowed.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    level --Level of information provided. Must be 2.

    buf --A pointer to a buffer containing the share information
        structure.

    parm_err --Optional pointer to a DWORD to return the index of the
        first parameter in error when ERROR_INVALID_PARAMETER is
        returned. If NULL the parameter is not returned on error.

Return Value:



--*/

{
    NET_API_STATUS              apiStatus;
    NTSTATUS                    status;
    ULONG                       SDLength = 0;
    ULONG                       oldSDLength;
    PSECURITY_DESCRIPTOR        securityDescriptor = NULL;
    PSECURITY_DESCRIPTOR        oldSecurityDescriptor = NULL;


    //
    // do the parameter validation here - this way we only need do it once and
    // in a centralized place
    //

    if (level != 2 && level != 502) {
        return ERROR_INVALID_LEVEL;
    }

    NET_REMOTE_TRY_RPC

        if ( level == 502 ) {

            PSHARE_INFO_502 shi502 = (LPSHARE_INFO_502) buf;

            //
            // Save this. We need to restore this later.
            //

            oldSecurityDescriptor = shi502->shi502_security_descriptor;
            oldSDLength = shi502->shi502_reserved;

            if ( oldSecurityDescriptor != NULL ) {

                if ( !RtlValidSecurityDescriptor( oldSecurityDescriptor) ) {
                    SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
                    return ERROR_INVALID_PARAMETER;
                }

                //
                // Make a self relative security descriptor for use in the
                // RPC call..
                //

                status = RtlMakeSelfRelativeSD(
                               oldSecurityDescriptor,
                               NULL,
                               &SDLength
                               );

                if (status != STATUS_BUFFER_TOO_SMALL) {

                    SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
                    return(ERROR_INVALID_PARAMETER);

                } else {

                    securityDescriptor = MIDL_user_allocate( SDLength );

                    if ( securityDescriptor == NULL) {

                        return ERROR_NOT_ENOUGH_MEMORY;

                    } else {

                        //
                        // make an appropriate self-relative security descriptor
                        //

                        status = RtlMakeSelfRelativeSD(
                                       oldSecurityDescriptor,
                                       (PSECURITY_DESCRIPTOR) securityDescriptor,
                                       &SDLength
                                       );

                        if ( !NT_SUCCESS(status) ) {
                            MIDL_user_free( securityDescriptor );
                            SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
                            return(ERROR_INVALID_PARAMETER);
                        }

                        shi502->shi502_security_descriptor = securityDescriptor;
                        shi502->shi502_reserved = SDLength;

                    }
                }

            } else {

                shi502->shi502_reserved = 0;

            }
        }

        apiStatus = NetrShareAdd (
                        servername,
                        level,
                        (LPSHARE_INFO) &buf,
                        parm_err);

        if ( securityDescriptor != NULL ) {

            //
            // restore old values
            //

            PSHARE_INFO_502 shi502 = (LPSHARE_INFO_502) buf;
            shi502->shi502_security_descriptor = oldSecurityDescriptor;
            shi502->shi502_reserved = oldSDLength;
            MIDL_user_free( securityDescriptor );
        }

    NET_REMOTE_RPC_FAILED(
            "NetShareAdd",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // Call downlevel server.
        //

        if ( level != 502 ) {
            apiStatus = RxNetShareAdd(
                            servername,
                            2,
                            buf,
                            parm_err
                            );
        } else {
            apiStatus = ERROR_NOT_SUPPORTED;
        }

    NET_REMOTE_END

    return(apiStatus);

} // NetShareAdd


NET_API_STATUS NET_API_FUNCTION
NetShareCheck   (
    IN  LPTSTR  servername,
    IN  LPTSTR  device,
    OUT LPDWORD type
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetShareCheck

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    device --A pointer to an ASCIIZ string containing the name of the
        device to check for shared access.

    type --On return the address pointed to by the type parameter
        contains the type of share the device is offered with. This
        field is only set if success was returned.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;


    if (!(device && *device) || !type) {
        return ERROR_INVALID_PARAMETER;
    }

    NET_REMOTE_TRY_RPC

        apiStatus = NetrShareCheck (
                        servername,
                        device,
                        type);

    NET_REMOTE_RPC_FAILED(
            "NetShareCheck",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // Call downlevel server.
        //

        apiStatus = RxNetShareCheck(servername, device, type);

    NET_REMOTE_END

    return(apiStatus);

} // NetShareCheck


NET_API_STATUS NET_API_FUNCTION
NetShareDel     (
    IN  LPTSTR  servername,
    IN  LPTSTR  netname,
    IN  DWORD   reserved
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetShareDel.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    netname --A pointer to an ASCIIZ string containing the netname of
        the share to delete.

    reserved --Reserved, must be zero.

Return Value:

--*/

{
    NET_API_STATUS apiStatus;
    BOOL committingIpcDelete = FALSE;
    SHARE_DEL_HANDLE handle;
    BOOL tryDownLevel = FALSE;

    if ( !netname || (*netname == 0) || reserved ) {
        return ERROR_INVALID_PARAMETER;
    }

    RpcTryExcept {

        if ( STRICMP( netname, TEXT("IPC$") ) != 0 ) {

            apiStatus = NetrShareDel(
                            servername,
                            netname,
                            reserved
                            );

        } else {

            apiStatus = NetrShareDelStart(
                            servername,
                            netname,
                            reserved,
                            &handle
                            );

            if ( apiStatus == NERR_Success ) {
                committingIpcDelete = TRUE;
                apiStatus = NetrShareDelCommit( &handle );
            }

        }

    } RpcExcept ( 1 ) {

        RPC_STATUS rpcStatus;

        rpcStatus = RpcExceptionCode( );

        if ( committingIpcDelete && (rpcStatus == RPC_S_CALL_FAILED) ) {

            apiStatus = NERR_Success;

        } else {

            apiStatus = NetpHandleRpcFailure(
                            "NetShareDel",
                            rpcStatus,
                            servername,
                            SERVICE_SERVER,
                            NET_REMOTE_FLAG_NORMAL,
                            &tryDownLevel
                            );

        }

    }

    RpcEndExcept

    if (apiStatus == NERR_TryDownLevel) {
        tryDownLevel = TRUE;
    }

    if ( tryDownLevel ) {

        //
        // Call downlevel server.
        //
        // note: push value 0 instead of real reserved
        //

        apiStatus = RxNetShareDel(servername, netname, 0);

    }

    return apiStatus;

} // NetShareDel


NET_API_STATUS NET_API_FUNCTION
NetShareDelSticky (
    IN  LPTSTR  servername,
    IN  LPTSTR  netname,
    IN  DWORD   reserved
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetShareDelSticky.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    netname --A pointer to an ASCIIZ string containing the netname of
        the share to delete.

    reserved --Reserved, must be zero.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;


    if (!(netname && *netname) || reserved) {
        return ERROR_INVALID_PARAMETER;
    }

    NET_REMOTE_TRY_RPC

        apiStatus = NetrShareDelSticky (
                        servername,
                        netname,
                        reserved);

    NET_REMOTE_RPC_FAILED(
            "NetShareDelSticky",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // No downlevel call.
        //

        apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetShareDelSticky


NET_API_STATUS NET_API_FUNCTION
NetShareEnum (
    IN  LPTSTR      servername,
    IN  DWORD       level,
    OUT LPBYTE      *bufptr,
    IN  DWORD       prefmaxlen,
    OUT LPDWORD     entriesread,
    OUT LPDWORD     totalentries,
    IN OUT LPDWORD  resume_handle
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetShareEnum

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    level --Level of information required. 0, 1 and 2 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

    prefmaxlen --Prefered maximum length of returned data (in 8-bit
        bytes). 0xffffffff specifies no limit.

    entriesread --On return the actual enumerated element count is
        located in the DWORD pointed to by entriesread.

    totalentries --On return the total entries available to be
        enumerated is located in the DWORD pointed to by
        totalentries.

    resumehandle --On return, a resume handle is stored in the DWORD
        pointed to by resumehandle, and is used to continue an
        existing share search. The handle should be zero on the first
        call and left unchanged for subsequent calls. If resumehandle
        is NULL, then no resume handle is stored..

Return Value:


--*/

{
    NET_API_STATUS              apiStatus;
    GENERIC_INFO_CONTAINER      genericInfoContainer;
    GENERIC_ENUM_STRUCT         infoStruct;

    //
    // check the caller's parameters
    //

    *totalentries = *entriesread = 0;
    *bufptr = NULL;

    if ( (level > 2) && (level != 501) && (level != 502) ) {
        return ERROR_INVALID_LEVEL;
    }

    genericInfoContainer.Buffer = NULL;
    genericInfoContainer.EntriesRead = 0;

    infoStruct.Container = &genericInfoContainer;
    infoStruct.Level = level;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrShareEnum (
                servername,
                (LPSHARE_ENUM_STRUCT) &infoStruct,
                prefmaxlen,
                totalentries,
                resume_handle);

        if (genericInfoContainer.Buffer != NULL) {
            *bufptr         = (LPBYTE)genericInfoContainer.Buffer;
            *entriesread    = genericInfoContainer.EntriesRead;

        } else {
            *bufptr = NULL;
            *entriesread = 0;
        }

    NET_REMOTE_RPC_FAILED(
            "NetShareEnum",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // Call downlevel server.
        //
        if ( level != 502 && level != 501 ) {
            apiStatus = RxNetShareEnum(servername, level, bufptr,
                    prefmaxlen, entriesread, totalentries, resume_handle);
        } else {
            apiStatus = ERROR_NOT_SUPPORTED;
        }

    NET_REMOTE_END

    //
    // If we haven't gotten anything, and the server is offline,
    //  return the offline share state
    //
    if( ( *bufptr == NULL || *entriesread == 0 ) &&
        ARGUMENT_PRESENT( servername ) &&
        CSCIsServerOffline( servername ) ) {

        NET_API_STATUS cscStatus;

        cscStatus = CSCNetShareEnum(
                                servername,
                                level,
                                bufptr,
                                entriesread,
                                totalentries
                                );

        if( cscStatus == NERR_Success ) {
            apiStatus = cscStatus;
        }
    }

    return(apiStatus);

} // NetShareEnum

NET_API_STATUS NET_API_FUNCTION
NetShareEnumSticky (
    IN  LPTSTR      servername,
    IN  DWORD       level,
    OUT LPBYTE      *bufptr,
    IN  DWORD       prefmaxlen,
    OUT LPDWORD     entriesread,
    OUT LPDWORD     totalentries,
    IN OUT LPDWORD  resume_handle
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetShareEnumSticky

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    level --Level of information required. 0, 1 and 2 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

    prefmaxlen --Prefered maximum length of returned data (in 8-bit
        bytes). 0xffffffff specifies no limit.

    entriesread --On return the actual enumerated element count is
        located in the DWORD pointed to by entriesread.

    totalentries --On return the total entries available to be
        enumerated is located in the DWORD pointed to by
        totalentries.

    resumehandle --On return, a resume handle is stored in the DWORD
        pointed to by resumehandle, and is used to continue an
        existing share search. The handle should be zero on the first
        call and left unchanged for subsequent calls. If resumehandle
        is NULL, then no resume handle is stored..

Return Value:


--*/

{
    NET_API_STATUS              apiStatus;
    GENERIC_INFO_CONTAINER      genericInfoContainer;
    GENERIC_ENUM_STRUCT         infoStruct;


    //
    // check the caller's parameters
    //

    *totalentries = *entriesread = 0;
    *bufptr = NULL;

    if ( (level > 2) && (level != 501) && (level != 502) ) {
        return ERROR_INVALID_LEVEL;
    }

    genericInfoContainer.Buffer = NULL;
    genericInfoContainer.EntriesRead = 0;

    infoStruct.Container = &genericInfoContainer;
    infoStruct.Level = level;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrShareEnumSticky (
                servername,
                (LPSHARE_ENUM_STRUCT) &infoStruct,
                prefmaxlen,
                totalentries,
                resume_handle);

        if (genericInfoContainer.Buffer != NULL) {
            *bufptr         = (LPBYTE)genericInfoContainer.Buffer;
            *entriesread    = genericInfoContainer.EntriesRead;
        } else {
            *bufptr = NULL;
            *entriesread = 0;
        }

    NET_REMOTE_RPC_FAILED(
            "NetShareEnum",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // No downlevel support
        //

        apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetShareEnumSticky


NET_API_STATUS NET_API_FUNCTION
NetShareGetInfo (
    IN  LPTSTR  servername,
    IN  LPTSTR  netname,
    IN  DWORD   level,
    OUT LPBYTE  *bufptr
    )
/*++

Routine Description:

    NetShareGetInfo

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    netname --A pointer to an ASCIIZ string containing the netname of
        the share to return information on.

    level --Level of information required. 0, 1 and 2 are valid.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    if (bufptr == NULL) {
        return ERROR_INVALID_PARAMETER;
    }

    *bufptr = NULL;         // Must be NULL so RPC knows to fill it in.

    if ( (level > 2)     &&
         (level != 501)  &&
         (level != 502)  &&
         (level != 1005) ) {

        return ERROR_INVALID_LEVEL;
    }

    if (!(netname && *netname)) {
        return ERROR_INVALID_PARAMETER;
    }

    NET_REMOTE_TRY_RPC

        apiStatus = NetrShareGetInfo (
            servername,
            netname,
            level,
            (LPSHARE_INFO) bufptr);

    NET_REMOTE_RPC_FAILED(
            "NetShareGetInfo",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // Call downlevel server.
        //

        if( level == 0 || level == 1 || level == 2 ) {
            apiStatus = RxNetShareGetInfo(servername, netname, level, bufptr);
        } else {
            apiStatus = ERROR_NOT_SUPPORTED;
        }

    NET_REMOTE_END

    if( *bufptr == NULL &&
        ARGUMENT_PRESENT( servername ) &&
        ARGUMENT_PRESENT( netname ) &&
        CSCIsServerOffline( servername) ) {

        NET_API_STATUS cscStatus;

        cscStatus = CSCNetShareGetInfo ( servername, netname, level, bufptr );

        if( cscStatus == NERR_Success ) {
            apiStatus = cscStatus;
        }
    }
    return(apiStatus);

} // NetShareGetInfo


NET_API_STATUS NET_API_FUNCTION
NetShareSetInfo (
    IN  LPTSTR  servername,
    IN  LPTSTR  netname,
    IN  DWORD   level,
    IN  LPBYTE  buf,
    OUT LPDWORD parm_err
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetShareSetInfo

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    netname --A pointer to an ASCIIZ string containing the netname of
        the share to set information on.

    level --Level of information to set.

    buf --A pointer to a buffer containing the share information. If
        parmnum is non zero then the buffer contains only the
        appropriate data for the specific element.

    parm_err --Optional pointer to a DWORD to return the index of the
        first parameter in error when ERROR_INVALID_PARAMETER is
        returned. If NULL the parameter is not returned on error.

Return Value:

--*/

{

    NET_API_STATUS              apiStatus;
    NTSTATUS                    status;
    ULONG                       sdLength = 0;
    ULONG                       oldSdLength;
    PSECURITY_DESCRIPTOR        securityDescriptor = NULL;
    PSECURITY_DESCRIPTOR        oldSecurityDescriptor = NULL;
    LPSHARE_INFO_1501           shi1501 = NULL;

    NET_REMOTE_TRY_RPC

        //
        // If the info level can change the security descriptor, get
        // the necessary information.
        //
        // *** Note that this code expects the layout of the reserved
        //     and security_descriptor fields in the 502 struct to
        //     match the 1501 struct.
        //

        if ( level == 502 ) {

            if (buf == NULL) {

                SET_ERROR_PARAMETER(PARM_ERROR_UNKNOWN);
                return ERROR_INVALID_PARAMETER;
            }

            shi1501 =
                (LPSHARE_INFO_1501)&((LPSHARE_INFO_502)buf)->shi502_reserved;

        } else if ( level == SHARE_FILE_SD_INFOLEVEL ) {

            shi1501 = (LPSHARE_INFO_1501)buf;

        }

        if ( shi1501 != NULL ) {

            oldSdLength = shi1501->shi1501_reserved;
            oldSecurityDescriptor = shi1501->shi1501_security_descriptor;

            if ( oldSecurityDescriptor != NULL ) {

                //
                // Make a self relative security descriptor for use in the
                // RPC call.
                //

                if ( !RtlValidSecurityDescriptor( oldSecurityDescriptor) ) {
                    SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
                    return ERROR_INVALID_PARAMETER;
                }

                status = RtlMakeSelfRelativeSD(
                               oldSecurityDescriptor,
                               NULL,
                               &sdLength
                               );

                if ( status != STATUS_BUFFER_TOO_SMALL ) {
                    SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
                    return ERROR_INVALID_PARAMETER;

                } else {

                    securityDescriptor = MIDL_user_allocate( sdLength );

                    if ( securityDescriptor == NULL) {
                        return ERROR_NOT_ENOUGH_MEMORY;

                    } else {

                        //
                        // Make an appropriate self-relative security
                        // descriptor.
                        //

                        status = RtlMakeSelfRelativeSD(
                                       oldSecurityDescriptor,
                                       securityDescriptor,
                                       &sdLength
                                       );

                        if ( !NT_SUCCESS(status) ) {
                            MIDL_user_free( securityDescriptor );
                            SET_ERROR_PARAMETER( SHARE_FILE_SD_PARMNUM );
                            return ERROR_INVALID_PARAMETER;
                        }

                        shi1501->shi1501_reserved = sdLength;
                        shi1501->shi1501_security_descriptor =
                                                    securityDescriptor;

                    }

                }

            } else {

                shi1501->shi1501_reserved = 0;

            }
        }

        apiStatus = NetrShareSetInfo(
                                servername,
                                netname,
                                level,
                                (LPSHARE_INFO) &buf,
                                parm_err);

        if ( shi1501 != NULL ) {

            //
            // restore old values
            //

            shi1501->shi1501_reserved = oldSdLength;
            shi1501->shi1501_security_descriptor = oldSecurityDescriptor;

            MIDL_user_free( securityDescriptor );

        }


    NET_REMOTE_RPC_FAILED(
            "NetShareSetInfo",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // Call downlevel server.
        //

        if ( level != 502 &&
             level != 501 &&
             level != SHARE_FILE_SD_INFOLEVEL &&
             level != 1005 ) {

            apiStatus = RxNetShareSetInfo(
                    servername,
                    netname,
                    level,
                    buf,
                    parm_err);
        } else {
            apiStatus = ERROR_NOT_SUPPORTED;
        }

    NET_REMOTE_END

    return(apiStatus);

} // NetShareSetInfo


NET_API_STATUS NET_API_FUNCTION
NetServerDiskEnum (
    IN  LPTSTR      servername,
    IN  DWORD       level,
    OUT LPBYTE      *bufptr,
    IN  DWORD       prefmaxlen,
    OUT LPDWORD     entriesread,
    OUT LPDWORD     totalentries,
    IN OUT LPDWORD  resume_handle
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetServerDiskEnum.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    level --Level of information required. 0 is the only valid level.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

    prefmaxlen --Prefered maximum length of returned data (in 8-bit
        bytes). 0xffffffff specifies no limit.

    entriesread --On return the actual enumerated element count is
        located in the DWORD pointed to by entriesread.

    totalentries --On return the total entries available to be
        enumerated is located in the DWORD pointed to by totalentries

    resumehandle --On return, a resume handle is stored in the DWORD
        pointed to by resumehandle, and is used to continue an
        existing server disk search. The handle should be zero on the
        first call and left unchanged for subsequent calls. If
        resumehandle is NULL, then no resume handle is stored..

Return Value:

--*/
{
    NET_API_STATUS              apiStatus;
    DISK_ENUM_CONTAINER         diskEnumContainer;


    diskEnumContainer.Buffer = NULL;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrServerDiskEnum (
                servername,
                level,
                &diskEnumContainer,
                prefmaxlen,
                totalentries,
                resume_handle);

        if (diskEnumContainer.Buffer != NULL) {
            *bufptr = (LPBYTE)diskEnumContainer.Buffer;
        } else {
            *bufptr = NULL;
        }

        if (diskEnumContainer.EntriesRead > 0) {

            //
            // We must subtract out the extra count that we added so
            // that RPC would buffer the extra NUL at the end of the list.
            //

            *entriesread = diskEnumContainer.EntriesRead - 1;

        } else {
            *entriesread = 0;
        }

    NET_REMOTE_RPC_FAILED(
            "NetServerDiskEnum",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // Call downlevel version of the API.
        //

        apiStatus = RxNetServerDiskEnum(
                servername,
                level,
                bufptr,
                prefmaxlen,
                entriesread,
                totalentries,
                resume_handle);

    NET_REMOTE_END

    return(apiStatus);

} // NetServerDiskEnum


NET_API_STATUS NET_API_FUNCTION
NetServerGetInfo (
    IN  LPTSTR      servername,
    IN  DWORD       level,
    OUT LPBYTE      *bufptr
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetServerGetInfo

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    level --Level of information required. 100, 101 and 102 are valid
        for all platforms. 302, 402, 403, 502 are valid for the
        appropriate platform.

    bufptr --On return a pointer to the return information structure
        is returned in the address pointed to by bufptr.

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    *bufptr = NULL;     // Must be NULL so RPC knows to fill it in.

    NET_REMOTE_TRY_RPC

        apiStatus = NetrServerGetInfo (
                servername,
                level,
                (LPSERVER_INFO) bufptr);

    NET_REMOTE_RPC_FAILED(
            "NetServerGetInfo",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // Call downlevel version of the API.
        //
        apiStatus = RxNetServerGetInfo (
                servername,
                level,
                bufptr);

    NET_REMOTE_END

    return(apiStatus);

} // NetServerGetInfo


NET_API_STATUS NET_API_FUNCTION
NetServerSetInfo (
    IN  LPTSTR  servername,
    IN  DWORD   level,
    IN  LPBYTE  buf,
    OUT LPDWORD parm_err
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetServerSetInfo.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    level --Level of information to set.

    buf --A pointer to a buffer containing the server information. If
        parmnum is non zero then the buffer contains only the
        appropriate data for the specific element.

    parm_err --Optional pointer to a DWORD to return the index of the
        first parameter in error when ERROR_INVALID_PARAMETER is
        returned. If NULL the parameter is not returned on error.

Return Value:

--*/
{
    NET_API_STATUS              apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrServerSetInfo (
                servername,
                level,
                (LPSERVER_INFO ) &buf,
                parm_err);

    NET_REMOTE_RPC_FAILED(
            "NetServerSetInfo",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // Call downlevel server.
        //

        apiStatus = RxNetServerSetInfo(
                servername,
                level,
                buf,
                parm_err);

    NET_REMOTE_END

    return(apiStatus);

} // NetServerSetInfo


NET_API_STATUS NET_API_FUNCTION
NetServerStatisticsGet (
    IN  LPTSTR  servername,
    IN  DWORD   level,
    IN  DWORD   options,
    OUT LPBYTE  *bufptr
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetStatisticsGet.

Arguments:

    servername --Points to an ASCIIZ string that contains the name of the
        server on which to execute NetStatisticsGet. A NULL pointer or
        NULL string specifies the local computer.

    level --Specifies the level of detail requested; must be 0.

    options --Specifies the options flags.

          Bit(s)            Meaning
          0                 Clear statistics.
          1-31              Reserved; must be 0.

    bufptr --On return a pointer to the returned information is
        returned in the address pointed to by bufptr.

Return Value:

--*/
{
    NET_API_STATUS              apiStatus;

    *bufptr = NULL;     // Must be NULL so RPC knows to fill it in.

    NET_REMOTE_TRY_RPC


        apiStatus = NetrServerStatisticsGet (
                servername,
                SERVICE_SERVER,
                level,
                options,
                (LPSTAT_SERVER_0 *) bufptr);

    NET_REMOTE_RPC_FAILED(
            "NetServerStatisticsGet",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // RPC call didn't work - try down-level routine
        //

        apiStatus = RxNetStatisticsGet(
            servername,
            SERVICE_SERVER,
            level,
            options,
            bufptr
            );

    NET_REMOTE_END

    return(apiStatus);

} // NetServerStatisticsGet


NET_API_STATUS NET_API_FUNCTION
NetServerTransportAdd (
    IN  LPTSTR  servername,
    IN  DWORD   level,
    IN  LPBYTE  bufptr
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetServerTransportAdd

Arguments:

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrServerTransportAdd (
                servername,
                level,
                (LPSERVER_TRANSPORT_INFO_0) bufptr);

    NET_REMOTE_RPC_FAILED(
            "NetServerTransportAdd",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // No downlevel call.
        //

        apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetServerTransportAdd

NET_API_STATUS NET_API_FUNCTION
NetServerTransportDelEx (
    IN  LPTSTR  servername,
    IN  DWORD   level,
    IN  LPBYTE  bufptr
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetServerTransportAdd

Arguments:

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrServerTransportDelEx (
                servername,
                level,
                (LPTRANSPORT_INFO) bufptr);

    NET_REMOTE_RPC_FAILED(
            "NetServerTransportDel",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // No downlevel call.
        //

        apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    // Around the Win2K/NT4 time frame a bug was introduced for TransportDel with
    // level other than 0.  In these cases, the buffer was cast instead of marshalled
    // correctly over RPC.  This means that Level 1 never worked anyway.  To fix this,
    // we added a new RPC interface that supports all the other levels.  However, downlevel
    // servers will not support this interface.  In these cases, we call back with the TRUE
    // level 0 interface to satisfy backwards compatibility
    if( apiStatus == RPC_S_PROCNUM_OUT_OF_RANGE )
    {
        apiStatus = NetServerTransportDel( servername, 0, bufptr );
    }

    return(apiStatus);

} // NetServerTransportDel



NET_API_STATUS NET_API_FUNCTION
NetServerTransportDel (
    IN  LPTSTR  servername,
    IN  DWORD   level,
    IN  LPBYTE  bufptr
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetServerTransportAdd

Arguments:

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;

    if( level == 0 )
    {

        NET_REMOTE_TRY_RPC

            apiStatus = NetrServerTransportDel (
                    servername,
                    level,
                    (LPSERVER_TRANSPORT_INFO_0) bufptr);

        NET_REMOTE_RPC_FAILED(
                "NetServerTransportDel",
                servername,
                apiStatus,
                NET_REMOTE_FLAG_NORMAL,
                SERVICE_SERVER)

            //
            // No downlevel call.
            //

            apiStatus = ERROR_NOT_SUPPORTED;

        NET_REMOTE_END
    }
    else
    {
        // If they want level 1, we need to use the new RPC interface.  See
        // the comment in the TransportDelEx code above
        apiStatus = NetServerTransportDelEx( servername, level, bufptr );
    }

    return(apiStatus);

} // NetServerTransportDel


NET_API_STATUS NET_API_FUNCTION
NetServerTransportEnum (
    IN  LPTSTR      servername,
    IN  DWORD       level,
    OUT LPBYTE      *bufptr,
    IN  DWORD       prefmaxlen,
    OUT LPDWORD     entriesread,
    OUT LPDWORD     totalentries,
    IN OUT LPDWORD  resume_handle
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetServerTransportEnum

Arguments:

Return Value:

--*/

{
    NET_API_STATUS              apiStatus;
    GENERIC_INFO_CONTAINER      genericInfoContainer;
    GENERIC_ENUM_STRUCT         infoStruct;

    genericInfoContainer.Buffer = NULL;
    genericInfoContainer.EntriesRead = 0;

    infoStruct.Container = &genericInfoContainer;
    infoStruct.Level = level;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrServerTransportEnum (
                servername,
                (LPSERVER_XPORT_ENUM_STRUCT) &infoStruct,
                prefmaxlen,
                totalentries,
                resume_handle);

        if (genericInfoContainer.Buffer != NULL) {
            *bufptr         = (LPBYTE)genericInfoContainer.Buffer;
            *entriesread    = genericInfoContainer.EntriesRead;
        } else {
            *bufptr = NULL;
            *entriesread = 0;
        }

    NET_REMOTE_RPC_FAILED(
            "NetServerTransportEnum",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // No downlevel call.
        //

        apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // NetServerTransportEnum


NET_API_STATUS NET_API_FUNCTION
NetRemoteTOD (
    IN  LPCWSTR       servername,
    OUT LPBYTE      *bufptr
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetRemoteTOD

Arguments:

    servername - name of the server on which the API so to be executed.
    bufptr     - the location where the address of the buffer allocated
                 for the time-of-day information is placed.

Return Value:

    NERR_SUCCESS if there was no error. Otherwise, the error code is
    returned.


--*/
{
    NET_API_STATUS          apiStatus;

    //
    // Call API
    //
    *bufptr = NULL;           // Must be NULL so RPC knows to fill it in.

    NET_REMOTE_TRY_RPC

        apiStatus = NetrRemoteTOD (
                (LPWSTR)servername,
                (TIME_OF_DAY_INFO **) bufptr);

    NET_REMOTE_RPC_FAILED(
            "NetRemoteTOD",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_TIMESOURCE )

        apiStatus = RxNetRemoteTOD (
                (LPWSTR)servername,
                (LPBYTE *) bufptr);

    NET_REMOTE_END

    return(apiStatus);
}


NET_API_STATUS
I_NetServerSetServiceBitsEx (
    IN  LPWSTR  ServerName,
    IN  LPWSTR  EmulatedServerName OPTIONAL,
    IN  LPTSTR  TransportName      OPTIONAL,
    IN  DWORD   ServiceBitsOfInterest,
    IN  DWORD   ServiceBits,
    IN  DWORD   UpdateImmediately
    )

/*++

Routine Description:

    This is the DLL entrypoint for I_NetServerSetServiceBitsEx.  This
    routine sets the value of the Server Type as sent in server
    announcement messages.  It is an internal API used only by the
    service controller.

Arguments:

    ServerName - Used by RPC to direct the call.  This API may only be
        issued locally.  This is enforced by the client stub.

    EmulatedServerName - the name server using for accepting connections
        on the network and for announcements.  If null, use the priamary
        server name.

    TransportName - the name of one of the transports the server is bound
        on.  If null, set the bits for all the transports.

    ServiceBitsOfInterest - a mask indicating which bits are significant
        in 'ServiceBits'

    ServiceBits - Bits (preassigned to various components by Microsoft)
        indicating which services are active.  This field is not
        interpreted by the server service.

Return Value:

    NET_API_STATUS - NO_ERROR or ERROR_NOT_SUPPORTED.

--*/

{
    NET_API_STATUS              apiStatus;
    DWORD                       localOrRemote;

    //
    // Don't let this API go remote.
    //

    if ((ServerName != NULL) && (*ServerName != '\0')) {
        apiStatus = NetpIsRemote(ServerName, &localOrRemote, NULL, 0);
        if (apiStatus != NERR_Success) {
            return apiStatus;
        }
        if (localOrRemote == ISREMOTE) {
            return ERROR_NOT_SUPPORTED;
        }
    }

    //
    // Do the call.
    //

    NET_REMOTE_TRY_RPC

        apiStatus = I_NetrServerSetServiceBitsEx (
                ServerName,
                EmulatedServerName,
                TransportName,
                ServiceBitsOfInterest,
                ServiceBits,
                UpdateImmediately);

    NET_REMOTE_RPC_FAILED(
            "I_NetServerSetServiceBitsEx",
            ServerName,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // No downlevel call.
        //

        apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // I_NetServerSetServiceBitsEx

NET_API_STATUS
I_NetServerSetServiceBits (
    IN  LPTSTR  servername,
    IN  LPTSTR  transportname,
    IN  DWORD   servicebits,
    IN  DWORD   updateimmediately
    )

/*++

Routine Description:

    This is the DLL entrypoint for I_NetServerSetServiceBits.  This
    routine sets the value of the Server Type as sent in server
    announcement messages.  It is an internal API used only by the
    service controller.

Arguments:

    ServerName - Used by RPC to direct the call.  This API may only be
        issued locally.  This is enforced by the client stub.

    ServiceBits - Bits (preassigned to various components by Microsoft)
        indicating which services are active.  This field is not
        interpreted by the server service.

Return Value:

    NET_API_STATUS - NO_ERROR or ERROR_NOT_SUPPORTED.

--*/

{
    NET_API_STATUS              apiStatus;
    DWORD                       localOrRemote;

    //
    // Don't let this API go remote.
    //

    if ((servername != NULL) && (*servername != '\0')) {
        apiStatus = NetpIsRemote(servername, &localOrRemote, NULL, 0);
        if (apiStatus != NERR_Success) {
            return apiStatus;
        }
        if (localOrRemote == ISREMOTE) {
            return ERROR_NOT_SUPPORTED;
        }
    }

    //
    // Do the call.
    //

    NET_REMOTE_TRY_RPC

        apiStatus = I_NetrServerSetServiceBits (
                servername,
                transportname,
                servicebits,
                updateimmediately);

    //
    // This API is called by the Service Controller only.  Don't let
    // the failure path call any SCM APIs since that may deadlock
    // services.exe in the loopback.
    //

    NET_REMOTE_RPC_FAILED(
            "I_NetServerSetServiceBits",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_SVC_CTRL,
            SERVICE_SERVER)

        //
        // No downlevel call.
        //

        apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    return(apiStatus);

} // I_NetServerSetServiceBits
//
// Netps canonicalization functions. These are essentially private functions
// and are called from the API stubs in canonapi.c. The canonicalization
// functions have to be usable locally without going via the server service
// hence they live in NETAPI.DLL, but call local functions in NETLIB if the
// ServerName parameter is NULL (or designates the local machine). If the
// ServerName parameter is not NULL and designates a remote computer then the
// RPC function (here) will be called, hence the remote server must be
// running in order to make remote canonicalization requests
//

NET_API_STATUS
NET_API_FUNCTION
NetpsNameCanonicalize(
    IN  LPTSTR  ServerName,
    IN  LPTSTR  Name,
    OUT LPTSTR  Outbuf,
    IN  DWORD   OutbufLen,
    IN  DWORD   NameType,
    IN  DWORD   Flags
    )

/*++

Routine Description:

    Canonicalizes a name

Arguments:

    ServerName  - where to run this API
    Name        - name to canonicalize
    Outbuf      - where to put canonicalized name
    OutbufLen   - length of Outbuf
    NameType    - type of name to canonicalize
    Flags       - control flags

Return Value:

    NET_API_STATUS

--*/

{
    NET_API_STATUS apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetprNameCanonicalize(ServerName,
                                            Name,
                                            Outbuf,
                                            OutbufLen,
                                            NameType,
                                            Flags
                                            );

    NET_REMOTE_RPC_FAILED("NetpsNameCanonicalize",
                            ServerName,
                            apiStatus,
                            NET_REMOTE_FLAG_NORMAL,
                            SERVICE_SERVER)

        //
        // RPC call didn't work - try down-level routine
        //

        apiStatus = RxNetpNameCanonicalize(ServerName,
                                            Name,
                                            Outbuf,
                                            OutbufLen,
                                            NameType,
                                            Flags
                                            );

    NET_REMOTE_END

    return apiStatus;
}

LONG
NET_API_FUNCTION
NetpsNameCompare(
    IN  LPTSTR  ServerName,
    IN  LPTSTR  Name1,
    IN  LPTSTR  Name2,
    IN  DWORD   NameType,
    IN  DWORD   Flags
    )

/*++

Routine Description:

    Compares two names. Must be of same type

Arguments:

    ServerName  - where to run this API
    Name1       - 1st name to compare
    Name2       - 2nd
    NameType    - type of names
    Flags       - control flags

Return Value:

    LONG

--*/

{
    NET_API_STATUS apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetprNameCompare(ServerName, Name1, Name2, NameType, Flags);

    NET_REMOTE_RPC_FAILED("NetpsNameCompare",
                            ServerName,
                            apiStatus,
                            NET_REMOTE_FLAG_NORMAL,
                            SERVICE_SERVER)

        //
        // RPC call didn't work - try down-level routine
        //

        apiStatus = RxNetpNameCompare(ServerName, Name1, Name2, NameType, Flags);

    NET_REMOTE_END

    return apiStatus;
}

NET_API_STATUS
NET_API_FUNCTION
NetpsNameValidate(
    IN  LPTSTR  ServerName,
    IN  LPTSTR  Name,
    IN  DWORD   NameType,
    IN  DWORD   Flags
    )

/*++

Routine Description:

    Validates a name - checks whether a name of a certain type conforms to
    canonicalization rules for that name type. Canonicalization rules mean
    character set, name syntax and length

Arguments:

    ServerName  - where to perform this function
    Name        - name to validate
    NameType    - what type of name it is
    Flags       - MBZ

Return Value:

    NET_API_STATUS

--*/

{
    NET_API_STATUS apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetprNameValidate(ServerName, Name, NameType, Flags);

    NET_REMOTE_RPC_FAILED("NetpsNameValidate",
                            ServerName,
                            apiStatus,
                            NET_REMOTE_FLAG_NORMAL,
                            SERVICE_SERVER)

        //
        // RPC call didn't work - try down-level routine
        //

        apiStatus = RxNetpNameValidate(ServerName, Name, NameType, Flags);

    NET_REMOTE_END

    return apiStatus;
}

NET_API_STATUS
NET_API_FUNCTION
NetpsPathCanonicalize(
    IN  LPTSTR  ServerName,
    IN  LPTSTR  PathName,
    OUT LPTSTR  Outbuf,
    IN  DWORD   OutbufLen,
    IN  LPTSTR  Prefix OPTIONAL,
    IN OUT LPDWORD PathType,
    IN  DWORD   Flags
    )

/*++

Routine Description:

    Canonicalizes a directory path or a device name

Arguments:

    ServerName  - where to run this API
    PathName    - path to canonicalize
    Outbuf      - where to write the canonicalized version
    OutbufLen   - length of Outbuf in bytes
    Prefix      - optional prefix which will be prepended to Path
    PathType    - the type of path to canonicalize. May be different at output
    Flags       - control flags

Return Value:

    NET_API_STATUS

--*/

{
    NET_API_STATUS apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetprPathCanonicalize(ServerName,
                                            PathName,
                                            (LPBYTE)Outbuf,
                                            OutbufLen,
                                            Prefix,
                                            PathType,
                                            Flags
                                            );

    NET_REMOTE_RPC_FAILED("NetpsPathCanonicalize",
                            ServerName,
                            apiStatus,
                            NET_REMOTE_FLAG_NORMAL,
                            SERVICE_SERVER)

        //
        // RPC call didn't work - try down-level routine
        //

        apiStatus = RxNetpPathCanonicalize(ServerName,
                                            PathName,
                                            Outbuf,
                                            OutbufLen,
                                            Prefix,
                                            PathType,
                                            Flags
                                            );

    NET_REMOTE_END

    return apiStatus;
}

LONG
NET_API_FUNCTION
NetpsPathCompare(
    IN  LPTSTR  ServerName,
    IN  LPTSTR  PathName1,
    IN  LPTSTR  PathName2,
    IN  DWORD   PathType,
    IN  DWORD   Flags
    )

/*++

Routine Description:

    Compares two paths. The paths are assumed to be of the same type

Arguments:

    ServerName  - where to run this API
    PathName1   - 1st path to compare
    PathName2   - 2nd
    PathType    - types of paths
    Flags       - control flags

Return Value:

    LONG

--*/

{
    NET_API_STATUS apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetprPathCompare(ServerName,
                                    PathName1,
                                    PathName2,
                                    PathType,
                                    Flags
                                    );

    NET_REMOTE_RPC_FAILED("NetpsPathCompare",
                            ServerName,
                            apiStatus,
                            NET_REMOTE_FLAG_NORMAL,
                            SERVICE_SERVER)

        //
        // RPC call didn't work - try down-level routine
        //

        apiStatus = RxNetpPathCompare(ServerName,
                                        PathName1,
                                        PathName2,
                                        PathType,
                                        Flags
                                        );

    NET_REMOTE_END

    return apiStatus;
}

NET_API_STATUS
NET_API_FUNCTION
NetpsPathType(
    IN  LPTSTR  ServerName,
    IN  LPTSTR  PathName,
    OUT LPDWORD PathType,
    IN  DWORD   Flags
    )

/*++

Routine Description:

    Determines the type of a path

Arguments:

    ServerName  - where to run this API
    PathName    - to find type of
    PathType    - returned path type
    Flags       - control flags

Return Value:

    NET_API_STATUS

--*/

{
    NET_API_STATUS apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetprPathType(ServerName, PathName, PathType, Flags);

    NET_REMOTE_RPC_FAILED("NetpsPathType",
                            ServerName,
                            apiStatus,
                            NET_REMOTE_FLAG_NORMAL,
                            SERVICE_SERVER)

        //
        // RPC call didn't work - try down-level routine
        //

        apiStatus = RxNetpPathType(ServerName, PathName, PathType, Flags);

    NET_REMOTE_END

    return apiStatus;
}

NET_API_STATUS NET_API_FUNCTION
NetServerTransportAddEx (
    IN  LPTSTR  servername,
    IN  DWORD   level,
    IN  LPBYTE  bufptr
    )

/*++

Routine Description:

    This is the DLL entrypoint for NetServerTransportAddEx.  It functions
     just like NetServerTransportAdd, but it supports level 1 as well as 0

--*/

{
    NET_API_STATUS              apiStatus;

    NET_REMOTE_TRY_RPC

        apiStatus = NetrServerTransportAddEx (
                servername,
                level,
                (LPTRANSPORT_INFO) bufptr);

    NET_REMOTE_RPC_FAILED(
            "NetServerTransportAddEx",
            servername,
            apiStatus,
            NET_REMOTE_FLAG_NORMAL,
            SERVICE_SERVER)

        //
        // No downlevel call.
        //

        apiStatus = ERROR_NOT_SUPPORTED;

    NET_REMOTE_END

    if( apiStatus == RPC_NT_PROCNUM_OUT_OF_RANGE ) {
        apiStatus = NERR_InvalidAPI;
    }

    return(apiStatus);

} // NetServerTransportAddEx


NET_API_STATUS NET_API_FUNCTION
NetServerComputerNameAdd(
    IN LPWSTR ServerName         OPTIONAL,
    IN LPWSTR EmulatedDomainName OPTIONAL,
    IN LPWSTR EmulatedServerName
)
/*++

Routine Description:

    This is the DLL entrypoint for NetServerComputerNameAdd.  This api
      causes 'ServerName' to respond to requests for 'EmulatedServerName'.

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    EmulatedServerName --A pointer to the ASCIIZ string containing the
        name which the server should stop supporting

    EmulatedDomainName --A pointer to the ASCIIZ string containing the
        domain name the server should use when announcing the presence of
        'EmulatedServerName'

Return Value:

    NERR_Success, or reason for failure

--*/
{
    DWORD resumehandle = 0;
    NET_API_STATUS retval;
    DWORD entriesread, totalentries;
    DWORD i, j;
    BOOLEAN AddedOne = FALSE;
    UCHAR NetBiosName[ MAX_PATH ];
    OEM_STRING NetBiosNameString;
    UNICODE_STRING UniName;

    //
    // Ensure a valid EmulatedServerName was passed in
    //
    if( EmulatedServerName == NULL ) {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Convert the EmulatedServerName to an OEM string
    //
    RtlInitUnicodeString( &UniName, EmulatedServerName );
    NetBiosNameString.Buffer = (PCHAR)NetBiosName;
    NetBiosNameString.MaximumLength = sizeof( NetBiosName );
    (VOID) RtlUpcaseUnicodeStringToOemString(
                                &NetBiosNameString,
                                &UniName,
                                FALSE
                                );

    if( ARGUMENT_PRESENT( EmulatedDomainName ) ) {

        //
        // The caller wants to set a new computer name and domain name to the
        //  server.  This requires level 1, which is new in 4.0
        //
        PSERVER_TRANSPORT_INFO_1 psti1;

        //
        // Enumerate all the transports so we can add the name and domain
        //  to each one.
        //
        retval = NetServerTransportEnum ( ServerName,
                                          1,
                                          (LPBYTE *)&psti1,
                                          (DWORD)-1,
                                          &entriesread,
                                          &totalentries,
                                          &resumehandle );
        if( retval == NERR_Success ) {
            //
            // Add the new name and domain to all of the transports
            //
            for( i=0; i < entriesread; i++ ) {

                //
                // Make sure we haven't already added to this transport
                //
                for( j = 0; j < i; j++ ) {
                    if( wcscmp( psti1[j].svti1_transportname, psti1[i].svti1_transportname ) == 0 )
                        break;
                }

                if( i != j )
                    continue;

                psti1[i].svti1_transportaddress = NetBiosName;
                psti1[i].svti1_transportaddresslength = strlen( NetBiosName );
                psti1[i].svti1_domain = EmulatedDomainName;

                retval = NetServerTransportAddEx( ServerName, 1, (LPBYTE)&psti1[ i ]  );

                if( retval == NERR_Success ) {
                    AddedOne = TRUE;
                }
            }

            MIDL_user_free( psti1 );
        }


    } else {

        //
        // The caller just wants to set an alternate server name.  Use level 0,
        //  since 3.51 servers support level 0
        //
        PSERVER_TRANSPORT_INFO_0 psti0;

        //
        // Enumerate all the transports so we can add the name and domain
        //  to each one.
        //
        retval = NetServerTransportEnum ( ServerName,
                                          0,
                                          (LPBYTE *)&psti0,
                                          (DWORD)-1,
                                          &entriesread,
                                          &totalentries,
                                          &resumehandle );
        if( retval == NERR_Success ) {
            //
            // Add the new name to all of the transports
            //
            for( i=0; i < entriesread; i++ ) {

                //
                // Make sure we haven't already added to this transport
                //
                for( j = 0; j < i; j++ ) {
                    if( wcscmp( psti0[j].svti0_transportname, psti0[i].svti0_transportname ) == 0 )
                        break;
                }

                if( i != j )
                    continue;

                psti0[i].svti0_transportaddress = NetBiosName;
                psti0[i].svti0_transportaddresslength = strlen( NetBiosName );

                retval = NetServerTransportAdd( ServerName, 0, (LPBYTE)&psti0[ i ]  );

                if( retval == NERR_Success ) {
                    AddedOne = TRUE;
                }
            }

            MIDL_user_free( psti0 );
        }
    }

    return AddedOne ? NERR_Success : retval;
}

NET_API_STATUS NET_API_FUNCTION
NetServerComputerNameDel (
    IN LPWSTR ServerName        OPTIONAL,
    IN LPWSTR EmulatedServerName
)
/*++

Routine Description:

    This is the DLL entrypoint for NetServerComputerNameDel.  This api
      causes 'ServerName' to cease responding to requests for 'EmulatedServerName'

Arguments:

    servername --A pointer to an ASCIIZ string containing the name of
        the remote server on which the function is to execute. A NULL
        pointer or string specifies the local machine.

    EmulatedServerName --A pointer to the ASCIIZ string containing the
        name which the server should stop supporting

Return Value:

    NERR_Success, or reason for failure

--*/
{
    DWORD resumehandle = 0;
    NET_API_STATUS retval, tmpretval;
    DWORD entriesread, totalentries;
    DWORD i;
    UCHAR NetBiosName[MAX_PATH];
    OEM_STRING NetBiosNameString;
    UNICODE_STRING UniName;
    PSERVER_TRANSPORT_INFO_0 psti0;
    BOOLEAN nameDeleted = FALSE;

    //
    // Ensure a valid EmulatedServerName was passed in
    //
    if( EmulatedServerName == NULL ) {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Convert the EmulatedServerName to an OEM string
    //
    RtlInitUnicodeString( &UniName, EmulatedServerName );
    NetBiosNameString.Buffer = (PCHAR)NetBiosName;
    NetBiosNameString.MaximumLength = sizeof( NetBiosName );
    (VOID) RtlUpcaseUnicodeStringToOemString(
                                &NetBiosNameString,
                                &UniName,
                                FALSE
                                );

    //
    // Enumerate all the transports so we can delete the name from each one
    //
    retval = NetServerTransportEnum ( ServerName,
                                      0,
                                      (LPBYTE *)&psti0,
                                      (DWORD)-1,
                                      &entriesread,
                                      &totalentries,
                                      &resumehandle );
    if( retval != NERR_Success ) {
        return retval;
    }

    retval = ERROR_BAD_NET_NAME;

    //
    // Delete the name from all of the transports.  If we were successful at least once,
    //  then we return success.
    //
    for( i=0; i < entriesread; i++ ) {

        if( psti0[i].svti0_transportaddresslength != NetBiosNameString.Length ) {
            continue;
        }

        if( RtlCompareMemory( psti0[i].svti0_transportaddress,
                              NetBiosName,
                              NetBiosNameString.Length) != NetBiosNameString.Length )  {
            continue;
        }

        tmpretval = NetServerTransportDel( ServerName, 0, (LPBYTE)&psti0[ i ]  );

        if( nameDeleted == FALSE ) {
            retval = tmpretval;
            if( retval == NERR_Success ) {
                nameDeleted = TRUE;
            }
        }
    }

    if( entriesread ) {
        MIDL_user_free( psti0 );
    }

    return retval;
}