[MPR] Implement remembered connection enumeration
[reactos.git] / dll / win32 / mpr / wnet.c
index 162b41f..14192c8 100644 (file)
@@ -73,12 +73,21 @@ typedef struct _WNetProviderTable
     WNetProvider     table[1];
 } WNetProviderTable, *PWNetProviderTable;
 
+#ifndef __REACTOS__
 #define WNET_ENUMERATOR_TYPE_NULL      0
 #define WNET_ENUMERATOR_TYPE_GLOBAL    1
 #define WNET_ENUMERATOR_TYPE_PROVIDER  2
 #define WNET_ENUMERATOR_TYPE_CONTEXT   3
 #define WNET_ENUMERATOR_TYPE_CONNECTED 4
+#else
+#define WNET_ENUMERATOR_TYPE_GLOBAL     0
+#define WNET_ENUMERATOR_TYPE_PROVIDER   1
+#define WNET_ENUMERATOR_TYPE_CONTEXT    2
+#define WNET_ENUMERATOR_TYPE_CONNECTED  3
+#define WNET_ENUMERATOR_TYPE_REMEMBERED 4
+#endif
 
+#ifndef __REACTOS__
 /* An WNet enumerator.  Note that the type doesn't correspond to the scope of
  * the enumeration; it represents one of the following types:
  * - a 'null' enumeration, one that contains no members
@@ -93,6 +102,23 @@ typedef struct _WNetProviderTable
  *   into a global enumeration (so the enumeration continues across all
  *   providers).
  */
+#else
+/* An WNet enumerator.  Note that the type doesn't correspond to the scope of
+ * the enumeration; it represents one of the following types:
+ * - a global enumeration, one that's executed across all providers
+ * - a provider-specific enumeration, one that's only executed by a single
+ *   provider
+ * - a context enumeration.  I know this contradicts what I just said about
+ *   there being no correspondence between the scope and the type, but it's
+ *   necessary for the special case that a "Entire Network" entry needs to
+ *   be enumerated in an enumeration of the context scope.  Thus an enumeration
+ *   of the context scope results in a context type enumerator, which morphs
+ *   into a global enumeration (so the enumeration continues across all
+ *   providers).
+ * - a remembered enumeration, not related to providers themselves, but it
+ *   is a registry enumeration for saved connections
+ */
+#endif
 typedef struct _WNetEnumerator
 {
     DWORD          enumType;
@@ -106,6 +132,13 @@ typedef struct _WNetEnumerator
     {
         NETRESOURCEW* net;
         HANDLE* handles;
+#ifdef __REACTOS__
+        struct
+        {
+            HKEY registry;
+            DWORD index;
+        } remembered;
+#endif
     } specific;
 } WNetEnumerator, *PWNetEnumerator;
 
@@ -553,6 +586,7 @@ static void _freeEnumNetResource(LPNETRESOURCEW lpNet)
     }
 }
 
+#ifndef __REACTOS__
 static PWNetEnumerator _createNullEnumerator(void)
 {
     PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
@@ -562,6 +596,7 @@ static PWNetEnumerator _createNullEnumerator(void)
         ret->enumType = WNET_ENUMERATOR_TYPE_NULL;
     return ret;
 }
+#endif
 
 static PWNetEnumerator _createGlobalEnumeratorW(DWORD dwScope, DWORD dwType,
  DWORD dwUsage, LPNETRESOURCEW lpNet)
@@ -639,6 +674,22 @@ static PWNetEnumerator _createConnectedEnumerator(DWORD dwScope, DWORD dwType,
     return ret;
 }
 
+#ifdef __REACTOS__
+static PWNetEnumerator _createRememberedEnumerator(DWORD dwScope, DWORD dwType,
+ HKEY remembered)
+{
+    PWNetEnumerator ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+    if (ret)
+    {
+        ret->enumType = WNET_ENUMERATOR_TYPE_REMEMBERED;
+        ret->dwScope = dwScope;
+        ret->dwType = dwType;
+        ret->specific.remembered.registry = remembered;
+    }
+    return ret;
+}
+#endif
+
 /* Thunks the array of wide-string LPNETRESOURCEs lpNetArrayIn into buffer
  * lpBuffer, with size *lpBufferSize.  lpNetArrayIn contains *lpcCount entries
  * to start.  On return, *lpcCount reflects the number thunked into lpBuffer.
@@ -1004,8 +1055,28 @@ DWORD WINAPI WNetOpenEnumW( DWORD dwScope, DWORD dwType, DWORD dwUsage,
                 ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
                 break;
             case RESOURCE_REMEMBERED:
+#ifndef __REACTOS__
                 *lphEnum = _createNullEnumerator();
                 ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+#else
+                {
+                    HKEY remembered, user_profile;
+
+                    ret = WN_OUT_OF_MEMORY;
+                    if (RegOpenCurrentUser(KEY_READ, &user_profile) == ERROR_SUCCESS)
+                    {
+                        WCHAR subkey[8] = {'N', 'e', 't', 'w', 'o', 'r', 'k', 0};
+
+                        if (RegOpenKeyExW(user_profile, subkey, 0, KEY_READ, &remembered) == ERROR_SUCCESS)
+                        {
+                            *lphEnum = _createRememberedEnumerator(dwScope, dwType, remembered);
+                            ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                        }
+
+                        RegCloseKey(user_profile);
+                    }
+                }
+#endif
                 break;
             default:
                 WARN("unknown scope 0x%08x\n", dwScope);
@@ -1512,6 +1583,168 @@ static DWORD _enumerateConnectedW(PWNetEnumerator enumerator, DWORD* user_count,
     return ret;
 }
 
+#ifdef __REACTOS__
+static const WCHAR connectionType[] = { 'C','o','n','n','e','c','t',
+ 'i','o','n','T','y','p','e',0 };
+static const WCHAR providerName[] = { 'P','r','o','v','i','d','e','r',
+ 'N','a','m','e',0 };
+static const WCHAR remotePath[] = { 'R','e','m','o','t','e','P',
+ 'a','t','h',0 };
+
+static DWORD _enumeratorRememberedW(PWNetEnumerator enumerator, DWORD* user_count,
+                                    void* user_buffer, DWORD* user_size)
+{
+    HKEY registry, connection;
+    WCHAR buffer[255];
+    DWORD index, ret, type, len, size, provider_size, remote_size, full_size, total_count, size_left = *user_size;
+    NETRESOURCEW * net_buffer = user_buffer;
+    WCHAR * str;
+
+    if (!enumerator)
+        return WN_BAD_POINTER;
+    if (enumerator->enumType != WNET_ENUMERATOR_TYPE_REMEMBERED)
+        return WN_BAD_VALUE;
+    if (!user_count || !user_buffer || !user_size)
+        return WN_BAD_POINTER;
+    if (!providerTable)
+        return WN_NO_NETWORK;
+
+    /* We will do the work in a single loop, so here is some things:
+     * we write netresource at the begin of the user buffer
+     * we write strings at the end of the user buffer
+     */
+    total_count = 0;
+    type = enumerator->dwType;
+    registry = enumerator->specific.remembered.registry;
+    str = (WCHAR *)((ULONG_PTR)user_buffer + *user_size - sizeof(WCHAR));
+    for (index = enumerator->specific.remembered.index; ; ++index)
+    {
+        enumerator->specific.remembered.index = index;
+
+        if (*user_count != -1 && total_count == *user_count)
+        {
+            ret = WN_SUCCESS;
+            break;
+        }
+
+        if (size_left < sizeof(NETRESOURCEW))
+        {
+            ret = WN_MORE_DATA;
+            break;
+        }
+
+        len = ARRAY_SIZE(buffer);
+        ret = RegEnumKeyExW(registry, index, buffer, &len, NULL, NULL, NULL, NULL);
+        if (ret != ERROR_SUCCESS)
+        {
+            /* We're done, that's a success! */
+            if (ret == ERROR_NO_MORE_ITEMS)
+            {
+                ret = WN_SUCCESS;
+                break;
+            }
+
+            continue;
+        }
+
+        if (RegOpenKeyExW(registry, buffer, 0, KEY_READ, &connection) != ERROR_SUCCESS)
+        {
+            continue;
+        }
+
+        size = sizeof(DWORD);
+        RegQueryValueExW(connection, connectionType, NULL, NULL, (BYTE *)&net_buffer->dwType, &size);
+        if (type != RESOURCETYPE_ANY && net_buffer->dwType != type)
+        {
+            RegCloseKey(connection);
+            continue;
+        }
+
+        net_buffer->dwScope = RESOURCE_REMEMBERED;
+        net_buffer->dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+        net_buffer->dwUsage = RESOURCEUSAGE_CONNECTABLE;
+
+        size_left -= sizeof(NETRESOURCEW);
+
+        /* Compute the whole size */
+        full_size = 0;
+        if (RegQueryValueExW(connection, providerName, NULL, NULL, NULL, &provider_size) != ERROR_SUCCESS)
+        {
+            RegCloseKey(connection);
+            continue;
+        }
+        full_size += provider_size;
+
+        if (RegQueryValueExW(connection, remotePath, NULL, NULL, NULL, &remote_size) != ERROR_SUCCESS)
+        {
+            RegCloseKey(connection);
+            continue;
+        }
+        full_size += remote_size;
+
+        /* FIXME: this only supports drive letters */
+        full_size += 3 * sizeof(WCHAR);
+        if (full_size > size_left)
+        {
+            RegCloseKey(connection);
+            ret = WN_MORE_DATA;
+            break;
+        }
+
+        str -= 3;
+        str[0] = buffer[0];
+        str[1] = L':';
+        str[2] = 0;
+        net_buffer->lpLocalName = str;
+        size_left -= 3 * sizeof(WCHAR);
+
+        size = provider_size;
+        str -= (provider_size / sizeof(WCHAR));
+        ret = RegQueryValueExW(connection, providerName, NULL, NULL, (BYTE *)str, &size);
+        if (ret == ERROR_MORE_DATA)
+        {
+            RegCloseKey(connection);
+            ret = WN_MORE_DATA;
+            break;
+        }
+        net_buffer->lpProvider = str;
+        size_left -= size;
+
+        size = remote_size;
+        str -= (remote_size / sizeof(WCHAR));
+        ret = RegQueryValueExW(connection, remotePath, NULL, NULL, (BYTE *)str, &size);
+        if (ret == ERROR_MORE_DATA)
+        {
+            RegCloseKey(connection);
+            ret = WN_MORE_DATA;
+            break;
+        }
+        net_buffer->lpRemoteName = str;
+        size_left -= size;
+
+        RegCloseKey(connection);
+
+        net_buffer->lpComment = NULL;
+
+        ++total_count;
+        ++net_buffer;
+    }
+
+    if (total_count == 0)
+        ret = WN_NO_MORE_ENTRIES;
+
+    *user_count = total_count;
+
+    if (ret != WN_MORE_DATA && ret != WN_NO_MORE_ENTRIES)
+        ret = WN_SUCCESS;
+
+    if (ret == WN_MORE_DATA)
+        *user_size = *user_size + full_size;
+
+    return ret;
+}
+#endif
+
 /*********************************************************************
  * WNetEnumResourceW [MPR.@]
  */
@@ -1541,9 +1774,11 @@ DWORD WINAPI WNetEnumResourceW( HANDLE hEnum, LPDWORD lpcCount,
 
         switch (enumerator->enumType)
         {
+#ifndef __REACTOS__
             case WNET_ENUMERATOR_TYPE_NULL:
                 ret = WN_NO_MORE_ENTRIES;
                 break;
+#endif
             case WNET_ENUMERATOR_TYPE_GLOBAL:
                 ret = _enumerateGlobalW(enumerator, lpcCount, lpBuffer,
                  lpBufferSize);
@@ -1560,6 +1795,12 @@ DWORD WINAPI WNetEnumResourceW( HANDLE hEnum, LPDWORD lpcCount,
                 ret = _enumerateConnectedW(enumerator, lpcCount, lpBuffer,
                  lpBufferSize);
                 break;
+#ifdef __REACTOS__
+            case WNET_ENUMERATOR_TYPE_REMEMBERED:
+                ret = _enumeratorRememberedW(enumerator, lpcCount, lpBuffer,
+                 lpBufferSize);
+                break;
+#endif
             default:
                 WARN("bogus enumerator type!\n");
                 ret = WN_NO_NETWORK;
@@ -1587,9 +1828,11 @@ DWORD WINAPI WNetCloseEnum( HANDLE hEnum )
 
         switch (enumerator->enumType)
         {
+#ifndef __REACTOS__
             case WNET_ENUMERATOR_TYPE_NULL:
                 ret = WN_SUCCESS;
                 break;
+#endif
             case WNET_ENUMERATOR_TYPE_GLOBAL:
                 if (enumerator->specific.net)
                     _freeEnumNetResource(enumerator->specific.net);
@@ -1614,6 +1857,12 @@ DWORD WINAPI WNetCloseEnum( HANDLE hEnum )
                 HeapFree(GetProcessHeap(), 0, handles);
                 ret = WN_SUCCESS;
                 break;
+#ifdef __REACTOS__
+            case WNET_ENUMERATOR_TYPE_REMEMBERED:
+                RegCloseKey(enumerator->specific.remembered.registry);
+                ret = WN_SUCCESS;
+                break;
+#endif
             default:
                 WARN("bogus enumerator type!\n");
                 ret = WN_BAD_HANDLE;