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