wintrust/tests: Fix test on win9x.
[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             lpNetResourceW = HeapAlloc(GetProcessHeap(), 0, size);
1316             if (lpNetResourceW)
1317                 ret = _thunkNetResourceArrayAToW(lpNetResource,
1318                         &count, lpNetResourceW, &size);
1319             else
1320                 ret = WN_OUT_OF_MEMORY;
1321         }
1322         if (ret == WN_SUCCESS)
1323         {
1324             LPWSTR lpSystemW = NULL;
1325             LPVOID lpBufferW;
1326             size = 1024;
1327             lpBufferW = HeapAlloc(GetProcessHeap(), 0, size);
1328             if (lpBufferW)
1329             {
1330                 ret = WNetGetResourceInformationW(lpNetResourceW,
1331                         lpBufferW, &size, &lpSystemW);
1332                 if (ret == WN_MORE_DATA)
1333                 {
1334                     HeapFree(GetProcessHeap(), 0, lpBufferW);
1335                     lpBufferW = HeapAlloc(GetProcessHeap(), 0, size);
1336                     if (lpBufferW)
1337                         ret = WNetGetResourceInformationW(lpNetResourceW,
1338                             lpBufferW, &size, &lpSystemW);
1339                     else
1340                         ret = WN_OUT_OF_MEMORY;
1341                 }
1342                 if (ret == WN_SUCCESS)
1343                 {
1344                     ret = _thunkNetResourceArrayWToA(lpBufferW,
1345                             &count, lpBuffer, cbBuffer);
1346                     lpNetResourceW = lpBufferW;
1347                     size = sizeof(NETRESOURCEA);
1348                     size += WideCharToMultiByte(CP_ACP, 0, lpNetResourceW->lpRemoteName,
1349                             -1, NULL, 0, NULL, NULL);
1350                     size += WideCharToMultiByte(CP_ACP, 0, lpNetResourceW->lpProvider,
1351                             -1, NULL, 0, NULL, NULL);
1352
1353                     len = WideCharToMultiByte(CP_ACP, 0, lpSystemW,
1354                                       -1, NULL, 0, NULL, NULL);
1355                     if ((len) && ( size + len < *cbBuffer))
1356                     {
1357                         *lplpSystem = (char*)lpBuffer + *cbBuffer - len;
1358                         WideCharToMultiByte(CP_ACP, 0, lpSystemW, -1,
1359                              *lplpSystem, len, NULL, NULL);
1360                          ret = WN_SUCCESS;
1361                     }
1362                     else
1363                         ret = WN_MORE_DATA;
1364                 }
1365                 else
1366                     ret = WN_OUT_OF_MEMORY;
1367                 HeapFree(GetProcessHeap(), 0, lpBufferW);
1368             }
1369             else
1370                 ret = WN_OUT_OF_MEMORY;
1371             HeapFree(GetProcessHeap(), 0, lpSystemW);
1372         }
1373         HeapFree(GetProcessHeap(), 0, lpNetResourceW);
1374     }
1375     else
1376         ret = WN_NO_NETWORK;
1377
1378     if (ret)
1379         SetLastError(ret);
1380     TRACE("Returning %d\n", ret);
1381     return ret;
1382 }
1383
1384 /*********************************************************************
1385  * WNetGetResourceInformationW [MPR.@]
1386  *
1387  * WNetGetResourceInformationW function identifies the network provider
1388  * that owns the resource and gets information about the type of the resource.
1389  *
1390  * PARAMS:
1391  *  lpNetResource    [ I]    the pointer to NETRESOURCEW structure, that
1392  *                          defines a network resource.
1393  *  lpBuffer         [ O]   the pointer to buffer, containing result. It
1394  *                          contains NETRESOURCEW structure and strings to
1395  *                          which the members of the NETRESOURCEW structure
1396  *                          point.
1397  *  cbBuffer         [I/O] the pointer to DWORD number - size of buffer
1398  *                          in bytes.
1399  *  lplpSystem       [ O]   the pointer to string in the output buffer,
1400  *                          containing the part of the resource name without
1401  *                          names of the server and share.
1402  *
1403  * RETURNS:
1404  *  NO_ERROR if the function succeeds. System error code if the function fails.
1405  */
1406
1407 DWORD WINAPI WNetGetResourceInformationW( LPNETRESOURCEW lpNetResource,
1408                                           LPVOID lpBuffer, LPDWORD cbBuffer,
1409                                           LPWSTR *lplpSystem )
1410 {
1411     DWORD ret = WN_NO_NETWORK;
1412     DWORD index;
1413
1414     TRACE( "(%p, %p, %p, %p)\n",
1415            lpNetResource, lpBuffer, cbBuffer, lplpSystem);
1416
1417     if (!(lpBuffer))
1418         ret = WN_OUT_OF_MEMORY;
1419     else
1420     {
1421         /* FIXME: For function value of a variable is indifferent, it does
1422          * search of all providers in a network.
1423          */
1424         for (index = 0; index < providerTable->numProviders; index++)
1425         {
1426             if(providerTable->table[index].getCaps(WNNC_DIALOG) &
1427                 WNNC_DLG_GETRESOURCEINFORMATION)
1428             {
1429                 if (providerTable->table[index].getResourceInformation)
1430                     ret = providerTable->table[index].getResourceInformation(
1431                         lpNetResource, lpBuffer, cbBuffer, lplpSystem);
1432                 else
1433                     ret = WN_NO_NETWORK;
1434                 if (ret == WN_SUCCESS)
1435                     break;
1436             }
1437         }
1438     }
1439     if (ret)
1440         SetLastError(ret);
1441     return ret;
1442 }
1443
1444 /*********************************************************************
1445  * WNetGetResourceParentA [MPR.@]
1446  */
1447 DWORD WINAPI WNetGetResourceParentA( LPNETRESOURCEA lpNetResource,
1448                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1449 {
1450     FIXME( "(%p, %p, %p): stub\n",
1451            lpNetResource, lpBuffer, lpBufferSize );
1452
1453     SetLastError(WN_NO_NETWORK);
1454     return WN_NO_NETWORK;
1455 }
1456
1457 /*********************************************************************
1458  * WNetGetResourceParentW [MPR.@]
1459  */
1460 DWORD WINAPI WNetGetResourceParentW( LPNETRESOURCEW lpNetResource,
1461                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1462 {
1463     FIXME( "(%p, %p, %p): stub\n",
1464            lpNetResource, lpBuffer, lpBufferSize );
1465
1466     SetLastError(WN_NO_NETWORK);
1467     return WN_NO_NETWORK;
1468 }
1469
1470
1471
1472 /*
1473  * Connection Functions
1474  */
1475
1476 /*********************************************************************
1477  *  WNetAddConnectionA [MPR.@]
1478  */
1479 DWORD WINAPI WNetAddConnectionA( LPCSTR lpRemoteName, LPCSTR lpPassword,
1480                                  LPCSTR lpLocalName )
1481 {
1482     FIXME( "(%s, %p, %s): stub\n",
1483            debugstr_a(lpRemoteName), lpPassword, debugstr_a(lpLocalName) );
1484
1485     SetLastError(WN_NO_NETWORK);
1486     return WN_NO_NETWORK;
1487 }
1488
1489 /*********************************************************************
1490  *  WNetAddConnectionW [MPR.@]
1491  */
1492 DWORD WINAPI WNetAddConnectionW( LPCWSTR lpRemoteName, LPCWSTR lpPassword,
1493                                  LPCWSTR lpLocalName )
1494 {
1495     FIXME( "(%s, %p, %s): stub\n",
1496            debugstr_w(lpRemoteName), lpPassword, debugstr_w(lpLocalName) );
1497
1498     SetLastError(WN_NO_NETWORK);
1499     return WN_NO_NETWORK;
1500 }
1501
1502 /*********************************************************************
1503  *  WNetAddConnection2A [MPR.@]
1504  */
1505 DWORD WINAPI WNetAddConnection2A( LPNETRESOURCEA lpNetResource,
1506                                   LPCSTR lpPassword, LPCSTR lpUserID,
1507                                   DWORD dwFlags )
1508 {
1509     FIXME( "(%p, %p, %s, 0x%08X): stub\n",
1510            lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags );
1511
1512     SetLastError(WN_NO_NETWORK);
1513     return WN_NO_NETWORK;
1514 }
1515
1516 /*********************************************************************
1517  * WNetAddConnection2W [MPR.@]
1518  */
1519 DWORD WINAPI WNetAddConnection2W( LPNETRESOURCEW lpNetResource,
1520                                   LPCWSTR lpPassword, LPCWSTR lpUserID,
1521                                   DWORD dwFlags )
1522 {
1523     FIXME( "(%p, %p, %s, 0x%08X): stub\n",
1524            lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags );
1525
1526     SetLastError(WN_NO_NETWORK);
1527     return WN_NO_NETWORK;
1528 }
1529
1530 /*********************************************************************
1531  * WNetAddConnection3A [MPR.@]
1532  */
1533 DWORD WINAPI WNetAddConnection3A( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
1534                                   LPCSTR lpPassword, LPCSTR lpUserID,
1535                                   DWORD dwFlags )
1536 {
1537     FIXME( "(%p, %p, %p, %s, 0x%08X), stub\n",
1538            hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags );
1539
1540     SetLastError(WN_NO_NETWORK);
1541     return WN_NO_NETWORK;
1542 }
1543
1544 /*********************************************************************
1545  * WNetAddConnection3W [MPR.@]
1546  */
1547 DWORD WINAPI WNetAddConnection3W( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
1548                                   LPCWSTR lpPassword, LPCWSTR lpUserID,
1549                                   DWORD dwFlags )
1550 {
1551     FIXME( "(%p, %p, %p, %s, 0x%08X), stub\n",
1552            hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags );
1553
1554     SetLastError(WN_NO_NETWORK);
1555     return WN_NO_NETWORK;
1556 }
1557
1558 /*****************************************************************
1559  *  WNetUseConnectionA [MPR.@]
1560  */
1561 DWORD WINAPI WNetUseConnectionA( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
1562                                  LPCSTR lpPassword, LPCSTR lpUserID, DWORD dwFlags,
1563                                  LPSTR lpAccessName, LPDWORD lpBufferSize,
1564                                  LPDWORD lpResult )
1565 {
1566     FIXME( "(%p, %p, %p, %s, 0x%08X, %s, %p, %p), stub\n",
1567            hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags,
1568            debugstr_a(lpAccessName), lpBufferSize, lpResult );
1569
1570     SetLastError(WN_NO_NETWORK);
1571     return WN_NO_NETWORK;
1572 }
1573
1574 /*****************************************************************
1575  *  WNetUseConnectionW [MPR.@]
1576  */
1577 DWORD WINAPI WNetUseConnectionW( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
1578                                  LPCWSTR lpPassword, LPCWSTR lpUserID, DWORD dwFlags,
1579                                  LPWSTR lpAccessName, LPDWORD lpBufferSize,
1580                                  LPDWORD lpResult )
1581 {
1582     FIXME( "(%p, %p, %p, %s, 0x%08X, %s, %p, %p), stub\n",
1583            hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags,
1584            debugstr_w(lpAccessName), lpBufferSize, lpResult );
1585
1586     SetLastError(WN_NO_NETWORK);
1587     return WN_NO_NETWORK;
1588 }
1589
1590 /*********************************************************************
1591  *  WNetCancelConnectionA [MPR.@]
1592  */
1593 DWORD WINAPI WNetCancelConnectionA( LPCSTR lpName, BOOL fForce )
1594 {
1595     FIXME( "(%s, %d), stub\n", debugstr_a(lpName), fForce );
1596
1597     return WN_SUCCESS;
1598 }
1599
1600 /*********************************************************************
1601  *  WNetCancelConnectionW [MPR.@]
1602  */
1603 DWORD WINAPI WNetCancelConnectionW( LPCWSTR lpName, BOOL fForce )
1604 {
1605     FIXME( "(%s, %d), stub\n", debugstr_w(lpName), fForce );
1606
1607     return WN_SUCCESS;
1608 }
1609
1610 /*********************************************************************
1611  *  WNetCancelConnection2A [MPR.@]
1612  */
1613 DWORD WINAPI WNetCancelConnection2A( LPCSTR lpName, DWORD dwFlags, BOOL fForce )
1614 {
1615     FIXME( "(%s, %08X, %d), stub\n", debugstr_a(lpName), dwFlags, fForce );
1616
1617     return WN_SUCCESS;
1618 }
1619
1620 /*********************************************************************
1621  *  WNetCancelConnection2W [MPR.@]
1622  */
1623 DWORD WINAPI WNetCancelConnection2W( LPCWSTR lpName, DWORD dwFlags, BOOL fForce )
1624 {
1625     FIXME( "(%s, %08X, %d), stub\n", debugstr_w(lpName), dwFlags, fForce );
1626
1627     return WN_SUCCESS;
1628 }
1629
1630 /*****************************************************************
1631  *  WNetRestoreConnectionA [MPR.@]
1632  */
1633 DWORD WINAPI WNetRestoreConnectionA( HWND hwndOwner, LPCSTR lpszDevice )
1634 {
1635     FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_a(lpszDevice) );
1636
1637     SetLastError(WN_NO_NETWORK);
1638     return WN_NO_NETWORK;
1639 }
1640
1641 /*****************************************************************
1642  *  WNetRestoreConnectionW [MPR.@]
1643  */
1644 DWORD WINAPI WNetRestoreConnectionW( HWND hwndOwner, LPCWSTR lpszDevice )
1645 {
1646     FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_w(lpszDevice) );
1647
1648     SetLastError(WN_NO_NETWORK);
1649     return WN_NO_NETWORK;
1650 }
1651
1652 /**************************************************************************
1653  * WNetGetConnectionA [MPR.@]
1654  *
1655  * RETURNS
1656  * - WN_BAD_LOCALNAME     lpLocalName makes no sense
1657  * - WN_NOT_CONNECTED     drive is a local drive
1658  * - WN_MORE_DATA         buffer isn't big enough
1659  * - WN_SUCCESS           success (net path in buffer)
1660  *
1661  * FIXME: need to test return values under different errors
1662  */
1663 DWORD WINAPI WNetGetConnectionA( LPCSTR lpLocalName,
1664                                  LPSTR lpRemoteName, LPDWORD lpBufferSize )
1665 {
1666     DWORD ret;
1667
1668     if (!lpLocalName)
1669         ret = WN_BAD_POINTER;
1670     else if (!lpBufferSize)
1671         ret = WN_BAD_POINTER;
1672     else if (!lpRemoteName && *lpBufferSize)
1673         ret = WN_BAD_POINTER;
1674     else
1675     {
1676         int len = MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, NULL, 0);
1677
1678         if (len)
1679         {
1680             PWSTR wideLocalName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1681
1682             if (wideLocalName)
1683             {
1684                 WCHAR wideRemoteStatic[MAX_PATH];
1685                 DWORD wideRemoteSize = sizeof(wideRemoteStatic) / sizeof(WCHAR);
1686
1687                 MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, wideLocalName, len);
1688
1689                 /* try once without memory allocation */
1690                 ret = WNetGetConnectionW(wideLocalName, wideRemoteStatic,
1691                  &wideRemoteSize);
1692                 if (ret == WN_SUCCESS)
1693                 {
1694                     int len = WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
1695                      -1, NULL, 0, NULL, NULL);
1696
1697                     if (len <= *lpBufferSize)
1698                     {
1699                         WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic, -1,
1700                          lpRemoteName, *lpBufferSize, NULL, NULL);
1701                         ret = WN_SUCCESS;
1702                     }
1703                     else
1704                     {
1705                         *lpBufferSize = len;
1706                         ret = WN_MORE_DATA;
1707                     }
1708                 }
1709                 else if (ret == WN_MORE_DATA)
1710                 {
1711                     PWSTR wideRemote = HeapAlloc(GetProcessHeap(), 0,
1712                      wideRemoteSize * sizeof(WCHAR));
1713
1714                     if (wideRemote)
1715                     {
1716                         ret = WNetGetConnectionW(wideLocalName, wideRemote,
1717                          &wideRemoteSize);
1718                         if (ret == WN_SUCCESS)
1719                         {
1720                             if (len <= *lpBufferSize)
1721                             {
1722                                 WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
1723                                  -1, lpRemoteName, *lpBufferSize, NULL, NULL);
1724                                 ret = WN_SUCCESS;
1725                             }
1726                             else
1727                             {
1728                                 *lpBufferSize = len;
1729                                 ret = WN_MORE_DATA;
1730                             }
1731                         }
1732                         HeapFree(GetProcessHeap(), 0, wideRemote);
1733                     }
1734                     else
1735                         ret = WN_OUT_OF_MEMORY;
1736                 }
1737                 HeapFree(GetProcessHeap(), 0, wideLocalName);
1738             }
1739             else
1740                 ret = WN_OUT_OF_MEMORY;
1741         }
1742         else
1743             ret = WN_BAD_LOCALNAME;
1744     }
1745     if (ret)
1746         SetLastError(ret);
1747     TRACE("Returning %d\n", ret);
1748     return ret;
1749 }
1750
1751 /**************************************************************************
1752  * WNetGetConnectionW [MPR.@]
1753  *
1754  * FIXME: need to test return values under different errors
1755  */
1756 DWORD WINAPI WNetGetConnectionW( LPCWSTR lpLocalName,
1757                                  LPWSTR lpRemoteName, LPDWORD lpBufferSize )
1758 {
1759     DWORD ret;
1760
1761     TRACE("(%s, %p, %p)\n", debugstr_w(lpLocalName), lpRemoteName,
1762      lpBufferSize);
1763
1764     if (!lpLocalName)
1765         ret = WN_BAD_POINTER;
1766     else if (!lpBufferSize)
1767         ret = WN_BAD_POINTER;
1768     else if (!lpRemoteName && *lpBufferSize)
1769         ret = WN_BAD_POINTER;
1770     else if (!lpLocalName[0])
1771         ret = WN_BAD_LOCALNAME;
1772     else
1773     {
1774         if (lpLocalName[1] == ':')
1775         {
1776             switch(GetDriveTypeW(lpLocalName))
1777             {
1778             case DRIVE_REMOTE:
1779             {
1780                 static const WCHAR unc[] = { 'u','n','c','\\' };
1781                 WCHAR rremote[MAX_PATH], *remote = rremote;
1782                 if (!QueryDosDeviceW( lpLocalName, remote, MAX_PATH )) remote[0] = 0;
1783                 else if (!strncmpW(remote, unc, 4))
1784                 {
1785                     remote += 2;
1786                     remote[0] = '\\';
1787                 }
1788                 else if (remote[0] != '\\' || remote[1] != '\\')
1789                     FIXME("Don't know how to convert %s to an unc\n", debugstr_w(remote));
1790
1791                 if (strlenW(remote) + 1 > *lpBufferSize)
1792                 {
1793                     *lpBufferSize = strlenW(remote) + 1;
1794                     ret = WN_MORE_DATA;
1795                 }
1796                 else
1797                 {
1798                     strcpyW( lpRemoteName, remote );
1799                     *lpBufferSize = strlenW(lpRemoteName) + 1;
1800                     ret = WN_SUCCESS;
1801                 }
1802                 break;
1803             }
1804             case DRIVE_REMOVABLE:
1805             case DRIVE_FIXED:
1806             case DRIVE_CDROM:
1807                 TRACE("file is local\n");
1808                 ret = WN_NOT_CONNECTED;
1809                 break;
1810             default:
1811                 ret = WN_BAD_LOCALNAME;
1812             }
1813         }
1814         else
1815             ret = WN_BAD_LOCALNAME;
1816     }
1817     if (ret)
1818         SetLastError(ret);
1819     TRACE("Returning %d\n", ret);
1820     return ret;
1821 }
1822
1823 /**************************************************************************
1824  * WNetSetConnectionA [MPR.@]
1825  */
1826 DWORD WINAPI WNetSetConnectionA( LPCSTR lpName, DWORD dwProperty,
1827                                  LPVOID pvValue )
1828 {
1829     FIXME( "(%s, %08X, %p): stub\n", debugstr_a(lpName), dwProperty, pvValue );
1830
1831     SetLastError(WN_NO_NETWORK);
1832     return WN_NO_NETWORK;
1833 }
1834
1835 /**************************************************************************
1836  * WNetSetConnectionW [MPR.@]
1837  */
1838 DWORD WINAPI WNetSetConnectionW( LPCWSTR lpName, DWORD dwProperty,
1839                                  LPVOID pvValue )
1840 {
1841     FIXME( "(%s, %08X, %p): stub\n", debugstr_w(lpName), dwProperty, pvValue );
1842
1843     SetLastError(WN_NO_NETWORK);
1844     return WN_NO_NETWORK;
1845 }
1846
1847 /*****************************************************************
1848  * WNetGetUniversalNameA [MPR.@]
1849  */
1850 DWORD WINAPI WNetGetUniversalNameA ( LPCSTR lpLocalPath, DWORD dwInfoLevel,
1851                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1852 {
1853     DWORD err, size;
1854
1855     FIXME( "(%s, 0x%08X, %p, %p): stub\n",
1856            debugstr_a(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
1857
1858     switch (dwInfoLevel)
1859     {
1860     case UNIVERSAL_NAME_INFO_LEVEL:
1861     {
1862         LPUNIVERSAL_NAME_INFOA info = (LPUNIVERSAL_NAME_INFOA)lpBuffer;
1863
1864         size = sizeof(*info) + lstrlenA(lpLocalPath) + 1;
1865         if (*lpBufferSize < size)
1866         {
1867             err = WN_MORE_DATA;
1868             break;
1869         }
1870         info->lpUniversalName = (char *)info + sizeof(*info);
1871         lstrcpyA(info->lpUniversalName, lpLocalPath);
1872         *lpBufferSize = size;
1873         err = WN_NO_ERROR;
1874         break;
1875     }
1876     case REMOTE_NAME_INFO_LEVEL:
1877         err = WN_NO_NETWORK;
1878         break;
1879
1880     default:
1881         err = WN_BAD_VALUE;
1882         break;
1883     }
1884
1885     SetLastError(err);
1886     return err;
1887 }
1888
1889 /*****************************************************************
1890  * WNetGetUniversalNameW [MPR.@]
1891  */
1892 DWORD WINAPI WNetGetUniversalNameW ( LPCWSTR lpLocalPath, DWORD dwInfoLevel,
1893                                      LPVOID lpBuffer, LPDWORD lpBufferSize )
1894 {
1895     DWORD err, size;
1896
1897     FIXME( "(%s, 0x%08X, %p, %p): stub\n",
1898            debugstr_w(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
1899
1900     switch (dwInfoLevel)
1901     {
1902     case UNIVERSAL_NAME_INFO_LEVEL:
1903     {
1904         LPUNIVERSAL_NAME_INFOW info = (LPUNIVERSAL_NAME_INFOW)lpBuffer;
1905
1906         size = sizeof(*info) + (lstrlenW(lpLocalPath) + 1) * sizeof(WCHAR);
1907         if (*lpBufferSize < size)
1908         {
1909             err = WN_MORE_DATA;
1910             break;
1911         }
1912         info->lpUniversalName = (LPWSTR)((char *)info + sizeof(*info));
1913         lstrcpyW(info->lpUniversalName, lpLocalPath);
1914         *lpBufferSize = size;
1915         err = WN_NO_ERROR;
1916         break;
1917     }
1918     case REMOTE_NAME_INFO_LEVEL:
1919         err = WN_NO_NETWORK;
1920         break;
1921
1922     default:
1923         err = WN_BAD_VALUE;
1924         break;
1925     }
1926
1927     SetLastError(err);
1928     return err;
1929 }
1930
1931
1932
1933 /*
1934  * Other Functions
1935  */
1936
1937 /**************************************************************************
1938  * WNetGetUserA [MPR.@]
1939  *
1940  * FIXME: we should not return ourselves, but the owner of the drive lpName
1941  */
1942 DWORD WINAPI WNetGetUserA( LPCSTR lpName, LPSTR lpUserID, LPDWORD lpBufferSize )
1943 {
1944     if (GetUserNameA( lpUserID, lpBufferSize )) return WN_SUCCESS;
1945     return GetLastError();
1946 }
1947
1948 /*****************************************************************
1949  * WNetGetUserW [MPR.@]
1950  *
1951  * FIXME: we should not return ourselves, but the owner of the drive lpName
1952  */
1953 DWORD WINAPI WNetGetUserW( LPCWSTR lpName, LPWSTR lpUserID, LPDWORD lpBufferSize )
1954 {
1955     if (GetUserNameW( lpUserID, lpBufferSize )) return WN_SUCCESS;
1956     return GetLastError();
1957 }
1958
1959 /*********************************************************************
1960  * WNetConnectionDialog [MPR.@]
1961  */
1962 DWORD WINAPI WNetConnectionDialog( HWND hwnd, DWORD dwType )
1963 {
1964     FIXME( "(%p, %08X): stub\n", hwnd, dwType );
1965
1966     SetLastError(WN_NO_NETWORK);
1967     return WN_NO_NETWORK;
1968 }
1969
1970 /*********************************************************************
1971  * WNetConnectionDialog1A [MPR.@]
1972  */
1973 DWORD WINAPI WNetConnectionDialog1A( LPCONNECTDLGSTRUCTA lpConnDlgStruct )
1974 {
1975     FIXME( "(%p): stub\n", lpConnDlgStruct );
1976
1977     SetLastError(WN_NO_NETWORK);
1978     return WN_NO_NETWORK;
1979 }
1980
1981 /*********************************************************************
1982  * WNetConnectionDialog1W [MPR.@]
1983  */
1984 DWORD WINAPI WNetConnectionDialog1W( LPCONNECTDLGSTRUCTW lpConnDlgStruct )
1985 {
1986     FIXME( "(%p): stub\n", lpConnDlgStruct );
1987
1988     SetLastError(WN_NO_NETWORK);
1989     return WN_NO_NETWORK;
1990 }
1991
1992 /*********************************************************************
1993  * WNetDisconnectDialog [MPR.@]
1994  */
1995 DWORD WINAPI WNetDisconnectDialog( HWND hwnd, DWORD dwType )
1996 {
1997     FIXME( "(%p, %08X): stub\n", hwnd, dwType );
1998
1999     SetLastError(WN_NO_NETWORK);
2000     return WN_NO_NETWORK;
2001 }
2002
2003 /*********************************************************************
2004  * WNetDisconnectDialog1A [MPR.@]
2005  */
2006 DWORD WINAPI WNetDisconnectDialog1A( LPDISCDLGSTRUCTA lpConnDlgStruct )
2007 {
2008     FIXME( "(%p): stub\n", lpConnDlgStruct );
2009
2010     SetLastError(WN_NO_NETWORK);
2011     return WN_NO_NETWORK;
2012 }
2013
2014 /*********************************************************************
2015  * WNetDisconnectDialog1W [MPR.@]
2016  */
2017 DWORD WINAPI WNetDisconnectDialog1W( LPDISCDLGSTRUCTW lpConnDlgStruct )
2018 {
2019     FIXME( "(%p): stub\n", lpConnDlgStruct );
2020
2021     SetLastError(WN_NO_NETWORK);
2022     return WN_NO_NETWORK;
2023 }
2024
2025 /*********************************************************************
2026  * WNetGetLastErrorA [MPR.@]
2027  */
2028 DWORD WINAPI WNetGetLastErrorA( LPDWORD lpError,
2029                                 LPSTR lpErrorBuf, DWORD nErrorBufSize,
2030                                 LPSTR lpNameBuf, DWORD nNameBufSize )
2031 {
2032     FIXME( "(%p, %p, %d, %p, %d): stub\n",
2033            lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
2034
2035     SetLastError(WN_NO_NETWORK);
2036     return WN_NO_NETWORK;
2037 }
2038
2039 /*********************************************************************
2040  * WNetGetLastErrorW [MPR.@]
2041  */
2042 DWORD WINAPI WNetGetLastErrorW( LPDWORD lpError,
2043                                 LPWSTR lpErrorBuf, DWORD nErrorBufSize,
2044                          LPWSTR lpNameBuf, DWORD nNameBufSize )
2045 {
2046     FIXME( "(%p, %p, %d, %p, %d): stub\n",
2047            lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
2048
2049     SetLastError(WN_NO_NETWORK);
2050     return WN_NO_NETWORK;
2051 }
2052
2053 /*********************************************************************
2054  * WNetGetNetworkInformationA [MPR.@]
2055  */
2056 DWORD WINAPI WNetGetNetworkInformationA( LPCSTR lpProvider,
2057                                          LPNETINFOSTRUCT lpNetInfoStruct )
2058 {
2059     DWORD ret;
2060
2061     TRACE( "(%s, %p)\n", debugstr_a(lpProvider), lpNetInfoStruct );
2062
2063     if (!lpProvider)
2064         ret = WN_BAD_POINTER;
2065     else
2066     {
2067         int len;
2068
2069         len = MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, NULL, 0);
2070         if (len)
2071         {
2072             LPWSTR wideProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2073
2074             if (wideProvider)
2075             {
2076                 MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, wideProvider,
2077                  len);
2078                 ret = WNetGetNetworkInformationW(wideProvider, lpNetInfoStruct);
2079                 HeapFree(GetProcessHeap(), 0, wideProvider);
2080             }
2081             else
2082                 ret = WN_OUT_OF_MEMORY;
2083         }
2084         else
2085             ret = GetLastError();
2086     }
2087     if (ret)
2088         SetLastError(ret);
2089     TRACE("Returning %d\n", ret);
2090     return ret;
2091 }
2092
2093 /*********************************************************************
2094  * WNetGetNetworkInformationW [MPR.@]
2095  */
2096 DWORD WINAPI WNetGetNetworkInformationW( LPCWSTR lpProvider,
2097                                          LPNETINFOSTRUCT lpNetInfoStruct )
2098 {
2099     DWORD ret;
2100
2101     TRACE( "(%s, %p)\n", debugstr_w(lpProvider), lpNetInfoStruct );
2102
2103     if (!lpProvider)
2104         ret = WN_BAD_POINTER;
2105     else if (!lpNetInfoStruct)
2106         ret = WN_BAD_POINTER;
2107     else if (lpNetInfoStruct->cbStructure < sizeof(NETINFOSTRUCT))
2108         ret = WN_BAD_VALUE;
2109     else
2110     {
2111         if (providerTable && providerTable->numProviders)
2112         {
2113             DWORD providerIndex = _findProviderIndexW(lpProvider);
2114
2115             if (providerIndex != BAD_PROVIDER_INDEX)
2116             {
2117                 lpNetInfoStruct->cbStructure = sizeof(NETINFOSTRUCT);
2118                 lpNetInfoStruct->dwProviderVersion =
2119                  providerTable->table[providerIndex].dwSpecVersion;
2120                 lpNetInfoStruct->dwStatus = NO_ERROR;
2121                 lpNetInfoStruct->dwCharacteristics = 0;
2122                 lpNetInfoStruct->dwHandle = (ULONG_PTR)NULL;
2123                 lpNetInfoStruct->wNetType =
2124                  HIWORD(providerTable->table[providerIndex].dwNetType);
2125                 lpNetInfoStruct->dwPrinters = -1;
2126                 lpNetInfoStruct->dwDrives = -1;
2127                 ret = WN_SUCCESS;
2128             }
2129             else
2130                 ret = WN_BAD_PROVIDER;
2131         }
2132         else
2133             ret = WN_NO_NETWORK;
2134     }
2135     if (ret)
2136         SetLastError(ret);
2137     TRACE("Returning %d\n", ret);
2138     return ret;
2139 }
2140
2141 /*****************************************************************
2142  *  WNetGetProviderNameA [MPR.@]
2143  */
2144 DWORD WINAPI WNetGetProviderNameA( DWORD dwNetType,
2145                                    LPSTR lpProvider, LPDWORD lpBufferSize )
2146 {
2147     DWORD ret;
2148
2149     TRACE("(0x%08x, %s, %p)\n", dwNetType, debugstr_a(lpProvider),
2150      lpBufferSize);
2151
2152     if (!lpProvider)
2153         ret = WN_BAD_POINTER;
2154     else if (!lpBufferSize)
2155         ret = WN_BAD_POINTER;
2156     else
2157     {
2158         if (providerTable)
2159         {
2160             DWORD i;
2161
2162             ret = WN_NO_NETWORK;
2163             for (i = 0; i < providerTable->numProviders &&
2164              HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
2165              i++)
2166                 ;
2167             if (i < providerTable->numProviders)
2168             {
2169                 DWORD sizeNeeded = WideCharToMultiByte(CP_ACP, 0,
2170                  providerTable->table[i].name, -1, NULL, 0, NULL, NULL);
2171
2172                 if (*lpBufferSize < sizeNeeded)
2173                 {
2174                     *lpBufferSize = sizeNeeded;
2175                     ret = WN_MORE_DATA;
2176                 }
2177                 else
2178                 {
2179                     WideCharToMultiByte(CP_ACP, 0, providerTable->table[i].name,
2180                      -1, lpProvider, *lpBufferSize, NULL, NULL);
2181                     ret = WN_SUCCESS;
2182                     /* FIXME: is *lpBufferSize set to the number of characters
2183                      * copied? */
2184                 }
2185             }
2186         }
2187         else
2188             ret = WN_NO_NETWORK;
2189     }
2190     if (ret)
2191         SetLastError(ret);
2192     TRACE("Returning %d\n", ret);
2193     return ret;
2194 }
2195
2196 /*****************************************************************
2197  *  WNetGetProviderNameW [MPR.@]
2198  */
2199 DWORD WINAPI WNetGetProviderNameW( DWORD dwNetType,
2200                                    LPWSTR lpProvider, LPDWORD lpBufferSize )
2201 {
2202     DWORD ret;
2203
2204     TRACE("(0x%08x, %s, %p)\n", dwNetType, debugstr_w(lpProvider),
2205      lpBufferSize);
2206
2207     if (!lpProvider)
2208         ret = WN_BAD_POINTER;
2209     else if (!lpBufferSize)
2210         ret = WN_BAD_POINTER;
2211     else
2212     {
2213         if (providerTable)
2214         {
2215             DWORD i;
2216
2217             ret = WN_NO_NETWORK;
2218             for (i = 0; i < providerTable->numProviders &&
2219              HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
2220              i++)
2221                 ;
2222             if (i < providerTable->numProviders)
2223             {
2224                 DWORD sizeNeeded = strlenW(providerTable->table[i].name) + 1;
2225
2226                 if (*lpBufferSize < sizeNeeded)
2227                 {
2228                     *lpBufferSize = sizeNeeded;
2229                     ret = WN_MORE_DATA;
2230                 }
2231                 else
2232                 {
2233                     strcpyW(lpProvider, providerTable->table[i].name);
2234                     ret = WN_SUCCESS;
2235                     /* FIXME: is *lpBufferSize set to the number of characters
2236                      * copied? */
2237                 }
2238             }
2239         }
2240         else
2241             ret = WN_NO_NETWORK;
2242     }
2243     if (ret)
2244         SetLastError(ret);
2245     TRACE("Returning %d\n", ret);
2246     return ret;
2247 }