mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-05 18:01:34 +00:00
1e117a5682
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
3030 lines
99 KiB
C
3030 lines
99 KiB
C
/*
|
|
* MPR WNet functions
|
|
*
|
|
* Copyright 1999 Ulrich Weigand
|
|
* Copyright 2004 Juan Lang
|
|
* Copyright 2007 Maarten Lankhorst
|
|
* Copyright 2016-2018 Pierre Schweitzer
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winnls.h"
|
|
#include "winioctl.h"
|
|
#include "winnetwk.h"
|
|
#include "npapi.h"
|
|
#include "winreg.h"
|
|
#include "winuser.h"
|
|
#define WINE_MOUNTMGR_EXTENSIONS
|
|
#include "ddk/mountmgr.h"
|
|
#include "wine/debug.h"
|
|
#include "mprres.h"
|
|
#include "wnetpriv.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(mpr);
|
|
|
|
/* Data structures representing network service providers. Assumes only one
|
|
* thread creates them, and that they are constant for the life of the process
|
|
* (and therefore doesn't synchronize access).
|
|
* FIXME: only basic provider data and enumeration-related data are implemented
|
|
* so far, need to implement the rest too.
|
|
*/
|
|
typedef struct _WNetProvider
|
|
{
|
|
HMODULE hLib;
|
|
PWSTR name;
|
|
PF_NPGetCaps getCaps;
|
|
DWORD dwSpecVersion;
|
|
DWORD dwNetType;
|
|
DWORD dwEnumScopes;
|
|
PF_NPOpenEnum openEnum;
|
|
PF_NPEnumResource enumResource;
|
|
PF_NPCloseEnum closeEnum;
|
|
PF_NPGetResourceInformation getResourceInformation;
|
|
PF_NPAddConnection addConnection;
|
|
PF_NPAddConnection3 addConnection3;
|
|
PF_NPCancelConnection cancelConnection;
|
|
} WNetProvider, *PWNetProvider;
|
|
|
|
typedef struct _WNetProviderTable
|
|
{
|
|
LPWSTR entireNetwork;
|
|
DWORD numAllocated;
|
|
DWORD numProviders;
|
|
WNetProvider table[1];
|
|
} WNetProviderTable, *PWNetProviderTable;
|
|
|
|
#define WNET_ENUMERATOR_TYPE_GLOBAL 0
|
|
#define WNET_ENUMERATOR_TYPE_PROVIDER 1
|
|
#define WNET_ENUMERATOR_TYPE_CONTEXT 2
|
|
#define WNET_ENUMERATOR_TYPE_CONNECTED 3
|
|
#define WNET_ENUMERATOR_TYPE_REMEMBERED 4
|
|
|
|
/* An WNet enumerator. Note that the type doesn't correspond to the scope of
|
|
* the enumeration; it represents one of the following types:
|
|
* - a global enumeration, one that's executed across all providers
|
|
* - a provider-specific enumeration, one that's only executed by a single
|
|
* provider
|
|
* - a context enumeration. I know this contradicts what I just said about
|
|
* there being no correspondence between the scope and the type, but it's
|
|
* necessary for the special case that a "Entire Network" entry needs to
|
|
* be enumerated in an enumeration of the context scope. Thus an enumeration
|
|
* of the context scope results in a context type enumerator, which morphs
|
|
* into a global enumeration (so the enumeration continues across all
|
|
* providers).
|
|
* - a remembered enumeration, not related to providers themselves, but it
|
|
* is a registry enumeration for saved connections
|
|
*/
|
|
typedef struct _WNetEnumerator
|
|
{
|
|
DWORD enumType;
|
|
DWORD providerIndex;
|
|
HANDLE handle;
|
|
BOOL providerDone;
|
|
DWORD dwScope;
|
|
DWORD dwType;
|
|
DWORD dwUsage;
|
|
union
|
|
{
|
|
NETRESOURCEW* net;
|
|
HANDLE* handles;
|
|
struct
|
|
{
|
|
HKEY registry;
|
|
DWORD index;
|
|
} remembered;
|
|
} specific;
|
|
} WNetEnumerator, *PWNetEnumerator;
|
|
|
|
#define BAD_PROVIDER_INDEX (DWORD)0xffffffff
|
|
|
|
/* Returns an index (into the global WNetProviderTable) of the provider with
|
|
* the given name, or BAD_PROVIDER_INDEX if not found.
|
|
*/
|
|
static DWORD _findProviderIndexW(LPCWSTR lpProvider);
|
|
|
|
static PWNetProviderTable providerTable;
|
|
|
|
/*
|
|
* Global provider table functions
|
|
*/
|
|
|
|
static void _tryLoadProvider(PCWSTR provider)
|
|
{
|
|
static const WCHAR servicePrefix[] = { 'S','y','s','t','e','m','\\',
|
|
'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
|
|
'S','e','r','v','i','c','e','s','\\',0 };
|
|
static const WCHAR serviceFmt[] = { '%','s','%','s','\\',
|
|
'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r',0 };
|
|
WCHAR serviceName[MAX_PATH];
|
|
HKEY hKey;
|
|
|
|
TRACE("%s\n", debugstr_w(provider));
|
|
swprintf(serviceName, ARRAY_SIZE(serviceName), serviceFmt, servicePrefix, provider);
|
|
serviceName[ARRAY_SIZE(serviceName) - 1] = '\0';
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, serviceName, 0, KEY_READ, &hKey) ==
|
|
ERROR_SUCCESS)
|
|
{
|
|
static const WCHAR szProviderPath[] = { 'P','r','o','v','i','d','e','r',
|
|
'P','a','t','h',0 };
|
|
WCHAR providerPath[MAX_PATH];
|
|
DWORD type, size = sizeof(providerPath);
|
|
|
|
if (RegQueryValueExW(hKey, szProviderPath, NULL, &type,
|
|
(LPBYTE)providerPath, &size) == ERROR_SUCCESS && (type == REG_SZ || type == REG_EXPAND_SZ))
|
|
{
|
|
static const WCHAR szProviderName[] = { 'N','a','m','e',0 };
|
|
PWSTR name = NULL;
|
|
|
|
if (type == REG_EXPAND_SZ)
|
|
{
|
|
WCHAR path[MAX_PATH];
|
|
if (ExpandEnvironmentStringsW(providerPath, path, MAX_PATH)) lstrcpyW( providerPath, path );
|
|
}
|
|
|
|
size = 0;
|
|
RegQueryValueExW(hKey, szProviderName, NULL, NULL, NULL, &size);
|
|
if (size)
|
|
{
|
|
name = HeapAlloc(GetProcessHeap(), 0, size);
|
|
if (RegQueryValueExW(hKey, szProviderName, NULL, &type,
|
|
(LPBYTE)name, &size) != ERROR_SUCCESS || type != REG_SZ)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, name);
|
|
name = NULL;
|
|
}
|
|
}
|
|
if (name)
|
|
{
|
|
HMODULE hLib = LoadLibraryW(providerPath);
|
|
|
|
if (hLib)
|
|
{
|
|
#define MPR_GETPROC(proc) ((PF_##proc)GetProcAddress(hLib, #proc))
|
|
|
|
PF_NPGetCaps getCaps = MPR_GETPROC(NPGetCaps);
|
|
|
|
TRACE("loaded lib %p\n", hLib);
|
|
if (getCaps)
|
|
{
|
|
DWORD connectCap;
|
|
PWNetProvider provider =
|
|
&providerTable->table[providerTable->numProviders];
|
|
|
|
provider->hLib = hLib;
|
|
provider->name = name;
|
|
TRACE("name is %s\n", debugstr_w(name));
|
|
provider->getCaps = getCaps;
|
|
provider->dwSpecVersion = getCaps(WNNC_SPEC_VERSION);
|
|
provider->dwNetType = getCaps(WNNC_NET_TYPE);
|
|
TRACE("net type is 0x%08x\n", provider->dwNetType);
|
|
provider->dwEnumScopes = getCaps(WNNC_ENUMERATION);
|
|
if (provider->dwEnumScopes)
|
|
{
|
|
TRACE("supports enumeration\n");
|
|
provider->openEnum = MPR_GETPROC(NPOpenEnum);
|
|
TRACE("NPOpenEnum %p\n", provider->openEnum);
|
|
provider->enumResource = MPR_GETPROC(NPEnumResource);
|
|
TRACE("NPEnumResource %p\n", provider->enumResource);
|
|
provider->closeEnum = MPR_GETPROC(NPCloseEnum);
|
|
TRACE("NPCloseEnum %p\n", provider->closeEnum);
|
|
provider->getResourceInformation = MPR_GETPROC(NPGetResourceInformation);
|
|
TRACE("NPGetResourceInformation %p\n", provider->getResourceInformation);
|
|
if (!provider->openEnum ||
|
|
!provider->enumResource ||
|
|
!provider->closeEnum)
|
|
{
|
|
provider->openEnum = NULL;
|
|
provider->enumResource = NULL;
|
|
provider->closeEnum = NULL;
|
|
provider->dwEnumScopes = 0;
|
|
WARN("Couldn't load enumeration functions\n");
|
|
}
|
|
}
|
|
connectCap = getCaps(WNNC_CONNECTION);
|
|
if (connectCap & WNNC_CON_ADDCONNECTION)
|
|
provider->addConnection = MPR_GETPROC(NPAddConnection);
|
|
if (connectCap & WNNC_CON_ADDCONNECTION3)
|
|
provider->addConnection3 = MPR_GETPROC(NPAddConnection3);
|
|
if (connectCap & WNNC_CON_CANCELCONNECTION)
|
|
provider->cancelConnection = MPR_GETPROC(NPCancelConnection);
|
|
TRACE("NPAddConnection %p\n", provider->addConnection);
|
|
TRACE("NPAddConnection3 %p\n", provider->addConnection3);
|
|
TRACE("NPCancelConnection %p\n", provider->cancelConnection);
|
|
providerTable->numProviders++;
|
|
}
|
|
else
|
|
{
|
|
WARN("Provider %s didn't export NPGetCaps\n",
|
|
debugstr_w(provider));
|
|
HeapFree(GetProcessHeap(), 0, name);
|
|
FreeLibrary(hLib);
|
|
}
|
|
|
|
#undef MPR_GETPROC
|
|
}
|
|
else
|
|
{
|
|
WARN("Couldn't load library %s for provider %s\n",
|
|
debugstr_w(providerPath), debugstr_w(provider));
|
|
HeapFree(GetProcessHeap(), 0, name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WARN("Couldn't get provider name for provider %s\n",
|
|
debugstr_w(provider));
|
|
}
|
|
}
|
|
else
|
|
WARN("Couldn't open value %s\n", debugstr_w(szProviderPath));
|
|
RegCloseKey(hKey);
|
|
}
|
|
else
|
|
WARN("Couldn't open service key for provider %s\n",
|
|
debugstr_w(provider));
|
|
}
|
|
|
|
void wnetInit(HINSTANCE hInstDll)
|
|
{
|
|
static const WCHAR providerOrderKey[] = { 'S','y','s','t','e','m','\\',
|
|
'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
|
|
'C','o','n','t','r','o','l','\\',
|
|
'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r','\\',
|
|
'O','r','d','e','r',0 };
|
|
static const WCHAR providerOrder[] = { 'P','r','o','v','i','d','e','r',
|
|
'O','r','d','e','r',0 };
|
|
HKEY hKey;
|
|
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, providerOrderKey, 0, KEY_READ, &hKey)
|
|
== ERROR_SUCCESS)
|
|
{
|
|
DWORD size = 0;
|
|
|
|
RegQueryValueExW(hKey, providerOrder, NULL, NULL, NULL, &size);
|
|
if (size)
|
|
{
|
|
PWSTR providers = HeapAlloc(GetProcessHeap(), 0, size);
|
|
|
|
if (providers)
|
|
{
|
|
DWORD type;
|
|
|
|
if (RegQueryValueExW(hKey, providerOrder, NULL, &type,
|
|
(LPBYTE)providers, &size) == ERROR_SUCCESS && type == REG_SZ)
|
|
{
|
|
PWSTR ptr;
|
|
DWORD numToAllocate;
|
|
|
|
TRACE("provider order is %s\n", debugstr_w(providers));
|
|
/* first count commas as a heuristic for how many to
|
|
* allocate space for */
|
|
for (ptr = providers, numToAllocate = 1; ptr; )
|
|
{
|
|
ptr = wcschr(ptr, ',');
|
|
if (ptr) {
|
|
numToAllocate++;
|
|
ptr++;
|
|
}
|
|
}
|
|
providerTable =
|
|
HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
|
|
sizeof(WNetProviderTable)
|
|
+ (numToAllocate - 1) * sizeof(WNetProvider));
|
|
if (providerTable)
|
|
{
|
|
PWSTR ptrPrev;
|
|
int entireNetworkLen;
|
|
LPCWSTR stringresource;
|
|
|
|
entireNetworkLen = LoadStringW(hInstDll,
|
|
IDS_ENTIRENETWORK, (LPWSTR)&stringresource, 0);
|
|
providerTable->entireNetwork = HeapAlloc(
|
|
GetProcessHeap(), 0, (entireNetworkLen + 1) *
|
|
sizeof(WCHAR));
|
|
if (providerTable->entireNetwork)
|
|
{
|
|
memcpy(providerTable->entireNetwork, stringresource, entireNetworkLen*sizeof(WCHAR));
|
|
providerTable->entireNetwork[entireNetworkLen] = 0;
|
|
}
|
|
providerTable->numAllocated = numToAllocate;
|
|
for (ptr = providers; ptr; )
|
|
{
|
|
ptrPrev = ptr;
|
|
ptr = wcschr(ptr, ',');
|
|
if (ptr)
|
|
*ptr++ = '\0';
|
|
_tryLoadProvider(ptrPrev);
|
|
}
|
|
}
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, providers);
|
|
}
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
void wnetFree(void)
|
|
{
|
|
if (providerTable)
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0; i < providerTable->numProviders; i++)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, providerTable->table[i].name);
|
|
FreeModule(providerTable->table[i].hLib);
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, providerTable->entireNetwork);
|
|
HeapFree(GetProcessHeap(), 0, providerTable);
|
|
providerTable = NULL;
|
|
}
|
|
}
|
|
|
|
static DWORD _findProviderIndexW(LPCWSTR lpProvider)
|
|
{
|
|
DWORD ret = BAD_PROVIDER_INDEX;
|
|
|
|
if (providerTable && providerTable->numProviders)
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0; i < providerTable->numProviders &&
|
|
ret == BAD_PROVIDER_INDEX; i++)
|
|
if (!lstrcmpW(lpProvider, providerTable->table[i].name))
|
|
ret = i;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Browsing Functions
|
|
*/
|
|
|
|
static LPNETRESOURCEW _copyNetResourceForEnumW(LPNETRESOURCEW lpNet)
|
|
{
|
|
LPNETRESOURCEW ret;
|
|
|
|
if (lpNet)
|
|
{
|
|
ret = HeapAlloc(GetProcessHeap(), 0, sizeof(NETRESOURCEW));
|
|
if (ret)
|
|
{
|
|
size_t len;
|
|
|
|
*ret = *lpNet;
|
|
ret->lpLocalName = ret->lpComment = ret->lpProvider = NULL;
|
|
if (lpNet->lpRemoteName)
|
|
{
|
|
len = lstrlenW(lpNet->lpRemoteName) + 1;
|
|
ret->lpRemoteName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
if (ret->lpRemoteName)
|
|
lstrcpyW(ret->lpRemoteName, lpNet->lpRemoteName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ret = NULL;
|
|
return ret;
|
|
}
|
|
|
|
static void _freeEnumNetResource(LPNETRESOURCEW lpNet)
|
|
{
|
|
if (lpNet)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, lpNet->lpRemoteName);
|
|
HeapFree(GetProcessHeap(), 0, lpNet);
|
|
}
|
|
}
|
|
|
|
static PWNetEnumerator _createGlobalEnumeratorW(DWORD dwScope, DWORD dwType,
|
|
DWORD dwUsage, LPNETRESOURCEW lpNet)
|
|
{
|
|
PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
|
|
|
|
if (ret)
|
|
{
|
|
ret->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
|
|
ret->dwScope = dwScope;
|
|
ret->dwType = dwType;
|
|
ret->dwUsage = dwUsage;
|
|
ret->specific.net = _copyNetResourceForEnumW(lpNet);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static PWNetEnumerator _createProviderEnumerator(DWORD dwScope, DWORD dwType,
|
|
DWORD dwUsage, DWORD index, HANDLE handle)
|
|
{
|
|
PWNetEnumerator ret;
|
|
|
|
if (!providerTable || index >= providerTable->numProviders)
|
|
ret = NULL;
|
|
else
|
|
{
|
|
ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
|
|
if (ret)
|
|
{
|
|
ret->enumType = WNET_ENUMERATOR_TYPE_PROVIDER;
|
|
ret->providerIndex = index;
|
|
ret->dwScope = dwScope;
|
|
ret->dwType = dwType;
|
|
ret->dwUsage = dwUsage;
|
|
ret->handle = handle;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static PWNetEnumerator _createContextEnumerator(DWORD dwScope, DWORD dwType,
|
|
DWORD dwUsage)
|
|
{
|
|
PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
|
|
|
|
if (ret)
|
|
{
|
|
ret->enumType = WNET_ENUMERATOR_TYPE_CONTEXT;
|
|
ret->dwScope = dwScope;
|
|
ret->dwType = dwType;
|
|
ret->dwUsage = dwUsage;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static PWNetEnumerator _createConnectedEnumerator(DWORD dwScope, DWORD dwType,
|
|
DWORD dwUsage)
|
|
{
|
|
PWNetEnumerator ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
|
|
if (ret)
|
|
{
|
|
ret->enumType = WNET_ENUMERATOR_TYPE_CONNECTED;
|
|
ret->dwScope = dwScope;
|
|
ret->dwType = dwType;
|
|
ret->dwUsage = dwUsage;
|
|
ret->specific.handles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HANDLE) * providerTable->numProviders);
|
|
if (!ret->specific.handles)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, ret);
|
|
ret = NULL;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static PWNetEnumerator _createRememberedEnumerator(DWORD dwScope, DWORD dwType,
|
|
HKEY remembered)
|
|
{
|
|
PWNetEnumerator ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
|
|
if (ret)
|
|
{
|
|
ret->enumType = WNET_ENUMERATOR_TYPE_REMEMBERED;
|
|
ret->dwScope = dwScope;
|
|
ret->dwType = dwType;
|
|
ret->specific.remembered.registry = remembered;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* Thunks the array of wide-string LPNETRESOURCEs lpNetArrayIn into buffer
|
|
* lpBuffer, with size *lpBufferSize. lpNetArrayIn contains *lpcCount entries
|
|
* to start. On return, *lpcCount reflects the number thunked into lpBuffer.
|
|
* Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA
|
|
* if not all members of the array could be thunked, and something else on
|
|
* failure.
|
|
*/
|
|
static DWORD _thunkNetResourceArrayWToA(const NETRESOURCEW *lpNetArrayIn,
|
|
const DWORD *lpcCount, LPVOID lpBuffer, const DWORD *lpBufferSize)
|
|
{
|
|
DWORD i, numToThunk, totalBytes, ret;
|
|
LPSTR strNext;
|
|
|
|
if (!lpNetArrayIn)
|
|
return WN_BAD_POINTER;
|
|
if (!lpcCount)
|
|
return WN_BAD_POINTER;
|
|
if (*lpcCount == -1)
|
|
return WN_BAD_VALUE;
|
|
if (!lpBuffer)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBufferSize)
|
|
return WN_BAD_POINTER;
|
|
|
|
for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++)
|
|
{
|
|
const NETRESOURCEW *lpNet = lpNetArrayIn + i;
|
|
|
|
totalBytes += sizeof(NETRESOURCEA);
|
|
if (lpNet->lpLocalName)
|
|
totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpLocalName,
|
|
-1, NULL, 0, NULL, NULL);
|
|
if (lpNet->lpRemoteName)
|
|
totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpRemoteName,
|
|
-1, NULL, 0, NULL, NULL);
|
|
if (lpNet->lpComment)
|
|
totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpComment,
|
|
-1, NULL, 0, NULL, NULL);
|
|
if (lpNet->lpProvider)
|
|
totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpProvider,
|
|
-1, NULL, 0, NULL, NULL);
|
|
if (totalBytes < *lpBufferSize)
|
|
numToThunk = i + 1;
|
|
}
|
|
strNext = (LPSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEA));
|
|
for (i = 0; i < numToThunk; i++)
|
|
{
|
|
LPNETRESOURCEA lpNetOut = (LPNETRESOURCEA)lpBuffer + i;
|
|
const NETRESOURCEW *lpNetIn = lpNetArrayIn + i;
|
|
|
|
memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEA));
|
|
/* lie about string lengths, we already verified how many
|
|
* we have space for above
|
|
*/
|
|
if (lpNetIn->lpLocalName)
|
|
{
|
|
lpNetOut->lpLocalName = strNext;
|
|
strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpLocalName, -1,
|
|
lpNetOut->lpLocalName, *lpBufferSize, NULL, NULL);
|
|
}
|
|
if (lpNetIn->lpRemoteName)
|
|
{
|
|
lpNetOut->lpRemoteName = strNext;
|
|
strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpRemoteName, -1,
|
|
lpNetOut->lpRemoteName, *lpBufferSize, NULL, NULL);
|
|
}
|
|
if (lpNetIn->lpComment)
|
|
{
|
|
lpNetOut->lpComment = strNext;
|
|
strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpComment, -1,
|
|
lpNetOut->lpComment, *lpBufferSize, NULL, NULL);
|
|
}
|
|
if (lpNetIn->lpProvider)
|
|
{
|
|
lpNetOut->lpProvider = strNext;
|
|
strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpProvider, -1,
|
|
lpNetOut->lpProvider, *lpBufferSize, NULL, NULL);
|
|
}
|
|
}
|
|
ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS;
|
|
TRACE("numToThunk is %d, *lpcCount is %d, returning %d\n", numToThunk,
|
|
*lpcCount, ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Thunks the array of multibyte-string LPNETRESOURCEs lpNetArrayIn into buffer
|
|
* lpBuffer, with size *lpBufferSize. lpNetArrayIn contains *lpcCount entries
|
|
* to start. On return, *lpcCount reflects the number thunked into lpBuffer.
|
|
* Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA
|
|
* if not all members of the array could be thunked, and something else on
|
|
* failure.
|
|
*/
|
|
static DWORD _thunkNetResourceArrayAToW(const NETRESOURCEA *lpNetArrayIn,
|
|
const DWORD *lpcCount, LPVOID lpBuffer, const DWORD *lpBufferSize)
|
|
{
|
|
DWORD i, numToThunk, totalBytes, ret;
|
|
LPWSTR strNext;
|
|
|
|
if (!lpNetArrayIn)
|
|
return WN_BAD_POINTER;
|
|
if (!lpcCount)
|
|
return WN_BAD_POINTER;
|
|
if (*lpcCount == -1)
|
|
return WN_BAD_VALUE;
|
|
if (!lpBuffer)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBufferSize)
|
|
return WN_BAD_POINTER;
|
|
|
|
for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++)
|
|
{
|
|
const NETRESOURCEA *lpNet = lpNetArrayIn + i;
|
|
|
|
totalBytes += sizeof(NETRESOURCEW);
|
|
if (lpNet->lpLocalName)
|
|
totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpLocalName,
|
|
-1, NULL, 0) * sizeof(WCHAR);
|
|
if (lpNet->lpRemoteName)
|
|
totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpRemoteName,
|
|
-1, NULL, 0) * sizeof(WCHAR);
|
|
if (lpNet->lpComment)
|
|
totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpComment,
|
|
-1, NULL, 0) * sizeof(WCHAR);
|
|
if (lpNet->lpProvider)
|
|
totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpProvider,
|
|
-1, NULL, 0) * sizeof(WCHAR);
|
|
if (totalBytes < *lpBufferSize)
|
|
numToThunk = i + 1;
|
|
}
|
|
strNext = (LPWSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEW));
|
|
for (i = 0; i < numToThunk; i++)
|
|
{
|
|
LPNETRESOURCEW lpNetOut = (LPNETRESOURCEW)lpBuffer + i;
|
|
const NETRESOURCEA *lpNetIn = lpNetArrayIn + i;
|
|
|
|
memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEW));
|
|
/* lie about string lengths, we already verified how many
|
|
* we have space for above
|
|
*/
|
|
if (lpNetIn->lpLocalName)
|
|
{
|
|
lpNetOut->lpLocalName = strNext;
|
|
strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpLocalName,
|
|
-1, lpNetOut->lpLocalName, *lpBufferSize);
|
|
}
|
|
if (lpNetIn->lpRemoteName)
|
|
{
|
|
lpNetOut->lpRemoteName = strNext;
|
|
strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpRemoteName,
|
|
-1, lpNetOut->lpRemoteName, *lpBufferSize);
|
|
}
|
|
if (lpNetIn->lpComment)
|
|
{
|
|
lpNetOut->lpComment = strNext;
|
|
strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpComment,
|
|
-1, lpNetOut->lpComment, *lpBufferSize);
|
|
}
|
|
if (lpNetIn->lpProvider)
|
|
{
|
|
lpNetOut->lpProvider = strNext;
|
|
strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpProvider,
|
|
-1, lpNetOut->lpProvider, *lpBufferSize);
|
|
}
|
|
}
|
|
ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS;
|
|
TRACE("numToThunk is %d, *lpcCount is %d, returning %d\n", numToThunk,
|
|
*lpcCount, ret);
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetOpenEnumA [MPR.@]
|
|
*
|
|
* See comments for WNetOpenEnumW.
|
|
*/
|
|
DWORD WINAPI WNetOpenEnumA( DWORD dwScope, DWORD dwType, DWORD dwUsage,
|
|
LPNETRESOURCEA lpNet, LPHANDLE lphEnum )
|
|
{
|
|
DWORD ret;
|
|
|
|
TRACE( "(%08X, %08X, %08X, %p, %p)\n",
|
|
dwScope, dwType, dwUsage, lpNet, lphEnum );
|
|
|
|
if (!lphEnum)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!providerTable || providerTable->numProviders == 0)
|
|
{
|
|
*lphEnum = NULL;
|
|
ret = WN_NO_NETWORK;
|
|
}
|
|
else
|
|
{
|
|
if (lpNet)
|
|
{
|
|
LPNETRESOURCEW lpNetWide = NULL;
|
|
BYTE buf[1024];
|
|
DWORD size = sizeof(buf), count = 1;
|
|
BOOL allocated = FALSE;
|
|
|
|
ret = _thunkNetResourceArrayAToW(lpNet, &count, buf, &size);
|
|
if (ret == WN_MORE_DATA)
|
|
{
|
|
lpNetWide = HeapAlloc(GetProcessHeap(), 0,
|
|
size);
|
|
if (lpNetWide)
|
|
{
|
|
ret = _thunkNetResourceArrayAToW(lpNet, &count, lpNetWide,
|
|
&size);
|
|
allocated = TRUE;
|
|
}
|
|
else
|
|
ret = WN_OUT_OF_MEMORY;
|
|
}
|
|
else if (ret == WN_SUCCESS)
|
|
lpNetWide = (LPNETRESOURCEW)buf;
|
|
if (ret == WN_SUCCESS)
|
|
ret = WNetOpenEnumW(dwScope, dwType, dwUsage, lpNetWide,
|
|
lphEnum);
|
|
if (allocated)
|
|
HeapFree(GetProcessHeap(), 0, lpNetWide);
|
|
}
|
|
else
|
|
ret = WNetOpenEnumW(dwScope, dwType, dwUsage, NULL, lphEnum);
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetOpenEnumW [MPR.@]
|
|
*
|
|
* Network enumeration has way too many parameters, so I'm not positive I got
|
|
* them right. What I've got so far:
|
|
*
|
|
* - If the scope is RESOURCE_GLOBALNET, and no LPNETRESOURCE is passed,
|
|
* all the network providers should be enumerated.
|
|
*
|
|
* - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
|
|
* and neither the LPNETRESOURCE's lpRemoteName nor the LPNETRESOURCE's
|
|
* lpProvider is set, all the network providers should be enumerated.
|
|
* (This means the enumeration is a list of network providers, not that the
|
|
* enumeration is passed on to the providers.)
|
|
*
|
|
* - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and the
|
|
* resource matches the "Entire Network" resource (no remote name, no
|
|
* provider, comment is the "Entire Network" string), a RESOURCE_GLOBALNET
|
|
* enumeration is done on every network provider.
|
|
*
|
|
* - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
|
|
* the LPNETRESOURCE's lpProvider is set, enumeration will be passed through
|
|
* only to the given network provider.
|
|
*
|
|
* - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
|
|
* no lpProvider is set, enumeration will be tried on every network provider,
|
|
* in the order in which they're loaded.
|
|
*
|
|
* - The LPNETRESOURCE should be disregarded for scopes besides
|
|
* RESOURCE_GLOBALNET. MSDN states that lpNet must be NULL if dwScope is not
|
|
* RESOURCE_GLOBALNET, but Windows doesn't return an error if it isn't NULL.
|
|
*
|
|
* - If the scope is RESOURCE_CONTEXT, MS includes an "Entire Network" net
|
|
* resource in the enumerated list, as well as any machines in your
|
|
* workgroup. The machines in your workgroup come from doing a
|
|
* RESOURCE_CONTEXT enumeration of every Network Provider.
|
|
*/
|
|
DWORD WINAPI WNetOpenEnumW( DWORD dwScope, DWORD dwType, DWORD dwUsage,
|
|
LPNETRESOURCEW lpNet, LPHANDLE lphEnum )
|
|
{
|
|
DWORD ret;
|
|
|
|
TRACE( "(%08X, %08X, %08X, %p, %p)\n",
|
|
dwScope, dwType, dwUsage, lpNet, lphEnum );
|
|
|
|
if (!lphEnum)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!providerTable || providerTable->numProviders == 0)
|
|
{
|
|
*lphEnum = NULL;
|
|
ret = WN_NO_NETWORK;
|
|
}
|
|
else
|
|
{
|
|
switch (dwScope)
|
|
{
|
|
case RESOURCE_GLOBALNET:
|
|
if (lpNet)
|
|
{
|
|
if (lpNet->lpProvider)
|
|
{
|
|
DWORD index = _findProviderIndexW(lpNet->lpProvider);
|
|
|
|
if (index != BAD_PROVIDER_INDEX)
|
|
{
|
|
if (providerTable->table[index].openEnum &&
|
|
providerTable->table[index].dwEnumScopes & WNNC_ENUM_GLOBAL)
|
|
{
|
|
HANDLE handle;
|
|
PWSTR RemoteName = lpNet->lpRemoteName;
|
|
|
|
if ((lpNet->dwUsage & RESOURCEUSAGE_CONTAINER) &&
|
|
RemoteName && !lstrcmpW(RemoteName, lpNet->lpProvider))
|
|
lpNet->lpRemoteName = NULL;
|
|
|
|
ret = providerTable->table[index].openEnum(
|
|
dwScope, dwType, dwUsage, lpNet, &handle);
|
|
if (ret == WN_SUCCESS)
|
|
{
|
|
*lphEnum = _createProviderEnumerator(
|
|
dwScope, dwType, dwUsage, index, handle);
|
|
ret = *lphEnum ? WN_SUCCESS :
|
|
WN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
lpNet->lpRemoteName = RemoteName;
|
|
}
|
|
else
|
|
ret = WN_NOT_SUPPORTED;
|
|
}
|
|
else
|
|
ret = WN_BAD_PROVIDER;
|
|
}
|
|
else if (lpNet->lpRemoteName)
|
|
{
|
|
*lphEnum = _createGlobalEnumeratorW(dwScope,
|
|
dwType, dwUsage, lpNet);
|
|
ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
if (lpNet->lpComment && !lstrcmpW(lpNet->lpComment,
|
|
providerTable->entireNetwork))
|
|
{
|
|
/* comment matches the "Entire Network", enumerate
|
|
* global scope of every provider
|
|
*/
|
|
*lphEnum = _createGlobalEnumeratorW(dwScope,
|
|
dwType, dwUsage, lpNet);
|
|
}
|
|
else
|
|
{
|
|
/* this is the same as not having passed lpNet */
|
|
*lphEnum = _createGlobalEnumeratorW(dwScope,
|
|
dwType, dwUsage, NULL);
|
|
}
|
|
ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*lphEnum = _createGlobalEnumeratorW(dwScope, dwType,
|
|
dwUsage, lpNet);
|
|
ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
|
|
}
|
|
break;
|
|
case RESOURCE_CONTEXT:
|
|
*lphEnum = _createContextEnumerator(dwScope, dwType, dwUsage);
|
|
ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
|
|
break;
|
|
case RESOURCE_CONNECTED:
|
|
*lphEnum = _createConnectedEnumerator(dwScope, dwType, dwUsage);
|
|
ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
|
|
break;
|
|
case RESOURCE_REMEMBERED:
|
|
{
|
|
HKEY remembered, user_profile;
|
|
|
|
ret = WN_OUT_OF_MEMORY;
|
|
if (RegOpenCurrentUser(KEY_READ, &user_profile) == ERROR_SUCCESS)
|
|
{
|
|
WCHAR subkey[8] = {'N', 'e', 't', 'w', 'o', 'r', 'k', 0};
|
|
|
|
if (RegOpenKeyExW(user_profile, subkey, 0, KEY_READ, &remembered) == ERROR_SUCCESS)
|
|
{
|
|
*lphEnum = _createRememberedEnumerator(dwScope, dwType, remembered);
|
|
ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
RegCloseKey(user_profile);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
WARN("unknown scope 0x%08x\n", dwScope);
|
|
ret = WN_BAD_VALUE;
|
|
}
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetEnumResourceA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetEnumResourceA( HANDLE hEnum, LPDWORD lpcCount,
|
|
LPVOID lpBuffer, LPDWORD lpBufferSize )
|
|
{
|
|
DWORD ret;
|
|
|
|
TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
|
|
|
|
if (!hEnum)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpcCount)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpBuffer)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpBufferSize)
|
|
ret = WN_BAD_POINTER;
|
|
else if (*lpBufferSize < sizeof(NETRESOURCEA))
|
|
{
|
|
*lpBufferSize = sizeof(NETRESOURCEA);
|
|
ret = WN_MORE_DATA;
|
|
}
|
|
else
|
|
{
|
|
DWORD localCount = *lpcCount, localSize = *lpBufferSize;
|
|
LPVOID localBuffer = HeapAlloc(GetProcessHeap(), 0, localSize);
|
|
|
|
if (localBuffer)
|
|
{
|
|
ret = WNetEnumResourceW(hEnum, &localCount, localBuffer,
|
|
&localSize);
|
|
if (ret == WN_SUCCESS || (ret == WN_MORE_DATA && localCount != -1))
|
|
{
|
|
/* FIXME: this isn't necessarily going to work in the case of
|
|
* WN_MORE_DATA, because our enumerator may have moved on to
|
|
* the next provider. MSDN states that a large (16KB) buffer
|
|
* size is the appropriate usage of this function, so
|
|
* hopefully it won't be an issue.
|
|
*/
|
|
ret = _thunkNetResourceArrayWToA(localBuffer, &localCount,
|
|
lpBuffer, lpBufferSize);
|
|
*lpcCount = localCount;
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, localBuffer);
|
|
}
|
|
else
|
|
ret = WN_OUT_OF_MEMORY;
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static DWORD _countProviderBytesW(PWNetProvider provider)
|
|
{
|
|
DWORD ret;
|
|
|
|
if (provider)
|
|
{
|
|
ret = sizeof(NETRESOURCEW);
|
|
ret += 2 * (lstrlenW(provider->name) + 1) * sizeof(WCHAR);
|
|
}
|
|
else
|
|
ret = 0;
|
|
return ret;
|
|
}
|
|
|
|
static DWORD _enumerateProvidersW(PWNetEnumerator enumerator, LPDWORD lpcCount,
|
|
LPVOID lpBuffer, const DWORD *lpBufferSize)
|
|
{
|
|
DWORD ret;
|
|
|
|
if (!enumerator)
|
|
return WN_BAD_POINTER;
|
|
if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
|
|
return WN_BAD_VALUE;
|
|
if (!lpcCount)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBuffer)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBufferSize)
|
|
return WN_BAD_POINTER;
|
|
if (*lpBufferSize < sizeof(NETRESOURCEA))
|
|
return WN_MORE_DATA;
|
|
|
|
if (!providerTable || enumerator->providerIndex >=
|
|
providerTable->numProviders)
|
|
ret = WN_NO_MORE_ENTRIES;
|
|
else
|
|
{
|
|
DWORD bytes = 0, count = 0, countLimit, i;
|
|
LPNETRESOURCEW resource;
|
|
LPWSTR strNext;
|
|
|
|
countLimit = *lpcCount == -1 ?
|
|
providerTable->numProviders - enumerator->providerIndex : *lpcCount;
|
|
while (count < countLimit && bytes < *lpBufferSize)
|
|
{
|
|
DWORD bytesNext = _countProviderBytesW(
|
|
&providerTable->table[count + enumerator->providerIndex]);
|
|
|
|
if (bytes + bytesNext < *lpBufferSize)
|
|
{
|
|
bytes += bytesNext;
|
|
count++;
|
|
}
|
|
}
|
|
strNext = (LPWSTR)((LPBYTE)lpBuffer + count * sizeof(NETRESOURCEW));
|
|
for (i = 0, resource = lpBuffer; i < count; i++, resource++)
|
|
{
|
|
resource->dwScope = RESOURCE_GLOBALNET;
|
|
resource->dwType = RESOURCETYPE_ANY;
|
|
resource->dwDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
|
|
resource->dwUsage = RESOURCEUSAGE_CONTAINER |
|
|
RESOURCEUSAGE_RESERVED;
|
|
resource->lpLocalName = NULL;
|
|
resource->lpRemoteName = strNext;
|
|
lstrcpyW(resource->lpRemoteName,
|
|
providerTable->table[i + enumerator->providerIndex].name);
|
|
strNext += lstrlenW(resource->lpRemoteName) + 1;
|
|
resource->lpComment = NULL;
|
|
resource->lpProvider = strNext;
|
|
lstrcpyW(resource->lpProvider,
|
|
providerTable->table[i + enumerator->providerIndex].name);
|
|
strNext += lstrlenW(resource->lpProvider) + 1;
|
|
}
|
|
enumerator->providerIndex += count;
|
|
*lpcCount = count;
|
|
ret = count > 0 ? WN_SUCCESS : WN_MORE_DATA;
|
|
}
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Advances the enumerator (assumed to be a global enumerator) to the next
|
|
* provider that supports the enumeration scope passed to WNetOpenEnum. Does
|
|
* not open a handle with the next provider.
|
|
* If the existing handle is NULL, may leave the enumerator unchanged, since
|
|
* the current provider may support the desired scope.
|
|
* If the existing handle is not NULL, closes it before moving on.
|
|
* Returns WN_SUCCESS on success, WN_NO_MORE_ENTRIES if there is no available
|
|
* provider, and another error on failure.
|
|
*/
|
|
static DWORD _globalEnumeratorAdvance(PWNetEnumerator enumerator)
|
|
{
|
|
if (!enumerator)
|
|
return WN_BAD_POINTER;
|
|
if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
|
|
return WN_BAD_VALUE;
|
|
if (!providerTable || enumerator->providerIndex >=
|
|
providerTable->numProviders)
|
|
return WN_NO_MORE_ENTRIES;
|
|
|
|
if (enumerator->providerDone)
|
|
{
|
|
DWORD dwEnum = 0;
|
|
enumerator->providerDone = FALSE;
|
|
if (enumerator->handle)
|
|
{
|
|
providerTable->table[enumerator->providerIndex].closeEnum(
|
|
enumerator->handle);
|
|
enumerator->handle = NULL;
|
|
enumerator->providerIndex++;
|
|
}
|
|
if (enumerator->dwScope == RESOURCE_CONNECTED)
|
|
dwEnum = WNNC_ENUM_LOCAL;
|
|
else if (enumerator->dwScope == RESOURCE_GLOBALNET)
|
|
dwEnum = WNNC_ENUM_GLOBAL;
|
|
else if (enumerator->dwScope == RESOURCE_CONTEXT)
|
|
dwEnum = WNNC_ENUM_CONTEXT;
|
|
for (; enumerator->providerIndex < providerTable->numProviders &&
|
|
!(providerTable->table[enumerator->providerIndex].dwEnumScopes
|
|
& dwEnum); enumerator->providerIndex++)
|
|
;
|
|
}
|
|
return enumerator->providerIndex < providerTable->numProviders ?
|
|
WN_SUCCESS : WN_NO_MORE_ENTRIES;
|
|
}
|
|
|
|
/* "Passes through" call to the next provider that supports the enumeration
|
|
* type.
|
|
* FIXME: if one call to a provider's enumerator succeeds while there's still
|
|
* space in lpBuffer, I don't call to the next provider. The caller may not
|
|
* expect that it should call EnumResourceW again with a return value of
|
|
* WN_SUCCESS (depending what *lpcCount was to begin with). That means strings
|
|
* may have to be moved around a bit, ick.
|
|
*/
|
|
static DWORD _enumerateGlobalPassthroughW(PWNetEnumerator enumerator,
|
|
LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
|
|
{
|
|
DWORD ret;
|
|
|
|
if (!enumerator)
|
|
return WN_BAD_POINTER;
|
|
if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
|
|
return WN_BAD_VALUE;
|
|
if (!lpcCount)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBuffer)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBufferSize)
|
|
return WN_BAD_POINTER;
|
|
if (*lpBufferSize < sizeof(NETRESOURCEW))
|
|
return WN_MORE_DATA;
|
|
|
|
ret = _globalEnumeratorAdvance(enumerator);
|
|
if (ret == WN_SUCCESS)
|
|
{
|
|
ret = providerTable->table[enumerator->providerIndex].
|
|
openEnum(enumerator->dwScope, enumerator->dwType,
|
|
enumerator->dwUsage, enumerator->specific.net,
|
|
&enumerator->handle);
|
|
if (ret == WN_SUCCESS)
|
|
{
|
|
ret = providerTable->table[enumerator->providerIndex].
|
|
enumResource(enumerator->handle, lpcCount, lpBuffer,
|
|
lpBufferSize);
|
|
if (ret != WN_MORE_DATA)
|
|
enumerator->providerDone = TRUE;
|
|
}
|
|
}
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static DWORD _enumerateGlobalW(PWNetEnumerator enumerator, LPDWORD lpcCount,
|
|
LPVOID lpBuffer, LPDWORD lpBufferSize)
|
|
{
|
|
DWORD ret;
|
|
|
|
if (!enumerator)
|
|
return WN_BAD_POINTER;
|
|
if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
|
|
return WN_BAD_VALUE;
|
|
if (!lpcCount)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBuffer)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBufferSize)
|
|
return WN_BAD_POINTER;
|
|
if (*lpBufferSize < sizeof(NETRESOURCEW))
|
|
return WN_MORE_DATA;
|
|
if (!providerTable)
|
|
return WN_NO_NETWORK;
|
|
|
|
switch (enumerator->dwScope)
|
|
{
|
|
case RESOURCE_GLOBALNET:
|
|
if (enumerator->specific.net)
|
|
ret = _enumerateGlobalPassthroughW(enumerator, lpcCount,
|
|
lpBuffer, lpBufferSize);
|
|
else
|
|
ret = _enumerateProvidersW(enumerator, lpcCount, lpBuffer,
|
|
lpBufferSize);
|
|
break;
|
|
case RESOURCE_CONTEXT:
|
|
ret = _enumerateGlobalPassthroughW(enumerator, lpcCount, lpBuffer,
|
|
lpBufferSize);
|
|
break;
|
|
default:
|
|
WARN("unexpected scope 0x%08x\n", enumerator->dwScope);
|
|
ret = WN_NO_MORE_ENTRIES;
|
|
}
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static DWORD _enumerateProviderW(PWNetEnumerator enumerator, LPDWORD lpcCount,
|
|
LPVOID lpBuffer, LPDWORD lpBufferSize)
|
|
{
|
|
if (!enumerator)
|
|
return WN_BAD_POINTER;
|
|
if (enumerator->enumType != WNET_ENUMERATOR_TYPE_PROVIDER)
|
|
return WN_BAD_VALUE;
|
|
if (!enumerator->handle)
|
|
return WN_BAD_VALUE;
|
|
if (!lpcCount)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBuffer)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBufferSize)
|
|
return WN_BAD_POINTER;
|
|
if (!providerTable)
|
|
return WN_NO_NETWORK;
|
|
if (enumerator->providerIndex >= providerTable->numProviders)
|
|
return WN_NO_MORE_ENTRIES;
|
|
if (!providerTable->table[enumerator->providerIndex].enumResource)
|
|
return WN_BAD_VALUE;
|
|
return providerTable->table[enumerator->providerIndex].enumResource(
|
|
enumerator->handle, lpcCount, lpBuffer, lpBufferSize);
|
|
}
|
|
|
|
static DWORD _enumerateContextW(PWNetEnumerator enumerator, LPDWORD lpcCount,
|
|
LPVOID lpBuffer, LPDWORD lpBufferSize)
|
|
{
|
|
DWORD ret;
|
|
size_t cchEntireNetworkLen, bytesNeeded;
|
|
|
|
if (!enumerator)
|
|
return WN_BAD_POINTER;
|
|
if (enumerator->enumType != WNET_ENUMERATOR_TYPE_CONTEXT)
|
|
return WN_BAD_VALUE;
|
|
if (!lpcCount)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBuffer)
|
|
return WN_BAD_POINTER;
|
|
if (!lpBufferSize)
|
|
return WN_BAD_POINTER;
|
|
if (!providerTable)
|
|
return WN_NO_NETWORK;
|
|
|
|
cchEntireNetworkLen = lstrlenW(providerTable->entireNetwork) + 1;
|
|
bytesNeeded = sizeof(NETRESOURCEW) + cchEntireNetworkLen * sizeof(WCHAR);
|
|
if (*lpBufferSize < bytesNeeded)
|
|
{
|
|
*lpBufferSize = bytesNeeded;
|
|
ret = WN_MORE_DATA;
|
|
}
|
|
else
|
|
{
|
|
LPNETRESOURCEW lpNet = lpBuffer;
|
|
|
|
lpNet->dwScope = RESOURCE_GLOBALNET;
|
|
lpNet->dwType = enumerator->dwType;
|
|
lpNet->dwDisplayType = RESOURCEDISPLAYTYPE_ROOT;
|
|
lpNet->dwUsage = RESOURCEUSAGE_CONTAINER;
|
|
lpNet->lpLocalName = NULL;
|
|
lpNet->lpRemoteName = NULL;
|
|
lpNet->lpProvider = NULL;
|
|
/* odd, but correct: put comment at end of buffer, so it won't get
|
|
* overwritten by subsequent calls to a provider's enumResource
|
|
*/
|
|
lpNet->lpComment = (LPWSTR)((LPBYTE)lpBuffer + *lpBufferSize -
|
|
(cchEntireNetworkLen * sizeof(WCHAR)));
|
|
lstrcpyW(lpNet->lpComment, providerTable->entireNetwork);
|
|
ret = WN_SUCCESS;
|
|
}
|
|
if (ret == WN_SUCCESS)
|
|
{
|
|
DWORD bufferSize = *lpBufferSize - bytesNeeded;
|
|
|
|
/* "Entire Network" entry enumerated--morph this into a global
|
|
* enumerator. enumerator->lpNet continues to be NULL, since it has
|
|
* no meaning when the scope isn't RESOURCE_GLOBALNET.
|
|
*/
|
|
enumerator->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
|
|
ret = _enumerateGlobalW(enumerator, lpcCount,
|
|
(LPBYTE)lpBuffer + bytesNeeded, &bufferSize);
|
|
if (ret == WN_SUCCESS)
|
|
{
|
|
/* reflect the fact that we already enumerated "Entire Network" */
|
|
(*lpcCount)++;
|
|
*lpBufferSize = bufferSize + bytesNeeded;
|
|
}
|
|
else
|
|
{
|
|
/* the provider enumeration failed, but we already succeeded in
|
|
* enumerating "Entire Network"--leave type as global to allow a
|
|
* retry, but indicate success with a count of one.
|
|
*/
|
|
ret = WN_SUCCESS;
|
|
*lpcCount = 1;
|
|
*lpBufferSize = bytesNeeded;
|
|
}
|
|
}
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static DWORD _copyStringToEnumW(const WCHAR *source, DWORD* left, void** end)
|
|
{
|
|
DWORD len;
|
|
WCHAR* local = *end;
|
|
|
|
len = lstrlenW(source) + 1;
|
|
len *= sizeof(WCHAR);
|
|
if (*left < len)
|
|
return WN_MORE_DATA;
|
|
|
|
local -= (len / sizeof(WCHAR));
|
|
memcpy(local, source, len);
|
|
*left -= len;
|
|
*end = local;
|
|
|
|
return WN_SUCCESS;
|
|
}
|
|
|
|
static DWORD _enumerateConnectedW(PWNetEnumerator enumerator, DWORD* user_count,
|
|
void* user_buffer, DWORD* user_size)
|
|
{
|
|
DWORD ret, index, count, total_count, size, i, left;
|
|
void* end;
|
|
NETRESOURCEW* curr, * buffer;
|
|
HANDLE* handles;
|
|
|
|
if (!enumerator)
|
|
return WN_BAD_POINTER;
|
|
if (enumerator->enumType != WNET_ENUMERATOR_TYPE_CONNECTED)
|
|
return WN_BAD_VALUE;
|
|
if (!user_count || !user_buffer || !user_size)
|
|
return WN_BAD_POINTER;
|
|
if (!providerTable)
|
|
return WN_NO_NETWORK;
|
|
|
|
handles = enumerator->specific.handles;
|
|
left = *user_size;
|
|
size = *user_size;
|
|
buffer = HeapAlloc(GetProcessHeap(), 0, *user_size);
|
|
if (!buffer)
|
|
return WN_NO_NETWORK;
|
|
|
|
curr = user_buffer;
|
|
end = (char *)user_buffer + size;
|
|
count = *user_count;
|
|
total_count = 0;
|
|
|
|
ret = WN_NO_MORE_ENTRIES;
|
|
for (index = 0; index < providerTable->numProviders; index++)
|
|
{
|
|
if (providerTable->table[index].dwEnumScopes)
|
|
{
|
|
if (handles[index] == 0)
|
|
{
|
|
ret = providerTable->table[index].openEnum(enumerator->dwScope,
|
|
enumerator->dwType,
|
|
enumerator->dwUsage,
|
|
NULL, &handles[index]);
|
|
if (ret != WN_SUCCESS)
|
|
continue;
|
|
}
|
|
|
|
ret = providerTable->table[index].enumResource(handles[index],
|
|
&count, buffer,
|
|
&size);
|
|
total_count += count;
|
|
if (ret == WN_MORE_DATA)
|
|
break;
|
|
|
|
if (ret == WN_SUCCESS)
|
|
{
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
if (left < sizeof(NETRESOURCEW))
|
|
{
|
|
ret = WN_MORE_DATA;
|
|
break;
|
|
}
|
|
|
|
memcpy(curr, &buffer[i], sizeof(NETRESOURCEW));
|
|
left -= sizeof(NETRESOURCEW);
|
|
|
|
ret = _copyStringToEnumW(buffer[i].lpLocalName, &left, &end);
|
|
if (ret == WN_MORE_DATA)
|
|
break;
|
|
curr->lpLocalName = end;
|
|
|
|
ret = _copyStringToEnumW(buffer[i].lpRemoteName, &left, &end);
|
|
if (ret == WN_MORE_DATA)
|
|
break;
|
|
curr->lpRemoteName = end;
|
|
|
|
ret = _copyStringToEnumW(buffer[i].lpProvider, &left, &end);
|
|
if (ret == WN_MORE_DATA)
|
|
break;
|
|
curr->lpProvider = end;
|
|
|
|
++curr;
|
|
}
|
|
|
|
size = left;
|
|
}
|
|
|
|
if (*user_count != -1)
|
|
count = *user_count - total_count;
|
|
else
|
|
count = *user_count;
|
|
}
|
|
}
|
|
|
|
if (total_count == 0)
|
|
ret = WN_NO_MORE_ENTRIES;
|
|
|
|
*user_count = total_count;
|
|
if (ret != WN_MORE_DATA && ret != WN_NO_MORE_ENTRIES)
|
|
ret = WN_SUCCESS;
|
|
|
|
HeapFree(GetProcessHeap(), 0, buffer);
|
|
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static const WCHAR connectionType[] = { 'C','o','n','n','e','c','t','i','o','n','T','y','p','e',0 };
|
|
static const WCHAR providerName[] = { 'P','r','o','v','i','d','e','r','N','a','m','e',0 };
|
|
static const WCHAR remotePath[] = { 'R','e','m','o','t','e','P','a','t','h',0 };
|
|
|
|
static WCHAR *get_reg_str(HKEY hkey, const WCHAR *value, DWORD *len)
|
|
{
|
|
DWORD type;
|
|
WCHAR *ret = NULL;
|
|
|
|
if (!RegQueryValueExW(hkey, value, NULL, &type, NULL, len) && type == REG_SZ)
|
|
{
|
|
if (!(ret = HeapAlloc(GetProcessHeap(), 0, *len))) return NULL;
|
|
RegQueryValueExW(hkey, value, 0, 0, (BYTE *)ret, len);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static DWORD _enumeratorRememberedW(PWNetEnumerator enumerator, DWORD* user_count,
|
|
void* user_buffer, DWORD* user_size)
|
|
{
|
|
HKEY registry, connection;
|
|
WCHAR buffer[255];
|
|
LONG size_left;
|
|
DWORD index, ret, type, len, size, registry_size, full_size = 0, total_count;
|
|
NETRESOURCEW * net_buffer = user_buffer;
|
|
WCHAR * str, * registry_string;
|
|
|
|
/* we will do the work in a single loop, so here is some things:
|
|
* we write netresource at the begin of the user buffer
|
|
* we write strings at the end of the user buffer
|
|
*/
|
|
size_left = *user_size;
|
|
total_count = 0;
|
|
type = enumerator->dwType;
|
|
registry = enumerator->specific.remembered.registry;
|
|
str = (WCHAR *)((ULONG_PTR)user_buffer + *user_size - sizeof(WCHAR));
|
|
for (index = enumerator->specific.remembered.index; ; ++index)
|
|
{
|
|
enumerator->specific.remembered.index = index;
|
|
|
|
if (*user_count != -1 && total_count == *user_count)
|
|
{
|
|
ret = WN_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
len = ARRAY_SIZE(buffer);
|
|
ret = RegEnumKeyExW(registry, index, buffer, &len, NULL, NULL, NULL, NULL);
|
|
if (ret != ERROR_SUCCESS)
|
|
{
|
|
if (ret == ERROR_NO_MORE_ITEMS) ret = WN_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if (RegOpenKeyExW(registry, buffer, 0, KEY_READ, &connection) != ERROR_SUCCESS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
full_size = sizeof(NETRESOURCEW);
|
|
size_left -= sizeof(NETRESOURCEW);
|
|
|
|
if (size_left > 0)
|
|
{
|
|
size = sizeof(DWORD);
|
|
RegQueryValueExW(connection, connectionType, NULL, NULL, (BYTE *)&net_buffer->dwType, &size);
|
|
if (type != RESOURCETYPE_ANY && net_buffer->dwType != type)
|
|
{
|
|
size_left += sizeof(NETRESOURCEW);
|
|
RegCloseKey(connection);
|
|
continue;
|
|
}
|
|
|
|
net_buffer->dwScope = RESOURCE_REMEMBERED;
|
|
net_buffer->dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
|
|
net_buffer->dwUsage = RESOURCEUSAGE_CONNECTABLE;
|
|
}
|
|
else
|
|
ret = WN_MORE_DATA;
|
|
|
|
/* FIXME: this only supports drive letters */
|
|
full_size += 3 * sizeof(WCHAR);
|
|
size_left -= 3 * sizeof(WCHAR);
|
|
if (size_left > 0)
|
|
{
|
|
str -= 3;
|
|
str[0] = buffer[0];
|
|
str[1] = ':';
|
|
str[2] = 0;
|
|
net_buffer->lpLocalName = str;
|
|
}
|
|
|
|
registry_size = 0;
|
|
registry_string = get_reg_str(connection, providerName, ®istry_size);
|
|
if (registry_string)
|
|
{
|
|
full_size += registry_size;
|
|
size_left -= registry_size;
|
|
|
|
if (size_left > 0)
|
|
{
|
|
str -= (registry_size / sizeof(WCHAR));
|
|
lstrcpyW(str, registry_string);
|
|
net_buffer->lpProvider = str;
|
|
}
|
|
else
|
|
ret = WN_MORE_DATA;
|
|
|
|
HeapFree(GetProcessHeap(), 0, registry_string);
|
|
}
|
|
|
|
registry_size = 0;
|
|
registry_string = get_reg_str(connection, remotePath, ®istry_size);
|
|
if (registry_string)
|
|
{
|
|
full_size += registry_size;
|
|
size_left -= registry_size;
|
|
|
|
if (size_left > 0)
|
|
{
|
|
str -= (registry_size / sizeof(WCHAR));
|
|
lstrcpyW(str, registry_string);
|
|
net_buffer->lpRemoteName = str;
|
|
}
|
|
else
|
|
ret = WN_MORE_DATA;
|
|
|
|
HeapFree(GetProcessHeap(), 0, registry_string);
|
|
}
|
|
|
|
RegCloseKey(connection);
|
|
|
|
net_buffer->lpComment = NULL;
|
|
|
|
if (size_left < 0)
|
|
break;
|
|
|
|
++total_count;
|
|
++net_buffer;
|
|
}
|
|
|
|
if (total_count == 0)
|
|
ret = WN_NO_MORE_ENTRIES;
|
|
|
|
*user_count = total_count;
|
|
|
|
if (ret != WN_MORE_DATA && ret != WN_NO_MORE_ENTRIES)
|
|
ret = WN_SUCCESS;
|
|
|
|
if (ret == WN_MORE_DATA)
|
|
*user_size = *user_size + full_size;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetEnumResourceW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetEnumResourceW( HANDLE hEnum, LPDWORD lpcCount,
|
|
LPVOID lpBuffer, LPDWORD lpBufferSize )
|
|
{
|
|
DWORD ret;
|
|
|
|
TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
|
|
|
|
if (!hEnum)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpcCount)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpBuffer)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpBufferSize)
|
|
ret = WN_BAD_POINTER;
|
|
else if (*lpBufferSize < sizeof(NETRESOURCEW))
|
|
{
|
|
*lpBufferSize = sizeof(NETRESOURCEW);
|
|
ret = WN_MORE_DATA;
|
|
}
|
|
else
|
|
{
|
|
PWNetEnumerator enumerator = (PWNetEnumerator)hEnum;
|
|
|
|
switch (enumerator->enumType)
|
|
{
|
|
case WNET_ENUMERATOR_TYPE_GLOBAL:
|
|
ret = _enumerateGlobalW(enumerator, lpcCount, lpBuffer,
|
|
lpBufferSize);
|
|
break;
|
|
case WNET_ENUMERATOR_TYPE_PROVIDER:
|
|
ret = _enumerateProviderW(enumerator, lpcCount, lpBuffer,
|
|
lpBufferSize);
|
|
break;
|
|
case WNET_ENUMERATOR_TYPE_CONTEXT:
|
|
ret = _enumerateContextW(enumerator, lpcCount, lpBuffer,
|
|
lpBufferSize);
|
|
break;
|
|
case WNET_ENUMERATOR_TYPE_CONNECTED:
|
|
ret = _enumerateConnectedW(enumerator, lpcCount, lpBuffer,
|
|
lpBufferSize);
|
|
break;
|
|
case WNET_ENUMERATOR_TYPE_REMEMBERED:
|
|
ret = _enumeratorRememberedW(enumerator, lpcCount, lpBuffer,
|
|
lpBufferSize);
|
|
break;
|
|
default:
|
|
WARN("bogus enumerator type!\n");
|
|
ret = WN_NO_NETWORK;
|
|
}
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetCloseEnum [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetCloseEnum( HANDLE hEnum )
|
|
{
|
|
DWORD ret, index;
|
|
HANDLE *handles;
|
|
|
|
TRACE( "(%p)\n", hEnum );
|
|
|
|
if (hEnum)
|
|
{
|
|
PWNetEnumerator enumerator = (PWNetEnumerator)hEnum;
|
|
|
|
switch (enumerator->enumType)
|
|
{
|
|
case WNET_ENUMERATOR_TYPE_GLOBAL:
|
|
if (enumerator->specific.net)
|
|
_freeEnumNetResource(enumerator->specific.net);
|
|
if (enumerator->handle)
|
|
providerTable->table[enumerator->providerIndex].
|
|
closeEnum(enumerator->handle);
|
|
ret = WN_SUCCESS;
|
|
break;
|
|
case WNET_ENUMERATOR_TYPE_PROVIDER:
|
|
if (enumerator->handle)
|
|
providerTable->table[enumerator->providerIndex].
|
|
closeEnum(enumerator->handle);
|
|
ret = WN_SUCCESS;
|
|
break;
|
|
case WNET_ENUMERATOR_TYPE_CONNECTED:
|
|
handles = enumerator->specific.handles;
|
|
for (index = 0; index < providerTable->numProviders; index++)
|
|
{
|
|
if (providerTable->table[index].dwEnumScopes && handles[index])
|
|
providerTable->table[index].closeEnum(handles[index]);
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, handles);
|
|
ret = WN_SUCCESS;
|
|
break;
|
|
case WNET_ENUMERATOR_TYPE_REMEMBERED:
|
|
RegCloseKey(enumerator->specific.remembered.registry);
|
|
ret = WN_SUCCESS;
|
|
break;
|
|
default:
|
|
WARN("bogus enumerator type!\n");
|
|
ret = WN_BAD_HANDLE;
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, hEnum);
|
|
}
|
|
else
|
|
ret = WN_BAD_HANDLE;
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetGetResourceInformationA [MPR.@]
|
|
*
|
|
* See WNetGetResourceInformationW
|
|
*/
|
|
DWORD WINAPI WNetGetResourceInformationA( LPNETRESOURCEA lpNetResource,
|
|
LPVOID lpBuffer, LPDWORD cbBuffer,
|
|
LPSTR *lplpSystem )
|
|
{
|
|
DWORD ret;
|
|
|
|
TRACE( "(%p, %p, %p, %p)\n",
|
|
lpNetResource, lpBuffer, cbBuffer, lplpSystem );
|
|
|
|
if (!providerTable || providerTable->numProviders == 0)
|
|
ret = WN_NO_NETWORK;
|
|
else if (lpNetResource)
|
|
{
|
|
LPNETRESOURCEW lpNetResourceW = NULL;
|
|
DWORD size = 1024, count = 1;
|
|
DWORD len;
|
|
|
|
lpNetResourceW = HeapAlloc(GetProcessHeap(), 0, size);
|
|
ret = _thunkNetResourceArrayAToW(lpNetResource, &count, lpNetResourceW, &size);
|
|
if (ret == WN_MORE_DATA)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, lpNetResourceW);
|
|
lpNetResourceW = HeapAlloc(GetProcessHeap(), 0, size);
|
|
if (lpNetResourceW)
|
|
ret = _thunkNetResourceArrayAToW(lpNetResource,
|
|
&count, lpNetResourceW, &size);
|
|
else
|
|
ret = WN_OUT_OF_MEMORY;
|
|
}
|
|
if (ret == WN_SUCCESS)
|
|
{
|
|
LPWSTR lpSystemW = NULL;
|
|
LPVOID lpBufferW;
|
|
size = 1024;
|
|
lpBufferW = HeapAlloc(GetProcessHeap(), 0, size);
|
|
if (lpBufferW)
|
|
{
|
|
ret = WNetGetResourceInformationW(lpNetResourceW,
|
|
lpBufferW, &size, &lpSystemW);
|
|
if (ret == WN_MORE_DATA)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, lpBufferW);
|
|
lpBufferW = HeapAlloc(GetProcessHeap(), 0, size);
|
|
if (lpBufferW)
|
|
ret = WNetGetResourceInformationW(lpNetResourceW,
|
|
lpBufferW, &size, &lpSystemW);
|
|
else
|
|
ret = WN_OUT_OF_MEMORY;
|
|
}
|
|
if (ret == WN_SUCCESS)
|
|
{
|
|
ret = _thunkNetResourceArrayWToA(lpBufferW,
|
|
&count, lpBuffer, cbBuffer);
|
|
HeapFree(GetProcessHeap(), 0, lpNetResourceW);
|
|
lpNetResourceW = lpBufferW;
|
|
size = sizeof(NETRESOURCEA);
|
|
size += WideCharToMultiByte(CP_ACP, 0, lpNetResourceW->lpRemoteName,
|
|
-1, NULL, 0, NULL, NULL);
|
|
size += WideCharToMultiByte(CP_ACP, 0, lpNetResourceW->lpProvider,
|
|
-1, NULL, 0, NULL, NULL);
|
|
|
|
len = WideCharToMultiByte(CP_ACP, 0, lpSystemW,
|
|
-1, NULL, 0, NULL, NULL);
|
|
if ((len) && ( size + len < *cbBuffer))
|
|
{
|
|
*lplpSystem = (char*)lpBuffer + *cbBuffer - len;
|
|
WideCharToMultiByte(CP_ACP, 0, lpSystemW, -1,
|
|
*lplpSystem, len, NULL, NULL);
|
|
ret = WN_SUCCESS;
|
|
}
|
|
else
|
|
ret = WN_MORE_DATA;
|
|
}
|
|
else
|
|
ret = WN_OUT_OF_MEMORY;
|
|
HeapFree(GetProcessHeap(), 0, lpBufferW);
|
|
}
|
|
else
|
|
ret = WN_OUT_OF_MEMORY;
|
|
HeapFree(GetProcessHeap(), 0, lpSystemW);
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, lpNetResourceW);
|
|
}
|
|
else
|
|
ret = WN_NO_NETWORK;
|
|
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetGetResourceInformationW [MPR.@]
|
|
*
|
|
* WNetGetResourceInformationW function identifies the network provider
|
|
* that owns the resource and gets information about the type of the resource.
|
|
*
|
|
* PARAMS:
|
|
* lpNetResource [ I] the pointer to NETRESOURCEW structure, that
|
|
* defines a network resource.
|
|
* lpBuffer [ O] the pointer to buffer, containing result. It
|
|
* contains NETRESOURCEW structure and strings to
|
|
* which the members of the NETRESOURCEW structure
|
|
* point.
|
|
* cbBuffer [I/O] the pointer to DWORD number - size of buffer
|
|
* in bytes.
|
|
* lplpSystem [ O] the pointer to string in the output buffer,
|
|
* containing the part of the resource name without
|
|
* names of the server and share.
|
|
*
|
|
* RETURNS:
|
|
* NO_ERROR if the function succeeds. System error code if the function fails.
|
|
*/
|
|
|
|
DWORD WINAPI WNetGetResourceInformationW( LPNETRESOURCEW lpNetResource,
|
|
LPVOID lpBuffer, LPDWORD cbBuffer,
|
|
LPWSTR *lplpSystem )
|
|
{
|
|
DWORD ret = WN_NO_NETWORK;
|
|
DWORD index;
|
|
|
|
TRACE( "(%p, %p, %p, %p)\n",
|
|
lpNetResource, lpBuffer, cbBuffer, lplpSystem);
|
|
|
|
if (!(lpBuffer))
|
|
ret = WN_OUT_OF_MEMORY;
|
|
else if (providerTable != NULL)
|
|
{
|
|
/* FIXME: For function value of a variable is indifferent, it does
|
|
* search of all providers in a network.
|
|
*/
|
|
for (index = 0; index < providerTable->numProviders; index++)
|
|
{
|
|
if(providerTable->table[index].getCaps(WNNC_DIALOG) &
|
|
WNNC_DLG_GETRESOURCEINFORMATION)
|
|
{
|
|
if (providerTable->table[index].getResourceInformation)
|
|
ret = providerTable->table[index].getResourceInformation(
|
|
lpNetResource, lpBuffer, cbBuffer, lplpSystem);
|
|
else
|
|
ret = WN_NO_NETWORK;
|
|
if (ret == WN_SUCCESS)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetGetResourceParentA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetGetResourceParentA( LPNETRESOURCEA lpNetResource,
|
|
LPVOID lpBuffer, LPDWORD lpBufferSize )
|
|
{
|
|
FIXME( "(%p, %p, %p): stub\n",
|
|
lpNetResource, lpBuffer, lpBufferSize );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetGetResourceParentW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetGetResourceParentW( LPNETRESOURCEW lpNetResource,
|
|
LPVOID lpBuffer, LPDWORD lpBufferSize )
|
|
{
|
|
FIXME( "(%p, %p, %p): stub\n",
|
|
lpNetResource, lpBuffer, lpBufferSize );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Connection Functions
|
|
*/
|
|
|
|
/*********************************************************************
|
|
* WNetAddConnectionA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetAddConnectionA( LPCSTR lpRemoteName, LPCSTR lpPassword,
|
|
LPCSTR lpLocalName )
|
|
{
|
|
NETRESOURCEA resourcesA;
|
|
|
|
memset(&resourcesA, 0, sizeof(resourcesA));
|
|
resourcesA.lpRemoteName = (LPSTR)lpRemoteName;
|
|
resourcesA.lpLocalName = (LPSTR)lpLocalName;
|
|
return WNetUseConnectionA(NULL, &resourcesA, lpPassword, NULL, 0, NULL, 0, NULL);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetAddConnectionW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetAddConnectionW( LPCWSTR lpRemoteName, LPCWSTR lpPassword,
|
|
LPCWSTR lpLocalName )
|
|
{
|
|
NETRESOURCEW resourcesW;
|
|
|
|
memset(&resourcesW, 0, sizeof(resourcesW));
|
|
resourcesW.lpRemoteName = (LPWSTR)lpRemoteName;
|
|
resourcesW.lpLocalName = (LPWSTR)lpLocalName;
|
|
return WNetUseConnectionW(NULL, &resourcesW, lpPassword, NULL, 0, NULL, 0, NULL);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetAddConnection2A [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetAddConnection2A( LPNETRESOURCEA lpNetResource,
|
|
LPCSTR lpPassword, LPCSTR lpUserID,
|
|
DWORD dwFlags )
|
|
{
|
|
return WNetUseConnectionA(NULL, lpNetResource, lpPassword, lpUserID, dwFlags,
|
|
NULL, 0, NULL);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetAddConnection2W [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetAddConnection2W( LPNETRESOURCEW lpNetResource,
|
|
LPCWSTR lpPassword, LPCWSTR lpUserID,
|
|
DWORD dwFlags )
|
|
{
|
|
return WNetUseConnectionW(NULL, lpNetResource, lpPassword, lpUserID, dwFlags,
|
|
NULL, 0, NULL);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetAddConnection3A [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetAddConnection3A( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
|
|
LPCSTR lpPassword, LPCSTR lpUserID,
|
|
DWORD dwFlags )
|
|
{
|
|
return WNetUseConnectionA(hwndOwner, lpNetResource, lpPassword, lpUserID,
|
|
dwFlags, NULL, 0, NULL);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetAddConnection3W [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetAddConnection3W( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
|
|
LPCWSTR lpPassword, LPCWSTR lpUserID,
|
|
DWORD dwFlags )
|
|
{
|
|
return WNetUseConnectionW(hwndOwner, lpNetResource, lpPassword, lpUserID,
|
|
dwFlags, NULL, 0, NULL);
|
|
}
|
|
|
|
struct use_connection_context
|
|
{
|
|
HWND hwndOwner;
|
|
NETRESOURCEW *resource;
|
|
NETRESOURCEA *resourceA; /* only set for WNetUseConnectionA */
|
|
WCHAR *password;
|
|
WCHAR *userid;
|
|
DWORD flags;
|
|
void *accessname;
|
|
DWORD *buffer_size;
|
|
DWORD *result;
|
|
DWORD (*pre_set_accessname)(struct use_connection_context*, WCHAR *);
|
|
void (*set_accessname)(struct use_connection_context*, WCHAR *);
|
|
};
|
|
|
|
static DWORD use_connection_pre_set_accessnameW(struct use_connection_context *ctxt, WCHAR *local_name)
|
|
{
|
|
if (ctxt->accessname && ctxt->buffer_size && *ctxt->buffer_size)
|
|
{
|
|
DWORD len;
|
|
|
|
if (local_name)
|
|
len = lstrlenW(local_name);
|
|
else
|
|
len = lstrlenW(ctxt->resource->lpRemoteName);
|
|
|
|
if (++len > *ctxt->buffer_size)
|
|
{
|
|
*ctxt->buffer_size = len;
|
|
return ERROR_MORE_DATA;
|
|
}
|
|
}
|
|
else
|
|
ctxt->accessname = NULL;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static void use_connection_set_accessnameW(struct use_connection_context *ctxt, WCHAR *local_name)
|
|
{
|
|
WCHAR *accessname = ctxt->accessname;
|
|
if (local_name)
|
|
{
|
|
lstrcpyW(accessname, local_name);
|
|
if (ctxt->result)
|
|
*ctxt->result = CONNECT_LOCALDRIVE;
|
|
}
|
|
else
|
|
lstrcpyW(accessname, ctxt->resource->lpRemoteName);
|
|
}
|
|
|
|
static DWORD wnet_use_provider( struct use_connection_context *ctxt, NETRESOURCEW * netres, WNetProvider *provider, BOOLEAN redirect )
|
|
{
|
|
DWORD caps, ret;
|
|
|
|
caps = provider->getCaps(WNNC_CONNECTION);
|
|
if (!(caps & (WNNC_CON_ADDCONNECTION | WNNC_CON_ADDCONNECTION3)))
|
|
return ERROR_BAD_PROVIDER;
|
|
|
|
ret = WN_ACCESS_DENIED;
|
|
do
|
|
{
|
|
if ((caps & WNNC_CON_ADDCONNECTION3) && provider->addConnection3)
|
|
ret = provider->addConnection3(ctxt->hwndOwner, netres, ctxt->password, ctxt->userid, ctxt->flags);
|
|
else if ((caps & WNNC_CON_ADDCONNECTION) && provider->addConnection)
|
|
ret = provider->addConnection(netres, ctxt->password, ctxt->userid);
|
|
|
|
if (ret == WN_ALREADY_CONNECTED && redirect)
|
|
netres->lpLocalName[0] -= 1;
|
|
} while (redirect && ret == WN_ALREADY_CONNECTED && netres->lpLocalName[0] >= 'C');
|
|
|
|
if (ret == WN_SUCCESS && ctxt->accessname)
|
|
ctxt->set_accessname(ctxt, netres->lpLocalName);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const WCHAR providerType[] = { 'P','r','o','v','i','d','e','r','T','y','p','e',0 };
|
|
static const WCHAR userName[] = { 'U','s','e','r','N','a','m','e',0 };
|
|
|
|
static DWORD wnet_use_connection( struct use_connection_context *ctxt )
|
|
{
|
|
WNetProvider *provider = NULL;
|
|
DWORD index, ret = WN_NO_NETWORK;
|
|
BOOL redirect = FALSE;
|
|
WCHAR letter[3] = {'Z', ':', 0};
|
|
NETRESOURCEW netres;
|
|
|
|
if (!providerTable || providerTable->numProviders == 0)
|
|
return WN_NO_NETWORK;
|
|
|
|
if (!ctxt->resource)
|
|
return ERROR_INVALID_PARAMETER;
|
|
netres = *ctxt->resource;
|
|
|
|
if (!netres.lpLocalName && (ctxt->flags & CONNECT_REDIRECT))
|
|
{
|
|
if (netres.dwType != RESOURCETYPE_DISK && netres.dwType != RESOURCETYPE_PRINT)
|
|
return ERROR_BAD_DEV_TYPE;
|
|
|
|
if (netres.dwType == RESOURCETYPE_PRINT)
|
|
{
|
|
FIXME("Local device selection is not implemented for printers.\n");
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
redirect = TRUE;
|
|
netres.lpLocalName = letter;
|
|
}
|
|
|
|
if (ctxt->flags & CONNECT_INTERACTIVE)
|
|
return ERROR_BAD_NET_NAME;
|
|
|
|
if ((ret = ctxt->pre_set_accessname(ctxt, netres.lpLocalName)))
|
|
return ret;
|
|
|
|
if (netres.lpProvider)
|
|
{
|
|
index = _findProviderIndexW(netres.lpProvider);
|
|
if (index == BAD_PROVIDER_INDEX)
|
|
return ERROR_BAD_PROVIDER;
|
|
|
|
provider = &providerTable->table[index];
|
|
ret = wnet_use_provider(ctxt, &netres, provider, redirect);
|
|
}
|
|
else
|
|
{
|
|
for (index = 0; index < providerTable->numProviders; index++)
|
|
{
|
|
provider = &providerTable->table[index];
|
|
ret = wnet_use_provider(ctxt, &netres, provider, redirect);
|
|
if (ret == WN_SUCCESS || ret == WN_ALREADY_CONNECTED)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret == WN_SUCCESS && ctxt->flags & CONNECT_UPDATE_PROFILE)
|
|
{
|
|
HKEY user_profile;
|
|
|
|
if (netres.dwType == RESOURCETYPE_PRINT)
|
|
{
|
|
FIXME("Persistent connection are not supported for printers\n");
|
|
return ret;
|
|
}
|
|
|
|
if (RegOpenCurrentUser(KEY_ALL_ACCESS, &user_profile) == ERROR_SUCCESS)
|
|
{
|
|
HKEY network;
|
|
WCHAR subkey[10] = {'N', 'e', 't', 'w', 'o', 'r', 'k', '\\', netres.lpLocalName[0], 0};
|
|
|
|
if (RegCreateKeyExW(user_profile, subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS, NULL, &network, NULL) == ERROR_SUCCESS)
|
|
{
|
|
DWORD dword_arg = RESOURCETYPE_DISK;
|
|
DWORD len = (lstrlenW(provider->name) + 1) * sizeof(WCHAR);
|
|
static const WCHAR empty[1] = {0};
|
|
|
|
RegSetValueExW(network, connectionType, 0, REG_DWORD, (const BYTE *)&dword_arg, sizeof(DWORD));
|
|
RegSetValueExW(network, providerName, 0, REG_SZ, (const BYTE *)provider->name, len);
|
|
RegSetValueExW(network, providerType, 0, REG_DWORD, (const BYTE *)&provider->dwNetType, sizeof(DWORD));
|
|
len = (lstrlenW(netres.lpRemoteName) + 1) * sizeof(WCHAR);
|
|
RegSetValueExW(network, remotePath, 0, REG_SZ, (const BYTE *)netres.lpRemoteName, len);
|
|
len = sizeof(empty);
|
|
RegSetValueExW(network, userName, 0, REG_SZ, (const BYTE *)empty, len);
|
|
RegCloseKey(network);
|
|
}
|
|
|
|
RegCloseKey(user_profile);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* WNetUseConnectionW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetUseConnectionW( HWND hwndOwner, NETRESOURCEW *resource, LPCWSTR password,
|
|
LPCWSTR userid, DWORD flags, LPWSTR accessname, DWORD *buffer_size, DWORD *result )
|
|
{
|
|
struct use_connection_context ctxt;
|
|
|
|
TRACE( "(%p, %p, %p, %s, 0x%08X, %p, %p, %p)\n",
|
|
hwndOwner, resource, password, debugstr_w(userid), flags,
|
|
accessname, buffer_size, result );
|
|
|
|
ctxt.hwndOwner = hwndOwner;
|
|
ctxt.resource = resource;
|
|
ctxt.resourceA = NULL;
|
|
ctxt.password = (WCHAR*)password;
|
|
ctxt.userid = (WCHAR*)userid;
|
|
ctxt.flags = flags;
|
|
ctxt.accessname = accessname;
|
|
ctxt.buffer_size = buffer_size;
|
|
ctxt.result = result;
|
|
ctxt.pre_set_accessname = use_connection_pre_set_accessnameW;
|
|
ctxt.set_accessname = use_connection_set_accessnameW;
|
|
|
|
return wnet_use_connection(&ctxt);
|
|
}
|
|
|
|
static DWORD use_connection_pre_set_accessnameA(struct use_connection_context *ctxt, WCHAR *local_name)
|
|
{
|
|
if (ctxt->accessname && ctxt->buffer_size && *ctxt->buffer_size)
|
|
{
|
|
DWORD len;
|
|
|
|
if (local_name)
|
|
len = WideCharToMultiByte(CP_ACP, 0, local_name, -1, NULL, 0, NULL, NULL) - 1;
|
|
else
|
|
len = strlen(ctxt->resourceA->lpRemoteName);
|
|
|
|
if (++len > *ctxt->buffer_size)
|
|
{
|
|
*ctxt->buffer_size = len;
|
|
return ERROR_MORE_DATA;
|
|
}
|
|
}
|
|
else
|
|
ctxt->accessname = NULL;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static void use_connection_set_accessnameA(struct use_connection_context *ctxt, WCHAR *local_name)
|
|
{
|
|
char *accessname = ctxt->accessname;
|
|
if (local_name)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0, local_name, -1, accessname, *ctxt->buffer_size, NULL, NULL);
|
|
if (ctxt->result)
|
|
*ctxt->result = CONNECT_LOCALDRIVE;
|
|
}
|
|
else
|
|
strcpy(accessname, ctxt->resourceA->lpRemoteName);
|
|
}
|
|
|
|
static LPWSTR strdupAtoW( LPCSTR str )
|
|
{
|
|
LPWSTR ret;
|
|
INT len;
|
|
|
|
if (!str) return NULL;
|
|
len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
|
|
ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
|
|
if (ret) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
|
|
return ret;
|
|
}
|
|
|
|
static void netresource_a_to_w( NETRESOURCEA *resourceA, NETRESOURCEW *resourceW )
|
|
{
|
|
resourceW->dwScope = resourceA->dwScope;
|
|
resourceW->dwType = resourceA->dwType;
|
|
resourceW->dwDisplayType = resourceA->dwDisplayType;
|
|
resourceW->dwUsage = resourceA->dwUsage;
|
|
resourceW->lpLocalName = strdupAtoW(resourceA->lpLocalName);
|
|
resourceW->lpRemoteName = strdupAtoW(resourceA->lpRemoteName);
|
|
resourceW->lpComment = strdupAtoW(resourceA->lpComment);
|
|
resourceW->lpProvider = strdupAtoW(resourceA->lpProvider);
|
|
}
|
|
|
|
static void free_netresourceW( NETRESOURCEW *resource )
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, resource->lpLocalName);
|
|
HeapFree(GetProcessHeap(), 0, resource->lpRemoteName);
|
|
HeapFree(GetProcessHeap(), 0, resource->lpComment);
|
|
HeapFree(GetProcessHeap(), 0, resource->lpProvider);
|
|
}
|
|
|
|
/*****************************************************************
|
|
* WNetUseConnectionA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetUseConnectionA( HWND hwndOwner, NETRESOURCEA *resource,
|
|
LPCSTR password, LPCSTR userid, DWORD flags, LPSTR accessname,
|
|
DWORD *buffer_size, DWORD *result )
|
|
{
|
|
struct use_connection_context ctxt;
|
|
NETRESOURCEW resourceW;
|
|
DWORD ret;
|
|
|
|
TRACE( "(%p, %p, %p, %s, 0x%08X, %p, %p, %p)\n", hwndOwner, resource, password, debugstr_a(userid), flags,
|
|
accessname, buffer_size, result );
|
|
|
|
netresource_a_to_w(resource, &resourceW);
|
|
|
|
ctxt.hwndOwner = hwndOwner;
|
|
ctxt.resource = &resourceW;
|
|
ctxt.resourceA = resource;
|
|
ctxt.password = strdupAtoW(password);
|
|
ctxt.userid = strdupAtoW(userid);
|
|
ctxt.flags = flags;
|
|
ctxt.accessname = accessname;
|
|
ctxt.buffer_size = buffer_size;
|
|
ctxt.result = result;
|
|
ctxt.pre_set_accessname = use_connection_pre_set_accessnameA;
|
|
ctxt.set_accessname = use_connection_set_accessnameA;
|
|
|
|
ret = wnet_use_connection(&ctxt);
|
|
|
|
free_netresourceW(&resourceW);
|
|
HeapFree(GetProcessHeap(), 0, ctxt.password);
|
|
HeapFree(GetProcessHeap(), 0, ctxt.userid);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetCancelConnectionA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetCancelConnectionA( LPCSTR lpName, BOOL fForce )
|
|
{
|
|
return WNetCancelConnection2A(lpName, 0, fForce);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetCancelConnectionW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetCancelConnectionW( LPCWSTR lpName, BOOL fForce )
|
|
{
|
|
return WNetCancelConnection2W(lpName, 0, fForce);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetCancelConnection2A [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetCancelConnection2A( LPCSTR lpName, DWORD dwFlags, BOOL fForce )
|
|
{
|
|
DWORD ret;
|
|
WCHAR * name = strdupAtoW(lpName);
|
|
if (!name)
|
|
return ERROR_NOT_CONNECTED;
|
|
|
|
ret = WNetCancelConnection2W(name, dwFlags, fForce);
|
|
HeapFree(GetProcessHeap(), 0, name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetCancelConnection2W [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetCancelConnection2W( LPCWSTR lpName, DWORD dwFlags, BOOL fForce )
|
|
{
|
|
DWORD ret = WN_NO_NETWORK;
|
|
DWORD index;
|
|
|
|
if (providerTable != NULL)
|
|
{
|
|
for (index = 0; index < providerTable->numProviders; index++)
|
|
{
|
|
if(providerTable->table[index].getCaps(WNNC_CONNECTION) &
|
|
WNNC_CON_CANCELCONNECTION)
|
|
{
|
|
if (providerTable->table[index].cancelConnection)
|
|
ret = providerTable->table[index].cancelConnection((LPWSTR)lpName, fForce);
|
|
else
|
|
ret = WN_NO_NETWORK;
|
|
if (ret == WN_SUCCESS || ret == WN_OPEN_FILES)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == WN_SUCCESS && dwFlags & CONNECT_UPDATE_PROFILE)
|
|
{
|
|
HKEY user_profile;
|
|
|
|
/* FIXME: Only remove it if that's a drive letter */
|
|
if (iswalpha(lpName[0]) && lpName[1] == ':' &&
|
|
RegOpenCurrentUser(KEY_ALL_ACCESS, &user_profile) == ERROR_SUCCESS)
|
|
{
|
|
WCHAR subkey[10] = {'N', 'e', 't', 'w', 'o', 'r', 'k', '\\', lpName[0], 0};
|
|
|
|
RegDeleteKeyW(user_profile, subkey);
|
|
|
|
RegCloseKey(user_profile);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* WNetRestoreConnectionA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetRestoreConnectionA( HWND hwndOwner, LPCSTR lpszDevice )
|
|
{
|
|
FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_a(lpszDevice) );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* WNetRestoreConnectionW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetRestoreConnectionW( HWND hwndOwner, LPCWSTR lpszDevice )
|
|
{
|
|
FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_w(lpszDevice) );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* WNetGetConnectionA [MPR.@]
|
|
*
|
|
* RETURNS
|
|
* - WN_BAD_LOCALNAME lpLocalName makes no sense
|
|
* - WN_NOT_CONNECTED drive is a local drive
|
|
* - WN_MORE_DATA buffer isn't big enough
|
|
* - WN_SUCCESS success (net path in buffer)
|
|
*
|
|
* FIXME: need to test return values under different errors
|
|
*/
|
|
DWORD WINAPI WNetGetConnectionA( LPCSTR lpLocalName,
|
|
LPSTR lpRemoteName, LPDWORD lpBufferSize )
|
|
{
|
|
DWORD ret;
|
|
|
|
if (!lpLocalName)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpBufferSize)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpRemoteName && *lpBufferSize)
|
|
ret = WN_BAD_POINTER;
|
|
else
|
|
{
|
|
int len = MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, NULL, 0);
|
|
|
|
if (len)
|
|
{
|
|
PWSTR wideLocalName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
|
|
if (wideLocalName)
|
|
{
|
|
WCHAR wideRemoteStatic[MAX_PATH];
|
|
DWORD wideRemoteSize = ARRAY_SIZE(wideRemoteStatic);
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, wideLocalName, len);
|
|
|
|
/* try once without memory allocation */
|
|
ret = WNetGetConnectionW(wideLocalName, wideRemoteStatic,
|
|
&wideRemoteSize);
|
|
if (ret == WN_SUCCESS)
|
|
{
|
|
int len = WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
|
|
-1, NULL, 0, NULL, NULL);
|
|
|
|
if (len <= *lpBufferSize)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic, -1,
|
|
lpRemoteName, *lpBufferSize, NULL, NULL);
|
|
ret = WN_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
*lpBufferSize = len;
|
|
ret = WN_MORE_DATA;
|
|
}
|
|
}
|
|
else if (ret == WN_MORE_DATA)
|
|
{
|
|
PWSTR wideRemote = HeapAlloc(GetProcessHeap(), 0,
|
|
wideRemoteSize * sizeof(WCHAR));
|
|
|
|
if (wideRemote)
|
|
{
|
|
ret = WNetGetConnectionW(wideLocalName, wideRemote,
|
|
&wideRemoteSize);
|
|
if (ret == WN_SUCCESS)
|
|
{
|
|
if (len <= *lpBufferSize)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
|
|
-1, lpRemoteName, *lpBufferSize, NULL, NULL);
|
|
ret = WN_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
*lpBufferSize = len;
|
|
ret = WN_MORE_DATA;
|
|
}
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, wideRemote);
|
|
}
|
|
else
|
|
ret = WN_OUT_OF_MEMORY;
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, wideLocalName);
|
|
}
|
|
else
|
|
ret = WN_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
ret = WN_BAD_LOCALNAME;
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* find the network connection for a given drive; helper for WNetGetConnection */
|
|
static DWORD get_drive_connection( WCHAR letter, LPWSTR remote, LPDWORD size )
|
|
{
|
|
char buffer[1024];
|
|
struct mountmgr_unix_drive *data = (struct mountmgr_unix_drive *)buffer;
|
|
HANDLE mgr;
|
|
DWORD ret = WN_NOT_CONNECTED;
|
|
DWORD bytes_returned;
|
|
|
|
if ((mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
0, 0 )) == INVALID_HANDLE_VALUE)
|
|
{
|
|
ERR( "failed to open mount manager err %u\n", GetLastError() );
|
|
return ret;
|
|
}
|
|
memset( data, 0, sizeof(*data) );
|
|
data->letter = letter;
|
|
if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, data, sizeof(*data),
|
|
data, sizeof(buffer), &bytes_returned, NULL ))
|
|
{
|
|
char *p, *mount_point = buffer + data->mount_point_offset;
|
|
DWORD len;
|
|
|
|
if (data->mount_point_offset && !strncmp( mount_point, "unc/", 4 ))
|
|
{
|
|
mount_point += 2;
|
|
mount_point[0] = '\\';
|
|
for (p = mount_point; *p; p++) if (*p == '/') *p = '\\';
|
|
|
|
len = MultiByteToWideChar( CP_UNIXCP, 0, mount_point, -1, NULL, 0 );
|
|
if (len > *size)
|
|
{
|
|
*size = len;
|
|
ret = WN_MORE_DATA;
|
|
}
|
|
else
|
|
{
|
|
*size = MultiByteToWideChar( CP_UNIXCP, 0, mount_point, -1, remote, *size);
|
|
ret = WN_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
CloseHandle( mgr );
|
|
return ret;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* WNetGetConnectionW [MPR.@]
|
|
*
|
|
* FIXME: need to test return values under different errors
|
|
*/
|
|
DWORD WINAPI WNetGetConnectionW( LPCWSTR lpLocalName,
|
|
LPWSTR lpRemoteName, LPDWORD lpBufferSize )
|
|
{
|
|
DWORD ret;
|
|
|
|
TRACE("(%s, %p, %p)\n", debugstr_w(lpLocalName), lpRemoteName,
|
|
lpBufferSize);
|
|
|
|
if (!lpLocalName)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpBufferSize)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpRemoteName && *lpBufferSize)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpLocalName[0])
|
|
ret = WN_BAD_LOCALNAME;
|
|
else
|
|
{
|
|
if (lpLocalName[1] == ':')
|
|
{
|
|
switch(GetDriveTypeW(lpLocalName))
|
|
{
|
|
case DRIVE_REMOTE:
|
|
ret = get_drive_connection( lpLocalName[0], lpRemoteName, lpBufferSize );
|
|
break;
|
|
case DRIVE_REMOVABLE:
|
|
case DRIVE_FIXED:
|
|
case DRIVE_CDROM:
|
|
TRACE("file is local\n");
|
|
ret = WN_NOT_CONNECTED;
|
|
break;
|
|
default:
|
|
ret = WN_BAD_LOCALNAME;
|
|
}
|
|
}
|
|
else
|
|
ret = WN_BAD_LOCALNAME;
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* WNetSetConnectionA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetSetConnectionA( LPCSTR lpName, DWORD dwProperty,
|
|
LPVOID pvValue )
|
|
{
|
|
FIXME( "(%s, %08X, %p): stub\n", debugstr_a(lpName), dwProperty, pvValue );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* WNetSetConnectionW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetSetConnectionW( LPCWSTR lpName, DWORD dwProperty,
|
|
LPVOID pvValue )
|
|
{
|
|
FIXME( "(%s, %08X, %p): stub\n", debugstr_w(lpName), dwProperty, pvValue );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* WNetGetUniversalNameA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetGetUniversalNameA ( LPCSTR lpLocalPath, DWORD dwInfoLevel,
|
|
LPVOID lpBuffer, LPDWORD lpBufferSize )
|
|
{
|
|
DWORD err, size;
|
|
|
|
FIXME( "(%s, 0x%08X, %p, %p): stub\n",
|
|
debugstr_a(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
|
|
|
|
switch (dwInfoLevel)
|
|
{
|
|
case UNIVERSAL_NAME_INFO_LEVEL:
|
|
{
|
|
LPUNIVERSAL_NAME_INFOA info = lpBuffer;
|
|
|
|
if (GetDriveTypeA(lpLocalPath) != DRIVE_REMOTE)
|
|
{
|
|
err = ERROR_NOT_CONNECTED;
|
|
break;
|
|
}
|
|
|
|
size = sizeof(*info) + lstrlenA(lpLocalPath) + 1;
|
|
if (*lpBufferSize < size)
|
|
{
|
|
err = WN_MORE_DATA;
|
|
break;
|
|
}
|
|
info->lpUniversalName = (char *)info + sizeof(*info);
|
|
lstrcpyA(info->lpUniversalName, lpLocalPath);
|
|
err = WN_NO_ERROR;
|
|
break;
|
|
}
|
|
case REMOTE_NAME_INFO_LEVEL:
|
|
err = WN_NOT_CONNECTED;
|
|
break;
|
|
|
|
default:
|
|
err = WN_BAD_VALUE;
|
|
break;
|
|
}
|
|
|
|
SetLastError(err);
|
|
return err;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* WNetGetUniversalNameW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetGetUniversalNameW ( LPCWSTR lpLocalPath, DWORD dwInfoLevel,
|
|
LPVOID lpBuffer, LPDWORD lpBufferSize )
|
|
{
|
|
DWORD err, size;
|
|
|
|
FIXME( "(%s, 0x%08X, %p, %p): stub\n",
|
|
debugstr_w(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
|
|
|
|
switch (dwInfoLevel)
|
|
{
|
|
case UNIVERSAL_NAME_INFO_LEVEL:
|
|
{
|
|
LPUNIVERSAL_NAME_INFOW info = lpBuffer;
|
|
|
|
if (GetDriveTypeW(lpLocalPath) != DRIVE_REMOTE)
|
|
{
|
|
err = ERROR_NOT_CONNECTED;
|
|
break;
|
|
}
|
|
|
|
size = sizeof(*info) + (lstrlenW(lpLocalPath) + 1) * sizeof(WCHAR);
|
|
if (*lpBufferSize < size)
|
|
{
|
|
*lpBufferSize = size;
|
|
err = WN_MORE_DATA;
|
|
break;
|
|
}
|
|
info->lpUniversalName = (LPWSTR)((char *)info + sizeof(*info));
|
|
lstrcpyW(info->lpUniversalName, lpLocalPath);
|
|
err = WN_NO_ERROR;
|
|
break;
|
|
}
|
|
case REMOTE_NAME_INFO_LEVEL:
|
|
err = WN_NO_NETWORK;
|
|
break;
|
|
|
|
default:
|
|
err = WN_BAD_VALUE;
|
|
break;
|
|
}
|
|
|
|
if (err != WN_NO_ERROR) SetLastError(err);
|
|
return err;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* WNetClearConnections [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetClearConnections ( HWND owner )
|
|
{
|
|
HANDLE connected;
|
|
PWSTR connection;
|
|
DWORD ret, size, count;
|
|
NETRESOURCEW * resources, * iter;
|
|
|
|
ret = WNetOpenEnumW(RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL, &connected);
|
|
if (ret != WN_SUCCESS)
|
|
{
|
|
if (ret != WN_NO_NETWORK)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Means no provider, then, clearing is OK */
|
|
return WN_SUCCESS;
|
|
}
|
|
|
|
size = 0x1000;
|
|
resources = HeapAlloc(GetProcessHeap(), 0, size);
|
|
if (!resources)
|
|
{
|
|
WNetCloseEnum(connected);
|
|
return WN_OUT_OF_MEMORY;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
size = 0x1000;
|
|
count = -1;
|
|
|
|
memset(resources, 0, size);
|
|
ret = WNetEnumResourceW(connected, &count, resources, &size);
|
|
if (ret == WN_SUCCESS || ret == WN_MORE_DATA)
|
|
{
|
|
for (iter = resources; count; count--, iter++)
|
|
{
|
|
if (iter->lpLocalName && iter->lpLocalName[0])
|
|
connection = iter->lpLocalName;
|
|
else
|
|
connection = iter->lpRemoteName;
|
|
|
|
WNetCancelConnection2W(connection, 0, TRUE);
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, resources);
|
|
WNetCloseEnum(connected);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Other Functions
|
|
*/
|
|
|
|
/**************************************************************************
|
|
* WNetGetUserA [MPR.@]
|
|
*
|
|
* FIXME: we should not return ourselves, but the owner of the drive lpName
|
|
*/
|
|
DWORD WINAPI WNetGetUserA( LPCSTR lpName, LPSTR lpUserID, LPDWORD lpBufferSize )
|
|
{
|
|
if (GetUserNameA( lpUserID, lpBufferSize )) return WN_SUCCESS;
|
|
return GetLastError();
|
|
}
|
|
|
|
/*****************************************************************
|
|
* WNetGetUserW [MPR.@]
|
|
*
|
|
* FIXME: we should not return ourselves, but the owner of the drive lpName
|
|
*/
|
|
DWORD WINAPI WNetGetUserW( LPCWSTR lpName, LPWSTR lpUserID, LPDWORD lpBufferSize )
|
|
{
|
|
if (GetUserNameW( lpUserID, lpBufferSize )) return WN_SUCCESS;
|
|
return GetLastError();
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetConnectionDialog [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetConnectionDialog( HWND hwnd, DWORD dwType )
|
|
{
|
|
CONNECTDLGSTRUCTW conn_dlg;
|
|
NETRESOURCEW net_res;
|
|
|
|
ZeroMemory(&conn_dlg, sizeof(conn_dlg));
|
|
ZeroMemory(&net_res, sizeof(net_res));
|
|
|
|
conn_dlg.cbStructure = sizeof(conn_dlg);
|
|
conn_dlg.lpConnRes = &net_res;
|
|
conn_dlg.hwndOwner = hwnd;
|
|
net_res.dwType = dwType;
|
|
|
|
return WNetConnectionDialog1W(&conn_dlg);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetConnectionDialog1A [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetConnectionDialog1A( LPCONNECTDLGSTRUCTA lpConnDlgStruct )
|
|
{
|
|
FIXME( "(%p): stub\n", lpConnDlgStruct );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetConnectionDialog1W [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetConnectionDialog1W( LPCONNECTDLGSTRUCTW lpConnDlgStruct )
|
|
{
|
|
FIXME( "(%p): stub\n", lpConnDlgStruct );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetDisconnectDialog [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetDisconnectDialog( HWND hwnd, DWORD dwType )
|
|
{
|
|
FIXME( "(%p, %08X): stub\n", hwnd, dwType );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetDisconnectDialog1A [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetDisconnectDialog1A( LPDISCDLGSTRUCTA lpConnDlgStruct )
|
|
{
|
|
FIXME( "(%p): stub\n", lpConnDlgStruct );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetDisconnectDialog1W [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetDisconnectDialog1W( LPDISCDLGSTRUCTW lpConnDlgStruct )
|
|
{
|
|
FIXME( "(%p): stub\n", lpConnDlgStruct );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetGetLastErrorA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetGetLastErrorA( LPDWORD lpError,
|
|
LPSTR lpErrorBuf, DWORD nErrorBufSize,
|
|
LPSTR lpNameBuf, DWORD nNameBufSize )
|
|
{
|
|
FIXME( "(%p, %p, %d, %p, %d): stub\n",
|
|
lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetGetLastErrorW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetGetLastErrorW( LPDWORD lpError,
|
|
LPWSTR lpErrorBuf, DWORD nErrorBufSize,
|
|
LPWSTR lpNameBuf, DWORD nNameBufSize )
|
|
{
|
|
FIXME( "(%p, %p, %d, %p, %d): stub\n",
|
|
lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
|
|
|
|
SetLastError(WN_NO_NETWORK);
|
|
return WN_NO_NETWORK;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetGetNetworkInformationA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetGetNetworkInformationA( LPCSTR lpProvider,
|
|
LPNETINFOSTRUCT lpNetInfoStruct )
|
|
{
|
|
DWORD ret;
|
|
|
|
TRACE( "(%s, %p)\n", debugstr_a(lpProvider), lpNetInfoStruct );
|
|
|
|
if (!lpProvider)
|
|
ret = WN_BAD_POINTER;
|
|
else
|
|
{
|
|
int len;
|
|
|
|
len = MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, NULL, 0);
|
|
if (len)
|
|
{
|
|
LPWSTR wideProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
|
|
if (wideProvider)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, wideProvider,
|
|
len);
|
|
ret = WNetGetNetworkInformationW(wideProvider, lpNetInfoStruct);
|
|
HeapFree(GetProcessHeap(), 0, wideProvider);
|
|
}
|
|
else
|
|
ret = WN_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
ret = GetLastError();
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WNetGetNetworkInformationW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetGetNetworkInformationW( LPCWSTR lpProvider,
|
|
LPNETINFOSTRUCT lpNetInfoStruct )
|
|
{
|
|
DWORD ret;
|
|
|
|
TRACE( "(%s, %p)\n", debugstr_w(lpProvider), lpNetInfoStruct );
|
|
|
|
if (!lpProvider)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpNetInfoStruct)
|
|
ret = WN_BAD_POINTER;
|
|
else if (lpNetInfoStruct->cbStructure < sizeof(NETINFOSTRUCT))
|
|
ret = WN_BAD_VALUE;
|
|
else
|
|
{
|
|
if (providerTable && providerTable->numProviders)
|
|
{
|
|
DWORD providerIndex = _findProviderIndexW(lpProvider);
|
|
|
|
if (providerIndex != BAD_PROVIDER_INDEX)
|
|
{
|
|
lpNetInfoStruct->cbStructure = sizeof(NETINFOSTRUCT);
|
|
lpNetInfoStruct->dwProviderVersion =
|
|
providerTable->table[providerIndex].dwSpecVersion;
|
|
lpNetInfoStruct->dwStatus = NO_ERROR;
|
|
lpNetInfoStruct->dwCharacteristics = 0;
|
|
lpNetInfoStruct->dwHandle = 0;
|
|
lpNetInfoStruct->wNetType =
|
|
HIWORD(providerTable->table[providerIndex].dwNetType);
|
|
lpNetInfoStruct->dwPrinters = -1;
|
|
lpNetInfoStruct->dwDrives = -1;
|
|
ret = WN_SUCCESS;
|
|
}
|
|
else
|
|
ret = WN_BAD_PROVIDER;
|
|
}
|
|
else
|
|
ret = WN_NO_NETWORK;
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* WNetGetProviderNameA [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetGetProviderNameA( DWORD dwNetType,
|
|
LPSTR lpProvider, LPDWORD lpBufferSize )
|
|
{
|
|
DWORD ret;
|
|
|
|
TRACE("(0x%08x, %s, %p)\n", dwNetType, debugstr_a(lpProvider),
|
|
lpBufferSize);
|
|
|
|
if (!lpProvider)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpBufferSize)
|
|
ret = WN_BAD_POINTER;
|
|
else
|
|
{
|
|
if (providerTable)
|
|
{
|
|
DWORD i;
|
|
|
|
ret = WN_NO_NETWORK;
|
|
for (i = 0; i < providerTable->numProviders &&
|
|
HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
|
|
i++)
|
|
;
|
|
if (i < providerTable->numProviders)
|
|
{
|
|
DWORD sizeNeeded = WideCharToMultiByte(CP_ACP, 0,
|
|
providerTable->table[i].name, -1, NULL, 0, NULL, NULL);
|
|
|
|
if (*lpBufferSize < sizeNeeded)
|
|
{
|
|
*lpBufferSize = sizeNeeded;
|
|
ret = WN_MORE_DATA;
|
|
}
|
|
else
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0, providerTable->table[i].name,
|
|
-1, lpProvider, *lpBufferSize, NULL, NULL);
|
|
ret = WN_SUCCESS;
|
|
/* FIXME: is *lpBufferSize set to the number of characters
|
|
* copied? */
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ret = WN_NO_NETWORK;
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* WNetGetProviderNameW [MPR.@]
|
|
*/
|
|
DWORD WINAPI WNetGetProviderNameW( DWORD dwNetType,
|
|
LPWSTR lpProvider, LPDWORD lpBufferSize )
|
|
{
|
|
DWORD ret;
|
|
|
|
TRACE("(0x%08x, %s, %p)\n", dwNetType, debugstr_w(lpProvider),
|
|
lpBufferSize);
|
|
|
|
if (!lpProvider)
|
|
ret = WN_BAD_POINTER;
|
|
else if (!lpBufferSize)
|
|
ret = WN_BAD_POINTER;
|
|
else
|
|
{
|
|
if (providerTable)
|
|
{
|
|
DWORD i;
|
|
|
|
ret = WN_NO_NETWORK;
|
|
for (i = 0; i < providerTable->numProviders &&
|
|
HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
|
|
i++)
|
|
;
|
|
if (i < providerTable->numProviders)
|
|
{
|
|
DWORD sizeNeeded = lstrlenW(providerTable->table[i].name) + 1;
|
|
|
|
if (*lpBufferSize < sizeNeeded)
|
|
{
|
|
*lpBufferSize = sizeNeeded;
|
|
ret = WN_MORE_DATA;
|
|
}
|
|
else
|
|
{
|
|
lstrcpyW(lpProvider, providerTable->table[i].name);
|
|
ret = WN_SUCCESS;
|
|
/* FIXME: is *lpBufferSize set to the number of characters
|
|
* copied? */
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ret = WN_NO_NETWORK;
|
|
}
|
|
if (ret)
|
|
SetLastError(ret);
|
|
TRACE("Returning %d\n", ret);
|
|
return ret;
|
|
}
|