[NETAPI32]
authorEric Kohl <eric.kohl@reactos.org>
Sun, 23 Dec 2012 18:56:13 +0000 (18:56 +0000)
committerEric Kohl <eric.kohl@reactos.org>
Sun, 23 Dec 2012 18:56:13 +0000 (18:56 +0000)
- Implement NetUserEnum partially.
- Move common code into a separate file.

svn path=/trunk/; revision=57980

reactos/dll/win32/netapi32/CMakeLists.txt
reactos/dll/win32/netapi32/local_group.c
reactos/dll/win32/netapi32/misc.c [new file with mode: 0644]
reactos/dll/win32/netapi32/netapi32.h
reactos/dll/win32/netapi32/user.c

index 64de1e0..457eca5 100644 (file)
@@ -11,6 +11,7 @@ list(APPEND SOURCE
     ds.c
     group.c
     local_group.c
+    misc.c
     nbcmdqueue.c
     nbnamecache.c
     nbt.c
index 546eed4..46655ca 100644 (file)
@@ -56,99 +56,6 @@ typedef struct _ENUM_CONTEXT
 
 } ENUM_CONTEXT, *PENUM_CONTEXT;
 
-static SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
-
-
-static
-NTSTATUS
-GetAccountDomainSid(PSID *AccountDomainSid)
-{
-    PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
-    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
-    LSA_HANDLE PolicyHandle = NULL;
-    ULONG Length = 0;
-    NTSTATUS Status;
-
-    memset(&ObjectAttributes, 0, sizeof(LSA_OBJECT_ATTRIBUTES));
-
-    Status = LsaOpenPolicy(NULL,
-                           &ObjectAttributes,
-                           POLICY_VIEW_LOCAL_INFORMATION,
-                           &PolicyHandle);
-    if (!NT_SUCCESS(Status))
-    {
-        ERR("LsaOpenPolicy failed (Status %08lx)\n", Status);
-        return Status;
-    }
-
-    Status = LsaQueryInformationPolicy(PolicyHandle,
-                                       PolicyAccountDomainInformation,
-                                       (PVOID *)&AccountDomainInfo);
-    if (!NT_SUCCESS(Status))
-    {
-        ERR("LsaQueryInformationPolicy failed (Status %08lx)\n", Status);
-        goto done;
-    }
-
-    Length = RtlLengthSid(AccountDomainInfo->DomainSid);
-
-    *AccountDomainSid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
-    if (*AccountDomainSid == NULL)
-    {
-        ERR("Failed to allocate SID\n");
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    memcpy(*AccountDomainSid, AccountDomainInfo->DomainSid, Length);
-
-done:
-    if (AccountDomainInfo != NULL)
-        LsaFreeMemory(AccountDomainInfo);
-
-    LsaClose(PolicyHandle);
-
-    return Status;
-}
-
-
-static
-NTSTATUS
-GetBuiltinDomainSid(PSID *BuiltinDomainSid)
-{
-    PSID Sid = NULL;
-    PULONG Ptr;
-    NTSTATUS Status = STATUS_SUCCESS;
-
-    *BuiltinDomainSid = NULL;
-
-    Sid = RtlAllocateHeap(RtlGetProcessHeap(),
-                          0,
-                          RtlLengthRequiredSid(1));
-    if (Sid == NULL)
-        return STATUS_INSUFFICIENT_RESOURCES;
-
-    Status = RtlInitializeSid(Sid,
-                              &NtAuthority,
-                              1);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
-    Ptr = RtlSubAuthoritySid(Sid, 0);
-    *Ptr = SECURITY_BUILTIN_DOMAIN_RID;
-
-    *BuiltinDomainSid = Sid;
-
-done:
-    if (!NT_SUCCESS(Status))
-    {
-        if (Sid != NULL)
-            RtlFreeHeap(RtlGetProcessHeap(), 0, Sid);
-    }
-
-    return Status;
-}
-
 
 /************************************************************
  *                NetLocalGroupAdd  (NETAPI32.@)
diff --git a/reactos/dll/win32/netapi32/misc.c b/reactos/dll/win32/netapi32/misc.c
new file mode 100644 (file)
index 0000000..0a46d55
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         NetAPI DLL
+ * FILE:            reactos/dll/win32/netapi32/misc.c
+ * PURPOSE:         Helper functions
+ *
+ * PROGRAMMERS:     Eric Kohl
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <stdarg.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "lmcons.h"
+#include "ntsecapi.h"
+#include "wine/debug.h"
+
+#define NTOS_MODE_USER
+#include <ndk/rtlfuncs.h>
+#include "ntsam.h"
+#include "netapi32.h"
+
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+
+/* GLOBALS *******************************************************************/
+
+static SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
+
+
+/* FUNCTIONS *****************************************************************/
+
+NTSTATUS
+GetAccountDomainSid(PSID *AccountDomainSid)
+{
+    PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
+    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+    LSA_HANDLE PolicyHandle = NULL;
+    ULONG Length = 0;
+    NTSTATUS Status;
+
+    memset(&ObjectAttributes, 0, sizeof(LSA_OBJECT_ATTRIBUTES));
+
+    Status = LsaOpenPolicy(NULL,
+                           &ObjectAttributes,
+                           POLICY_VIEW_LOCAL_INFORMATION,
+                           &PolicyHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("LsaOpenPolicy failed (Status %08lx)\n", Status);
+        return Status;
+    }
+
+    Status = LsaQueryInformationPolicy(PolicyHandle,
+                                       PolicyAccountDomainInformation,
+                                       (PVOID *)&AccountDomainInfo);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("LsaQueryInformationPolicy failed (Status %08lx)\n", Status);
+        goto done;
+    }
+
+    Length = RtlLengthSid(AccountDomainInfo->DomainSid);
+
+    *AccountDomainSid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
+    if (*AccountDomainSid == NULL)
+    {
+        ERR("Failed to allocate SID\n");
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto done;
+    }
+
+    memcpy(*AccountDomainSid, AccountDomainInfo->DomainSid, Length);
+
+done:
+    if (AccountDomainInfo != NULL)
+        LsaFreeMemory(AccountDomainInfo);
+
+    LsaClose(PolicyHandle);
+
+    return Status;
+}
+
+
+NTSTATUS
+GetBuiltinDomainSid(PSID *BuiltinDomainSid)
+{
+    PSID Sid = NULL;
+    PULONG Ptr;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    *BuiltinDomainSid = NULL;
+
+    Sid = RtlAllocateHeap(RtlGetProcessHeap(),
+                          0,
+                          RtlLengthRequiredSid(1));
+    if (Sid == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    Status = RtlInitializeSid(Sid,
+                              &NtAuthority,
+                              1);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    Ptr = RtlSubAuthoritySid(Sid, 0);
+    *Ptr = SECURITY_BUILTIN_DOMAIN_RID;
+
+    *BuiltinDomainSid = Sid;
+
+done:
+    if (!NT_SUCCESS(Status))
+    {
+        if (Sid != NULL)
+            RtlFreeHeap(RtlGetProcessHeap(), 0, Sid);
+    }
+
+    return Status;
+}
+
+/* EOF */
index f7a0a7a..67f5548 100644 (file)
@@ -6,5 +6,12 @@ NET_API_STATUS
 WINAPI
 NetpNtStatusToApiStatus(NTSTATUS Status);
 
+/* misc.c */
+
+NTSTATUS
+GetAccountDomainSid(PSID *AccountDomainSid);
+
+NTSTATUS
+GetBuiltinDomainSid(PSID *BuiltinDomainSid);
 
 #endif
\ No newline at end of file
index 6db0c81..1f6262e 100644 (file)
 #include "wine/unicode.h"
 #include "wine/list.h"
 
+#define NTOS_MODE_USER
+#include <ndk/rtlfuncs.h>
+#include "ntsam.h"
+#include "netapi32.h"
+
 WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
 
+
+typedef struct _ENUM_CONTEXT
+{
+    SAM_HANDLE ServerHandle;
+    SAM_HANDLE BuiltinDomainHandle;
+    SAM_HANDLE AccountDomainHandle;
+
+    SAM_ENUMERATE_HANDLE EnumerationContext;
+    PSAM_RID_ENUMERATION Buffer;
+    ULONG Returned;
+    ULONG Index;
+    BOOLEAN BuiltinDone;
+
+} ENUM_CONTEXT, *PENUM_CONTEXT;
+
+
 /* NOTE: So far, this is implemented to support tests that require user logins,
  *       but not designed to handle real user databases. Those should probably
  *       be synced with either the host's user database or with Samba.
@@ -134,7 +155,7 @@ end:
 }
 
 /************************************************************
- *                NetUserAdd (NETAPI32.@)
+ * NetUserAdd (NETAPI32.@)
  */
 NET_API_STATUS
 WINAPI
@@ -257,7 +278,7 @@ NetUserChangePassword(LPCWSTR domainname,
 
 
 /************************************************************
- *                NetUserDel  (NETAPI32.@)
+ * NetUserDel  (NETAPI32.@)
  */
 NET_API_STATUS
 WINAPI
@@ -287,7 +308,7 @@ NetUserDel(LPCWSTR servername,
 
 
 /************************************************************
- *                NetUserEnum  (NETAPI32.@)
+ * NetUserEnum  (NETAPI32.@)
  */
 NET_API_STATUS
 WINAPI
@@ -300,15 +321,466 @@ NetUserEnum(LPCWSTR servername,
             LPDWORD totalentries,
             LPDWORD resume_handle)
 {
-  FIXME("(%s,%d, 0x%d,%p,%d,%p,%p,%p) stub!\n", debugstr_w(servername), level,
-        filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
+    PSAM_RID_ENUMERATION CurrentUser;
+    PENUM_CONTEXT EnumContext = NULL;
+    LPVOID Buffer = NULL;
+    PSID DomainSid = NULL;
+    PUSER_INFO_0 UserInfo0;
+    PUSER_INFO_1 UserInfo1;
+    PUSER_INFO_20 UserInfo20;
+
+    LPWSTR Ptr;
+    ULONG i;
+    ULONG Size;
+
+    SAM_HANDLE UserHandle = NULL;
+    PUSER_ACCOUNT_INFORMATION UserInfo = NULL;
+
+    NET_API_STATUS ApiStatus = NERR_Success;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    FIXME("(%s %d 0x%d %p %d %p %p %p) stub!\n", debugstr_w(servername), level,
+          filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
+
+    *entriesread = 0;
+    *totalentries = 0;
+    *bufptr = NULL;
+
+    if (resume_handle != NULL && *resume_handle != 0)
+    {
+        EnumContext = (PENUM_CONTEXT)*resume_handle;
+    }
+    else
+    {
+        ApiStatus = NetApiBufferAllocate(sizeof(ENUM_CONTEXT), (PVOID*)&EnumContext);
+        if (ApiStatus != NERR_Success)
+            goto done;
+
+        EnumContext->EnumerationContext = 0;
+        EnumContext->Buffer = NULL;
+        EnumContext->Returned = 0;
+        EnumContext->Index = 0;
+        EnumContext->BuiltinDone = FALSE;
+
+        Status = SamConnect(NULL,
+                            &EnumContext->ServerHandle,
+                            SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
+                            NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamConnect failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        Status = GetAccountDomainSid(&DomainSid);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("GetAccountDomainSid failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        Status = SamOpenDomain(EnumContext->ServerHandle,
+                               DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
+                               DomainSid,
+                               &EnumContext->AccountDomainHandle);
+
+        RtlFreeHeap(RtlGetProcessHeap(), 0, DomainSid);
+
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamOpenDomain failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        Status = GetBuiltinDomainSid(&DomainSid);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("GetAccountDomainSid failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        Status = SamOpenDomain(EnumContext->ServerHandle,
+                               DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
+                               DomainSid,
+                               &EnumContext->BuiltinDomainHandle);
+
+        RtlFreeHeap(RtlGetProcessHeap(), 0, DomainSid);
+
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamOpenDomain failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+    }
+
+//    while (TRUE)
+//    {
+        TRACE("EnumContext->Index: %lu\n", EnumContext->Index);
+        TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
+
+        if (EnumContext->Index >= EnumContext->Returned)
+        {
+//            if (EnumContext->BuiltinDone == TRUE)
+//            {
+//                ApiStatus = NERR_Success;
+//                goto done;
+//            }
+
+            TRACE("Calling SamEnumerateUsersInDomain\n");
+            Status = SamEnumerateUsersInDomain(EnumContext->AccountDomainHandle, //BuiltinDomainHandle,
+                                               &EnumContext->EnumerationContext,
+                                               0,
+                                               (PVOID *)&EnumContext->Buffer,
+                                               prefmaxlen,
+                                               &EnumContext->Returned);
+
+            TRACE("SamEnumerateUsersInDomain returned (Status %08lx)\n", Status);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("SamEnumerateUsersInDomain failed (Status %08lx)\n", Status);
+                ApiStatus = NetpNtStatusToApiStatus(Status);
+                goto done;
+            }
+
+            if (Status == STATUS_MORE_ENTRIES)
+            {
+                ApiStatus = NERR_BufTooSmall;
+                goto done;
+            }
+            else
+            {
+                EnumContext->BuiltinDone = TRUE;
+            }
+        }
+
+        TRACE("EnumContext: %lu\n", EnumContext);
+        TRACE("EnumContext->Returned: %lu\n", EnumContext->Returned);
+        TRACE("EnumContext->Buffer: %p\n", EnumContext->Buffer);
+
+        /* Get a pointer to the current user */
+        CurrentUser = &EnumContext->Buffer[EnumContext->Index];
+
+        TRACE("RID: %lu\n", CurrentUser->RelativeId);
+
+        Status = SamOpenUser(EnumContext->AccountDomainHandle, //BuiltinDomainHandle,
+                             USER_READ_GENERAL | USER_READ_PREFERENCES | USER_READ_LOGON | USER_READ_ACCOUNT,
+                             CurrentUser->RelativeId,
+                             &UserHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamOpenUser failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        Status = SamQueryInformationUser(UserHandle,
+                                         UserAccountInformation,
+                                         (PVOID *)&UserInfo);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("SamQueryInformationUser failed (Status %08lx)\n", Status);
+            ApiStatus = NetpNtStatusToApiStatus(Status);
+            goto done;
+        }
+
+        SamCloseHandle(UserHandle);
+        UserHandle = NULL;
+
+        switch (level)
+        {
+            case 0:
+                Size = sizeof(USER_INFO_0) +
+                       UserInfo->UserName.Length + sizeof(WCHAR);
+                break;
+
+            case 1:
+                Size = sizeof(USER_INFO_1) +
+                       UserInfo->UserName.Length + sizeof(WCHAR);
+
+                if (UserInfo->HomeDirectory.Length > 0)
+                    Size += UserInfo->HomeDirectory.Length + sizeof(WCHAR);
+
+                if (UserInfo->AdminComment.Length > 0)
+                    Size += UserInfo->AdminComment.Length + sizeof(WCHAR);
+
+                if (UserInfo->ScriptPath.Length > 0)
+                    Size = UserInfo->ScriptPath.Length + sizeof(WCHAR);
+                break;
+
+//            case 2:
+//            case 3:
+//            case 10:
+//            case 11:
+
+            case 20:
+                Size = sizeof(USER_INFO_20) +
+                       UserInfo->UserName.Length + sizeof(WCHAR);
+
+                if (UserInfo->FullName.Length > 0)
+                    Size += UserInfo->FullName.Length + sizeof(WCHAR);
+
+                if (UserInfo->AdminComment.Length > 0)
+                    Size += UserInfo->AdminComment.Length + sizeof(WCHAR);
+                break;
+
+//            case 23:
+
+            default:
+                ApiStatus = ERROR_INVALID_LEVEL;
+                goto done;
+        }
+
+        ApiStatus = NetApiBufferAllocate(Size, &Buffer);
+        if (ApiStatus != NERR_Success)
+            goto done;
+
+        switch (level)
+        {
+            case 0:
+                UserInfo0 = (PUSER_INFO_0)Buffer;
+
+                Ptr = (LPWSTR)((ULONG_PTR)UserInfo0 + sizeof(USER_INFO_0));
+                UserInfo0->usri0_name = Ptr;
+
+                memcpy(UserInfo0->usri0_name,
+                       UserInfo->UserName.Buffer,
+                       UserInfo->UserName.Length);
+                UserInfo0->usri0_name[UserInfo->UserName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+                break;
+
+            case 1:
+                UserInfo1 = (PUSER_INFO_1)Buffer;
+
+                Ptr = (LPWSTR)((ULONG_PTR)UserInfo1 + sizeof(USER_INFO_1));
 
-  return ERROR_ACCESS_DENIED;
+                UserInfo1->usri1_name = Ptr;
+
+                memcpy(UserInfo1->usri1_name,
+                       UserInfo->UserName.Buffer,
+                       UserInfo->UserName.Length);
+                UserInfo1->usri1_name[UserInfo->UserName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+                Ptr = (LPWSTR)((ULONG_PTR)Ptr + UserInfo->UserName.Length + sizeof(WCHAR));
+
+                UserInfo1->usri1_password = NULL;
+
+                UserInfo1->usri1_password_age = 0; /* FIXME */
+
+                UserInfo1->usri1_priv = 0; /* FIXME */
+
+                if (UserInfo->HomeDirectory.Length > 0)
+                {
+                    UserInfo1->usri1_home_dir = Ptr;
+
+                    memcpy(UserInfo1->usri1_home_dir,
+                           UserInfo->HomeDirectory.Buffer,
+                           UserInfo->HomeDirectory.Length);
+                    UserInfo1->usri1_home_dir[UserInfo->HomeDirectory.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+                    Ptr = (LPWSTR)((ULONG_PTR)Ptr + UserInfo->HomeDirectory.Length + sizeof(WCHAR));
+                }
+
+                if (UserInfo->AdminComment.Length > 0)
+                {
+                    UserInfo1->usri1_comment = Ptr;
+
+                    memcpy(UserInfo1->usri1_comment,
+                           UserInfo->AdminComment.Buffer,
+                           UserInfo->AdminComment.Length);
+                    UserInfo1->usri1_comment[UserInfo->AdminComment.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+                    Ptr = (LPWSTR)((ULONG_PTR)Ptr + UserInfo->AdminComment.Length + sizeof(WCHAR));
+                }
+
+                UserInfo1->usri1_flags = UserInfo->UserAccountControl;
+
+                if (UserInfo->ScriptPath.Length > 0)
+                {
+                    UserInfo1->usri1_script_path = Ptr;
+
+                    memcpy(UserInfo1->usri1_script_path,
+                           UserInfo->ScriptPath.Buffer,
+                           UserInfo->ScriptPath.Length);
+                    UserInfo1->usri1_script_path[UserInfo->ScriptPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
+                }
+                break;
+
+//            case 2:
+//            case 3:
+//            case 10:
+//            case 11:
+
+            case 20:
+                UserInfo20 = (PUSER_INFO_20)Buffer;
+
+                Ptr = (LPWSTR)((ULONG_PTR)UserInfo20 + sizeof(USER_INFO_20));
+
+                UserInfo20->usri20_name = Ptr;
+
+                memcpy(UserInfo20->usri20_name,
+                       UserInfo->UserName.Buffer,
+                       UserInfo->UserName.Length);
+                UserInfo20->usri20_name[UserInfo->UserName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+                Ptr = (LPWSTR)((ULONG_PTR)Ptr + UserInfo->UserName.Length + sizeof(WCHAR));
+
+                if (UserInfo->FullName.Length > 0)
+                {
+                    UserInfo20->usri20_full_name = Ptr;
+
+                    memcpy(UserInfo20->usri20_full_name,
+                           UserInfo->FullName.Buffer,
+                           UserInfo->FullName.Length);
+                    UserInfo20->usri20_full_name[UserInfo->FullName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+                    Ptr = (LPWSTR)((ULONG_PTR)Ptr + UserInfo->FullName.Length + sizeof(WCHAR));
+                }
+
+                if (UserInfo->AdminComment.Length > 0)
+                {
+                    UserInfo20->usri20_comment = Ptr;
+
+                    memcpy(UserInfo20->usri20_comment,
+                           UserInfo->AdminComment.Buffer,
+                           UserInfo->AdminComment.Length);
+                    UserInfo20->usri20_comment[UserInfo->AdminComment.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+                    Ptr = (LPWSTR)((ULONG_PTR)Ptr + UserInfo->AdminComment.Length + sizeof(WCHAR));
+                }
+
+                UserInfo20->usri20_flags = UserInfo->UserAccountControl;
+                UserInfo20->usri20_user_id = CurrentUser->RelativeId;
+                break;
+
+//            case 23:
+        }
+
+        if (UserInfo != NULL)
+        {
+            if (UserInfo->UserName.Buffer != NULL)
+                SamFreeMemory(UserInfo->UserName.Buffer);
+
+            if (UserInfo->FullName.Buffer != NULL)
+                SamFreeMemory(UserInfo->FullName.Buffer);
+
+            if (UserInfo->HomeDirectory.Buffer != NULL)
+                SamFreeMemory(UserInfo->HomeDirectory.Buffer);
+
+            if (UserInfo->HomeDirectoryDrive.Buffer != NULL)
+                SamFreeMemory(UserInfo->HomeDirectoryDrive.Buffer);
+
+            if (UserInfo->ScriptPath.Buffer != NULL)
+                SamFreeMemory(UserInfo->ScriptPath.Buffer);
+
+            if (UserInfo->ProfilePath.Buffer != NULL)
+                SamFreeMemory(UserInfo->ProfilePath.Buffer);
+
+            if (UserInfo->AdminComment.Buffer != NULL)
+                SamFreeMemory(UserInfo->AdminComment.Buffer);
+
+            if (UserInfo->WorkStations.Buffer != NULL)
+                SamFreeMemory(UserInfo->WorkStations.Buffer);
+
+            if (UserInfo->LogonHours.LogonHours != NULL)
+                SamFreeMemory(UserInfo->LogonHours.LogonHours);
+
+            SamFreeMemory(UserInfo);
+            UserInfo = NULL;
+        }
+
+        EnumContext->Index++;
+
+        (*entriesread)++;
+//    }
+
+done:
+    if (ApiStatus == NERR_Success && EnumContext->Index < EnumContext->Returned)
+        ApiStatus = ERROR_MORE_DATA;
+
+    if (EnumContext != NULL)
+        *totalentries = EnumContext->Returned;
+
+    if (resume_handle == NULL || ApiStatus != ERROR_MORE_DATA)
+    {
+        if (EnumContext != NULL)
+        {
+            if (EnumContext->BuiltinDomainHandle != NULL)
+                SamCloseHandle(EnumContext->BuiltinDomainHandle);
+
+            if (EnumContext->AccountDomainHandle != NULL)
+                SamCloseHandle(EnumContext->AccountDomainHandle);
+
+            if (EnumContext->ServerHandle != NULL)
+                SamCloseHandle(EnumContext->ServerHandle);
+
+            if (EnumContext->Buffer != NULL)
+            {
+                for (i = 0; i < EnumContext->Returned; i++)
+                {
+                    SamFreeMemory(EnumContext->Buffer[i].Name.Buffer);
+                }
+
+                SamFreeMemory(EnumContext->Buffer);
+            }
+
+            NetApiBufferFree(EnumContext);
+            EnumContext = NULL;
+        }
+    }
+
+    if (UserHandle != NULL)
+        SamCloseHandle(UserHandle);
+
+    if (UserInfo != NULL)
+    {
+        if (UserInfo->UserName.Buffer != NULL)
+            SamFreeMemory(UserInfo->UserName.Buffer);
+
+        if (UserInfo->FullName.Buffer != NULL)
+            SamFreeMemory(UserInfo->FullName.Buffer);
+
+        if (UserInfo->HomeDirectory.Buffer != NULL)
+            SamFreeMemory(UserInfo->HomeDirectory.Buffer);
+
+        if (UserInfo->HomeDirectoryDrive.Buffer != NULL)
+            SamFreeMemory(UserInfo->HomeDirectoryDrive.Buffer);
+
+        if (UserInfo->ScriptPath.Buffer != NULL)
+            SamFreeMemory(UserInfo->ScriptPath.Buffer);
+
+        if (UserInfo->ProfilePath.Buffer != NULL)
+            SamFreeMemory(UserInfo->ProfilePath.Buffer);
+
+        if (UserInfo->AdminComment.Buffer != NULL)
+            SamFreeMemory(UserInfo->AdminComment.Buffer);
+
+        if (UserInfo->WorkStations.Buffer != NULL)
+            SamFreeMemory(UserInfo->WorkStations.Buffer);
+
+        if (UserInfo->LogonHours.LogonHours != NULL)
+            SamFreeMemory(UserInfo->LogonHours.LogonHours);
+
+        SamFreeMemory(UserInfo);
+    }
+
+    if (resume_handle != NULL)
+        *resume_handle = (DWORD_PTR)EnumContext;
+
+    *bufptr = (LPBYTE)Buffer;
+
+    TRACE("return %lu\n", ApiStatus);
+
+    return ApiStatus;
 }
 
 
 /************************************************************
- *                NetUserGetGroups (NETAPI32.@)
+ * NetUserGetGroups (NETAPI32.@)
  */
 NET_API_STATUS
 WINAPI
@@ -592,7 +1064,6 @@ NetUserSetGroups(LPCWSTR servername,
 }
 
 
-
 /******************************************************************************
  * NetUserSetInfo  (NETAPI32.@)
  */