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