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