xref: /reactos/dll/win32/mpr/wnet.c (revision c3483339)
1 /*
2  * MPR WNet functions
3  *
4  * Copyright 1999 Ulrich Weigand
5  * Copyright 2004 Juan Lang
6  * Copyright 2007 Maarten Lankhorst
7  * Copyright 2016-2018 Pierre Schweitzer
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winnls.h"
28 #include "winioctl.h"
29 #include "winnetwk.h"
30 #include "npapi.h"
31 #include "winreg.h"
32 #include "winuser.h"
33 #define WINE_MOUNTMGR_EXTENSIONS
34 #include "ddk/mountmgr.h"
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
37 #include "mprres.h"
38 #include "wnetpriv.h"
39 
40 WINE_DEFAULT_DEBUG_CHANNEL(mpr);
41 
42 /* Data structures representing network service providers.  Assumes only one
43  * thread creates them, and that they are constant for the life of the process
44  * (and therefore doesn't synchronize access).
45  * FIXME: only basic provider data and enumeration-related data are implemented
46  * so far, need to implement the rest too.
47  */
48 typedef struct _WNetProvider
49 {
50     HMODULE           hLib;
51     PWSTR             name;
52     PF_NPGetCaps      getCaps;
53     DWORD             dwSpecVersion;
54     DWORD             dwNetType;
55     DWORD             dwEnumScopes;
56     PF_NPOpenEnum     openEnum;
57     PF_NPEnumResource enumResource;
58     PF_NPCloseEnum    closeEnum;
59     PF_NPGetResourceInformation getResourceInformation;
60     PF_NPAddConnection addConnection;
61     PF_NPAddConnection3 addConnection3;
62     PF_NPCancelConnection cancelConnection;
63 #ifdef __REACTOS__
64     PF_NPGetConnection getConnection;
65 #endif
66 } WNetProvider, *PWNetProvider;
67 
68 typedef struct _WNetProviderTable
69 {
70     LPWSTR           entireNetwork;
71     DWORD            numAllocated;
72     DWORD            numProviders;
73     WNetProvider     table[1];
74 } WNetProviderTable, *PWNetProviderTable;
75 
76 #define WNET_ENUMERATOR_TYPE_GLOBAL     0
77 #define WNET_ENUMERATOR_TYPE_PROVIDER   1
78 #define WNET_ENUMERATOR_TYPE_CONTEXT    2
79 #define WNET_ENUMERATOR_TYPE_CONNECTED  3
80 #define WNET_ENUMERATOR_TYPE_REMEMBERED 4
81 
82 /* An WNet enumerator.  Note that the type doesn't correspond to the scope of
83  * the enumeration; it represents one of the following types:
84  * - a global enumeration, one that's executed across all providers
85  * - a provider-specific enumeration, one that's only executed by a single
86  *   provider
87  * - a context enumeration.  I know this contradicts what I just said about
88  *   there being no correspondence between the scope and the type, but it's
89  *   necessary for the special case that a "Entire Network" entry needs to
90  *   be enumerated in an enumeration of the context scope.  Thus an enumeration
91  *   of the context scope results in a context type enumerator, which morphs
92  *   into a global enumeration (so the enumeration continues across all
93  *   providers).
94  * - a remembered enumeration, not related to providers themselves, but it
95  *   is a registry enumeration for saved connections
96  */
97 typedef struct _WNetEnumerator
98 {
99     DWORD          enumType;
100     DWORD          providerIndex;
101     HANDLE         handle;
102     BOOL           providerDone;
103     DWORD          dwScope;
104     DWORD          dwType;
105     DWORD          dwUsage;
106     union
107     {
108         NETRESOURCEW* net;
109         HANDLE* handles;
110         struct
111         {
112             HKEY registry;
113             DWORD index;
114         } remembered;
115     } specific;
116 } WNetEnumerator, *PWNetEnumerator;
117 
118 #define BAD_PROVIDER_INDEX (DWORD)0xffffffff
119 
120 /* Returns an index (into the global WNetProviderTable) of the provider with
121  * the given name, or BAD_PROVIDER_INDEX if not found.
122  */
123 static DWORD _findProviderIndexW(LPCWSTR lpProvider);
124 
125 static PWNetProviderTable providerTable;
126 
127 /*
128  * Global provider table functions
129  */
130 
131 static void _tryLoadProvider(PCWSTR provider)
132 {
133     static const WCHAR servicePrefix[] = { 'S','y','s','t','e','m','\\',
134      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
135      'S','e','r','v','i','c','e','s','\\',0 };
136     static const WCHAR serviceFmt[] = { '%','s','%','s','\\',
137      'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r',0 };
138     WCHAR serviceName[MAX_PATH];
139     HKEY hKey;
140 
141     TRACE("%s\n", debugstr_w(provider));
142     snprintfW(serviceName, ARRAY_SIZE(serviceName), serviceFmt, servicePrefix, provider);
143     serviceName[ARRAY_SIZE(serviceName) - 1] = '\0';
144     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, serviceName, 0, KEY_READ, &hKey) ==
145      ERROR_SUCCESS)
146     {
147         static const WCHAR szProviderPath[] = { 'P','r','o','v','i','d','e','r',
148          'P','a','t','h',0 };
149         WCHAR providerPath[MAX_PATH];
150         DWORD type, size = sizeof(providerPath);
151 
152         if (RegQueryValueExW(hKey, szProviderPath, NULL, &type,
153          (LPBYTE)providerPath, &size) == ERROR_SUCCESS && (type == REG_SZ || type == REG_EXPAND_SZ))
154         {
155             static const WCHAR szProviderName[] = { 'N','a','m','e',0 };
156             PWSTR name = NULL;
157 
158             if (type == REG_EXPAND_SZ)
159             {
160                 WCHAR path[MAX_PATH];
161                 if (ExpandEnvironmentStringsW(providerPath, path, MAX_PATH)) lstrcpyW( providerPath, path );
162             }
163 
164             size = 0;
165             RegQueryValueExW(hKey, szProviderName, NULL, NULL, NULL, &size);
166             if (size)
167             {
168                 name = HeapAlloc(GetProcessHeap(), 0, size);
169                 if (RegQueryValueExW(hKey, szProviderName, NULL, &type,
170                  (LPBYTE)name, &size) != ERROR_SUCCESS || type != REG_SZ)
171                 {
172                     HeapFree(GetProcessHeap(), 0, name);
173                     name = NULL;
174                 }
175             }
176             if (name)
177             {
178                 HMODULE hLib = LoadLibraryW(providerPath);
179 
180                 if (hLib)
181                 {
182 #define MPR_GETPROC(proc) ((PF_##proc)GetProcAddress(hLib, #proc))
183 
184                     PF_NPGetCaps getCaps = MPR_GETPROC(NPGetCaps);
185 
186                     TRACE("loaded lib %p\n", hLib);
187                     if (getCaps)
188                     {
189                         DWORD connectCap;
190                         PWNetProvider provider =
191                          &providerTable->table[providerTable->numProviders];
192 
193                         provider->hLib = hLib;
194                         provider->name = name;
195                         TRACE("name is %s\n", debugstr_w(name));
196                         provider->getCaps = getCaps;
197                         provider->dwSpecVersion = getCaps(WNNC_SPEC_VERSION);
198                         provider->dwNetType = getCaps(WNNC_NET_TYPE);
199                         TRACE("net type is 0x%08x\n", provider->dwNetType);
200                         provider->dwEnumScopes = getCaps(WNNC_ENUMERATION);
201                         if (provider->dwEnumScopes)
202                         {
203                             TRACE("supports enumeration\n");
204                             provider->openEnum = MPR_GETPROC(NPOpenEnum);
205                             TRACE("NPOpenEnum %p\n", provider->openEnum);
206                             provider->enumResource = MPR_GETPROC(NPEnumResource);
207                             TRACE("NPEnumResource %p\n", provider->enumResource);
208                             provider->closeEnum = MPR_GETPROC(NPCloseEnum);
209                             TRACE("NPCloseEnum %p\n", provider->closeEnum);
210                             provider->getResourceInformation = MPR_GETPROC(NPGetResourceInformation);
211                             TRACE("NPGetResourceInformation %p\n", provider->getResourceInformation);
212                             if (!provider->openEnum ||
213                                 !provider->enumResource ||
214                                 !provider->closeEnum)
215                             {
216                                 provider->openEnum = NULL;
217                                 provider->enumResource = NULL;
218                                 provider->closeEnum = NULL;
219                                 provider->dwEnumScopes = 0;
220                                 WARN("Couldn't load enumeration functions\n");
221                             }
222                         }
223                         connectCap = getCaps(WNNC_CONNECTION);
224                         if (connectCap & WNNC_CON_ADDCONNECTION)
225                             provider->addConnection = MPR_GETPROC(NPAddConnection);
226                         if (connectCap & WNNC_CON_ADDCONNECTION3)
227                             provider->addConnection3 = MPR_GETPROC(NPAddConnection3);
228                         if (connectCap & WNNC_CON_CANCELCONNECTION)
229                             provider->cancelConnection = MPR_GETPROC(NPCancelConnection);
230 #ifdef __REACTOS__
231                         if (connectCap & WNNC_CON_GETCONNECTIONS)
232                             provider->getConnection = MPR_GETPROC(NPGetConnection);
233 #endif
234                         TRACE("NPAddConnection %p\n", provider->addConnection);
235                         TRACE("NPAddConnection3 %p\n", provider->addConnection3);
236                         TRACE("NPCancelConnection %p\n", provider->cancelConnection);
237                         providerTable->numProviders++;
238                     }
239                     else
240                     {
241                         WARN("Provider %s didn't export NPGetCaps\n",
242                          debugstr_w(provider));
243                         HeapFree(GetProcessHeap(), 0, name);
244                         FreeLibrary(hLib);
245                     }
246 
247 #undef MPR_GETPROC
248                 }
249                 else
250                 {
251                     WARN("Couldn't load library %s for provider %s\n",
252                      debugstr_w(providerPath), debugstr_w(provider));
253                     HeapFree(GetProcessHeap(), 0, name);
254                 }
255             }
256             else
257             {
258                 WARN("Couldn't get provider name for provider %s\n",
259                  debugstr_w(provider));
260             }
261         }
262         else
263             WARN("Couldn't open value %s\n", debugstr_w(szProviderPath));
264         RegCloseKey(hKey);
265     }
266     else
267         WARN("Couldn't open service key for provider %s\n",
268          debugstr_w(provider));
269 }
270 
271 void wnetInit(HINSTANCE hInstDll)
272 {
273     static const WCHAR providerOrderKey[] = { 'S','y','s','t','e','m','\\',
274      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
275      'C','o','n','t','r','o','l','\\',
276      'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r','\\',
277      'O','r','d','e','r',0 };
278      static const WCHAR providerOrder[] = { 'P','r','o','v','i','d','e','r',
279       'O','r','d','e','r',0 };
280     HKEY hKey;
281 
282     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, providerOrderKey, 0, KEY_READ, &hKey)
283      == ERROR_SUCCESS)
284     {
285         DWORD size = 0;
286 
287         RegQueryValueExW(hKey, providerOrder, NULL, NULL, NULL, &size);
288         if (size)
289         {
290             PWSTR providers = HeapAlloc(GetProcessHeap(), 0, size);
291 
292             if (providers)
293             {
294                 DWORD type;
295 
296                 if (RegQueryValueExW(hKey, providerOrder, NULL, &type,
297                  (LPBYTE)providers, &size) == ERROR_SUCCESS && type == REG_SZ)
298                 {
299                     PWSTR ptr;
300                     DWORD numToAllocate;
301 
302                     TRACE("provider order is %s\n", debugstr_w(providers));
303                     /* first count commas as a heuristic for how many to
304                      * allocate space for */
305                     for (ptr = providers, numToAllocate = 1; ptr; )
306                     {
307                         ptr = strchrW(ptr, ',');
308                         if (ptr) {
309                             numToAllocate++;
310                             ptr++;
311                         }
312                     }
313                     providerTable =
314                      HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
315                      sizeof(WNetProviderTable)
316                      + (numToAllocate - 1) * sizeof(WNetProvider));
317                     if (providerTable)
318                     {
319                         PWSTR ptrPrev;
320                         int entireNetworkLen;
321                         LPCWSTR stringresource;
322 
323                         entireNetworkLen = LoadStringW(hInstDll,
324                          IDS_ENTIRENETWORK, (LPWSTR)&stringresource, 0);
325                         providerTable->entireNetwork = HeapAlloc(
326                          GetProcessHeap(), 0, (entireNetworkLen + 1) *
327                          sizeof(WCHAR));
328                         if (providerTable->entireNetwork)
329                         {
330                             memcpy(providerTable->entireNetwork, stringresource, entireNetworkLen*sizeof(WCHAR));
331                             providerTable->entireNetwork[entireNetworkLen] = 0;
332                         }
333                         providerTable->numAllocated = numToAllocate;
334                         for (ptr = providers; ptr; )
335                         {
336                             ptrPrev = ptr;
337                             ptr = strchrW(ptr, ',');
338                             if (ptr)
339                                 *ptr++ = '\0';
340                             _tryLoadProvider(ptrPrev);
341                         }
342                     }
343                 }
344                 HeapFree(GetProcessHeap(), 0, providers);
345             }
346         }
347         RegCloseKey(hKey);
348     }
349 }
350 
351 void wnetFree(void)
352 {
353     if (providerTable)
354     {
355         DWORD i;
356 
357         for (i = 0; i < providerTable->numProviders; i++)
358         {
359             HeapFree(GetProcessHeap(), 0, providerTable->table[i].name);
360             FreeModule(providerTable->table[i].hLib);
361         }
362         HeapFree(GetProcessHeap(), 0, providerTable->entireNetwork);
363         HeapFree(GetProcessHeap(), 0, providerTable);
364         providerTable = NULL;
365     }
366 }
367 
368 static DWORD _findProviderIndexW(LPCWSTR lpProvider)
369 {
370     DWORD ret = BAD_PROVIDER_INDEX;
371 
372     if (providerTable && providerTable->numProviders)
373     {
374         DWORD i;
375 
376         for (i = 0; i < providerTable->numProviders &&
377          ret == BAD_PROVIDER_INDEX; i++)
378             if (!strcmpW(lpProvider, providerTable->table[i].name))
379                 ret = i;
380     }
381     return ret;
382 }
383 
384 /*
385  * Browsing Functions
386  */
387 
388 static LPNETRESOURCEW _copyNetResourceForEnumW(LPNETRESOURCEW lpNet)
389 {
390     LPNETRESOURCEW ret;
391 
392     if (lpNet)
393     {
394         ret = HeapAlloc(GetProcessHeap(), 0, sizeof(NETRESOURCEW));
395         if (ret)
396         {
397             size_t len;
398 
399             *ret = *lpNet;
400             ret->lpLocalName = ret->lpComment = ret->lpProvider = NULL;
401             if (lpNet->lpRemoteName)
402             {
403                 len = strlenW(lpNet->lpRemoteName) + 1;
404                 ret->lpRemoteName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
405                 if (ret->lpRemoteName)
406                     strcpyW(ret->lpRemoteName, lpNet->lpRemoteName);
407             }
408         }
409     }
410     else
411         ret = NULL;
412     return ret;
413 }
414 
415 static void _freeEnumNetResource(LPNETRESOURCEW lpNet)
416 {
417     if (lpNet)
418     {
419         HeapFree(GetProcessHeap(), 0, lpNet->lpRemoteName);
420         HeapFree(GetProcessHeap(), 0, lpNet);
421     }
422 }
423 
424 static PWNetEnumerator _createGlobalEnumeratorW(DWORD dwScope, DWORD dwType,
425  DWORD dwUsage, LPNETRESOURCEW lpNet)
426 {
427     PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
428      HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
429 
430     if (ret)
431     {
432         ret->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
433         ret->dwScope = dwScope;
434         ret->dwType  = dwType;
435         ret->dwUsage = dwUsage;
436         ret->specific.net = _copyNetResourceForEnumW(lpNet);
437     }
438     return ret;
439 }
440 
441 static PWNetEnumerator _createProviderEnumerator(DWORD dwScope, DWORD dwType,
442  DWORD dwUsage, DWORD index, HANDLE handle)
443 {
444     PWNetEnumerator ret;
445 
446     if (!providerTable || index >= providerTable->numProviders)
447         ret = NULL;
448     else
449     {
450         ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
451         if (ret)
452         {
453             ret->enumType      = WNET_ENUMERATOR_TYPE_PROVIDER;
454             ret->providerIndex = index;
455             ret->dwScope       = dwScope;
456             ret->dwType        = dwType;
457             ret->dwUsage       = dwUsage;
458             ret->handle        = handle;
459         }
460     }
461     return ret;
462 }
463 
464 static PWNetEnumerator _createContextEnumerator(DWORD dwScope, DWORD dwType,
465  DWORD dwUsage)
466 {
467     PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
468      HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
469 
470     if (ret)
471     {
472         ret->enumType = WNET_ENUMERATOR_TYPE_CONTEXT;
473         ret->dwScope = dwScope;
474         ret->dwType  = dwType;
475         ret->dwUsage = dwUsage;
476     }
477     return ret;
478 }
479 
480 static PWNetEnumerator _createConnectedEnumerator(DWORD dwScope, DWORD dwType,
481  DWORD dwUsage)
482 {
483     PWNetEnumerator ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
484     if (ret)
485     {
486         ret->enumType = WNET_ENUMERATOR_TYPE_CONNECTED;
487         ret->dwScope = dwScope;
488         ret->dwType  = dwType;
489         ret->dwUsage = dwUsage;
490         ret->specific.handles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HANDLE) * providerTable->numProviders);
491         if (!ret->specific.handles)
492         {
493             HeapFree(GetProcessHeap(), 0, ret);
494             ret = NULL;
495         }
496     }
497     return ret;
498 }
499 
500 static PWNetEnumerator _createRememberedEnumerator(DWORD dwScope, DWORD dwType,
501  HKEY remembered)
502 {
503     PWNetEnumerator ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
504     if (ret)
505     {
506         ret->enumType = WNET_ENUMERATOR_TYPE_REMEMBERED;
507         ret->dwScope = dwScope;
508         ret->dwType = dwType;
509         ret->specific.remembered.registry = remembered;
510     }
511     return ret;
512 }
513 
514 /* Thunks the array of wide-string LPNETRESOURCEs lpNetArrayIn into buffer
515  * lpBuffer, with size *lpBufferSize.  lpNetArrayIn contains *lpcCount entries
516  * to start.  On return, *lpcCount reflects the number thunked into lpBuffer.
517  * Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA
518  * if not all members of the array could be thunked, and something else on
519  * failure.
520  */
521 static DWORD _thunkNetResourceArrayWToA(const NETRESOURCEW *lpNetArrayIn,
522  const DWORD *lpcCount, LPVOID lpBuffer, const DWORD *lpBufferSize)
523 {
524     DWORD i, numToThunk, totalBytes, ret;
525     LPSTR strNext;
526 
527     if (!lpNetArrayIn)
528         return WN_BAD_POINTER;
529     if (!lpcCount)
530         return WN_BAD_POINTER;
531     if (*lpcCount == -1)
532         return WN_BAD_VALUE;
533     if (!lpBuffer)
534         return WN_BAD_POINTER;
535     if (!lpBufferSize)
536         return WN_BAD_POINTER;
537 
538     for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++)
539     {
540         const NETRESOURCEW *lpNet = lpNetArrayIn + i;
541 
542         totalBytes += sizeof(NETRESOURCEA);
543         if (lpNet->lpLocalName)
544             totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpLocalName,
545              -1, NULL, 0, NULL, NULL);
546         if (lpNet->lpRemoteName)
547             totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpRemoteName,
548              -1, NULL, 0, NULL, NULL);
549         if (lpNet->lpComment)
550             totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpComment,
551              -1, NULL, 0, NULL, NULL);
552         if (lpNet->lpProvider)
553             totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpProvider,
554              -1, NULL, 0, NULL, NULL);
555         if (totalBytes < *lpBufferSize)
556             numToThunk = i + 1;
557     }
558     strNext = (LPSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEA));
559     for (i = 0; i < numToThunk; i++)
560     {
561         LPNETRESOURCEA lpNetOut = (LPNETRESOURCEA)lpBuffer + i;
562         const NETRESOURCEW *lpNetIn = lpNetArrayIn + i;
563 
564         memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEA));
565         /* lie about string lengths, we already verified how many
566          * we have space for above
567          */
568         if (lpNetIn->lpLocalName)
569         {
570             lpNetOut->lpLocalName = strNext;
571             strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpLocalName, -1,
572              lpNetOut->lpLocalName, *lpBufferSize, NULL, NULL);
573         }
574         if (lpNetIn->lpRemoteName)
575         {
576             lpNetOut->lpRemoteName = strNext;
577             strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpRemoteName, -1,
578              lpNetOut->lpRemoteName, *lpBufferSize, NULL, NULL);
579         }
580         if (lpNetIn->lpComment)
581         {
582             lpNetOut->lpComment = strNext;
583             strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpComment, -1,
584              lpNetOut->lpComment, *lpBufferSize, NULL, NULL);
585         }
586         if (lpNetIn->lpProvider)
587         {
588             lpNetOut->lpProvider = strNext;
589             strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpProvider, -1,
590              lpNetOut->lpProvider, *lpBufferSize, NULL, NULL);
591         }
592     }
593     ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS;
594     TRACE("numToThunk is %d, *lpcCount is %d, returning %d\n", numToThunk,
595      *lpcCount, ret);
596     return ret;
597 }
598 
599 /* Thunks the array of multibyte-string LPNETRESOURCEs lpNetArrayIn into buffer
600  * lpBuffer, with size *lpBufferSize.  lpNetArrayIn contains *lpcCount entries
601  * to start.  On return, *lpcCount reflects the number thunked into lpBuffer.
602  * Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA
603  * if not all members of the array could be thunked, and something else on
604  * failure.
605  */
606 static DWORD _thunkNetResourceArrayAToW(const NETRESOURCEA *lpNetArrayIn,
607  const DWORD *lpcCount, LPVOID lpBuffer, const DWORD *lpBufferSize)
608 {
609     DWORD i, numToThunk, totalBytes, ret;
610     LPWSTR strNext;
611 
612     if (!lpNetArrayIn)
613         return WN_BAD_POINTER;
614     if (!lpcCount)
615         return WN_BAD_POINTER;
616     if (*lpcCount == -1)
617         return WN_BAD_VALUE;
618     if (!lpBuffer)
619         return WN_BAD_POINTER;
620     if (!lpBufferSize)
621         return WN_BAD_POINTER;
622 
623     for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++)
624     {
625         const NETRESOURCEA *lpNet = lpNetArrayIn + i;
626 
627         totalBytes += sizeof(NETRESOURCEW);
628         if (lpNet->lpLocalName)
629             totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpLocalName,
630              -1, NULL, 0) * sizeof(WCHAR);
631         if (lpNet->lpRemoteName)
632             totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpRemoteName,
633              -1, NULL, 0) * sizeof(WCHAR);
634         if (lpNet->lpComment)
635             totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpComment,
636              -1, NULL, 0) * sizeof(WCHAR);
637         if (lpNet->lpProvider)
638             totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpProvider,
639              -1, NULL, 0) * sizeof(WCHAR);
640         if (totalBytes < *lpBufferSize)
641             numToThunk = i + 1;
642     }
643     strNext = (LPWSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEW));
644     for (i = 0; i < numToThunk; i++)
645     {
646         LPNETRESOURCEW lpNetOut = (LPNETRESOURCEW)lpBuffer + i;
647         const NETRESOURCEA *lpNetIn = lpNetArrayIn + i;
648 
649         memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEW));
650         /* lie about string lengths, we already verified how many
651          * we have space for above
652          */
653         if (lpNetIn->lpLocalName)
654         {
655             lpNetOut->lpLocalName = strNext;
656             strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpLocalName,
657              -1, lpNetOut->lpLocalName, *lpBufferSize);
658         }
659         if (lpNetIn->lpRemoteName)
660         {
661             lpNetOut->lpRemoteName = strNext;
662             strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpRemoteName,
663              -1, lpNetOut->lpRemoteName, *lpBufferSize);
664         }
665         if (lpNetIn->lpComment)
666         {
667             lpNetOut->lpComment = strNext;
668             strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpComment,
669              -1, lpNetOut->lpComment, *lpBufferSize);
670         }
671         if (lpNetIn->lpProvider)
672         {
673             lpNetOut->lpProvider = strNext;
674             strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpProvider,
675              -1, lpNetOut->lpProvider, *lpBufferSize);
676         }
677     }
678     ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS;
679     TRACE("numToThunk is %d, *lpcCount is %d, returning %d\n", numToThunk,
680      *lpcCount, ret);
681     return ret;
682 }
683 
684 /*********************************************************************
685  * WNetOpenEnumA [MPR.@]
686  *
687  * See comments for WNetOpenEnumW.
688  */
689 DWORD WINAPI WNetOpenEnumA( DWORD dwScope, DWORD dwType, DWORD dwUsage,
690                             LPNETRESOURCEA lpNet, LPHANDLE lphEnum )
691 {
692     DWORD ret;
693 
694     TRACE( "(%08X, %08X, %08X, %p, %p)\n",
695 	    dwScope, dwType, dwUsage, lpNet, lphEnum );
696 
697     if (!lphEnum)
698         ret = WN_BAD_POINTER;
699     else if (!providerTable || providerTable->numProviders == 0)
700     {
701         *lphEnum = NULL;
702         ret = WN_NO_NETWORK;
703     }
704     else
705     {
706         if (lpNet)
707         {
708             LPNETRESOURCEW lpNetWide = NULL;
709             BYTE buf[1024];
710             DWORD size = sizeof(buf), count = 1;
711             BOOL allocated = FALSE;
712 
713             ret = _thunkNetResourceArrayAToW(lpNet, &count, buf, &size);
714             if (ret == WN_MORE_DATA)
715             {
716                 lpNetWide = HeapAlloc(GetProcessHeap(), 0,
717                  size);
718                 if (lpNetWide)
719                 {
720                     ret = _thunkNetResourceArrayAToW(lpNet, &count, lpNetWide,
721                      &size);
722                     allocated = TRUE;
723                 }
724                 else
725                     ret = WN_OUT_OF_MEMORY;
726             }
727             else if (ret == WN_SUCCESS)
728                 lpNetWide = (LPNETRESOURCEW)buf;
729             if (ret == WN_SUCCESS)
730                 ret = WNetOpenEnumW(dwScope, dwType, dwUsage, lpNetWide,
731                  lphEnum);
732             if (allocated)
733                 HeapFree(GetProcessHeap(), 0, lpNetWide);
734         }
735         else
736             ret = WNetOpenEnumW(dwScope, dwType, dwUsage, NULL, lphEnum);
737     }
738     if (ret)
739         SetLastError(ret);
740     TRACE("Returning %d\n", ret);
741     return ret;
742 }
743 
744 /*********************************************************************
745  * WNetOpenEnumW [MPR.@]
746  *
747  * Network enumeration has way too many parameters, so I'm not positive I got
748  * them right.  What I've got so far:
749  *
750  * - If the scope is RESOURCE_GLOBALNET, and no LPNETRESOURCE is passed,
751  *   all the network providers should be enumerated.
752  *
753  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
754  *   and neither the LPNETRESOURCE's lpRemoteName nor the LPNETRESOURCE's
755  *   lpProvider is set, all the network providers should be enumerated.
756  *   (This means the enumeration is a list of network providers, not that the
757  *   enumeration is passed on to the providers.)
758  *
759  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and the
760  *   resource matches the "Entire Network" resource (no remote name, no
761  *   provider, comment is the "Entire Network" string), a RESOURCE_GLOBALNET
762  *   enumeration is done on every network provider.
763  *
764  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
765  *   the LPNETRESOURCE's lpProvider is set, enumeration will be passed through
766  *   only to the given network provider.
767  *
768  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
769  *   no lpProvider is set, enumeration will be tried on every network provider,
770  *   in the order in which they're loaded.
771  *
772  * - The LPNETRESOURCE should be disregarded for scopes besides
773  *   RESOURCE_GLOBALNET.  MSDN states that lpNet must be NULL if dwScope is not
774  *   RESOURCE_GLOBALNET, but Windows doesn't return an error if it isn't NULL.
775  *
776  * - If the scope is RESOURCE_CONTEXT, MS includes an "Entire Network" net
777  *   resource in the enumerated list, as well as any machines in your
778  *   workgroup.  The machines in your workgroup come from doing a
779  *   RESOURCE_CONTEXT enumeration of every Network Provider.
780  */
781 DWORD WINAPI WNetOpenEnumW( DWORD dwScope, DWORD dwType, DWORD dwUsage,
782                             LPNETRESOURCEW lpNet, LPHANDLE lphEnum )
783 {
784     DWORD ret;
785 
786     TRACE( "(%08X, %08X, %08X, %p, %p)\n",
787           dwScope, dwType, dwUsage, lpNet, lphEnum );
788 
789     if (!lphEnum)
790         ret = WN_BAD_POINTER;
791     else if (!providerTable || providerTable->numProviders == 0)
792     {
793         *lphEnum = NULL;
794         ret = WN_NO_NETWORK;
795     }
796     else
797     {
798         switch (dwScope)
799         {
800             case RESOURCE_GLOBALNET:
801                 if (lpNet)
802                 {
803                     if (lpNet->lpProvider)
804                     {
805                         DWORD index = _findProviderIndexW(lpNet->lpProvider);
806 
807                         if (index != BAD_PROVIDER_INDEX)
808                         {
809                             if (providerTable->table[index].openEnum &&
810                              providerTable->table[index].dwEnumScopes & WNNC_ENUM_GLOBAL)
811                             {
812                                 HANDLE handle;
813                                 PWSTR RemoteName = lpNet->lpRemoteName;
814 
815                                 if ((lpNet->dwUsage & RESOURCEUSAGE_CONTAINER) &&
816                                     RemoteName && !strcmpW(RemoteName, lpNet->lpProvider))
817                                     lpNet->lpRemoteName = NULL;
818 
819                                 ret = providerTable->table[index].openEnum(
820                                  dwScope, dwType, dwUsage, lpNet, &handle);
821                                 if (ret == WN_SUCCESS)
822                                 {
823                                     *lphEnum = _createProviderEnumerator(
824                                      dwScope, dwType, dwUsage, index, handle);
825                                     ret = *lphEnum ? WN_SUCCESS :
826                                      WN_OUT_OF_MEMORY;
827                                 }
828 
829                                 lpNet->lpRemoteName = RemoteName;
830                             }
831                             else
832                                 ret = WN_NOT_SUPPORTED;
833                         }
834                         else
835                             ret = WN_BAD_PROVIDER;
836                     }
837                     else if (lpNet->lpRemoteName)
838                     {
839                         *lphEnum = _createGlobalEnumeratorW(dwScope,
840                          dwType, dwUsage, lpNet);
841                         ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
842                     }
843                     else
844                     {
845                         if (lpNet->lpComment && !strcmpW(lpNet->lpComment,
846                          providerTable->entireNetwork))
847                         {
848                             /* comment matches the "Entire Network", enumerate
849                              * global scope of every provider
850                              */
851                             *lphEnum = _createGlobalEnumeratorW(dwScope,
852                              dwType, dwUsage, lpNet);
853                         }
854                         else
855                         {
856                             /* this is the same as not having passed lpNet */
857                             *lphEnum = _createGlobalEnumeratorW(dwScope,
858                              dwType, dwUsage, NULL);
859                         }
860                         ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
861                     }
862                 }
863                 else
864                 {
865                     *lphEnum = _createGlobalEnumeratorW(dwScope, dwType,
866                      dwUsage, lpNet);
867                     ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
868                 }
869                 break;
870             case RESOURCE_CONTEXT:
871                 *lphEnum = _createContextEnumerator(dwScope, dwType, dwUsage);
872                 ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
873                 break;
874             case RESOURCE_CONNECTED:
875                 *lphEnum = _createConnectedEnumerator(dwScope, dwType, dwUsage);
876                 ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
877                 break;
878             case RESOURCE_REMEMBERED:
879                 {
880                     HKEY remembered, user_profile;
881 
882                     ret = WN_OUT_OF_MEMORY;
883                     if (RegOpenCurrentUser(KEY_READ, &user_profile) == ERROR_SUCCESS)
884                     {
885                         WCHAR subkey[8] = {'N', 'e', 't', 'w', 'o', 'r', 'k', 0};
886 
887                         if (RegOpenKeyExW(user_profile, subkey, 0, KEY_READ, &remembered) == ERROR_SUCCESS)
888                         {
889                             *lphEnum = _createRememberedEnumerator(dwScope, dwType, remembered);
890                             ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
891                         }
892 
893                         RegCloseKey(user_profile);
894                     }
895                 }
896                 break;
897             default:
898                 WARN("unknown scope 0x%08x\n", dwScope);
899                 ret = WN_BAD_VALUE;
900         }
901     }
902     if (ret)
903         SetLastError(ret);
904     TRACE("Returning %d\n", ret);
905     return ret;
906 }
907 
908 /*********************************************************************
909  * WNetEnumResourceA [MPR.@]
910  */
911 DWORD WINAPI WNetEnumResourceA( HANDLE hEnum, LPDWORD lpcCount,
912                                 LPVOID lpBuffer, LPDWORD lpBufferSize )
913 {
914     DWORD ret;
915 
916     TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
917 
918     if (!hEnum)
919         ret = WN_BAD_POINTER;
920     else if (!lpcCount)
921         ret = WN_BAD_POINTER;
922     else if (!lpBuffer)
923         ret = WN_BAD_POINTER;
924     else if (!lpBufferSize)
925         ret = WN_BAD_POINTER;
926     else if (*lpBufferSize < sizeof(NETRESOURCEA))
927     {
928         *lpBufferSize = sizeof(NETRESOURCEA);
929         ret = WN_MORE_DATA;
930     }
931     else
932     {
933         DWORD localCount = *lpcCount, localSize = *lpBufferSize;
934         LPVOID localBuffer = HeapAlloc(GetProcessHeap(), 0, localSize);
935 
936         if (localBuffer)
937         {
938             ret = WNetEnumResourceW(hEnum, &localCount, localBuffer,
939              &localSize);
940             if (ret == WN_SUCCESS || (ret == WN_MORE_DATA && localCount != -1))
941             {
942                 /* FIXME: this isn't necessarily going to work in the case of
943                  * WN_MORE_DATA, because our enumerator may have moved on to
944                  * the next provider.  MSDN states that a large (16KB) buffer
945                  * size is the appropriate usage of this function, so
946                  * hopefully it won't be an issue.
947                  */
948                 ret = _thunkNetResourceArrayWToA(localBuffer, &localCount,
949                  lpBuffer, lpBufferSize);
950                 *lpcCount = localCount;
951             }
952             HeapFree(GetProcessHeap(), 0, localBuffer);
953         }
954         else
955             ret = WN_OUT_OF_MEMORY;
956     }
957     if (ret)
958         SetLastError(ret);
959     TRACE("Returning %d\n", ret);
960     return ret;
961 }
962 
963 static DWORD _countProviderBytesW(PWNetProvider provider)
964 {
965     DWORD ret;
966 
967     if (provider)
968     {
969         ret = sizeof(NETRESOURCEW);
970         ret += 2 * (strlenW(provider->name) + 1) * sizeof(WCHAR);
971     }
972     else
973         ret = 0;
974     return ret;
975 }
976 
977 static DWORD _enumerateProvidersW(PWNetEnumerator enumerator, LPDWORD lpcCount,
978  LPVOID lpBuffer, const DWORD *lpBufferSize)
979 {
980     DWORD ret;
981 
982     if (!enumerator)
983         return WN_BAD_POINTER;
984     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
985         return WN_BAD_VALUE;
986     if (!lpcCount)
987         return WN_BAD_POINTER;
988     if (!lpBuffer)
989         return WN_BAD_POINTER;
990     if (!lpBufferSize)
991         return WN_BAD_POINTER;
992     if (*lpBufferSize < sizeof(NETRESOURCEA))
993         return WN_MORE_DATA;
994 
995     if (!providerTable || enumerator->providerIndex >=
996      providerTable->numProviders)
997         ret = WN_NO_MORE_ENTRIES;
998     else
999     {
1000         DWORD bytes = 0, count = 0, countLimit, i;
1001         LPNETRESOURCEW resource;
1002         LPWSTR strNext;
1003 
1004         countLimit = *lpcCount == -1 ?
1005          providerTable->numProviders - enumerator->providerIndex : *lpcCount;
1006         while (count < countLimit && bytes < *lpBufferSize)
1007         {
1008             DWORD bytesNext = _countProviderBytesW(
1009              &providerTable->table[count + enumerator->providerIndex]);
1010 
1011             if (bytes + bytesNext < *lpBufferSize)
1012             {
1013                 bytes += bytesNext;
1014                 count++;
1015             }
1016         }
1017         strNext = (LPWSTR)((LPBYTE)lpBuffer + count * sizeof(NETRESOURCEW));
1018         for (i = 0, resource = lpBuffer; i < count; i++, resource++)
1019         {
1020             resource->dwScope = RESOURCE_GLOBALNET;
1021             resource->dwType = RESOURCETYPE_ANY;
1022             resource->dwDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
1023             resource->dwUsage = RESOURCEUSAGE_CONTAINER |
1024              RESOURCEUSAGE_RESERVED;
1025             resource->lpLocalName = NULL;
1026             resource->lpRemoteName = strNext;
1027             strcpyW(resource->lpRemoteName,
1028              providerTable->table[i + enumerator->providerIndex].name);
1029             strNext += strlenW(resource->lpRemoteName) + 1;
1030             resource->lpComment = NULL;
1031             resource->lpProvider = strNext;
1032             strcpyW(resource->lpProvider,
1033              providerTable->table[i + enumerator->providerIndex].name);
1034             strNext += strlenW(resource->lpProvider) + 1;
1035         }
1036         enumerator->providerIndex += count;
1037         *lpcCount = count;
1038         ret = count > 0 ? WN_SUCCESS : WN_MORE_DATA;
1039     }
1040     TRACE("Returning %d\n", ret);
1041     return ret;
1042 }
1043 
1044 /* Advances the enumerator (assumed to be a global enumerator) to the next
1045  * provider that supports the enumeration scope passed to WNetOpenEnum.  Does
1046  * not open a handle with the next provider.
1047  * If the existing handle is NULL, may leave the enumerator unchanged, since
1048  * the current provider may support the desired scope.
1049  * If the existing handle is not NULL, closes it before moving on.
1050  * Returns WN_SUCCESS on success, WN_NO_MORE_ENTRIES if there is no available
1051  * provider, and another error on failure.
1052  */
1053 static DWORD _globalEnumeratorAdvance(PWNetEnumerator enumerator)
1054 {
1055     if (!enumerator)
1056         return WN_BAD_POINTER;
1057     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
1058         return WN_BAD_VALUE;
1059     if (!providerTable || enumerator->providerIndex >=
1060      providerTable->numProviders)
1061         return WN_NO_MORE_ENTRIES;
1062 
1063     if (enumerator->providerDone)
1064     {
1065         DWORD dwEnum = 0;
1066         enumerator->providerDone = FALSE;
1067         if (enumerator->handle)
1068         {
1069             providerTable->table[enumerator->providerIndex].closeEnum(
1070              enumerator->handle);
1071             enumerator->handle = NULL;
1072             enumerator->providerIndex++;
1073         }
1074         if (enumerator->dwScope == RESOURCE_CONNECTED)
1075             dwEnum = WNNC_ENUM_LOCAL;
1076         else if (enumerator->dwScope == RESOURCE_GLOBALNET)
1077             dwEnum = WNNC_ENUM_GLOBAL;
1078         else if (enumerator->dwScope == RESOURCE_CONTEXT)
1079             dwEnum = WNNC_ENUM_CONTEXT;
1080         for (; enumerator->providerIndex < providerTable->numProviders &&
1081          !(providerTable->table[enumerator->providerIndex].dwEnumScopes
1082          & dwEnum); enumerator->providerIndex++)
1083             ;
1084     }
1085     return enumerator->providerIndex < providerTable->numProviders ?
1086      WN_SUCCESS : WN_NO_MORE_ENTRIES;
1087 }
1088 
1089 /* "Passes through" call to the next provider that supports the enumeration
1090  * type.
1091  * FIXME: if one call to a provider's enumerator succeeds while there's still
1092  * space in lpBuffer, I don't call to the next provider.  The caller may not
1093  * expect that it should call EnumResourceW again with a return value of
1094  * WN_SUCCESS (depending what *lpcCount was to begin with).  That means strings
1095  * may have to be moved around a bit, ick.
1096  */
1097 static DWORD _enumerateGlobalPassthroughW(PWNetEnumerator enumerator,
1098  LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
1099 {
1100     DWORD ret;
1101 
1102     if (!enumerator)
1103         return WN_BAD_POINTER;
1104     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
1105         return WN_BAD_VALUE;
1106     if (!lpcCount)
1107         return WN_BAD_POINTER;
1108     if (!lpBuffer)
1109         return WN_BAD_POINTER;
1110     if (!lpBufferSize)
1111         return WN_BAD_POINTER;
1112     if (*lpBufferSize < sizeof(NETRESOURCEW))
1113         return WN_MORE_DATA;
1114 
1115     ret = _globalEnumeratorAdvance(enumerator);
1116     if (ret == WN_SUCCESS)
1117     {
1118         ret = providerTable->table[enumerator->providerIndex].
1119          openEnum(enumerator->dwScope, enumerator->dwType,
1120          enumerator->dwUsage, enumerator->specific.net,
1121          &enumerator->handle);
1122         if (ret == WN_SUCCESS)
1123         {
1124             ret = providerTable->table[enumerator->providerIndex].
1125              enumResource(enumerator->handle, lpcCount, lpBuffer,
1126              lpBufferSize);
1127             if (ret != WN_MORE_DATA)
1128                 enumerator->providerDone = TRUE;
1129         }
1130     }
1131     TRACE("Returning %d\n", ret);
1132     return ret;
1133 }
1134 
1135 static DWORD _enumerateGlobalW(PWNetEnumerator enumerator, LPDWORD lpcCount,
1136  LPVOID lpBuffer, LPDWORD lpBufferSize)
1137 {
1138     DWORD ret;
1139 
1140     if (!enumerator)
1141         return WN_BAD_POINTER;
1142     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
1143         return WN_BAD_VALUE;
1144     if (!lpcCount)
1145         return WN_BAD_POINTER;
1146     if (!lpBuffer)
1147         return WN_BAD_POINTER;
1148     if (!lpBufferSize)
1149         return WN_BAD_POINTER;
1150     if (*lpBufferSize < sizeof(NETRESOURCEW))
1151         return WN_MORE_DATA;
1152     if (!providerTable)
1153         return WN_NO_NETWORK;
1154 
1155     switch (enumerator->dwScope)
1156     {
1157         case RESOURCE_GLOBALNET:
1158             if (enumerator->specific.net)
1159                 ret = _enumerateGlobalPassthroughW(enumerator, lpcCount,
1160                  lpBuffer, lpBufferSize);
1161             else
1162                 ret = _enumerateProvidersW(enumerator, lpcCount, lpBuffer,
1163                  lpBufferSize);
1164             break;
1165         case RESOURCE_CONTEXT:
1166             ret = _enumerateGlobalPassthroughW(enumerator, lpcCount, lpBuffer,
1167              lpBufferSize);
1168             break;
1169         default:
1170             WARN("unexpected scope 0x%08x\n", enumerator->dwScope);
1171             ret = WN_NO_MORE_ENTRIES;
1172     }
1173     TRACE("Returning %d\n", ret);
1174     return ret;
1175 }
1176 
1177 static DWORD _enumerateProviderW(PWNetEnumerator enumerator, LPDWORD lpcCount,
1178  LPVOID lpBuffer, LPDWORD lpBufferSize)
1179 {
1180     if (!enumerator)
1181         return WN_BAD_POINTER;
1182     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_PROVIDER)
1183         return WN_BAD_VALUE;
1184     if (!enumerator->handle)
1185         return WN_BAD_VALUE;
1186     if (!lpcCount)
1187         return WN_BAD_POINTER;
1188     if (!lpBuffer)
1189         return WN_BAD_POINTER;
1190     if (!lpBufferSize)
1191         return WN_BAD_POINTER;
1192     if (!providerTable)
1193         return WN_NO_NETWORK;
1194     if (enumerator->providerIndex >= providerTable->numProviders)
1195         return WN_NO_MORE_ENTRIES;
1196     if (!providerTable->table[enumerator->providerIndex].enumResource)
1197         return WN_BAD_VALUE;
1198     return providerTable->table[enumerator->providerIndex].enumResource(
1199      enumerator->handle, lpcCount, lpBuffer, lpBufferSize);
1200 }
1201 
1202 static DWORD _enumerateContextW(PWNetEnumerator enumerator, LPDWORD lpcCount,
1203  LPVOID lpBuffer, LPDWORD lpBufferSize)
1204 {
1205     DWORD ret;
1206     size_t cchEntireNetworkLen, bytesNeeded;
1207 
1208     if (!enumerator)
1209         return WN_BAD_POINTER;
1210     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_CONTEXT)
1211         return WN_BAD_VALUE;
1212     if (!lpcCount)
1213         return WN_BAD_POINTER;
1214     if (!lpBuffer)
1215         return WN_BAD_POINTER;
1216     if (!lpBufferSize)
1217         return WN_BAD_POINTER;
1218     if (!providerTable)
1219         return WN_NO_NETWORK;
1220 
1221     cchEntireNetworkLen = strlenW(providerTable->entireNetwork) + 1;
1222     bytesNeeded = sizeof(NETRESOURCEW) + cchEntireNetworkLen * sizeof(WCHAR);
1223     if (*lpBufferSize < bytesNeeded)
1224     {
1225         *lpBufferSize = bytesNeeded;
1226         ret = WN_MORE_DATA;
1227     }
1228     else
1229     {
1230         LPNETRESOURCEW lpNet = lpBuffer;
1231 
1232         lpNet->dwScope = RESOURCE_GLOBALNET;
1233         lpNet->dwType = enumerator->dwType;
1234         lpNet->dwDisplayType = RESOURCEDISPLAYTYPE_ROOT;
1235         lpNet->dwUsage = RESOURCEUSAGE_CONTAINER;
1236         lpNet->lpLocalName = NULL;
1237         lpNet->lpRemoteName = NULL;
1238         lpNet->lpProvider = NULL;
1239         /* odd, but correct: put comment at end of buffer, so it won't get
1240          * overwritten by subsequent calls to a provider's enumResource
1241          */
1242         lpNet->lpComment = (LPWSTR)((LPBYTE)lpBuffer + *lpBufferSize -
1243          (cchEntireNetworkLen * sizeof(WCHAR)));
1244         strcpyW(lpNet->lpComment, providerTable->entireNetwork);
1245         ret = WN_SUCCESS;
1246     }
1247     if (ret == WN_SUCCESS)
1248     {
1249         DWORD bufferSize = *lpBufferSize - bytesNeeded;
1250 
1251         /* "Entire Network" entry enumerated--morph this into a global
1252          * enumerator.  enumerator->lpNet continues to be NULL, since it has
1253          * no meaning when the scope isn't RESOURCE_GLOBALNET.
1254          */
1255         enumerator->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
1256         ret = _enumerateGlobalW(enumerator, lpcCount,
1257          (LPBYTE)lpBuffer + bytesNeeded, &bufferSize);
1258         if (ret == WN_SUCCESS)
1259         {
1260             /* reflect the fact that we already enumerated "Entire Network" */
1261             (*lpcCount)++;
1262             *lpBufferSize = bufferSize + bytesNeeded;
1263         }
1264         else
1265         {
1266             /* the provider enumeration failed, but we already succeeded in
1267              * enumerating "Entire Network"--leave type as global to allow a
1268              * retry, but indicate success with a count of one.
1269              */
1270             ret = WN_SUCCESS;
1271             *lpcCount = 1;
1272             *lpBufferSize = bytesNeeded;
1273         }
1274     }
1275     TRACE("Returning %d\n", ret);
1276     return ret;
1277 }
1278 
1279 static DWORD _copyStringToEnumW(const WCHAR *source, DWORD* left, void** end)
1280 {
1281     DWORD len;
1282     WCHAR* local = *end;
1283 
1284     len = strlenW(source) + 1;
1285     len *= sizeof(WCHAR);
1286     if (*left < len)
1287         return WN_MORE_DATA;
1288 
1289     local -= (len / sizeof(WCHAR));
1290     memcpy(local, source, len);
1291     *left -= len;
1292     *end = local;
1293 
1294     return WN_SUCCESS;
1295 }
1296 
1297 static DWORD _enumerateConnectedW(PWNetEnumerator enumerator, DWORD* user_count,
1298                                   void* user_buffer, DWORD* user_size)
1299 {
1300     DWORD ret, index, count, total_count, size, i, left;
1301     void* end;
1302     NETRESOURCEW* curr, * buffer;
1303     HANDLE* handles;
1304 
1305     if (!enumerator)
1306         return WN_BAD_POINTER;
1307     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_CONNECTED)
1308         return WN_BAD_VALUE;
1309     if (!user_count || !user_buffer || !user_size)
1310         return WN_BAD_POINTER;
1311     if (!providerTable)
1312         return WN_NO_NETWORK;
1313 
1314     handles = enumerator->specific.handles;
1315     left = *user_size;
1316     size = *user_size;
1317     buffer = HeapAlloc(GetProcessHeap(), 0, *user_size);
1318     if (!buffer)
1319         return WN_NO_NETWORK;
1320 
1321     curr = user_buffer;
1322     end = (char *)user_buffer + size;
1323     count = *user_count;
1324     total_count = 0;
1325 
1326     ret = WN_NO_MORE_ENTRIES;
1327     for (index = 0; index < providerTable->numProviders; index++)
1328     {
1329         if (providerTable->table[index].dwEnumScopes)
1330         {
1331             if (handles[index] == 0)
1332             {
1333                 ret = providerTable->table[index].openEnum(enumerator->dwScope,
1334                                                            enumerator->dwType,
1335                                                            enumerator->dwUsage,
1336                                                            NULL, &handles[index]);
1337                 if (ret != WN_SUCCESS)
1338                     continue;
1339             }
1340 
1341             ret = providerTable->table[index].enumResource(handles[index],
1342                                                            &count, buffer,
1343                                                            &size);
1344             total_count += count;
1345             if (ret == WN_MORE_DATA)
1346                 break;
1347 
1348             if (ret == WN_SUCCESS)
1349             {
1350                 for (i = 0; i < count; ++i)
1351                 {
1352                     if (left < sizeof(NETRESOURCEW))
1353                     {
1354                         ret = WN_MORE_DATA;
1355                         break;
1356                     }
1357 
1358                     memcpy(curr, &buffer[i], sizeof(NETRESOURCEW));
1359                     left -= sizeof(NETRESOURCEW);
1360 
1361                     ret = _copyStringToEnumW(buffer[i].lpLocalName, &left, &end);
1362                     if (ret == WN_MORE_DATA)
1363                         break;
1364                     curr->lpLocalName = end;
1365 
1366                     ret = _copyStringToEnumW(buffer[i].lpRemoteName, &left, &end);
1367                     if (ret == WN_MORE_DATA)
1368                         break;
1369                     curr->lpRemoteName = end;
1370 
1371                     ret = _copyStringToEnumW(buffer[i].lpProvider, &left, &end);
1372                     if (ret == WN_MORE_DATA)
1373                         break;
1374                     curr->lpProvider = end;
1375 
1376                     ++curr;
1377                 }
1378 
1379                 size = left;
1380             }
1381 
1382             if (*user_count != -1)
1383                 count = *user_count - total_count;
1384             else
1385                 count = *user_count;
1386         }
1387     }
1388 
1389     if (total_count == 0)
1390         ret = WN_NO_MORE_ENTRIES;
1391 
1392     *user_count = total_count;
1393     if (ret != WN_MORE_DATA && ret != WN_NO_MORE_ENTRIES)
1394         ret = WN_SUCCESS;
1395 
1396     HeapFree(GetProcessHeap(), 0, buffer);
1397 
1398     TRACE("Returning %d\n", ret);
1399     return ret;
1400 }
1401 
1402 static const WCHAR connectionType[] = { 'C','o','n','n','e','c','t','i','o','n','T','y','p','e',0 };
1403 static const WCHAR providerName[] = { 'P','r','o','v','i','d','e','r','N','a','m','e',0 };
1404 static const WCHAR remotePath[] = { 'R','e','m','o','t','e','P','a','t','h',0 };
1405 
1406 static WCHAR *get_reg_str(HKEY hkey, const WCHAR *value, DWORD *len)
1407 {
1408     DWORD type;
1409     WCHAR *ret = NULL;
1410 
1411     if (!RegQueryValueExW(hkey, value, NULL, &type, NULL, len) && type == REG_SZ)
1412     {
1413         if (!(ret = HeapAlloc(GetProcessHeap(), 0, *len))) return NULL;
1414         RegQueryValueExW(hkey, value, 0, 0, (BYTE *)ret, len);
1415     }
1416 
1417     return ret;
1418 }
1419 
1420 static DWORD _enumeratorRememberedW(PWNetEnumerator enumerator, DWORD* user_count,
1421                                     void* user_buffer, DWORD* user_size)
1422 {
1423     HKEY registry, connection;
1424     WCHAR buffer[255];
1425     LONG size_left;
1426     DWORD index, ret, type, len, size, registry_size, full_size = 0, total_count;
1427     NETRESOURCEW * net_buffer = user_buffer;
1428     WCHAR * str, * registry_string;
1429 
1430     /* we will do the work in a single loop, so here is some things:
1431      * we write netresource at the begin of the user buffer
1432      * we write strings at the end of the user buffer
1433      */
1434     size_left = *user_size;
1435     total_count = 0;
1436     type = enumerator->dwType;
1437     registry = enumerator->specific.remembered.registry;
1438     str = (WCHAR *)((ULONG_PTR)user_buffer + *user_size - sizeof(WCHAR));
1439     for (index = enumerator->specific.remembered.index; ; ++index)
1440     {
1441         enumerator->specific.remembered.index = index;
1442 
1443         if (*user_count != -1 && total_count == *user_count)
1444         {
1445             ret = WN_SUCCESS;
1446             break;
1447         }
1448 
1449         len = ARRAY_SIZE(buffer);
1450         ret = RegEnumKeyExW(registry, index, buffer, &len, NULL, NULL, NULL, NULL);
1451         if (ret != ERROR_SUCCESS)
1452         {
1453             if (ret == ERROR_NO_MORE_ITEMS) ret = WN_SUCCESS;
1454             break;
1455         }
1456 
1457         if (RegOpenKeyExW(registry, buffer, 0, KEY_READ, &connection) != ERROR_SUCCESS)
1458         {
1459             continue;
1460         }
1461 
1462         full_size = sizeof(NETRESOURCEW);
1463         size_left -= sizeof(NETRESOURCEW);
1464 
1465         if (size_left > 0)
1466         {
1467             size = sizeof(DWORD);
1468             RegQueryValueExW(connection, connectionType, NULL, NULL, (BYTE *)&net_buffer->dwType, &size);
1469             if (type != RESOURCETYPE_ANY && net_buffer->dwType != type)
1470             {
1471                 size_left += sizeof(NETRESOURCEW);
1472                 RegCloseKey(connection);
1473                 continue;
1474             }
1475 
1476             net_buffer->dwScope = RESOURCE_REMEMBERED;
1477             net_buffer->dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
1478             net_buffer->dwUsage = RESOURCEUSAGE_CONNECTABLE;
1479         }
1480         else
1481             ret = WN_MORE_DATA;
1482 
1483         /* FIXME: this only supports drive letters */
1484         full_size += 3 * sizeof(WCHAR);
1485         size_left -= 3 * sizeof(WCHAR);
1486         if (size_left > 0)
1487         {
1488             str -= 3;
1489             str[0] = buffer[0];
1490             str[1] = ':';
1491             str[2] = 0;
1492             net_buffer->lpLocalName = str;
1493         }
1494 
1495         registry_size = 0;
1496         registry_string = get_reg_str(connection, providerName, &registry_size);
1497         if (registry_string)
1498         {
1499             full_size += registry_size;
1500             size_left -= registry_size;
1501 
1502             if (size_left > 0)
1503             {
1504                 str -= (registry_size / sizeof(WCHAR));
1505                 lstrcpyW(str, registry_string);
1506                 net_buffer->lpProvider = str;
1507             }
1508             else
1509                 ret = WN_MORE_DATA;
1510 
1511             HeapFree(GetProcessHeap(), 0, registry_string);
1512         }
1513 
1514         registry_size = 0;
1515         registry_string = get_reg_str(connection, remotePath, &registry_size);
1516         if (registry_string)
1517         {
1518             full_size += registry_size;
1519             size_left -= registry_size;
1520 
1521             if (size_left > 0)
1522             {
1523                 str -= (registry_size / sizeof(WCHAR));
1524                 lstrcpyW(str, registry_string);
1525                 net_buffer->lpRemoteName = str;
1526             }
1527             else
1528                 ret = WN_MORE_DATA;
1529 
1530             HeapFree(GetProcessHeap(), 0, registry_string);
1531         }
1532 
1533         RegCloseKey(connection);
1534 
1535         net_buffer->lpComment = NULL;
1536 
1537         if (size_left < 0)
1538             break;
1539 
1540         ++total_count;
1541         ++net_buffer;
1542     }
1543 
1544     if (total_count == 0)
1545         ret = WN_NO_MORE_ENTRIES;
1546 
1547     *user_count = total_count;
1548 
1549     if (ret != WN_MORE_DATA && ret != WN_NO_MORE_ENTRIES)
1550         ret = WN_SUCCESS;
1551 
1552     if (ret == WN_MORE_DATA)
1553         *user_size = *user_size + full_size;
1554 
1555     return ret;
1556 }
1557 
1558 /*********************************************************************
1559  * WNetEnumResourceW [MPR.@]
1560  */
1561 DWORD WINAPI WNetEnumResourceW( HANDLE hEnum, LPDWORD lpcCount,
1562                                 LPVOID lpBuffer, LPDWORD lpBufferSize )
1563 {
1564     DWORD ret;
1565 
1566     TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
1567 
1568     if (!hEnum)
1569         ret = WN_BAD_POINTER;
1570     else if (!lpcCount)
1571         ret = WN_BAD_POINTER;
1572     else if (!lpBuffer)
1573         ret = WN_BAD_POINTER;
1574     else if (!lpBufferSize)
1575         ret = WN_BAD_POINTER;
1576     else if (*lpBufferSize < sizeof(NETRESOURCEW))
1577     {
1578         *lpBufferSize = sizeof(NETRESOURCEW);
1579         ret = WN_MORE_DATA;
1580     }
1581     else
1582     {
1583         PWNetEnumerator enumerator = (PWNetEnumerator)hEnum;
1584 
1585         switch (enumerator->enumType)
1586         {
1587             case WNET_ENUMERATOR_TYPE_GLOBAL:
1588                 ret = _enumerateGlobalW(enumerator, lpcCount, lpBuffer,
1589                  lpBufferSize);
1590                 break;
1591             case WNET_ENUMERATOR_TYPE_PROVIDER:
1592                 ret = _enumerateProviderW(enumerator, lpcCount, lpBuffer,
1593                  lpBufferSize);
1594                 break;
1595             case WNET_ENUMERATOR_TYPE_CONTEXT:
1596                 ret = _enumerateContextW(enumerator, lpcCount, lpBuffer,
1597                  lpBufferSize);
1598                 break;
1599             case WNET_ENUMERATOR_TYPE_CONNECTED:
1600                 ret = _enumerateConnectedW(enumerator, lpcCount, lpBuffer,
1601                  lpBufferSize);
1602                 break;
1603             case WNET_ENUMERATOR_TYPE_REMEMBERED:
1604                 ret = _enumeratorRememberedW(enumerator, lpcCount, lpBuffer,
1605                  lpBufferSize);
1606                 break;
1607             default:
1608                 WARN("bogus enumerator type!\n");
1609                 ret = WN_NO_NETWORK;
1610         }
1611     }
1612     if (ret)
1613         SetLastError(ret);
1614     TRACE("Returning %d\n", ret);
1615     return ret;
1616 }
1617 
1618 /*********************************************************************
1619  * WNetCloseEnum [MPR.@]
1620  */
1621 DWORD WINAPI WNetCloseEnum( HANDLE hEnum )
1622 {
1623     DWORD ret, index;
1624     HANDLE *handles;
1625 
1626     TRACE( "(%p)\n", hEnum );
1627 
1628     if (hEnum)
1629     {
1630         PWNetEnumerator enumerator = (PWNetEnumerator)hEnum;
1631 
1632         switch (enumerator->enumType)
1633         {
1634             case WNET_ENUMERATOR_TYPE_GLOBAL:
1635                 if (enumerator->specific.net)
1636                     _freeEnumNetResource(enumerator->specific.net);
1637                 if (enumerator->handle)
1638                     providerTable->table[enumerator->providerIndex].
1639                      closeEnum(enumerator->handle);
1640                 ret = WN_SUCCESS;
1641                 break;
1642             case WNET_ENUMERATOR_TYPE_PROVIDER:
1643                 if (enumerator->handle)
1644                     providerTable->table[enumerator->providerIndex].
1645                      closeEnum(enumerator->handle);
1646                 ret = WN_SUCCESS;
1647                 break;
1648             case WNET_ENUMERATOR_TYPE_CONNECTED:
1649                 handles = enumerator->specific.handles;
1650                 for (index = 0; index < providerTable->numProviders; index++)
1651                 {
1652                     if (providerTable->table[index].dwEnumScopes && handles[index])
1653                         providerTable->table[index].closeEnum(handles[index]);
1654                 }
1655                 HeapFree(GetProcessHeap(), 0, handles);
1656                 ret = WN_SUCCESS;
1657                 break;
1658             case WNET_ENUMERATOR_TYPE_REMEMBERED:
1659                 RegCloseKey(enumerator->specific.remembered.registry);
1660                 ret = WN_SUCCESS;
1661                 break;
1662             default:
1663                 WARN("bogus enumerator type!\n");
1664                 ret = WN_BAD_HANDLE;
1665         }
1666         HeapFree(GetProcessHeap(), 0, hEnum);
1667     }
1668     else
1669         ret = WN_BAD_HANDLE;
1670     if (ret)
1671         SetLastError(ret);
1672     TRACE("Returning %d\n", ret);
1673     return ret;
1674 }
1675 
1676 /*********************************************************************
1677  * WNetGetResourceInformationA [MPR.@]
1678  *
1679  * See WNetGetResourceInformationW
1680  */
1681 DWORD WINAPI WNetGetResourceInformationA( LPNETRESOURCEA lpNetResource,
1682                                           LPVOID lpBuffer, LPDWORD cbBuffer,
1683                                           LPSTR *lplpSystem )
1684 {
1685     DWORD ret;
1686 
1687     TRACE( "(%p, %p, %p, %p)\n",
1688            lpNetResource, lpBuffer, cbBuffer, lplpSystem );
1689 
1690     if (!providerTable || providerTable->numProviders == 0)
1691         ret = WN_NO_NETWORK;
1692     else if (lpNetResource)
1693     {
1694         LPNETRESOURCEW lpNetResourceW = NULL;
1695         DWORD size = 1024, count = 1;
1696         DWORD len;
1697 
1698         lpNetResourceW = HeapAlloc(GetProcessHeap(), 0, size);
1699         ret = _thunkNetResourceArrayAToW(lpNetResource, &count, lpNetResourceW, &size);
1700         if (ret == WN_MORE_DATA)
1701         {
1702             HeapFree(GetProcessHeap(), 0, lpNetResourceW);
1703             lpNetResourceW = HeapAlloc(GetProcessHeap(), 0, size);
1704             if (lpNetResourceW)
1705                 ret = _thunkNetResourceArrayAToW(lpNetResource,
1706                         &count, lpNetResourceW, &size);
1707             else
1708                 ret = WN_OUT_OF_MEMORY;
1709         }
1710         if (ret == WN_SUCCESS)
1711         {
1712             LPWSTR lpSystemW = NULL;
1713             LPVOID lpBufferW;
1714             size = 1024;
1715             lpBufferW = HeapAlloc(GetProcessHeap(), 0, size);
1716             if (lpBufferW)
1717             {
1718                 ret = WNetGetResourceInformationW(lpNetResourceW,
1719                         lpBufferW, &size, &lpSystemW);
1720                 if (ret == WN_MORE_DATA)
1721                 {
1722                     HeapFree(GetProcessHeap(), 0, lpBufferW);
1723                     lpBufferW = HeapAlloc(GetProcessHeap(), 0, size);
1724                     if (lpBufferW)
1725                         ret = WNetGetResourceInformationW(lpNetResourceW,
1726                             lpBufferW, &size, &lpSystemW);
1727                     else
1728                         ret = WN_OUT_OF_MEMORY;
1729                 }
1730                 if (ret == WN_SUCCESS)
1731                 {
1732                     ret = _thunkNetResourceArrayWToA(lpBufferW,
1733                             &count, lpBuffer, cbBuffer);
1734                     HeapFree(GetProcessHeap(), 0, lpNetResourceW);
1735                     lpNetResourceW = lpBufferW;
1736                     size = sizeof(NETRESOURCEA);
1737                     size += WideCharToMultiByte(CP_ACP, 0, lpNetResourceW->lpRemoteName,
1738                             -1, NULL, 0, NULL, NULL);
1739                     size += WideCharToMultiByte(CP_ACP, 0, lpNetResourceW->lpProvider,
1740                             -1, NULL, 0, NULL, NULL);
1741 
1742                     len = WideCharToMultiByte(CP_ACP, 0, lpSystemW,
1743                                       -1, NULL, 0, NULL, NULL);
1744                     if ((len) && ( size + len < *cbBuffer))
1745                     {
1746                         *lplpSystem = (char*)lpBuffer + *cbBuffer - len;
1747                         WideCharToMultiByte(CP_ACP, 0, lpSystemW, -1,
1748                              *lplpSystem, len, NULL, NULL);
1749                          ret = WN_SUCCESS;
1750                     }
1751                     else
1752                         ret = WN_MORE_DATA;
1753                 }
1754                 else
1755                     ret = WN_OUT_OF_MEMORY;
1756                 HeapFree(GetProcessHeap(), 0, lpBufferW);
1757             }
1758             else
1759                 ret = WN_OUT_OF_MEMORY;
1760             HeapFree(GetProcessHeap(), 0, lpSystemW);
1761         }
1762         HeapFree(GetProcessHeap(), 0, lpNetResourceW);
1763     }
1764     else
1765         ret = WN_NO_NETWORK;
1766 
1767     if (ret)
1768         SetLastError(ret);
1769     TRACE("Returning %d\n", ret);
1770     return ret;
1771 }
1772 
1773 /*********************************************************************
1774  * WNetGetResourceInformationW [MPR.@]
1775  *
1776  * WNetGetResourceInformationW function identifies the network provider
1777  * that owns the resource and gets information about the type of the resource.
1778  *
1779  * PARAMS:
1780  *  lpNetResource    [ I]    the pointer to NETRESOURCEW structure, that
1781  *                          defines a network resource.
1782  *  lpBuffer         [ O]   the pointer to buffer, containing result. It
1783  *                          contains NETRESOURCEW structure and strings to
1784  *                          which the members of the NETRESOURCEW structure
1785  *                          point.
1786  *  cbBuffer         [I/O] the pointer to DWORD number - size of buffer
1787  *                          in bytes.
1788  *  lplpSystem       [ O]   the pointer to string in the output buffer,
1789  *                          containing the part of the resource name without
1790  *                          names of the server and share.
1791  *
1792  * RETURNS:
1793  *  NO_ERROR if the function succeeds. System error code if the function fails.
1794  */
1795 
1796 DWORD WINAPI WNetGetResourceInformationW( LPNETRESOURCEW lpNetResource,
1797                                           LPVOID lpBuffer, LPDWORD cbBuffer,
1798                                           LPWSTR *lplpSystem )
1799 {
1800     DWORD ret = WN_NO_NETWORK;
1801     DWORD index;
1802 
1803     TRACE( "(%p, %p, %p, %p)\n",
1804            lpNetResource, lpBuffer, cbBuffer, lplpSystem);
1805 
1806     if (!(lpBuffer))
1807         ret = WN_OUT_OF_MEMORY;
1808     else if (providerTable != NULL)
1809     {
1810         /* FIXME: For function value of a variable is indifferent, it does
1811          * search of all providers in a network.
1812          */
1813         for (index = 0; index < providerTable->numProviders; index++)
1814         {
1815             if(providerTable->table[index].getCaps(WNNC_DIALOG) &
1816                 WNNC_DLG_GETRESOURCEINFORMATION)
1817             {
1818                 if (providerTable->table[index].getResourceInformation)
1819                     ret = providerTable->table[index].getResourceInformation(
1820                         lpNetResource, lpBuffer, cbBuffer, lplpSystem);
1821                 else
1822                     ret = WN_NO_NETWORK;
1823                 if (ret == WN_SUCCESS)
1824                     break;
1825             }
1826         }
1827     }
1828     if (ret)
1829         SetLastError(ret);
1830     return ret;
1831 }
1832 
1833 /*********************************************************************
1834  * WNetGetResourceParentA [MPR.@]
1835  */
1836 DWORD WINAPI WNetGetResourceParentA( LPNETRESOURCEA lpNetResource,
1837                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1838 {
1839     FIXME( "(%p, %p, %p): stub\n",
1840            lpNetResource, lpBuffer, lpBufferSize );
1841 
1842     SetLastError(WN_NO_NETWORK);
1843     return WN_NO_NETWORK;
1844 }
1845 
1846 /*********************************************************************
1847  * WNetGetResourceParentW [MPR.@]
1848  */
1849 DWORD WINAPI WNetGetResourceParentW( LPNETRESOURCEW lpNetResource,
1850                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1851 {
1852     FIXME( "(%p, %p, %p): stub\n",
1853            lpNetResource, lpBuffer, lpBufferSize );
1854 
1855     SetLastError(WN_NO_NETWORK);
1856     return WN_NO_NETWORK;
1857 }
1858 
1859 
1860 
1861 /*
1862  * Connection Functions
1863  */
1864 
1865 /*********************************************************************
1866  *  WNetAddConnectionA [MPR.@]
1867  */
1868 DWORD WINAPI WNetAddConnectionA( LPCSTR lpRemoteName, LPCSTR lpPassword,
1869                                  LPCSTR lpLocalName )
1870 {
1871     NETRESOURCEA resourcesA;
1872 
1873     memset(&resourcesA, 0, sizeof(resourcesA));
1874     resourcesA.lpRemoteName = (LPSTR)lpRemoteName;
1875     resourcesA.lpLocalName = (LPSTR)lpLocalName;
1876     return WNetUseConnectionA(NULL, &resourcesA, lpPassword, NULL, 0, NULL, 0, NULL);
1877 }
1878 
1879 /*********************************************************************
1880  *  WNetAddConnectionW [MPR.@]
1881  */
1882 DWORD WINAPI WNetAddConnectionW( LPCWSTR lpRemoteName, LPCWSTR lpPassword,
1883                                  LPCWSTR lpLocalName )
1884 {
1885     NETRESOURCEW resourcesW;
1886 
1887     memset(&resourcesW, 0, sizeof(resourcesW));
1888     resourcesW.lpRemoteName = (LPWSTR)lpRemoteName;
1889     resourcesW.lpLocalName = (LPWSTR)lpLocalName;
1890     return WNetUseConnectionW(NULL, &resourcesW, lpPassword, NULL, 0, NULL, 0, NULL);
1891 }
1892 
1893 /*********************************************************************
1894  *  WNetAddConnection2A [MPR.@]
1895  */
1896 DWORD WINAPI WNetAddConnection2A( LPNETRESOURCEA lpNetResource,
1897                                   LPCSTR lpPassword, LPCSTR lpUserID,
1898                                   DWORD dwFlags )
1899 {
1900     return WNetUseConnectionA(NULL, lpNetResource, lpPassword, lpUserID, dwFlags,
1901                               NULL, 0, NULL);
1902 }
1903 
1904 /*********************************************************************
1905  * WNetAddConnection2W [MPR.@]
1906  */
1907 DWORD WINAPI WNetAddConnection2W( LPNETRESOURCEW lpNetResource,
1908                                   LPCWSTR lpPassword, LPCWSTR lpUserID,
1909                                   DWORD dwFlags )
1910 {
1911     return WNetUseConnectionW(NULL, lpNetResource, lpPassword, lpUserID, dwFlags,
1912                               NULL, 0, NULL);
1913 }
1914 
1915 /*********************************************************************
1916  * WNetAddConnection3A [MPR.@]
1917  */
1918 DWORD WINAPI WNetAddConnection3A( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
1919                                   LPCSTR lpPassword, LPCSTR lpUserID,
1920                                   DWORD dwFlags )
1921 {
1922     return WNetUseConnectionA(hwndOwner, lpNetResource, lpPassword, lpUserID,
1923                               dwFlags, NULL, 0, NULL);
1924 }
1925 
1926 /*********************************************************************
1927  * WNetAddConnection3W [MPR.@]
1928  */
1929 DWORD WINAPI WNetAddConnection3W( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
1930                                   LPCWSTR lpPassword, LPCWSTR lpUserID,
1931                                   DWORD dwFlags )
1932 {
1933     return WNetUseConnectionW(hwndOwner, lpNetResource, lpPassword, lpUserID,
1934                               dwFlags, NULL, 0, NULL);
1935 }
1936 
1937 struct use_connection_context
1938 {
1939     HWND hwndOwner;
1940     NETRESOURCEW *resource;
1941     NETRESOURCEA *resourceA; /* only set for WNetUseConnectionA */
1942     WCHAR *password;
1943     WCHAR *userid;
1944     DWORD flags;
1945     void *accessname;
1946     DWORD *buffer_size;
1947     DWORD *result;
1948     DWORD (*pre_set_accessname)(struct use_connection_context*, WCHAR *);
1949     void  (*set_accessname)(struct use_connection_context*, WCHAR *);
1950 };
1951 
1952 static DWORD use_connection_pre_set_accessnameW(struct use_connection_context *ctxt, WCHAR *local_name)
1953 {
1954     if (ctxt->accessname && ctxt->buffer_size && *ctxt->buffer_size)
1955     {
1956         DWORD len;
1957 
1958         if (local_name)
1959             len = strlenW(local_name);
1960         else
1961             len = strlenW(ctxt->resource->lpRemoteName);
1962 
1963         if (++len > *ctxt->buffer_size)
1964         {
1965             *ctxt->buffer_size = len;
1966             return ERROR_MORE_DATA;
1967         }
1968     }
1969     else
1970         ctxt->accessname = NULL;
1971 
1972     return ERROR_SUCCESS;
1973 }
1974 
1975 static void use_connection_set_accessnameW(struct use_connection_context *ctxt, WCHAR *local_name)
1976 {
1977     WCHAR *accessname = ctxt->accessname;
1978     if (local_name)
1979     {
1980         strcpyW(accessname, local_name);
1981         if (ctxt->result)
1982             *ctxt->result = CONNECT_LOCALDRIVE;
1983     }
1984     else
1985         strcpyW(accessname, ctxt->resource->lpRemoteName);
1986 }
1987 
1988 static DWORD wnet_use_provider( struct use_connection_context *ctxt, NETRESOURCEW * netres, WNetProvider *provider, BOOLEAN redirect )
1989 {
1990     DWORD caps, ret;
1991 
1992     caps = provider->getCaps(WNNC_CONNECTION);
1993     if (!(caps & (WNNC_CON_ADDCONNECTION | WNNC_CON_ADDCONNECTION3)))
1994         return ERROR_BAD_PROVIDER;
1995 
1996     ret = WN_ACCESS_DENIED;
1997     do
1998     {
1999         if ((caps & WNNC_CON_ADDCONNECTION3) && provider->addConnection3)
2000             ret = provider->addConnection3(ctxt->hwndOwner, netres, ctxt->password, ctxt->userid, ctxt->flags);
2001         else if ((caps & WNNC_CON_ADDCONNECTION) && provider->addConnection)
2002             ret = provider->addConnection(netres, ctxt->password, ctxt->userid);
2003 
2004         if (ret == WN_ALREADY_CONNECTED && redirect)
2005             netres->lpLocalName[0] -= 1;
2006     } while (redirect && ret == WN_ALREADY_CONNECTED && netres->lpLocalName[0] >= 'C');
2007 
2008     if (ret == WN_SUCCESS && ctxt->accessname)
2009         ctxt->set_accessname(ctxt, netres->lpLocalName);
2010 
2011     return ret;
2012 }
2013 
2014 static const WCHAR providerType[] = { 'P','r','o','v','i','d','e','r','T','y','p','e',0 };
2015 static const WCHAR userName[] = { 'U','s','e','r','N','a','m','e',0 };
2016 
2017 static DWORD wnet_use_connection( struct use_connection_context *ctxt )
2018 {
2019     WNetProvider *provider;
2020     DWORD index, ret = WN_NO_NETWORK;
2021     BOOL redirect = FALSE;
2022     WCHAR letter[3] = {'Z', ':', 0};
2023     NETRESOURCEW netres;
2024 
2025     if (!providerTable || providerTable->numProviders == 0)
2026         return WN_NO_NETWORK;
2027 
2028     if (!ctxt->resource)
2029         return ERROR_INVALID_PARAMETER;
2030     netres = *ctxt->resource;
2031 
2032     if (!netres.lpLocalName && (ctxt->flags & CONNECT_REDIRECT))
2033     {
2034         if (netres.dwType != RESOURCETYPE_DISK && netres.dwType != RESOURCETYPE_PRINT)
2035             return ERROR_BAD_DEV_TYPE;
2036 
2037         if (netres.dwType == RESOURCETYPE_PRINT)
2038         {
2039             FIXME("Local device selection is not implemented for printers.\n");
2040             return WN_NO_NETWORK;
2041         }
2042 
2043         redirect = TRUE;
2044         netres.lpLocalName = letter;
2045     }
2046 
2047     if (ctxt->flags & CONNECT_INTERACTIVE)
2048         return ERROR_BAD_NET_NAME;
2049 
2050     if ((ret = ctxt->pre_set_accessname(ctxt, netres.lpLocalName)))
2051         return ret;
2052 
2053     if (netres.lpProvider)
2054     {
2055         index = _findProviderIndexW(netres.lpProvider);
2056         if (index == BAD_PROVIDER_INDEX)
2057             return ERROR_BAD_PROVIDER;
2058 
2059         provider = &providerTable->table[index];
2060         ret = wnet_use_provider(ctxt, &netres, provider, redirect);
2061     }
2062     else
2063     {
2064         for (index = 0; index < providerTable->numProviders; index++)
2065         {
2066             provider = &providerTable->table[index];
2067             ret = wnet_use_provider(ctxt, &netres, provider, redirect);
2068             if (ret == WN_SUCCESS || ret == WN_ALREADY_CONNECTED)
2069                 break;
2070         }
2071     }
2072 
2073     if (ret == WN_SUCCESS && ctxt->flags & CONNECT_UPDATE_PROFILE)
2074     {
2075         HKEY user_profile;
2076 
2077         if (netres.dwType == RESOURCETYPE_PRINT)
2078         {
2079             FIXME("Persistent connection are not supported for printers\n");
2080             return ret;
2081         }
2082 
2083         if (RegOpenCurrentUser(KEY_ALL_ACCESS, &user_profile) == ERROR_SUCCESS)
2084         {
2085             HKEY network;
2086             WCHAR subkey[10] = {'N', 'e', 't', 'w', 'o', 'r', 'k', '\\', netres.lpLocalName[0], 0};
2087 
2088             if (RegCreateKeyExW(user_profile, subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
2089                                 KEY_ALL_ACCESS, NULL, &network, NULL) == ERROR_SUCCESS)
2090             {
2091                 DWORD dword_arg = RESOURCETYPE_DISK;
2092                 DWORD len = (strlenW(provider->name) + 1) * sizeof(WCHAR);
2093                 static const WCHAR empty[1] = {0};
2094 
2095                 RegSetValueExW(network, connectionType, 0, REG_DWORD, (const BYTE *)&dword_arg, sizeof(DWORD));
2096                 RegSetValueExW(network, providerName, 0, REG_SZ, (const BYTE *)provider->name, len);
2097                 RegSetValueExW(network, providerType, 0, REG_DWORD, (const BYTE *)&provider->dwNetType, sizeof(DWORD));
2098                 len = (strlenW(netres.lpRemoteName) + 1) * sizeof(WCHAR);
2099                 RegSetValueExW(network, remotePath, 0, REG_SZ, (const BYTE *)netres.lpRemoteName, len);
2100                 len = sizeof(empty);
2101                 RegSetValueExW(network, userName, 0, REG_SZ, (const BYTE *)empty, len);
2102                 RegCloseKey(network);
2103             }
2104 
2105             RegCloseKey(user_profile);
2106         }
2107     }
2108 
2109     return ret;
2110 }
2111 
2112 /*****************************************************************
2113  *  WNetUseConnectionW [MPR.@]
2114  */
2115 DWORD WINAPI WNetUseConnectionW( HWND hwndOwner, NETRESOURCEW *resource, LPCWSTR password,
2116     LPCWSTR userid, DWORD flags, LPWSTR accessname, DWORD *buffer_size, DWORD *result )
2117 {
2118     struct use_connection_context ctxt;
2119 
2120     TRACE( "(%p, %p, %p, %s, 0x%08X, %p, %p, %p)\n",
2121            hwndOwner, resource, password, debugstr_w(userid), flags,
2122            accessname, buffer_size, result );
2123 
2124     ctxt.hwndOwner = hwndOwner;
2125     ctxt.resource = resource;
2126     ctxt.resourceA = NULL;
2127     ctxt.password = (WCHAR*)password;
2128     ctxt.userid = (WCHAR*)userid;
2129     ctxt.flags = flags;
2130     ctxt.accessname = accessname;
2131     ctxt.buffer_size = buffer_size;
2132     ctxt.result = result;
2133     ctxt.pre_set_accessname = use_connection_pre_set_accessnameW;
2134     ctxt.set_accessname = use_connection_set_accessnameW;
2135 
2136     return wnet_use_connection(&ctxt);
2137 }
2138 
2139 static DWORD use_connection_pre_set_accessnameA(struct use_connection_context *ctxt, WCHAR *local_name)
2140 {
2141     if (ctxt->accessname && ctxt->buffer_size && *ctxt->buffer_size)
2142     {
2143         DWORD len;
2144 
2145         if (local_name)
2146             len = WideCharToMultiByte(CP_ACP, 0, local_name, -1, NULL, 0, NULL, NULL) - 1;
2147         else
2148             len = strlen(ctxt->resourceA->lpRemoteName);
2149 
2150         if (++len > *ctxt->buffer_size)
2151         {
2152             *ctxt->buffer_size = len;
2153             return ERROR_MORE_DATA;
2154         }
2155     }
2156     else
2157         ctxt->accessname = NULL;
2158 
2159     return ERROR_SUCCESS;
2160 }
2161 
2162 static void use_connection_set_accessnameA(struct use_connection_context *ctxt, WCHAR *local_name)
2163 {
2164     char *accessname = ctxt->accessname;
2165     if (local_name)
2166     {
2167         WideCharToMultiByte(CP_ACP, 0, local_name, -1, accessname, *ctxt->buffer_size, NULL, NULL);
2168         if (ctxt->result)
2169             *ctxt->result = CONNECT_LOCALDRIVE;
2170     }
2171     else
2172         strcpy(accessname, ctxt->resourceA->lpRemoteName);
2173 }
2174 
2175 static LPWSTR strdupAtoW( LPCSTR str )
2176 {
2177     LPWSTR ret;
2178     INT len;
2179 
2180     if (!str) return NULL;
2181     len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
2182     ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
2183     if (ret) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
2184     return ret;
2185 }
2186 
2187 static void netresource_a_to_w( NETRESOURCEA *resourceA, NETRESOURCEW *resourceW )
2188 {
2189     resourceW->dwScope = resourceA->dwScope;
2190     resourceW->dwType = resourceA->dwType;
2191     resourceW->dwDisplayType = resourceA->dwDisplayType;
2192     resourceW->dwUsage = resourceA->dwUsage;
2193     resourceW->lpLocalName = strdupAtoW(resourceA->lpLocalName);
2194     resourceW->lpRemoteName = strdupAtoW(resourceA->lpRemoteName);
2195     resourceW->lpComment = strdupAtoW(resourceA->lpComment);
2196     resourceW->lpProvider = strdupAtoW(resourceA->lpProvider);
2197 }
2198 
2199 static void free_netresourceW( NETRESOURCEW *resource )
2200 {
2201     HeapFree(GetProcessHeap(), 0, resource->lpLocalName);
2202     HeapFree(GetProcessHeap(), 0, resource->lpRemoteName);
2203     HeapFree(GetProcessHeap(), 0, resource->lpComment);
2204     HeapFree(GetProcessHeap(), 0, resource->lpProvider);
2205 }
2206 
2207 /*****************************************************************
2208  *  WNetUseConnectionA [MPR.@]
2209  */
2210 DWORD WINAPI WNetUseConnectionA( HWND hwndOwner, NETRESOURCEA *resource,
2211     LPCSTR password, LPCSTR userid, DWORD flags, LPSTR accessname,
2212     DWORD *buffer_size, DWORD *result )
2213 {
2214     struct use_connection_context ctxt;
2215     NETRESOURCEW resourceW;
2216     DWORD ret;
2217 
2218     TRACE( "(%p, %p, %p, %s, 0x%08X, %p, %p, %p)\n", hwndOwner, resource, password, debugstr_a(userid), flags,
2219         accessname, buffer_size, result );
2220 
2221     netresource_a_to_w(resource, &resourceW);
2222 
2223     ctxt.hwndOwner = hwndOwner;
2224     ctxt.resource = &resourceW;
2225     ctxt.resourceA = resource;
2226     ctxt.password = strdupAtoW(password);
2227     ctxt.userid = strdupAtoW(userid);
2228     ctxt.flags = flags;
2229     ctxt.accessname = accessname;
2230     ctxt.buffer_size = buffer_size;
2231     ctxt.result = result;
2232     ctxt.pre_set_accessname = use_connection_pre_set_accessnameA;
2233     ctxt.set_accessname = use_connection_set_accessnameA;
2234 
2235     ret = wnet_use_connection(&ctxt);
2236 
2237     free_netresourceW(&resourceW);
2238     HeapFree(GetProcessHeap(), 0, ctxt.password);
2239     HeapFree(GetProcessHeap(), 0, ctxt.userid);
2240 
2241     return ret;
2242 }
2243 
2244 /*********************************************************************
2245  *  WNetCancelConnectionA [MPR.@]
2246  */
2247 DWORD WINAPI WNetCancelConnectionA( LPCSTR lpName, BOOL fForce )
2248 {
2249     return WNetCancelConnection2A(lpName, 0, fForce);
2250 }
2251 
2252 /*********************************************************************
2253  *  WNetCancelConnectionW [MPR.@]
2254  */
2255 DWORD WINAPI WNetCancelConnectionW( LPCWSTR lpName, BOOL fForce )
2256 {
2257     return WNetCancelConnection2W(lpName, 0, fForce);
2258 }
2259 
2260 /*********************************************************************
2261  *  WNetCancelConnection2A [MPR.@]
2262  */
2263 DWORD WINAPI WNetCancelConnection2A( LPCSTR lpName, DWORD dwFlags, BOOL fForce )
2264 {
2265     DWORD ret;
2266     WCHAR * name = strdupAtoW(lpName);
2267     if (!name)
2268         return ERROR_NOT_CONNECTED;
2269 
2270     ret = WNetCancelConnection2W(name, dwFlags, fForce);
2271     HeapFree(GetProcessHeap(), 0, name);
2272 
2273     return ret;
2274 }
2275 
2276 /*********************************************************************
2277  *  WNetCancelConnection2W [MPR.@]
2278  */
2279 DWORD WINAPI WNetCancelConnection2W( LPCWSTR lpName, DWORD dwFlags, BOOL fForce )
2280 {
2281     DWORD ret = WN_NO_NETWORK;
2282     DWORD index;
2283 
2284     if (providerTable != NULL)
2285     {
2286         for (index = 0; index < providerTable->numProviders; index++)
2287         {
2288             if(providerTable->table[index].getCaps(WNNC_CONNECTION) &
2289                 WNNC_CON_CANCELCONNECTION)
2290             {
2291                 if (providerTable->table[index].cancelConnection)
2292                     ret = providerTable->table[index].cancelConnection((LPWSTR)lpName, fForce);
2293                 else
2294                     ret = WN_NO_NETWORK;
2295                 if (ret == WN_SUCCESS || ret == WN_OPEN_FILES)
2296                     break;
2297             }
2298         }
2299     }
2300 
2301     if (ret == WN_SUCCESS && dwFlags & CONNECT_UPDATE_PROFILE)
2302     {
2303         HKEY user_profile;
2304 
2305         /* FIXME: Only remove it if that's a drive letter */
2306         if (isalphaW(lpName[0]) && lpName[1] == ':' &&
2307             RegOpenCurrentUser(KEY_ALL_ACCESS, &user_profile) == ERROR_SUCCESS)
2308         {
2309             WCHAR subkey[10] = {'N', 'e', 't', 'w', 'o', 'r', 'k', '\\', lpName[0], 0};
2310 
2311             RegDeleteKeyW(user_profile, subkey);
2312 
2313             RegCloseKey(user_profile);
2314         }
2315     }
2316 
2317     return ret;
2318 }
2319 
2320 /*****************************************************************
2321  *  WNetRestoreConnectionA [MPR.@]
2322  */
2323 DWORD WINAPI WNetRestoreConnectionA( HWND hwndOwner, LPCSTR lpszDevice )
2324 {
2325     FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_a(lpszDevice) );
2326 
2327     SetLastError(WN_NO_NETWORK);
2328     return WN_NO_NETWORK;
2329 }
2330 
2331 /*****************************************************************
2332  *  WNetRestoreConnectionW [MPR.@]
2333  */
2334 DWORD WINAPI WNetRestoreConnectionW( HWND hwndOwner, LPCWSTR lpszDevice )
2335 {
2336     FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_w(lpszDevice) );
2337 
2338     SetLastError(WN_NO_NETWORK);
2339     return WN_NO_NETWORK;
2340 }
2341 
2342 /**************************************************************************
2343  * WNetGetConnectionA [MPR.@]
2344  *
2345  * RETURNS
2346  * - WN_BAD_LOCALNAME     lpLocalName makes no sense
2347  * - WN_NOT_CONNECTED     drive is a local drive
2348  * - WN_MORE_DATA         buffer isn't big enough
2349  * - WN_SUCCESS           success (net path in buffer)
2350  *
2351  * FIXME: need to test return values under different errors
2352  */
2353 DWORD WINAPI WNetGetConnectionA( LPCSTR lpLocalName,
2354                                  LPSTR lpRemoteName, LPDWORD lpBufferSize )
2355 {
2356     DWORD ret;
2357 
2358     if (!lpLocalName)
2359         ret = WN_BAD_POINTER;
2360     else if (!lpBufferSize)
2361         ret = WN_BAD_POINTER;
2362     else if (!lpRemoteName && *lpBufferSize)
2363         ret = WN_BAD_POINTER;
2364     else
2365     {
2366         int len = MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, NULL, 0);
2367 
2368         if (len)
2369         {
2370             PWSTR wideLocalName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2371 
2372             if (wideLocalName)
2373             {
2374                 WCHAR wideRemoteStatic[MAX_PATH];
2375                 DWORD wideRemoteSize = ARRAY_SIZE(wideRemoteStatic);
2376 
2377                 MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, wideLocalName, len);
2378 
2379                 /* try once without memory allocation */
2380                 ret = WNetGetConnectionW(wideLocalName, wideRemoteStatic,
2381                  &wideRemoteSize);
2382                 if (ret == WN_SUCCESS)
2383                 {
2384                     int len = WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
2385                      -1, NULL, 0, NULL, NULL);
2386 
2387                     if (len <= *lpBufferSize)
2388                     {
2389                         WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic, -1,
2390                          lpRemoteName, *lpBufferSize, NULL, NULL);
2391                         ret = WN_SUCCESS;
2392                     }
2393                     else
2394                     {
2395                         *lpBufferSize = len;
2396                         ret = WN_MORE_DATA;
2397                     }
2398                 }
2399                 else if (ret == WN_MORE_DATA)
2400                 {
2401                     PWSTR wideRemote = HeapAlloc(GetProcessHeap(), 0,
2402                      wideRemoteSize * sizeof(WCHAR));
2403 
2404                     if (wideRemote)
2405                     {
2406                         ret = WNetGetConnectionW(wideLocalName, wideRemote,
2407                          &wideRemoteSize);
2408                         if (ret == WN_SUCCESS)
2409                         {
2410                             if (len <= *lpBufferSize)
2411                             {
2412                                 WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
2413                                  -1, lpRemoteName, *lpBufferSize, NULL, NULL);
2414                                 ret = WN_SUCCESS;
2415                             }
2416                             else
2417                             {
2418                                 *lpBufferSize = len;
2419                                 ret = WN_MORE_DATA;
2420                             }
2421                         }
2422                         HeapFree(GetProcessHeap(), 0, wideRemote);
2423                     }
2424                     else
2425                         ret = WN_OUT_OF_MEMORY;
2426                 }
2427                 HeapFree(GetProcessHeap(), 0, wideLocalName);
2428             }
2429             else
2430                 ret = WN_OUT_OF_MEMORY;
2431         }
2432         else
2433             ret = WN_BAD_LOCALNAME;
2434     }
2435     if (ret)
2436         SetLastError(ret);
2437     TRACE("Returning %d\n", ret);
2438     return ret;
2439 }
2440 
2441 /* find the network connection for a given drive; helper for WNetGetConnection */
2442 static DWORD get_drive_connection( WCHAR letter, LPWSTR remote, LPDWORD size )
2443 {
2444 #ifndef __REACTOS__
2445     char buffer[1024];
2446     struct mountmgr_unix_drive *data = (struct mountmgr_unix_drive *)buffer;
2447     HANDLE mgr;
2448     DWORD ret = WN_NOT_CONNECTED;
2449     DWORD bytes_returned;
2450 
2451     if ((mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE,
2452                             FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
2453                             0, 0 )) == INVALID_HANDLE_VALUE)
2454     {
2455         ERR( "failed to open mount manager err %u\n", GetLastError() );
2456         return ret;
2457     }
2458     memset( data, 0, sizeof(*data) );
2459     data->letter = letter;
2460     if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, data, sizeof(*data),
2461                          data, sizeof(buffer), &bytes_returned, NULL ))
2462     {
2463         char *p, *mount_point = buffer + data->mount_point_offset;
2464         DWORD len;
2465 
2466         if (data->mount_point_offset && !strncmp( mount_point, "unc/", 4 ))
2467         {
2468             mount_point += 2;
2469             mount_point[0] = '\\';
2470             for (p = mount_point; *p; p++) if (*p == '/') *p = '\\';
2471 
2472             len = MultiByteToWideChar( CP_UNIXCP, 0, mount_point, -1, NULL, 0 );
2473             if (len > *size)
2474             {
2475                 *size = len;
2476                 ret = WN_MORE_DATA;
2477             }
2478             else
2479             {
2480                 *size = MultiByteToWideChar( CP_UNIXCP, 0, mount_point, -1, remote, *size);
2481                 ret = WN_SUCCESS;
2482             }
2483         }
2484     }
2485     CloseHandle( mgr );
2486     return ret;
2487 #else
2488     DWORD ret = WN_NO_NETWORK;
2489     DWORD index;
2490     WCHAR local[3] = {letter, ':', 0};
2491 
2492     if (providerTable != NULL)
2493     {
2494         for (index = 0; index < providerTable->numProviders; index++)
2495         {
2496             if(providerTable->table[index].getCaps(WNNC_CONNECTION) &
2497                 WNNC_CON_GETCONNECTIONS)
2498             {
2499                 if (providerTable->table[index].getConnection)
2500                     ret = providerTable->table[index].getConnection(
2501                         local, remote, size);
2502                 else
2503                     ret = WN_NO_NETWORK;
2504                 if (ret == WN_SUCCESS || ret == WN_MORE_DATA)
2505                     break;
2506             }
2507         }
2508     }
2509     if (ret)
2510         SetLastError(ret);
2511     return ret;
2512 #endif
2513 }
2514 
2515 /**************************************************************************
2516  * WNetGetConnectionW [MPR.@]
2517  *
2518  * FIXME: need to test return values under different errors
2519  */
2520 DWORD WINAPI WNetGetConnectionW( LPCWSTR lpLocalName,
2521                                  LPWSTR lpRemoteName, LPDWORD lpBufferSize )
2522 {
2523     DWORD ret;
2524 
2525     TRACE("(%s, %p, %p)\n", debugstr_w(lpLocalName), lpRemoteName,
2526      lpBufferSize);
2527 
2528     if (!lpLocalName)
2529         ret = WN_BAD_POINTER;
2530     else if (!lpBufferSize)
2531         ret = WN_BAD_POINTER;
2532     else if (!lpRemoteName && *lpBufferSize)
2533         ret = WN_BAD_POINTER;
2534     else if (!lpLocalName[0])
2535         ret = WN_BAD_LOCALNAME;
2536     else
2537     {
2538         if (lpLocalName[1] == ':')
2539         {
2540             switch(GetDriveTypeW(lpLocalName))
2541             {
2542             case DRIVE_REMOTE:
2543                 ret = get_drive_connection( lpLocalName[0], lpRemoteName, lpBufferSize );
2544                 break;
2545             case DRIVE_REMOVABLE:
2546             case DRIVE_FIXED:
2547             case DRIVE_CDROM:
2548                 TRACE("file is local\n");
2549                 ret = WN_NOT_CONNECTED;
2550                 break;
2551             default:
2552                 ret = WN_BAD_LOCALNAME;
2553             }
2554         }
2555         else
2556             ret = WN_BAD_LOCALNAME;
2557     }
2558     if (ret)
2559         SetLastError(ret);
2560     TRACE("Returning %d\n", ret);
2561     return ret;
2562 }
2563 
2564 /**************************************************************************
2565  * WNetSetConnectionA [MPR.@]
2566  */
2567 DWORD WINAPI WNetSetConnectionA( LPCSTR lpName, DWORD dwProperty,
2568                                  LPVOID pvValue )
2569 {
2570     FIXME( "(%s, %08X, %p): stub\n", debugstr_a(lpName), dwProperty, pvValue );
2571 
2572     SetLastError(WN_NO_NETWORK);
2573     return WN_NO_NETWORK;
2574 }
2575 
2576 /**************************************************************************
2577  * WNetSetConnectionW [MPR.@]
2578  */
2579 DWORD WINAPI WNetSetConnectionW( LPCWSTR lpName, DWORD dwProperty,
2580                                  LPVOID pvValue )
2581 {
2582     FIXME( "(%s, %08X, %p): stub\n", debugstr_w(lpName), dwProperty, pvValue );
2583 
2584     SetLastError(WN_NO_NETWORK);
2585     return WN_NO_NETWORK;
2586 }
2587 
2588 /*****************************************************************
2589  * WNetGetUniversalNameA [MPR.@]
2590  */
2591 DWORD WINAPI WNetGetUniversalNameA ( LPCSTR lpLocalPath, DWORD dwInfoLevel,
2592                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
2593 {
2594     DWORD err, size;
2595 
2596     FIXME( "(%s, 0x%08X, %p, %p): stub\n",
2597            debugstr_a(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
2598 
2599     switch (dwInfoLevel)
2600     {
2601     case UNIVERSAL_NAME_INFO_LEVEL:
2602     {
2603         LPUNIVERSAL_NAME_INFOA info = lpBuffer;
2604 
2605         if (GetDriveTypeA(lpLocalPath) != DRIVE_REMOTE)
2606         {
2607             err = ERROR_NOT_CONNECTED;
2608             break;
2609         }
2610 
2611         size = sizeof(*info) + lstrlenA(lpLocalPath) + 1;
2612         if (*lpBufferSize < size)
2613         {
2614             err = WN_MORE_DATA;
2615             break;
2616         }
2617         info->lpUniversalName = (char *)info + sizeof(*info);
2618         lstrcpyA(info->lpUniversalName, lpLocalPath);
2619         err = WN_NO_ERROR;
2620         break;
2621     }
2622     case REMOTE_NAME_INFO_LEVEL:
2623         err = WN_NOT_CONNECTED;
2624         break;
2625 
2626     default:
2627         err = WN_BAD_VALUE;
2628         break;
2629     }
2630 
2631     SetLastError(err);
2632     return err;
2633 }
2634 
2635 /*****************************************************************
2636  * WNetGetUniversalNameW [MPR.@]
2637  */
2638 DWORD WINAPI WNetGetUniversalNameW ( LPCWSTR lpLocalPath, DWORD dwInfoLevel,
2639                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
2640 {
2641     DWORD err, size;
2642 
2643     FIXME( "(%s, 0x%08X, %p, %p): stub\n",
2644            debugstr_w(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
2645 
2646     switch (dwInfoLevel)
2647     {
2648     case UNIVERSAL_NAME_INFO_LEVEL:
2649     {
2650         LPUNIVERSAL_NAME_INFOW info = lpBuffer;
2651 
2652         if (GetDriveTypeW(lpLocalPath) != DRIVE_REMOTE)
2653         {
2654             err = ERROR_NOT_CONNECTED;
2655             break;
2656         }
2657 
2658         size = sizeof(*info) + (lstrlenW(lpLocalPath) + 1) * sizeof(WCHAR);
2659         if (*lpBufferSize < size)
2660         {
2661             *lpBufferSize = size;
2662             err = WN_MORE_DATA;
2663             break;
2664         }
2665         info->lpUniversalName = (LPWSTR)((char *)info + sizeof(*info));
2666         lstrcpyW(info->lpUniversalName, lpLocalPath);
2667         err = WN_NO_ERROR;
2668         break;
2669     }
2670     case REMOTE_NAME_INFO_LEVEL:
2671         err = WN_NO_NETWORK;
2672         break;
2673 
2674     default:
2675         err = WN_BAD_VALUE;
2676         break;
2677     }
2678 
2679     if (err != WN_NO_ERROR) SetLastError(err);
2680     return err;
2681 }
2682 
2683 /*****************************************************************
2684  * WNetClearConnections [MPR.@]
2685  */
2686 DWORD WINAPI WNetClearConnections ( HWND owner )
2687 {
2688     HANDLE connected;
2689     PWSTR connection;
2690     DWORD ret, size, count;
2691     NETRESOURCEW * resources, * iter;
2692 
2693     ret = WNetOpenEnumW(RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL, &connected);
2694     if (ret != WN_SUCCESS)
2695     {
2696         if (ret != WN_NO_NETWORK)
2697         {
2698             return ret;
2699         }
2700 
2701         /* Means no provider, then, clearing is OK */
2702         return WN_SUCCESS;
2703     }
2704 
2705     size = 0x1000;
2706     resources = HeapAlloc(GetProcessHeap(), 0, size);
2707     if (!resources)
2708     {
2709         WNetCloseEnum(connected);
2710         return WN_OUT_OF_MEMORY;
2711     }
2712 
2713     for (;;)
2714     {
2715         size = 0x1000;
2716         count = -1;
2717 
2718         memset(resources, 0, size);
2719         ret = WNetEnumResourceW(connected, &count, resources, &size);
2720         if (ret == WN_SUCCESS || ret == WN_MORE_DATA)
2721         {
2722             for (iter = resources; count; count--, iter++)
2723             {
2724                 if (iter->lpLocalName && iter->lpLocalName[0])
2725                     connection = iter->lpLocalName;
2726                 else
2727                     connection = iter->lpRemoteName;
2728 
2729                 WNetCancelConnection2W(connection, 0, TRUE);
2730             }
2731         }
2732         else
2733             break;
2734     }
2735 
2736     HeapFree(GetProcessHeap(), 0, resources);
2737     WNetCloseEnum(connected);
2738 
2739     return ret;
2740 }
2741 
2742 
2743 /*
2744  * Other Functions
2745  */
2746 
2747 /**************************************************************************
2748  * WNetGetUserA [MPR.@]
2749  *
2750  * FIXME: we should not return ourselves, but the owner of the drive lpName
2751  */
2752 DWORD WINAPI WNetGetUserA( LPCSTR lpName, LPSTR lpUserID, LPDWORD lpBufferSize )
2753 {
2754     if (GetUserNameA( lpUserID, lpBufferSize )) return WN_SUCCESS;
2755     return GetLastError();
2756 }
2757 
2758 /*****************************************************************
2759  * WNetGetUserW [MPR.@]
2760  *
2761  * FIXME: we should not return ourselves, but the owner of the drive lpName
2762  */
2763 DWORD WINAPI WNetGetUserW( LPCWSTR lpName, LPWSTR lpUserID, LPDWORD lpBufferSize )
2764 {
2765     if (GetUserNameW( lpUserID, lpBufferSize )) return WN_SUCCESS;
2766     return GetLastError();
2767 }
2768 
2769 /*********************************************************************
2770  * WNetConnectionDialog [MPR.@]
2771  */
2772 DWORD WINAPI WNetConnectionDialog( HWND hwnd, DWORD dwType )
2773 {
2774     CONNECTDLGSTRUCTW conn_dlg;
2775     NETRESOURCEW net_res;
2776 
2777     ZeroMemory(&conn_dlg, sizeof(conn_dlg));
2778     ZeroMemory(&net_res, sizeof(net_res));
2779 
2780     conn_dlg.cbStructure = sizeof(conn_dlg);
2781     conn_dlg.lpConnRes = &net_res;
2782     conn_dlg.hwndOwner = hwnd;
2783     net_res.dwType = dwType;
2784 
2785     return WNetConnectionDialog1W(&conn_dlg);
2786 }
2787 
2788 /*********************************************************************
2789  * WNetConnectionDialog1A [MPR.@]
2790  */
2791 DWORD WINAPI WNetConnectionDialog1A( LPCONNECTDLGSTRUCTA lpConnDlgStruct )
2792 {
2793     FIXME( "(%p): stub\n", lpConnDlgStruct );
2794 
2795     SetLastError(WN_NO_NETWORK);
2796     return WN_NO_NETWORK;
2797 }
2798 
2799 /*********************************************************************
2800  * WNetConnectionDialog1W [MPR.@]
2801  */
2802 DWORD WINAPI WNetConnectionDialog1W( LPCONNECTDLGSTRUCTW lpConnDlgStruct )
2803 {
2804     FIXME( "(%p): stub\n", lpConnDlgStruct );
2805 
2806     SetLastError(WN_NO_NETWORK);
2807     return WN_NO_NETWORK;
2808 }
2809 
2810 /*********************************************************************
2811  * WNetDisconnectDialog [MPR.@]
2812  */
2813 DWORD WINAPI WNetDisconnectDialog( HWND hwnd, DWORD dwType )
2814 {
2815     FIXME( "(%p, %08X): stub\n", hwnd, dwType );
2816 
2817     SetLastError(WN_NO_NETWORK);
2818     return WN_NO_NETWORK;
2819 }
2820 
2821 /*********************************************************************
2822  * WNetDisconnectDialog1A [MPR.@]
2823  */
2824 DWORD WINAPI WNetDisconnectDialog1A( LPDISCDLGSTRUCTA lpConnDlgStruct )
2825 {
2826     FIXME( "(%p): stub\n", lpConnDlgStruct );
2827 
2828     SetLastError(WN_NO_NETWORK);
2829     return WN_NO_NETWORK;
2830 }
2831 
2832 /*********************************************************************
2833  * WNetDisconnectDialog1W [MPR.@]
2834  */
2835 DWORD WINAPI WNetDisconnectDialog1W( LPDISCDLGSTRUCTW lpConnDlgStruct )
2836 {
2837     FIXME( "(%p): stub\n", lpConnDlgStruct );
2838 
2839     SetLastError(WN_NO_NETWORK);
2840     return WN_NO_NETWORK;
2841 }
2842 
2843 /*********************************************************************
2844  * WNetGetLastErrorA [MPR.@]
2845  */
2846 DWORD WINAPI WNetGetLastErrorA( LPDWORD lpError,
2847                                 LPSTR lpErrorBuf, DWORD nErrorBufSize,
2848                                 LPSTR lpNameBuf, DWORD nNameBufSize )
2849 {
2850     FIXME( "(%p, %p, %d, %p, %d): stub\n",
2851            lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
2852 
2853     SetLastError(WN_NO_NETWORK);
2854     return WN_NO_NETWORK;
2855 }
2856 
2857 /*********************************************************************
2858  * WNetGetLastErrorW [MPR.@]
2859  */
2860 DWORD WINAPI WNetGetLastErrorW( LPDWORD lpError,
2861                                 LPWSTR lpErrorBuf, DWORD nErrorBufSize,
2862                          LPWSTR lpNameBuf, DWORD nNameBufSize )
2863 {
2864     FIXME( "(%p, %p, %d, %p, %d): stub\n",
2865            lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
2866 
2867     SetLastError(WN_NO_NETWORK);
2868     return WN_NO_NETWORK;
2869 }
2870 
2871 /*********************************************************************
2872  * WNetGetNetworkInformationA [MPR.@]
2873  */
2874 DWORD WINAPI WNetGetNetworkInformationA( LPCSTR lpProvider,
2875                                          LPNETINFOSTRUCT lpNetInfoStruct )
2876 {
2877     DWORD ret;
2878 
2879     TRACE( "(%s, %p)\n", debugstr_a(lpProvider), lpNetInfoStruct );
2880 
2881     if (!lpProvider)
2882         ret = WN_BAD_POINTER;
2883     else
2884     {
2885         int len;
2886 
2887         len = MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, NULL, 0);
2888         if (len)
2889         {
2890             LPWSTR wideProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2891 
2892             if (wideProvider)
2893             {
2894                 MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, wideProvider,
2895                  len);
2896                 ret = WNetGetNetworkInformationW(wideProvider, lpNetInfoStruct);
2897                 HeapFree(GetProcessHeap(), 0, wideProvider);
2898             }
2899             else
2900                 ret = WN_OUT_OF_MEMORY;
2901         }
2902         else
2903             ret = GetLastError();
2904     }
2905     if (ret)
2906         SetLastError(ret);
2907     TRACE("Returning %d\n", ret);
2908     return ret;
2909 }
2910 
2911 /*********************************************************************
2912  * WNetGetNetworkInformationW [MPR.@]
2913  */
2914 DWORD WINAPI WNetGetNetworkInformationW( LPCWSTR lpProvider,
2915                                          LPNETINFOSTRUCT lpNetInfoStruct )
2916 {
2917     DWORD ret;
2918 
2919     TRACE( "(%s, %p)\n", debugstr_w(lpProvider), lpNetInfoStruct );
2920 
2921     if (!lpProvider)
2922         ret = WN_BAD_POINTER;
2923     else if (!lpNetInfoStruct)
2924         ret = WN_BAD_POINTER;
2925     else if (lpNetInfoStruct->cbStructure < sizeof(NETINFOSTRUCT))
2926         ret = WN_BAD_VALUE;
2927     else
2928     {
2929         if (providerTable && providerTable->numProviders)
2930         {
2931             DWORD providerIndex = _findProviderIndexW(lpProvider);
2932 
2933             if (providerIndex != BAD_PROVIDER_INDEX)
2934             {
2935                 lpNetInfoStruct->cbStructure = sizeof(NETINFOSTRUCT);
2936                 lpNetInfoStruct->dwProviderVersion =
2937                  providerTable->table[providerIndex].dwSpecVersion;
2938                 lpNetInfoStruct->dwStatus = NO_ERROR;
2939                 lpNetInfoStruct->dwCharacteristics = 0;
2940                 lpNetInfoStruct->dwHandle = 0;
2941                 lpNetInfoStruct->wNetType =
2942                  HIWORD(providerTable->table[providerIndex].dwNetType);
2943                 lpNetInfoStruct->dwPrinters = -1;
2944                 lpNetInfoStruct->dwDrives = -1;
2945                 ret = WN_SUCCESS;
2946             }
2947             else
2948                 ret = WN_BAD_PROVIDER;
2949         }
2950         else
2951             ret = WN_NO_NETWORK;
2952     }
2953     if (ret)
2954         SetLastError(ret);
2955     TRACE("Returning %d\n", ret);
2956     return ret;
2957 }
2958 
2959 /*****************************************************************
2960  *  WNetGetProviderNameA [MPR.@]
2961  */
2962 DWORD WINAPI WNetGetProviderNameA( DWORD dwNetType,
2963                                    LPSTR lpProvider, LPDWORD lpBufferSize )
2964 {
2965     DWORD ret;
2966 
2967     TRACE("(0x%08x, %s, %p)\n", dwNetType, debugstr_a(lpProvider),
2968      lpBufferSize);
2969 
2970     if (!lpProvider)
2971         ret = WN_BAD_POINTER;
2972     else if (!lpBufferSize)
2973         ret = WN_BAD_POINTER;
2974     else
2975     {
2976         if (providerTable)
2977         {
2978             DWORD i;
2979 
2980             ret = WN_NO_NETWORK;
2981             for (i = 0; i < providerTable->numProviders &&
2982              HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
2983              i++)
2984                 ;
2985             if (i < providerTable->numProviders)
2986             {
2987                 DWORD sizeNeeded = WideCharToMultiByte(CP_ACP, 0,
2988                  providerTable->table[i].name, -1, NULL, 0, NULL, NULL);
2989 
2990                 if (*lpBufferSize < sizeNeeded)
2991                 {
2992                     *lpBufferSize = sizeNeeded;
2993                     ret = WN_MORE_DATA;
2994                 }
2995                 else
2996                 {
2997                     WideCharToMultiByte(CP_ACP, 0, providerTable->table[i].name,
2998                      -1, lpProvider, *lpBufferSize, NULL, NULL);
2999                     ret = WN_SUCCESS;
3000                     /* FIXME: is *lpBufferSize set to the number of characters
3001                      * copied? */
3002                 }
3003             }
3004         }
3005         else
3006             ret = WN_NO_NETWORK;
3007     }
3008     if (ret)
3009         SetLastError(ret);
3010     TRACE("Returning %d\n", ret);
3011     return ret;
3012 }
3013 
3014 /*****************************************************************
3015  *  WNetGetProviderNameW [MPR.@]
3016  */
3017 DWORD WINAPI WNetGetProviderNameW( DWORD dwNetType,
3018                                    LPWSTR lpProvider, LPDWORD lpBufferSize )
3019 {
3020     DWORD ret;
3021 
3022     TRACE("(0x%08x, %s, %p)\n", dwNetType, debugstr_w(lpProvider),
3023      lpBufferSize);
3024 
3025     if (!lpProvider)
3026         ret = WN_BAD_POINTER;
3027     else if (!lpBufferSize)
3028         ret = WN_BAD_POINTER;
3029     else
3030     {
3031         if (providerTable)
3032         {
3033             DWORD i;
3034 
3035             ret = WN_NO_NETWORK;
3036             for (i = 0; i < providerTable->numProviders &&
3037              HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
3038              i++)
3039                 ;
3040             if (i < providerTable->numProviders)
3041             {
3042                 DWORD sizeNeeded = strlenW(providerTable->table[i].name) + 1;
3043 
3044                 if (*lpBufferSize < sizeNeeded)
3045                 {
3046                     *lpBufferSize = sizeNeeded;
3047                     ret = WN_MORE_DATA;
3048                 }
3049                 else
3050                 {
3051                     strcpyW(lpProvider, providerTable->table[i].name);
3052                     ret = WN_SUCCESS;
3053                     /* FIXME: is *lpBufferSize set to the number of characters
3054                      * copied? */
3055                 }
3056             }
3057         }
3058         else
3059             ret = WN_NO_NETWORK;
3060     }
3061     if (ret)
3062         SetLastError(ret);
3063     TRACE("Returning %d\n", ret);
3064     return ret;
3065 }
3066