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