/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    netinfo.cxx

Abstract:

    Contains entry points for Winnet API supported by the
    Multi-Provider Router.
    Contains:
        WNetGetNetworkInformationW
        WNetGetProviderNameW

Author:

    Anirudh Sahni  (anirudhs)  08-Jun-1995

Environment:

    User Mode -Win32

Notes:

Revision History:

    08-Jun-1995     anirudhs
        Created

    05-May-1999     jschwart
        Make provider addition/removal dynamic

--*/

//
// INCLUDES
//

#include "precomp.hxx"
#include <tstr.h>       // WCSSIZE

//
// EXTERNAL GLOBALS
//

//
// Defines
//


//
// Local Function Prototypes
//


DWORD
WNetGetNetworkInformationW(
    IN  LPCWSTR         lpProvider,
    OUT LPNETINFOSTRUCT lpNetInfoStruct
    )

/*++

Routine Description:

    This function returns extended information about a named network provider.

Arguments:

    lpProvider - Pointer to the name of the provider for which information is
        required.

    lpNetInfoStruct - Pointer to a structure that describes the behavior of
        the network.

Return Value:

    WN_SUCCESS - Call is successful.

    WN_BAD_PROVIDER - lpProvider does not match any active network provider.

    WN_BAD_VALUE - lpProvider->cbStructure does not contain a valid structure
        size.

Notes:

    Win 95's implementation of this API will accept a structure smaller than
    a NETINFOSTRUCT, and will fill in as many elements of the structure as
    will fit.  This strange feature is not documented and is not used in
    the shell, so we don't do it here.  It may be useful for future versions
    of this API that add fields to the NETINFOSTRUCT.

--*/

{
    DWORD       status = WN_SUCCESS;
    LPPROVIDER  Provider;

    if (!(ARGUMENT_PRESENT(lpProvider) &&
          ARGUMENT_PRESENT(lpNetInfoStruct)))
    {
        SetLastError(WN_BAD_POINTER);
        return WN_BAD_POINTER;
    }

    MprCheckProviders();

    CProviderSharedLock    PLock;

    INIT_IF_NECESSARY(NETWORK_LEVEL,status);

    __try
    {
        //
        // Validate the parameters that we can.
        //

        if (lpNetInfoStruct->cbStructure < sizeof(NETINFOSTRUCT))
        {
            status = WN_BAD_VALUE;
            __leave;
        }

        if (IsBadWritePtr(lpNetInfoStruct, lpNetInfoStruct->cbStructure))
        {
            status = WN_BAD_POINTER;
            __leave;
        }

        //
        // Look up the provider by name
        //
        DWORD i;
        if (!MprGetProviderIndex(lpProvider, &i))
        {
            status = WN_BAD_PROVIDER;
            __leave;
        }

        Provider = & GlobalProviderInfo[i];
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        status = GetExceptionCode();
        if (status != EXCEPTION_ACCESS_VIOLATION)
        {
            MPR_LOG(ERROR,
                    "WNetGetNetworkInformationW: Unexpected Exception %#lx\n",
                    status);
        }
        status = WN_BAD_POINTER;
    }

    if (status != WN_SUCCESS)
    {
        SetLastError(status);
        return status;
    }

    //
    // Fill in the fields of the structure
    //

    lpNetInfoStruct->cbStructure = sizeof(NETINFOSTRUCT);

    lpNetInfoStruct->dwProviderVersion = Provider->GetCaps(WNNC_DRIVER_VERSION);

    switch (Provider->GetCaps(WNNC_START))
    {
        case 0x0:
            lpNetInfoStruct->dwStatus = WN_NO_NETWORK;
            break;

        case 0x1:
            lpNetInfoStruct->dwStatus = WN_SUCCESS;
            break;

        default:
            lpNetInfoStruct->dwStatus = WN_FUNCTION_BUSY;
            break;
    }

    // We don't support this field.  The shell doesn't use it.
    // Win 95 gets it by looking at registry entries created by the
    // provider.  If the registry entries don't exist it leaves the
    // dwCharacteristics field as 0, which means that the provider
    // doesn't require redirection of a local drive to make a connection.
    lpNetInfoStruct->dwCharacteristics = 0;

    lpNetInfoStruct->dwHandle = (ULONG_PTR) Provider->Handle;

    // Note, this is a WORD field, not a DWORD.
    // Why does Win 95 omit the LOWORD anyway?
    lpNetInfoStruct->wNetType = HIWORD(Provider->Type);

    // We don't support these 2 fields.  The shell doesn't use them.
    // Win 95 gets them by calling NPValidLocalDevices.  If this entry
    // point doesn't exist it looks in the registry.  If the registry
    // value doesn't exist it uses NPP_ALLVALID which is defined as
    // 0xffffffff.
    lpNetInfoStruct->dwPrinters = 0xffffffff;

    lpNetInfoStruct->dwDrives = 0xffffffff;

    return WN_SUCCESS;
}


DWORD
WNetGetProviderNameW(
    IN      DWORD   dwNetType,
    OUT     LPWSTR  lpProviderName,
    IN OUT  LPDWORD lpBufferSize
    )

/*++

Routine Description:

    This function returns the provider name for a specified type of network.

Arguments:

    dwNetType - The network type unique to the network.  Only the high word
        of the network type is used; the subtype in the low word is ignored.
        If two networks claim the same type, the first one loaded is returned.

    lpProviderName - Pointer to a buffer in which to return the provider name.

    lpBufferSize - On entry, size of the lpProviderName buffer in characters.
        On exit, iff the return code is WN_MORE_DATA, set to the required size
        to hold the provider name.

Return Value:

    WN_SUCCESS - Call is successful.

    WN_MORE_DATA - The buffer is too small to hold the provider name.

    WN_NO_NETWORK - lpProvider does not match any active network provider.

--*/

{
    DWORD   status = WN_SUCCESS;

    __try
    {
        //
        // Validate the parameters that we can.
        //
        if (IS_BAD_WCHAR_BUFFER(lpProviderName, lpBufferSize))
        {
            status = WN_BAD_POINTER;
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        status = GetExceptionCode();
        if (status != EXCEPTION_ACCESS_VIOLATION)
        {
            MPR_LOG(ERROR,
                    "WNetGetProviderNameW: Unexpected Exception %#lx\n",
                    status);
        }
        status = WN_BAD_POINTER;
    }

    if (status != WN_SUCCESS)
    {
        SetLastError(status);
        return status;
    }

    MprCheckProviders();

    CProviderSharedLock    PLock;

    INIT_IF_NECESSARY(NETWORK_LEVEL,status);

    //
    // Loop through the list of providers to find one with the specified
    // net type.
    //
    LPPROVIDER provider = MprFindProviderByType(dwNetType);

    if (provider == NULL)
    {
        status = WN_NO_NETWORK;
    }
    else
    {
        //
        // Copy the provider name to the caller's buffer
        //
        DWORD dwReqSize = wcslen(provider->Resource.lpProvider) + 1;
        if (*lpBufferSize < dwReqSize)
        {
            status = WN_MORE_DATA;
            *lpBufferSize = dwReqSize;
        }
        else
        {
            status = WN_SUCCESS;
            wcscpy(lpProviderName, provider->Resource.lpProvider);
        }
    }

    if (status != WN_SUCCESS)
    {
        SetLastError(status);
    }

    return status;
}



DWORD
WNetGetProviderTypeW(
    IN  LPCWSTR         lpProvider,
    OUT LPDWORD         lpdwNetType
    )

/*++

Routine Description:

    This function returns the network type for a named network provider.

Arguments:

    lpProvider - Pointer to the name of the provider for which information is
        required.

    lpdwNetType - Pointer to a network type value that is filled in.

Return Value:

    WN_SUCCESS - Call is successful.

    WN_BAD_PROVIDER - lpProvider does not match any active network provider.

    WN_BAD_POINTER - an illegal argument was passed in.

Notes:

    Since this is an internal, private API used only by the shell, we do
    minimal parameter validation, to make it as fast as possible.

--*/

{
    DWORD       status = WN_SUCCESS;

    if (!(ARGUMENT_PRESENT(lpProvider) &&
          ARGUMENT_PRESENT(lpdwNetType)))
    {
        SetLastError(WN_BAD_POINTER);
        return WN_BAD_POINTER;
    }

    MprCheckProviders();

    CProviderSharedLock    PLock;

    INIT_IF_NECESSARY(NETWORK_LEVEL,status);

    //
    // Look up the provider by name
    //
    LPPROVIDER provider = MprFindProviderByName(lpProvider);
    if (NULL == provider)
    {
        SetLastError(WN_BAD_PROVIDER);
        return WN_BAD_PROVIDER;
    }

    *lpdwNetType = provider->Type;
    return WN_SUCCESS;
}