/* * 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 #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 "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; PF_NPGetResourceInformation getResourceInformation; PF_NPAddConnection addConnection; PF_NPAddConnection3 addConnection3; PF_NPCancelConnection cancelConnection; #ifdef __REACTOS__ PF_NPGetConnection getConnection; #endif } 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)); snprintfW(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); #ifdef __REACTOS__ if (connectCap & WNNC_CON_GETCONNECTIONS) provider->getConnection = MPR_GETPROC(NPGetConnection); #endif 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 = strchrW(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 = 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 = HeapAlloc(GetProcessHeap(), 0, sizeof(NETRESOURCEW)); if (ret) { size_t len; *ret = *lpNet; ret->lpLocalName = ret->lpComment = ret->lpProvider = NULL; if (lpNet->lpRemoteName) { len = strlenW(lpNet->lpRemoteName) + 1; ret->lpRemoteName = 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 _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 && !strcmpW(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 && !strcmpW(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 * (strlenW(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; 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 %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 = strlenW(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))); 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 %d\n", ret); return ret; } static DWORD _copyStringToEnumW(const WCHAR *source, DWORD* left, void** end) { DWORD len; WCHAR* local = *end; len = strlenW(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 = strlenW(local_name); else len = strlenW(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) { strcpyW(accessname, local_name); if (ctxt->result) *ctxt->result = CONNECT_LOCALDRIVE; } else strcpyW(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; 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 = (strlenW(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 = (strlenW(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 (isalphaW(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 ) { #ifndef __REACTOS__ 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; #else DWORD ret = WN_NO_NETWORK; DWORD index; WCHAR local[3] = {letter, ':', 0}; if (providerTable != NULL) { for (index = 0; index < providerTable->numProviders; index++) { if(providerTable->table[index].getCaps(WNNC_CONNECTION) & WNNC_CON_GETCONNECTIONS) { if (providerTable->table[index].getConnection) ret = providerTable->table[index].getConnection( local, remote, size); else ret = WN_NO_NETWORK; if (ret == WN_SUCCESS || ret == WN_MORE_DATA) break; } } } if (ret) SetLastError(ret); return ret; #endif } /************************************************************************** * 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 = 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 %d\n", ret); return ret; }