browseui: Fix several memory leaks in the tests.
[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     {
641         lphEnum = NULL;
642         ret = WN_NO_NETWORK;
643     }
644     else
645     {
646         if (lpNet)
647         {
648             LPNETRESOURCEW lpNetWide = NULL;
649             BYTE buf[1024];
650             DWORD size = sizeof(buf), count = 1;
651             BOOL allocated = FALSE;
652
653             ret = _thunkNetResourceArrayAToW(lpNet, &count, buf, &size);
654             if (ret == WN_MORE_DATA)
655             {
656                 lpNetWide = HeapAlloc(GetProcessHeap(), 0,
657                  size);
658                 if (lpNetWide)
659                 {
660                     ret = _thunkNetResourceArrayAToW(lpNet, &count, lpNetWide,
661                      &size);
662                     allocated = TRUE;
663                 }
664                 else
665                     ret = WN_OUT_OF_MEMORY;
666             }
667             else if (ret == WN_SUCCESS)
668                 lpNetWide = (LPNETRESOURCEW)buf;
669             if (ret == WN_SUCCESS)
670                 ret = WNetOpenEnumW(dwScope, dwType, dwUsage, lpNetWide,
671                  lphEnum);
672             if (allocated)
673                 HeapFree(GetProcessHeap(), 0, lpNetWide);
674         }
675         else
676             ret = WNetOpenEnumW(dwScope, dwType, dwUsage, NULL, lphEnum);
677     }
678     if (ret)
679         SetLastError(ret);
680     TRACE("Returning %d\n", ret);
681     return ret;
682 }
683
684 /*********************************************************************
685  * WNetOpenEnumW [MPR.@]
686  *
687  * Network enumeration has way too many parameters, so I'm not positive I got
688  * them right.  What I've got so far:
689  *
690  * - If the scope is RESOURCE_GLOBALNET, and no LPNETRESOURCE is passed,
691  *   all the network providers should be enumerated.
692  *
693  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
694  *   and neither the LPNETRESOURCE's lpRemoteName nor the LPNETRESOURCE's
695  *   lpProvider is set, all the network providers should be enumerated.
696  *   (This means the enumeration is a list of network providers, not that the
697  *   enumeration is passed on to the providers.)
698  *
699  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and the
700  *   resource matches the "Entire Network" resource (no remote name, no
701  *   provider, comment is the "Entire Network" string), a RESOURCE_GLOBALNET
702  *   enumeration is done on every network provider.
703  *
704  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
705  *   the LPNETRESOURCE's lpProvider is set, enumeration will be passed through
706  *   only to the given network provider.
707  *
708  * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
709  *   no lpProvider is set, enumeration will be tried on every network provider,
710  *   in the order in which they're loaded.
711  *
712  * - The LPNETRESOURCE should be disregarded for scopes besides
713  *   RESOURCE_GLOBALNET.  MSDN states that lpNet must be NULL if dwScope is not
714  *   RESOURCE_GLOBALNET, but Windows doesn't return an error if it isn't NULL.
715  *
716  * - If the scope is RESOURCE_CONTEXT, MS includes an "Entire Network" net
717  *   resource in the enumerated list, as well as any machines in your
718  *   workgroup.  The machines in your workgroup come from doing a
719  *   RESOURCE_CONTEXT enumeration of every Network Provider.
720  */
721 DWORD WINAPI WNetOpenEnumW( DWORD dwScope, DWORD dwType, DWORD dwUsage,
722                             LPNETRESOURCEW lpNet, LPHANDLE lphEnum )
723 {
724     DWORD ret;
725
726     TRACE( "(%08X, %08X, %08X, %p, %p)\n",
727           dwScope, dwType, dwUsage, lpNet, lphEnum );
728
729     if (!lphEnum)
730         ret = WN_BAD_POINTER;
731     else if (!providerTable || providerTable->numProviders == 0)
732     {
733         lphEnum = NULL;
734         ret = WN_NO_NETWORK;
735     }
736     else
737     {
738         switch (dwScope)
739         {
740             case RESOURCE_GLOBALNET:
741                 if (lpNet)
742                 {
743                     if (lpNet->lpProvider)
744                     {
745                         DWORD index = _findProviderIndexW(lpNet->lpProvider);
746
747                         if (index != BAD_PROVIDER_INDEX)
748                         {
749                             if (providerTable->table[index].openEnum &&
750                              providerTable->table[index].dwEnumScopes & WNNC_ENUM_GLOBAL)
751                             {
752                                 HANDLE handle;
753
754                                 ret = providerTable->table[index].openEnum(
755                                  dwScope, dwType, dwUsage, lpNet, &handle);
756                                 if (ret == WN_SUCCESS)
757                                 {
758                                     *lphEnum = _createProviderEnumerator(
759                                      dwScope, dwType, dwUsage, index, handle);
760                                     ret = *lphEnum ? WN_SUCCESS :
761                                      WN_OUT_OF_MEMORY;
762                                 }
763                             }
764                             else
765                                 ret = WN_NOT_SUPPORTED;
766                         }
767                         else
768                             ret = WN_BAD_PROVIDER;
769                     }
770                     else if (lpNet->lpRemoteName)
771                     {
772                         *lphEnum = _createGlobalEnumeratorW(dwScope,
773                          dwType, dwUsage, lpNet);
774                         ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
775                     }
776                     else
777                     {
778                         if (lpNet->lpComment && !strcmpW(lpNet->lpComment,
779                          providerTable->entireNetwork))
780                         {
781                             /* comment matches the "Entire Network", enumerate
782                              * global scope of every provider
783                              */
784                             *lphEnum = _createGlobalEnumeratorW(dwScope,
785                              dwType, dwUsage, lpNet);
786                         }
787                         else
788                         {
789                             /* this is the same as not having passed lpNet */
790                             *lphEnum = _createGlobalEnumeratorW(dwScope,
791                              dwType, dwUsage, NULL);
792                         }
793                         ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
794                     }
795                 }
796                 else
797                 {
798                     *lphEnum = _createGlobalEnumeratorW(dwScope, dwType,
799                      dwUsage, lpNet);
800                     ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
801                 }
802                 break;
803             case RESOURCE_CONTEXT:
804                 *lphEnum = _createContextEnumerator(dwScope, dwType, dwUsage);
805                 ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
806                 break;
807             case RESOURCE_REMEMBERED:
808             case RESOURCE_CONNECTED:
809                 *lphEnum = _createNullEnumerator();
810                 ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
811                 break;
812             default:
813                 WARN("unknown scope 0x%08x\n", dwScope);
814                 ret = WN_BAD_VALUE;
815         }
816     }
817     if (ret)
818         SetLastError(ret);
819     TRACE("Returning %d\n", ret);
820     return ret;
821 }
822
823 /*********************************************************************
824  * WNetEnumResourceA [MPR.@]
825  */
826 DWORD WINAPI WNetEnumResourceA( HANDLE hEnum, LPDWORD lpcCount,
827                                 LPVOID lpBuffer, LPDWORD lpBufferSize )
828 {
829     DWORD ret;
830
831     TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
832
833     if (!hEnum)
834         ret = WN_BAD_POINTER;
835     else if (!lpcCount)
836         ret = WN_BAD_POINTER;
837     else if (!lpBuffer)
838         ret = WN_BAD_POINTER;
839     else if (!lpBufferSize)
840         ret = WN_BAD_POINTER;
841     else if (*lpBufferSize < sizeof(NETRESOURCEA))
842     {
843         *lpBufferSize = sizeof(NETRESOURCEA);
844         ret = WN_MORE_DATA;
845     }
846     else
847     {
848         DWORD localCount = *lpcCount, localSize = *lpBufferSize;
849         LPVOID localBuffer = HeapAlloc(GetProcessHeap(), 0, localSize);
850
851         if (localBuffer)
852         {
853             ret = WNetEnumResourceW(hEnum, &localCount, localBuffer,
854              &localSize);
855             if (ret == WN_SUCCESS || (ret == WN_MORE_DATA && localCount != -1))
856             {
857                 /* FIXME: this isn't necessarily going to work in the case of
858                  * WN_MORE_DATA, because our enumerator may have moved on to
859                  * the next provider.  MSDN states that a large (16KB) buffer
860                  * size is the appropriate usage of this function, so
861                  * hopefully it won't be an issue.
862                  */
863                 ret = _thunkNetResourceArrayWToA(localBuffer, &localCount,
864                  lpBuffer, lpBufferSize);
865                 *lpcCount = localCount;
866             }
867             HeapFree(GetProcessHeap(), 0, localBuffer);
868         }
869         else
870             ret = WN_OUT_OF_MEMORY;
871     }
872     if (ret)
873         SetLastError(ret);
874     TRACE("Returning %d\n", ret);
875     return ret;
876 }
877
878 static DWORD _countProviderBytesW(PWNetProvider provider)
879 {
880     DWORD ret;
881
882     if (provider)
883     {
884         ret = sizeof(NETRESOURCEW);
885         ret += 2 * (strlenW(provider->name) + 1) * sizeof(WCHAR);
886     }
887     else
888         ret = 0;
889     return ret;
890 }
891
892 static DWORD _enumerateProvidersW(PWNetEnumerator enumerator, LPDWORD lpcCount,
893  LPVOID lpBuffer, const DWORD *lpBufferSize)
894 {
895     DWORD ret;
896
897     if (!enumerator)
898         return WN_BAD_POINTER;
899     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
900         return WN_BAD_VALUE;
901     if (!lpcCount)
902         return WN_BAD_POINTER;
903     if (!lpBuffer)
904         return WN_BAD_POINTER;
905     if (!lpBufferSize)
906         return WN_BAD_POINTER;
907     if (*lpBufferSize < sizeof(NETRESOURCEA))
908         return WN_MORE_DATA;
909
910     if (!providerTable || enumerator->providerIndex >= 
911      providerTable->numProviders)
912         ret = WN_NO_MORE_ENTRIES;
913     else
914     {
915         DWORD bytes = 0, count = 0, countLimit, i;
916         LPNETRESOURCEW resource;
917         LPWSTR strNext;
918
919         countLimit = *lpcCount == -1 ?
920          providerTable->numProviders - enumerator->providerIndex : *lpcCount;
921         while (count < countLimit && bytes < *lpBufferSize)
922         {
923             DWORD bytesNext = _countProviderBytesW(
924              &providerTable->table[count + enumerator->providerIndex]);
925
926             if (bytes + bytesNext < *lpBufferSize)
927             {
928                 bytes += bytesNext;
929                 count++;
930             }
931         }
932         strNext = (LPWSTR)((LPBYTE)lpBuffer + count * sizeof(NETRESOURCEW));
933         for (i = 0, resource = lpBuffer; i < count; i++, resource++)
934         {
935             resource->dwScope = RESOURCE_GLOBALNET;
936             resource->dwType = RESOURCETYPE_ANY;
937             resource->dwDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
938             resource->dwUsage = RESOURCEUSAGE_CONTAINER |
939              RESOURCEUSAGE_RESERVED;
940             resource->lpLocalName = NULL;
941             resource->lpRemoteName = strNext;
942             strcpyW(resource->lpRemoteName,
943              providerTable->table[i + enumerator->providerIndex].name);
944             strNext += strlenW(resource->lpRemoteName) + 1;
945             resource->lpComment = NULL;
946             resource->lpProvider = strNext;
947             strcpyW(resource->lpProvider,
948              providerTable->table[i + enumerator->providerIndex].name);
949             strNext += strlenW(resource->lpProvider) + 1;
950         }
951         enumerator->providerIndex += count;
952         *lpcCount = count;
953         ret = count > 0 ? WN_SUCCESS : WN_MORE_DATA;
954     }
955     TRACE("Returning %d\n", ret);
956     return ret;
957 }
958
959 /* Advances the enumerator (assumed to be a global enumerator) to the next
960  * provider that supports the enumeration scope passed to WNetOpenEnum.  Does
961  * not open a handle with the next provider.
962  * If the existing handle is NULL, may leave the enumerator unchanged, since
963  * the current provider may support the desired scope.
964  * If the existing handle is not NULL, closes it before moving on.
965  * Returns WN_SUCCESS on success, WN_NO_MORE_ENTRIES if there is no available
966  * provider, and another error on failure.
967  */
968 static DWORD _globalEnumeratorAdvance(PWNetEnumerator enumerator)
969 {
970     if (!enumerator)
971         return WN_BAD_POINTER;
972     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
973         return WN_BAD_VALUE;
974     if (!providerTable || enumerator->providerIndex >=
975      providerTable->numProviders)
976         return WN_NO_MORE_ENTRIES;
977
978     if (enumerator->providerDone)
979     {
980         DWORD dwEnum = 0;
981         enumerator->providerDone = FALSE;
982         if (enumerator->handle)
983         {
984             providerTable->table[enumerator->providerIndex].closeEnum(
985              enumerator->handle);
986             enumerator->handle = NULL;
987             enumerator->providerIndex++;
988         }
989         if (enumerator->dwScope == RESOURCE_CONNECTED)
990             dwEnum = WNNC_ENUM_LOCAL;
991         else if (enumerator->dwScope == RESOURCE_GLOBALNET)
992             dwEnum = WNNC_ENUM_GLOBAL;
993         else if (enumerator->dwScope == RESOURCE_CONTEXT)
994             dwEnum = WNNC_ENUM_CONTEXT;
995         for (; enumerator->providerIndex < providerTable->numProviders &&
996          !(providerTable->table[enumerator->providerIndex].dwEnumScopes
997          & dwEnum); enumerator->providerIndex++)
998             ;
999     }
1000     return enumerator->providerIndex < providerTable->numProviders ?
1001      WN_SUCCESS : WN_NO_MORE_ENTRIES;
1002 }
1003
1004 /* "Passes through" call to the next provider that supports the enumeration
1005  * type.
1006  * FIXME: if one call to a provider's enumerator succeeds while there's still
1007  * space in lpBuffer, I don't call to the next provider.  The caller may not
1008  * expect that it should call EnumResourceW again with a return value of
1009  * WN_SUCCESS (depending what *lpcCount was to begin with).  That means strings
1010  * may have to be moved around a bit, ick.
1011  */
1012 static DWORD _enumerateGlobalPassthroughW(PWNetEnumerator enumerator,
1013  LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
1014 {
1015     DWORD ret;
1016
1017     if (!enumerator)
1018         return WN_BAD_POINTER;
1019     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
1020         return WN_BAD_VALUE;
1021     if (!lpcCount)
1022         return WN_BAD_POINTER;
1023     if (!lpBuffer)
1024         return WN_BAD_POINTER;
1025     if (!lpBufferSize)
1026         return WN_BAD_POINTER;
1027     if (*lpBufferSize < sizeof(NETRESOURCEW))
1028         return WN_MORE_DATA;
1029
1030     ret = _globalEnumeratorAdvance(enumerator);
1031     if (ret == WN_SUCCESS)
1032     {
1033         ret = providerTable->table[enumerator->providerIndex].
1034          openEnum(enumerator->dwScope, enumerator->dwType,
1035          enumerator->dwUsage, enumerator->lpNet,
1036          &enumerator->handle);
1037         if (ret == WN_SUCCESS)
1038         {
1039             ret = providerTable->table[enumerator->providerIndex].
1040              enumResource(enumerator->handle, lpcCount, lpBuffer,
1041              lpBufferSize);
1042             if (ret != WN_MORE_DATA)
1043                 enumerator->providerDone = TRUE;
1044         }
1045     }
1046     TRACE("Returning %d\n", ret);
1047     return ret;
1048 }
1049
1050 static DWORD _enumerateGlobalW(PWNetEnumerator enumerator, LPDWORD lpcCount,
1051  LPVOID lpBuffer, LPDWORD lpBufferSize)
1052 {
1053     DWORD ret;
1054
1055     if (!enumerator)
1056         return WN_BAD_POINTER;
1057     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
1058         return WN_BAD_VALUE;
1059     if (!lpcCount)
1060         return WN_BAD_POINTER;
1061     if (!lpBuffer)
1062         return WN_BAD_POINTER;
1063     if (!lpBufferSize)
1064         return WN_BAD_POINTER;
1065     if (*lpBufferSize < sizeof(NETRESOURCEW))
1066         return WN_MORE_DATA;
1067     if (!providerTable)
1068         return WN_NO_NETWORK;
1069
1070     switch (enumerator->dwScope)
1071     {
1072         case RESOURCE_GLOBALNET:
1073             if (enumerator->lpNet)
1074                 ret = _enumerateGlobalPassthroughW(enumerator, lpcCount,
1075                  lpBuffer, lpBufferSize);
1076             else
1077                 ret = _enumerateProvidersW(enumerator, lpcCount, lpBuffer,
1078                  lpBufferSize);
1079             break;
1080         case RESOURCE_CONTEXT:
1081             ret = _enumerateGlobalPassthroughW(enumerator, lpcCount, lpBuffer,
1082              lpBufferSize);
1083             break;
1084         default:
1085             WARN("unexpected scope 0x%08x\n", enumerator->dwScope);
1086             ret = WN_NO_MORE_ENTRIES;
1087     }
1088     TRACE("Returning %d\n", ret);
1089     return ret;
1090 }
1091
1092 static DWORD _enumerateProviderW(PWNetEnumerator enumerator, LPDWORD lpcCount,
1093  LPVOID lpBuffer, LPDWORD lpBufferSize)
1094 {
1095     if (!enumerator)
1096         return WN_BAD_POINTER;
1097     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_PROVIDER)
1098         return WN_BAD_VALUE;
1099     if (!enumerator->handle)
1100         return WN_BAD_VALUE;
1101     if (!lpcCount)
1102         return WN_BAD_POINTER;
1103     if (!lpBuffer)
1104         return WN_BAD_POINTER;
1105     if (!lpBufferSize)
1106         return WN_BAD_POINTER;
1107     if (!providerTable)
1108         return WN_NO_NETWORK;
1109     if (enumerator->providerIndex >= providerTable->numProviders)
1110         return WN_NO_MORE_ENTRIES;
1111     if (!providerTable->table[enumerator->providerIndex].enumResource)
1112         return WN_BAD_VALUE;
1113     return providerTable->table[enumerator->providerIndex].enumResource(
1114      enumerator->handle, lpcCount, lpBuffer, lpBufferSize);
1115 }
1116
1117 static DWORD _enumerateContextW(PWNetEnumerator enumerator, LPDWORD lpcCount,
1118  LPVOID lpBuffer, LPDWORD lpBufferSize)
1119 {
1120     DWORD ret;
1121     size_t cchEntireNetworkLen, bytesNeeded;
1122
1123     if (!enumerator)
1124         return WN_BAD_POINTER;
1125     if (enumerator->enumType != WNET_ENUMERATOR_TYPE_CONTEXT)
1126         return WN_BAD_VALUE;
1127     if (!lpcCount)
1128         return WN_BAD_POINTER;
1129     if (!lpBuffer)
1130         return WN_BAD_POINTER;
1131     if (!lpBufferSize)
1132         return WN_BAD_POINTER;
1133     if (!providerTable)
1134         return WN_NO_NETWORK;
1135
1136     cchEntireNetworkLen = strlenW(providerTable->entireNetwork) + 1;
1137     bytesNeeded = sizeof(NETRESOURCEW) + cchEntireNetworkLen * sizeof(WCHAR);
1138     if (*lpBufferSize < bytesNeeded)
1139     {
1140         *lpBufferSize = bytesNeeded;
1141         ret = WN_MORE_DATA;
1142     }
1143     else
1144     {
1145         LPNETRESOURCEW lpNet = lpBuffer;
1146
1147         lpNet->dwScope = RESOURCE_GLOBALNET;
1148         lpNet->dwType = enumerator->dwType;
1149         lpNet->dwDisplayType = RESOURCEDISPLAYTYPE_ROOT;
1150         lpNet->dwUsage = RESOURCEUSAGE_CONTAINER;
1151         lpNet->lpLocalName = NULL;
1152         lpNet->lpRemoteName = NULL;
1153         lpNet->lpProvider = NULL;
1154         /* odd, but correct: put comment at end of buffer, so it won't get
1155          * overwritten by subsequent calls to a provider's enumResource
1156          */
1157         lpNet->lpComment = (LPWSTR)((LPBYTE)lpBuffer + *lpBufferSize -
1158          (cchEntireNetworkLen * sizeof(WCHAR)));
1159         strcpyW(lpNet->lpComment, providerTable->entireNetwork);
1160         ret = WN_SUCCESS;
1161     }
1162     if (ret == WN_SUCCESS)
1163     {
1164         DWORD bufferSize = *lpBufferSize - bytesNeeded;
1165
1166         /* "Entire Network" entry enumerated--morph this into a global
1167          * enumerator.  enumerator->lpNet continues to be NULL, since it has
1168          * no meaning when the scope isn't RESOURCE_GLOBALNET.
1169          */
1170         enumerator->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
1171         ret = _enumerateGlobalW(enumerator, lpcCount,
1172          (LPBYTE)lpBuffer + bytesNeeded, &bufferSize);
1173         if (ret == WN_SUCCESS)
1174         {
1175             /* reflect the fact that we already enumerated "Entire Network" */
1176             lpcCount++;
1177             *lpBufferSize = bufferSize + bytesNeeded;
1178         }
1179         else
1180         {
1181             /* the provider enumeration failed, but we already succeeded in
1182              * enumerating "Entire Network"--leave type as global to allow a
1183              * retry, but indicate success with a count of one.
1184              */
1185             ret = WN_SUCCESS;
1186             *lpcCount = 1;
1187             *lpBufferSize = bytesNeeded;
1188         }
1189     }
1190     TRACE("Returning %d\n", ret);
1191     return ret;
1192 }
1193
1194 /*********************************************************************
1195  * WNetEnumResourceW [MPR.@]
1196  */
1197 DWORD WINAPI WNetEnumResourceW( HANDLE hEnum, LPDWORD lpcCount,
1198                                 LPVOID lpBuffer, LPDWORD lpBufferSize )
1199 {
1200     DWORD ret;
1201
1202     TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
1203
1204     if (!hEnum)
1205         ret = WN_BAD_POINTER;
1206     else if (!lpcCount)
1207         ret = WN_BAD_POINTER;
1208     else if (!lpBuffer)
1209         ret = WN_BAD_POINTER;
1210     else if (!lpBufferSize)
1211         ret = WN_BAD_POINTER;
1212     else if (*lpBufferSize < sizeof(NETRESOURCEW))
1213     {
1214         *lpBufferSize = sizeof(NETRESOURCEW);
1215         ret = WN_MORE_DATA;
1216     }
1217     else
1218     {
1219         PWNetEnumerator enumerator = (PWNetEnumerator)hEnum;
1220
1221         switch (enumerator->enumType)
1222         {
1223             case WNET_ENUMERATOR_TYPE_NULL:
1224                 ret = WN_NO_MORE_ENTRIES;
1225                 break;
1226             case WNET_ENUMERATOR_TYPE_GLOBAL:
1227                 ret = _enumerateGlobalW(enumerator, lpcCount, lpBuffer,
1228                  lpBufferSize);
1229                 break;
1230             case WNET_ENUMERATOR_TYPE_PROVIDER:
1231                 ret = _enumerateProviderW(enumerator, lpcCount, lpBuffer,
1232                  lpBufferSize);
1233                 break;
1234             case WNET_ENUMERATOR_TYPE_CONTEXT:
1235                 ret = _enumerateContextW(enumerator, lpcCount, lpBuffer,
1236                  lpBufferSize);
1237                 break;
1238             default:
1239                 WARN("bogus enumerator type!\n");
1240                 ret = WN_NO_NETWORK;
1241         }
1242     }
1243     if (ret)
1244         SetLastError(ret);
1245     TRACE("Returning %d\n", ret);
1246     return ret;
1247 }
1248
1249 /*********************************************************************
1250  * WNetCloseEnum [MPR.@]
1251  */
1252 DWORD WINAPI WNetCloseEnum( HANDLE hEnum )
1253 {
1254     DWORD ret;
1255
1256     TRACE( "(%p)\n", hEnum );
1257
1258     if (hEnum)
1259     {
1260         PWNetEnumerator enumerator = (PWNetEnumerator)hEnum;
1261
1262         switch (enumerator->enumType)
1263         {
1264             case WNET_ENUMERATOR_TYPE_NULL:
1265                 ret = WN_SUCCESS;
1266                 break;
1267             case WNET_ENUMERATOR_TYPE_GLOBAL:
1268                 if (enumerator->lpNet)
1269                     _freeEnumNetResource(enumerator->lpNet);
1270                 if (enumerator->handle)
1271                     providerTable->table[enumerator->providerIndex].
1272                      closeEnum(enumerator->handle);
1273                 ret = WN_SUCCESS;
1274                 break;
1275             case WNET_ENUMERATOR_TYPE_PROVIDER:
1276                 if (enumerator->handle)
1277                     providerTable->table[enumerator->providerIndex].
1278                      closeEnum(enumerator->handle);
1279                 ret = WN_SUCCESS;
1280                 break;
1281             default:
1282                 WARN("bogus enumerator type!\n");
1283                 ret = WN_BAD_HANDLE;
1284         }
1285         HeapFree(GetProcessHeap(), 0, hEnum);
1286     }
1287     else
1288         ret = WN_BAD_HANDLE;
1289     if (ret)
1290         SetLastError(ret);
1291     TRACE("Returning %d\n", ret);
1292     return ret;
1293 }
1294
1295 /*********************************************************************
1296  * WNetGetResourceInformationA [MPR.@]
1297  *
1298  * See WNetGetResourceInformationW
1299  */
1300 DWORD WINAPI WNetGetResourceInformationA( LPNETRESOURCEA lpNetResource,
1301                                           LPVOID lpBuffer, LPDWORD cbBuffer,
1302                                           LPSTR *lplpSystem )
1303 {
1304     DWORD ret;
1305
1306     TRACE( "(%p, %p, %p, %p)\n",
1307            lpNetResource, lpBuffer, cbBuffer, lplpSystem );
1308
1309     if (!providerTable || providerTable->numProviders == 0)
1310         ret = WN_NO_NETWORK;
1311     else if (lpNetResource)
1312     {
1313         LPNETRESOURCEW lpNetResourceW = NULL;
1314         DWORD size = 1024, count = 1;
1315         DWORD len;
1316
1317         lpNetResourceW = HeapAlloc(GetProcessHeap(), 0, size);
1318         ret = _thunkNetResourceArrayAToW(lpNetResource, &count, lpNetResourceW, &size);
1319         if (ret == WN_MORE_DATA)
1320         {
1321             HeapFree(GetProcessHeap(), 0, lpNetResourceW);
1322             lpNetResourceW = HeapAlloc(GetProcessHeap(), 0, size);
1323             if (lpNetResourceW)
1324                 ret = _thunkNetResourceArrayAToW(lpNetResource,
1325                         &count, lpNetResourceW, &size);
1326             else
1327                 ret = WN_OUT_OF_MEMORY;
1328         }
1329         if (ret == WN_SUCCESS)
1330         {
1331             LPWSTR lpSystemW = NULL;
1332             LPVOID lpBufferW;
1333             size = 1024;
1334             lpBufferW = HeapAlloc(GetProcessHeap(), 0, size);
1335             if (lpBufferW)
1336             {
1337                 ret = WNetGetResourceInformationW(lpNetResourceW,
1338                         lpBufferW, &size, &lpSystemW);
1339                 if (ret == WN_MORE_DATA)
1340                 {
1341                     HeapFree(GetProcessHeap(), 0, lpBufferW);
1342                     lpBufferW = HeapAlloc(GetProcessHeap(), 0, size);
1343                     if (lpBufferW)
1344                         ret = WNetGetResourceInformationW(lpNetResourceW,
1345                             lpBufferW, &size, &lpSystemW);
1346                     else
1347                         ret = WN_OUT_OF_MEMORY;
1348                 }
1349                 if (ret == WN_SUCCESS)
1350                 {
1351                     ret = _thunkNetResourceArrayWToA(lpBufferW,
1352                             &count, lpBuffer, cbBuffer);
1353                     HeapFree(GetProcessHeap(), 0, lpNetResourceW);
1354                     lpNetResourceW = lpBufferW;
1355                     size = sizeof(NETRESOURCEA);
1356                     size += WideCharToMultiByte(CP_ACP, 0, lpNetResourceW->lpRemoteName,
1357                             -1, NULL, 0, NULL, NULL);
1358                     size += WideCharToMultiByte(CP_ACP, 0, lpNetResourceW->lpProvider,
1359                             -1, NULL, 0, NULL, NULL);
1360
1361                     len = WideCharToMultiByte(CP_ACP, 0, lpSystemW,
1362                                       -1, NULL, 0, NULL, NULL);
1363                     if ((len) && ( size + len < *cbBuffer))
1364                     {
1365                         *lplpSystem = (char*)lpBuffer + *cbBuffer - len;
1366                         WideCharToMultiByte(CP_ACP, 0, lpSystemW, -1,
1367                              *lplpSystem, len, NULL, NULL);
1368                          ret = WN_SUCCESS;
1369                     }
1370                     else
1371                         ret = WN_MORE_DATA;
1372                 }
1373                 else
1374                     ret = WN_OUT_OF_MEMORY;
1375                 HeapFree(GetProcessHeap(), 0, lpBufferW);
1376             }
1377             else
1378                 ret = WN_OUT_OF_MEMORY;
1379             HeapFree(GetProcessHeap(), 0, lpSystemW);
1380         }
1381         HeapFree(GetProcessHeap(), 0, lpNetResourceW);
1382     }
1383     else
1384         ret = WN_NO_NETWORK;
1385
1386     if (ret)
1387         SetLastError(ret);
1388     TRACE("Returning %d\n", ret);
1389     return ret;
1390 }
1391
1392 /*********************************************************************
1393  * WNetGetResourceInformationW [MPR.@]
1394  *
1395  * WNetGetResourceInformationW function identifies the network provider
1396  * that owns the resource and gets information about the type of the resource.
1397  *
1398  * PARAMS:
1399  *  lpNetResource    [ I]    the pointer to NETRESOURCEW structure, that
1400  *                          defines a network resource.
1401  *  lpBuffer         [ O]   the pointer to buffer, containing result. It
1402  *                          contains NETRESOURCEW structure and strings to
1403  *                          which the members of the NETRESOURCEW structure
1404  *                          point.
1405  *  cbBuffer         [I/O] the pointer to DWORD number - size of buffer
1406  *                          in bytes.
1407  *  lplpSystem       [ O]   the pointer to string in the output buffer,
1408  *                          containing the part of the resource name without
1409  *                          names of the server and share.
1410  *
1411  * RETURNS:
1412  *  NO_ERROR if the function succeeds. System error code if the function fails.
1413  */
1414
1415 DWORD WINAPI WNetGetResourceInformationW( LPNETRESOURCEW lpNetResource,
1416                                           LPVOID lpBuffer, LPDWORD cbBuffer,
1417                                           LPWSTR *lplpSystem )
1418 {
1419     DWORD ret = WN_NO_NETWORK;
1420     DWORD index;
1421
1422     TRACE( "(%p, %p, %p, %p)\n",
1423            lpNetResource, lpBuffer, cbBuffer, lplpSystem);
1424
1425     if (!(lpBuffer))
1426         ret = WN_OUT_OF_MEMORY;
1427     else if (providerTable != NULL)
1428     {
1429         /* FIXME: For function value of a variable is indifferent, it does
1430          * search of all providers in a network.
1431          */
1432         for (index = 0; index < providerTable->numProviders; index++)
1433         {
1434             if(providerTable->table[index].getCaps(WNNC_DIALOG) &
1435                 WNNC_DLG_GETRESOURCEINFORMATION)
1436             {
1437                 if (providerTable->table[index].getResourceInformation)
1438                     ret = providerTable->table[index].getResourceInformation(
1439                         lpNetResource, lpBuffer, cbBuffer, lplpSystem);
1440                 else
1441                     ret = WN_NO_NETWORK;
1442                 if (ret == WN_SUCCESS)
1443                     break;
1444             }
1445         }
1446     }
1447     if (ret)
1448         SetLastError(ret);
1449     return ret;
1450 }
1451
1452 /*********************************************************************
1453  * WNetGetResourceParentA [MPR.@]
1454  */
1455 DWORD WINAPI WNetGetResourceParentA( LPNETRESOURCEA lpNetResource,
1456                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1457 {
1458     FIXME( "(%p, %p, %p): stub\n",
1459            lpNetResource, lpBuffer, lpBufferSize );
1460
1461     SetLastError(WN_NO_NETWORK);
1462     return WN_NO_NETWORK;
1463 }
1464
1465 /*********************************************************************
1466  * WNetGetResourceParentW [MPR.@]
1467  */
1468 DWORD WINAPI WNetGetResourceParentW( LPNETRESOURCEW lpNetResource,
1469                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1470 {
1471     FIXME( "(%p, %p, %p): stub\n",
1472            lpNetResource, lpBuffer, lpBufferSize );
1473
1474     SetLastError(WN_NO_NETWORK);
1475     return WN_NO_NETWORK;
1476 }
1477
1478
1479
1480 /*
1481  * Connection Functions
1482  */
1483
1484 /*********************************************************************
1485  *  WNetAddConnectionA [MPR.@]
1486  */
1487 DWORD WINAPI WNetAddConnectionA( LPCSTR lpRemoteName, LPCSTR lpPassword,
1488                                  LPCSTR lpLocalName )
1489 {
1490     FIXME( "(%s, %p, %s): stub\n",
1491            debugstr_a(lpRemoteName), lpPassword, debugstr_a(lpLocalName) );
1492
1493     SetLastError(WN_NO_NETWORK);
1494     return WN_NO_NETWORK;
1495 }
1496
1497 /*********************************************************************
1498  *  WNetAddConnectionW [MPR.@]
1499  */
1500 DWORD WINAPI WNetAddConnectionW( LPCWSTR lpRemoteName, LPCWSTR lpPassword,
1501                                  LPCWSTR lpLocalName )
1502 {
1503     FIXME( "(%s, %p, %s): stub\n",
1504            debugstr_w(lpRemoteName), lpPassword, debugstr_w(lpLocalName) );
1505
1506     SetLastError(WN_NO_NETWORK);
1507     return WN_NO_NETWORK;
1508 }
1509
1510 /*********************************************************************
1511  *  WNetAddConnection2A [MPR.@]
1512  */
1513 DWORD WINAPI WNetAddConnection2A( LPNETRESOURCEA lpNetResource,
1514                                   LPCSTR lpPassword, LPCSTR lpUserID,
1515                                   DWORD dwFlags )
1516 {
1517     FIXME( "(%p, %p, %s, 0x%08X): stub\n",
1518            lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags );
1519
1520     SetLastError(WN_NO_NETWORK);
1521     return WN_NO_NETWORK;
1522 }
1523
1524 /*********************************************************************
1525  * WNetAddConnection2W [MPR.@]
1526  */
1527 DWORD WINAPI WNetAddConnection2W( LPNETRESOURCEW lpNetResource,
1528                                   LPCWSTR lpPassword, LPCWSTR lpUserID,
1529                                   DWORD dwFlags )
1530 {
1531     FIXME( "(%p, %p, %s, 0x%08X): stub\n",
1532            lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags );
1533
1534     SetLastError(WN_NO_NETWORK);
1535     return WN_NO_NETWORK;
1536 }
1537
1538 /*********************************************************************
1539  * WNetAddConnection3A [MPR.@]
1540  */
1541 DWORD WINAPI WNetAddConnection3A( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
1542                                   LPCSTR lpPassword, LPCSTR lpUserID,
1543                                   DWORD dwFlags )
1544 {
1545     FIXME( "(%p, %p, %p, %s, 0x%08X), stub\n",
1546            hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags );
1547
1548     SetLastError(WN_NO_NETWORK);
1549     return WN_NO_NETWORK;
1550 }
1551
1552 /*********************************************************************
1553  * WNetAddConnection3W [MPR.@]
1554  */
1555 DWORD WINAPI WNetAddConnection3W( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
1556                                   LPCWSTR lpPassword, LPCWSTR lpUserID,
1557                                   DWORD dwFlags )
1558 {
1559     FIXME( "(%p, %p, %p, %s, 0x%08X), stub\n",
1560            hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags );
1561
1562     SetLastError(WN_NO_NETWORK);
1563     return WN_NO_NETWORK;
1564 }
1565
1566 /*****************************************************************
1567  *  WNetUseConnectionA [MPR.@]
1568  */
1569 DWORD WINAPI WNetUseConnectionA( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
1570                                  LPCSTR lpPassword, LPCSTR lpUserID, DWORD dwFlags,
1571                                  LPSTR lpAccessName, LPDWORD lpBufferSize,
1572                                  LPDWORD lpResult )
1573 {
1574     FIXME( "(%p, %p, %p, %s, 0x%08X, %s, %p, %p), stub\n",
1575            hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags,
1576            debugstr_a(lpAccessName), lpBufferSize, lpResult );
1577
1578     SetLastError(WN_NO_NETWORK);
1579     return WN_NO_NETWORK;
1580 }
1581
1582 /*****************************************************************
1583  *  WNetUseConnectionW [MPR.@]
1584  */
1585 DWORD WINAPI WNetUseConnectionW( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
1586                                  LPCWSTR lpPassword, LPCWSTR lpUserID, DWORD dwFlags,
1587                                  LPWSTR lpAccessName, LPDWORD lpBufferSize,
1588                                  LPDWORD lpResult )
1589 {
1590     FIXME( "(%p, %p, %p, %s, 0x%08X, %s, %p, %p), stub\n",
1591            hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags,
1592            debugstr_w(lpAccessName), lpBufferSize, lpResult );
1593
1594     SetLastError(WN_NO_NETWORK);
1595     return WN_NO_NETWORK;
1596 }
1597
1598 /*********************************************************************
1599  *  WNetCancelConnectionA [MPR.@]
1600  */
1601 DWORD WINAPI WNetCancelConnectionA( LPCSTR lpName, BOOL fForce )
1602 {
1603     FIXME( "(%s, %d), stub\n", debugstr_a(lpName), fForce );
1604
1605     return WN_SUCCESS;
1606 }
1607
1608 /*********************************************************************
1609  *  WNetCancelConnectionW [MPR.@]
1610  */
1611 DWORD WINAPI WNetCancelConnectionW( LPCWSTR lpName, BOOL fForce )
1612 {
1613     FIXME( "(%s, %d), stub\n", debugstr_w(lpName), fForce );
1614
1615     return WN_SUCCESS;
1616 }
1617
1618 /*********************************************************************
1619  *  WNetCancelConnection2A [MPR.@]
1620  */
1621 DWORD WINAPI WNetCancelConnection2A( LPCSTR lpName, DWORD dwFlags, BOOL fForce )
1622 {
1623     FIXME( "(%s, %08X, %d), stub\n", debugstr_a(lpName), dwFlags, fForce );
1624
1625     return WN_SUCCESS;
1626 }
1627
1628 /*********************************************************************
1629  *  WNetCancelConnection2W [MPR.@]
1630  */
1631 DWORD WINAPI WNetCancelConnection2W( LPCWSTR lpName, DWORD dwFlags, BOOL fForce )
1632 {
1633     FIXME( "(%s, %08X, %d), stub\n", debugstr_w(lpName), dwFlags, fForce );
1634
1635     return WN_SUCCESS;
1636 }
1637
1638 /*****************************************************************
1639  *  WNetRestoreConnectionA [MPR.@]
1640  */
1641 DWORD WINAPI WNetRestoreConnectionA( HWND hwndOwner, LPCSTR lpszDevice )
1642 {
1643     FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_a(lpszDevice) );
1644
1645     SetLastError(WN_NO_NETWORK);
1646     return WN_NO_NETWORK;
1647 }
1648
1649 /*****************************************************************
1650  *  WNetRestoreConnectionW [MPR.@]
1651  */
1652 DWORD WINAPI WNetRestoreConnectionW( HWND hwndOwner, LPCWSTR lpszDevice )
1653 {
1654     FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_w(lpszDevice) );
1655
1656     SetLastError(WN_NO_NETWORK);
1657     return WN_NO_NETWORK;
1658 }
1659
1660 /**************************************************************************
1661  * WNetGetConnectionA [MPR.@]
1662  *
1663  * RETURNS
1664  * - WN_BAD_LOCALNAME     lpLocalName makes no sense
1665  * - WN_NOT_CONNECTED     drive is a local drive
1666  * - WN_MORE_DATA         buffer isn't big enough
1667  * - WN_SUCCESS           success (net path in buffer)
1668  *
1669  * FIXME: need to test return values under different errors
1670  */
1671 DWORD WINAPI WNetGetConnectionA( LPCSTR lpLocalName,
1672                                  LPSTR lpRemoteName, LPDWORD lpBufferSize )
1673 {
1674     DWORD ret;
1675
1676     if (!lpLocalName)
1677         ret = WN_BAD_POINTER;
1678     else if (!lpBufferSize)
1679         ret = WN_BAD_POINTER;
1680     else if (!lpRemoteName && *lpBufferSize)
1681         ret = WN_BAD_POINTER;
1682     else
1683     {
1684         int len = MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, NULL, 0);
1685
1686         if (len)
1687         {
1688             PWSTR wideLocalName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1689
1690             if (wideLocalName)
1691             {
1692                 WCHAR wideRemoteStatic[MAX_PATH];
1693                 DWORD wideRemoteSize = sizeof(wideRemoteStatic) / sizeof(WCHAR);
1694
1695                 MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, wideLocalName, len);
1696
1697                 /* try once without memory allocation */
1698                 ret = WNetGetConnectionW(wideLocalName, wideRemoteStatic,
1699                  &wideRemoteSize);
1700                 if (ret == WN_SUCCESS)
1701                 {
1702                     int len = WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
1703                      -1, NULL, 0, NULL, NULL);
1704
1705                     if (len <= *lpBufferSize)
1706                     {
1707                         WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic, -1,
1708                          lpRemoteName, *lpBufferSize, NULL, NULL);
1709                         ret = WN_SUCCESS;
1710                     }
1711                     else
1712                     {
1713                         *lpBufferSize = len;
1714                         ret = WN_MORE_DATA;
1715                     }
1716                 }
1717                 else if (ret == WN_MORE_DATA)
1718                 {
1719                     PWSTR wideRemote = HeapAlloc(GetProcessHeap(), 0,
1720                      wideRemoteSize * sizeof(WCHAR));
1721
1722                     if (wideRemote)
1723                     {
1724                         ret = WNetGetConnectionW(wideLocalName, wideRemote,
1725                          &wideRemoteSize);
1726                         if (ret == WN_SUCCESS)
1727                         {
1728                             if (len <= *lpBufferSize)
1729                             {
1730                                 WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
1731                                  -1, lpRemoteName, *lpBufferSize, NULL, NULL);
1732                                 ret = WN_SUCCESS;
1733                             }
1734                             else
1735                             {
1736                                 *lpBufferSize = len;
1737                                 ret = WN_MORE_DATA;
1738                             }
1739                         }
1740                         HeapFree(GetProcessHeap(), 0, wideRemote);
1741                     }
1742                     else
1743                         ret = WN_OUT_OF_MEMORY;
1744                 }
1745                 HeapFree(GetProcessHeap(), 0, wideLocalName);
1746             }
1747             else
1748                 ret = WN_OUT_OF_MEMORY;
1749         }
1750         else
1751             ret = WN_BAD_LOCALNAME;
1752     }
1753     if (ret)
1754         SetLastError(ret);
1755     TRACE("Returning %d\n", ret);
1756     return ret;
1757 }
1758
1759 /* find the network connection for a given drive; helper for WNetGetConnection */
1760 static DWORD get_drive_connection( WCHAR letter, LPWSTR remote, LPDWORD size )
1761 {
1762     char buffer[1024];
1763     struct mountmgr_unix_drive *data = (struct mountmgr_unix_drive *)buffer;
1764     HANDLE mgr;
1765     DWORD ret = WN_NOT_CONNECTED;
1766
1767     if ((mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE,
1768                             FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
1769                             0, 0 )) == INVALID_HANDLE_VALUE)
1770     {
1771         ERR( "failed to open mount manager err %u\n", GetLastError() );
1772         return ret;
1773     }
1774     memset( data, 0, sizeof(*data) );
1775     data->letter = letter;
1776     if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, data, sizeof(*data),
1777                          data, sizeof(buffer), NULL, NULL ))
1778     {
1779         char *p, *mount_point = buffer + data->mount_point_offset;
1780         DWORD len;
1781
1782         if (data->mount_point_offset && !strncmp( mount_point, "unc/", 4 ))
1783         {
1784             mount_point += 2;
1785             mount_point[0] = '\\';
1786             for (p = mount_point; *p; p++) if (*p == '/') *p = '\\';
1787
1788             len = MultiByteToWideChar( CP_UNIXCP, 0, mount_point, -1, NULL, 0 );
1789             if (len > *size)
1790             {
1791                 *size = len;
1792                 ret = WN_MORE_DATA;
1793             }
1794             else
1795             {
1796                 *size = MultiByteToWideChar( CP_UNIXCP, 0, mount_point, -1, remote, *size);
1797                 ret = WN_SUCCESS;
1798             }
1799         }
1800     }
1801     CloseHandle( mgr );
1802     return ret;
1803 }
1804
1805 /**************************************************************************
1806  * WNetGetConnectionW [MPR.@]
1807  *
1808  * FIXME: need to test return values under different errors
1809  */
1810 DWORD WINAPI WNetGetConnectionW( LPCWSTR lpLocalName,
1811                                  LPWSTR lpRemoteName, LPDWORD lpBufferSize )
1812 {
1813     DWORD ret;
1814
1815     TRACE("(%s, %p, %p)\n", debugstr_w(lpLocalName), lpRemoteName,
1816      lpBufferSize);
1817
1818     if (!lpLocalName)
1819         ret = WN_BAD_POINTER;
1820     else if (!lpBufferSize)
1821         ret = WN_BAD_POINTER;
1822     else if (!lpRemoteName && *lpBufferSize)
1823         ret = WN_BAD_POINTER;
1824     else if (!lpLocalName[0])
1825         ret = WN_BAD_LOCALNAME;
1826     else
1827     {
1828         if (lpLocalName[1] == ':')
1829         {
1830             switch(GetDriveTypeW(lpLocalName))
1831             {
1832             case DRIVE_REMOTE:
1833                 ret = get_drive_connection( lpLocalName[0], lpRemoteName, lpBufferSize );
1834                 break;
1835             case DRIVE_REMOVABLE:
1836             case DRIVE_FIXED:
1837             case DRIVE_CDROM:
1838                 TRACE("file is local\n");
1839                 ret = WN_NOT_CONNECTED;
1840                 break;
1841             default:
1842                 ret = WN_BAD_LOCALNAME;
1843             }
1844         }
1845         else
1846             ret = WN_BAD_LOCALNAME;
1847     }
1848     if (ret)
1849         SetLastError(ret);
1850     TRACE("Returning %d\n", ret);
1851     return ret;
1852 }
1853
1854 /**************************************************************************
1855  * WNetSetConnectionA [MPR.@]
1856  */
1857 DWORD WINAPI WNetSetConnectionA( LPCSTR lpName, DWORD dwProperty,
1858                                  LPVOID pvValue )
1859 {
1860     FIXME( "(%s, %08X, %p): stub\n", debugstr_a(lpName), dwProperty, pvValue );
1861
1862     SetLastError(WN_NO_NETWORK);
1863     return WN_NO_NETWORK;
1864 }
1865
1866 /**************************************************************************
1867  * WNetSetConnectionW [MPR.@]
1868  */
1869 DWORD WINAPI WNetSetConnectionW( LPCWSTR lpName, DWORD dwProperty,
1870                                  LPVOID pvValue )
1871 {
1872     FIXME( "(%s, %08X, %p): stub\n", debugstr_w(lpName), dwProperty, pvValue );
1873
1874     SetLastError(WN_NO_NETWORK);
1875     return WN_NO_NETWORK;
1876 }
1877
1878 /*****************************************************************
1879  * WNetGetUniversalNameA [MPR.@]
1880  */
1881 DWORD WINAPI WNetGetUniversalNameA ( LPCSTR lpLocalPath, DWORD dwInfoLevel,
1882                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1883 {
1884     DWORD err, size;
1885
1886     FIXME( "(%s, 0x%08X, %p, %p): stub\n",
1887            debugstr_a(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
1888
1889     switch (dwInfoLevel)
1890     {
1891     case UNIVERSAL_NAME_INFO_LEVEL:
1892     {
1893         LPUNIVERSAL_NAME_INFOA info = lpBuffer;
1894
1895         size = sizeof(*info) + lstrlenA(lpLocalPath) + 1;
1896         if (*lpBufferSize < size)
1897         {
1898             err = WN_MORE_DATA;
1899             break;
1900         }
1901         info->lpUniversalName = (char *)info + sizeof(*info);
1902         lstrcpyA(info->lpUniversalName, lpLocalPath);
1903         *lpBufferSize = size;
1904         err = WN_NO_ERROR;
1905         break;
1906     }
1907     case REMOTE_NAME_INFO_LEVEL:
1908         err = WN_NO_NETWORK;
1909         break;
1910
1911     default:
1912         err = WN_BAD_VALUE;
1913         break;
1914     }
1915
1916     SetLastError(err);
1917     return err;
1918 }
1919
1920 /*****************************************************************
1921  * WNetGetUniversalNameW [MPR.@]
1922  */
1923 DWORD WINAPI WNetGetUniversalNameW ( LPCWSTR lpLocalPath, DWORD dwInfoLevel,
1924                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1925 {
1926     DWORD err, size;
1927
1928     FIXME( "(%s, 0x%08X, %p, %p): stub\n",
1929            debugstr_w(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
1930
1931     switch (dwInfoLevel)
1932     {
1933     case UNIVERSAL_NAME_INFO_LEVEL:
1934     {
1935         LPUNIVERSAL_NAME_INFOW info = lpBuffer;
1936
1937         size = sizeof(*info) + (lstrlenW(lpLocalPath) + 1) * sizeof(WCHAR);
1938         if (*lpBufferSize < size)
1939         {
1940             err = WN_MORE_DATA;
1941             break;
1942         }
1943         info->lpUniversalName = (LPWSTR)((char *)info + sizeof(*info));
1944         lstrcpyW(info->lpUniversalName, lpLocalPath);
1945         *lpBufferSize = size;
1946         err = WN_NO_ERROR;
1947         break;
1948     }
1949     case REMOTE_NAME_INFO_LEVEL:
1950         err = WN_NO_NETWORK;
1951         break;
1952
1953     default:
1954         err = WN_BAD_VALUE;
1955         break;
1956     }
1957
1958     if (err != WN_NO_ERROR) SetLastError(err);
1959     return err;
1960 }
1961
1962
1963
1964 /*
1965  * Other Functions
1966  */
1967
1968 /**************************************************************************
1969  * WNetGetUserA [MPR.@]
1970  *
1971  * FIXME: we should not return ourselves, but the owner of the drive lpName
1972  */
1973 DWORD WINAPI WNetGetUserA( LPCSTR lpName, LPSTR lpUserID, LPDWORD lpBufferSize )
1974 {
1975     if (GetUserNameA( lpUserID, lpBufferSize )) return WN_SUCCESS;
1976     return GetLastError();
1977 }
1978
1979 /*****************************************************************
1980  * WNetGetUserW [MPR.@]
1981  *
1982  * FIXME: we should not return ourselves, but the owner of the drive lpName
1983  */
1984 DWORD WINAPI WNetGetUserW( LPCWSTR lpName, LPWSTR lpUserID, LPDWORD lpBufferSize )
1985 {
1986     if (GetUserNameW( lpUserID, lpBufferSize )) return WN_SUCCESS;
1987     return GetLastError();
1988 }
1989
1990 /*********************************************************************
1991  * WNetConnectionDialog [MPR.@]
1992  */
1993 DWORD WINAPI WNetConnectionDialog( HWND hwnd, DWORD dwType )
1994 {
1995     FIXME( "(%p, %08X): stub\n", hwnd, dwType );
1996
1997     SetLastError(WN_NO_NETWORK);
1998     return WN_NO_NETWORK;
1999 }
2000
2001 /*********************************************************************
2002  * WNetConnectionDialog1A [MPR.@]
2003  */
2004 DWORD WINAPI WNetConnectionDialog1A( LPCONNECTDLGSTRUCTA lpConnDlgStruct )
2005 {
2006     FIXME( "(%p): stub\n", lpConnDlgStruct );
2007
2008     SetLastError(WN_NO_NETWORK);
2009     return WN_NO_NETWORK;
2010 }
2011
2012 /*********************************************************************
2013  * WNetConnectionDialog1W [MPR.@]
2014  */
2015 DWORD WINAPI WNetConnectionDialog1W( LPCONNECTDLGSTRUCTW lpConnDlgStruct )
2016 {
2017     FIXME( "(%p): stub\n", lpConnDlgStruct );
2018
2019     SetLastError(WN_NO_NETWORK);
2020     return WN_NO_NETWORK;
2021 }
2022
2023 /*********************************************************************
2024  * WNetDisconnectDialog [MPR.@]
2025  */
2026 DWORD WINAPI WNetDisconnectDialog( HWND hwnd, DWORD dwType )
2027 {
2028     FIXME( "(%p, %08X): stub\n", hwnd, dwType );
2029
2030     SetLastError(WN_NO_NETWORK);
2031     return WN_NO_NETWORK;
2032 }
2033
2034 /*********************************************************************
2035  * WNetDisconnectDialog1A [MPR.@]
2036  */
2037 DWORD WINAPI WNetDisconnectDialog1A( LPDISCDLGSTRUCTA lpConnDlgStruct )
2038 {
2039     FIXME( "(%p): stub\n", lpConnDlgStruct );
2040
2041     SetLastError(WN_NO_NETWORK);
2042     return WN_NO_NETWORK;
2043 }
2044
2045 /*********************************************************************
2046  * WNetDisconnectDialog1W [MPR.@]
2047  */
2048 DWORD WINAPI WNetDisconnectDialog1W( LPDISCDLGSTRUCTW lpConnDlgStruct )
2049 {
2050     FIXME( "(%p): stub\n", lpConnDlgStruct );
2051
2052     SetLastError(WN_NO_NETWORK);
2053     return WN_NO_NETWORK;
2054 }
2055
2056 /*********************************************************************
2057  * WNetGetLastErrorA [MPR.@]
2058  */
2059 DWORD WINAPI WNetGetLastErrorA( LPDWORD lpError,
2060                                 LPSTR lpErrorBuf, DWORD nErrorBufSize,
2061                                 LPSTR lpNameBuf, DWORD nNameBufSize )
2062 {
2063     FIXME( "(%p, %p, %d, %p, %d): stub\n",
2064            lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
2065
2066     SetLastError(WN_NO_NETWORK);
2067     return WN_NO_NETWORK;
2068 }
2069
2070 /*********************************************************************
2071  * WNetGetLastErrorW [MPR.@]
2072  */
2073 DWORD WINAPI WNetGetLastErrorW( LPDWORD lpError,
2074                                 LPWSTR lpErrorBuf, DWORD nErrorBufSize,
2075                          LPWSTR lpNameBuf, DWORD nNameBufSize )
2076 {
2077     FIXME( "(%p, %p, %d, %p, %d): stub\n",
2078            lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
2079
2080     SetLastError(WN_NO_NETWORK);
2081     return WN_NO_NETWORK;
2082 }
2083
2084 /*********************************************************************
2085  * WNetGetNetworkInformationA [MPR.@]
2086  */
2087 DWORD WINAPI WNetGetNetworkInformationA( LPCSTR lpProvider,
2088                                          LPNETINFOSTRUCT lpNetInfoStruct )
2089 {
2090     DWORD ret;
2091
2092     TRACE( "(%s, %p)\n", debugstr_a(lpProvider), lpNetInfoStruct );
2093
2094     if (!lpProvider)
2095         ret = WN_BAD_POINTER;
2096     else
2097     {
2098         int len;
2099
2100         len = MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, NULL, 0);
2101         if (len)
2102         {
2103             LPWSTR wideProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2104
2105             if (wideProvider)
2106             {
2107                 MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, wideProvider,
2108                  len);
2109                 ret = WNetGetNetworkInformationW(wideProvider, lpNetInfoStruct);
2110                 HeapFree(GetProcessHeap(), 0, wideProvider);
2111             }
2112             else
2113                 ret = WN_OUT_OF_MEMORY;
2114         }
2115         else
2116             ret = GetLastError();
2117     }
2118     if (ret)
2119         SetLastError(ret);
2120     TRACE("Returning %d\n", ret);
2121     return ret;
2122 }
2123
2124 /*********************************************************************
2125  * WNetGetNetworkInformationW [MPR.@]
2126  */
2127 DWORD WINAPI WNetGetNetworkInformationW( LPCWSTR lpProvider,
2128                                          LPNETINFOSTRUCT lpNetInfoStruct )
2129 {
2130     DWORD ret;
2131
2132     TRACE( "(%s, %p)\n", debugstr_w(lpProvider), lpNetInfoStruct );
2133
2134     if (!lpProvider)
2135         ret = WN_BAD_POINTER;
2136     else if (!lpNetInfoStruct)
2137         ret = WN_BAD_POINTER;
2138     else if (lpNetInfoStruct->cbStructure < sizeof(NETINFOSTRUCT))
2139         ret = WN_BAD_VALUE;
2140     else
2141     {
2142         if (providerTable && providerTable->numProviders)
2143         {
2144             DWORD providerIndex = _findProviderIndexW(lpProvider);
2145
2146             if (providerIndex != BAD_PROVIDER_INDEX)
2147             {
2148                 lpNetInfoStruct->cbStructure = sizeof(NETINFOSTRUCT);
2149                 lpNetInfoStruct->dwProviderVersion =
2150                  providerTable->table[providerIndex].dwSpecVersion;
2151                 lpNetInfoStruct->dwStatus = NO_ERROR;
2152                 lpNetInfoStruct->dwCharacteristics = 0;
2153                 lpNetInfoStruct->dwHandle = 0;
2154                 lpNetInfoStruct->wNetType =
2155                  HIWORD(providerTable->table[providerIndex].dwNetType);
2156                 lpNetInfoStruct->dwPrinters = -1;
2157                 lpNetInfoStruct->dwDrives = -1;
2158                 ret = WN_SUCCESS;
2159             }
2160             else
2161                 ret = WN_BAD_PROVIDER;
2162         }
2163         else
2164             ret = WN_NO_NETWORK;
2165     }
2166     if (ret)
2167         SetLastError(ret);
2168     TRACE("Returning %d\n", ret);
2169     return ret;
2170 }
2171
2172 /*****************************************************************
2173  *  WNetGetProviderNameA [MPR.@]
2174  */
2175 DWORD WINAPI WNetGetProviderNameA( DWORD dwNetType,
2176                                    LPSTR lpProvider, LPDWORD lpBufferSize )
2177 {
2178     DWORD ret;
2179
2180     TRACE("(0x%08x, %s, %p)\n", dwNetType, debugstr_a(lpProvider),
2181      lpBufferSize);
2182
2183     if (!lpProvider)
2184         ret = WN_BAD_POINTER;
2185     else if (!lpBufferSize)
2186         ret = WN_BAD_POINTER;
2187     else
2188     {
2189         if (providerTable)
2190         {
2191             DWORD i;
2192
2193             ret = WN_NO_NETWORK;
2194             for (i = 0; i < providerTable->numProviders &&
2195              HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
2196              i++)
2197                 ;
2198             if (i < providerTable->numProviders)
2199             {
2200                 DWORD sizeNeeded = WideCharToMultiByte(CP_ACP, 0,
2201                  providerTable->table[i].name, -1, NULL, 0, NULL, NULL);
2202
2203                 if (*lpBufferSize < sizeNeeded)
2204                 {
2205                     *lpBufferSize = sizeNeeded;
2206                     ret = WN_MORE_DATA;
2207                 }
2208                 else
2209                 {
2210                     WideCharToMultiByte(CP_ACP, 0, providerTable->table[i].name,
2211                      -1, lpProvider, *lpBufferSize, NULL, NULL);
2212                     ret = WN_SUCCESS;
2213                     /* FIXME: is *lpBufferSize set to the number of characters
2214                      * copied? */
2215                 }
2216             }
2217         }
2218         else
2219             ret = WN_NO_NETWORK;
2220     }
2221     if (ret)
2222         SetLastError(ret);
2223     TRACE("Returning %d\n", ret);
2224     return ret;
2225 }
2226
2227 /*****************************************************************
2228  *  WNetGetProviderNameW [MPR.@]
2229  */
2230 DWORD WINAPI WNetGetProviderNameW( DWORD dwNetType,
2231                                    LPWSTR lpProvider, LPDWORD lpBufferSize )
2232 {
2233     DWORD ret;
2234
2235     TRACE("(0x%08x, %s, %p)\n", dwNetType, debugstr_w(lpProvider),
2236      lpBufferSize);
2237
2238     if (!lpProvider)
2239         ret = WN_BAD_POINTER;
2240     else if (!lpBufferSize)
2241         ret = WN_BAD_POINTER;
2242     else
2243     {
2244         if (providerTable)
2245         {
2246             DWORD i;
2247
2248             ret = WN_NO_NETWORK;
2249             for (i = 0; i < providerTable->numProviders &&
2250              HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
2251              i++)
2252                 ;
2253             if (i < providerTable->numProviders)
2254             {
2255                 DWORD sizeNeeded = strlenW(providerTable->table[i].name) + 1;
2256
2257                 if (*lpBufferSize < sizeNeeded)
2258                 {
2259                     *lpBufferSize = sizeNeeded;
2260                     ret = WN_MORE_DATA;
2261                 }
2262                 else
2263                 {
2264                     strcpyW(lpProvider, providerTable->table[i].name);
2265                     ret = WN_SUCCESS;
2266                     /* FIXME: is *lpBufferSize set to the number of characters
2267                      * copied? */
2268                 }
2269             }
2270         }
2271         else
2272             ret = WN_NO_NETWORK;
2273     }
2274     if (ret)
2275         SetLastError(ret);
2276     TRACE("Returning %d\n", ret);
2277     return ret;
2278 }