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