Fixed some issues found by winapi_check.
[wine] / dlls / mpr / wnet.c
1 /*
2  * MPR WNet functions
3  *
4  * Copyright 1999 Ulrich Weigand
5  * Copyright 2004 Juan Lang
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <stdarg.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winnls.h"
26 #include "winnetwk.h"
27 #include "npapi.h"
28 #include "winreg.h"
29 #include "winuser.h"
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
32 #include "mprres.h"
33 #include "wnetpriv.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(mpr);
36
37 /* Data structures representing network service providers.  Assumes only one
38  * thread creates them, and that they are constant for the life of the process
39  * (and therefore doesn't synchronize access).
40  * FIXME: only basic provider data and enumeration-related data are implemented
41  * so far, need to implement the rest too.
42  */
43 typedef struct _WNetProvider
44 {
45     HMODULE           hLib;
46     PWSTR             name;
47     PF_NPGetCaps      getCaps;
48     DWORD             dwSpecVersion;
49     DWORD             dwNetType;
50     DWORD             dwEnumScopes;
51     PF_NPOpenEnum     openEnum;
52     PF_NPEnumResource enumResource;
53     PF_NPCloseEnum    closeEnum;
54 } WNetProvider, *PWNetProvider;
55
56 typedef struct _WNetProviderTable
57 {
58     LPWSTR           entireNetwork;
59     DWORD            numAllocated;
60     DWORD            numProviders;
61     WNetProvider     table[1];
62 } WNetProviderTable, *PWNetProviderTable;
63
64 #define WNET_ENUMERATOR_TYPE_NULL     0
65 #define WNET_ENUMERATOR_TYPE_GLOBAL   1
66 #define WNET_ENUMERATOR_TYPE_PROVIDER 2
67 #define WNET_ENUMERATOR_TYPE_CONTEXT  3
68
69 /* An WNet enumerator.  Note that the type doesn't correspond to the scope of
70  * the enumeration; it represents one of the following types:
71  * - a 'null' enumeration, one that contains no members
72  * - a global enumeration, one that's executed across all providers
73  * - a provider-specific enumeration, one that's only executed by a single
74  *   provider
75  * - a context enumeration.  I know this contradicts what I just said about
76  *   there being no correspondence between the scope and the type, but it's
77  *   necessary for the special case that a "Entire Network" entry needs to
78  *   be enumerated in an enumeration of the context scope.  Thus an enumeration
79  *   of the context scope results in a context type enumerator, which morphs
80  *   into a global enumeration (so the enumeration continues across all
81  *   providers).
82  */
83 typedef struct _WNetEnumerator
84 {
85     DWORD          enumType;
86     DWORD          providerIndex;
87     HANDLE         handle;
88     BOOL           providerDone;
89     DWORD          dwScope;
90     DWORD          dwType;
91     DWORD          dwUsage;
92     LPNETRESOURCEW lpNet;
93 } WNetEnumerator, *PWNetEnumerator;
94
95 #define BAD_PROVIDER_INDEX (DWORD)0xffffffff
96
97 /* Returns an index (into the global WNetProviderTable) of the provider with
98  * the given name, or BAD_PROVIDER_INDEX if not found.
99  */
100 static DWORD _findProviderIndexW(LPCWSTR lpProvider);
101
102 PWNetProviderTable providerTable;
103
104 /*
105  * Global provider table functions
106  */
107
108 static void _tryLoadProvider(PCWSTR provider)
109 {
110     static const WCHAR servicePrefix[] = { 'S','y','s','t','e','m','\\',
111      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
112      'S','e','r','v','i','c','e','s','\\',0 };
113     static const WCHAR serviceFmt[] = { '%','s','%','s','\\',
114      'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r',0 };
115     WCHAR serviceName[MAX_PATH];
116     HKEY hKey;
117
118     TRACE("%s\n", debugstr_w(provider));
119     snprintfW(serviceName, sizeof(serviceName) / sizeof(WCHAR), serviceFmt,
120      servicePrefix, provider);
121     serviceName[sizeof(serviceName) / sizeof(WCHAR) - 1] = '\0';
122     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, serviceName, 0, KEY_READ, &hKey) ==
123      ERROR_SUCCESS)
124     {
125         static const WCHAR szProviderPath[] = { 'P','r','o','v','i','d','e','r',
126          'P','a','t','h',0 };
127         WCHAR providerPath[MAX_PATH];
128         DWORD type, size = sizeof(providerPath);
129
130         if (RegQueryValueExW(hKey, szProviderPath, NULL, &type,
131          (LPBYTE)providerPath, &size) == ERROR_SUCCESS && type == REG_SZ)
132         {
133             static const WCHAR szProviderName[] = { 'N','a','m','e',0 };
134             PWSTR name = NULL;
135            
136             size = 0;
137             RegQueryValueExW(hKey, szProviderName, NULL, NULL, NULL, &size);
138             if (size)
139             {
140                 name = (PWSTR)HeapAlloc(GetProcessHeap(), 0, size);
141                 if (RegQueryValueExW(hKey, szProviderName, NULL, &type,
142                  (LPBYTE)name, &size) != ERROR_SUCCESS || type != REG_SZ)
143                 {
144                     HeapFree(GetProcessHeap(), 0, name);
145                     name = NULL;
146                 }
147             }
148             if (name)
149             {
150                 HMODULE hLib = LoadLibraryW(providerPath);
151
152                 if (hLib)
153                 {
154                     PF_NPGetCaps getCaps = (PF_NPGetCaps)GetProcAddress(hLib,
155                      "NPGetCaps");
156
157                     TRACE("loaded lib %p\n", hLib);
158                     if (getCaps)
159                     {
160                         PWNetProvider provider =
161                          &providerTable->table[providerTable->numProviders];
162
163                         provider->hLib = hLib;
164                         provider->name = name;
165                         TRACE("name is %s\n", debugstr_w(name));
166                         provider->getCaps = getCaps;
167                         provider->dwSpecVersion = getCaps(WNNC_SPEC_VERSION);
168                         provider->dwNetType = getCaps(WNNC_NET_TYPE);
169                         TRACE("net type is 0x%08lx\n", provider->dwNetType);
170                         provider->dwEnumScopes = getCaps(WNNC_ENUMERATION);
171                         if (provider->dwEnumScopes)
172                         {
173                             TRACE("supports enumeration\n");
174                             provider->openEnum = (PF_NPOpenEnum)
175                              GetProcAddress(hLib, "NPOpenEnum");
176                             TRACE("openEnum is %p\n", provider->openEnum);
177                             provider->enumResource = (PF_NPEnumResource)
178                              GetProcAddress(hLib, "NPEnumResource");
179                             TRACE("enumResource is %p\n",
180                              provider->enumResource);
181                             provider->closeEnum = (PF_NPCloseEnum)
182                              GetProcAddress(hLib, "NPCloseEnum");
183                             TRACE("closeEnum is %p\n", provider->closeEnum);
184                             if (!provider->openEnum || !provider->enumResource
185                              || !provider->closeEnum)
186                             {
187                                 provider->openEnum = NULL;
188                                 provider->enumResource = NULL;
189                                 provider->closeEnum = NULL;
190                                 provider->dwEnumScopes = 0;
191                                 WARN("Couldn't load enumeration functions\n");
192                             }
193                         }
194                         providerTable->numProviders++;
195                     }
196                     else
197                     {
198                         WARN("Provider %s didn't export NPGetCaps\n",
199                          debugstr_w(provider));
200                         if (name)
201                             HeapFree(GetProcessHeap(), 0, name);
202                         FreeLibrary(hLib);
203                     }
204                 }
205                 else
206                 {
207                     WARN("Couldn't load library %s for provider %s\n",
208                      debugstr_w(providerPath), debugstr_w(provider));
209                     HeapFree(GetProcessHeap(), 0, name);
210                 }
211             }
212             else
213             {
214                 WARN("Couldn't get provider name for provider %s\n",
215                  debugstr_w(provider));
216             }
217         }
218         else
219             WARN("Couldn't open value %s\n", debugstr_w(szProviderPath));
220         RegCloseKey(hKey);
221     }
222     else
223         WARN("Couldn't open service key for provider %s\n",
224          debugstr_w(provider));
225 }
226
227 void wnetInit(HINSTANCE hInstDll)
228 {
229     static const WCHAR providerOrderKey[] = { 'S','y','s','t','e','m','\\',
230      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
231      'C','o','n','t','r','o','l','\\',
232      'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r','\\',
233      'O','r','d','e','r',0 };
234      static const WCHAR providerOrder[] = { 'P','r','o','v','i','d','e','r',
235       'O','r','d','e','r',0 };
236     HKEY hKey;
237
238     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, providerOrderKey, 0, KEY_READ, &hKey)
239      == ERROR_SUCCESS)
240     {
241         DWORD size = 0;
242
243         RegQueryValueExW(hKey, providerOrder, NULL, NULL, NULL, &size);
244         if (size)
245         {
246             PWSTR providers = (PWSTR)HeapAlloc(GetProcessHeap(), 0, size);
247
248             if (providers)
249             {
250                 DWORD type;
251
252                 if (RegQueryValueExW(hKey, providerOrder, NULL, &type,
253                  (LPBYTE)providers, &size) == ERROR_SUCCESS && type == REG_SZ)
254                 {
255                     PWSTR ptr;
256                     DWORD numToAllocate;
257
258                     TRACE("provider order is %s\n", debugstr_w(providers));
259                     /* first count commas as a heuristic for how many to
260                      * allocate space for */
261                     for (ptr = providers, numToAllocate = 1; ptr; )
262                     {
263                         ptr = strchrW(ptr, ',');
264                         if (ptr)
265                             numToAllocate++;
266                     }
267                     providerTable = (PWNetProviderTable)
268                      HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
269                      sizeof(WNetProviderTable)
270                      + (numToAllocate - 1) * sizeof(WNetProvider));
271                     if (providerTable)
272                     {
273                         PWSTR ptrPrev;
274                         int entireNetworkLen;
275
276                         entireNetworkLen = LoadStringW(hInstDll,
277                          IDS_ENTIRENETWORK, NULL, 0);
278                         providerTable->entireNetwork = (LPWSTR)HeapAlloc(
279                          GetProcessHeap(), 0, (entireNetworkLen + 1) *
280                          sizeof(WCHAR));
281                         if (providerTable->entireNetwork)
282                             LoadStringW(hInstDll, IDS_ENTIRENETWORK,
283                              providerTable->entireNetwork,
284                              entireNetworkLen + 1);
285                         providerTable->numAllocated = numToAllocate;
286                         for (ptr = providers; ptr; )
287                         {
288                             ptrPrev = ptr;
289                             ptr = strchrW(ptr, ',');
290                             if (ptr)
291                                 *ptr = '\0';
292                             _tryLoadProvider(ptrPrev);
293                         }
294                     }
295                 }
296                 HeapFree(GetProcessHeap(), 0, providers);
297             }
298         }
299         RegCloseKey(hKey);
300     }
301 }
302
303 void wnetFree(void)
304 {
305     if (providerTable)
306     {
307         DWORD i;
308
309         for (i = 0; i < providerTable->numProviders; i++)
310         {
311             HeapFree(GetProcessHeap(), 0, providerTable->table[i].name);
312             FreeModule(providerTable->table[i].hLib);
313         }
314         if (providerTable->entireNetwork)
315             HeapFree(GetProcessHeap(), 0, providerTable->entireNetwork);
316         HeapFree(GetProcessHeap(), 0, providerTable);
317         providerTable = NULL;
318     }
319 }
320
321 static DWORD _findProviderIndexW(LPCWSTR lpProvider)
322 {
323     DWORD ret = BAD_PROVIDER_INDEX;
324
325     if (providerTable && providerTable->numProviders)
326     {
327         DWORD i;
328
329         for (i = 0; i < providerTable->numProviders &&
330          ret == BAD_PROVIDER_INDEX; i++)
331             if (!strcmpW(lpProvider, providerTable->table[i].name))
332                 ret = i;
333     }
334     return ret;
335 }
336
337 /*
338  * Browsing Functions
339  */
340
341 static LPNETRESOURCEW _copyNetResourceForEnumW(LPNETRESOURCEW lpNet)
342 {
343     LPNETRESOURCEW ret;
344
345     if (lpNet)
346     {
347         ret = (LPNETRESOURCEW)HeapAlloc(GetProcessHeap(), 0,
348          sizeof(NETRESOURCEW));
349         if (ret)
350         {
351             size_t len;
352
353             memcpy(ret, lpNet, sizeof(ret));
354             ret->lpLocalName = ret->lpComment = ret->lpProvider = NULL;
355             if (lpNet->lpRemoteName)
356             {
357                 len = strlenW(lpNet->lpRemoteName) + 1;
358                 ret->lpRemoteName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
359                  len * sizeof(WCHAR));
360                 if (ret->lpRemoteName)
361                     strcpyW(ret->lpRemoteName, lpNet->lpRemoteName);
362             }
363         }
364     }
365     else
366         ret = NULL;
367     return ret;
368 }
369
370 static void _freeEnumNetResource(LPNETRESOURCEW lpNet)
371 {
372     if (lpNet)
373     {
374         if (lpNet->lpRemoteName)
375             HeapFree(GetProcessHeap(), 0, lpNet->lpRemoteName);
376         HeapFree(GetProcessHeap(), 0, lpNet);
377     }
378 }
379
380 static PWNetEnumerator _createNullEnumerator(void)
381 {
382     PWNetEnumerator ret = (PWNetEnumerator)HeapAlloc(GetProcessHeap(),
383      HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
384
385     if (ret)
386         ret->enumType = WNET_ENUMERATOR_TYPE_NULL;
387     return ret;
388 }
389
390 static PWNetEnumerator _createGlobalEnumeratorW(DWORD dwScope, DWORD dwType,
391  DWORD dwUsage, LPNETRESOURCEW lpNet)
392 {
393     PWNetEnumerator ret = (PWNetEnumerator)HeapAlloc(GetProcessHeap(),
394      HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
395
396     if (ret)
397     {
398         ret->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
399         ret->dwScope = dwScope;
400         ret->dwType  = dwType;
401         ret->dwUsage = dwUsage;
402         ret->lpNet   = _copyNetResourceForEnumW(lpNet);
403     }
404     return ret;
405 }
406
407 static PWNetEnumerator _createProviderEnumerator(DWORD dwScope, DWORD dwType,
408  DWORD dwUsage, DWORD index, HANDLE handle)
409 {
410     PWNetEnumerator ret;
411
412     if (!providerTable || index >= providerTable->numProviders)
413         ret = NULL;
414     else
415     {
416         ret = (PWNetEnumerator)HeapAlloc(GetProcessHeap(),
417          HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
418         if (ret)
419         {
420             ret->enumType      = WNET_ENUMERATOR_TYPE_PROVIDER;
421             ret->providerIndex = index;
422             ret->dwScope       = dwScope;
423             ret->dwType        = dwType;
424             ret->dwUsage       = dwUsage;
425             ret->handle        = handle;
426         }
427     }
428     return ret;
429 }
430
431 static PWNetEnumerator _createContextEnumerator(DWORD dwScope, DWORD dwType,
432  DWORD dwUsage)
433 {
434     PWNetEnumerator ret = (PWNetEnumerator)HeapAlloc(GetProcessHeap(),
435      HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
436
437     if (ret)
438     {
439         ret->enumType = WNET_ENUMERATOR_TYPE_CONTEXT;
440         ret->dwScope = dwScope;
441         ret->dwType  = dwType;
442         ret->dwUsage = dwUsage;
443     }
444     return ret;
445 }
446
447 /* Thunks the array of wide-string LPNETRESOURCEs lpNetArrayIn into buffer
448  * lpBuffer, with size *lpBufferSize.  lpNetArrayIn contains *lpcCount entries
449  * to start.  On return, *lpcCount reflects the number thunked into lpBuffer.
450  * Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA
451  * if not all members of the array could be thunked, and something else on
452  * failure.
453  */
454 static DWORD _thunkNetResourceArrayWToA(const LPNETRESOURCEW lpNetArrayIn,
455  LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
456 {
457     DWORD i, numToThunk, totalBytes, ret;
458     LPSTR strNext;
459
460     if (!lpNetArrayIn)
461         return WN_BAD_POINTER;
462     if (!lpcCount)
463         return WN_BAD_POINTER;
464     if (*lpcCount == -1)
465         return WN_BAD_VALUE;
466     if (!lpBuffer)
467         return WN_BAD_POINTER;
468     if (!lpBufferSize)
469         return WN_BAD_POINTER;
470
471     for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++)
472     {
473         LPNETRESOURCEW lpNet = lpNetArrayIn + i;
474
475         totalBytes += sizeof(NETRESOURCEA);
476         if (lpNet->lpLocalName)
477             totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpLocalName,
478              -1, NULL, 0, NULL, NULL);
479         if (lpNet->lpRemoteName)
480             totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpRemoteName,
481              -1, NULL, 0, NULL, NULL);
482         if (lpNet->lpComment)
483             totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpComment,
484              -1, NULL, 0, NULL, NULL);
485         if (lpNet->lpProvider)
486             totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpProvider,
487              -1, NULL, 0, NULL, NULL);
488         if (totalBytes < *lpBufferSize)
489             numToThunk = i + 1;
490     }
491     strNext = (LPSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEA));
492     for (i = 0; i < numToThunk; i++)
493     {
494         LPNETRESOURCEA lpNetOut = (LPNETRESOURCEA)lpBuffer + i;
495         LPNETRESOURCEW lpNetIn = lpNetArrayIn + i;
496
497         memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEA));
498         /* lie about string lengths, we already verified how many
499          * we have space for above
500          */
501         if (lpNetIn->lpLocalName)
502         {
503             lpNetOut->lpLocalName = strNext;
504             strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpLocalName, -1,
505              lpNetOut->lpLocalName, *lpBufferSize, NULL, NULL);
506         }
507         if (lpNetIn->lpRemoteName)
508         {
509             lpNetOut->lpRemoteName = strNext;
510             strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpRemoteName, -1,
511              lpNetOut->lpRemoteName, *lpBufferSize, NULL, NULL);
512         }
513         if (lpNetIn->lpComment)
514         {
515             lpNetOut->lpComment = strNext;
516             strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpComment, -1,
517              lpNetOut->lpComment, *lpBufferSize, NULL, NULL);
518         }
519         if (lpNetIn->lpProvider)
520         {
521             lpNetOut->lpProvider = strNext;
522             strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpProvider, -1,
523              lpNetOut->lpProvider, *lpBufferSize, NULL, NULL);
524         }
525     }
526     ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS;
527     TRACE("numToThunk is %ld, *lpcCount is %ld, returning %ld\n", numToThunk,
528      *lpcCount, ret);
529     return ret;
530 }
531
532 /* Thunks the array of multibyte-string LPNETRESOURCEs lpNetArrayIn into buffer
533  * lpBuffer, with size *lpBufferSize.  lpNetArrayIn contains *lpcCount entries
534  * to start.  On return, *lpcCount reflects the number thunked into lpBuffer.
535  * Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA
536  * if not all members of the array could be thunked, and something else on
537  * failure.
538  */
539 static DWORD _thunkNetResourceArrayAToW(const LPNETRESOURCEA lpNetArrayIn,
540  LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
541 {
542     DWORD i, numToThunk, totalBytes, ret;
543     LPWSTR strNext;
544
545     if (!lpNetArrayIn)
546         return WN_BAD_POINTER;
547     if (!lpcCount)
548         return WN_BAD_POINTER;
549     if (*lpcCount == -1)
550         return WN_BAD_VALUE;
551     if (!lpBuffer)
552         return WN_BAD_POINTER;
553     if (!lpBufferSize)
554         return WN_BAD_POINTER;
555
556     for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++)
557     {
558         LPNETRESOURCEA lpNet = lpNetArrayIn + i;
559
560         totalBytes += sizeof(NETRESOURCEW);
561         if (lpNet->lpLocalName)
562             totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpLocalName,
563              -1, NULL, 0) * sizeof(WCHAR);
564         if (lpNet->lpRemoteName)
565             totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpRemoteName,
566              -1, NULL, 0) * sizeof(WCHAR);
567         if (lpNet->lpComment)
568             totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpComment,
569              -1, NULL, 0) * sizeof(WCHAR);
570         if (lpNet->lpProvider)
571             totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpProvider,
572              -1, NULL, 0) * sizeof(WCHAR);
573         if (totalBytes < *lpBufferSize)
574             numToThunk = i + 1;
575     }
576     strNext = (LPWSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEW));
577     for (i = 0; i < numToThunk; i++)
578     {
579         LPNETRESOURCEW lpNetOut = (LPNETRESOURCEW)lpBuffer + i;
580         LPNETRESOURCEA lpNetIn = lpNetArrayIn + i;
581
582         memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEW));
583         /* lie about string lengths, we already verified how many
584          * we have space for above
585          */
586         if (lpNetIn->lpLocalName)
587         {
588             lpNetOut->lpLocalName = strNext;
589             strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpLocalName,
590              -1, lpNetOut->lpLocalName, *lpBufferSize);
591         }
592         if (lpNetIn->lpRemoteName)
593         {
594             lpNetOut->lpRemoteName = strNext;
595             strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpRemoteName,
596              -1, lpNetOut->lpRemoteName, *lpBufferSize);
597         }
598         if (lpNetIn->lpComment)
599         {
600             lpNetOut->lpComment = strNext;
601             strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpComment,
602              -1, lpNetOut->lpComment, *lpBufferSize);
603         }
604         if (lpNetIn->lpProvider)
605         {
606             lpNetOut->lpProvider = strNext;
607             strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpProvider,
608              -1, lpNetOut->lpProvider, *lpBufferSize);
609         }
610     }
611     ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS;
612     TRACE("numToThunk is %ld, *lpcCount is %ld, returning %ld\n", numToThunk,
613      *lpcCount, ret);
614     return ret;
615 }
616
617 /*********************************************************************
618  * WNetOpenEnumA [MPR.@]
619  *
620  * See comments for WNetOpenEnumW.
621  */
622 DWORD WINAPI WNetOpenEnumA( DWORD dwScope, DWORD dwType, DWORD dwUsage,
623                             LPNETRESOURCEA lpNet, LPHANDLE lphEnum )
624 {
625     DWORD ret;
626
627     TRACE( "(%08lX, %08lX, %08lX, %p, %p)\n",
628             dwScope, dwType, dwUsage, lpNet, lphEnum );
629
630     if (!lphEnum)
631         ret = WN_BAD_POINTER;
632     else if (!providerTable || providerTable->numProviders == 0)
633         ret = WN_NO_NETWORK;
634     else
635     {
636         if (lpNet)
637         {
638             LPNETRESOURCEW lpNetWide = NULL;
639             BYTE buf[1024];
640             DWORD size = sizeof(buf), count = 1;
641             BOOL allocated = FALSE;
642
643             ret = _thunkNetResourceArrayAToW(lpNet, &count, buf, &size);
644             if (ret == WN_MORE_DATA)
645             {
646                 lpNetWide = (LPNETRESOURCEW)HeapAlloc(GetProcessHeap(), 0,
647                  size);
648                 if (lpNetWide)
649                 {
650                     ret = _thunkNetResourceArrayAToW(lpNet, &count, lpNetWide,
651                      &size);
652                     allocated = TRUE;
653                 }
654                 else
655                     ret = WN_OUT_OF_MEMORY;
656             }
657             else if (ret == WN_SUCCESS)
658                 lpNetWide = (LPNETRESOURCEW)buf;
659             if (ret == WN_SUCCESS)
660                 ret = WNetOpenEnumW(dwScope, dwType, dwUsage, lpNetWide,
661                  lphEnum);
662             if (allocated && lpNetWide)
663                 HeapFree(GetProcessHeap(), 0, lpNetWide);
664         }
665         else
666             ret = WNetOpenEnumW(dwScope, dwType, dwUsage, NULL, lphEnum);
667     }
668     if (ret)
669         SetLastError(ret);
670     TRACE("Returning %ld\n", ret);
671     return ret;
672 }
673
674 /*********************************************************************
675  * WNetOpenEnumW [MPR.@]
676  *
677  * Network enumeration has way too many parameters, so I'm not positive I got
678  * them right.  What I've got so far:
679  *
680  * - If the scope is RESOURCE_GLOBALNET, and no LPNETRESOURCE is passed,
681  *   all the network providers should be enumerated.
682  *
683  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
684  *   and neither the LPNETRESOURCE's lpRemoteName nor the LPNETRESOURCE's
685  *   lpProvider is set, all the network providers should be enumerated.
686  *   (This means the enumeration is a list of network providers, not that the
687  *   enumeration is passed on to the providers.)
688  *
689  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and the
690  *   resource matches the "Entire Network" resource (no remote name, no
691  *   provider, comment is the "Entire Network" string), a RESOURCE_GLOBALNET
692  *   enumeration is done on every network provider.
693  *
694  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
695  *   the LPNETRESOURCE's lpProvider is set, enumeration will be passed through
696  *   only to the given network provider.
697  *
698  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
699  *   no lpProvider is set, enumeration will be tried on every network provider,
700  *   in the order in which they're loaded.
701  *
702  * - The LPNETRESOURCE should be disregarded for scopes besides
703  *   RESOURCE_GLOBALNET.  MSDN states that lpNet must be NULL if dwScope is not
704  *   RESOURCE_GLOBALNET, but Windows doesn't return an error if it isn't NULL.
705  *
706  * - If the scope is RESOURCE_CONTEXT, MS includes an "Entire Network" net
707  *   resource in the enumerated list, as well as any machines in your
708  *   workgroup.  The machines in your workgroup come from doing a
709  *   RESOURCE_CONTEXT enumeration of every Network Provider.
710  */
711 DWORD WINAPI WNetOpenEnumW( DWORD dwScope, DWORD dwType, DWORD dwUsage,
712                             LPNETRESOURCEW lpNet, LPHANDLE lphEnum )
713 {
714     DWORD ret;
715
716     TRACE( "(%08lX, %08lX, %08lX, %p, %p)\n",
717           dwScope, dwType, dwUsage, lpNet, lphEnum );
718
719     if (!lphEnum)
720         ret = WN_BAD_POINTER;
721     else if (!providerTable || providerTable->numProviders == 0)
722         ret = WN_NO_NETWORK;
723     else
724     {
725         switch (dwScope)
726         {
727             case RESOURCE_GLOBALNET:
728                 if (lpNet)
729                 {
730                     if (lpNet->lpProvider)
731                     {
732                         DWORD index = _findProviderIndexW(lpNet->lpProvider);
733
734                         if (index != BAD_PROVIDER_INDEX)
735                         {
736                             if (providerTable->table[index].openEnum &&
737                              providerTable->table[index].dwEnumScopes & dwScope)
738                             {
739                                 HANDLE handle;
740
741                                 ret = providerTable->table[index].openEnum(
742                                  dwScope, dwType, dwUsage, lpNet, &handle);
743                                 if (ret == WN_SUCCESS)
744                                 {
745                                     *lphEnum =
746                                      (HANDLE)_createProviderEnumerator(
747                                      dwScope, dwType, dwUsage, index, handle);
748                                     ret = *lphEnum ? WN_SUCCESS :
749                                      WN_OUT_OF_MEMORY;
750                                 }
751                             }
752                             else
753                                 ret = WN_NOT_SUPPORTED;
754                         }
755                         else
756                             ret = WN_BAD_PROVIDER;
757                     }
758                     else if (lpNet->lpRemoteName)
759                     {
760                         *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope,
761                          dwType, dwUsage, lpNet);
762                         ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
763                     }
764                     else
765                     {
766                         if (lpNet->lpComment && !strcmpW(lpNet->lpComment,
767                          providerTable->entireNetwork))
768                         {
769                             /* comment matches the "Entire Network", enumerate
770                              * global scope of every provider
771                              */
772                             *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope,
773                              dwType, dwUsage, lpNet);
774                         }
775                         else
776                         {
777                             /* this is the same as not having passed lpNet */
778                             *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope,
779                              dwType, dwUsage, NULL);
780                         }
781                         ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
782                     }
783                 }
784                 else
785                 {
786                     *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, dwType,
787                      dwUsage, lpNet);
788                     ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
789                 }
790                 break;
791             case RESOURCE_CONTEXT:
792                 *lphEnum = (HANDLE)_createContextEnumerator(dwScope, dwType,
793                  dwUsage);
794                 ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
795                 break;
796             case RESOURCE_REMEMBERED:
797             case RESOURCE_CONNECTED:
798                 *lphEnum = (HANDLE)_createNullEnumerator();
799                 ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
800                 break;
801             default:
802                 WARN("unknown scope 0x%08lx\n", dwScope);
803                 ret = WN_BAD_VALUE;
804         }
805     }
806     if (ret)
807         SetLastError(ret);
808     TRACE("Returning %ld\n", ret);
809     return ret;
810 }
811
812 /*********************************************************************
813  * WNetEnumResourceA [MPR.@]
814  */
815 DWORD WINAPI WNetEnumResourceA( HANDLE hEnum, LPDWORD lpcCount,
816                                 LPVOID lpBuffer, LPDWORD lpBufferSize )
817 {
818     DWORD ret;
819
820     TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
821
822     if (!hEnum)
823         ret = WN_BAD_POINTER;
824     else if (!lpcCount)
825         ret = WN_BAD_POINTER;
826     if (!lpBuffer)
827         ret = WN_BAD_POINTER;
828     else if (!lpBufferSize)
829         ret = WN_BAD_POINTER;
830     else if (*lpBufferSize < sizeof(NETRESOURCEA))
831     {
832         *lpBufferSize = sizeof(NETRESOURCEA);
833         ret = WN_MORE_DATA;
834     }
835     else
836     {
837         DWORD localCount = *lpcCount, localSize = *lpBufferSize;
838         LPVOID localBuffer = HeapAlloc(GetProcessHeap(), 0, localSize);
839
840         if (localBuffer)
841         {
842             ret = WNetEnumResourceW(hEnum, &localCount, localBuffer,
843              &localSize);
844             if (ret == WN_SUCCESS || (ret == WN_MORE_DATA && localCount != -1))
845             {
846                 /* FIXME: this isn't necessarily going to work in the case of
847                  * WN_MORE_DATA, because our enumerator may have moved on to
848                  * the next provider.  MSDN states that a large (16KB) buffer
849                  * size is the appropriate usage of this function, so
850                  * hopefully it won't be an issue.
851                  */
852                 ret = _thunkNetResourceArrayWToA((LPNETRESOURCEW)localBuffer,
853                  &localCount, lpBuffer, lpBufferSize);
854                 *lpcCount = localCount;
855             }
856             HeapFree(GetProcessHeap(), 0, localBuffer);
857         }
858         else
859             ret = WN_OUT_OF_MEMORY;
860     }
861     if (ret)
862         SetLastError(ret);
863     TRACE("Returning %ld\n", ret);
864     return ret;
865 }
866
867 static DWORD _countProviderBytesW(PWNetProvider provider)
868 {
869     DWORD ret;
870
871     if (provider)
872     {
873         ret = sizeof(NETRESOURCEW);
874         ret += 2 * (strlenW(provider->name) + 1) * sizeof(WCHAR);
875     }
876     else
877         ret = 0;
878     return ret;
879 }
880
881 static DWORD _enumerateProvidersW(PWNetEnumerator enumerator, LPDWORD lpcCount,
882  LPVOID lpBuffer, LPDWORD lpBufferSize)
883 {
884     DWORD ret;
885
886     if (!enumerator)
887         return WN_BAD_POINTER;
888     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
889         return WN_BAD_VALUE;
890     if (!lpcCount)
891         return WN_BAD_POINTER;
892     if (!lpBuffer)
893         return WN_BAD_POINTER;
894     if (!lpBufferSize)
895         return WN_BAD_POINTER;
896     if (*lpBufferSize < sizeof(NETRESOURCEA))
897         return WN_MORE_DATA;
898
899     if (!providerTable || enumerator->providerIndex >= 
900      providerTable->numProviders)
901         ret = WN_NO_MORE_ENTRIES;
902     else
903     {
904         DWORD bytes = 0, count = 0, countLimit, i;
905         LPNETRESOURCEW resource;
906         LPWSTR strNext;
907
908         countLimit = *lpcCount == -1 ?
909          providerTable->numProviders - enumerator->providerIndex : *lpcCount;
910         while (count < countLimit && bytes < *lpBufferSize)
911         {
912             DWORD bytesNext = _countProviderBytesW(
913              &providerTable->table[count + enumerator->providerIndex]);
914
915             if (bytes + bytesNext < *lpBufferSize)
916             {
917                 bytes += bytesNext;
918                 count++;
919             }
920         }
921         strNext = (LPWSTR)((LPBYTE)lpBuffer + count * sizeof(NETRESOURCEW));
922         for (i = 0, resource = (LPNETRESOURCEW)lpBuffer; i < count;
923          i++, resource++)
924         {
925             resource->dwScope = RESOURCE_GLOBALNET;
926             resource->dwType = RESOURCETYPE_ANY;
927             resource->dwDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
928             resource->dwUsage = RESOURCEUSAGE_CONTAINER |
929              RESOURCEUSAGE_RESERVED;
930             resource->lpLocalName = NULL;
931             resource->lpRemoteName = strNext;
932             strcpyW(resource->lpRemoteName,
933              providerTable->table[i + enumerator->providerIndex].name);
934             strNext += strlenW(resource->lpRemoteName) + 1;
935             resource->lpComment = NULL;
936             resource->lpProvider = strNext;
937             strcpyW(resource->lpProvider,
938              providerTable->table[i + enumerator->providerIndex].name);
939             strNext += strlenW(resource->lpProvider) + 1;
940         }
941         enumerator->providerIndex += count;
942         *lpcCount = count;
943         ret = count > 0 ? WN_SUCCESS : WN_MORE_DATA;
944     }
945     TRACE("Returning %ld\n", ret);
946     return ret;
947 }
948
949 /* Advances the enumerator (assumed to be a global enumerator) to the next
950  * provider that supports the enumeration scope passed to WNetOpenEnum.  Does
951  * not open a handle with the next provider.
952  * If the existing handle is NULL, may leave the enumerator unchanged, since
953  * the current provider may support the desired scope.
954  * If the existing handle is not NULL, closes it before moving on.
955  * Returns WN_SUCCESS on success, WN_NO_MORE_ENTRIES if there is no available
956  * provider, and another error on failure.
957  */
958 static DWORD _globalEnumeratorAdvance(PWNetEnumerator enumerator)
959 {
960     if (!enumerator)
961         return WN_BAD_POINTER;
962     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
963         return WN_BAD_VALUE;
964     if (!providerTable || enumerator->providerIndex >=
965      providerTable->numProviders)
966         return WN_NO_MORE_ENTRIES;
967
968     if (enumerator->providerDone)
969     {
970         enumerator->providerDone = FALSE;
971         if (enumerator->handle)
972         {
973             providerTable->table[enumerator->providerIndex].closeEnum(
974              enumerator->handle);
975             enumerator->handle = NULL;
976             enumerator->providerIndex++;
977         }
978         for (; enumerator->providerIndex < providerTable->numProviders &&
979          !(enumerator->dwScope & providerTable->table
980          [enumerator->providerIndex].dwEnumScopes);
981          enumerator->providerIndex++)
982             ;
983     }
984     return enumerator->providerIndex < providerTable->numProviders ?
985      WN_SUCCESS : WN_NO_MORE_ENTRIES;
986 }
987
988 /* "Passes through" call to the next provider that supports the enumeration
989  * type.
990  * FIXME: if one call to a provider's enumerator succeeds while there's still
991  * space in lpBuffer, I don't call to the next provider.  The caller may not
992  * expect that it should call EnumResourceW again with a return value of
993  * WN_SUCCESS (depending what *lpcCount was to begin with).  That means strings
994  * may have to be moved around a bit, ick.
995  */
996 static DWORD _enumerateGlobalPassthroughW(PWNetEnumerator enumerator,
997  LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
998 {
999     DWORD ret;
1000
1001     if (!enumerator)
1002         return WN_BAD_POINTER;
1003     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
1004         return WN_BAD_VALUE;
1005     if (!lpcCount)
1006         return WN_BAD_POINTER;
1007     if (!lpBuffer)
1008         return WN_BAD_POINTER;
1009     if (!lpBufferSize)
1010         return WN_BAD_POINTER;
1011     if (*lpBufferSize < sizeof(NETRESOURCEW))
1012         return WN_MORE_DATA;
1013
1014     ret = _globalEnumeratorAdvance(enumerator);
1015     if (ret == WN_SUCCESS)
1016     {
1017         ret = providerTable->table[enumerator->providerIndex].
1018          openEnum(enumerator->dwScope, enumerator->dwType,
1019          enumerator->dwUsage, enumerator->lpNet,
1020          &enumerator->handle);
1021         if (ret == WN_SUCCESS)
1022         {
1023             ret = providerTable->table[enumerator->providerIndex].
1024              enumResource(enumerator->handle, lpcCount, lpBuffer,
1025              lpBufferSize);
1026             if (ret != WN_MORE_DATA)
1027                 enumerator->providerDone = TRUE;
1028         }
1029     }
1030     TRACE("Returning %ld\n", ret);
1031     return ret;
1032 }
1033
1034 static DWORD _enumerateGlobalW(PWNetEnumerator enumerator, LPDWORD lpcCount,
1035  LPVOID lpBuffer, LPDWORD lpBufferSize)
1036 {
1037     DWORD ret;
1038
1039     if (!enumerator)
1040         return WN_BAD_POINTER;
1041     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
1042         return WN_BAD_VALUE;
1043     if (!lpcCount)
1044         return WN_BAD_POINTER;
1045     if (!lpBuffer)
1046         return WN_BAD_POINTER;
1047     if (!lpBufferSize)
1048         return WN_BAD_POINTER;
1049     if (*lpBufferSize < sizeof(NETRESOURCEW))
1050         return WN_MORE_DATA;
1051     if (!providerTable)
1052         return WN_NO_NETWORK;
1053
1054     switch (enumerator->dwScope)
1055     {
1056         case RESOURCE_GLOBALNET:
1057             if (enumerator->lpNet)
1058                 ret = _enumerateGlobalPassthroughW(enumerator, lpcCount,
1059                  lpBuffer, lpBufferSize);
1060             else
1061                 ret = _enumerateProvidersW(enumerator, lpcCount, lpBuffer,
1062                  lpBufferSize);
1063             break;
1064         case RESOURCE_CONTEXT:
1065             ret = _enumerateGlobalPassthroughW(enumerator, lpcCount, lpBuffer,
1066              lpBufferSize);
1067             break;
1068         default:
1069             WARN("unexpected scope 0x%08lx\n", enumerator->dwScope);
1070             ret = WN_NO_MORE_ENTRIES;
1071     }
1072     TRACE("Returning %ld\n", ret);
1073     return ret;
1074 }
1075
1076 static DWORD _enumerateProviderW(PWNetEnumerator enumerator, LPDWORD lpcCount,
1077  LPVOID lpBuffer, LPDWORD lpBufferSize)
1078 {
1079     if (!enumerator)
1080         return WN_BAD_POINTER;
1081     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_PROVIDER)
1082         return WN_BAD_VALUE;
1083     if (!enumerator->handle)
1084         return WN_BAD_VALUE;
1085     if (!lpcCount)
1086         return WN_BAD_POINTER;
1087     if (!lpBuffer)
1088         return WN_BAD_POINTER;
1089     if (!lpBufferSize)
1090         return WN_BAD_POINTER;
1091     if (!providerTable)
1092         return WN_NO_NETWORK;
1093     if (enumerator->providerIndex >= providerTable->numProviders)
1094         return WN_NO_MORE_ENTRIES;
1095     if (!providerTable->table[enumerator->providerIndex].enumResource)
1096         return WN_BAD_VALUE;
1097     return providerTable->table[enumerator->providerIndex].enumResource(
1098      enumerator->handle, lpcCount, lpBuffer, lpBufferSize);
1099 }
1100
1101 static DWORD _enumerateContextW(PWNetEnumerator enumerator, LPDWORD lpcCount,
1102  LPVOID lpBuffer, LPDWORD lpBufferSize)
1103 {
1104     DWORD ret;
1105     size_t cchEntireNetworkLen, bytesNeeded;
1106
1107     if (!enumerator)
1108         return WN_BAD_POINTER;
1109     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_CONTEXT)
1110         return WN_BAD_VALUE;
1111     if (!lpcCount)
1112         return WN_BAD_POINTER;
1113     if (!lpBuffer)
1114         return WN_BAD_POINTER;
1115     if (!lpBufferSize)
1116         return WN_BAD_POINTER;
1117     if (!providerTable)
1118         return WN_NO_NETWORK;
1119
1120     cchEntireNetworkLen = strlenW(providerTable->entireNetwork) + 1;
1121     bytesNeeded = sizeof(NETRESOURCEW) + cchEntireNetworkLen * sizeof(WCHAR);
1122     if (*lpBufferSize < bytesNeeded)
1123     {
1124         *lpBufferSize = bytesNeeded;
1125         ret = WN_MORE_DATA;
1126     }
1127     else
1128     {
1129         LPNETRESOURCEW lpNet = (LPNETRESOURCEW)lpBuffer;
1130
1131         lpNet->dwScope = RESOURCE_GLOBALNET;
1132         lpNet->dwType = enumerator->dwType;
1133         lpNet->dwDisplayType = RESOURCEDISPLAYTYPE_ROOT;
1134         lpNet->dwUsage = RESOURCEUSAGE_CONTAINER;
1135         lpNet->lpLocalName = NULL;
1136         lpNet->lpRemoteName = NULL;
1137         lpNet->lpProvider = NULL;
1138         /* odd, but correct: put comment at end of buffer, so it won't get
1139          * overwritten by subsequent calls to a provider's enumResource
1140          */
1141         lpNet->lpComment = (LPWSTR)((LPBYTE)lpBuffer + *lpBufferSize -
1142          (cchEntireNetworkLen * sizeof(WCHAR)));
1143         strcpyW(lpNet->lpComment, providerTable->entireNetwork);
1144         ret = WN_SUCCESS;
1145     }
1146     if (ret == WN_SUCCESS)
1147     {
1148         DWORD bufferSize = *lpBufferSize - bytesNeeded;
1149
1150         /* "Entire Network" entry enumerated--morph this into a global
1151          * enumerator.  enumerator->lpNet continues to be NULL, since it has
1152          * no meaning when the scope isn't RESOURCE_GLOBALNET.
1153          */
1154         enumerator->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
1155         ret = _enumerateGlobalW(enumerator, lpcCount,
1156          (LPBYTE)lpBuffer + bytesNeeded, &bufferSize);
1157         if (ret == WN_SUCCESS)
1158         {
1159             /* reflect the fact that we already enumerated "Entire Network" */
1160             lpcCount++;
1161             *lpBufferSize = bufferSize + bytesNeeded;
1162         }
1163         else
1164         {
1165             /* the provider enumeration failed, but we already succeeded in
1166              * enumerating "Entire Network"--leave type as global to allow a
1167              * retry, but indicate success with a count of one.
1168              */
1169             ret = WN_SUCCESS;
1170             *lpcCount = 1;
1171             *lpBufferSize = bytesNeeded;
1172         }
1173     }
1174     TRACE("Returning %ld\n", ret);
1175     return ret;
1176 }
1177
1178 /*********************************************************************
1179  * WNetEnumResourceW [MPR.@]
1180  */
1181 DWORD WINAPI WNetEnumResourceW( HANDLE hEnum, LPDWORD lpcCount,
1182                                 LPVOID lpBuffer, LPDWORD lpBufferSize )
1183 {
1184     DWORD ret;
1185
1186     TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
1187
1188     if (!hEnum)
1189         ret = WN_BAD_POINTER;
1190     else if (!lpcCount)
1191         ret = WN_BAD_POINTER;
1192     else if (!lpBuffer)
1193         ret = WN_BAD_POINTER;
1194     else if (!lpBufferSize)
1195         ret = WN_BAD_POINTER;
1196     else if (*lpBufferSize < sizeof(NETRESOURCEW))
1197     {
1198         *lpBufferSize = sizeof(NETRESOURCEW);
1199         ret = WN_MORE_DATA;
1200     }
1201     else
1202     {
1203         PWNetEnumerator enumerator = (PWNetEnumerator)hEnum;
1204
1205         switch (enumerator->enumType)
1206         {
1207             case WNET_ENUMERATOR_TYPE_NULL:
1208                 ret = WN_NO_MORE_ENTRIES;
1209                 break;
1210             case WNET_ENUMERATOR_TYPE_GLOBAL:
1211                 ret = _enumerateGlobalW(enumerator, lpcCount, lpBuffer,
1212                  lpBufferSize);
1213                 break;
1214             case WNET_ENUMERATOR_TYPE_PROVIDER:
1215                 ret = _enumerateProviderW(enumerator, lpcCount, lpBuffer,
1216                  lpBufferSize);
1217                 break;
1218             case WNET_ENUMERATOR_TYPE_CONTEXT:
1219                 ret = _enumerateContextW(enumerator, lpcCount, lpBuffer,
1220                  lpBufferSize);
1221                 break;
1222             default:
1223                 WARN("bogus enumerator type!\n");
1224                 ret = WN_NO_NETWORK;
1225         }
1226     }
1227     if (ret)
1228         SetLastError(ret);
1229     TRACE("Returning %ld\n", ret);
1230     return ret;
1231 }
1232
1233 /*********************************************************************
1234  * WNetCloseEnum [MPR.@]
1235  */
1236 DWORD WINAPI WNetCloseEnum( HANDLE hEnum )
1237 {
1238     DWORD ret;
1239
1240     TRACE( "(%p)\n", hEnum );
1241
1242     if (hEnum)
1243     {
1244         PWNetEnumerator enumerator = (PWNetEnumerator)hEnum;
1245
1246         switch (enumerator->enumType)
1247         {
1248             case WNET_ENUMERATOR_TYPE_NULL:
1249                 ret = WN_SUCCESS;
1250                 break;
1251             case WNET_ENUMERATOR_TYPE_GLOBAL:
1252                 if (enumerator->lpNet)
1253                     _freeEnumNetResource(enumerator->lpNet);
1254                 if (enumerator->handle)
1255                     providerTable->table[enumerator->providerIndex].
1256                      closeEnum(enumerator->handle);
1257                 ret = WN_SUCCESS;
1258                 break;
1259             case WNET_ENUMERATOR_TYPE_PROVIDER:
1260                 if (enumerator->handle)
1261                     providerTable->table[enumerator->providerIndex].
1262                      closeEnum(enumerator->handle);
1263                 ret = WN_SUCCESS;
1264                 break;
1265             default:
1266                 WARN("bogus enumerator type!\n");
1267                 ret = WN_BAD_HANDLE;
1268         }
1269         HeapFree(GetProcessHeap(), 0, hEnum);
1270     }
1271     else
1272         ret = WN_BAD_HANDLE;
1273     if (ret)
1274         SetLastError(ret);
1275     TRACE("Returning %ld\n", ret);
1276     return ret;
1277 }
1278
1279 /*********************************************************************
1280  * WNetGetResourceInformationA [MPR.@]
1281  */
1282 DWORD WINAPI WNetGetResourceInformationA( LPNETRESOURCEA lpNetResource,
1283                                           LPVOID lpBuffer, LPDWORD cbBuffer,
1284                                           LPSTR *lplpSystem )
1285 {
1286     FIXME( "(%p, %p, %p, %p): stub\n",
1287            lpNetResource, lpBuffer, cbBuffer, lplpSystem );
1288
1289     SetLastError(WN_NO_NETWORK);
1290     return WN_NO_NETWORK;
1291 }
1292
1293 /*********************************************************************
1294  * WNetGetResourceInformationW [MPR.@]
1295  */
1296 DWORD WINAPI WNetGetResourceInformationW( LPNETRESOURCEW lpNetResource,
1297                                           LPVOID lpBuffer, LPDWORD cbBuffer,
1298                                           LPWSTR *lplpSystem )
1299 {
1300     FIXME( "(%p, %p, %p, %p): stub\n",
1301            lpNetResource, lpBuffer, cbBuffer, lplpSystem );
1302
1303     SetLastError(WN_NO_NETWORK);
1304     return WN_NO_NETWORK;
1305 }
1306
1307 /*********************************************************************
1308  * WNetGetResourceParentA [MPR.@]
1309  */
1310 DWORD WINAPI WNetGetResourceParentA( LPNETRESOURCEA lpNetResource,
1311                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1312 {
1313     FIXME( "(%p, %p, %p): stub\n",
1314            lpNetResource, lpBuffer, lpBufferSize );
1315
1316     SetLastError(WN_NO_NETWORK);
1317     return WN_NO_NETWORK;
1318 }
1319
1320 /*********************************************************************
1321  * WNetGetResourceParentW [MPR.@]
1322  */
1323 DWORD WINAPI WNetGetResourceParentW( LPNETRESOURCEW lpNetResource,
1324                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1325 {
1326     FIXME( "(%p, %p, %p): stub\n",
1327            lpNetResource, lpBuffer, lpBufferSize );
1328
1329     SetLastError(WN_NO_NETWORK);
1330     return WN_NO_NETWORK;
1331 }
1332
1333
1334
1335 /*
1336  * Connection Functions
1337  */
1338
1339 /*********************************************************************
1340  *  WNetAddConnectionA [MPR.@]
1341  */
1342 DWORD WINAPI WNetAddConnectionA( LPCSTR lpRemoteName, LPCSTR lpPassword,
1343                                  LPCSTR lpLocalName )
1344 {
1345     FIXME( "(%s, %p, %s): stub\n",
1346            debugstr_a(lpRemoteName), lpPassword, debugstr_a(lpLocalName) );
1347
1348     SetLastError(WN_NO_NETWORK);
1349     return WN_NO_NETWORK;
1350 }
1351
1352 /*********************************************************************
1353  *  WNetAddConnectionW [MPR.@]
1354  */
1355 DWORD WINAPI WNetAddConnectionW( LPCWSTR lpRemoteName, LPCWSTR lpPassword,
1356                                  LPCWSTR lpLocalName )
1357 {
1358     FIXME( "(%s, %p, %s): stub\n",
1359            debugstr_w(lpRemoteName), lpPassword, debugstr_w(lpLocalName) );
1360
1361     SetLastError(WN_NO_NETWORK);
1362     return WN_NO_NETWORK;
1363 }
1364
1365 /*********************************************************************
1366  *  WNetAddConnection2A [MPR.@]
1367  */
1368 DWORD WINAPI WNetAddConnection2A( LPNETRESOURCEA lpNetResource,
1369                                   LPCSTR lpPassword, LPCSTR lpUserID,
1370                                   DWORD dwFlags )
1371 {
1372     FIXME( "(%p, %p, %s, 0x%08lX): stub\n",
1373            lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags );
1374
1375     SetLastError(WN_NO_NETWORK);
1376     return WN_NO_NETWORK;
1377 }
1378
1379 /*********************************************************************
1380  * WNetAddConnection2W [MPR.@]
1381  */
1382 DWORD WINAPI WNetAddConnection2W( LPNETRESOURCEW lpNetResource,
1383                                   LPCWSTR lpPassword, LPCWSTR lpUserID,
1384                                   DWORD dwFlags )
1385 {
1386     FIXME( "(%p, %p, %s, 0x%08lX): stub\n",
1387            lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags );
1388
1389     SetLastError(WN_NO_NETWORK);
1390     return WN_NO_NETWORK;
1391 }
1392
1393 /*********************************************************************
1394  * WNetAddConnection3A [MPR.@]
1395  */
1396 DWORD WINAPI WNetAddConnection3A( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
1397                                   LPCSTR lpPassword, LPCSTR lpUserID,
1398                                   DWORD dwFlags )
1399 {
1400     FIXME( "(%p, %p, %p, %s, 0x%08lX), stub\n",
1401            hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags );
1402
1403     SetLastError(WN_NO_NETWORK);
1404     return WN_NO_NETWORK;
1405 }
1406
1407 /*********************************************************************
1408  * WNetAddConnection3W [MPR.@]
1409  */
1410 DWORD WINAPI WNetAddConnection3W( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
1411                                   LPCWSTR lpPassword, LPCWSTR lpUserID,
1412                                   DWORD dwFlags )
1413 {
1414     FIXME( "(%p, %p, %p, %s, 0x%08lX), stub\n",
1415            hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags );
1416
1417     SetLastError(WN_NO_NETWORK);
1418     return WN_NO_NETWORK;
1419 }
1420
1421 /*****************************************************************
1422  *  WNetUseConnectionA [MPR.@]
1423  */
1424 DWORD WINAPI WNetUseConnectionA( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
1425                                  LPCSTR lpPassword, LPCSTR lpUserID, DWORD dwFlags,
1426                                  LPSTR lpAccessName, LPDWORD lpBufferSize,
1427                                  LPDWORD lpResult )
1428 {
1429     FIXME( "(%p, %p, %p, %s, 0x%08lX, %s, %p, %p), stub\n",
1430            hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags,
1431            debugstr_a(lpAccessName), lpBufferSize, lpResult );
1432
1433     SetLastError(WN_NO_NETWORK);
1434     return WN_NO_NETWORK;
1435 }
1436
1437 /*****************************************************************
1438  *  WNetUseConnectionW [MPR.@]
1439  */
1440 DWORD WINAPI WNetUseConnectionW( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
1441                                  LPCWSTR lpPassword, LPCWSTR lpUserID, DWORD dwFlags,
1442                                  LPWSTR lpAccessName, LPDWORD lpBufferSize,
1443                                  LPDWORD lpResult )
1444 {
1445     FIXME( "(%p, %p, %p, %s, 0x%08lX, %s, %p, %p), stub\n",
1446            hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags,
1447            debugstr_w(lpAccessName), lpBufferSize, lpResult );
1448
1449     SetLastError(WN_NO_NETWORK);
1450     return WN_NO_NETWORK;
1451 }
1452
1453 /*********************************************************************
1454  *  WNetCancelConnectionA [MPR.@]
1455  */
1456 DWORD WINAPI WNetCancelConnectionA( LPCSTR lpName, BOOL fForce )
1457 {
1458     FIXME( "(%s, %d), stub\n", debugstr_a(lpName), fForce );
1459
1460     return WN_SUCCESS;
1461 }
1462
1463 /*********************************************************************
1464  *  WNetCancelConnectionW [MPR.@]
1465  */
1466 DWORD WINAPI WNetCancelConnectionW( LPCWSTR lpName, BOOL fForce )
1467 {
1468     FIXME( "(%s, %d), stub\n", debugstr_w(lpName), fForce );
1469
1470     return WN_SUCCESS;
1471 }
1472
1473 /*********************************************************************
1474  *  WNetCancelConnection2A [MPR.@]
1475  */
1476 DWORD WINAPI WNetCancelConnection2A( LPCSTR lpName, DWORD dwFlags, BOOL fForce )
1477 {
1478     FIXME( "(%s, %08lX, %d), stub\n", debugstr_a(lpName), dwFlags, fForce );
1479
1480     return WN_SUCCESS;
1481 }
1482
1483 /*********************************************************************
1484  *  WNetCancelConnection2W [MPR.@]
1485  */
1486 DWORD WINAPI WNetCancelConnection2W( LPCWSTR lpName, DWORD dwFlags, BOOL fForce )
1487 {
1488     FIXME( "(%s, %08lX, %d), stub\n", debugstr_w(lpName), dwFlags, fForce );
1489
1490     return WN_SUCCESS;
1491 }
1492
1493 /*****************************************************************
1494  *  WNetRestoreConnectionA [MPR.@]
1495  */
1496 DWORD WINAPI WNetRestoreConnectionA( HWND hwndOwner, LPSTR lpszDevice )
1497 {
1498     FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_a(lpszDevice) );
1499
1500     SetLastError(WN_NO_NETWORK);
1501     return WN_NO_NETWORK;
1502 }
1503
1504 /*****************************************************************
1505  *  WNetRestoreConnectionW [MPR.@]
1506  */
1507 DWORD WINAPI WNetRestoreConnectionW( HWND hwndOwner, LPWSTR lpszDevice )
1508 {
1509     FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_w(lpszDevice) );
1510
1511     SetLastError(WN_NO_NETWORK);
1512     return WN_NO_NETWORK;
1513 }
1514
1515 /**************************************************************************
1516  * WNetGetConnectionA [MPR.@]
1517  *
1518  * RETURNS
1519  * - WN_BAD_LOCALNAME     lpLocalName makes no sense
1520  * - WN_NOT_CONNECTED     drive is a local drive
1521  * - WN_MORE_DATA         buffer isn't big enough
1522  * - WN_SUCCESS           success (net path in buffer)
1523  *
1524  * FIXME: need to test return values under different errors
1525  */
1526 DWORD WINAPI WNetGetConnectionA( LPCSTR lpLocalName,
1527                                  LPSTR lpRemoteName, LPDWORD lpBufferSize )
1528 {
1529     DWORD ret;
1530
1531     if (!lpLocalName)
1532         ret = WN_BAD_POINTER;
1533     else if (!lpRemoteName)
1534         ret = WN_BAD_POINTER;
1535     else if (!lpBufferSize)
1536         ret = WN_BAD_POINTER;
1537     else
1538     {
1539         int len = MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, NULL, 0);
1540
1541         if (len)
1542         {
1543             PWSTR wideLocalName = (PWSTR)HeapAlloc(GetProcessHeap(), 0, len);
1544
1545             if (wideLocalName)
1546             {
1547                 WCHAR wideRemoteStatic[MAX_PATH];
1548                 DWORD wideRemoteSize = sizeof(wideRemoteStatic) / sizeof(WCHAR);
1549
1550                 MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, wideLocalName, len);
1551
1552                 /* try once without memory allocation */
1553                 ret = WNetGetConnectionW(wideLocalName, wideRemoteStatic,
1554                  &wideRemoteSize);
1555                 if (ret == WN_SUCCESS)
1556                 {
1557                     int len = WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
1558                      -1, NULL, 0, NULL, NULL);
1559
1560                     if (len <= *lpBufferSize)
1561                     {
1562                         WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic, -1,
1563                          lpRemoteName, *lpBufferSize, NULL, NULL);
1564                         ret = WN_SUCCESS;
1565                     }
1566                     else
1567                     {
1568                         *lpBufferSize = len;
1569                         ret = WN_MORE_DATA;
1570                     }
1571                 }
1572                 else if (ret == WN_MORE_DATA)
1573                 {
1574                     PWSTR wideRemote = (PWSTR)HeapAlloc(GetProcessHeap(), 0,
1575                      wideRemoteSize * sizeof(WCHAR));
1576
1577                     if (wideRemote)
1578                     {
1579                         ret = WNetGetConnectionW(wideLocalName, wideRemote,
1580                          &wideRemoteSize);
1581                         if (ret == WN_SUCCESS)
1582                         {
1583                             if (len <= *lpBufferSize)
1584                             {
1585                                 WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
1586                                  -1, lpRemoteName, *lpBufferSize, NULL, NULL);
1587                                 ret = WN_SUCCESS;
1588                             }
1589                             else
1590                             {
1591                                 *lpBufferSize = len;
1592                                 ret = WN_MORE_DATA;
1593                             }
1594                         }
1595                         HeapFree(GetProcessHeap(), 0, wideRemote);
1596                     }
1597                     else
1598                         ret = WN_OUT_OF_MEMORY;
1599                 }
1600                 HeapFree(GetProcessHeap(), 0, wideLocalName);
1601             }
1602             else
1603                 ret = WN_OUT_OF_MEMORY;
1604         }
1605         else
1606             ret = WN_BAD_LOCALNAME;
1607     }
1608     if (ret)
1609         SetLastError(ret);
1610     TRACE("Returning %ld\n", ret);
1611     return ret;
1612 }
1613
1614 /**************************************************************************
1615  * WNetGetConnectionW [MPR.@]
1616  *
1617  * FIXME: need to test return values under different errors
1618  */
1619 DWORD WINAPI WNetGetConnectionW( LPCWSTR lpLocalName,
1620                                  LPWSTR lpRemoteName, LPDWORD lpBufferSize )
1621 {
1622     DWORD ret;
1623
1624     TRACE("(%s, %p, %p)\n", debugstr_w(lpLocalName), lpRemoteName,
1625      lpBufferSize);
1626
1627     if (!lpLocalName)
1628         ret = WN_BAD_POINTER;
1629     else if (!lpRemoteName)
1630         ret = WN_BAD_POINTER;
1631     else if (!lpBufferSize)
1632         ret = WN_BAD_POINTER;
1633     else if (!lpLocalName[0])
1634         ret = WN_BAD_LOCALNAME;
1635     else
1636     {
1637         if (lpLocalName[1] == ':')
1638         {
1639             switch(GetDriveTypeW(lpLocalName))
1640             {
1641             case DRIVE_REMOTE:
1642             {
1643                 WCHAR remote[MAX_PATH];
1644                 if (!QueryDosDeviceW( lpLocalName, remote, MAX_PATH )) remote[0] = 0;
1645                 if (strlenW(remote) + 1 > *lpBufferSize)
1646                 {
1647                     *lpBufferSize = strlenW(remote) + 1;
1648                     ret = WN_MORE_DATA;
1649                 }
1650                 else
1651                 {
1652                     strcpyW( lpRemoteName, remote );
1653                     *lpBufferSize = strlenW(lpRemoteName) + 1;
1654                     ret = WN_SUCCESS;
1655                 }
1656                 break;
1657             }
1658             case DRIVE_REMOVABLE:
1659             case DRIVE_FIXED:
1660             case DRIVE_CDROM:
1661                 TRACE("file is local\n");
1662                 ret = WN_NOT_CONNECTED;
1663                 break;
1664             default:
1665                 ret = WN_BAD_LOCALNAME;
1666             }
1667         }
1668         else
1669             ret = WN_BAD_LOCALNAME;
1670     }
1671     if (ret)
1672         SetLastError(ret);
1673     TRACE("Returning %ld\n", ret);
1674     return ret;
1675 }
1676
1677 /**************************************************************************
1678  * WNetSetConnectionA [MPR.@]
1679  */
1680 DWORD WINAPI WNetSetConnectionA( LPCSTR lpName, DWORD dwProperty,
1681                                  LPVOID pvValue )
1682 {
1683     FIXME( "(%s, %08lX, %p): stub\n", debugstr_a(lpName), dwProperty, pvValue );
1684
1685     SetLastError(WN_NO_NETWORK);
1686     return WN_NO_NETWORK;
1687 }
1688
1689 /**************************************************************************
1690  * WNetSetConnectionW [MPR.@]
1691  */
1692 DWORD WINAPI WNetSetConnectionW( LPCWSTR lpName, DWORD dwProperty,
1693                                  LPVOID pvValue )
1694 {
1695     FIXME( "(%s, %08lX, %p): stub\n", debugstr_w(lpName), dwProperty, pvValue );
1696
1697     SetLastError(WN_NO_NETWORK);
1698     return WN_NO_NETWORK;
1699 }
1700
1701 /*****************************************************************
1702  * WNetGetUniversalNameA [MPR.@]
1703  */
1704 DWORD WINAPI WNetGetUniversalNameA ( LPCSTR lpLocalPath, DWORD dwInfoLevel,
1705                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1706 {
1707     FIXME( "(%s, 0x%08lX, %p, %p): stub\n",
1708            debugstr_a(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
1709
1710     SetLastError(WN_NO_NETWORK);
1711     return WN_NO_NETWORK;
1712 }
1713
1714 /*****************************************************************
1715  * WNetGetUniversalNameW [MPR.@]
1716  */
1717 DWORD WINAPI WNetGetUniversalNameW ( LPCWSTR lpLocalPath, DWORD dwInfoLevel,
1718                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1719 {
1720     FIXME( "(%s, 0x%08lX, %p, %p): stub\n",
1721            debugstr_w(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
1722
1723     SetLastError(WN_NO_NETWORK);
1724     return WN_NO_NETWORK;
1725 }
1726
1727
1728
1729 /*
1730  * Other Functions
1731  */
1732
1733 /**************************************************************************
1734  * WNetGetUserA [MPR.@]
1735  *
1736  * FIXME: we should not return ourselves, but the owner of the drive lpName
1737  */
1738 DWORD WINAPI WNetGetUserA( LPCSTR lpName, LPSTR lpUserID, LPDWORD lpBufferSize )
1739 {
1740     if (GetUserNameA( lpUserID, lpBufferSize )) return WN_SUCCESS;
1741     return GetLastError();
1742 }
1743
1744 /*****************************************************************
1745  * WNetGetUserW [MPR.@]
1746  *
1747  * FIXME: we should not return ourselves, but the owner of the drive lpName
1748  */
1749 DWORD WINAPI WNetGetUserW( LPCWSTR lpName, LPWSTR lpUserID, LPDWORD lpBufferSize )
1750 {
1751     if (GetUserNameW( lpUserID, lpBufferSize )) return WN_SUCCESS;
1752     return GetLastError();
1753 }
1754
1755 /*********************************************************************
1756  * WNetConnectionDialog [MPR.@]
1757  */
1758 DWORD WINAPI WNetConnectionDialog( HWND hwnd, DWORD dwType )
1759 {
1760     FIXME( "(%p, %08lX): stub\n", hwnd, dwType );
1761
1762     SetLastError(WN_NO_NETWORK);
1763     return WN_NO_NETWORK;
1764 }
1765
1766 /*********************************************************************
1767  * WNetConnectionDialog1A [MPR.@]
1768  */
1769 DWORD WINAPI WNetConnectionDialog1A( LPCONNECTDLGSTRUCTA lpConnDlgStruct )
1770 {
1771     FIXME( "(%p): stub\n", lpConnDlgStruct );
1772
1773     SetLastError(WN_NO_NETWORK);
1774     return WN_NO_NETWORK;
1775 }
1776
1777 /*********************************************************************
1778  * WNetConnectionDialog1W [MPR.@]
1779  */
1780 DWORD WINAPI WNetConnectionDialog1W( LPCONNECTDLGSTRUCTW lpConnDlgStruct )
1781 {
1782     FIXME( "(%p): stub\n", lpConnDlgStruct );
1783
1784     SetLastError(WN_NO_NETWORK);
1785     return WN_NO_NETWORK;
1786 }
1787
1788 /*********************************************************************
1789  * WNetDisconnectDialog [MPR.@]
1790  */
1791 DWORD WINAPI WNetDisconnectDialog( HWND hwnd, DWORD dwType )
1792 {
1793     FIXME( "(%p, %08lX): stub\n", hwnd, dwType );
1794
1795     SetLastError(WN_NO_NETWORK);
1796     return WN_NO_NETWORK;
1797 }
1798
1799 /*********************************************************************
1800  * WNetDisconnectDialog1A [MPR.@]
1801  */
1802 DWORD WINAPI WNetDisconnectDialog1A( LPDISCDLGSTRUCTA lpConnDlgStruct )
1803 {
1804     FIXME( "(%p): stub\n", lpConnDlgStruct );
1805
1806     SetLastError(WN_NO_NETWORK);
1807     return WN_NO_NETWORK;
1808 }
1809
1810 /*********************************************************************
1811  * WNetDisconnectDialog1W [MPR.@]
1812  */
1813 DWORD WINAPI WNetDisconnectDialog1W( LPDISCDLGSTRUCTW lpConnDlgStruct )
1814 {
1815     FIXME( "(%p): stub\n", lpConnDlgStruct );
1816
1817     SetLastError(WN_NO_NETWORK);
1818     return WN_NO_NETWORK;
1819 }
1820
1821 /*********************************************************************
1822  * WNetGetLastErrorA [MPR.@]
1823  */
1824 DWORD WINAPI WNetGetLastErrorA( LPDWORD lpError,
1825                                 LPSTR lpErrorBuf, DWORD nErrorBufSize,
1826                                 LPSTR lpNameBuf, DWORD nNameBufSize )
1827 {
1828     FIXME( "(%p, %p, %ld, %p, %ld): stub\n",
1829            lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
1830
1831     SetLastError(WN_NO_NETWORK);
1832     return WN_NO_NETWORK;
1833 }
1834
1835 /*********************************************************************
1836  * WNetGetLastErrorW [MPR.@]
1837  */
1838 DWORD WINAPI WNetGetLastErrorW( LPDWORD lpError,
1839                                 LPWSTR lpErrorBuf, DWORD nErrorBufSize,
1840                          LPWSTR lpNameBuf, DWORD nNameBufSize )
1841 {
1842     FIXME( "(%p, %p, %ld, %p, %ld): stub\n",
1843            lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
1844
1845     SetLastError(WN_NO_NETWORK);
1846     return WN_NO_NETWORK;
1847 }
1848
1849 /*********************************************************************
1850  * WNetGetNetworkInformationA [MPR.@]
1851  */
1852 DWORD WINAPI WNetGetNetworkInformationA( LPCSTR lpProvider,
1853                                          LPNETINFOSTRUCT lpNetInfoStruct )
1854 {
1855     DWORD ret;
1856
1857     TRACE( "(%s, %p)\n", debugstr_a(lpProvider), lpNetInfoStruct );
1858
1859     if (!lpProvider)
1860         ret = WN_BAD_POINTER;
1861     else
1862     {
1863         int len;
1864
1865         len = MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, NULL, 0);
1866         if (len)
1867         {
1868             LPWSTR wideProvider = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
1869              len * sizeof(WCHAR));
1870
1871             if (wideProvider)
1872             {
1873                 MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, wideProvider,
1874                  len);
1875                 ret = WNetGetNetworkInformationW(wideProvider, lpNetInfoStruct);
1876                 HeapFree(GetProcessHeap(), 0, wideProvider);
1877             }
1878             else
1879                 ret = WN_OUT_OF_MEMORY;
1880         }
1881         else
1882             ret = GetLastError();
1883     }
1884     if (ret)
1885         SetLastError(ret);
1886     TRACE("Returning %ld\n", ret);
1887     return ret;
1888 }
1889
1890 /*********************************************************************
1891  * WNetGetNetworkInformationW [MPR.@]
1892  */
1893 DWORD WINAPI WNetGetNetworkInformationW( LPCWSTR lpProvider,
1894                                          LPNETINFOSTRUCT lpNetInfoStruct )
1895 {
1896     DWORD ret;
1897
1898     TRACE( "(%s, %p)\n", debugstr_w(lpProvider), lpNetInfoStruct );
1899
1900     if (!lpProvider)
1901         ret = WN_BAD_POINTER;
1902     else if (!lpNetInfoStruct)
1903         ret = WN_BAD_POINTER;
1904     else if (lpNetInfoStruct->cbStructure < sizeof(NETINFOSTRUCT))
1905         ret = WN_BAD_VALUE;
1906     else
1907     {
1908         if (providerTable && providerTable->numProviders)
1909         {
1910             DWORD providerIndex = _findProviderIndexW(lpProvider);
1911
1912             if (providerIndex != BAD_PROVIDER_INDEX)
1913             {
1914                 lpNetInfoStruct->cbStructure = sizeof(NETINFOSTRUCT);
1915                 lpNetInfoStruct->dwProviderVersion =
1916                  providerTable->table[providerIndex].dwSpecVersion;
1917                 lpNetInfoStruct->dwStatus = NO_ERROR;
1918                 lpNetInfoStruct->dwCharacteristics = 0;
1919                 lpNetInfoStruct->dwHandle = (ULONG_PTR)NULL;
1920                 lpNetInfoStruct->wNetType =
1921                  HIWORD(providerTable->table[providerIndex].dwNetType);
1922                 lpNetInfoStruct->dwPrinters = -1;
1923                 lpNetInfoStruct->dwDrives = -1;
1924                 ret = WN_SUCCESS;
1925             }
1926             else
1927                 ret = WN_BAD_PROVIDER;
1928         }
1929         else
1930             ret = WN_NO_NETWORK;
1931     }
1932     if (ret)
1933         SetLastError(ret);
1934     TRACE("Returning %ld\n", ret);
1935     return ret;
1936 }
1937
1938 /*****************************************************************
1939  *  WNetGetProviderNameA [MPR.@]
1940  */
1941 DWORD WINAPI WNetGetProviderNameA( DWORD dwNetType,
1942                                    LPSTR lpProvider, LPDWORD lpBufferSize )
1943 {
1944     DWORD ret;
1945
1946     TRACE("(0x%08lx, %s, %p)\n", dwNetType, debugstr_a(lpProvider),
1947      lpBufferSize);
1948
1949     if (!lpProvider)
1950         ret = WN_BAD_POINTER;
1951     else if (!lpBufferSize)
1952         ret = WN_BAD_POINTER;
1953     else
1954     {
1955         if (providerTable)
1956         {
1957             DWORD i;
1958
1959             ret = WN_NO_NETWORK;
1960             for (i = 0; i < providerTable->numProviders &&
1961              HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
1962              i++)
1963                 ;
1964             if (i < providerTable->numProviders)
1965             {
1966                 DWORD sizeNeeded = WideCharToMultiByte(CP_ACP, 0,
1967                  providerTable->table[i].name, -1, NULL, 0, NULL, NULL);
1968
1969                 if (*lpBufferSize < sizeNeeded)
1970                 {
1971                     *lpBufferSize = sizeNeeded;
1972                     ret = WN_MORE_DATA;
1973                 }
1974                 else
1975                 {
1976                     WideCharToMultiByte(CP_ACP, 0, providerTable->table[i].name,
1977                      -1, lpProvider, *lpBufferSize, NULL, NULL);
1978                     ret = WN_SUCCESS;
1979                     /* FIXME: is *lpBufferSize set to the number of characters
1980                      * copied? */
1981                 }
1982             }
1983         }
1984         else
1985             ret = WN_NO_NETWORK;
1986     }
1987     if (ret)
1988         SetLastError(ret);
1989     TRACE("Returning %ld\n", ret);
1990     return ret;
1991 }
1992
1993 /*****************************************************************
1994  *  WNetGetProviderNameW [MPR.@]
1995  */
1996 DWORD WINAPI WNetGetProviderNameW( DWORD dwNetType,
1997                                    LPWSTR lpProvider, LPDWORD lpBufferSize )
1998 {
1999     DWORD ret;
2000
2001     TRACE("(0x%08lx, %s, %p)\n", dwNetType, debugstr_w(lpProvider),
2002      lpBufferSize);
2003
2004     if (!lpProvider)
2005         ret = WN_BAD_POINTER;
2006     else if (!lpBufferSize)
2007         ret = WN_BAD_POINTER;
2008     else
2009     {
2010         if (providerTable)
2011         {
2012             DWORD i;
2013
2014             ret = WN_NO_NETWORK;
2015             for (i = 0; i < providerTable->numProviders &&
2016              HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
2017              i++)
2018                 ;
2019             if (i < providerTable->numProviders)
2020             {
2021                 DWORD sizeNeeded = strlenW(providerTable->table[i].name) + 1;
2022
2023                 if (*lpBufferSize < sizeNeeded)
2024                 {
2025                     *lpBufferSize = sizeNeeded;
2026                     ret = WN_MORE_DATA;
2027                 }
2028                 else
2029                 {
2030                     strcpyW(lpProvider, providerTable->table[i].name);
2031                     ret = WN_SUCCESS;
2032                     /* FIXME: is *lpBufferSize set to the number of characters
2033                      * copied? */
2034                 }
2035             }
2036         }
2037         else
2038             ret = WN_NO_NETWORK;
2039     }
2040     if (ret)
2041         SetLastError(ret);
2042     TRACE("Returning %ld\n", ret);
2043     return ret;
2044 }