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