gdiplus: Added GdipSetTextRenderingHint/GdipGetTextRenderingHint.
[wine] / dlls / mpr / wnet.c
1 /*
2  * MPR WNet functions
3  *
4  * Copyright 1999 Ulrich Weigand
5  * Copyright 2004 Juan Lang
6  * Copyright 2007 Maarten Lankhorst
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winnls.h"
27 #include "winnetwk.h"
28 #include "npapi.h"
29 #include "winreg.h"
30 #include "winuser.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "mprres.h"
34 #include "wnetpriv.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(mpr);
37
38 /* Data structures representing network service providers.  Assumes only one
39  * thread creates them, and that they are constant for the life of the process
40  * (and therefore doesn't synchronize access).
41  * FIXME: only basic provider data and enumeration-related data are implemented
42  * so far, need to implement the rest too.
43  */
44 typedef struct _WNetProvider
45 {
46     HMODULE           hLib;
47     PWSTR             name;
48     PF_NPGetCaps      getCaps;
49     DWORD             dwSpecVersion;
50     DWORD             dwNetType;
51     DWORD             dwEnumScopes;
52     PF_NPOpenEnum     openEnum;
53     PF_NPEnumResource enumResource;
54     PF_NPCloseEnum    closeEnum;
55 } WNetProvider, *PWNetProvider;
56
57 typedef struct _WNetProviderTable
58 {
59     LPWSTR           entireNetwork;
60     DWORD            numAllocated;
61     DWORD            numProviders;
62     WNetProvider     table[1];
63 } WNetProviderTable, *PWNetProviderTable;
64
65 #define WNET_ENUMERATOR_TYPE_NULL     0
66 #define WNET_ENUMERATOR_TYPE_GLOBAL   1
67 #define WNET_ENUMERATOR_TYPE_PROVIDER 2
68 #define WNET_ENUMERATOR_TYPE_CONTEXT  3
69
70 /* An WNet enumerator.  Note that the type doesn't correspond to the scope of
71  * the enumeration; it represents one of the following types:
72  * - a 'null' enumeration, one that contains no members
73  * - a global enumeration, one that's executed across all providers
74  * - a provider-specific enumeration, one that's only executed by a single
75  *   provider
76  * - a context enumeration.  I know this contradicts what I just said about
77  *   there being no correspondence between the scope and the type, but it's
78  *   necessary for the special case that a "Entire Network" entry needs to
79  *   be enumerated in an enumeration of the context scope.  Thus an enumeration
80  *   of the context scope results in a context type enumerator, which morphs
81  *   into a global enumeration (so the enumeration continues across all
82  *   providers).
83  */
84 typedef struct _WNetEnumerator
85 {
86     DWORD          enumType;
87     DWORD          providerIndex;
88     HANDLE         handle;
89     BOOL           providerDone;
90     DWORD          dwScope;
91     DWORD          dwType;
92     DWORD          dwUsage;
93     LPNETRESOURCEW lpNet;
94 } WNetEnumerator, *PWNetEnumerator;
95
96 #define BAD_PROVIDER_INDEX (DWORD)0xffffffff
97
98 /* Returns an index (into the global WNetProviderTable) of the provider with
99  * the given name, or BAD_PROVIDER_INDEX if not found.
100  */
101 static DWORD _findProviderIndexW(LPCWSTR lpProvider);
102
103 static PWNetProviderTable providerTable;
104
105 /*
106  * Global provider table functions
107  */
108
109 static void _tryLoadProvider(PCWSTR provider)
110 {
111     static const WCHAR servicePrefix[] = { 'S','y','s','t','e','m','\\',
112      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
113      'S','e','r','v','i','c','e','s','\\',0 };
114     static const WCHAR serviceFmt[] = { '%','s','%','s','\\',
115      'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r',0 };
116     WCHAR serviceName[MAX_PATH];
117     HKEY hKey;
118
119     TRACE("%s\n", debugstr_w(provider));
120     snprintfW(serviceName, sizeof(serviceName) / sizeof(WCHAR), serviceFmt,
121      servicePrefix, provider);
122     serviceName[sizeof(serviceName) / sizeof(WCHAR) - 1] = '\0';
123     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, serviceName, 0, KEY_READ, &hKey) ==
124      ERROR_SUCCESS)
125     {
126         static const WCHAR szProviderPath[] = { 'P','r','o','v','i','d','e','r',
127          'P','a','t','h',0 };
128         WCHAR providerPath[MAX_PATH];
129         DWORD type, size = sizeof(providerPath);
130
131         if (RegQueryValueExW(hKey, szProviderPath, NULL, &type,
132          (LPBYTE)providerPath, &size) == ERROR_SUCCESS && type == REG_SZ)
133         {
134             static const WCHAR szProviderName[] = { 'N','a','m','e',0 };
135             PWSTR name = NULL;
136            
137             size = 0;
138             RegQueryValueExW(hKey, szProviderName, NULL, NULL, NULL, &size);
139             if (size)
140             {
141                 name = HeapAlloc(GetProcessHeap(), 0, size);
142                 if (RegQueryValueExW(hKey, szProviderName, NULL, &type,
143                  (LPBYTE)name, &size) != ERROR_SUCCESS || type != REG_SZ)
144                 {
145                     HeapFree(GetProcessHeap(), 0, name);
146                     name = NULL;
147                 }
148             }
149             if (name)
150             {
151                 HMODULE hLib = LoadLibraryW(providerPath);
152
153                 if (hLib)
154                 {
155                     PF_NPGetCaps getCaps = (PF_NPGetCaps)GetProcAddress(hLib,
156                      "NPGetCaps");
157
158                     TRACE("loaded lib %p\n", hLib);
159                     if (getCaps)
160                     {
161                         PWNetProvider provider =
162                          &providerTable->table[providerTable->numProviders];
163
164                         provider->hLib = hLib;
165                         provider->name = name;
166                         TRACE("name is %s\n", debugstr_w(name));
167                         provider->getCaps = getCaps;
168                         provider->dwSpecVersion = getCaps(WNNC_SPEC_VERSION);
169                         provider->dwNetType = getCaps(WNNC_NET_TYPE);
170                         TRACE("net type is 0x%08x\n", provider->dwNetType);
171                         provider->dwEnumScopes = getCaps(WNNC_ENUMERATION);
172                         if (provider->dwEnumScopes)
173                         {
174                             TRACE("supports enumeration\n");
175                             provider->openEnum = (PF_NPOpenEnum)
176                              GetProcAddress(hLib, "NPOpenEnum");
177                             TRACE("openEnum is %p\n", provider->openEnum);
178                             provider->enumResource = (PF_NPEnumResource)
179                              GetProcAddress(hLib, "NPEnumResource");
180                             TRACE("enumResource is %p\n",
181                              provider->enumResource);
182                             provider->closeEnum = (PF_NPCloseEnum)
183                              GetProcAddress(hLib, "NPCloseEnum");
184                             TRACE("closeEnum is %p\n", provider->closeEnum);
185                             if (!provider->openEnum || !provider->enumResource
186                              || !provider->closeEnum)
187                             {
188                                 provider->openEnum = NULL;
189                                 provider->enumResource = NULL;
190                                 provider->closeEnum = NULL;
191                                 provider->dwEnumScopes = 0;
192                                 WARN("Couldn't load enumeration functions\n");
193                             }
194                         }
195                         providerTable->numProviders++;
196                     }
197                     else
198                     {
199                         WARN("Provider %s didn't export NPGetCaps\n",
200                          debugstr_w(provider));
201                         HeapFree(GetProcessHeap(), 0, name);
202                         FreeLibrary(hLib);
203                     }
204                 }
205                 else
206                 {
207                     WARN("Couldn't load library %s for provider %s\n",
208                      debugstr_w(providerPath), debugstr_w(provider));
209                     HeapFree(GetProcessHeap(), 0, name);
210                 }
211             }
212             else
213             {
214                 WARN("Couldn't get provider name for provider %s\n",
215                  debugstr_w(provider));
216             }
217         }
218         else
219             WARN("Couldn't open value %s\n", debugstr_w(szProviderPath));
220         RegCloseKey(hKey);
221     }
222     else
223         WARN("Couldn't open service key for provider %s\n",
224          debugstr_w(provider));
225 }
226
227 void wnetInit(HINSTANCE hInstDll)
228 {
229     static const WCHAR providerOrderKey[] = { 'S','y','s','t','e','m','\\',
230      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
231      'C','o','n','t','r','o','l','\\',
232      'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r','\\',
233      'O','r','d','e','r',0 };
234      static const WCHAR providerOrder[] = { 'P','r','o','v','i','d','e','r',
235       'O','r','d','e','r',0 };
236     HKEY hKey;
237
238     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, providerOrderKey, 0, KEY_READ, &hKey)
239      == ERROR_SUCCESS)
240     {
241         DWORD size = 0;
242
243         RegQueryValueExW(hKey, providerOrder, NULL, NULL, NULL, &size);
244         if (size)
245         {
246             PWSTR providers = HeapAlloc(GetProcessHeap(), 0, size);
247
248             if (providers)
249             {
250                 DWORD type;
251
252                 if (RegQueryValueExW(hKey, providerOrder, NULL, &type,
253                  (LPBYTE)providers, &size) == ERROR_SUCCESS && type == REG_SZ)
254                 {
255                     PWSTR ptr;
256                     DWORD numToAllocate;
257
258                     TRACE("provider order is %s\n", debugstr_w(providers));
259                     /* first count commas as a heuristic for how many to
260                      * allocate space for */
261                     for (ptr = providers, numToAllocate = 1; ptr; )
262                     {
263                         ptr = strchrW(ptr, ',');
264                         if (ptr) {
265                             numToAllocate++;
266                             ptr++;
267                         }
268                     }
269                     providerTable =
270                      HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
271                      sizeof(WNetProviderTable)
272                      + (numToAllocate - 1) * sizeof(WNetProvider));
273                     if (providerTable)
274                     {
275                         PWSTR ptrPrev;
276                         int entireNetworkLen;
277
278                         entireNetworkLen = LoadStringW(hInstDll,
279                          IDS_ENTIRENETWORK, NULL, 0);
280                         providerTable->entireNetwork = HeapAlloc(
281                          GetProcessHeap(), 0, (entireNetworkLen + 1) *
282                          sizeof(WCHAR));
283                         if (providerTable->entireNetwork)
284                             LoadStringW(hInstDll, IDS_ENTIRENETWORK,
285                              providerTable->entireNetwork,
286                              entireNetworkLen + 1);
287                         providerTable->numAllocated = numToAllocate;
288                         for (ptr = providers; ptr; )
289                         {
290                             ptrPrev = ptr;
291                             ptr = strchrW(ptr, ',');
292                             if (ptr)
293                                 *ptr++ = '\0';
294                             _tryLoadProvider(ptrPrev);
295                         }
296                     }
297                 }
298                 HeapFree(GetProcessHeap(), 0, providers);
299             }
300         }
301         RegCloseKey(hKey);
302     }
303 }
304
305 void wnetFree(void)
306 {
307     if (providerTable)
308     {
309         DWORD i;
310
311         for (i = 0; i < providerTable->numProviders; i++)
312         {
313             HeapFree(GetProcessHeap(), 0, providerTable->table[i].name);
314             FreeModule(providerTable->table[i].hLib);
315         }
316         HeapFree(GetProcessHeap(), 0, providerTable->entireNetwork);
317         HeapFree(GetProcessHeap(), 0, providerTable);
318         providerTable = NULL;
319     }
320 }
321
322 static DWORD _findProviderIndexW(LPCWSTR lpProvider)
323 {
324     DWORD ret = BAD_PROVIDER_INDEX;
325
326     if (providerTable && providerTable->numProviders)
327     {
328         DWORD i;
329
330         for (i = 0; i < providerTable->numProviders &&
331          ret == BAD_PROVIDER_INDEX; i++)
332             if (!strcmpW(lpProvider, providerTable->table[i].name))
333                 ret = i;
334     }
335     return ret;
336 }
337
338 /*
339  * Browsing Functions
340  */
341
342 static LPNETRESOURCEW _copyNetResourceForEnumW(LPNETRESOURCEW lpNet)
343 {
344     LPNETRESOURCEW ret;
345
346     if (lpNet)
347     {
348         ret = HeapAlloc(GetProcessHeap(), 0, sizeof(NETRESOURCEW));
349         if (ret)
350         {
351             size_t len;
352
353             memcpy(ret, lpNet, sizeof(ret));
354             ret->lpLocalName = ret->lpComment = ret->lpProvider = NULL;
355             if (lpNet->lpRemoteName)
356             {
357                 len = strlenW(lpNet->lpRemoteName) + 1;
358                 ret->lpRemoteName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
359                 if (ret->lpRemoteName)
360                     strcpyW(ret->lpRemoteName, lpNet->lpRemoteName);
361             }
362         }
363     }
364     else
365         ret = NULL;
366     return ret;
367 }
368
369 static void _freeEnumNetResource(LPNETRESOURCEW lpNet)
370 {
371     if (lpNet)
372     {
373         HeapFree(GetProcessHeap(), 0, lpNet->lpRemoteName);
374         HeapFree(GetProcessHeap(), 0, lpNet);
375     }
376 }
377
378 static PWNetEnumerator _createNullEnumerator(void)
379 {
380     PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
381      HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
382
383     if (ret)
384         ret->enumType = WNET_ENUMERATOR_TYPE_NULL;
385     return ret;
386 }
387
388 static PWNetEnumerator _createGlobalEnumeratorW(DWORD dwScope, DWORD dwType,
389  DWORD dwUsage, LPNETRESOURCEW lpNet)
390 {
391     PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
392      HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
393
394     if (ret)
395     {
396         ret->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
397         ret->dwScope = dwScope;
398         ret->dwType  = dwType;
399         ret->dwUsage = dwUsage;
400         ret->lpNet   = _copyNetResourceForEnumW(lpNet);
401     }
402     return ret;
403 }
404
405 static PWNetEnumerator _createProviderEnumerator(DWORD dwScope, DWORD dwType,
406  DWORD dwUsage, DWORD index, HANDLE handle)
407 {
408     PWNetEnumerator ret;
409
410     if (!providerTable || index >= providerTable->numProviders)
411         ret = NULL;
412     else
413     {
414         ret = HeapAlloc(GetProcessHeap(), 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 = 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 NETRESOURCEW *lpNetArrayIn,
452  const DWORD *lpcCount, LPVOID lpBuffer, const DWORD *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         const NETRESOURCEW *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         const NETRESOURCEW *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 %d, *lpcCount is %d, returning %d\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 NETRESOURCEA *lpNetArrayIn,
537  const DWORD *lpcCount, LPVOID lpBuffer, const DWORD *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         const NETRESOURCEA *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         const NETRESOURCEA *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 %d, *lpcCount is %d, returning %d\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( "(%08X, %08X, %08X, %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 = 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)
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 %d\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( "(%08X, %08X, %08X, %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%08x\n", dwScope);
800                 ret = WN_BAD_VALUE;
801         }
802     }
803     if (ret)
804         SetLastError(ret);
805     TRACE("Returning %d\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     else 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 %d\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, const DWORD *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 %d\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 %d\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%08x\n", enumerator->dwScope);
1067             ret = WN_NO_MORE_ENTRIES;
1068     }
1069     TRACE("Returning %d\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 %d\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 %d\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 %d\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%08X): 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%08X): 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%08X), 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%08X), 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%08X, %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%08X, %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, %08X, %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, %08X, %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, LPCSTR 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, LPCWSTR 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 (!lpBufferSize)
1531         ret = WN_BAD_POINTER;
1532     else if (!lpRemoteName && *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 = 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 = 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 %d\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 (!lpBufferSize)
1627         ret = WN_BAD_POINTER;
1628     else if (!lpRemoteName && *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                 static const WCHAR unc[] = { 'u','n','c','\\' };
1641                 WCHAR rremote[MAX_PATH], *remote = rremote;
1642                 if (!QueryDosDeviceW( lpLocalName, remote, MAX_PATH )) remote[0] = 0;
1643                 else if (!strncmpW(remote, unc, 4))
1644                 {
1645                     remote += 2;
1646                     remote[0] = '\\';
1647                 }
1648                 else if (remote[0] != '\\' || remote[1] != '\\')
1649                     FIXME("Don't know how to convert %s to an unc\n", debugstr_w(remote));
1650
1651                 if (strlenW(remote) + 1 > *lpBufferSize)
1652                 {
1653                     *lpBufferSize = strlenW(remote) + 1;
1654                     ret = WN_MORE_DATA;
1655                 }
1656                 else
1657                 {
1658                     strcpyW( lpRemoteName, remote );
1659                     *lpBufferSize = strlenW(lpRemoteName) + 1;
1660                     ret = WN_SUCCESS;
1661                 }
1662                 break;
1663             }
1664             case DRIVE_REMOVABLE:
1665             case DRIVE_FIXED:
1666             case DRIVE_CDROM:
1667                 TRACE("file is local\n");
1668                 ret = WN_NOT_CONNECTED;
1669                 break;
1670             default:
1671                 ret = WN_BAD_LOCALNAME;
1672             }
1673         }
1674         else
1675             ret = WN_BAD_LOCALNAME;
1676     }
1677     if (ret)
1678         SetLastError(ret);
1679     TRACE("Returning %d\n", ret);
1680     return ret;
1681 }
1682
1683 /**************************************************************************
1684  * WNetSetConnectionA [MPR.@]
1685  */
1686 DWORD WINAPI WNetSetConnectionA( LPCSTR lpName, DWORD dwProperty,
1687                                  LPVOID pvValue )
1688 {
1689     FIXME( "(%s, %08X, %p): stub\n", debugstr_a(lpName), dwProperty, pvValue );
1690
1691     SetLastError(WN_NO_NETWORK);
1692     return WN_NO_NETWORK;
1693 }
1694
1695 /**************************************************************************
1696  * WNetSetConnectionW [MPR.@]
1697  */
1698 DWORD WINAPI WNetSetConnectionW( LPCWSTR lpName, DWORD dwProperty,
1699                                  LPVOID pvValue )
1700 {
1701     FIXME( "(%s, %08X, %p): stub\n", debugstr_w(lpName), dwProperty, pvValue );
1702
1703     SetLastError(WN_NO_NETWORK);
1704     return WN_NO_NETWORK;
1705 }
1706
1707 /*****************************************************************
1708  * WNetGetUniversalNameA [MPR.@]
1709  */
1710 DWORD WINAPI WNetGetUniversalNameA ( LPCSTR lpLocalPath, DWORD dwInfoLevel,
1711                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1712 {
1713     DWORD err, size;
1714
1715     FIXME( "(%s, 0x%08X, %p, %p): stub\n",
1716            debugstr_a(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
1717
1718     switch (dwInfoLevel)
1719     {
1720     case UNIVERSAL_NAME_INFO_LEVEL:
1721     {
1722         LPUNIVERSAL_NAME_INFOA info = (LPUNIVERSAL_NAME_INFOA)lpBuffer;
1723
1724         size = sizeof(*info) + lstrlenA(lpLocalPath) + 1;
1725         if (*lpBufferSize < size)
1726         {
1727             err = WN_MORE_DATA;
1728             break;
1729         }
1730         info->lpUniversalName = (char *)info + sizeof(*info);
1731         lstrcpyA(info->lpUniversalName, lpLocalPath);
1732         *lpBufferSize = size;
1733         err = WN_NO_ERROR;
1734         break;
1735     }
1736     case REMOTE_NAME_INFO_LEVEL:
1737         err = WN_NO_NETWORK;
1738         break;
1739
1740     default:
1741         err = WN_BAD_VALUE;
1742         break;
1743     }
1744
1745     SetLastError(err);
1746     return err;
1747 }
1748
1749 /*****************************************************************
1750  * WNetGetUniversalNameW [MPR.@]
1751  */
1752 DWORD WINAPI WNetGetUniversalNameW ( LPCWSTR lpLocalPath, DWORD dwInfoLevel,
1753                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1754 {
1755     DWORD err, size;
1756
1757     FIXME( "(%s, 0x%08X, %p, %p): stub\n",
1758            debugstr_w(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
1759
1760     switch (dwInfoLevel)
1761     {
1762     case UNIVERSAL_NAME_INFO_LEVEL:
1763     {
1764         LPUNIVERSAL_NAME_INFOW info = (LPUNIVERSAL_NAME_INFOW)lpBuffer;
1765
1766         size = sizeof(*info) + (lstrlenW(lpLocalPath) + 1) * sizeof(WCHAR);
1767         if (*lpBufferSize < size)
1768         {
1769             err = WN_MORE_DATA;
1770             break;
1771         }
1772         info->lpUniversalName = (LPWSTR)((char *)info + sizeof(*info));
1773         lstrcpyW(info->lpUniversalName, lpLocalPath);
1774         *lpBufferSize = size;
1775         err = WN_NO_ERROR;
1776         break;
1777     }
1778     case REMOTE_NAME_INFO_LEVEL:
1779         err = WN_NO_NETWORK;
1780         break;
1781
1782     default:
1783         err = WN_BAD_VALUE;
1784         break;
1785     }
1786
1787     SetLastError(err);
1788     return err;
1789 }
1790
1791
1792
1793 /*
1794  * Other Functions
1795  */
1796
1797 /**************************************************************************
1798  * WNetGetUserA [MPR.@]
1799  *
1800  * FIXME: we should not return ourselves, but the owner of the drive lpName
1801  */
1802 DWORD WINAPI WNetGetUserA( LPCSTR lpName, LPSTR lpUserID, LPDWORD lpBufferSize )
1803 {
1804     if (GetUserNameA( lpUserID, lpBufferSize )) return WN_SUCCESS;
1805     return GetLastError();
1806 }
1807
1808 /*****************************************************************
1809  * WNetGetUserW [MPR.@]
1810  *
1811  * FIXME: we should not return ourselves, but the owner of the drive lpName
1812  */
1813 DWORD WINAPI WNetGetUserW( LPCWSTR lpName, LPWSTR lpUserID, LPDWORD lpBufferSize )
1814 {
1815     if (GetUserNameW( lpUserID, lpBufferSize )) return WN_SUCCESS;
1816     return GetLastError();
1817 }
1818
1819 /*********************************************************************
1820  * WNetConnectionDialog [MPR.@]
1821  */
1822 DWORD WINAPI WNetConnectionDialog( HWND hwnd, DWORD dwType )
1823 {
1824     FIXME( "(%p, %08X): stub\n", hwnd, dwType );
1825
1826     SetLastError(WN_NO_NETWORK);
1827     return WN_NO_NETWORK;
1828 }
1829
1830 /*********************************************************************
1831  * WNetConnectionDialog1A [MPR.@]
1832  */
1833 DWORD WINAPI WNetConnectionDialog1A( LPCONNECTDLGSTRUCTA lpConnDlgStruct )
1834 {
1835     FIXME( "(%p): stub\n", lpConnDlgStruct );
1836
1837     SetLastError(WN_NO_NETWORK);
1838     return WN_NO_NETWORK;
1839 }
1840
1841 /*********************************************************************
1842  * WNetConnectionDialog1W [MPR.@]
1843  */
1844 DWORD WINAPI WNetConnectionDialog1W( LPCONNECTDLGSTRUCTW lpConnDlgStruct )
1845 {
1846     FIXME( "(%p): stub\n", lpConnDlgStruct );
1847
1848     SetLastError(WN_NO_NETWORK);
1849     return WN_NO_NETWORK;
1850 }
1851
1852 /*********************************************************************
1853  * WNetDisconnectDialog [MPR.@]
1854  */
1855 DWORD WINAPI WNetDisconnectDialog( HWND hwnd, DWORD dwType )
1856 {
1857     FIXME( "(%p, %08X): stub\n", hwnd, dwType );
1858
1859     SetLastError(WN_NO_NETWORK);
1860     return WN_NO_NETWORK;
1861 }
1862
1863 /*********************************************************************
1864  * WNetDisconnectDialog1A [MPR.@]
1865  */
1866 DWORD WINAPI WNetDisconnectDialog1A( LPDISCDLGSTRUCTA lpConnDlgStruct )
1867 {
1868     FIXME( "(%p): stub\n", lpConnDlgStruct );
1869
1870     SetLastError(WN_NO_NETWORK);
1871     return WN_NO_NETWORK;
1872 }
1873
1874 /*********************************************************************
1875  * WNetDisconnectDialog1W [MPR.@]
1876  */
1877 DWORD WINAPI WNetDisconnectDialog1W( LPDISCDLGSTRUCTW lpConnDlgStruct )
1878 {
1879     FIXME( "(%p): stub\n", lpConnDlgStruct );
1880
1881     SetLastError(WN_NO_NETWORK);
1882     return WN_NO_NETWORK;
1883 }
1884
1885 /*********************************************************************
1886  * WNetGetLastErrorA [MPR.@]
1887  */
1888 DWORD WINAPI WNetGetLastErrorA( LPDWORD lpError,
1889                                 LPSTR lpErrorBuf, DWORD nErrorBufSize,
1890                                 LPSTR lpNameBuf, DWORD nNameBufSize )
1891 {
1892     FIXME( "(%p, %p, %d, %p, %d): stub\n",
1893            lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
1894
1895     SetLastError(WN_NO_NETWORK);
1896     return WN_NO_NETWORK;
1897 }
1898
1899 /*********************************************************************
1900  * WNetGetLastErrorW [MPR.@]
1901  */
1902 DWORD WINAPI WNetGetLastErrorW( LPDWORD lpError,
1903                                 LPWSTR lpErrorBuf, DWORD nErrorBufSize,
1904                          LPWSTR lpNameBuf, DWORD nNameBufSize )
1905 {
1906     FIXME( "(%p, %p, %d, %p, %d): stub\n",
1907            lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
1908
1909     SetLastError(WN_NO_NETWORK);
1910     return WN_NO_NETWORK;
1911 }
1912
1913 /*********************************************************************
1914  * WNetGetNetworkInformationA [MPR.@]
1915  */
1916 DWORD WINAPI WNetGetNetworkInformationA( LPCSTR lpProvider,
1917                                          LPNETINFOSTRUCT lpNetInfoStruct )
1918 {
1919     DWORD ret;
1920
1921     TRACE( "(%s, %p)\n", debugstr_a(lpProvider), lpNetInfoStruct );
1922
1923     if (!lpProvider)
1924         ret = WN_BAD_POINTER;
1925     else
1926     {
1927         int len;
1928
1929         len = MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, NULL, 0);
1930         if (len)
1931         {
1932             LPWSTR wideProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1933
1934             if (wideProvider)
1935             {
1936                 MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, wideProvider,
1937                  len);
1938                 ret = WNetGetNetworkInformationW(wideProvider, lpNetInfoStruct);
1939                 HeapFree(GetProcessHeap(), 0, wideProvider);
1940             }
1941             else
1942                 ret = WN_OUT_OF_MEMORY;
1943         }
1944         else
1945             ret = GetLastError();
1946     }
1947     if (ret)
1948         SetLastError(ret);
1949     TRACE("Returning %d\n", ret);
1950     return ret;
1951 }
1952
1953 /*********************************************************************
1954  * WNetGetNetworkInformationW [MPR.@]
1955  */
1956 DWORD WINAPI WNetGetNetworkInformationW( LPCWSTR lpProvider,
1957                                          LPNETINFOSTRUCT lpNetInfoStruct )
1958 {
1959     DWORD ret;
1960
1961     TRACE( "(%s, %p)\n", debugstr_w(lpProvider), lpNetInfoStruct );
1962
1963     if (!lpProvider)
1964         ret = WN_BAD_POINTER;
1965     else if (!lpNetInfoStruct)
1966         ret = WN_BAD_POINTER;
1967     else if (lpNetInfoStruct->cbStructure < sizeof(NETINFOSTRUCT))
1968         ret = WN_BAD_VALUE;
1969     else
1970     {
1971         if (providerTable && providerTable->numProviders)
1972         {
1973             DWORD providerIndex = _findProviderIndexW(lpProvider);
1974
1975             if (providerIndex != BAD_PROVIDER_INDEX)
1976             {
1977                 lpNetInfoStruct->cbStructure = sizeof(NETINFOSTRUCT);
1978                 lpNetInfoStruct->dwProviderVersion =
1979                  providerTable->table[providerIndex].dwSpecVersion;
1980                 lpNetInfoStruct->dwStatus = NO_ERROR;
1981                 lpNetInfoStruct->dwCharacteristics = 0;
1982                 lpNetInfoStruct->dwHandle = (ULONG_PTR)NULL;
1983                 lpNetInfoStruct->wNetType =
1984                  HIWORD(providerTable->table[providerIndex].dwNetType);
1985                 lpNetInfoStruct->dwPrinters = -1;
1986                 lpNetInfoStruct->dwDrives = -1;
1987                 ret = WN_SUCCESS;
1988             }
1989             else
1990                 ret = WN_BAD_PROVIDER;
1991         }
1992         else
1993             ret = WN_NO_NETWORK;
1994     }
1995     if (ret)
1996         SetLastError(ret);
1997     TRACE("Returning %d\n", ret);
1998     return ret;
1999 }
2000
2001 /*****************************************************************
2002  *  WNetGetProviderNameA [MPR.@]
2003  */
2004 DWORD WINAPI WNetGetProviderNameA( DWORD dwNetType,
2005                                    LPSTR lpProvider, LPDWORD lpBufferSize )
2006 {
2007     DWORD ret;
2008
2009     TRACE("(0x%08x, %s, %p)\n", dwNetType, debugstr_a(lpProvider),
2010      lpBufferSize);
2011
2012     if (!lpProvider)
2013         ret = WN_BAD_POINTER;
2014     else if (!lpBufferSize)
2015         ret = WN_BAD_POINTER;
2016     else
2017     {
2018         if (providerTable)
2019         {
2020             DWORD i;
2021
2022             ret = WN_NO_NETWORK;
2023             for (i = 0; i < providerTable->numProviders &&
2024              HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
2025              i++)
2026                 ;
2027             if (i < providerTable->numProviders)
2028             {
2029                 DWORD sizeNeeded = WideCharToMultiByte(CP_ACP, 0,
2030                  providerTable->table[i].name, -1, NULL, 0, NULL, NULL);
2031
2032                 if (*lpBufferSize < sizeNeeded)
2033                 {
2034                     *lpBufferSize = sizeNeeded;
2035                     ret = WN_MORE_DATA;
2036                 }
2037                 else
2038                 {
2039                     WideCharToMultiByte(CP_ACP, 0, providerTable->table[i].name,
2040                      -1, lpProvider, *lpBufferSize, NULL, NULL);
2041                     ret = WN_SUCCESS;
2042                     /* FIXME: is *lpBufferSize set to the number of characters
2043                      * copied? */
2044                 }
2045             }
2046         }
2047         else
2048             ret = WN_NO_NETWORK;
2049     }
2050     if (ret)
2051         SetLastError(ret);
2052     TRACE("Returning %d\n", ret);
2053     return ret;
2054 }
2055
2056 /*****************************************************************
2057  *  WNetGetProviderNameW [MPR.@]
2058  */
2059 DWORD WINAPI WNetGetProviderNameW( DWORD dwNetType,
2060                                    LPWSTR lpProvider, LPDWORD lpBufferSize )
2061 {
2062     DWORD ret;
2063
2064     TRACE("(0x%08x, %s, %p)\n", dwNetType, debugstr_w(lpProvider),
2065      lpBufferSize);
2066
2067     if (!lpProvider)
2068         ret = WN_BAD_POINTER;
2069     else if (!lpBufferSize)
2070         ret = WN_BAD_POINTER;
2071     else
2072     {
2073         if (providerTable)
2074         {
2075             DWORD i;
2076
2077             ret = WN_NO_NETWORK;
2078             for (i = 0; i < providerTable->numProviders &&
2079              HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
2080              i++)
2081                 ;
2082             if (i < providerTable->numProviders)
2083             {
2084                 DWORD sizeNeeded = strlenW(providerTable->table[i].name) + 1;
2085
2086                 if (*lpBufferSize < sizeNeeded)
2087                 {
2088                     *lpBufferSize = sizeNeeded;
2089                     ret = WN_MORE_DATA;
2090                 }
2091                 else
2092                 {
2093                     strcpyW(lpProvider, providerTable->table[i].name);
2094                     ret = WN_SUCCESS;
2095                     /* FIXME: is *lpBufferSize set to the number of characters
2096                      * copied? */
2097                 }
2098             }
2099         }
2100         else
2101             ret = WN_NO_NETWORK;
2102     }
2103     if (ret)
2104         SetLastError(ret);
2105     TRACE("Returning %d\n", ret);
2106     return ret;
2107 }