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