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