| /* | |
| * MPR WNet functions | |
| * | |
| * Copyright 1999 Ulrich Weigand | |
| * Copyright 2004 Juan Lang | |
| * | |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winnls.h" | |
| #include "winnetwk.h" | |
| #include "npapi.h" | |
| #include "winreg.h" | |
| #include "winuser.h" | |
| #include "wine/debug.h" | |
| #include "wine/unicode.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; | |
| } WNetProvider, *PWNetProvider; | |
| typedef struct _WNetProviderTable | |
| { | |
| LPWSTR entireNetwork; | |
| DWORD numAllocated; | |
| DWORD numProviders; | |
| WNetProvider table[1]; | |
| } WNetProviderTable, *PWNetProviderTable; | |
| #define WNET_ENUMERATOR_TYPE_NULL 0 | |
| #define WNET_ENUMERATOR_TYPE_GLOBAL 1 | |
| #define WNET_ENUMERATOR_TYPE_PROVIDER 2 | |
| #define WNET_ENUMERATOR_TYPE_CONTEXT 3 | |
| /* An WNet enumerator. Note that the type doesn't correspond to the scope of | |
| * the enumeration; it represents one of the following types: | |
| * - a 'null' enumeration, one that contains no members | |
| * - 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). | |
| */ | |
| typedef struct _WNetEnumerator | |
| { | |
| DWORD enumType; | |
| DWORD providerIndex; | |
| HANDLE handle; | |
| BOOL providerDone; | |
| DWORD dwScope; | |
| DWORD dwType; | |
| DWORD dwUsage; | |
| LPNETRESOURCEW lpNet; | |
| } 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); | |
| 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)); | |
| snprintfW(serviceName, sizeof(serviceName) / sizeof(WCHAR), serviceFmt, | |
| servicePrefix, provider); | |
| serviceName[sizeof(serviceName) / sizeof(WCHAR) - 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) | |
| { | |
| static const WCHAR szProviderName[] = { 'N','a','m','e',0 }; | |
| PWSTR name = NULL; | |
| size = 0; | |
| RegQueryValueExW(hKey, szProviderName, NULL, NULL, NULL, &size); | |
| if (size) | |
| { | |
| name = (PWSTR)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) | |
| { | |
| PF_NPGetCaps getCaps = (PF_NPGetCaps)GetProcAddress(hLib, | |
| "NPGetCaps"); | |
| TRACE("loaded lib %p\n", hLib); | |
| if (getCaps) | |
| { | |
| 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%08lx\n", provider->dwNetType); | |
| provider->dwEnumScopes = getCaps(WNNC_ENUMERATION); | |
| if (provider->dwEnumScopes) | |
| { | |
| TRACE("supports enumeration\n"); | |
| provider->openEnum = (PF_NPOpenEnum) | |
| GetProcAddress(hLib, "NPOpenEnum"); | |
| TRACE("openEnum is %p\n", provider->openEnum); | |
| provider->enumResource = (PF_NPEnumResource) | |
| GetProcAddress(hLib, "NPEnumResource"); | |
| TRACE("enumResource is %p\n", | |
| provider->enumResource); | |
| provider->closeEnum = (PF_NPCloseEnum) | |
| GetProcAddress(hLib, "NPCloseEnum"); | |
| TRACE("closeEnum is %p\n", provider->closeEnum); | |
| 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"); | |
| } | |
| } | |
| providerTable->numProviders++; | |
| } | |
| else | |
| { | |
| WARN("Provider %s didn't export NPGetCaps\n", | |
| debugstr_w(provider)); | |
| HeapFree(GetProcessHeap(), 0, name); | |
| FreeLibrary(hLib); | |
| } | |
| } | |
| 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 = (PWSTR)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 = strchrW(ptr, ','); | |
| if (ptr) | |
| numToAllocate++; | |
| } | |
| providerTable = (PWNetProviderTable) | |
| HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | |
| sizeof(WNetProviderTable) | |
| + (numToAllocate - 1) * sizeof(WNetProvider)); | |
| if (providerTable) | |
| { | |
| PWSTR ptrPrev; | |
| int entireNetworkLen; | |
| entireNetworkLen = LoadStringW(hInstDll, | |
| IDS_ENTIRENETWORK, NULL, 0); | |
| providerTable->entireNetwork = (LPWSTR)HeapAlloc( | |
| GetProcessHeap(), 0, (entireNetworkLen + 1) * | |
| sizeof(WCHAR)); | |
| if (providerTable->entireNetwork) | |
| LoadStringW(hInstDll, IDS_ENTIRENETWORK, | |
| providerTable->entireNetwork, | |
| entireNetworkLen + 1); | |
| providerTable->numAllocated = numToAllocate; | |
| for (ptr = providers; ptr; ) | |
| { | |
| ptrPrev = ptr; | |
| ptr = strchrW(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 (!strcmpW(lpProvider, providerTable->table[i].name)) | |
| ret = i; | |
| } | |
| return ret; | |
| } | |
| /* | |
| * Browsing Functions | |
| */ | |
| static LPNETRESOURCEW _copyNetResourceForEnumW(LPNETRESOURCEW lpNet) | |
| { | |
| LPNETRESOURCEW ret; | |
| if (lpNet) | |
| { | |
| ret = (LPNETRESOURCEW)HeapAlloc(GetProcessHeap(), 0, | |
| sizeof(NETRESOURCEW)); | |
| if (ret) | |
| { | |
| size_t len; | |
| memcpy(ret, lpNet, sizeof(ret)); | |
| ret->lpLocalName = ret->lpComment = ret->lpProvider = NULL; | |
| if (lpNet->lpRemoteName) | |
| { | |
| len = strlenW(lpNet->lpRemoteName) + 1; | |
| ret->lpRemoteName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, | |
| len * sizeof(WCHAR)); | |
| if (ret->lpRemoteName) | |
| strcpyW(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 _createNullEnumerator(void) | |
| { | |
| PWNetEnumerator ret = (PWNetEnumerator)HeapAlloc(GetProcessHeap(), | |
| HEAP_ZERO_MEMORY, sizeof(WNetEnumerator)); | |
| if (ret) | |
| ret->enumType = WNET_ENUMERATOR_TYPE_NULL; | |
| return ret; | |
| } | |
| static PWNetEnumerator _createGlobalEnumeratorW(DWORD dwScope, DWORD dwType, | |
| DWORD dwUsage, LPNETRESOURCEW lpNet) | |
| { | |
| PWNetEnumerator ret = (PWNetEnumerator)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->lpNet = _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 = (PWNetEnumerator)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 = (PWNetEnumerator)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; | |
| } | |
| /* 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 LPNETRESOURCEW lpNetArrayIn, | |
| LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD 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++) | |
| { | |
| LPNETRESOURCEW 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; | |
| LPNETRESOURCEW 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 %ld, *lpcCount is %ld, returning %ld\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 LPNETRESOURCEA lpNetArrayIn, | |
| LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD 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++) | |
| { | |
| LPNETRESOURCEA 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; | |
| LPNETRESOURCEA 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 %ld, *lpcCount is %ld, returning %ld\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( "(%08lX, %08lX, %08lX, %p, %p)\n", | |
| dwScope, dwType, dwUsage, lpNet, lphEnum ); | |
| if (!lphEnum) | |
| ret = WN_BAD_POINTER; | |
| else if (!providerTable || providerTable->numProviders == 0) | |
| 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 = (LPNETRESOURCEW)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 && lpNetWide) | |
| HeapFree(GetProcessHeap(), 0, lpNetWide); | |
| } | |
| else | |
| ret = WNetOpenEnumW(dwScope, dwType, dwUsage, NULL, lphEnum); | |
| } | |
| if (ret) | |
| SetLastError(ret); | |
| TRACE("Returning %ld\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( "(%08lX, %08lX, %08lX, %p, %p)\n", | |
| dwScope, dwType, dwUsage, lpNet, lphEnum ); | |
| if (!lphEnum) | |
| ret = WN_BAD_POINTER; | |
| else if (!providerTable || providerTable->numProviders == 0) | |
| 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 & dwScope) | |
| { | |
| HANDLE handle; | |
| ret = providerTable->table[index].openEnum( | |
| dwScope, dwType, dwUsage, lpNet, &handle); | |
| if (ret == WN_SUCCESS) | |
| { | |
| *lphEnum = | |
| (HANDLE)_createProviderEnumerator( | |
| dwScope, dwType, dwUsage, index, handle); | |
| ret = *lphEnum ? WN_SUCCESS : | |
| WN_OUT_OF_MEMORY; | |
| } | |
| } | |
| else | |
| ret = WN_NOT_SUPPORTED; | |
| } | |
| else | |
| ret = WN_BAD_PROVIDER; | |
| } | |
| else if (lpNet->lpRemoteName) | |
| { | |
| *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, | |
| dwType, dwUsage, lpNet); | |
| ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY; | |
| } | |
| else | |
| { | |
| if (lpNet->lpComment && !strcmpW(lpNet->lpComment, | |
| providerTable->entireNetwork)) | |
| { | |
| /* comment matches the "Entire Network", enumerate | |
| * global scope of every provider | |
| */ | |
| *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, | |
| dwType, dwUsage, lpNet); | |
| } | |
| else | |
| { | |
| /* this is the same as not having passed lpNet */ | |
| *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, | |
| dwType, dwUsage, NULL); | |
| } | |
| ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY; | |
| } | |
| } | |
| else | |
| { | |
| *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, dwType, | |
| dwUsage, lpNet); | |
| ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY; | |
| } | |
| break; | |
| case RESOURCE_CONTEXT: | |
| *lphEnum = (HANDLE)_createContextEnumerator(dwScope, dwType, | |
| dwUsage); | |
| ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY; | |
| break; | |
| case RESOURCE_REMEMBERED: | |
| case RESOURCE_CONNECTED: | |
| *lphEnum = (HANDLE)_createNullEnumerator(); | |
| ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY; | |
| break; | |
| default: | |
| WARN("unknown scope 0x%08lx\n", dwScope); | |
| ret = WN_BAD_VALUE; | |
| } | |
| } | |
| if (ret) | |
| SetLastError(ret); | |
| TRACE("Returning %ld\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; | |
| 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((LPNETRESOURCEW)localBuffer, | |
| &localCount, lpBuffer, lpBufferSize); | |
| *lpcCount = localCount; | |
| } | |
| HeapFree(GetProcessHeap(), 0, localBuffer); | |
| } | |
| else | |
| ret = WN_OUT_OF_MEMORY; | |
| } | |
| if (ret) | |
| SetLastError(ret); | |
| TRACE("Returning %ld\n", ret); | |
| return ret; | |
| } | |
| static DWORD _countProviderBytesW(PWNetProvider provider) | |
| { | |
| DWORD ret; | |
| if (provider) | |
| { | |
| ret = sizeof(NETRESOURCEW); | |
| ret += 2 * (strlenW(provider->name) + 1) * sizeof(WCHAR); | |
| } | |
| else | |
| ret = 0; | |
| return ret; | |
| } | |
| static DWORD _enumerateProvidersW(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(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 = (LPNETRESOURCEW)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; | |
| strcpyW(resource->lpRemoteName, | |
| providerTable->table[i + enumerator->providerIndex].name); | |
| strNext += strlenW(resource->lpRemoteName) + 1; | |
| resource->lpComment = NULL; | |
| resource->lpProvider = strNext; | |
| strcpyW(resource->lpProvider, | |
| providerTable->table[i + enumerator->providerIndex].name); | |
| strNext += strlenW(resource->lpProvider) + 1; | |
| } | |
| enumerator->providerIndex += count; | |
| *lpcCount = count; | |
| ret = count > 0 ? WN_SUCCESS : WN_MORE_DATA; | |
| } | |
| TRACE("Returning %ld\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) | |
| { | |
| enumerator->providerDone = FALSE; | |
| if (enumerator->handle) | |
| { | |
| providerTable->table[enumerator->providerIndex].closeEnum( | |
| enumerator->handle); | |
| enumerator->handle = NULL; | |
| enumerator->providerIndex++; | |
| } | |
| for (; enumerator->providerIndex < providerTable->numProviders && | |
| !(enumerator->dwScope & providerTable->table | |
| [enumerator->providerIndex].dwEnumScopes); | |
| 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->lpNet, | |
| &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 %ld\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->lpNet) | |
| 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%08lx\n", enumerator->dwScope); | |
| ret = WN_NO_MORE_ENTRIES; | |
| } | |
| TRACE("Returning %ld\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 = strlenW(providerTable->entireNetwork) + 1; | |
| bytesNeeded = sizeof(NETRESOURCEW) + cchEntireNetworkLen * sizeof(WCHAR); | |
| if (*lpBufferSize < bytesNeeded) | |
| { | |
| *lpBufferSize = bytesNeeded; | |
| ret = WN_MORE_DATA; | |
| } | |
| else | |
| { | |
| LPNETRESOURCEW lpNet = (LPNETRESOURCEW)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))); | |
| strcpyW(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 %ld\n", ret); | |
| 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_NULL: | |
| ret = WN_NO_MORE_ENTRIES; | |
| break; | |
| 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; | |
| default: | |
| WARN("bogus enumerator type!\n"); | |
| ret = WN_NO_NETWORK; | |
| } | |
| } | |
| if (ret) | |
| SetLastError(ret); | |
| TRACE("Returning %ld\n", ret); | |
| return ret; | |
| } | |
| /********************************************************************* | |
| * WNetCloseEnum [MPR.@] | |
| */ | |
| DWORD WINAPI WNetCloseEnum( HANDLE hEnum ) | |
| { | |
| DWORD ret; | |
| TRACE( "(%p)\n", hEnum ); | |
| if (hEnum) | |
| { | |
| PWNetEnumerator enumerator = (PWNetEnumerator)hEnum; | |
| switch (enumerator->enumType) | |
| { | |
| case WNET_ENUMERATOR_TYPE_NULL: | |
| ret = WN_SUCCESS; | |
| break; | |
| case WNET_ENUMERATOR_TYPE_GLOBAL: | |
| if (enumerator->lpNet) | |
| _freeEnumNetResource(enumerator->lpNet); | |
| 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; | |
| 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 %ld\n", ret); | |
| return ret; | |
| } | |
| /********************************************************************* | |
| * WNetGetResourceInformationA [MPR.@] | |
| */ | |
| DWORD WINAPI WNetGetResourceInformationA( LPNETRESOURCEA lpNetResource, | |
| LPVOID lpBuffer, LPDWORD cbBuffer, | |
| LPSTR *lplpSystem ) | |
| { | |
| FIXME( "(%p, %p, %p, %p): stub\n", | |
| lpNetResource, lpBuffer, cbBuffer, lplpSystem ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /********************************************************************* | |
| * WNetGetResourceInformationW [MPR.@] | |
| */ | |
| DWORD WINAPI WNetGetResourceInformationW( LPNETRESOURCEW lpNetResource, | |
| LPVOID lpBuffer, LPDWORD cbBuffer, | |
| LPWSTR *lplpSystem ) | |
| { | |
| FIXME( "(%p, %p, %p, %p): stub\n", | |
| lpNetResource, lpBuffer, cbBuffer, lplpSystem ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /********************************************************************* | |
| * 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 ) | |
| { | |
| FIXME( "(%s, %p, %s): stub\n", | |
| debugstr_a(lpRemoteName), lpPassword, debugstr_a(lpLocalName) ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /********************************************************************* | |
| * WNetAddConnectionW [MPR.@] | |
| */ | |
| DWORD WINAPI WNetAddConnectionW( LPCWSTR lpRemoteName, LPCWSTR lpPassword, | |
| LPCWSTR lpLocalName ) | |
| { | |
| FIXME( "(%s, %p, %s): stub\n", | |
| debugstr_w(lpRemoteName), lpPassword, debugstr_w(lpLocalName) ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /********************************************************************* | |
| * WNetAddConnection2A [MPR.@] | |
| */ | |
| DWORD WINAPI WNetAddConnection2A( LPNETRESOURCEA lpNetResource, | |
| LPCSTR lpPassword, LPCSTR lpUserID, | |
| DWORD dwFlags ) | |
| { | |
| FIXME( "(%p, %p, %s, 0x%08lX): stub\n", | |
| lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /********************************************************************* | |
| * WNetAddConnection2W [MPR.@] | |
| */ | |
| DWORD WINAPI WNetAddConnection2W( LPNETRESOURCEW lpNetResource, | |
| LPCWSTR lpPassword, LPCWSTR lpUserID, | |
| DWORD dwFlags ) | |
| { | |
| FIXME( "(%p, %p, %s, 0x%08lX): stub\n", | |
| lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /********************************************************************* | |
| * WNetAddConnection3A [MPR.@] | |
| */ | |
| DWORD WINAPI WNetAddConnection3A( HWND hwndOwner, LPNETRESOURCEA lpNetResource, | |
| LPCSTR lpPassword, LPCSTR lpUserID, | |
| DWORD dwFlags ) | |
| { | |
| FIXME( "(%p, %p, %p, %s, 0x%08lX), stub\n", | |
| hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /********************************************************************* | |
| * WNetAddConnection3W [MPR.@] | |
| */ | |
| DWORD WINAPI WNetAddConnection3W( HWND hwndOwner, LPNETRESOURCEW lpNetResource, | |
| LPCWSTR lpPassword, LPCWSTR lpUserID, | |
| DWORD dwFlags ) | |
| { | |
| FIXME( "(%p, %p, %p, %s, 0x%08lX), stub\n", | |
| hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /***************************************************************** | |
| * WNetUseConnectionA [MPR.@] | |
| */ | |
| DWORD WINAPI WNetUseConnectionA( HWND hwndOwner, LPNETRESOURCEA lpNetResource, | |
| LPCSTR lpPassword, LPCSTR lpUserID, DWORD dwFlags, | |
| LPSTR lpAccessName, LPDWORD lpBufferSize, | |
| LPDWORD lpResult ) | |
| { | |
| FIXME( "(%p, %p, %p, %s, 0x%08lX, %s, %p, %p), stub\n", | |
| hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags, | |
| debugstr_a(lpAccessName), lpBufferSize, lpResult ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /***************************************************************** | |
| * WNetUseConnectionW [MPR.@] | |
| */ | |
| DWORD WINAPI WNetUseConnectionW( HWND hwndOwner, LPNETRESOURCEW lpNetResource, | |
| LPCWSTR lpPassword, LPCWSTR lpUserID, DWORD dwFlags, | |
| LPWSTR lpAccessName, LPDWORD lpBufferSize, | |
| LPDWORD lpResult ) | |
| { | |
| FIXME( "(%p, %p, %p, %s, 0x%08lX, %s, %p, %p), stub\n", | |
| hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags, | |
| debugstr_w(lpAccessName), lpBufferSize, lpResult ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /********************************************************************* | |
| * WNetCancelConnectionA [MPR.@] | |
| */ | |
| DWORD WINAPI WNetCancelConnectionA( LPCSTR lpName, BOOL fForce ) | |
| { | |
| FIXME( "(%s, %d), stub\n", debugstr_a(lpName), fForce ); | |
| return WN_SUCCESS; | |
| } | |
| /********************************************************************* | |
| * WNetCancelConnectionW [MPR.@] | |
| */ | |
| DWORD WINAPI WNetCancelConnectionW( LPCWSTR lpName, BOOL fForce ) | |
| { | |
| FIXME( "(%s, %d), stub\n", debugstr_w(lpName), fForce ); | |
| return WN_SUCCESS; | |
| } | |
| /********************************************************************* | |
| * WNetCancelConnection2A [MPR.@] | |
| */ | |
| DWORD WINAPI WNetCancelConnection2A( LPCSTR lpName, DWORD dwFlags, BOOL fForce ) | |
| { | |
| FIXME( "(%s, %08lX, %d), stub\n", debugstr_a(lpName), dwFlags, fForce ); | |
| return WN_SUCCESS; | |
| } | |
| /********************************************************************* | |
| * WNetCancelConnection2W [MPR.@] | |
| */ | |
| DWORD WINAPI WNetCancelConnection2W( LPCWSTR lpName, DWORD dwFlags, BOOL fForce ) | |
| { | |
| FIXME( "(%s, %08lX, %d), stub\n", debugstr_w(lpName), dwFlags, fForce ); | |
| return WN_SUCCESS; | |
| } | |
| /***************************************************************** | |
| * WNetRestoreConnectionA [MPR.@] | |
| */ | |
| DWORD WINAPI WNetRestoreConnectionA( HWND hwndOwner, LPSTR 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, LPWSTR 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 (!lpRemoteName) | |
| ret = WN_BAD_POINTER; | |
| else if (!lpBufferSize) | |
| ret = WN_BAD_POINTER; | |
| else | |
| { | |
| int len = MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, NULL, 0); | |
| if (len) | |
| { | |
| PWSTR wideLocalName = (PWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| if (wideLocalName) | |
| { | |
| WCHAR wideRemoteStatic[MAX_PATH]; | |
| DWORD wideRemoteSize = sizeof(wideRemoteStatic) / sizeof(WCHAR); | |
| 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 = (PWSTR)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 %ld\n", ret); | |
| 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 (!lpRemoteName) | |
| ret = WN_BAD_POINTER; | |
| else if (!lpBufferSize) | |
| ret = WN_BAD_POINTER; | |
| else if (!lpLocalName[0]) | |
| ret = WN_BAD_LOCALNAME; | |
| else | |
| { | |
| if (lpLocalName[1] == ':') | |
| { | |
| switch(GetDriveTypeW(lpLocalName)) | |
| { | |
| case DRIVE_REMOTE: | |
| { | |
| WCHAR remote[MAX_PATH]; | |
| if (!QueryDosDeviceW( lpLocalName, remote, MAX_PATH )) remote[0] = 0; | |
| if (strlenW(remote) + 1 > *lpBufferSize) | |
| { | |
| *lpBufferSize = strlenW(remote) + 1; | |
| ret = WN_MORE_DATA; | |
| } | |
| else | |
| { | |
| strcpyW( lpRemoteName, remote ); | |
| *lpBufferSize = strlenW(lpRemoteName) + 1; | |
| ret = WN_SUCCESS; | |
| } | |
| 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 %ld\n", ret); | |
| return ret; | |
| } | |
| /************************************************************************** | |
| * WNetSetConnectionA [MPR.@] | |
| */ | |
| DWORD WINAPI WNetSetConnectionA( LPCSTR lpName, DWORD dwProperty, | |
| LPVOID pvValue ) | |
| { | |
| FIXME( "(%s, %08lX, %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, %08lX, %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 ) | |
| { | |
| FIXME( "(%s, 0x%08lX, %p, %p): stub\n", | |
| debugstr_a(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /***************************************************************** | |
| * WNetGetUniversalNameW [MPR.@] | |
| */ | |
| DWORD WINAPI WNetGetUniversalNameW ( LPCWSTR lpLocalPath, DWORD dwInfoLevel, | |
| LPVOID lpBuffer, LPDWORD lpBufferSize ) | |
| { | |
| FIXME( "(%s, 0x%08lX, %p, %p): stub\n", | |
| debugstr_w(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /* | |
| * 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 ) | |
| { | |
| FIXME( "(%p, %08lX): stub\n", hwnd, dwType ); | |
| SetLastError(WN_NO_NETWORK); | |
| return WN_NO_NETWORK; | |
| } | |
| /********************************************************************* | |
| * 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, %08lX): 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, %ld, %p, %ld): 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, %ld, %p, %ld): 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 = (LPWSTR)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 %ld\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 = (ULONG_PTR)NULL; | |
| 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 %ld\n", ret); | |
| return ret; | |
| } | |
| /***************************************************************** | |
| * WNetGetProviderNameA [MPR.@] | |
| */ | |
| DWORD WINAPI WNetGetProviderNameA( DWORD dwNetType, | |
| LPSTR lpProvider, LPDWORD lpBufferSize ) | |
| { | |
| DWORD ret; | |
| TRACE("(0x%08lx, %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 %ld\n", ret); | |
| return ret; | |
| } | |
| /***************************************************************** | |
| * WNetGetProviderNameW [MPR.@] | |
| */ | |
| DWORD WINAPI WNetGetProviderNameW( DWORD dwNetType, | |
| LPWSTR lpProvider, LPDWORD lpBufferSize ) | |
| { | |
| DWORD ret; | |
| TRACE("(0x%08lx, %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 = strlenW(providerTable->table[i].name) + 1; | |
| if (*lpBufferSize < sizeNeeded) | |
| { | |
| *lpBufferSize = sizeNeeded; | |
| ret = WN_MORE_DATA; | |
| } | |
| else | |
| { | |
| strcpyW(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 %ld\n", ret); | |
| return ret; | |
| } |