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