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